From cc352d31847b97a9f56938f2dd5c367518f5a879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E6=96=B9=E6=88=90?= Date: Wed, 19 Nov 2025 19:36:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=B9=B6=E6=B7=BB=E5=8A=A0=E6=96=B0=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor: 优化API路由和响应模型 feat(admin): 添加App用户管理接口 feat(sms): 实现阿里云短信服务集成 feat(email): 添加SMTP邮件发送功能 feat(upload): 支持文件上传接口 feat(rate-limiter): 实现手机号限流器 fix: 修复计算步骤入库问题 docs: 更新API文档和测试计划 chore: 更新依赖和配置 --- .github/copilot-instructions.md | 54 - .../API 文档重组与分类(v1)实施计划.md | 59 + .../交易管理_用户管理_估值模块增量改造方案.md | 184 + .trae/documents/估值二期 API 设计方案.md | 71 + .../估值计算步骤 API 设计与落实方案.md | 184 + ...算任务的NameError并确保步骤入库与日志可见.md | 23 + ...并输出:估值二期前后端 API 需求总览文档.md | 29 + ...逻辑前提下修复步骤入库(step_order类型调整).md | 26 + .../完善计算步骤落库与测试输出详细化.md | 17 + .../documents/接入阿里云短信并提供两个接口.md | 99 + .../接口测试脚本(用户端 + 后台)实施计划.md | 41 + .../更新测试脚本以使用你提供的参数.md | 20 + .trae/documents/更新用户端 API 到文档.md | 48 + .../项目结构整理与架构文档(web目录).md | 103 + AGENTS.md | 23 - aaa.json | 83 - app/__init__.py | 22 + app/api/v1/__init__.py | 39 +- app/api/v1/apis/apis.py | 14 +- app/api/v1/app_users/admin_manage.py | 76 + app/api/v1/app_users/app_users.py | 69 +- app/api/v1/app_valuations/app_valuations.py | 132 +- app/api/v1/base/base.py | 12 +- app/api/v1/depts/depts.py | 12 +- app/api/v1/esg/esg.py | 12 +- app/api/v1/index/index.py | 12 +- app/api/v1/industry/industry.py | 11 +- app/api/v1/invoice/__init__.py | 3 + app/api/v1/invoice/invoice.py | 154 + app/api/v1/menus/menus.py | 13 +- app/api/v1/policy/policy.py | 12 +- app/api/v1/roles/roles.py | 17 +- app/api/v1/sms/sms.py | 197 + app/api/v1/transactions/__init__.py | 3 + app/api/v1/transactions/transactions.py | 104 + app/api/v1/upload/upload.py | 8 +- app/api/v1/users/users.py | 15 +- app/api/v1/valuations/valuations.py | 68 +- app/controllers/app_user.py | 24 + app/controllers/invoice.py | 286 ++ app/controllers/upload.py | 40 +- app/controllers/user_valuation.py | 4 + app/controllers/valuation.py | 59 +- app/models/__init__.py | 3 +- app/models/invoice.py | 64 + app/models/user.py | 17 +- app/models/valuation.py | 35 +- app/schemas/app_user.py | 48 +- app/schemas/base.py | 27 +- app/schemas/invoice.py | 102 + app/schemas/transactions.py | 25 + app/schemas/upload.py | 7 +- app/schemas/valuation.py | 55 +- app/services/email_client.py | 49 + app/services/rate_limiter.py | 52 + app/services/sms_client.py | 91 + app/services/sms_store.py | 144 + app/settings/config.py | 42 +- app/static/files/demo.pdf | 2 + app/static/files/demo_1.pdf | 2 + app/static/files/demo_1_2.pdf | 2 + app/static/files/demo_1_2_3.pdf | 2 + app/static/files/test.pdf | 1 + app/static/files/test_1.pdf | 1 + app/static/files/test_1_2.pdf | 1 + app/static/files/test_1_2_3.pdf | 1 + app/utils/__init__.py | 0 .../cultural_value_b2/cultural_value_b2.py | 132 +- .../sub_formulas/living_heritage_b21.py | 137 +- .../sub_formulas/pattern_gene_b22.py | 84 +- .../economic_value_b1/economic_value_b1.py | 188 +- .../sub_formulas/basic_value_b11.py | 198 +- .../sub_formulas/policy_multiplier_b13.py | 67 +- .../sub_formulas/traffic_factor_b12.py | 58 +- .../final_value_ab/final_value_a.py | 241 +- .../final_value_ab/model_value_b.py | 149 +- .../market_value_c/market_value_c.py | 161 +- .../sub_formulas/risk_adjustment_b3.py | 99 +- app/utils/专利.json | 4551 ----------------- demo_api.py | 384 -- docs/sql/add_transaction_menu.sql | 24 - docs/sql/add_valuation_menu.sql | 24 - docs/sql/complete_menu_setup.sql | 86 - .../.vite/deps_temp_e96670e1/package.json | 3 - package-lock.json | 34 - package.json | 5 - pyproject.toml | 5 + requirements.txt | 4 + run.py | 2 +- scripts/admin_flow_test.py | 213 + scripts/api_smoke_test.py | 270 + scripts/user_flow_test.py | 381 ++ test_dynamic_default.py | 86 - 估值字段.txt | 4 +- 94 files changed, 4791 insertions(+), 6054 deletions(-) delete mode 100644 .github/copilot-instructions.md create mode 100644 .trae/documents/API 文档重组与分类(v1)实施计划.md create mode 100644 .trae/documents/交易管理_用户管理_估值模块增量改造方案.md create mode 100644 .trae/documents/估值二期 API 设计方案.md create mode 100644 .trae/documents/估值计算步骤 API 设计与落实方案.md create mode 100644 .trae/documents/修复计算任务的NameError并确保步骤入库与日志可见.md create mode 100644 .trae/documents/合并输出:估值二期前后端 API 需求总览文档.md create mode 100644 .trae/documents/在不改计算逻辑前提下修复步骤入库(step_order类型调整).md create mode 100644 .trae/documents/完善计算步骤落库与测试输出详细化.md create mode 100644 .trae/documents/接入阿里云短信并提供两个接口.md create mode 100644 .trae/documents/接口测试脚本(用户端 + 后台)实施计划.md create mode 100644 .trae/documents/更新测试脚本以使用你提供的参数.md create mode 100644 .trae/documents/更新用户端 API 到文档.md create mode 100644 .trae/documents/项目结构整理与架构文档(web目录).md delete mode 100644 AGENTS.md delete mode 100644 aaa.json create mode 100644 app/api/v1/app_users/admin_manage.py create mode 100644 app/api/v1/invoice/__init__.py create mode 100644 app/api/v1/invoice/invoice.py create mode 100644 app/api/v1/sms/sms.py create mode 100644 app/api/v1/transactions/__init__.py create mode 100644 app/api/v1/transactions/transactions.py create mode 100644 app/controllers/invoice.py create mode 100644 app/models/invoice.py create mode 100644 app/schemas/invoice.py create mode 100644 app/schemas/transactions.py create mode 100644 app/services/email_client.py create mode 100644 app/services/rate_limiter.py create mode 100644 app/services/sms_client.py create mode 100644 app/services/sms_store.py create mode 100644 app/static/files/demo.pdf create mode 100644 app/static/files/demo_1.pdf create mode 100644 app/static/files/demo_1_2.pdf create mode 100644 app/static/files/demo_1_2_3.pdf create mode 100644 app/static/files/test.pdf create mode 100644 app/static/files/test_1.pdf create mode 100644 app/static/files/test_1_2.pdf create mode 100644 app/static/files/test_1_2_3.pdf delete mode 100644 app/utils/__init__.py delete mode 100644 app/utils/专利.json delete mode 100644 demo_api.py delete mode 100644 docs/sql/add_transaction_menu.sql delete mode 100644 docs/sql/add_valuation_menu.sql delete mode 100644 docs/sql/complete_menu_setup.sql delete mode 100644 node_modules/.vite/deps_temp_e96670e1/package.json delete mode 100644 package-lock.json delete mode 100644 package.json create mode 100644 scripts/admin_flow_test.py create mode 100644 scripts/api_smoke_test.py create mode 100644 scripts/user_flow_test.py delete mode 100644 test_dynamic_default.py diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index 16dc7b4..0000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,54 +0,0 @@ -## 项目(快速)指导 — 供 AI 编码代理使用 - -下面的要点帮助你快速理解并在本代码库中高效工作。保持简短、具体并以可执行示例为主。 - -- 项目类型:FastAPI 后端 (Python 3.11) + Vue3/Vite 前端(目录 `web/`)。后端使用 Tortoise ORM(配置在 `app/settings/config.py`),前端用 pnpm/vite。 - -- 快速启动(后端):在项目根目录 - - 建议 Python venv,然后安装依赖:`pip install -r requirements.txt`(或使用项目 README 中的 uv/uvenv 过程)。 - - 启动:`python run.py`。这会通过 `uvicorn` 运行 `app:app`(见 `run.py`),开启 `reload=True`,OpenAPI 在 `/docs`。 - -- 快速启动(前端):进入 `web/`,使用 pnpm(或 npm)安装并运行:`pnpm i`,`pnpm dev`。 - -- 后端关键入口 - - `run.py`:应用启动脚本,设置 uvicorn 日志格式并运行 `app:app`。 - - `app/__init__.py`:创建 FastAPI app(调用 `core/init_app.py` 中的注册函数):init 数据、注册中间件、异常处理与路由(路由前缀为 `/api`)。 - - `app/core/init_app.py`(注意:此文件包含启动时的路由/中间件/异常注册逻辑,请优先阅读它来理解请求生命周期)。 - -- 重要配置点 - - `app/settings/config.py`:使用 Pydantic Settings,包含 `TORTOISE_ORM`(默认 SQLite,db 文件在项目根 `db.sqlite3`)、JWT、SECRET_KEY、CORS 等。修改环境变量即可覆盖设置。 - - `app/utils/api_config.py`:提供 `api_config` 全局实例,用来存放第三方 API(示例:`chinaz`、`xiaohongshu`)。常用方法:`api_config.get_api_key(provider)`、`get_endpoint_config(provider, endpoint)`、`add_endpoint(...)`、`save_config()`。 - -- 路由与模块约定 - - API 版本化:`app/api/v1/` 下放置 v1 接口。路由统一由 `core/init_app.py` 通过 `register_routers(..., prefix='/api')` 注册。 - - 控制器(HTTP handlers)位于 `app/controllers/`,数据模型在 `app/models/`,Pydantic schemas 在 `app/schemas/`。 - -- 数据库与迁移 - - 使用 Tortoise ORM,`TORTOISE_ORM` 在 `app/settings/config.py`。项目把 `aerich.models` 列入 models(见配置),repository 中存在 `migrations/` 文件夹。若需变更模型,按项目现有工具链(如 aerich)执行迁移;在不确定时,先检查 `pyproject.toml`/`requirements.txt` 是否包含 aerich 并复核 README。 - -- 日志与持久化 - - 日志目录:`app/logs`(可在 `settings.LOGS_ROOT` 找到)。运行时可根据 `run.py` 中的 LOGGING_CONFIG 调整格式。 - -- 第三方 API 集成(示例) - - `api_config` 示例用法(Python): - ```py - from app.utils.api_config import api_config - cfg = api_config.get_endpoint_config('xiaohongshu', 'xiaohongshu_note_detail') - base = api_config.get_base_url('xiaohongshu') - key = api_config.get_api_key('xiaohongshu') - ``` - - 环境变量覆盖:CHINAZ_API_KEY、XIAOHONGSHU_TOKEN、EXAMPLE_API_KEY 等会被 `api_config` 或 settings 读取。 - -- 编辑/贡献约定(可自动推断的现有模式) - - 新增 API:在 `app/api/v1/...` 添加路由模块,控制器放 `app/controllers/`,schema 放 `app/schemas/`,并在 `core/init_app.py` 中确保路由被注册。 - - 新增模型:更新 `app/models/` 并生成迁移(项目使用 Tortoise + aerich 风格)。先检查 `migrations/models` 是否有对应变更。 - -- 调试提示 - - 本地运行时使用 `python run.py`(reload=True),然后访问 `http://localhost:9999/docs` 查看 OpenAPI,确认路由/依赖注入是否按预期工作。 - - 常见故障点:环境变量未设置(导致 API keys 丢失)、Tortoise 连接配置错误(检查 `TORTOISE_ORM.connections`)、以及中间件注册顺序会影响异常处理。 - -- 其它注意事项(小而具体) - - 前端以 `/api` 为后端前缀,修改后端接口时请同步前端 `web/src/api` 的调用。 - - `app/utils/api_config.py` 会在模块导入时创建 `api_config` 单例;修改该文件时注意导入时机(不要在模块顶层做阻塞网络调用)。 - -如果需要我把 README 中的启动说明转成更精确的 shell 命令(或添加 aerich 的迁移示例命令),我可以继续补充。请告诉我你希望强调的额外部分或需要澄清的地方。 diff --git a/.trae/documents/API 文档重组与分类(v1)实施计划.md b/.trae/documents/API 文档重组与分类(v1)实施计划.md new file mode 100644 index 0000000..ab35f4c --- /dev/null +++ b/.trae/documents/API 文档重组与分类(v1)实施计划.md @@ -0,0 +1,59 @@ +## 输出目标 +- 以 `admin`(后台)与 `app`(用户端)两大类重组全部现有 `v1` API。 +- 统一每个接口的文档格式:路径、方法、版本、功能说明、公开/认证、(admin)权限要求、请求参数与格式、响应结构、错误代码。 +- 版本标注统一为 `v1`(前缀 `"/api/v1"`)。 + +## 分类规则 +- `admin`:在 `app/api/v1/__init__.py:33-38,45-51` 通过 `dependencies=[DependAuth, DependPermission]` 绑定的模块及其接口:`user/role/menu/api/dept/auditlog/valuations/invoice/transactions/third_party_api`,以及 `base`(后台登录与个人信息)。 +- `app`:面向终端用户的模块:`app-user`、`app-valuations`、`sms`(登录与验证码相关)、`upload`。 +- 管理功能但当前公开(未绑定后台依赖):`industry/index/policy/esg`,归入 `admin(公开)`,在文档中明确“公开接口”。 + +## 文档结构 +- 顶层两章: + - `app(用户端)`:模块分组(用户认证与账户、用户资料与仪表盘、用户端估值、短信验证码、上传) + - `admin(后台)`:模块分组(用户管理、角色管理、菜单管理、API 权限管理、部门管理、审计日志、估值评估、发票管理、交易管理、第三方内置接口、内容管理:行业/指数/政策/ESG、基础登录/个人信息) +- 接口条目统一字段: + - 路径:`/api/v1//` + - 方法:`GET/POST/PUT/DELETE` + - 版本:`v1` + - 功能说明:一句话摘要 + - 公开/认证:`公开` 或 `需认证`(`admin` 另标注“需权限校验”) + - 权限要求(admin):是否受 `DependPermission` 控制(匹配 `(method, path)`) + - 请求参数:Query/Path/Body(Body 引用 `pydantic` 模型名) + - 响应结构:统一 `{code,msg,data}` 或分页 `{code,msg,data,total,page,page_size}`(引用响应模型) + - 错误代码:200/400/401/403/404/422/500(依据全局异常与业务抛出) + +## 信息来源与标注依据 +- 路由与版本:`app/api/v1/__init__.py:28-52`(前缀与模块挂载)。 +- 认证与权限: + - `admin` 统一:`DependAuth`、`DependPermission`(`app/api/v1/__init__.py:33-38,45-51`;依赖定义于 `app/core/dependency.py`)。 + - `app` 认证:`Depends(get_current_app_user)`/`Depends(get_current_app_user_id)`(`app/utils/app_user_jwt.py:51,71-72`)。 + - 单接口特例:`sms` 的 `/send-report` 需后台认证(`app/api/v1/sms/sms.py:68`)。 +- 请求/响应模型:端点签名与 `response_model`(来源 `app/schemas/*`)。 +- 错误码与统一响应:`app/core/init_app.py:56-59`(注册),`app/core/exceptions.py:15,23,31`(处理器)。 + +## 示例格式(两条) +- `app|POST /api/v1/sms/send-code`(v1) + - 功能:发送登录验证码到手机号 + - 公开/认证:公开 + - 请求参数(Body):`SendCodeRequest` + - 响应结构:`SendResponse`(`{code,msg,data}`) + - 错误码:`400/422/500` + - 代码参照:`app/api/v1/sms/sms.py:68` +- `admin|GET /api/v1/user/list`(v1) + - 功能:分页查询后台用户 + - 公开/认证:需认证;需权限校验 + - 请求参数(Query):分页与过滤 + - 响应结构:`SuccessExtra`(`{code,msg,data,total,page,page_size}`) + - 错误码:`401/403/422/500` + - 代码参照:`app/api/v1/__init__.py:33` + +## 交付内容 +- 生成统一的 Markdown 文档(建议 `docs/api-v1.md`),按 `app` 与 `admin` 两章、功能模块分组列出全部接口,逐条填充统一字段。 +- 附“错误码说明”与“认证/权限机制”章节,提供关键代码路径引用,便于后续维护与审计。 + +## 验证与维护 +- 通过聚合路由与端点扫描确认无遗漏;如有新接口,按相同格式追加。 +- 校验每条接口的认证与权限标注是否与代码一致;抽样比对响应结构、错误码与异常处理一致性。 + +请确认上述按 `admin` 与 `app` 分类的计划;确认后我将开始生成完整文档并交付。 \ No newline at end of file diff --git a/.trae/documents/交易管理_用户管理_估值模块增量改造方案.md b/.trae/documents/交易管理_用户管理_估值模块增量改造方案.md new file mode 100644 index 0000000..64c1c8c --- /dev/null +++ b/.trae/documents/交易管理_用户管理_估值模块增量改造方案.md @@ -0,0 +1,184 @@ +## 现状速览 + +* 后端框架:FastAPI;ORM:Tortoise;权限:`DependAuth` + `DependPermission` 挂载在 admin 路由(`app/api/v1/__init__.py:33-37`)。 + +* 用户端估值评估路由:`/api/v1/app-valuations`(`app/api/v1/app_valuations/app_valuations.py`),控制器分层清晰(`app/controllers/user_valuation.py`、`app/controllers/valuation.py`)。 + +* 上传目前仅支持图片(`app/controllers/upload.py:12-52`,`app/api/v1/upload/upload.py:7-14`)。 + +* 发票与抬头能力完善(`app/api/v1/invoice/invoice.py`、`app/controllers/invoice.py`)。 + +* Web 管理端用户列表当前使用 Mock 数据展示“剩余体验次数”(`web/src/views/user-management/user-list/index.vue:115-122` 与 `web/src/api/index.js:279-352`)。 + +## 目标改造概览 + +* 交易管理新增“邮件发送”接口,支持正文与附件,完备校验与日志。 + +* 用户管理新增“剩余估值次数”字段与管理员调额能力,提供操作日志(前/后值、类型、备注)。 + +* 估值表新增“报告/证书”URL 字段与多格式上传能力,生成可下载链接。 + +* 估值表新增“统一社会信用代码/身份证号”与“业务/传承介绍”,前后端同步与校验。 + +* 全量权限控制、API 文档补充、数据库变更记录与单元测试覆盖。 + +## 数据库与模型变更 + +* ValuationAssessment(`app/models/valuation.py`)新增: + + * `report_url: CharField(512)`、`certificate_url: CharField(512)` 用于管理员上传的报告/证书下载地址。 + + * `credit_code_or_id: CharField(64)` 用于统一社会信用代码或身份证号。 + + * `biz_intro: TextField` 业务/传承介绍。 + +* 用户配额与日志: + + * AppUser(`app/models/user.py`)新增 `remaining_quota: IntField(default=0)`。 + + * 新增操作日志模型(建议放入 `app/models/invoice.py` 同模块管理,或新建 `app/models/transaction.py`):`AppUserQuotaLog` 字段:`app_user_id`、`operator_id`、`operator_name`、`before_count`、`after_count`、`op_type`(如:付费估值)、`remark`、`created_at`。 + + * 邮件发送日志模型:`EmailSendLog` 字段:`email`、`subject`、`body` 摘要(前 N 字符)、`file_name`/`file_url`、`status`(OK/FAIL)、`error`(可空)、`sent_at`。 + +* 迁移:使用 Aerich 生成并升级(保持兼容,所有新增字段均可空或有安全默认值)。 + +## 接口设计与权限控制 + +* 交易管理新增:`POST /api/v1/transactions/send-email` + + * Body(multipart 或 JSON): + + * `email`(必填,邮箱校验) + + * `subject`(可选,默认“估值服务通知”) + + * `body`(必填,文案内容,长度与危险字符校验) + + * `file`(可选,`UploadFile`,或 `file_url` 字符串二选一) + + * 行为:使用标准库 `smtplib` + `email` 组合发送;支持 TLS/SSL;发送后落库 `EmailSendLog`;返回发送状态与日志 ID。 + + * 权限:挂载于 `transactions_router`(已带 `DependAuth`、`DependPermission`,`app/api/v1/__init__.py:52`)。 + +* 用户管理新增: + + * `GET /api/v1/user/list` 返回结构新增 `remaining_quota` 字段。 + + * `POST /api/v1/user/quota`(管理员)调整用户剩余估值次数: + + * Body:`user_id`、`target_count` 或 `delta`、`op_type`、`remark` + + * 行为:读取当前值,计算前后值,更新 `AppUser.remaining_quota`,记录 `AppUserQuotaLog`。 + + * `GET /api/v1/user/{id}/quota-logs` 返回日志列表(分页、类型筛选)。 + +* 发票抬头查看: + + * 复用现有接口 `GET /api/v1/invoice/headers?app_user_id=...`(`app/api/v1/invoice/invoice.py:118-124`)。 + +* 估值评估新增字段对外: + + * 管理端与用户端输出 Schema 同步包含 `report_url`、`certificate_url`、`credit_code_or_id`、`biz_intro`。 + +## 上传能力扩展 + +* 控制器:新增 `upload_file(file: UploadFile)` 支持 `pdf/docx/xlsx/zip` 等白名单;存储到 `app/static/files`;生成可下载链接 `settings.BASE_URL + /static/files/{name}`。 + +* 路由:`POST /api/v1/upload/file`(保留图片接口不变)。 + +* 校验: + + * MIME 白名单与大小限制;文件名清洗与去重;异常返回 4xx/5xx。 + +## Schema 与后端校验 + +* 估值 Schema(`app/schemas/valuation.py`)新增并校验: + + * `credit_code_or_id`:正则校验(统一社会信用代码/18位身份证格式二选一)。 + + * `report_url`、`certificate_url`:URL 格式校验。 + +* 用户配额:新增 `AppUserQuotaUpdateSchema` 与 `AppUserQuotaLogOutSchema`。 + +* 邮件发送:`SendEmailRequest` 支持两种附件输入;返回 `SendEmailResponse`。 + +
+ +## 发送逻辑实现要点 + +* 设置:在 `app/settings/config.py` 增加 SMTP 相关配置:`SMTP_HOST`、`SMTP_PORT`、`SMTP_USERNAME`、`SMTP_PASSWORD`、`SMTP_TLS`、`SMTP_FROM`(默认 None,走环境变量注入)。 + +* 发送器:`app/services/email_client.py`(或 `app/controllers/transactions.py` 内部封装),使用 `smtplib.SMTP_SSL`/`SMTP.starttls()`,构造 `MIMEText` 与 `MIMEBase`,附件从 `UploadFile` 或远程 `file_url` 下载后附加。 + +* 错误处理: + + * 参数校验失败返回 422;SMTP 异常记录 `EmailSendLog.error` 并返回 500;长正文截断日志摘要防止超长存储。 + +* 日志:统一 `loguru` 记录关键事件(如 `transactions.email_send_start/ok/fail`)。 + +## 权限控制与 API 权限表 + +* 新增接口在 `Api` 表登记(路径+方法+标签),使用现有刷新接口 `POST /api/v1/api/refresh` 扫描路由自动入库。 + +* 路由均落于已挂载依赖的 admin 模块,App 端路由继续独立(`app/api/v1/__init__.py:28-33`)。 + +## API 文档与变更记录 + +* 为所有新增接口与控制器方法补充函数级注释(功能、参数、返回值),满足用户规范。 + +* 通过 FastAPI 自动生成的 OpenAPI 展示;补充接口示例与错误码说明(Docstring)。 + +* 数据库变更记录:在迁移文件中含新增字段/表说明;在说明文档中列出字段语义与默认值(本次提交提供变更概要)。 + +## 单元测试计划 + +* 邮件发送: + + * 伪造 `FakeEmailClient`,覆盖成功/失败/附件两种输入;比照 `tests/api/v1/test_sms.py` 的 monkeypatch 风格。 + +* 用户配额: + + * 调额接口:前后值正确、日志记录正确、权限检查(需登录管理员)。 + +* 估值字段: + + * 创建/更新时包含新字段;URL 与正则校验失败用例;上传文件生成链接断言。 + +* 上传: + + * 非法 MIME 与超限大小拒绝;合法文件成功返回 URL。 + +## 兼容性与回滚策略 + +* 所有新增字段均为可空或安全默认,旧数据不受影响。 + +* 新增接口均为新增路由,不改动原有行为;前端逐步切换数据源,保留 Mock 作为回退。 + +* 迁移脚本按标准生成;如需回滚 aerich 支持 `downgrade`。 + +## 实施步骤 + +1. 模型与 Schema 更新;生成 Aerich 迁移;本地升级并验证。 +2. 上传控制器扩展与新路由;估值控制器/输出字段同步。 +3. 交易管理发送接口(含 SMTP 封装、日志落库、异常处理)。 +4. 用户配额接口与日志模型/路由;admin 列表与详情改造。 +5. 权限入库与刷新;为接口添加函数级注释。 +6. 单元测试编写与通过;OpenAPI 检视;交付 API/DB 变更说明。 + +## 关键代码定位参考 + +* 路由注册:`app/api/v1/__init__.py:28-52` + +* 用户端估值入口:`app/api/v1/app_valuations/app_valuations.py:233-318` + +* 估值控制器:`app/controllers/valuation.py:73-100`、`app/controllers/user_valuation.py:21-42` + +* 上传控制器:`app/controllers/upload.py:12-52` + +* 发票抬头接口:`app/api/v1/invoice/invoice.py:118-144` + +* 管理端用户列表(前端):`web/src/views/user-management/user-list/index.vue:69-166` + +* Mock 数据与 API:`web/src/api/index.js:279-352, 433-479` + +——请确认方案后,我将按上述步骤开始落地实现、编写迁移与测试。 diff --git a/.trae/documents/估值二期 API 设计方案.md b/.trae/documents/估值二期 API 设计方案.md new file mode 100644 index 0000000..3b5a913 --- /dev/null +++ b/.trae/documents/估值二期 API 设计方案.md @@ -0,0 +1,71 @@ +## 目标与范围 +- 针对“估值二期”需求(用户端、管理端)设计完整 API,去除 Webhook 回调。 +- 对齐现有约定:认证 `POST /api/v1/base/access_token`(app/api/v1/base/base.py:19-38)、`token` 请求头(web/src/utils/http/interceptors.js:11-14);响应 `Success/SuccessExtra/Fail`(app/schemas/base.py),成功码 `code===200`(web/src/utils/http/interceptors.js:23-33);估值域已有 `/api/v1/valuations`(app/api/v1/valuations/valuations.py:21-191)。 + +## 核心流程 +- 用户端:登录→评估提交→个人中心(汇款凭证、抬头选择、类型选择、发票列表/详情)→估值记录(下载证书/报告、分享、历史结果、剩余次数)。 +- 管理端:交易管理(查看/核验/邮件/开票/状态)→用户管理(信息/操作/修改/审核/投诉/短信文案/证书与报告)→审核列表(上传证书/下载/重传)。 + +## 实体与关系 +- AppUser ⇄ Valuation(1..n)、Invoice(1..n)、InvoiceHeader(n) +- Valuation ⇄ ValuationCalculationStep(1..n) ⇄ Certificate/Report +- Invoice ⇄ InvoiceHeader/PaymentReceipt/Transaction +- Complaint、SMSMessage 与用户/估值/发票按需关联 + +## 端点设计(与前端映射保持一致) +- 认证与用户 + - POST `/api/v1/base/access_token` 登录(app/api/v1/base/base.py:19-38) + - GET `/api/v1/base/userinfo` 用户信息(app/api/v1/base/base.py:40-46) + - GET `/api/v1/app-user/profile` 当前用户画像与剩余估值次数 + - GET `/api/v1/app-user/list`、GET `/api/v1/app-user/get`、POST `/api/v1/app-user/register|update`、DELETE `/api/v1/app-user/delete`(对齐 web/src/api/index.js:433-503) +- 估值评估(沿用并扩展) + - POST `/api/v1/valuations/`、GET `/api/v1/valuations/`、GET `/api/v1/valuations/{id}`(已存在) + - GET `/api/v1/valuations/{id}/steps`(已存在,用于过程展示) + - GET `/api/v1/valuations/{id}/certificate`(新增:证书下载) + - GET `/api/v1/valuations/{id}/report`(新增:报告下载) + - POST `/api/v1/valuations/{id}/share`(新增:生成分享链接/小程序码,异步) + - POST `/api/v1/valuations/batch/delete`(已存在) +- 发票与交易(保留现有路径) + - GET `/api/v1/invoice/list`、GET `/api/v1/invoice/detail`、POST `/api/v1/invoice/create|update|send|remind|refund`、DELETE `/api/v1/invoice/delete`、POST `/api/v1/invoice/update-status`(对齐 web/src/api/index.js:504-725) + - POST `/api/v1/invoice/{id}/receipt`(新增:上传付款凭证) + - GET `/api/v1/invoice/headers`、GET `/api/v1/invoice/headers/{id}`、POST `/api/v1/invoice/headers`(新增:抬头管理) + - POST `/api/v1/invoice/{id}/issue`(新增:开票,异步 Job) +- 审核与证书 + - GET `/api/v1/review/valuations` 审核列表(新增) + - POST `/api/v1/review/valuations/{id}/approve|reject`(复用估值审核,app/api/v1/valuations/valuations.py:167-183) + - POST `/api/v1/review/valuations/{id}/certificate` 上传证书(新增) + - PUT `/api/v1/review/valuations/{id}/report` 重传报告(新增) +- 投诉与短信 + - GET `/api/v1/complaints`、GET `/api/v1/complaints/{id}`、PUT `/api/v1/complaints/{id}`(新增) + - GET `/api/v1/sms/templates`、POST `/api/v1/sms/templates`、POST `/api/v1/sms/send`(新增) + +## 请求/响应格式与认证 +- 统一 JSON;请求头 `token` 必填(除登录与公共资源)。 +- 成功:`{code:200,data:...,msg:"success"}`;失败:`{code:4xx/5xx,msg:"错误"}`。 + +## 字段与校验(示例) +- ValuationCreate:`asset_name(1-64)`, `institution(1-128)`, `industry(1-64)`, `heritage_level?`, `inputs(object)`, `attachments?[url[]]` +- InvoiceCreate:`ticket_type(electronic|paper)`, `invoice_type(special|normal)`, `phone`, `email`, `company_name`, `tax_number`, `register_address`, `register_phone`, `bank_name`, `bank_account` +- PaymentReceipt:`url`, `uploaded_at`, `verified` +- ShareRequest:`channel(miniprogram|link)`, `expire(<=604800)` +- 规则:邮箱/手机号/税号格式;枚举校验;附件数量与大小限制。 + +## 错误码 +- 200 成功;400 参数错误;401 未认证(前端自动登出,web/src/utils/http/interceptors.js:45-53);403 无权限;404 不存在;409 冲突;422 校验失败;429 频率限制;500 内部错误。 + +## 批量与异步(无 Webhook) +- 批量:发票批量开具、邮件批量发送(`POST /api/v1/invoice/batch/issue|send`)。 +- 异步 Job:开票/报告/分享生成返回 `job_id`;查询 `GET /api/v1/jobs/{id}`(`status: pending|running|success|failed`)。客户端采用轮询或前端提示重试。 + +## 性能目标 +- 登录/用户信息 P95 ≤ 100ms;列表分页 P95 ≤ 200ms(单页 ≤ 100 条)。 +- 异步任务完成 ≤ 5s;QPS(单实例):读 200+/s、写 50+/s。 + +## 实施建议 +1. 在 `app/api/v1/` 新增 invoices/reviews/complaints/sms 路由文件。 +2. 在 `controllers/` 实现控制器,复用 `ValuationController` 的计算步骤记录(app/controllers/valuation.py:24-53)。 +3. 在 `schemas/` 新增/扩展 Pydantic 模型,严格校验。 +4. 增加 Job 状态查询端点,统一返回结构;无 Webhook 的情况下采用客户端轮询。 +5. 前端按 `web/src/api/index.js` 对齐接入,复用错误处理与 401 登出。 + +——请确认该无 Webhook 版本的 API 方案,确认后我将开始后端路由/控制器/模型实现并提供前端对接示例。 \ No newline at end of file diff --git a/.trae/documents/估值计算步骤 API 设计与落实方案.md b/.trae/documents/估值计算步骤 API 设计与落实方案.md new file mode 100644 index 0000000..47cceef --- /dev/null +++ b/.trae/documents/估值计算步骤 API 设计与落实方案.md @@ -0,0 +1,184 @@ +## 目标 + +* 完整设计并落实“估值计算步骤”API与落库机制,保证: + + 1. 用户提交估值后,所有中间计算步骤按规范写入数据库; + 2. 管理端在详情中查看完整步骤链条与中间结果; + 3. 统一数学公式、变量来源、步骤编号与展示结构。 + +## 现有能力与锚点 + +* 步骤模型:`ValuationCalculationStep`(app/models/valuation.py:88-107) + +* 步骤写入:控制器提供创建/查询(app/controllers/valuation.py:24-53, 37-53) + +* 管理端步骤查询:`GET /api/v1/valuations/{id}/steps`(app/api/v1/valuations/valuations.py:50-56) + +* 已有示例记录:风险调整B3模块内已演示步骤写入(app/utils/calculation\_engine/risk\_adjustment\_b3/sub\_formulas/risk\_adjustment\_b3.py:195-237) + +* 用户端计算入口:后台任务执行统一计算(app/api/v1/app\_valuations/app\_valuations.py:210-299) + +## 公式总览与数学表达 + +1. 经济价值 B1(economic\_value\_b1) + +* 基础价值 B11:依据财务与法律/创新、普及度 + + * 示例表达:`B11 = w_f * f(three_year_income) + w_i * innovation_ratio + w_p * popularity_score + w_l * infringement_factor + w_pat * patent_score` + +* 流量因子 B12:`S = α * S1 + β * S2 + γ * S3`;其中 S1 搜索指数(百度/微信/微博),S2 行业均值,S3 社交传播(点赞/评论/分享) + +* 政策乘数 B13:`P = p_impl * implementation_stage_score + p_fund * funding_support_score` + +* 汇总:`B1 = B11 * (1 + θ * S) * (1 + λ * P)` + +1. 文化价值 B2(cultural\_value\_b2) + +* 活态传承 B21:`B21 = κ1 * inheritor_level_coefficient + κ2 * offline_sessions + κ3 * social_views` + +* 纹样基因 B22:`B22 = μ1 * historical_inheritance + μ2 * structure_complexity + μ3 * normalized_entropy` + +* 汇总:`B2 = B21 + B22` + +1. 风险调整 B3(risk\_adjustment\_b3) + +* 风险评分总和:`R = 0.3 * market_risk + 0.4 * legal_risk + 0.3 * inheritance_risk` + +* 风险调整系数:`B3 = 0.8 + 0.4 * R`(app/utils/.../risk\_adjustment\_b3.py:33-45, 47-66) + +1. 市场价值 C(market\_value\_c) + +* 竞价 C1:`C1 = weighted_average_price(transaction_data, manual_bids, expert_valuations)` + +* 热度系数 C2:`C2 = ψ1 * daily_browse_volume + ψ2 * collection_count` + +* 稀缺性乘数 C3:`C3 = φ(circulation)`(限量>稀缺性高) + +* 时效性衰减 C4:`C4 = decay(recent_market_activity)` + +* 汇总:`C = C1 * (1 + C2) * C3 * C4` + +1. 最终估值 AB(final\_value\_ab) + +* 模型估值 B:`B = B1 + B2`;再叠加风险调整:`B_adj = B * B3` + +* 市场估值:`C` + +* 最终:`Final = f(B_adj, C)`(例如加权平均或规则合成) + +## 变量定义与来源映射 + +* 用户输入(UserValuationCreate,app/schemas/valuation.py:144-147): + + * `three_year_income`、`annual_revenue`、`rd_investment`、`application_coverage`、`offline_activities`、`platform_accounts`、`sales_volume`、`link_views`、`circulation`、`last_market_activity`、`price_fluctuation`、`funding_status`、`implementation_stage`、`patent_application_no`、`historical_evidence`、`pattern_images`、`inheritor_level`、`inheritor_age_count` + +* 系统/API来源: + + * 搜索指数S1、行业均值S2、社交传播S3(app/api/v1/app\_valuations/app\_valuations.py:328-347, 333-343) + + * ESG分、行业系数、政策匹配度(app/api/v1/app\_valuations/app\_valuations.py:47-80) + + * 侵权/专利校验(app/api/v1/app\_valuations/app\_valuations.py:81-118) + +## 计算步骤落库设计 + +* 统一步骤结构(app/schemas/valuation.py:239-259): + + * `step_order`:序号(含小数层级,如 1.11, 2.31) + + * `step_name`:中文名称(如“基础价值B11计算”) + + * `step_description`:公式与解释 + + * `input_params`:输入参数 JSON(含变量与其来源) + + * `output_result`:中间结果(如每项得分,最终值) + + * `status`:`in_progress|completed|failed` + + * `error_message`:失败描述 + +* 步骤编号建议: + + * 经济价值 B1:2.1x(B11=2.11,B12=2.12,B13=2.13,汇总B1=2.19) + + * 文化价值 B2:2.2x(B21=2.21,B22=2.22,汇总B2=2.29) + + * 风险调整 B3:2.3x(总评R=2.30,B3=2.31) + + * 市场价值 C:3.1x(C1=3.11,C2=3.12,C3=3.13,C4=3.14,汇总C=3.19) + + * 最终估值 AB:4.1x(B组合=4.11,B×B3=4.12,Final=4.19) + +* 落库时机:统一在后台任务中分模块记录(app/api/v1/app\_valuations/app\_valuations.py:38-41, 142-171) + +* 写入方式:通过控制器 `create_calculation_step`(app/controllers/valuation.py:24-36) + +* 已有范例:风险调整B3模块先 `in_progress` 再 `completed`(app/utils/.../risk\_adjustment\_b3.py:195-237) + +## 完整流程说明 + +1. 原始数据输入:`POST /api/v1/app-valuations/`(app/api/v1/app\_valuations/app\_valuations.py:210-299) +2. 后台任务提取参数:B1/B2/B3/C(app/api/v1/app\_valuations/app\_valuations.py:302-567) +3. 模块计算与步骤记录:按编号分别执行,逐步写入 `ValuationCalculationStep` +4. 汇总合成:计算 `model_value_b`、`market_value_c`、`final_value_ab` 与 `dynamic_pledge_rate` 并存入 `ValuationAssessment` +5. 管理端查看: + +* 详情:`GET /api/v1/valuations/{id}`(返回序列化后的详情) + +* 步骤:`GET /api/v1/valuations/{id}/steps`(返回序列化后的步骤数组) + +## 示例计算过程(模拟数据) + +* 输入(节选): + + * `three_year_income=[400,450,500]`,`annual_revenue=500`,`rd_investment=50`(创新投入比=10%) + + * `application_coverage=全国覆盖`(popularity\_score→由B11计算器给分)、`offline_activities=12` + + * `platform_accounts.douyin.likes=1200`(S3参数),`price_fluctuation=[95,105]`(波动率) + + * `inheritor_level=市级传承人`(转换为系数)、`inheritor_age_count=[45,60,75]` + + * `historical_evidence={历史文献:3, 考古发现:2, 传承谱系:5}` + +* 步骤样例: + + * 2.11 基础价值B11:`input_params={three_year_income, innovation_ratio, popularity_score, infringement_score, patent_score}` → `output_result={B11: 123.45}` + + * 2.12 流量因子B12:`input_params={S1,S2,S3}` → `output_result={S: 0.32}` + + * 2.13 政策乘数B13:`input_params={implementation_stage,funding_support}` → `output_result={P: 0.15}` + + * 2.19 B1汇总:`output_result={B1: 156.78}` + + * 2.21 活态传承B21:`input_params={inheritor_level_coefficient,offline_sessions, social_views}` → `output_result={B21: 10.2}` + + * 2.22 纹样基因B22:`input_params={historical_inheritance,structure_complexity,normalized_entropy}` → `output_result={B22: 8.9}` + + * 2.30 风险总评R:`input_params={market_risk,legal_risk,inheritance_risk}` → `output_result={R: 0.42}` + + * 2.31 风险调整B3:`output_result={B3: 0.97}` + + * 3.11~~3.14 市场价值子项:分别写入 C1~~C4 + + * 3.19 市场价值C:`output_result={C: 118.0}` + + * 4.11/4.12/4.19 最终汇总:`output_result={B: 175.88, B_adj: 170.6, Final: 122.0}` + +## 后台展示规范 + +* 列表返回序列化后的 Pydantic 对象,避免 JSONResponse 序列化错误(已在管理端端点处理) + +* 步骤展示:按照 `step_order` 升序,逐条显示 `step_name`、`step_description`、`input_params`、`output_result`、`status`;失败步骤显示 `error_message` + +## 实施项 + +1. 将 B1、B2、C 模块对齐 B3 的“步骤写入”模式:每个子公式在计算前记录 `in_progress`,完成后记录 `completed` 并带结果;异常时标记 `failed`。 +2. 在 `FinalValueACalculator` 合成阶段补充步骤记录(B组合、B×B3、Final)。 +3. 确保管理端详情与步骤返回统一进行 JSON 序列化(管理端端点已按 `model_dump_json()` 修复)。 + +## 交付 + +* 我将按上述规范逐步在计算引擎各子模块与统一计算入口中补充“步骤写入”,并确保管理端端点返回可序列化的数据结构;完成后会提供一份面向管理员的“估值步骤查看”前后端对接说明(端点与字段)。 + diff --git a/.trae/documents/修复计算任务的NameError并确保步骤入库与日志可见.md b/.trae/documents/修复计算任务的NameError并确保步骤入库与日志可见.md new file mode 100644 index 0000000..82a29ad --- /dev/null +++ b/.trae/documents/修复计算任务的NameError并确保步骤入库与日志可见.md @@ -0,0 +1,23 @@ +## 问题 +- 后台任务在提取B1参数时引用未定义函数(calculate_popularity_score、calculate_patent_score),导致计算中止,步骤未入库。 + +## 修复方案 +1) 移除未定义函数引用,在 `_extract_calculation_params_b1` 内实现本地计算: +- 普及地域分:mapping {全球覆盖:10,全国覆盖:7,区域覆盖:4},默认7 +- 专利分:按剩余年限近似 {>10年:10,5-10年:7,<5年:3};用已有 `calculate_total_years(data_list)` 近似转换 +- 保留创新投入比与搜索指数、行业均值等现有逻辑 +- 增加logger输出:popularity_score、innovation_ratio、patent_score + +2) 确保步骤写入链路: +- 计算入口:先创建估值记录取 `valuation_id`;将 `valuation_id` 传入统一计算器;计算后用 `ValuationAssessmentUpdate` 更新记录 +- 模型B汇总:对经济B1、文化B2、风险B3的计算改为 `await` 并传 `valuation_id` +- 市场C与风险B3保持原有按 `valuation_id` 写入 + +3) 日志增强: +- 在步骤创建与更新时写入日志(calcstep.create/update/list) +- 在估值初始化与更新时写入日志(valuation.init_created/updated) + +## 验证 +- 重启服务、运行脚本;查看服务日志:应出现calcstep.create/update/list与valuation.updated;Admin脚本打印“后台估值计算步骤”。 + +——确认后我将按以上方案进行代码调整并回填日志。 \ No newline at end of file diff --git a/.trae/documents/合并输出:估值二期前后端 API 需求总览文档.md b/.trae/documents/合并输出:估值二期前后端 API 需求总览文档.md new file mode 100644 index 0000000..161cc9d --- /dev/null +++ b/.trae/documents/合并输出:估值二期前后端 API 需求总览文档.md @@ -0,0 +1,29 @@ +## 目标 +- 汇总所有前后端需求与 API 到一个文档,其他分散文档删除,仅保留总览,便于统一查看与维护。 + +## 操作与产物 +- 新增单一文档:`docs/估值二期-需求与API总览.md` +- 内容包含: + 1) 概述与约定(认证、响应包装、错误码、前缀) + 2) 数据实体(InvoiceHeader、PaymentReceipt、TransactionRecord、Valuation、AppUser、Complaint、SMSMessage)与字段定义 + 3) 用户端流程与 API(登录/首页摘要、评估提交/校验/状态、估值记录下载与分享、个人中心-对公转账、发票抬头/类型、发票列表与详情、剩余次数、投诉与短信、批量与异步Job) + 4) 管理端流程与 API(交易管理记录:列表/详情/状态/邮件/开票/批量;审核列表;用户管理) + 5) 前端对接映射(`web/src/api/index.js` 现有与新增占位) + 6) 请求/响应示例与字段校验(重点:抬头管理、对公转账、交易管理记录) + 7) 性能与限制(分页、异步、频率) +- 删除分散文档: + - `web/docs/项目结构.md`、`web/docs/技术架构.md`、`web/docs/关键模块说明.md`、`web/docs/接口约定与预留.md` + - `app/docs/项目结构.md`、`app/docs/技术架构.md`、`app/docs/关键模块说明.md`、`app/docs/接口约定与预留.md` + +## 你提出的新增明确项(将详列在文档中) +- 抬头管理字段(必填项标注):公司名称、公司税号、注册地址、注册电话、开户银行、银行账号、邮箱 +- 对公转账(用户端):上传图片(付款凭证)+ 选择发票抬头 + 开票类型(电子/纸质、专票/普票) +- 交易管理记录(管理端):提交时间、付款凭证、手机号、微信号、公司名称、公司税号、注册地址、注册电话、开户银行、银行账号、接收邮箱、开票类型、状态 +- 状态枚举:pending/verified/issued/rejected 等 + +## 执行步骤 +1. 创建总览文档并写入上述结构内容与端点清单 +2. 迁移现有 app/web 文档中的有效信息到总览文档 +3. 删除分散文档,仅保留总览 + +——请确认上述方案,确认后我将生成唯一的总览文档并删除分散文档。 \ No newline at end of file diff --git a/.trae/documents/在不改计算逻辑前提下修复步骤入库(step_order类型调整).md b/.trae/documents/在不改计算逻辑前提下修复步骤入库(step_order类型调整).md new file mode 100644 index 0000000..c936b0c --- /dev/null +++ b/.trae/documents/在不改计算逻辑前提下修复步骤入库(step_order类型调整).md @@ -0,0 +1,26 @@ +## 问题 +- 计算步骤创建时报 Pydantic 校验错误:`step_order` 期望整型,但代码使用层级小数(如 2.1、2.11)。这不是计算公式问题,而是“类型不匹配”导致步骤未入库。 + +## 修复原则 +- 不改变任何计算公式或数值流程,仅调整“步骤顺序”的存储与校验类型,使其能接受层级小数。 + +## 具体改动 +1) 模型字段修改(不涉公式): +- `app/models/valuation.py` 中 `ValuationCalculationStep.step_order: IntField → DecimalField(max_digits=8, decimal_places=3)`;保留 `ordering=["step_order"]`,确保排序正确。 + +2) Schema 修改(不涉公式): +- `ValuationCalculationStepCreate.step_order: int → Decimal`,添加前置校验,支持 int/float/str 自动转换为 Decimal;`ValuationCalculationStepOut` 同步为 Decimal。 +- 列表与详情端点已使用 `model_dump_json()` 再 `json.loads()`,Decimal 会被正确序列化为 JSON 数字,无需改动。 + +3) 代码调用无需改(不涉公式): +- 由于 Schema 接受 float 并转换为 Decimal,现有调用处传入 `2.1/2.11/...` 不需改。 + +4) 迁移与验证 +- 启动时执行 Aerich 迁移更新列类型(项目已有初始化流程)。 +- 跑脚本观测:`calcstep.create` 不再报错;`calcstep.list` 数量 > 0;后台“估值计算步骤”返回完整数组。 + +## 影响范围与安全性 +- 仅变更“步骤顺序”的字段类型与 Schema 校验,不触及任何计算逻辑或公式。 +- 排序按照 Decimal 正常工作,层级表达(2.11 < 2.2)保留。 + +——确认后,我将按以上方案修改模型与 Schema,并执行验证,保证不改变计算逻辑,仅解决类型不匹配问题。 \ No newline at end of file diff --git a/.trae/documents/完善计算步骤落库与测试输出详细化.md b/.trae/documents/完善计算步骤落库与测试输出详细化.md new file mode 100644 index 0000000..218f224 --- /dev/null +++ b/.trae/documents/完善计算步骤落库与测试输出详细化.md @@ -0,0 +1,17 @@ +## 目标 +- 让每次用户估值的所有中间步骤写入 `valuation_calculation_steps` 并可关联该估值ID +- 测试脚本打印详细步骤链,包括 step_order、step_name、step_description、input_params、output_result、status + +## 代码改动 +1) 计算入口 `_perform_valuation_calculation`: +- 先创建估值记录以拿到 `valuation_id` +- 传 `valuation_id` 给 `FinalValueACalculator.calculate_complete_final_value_a` +- 计算完成后用 `ValuationAssessmentUpdate` 将结果更新到该记录 + +2) 测试脚本: +- 在 AdminClient 增加 `valuation_steps(id)` 方法 +- 打印步骤数组,包含名称、描述、输入与输出 + +## 验证 +- 运行 `python run.py` +- 运行脚本并查看详细步骤输出 diff --git a/.trae/documents/接入阿里云短信并提供两个接口.md b/.trae/documents/接入阿里云短信并提供两个接口.md new file mode 100644 index 0000000..8ba772d --- /dev/null +++ b/.trae/documents/接入阿里云短信并提供两个接口.md @@ -0,0 +1,99 @@ +## 目标与范围 +- 接入阿里云短信服务,封装发送客户端 +- 提供两类发送接口:验证码通知、报告生成通知,供 App 调用 +- 支持模板动态调用与验证码变量 `${code}` 的正确替换 +- 记录发送日志并融入现有审计体系 +- 实现同一手机号每分钟不超过 1 条的频率限制 +- 安全存储 AccessKey 等敏感信息(环境变量/配置) + +## 技术选型 +- 后端框架:FastAPI(现有工程) +- 短信 SDK:Alibaba Cloud SMS Python SDK(Tea/OpenAPI V2,`alibabacloud_dysmsapi20170525`) + - 端点(中国站):`dysmsapi.aliyuncs.com` + - 关键请求字段:`PhoneNumbers`、`SignName`、`TemplateCode`、`TemplateParam` +- 日志:沿用 `app/log` 的 Loguru 与审计中间件 +- 频率限制:服务内共享的内存限流(后续可升级为 Redis) +- 安全:通过环境变量注入凭证,Pydantic Settings 读取 +- 参考文档: + - Alibaba Cloud SDK V2(Python)示例(SendSms):https://www.alibabacloud.com/help/en/sdk/developer-reference/v2-python-integrated-sdk + - 短信服务 SendSms 接口(2017-05-25):https://help.aliyun.com/zh/sms/developer-reference/api-dysmsapi-2017-05-25-sendsms + +## 代码改动 +- 新增:`app/services/sms_client.py` + - 初始化 Dysms 客户端(读取 `ALIBABA_CLOUD_ACCESS_KEY_ID`、`ALIBABA_CLOUD_ACCESS_KEY_SECRET`、`ALIYUN_SMS_SIGN_NAME`、`ALIYUN_SMS_ENDPOINT`) + - 方法:`send_by_template(phone, template_code, template_param_json)` + - 方法:`send_code(phone, code)`(模板:`SMS_498190229`) + - 方法:`send_report(phone)`(模板:`SMS_498140213`) +- 新增:`app/services/rate_limiter.py` + - 类:`PhoneRateLimiter`,键为手机号,值为最近一次发送时间戳;判定 60s 内拒绝 +- 新增路由:`app/api/v1/sms/sms.py` + - `POST /api/v1/sms/send-code`(无鉴权,用于登录场景) + - `POST /api/v1/sms/send-report`(需要鉴权,防滥用) + - 统一返回结构:`{status, message, request_id}` +- 路由聚合:在 `app/api/v1/__init__.py` 注册 `sms_router(prefix="/sms", tags=["短信服务"])` +- 配置:扩展 `app/settings/config.py`(Pydantic Settings)增加短信相关字段并从环境读入 + +## 接口设计 +- `POST /api/v1/sms/send-code` + - 请求体:`{ "phone": "1390000****", "code": "123456" }` + - 处理:限流校验 → 构造 `TemplateParam` 为 `{"code": "123456"}` → 调用 `SMS_498190229` + - 成功:`{ "status": "OK", "message": "sent", "request_id": "..." }` + - 失败:`{ "status": "ERROR", "message": "..." }` +- `POST /api/v1/sms/send-report` + - 请求体:`{ "phone": "1390000****" }` + - 处理:鉴权(`DependAuth`)→ 限流校验 → 调用 `SMS_498140213` + - 返回同上 +- 校验:手机号格式(支持无前缀或 `+86`),`code` 为 4–8 位数字(可按需约束) + +## 模板与变量替换 +- 验证码模板:`SMS_498190229` + - `TemplateParam`:`{"code": "<动态验证码>"}` 与 `${code}` 正确对应 +- 报告通知模板:`SMS_498140213` + - 不含变量,可传空对象 `{}` 或不传 `TemplateParam` +- 签名:`ALIYUN_SMS_SIGN_NAME` 读取为“成都文化产权交易所”且不在代码中硬编码 + +## 日志与审计 +- 路由层:审计中间件自动记录请求/响应(`module=短信服务`,`summary=验证码发送/报告通知发送`) +- 服务层:`from app.log import logger` + - 发送开始、Provider 请求入参(不含敏感信息)、返回码、`RequestId`、耗时、失败异常 +- 敏感信息不入日志:AccessKey、完整模板内容不打印 + +## 频率限制 +- 策略:同一手机号在 60 秒内全模板合并限 1 次(共享窗口) +- 实现:进程内 `dict[phone]=last_ts`;进入路由先校验再发送;返回 429(或业务错误码) +- 进阶:如需多实例一致性,后续接入 Redis,键:`sms:limit:{phone}` TTL=60s + +## 安全与配置 +- 环境变量: + - `ALIBABA_CLOUD_ACCESS_KEY_ID` + - `ALIBABA_CLOUD_ACCESS_KEY_SECRET` + - `ALIYUN_SMS_SIGN_NAME` + - `ALIYUN_SMS_ENDPOINT`(默认 `dysmsapi.aliyuncs.com`) +- Pydantic Settings 统一读取,避免硬编码,并在 `/docs` 与审计中隐藏敏感字段 + +## 依赖与安装 +- 在 `pyproject.toml` 添加: + - `alibabacloud_dysmsapi20170525` + - `alibabacloud_tea_openapi` + - `alibabacloud_tea_util` +- 与 `requirements.txt` 保持一致版本钉死策略;Python 3.11 兼容性验证 + +## 测试与验证 +- 单元测试: + - Mock SDK 客户端,校验 `TemplateCode` 与 `TemplateParam` 的正确构造 + - 限流:同号 60s 内第二次返回限制错误 +- 集成测试: + - 使用 `httpx.AsyncClient` 调用两个接口并断言响应结构 + - 在预设测试手机号上进行真实发送,观察到达与模板内容正确 +- 观测: + - 查看应用日志与审计表(`AuditLog`)记录 + +## 风险与回滚 +- 进程内限流仅在单实例有效,多实例需 Redis(后续迭代) +- SDK 版本冲突,采用独立最小版本并逐项验证;必要时锁版本 +- 若出现发送失败,保留错误码与 `RequestId`,按官方错误码表排查(见 SendSms 文档) + +## 交付物 +- 新增短信客户端与路由模块 +- 两个可调用接口(验证码发送、报告通知发送) +- 限流与日志落地,配置基于环境变量 \ No newline at end of file diff --git a/.trae/documents/接口测试脚本(用户端 + 后台)实施计划.md b/.trae/documents/接口测试脚本(用户端 + 后台)实施计划.md new file mode 100644 index 0000000..e484ed0 --- /dev/null +++ b/.trae/documents/接口测试脚本(用户端 + 后台)实施计划.md @@ -0,0 +1,41 @@ +## 目标 +- 编写一个一次性可运行的接口测试脚本,按照总览文档顺序执行: + 1) App 用户注册 → 登录 → 用户相关接口 + 2) 提交估值(用户端)并轮询结果(列表/详情) + 3) 管理端登录(admin)→ 查看估值数据(列表/详情) +- 输出结构化结果与关键字段校验,便于快速人工检查。 + +## 脚本位置与运行 +- 路径:`scripts/api_smoke_test.py` +- 运行:`python scripts/api_smoke_test.py --base http://127.0.0.1:9991/api/v1` +- 基础:默认使用 `http://127.0.0.1:9991/api/v1`(根据 `run.py`),可通过参数覆盖。 + +## 步骤与端点 +1. App 用户注册与登录 +- POST `/app-user/register`(若手机号已存在则跳过) +- POST `/app-user/login` → 获取 `access_token` +- GET `/app-user/profile`、GET `/app-user/dashboard`、GET `/app-user/quota` + +2. 用户端估值 +- POST `/app-valuations/`(使用总览文档示例数据,最小必要字段) +- 轮询 GET `/app-valuations/`(分页)查看是否新增记录,捕获 `id` +- GET `/app-valuations/{id}` 详情 + +3. 管理端数据查看 +- POST `/base/access_token`(admin/123456)获取后端 `token` +- GET `/valuations` 列表(后台视角) +- GET `/valuations/{id}` 详情(与用户端一致性对比) + +## 输出与校验 +- 每步打印:请求路径、状态码、关键字段(如 `access_token`、`user_id`、`latest_valuation`、`final_value_ab`) +- 断言:登录成功、列表包含新记录、详情字段存在。 +- 错误处理:捕获非 200 情况并打印 `code/msg`。 + +## 技术细节 +- 使用 `requests` 与 `Session` 维护 `token`;用户端与后台端各独立 `Session`。 +- 函数化:为每个步骤提供函数与函数级注释(描述、参数、返回值)。 +- 兼容:对注册接口“手机号已存在”返回情况做兼容(脚本继续执行)。 + +## 交付 +- 创建 `scripts/api_smoke_test.py` 并填充完整逻辑;默认数据内置,必要处留参数。 +- 如你需要,我可在脚本创建后直接运行,输出结果供你检查。 \ No newline at end of file diff --git a/.trae/documents/更新测试脚本以使用你提供的参数.md b/.trae/documents/更新测试脚本以使用你提供的参数.md new file mode 100644 index 0000000..5b4511d --- /dev/null +++ b/.trae/documents/更新测试脚本以使用你提供的参数.md @@ -0,0 +1,20 @@ +## 目标 +- 将 `scripts/api_smoke_test.py` 的示例负载替换为你提供的完整参数,保持后端现有计算逻辑不变。 + +## 具体改动 +- 替换 `build_sample_payload()` 返回值为你提供的 JSON;字段逐项对齐: + - `asset_name`、`institution`、`industry`、`annual_revenue`、`rd_investment`、`three_year_income`、`funding_status`、`sales_volume`、`link_views`、`circulation`、`last_market_activity`、`monthly_transaction`、`price_fluctuation`、`application_maturity`、`application_coverage`、`cooperation_depth`、`offline_activities`、`inheritor_level`、`inheritor_age_count`、`inheritor_certificates`、`heritage_level`、`historical_evidence`、`patent_certificates`、`pattern_images`、`patent_application_no`、`heritage_asset_level`、`inheritor_ages`、`implementation_stage`、`coverage_area`、`collaboration_type`、`scarcity_level`、`market_activity_time`、`monthly_transaction_amount`、`platform_accounts` + - 保留 `platform_accounts`(douyin)作为后端期望的数据源;`online_accounts`(数组)不参与当前计算,保留或忽略均可;默认保留以便后续扩展。 + - `application_coverage`:后端当前使用该字段计算普及分;你的参数中同时有 `coverage_area`,将按优先 `application_coverage` 使用;若 `application_coverage` 为占位(如“0”),建议同步设置为“全球覆盖”(或我在脚本中用 `coverage_area` 回填)。 + - 其余数值以字符串提供,后端已通过 `safe_float` 做转换,无需脚本侧强制转数值。 + +## 兼容与注意 +- 不改计算逻辑;仅更新脚本负载以贴合后端字段期望。 +- 保持 `AdminClient` 输出“后台估值详情”和“后台估值计算步骤”打印,便于你核验。 + +## 验证 +- 启动后端并确保迁移已执行(`step_order` 已支持 Decimal)。 +- 运行脚本:`python scripts/api_smoke_test.py --base http://127.0.0.1:9991/api/v1 --phone 13800138001` +- 观察输出:用户侧成功提交,后台列表/详情显示完整数据,步骤列表非空。 + +——确认后我将直接更新脚本并提交。 \ No newline at end of file diff --git a/.trae/documents/更新用户端 API 到文档.md b/.trae/documents/更新用户端 API 到文档.md new file mode 100644 index 0000000..1a9a0b5 --- /dev/null +++ b/.trae/documents/更新用户端 API 到文档.md @@ -0,0 +1,48 @@ +## 目标 + +* 将“用户端”API补齐内容更新到现有文档,便于你审阅。 + +## 更新范围 + +* 修改 `app/docs/接口约定与预留.md`:新增“用户端 API”章节,覆盖登录/首页/评估/个人中心/估值记录/通知/批量与异步,与错误码和校验对齐。 + +* 修改 `web/docs/接口约定与预留.md`:新增“前端对接(用户端)”章节,列出与 `web/src/api/index.js` 的映射与新增端点占位,确保路径与请求方式一致。 + +## 文档结构变更 + +* `app/docs/接口约定与预留.md` + + * 新增: + + * “用户端 API 概览” + + * “认证与首页” + + * “评估提交与引导提示” + + * “估值记录与分享” + + * “个人中心:汇款凭证/发票抬头与类型/发票列表与详情” + + * “剩余估值次数” + +
+ +* `web/docs/接口约定与预留.md` + + * 新增: + + * “用户端对接路径”与现有 `invoice/*`、`valuation/*`、`app-user/*` 的映射 + + * 新增端点建议(如 `app-valuation/*`、`invoice/headers`、`invoice/{id}/receipt`、`jobs/{id}`)的前端占位说明 + +## 风格与格式 + +* 统一中文、RESTful端点风格,示例以JSON格式。 + +* 保持与现有文档用语一致(Success/Fail、token、code===200)。 + +## 交付 + +* 完成上述两处文档更新,不新增新文档文件;更新内容可直接在IDE中查看。 + diff --git a/.trae/documents/项目结构整理与架构文档(web目录).md b/.trae/documents/项目结构整理与架构文档(web目录).md new file mode 100644 index 0000000..f1b384b --- /dev/null +++ b/.trae/documents/项目结构整理与架构文档(web目录).md @@ -0,0 +1,103 @@ +## 工作范围与目标 +- 范围:梳理 `web` 目录(排除 `web1`),形成结构与架构产物 +- 目标: + - 生成完整项目结构文档(目录树、职责说明、关键路径) + - 制作技术架构示意图(前端分层、运行时链路、对后端的调用关系) + - 编写关键模块说明文档(路由、状态、鉴权、HTTP、页面域) + - 预留接口说明,支撑后续开发接入 + +## 技术栈识别 +- 框架:Vue 3(`web/package.json:37`),构建:Vite(`web/package.json:43`,`web/vite.config.js:1`) +- 路由:vue-router@4(`web/package.json:39`);守卫统一注册(`web/src/router/guard/index.js:5-9`) +- 状态:Pinia(`web/package.json:26`,`web/src/store/index.js:1-5`) +- UI 与样式:Naive UI(`web/package.json:25`),UnoCSS(`web/package.json:30`,`web/src/main.js:3`),全局样式(`web/src/styles/global.scss`) +- 国际化:vue-i18n(`web/package.json:38`,`web/src/main.js:22`,`web/i18n/index.js`) +- 网络:Axios(`web/package.json:20`),自建请求封装(`web/src/utils/http/index.js:4-18`)与拦截器(`web/src/utils/http/interceptors.js:23-33,35-59`) +- 运行环境:`.env.*` 配置,开发代理到后端 `127.0.0.1:9999` 的 `/api/v1`(`web/.env.development:8`,`web/build/constant.js:19-22`,`web/vite.config.js:31-35`) + +## 项目结构综述 +- 顶层关键目录: + - `build/`:Vite 定制化(定义、插件、脚本、代理)(`web/build/*`) + - `i18n/`:国际化资源与实例(`web/i18n/index.js`,`web/i18n/messages/*`) + - `settings/`:主题与全局设置(`web/settings/theme.json`,`web/settings/index.js`) + - `public/`:静态资源与加载占位(`web/public/resource/*`) + - `src/`:业务主目录(见下) +- `src/` 结构与职责: + - 入口与应用:`main.js`(应用装配,挂载插件)(`web/src/main.js:14-23`),`App.vue` + - 路由:`router/`(基本路由、动态路由、守卫、滚动)(`web/src/router/index.js:7-18,30-55`) + - 状态:`store/`(Pinia 注册与模块聚合)(`web/src/store/index.js:1-5`,`web/src/store/modules/index.js:1-4`) + - 组件:`components/`(通用、表格、查询栏、图标、页面容器) + - 视图:`views/`(系统管理、估值评估、交易开票、登录、工作台等域) + - 工具:`utils/`(鉴权、存储、HTTP、通用工具)(`web/src/utils/*`) + - 指令:`directives/`(权限等自定义指令) + - 可复用逻辑:`composables/`(如 `useCRUD`) + - 样式:`styles/`(Reset、全局样式,UnoCSS 原子类) + +## 核心模块与功能点 +- 鉴权与导航: + - 登录白名单与重定向(`web/src/router/guard/auth-guard.js:3-16`) + - 动态路由注入、用户与权限联动(`web/src/router/index.js:30-55`) +- 状态管理: + - 用户信息获取、登出流程(`web/src/store/modules/user/index.js:37-60`) + - 标签、权限、应用模块聚合(`web/src/store/modules/index.js:1-4`) +- 网络与错误处理: + - `request` 实例与 `baseURL` 环境绑定(`web/src/utils/http/index.js:17-19`) + - 成功码约定 `code === 200`、统一错误提示(`web/src/utils/http/interceptors.js:23-33`) + - 401 处理与自动登出(`web/src/utils/http/interceptors.js:45-53`) +- 业务域: + - 系统管理(用户、角色、菜单、部门、审计日志)(`web/src/views/system/*`,API 汇总:`web/src/api/index.js:393-431`) + - 客户端用户管理、开票记录、估值评估(`web/src/views/user-management/*`,`web/src/views/transaction/invoice/*`,`web/src/views/valuation/*`;对应 API:`web/src/api/index.js`) +- UI 框架与布局: + - 布局与头部/侧栏/标签页组件(`web/src/layout/*`) + - Naive UI 组件与 UnoCSS 原子化样式协同 + +## 技术架构示意图 +```mermaid +graph TD + U[用户] --> A[Vue 应用] + A --> R[Router] + A --> S[Pinia] + A --> V[视图与组件] + V --> UI[Naive UI / UnoCSS] + A --> I18N[vue-i18n] + S --> H[HTTP 封装] + R --> G[路由守卫] + H --> X[Axios 拦截器] + X --> B[(后端 API /api/v1)] + subgraph 构建与运行 + Vite[Vite Dev/Build] + end + A --> Vite +``` + +## 文档产出方案 +- 目录与位置:在 `web/docs/` 下生成 4 个文档,统一中文、层级清晰、可落地 + - `项目结构.md`:目录树 + 角色说明 + 入口与关键路径 + - `技术架构.md`:架构分层 + 运行链路 + Mermaid 图 + - `关键模块说明.md`:路由、状态、HTTP、业务域的职责与协作 + - `接口约定与预留.md`:环境、鉴权、响应约定、扩展接入指引 +- 文档格式约定: + - 标题层级:H1 总览,H2 模块,H3 文件与职责;统一术语与中文阐述 + - 代码引用统一用内联反引号与文件定位(如 `web/src/router/index.js:30-55`) + +## 接口预留说明(用于后续开发) +- 基础约定: + - `baseURL`:`VITE_BASE_API`(默认 `/api/v1`,`web/.env.development:8`) + - 认证头:`token`(由拦截器自动注入,`web/src/utils/http/interceptors.js:11-14`) + - 成功响应:`{ code: 200, data, msg }`(`web/src/utils/http/interceptors.js:23-33`) +- 接入方式:在 `web/src/api/index.js` 中以函数方式声明对应业务端点,统一走 `request` +- 错误处理:全局弹窗与 401 自动登出链路已就绪(`web/src/utils/http/interceptors.js:45-53`) + +## 执行步骤 +1. 固化目录树与职责说明,输出《项目结构.md》 +2. 绘制 Mermaid 架构图并输出《技术架构.md》 +3. 编写《关键模块说明.md》,覆盖路由、状态、HTTP、页面域 +4. 编写《接口约定与预留.md》,包含新增接口接入模板与约束 +5. 交付文档后,等待新需求文档,启动开发 + +## 输出验收与规范 +- 文档格式:统一中文,标题层级一致,引用路径与行号定位 +- 风格一致:术语与代码片段与现有实现保持一致(如 `request`、`useUserStore`) +- 可演进:接口文档预留扩展章节,支持后续模块按同规范接入 + +——请确认以上方案,确认后我将按该方案生成 4 个文档并提交供评审。 \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 06c8b6c..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,23 +0,0 @@ -# Repository Guidelines - -## Project Structure & Module Organization -FastAPI backend code lives in `app/`: routers under `app/api/v1`, orchestration in `app/controllers`, schemas/models in `app/schemas` and `app/models`, and shared helpers in `app/utils`. Config defaults stay in `app/settings/config.py`, migrations in `migrations/`, and the service boots through `run.py`. Frontend assets reside in `web/` with source code in `web/src`, static files in `web/public`, and build toggles in `web/settings`; deployment collateral sits in `deploy/`. - -## Build, Test, and Development Commands -- `make install` (uv) or `pip install -r requirements.txt` prepares backend deps; `pnpm i` handles `web/`. -- `make start` / `python run.py` launches the API against `db.sqlite3`; `cd web && pnpm dev` starts the SPA; `pnpm build` prepares production assets. -- `make check` runs Black+isort in check mode plus Ruff; `make format` applies fixes; `make lint` is Ruff-only. -- `make test` loads `.env` variables into the shell and executes `pytest -vv -s`; target files with `pytest tests/api/test_x.py -k keyword`. -- Database maintenance: `make migrate` (generate Aerich migrations), `make upgrade` (apply), `make clean-db` (reset SQLite + migrations). - -## Coding Style & Naming Conventions -Python follows Black (120 columns), isort’s Black profile, and Ruff; keep modules snake_case and Pydantic models PascalCase. Vue code respects the repo ESLint + UnoCSS presets, uses TypeScript script blocks, and keeps component directories kebab-case; run `pnpm lint` or `pnpm lint:fix` as needed. - -## Testing Guidelines -Back-end features need pytest coverage mirroring the `app` layout—e.g., `tests/api/v1/test_users.py` for router logic and async tests following the patterns in `test_dynamic_default.py`. Seed deterministic data via fixtures instead of the shared `db.sqlite3`, and document any `.env` flags a test requires. Frontend changes should gain vitest or Playwright checks under `web/tests` before UI regressions reach `main`. - -## Commit & Pull Request Guidelines -Stick to Conventional Commit prefixes already present (`feat:`, `refactor:`, `debug:`) and keep subject lines imperative with optional scopes (`feat(api):`). Each PR must summarize changes, list verification commands, reference related issues, and attach UI screenshots/GIFs when touching `web/`. Run `make check` and relevant tests locally, avoid committing `web/dist` or SQLite WAL files, and prefer small, reviewable diffs. - -## Security & Configuration Tips -Secrets belong in `.env`, which `app/settings/config.py` loads automatically; rotate `SECRET_KEY`, JWT parameters, and database credentials before deployment. Swap the Tortoise connection from SQLite to MySQL/PostgreSQL by editing the provided templates and running `make migrate && make upgrade`. Lock down CORS (`CORS_ORIGINS`) before exposing the API publicly. diff --git a/aaa.json b/aaa.json deleted file mode 100644 index 1ee17fc..0000000 --- a/aaa.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "asset_name": "资产名称", - "institution": "所属机构", - "industry": "农业", - "annual_revenue": "22", - "rd_investment": "33", - "three_year_income": [ - "11", - "22", - "33" - ], - "funding_status": "国家级资助", - "sales_volume": "22", - "link_views": "22", - "circulation": "0", - "last_market_activity": "0", - "monthly_transaction": "0", - "price_fluctuation": [ - "2", - "3" - ], - "application_maturity": "0", - "application_coverage": "0", - "cooperation_depth": "1", - "offline_activities": "3", - "online_accounts": [ - "0", - "333" - ], - "inheritor_level": "国家级传承人", - "inheritor_age_count": [ - "55", - "66", - "77" - ], - "inheritor_certificates": [ - "http://example.com/国家级非遗传承人证书.jpg" - ], - "heritage_level": "0", - "historical_evidence": { - "artifacts": "22", - "ancient_literature": "33", - "inheritor_testimony": "66" - }, - "patent_certificates": [ - "http://example.com/专利证书1.jpg", - "http://example.com/专利证书2.jpg" - ], - "pattern_images": [ - "pattern1.jpg" - ], - "patent_application_no": "22", - "heritage_asset_level": "国家级非遗", - "inheritor_ages": [ - "55", - "66", - "77" - ], - "implementation_stage": "成熟应用", - "coverage_area": "全球覆盖", - "collaboration_type": "品牌联名", - "platform_accounts": { - "bilibili": { - "followers_count": 8000, - "likes": 1000, - "comments": 500, - "shares": 500 - }, - "douyin": { - "followers_count": 8000, - "likes": 1000, - "comments": 500, - "shares": 500 - } - }, - "scarcity_level": "孤品:全球唯一,不可复制(如特定版权、唯一实物)", - "market_activity_time": "近一周", - "price_range": { - "highest": "2", - "lowest": "3" - }, - "monthly_transaction_amount": "月交易额<100万元" -} \ No newline at end of file diff --git a/app/__init__.py b/app/__init__.py index bab99b1..c28324d 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -26,11 +26,33 @@ async def lifespan(app: FastAPI): def create_app() -> FastAPI: + openapi_tags = [ + {"name": "app-用户认证与账户", "description": "用户端账户与认证相关接口(公开/需认证以端点说明为准)"}, + {"name": "app-估值评估", "description": "用户端估值评估相关接口(需用户端认证)"}, + {"name": "app-短信服务", "description": "用户端短信验证码与登录相关接口(公开)"}, + {"name": "app-上传", "description": "用户端文件上传接口(公开)"}, + {"name": "admin-基础", "description": "后台登录与个人信息接口(部分公开,其他需认证)"}, + {"name": "admin-用户管理", "description": "后台用户管理接口(需认证与权限)"}, + {"name": "admin-角色管理", "description": "后台角色管理接口(需认证与权限)"}, + {"name": "admin-菜单管理", "description": "后台菜单管理接口(需认证与权限)"}, + {"name": "admin-API权限管理", "description": "后台 API 权限管理接口(需认证与权限)"}, + {"name": "admin-部门管理", "description": "后台部门管理接口(需认证与权限)"}, + {"name": "admin-审计日志", "description": "后台审计日志查询接口(需认证与权限)"}, + {"name": "admin-估值评估", "description": "后台估值评估接口(需认证与权限)"}, + {"name": "admin-发票管理", "description": "后台发票与抬头管理接口(需认证与权限)"}, + {"name": "admin-交易管理", "description": "后台交易/对公转账记录接口(需认证与权限)"}, + {"name": "admin-内置接口", "description": "后台第三方内置接口调用(需认证与权限)"}, + {"name": "admin-行业管理", "description": "后台行业数据管理(当前公开)"}, + {"name": "admin-指数管理", "description": "后台指数数据管理(当前公开)"}, + {"name": "admin-政策管理", "description": "后台政策数据管理(当前公开)"}, + {"name": "admin-ESG管理", "description": "后台 ESG 数据管理(当前公开)"}, + ] app = FastAPI( title=settings.APP_TITLE, description=settings.APP_DESCRIPTION, version=settings.VERSION, openapi_url="/openapi.json", + openapi_tags=openapi_tags, middleware=make_middlewares(), lifespan=lifespan, redirect_slashes=False, # 禁用尾部斜杠重定向 diff --git a/app/api/v1/__init__.py b/app/api/v1/__init__.py index 212431d..f6f2711 100644 --- a/app/api/v1/__init__.py +++ b/app/api/v1/__init__.py @@ -5,6 +5,7 @@ from app.utils.app_user_jwt import get_current_app_user from .apis import apis_router from .app_users import app_users_router +from .app_users.admin_manage import admin_app_users_router from .app_valuations import app_valuations_router from .auditlog import auditlog_router from .base import base_router @@ -19,28 +20,36 @@ from .third_party_api import third_party_api_router from .upload import router as upload_router from .users import users_router from .valuations import router as valuations_router +from .invoice.invoice import invoice_router +from .transactions.transactions import transactions_router +from .sms.sms import router as sms_router v1_router = APIRouter() -v1_router.include_router(base_router, prefix="/base") -v1_router.include_router(app_users_router, prefix="/app-user") # AppUser路由,无需权限依赖 +v1_router.include_router(base_router, prefix="/base", tags=["admin-基础"]) +v1_router.include_router(app_users_router, prefix="/app-user", tags=["app-用户认证与账户"]) # AppUser路由,无需权限依赖 +v1_router.include_router(admin_app_users_router, prefix="/app-user-admin", tags=["admin-App用户管理"]) # 注意:app-valuations 路由在各自的端点内部使用 get_current_app_user 进行认证 # 这样可以保持App用户认证系统的独立性,不与后台管理权限系统混合 -v1_router.include_router(app_valuations_router, prefix="/app-valuations") # 用户端估值评估路由 -v1_router.include_router(users_router, prefix="/user", dependencies=[DependAuth, DependPermission]) -v1_router.include_router(roles_router, prefix="/role", dependencies=[DependAuth, DependPermission]) -v1_router.include_router(menus_router, prefix="/menu", dependencies=[DependAuth, DependPermission]) -v1_router.include_router(apis_router, prefix="/api", dependencies=[DependAuth, DependPermission]) -v1_router.include_router(depts_router, prefix="/dept", dependencies=[DependAuth, DependPermission]) -v1_router.include_router(auditlog_router, prefix="/auditlog", dependencies=[DependAuth, DependPermission]) -v1_router.include_router(esg_router, prefix="/esg") -v1_router.include_router(index_router, prefix="/index") -v1_router.include_router(industry_router, prefix="/industry") -v1_router.include_router(policy_router, prefix="/policy") -v1_router.include_router(upload_router, prefix="/upload") # 文件上传路由 +v1_router.include_router(app_valuations_router, prefix="/app-valuations", tags=["app-估值评估"]) # 用户端估值评估路由 +v1_router.include_router(users_router, prefix="/user", dependencies=[DependAuth, DependPermission], tags=["admin-用户管理"]) +v1_router.include_router(roles_router, prefix="/role", dependencies=[DependAuth, DependPermission], tags=["admin-角色管理"]) +v1_router.include_router(menus_router, prefix="/menu", dependencies=[DependAuth, DependPermission], tags=["admin-菜单管理"]) +v1_router.include_router(apis_router, prefix="/api", dependencies=[DependAuth, DependPermission], tags=["admin-API权限管理"]) +v1_router.include_router(depts_router, prefix="/dept", dependencies=[DependAuth, DependPermission], tags=["admin-部门管理"]) +v1_router.include_router(auditlog_router, prefix="/auditlog", dependencies=[DependAuth, DependPermission], tags=["admin-审计日志"]) +v1_router.include_router(esg_router, prefix="/esg", tags=["admin-ESG管理"]) +v1_router.include_router(index_router, prefix="/index", tags=["admin-指数管理"]) +v1_router.include_router(industry_router, prefix="/industry", tags=["admin-行业管理"]) +v1_router.include_router(policy_router, prefix="/policy", tags=["admin-政策管理"]) +v1_router.include_router(upload_router, prefix="/upload", tags=["app-上传"]) # 文件上传路由 v1_router.include_router( third_party_api_router, prefix="/third_party_api", dependencies=[DependAuth, DependPermission], + tags=["admin-内置接口"], ) -v1_router.include_router(valuations_router, prefix="/valuations", dependencies=[DependAuth, DependPermission]) +v1_router.include_router(valuations_router, prefix="/valuations", dependencies=[DependAuth, DependPermission], tags=["admin-估值评估"]) +v1_router.include_router(invoice_router, prefix="/invoice", dependencies=[DependAuth, DependPermission], tags=["admin-发票管理"]) +v1_router.include_router(transactions_router, prefix="/transactions", dependencies=[DependAuth, DependPermission], tags=["admin-交易管理"]) +v1_router.include_router(sms_router, prefix="/sms", tags=["app-短信服务"]) diff --git a/app/api/v1/apis/apis.py b/app/api/v1/apis/apis.py index 9cdca28..4ed5918 100644 --- a/app/api/v1/apis/apis.py +++ b/app/api/v1/apis/apis.py @@ -3,12 +3,14 @@ from tortoise.expressions import Q from app.controllers.api import api_controller from app.schemas import Success, SuccessExtra +from app.schemas.base import BasicResponse, PageResponse, MessageOut +from app.schemas.apis import BaseApi from app.schemas.apis import * router = APIRouter() -@router.get("/list", summary="查看API列表") +@router.get("/list", summary="查看API列表", response_model=PageResponse[BaseApi]) async def list_api( page: int = Query(1, description="页码"), page_size: int = Query(10, description="每页数量"), @@ -28,7 +30,7 @@ async def list_api( return SuccessExtra(data=data, total=total, page=page, page_size=page_size) -@router.get("/get", summary="查看Api") +@router.get("/get", summary="查看Api", response_model=BasicResponse[BaseApi]) async def get_api( id: int = Query(..., description="Api"), ): @@ -37,7 +39,7 @@ async def get_api( return Success(data=data) -@router.post("/create", summary="创建Api") +@router.post("/create", summary="创建Api", response_model=BasicResponse[MessageOut]) async def create_api( api_in: ApiCreate, ): @@ -45,7 +47,7 @@ async def create_api( return Success(msg="Created Successfully") -@router.post("/update", summary="更新Api") +@router.post("/update", summary="更新Api", response_model=BasicResponse[MessageOut]) async def update_api( api_in: ApiUpdate, ): @@ -53,7 +55,7 @@ async def update_api( return Success(msg="Update Successfully") -@router.delete("/delete", summary="删除Api") +@router.delete("/delete", summary="删除Api", response_model=BasicResponse[MessageOut]) async def delete_api( api_id: int = Query(..., description="ApiID"), ): @@ -61,7 +63,7 @@ async def delete_api( return Success(msg="Deleted Success") -@router.post("/refresh", summary="刷新API列表") +@router.post("/refresh", summary="刷新API列表", response_model=BasicResponse[MessageOut]) async def refresh_api(): await api_controller.refresh_api() return Success(msg="OK") diff --git a/app/api/v1/app_users/admin_manage.py b/app/api/v1/app_users/admin_manage.py new file mode 100644 index 0000000..3d9688c --- /dev/null +++ b/app/api/v1/app_users/admin_manage.py @@ -0,0 +1,76 @@ +from fastapi import APIRouter, Query, Depends, HTTPException +from typing import Optional + +from app.schemas.base import Success, SuccessExtra, BasicResponse, PageResponse +from app.schemas.app_user import AppUserQuotaUpdateSchema, AppUserQuotaLogOut +from app.controllers.app_user import app_user_controller +from app.models.user import AppUser, AppUserQuotaLog +from app.core.dependency import DependAuth, DependPermission, AuthControl + + +admin_app_users_router = APIRouter(dependencies=[DependAuth, DependPermission], tags=["admin-App用户管理"]) + + +@admin_app_users_router.get("/list", summary="App用户列表", response_model=PageResponse[dict]) +async def list_app_users( + phone: Optional[str] = Query(None), + wechat: Optional[str] = Query(None), + page: int = Query(1, ge=1), + page_size: int = Query(10, ge=1, le=100), +): + qs = AppUser.filter() + if phone: + qs = qs.filter(phone__icontains=phone) + if wechat: + qs = qs.filter(alias__icontains=wechat) + total = await qs.count() + rows = await qs.order_by("-created_at").offset((page - 1) * page_size).limit(page_size) + items = [] + for u in rows: + items.append({ + "id": u.id, + "phone": u.phone, + "wechat": u.alias, + "created_at": u.created_at.isoformat() if u.created_at else "", + "notes": "", + "remaining_count": int(getattr(u, "remaining_quota", 0) or 0), + "user_type": None, + }) + return SuccessExtra(data=items, total=total, page=page, page_size=page_size, msg="获取成功") + + +@admin_app_users_router.post("/quota", summary="调整用户剩余估值次数", response_model=BasicResponse[dict]) +async def update_quota(payload: AppUserQuotaUpdateSchema, operator=Depends(AuthControl.is_authed)): + user = await app_user_controller.update_quota( + operator_id=getattr(operator, "id", 0), + operator_name=getattr(operator, "username", "admin"), + user_id=payload.user_id, + target_count=payload.target_count, + delta=payload.delta, + op_type=payload.op_type, + remark=payload.remark, + ) + if not user: + raise HTTPException(status_code=404, detail="用户不存在") + return Success(data={"user_id": user.id, "remaining_quota": user.remaining_quota}, msg="调整成功") + + +@admin_app_users_router.get("/{user_id}/quota-logs", summary="用户估值次数操作日志", response_model=PageResponse[AppUserQuotaLogOut]) +async def quota_logs(user_id: int, page: int = Query(1, ge=1), page_size: int = Query(10, ge=1, le=100)): + qs = AppUserQuotaLog.filter(app_user_id=user_id) + total = await qs.count() + rows = await qs.order_by("-created_at").offset((page - 1) * page_size).limit(page_size) + models = [ + AppUserQuotaLogOut( + id=r.id, + app_user_id=r.app_user_id, + operator_id=r.operator_id, + operator_name=r.operator_name, + before_count=r.before_count, + after_count=r.after_count, + op_type=r.op_type, + remark=r.remark, + ) for r in rows + ] + data_items = [m.model_dump() for m in models] + return SuccessExtra(data=data_items, total=total, page=page, page_size=page_size, msg="获取成功") \ No newline at end of file diff --git a/app/api/v1/app_users/app_users.py b/app/api/v1/app_users/app_users.py index c041a17..67fd06c 100644 --- a/app/api/v1/app_users/app_users.py +++ b/app/api/v1/app_users/app_users.py @@ -6,19 +6,25 @@ from app.schemas.app_user import ( AppUserJWTOut, AppUserInfoOut, AppUserUpdateSchema, - AppUserChangePasswordSchema + AppUserChangePasswordSchema, + AppUserDashboardOut, + AppUserQuotaOut, ) +from app.schemas.app_user import AppUserRegisterOut, TokenValidateOut +from app.schemas.base import BasicResponse, MessageOut from app.utils.app_user_jwt import ( create_app_user_access_token, get_current_app_user, ACCESS_TOKEN_EXPIRE_MINUTES ) from app.models.user import AppUser +from app.controllers.user_valuation import user_valuation_controller +from app.controllers.invoice import invoice_controller router = APIRouter() -@router.post("/register", response_model=dict, summary="用户注册") +@router.post("/register", response_model=BasicResponse[AppUserRegisterOut], summary="用户注册") async def register( register_data: AppUserRegisterSchema ): @@ -30,11 +36,11 @@ async def register( user = await app_user_controller.register(register_data) return { "code": 200, - "message": "注册成功", + "msg": "注册成功", "data": { "user_id": user.id, "phone": user.phone, - "default_password": register_data.phone[-6:] # 返回默认密码供用户知晓 + "default_password": register_data.phone[-6:] } } except Exception as e: @@ -68,12 +74,12 @@ async def login( ) -@router.post("/logout", summary="用户登出") +@router.post("/logout", summary="用户登出", response_model=BasicResponse[MessageOut]) async def logout(current_user: AppUser = Depends(get_current_app_user)): """ 用户登出(客户端需要删除本地token) """ - return {"code": 200, "message": "登出成功"} + return {"code": 200, "msg": "OK", "data": {"message": "登出成功"}} @router.get("/profile", response_model=AppUserInfoOut, summary="获取用户信息") @@ -84,6 +90,49 @@ async def get_profile(current_user: AppUser = Depends(get_current_app_user)): return current_user +@router.get("/dashboard", response_model=AppUserDashboardOut, summary="用户首页摘要") +async def get_dashboard(current_user: AppUser = Depends(get_current_app_user)): + """ + 用户首页摘要 + 功能: + - 返回剩余估值次数(暂以 0 占位,后续可接入配额系统) + - 返回最近一条估值评估记录(若有) + - 返回待处理发票数量 + """ + # 最近估值记录 + latest = await user_valuation_controller.model.filter(user_id=current_user.id).order_by("-created_at").first() + latest_out = None + if latest: + latest_out = { + "id": latest.id, + "asset_name": latest.asset_name, + "valuation_result": latest.final_value_ab, + "status": latest.status, + "created_at": latest.created_at.isoformat() if latest.created_at else "", + } + # 待处理发票数量 + try: + pending_invoices = await invoice_controller.count_pending_for_user(current_user.id) + except Exception: + pending_invoices = 0 + # 剩余估值次数(占位,可从用户扩展字段或配额表获取) + remaining_quota = 0 + return AppUserDashboardOut(remaining_quota=remaining_quota, latest_valuation=latest_out, pending_invoices=pending_invoices) + + +@router.get("/quota", response_model=AppUserQuotaOut, summary="剩余估值次数") +async def get_quota(current_user: AppUser = Depends(get_current_app_user)): + """ + 剩余估值次数查询 + 说明: + - 当前实现返回默认 0 次与用户类型占位 + - 若后续接入配额系统,可从数据库中读取真实值 + """ + remaining_count = 0 + user_type = "体验用户" + return AppUserQuotaOut(remaining_count=remaining_count, user_type=user_type) + + @router.put("/profile", response_model=AppUserInfoOut, summary="更新用户信息") async def update_profile( update_data: AppUserUpdateSchema, @@ -99,7 +148,7 @@ async def update_profile( return updated_user -@router.post("/change-password", summary="修改密码") +@router.post("/change-password", summary="修改密码", response_model=BasicResponse[MessageOut]) async def change_password( password_data: AppUserChangePasswordSchema, current_user: AppUser = Depends(get_current_app_user) @@ -116,17 +165,17 @@ async def change_password( if not success: raise HTTPException(status_code=400, detail="原密码错误") - return {"code": 200, "message": "密码修改成功"} + return {"code": 200, "msg": "OK", "data": {"message": "密码修改成功"}} -@router.get("/validate-token", summary="验证token") +@router.get("/validate-token", summary="验证token", response_model=BasicResponse[TokenValidateOut]) async def validate_token(current_user: AppUser = Depends(get_current_app_user)): """ 验证token是否有效 """ return { "code": 200, - "message": "token有效", + "msg": "token有效", "data": { "user_id": current_user.id, "phone": current_user.phone diff --git a/app/api/v1/app_valuations/app_valuations.py b/app/api/v1/app_valuations/app_valuations.py index 7e1b96d..495b461 100644 --- a/app/api/v1/app_valuations/app_valuations.py +++ b/app/api/v1/app_valuations/app_valuations.py @@ -9,6 +9,8 @@ import asyncio import time from app.controllers.user_valuation import user_valuation_controller +from app.controllers.valuation import valuation_controller +from app.schemas.valuation import ValuationAssessmentUpdate from app.schemas.valuation import ( UserValuationCreate, UserValuationQuery, @@ -16,13 +18,13 @@ from app.schemas.valuation import ( UserValuationOut, UserValuationDetail ) -from app.schemas.base import Success, SuccessExtra +from app.schemas.base import Success, SuccessExtra, BasicResponse, PageResponse from app.utils.app_user_jwt import get_current_app_user_id, get_current_app_user from app.utils.calculation_engine import FinalValueACalculator -from app.utils.calculation_engine.cultural_value_b2.sub_formulas.living_heritage_b21 import cross_border_depth_dict +# from app.utils.calculation_engine.cultural_value_b2.sub_formulas.living_heritage_b21 import cross_border_depth_dict from app.utils.calculation_engine.drp import DynamicPledgeRateCalculator -from app.utils.calculation_engine.economic_value_b1.sub_formulas.basic_value_b11 import calculate_popularity_score, \ - calculate_infringement_score, calculate_patent_usage_score, calculate_patent_score +# from app.utils.calculation_engine.economic_value_b1.sub_formulas.basic_value_b11 import calculate_popularity_score + from app.utils.calculation_engine.economic_value_b1.sub_formulas.traffic_factor_b12 import calculate_search_index_s1 from app.log.log import logger from app.models.esg import ESG @@ -108,12 +110,18 @@ async def _perform_valuation_calculation(user_id: int, data: UserValuationCreate matched = [item for item in data_list if isinstance(item, dict) and item.get("SQH") == getattr(data, 'patent_application_no', None)] if matched: - patent_count = calculate_patent_usage_score(len(matched)) - input_data_by_b1["patent_count"] = float(patent_count) + patent_count_score = min(len(matched) * 2.5, 10.0) + input_data_by_b1["patent_count"] = float(patent_count_score) else: input_data_by_b1["patent_count"] = 0.0 - patent_score = calculate_patent_score(calculate_total_years(data_list)) + years_total = calculate_total_years(data_list) + if years_total > 10: + patent_score = 10.0 + elif years_total >= 5: + patent_score = 7.0 + else: + patent_score = 3.0 input_data_by_b1["patent_score"] = patent_score # 提取 文化价值B2 计算参数 @@ -141,8 +149,20 @@ async def _perform_valuation_calculation(user_id: int, data: UserValuationCreate calculator = FinalValueACalculator() - # 计算最终估值A(统一计算) - calculation_result = await calculator.calculate_complete_final_value_a(input_data) + # 先创建估值记录以获取ID,方便步骤落库关联 + initial_detail = await user_valuation_controller.create_valuation( + user_id=user_id, + data=data, + calculation_result=None, + calculation_input=None, + drp_result=None, + status='pending' + ) + valuation_id = initial_detail.id + logger.info("valuation.init_created user_id={} valuation_id={}", user_id, valuation_id) + + # 计算最终估值A(统一计算),传入估值ID以关联步骤落库 + calculation_result = await calculator.calculate_complete_final_value_a(valuation_id, input_data) # 计算动态质押 drp_c = DynamicPledgeRateCalculator() @@ -168,10 +188,12 @@ async def _perform_valuation_calculation(user_id: int, data: UserValuationCreate except Exception: pass - # 创建估值评估记录 - result = await user_valuation_controller.create_valuation( - user_id=user_id, - data=data, + # 更新估值评估记录(写入计算结果与输入摘要) + update_data = ValuationAssessmentUpdate( + model_value_b=calculation_result.get('model_value_b'), + market_value_c=calculation_result.get('market_value_c'), + final_value_ab=calculation_result.get('final_value_ab'), + dynamic_pledge_rate=drp_result, calculation_result=calculation_result, calculation_input={ 'model_data': { @@ -181,8 +203,15 @@ async def _perform_valuation_calculation(user_id: int, data: UserValuationCreate }, 'market_data': list(input_data.get('market_data', {}).keys()), }, - drp_result=drp_result, - status='success' # 计算成功,设置为approved状态 + status='success' + ) + result = await valuation_controller.update(valuation_id, update_data) + logger.info( + "valuation.updated valuation_id={} model_b={} market_c={} final_ab={}", + valuation_id, + calculation_result.get('model_value_b'), + calculation_result.get('market_value_c'), + calculation_result.get('final_value_ab'), ) logger.info("valuation.background_calc_success user_id={} valuation_id={}", user_id, result.id) @@ -192,22 +221,16 @@ async def _perform_valuation_calculation(user_id: int, data: UserValuationCreate print(traceback.format_exc()) logger.error("valuation.background_calc_failed user_id={} err={}", user_id, repr(e)) - # 计算失败时也创建记录,状态设置为failed + # 计算失败时更新记录为失败状态 try: - result = await user_valuation_controller.create_valuation( - user_id=user_id, - data=data, - calculation_result=None, - calculation_input=None, - drp_result=None, - status='rejected' # 计算失败,设置为rejected状态 - ) - logger.info("valuation.failed_record_created user_id={} valuation_id={}", user_id, result.id) + if 'valuation_id' in locals(): + fail_update = ValuationAssessmentUpdate(status='rejected') + await valuation_controller.update(valuation_id, fail_update) except Exception as create_error: - logger.error("valuation.failed_to_create_record user_id={} err={}", user_id, repr(create_error)) + logger.error("valuation.failed_to_update_record user_id={} err={}", user_id, repr(create_error)) -@app_valuations_router.post("/", summary="创建估值评估") +@app_valuations_router.post("/", summary="创建估值评估", response_model=BasicResponse[dict]) async def calculate_valuation( background_tasks: BackgroundTasks, data: UserValuationCreate, @@ -315,7 +338,13 @@ async def _extract_calculation_params_b1(data: UserValuationCreate) -> Dict[str, # 法律强度L相关参数 # 普及地域分值 默认 7分 - popularity_score = calculate_popularity_score(data.application_coverage) + # 普及地域分:全球覆盖(10)、全国覆盖(7)、区域覆盖(4),默认全国覆盖(7) + try: + coverage = data.application_coverage or "全国覆盖" + mapping = {"全球覆盖": 10.0, "全国覆盖": 7.0, "区域覆盖": 4.0} + popularity_score = mapping.get(coverage, 7.0) + except Exception: + popularity_score = 7.0 # 创新投入比 = (研发费用/营收) * 100 try: @@ -427,18 +456,50 @@ async def _extract_calculation_params_b2(data: UserValuationCreate) -> Dict[str, kuaishou_views = safe_float(rs.get("kuaishou", None).get("likes", 0)) if rs.get("kuaishou", None) else 0 bilibili_views = safe_float(rs.get("bilibili", None).get("likes", 0)) if rs.get("bilibili", None) else 0 - # 跨界合作深度 品牌联名0.3,科技载体0.5,国家外交礼品1.0 - cross_border_depth = cross_border_depth_dict(data.cooperation_depth) + # 跨界合作深度:将枚举映射为项目数;若为数值字符串则直接取数值 + try: + val = getattr(data, 'cooperation_depth', None) + mapping = { + "品牌联名": 3.0, + "科技载体": 5.0, + "国家外交礼品": 10.0, + } + if isinstance(val, str): + cross_border_depth = mapping.get(val, safe_float(val)) + else: + cross_border_depth = safe_float(val) + except Exception: + cross_border_depth = 0.0 # 纹样基因值B22相关参数 # 以下三项需由后续模型/服务计算;此处提供默认可计算占位 # # 历史传承度HI(用户填写) - historical_inheritance = sum([safe_float(i) for i in data.historical_evidence]) + historical_inheritance = 0.0 + try: + if isinstance(data.historical_evidence, dict): + historical_inheritance = sum([safe_float(v) for v in data.historical_evidence.values()]) + elif isinstance(data.historical_evidence, (list, tuple)): + historical_inheritance = sum([safe_float(i) for i in data.historical_evidence]) + except Exception: + historical_inheritance = 0.0 structure_complexity = 1.5 # 默认值 纹样基因熵值B22(系统计算) normalized_entropy = 9 # 默认值 归一化信息熵H(系统计算) + logger.info( + "b2.params inheritor_level_coefficient={} offline_sessions={} douyin_views={} kuaishou_views={} bilibili_views={} cross_border_depth={} historical_inheritance={} structure_complexity={} normalized_entropy={}", + inheritor_level_coefficient, + offline_sessions, + douyin_views, + kuaishou_views, + bilibili_views, + cross_border_depth, + historical_inheritance, + structure_complexity, + normalized_entropy, + ) + return { "inheritor_level_coefficient": inheritor_level_coefficient, "offline_sessions": offline_sessions, @@ -563,7 +624,7 @@ async def _extract_calculation_params_c(data: UserValuationCreate) -> Dict[str, } -@app_valuations_router.get("/", summary="获取我的估值评估列表") +@app_valuations_router.get("/", summary="获取我的估值评估列表", response_model=PageResponse[UserValuationOut]) async def get_my_valuations( query: UserValuationQuery = Depends(), current_user: AppUser = Depends(get_current_app_user) @@ -595,7 +656,7 @@ async def get_my_valuations( ) -@app_valuations_router.get("/{valuation_id}", summary="获取估值评估详情") +@app_valuations_router.get("/{valuation_id}", summary="获取估值评估详情", response_model=BasicResponse[UserValuationDetail]) async def get_valuation_detail( valuation_id: int, current_user: AppUser = Depends(get_current_app_user) @@ -628,7 +689,7 @@ async def get_valuation_detail( ) -@app_valuations_router.get("/statistics/overview", summary="获取我的估值评估统计") +@app_valuations_router.get("/statistics/overview", summary="获取我的估值评估统计", response_model=BasicResponse[dict]) async def get_my_valuation_statistics( current_user: AppUser = Depends(get_current_app_user) ): @@ -647,7 +708,7 @@ async def get_my_valuation_statistics( ) -@app_valuations_router.delete("/{valuation_id}", summary="删除估值评估") +@app_valuations_router.delete("/{valuation_id}", summary="删除估值评估", response_model=BasicResponse[dict]) async def delete_valuation( valuation_id: int, current_user: AppUser = Depends(get_current_app_user) @@ -705,3 +766,4 @@ def safe_float(v): return float(v) except (ValueError, TypeError): return 0.0 +from app.log.log import logger diff --git a/app/api/v1/base/base.py b/app/api/v1/base/base.py index 5f8f8ae..ebe96c2 100644 --- a/app/api/v1/base/base.py +++ b/app/api/v1/base/base.py @@ -6,7 +6,7 @@ from app.controllers.user import user_controller from app.core.ctx import CTX_USER_ID from app.core.dependency import DependAuth from app.models.admin import Api, Menu, Role, User -from app.schemas.base import Fail, Success +from app.schemas.base import Fail, Success, BasicResponse from app.schemas.login import * from app.schemas.users import UpdatePassword from app.settings import settings @@ -16,7 +16,7 @@ from app.utils.password import get_password_hash, verify_password router = APIRouter() -@router.post("/access_token", summary="获取token") +@router.post("/access_token", summary="获取token", response_model=BasicResponse[JWTOut]) async def login_access_token(credentials: CredentialsSchema): user: User = await user_controller.authenticate(credentials) await user_controller.update_last_login(user.id) @@ -37,7 +37,7 @@ async def login_access_token(credentials: CredentialsSchema): return Success(data=data.model_dump()) -@router.get("/userinfo", summary="查看用户信息", dependencies=[DependAuth]) +@router.get("/userinfo", summary="查看用户信息", dependencies=[DependAuth], response_model=BasicResponse[dict]) async def get_userinfo(): user_id = CTX_USER_ID.get() user_obj = await user_controller.get(id=user_id) @@ -46,7 +46,7 @@ async def get_userinfo(): return Success(data=data) -@router.get("/usermenu", summary="查看用户菜单", dependencies=[DependAuth]) +@router.get("/usermenu", summary="查看用户菜单", dependencies=[DependAuth], response_model=BasicResponse[list]) async def get_user_menu(): user_id = CTX_USER_ID.get() user_obj = await User.filter(id=user_id).first() @@ -74,7 +74,7 @@ async def get_user_menu(): return Success(data=res) -@router.get("/userapi", summary="查看用户API", dependencies=[DependAuth]) +@router.get("/userapi", summary="查看用户API", dependencies=[DependAuth], response_model=BasicResponse[list]) async def get_user_api(): user_id = CTX_USER_ID.get() user_obj = await User.filter(id=user_id).first() @@ -91,7 +91,7 @@ async def get_user_api(): return Success(data=apis) -@router.post("/update_password", summary="修改密码", dependencies=[DependAuth]) +@router.post("/update_password", summary="修改密码", dependencies=[DependAuth], response_model=BasicResponse[dict]) async def update_user_password(req_in: UpdatePassword): user_id = CTX_USER_ID.get() user = await user_controller.get(user_id) diff --git a/app/api/v1/depts/depts.py b/app/api/v1/depts/depts.py index a22ef0a..5353060 100644 --- a/app/api/v1/depts/depts.py +++ b/app/api/v1/depts/depts.py @@ -2,12 +2,14 @@ from fastapi import APIRouter, Query from app.controllers.dept import dept_controller from app.schemas import Success +from app.schemas.base import BasicResponse, MessageOut +from app.schemas.depts import BaseDept from app.schemas.depts import * router = APIRouter() -@router.get("/list", summary="查看部门列表") +@router.get("/list", summary="查看部门列表", response_model=BasicResponse[list[BaseDept]]) async def list_dept( name: str = Query(None, description="部门名称"), ): @@ -15,7 +17,7 @@ async def list_dept( return Success(data=dept_tree) -@router.get("/get", summary="查看部门") +@router.get("/get", summary="查看部门", response_model=BasicResponse[BaseDept]) async def get_dept( id: int = Query(..., description="部门ID"), ): @@ -24,7 +26,7 @@ async def get_dept( return Success(data=data) -@router.post("/create", summary="创建部门") +@router.post("/create", summary="创建部门", response_model=BasicResponse[MessageOut]) async def create_dept( dept_in: DeptCreate, ): @@ -32,7 +34,7 @@ async def create_dept( return Success(msg="Created Successfully") -@router.post("/update", summary="更新部门") +@router.post("/update", summary="更新部门", response_model=BasicResponse[MessageOut]) async def update_dept( dept_in: DeptUpdate, ): @@ -40,7 +42,7 @@ async def update_dept( return Success(msg="Update Successfully") -@router.delete("/delete", summary="删除部门") +@router.delete("/delete", summary="删除部门", response_model=BasicResponse[MessageOut]) async def delete_dept( dept_id: int = Query(..., description="部门ID"), ): diff --git a/app/api/v1/esg/esg.py b/app/api/v1/esg/esg.py index 1ef3306..82444e5 100644 --- a/app/api/v1/esg/esg.py +++ b/app/api/v1/esg/esg.py @@ -3,12 +3,14 @@ from tortoise.expressions import Q from app.controllers.esg import esg_controller from app.schemas import Success, SuccessExtra +from app.schemas.base import BasicResponse, PageResponse, MessageOut +from app.schemas.esg import ESGResponse from app.schemas.esg import ESGCreate, ESGUpdate, ESGResponse router = APIRouter(tags=["ESG管理"]) -@router.get("/list", summary="查看ESG列表") +@router.get("/list", summary="查看ESG列表", response_model=PageResponse[ESGResponse]) async def list_esg( page: int = Query(1, description="页码"), page_size: int = Query(10, description="每页数量"), @@ -28,7 +30,7 @@ async def list_esg( return SuccessExtra(data=data, total=total, page=page, page_size=page_size) -@router.get("/get", summary="查看ESG详情") +@router.get("/get", summary="查看ESG详情", response_model=BasicResponse[ESGResponse]) async def get_esg( id: int = Query(..., description="ESG ID"), ): @@ -37,7 +39,7 @@ async def get_esg( return Success(data=data) -@router.post("/create", summary="创建ESG") +@router.post("/create", summary="创建ESG", response_model=BasicResponse[MessageOut]) async def create_esg( esg_in: ESGCreate, ): @@ -49,7 +51,7 @@ async def create_esg( return Success(msg="创建成功") -@router.post("/update", summary="更新ESG") +@router.post("/update", summary="更新ESG", response_model=BasicResponse[MessageOut]) async def update_esg( esg_in: ESGUpdate, ): @@ -63,7 +65,7 @@ async def update_esg( return Success(msg="更新成功") -@router.delete("/delete", summary="删除ESG") +@router.delete("/delete", summary="删除ESG", response_model=BasicResponse[MessageOut]) async def delete_esg( esg_id: int = Query(..., description="ESG ID"), ): diff --git a/app/api/v1/index/index.py b/app/api/v1/index/index.py index 9a56eef..9310c74 100644 --- a/app/api/v1/index/index.py +++ b/app/api/v1/index/index.py @@ -3,12 +3,14 @@ from tortoise.expressions import Q from app.controllers.index import index_controller from app.schemas import Success, SuccessExtra +from app.schemas.base import BasicResponse, PageResponse, MessageOut +from app.schemas.index import IndexResponse from app.schemas.index import IndexCreate, IndexUpdate, IndexResponse router = APIRouter(tags=["指数管理"]) -@router.get("/list", summary="查看指数列表") +@router.get("/list", summary="查看指数列表", response_model=PageResponse[IndexResponse]) async def list_index( page: int = Query(1, description="页码"), page_size: int = Query(10, description="每页数量"), @@ -25,7 +27,7 @@ async def list_index( return SuccessExtra(data=data, total=total, page=page, page_size=page_size) -@router.get("/get", summary="查看指数详情") +@router.get("/get", summary="查看指数详情", response_model=BasicResponse[IndexResponse]) async def get_index( id: int = Query(..., description="指数 ID"), ): @@ -34,7 +36,7 @@ async def get_index( return Success(data=data) -@router.post("/create", summary="创建指数") +@router.post("/create", summary="创建指数", response_model=BasicResponse[MessageOut]) async def create_index( index_in: IndexCreate, ): @@ -46,7 +48,7 @@ async def create_index( return Success(msg="创建成功") -@router.post("/update", summary="更新指数") +@router.post("/update", summary="更新指数", response_model=BasicResponse[MessageOut]) async def update_index( index_in: IndexUpdate, ): @@ -60,7 +62,7 @@ async def update_index( return Success(msg="更新成功") -@router.delete("/delete", summary="删除指数") +@router.delete("/delete", summary="删除指数", response_model=BasicResponse[MessageOut]) async def delete_index( index_id: int = Query(..., description="指数 ID"), ): diff --git a/app/api/v1/industry/industry.py b/app/api/v1/industry/industry.py index 127bea0..78078ec 100644 --- a/app/api/v1/industry/industry.py +++ b/app/api/v1/industry/industry.py @@ -3,12 +3,13 @@ from tortoise.expressions import Q from app.controllers.industry import industry_controller from app.schemas import Success, SuccessExtra +from app.schemas.base import BasicResponse, PageResponse, MessageOut from app.schemas.industry import IndustryCreate, IndustryUpdate, IndustryResponse router = APIRouter(tags=["行业管理"]) -@router.get("/list", summary="查看行业列表") +@router.get("/list", summary="查看行业列表", response_model=PageResponse[IndustryResponse]) async def list_industry( page: int = Query(1, description="页码"), page_size: int = Query(10, description="每页数量"), @@ -25,7 +26,7 @@ async def list_industry( return SuccessExtra(data=data, total=total, page=page, page_size=page_size) -@router.get("/get", summary="查看行业详情") +@router.get("/get", summary="查看行业详情", response_model=BasicResponse[IndustryResponse]) async def get_industry( id: int = Query(..., description="行业 ID"), ): @@ -34,7 +35,7 @@ async def get_industry( return Success(data=data) -@router.post("/create", summary="创建行业") +@router.post("/create", summary="创建行业", response_model=BasicResponse[MessageOut]) async def create_industry( industry_in: IndustryCreate, ): @@ -46,7 +47,7 @@ async def create_industry( return Success(msg="创建成功") -@router.post("/update", summary="更新行业") +@router.post("/update", summary="更新行业", response_model=BasicResponse[MessageOut]) async def update_industry( industry_in: IndustryUpdate, ): @@ -60,7 +61,7 @@ async def update_industry( return Success(msg="更新成功") -@router.delete("/delete", summary="删除行业") +@router.delete("/delete", summary="删除行业", response_model=BasicResponse[MessageOut]) async def delete_industry( industry_id: int = Query(..., description="行业 ID"), ): diff --git a/app/api/v1/invoice/__init__.py b/app/api/v1/invoice/__init__.py new file mode 100644 index 0000000..9c5eea5 --- /dev/null +++ b/app/api/v1/invoice/__init__.py @@ -0,0 +1,3 @@ +from .invoice import invoice_router + +__all__ = ["invoice_router"] \ No newline at end of file diff --git a/app/api/v1/invoice/invoice.py b/app/api/v1/invoice/invoice.py new file mode 100644 index 0000000..2706ba2 --- /dev/null +++ b/app/api/v1/invoice/invoice.py @@ -0,0 +1,154 @@ +from fastapi import APIRouter, Query +from typing import Optional + +from app.schemas.base import Success, SuccessExtra, BasicResponse, PageResponse, MessageOut +from app.schemas.invoice import ( + InvoiceCreate, + InvoiceUpdate, + UpdateStatus, + UpdateType, + InvoiceHeaderCreate, + PaymentReceiptCreate, + InvoiceOut, + InvoiceList, + InvoiceHeaderOut, + PaymentReceiptOut, +) +from app.controllers.invoice import invoice_controller + + +invoice_router = APIRouter(tags=["发票管理"]) + + +@invoice_router.get("/list", summary="获取发票列表", response_model=PageResponse[InvoiceOut]) +async def list_invoices( + phone: Optional[str] = Query(None), + company_name: Optional[str] = Query(None), + tax_number: Optional[str] = Query(None), + status: Optional[str] = Query(None), + ticket_type: Optional[str] = Query(None), + invoice_type: Optional[str] = Query(None), + page: int = Query(1, ge=1), + page_size: int = Query(10, ge=1, le=100), +): + """ + 发票列表查询 + 参数支持按手机号、公司名称、税号、状态、发票类型进行筛选 + 返回分页结构 + """ + result = await invoice_controller.list( + page=page, + page_size=page_size, + phone=phone, + company_name=company_name, + tax_number=tax_number, + status=status, + ticket_type=ticket_type, + invoice_type=invoice_type, + ) + return SuccessExtra( + data=result.items, total=result.total, page=result.page, page_size=result.page_size, msg="获取成功" + ) + + +@invoice_router.get("/detail", summary="发票详情", response_model=BasicResponse[InvoiceOut]) +async def invoice_detail(id: int = Query(...)): + """ + 根据ID获取发票详情 + """ + out = await invoice_controller.get_out(id) + if not out: + return Success(data={}, msg="未找到") + return Success(data=out, msg="获取成功") + + +@invoice_router.post("/create", summary="创建发票", response_model=BasicResponse[InvoiceOut]) +async def create_invoice(data: InvoiceCreate): + """ + 创建发票记录 + """ + inv = await invoice_controller.create(data) + out = await invoice_controller.get_out(inv.id) + return Success(data=out, msg="创建成功") + + +@invoice_router.post("/update", summary="更新发票", response_model=BasicResponse[InvoiceOut]) +async def update_invoice(data: InvoiceUpdate, id: int = Query(...)): + """ + 更新发票记录 + """ + updated = await invoice_controller.update(id, data) + out = await invoice_controller.get_out(id) if updated else None + return Success(data=out or {}, msg="更新成功" if updated else "未找到") + + +@invoice_router.delete("/delete", summary="删除发票", response_model=BasicResponse[MessageOut]) +async def delete_invoice(id: int = Query(...)): + """ + 删除发票记录 + """ + try: + await invoice_controller.remove(id) + ok = True + except Exception: + ok = False + return Success(data={"deleted": ok}, msg="删除成功" if ok else "未找到") + + +@invoice_router.post("/update-status", summary="更新发票状态", response_model=BasicResponse[InvoiceOut]) +async def update_invoice_status(data: UpdateStatus): + """ + 更新发票状态(pending|invoiced|rejected|refunded) + """ + out = await invoice_controller.update_status(data) + return Success(data=out or {}, msg="更新成功" if out else "未找到") + + + + +@invoice_router.post("/{id}/receipt", summary="上传付款凭证", response_model=BasicResponse[PaymentReceiptOut]) +async def upload_payment_receipt(id: int, data: PaymentReceiptCreate): + """ + 上传对公转账付款凭证 + """ + receipt = await invoice_controller.create_receipt(id, data) + return Success(data=receipt, msg="上传成功") + + +@invoice_router.get("/headers", summary="发票抬头列表", response_model=BasicResponse[list[InvoiceHeaderOut]]) +async def get_invoice_headers(app_user_id: Optional[int] = Query(None)): + """ + 获取发票抬头列表,可按 AppUser 过滤 + """ + headers = await invoice_controller.get_headers(user_id=app_user_id) + return Success(data=headers, msg="获取成功") + + +@invoice_router.get("/headers/{id}", summary="发票抬头详情", response_model=BasicResponse[InvoiceHeaderOut]) +async def get_invoice_header_by_id(id: int): + """ + 获取发票抬头详情 + """ + header = await invoice_controller.get_header_by_id(id) + return Success(data=header or {}, msg="获取成功" if header else "未找到") + + +@invoice_router.post("/headers", summary="新增发票抬头", response_model=BasicResponse[InvoiceHeaderOut]) +async def create_invoice_header(data: InvoiceHeaderCreate, app_user_id: Optional[int] = Query(None)): + """ + 新增发票抬头 + """ + header = await invoice_controller.create_header(user_id=app_user_id, data=data) + return Success(data=header, msg="创建成功") + + +@invoice_router.put("/{id}/type", summary="更新发票类型", response_model=BasicResponse[InvoiceOut]) +async def update_invoice_type(id: int, data: UpdateType): + """ + 更新发票的电子/纸质与专票/普票类型 + """ + out = await invoice_controller.update_type(id, data) + return Success(data=out or {}, msg="更新成功" if out else "未找到") + + +# 对公转账记录接口在 transactions 路由中统一暴露 \ No newline at end of file diff --git a/app/api/v1/menus/menus.py b/app/api/v1/menus/menus.py index 308e8ca..22867f2 100644 --- a/app/api/v1/menus/menus.py +++ b/app/api/v1/menus/menus.py @@ -3,7 +3,8 @@ import logging from fastapi import APIRouter, Query from app.controllers.menu import menu_controller -from app.schemas.base import Fail, Success, SuccessExtra +from app.schemas.base import Fail, Success, SuccessExtra, BasicResponse, PageResponse, MessageOut +from app.schemas.menus import BaseMenu from app.schemas.menus import * logger = logging.getLogger(__name__) @@ -11,7 +12,7 @@ logger = logging.getLogger(__name__) router = APIRouter() -@router.get("/list", summary="查看菜单列表") +@router.get("/list", summary="查看菜单列表", response_model=PageResponse[BaseMenu]) async def list_menu( page: int = Query(1, description="页码"), page_size: int = Query(10, description="每页数量"), @@ -28,7 +29,7 @@ async def list_menu( return SuccessExtra(data=res_menu, total=len(res_menu), page=page, page_size=page_size) -@router.get("/get", summary="查看菜单") +@router.get("/get", summary="查看菜单", response_model=BasicResponse[BaseMenu]) async def get_menu( menu_id: int = Query(..., description="菜单id"), ): @@ -36,7 +37,7 @@ async def get_menu( return Success(data=result) -@router.post("/create", summary="创建菜单") +@router.post("/create", summary="创建菜单", response_model=BasicResponse[MessageOut]) async def create_menu( menu_in: MenuCreate, ): @@ -44,7 +45,7 @@ async def create_menu( return Success(msg="Created Success") -@router.post("/update", summary="更新菜单") +@router.post("/update", summary="更新菜单", response_model=BasicResponse[MessageOut]) async def update_menu( menu_in: MenuUpdate, ): @@ -52,7 +53,7 @@ async def update_menu( return Success(msg="Updated Success") -@router.delete("/delete", summary="删除菜单") +@router.delete("/delete", summary="删除菜单", response_model=BasicResponse[MessageOut]) async def delete_menu( id: int = Query(..., description="菜单id"), ): diff --git a/app/api/v1/policy/policy.py b/app/api/v1/policy/policy.py index b3f6350..bea9eb0 100644 --- a/app/api/v1/policy/policy.py +++ b/app/api/v1/policy/policy.py @@ -3,12 +3,14 @@ from tortoise.expressions import Q from app.controllers.policy import policy_controller from app.schemas import Success, SuccessExtra +from app.schemas.base import BasicResponse, PageResponse, MessageOut +from app.schemas.policy import PolicyResponse from app.schemas.policy import PolicyCreate, PolicyUpdate, PolicyResponse router = APIRouter(tags=["政策管理"]) -@router.get("/list", summary="查看政策列表") +@router.get("/list", summary="查看政策列表", response_model=PageResponse[PolicyResponse]) async def list_policy( page: int = Query(1, description="页码"), page_size: int = Query(10, description="每页数量"), @@ -28,7 +30,7 @@ async def list_policy( return SuccessExtra(data=data, total=total, page=page, page_size=page_size) -@router.get("/get", summary="查看政策详情") +@router.get("/get", summary="查看政策详情", response_model=BasicResponse[PolicyResponse]) async def get_policy( id: int = Query(..., description="政策 ID"), ): @@ -37,7 +39,7 @@ async def get_policy( return Success(data=data) -@router.post("/create", summary="创建政策") +@router.post("/create", summary="创建政策", response_model=BasicResponse[MessageOut]) async def create_policy( policy_in: PolicyCreate, ): @@ -49,7 +51,7 @@ async def create_policy( return Success(msg="创建成功") -@router.post("/update", summary="更新政策") +@router.post("/update", summary="更新政策", response_model=BasicResponse[MessageOut]) async def update_policy( policy_in: PolicyUpdate, ): @@ -63,7 +65,7 @@ async def update_policy( return Success(msg="更新成功") -@router.delete("/delete", summary="删除政策") +@router.delete("/delete", summary="删除政策", response_model=BasicResponse[MessageOut]) async def delete_policy( policy_id: int = Query(..., description="政策 ID"), ): diff --git a/app/api/v1/roles/roles.py b/app/api/v1/roles/roles.py index f43a304..6f1c337 100644 --- a/app/api/v1/roles/roles.py +++ b/app/api/v1/roles/roles.py @@ -5,14 +5,15 @@ from fastapi.exceptions import HTTPException from tortoise.expressions import Q from app.controllers import role_controller -from app.schemas.base import Success, SuccessExtra +from app.schemas.base import Success, SuccessExtra, BasicResponse, PageResponse, MessageOut +from app.schemas.roles import BaseRole from app.schemas.roles import * logger = logging.getLogger(__name__) router = APIRouter() -@router.get("/list", summary="查看角色列表") +@router.get("/list", summary="查看角色列表", response_model=PageResponse[BaseRole]) async def list_role( page: int = Query(1, description="页码"), page_size: int = Query(10, description="每页数量"), @@ -26,7 +27,7 @@ async def list_role( return SuccessExtra(data=data, total=total, page=page, page_size=page_size) -@router.get("/get", summary="查看角色") +@router.get("/get", summary="查看角色", response_model=BasicResponse[BaseRole]) async def get_role( role_id: int = Query(..., description="角色ID"), ): @@ -34,7 +35,7 @@ async def get_role( return Success(data=await role_obj.to_dict()) -@router.post("/create", summary="创建角色") +@router.post("/create", summary="创建角色", response_model=BasicResponse[MessageOut]) async def create_role(role_in: RoleCreate): if await role_controller.is_exist(name=role_in.name): raise HTTPException( @@ -45,13 +46,13 @@ async def create_role(role_in: RoleCreate): return Success(msg="Created Successfully") -@router.post("/update", summary="更新角色") +@router.post("/update", summary="更新角色", response_model=BasicResponse[MessageOut]) async def update_role(role_in: RoleUpdate): await role_controller.update(id=role_in.id, obj_in=role_in) return Success(msg="Updated Successfully") -@router.delete("/delete", summary="删除角色") +@router.delete("/delete", summary="删除角色", response_model=BasicResponse[MessageOut]) async def delete_role( role_id: int = Query(..., description="角色ID"), ): @@ -59,14 +60,14 @@ async def delete_role( return Success(msg="Deleted Success") -@router.get("/authorized", summary="查看角色权限") +@router.get("/authorized", summary="查看角色权限", response_model=BasicResponse[BaseRole]) async def get_role_authorized(id: int = Query(..., description="角色ID")): role_obj = await role_controller.get(id=id) data = await role_obj.to_dict(m2m=True) return Success(data=data) -@router.post("/authorized", summary="更新角色权限") +@router.post("/authorized", summary="更新角色权限", response_model=BasicResponse[MessageOut]) async def update_role_authorized(role_in: RoleUpdateMenusApis): role_obj = await role_controller.get(id=role_in.id) await role_controller.update_roles(role=role_obj, menu_ids=role_in.menu_ids, api_infos=role_in.api_infos) diff --git a/app/api/v1/sms/sms.py b/app/api/v1/sms/sms.py new file mode 100644 index 0000000..f4a9267 --- /dev/null +++ b/app/api/v1/sms/sms.py @@ -0,0 +1,197 @@ +from fastapi import APIRouter, HTTPException, status, Depends +from pydantic import BaseModel, Field +from typing import Optional +import time + +from app.services.sms_client import sms_client +from app.services.rate_limiter import PhoneRateLimiter +from app.services.sms_store import store +from app.core.dependency import DependAuth +from app.log import logger +from app.schemas.app_user import AppUserInfoOut, AppUserJWTOut + + +class SendCodeRequest(BaseModel): + phone: str = Field(...) + + +class SendReportRequest(BaseModel): + phone: str = Field(...) + + +class VerifyCodeRequest(BaseModel): + phone: str = Field(...) + code: str = Field(...) + + +class SendResponse(BaseModel): + status: str = Field(..., description="发送状态") + message: str = Field(..., description="说明") + request_id: Optional[str] = Field(None, description="请求ID") + + +class VerifyResponse(BaseModel): + status: str = Field(..., description="验证状态") + message: str = Field(..., description="说明") + + +class SMSLoginResponse(BaseModel): + user: AppUserInfoOut + token: AppUserJWTOut + + +rate_limiter = PhoneRateLimiter(60) +router = APIRouter(tags=["短信服务"]) + + +@router.post("/send-code", response_model=SendResponse, summary="验证码发送") +async def send_code(payload: SendCodeRequest) -> SendResponse: + """发送验证码短信 + + Args: + payload: 请求体,含手机号与验证码 + + Returns: + 发送结果响应 + """ + ok, reason = store.allow_send(payload.phone) + if not ok: + raise HTTPException(status_code=status.HTTP_429_TOO_MANY_REQUESTS, detail=str(reason)) + try: + code = store.generate_code() + store.set_code(payload.phone, code) + res = sms_client.send_code(payload.phone, code) + code = res.get("Code") or res.get("ResponseCode") + rid = res.get("RequestId") or res.get("MessageId") + if code == "OK": + logger.info("sms.send_code success phone={} request_id={}", payload.phone, rid) + return SendResponse(status="OK", message="sent", request_id=str(rid) if rid else None) + msg = res.get("Message") or res.get("ResponseDescription") or "error" + logger.warning("sms.send_code fail phone={} code={} msg={}", payload.phone, code, msg) + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(msg)) + except HTTPException: + raise + except Exception as e: + logger.error("sms.send_code exception err={}", repr(e)) + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="短信服务异常") + + +@router.post("/send-report", response_model=SendResponse, summary="报告通知发送", dependencies=[DependAuth]) +async def send_report(payload: SendReportRequest) -> SendResponse: + """发送报告通知短信 + + Args: + payload: 请求体,含手机号 + + Returns: + 发送结果响应 + """ + ok, reason = store.allow_send(payload.phone) + if not ok: + raise HTTPException(status_code=status.HTTP_429_TOO_MANY_REQUESTS, detail=str(reason)) + try: + res = sms_client.send_report(payload.phone) + code = res.get("Code") or res.get("ResponseCode") + rid = res.get("RequestId") or res.get("MessageId") + if code == "OK": + logger.info("sms.send_report success phone={} request_id={}", payload.phone, rid) + return SendResponse(status="OK", message="sent", request_id=str(rid) if rid else None) + msg = res.get("Message") or res.get("ResponseDescription") or "error" + logger.warning("sms.send_report fail phone={} code={} msg={}", payload.phone, code, msg) + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(msg)) + except HTTPException: + raise + except Exception as e: + logger.error("sms.send_report exception err={}", repr(e)) + raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail="短信服务异常") + + +@router.post("/verify-code", summary="验证码验证", response_model=VerifyResponse) +async def verify_code(payload: VerifyCodeRequest) -> VerifyResponse: + """验证验证码 + + Args: + payload: 请求体,含手机号与验证码 + + Returns: + 验证结果字典 + """ + ok, reason = store.can_verify(payload.phone) + if not ok: + raise HTTPException(status_code=status.HTTP_423_LOCKED, detail=str(reason)) + record = store.get_code(payload.phone) + if not record: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="验证码已过期") + code, expires_at = record + if time.time() > expires_at: + store.clear_code(payload.phone) + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="验证码已过期") + if payload.code != code: + count, locked = store.record_verify_failure(payload.phone) + if locked: + raise HTTPException(status_code=status.HTTP_423_LOCKED, detail="尝试次数过多,已锁定") + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="验证码错误") + store.clear_code(payload.phone) + store.reset_failures(payload.phone) + logger.info("sms.verify_code success phone={}", payload.phone) + return VerifyResponse(status="OK", message="verified") + + +class SMSLoginRequest(BaseModel): + phone_number: str = Field(...) + verification_code: str = Field(...) + device_id: Optional[str] = Field(None) + + +@router.post("/login", summary="短信验证码登录", response_model=SMSLoginResponse) +async def sms_login(payload: SMSLoginRequest) -> SMSLoginResponse: + ok, reason = store.can_verify(payload.phone_number) + if not ok: + raise HTTPException(status_code=status.HTTP_423_LOCKED, detail=str(reason)) + record = store.get_code(payload.phone_number) + if not record: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="验证码过期") + code, expires_at = record + if time.time() > expires_at: + store.clear_code(payload.phone_number) + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="验证码过期") + if payload.verification_code != code: + count, locked = store.record_verify_failure(payload.phone_number) + if locked: + raise HTTPException(status_code=status.HTTP_423_LOCKED, detail="尝试次数过多,已锁定") + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="验证码错误") + + from app.controllers.app_user import app_user_controller + from app.schemas.app_user import AppUserRegisterSchema, AppUserInfoOut, AppUserJWTOut + from app.utils.app_user_jwt import create_app_user_access_token, ACCESS_TOKEN_EXPIRE_MINUTES + + user = await app_user_controller.get_user_by_phone(payload.phone_number) + if user is None: + user = await app_user_controller.register(AppUserRegisterSchema(phone=payload.phone_number)) + await app_user_controller.update_last_login(user.id) + + access_token = create_app_user_access_token(user_id=user.id, phone=user.phone) + store.clear_code(payload.phone_number) + store.reset_failures(payload.phone_number) + logger.info("sms.login success phone={}", payload.phone_number) + + user_info = AppUserInfoOut( + id=user.id, + phone=user.phone, + nickname=user.nickname, + avatar=user.avatar, + company_name=user.company_name, + company_address=user.company_address, + company_contact=user.company_contact, + company_phone=user.company_phone, + company_email=user.company_email, + is_active=user.is_active, + last_login=user.last_login, + created_at=user.created_at, + updated_at=user.updated_at, + ) + token_out = AppUserJWTOut(access_token=access_token, expires_in=ACCESS_TOKEN_EXPIRE_MINUTES) + return SMSLoginResponse(user=user_info, token=token_out) +class VerifyCodeRequest(BaseModel): + phone: str = Field(...) + code: str = Field(...) \ No newline at end of file diff --git a/app/api/v1/transactions/__init__.py b/app/api/v1/transactions/__init__.py new file mode 100644 index 0000000..0ac1e06 --- /dev/null +++ b/app/api/v1/transactions/__init__.py @@ -0,0 +1,3 @@ +from .transactions import transactions_router + +__all__ = ["transactions_router"] \ No newline at end of file diff --git a/app/api/v1/transactions/transactions.py b/app/api/v1/transactions/transactions.py new file mode 100644 index 0000000..0659617 --- /dev/null +++ b/app/api/v1/transactions/transactions.py @@ -0,0 +1,104 @@ +from fastapi import APIRouter, Query, UploadFile, File, HTTPException +from typing import Optional + +from app.schemas.base import Success, SuccessExtra, PageResponse, BasicResponse +from app.schemas.invoice import PaymentReceiptOut +from app.controllers.invoice import invoice_controller +from app.schemas.transactions import SendEmailRequest, SendEmailResponse +from app.services.email_client import email_client +from app.models.invoice import EmailSendLog +from app.settings.config import settings +from app.log.log import logger +import httpx + + +transactions_router = APIRouter(tags=["交易管理"]) + + +@transactions_router.get("/receipts", summary="对公转账记录列表", response_model=PageResponse[PaymentReceiptOut]) +async def list_receipts( + phone: Optional[str] = Query(None), + wechat: Optional[str] = Query(None), + company_name: Optional[str] = Query(None), + tax_number: Optional[str] = Query(None), + status: Optional[str] = Query(None), + ticket_type: Optional[str] = Query(None), + invoice_type: Optional[str] = Query(None), + page: int = Query(1, ge=1), + page_size: int = Query(10, ge=1, le=100), +): + """ + 对公转账记录列表(含提交时间、凭证与关联企业信息) + """ + result = await invoice_controller.list_receipts( + page=page, + page_size=page_size, + phone=phone, + wechat=wechat, + company_name=company_name, + tax_number=tax_number, + status=status, + ticket_type=ticket_type, + invoice_type=invoice_type, + ) + return SuccessExtra( + data=result["items"], + total=result["total"], + page=result["page"], + page_size=result["page_size"], + msg="获取成功", + ) + + +@transactions_router.get("/receipts/{id}", summary="对公转账记录详情", response_model=BasicResponse[PaymentReceiptOut]) +async def get_receipt_detail(id: int): + """ + 对公转账记录详情 + """ + data = await invoice_controller.get_receipt_by_id(id) + return Success(data=data or {}, msg="获取成功" if data else "未找到") + + +@transactions_router.post("/send-email", summary="发送邮件", response_model=BasicResponse[SendEmailResponse]) +async def send_email(data: SendEmailRequest, file: Optional[UploadFile] = File(None)): + if not data.email or "@" not in data.email: + raise HTTPException(status_code=422, detail="邮箱格式不正确") + if not data.body: + raise HTTPException(status_code=422, detail="文案内容不能为空") + + file_bytes = None + file_name = None + if file is not None: + file_bytes = await file.read() + file_name = file.filename + elif data.file_url: + try: + async with httpx.AsyncClient(timeout=10) as client: + r = await client.get(data.file_url) + r.raise_for_status() + file_bytes = r.content + file_name = data.file_url.split("/")[-1] + except Exception as e: + raise HTTPException(status_code=400, detail=f"附件下载失败: {e}") + + logger.info("transactions.email_send_start email={} subject={}", data.email, data.subject or "") + result = email_client.send(data.email, data.subject, data.body, file_bytes, file_name, getattr(file, "content_type", None)) + + body_summary = data.body[:500] + status = result.get("status") + error = result.get("error") + log = await EmailSendLog.create( + email=data.email, + subject=data.subject, + body_summary=body_summary, + file_name=file_name, + file_url=data.file_url, + status=status, + error=error, + ) + if status == "OK": + logger.info("transactions.email_send_ok email={}", data.email) + else: + logger.error("transactions.email_send_fail email={} err={}", data.email, error) + + return Success(data={"status": status, "log_id": log.id, "error": error}, msg="发送成功" if status == "OK" else "发送失败") \ No newline at end of file diff --git a/app/api/v1/upload/upload.py b/app/api/v1/upload/upload.py index 9abd6f2..449c119 100644 --- a/app/api/v1/upload/upload.py +++ b/app/api/v1/upload/upload.py @@ -1,6 +1,6 @@ from fastapi import APIRouter, UploadFile, File from app.controllers.upload import UploadController -from app.schemas.upload import ImageUploadResponse +from app.schemas.upload import ImageUploadResponse, FileUploadResponse router = APIRouter() @@ -11,4 +11,8 @@ async def upload_image(file: UploadFile = File(...)) -> ImageUploadResponse: :param file: 图片文件 :return: 图片URL和文件名 """ - return await UploadController.upload_image(file) \ No newline at end of file + return await UploadController.upload_image(file) + +@router.post("/file", response_model=FileUploadResponse, summary="上传文件") +async def upload_file(file: UploadFile = File(...)) -> FileUploadResponse: + return await UploadController.upload_file(file) \ No newline at end of file diff --git a/app/api/v1/users/users.py b/app/api/v1/users/users.py index d89d303..c41c784 100644 --- a/app/api/v1/users/users.py +++ b/app/api/v1/users/users.py @@ -5,7 +5,8 @@ from tortoise.expressions import Q from app.controllers.dept import dept_controller from app.controllers.user import user_controller -from app.schemas.base import Fail, Success, SuccessExtra +from app.schemas.base import Fail, Success, SuccessExtra, BasicResponse, PageResponse, MessageOut +from app.schemas.users import BaseUser from app.schemas.users import * logger = logging.getLogger(__name__) @@ -13,7 +14,7 @@ logger = logging.getLogger(__name__) router = APIRouter() -@router.get("/list", summary="查看用户列表") +@router.get("/list", summary="查看用户列表", response_model=PageResponse[BaseUser]) async def list_user( page: int = Query(1, description="页码"), page_size: int = Query(10, description="每页数量"), @@ -37,7 +38,7 @@ async def list_user( return SuccessExtra(data=data, total=total, page=page, page_size=page_size) -@router.get("/get", summary="查看用户") +@router.get("/get", summary="查看用户", response_model=BasicResponse[BaseUser]) async def get_user( user_id: int = Query(..., description="用户ID"), ): @@ -46,7 +47,7 @@ async def get_user( return Success(data=user_dict) -@router.post("/create", summary="创建用户") +@router.post("/create", summary="创建用户", response_model=BasicResponse[MessageOut]) async def create_user( user_in: UserCreate, ): @@ -58,7 +59,7 @@ async def create_user( return Success(msg="Created Successfully") -@router.post("/update", summary="更新用户") +@router.post("/update", summary="更新用户", response_model=BasicResponse[MessageOut]) async def update_user( user_in: UserUpdate, ): @@ -67,7 +68,7 @@ async def update_user( return Success(msg="Updated Successfully") -@router.delete("/delete", summary="删除用户") +@router.delete("/delete", summary="删除用户", response_model=BasicResponse[MessageOut]) async def delete_user( user_id: int = Query(..., description="用户ID"), ): @@ -75,7 +76,7 @@ async def delete_user( return Success(msg="Deleted Successfully") -@router.post("/reset_password", summary="重置密码") +@router.post("/reset_password", summary="重置密码", response_model=BasicResponse[MessageOut]) async def reset_password(user_id: int = Body(..., description="用户ID", embed=True)): await user_controller.reset_password(user_id) return Success(msg="密码已重置为123456") diff --git a/app/api/v1/valuations/valuations.py b/app/api/v1/valuations/valuations.py index 140be3e..49c46ce 100644 --- a/app/api/v1/valuations/valuations.py +++ b/app/api/v1/valuations/valuations.py @@ -1,5 +1,5 @@ from fastapi import APIRouter, HTTPException, Query, Depends -from typing import Optional +from typing import Optional, List from app.controllers.valuation import valuation_controller from app.schemas.valuation import ( @@ -9,15 +9,16 @@ from app.schemas.valuation import ( ValuationAssessmentList, ValuationAssessmentQuery, ValuationApprovalRequest, - ValuationAdminNotesUpdate + ValuationAdminNotesUpdate, + ValuationCalculationStepOut ) -from app.schemas.base import Success, SuccessExtra +from app.schemas.base import Success, SuccessExtra, BasicResponse, PageResponse from app.core.ctx import CTX_USER_ID valuations_router = APIRouter(tags=["估值评估"]) -@valuations_router.post("/", summary="创建估值评估") +@valuations_router.post("/", summary="创建估值评估", response_model=BasicResponse[ValuationAssessmentOut]) async def create_valuation(data: ValuationAssessmentCreate): """创建新的估值评估记录""" try: @@ -25,37 +26,51 @@ async def create_valuation(data: ValuationAssessmentCreate): user_id = CTX_USER_ID.get() print(user_id) result = await valuation_controller.create(data, user_id) - return Success(data=result, msg="创建成功") + import json + return Success(data=json.loads(result.model_dump_json()), msg="创建成功") except Exception as e: raise HTTPException(status_code=400, detail=f"创建失败: {str(e)}") -@valuations_router.get("/statistics/overview", summary="获取统计信息") +@valuations_router.get("/statistics/overview", summary="获取统计信息", response_model=BasicResponse[dict]) async def get_statistics(): """获取估值评估统计信息""" result = await valuation_controller.get_statistics() return Success(data=result, msg="获取统计信息成功") -@valuations_router.get("/{valuation_id}", summary="获取估值评估详情") +@valuations_router.get("/{valuation_id}", summary="获取估值评估详情", response_model=BasicResponse[ValuationAssessmentOut]) async def get_valuation(valuation_id: int): """根据ID获取估值评估详情""" result = await valuation_controller.get_by_id(valuation_id) if not result: raise HTTPException(status_code=404, detail="估值评估记录不存在") - return Success(data=result, msg="获取成功") + import json + return Success(data=json.loads(result.model_dump_json()), msg="获取成功") -@valuations_router.put("/{valuation_id}", summary="更新估值评估") +@valuations_router.get("/{valuation_id}/steps", summary="获取估值计算步骤", response_model=BasicResponse[List[ValuationCalculationStepOut]]) +async def get_valuation_steps(valuation_id: int): + """根据估值ID获取所有计算步骤""" + steps = await valuation_controller.get_calculation_steps(valuation_id) + if not steps: + raise HTTPException(status_code=404, detail="未找到该估值的计算步骤") + import json + steps_out = [json.loads(step.model_dump_json()) for step in steps] + return Success(data=steps_out, msg="获取计算步骤成功") + + +@valuations_router.put("/{valuation_id}", summary="更新估值评估", response_model=BasicResponse[ValuationAssessmentOut]) async def update_valuation(valuation_id: int, data: ValuationAssessmentUpdate): """更新估值评估记录""" result = await valuation_controller.update(valuation_id, data) if not result: raise HTTPException(status_code=404, detail="估值评估记录不存在") - return Success(data=result, msg="更新成功") + import json + return Success(data=json.loads(result.model_dump_json()), msg="更新成功") -@valuations_router.delete("/{valuation_id}", summary="删除估值评估") +@valuations_router.delete("/{valuation_id}", summary="删除估值评估", response_model=BasicResponse[dict]) async def delete_valuation(valuation_id: int): """软删除估值评估记录""" result = await valuation_controller.delete(valuation_id) @@ -64,7 +79,7 @@ async def delete_valuation(valuation_id: int): return Success(data={"deleted": True}, msg="删除成功") -@valuations_router.get("/", summary="获取估值评估列表") +@valuations_router.get("/", summary="获取估值评估列表", response_model=PageResponse[ValuationAssessmentOut]) async def get_valuations( asset_name: Optional[str] = Query(None, description="资产名称"), institution: Optional[str] = Query(None, description="所属机构"), @@ -87,8 +102,10 @@ async def get_valuations( size=size ) result = await valuation_controller.get_list(query) + import json + items = [json.loads(item.model_dump_json()) for item in result.items] return SuccessExtra( - data=result.items, + data=items, total=result.total, page=result.page, page_size=result.size, @@ -97,7 +114,7 @@ async def get_valuations( ) -@valuations_router.get("/search/keyword", summary="搜索估值评估") +@valuations_router.get("/search/keyword", summary="搜索估值评估", response_model=PageResponse[ValuationAssessmentOut]) async def search_valuations( keyword: str = Query(..., description="搜索关键词"), page: int = Query(1, ge=1, description="页码"), @@ -105,8 +122,10 @@ async def search_valuations( ): """根据关键词搜索估值评估记录""" result = await valuation_controller.search(keyword, page, size) + import json + items = [json.loads(item.model_dump_json()) for item in result.items] return SuccessExtra( - data=result.items, + data=items, total=result.total, page=result.page, page_size=result.size, @@ -116,7 +135,7 @@ async def search_valuations( # 批量操作接口 -@valuations_router.post("/batch/delete", summary="批量删除估值评估") +@valuations_router.post("/batch/delete", summary="批量删除估值评估", response_model=BasicResponse[dict]) async def batch_delete_valuations(valuation_ids: list[int]): """批量软删除估值评估记录""" success_count = 0 @@ -140,7 +159,7 @@ async def batch_delete_valuations(valuation_ids: list[int]): # 导出接口 -@valuations_router.get("/export/excel", summary="导出估值评估数据") +@valuations_router.get("/export/excel", summary="导出估值评估数据", response_model=BasicResponse[dict]) async def export_valuations( asset_name: Optional[str] = Query(None, description="资产名称"), institution: Optional[str] = Query(None, description="所属机构"), @@ -154,28 +173,31 @@ async def export_valuations( # 审核管理接口 -@valuations_router.post("/{valuation_id}/approve", summary="审核通过估值评估") +@valuations_router.post("/{valuation_id}/approve", summary="审核通过估值评估", response_model=BasicResponse[ValuationAssessmentOut]) async def approve_valuation(valuation_id: int, data: ValuationApprovalRequest): """审核通过估值评估""" result = await valuation_controller.approve_valuation(valuation_id, data.admin_notes) if not result: raise HTTPException(status_code=404, detail="估值评估记录不存在") - return Success(data=result, msg="审核通过成功") + import json + return Success(data=json.loads(result.model_dump_json()), msg="审核通过成功") -@valuations_router.post("/{valuation_id}/reject", summary="审核拒绝估值评估") +@valuations_router.post("/{valuation_id}/reject", summary="审核拒绝估值评估", response_model=BasicResponse[ValuationAssessmentOut]) async def reject_valuation(valuation_id: int, data: ValuationApprovalRequest): """审核拒绝估值评估""" result = await valuation_controller.reject_valuation(valuation_id, data.admin_notes) if not result: raise HTTPException(status_code=404, detail="估值评估记录不存在") - return Success(data=result, msg="审核拒绝成功") + import json + return Success(data=json.loads(result.model_dump_json()), msg="审核拒绝成功") -@valuations_router.put("/{valuation_id}/admin-notes", summary="更新管理员备注") +@valuations_router.put("/{valuation_id}/admin-notes", summary="更新管理员备注", response_model=BasicResponse[ValuationAssessmentOut]) async def update_admin_notes(valuation_id: int, data: ValuationAdminNotesUpdate): """更新管理员备注""" result = await valuation_controller.update_admin_notes(valuation_id, data.admin_notes) if not result: raise HTTPException(status_code=404, detail="估值评估记录不存在") - return Success(data=result, msg="管理员备注更新成功") \ No newline at end of file + import json + return Success(data=json.loads(result.model_dump_json()), msg="管理员备注更新成功") \ No newline at end of file diff --git a/app/controllers/app_user.py b/app/controllers/app_user.py index 5ffd1ff..563b45c 100644 --- a/app/controllers/app_user.py +++ b/app/controllers/app_user.py @@ -1,4 +1,5 @@ from app.models.user import AppUser +from app.models.user import AppUserQuotaLog from app.schemas.app_user import AppUserRegisterSchema, AppUserLoginSchema, AppUserUpdateSchema from app.utils.password import get_password_hash, verify_password from app.core.crud import CRUDBase @@ -90,6 +91,29 @@ class AppUserController(CRUDBase[AppUser, AppUserRegisterSchema, AppUserUpdateSc await user.save() return user + + async def update_quota(self, operator_id: int, operator_name: str, user_id: int, target_count: Optional[int] = None, delta: Optional[int] = None, op_type: str = "调整", remark: Optional[str] = None) -> Optional[AppUser]: + user = await self.model.filter(id=user_id).first() + if not user: + return None + before = int(getattr(user, "remaining_quota", 0) or 0) + after = before + if target_count is not None: + after = max(0, int(target_count)) + elif delta is not None: + after = max(0, before + int(delta)) + user.remaining_quota = after + await user.save() + await AppUserQuotaLog.create( + app_user_id=user_id, + operator_id=operator_id, + operator_name=operator_name, + before_count=before, + after_count=after, + op_type=op_type, + remark=remark, + ) + return user async def change_password(self, user_id: int, old_password: str, new_password: str) -> bool: """ diff --git a/app/controllers/invoice.py b/app/controllers/invoice.py new file mode 100644 index 0000000..fd8fd49 --- /dev/null +++ b/app/controllers/invoice.py @@ -0,0 +1,286 @@ +from typing import Optional, List +from tortoise.queryset import QuerySet + +from app.core.crud import CRUDBase +from app.models.invoice import Invoice, InvoiceHeader, PaymentReceipt +from app.schemas.invoice import ( + InvoiceCreate, + InvoiceUpdate, + InvoiceOut, + InvoiceList, + InvoiceHeaderCreate, + InvoiceHeaderOut, + UpdateStatus, + UpdateType, + PaymentReceiptCreate, + PaymentReceiptOut, +) + + +class InvoiceController(CRUDBase[Invoice, InvoiceCreate, InvoiceUpdate]): + """发票控制器""" + + def __init__(self): + super().__init__(model=Invoice) + + async def create_header(self, user_id: Optional[int], data: InvoiceHeaderCreate) -> InvoiceHeaderOut: + """ + 创建发票抬头 + 参数: + user_id: 关联的 AppUser ID(可选) + data: 发票抬头创建数据 + 返回: + InvoiceHeaderOut: 抬头输出对象 + """ + header = await InvoiceHeader.create(app_user_id=user_id, **data.model_dump()) + return InvoiceHeaderOut.model_validate(header) + + async def get_headers(self, user_id: Optional[int] = None) -> List[InvoiceHeaderOut]: + """ + 获取发票抬头列表 + 参数: + user_id: 可筛选 AppUser 的抬头 + 返回: + List[InvoiceHeaderOut]: 抬头列表 + """ + qs = InvoiceHeader.all() + if user_id is not None: + qs = qs.filter(app_user_id=user_id) + headers = await qs.order_by("-created_at") + return [InvoiceHeaderOut.model_validate(h) for h in headers] + + async def get_header_by_id(self, id_: int) -> Optional[InvoiceHeaderOut]: + """ + 根据ID获取抬头 + 参数: + id_: 抬头ID + 返回: + InvoiceHeaderOut 或 None + """ + header = await InvoiceHeader.filter(id=id_).first() + return InvoiceHeaderOut.model_validate(header) if header else None + + async def list(self, page: int = 1, page_size: int = 10, **filters) -> InvoiceList: + """ + 获取发票列表(支持筛选与分页) + 参数: + page: 页码 + page_size: 每页数量 + **filters: phone、company_name、tax_number、status、ticket_type、invoice_type、时间范围等 + 返回: + InvoiceList: 分页结果 + """ + qs: QuerySet = self.model.all() + if filters.get("phone"): + qs = qs.filter(phone__icontains=filters["phone"]) + if filters.get("company_name"): + qs = qs.filter(company_name__icontains=filters["company_name"]) + if filters.get("tax_number"): + qs = qs.filter(tax_number__icontains=filters["tax_number"]) + if filters.get("status"): + qs = qs.filter(status=filters["status"]) + if filters.get("ticket_type"): + qs = qs.filter(ticket_type=filters["ticket_type"]) + if filters.get("invoice_type"): + qs = qs.filter(invoice_type=filters["invoice_type"]) + + total = await qs.count() + rows = await qs.order_by("-created_at").offset((page - 1) * page_size).limit(page_size) + + items = [ + InvoiceOut( + id=row.id, + created_at=row.created_at.isoformat() if row.created_at else "", + ticket_type=row.ticket_type, + invoice_type=row.invoice_type, + phone=row.phone, + email=row.email, + company_name=row.company_name, + tax_number=row.tax_number, + register_address=row.register_address, + register_phone=row.register_phone, + bank_name=row.bank_name, + bank_account=row.bank_account, + status=row.status, + app_user_id=row.app_user_id, + header_id=row.header_id, + wechat=row.wechat, + ) + for row in rows + ] + + return InvoiceList(items=items, total=total, page=page, page_size=page_size) + + async def update_status(self, data: UpdateStatus) -> Optional[InvoiceOut]: + """ + 更新发票状态 + 参数: + data: 包含 id 与目标状态 + 返回: + 更新后的发票输出或 None + """ + inv = await self.model.filter(id=data.id).first() + if not inv: + return None + inv.status = data.status + await inv.save() + return await self.get_out(inv.id) + + async def update_type(self, id_: int, data: UpdateType) -> Optional[InvoiceOut]: + """ + 更新发票类型(电子/纸质、专票/普票) + 参数: + id_: 发票ID + data: 类型更新数据 + 返回: + 更新后的发票输出或 None + """ + inv = await self.model.filter(id=id_).first() + if not inv: + return None + inv.ticket_type = data.ticket_type + inv.invoice_type = data.invoice_type + await inv.save() + return await self.get_out(inv.id) + + async def create_receipt(self, invoice_id: int, data: PaymentReceiptCreate) -> PaymentReceiptOut: + """ + 上传付款凭证 + 参数: + invoice_id: 发票ID + data: 凭证创建数据 + 返回: + PaymentReceiptOut + """ + receipt = await PaymentReceipt.create(invoice_id=invoice_id, **data.model_dump()) + return PaymentReceiptOut( + id=receipt.id, + url=receipt.url, + note=receipt.note, + verified=receipt.verified, + created_at=receipt.created_at.isoformat() if receipt.created_at else "", + ) + + async def list_receipts(self, page: int = 1, page_size: int = 10, **filters) -> dict: + """ + 对公转账记录列表 + 参数: + page: 页码 + page_size: 每页数量 + **filters: 提交时间范围、手机号、微信号、公司名称/税号、状态、开票类型等 + 返回: + dict: { items, total, page, page_size } + """ + qs = PaymentReceipt.all().prefetch_related("invoice") + # 通过关联发票进行筛选 + if filters.get("phone"): + qs = qs.filter(invoice__phone__icontains=filters["phone"]) + if filters.get("wechat"): + qs = qs.filter(invoice__wechat__icontains=filters["wechat"]) + if filters.get("company_name"): + qs = qs.filter(invoice__company_name__icontains=filters["company_name"]) + if filters.get("tax_number"): + qs = qs.filter(invoice__tax_number__icontains=filters["tax_number"]) + if filters.get("status"): + qs = qs.filter(invoice__status=filters["status"]) + if filters.get("ticket_type"): + qs = qs.filter(invoice__ticket_type=filters["ticket_type"]) + if filters.get("invoice_type"): + qs = qs.filter(invoice__invoice_type=filters["invoice_type"]) + + total = await qs.count() + rows = await qs.order_by("-created_at").offset((page - 1) * page_size).limit(page_size) + + items = [] + for r in rows: + inv = await r.invoice + items.append({ + "submitted_at": r.created_at.isoformat() if r.created_at else "", + "receipt": { + "id": r.id, + "url": r.url, + "note": r.note, + "verified": r.verified, + }, + "phone": inv.phone, + "wechat": inv.wechat, + "company_name": inv.company_name, + "tax_number": inv.tax_number, + "register_address": inv.register_address, + "register_phone": inv.register_phone, + "bank_name": inv.bank_name, + "bank_account": inv.bank_account, + "email": inv.email, + "ticket_type": inv.ticket_type, + "invoice_type": inv.invoice_type, + "status": inv.status, + }) + + return {"items": items, "total": total, "page": page, "page_size": page_size} + + async def get_receipt_by_id(self, id_: int) -> Optional[dict]: + """ + 对公转账记录详情 + 参数: + id_: 付款凭证ID + 返回: + dict 或 None + """ + r = await PaymentReceipt.filter(id=id_).first() + if not r: + return None + inv = await r.invoice + return { + "submitted_at": r.created_at.isoformat() if r.created_at else "", + "receipt": { + "id": r.id, + "url": r.url, + "note": r.note, + "verified": r.verified, + }, + "phone": inv.phone, + "wechat": inv.wechat, + "company_name": inv.company_name, + "tax_number": inv.tax_number, + "register_address": inv.register_address, + "register_phone": inv.register_phone, + "bank_name": inv.bank_name, + "bank_account": inv.bank_account, + "email": inv.email, + "ticket_type": inv.ticket_type, + "invoice_type": inv.invoice_type, + "status": inv.status, + } + + async def get_out(self, id_: int) -> Optional[InvoiceOut]: + """ + 根据ID返回发票输出对象 + 参数: + id_: 发票ID + 返回: + InvoiceOut 或 None + """ + inv = await self.model.filter(id=id_).first() + if not inv: + return None + return InvoiceOut( + id=inv.id, + created_at=inv.created_at.isoformat() if inv.created_at else "", + ticket_type=inv.ticket_type, + invoice_type=inv.invoice_type, + phone=inv.phone, + email=inv.email, + company_name=inv.company_name, + tax_number=inv.tax_number, + register_address=inv.register_address, + register_phone=inv.register_phone, + bank_name=inv.bank_name, + bank_account=inv.bank_account, + status=inv.status, + app_user_id=inv.app_user_id, + header_id=inv.header_id, + wechat=inv.wechat, + ) + + +invoice_controller = InvoiceController() \ No newline at end of file diff --git a/app/controllers/upload.py b/app/controllers/upload.py index ddaed8a..3182702 100644 --- a/app/controllers/upload.py +++ b/app/controllers/upload.py @@ -2,7 +2,7 @@ import os from pathlib import Path from typing import List from fastapi import UploadFile -from app.schemas.upload import ImageUploadResponse +from app.schemas.upload import ImageUploadResponse, FileUploadResponse from app.settings.config import settings class UploadController: @@ -49,4 +49,42 @@ class UploadController: return ImageUploadResponse( url=f"{settings.BASE_URL}/static/images/{filename}", filename=filename + ) + + @staticmethod + async def upload_file(file: UploadFile) -> FileUploadResponse: + allowed = { + "application/pdf", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/msword", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.ms-excel", + "application/zip", + "application/x-zip-compressed", + } + if file.content_type not in allowed: + raise ValueError("不支持的文件类型") + + base_dir = Path(__file__).resolve().parent.parent + upload_dir = base_dir / "static" / "files" + if not upload_dir.exists(): + upload_dir.mkdir(parents=True, exist_ok=True) + + filename = file.filename + file_path = upload_dir / filename + counter = 1 + while file_path.exists(): + name, ext = os.path.splitext(filename) + filename = f"{name}_{counter}{ext}" + file_path = upload_dir / filename + counter += 1 + + content = await file.read() + with open(file_path, "wb") as f: + f.write(content) + + return FileUploadResponse( + url=f"{settings.BASE_URL}/static/files/{filename}", + filename=filename, + content_type=file.content_type, ) \ No newline at end of file diff --git a/app/controllers/user_valuation.py b/app/controllers/user_valuation.py index 38d7f88..f1dfc18 100644 --- a/app/controllers/user_valuation.py +++ b/app/controllers/user_valuation.py @@ -138,6 +138,8 @@ class UserValuationController: historical_evidence=valuation.historical_evidence, patent_certificates=valuation.patent_certificates, pattern_images=valuation.pattern_images, + report_url=valuation.report_url, + certificate_url=valuation.certificate_url, application_maturity=valuation.application_maturity, implementation_stage=valuation.implementation_stage, application_coverage=valuation.application_coverage, @@ -159,6 +161,8 @@ class UserValuationController: price_fluctuation=valuation.price_fluctuation, price_range=valuation.price_range, market_price=valuation.market_price, + credit_code_or_id=valuation.credit_code_or_id, + biz_intro=valuation.biz_intro, infringement_record=valuation.infringement_record, patent_count=valuation.patent_count, esg_value=valuation.esg_value, diff --git a/app/controllers/valuation.py b/app/controllers/valuation.py index 8e343e0..a2e95ed 100644 --- a/app/controllers/valuation.py +++ b/app/controllers/valuation.py @@ -3,13 +3,15 @@ from tortoise.expressions import Q from tortoise.queryset import QuerySet from tortoise.functions import Count -from app.models.valuation import ValuationAssessment +from app.models.valuation import ValuationAssessment, ValuationCalculationStep from app.schemas.valuation import ( ValuationAssessmentCreate, ValuationAssessmentUpdate, ValuationAssessmentQuery, ValuationAssessmentOut, - ValuationAssessmentList + ValuationAssessmentList, + ValuationCalculationStepCreate, + ValuationCalculationStepOut ) @@ -17,6 +19,56 @@ class ValuationController: """估值评估控制器""" model = ValuationAssessment + step_model = ValuationCalculationStep + + async def create_calculation_step(self, data: ValuationCalculationStepCreate) -> ValuationCalculationStepOut: + """ + 创建估值计算步骤 + + Args: + data (ValuationCalculationStepCreate): 估值计算步骤数据 + + Returns: + ValuationCalculationStepOut: 创建的估值计算步骤 + """ + step = await self.step_model.create(**data.model_dump()) + logger.info( + "calcstep.create valuation_id={} order={} name={}", + data.valuation_id, + data.step_order, + data.step_name, + ) + return ValuationCalculationStepOut.model_validate(step) + + async def update_calculation_step(self, step_id: int, update: dict) -> ValuationCalculationStepOut: + step = await self.step_model.filter(id=step_id).first() + if not step: + raise ValueError(f"calculation_step not found: {step_id}") + await step.update_from_dict(update).save() + logger.info( + "calcstep.update id={} fields={}", + step_id, + list(update.keys()), + ) + return ValuationCalculationStepOut.model_validate(step) + + async def get_calculation_steps(self, valuation_id: int) -> List[ValuationCalculationStepOut]: + """ + 根据估值ID获取所有相关的计算步骤。 + + 此方法从数据库中检索与特定估值ID关联的所有计算步骤记录, + 并按创建时间升序排序,确保步骤的顺序正确。 + + Args: + valuation_id (int): 估值的唯一标识符。 + + Returns: + List[ValuationCalculationStepOut]: 一个包含所有相关计算步骤的列表, + 如果找不到任何步骤,则返回空列表。 + """ + steps = await self.step_model.filter(valuation_id=valuation_id).order_by('created_at') + logger.info("calcstep.list valuation_id={} count={}", valuation_id, len(steps)) + return [ValuationCalculationStepOut.model_validate(step) for step in steps] async def create(self, data: ValuationAssessmentCreate, user_id: int) -> ValuationAssessmentOut: """创建估值评估""" @@ -192,4 +244,5 @@ class ValuationController: # 创建控制器实例 -valuation_controller = ValuationController() \ No newline at end of file +valuation_controller = ValuationController() +from app.log import logger \ No newline at end of file diff --git a/app/models/__init__.py b/app/models/__init__.py index 7ace5e6..6a76223 100644 --- a/app/models/__init__.py +++ b/app/models/__init__.py @@ -5,4 +5,5 @@ from .index import * from .industry import * from .policy import * from .user import * -from .valuation import * \ No newline at end of file +from .valuation import * +from .invoice import * \ No newline at end of file diff --git a/app/models/invoice.py b/app/models/invoice.py new file mode 100644 index 0000000..008227c --- /dev/null +++ b/app/models/invoice.py @@ -0,0 +1,64 @@ +from tortoise import fields + +from .base import BaseModel, TimestampMixin + + +class InvoiceHeader(BaseModel, TimestampMixin): + app_user_id = fields.IntField(null=True, description="App用户ID", index=True) + company_name = fields.CharField(max_length=128, description="公司名称", index=True) + tax_number = fields.CharField(max_length=32, description="公司税号", index=True) + register_address = fields.CharField(max_length=256, description="注册地址") + register_phone = fields.CharField(max_length=32, description="注册电话") + bank_name = fields.CharField(max_length=128, description="开户银行") + bank_account = fields.CharField(max_length=64, description="银行账号") + email = fields.CharField(max_length=128, description="接收邮箱") + + class Meta: + table = "invoice_header" + table_description = "发票抬头" + + +class Invoice(BaseModel, TimestampMixin): + ticket_type = fields.CharField(max_length=16, description="票据类型: electronic|paper", index=True) + invoice_type = fields.CharField(max_length=16, description="发票类型: special|normal", index=True) + phone = fields.CharField(max_length=20, description="手机号", index=True) + email = fields.CharField(max_length=128, description="接收邮箱") + company_name = fields.CharField(max_length=128, description="公司名称", index=True) + tax_number = fields.CharField(max_length=32, description="公司税号", index=True) + register_address = fields.CharField(max_length=256, description="注册地址") + register_phone = fields.CharField(max_length=32, description="注册电话") + bank_name = fields.CharField(max_length=128, description="开户银行") + bank_account = fields.CharField(max_length=64, description="银行账号") + status = fields.CharField(max_length=16, description="状态: pending|invoiced|rejected|refunded", index=True) + app_user_id = fields.IntField(null=True, description="App用户ID", index=True) + header = fields.ForeignKeyField("models.InvoiceHeader", related_name="invoices", null=True, description="抬头关联") + wechat = fields.CharField(max_length=64, null=True, description="微信号", index=True) + + class Meta: + table = "invoice" + table_description = "发票记录" + + +class PaymentReceipt(BaseModel, TimestampMixin): + invoice = fields.ForeignKeyField("models.Invoice", related_name="receipts", description="关联发票") + url = fields.CharField(max_length=512, description="付款凭证图片地址") + note = fields.CharField(max_length=256, null=True, description="备注") + verified = fields.BooleanField(default=False, description="是否已核验") + + class Meta: + table = "payment_receipt" + table_description = "对公转账付款凭证" + + +class EmailSendLog(BaseModel, TimestampMixin): + email = fields.CharField(max_length=255, description="目标邮箱", index=True) + subject = fields.CharField(max_length=255, null=True, description="主题") + body_summary = fields.CharField(max_length=512, null=True, description="正文摘要") + file_name = fields.CharField(max_length=255, null=True, description="附件文件名") + file_url = fields.CharField(max_length=512, null=True, description="附件URL") + status = fields.CharField(max_length=16, description="状态: OK|FAIL", index=True) + error = fields.TextField(null=True, description="错误信息") + + class Meta: + table = "email_send_log" + table_description = "邮件发送日志" \ No newline at end of file diff --git a/app/models/user.py b/app/models/user.py index bee95ad..b3f91cd 100644 --- a/app/models/user.py +++ b/app/models/user.py @@ -19,7 +19,22 @@ class AppUser(BaseModel, TimestampMixin): company_email = fields.CharField(max_length=100, null=True, description="公司邮箱") is_active = fields.BooleanField(default=True, description="是否激活", index=True) last_login = fields.DatetimeField(null=True, description="最后登录时间", index=True) + remaining_quota = fields.IntField(default=0, description="剩余估值次数", index=True) class Meta: table = "app_user" - table_description = "用户表" \ No newline at end of file + table_description = "用户表" + + +class AppUserQuotaLog(BaseModel, TimestampMixin): + app_user_id = fields.IntField(description="App用户ID", index=True) + operator_id = fields.IntField(description="操作人ID", index=True) + operator_name = fields.CharField(max_length=64, description="操作人") + before_count = fields.IntField(description="变更前次数") + after_count = fields.IntField(description="变更后次数") + op_type = fields.CharField(max_length=32, description="操作类型") + remark = fields.CharField(max_length=256, null=True, description="备注") + + class Meta: + table = "app_user_quota_log" + table_description = "App用户估值次数操作日志" \ No newline at end of file diff --git a/app/models/valuation.py b/app/models/valuation.py index f27c979..6c3f886 100644 --- a/app/models/valuation.py +++ b/app/models/valuation.py @@ -23,18 +23,26 @@ class ValuationAssessment(Model): inheritor_ages = fields.JSONField(null=True, description="传承人年龄列表") inheritor_age_count = fields.JSONField(null=True, description="非遗传承人年龄水平及数量") inheritor_certificates = fields.JSONField(null=True, description="非遗传承人等级证书") + heritage_level = fields.CharField(max_length=50, null=True, description="非遗等级") heritage_asset_level = fields.CharField(max_length=50, null=True, description="非遗资产等级") patent_application_no = fields.CharField(max_length=100, null=True, description="非遗资产所用专利的申请号") patent_remaining_years = fields.CharField(max_length=50, null=True, description="专利剩余年限") historical_evidence = fields.JSONField(null=True, description="非遗资产历史证明证据及数量") patent_certificates = fields.JSONField(null=True, description="非遗资产所用专利的证书") pattern_images = fields.JSONField(null=True, description="非遗纹样图片") + report_url = fields.CharField(max_length=512, null=True, description="管理员上传的评估报告URL") + certificate_url = fields.CharField(max_length=512, null=True, description="管理员上传的证书URL") # 非遗应用与推广 implementation_stage = fields.CharField(max_length=100, null=True, description="非遗资产应用成熟度") + application_maturity = fields.CharField(max_length=100, null=True, description="非遗资产应用成熟度") application_coverage = fields.CharField(max_length=100, null=True, description="非遗资产应用覆盖范围") + coverage_area = fields.CharField(max_length=100, null=True, description="应用覆盖范围") cooperation_depth = fields.CharField(max_length=100, null=True, description="非遗资产跨界合作深度") + collaboration_type = fields.CharField(max_length=100, null=True, description="跨界合作类型") offline_activities = fields.CharField(max_length=50, null=True, description="近12个月线下相关宣讲活动次数") + offline_teaching_count = fields.IntField(null=True, description="近12个月线下相关演讲活动次数") + online_accounts = fields.JSONField(null=True, description="线上相关宣传账号信息") platform_accounts = fields.JSONField(null=True, description="线上相关宣传账号信息") # 非遗资产衍生商品信息 @@ -44,10 +52,13 @@ class ValuationAssessment(Model): scarcity_level = fields.CharField(max_length=50, null=True, description="稀缺等级") last_market_activity = fields.CharField(max_length=100, null=True, description="该商品最近一次市场活动时间") market_activity_time = fields.CharField(max_length=100, null=True, description="市场活动的时间") + monthly_transaction = fields.CharField(max_length=50, null=True, description="月交易额") monthly_transaction_amount = fields.CharField(max_length=50, null=True, description="月交易额") price_fluctuation = fields.JSONField(null=True, description="该商品近30天价格波动区间") price_range = fields.JSONField(null=True, description="资产商品的价格波动率") market_price = fields.FloatField(null=True, description="市场价格(单位:万元)") + credit_code_or_id = fields.CharField(max_length=64, null=True, description="统一社会信用代码或身份证号") + biz_intro = fields.TextField(null=True, description="业务/传承介绍") # 内置API计算字段 infringement_record = fields.CharField(max_length=100, null=True, description="侵权记录") @@ -82,4 +93,26 @@ class ValuationAssessment(Model): table_description = "估值评估表" def __str__(self): - return f"估值评估-{self.asset_name}" \ No newline at end of file + return f"估值评估-{self.asset_name}" + + +class ValuationCalculationStep(Model): + """估值计算步骤模型""" + id = fields.IntField(pk=True, description="主键ID") + valuation = fields.ForeignKeyField("models.ValuationAssessment", related_name="calculation_steps", description="关联的估值评估") + step_order = fields.DecimalField(max_digits=8, decimal_places=3, description="步骤顺序") + step_name = fields.CharField(max_length=255, description="步骤名称") + step_description = fields.TextField(null=True, description="步骤描述") + input_params = fields.JSONField(null=True, description="输入参数") + output_result = fields.JSONField(null=True, description="输出结果") + status = fields.CharField(max_length=20, default="SUCCESS", description="步骤状态: SUCCESS, FAILED") + error_message = fields.TextField(null=True, description="错误信息") + created_at = fields.DatetimeField(auto_now_add=True, description="创建时间") + + class Meta: + table = "valuation_calculation_steps" + table_description = "估值计算步骤表" + ordering = ["step_order"] + + def __str__(self): + return f"估值ID {self.valuation_id} - 步骤 {self.step_order}: {self.step_name}" \ No newline at end of file diff --git a/app/schemas/app_user.py b/app/schemas/app_user.py index eb0cae0..ac6417c 100644 --- a/app/schemas/app_user.py +++ b/app/schemas/app_user.py @@ -50,6 +50,7 @@ class AppUserInfoOut(BaseModel): last_login: Optional[datetime] = None created_at: datetime updated_at: datetime + remaining_quota: int class AppUserUpdateSchema(BaseModel): @@ -66,4 +67,49 @@ class AppUserUpdateSchema(BaseModel): class AppUserChangePasswordSchema(BaseModel): """AppUser修改密码Schema""" old_password: str = Field(..., description="原密码") - new_password: str = Field(..., description="新密码") \ No newline at end of file + new_password: str = Field(..., description="新密码") + + +class AppUserDashboardOut(BaseModel): + """AppUser首页摘要输出""" + remaining_quota: int + latest_valuation: Optional[dict] = None + pending_invoices: int + + +class AppUserQuotaOut(BaseModel): + """AppUser剩余估值次数输出""" + remaining_count: int + user_type: Optional[str] = None + + +class AppUserQuotaUpdateSchema(BaseModel): + user_id: int = Field(..., description="用户ID") + target_count: Optional[int] = Field(None, description="目标次数") + delta: Optional[int] = Field(None, description="增减次数") + op_type: str = Field(..., description="操作类型") + remark: Optional[str] = Field(None, description="备注") + + +class AppUserQuotaLogOut(BaseModel): + id: int + app_user_id: int + operator_id: int + operator_name: str + before_count: int + after_count: int + op_type: str + remark: Optional[str] = None + + +class AppUserRegisterOut(BaseModel): + """App 用户注册结果""" + user_id: int = Field(..., description="用户ID") + phone: str = Field(..., description="手机号") + default_password: str = Field(..., description="默认密码(手机号后六位)") + + +class TokenValidateOut(BaseModel): + """Token 校验结果""" + user_id: int = Field(..., description="用户ID") + phone: str = Field(..., description="手机号") \ No newline at end of file diff --git a/app/schemas/base.py b/app/schemas/base.py index bbb69b8..57e7e9d 100644 --- a/app/schemas/base.py +++ b/app/schemas/base.py @@ -1,4 +1,6 @@ -from typing import Any, Optional +from typing import Any, Optional, Generic, TypeVar, List +from pydantic import BaseModel, Field +from pydantic.generics import GenericModel from fastapi.responses import JSONResponse @@ -50,3 +52,26 @@ class SuccessExtra(JSONResponse): } content.update(kwargs) super().__init__(content=content, status_code=code) + + +T = TypeVar("T") + + +class BasicResponse(GenericModel, Generic[T]): + code: int = Field(200, description="状态码") + msg: Optional[str] = Field("OK", description="信息") + data: Optional[T] = Field(None, description="数据载荷") + + +class PageResponse(GenericModel, Generic[T]): + code: int = Field(200, description="状态码") + msg: Optional[str] = Field(None, description="信息") + data: List[T] = Field(default_factory=list, description="数据列表") + total: int = Field(0, description="总数量") + page: int = Field(1, description="当前页码") + page_size: int = Field(20, description="每页数量") + pages: Optional[int] = Field(None, description="总页数") + + +class MessageOut(BaseModel): + message: str = Field(..., description="提示信息") diff --git a/app/schemas/invoice.py b/app/schemas/invoice.py new file mode 100644 index 0000000..22ca22a --- /dev/null +++ b/app/schemas/invoice.py @@ -0,0 +1,102 @@ +from typing import Optional, List +from pydantic import BaseModel, Field, EmailStr + + +class InvoiceHeaderCreate(BaseModel): + company_name: str = Field(..., min_length=1, max_length=128) + tax_number: str = Field(..., min_length=1, max_length=32) + register_address: str = Field(..., min_length=1, max_length=256) + register_phone: str = Field(..., min_length=1, max_length=32) + bank_name: str = Field(..., min_length=1, max_length=128) + bank_account: str = Field(..., min_length=1, max_length=64) + email: EmailStr + + +class InvoiceHeaderOut(BaseModel): + id: int + company_name: str + tax_number: str + register_address: str + register_phone: str + bank_name: str + bank_account: str + email: EmailStr + + +class InvoiceCreate(BaseModel): + ticket_type: str = Field(..., pattern=r"^(electronic|paper)$") + invoice_type: str = Field(..., pattern=r"^(special|normal)$") + phone: str = Field(..., min_length=5, max_length=20) + email: EmailStr + company_name: str = Field(..., min_length=1, max_length=128) + tax_number: str = Field(..., min_length=1, max_length=32) + register_address: str = Field(..., min_length=1, max_length=256) + register_phone: str = Field(..., min_length=1, max_length=32) + bank_name: str = Field(..., min_length=1, max_length=128) + bank_account: str = Field(..., min_length=1, max_length=64) + app_user_id: Optional[int] = None + header_id: Optional[int] = None + wechat: Optional[str] = None + + +class InvoiceUpdate(BaseModel): + ticket_type: Optional[str] = Field(None, pattern=r"^(electronic|paper)$") + invoice_type: Optional[str] = Field(None, pattern=r"^(special|normal)$") + phone: Optional[str] = Field(None, min_length=5, max_length=20) + email: Optional[EmailStr] = None + company_name: Optional[str] = Field(None, min_length=1, max_length=128) + tax_number: Optional[str] = Field(None, min_length=1, max_length=32) + register_address: Optional[str] = Field(None, min_length=1, max_length=256) + register_phone: Optional[str] = Field(None, min_length=1, max_length=32) + bank_name: Optional[str] = Field(None, min_length=1, max_length=128) + bank_account: Optional[str] = Field(None, min_length=1, max_length=64) + wechat: Optional[str] = None + + +class InvoiceOut(BaseModel): + id: int + created_at: str + ticket_type: str + invoice_type: str + phone: str + email: EmailStr + company_name: str + tax_number: str + register_address: str + register_phone: str + bank_name: str + bank_account: str + status: str + app_user_id: Optional[int] + header_id: Optional[int] + wechat: Optional[str] + + +class InvoiceList(BaseModel): + items: List[InvoiceOut] + total: int + page: int + page_size: int + + +class UpdateStatus(BaseModel): + id: int + status: str = Field(..., pattern=r"^(pending|invoiced|rejected|refunded)$") + + +class UpdateType(BaseModel): + ticket_type: str = Field(..., pattern=r"^(electronic|paper)$") + invoice_type: str = Field(..., pattern=r"^(special|normal)$") + + +class PaymentReceiptCreate(BaseModel): + url: str = Field(..., min_length=1, max_length=512) + note: Optional[str] = Field(None, max_length=256) + + +class PaymentReceiptOut(BaseModel): + id: int + url: str + note: Optional[str] + verified: bool + created_at: str \ No newline at end of file diff --git a/app/schemas/transactions.py b/app/schemas/transactions.py new file mode 100644 index 0000000..7b2ed27 --- /dev/null +++ b/app/schemas/transactions.py @@ -0,0 +1,25 @@ +from pydantic import BaseModel, Field +from typing import Optional + + +class SendEmailRequest(BaseModel): + email: str = Field(..., description="邮箱地址") + subject: Optional[str] = Field(None, description="邮件主题") + body: str = Field(..., description="文案内容") + file_url: Optional[str] = Field(None, description="附件URL") + + +class SendEmailResponse(BaseModel): + status: str + log_id: Optional[int] = None + error: Optional[str] = None + + +class EmailSendLogOut(BaseModel): + id: int + email: str + subject: Optional[str] + body_summary: Optional[str] + file_name: Optional[str] + file_url: Optional[str] + status: str \ No newline at end of file diff --git a/app/schemas/upload.py b/app/schemas/upload.py index 3db93b3..5c6f65a 100644 --- a/app/schemas/upload.py +++ b/app/schemas/upload.py @@ -3,4 +3,9 @@ from pydantic import BaseModel class ImageUploadResponse(BaseModel): """图片上传响应模型""" url: str - filename: str \ No newline at end of file + filename: str + +class FileUploadResponse(BaseModel): + url: str + filename: str + content_type: str \ No newline at end of file diff --git a/app/schemas/valuation.py b/app/schemas/valuation.py index 361c2b9..adac318 100644 --- a/app/schemas/valuation.py +++ b/app/schemas/valuation.py @@ -1,6 +1,7 @@ from datetime import datetime from typing import List, Optional, Any, Dict, Union -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, field_validator +from decimal import Decimal class ValuationAssessmentBase(BaseModel): @@ -28,6 +29,8 @@ class ValuationAssessmentBase(BaseModel): historical_evidence: Optional[Dict[str, int]] = Field(None, description="非遗资产历史证明证据及数量") patent_certificates: Optional[List[str]] = Field(None, description="非遗资产所用专利的证书") pattern_images: Optional[List[str]] = Field(None, description="非遗纹样图片") + report_url: Optional[str] = Field(None, description="评估报告URL") + certificate_url: Optional[str] = Field(None, description="证书URL") # 非遗应用与推广 application_maturity: Optional[str] = Field(None, description="非遗资产应用成熟度") @@ -53,6 +56,8 @@ class ValuationAssessmentBase(BaseModel): price_fluctuation: Optional[List[Union[str, int, float]]] = Field(None, description="该商品近30天价格波动区间") price_range: Optional[Dict[str, Union[int, float]]] = Field(None, description="资产商品的价格波动率") # 未使用 market_price: Optional[Union[int, float]] = Field(None, description="市场价格(单位:万元)") # 未使用 + credit_code_or_id: Optional[str] = Field(None, description="统一社会信用代码或身份证号") + biz_intro: Optional[str] = Field(None, description="业务/传承介绍") # 内置API计算字段 infringement_record: Optional[str] = Field(None, description="侵权记录") @@ -102,6 +107,8 @@ class ValuationAssessmentUpdate(BaseModel): historical_evidence: Optional[List[Any]] = Field(None, description="非遗资产历史证明证据及数量") patent_certificates: Optional[List[Any]] = Field(None, description="非遗资产所用专利的证书") pattern_images: Optional[List[Any]] = Field(None, description="非遗纹样图片") + report_url: Optional[str] = Field(None, description="评估报告URL") + certificate_url: Optional[str] = Field(None, description="证书URL") # 非遗应用与推广 application_maturity: Optional[str] = Field(None, description="非遗资产应用成熟度") @@ -117,6 +124,8 @@ class ValuationAssessmentUpdate(BaseModel): last_market_activity: Optional[str] = Field(None, description="该商品最近一次市场活动时间") monthly_transaction: Optional[str] = Field(None, description="月交易额") price_fluctuation: Optional[List[Union[str, int, float]]] = Field(None, description="该商品近30天价格波动区间") + credit_code_or_id: Optional[str] = Field(None, description="统一社会信用代码或身份证号") + biz_intro: Optional[str] = Field(None, description="业务/传承介绍") is_active: Optional[bool] = Field(None, description="是否激活") @@ -233,4 +242,46 @@ class ValuationApprovalRequest(BaseModel): class ValuationAdminNotesUpdate(BaseModel): """管理员备注更新模型""" - admin_notes: str = Field(..., description="管理员备注") \ No newline at end of file + admin_notes: str = Field(..., description="管理员备注") + + +class ValuationCalculationStepBase(BaseModel): + """估值计算步骤基础模型""" + step_order: Decimal = Field(..., description="步骤顺序") + step_name: str = Field(..., description="步骤名称") + step_description: Optional[str] = Field(None, description="步骤描述") + input_params: Optional[Dict[str, Any]] = Field(None, description="输入参数") + output_result: Optional[Dict[str, Any]] = Field(None, description="输出结果") + status: str = Field(..., description="步骤状态") + error_message: Optional[str] = Field(None, description="错误信息") + + @field_validator('step_order', mode='before') + @classmethod + def _coerce_step_order(cls, v): + if isinstance(v, Decimal): + return v + if isinstance(v, (int, float, str)): + try: + return Decimal(str(v)) + except Exception: + raise ValueError('Invalid step_order') + raise ValueError('Invalid step_order type') + + +class ValuationCalculationStepCreate(ValuationCalculationStepBase): + """创建估值计算步骤模型""" + valuation_id: int = Field(..., description="关联的估值评估ID") + + +class ValuationCalculationStepOut(ValuationCalculationStepBase): + """估值计算步骤输出模型""" + id: int = Field(..., description="主键ID") + valuation_id: int = Field(..., description="关联的估值评估ID") + created_at: datetime = Field(..., description="创建时间") + + class Config: + from_attributes = True + json_encoders = { + datetime: lambda v: v.isoformat(), + Decimal: lambda v: float(v) + } \ No newline at end of file diff --git a/app/services/email_client.py b/app/services/email_client.py new file mode 100644 index 0000000..65beea4 --- /dev/null +++ b/app/services/email_client.py @@ -0,0 +1,49 @@ +import smtplib +from email.mime.base import MIMEBase +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from email import encoders +from typing import Optional +import httpx + +from app.settings.config import settings + + +class EmailClient: + def send(self, to_email: str, subject: Optional[str], body: str, file_bytes: Optional[bytes], file_name: Optional[str], content_type: Optional[str]) -> dict: + if not settings.SMTP_HOST or not settings.SMTP_PORT or not settings.SMTP_FROM: + raise RuntimeError("SMTP 未配置") + + msg = MIMEMultipart() + msg["From"] = settings.SMTP_FROM + msg["To"] = to_email + msg["Subject"] = subject or "估值服务通知" + msg.attach(MIMEText(body, "plain", "utf-8")) + + if file_bytes and file_name: + part = MIMEBase("application", "octet-stream") + part.set_payload(file_bytes) + encoders.encode_base64(part) + part.add_header("Content-Disposition", f"attachment; filename=\"{file_name}\"") + msg.attach(part) + + if settings.SMTP_TLS: + server = smtplib.SMTP(settings.SMTP_HOST, settings.SMTP_PORT) + server.starttls() + else: + server = smtplib.SMTP_SSL(settings.SMTP_HOST, settings.SMTP_PORT) + try: + if settings.SMTP_USERNAME and settings.SMTP_PASSWORD: + server.login(settings.SMTP_USERNAME, settings.SMTP_PASSWORD) + server.sendmail(settings.SMTP_FROM, [to_email], msg.as_string()) + server.quit() + return {"status": "OK"} + except Exception as e: + try: + server.quit() + except Exception: + pass + return {"status": "FAIL", "error": str(e)} + + +email_client = EmailClient() \ No newline at end of file diff --git a/app/services/rate_limiter.py b/app/services/rate_limiter.py new file mode 100644 index 0000000..d5aa450 --- /dev/null +++ b/app/services/rate_limiter.py @@ -0,0 +1,52 @@ +import time +from typing import Dict + + +class PhoneRateLimiter: + def __init__(self, window_seconds: int = 60) -> None: + """手机号限流器 + + Args: + window_seconds: 限流窗口秒数 + + Returns: + None + """ + self.window = window_seconds + self.last_sent: Dict[str, float] = {} + + def allow(self, phone: str) -> bool: + """校验是否允许发送 + + Args: + phone: 手机号 + + Returns: + True 表示允许发送,False 表示命中限流 + """ + now = time.time() + ts = self.last_sent.get(phone, 0) + if now - ts < self.window: + return False + self.last_sent[phone] = now + return True + + def next_allowed_at(self, phone: str) -> float: + """返回下一次允许发送的时间戳 + + Args: + phone: 手机号 + + Returns: + 时间戳(秒) + """ + ts = self.last_sent.get(phone, 0) + return ts + self.window + + def reset(self) -> None: + """重置限流状态 + + Returns: + None + """ + self.last_sent.clear() \ No newline at end of file diff --git a/app/services/sms_client.py b/app/services/sms_client.py new file mode 100644 index 0000000..5b42490 --- /dev/null +++ b/app/services/sms_client.py @@ -0,0 +1,91 @@ +import json +from typing import Optional, Dict, Any + +from app.settings import settings +from app.log import logger + + +class SMSClient: + def __init__(self) -> None: + """初始化短信客户端 + + Returns: + None + """ + self.client = None + + def _ensure_client(self) -> None: + """确保客户端初始化 + + Returns: + None + """ + if self.client is not None: + return + from alibabacloud_tea_openapi import models as open_api_models # type: ignore + from alibabacloud_dysmsapi20170525.client import Client as DysmsClient # type: ignore + if not settings.ALIBABA_CLOUD_ACCESS_KEY_ID or not settings.ALIBABA_CLOUD_ACCESS_KEY_SECRET: + raise RuntimeError("短信凭证未配置:请设置 ALIBABA_CLOUD_ACCESS_KEY_ID/ALIBABA_CLOUD_ACCESS_KEY_SECRET") + if not settings.ALIYUN_SMS_SIGN_NAME: + raise RuntimeError("短信签名未配置:请设置 ALIYUN_SMS_SIGN_NAME") + config = open_api_models.Config( + access_key_id=settings.ALIBABA_CLOUD_ACCESS_KEY_ID, + access_key_secret=settings.ALIBABA_CLOUD_ACCESS_KEY_SECRET, + ) + config.endpoint = settings.ALIYUN_SMS_ENDPOINT + self.client = DysmsClient(config) + + def send_by_template(self, phone: str, template_code: str, template_param: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: + """按模板发送短信 + + Args: + phone: 接收短信手机号 + template_code: 模板 Code + template_param: 模板变量字典 + + Returns: + 返回体映射字典 + """ + from alibabacloud_dysmsapi20170525 import models as sms_models # type: ignore + self._ensure_client() + req = sms_models.SendSmsRequest( + phone_numbers=phone, + sign_name=settings.ALIYUN_SMS_SIGN_NAME, + template_code=template_code, + template_param=json.dumps(template_param or {}), + ) + logger.info("sms.send start phone={} template={}", phone, template_code) + try: + resp = self.client.send_sms(req) + body = resp.body.to_map() if hasattr(resp, "body") else {} + logger.info("sms.send response code={} request_id={} phone={}", body.get("Code"), body.get("RequestId"), phone) + return body + except Exception as e: + logger.error("sms.provider_error err={}", repr(e)) + return {"Code": "ERROR", "Message": str(e)} + + def send_code(self, phone: str, code: str) -> Dict[str, Any]: + """发送验证码短信 + + Args: + phone: 接收短信手机号 + code: 验证码 + + Returns: + 返回体映射字典 + """ + return self.send_by_template(phone, "SMS_498190229", {"code": code}) + + def send_report(self, phone: str) -> Dict[str, Any]: + """发送报告通知短信 + + Args: + phone: 接收短信手机号 + + Returns: + 返回体映射字典 + """ + return self.send_by_template(phone, "SMS_498140213", {}) + + +sms_client = SMSClient() \ No newline at end of file diff --git a/app/services/sms_store.py b/app/services/sms_store.py new file mode 100644 index 0000000..2524697 --- /dev/null +++ b/app/services/sms_store.py @@ -0,0 +1,144 @@ +import random +import time +from datetime import date +from typing import Dict, Optional, Tuple + + +class VerificationStore: + def __init__(self, code_ttl_seconds: int = 300, minute_window: int = 60, daily_limit: int = 10, max_failures: int = 5, lock_seconds: int = 3600) -> None: + """验证码与限流存储 + + Args: + code_ttl_seconds: 验证码有效期秒数 + minute_window: 同号分钟级限流窗口 + daily_limit: 每日发送上限次数 + max_failures: 最大失败次数后锁定 + lock_seconds: 锁定时长秒数 + + Returns: + None + """ + self.code_ttl = code_ttl_seconds + self.minute_window = minute_window + self.daily_limit = daily_limit + self.max_failures = max_failures + self.lock_seconds = lock_seconds + self.codes: Dict[str, Tuple[str, float]] = {} + self.sends: Dict[str, Dict[str, float]] = {} + self.failures: Dict[str, Dict[str, float]] = {} + + def generate_code(self) -> str: + """生成6位数字验证码 + + Returns: + 六位数字字符串 + """ + return f"{random.randint(0, 999999):06d}" + + def set_code(self, phone: str, code: str) -> None: + """设置验证码与过期时间 + + Args: + phone: 手机号 + code: 验证码 + + Returns: + None + """ + expires_at = time.time() + self.code_ttl + self.codes[phone] = (code, expires_at) + + def get_code(self, phone: str) -> Optional[Tuple[str, float]]: + """获取存储的验证码与过期时间 + + Args: + phone: 手机号 + + Returns: + 元组(code, expires_at)或None + """ + return self.codes.get(phone) + + def clear_code(self, phone: str) -> None: + """清除验证码记录 + + Args: + phone: 手机号 + + Returns: + None + """ + self.codes.pop(phone, None) + + def allow_send(self, phone: str) -> Tuple[bool, Optional[str]]: + """校验是否允许发送验证码 + + Args: + phone: 手机号 + + Returns: + (允许, 拒绝原因) + """ + now = time.time() + dkey = date.today().isoformat() + info = self.sends.get(phone) or {"day": dkey, "count": 0.0, "last_ts": 0.0} + if info["day"] != dkey: + info = {"day": dkey, "count": 0.0, "last_ts": 0.0} + if now - info["last_ts"] < self.minute_window: + self.sends[phone] = info + return False, "发送频率过高" + if info["count"] >= float(self.daily_limit): + self.sends[phone] = info + return False, "今日发送次数已达上限" + info["last_ts"] = now + info["count"] = info["count"] + 1.0 + self.sends[phone] = info + return True, None + + def can_verify(self, phone: str) -> Tuple[bool, Optional[str]]: + """校验是否允许验证 + + Args: + phone: 手机号 + + Returns: + (允许, 拒绝原因) + """ + now = time.time() + stat = self.failures.get(phone) + if stat and stat.get("lock_until", 0.0) > now: + return False, "尝试次数过多,已锁定" + return True, None + + def record_verify_failure(self, phone: str) -> Tuple[int, bool]: + """记录一次验证失败并判断是否触发锁定 + + Args: + phone: 手机号 + + Returns: + (失败次数, 是否锁定) + """ + now = time.time() + stat = self.failures.get(phone) or {"count": 0.0, "lock_until": 0.0} + if stat.get("lock_until", 0.0) > now: + return int(stat["count"]), True + stat["count"] = stat.get("count", 0.0) + 1.0 + if int(stat["count"]) >= self.max_failures: + stat["lock_until"] = now + self.lock_seconds + self.failures[phone] = stat + return int(stat["count"]), stat["lock_until"] > now + + def reset_failures(self, phone: str) -> None: + """重置失败计数 + + Args: + phone: 手机号 + + Returns: + None + """ + self.failures.pop(phone, None) + + +store = VerificationStore() \ No newline at end of file diff --git a/app/settings/config.py b/app/settings/config.py index 0eecc42..da405c7 100644 --- a/app/settings/config.py +++ b/app/settings/config.py @@ -31,22 +31,22 @@ class Settings(BaseSettings): TORTOISE_ORM: dict = { "connections": { # SQLite configuration - "sqlite": { - "engine": "tortoise.backends.sqlite", - "credentials": {"file_path": f"{BASE_DIR}/db.sqlite3"}, # Path to SQLite database file - }, + # "sqlite": { + # "engine": "tortoise.backends.sqlite", + # "credentials": {"file_path": f"{BASE_DIR}/db.sqlite3"}, # Path to SQLite database file + # }, # MySQL/MariaDB configuration # Install with: tortoise-orm[asyncmy] - # "mysql": { - # "engine": "tortoise.backends.mysql", - # "credentials": { - # "host": "localhost", # Database host address - # "port": 3306, # Database port - # "user": "yourusername", # Database username - # "password": "yourpassword", # Database password - # "database": "yourdatabase", # Database name - # }, - # }, + "mysql": { + "engine": "tortoise.backends.mysql", + "credentials": { + "host": "sh-cynosdbmysql-grp-88th45wy.sql.tencentcdb.com", # Database host address + "port": 28555, # Database port + "user": "root", # Database username + "password": "api2api..", # Database password + "database": "valuation_service", # Database name + }, + }, # PostgreSQL configuration # Install with: tortoise-orm[asyncpg] # "postgres": { @@ -87,7 +87,7 @@ class Settings(BaseSettings): "apps": { "models": { "models": ["app.models", "aerich.models"], - "default_connection": "sqlite", + "default_connection": "mysql", }, }, "use_tz": False, # Whether to use timezone-aware datetimes @@ -95,5 +95,17 @@ class Settings(BaseSettings): } DATETIME_FORMAT: str = "%Y-%m-%d %H:%M:%S" + ALIBABA_CLOUD_ACCESS_KEY_ID: typing.Optional[str] = "LTAI5tA8gcgM8Qc7K9qCtmXg" + ALIBABA_CLOUD_ACCESS_KEY_SECRET: typing.Optional[str] = "eWZIWi6xILGtmPSGyJEAhILX5fQx0h" + ALIYUN_SMS_SIGN_NAME: typing.Optional[str] = "SMS_498140213" + ALIYUN_SMS_ENDPOINT: str = "dysmsapi.aliyuncs.com" + + SMTP_HOST: typing.Optional[str] = None + SMTP_PORT: typing.Optional[int] = None + SMTP_USERNAME: typing.Optional[str] = None + SMTP_PASSWORD: typing.Optional[str] = None + SMTP_TLS: bool = True + SMTP_FROM: typing.Optional[str] = None + settings = Settings() diff --git a/app/static/files/demo.pdf b/app/static/files/demo.pdf new file mode 100644 index 0000000..be5830c --- /dev/null +++ b/app/static/files/demo.pdf @@ -0,0 +1,2 @@ +%PDF-1.4 +% diff --git a/app/static/files/demo_1.pdf b/app/static/files/demo_1.pdf new file mode 100644 index 0000000..be5830c --- /dev/null +++ b/app/static/files/demo_1.pdf @@ -0,0 +1,2 @@ +%PDF-1.4 +% diff --git a/app/static/files/demo_1_2.pdf b/app/static/files/demo_1_2.pdf new file mode 100644 index 0000000..be5830c --- /dev/null +++ b/app/static/files/demo_1_2.pdf @@ -0,0 +1,2 @@ +%PDF-1.4 +% diff --git a/app/static/files/demo_1_2_3.pdf b/app/static/files/demo_1_2_3.pdf new file mode 100644 index 0000000..be5830c --- /dev/null +++ b/app/static/files/demo_1_2_3.pdf @@ -0,0 +1,2 @@ +%PDF-1.4 +% diff --git a/app/static/files/test.pdf b/app/static/files/test.pdf new file mode 100644 index 0000000..7e761f9 --- /dev/null +++ b/app/static/files/test.pdf @@ -0,0 +1 @@ +%PDF-1.4 \ No newline at end of file diff --git a/app/static/files/test_1.pdf b/app/static/files/test_1.pdf new file mode 100644 index 0000000..7e761f9 --- /dev/null +++ b/app/static/files/test_1.pdf @@ -0,0 +1 @@ +%PDF-1.4 \ No newline at end of file diff --git a/app/static/files/test_1_2.pdf b/app/static/files/test_1_2.pdf new file mode 100644 index 0000000..7e761f9 --- /dev/null +++ b/app/static/files/test_1_2.pdf @@ -0,0 +1 @@ +%PDF-1.4 \ No newline at end of file diff --git a/app/static/files/test_1_2_3.pdf b/app/static/files/test_1_2_3.pdf new file mode 100644 index 0000000..7e761f9 --- /dev/null +++ b/app/static/files/test_1_2_3.pdf @@ -0,0 +1 @@ +%PDF-1.4 \ No newline at end of file diff --git a/app/utils/__init__.py b/app/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/app/utils/calculation_engine/cultural_value_b2/cultural_value_b2.py b/app/utils/calculation_engine/cultural_value_b2/cultural_value_b2.py index 8ff3d0b..8567ae6 100644 --- a/app/utils/calculation_engine/cultural_value_b2/cultural_value_b2.py +++ b/app/utils/calculation_engine/cultural_value_b2/cultural_value_b2.py @@ -6,14 +6,25 @@ """ from typing import Dict, List, Optional +import sys +import os + +# 添加项目根目录到Python路径 +current_dir = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(os.path.join(current_dir, '..', '..', '..')) + try: # 相对导入(当作为包使用时) from .sub_formulas.living_heritage_b21 import LivingHeritageB21Calculator from .sub_formulas.pattern_gene_b22 import PatternGeneB22Calculator + from app.controllers.valuation import ValuationController + from app.schemas.valuation import ValuationCalculationStepCreate except ImportError: # 绝对导入(当直接运行时) from sub_formulas.living_heritage_b21 import LivingHeritageB21Calculator from sub_formulas.pattern_gene_b22 import PatternGeneB22Calculator + from app.controllers.valuation import ValuationController + from app.schemas.valuation import ValuationCalculationStepCreate class CulturalValueB2Calculator: @@ -23,6 +34,7 @@ class CulturalValueB2Calculator: """初始化计算器""" self.living_heritage_calculator = LivingHeritageB21Calculator() self.pattern_gene_calculator = PatternGeneB22Calculator() + self.valuation_controller = ValuationController() def calculate_cultural_value_b2(self, living_heritage_b21: float, @@ -42,48 +54,88 @@ class CulturalValueB2Calculator: return cultural_value - def calculate_complete_cultural_value_b2(self, input_data: Dict) -> Dict: + async def calculate_complete_cultural_value_b2(self, valuation_id: int, input_data: Dict) -> float: """ - 计算完整的文化价值B2,包含所有子公式 - - args: - input_data: 输入数据字典,包含所有必要的参数 - - return: - Dict: 包含所有中间计算结果和最终结果的字典 - """ - # 计算活态传承系数B21 - teaching_frequency = self.living_heritage_calculator.calculate_teaching_frequency( - input_data["offline_sessions"], - input_data["douyin_views"], - input_data["kuaishou_views"], - input_data["bilibili_views"] - ) - living_heritage_b21 = self.living_heritage_calculator.calculate_living_heritage_b21( - input_data['inheritor_level_coefficient'], - teaching_frequency, - input_data['cross_border_depth'] - ) - - # 计算纹样基因值B22 - pattern_gene_b22 = self.pattern_gene_calculator.calculate_pattern_gene_b22( - input_data['structure_complexity'], - input_data['normalized_entropy'], - input_data['historical_inheritance'] - ) - - # 计算文化价值B2 - cultural_value_b2 = self.calculate_cultural_value_b2( - living_heritage_b21, - pattern_gene_b22 - ) - - return { - 'living_heritage_b21': living_heritage_b21, - 'pattern_gene_b22': pattern_gene_b22, - 'cultural_value_b2': cultural_value_b2 - } + 计算完整的文化价值B2,并记录所有计算步骤。 + 该函数通过整合活态传承系数B21和纹样基因值B22的计算, + 最终得出文化价值B2。每一步的计算过程都会被记录下来, + 以确保计算的透明度和可追溯性。 + + Args: + valuation_id (int): 估值的唯一标识符,用于关联所有计算步骤。 + input_data (Dict): 包含所有计算所需参数的字典,例如: + { + 'inheritor_level_coefficient': 10.0, # B21 + 'offline_sessions': 1, # B21 + 'structure_complexity': 0.75, # B22 + ... + } + + Returns: + float: 计算得出的文化价值B2。 + + Raises: + Exception: 在计算过程中遇到的任何异常都会被捕获、记录,并重新抛出。 + """ + step = await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=2.2, + step_name="文化价值B2计算", + step_description="开始计算文化价值B2,公式为:活态传承系数B21 × 0.6 + (纹样基因值B22 / 10) × 0.4", + input_params=input_data, + status="in_progress" + ) + ) + try: + # 计算活态传承系数B21 + living_heritage_b21 = self.living_heritage_calculator.calculate_living_heritage_b21( + input_data['inheritor_level_coefficient'], + self.living_heritage_calculator.calculate_teaching_frequency( + input_data["offline_sessions"], + input_data["douyin_views"], + input_data["kuaishou_views"], + input_data["bilibili_views"] + ), + input_data['cross_border_depth'] + ) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, step_order=2.21, step_name="活态传承系数B21", + output_result={'living_heritage_b21': living_heritage_b21}, status="completed" + ) + ) + + # 计算纹样基因值B22 + pattern_gene_b22 = self.pattern_gene_calculator.calculate_pattern_gene_b22( + input_data['structure_complexity'], + input_data['normalized_entropy'], + input_data['historical_inheritance'] + ) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, step_order=2.22, step_name="纹样基因值B22", + output_result={'pattern_gene_b22': pattern_gene_b22}, status="completed" + ) + ) + + # 计算文化价值B2 + cultural_value_b2 = self.calculate_cultural_value_b2( + living_heritage_b21, + pattern_gene_b22 + ) + + await self.valuation_controller.update_calculation_step( + step.id, {"status": "completed", "output_result": {"cultural_value_b2": cultural_value_b2}} + ) + return cultural_value_b2 + except Exception as e: + error_message = f"文化价值B2计算失败: {e}" + await self.valuation_controller.update_calculation_step( + step.id, {"status": "failed", "error_message": error_message} + ) + raise # 示例使用 if __name__ == "__main__": diff --git a/app/utils/calculation_engine/cultural_value_b2/sub_formulas/living_heritage_b21.py b/app/utils/calculation_engine/cultural_value_b2/sub_formulas/living_heritage_b21.py index c55bb0c..d09d6ad 100644 --- a/app/utils/calculation_engine/cultural_value_b2/sub_formulas/living_heritage_b21.py +++ b/app/utils/calculation_engine/cultural_value_b2/sub_formulas/living_heritage_b21.py @@ -8,12 +8,26 @@ +import sys +import os + +# 添加项目根目录到Python路径 +current_dir = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(os.path.join(current_dir, '..', '..', '..', '..')) + +try: + from app.controllers.valuation import ValuationController + from app.schemas.valuation import ValuationCalculationStepCreate +except ImportError: + # 处理可能的导入错误 + pass + class LivingHeritageB21Calculator: """活态传承系数B21计算器""" def __init__(self): """初始化计算器""" - pass + self.valuation_controller = ValuationController() def calculate_living_heritage_b21(self, inheritor_level_coefficient: float, @@ -22,7 +36,6 @@ class LivingHeritageB21Calculator: """ 计算活态传承系数B21 - 活态传承系数B21 = 传承人等级系数 × 0.4 + 教学传播频次 × 0.3 + 跨界合作深度 × 0.3 args: @@ -30,14 +43,12 @@ class LivingHeritageB21Calculator: teaching_frequency: 教学传播频次 (用户填写) cross_border_depth: 跨界合作深度 (用户填写) - return: + return: float: 活态传承系数B21 """ - # living_heritage = (inheritor_level_coefficient * 0.4 + teaching_frequency * 0.3 + cross_border_depth * 0.3) - return living_heritage def calculate_inheritor_level_coefficient(self, inheritor_level: str) -> float: @@ -47,13 +58,12 @@ class LivingHeritageB21Calculator: 传承人等级评分标准: - 国家级传承人: 1分 - 省级传承人: 0.7分 - - 市级传承人: .44分 - + - 市级传承人: 0.4分 args: inheritor_level: 传承人等级 (用户填写) - return: + return: float: 传承人等级系数 """ level_scores = { @@ -61,7 +71,6 @@ class LivingHeritageB21Calculator: "省级传承人": 0.7, "市级传承人": 0.4, } - return level_scores.get(inheritor_level, 0.4) def calculate_teaching_frequency(self, @@ -74,16 +83,8 @@ class LivingHeritageB21Calculator: 教学传播频次 = 线下传习次数 × 0.6 + 线上课程点击量(万) × 0.4 - 线下传习次数统计规范: - 1) 单次活动标准:传承人主导、时长≥2小时、参与人数≥5人 - 2) 频次计算:按自然年度累计,同一内容重复培训不计入 - - 线上课程折算: - - 抖音/快手播放量按100:1折算为学习人次 - - B站课程按50:1折算 - args: - offline_sessions: 线下传习次数(符合标准的活动次数) + offline_sessions: 线下传习次数 douyin_views: 抖音播放量 kuaishou_views: 快手播放量 bilibili_views: B站播放量 @@ -91,71 +92,55 @@ class LivingHeritageB21Calculator: returns: float: 教学传播频次评分 """ - # 线下传习次数权重计算 offline_score = offline_sessions * 0.6 - - # 线上课程点击量折算 - # 抖音/快手按100:1折算 douyin_kuaishou_learning_sessions = (douyin_views + kuaishou_views) / 100 - # B站按50:1折算 bilibili_learning_sessions = bilibili_views / 50 + online_views_in_ten_thousands = (douyin_kuaishou_learning_sessions + bilibili_learning_sessions) / 10000 + online_score = online_views_in_ten_thousands * 0.4 + teaching_frequency_score = offline_score + online_score + return teaching_frequency_score + + def calculate_cross_border_depth(self, cross_border_projects: int) -> float: + """ + 计算跨界合作深度 - # 线上总学习人次(万) - online_learning_sessions_10k = (douyin_kuaishou_learning_sessions + bilibili_learning_sessions) / 10000 + 每参与1个跨界合作项目+1分,最高10分 - # 线上课程权重计算 - online_score = online_learning_sessions_10k * 0.4 - - # 总教学传播频次 - teaching_frequency = offline_score + online_score - - return teaching_frequency -def cross_border_depth_dict(border_depth: str) -> float: - cross_border_depth_scores = { - "品牌联名": 0.3, - "科技载体": 0.5, - "国家外交礼品": 1, - } - return cross_border_depth_scores.get(border_depth, 0.3) - + args: + cross_border_projects: 跨界合作项目数 + + returns: + float: 跨界合作深度评分 + """ + return min(cross_border_projects, 10.0) -# 示例使用 -if __name__ == "__main__": - calculator = LivingHeritageB21Calculator() - - # 示例数据 - inheritor_level = "国家级传承人" # 传承人等级 (用户填写) - cross_border_depth = 50.0 - # 教学传播频次数据 - offline_sessions = 20 # 线下传习次数(符合标准:传承人主导、时长≥2小时、参与人数≥5人) - douyin_views = 10000000 # 抖音播放量 - kuaishou_views = 0 # 快手播放量 - bilibili_views = 0 # B站播放量 - + async def calculate_complete_living_heritage_b21(self, valuation_id: int, input_data: dict) -> float: + step = await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=2.21, + step_name="活态传承系数B21计算", + step_description="开始计算活态传承系数B21", + input_params=input_data, + status="in_progress" + ) + ) + try: + inheritor_level_coefficient = self.calculate_inheritor_level_coefficient(input_data['inheritor_level']) + teaching_frequency = self.calculate_teaching_frequency(input_data['offline_sessions'], input_data.get('douyin_views', 0), input_data.get('kuaishou_views', 0), input_data.get('bilibili_views', 0)) + cross_border_depth = self.calculate_cross_border_depth(input_data['cross_border_projects']) - - # 计算各项指标 - inheritor_level_coefficient = calculator.calculate_inheritor_level_coefficient(inheritor_level) - teaching_frequency = calculator.calculate_teaching_frequency( - offline_sessions=offline_sessions, - douyin_views=douyin_views, - kuaishou_views=kuaishou_views, - bilibili_views=bilibili_views - ) - print(teaching_frequency) + living_heritage_b21 = self.calculate_living_heritage_b21(inheritor_level_coefficient, teaching_frequency, cross_border_depth) - # 计算活态传承系数B21 - living_heritage_b21 = calculator.calculate_living_heritage_b21( - 1, teaching_frequency, 0.3 - ) - - print(f"传承人等级系数: {inheritor_level_coefficient:.2f}") - print(f"教学传播频次: {teaching_frequency:.2f}") - print(f" - 线下传习次数: {offline_sessions}次") - print(f" - 抖音播放量: {douyin_views:,}次") - print(f" - 快手播放量: {kuaishou_views:,}次") - print(f" - B站播放量: {bilibili_views:,}次") - print(f"跨界合作深度: {cross_border_depth:.2f}") - print(f"活态传承系数B21: {living_heritage_b21:.4f}") + await self.valuation_controller.update_calculation_step( + step.id, {"status": "completed", "output_result": {"living_heritage_b21": living_heritage_b21}} + ) + return living_heritage_b21 + except Exception as e: + error_message = f"活态传承系数B21计算失败: {e}" + await self.valuation_controller.update_calculation_step( + step.id, {"status": "failed", "error_message": error_message} + ) + raise diff --git a/app/utils/calculation_engine/cultural_value_b2/sub_formulas/pattern_gene_b22.py b/app/utils/calculation_engine/cultural_value_b2/sub_formulas/pattern_gene_b22.py index 039035e..84b965a 100644 --- a/app/utils/calculation_engine/cultural_value_b2/sub_formulas/pattern_gene_b22.py +++ b/app/utils/calculation_engine/cultural_value_b2/sub_formulas/pattern_gene_b22.py @@ -1,21 +1,26 @@ -""" -纹样基因值B22计算模块 - -纹样基因值B22 = (结构复杂度SC × 0.6 + 归一化信息熵H × 0.4) × 历史传承度HI × 10 - -""" - +import sys +import os import math from typing import Dict, List +# 添加项目根目录到Python路径 +current_dir = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(os.path.join(current_dir, '..', '..', '..', '..')) + +try: + from app.controllers.valuation import ValuationController + from app.schemas.valuation import ValuationCalculationStepCreate +except ImportError: + # 处理可能的导入错误 + pass class PatternGeneB22Calculator: """纹样基因值B22计算器""" def __init__(self): """初始化计算器""" - pass + self.valuation_controller = ValuationController() def calculate_pattern_gene_b22(self, structure_complexity: float, @@ -24,7 +29,6 @@ class PatternGeneB22Calculator: """ 计算纹样基因值B22 - 纹样基因值B22 = (结构复杂度SC × 0.6 + 归一化信息熵H × 0.4) × 历史传承度HI × 10 args: @@ -35,11 +39,9 @@ class PatternGeneB22Calculator: return: float: 纹样基因值B22 """ - pattern_gene = ((structure_complexity * 0.6 + normalized_entropy * 0.4) * historical_inheritance * 10) - return pattern_gene def calculate_structure_complexity(self, pattern_elements: List[Dict]) -> float: @@ -87,57 +89,49 @@ class PatternGeneB22Calculator: if not pattern_data or len(pattern_data) <= 1: return 0.0 - # 计算概率分布 total = sum(pattern_data) if total == 0: return 0.0 probabilities = [x / total for x in pattern_data if x > 0] - # 计算信息熵 entropy = 0.0 for p in probabilities: if p > 0: entropy -= p * math.log2(p) - # 归一化 n = len(probabilities) if n <= 1: return 0.0 normalized_entropy = entropy / math.log2(n) return normalized_entropy - + async def calculate_complete_pattern_gene_b22(self, valuation_id: int, input_data: dict) -> float: + step = await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=2.22, + step_name="纹样基因值B22计算", + step_description="开始计算纹样基因值B22", + input_params=input_data, + status="in_progress" + ) + ) + try: + structure_complexity = self.calculate_structure_complexity(input_data['pattern_elements']) + normalized_entropy = self.calculate_normalized_entropy(input_data['entropy_data']) + historical_inheritance = input_data['historical_inheritance'] -# 示例使用 -if __name__ == "__main__": + pattern_gene_b22 = self.calculate_pattern_gene_b22(structure_complexity, normalized_entropy, historical_inheritance) - calculator = PatternGeneB22Calculator() - - # 示例数据 - pattern_elements = [ - {'type': '几何图形', 'weight': 0.3, 'complexity': 0.7}, - {'type': '植物纹样', 'weight': 0.4, 'complexity': 0.8}, - {'type': '动物纹样', 'weight': 0.3, 'complexity': 0.6} - ] - entropy_data = [0.3, 0.4, 0.3] - inheritance_years = 500 # 传承年数 (用户填写) - cultural_significance = "国家级" # 文化意义等级 (用户填写) - preservation_status = "良好" # 保护状况 (用户填写) - historical_inheritance = 100.0 - - # 计算各项指标 - structure_complexity = calculator.calculate_structure_complexity(pattern_elements) - normalized_entropy = calculator.calculate_normalized_entropy(entropy_data) - - - # 计算纹样基因值B22 - pattern_gene_b22 = calculator.calculate_pattern_gene_b22( - 1.5, 9, historical_inheritance - ) - - print(f"结构复杂度SC: {structure_complexity:.4f}") - print(f"归一化信息熵H: {normalized_entropy:.4f}") - print(f"历史传承度HI: {historical_inheritance:.4f}") - print(f"纹样基因值B22: {pattern_gene_b22:.4f}") + await self.valuation_controller.update_calculation_step( + step.id, {"status": "completed", "output_result": {"pattern_gene_b22": pattern_gene_b22}} + ) + return pattern_gene_b22 + except Exception as e: + error_message = f"纹样基因值B22计算失败: {e}" + await self.valuation_controller.update_calculation_step( + step.id, {"status": "failed", "error_message": error_message} + ) + raise diff --git a/app/utils/calculation_engine/economic_value_b1/economic_value_b1.py b/app/utils/calculation_engine/economic_value_b1/economic_value_b1.py index 67c7c8a..6f9c762 100644 --- a/app/utils/calculation_engine/economic_value_b1/economic_value_b1.py +++ b/app/utils/calculation_engine/economic_value_b1/economic_value_b1.py @@ -6,17 +6,12 @@ """ from typing import Dict +from app.controllers.valuation import ValuationController +from app.schemas.valuation import ValuationCalculationStepCreate -try: - # 相对导入(当作为包使用时) - from .sub_formulas.basic_value_b11 import BasicValueB11Calculator, calculate_popularity_score - from .sub_formulas.traffic_factor_b12 import TrafficFactorB12Calculator - from .sub_formulas.policy_multiplier_b13 import PolicyMultiplierB13Calculator -except ImportError: - # 绝对导入(当直接运行时) - from sub_formulas.basic_value_b11 import BasicValueB11Calculator - from sub_formulas.traffic_factor_b12 import TrafficFactorB12Calculator - from sub_formulas.policy_multiplier_b13 import PolicyMultiplierB13Calculator +from .sub_formulas.basic_value_b11 import BasicValueB11Calculator +from .sub_formulas.traffic_factor_b12 import TrafficFactorB12Calculator +from .sub_formulas.policy_multiplier_b13 import PolicyMultiplierB13Calculator class EconomicValueB1Calculator: @@ -27,6 +22,7 @@ class EconomicValueB1Calculator: self.basic_value_calculator = BasicValueB11Calculator() self.traffic_factor_calculator = TrafficFactorB12Calculator() self.policy_multiplier_calculator = PolicyMultiplierB13Calculator() + self.valuation_controller = ValuationController() def calculate_economic_value_b1(self, basic_value_b11: float, @@ -50,95 +46,103 @@ class EconomicValueB1Calculator: return economic_value - def calculate_complete_economic_value_b1(self, input_data: Dict) -> Dict: + async def calculate_complete_economic_value_b1(self, valuation_id: int, input_data: Dict) -> float: """ - 计算完整的经济价值B1,包含所有子公式 - - args: - input_data: 输入数据字典,包含所有必要的参数 - - returns: - Dict: 包含所有中间计算结果和最终结果的字典 + 计算完整的经济价值B1,并记录所有计算步骤。 + + 此函数集成了基础价值B11、流量因子B12和政策乘数B13的计算, + 通过调用相应的子计算器来完成。每一步的计算结果都会被记录下来, + 以支持后续的审计和分析。 + + Args: + valuation_id (int): 估值的唯一标识符,用于关联所有计算步骤。 + input_data (Dict): 包含所有计算所需参数的字典,例如: + { + 'three_year_income': [2000, 2400, 2600], # B11 + 'patent_score': 1, # B11 + 'search_index_s1': 4500.0, # B12 + 'policy_match_score': 10.0, # B13 + ... + } + + Returns: + float: 计算得出的经济价值B1。 + + Raises: + Exception: 在计算过程中发生的任何异常都会被捕获、记录,并重新抛出。 """ - # 财务价值F 近三年年均收益列表 [1,2,3] - financial_value = self.basic_value_calculator.calculate_financial_value_f(input_data["three_year_income"]) - # 计算法律强度L patent_score: 专利分 (0-10分) (用户填写) - # popularity_score: 普及地域分 (0-10分) (用户填写) - # infringement_score: 侵权分 (0-10分) (用户填写) - - legal_strength = self.basic_value_calculator.calculate_legal_strength_l( - input_data["patent_score"], - input_data["popularity_score"], - input_data["infringement_score"], + step = await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=2.1, + step_name="经济价值B1计算", + step_description="开始计算经济价值B1,公式为:基础价值B11 × (1 + 流量因子B12) × 政策乘数B13", + input_params=input_data, + status="in_progress" + ) ) + try: + # 计算基础价值B11 + basic_value_b11 = self.basic_value_calculator.calculate_basic_value_b11( + self.basic_value_calculator.calculate_financial_value_f(input_data["three_year_income"]), + self.basic_value_calculator.calculate_legal_strength_l(input_data["patent_score"], input_data["popularity_score"], input_data["infringement_score"]), + self.basic_value_calculator.calculate_development_potential_d(input_data["patent_count"], input_data["esg_score"], input_data["innovation_ratio"]), + input_data["industry_coefficient"] + ) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, step_order=2.11, step_name="基础价值B11", + output_result={'basic_value_b11': basic_value_b11}, status="completed" + ) + ) - # 发展潜力 patent_count: 专利分 (0-10分) (用户填写) - # esg_score: ESG分 (0-10分) (用户填写) - # innovation_ratio: 创新投入比 (研发费用/营收) * 100 (用户填写) - development_potential = self.basic_value_calculator.calculate_development_potential_d( + # 计算流量因子B12 + traffic_factor_b12 = self.traffic_factor_calculator.calculate_traffic_factor_b12( + input_data['search_index_s1'], + input_data['industry_average_s2'], + self.traffic_factor_calculator.calculate_social_media_spread_s3( + self.traffic_factor_calculator.calculate_interaction_index(input_data["likes"], input_data["comments"], input_data["shares"]), + self.traffic_factor_calculator.calculate_coverage_index(0), + self.traffic_factor_calculator.calculate_conversion_efficiency(input_data["sales_volume"], input_data["link_views"]) + ) + ) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, step_order=2.12, step_name="流量因子B12", + output_result={'traffic_factor_b12': traffic_factor_b12}, status="completed" + ) + ) - input_data["patent_count"], - input_data["esg_score"], - input_data["innovation_ratio"], - ) - # 计算行业系数I target_industry_roe: 目标行业平均ROE (系统配置) - # benchmark_industry_roe: 基准行业ROE (系统配置) - # industry_coefficient = self.basic_value_calculator.calculate_industry_coefficient_i( - # - # ) - # 计算基础价值B11 - basic_value_b11 = self.basic_value_calculator.calculate_basic_value_b11( - financial_value, # 财务价值F - legal_strength, # 法律强度L - development_potential, - input_data["industry_coefficient"] - ) + # 计算政策乘数B13 + policy_multiplier_b13 = self.policy_multiplier_calculator.calculate_policy_multiplier_b13( + self.policy_multiplier_calculator.calculate_policy_compatibility_score( + input_data["policy_match_score"], input_data["implementation_stage"], input_data["funding_support"] + ) + ) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, step_order=2.13, step_name="政策乘数B13", + output_result={'policy_multiplier_b13': policy_multiplier_b13}, status="completed" + ) + ) - # 计算互动量指数 - interaction_index = self.traffic_factor_calculator.calculate_interaction_index( - input_data["likes"], - input_data["comments"], - input_data["shares"], - ) - # 计算覆盖人群指数 - coverage_index = self.traffic_factor_calculator.calculate_coverage_index(0) - # 计算转化率 - conversion_efficiency = self.traffic_factor_calculator.calculate_conversion_efficiency( - input_data["sales_volume"], input_data["link_views"]) - - social_media_spread_s3 = self.traffic_factor_calculator.calculate_social_media_spread_s3(interaction_index, - coverage_index, - conversion_efficiency) - - traffic_factor_b12 = self.traffic_factor_calculator.calculate_traffic_factor_b12( - input_data['search_index_s1'], - input_data['industry_average_s2'], - social_media_spread_s3 - ) - - # 计算政策乘数B13 - policy_compatibility_score = self.policy_multiplier_calculator.calculate_policy_compatibility_score( - input_data["policy_match_score"], - input_data["implementation_stage"], - input_data["funding_support"]) - policy_multiplier_b13 = self.policy_multiplier_calculator.calculate_policy_multiplier_b13( - policy_compatibility_score - ) - - # 计算经济价值B1 - economic_value_b1 = self.calculate_economic_value_b1( - basic_value_b11, - traffic_factor_b12, - policy_multiplier_b13 - ) - - return { - 'basic_value_b11': basic_value_b11, - 'traffic_factor_b12': traffic_factor_b12, - 'policy_multiplier_b13': policy_multiplier_b13, - 'economic_value_b1': economic_value_b1 - } + # 计算经济价值B1 + economic_value_b1 = self.calculate_economic_value_b1( + basic_value_b11, + traffic_factor_b12, + policy_multiplier_b13 + ) + await self.valuation_controller.update_calculation_step( + step.id, {"status": "completed", "output_result": {"economic_value_b1": economic_value_b1}} + ) + return economic_value_b1 + except Exception as e: + error_message = f"经济价值B1计算失败: {e}" + await self.valuation_controller.update_calculation_step( + step.id, {"status": "failed", "error_message": error_message} + ) + raise # 示例使用 if __name__ == "__main__": diff --git a/app/utils/calculation_engine/economic_value_b1/sub_formulas/basic_value_b11.py b/app/utils/calculation_engine/economic_value_b1/sub_formulas/basic_value_b11.py index ab643d4..1d670ff 100644 --- a/app/utils/calculation_engine/economic_value_b1/sub_formulas/basic_value_b11.py +++ b/app/utils/calculation_engine/economic_value_b1/sub_formulas/basic_value_b11.py @@ -1,13 +1,25 @@ import math from typing import List, Optional +import sys +import os +# 添加项目根目录到Python路径 +current_dir = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(os.path.join(current_dir, '..', '..', '..', '..')) + +try: + from app.controllers.valuation import ValuationController + from app.schemas.valuation import ValuationCalculationStepCreate +except ImportError: + # 处理可能的导入错误 + pass class BasicValueB11Calculator: """基础价值B11计算器""" def __init__(self): """初始化计算器""" - pass + self.valuation_controller = ValuationController() def calculate_basic_value_b11(self, financial_value: float, @@ -168,94 +180,146 @@ class BasicValueB11Calculator: return industry_coefficient + def _calculate_patent_score(self, patent_remaining_years: int) -> float: + """ + 计算专利分 -# 专利相关计算函数 -def calculate_patent_score(patent_remaining_years: int) -> float: - """ - 计算专利分 + 专利剩余保护期评分标准: + - >10年: 10分 + - 5-10年: 7分 + - <5年: 3分 - 专利剩余保护期评分标准: - - >10年: 10分 - - 5-10年: 7分 - - <5年: 3分 + args: + patent_remaining_years: 专利剩余保护期(年) (用户填写) - args: - patent_remaining_years: 专利剩余保护期(年) (用户填写) + returns: + float: 专利分 + """ + if patent_remaining_years > 10: + return 10.0 + elif patent_remaining_years >= 5: + return 7.0 + else: + return 3.0 - returns: - float: 专利分 - """ - if patent_remaining_years > 10: - return 10.0 - elif patent_remaining_years >= 5: - return 7.0 - else: - return 3.0 + def _calculate_patent_usage_score(self, patent_count: int) -> float: + """ + 计算专利使用量分 + 专利使用量评分标准: + - 未引用: 0分 + - 每引用一项: +2.5分 + - 10分封顶 -# 识别用户所上传的图像中的专利号,通过API验证专利是否存在,按所用专利数量赋分,未引用0分,每引用一项+2.5分,10分封顶(0-10分) -def calculate_patent_usage_score(patent_count: int) -> float: - """ - 计算专利使用量分 + args: + patent_count: 专利数量 (用户填写) - 专利使用量评分标准: - - 未引用: 0分 - - 每引用一项: +2.5分 - - 10分封顶 + returns: + float: 专利使用量分 + """ + score = min(patent_count * 2.5, 10.0) + return score - args: - patent_count: 专利数量 (用户填写) + def _calculate_popularity_score(self, region_coverage: str) -> float: + """ + 计算普及地域分 - returns: - float: 专利使用量分 - """ - score = min(patent_count * 2.5, 10.0) - return score + 全球覆盖(10分),全国覆盖(7分),区域覆盖(4分) + args: + region_coverage: 普及地域类型 (用户填写) -# 普及地域评分 -def calculate_popularity_score(region_coverage: str) -> float: - """ - 计算普及地域分 + returns: + float: 普及地域分 + """ + coverage_scores = { + "全球覆盖": 10.0, + "全国覆盖": 7.0, + "区域覆盖": 4.0 + } - 全球覆盖(10分),全国覆盖(7分),区域覆盖(4分) + return coverage_scores.get(region_coverage, 7.0) - args: - region_coverage: 普及地域类型 (用户填写) + def _calculate_infringement_score(self, infringement_status: str) -> float: + """ + 计算侵权记录分 - returns: - float: 普及地域分 - """ - coverage_scores = { - "全球覆盖": 10.0, - "全国覆盖": 7.0, - "区域覆盖": 4.0 - } + 无侵权记录(10分),历史侵权已解决(6分),现存纠纷(2分) - return coverage_scores.get(region_coverage, 7.0) + args: + infringement_status: 侵权记录状态 (用户填写) + returns: + float: 侵权记录分 + """ + infringement_scores = { + "无侵权记录": 10.0, + "历史侵权已解决": 6.0, + "现存纠纷": 2.0 + } -# 侵权记录评分 -def calculate_infringement_score(infringement_status: str) -> float: - """ - 计算侵权记录分 + return infringement_scores.get(infringement_status, 6.0) - 无侵权记录(10分),历史侵权已解决(6分),现存纠纷(2分) + async def calculate_complete_basic_value_b11(self, valuation_id: int, input_data: dict) -> float: + step = await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=2.11, + step_name="基础价值B11计算", + step_description="开始计算基础价值B11", + input_params=input_data, + status="in_progress" + ) + ) + try: + financial_value = self.calculate_financial_value_f(input_data['annual_revenue_3_years']) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, step_order=2.111, step_name="财务价值F", + output_result={'financial_value': financial_value}, status="completed" + ) + ) - args: - infringement_status: 侵权记录状态 (用户填写) + patent_score = self._calculate_patent_score(input_data['patent_remaining_years']) + popularity_score = self._calculate_popularity_score(input_data['region_coverage']) + infringement_score = self._calculate_infringement_score(input_data['infringement_status']) + legal_strength = self.calculate_legal_strength_l(patent_score, popularity_score, infringement_score) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, step_order=2.112, step_name="法律强度L", + output_result={'legal_strength': legal_strength}, status="completed" + ) + ) - returns: - float: 侵权记录分 - """ - infringement_scores = { - "无侵权记录": 10.0, - "历史侵权已解决": 6.0, - "现存纠纷": 2.0 - } + patent_usage_score = self._calculate_patent_usage_score(input_data['patent_count']) + development_potential = self.calculate_development_potential_d(patent_usage_score, input_data['esg_score'], input_data['innovation_ratio']) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, step_order=2.113, step_name="发展潜力D", + output_result={'development_potential': development_potential}, status="completed" + ) + ) - return infringement_scores.get(infringement_status, 6.0) + industry_coefficient = self.calculate_industry_coefficient_i(input_data['target_industry_roe'], input_data['benchmark_industry_roe']) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, step_order=2.114, step_name="行业系数I", + output_result={'industry_coefficient': industry_coefficient}, status="completed" + ) + ) + basic_value_b11 = self.calculate_basic_value_b11(financial_value, legal_strength, development_potential, industry_coefficient) + + await self.valuation_controller.update_calculation_step( + step.id, {"status": "completed", "output_result": {"basic_value_b11": basic_value_b11}} + ) + return basic_value_b11 + except Exception as e: + error_message = f"基础价值B11计算失败: {e}" + await self.valuation_controller.update_calculation_step( + step.id, {"status": "failed", "error_message": error_message} + ) + raise # 示例使用 if __name__ == "__main__": diff --git a/app/utils/calculation_engine/economic_value_b1/sub_formulas/policy_multiplier_b13.py b/app/utils/calculation_engine/economic_value_b1/sub_formulas/policy_multiplier_b13.py index 21e3540..7d56264 100644 --- a/app/utils/calculation_engine/economic_value_b1/sub_formulas/policy_multiplier_b13.py +++ b/app/utils/calculation_engine/economic_value_b1/sub_formulas/policy_multiplier_b13.py @@ -1,15 +1,28 @@ +import sys +import os + +# 添加项目根目录到Python路径 +current_dir = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(os.path.join(current_dir, '..', '..', '..', '..')) + +try: + from app.controllers.valuation import ValuationController + from app.schemas.valuation import ValuationCalculationStepCreate +except ImportError: + # 处理可能的导入错误 + pass + class PolicyMultiplierB13Calculator: """政策乘数B13计算器""" def __init__(self): """初始化计算器""" - pass + self.valuation_controller = ValuationController() def calculate_policy_multiplier_b13(self, policy_compatibility_score: float) -> float: """ 计算政策乘数B13 - 政策乘数B13 = 1 + (政策契合度评分P × 0.15) Args: @@ -18,9 +31,7 @@ class PolicyMultiplierB13Calculator: returns: float: 政策乘数B13 """ - # policy_multiplier = 1 + (policy_compatibility_score * 0.15) - return policy_multiplier def calculate_policy_compatibility_score(self, @@ -30,7 +41,6 @@ class PolicyMultiplierB13Calculator: """ 计算政策契合度评分P - 政策契合度P = 政策匹配度 × 0.4 + 实施阶段评分 × 0.3 + 资金支持度 × 0.3 Args: @@ -41,11 +51,9 @@ class PolicyMultiplierB13Calculator: returns: float: 政策契合度评分P """ - # policy_compatibility = (policy_match_score * 0.4 + implementation_stage_score * 0.3 + funding_support_score * 0.3) - return policy_compatibility def calculate_policy_match_score(self, industry: str) -> float: @@ -60,8 +68,8 @@ class PolicyMultiplierB13Calculator: returns: float: 政策匹配度 """ - - return 5 + # 此处应有更复杂的逻辑根据行业匹配政策,暂时返回固定值 + return 5.0 def calculate_implementation_stage_score(self, implementation_stage: str) -> float: """ @@ -80,8 +88,7 @@ class PolicyMultiplierB13Calculator: "推广阶段": 7.0, "试点阶段": 4.0 } - - return stage_scores.get(implementation_stage, 10.0) + return stage_scores.get(implementation_stage, 7.0) def calculate_funding_support_score(self, funding_support: str) -> float: """ @@ -100,8 +107,44 @@ class PolicyMultiplierB13Calculator: "省级资助": 7.0, "无资助": 0.0 } + return funding_scores.get(funding_support, 7.0) - return funding_scores.get(funding_support, 0.0) + async def calculate_complete_policy_multiplier_b13(self, valuation_id: int, input_data: dict) -> float: + step = await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=2.13, + step_name="政策乘数B13计算", + step_description="开始计算政策乘数B13", + input_params=input_data, + status="in_progress" + ) + ) + try: + policy_match_score = self.calculate_policy_match_score(input_data['industry']) + implementation_stage_score = self.calculate_implementation_stage_score(input_data['implementation_stage']) + funding_support_score = self.calculate_funding_support_score(input_data['funding_support']) + + policy_compatibility_score = self.calculate_policy_compatibility_score(policy_match_score, implementation_stage_score, funding_support_score) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, step_order=2.131, step_name="政策契合度评分P", + output_result={'policy_compatibility_score': policy_compatibility_score}, status="completed" + ) + ) + + policy_multiplier_b13 = self.calculate_policy_multiplier_b13(policy_compatibility_score) + + await self.valuation_controller.update_calculation_step( + step.id, {"status": "completed", "output_result": {"policy_multiplier_b13": policy_multiplier_b13}} + ) + return policy_multiplier_b13 + except Exception as e: + error_message = f"政策乘数B13计算失败: {e}" + await self.valuation_controller.update_calculation_step( + step.id, {"status": "failed", "error_message": error_message} + ) + raise # 示例使用 diff --git a/app/utils/calculation_engine/economic_value_b1/sub_formulas/traffic_factor_b12.py b/app/utils/calculation_engine/economic_value_b1/sub_formulas/traffic_factor_b12.py index dbca9f5..74a203f 100644 --- a/app/utils/calculation_engine/economic_value_b1/sub_formulas/traffic_factor_b12.py +++ b/app/utils/calculation_engine/economic_value_b1/sub_formulas/traffic_factor_b12.py @@ -1,14 +1,25 @@ import math from typing import Dict, Tuple +import sys +import os +# 添加项目根目录到Python路径 +current_dir = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(os.path.join(current_dir, '..', '..', '..', '..')) +try: + from app.controllers.valuation import ValuationController + from app.schemas.valuation import ValuationCalculationStepCreate +except ImportError: + # 处理可能的导入错误 + pass class TrafficFactorB12Calculator: """流量因子B12计算器""" def __init__(self): """初始化计算器""" - pass + self.valuation_controller = ValuationController() def calculate_traffic_factor_b12(self, search_index_s1: float, @@ -31,18 +42,15 @@ class TrafficFactorB12Calculator: if industry_average_s2 == 0: raise ValueError("行业均值S2必须大于0") - if search_index_s1 == 0: + if search_index_s1 <= 0: # 如果搜索指数为0或负数,使用最小值避免对数计算错误 search_index_s1 = 1.0 - # ,不进行任何拆分 traffic_factor = (math.log(search_index_s1 / industry_average_s2) * 0.3 + social_media_spread_s3 * 0.7) return traffic_factor - - def calculate_social_media_spread_s3(self, interaction_index: float, coverage_index: float, @@ -60,7 +68,6 @@ class TrafficFactorB12Calculator: returns: float: 社交媒体传播度S3 """ - # social_media_spread = (interaction_index * 0.4 + coverage_index * 0.3 + conversion_efficiency * 0.3) @@ -84,7 +91,6 @@ class TrafficFactorB12Calculator: returns: float: 互动量指数 """ - # interaction_index = (likes + comments + shares) / 1000.0 return interaction_index @@ -101,11 +107,45 @@ class TrafficFactorB12Calculator: returns: float: 覆盖人群指数 """ - # if followers == 0: - return 0 + return 0.0 coverage_index = followers / 10000.0 return coverage_index + + async def calculate_complete_traffic_factor_b12(self, valuation_id: int, input_data: dict) -> float: + step = await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=2.12, + step_name="流量因子B12计算", + step_description="开始计算流量因子B12", + input_params=input_data, + status="in_progress" + ) + ) + try: + interaction_index = self.calculate_interaction_index(input_data['likes'], input_data['comments'], input_data['shares']) + coverage_index = self.calculate_coverage_index(input_data['followers']) + social_media_spread_s3 = self.calculate_social_media_spread_s3(interaction_index, coverage_index, input_data['conversion_efficiency']) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, step_order=2.121, step_name="社交媒体传播度S3", + output_result={'social_media_spread_s3': social_media_spread_s3}, status="completed" + ) + ) + + traffic_factor_b12 = self.calculate_traffic_factor_b12(input_data['search_index_s1'], input_data['industry_average_s2'], social_media_spread_s3) + + await self.valuation_controller.update_calculation_step( + step.id, {"status": "completed", "output_result": {"traffic_factor_b12": traffic_factor_b12}} + ) + return traffic_factor_b12 + except Exception as e: + error_message = f"流量因子B12计算失败: {e}" + await self.valuation_controller.update_calculation_step( + step.id, {"status": "failed", "error_message": error_message} + ) + raise def calculate_conversion_efficiency(self, click_count: int, diff --git a/app/utils/calculation_engine/final_value_ab/final_value_a.py b/app/utils/calculation_engine/final_value_ab/final_value_a.py index 80c7e6a..3282b05 100644 --- a/app/utils/calculation_engine/final_value_ab/final_value_a.py +++ b/app/utils/calculation_engine/final_value_ab/final_value_a.py @@ -19,10 +19,14 @@ try: # 包内相对导入 from .model_value_b import ModelValueBCalculator from ..market_value_c import MarketValueCCalculator + from app.controllers.valuation import ValuationController + from app.schemas.valuation import ValuationCalculationStepCreate except ImportError: # 直接运行时的绝对导入 from app.utils.calculation_engine.final_value_ab.model_value_b import ModelValueBCalculator from app.utils.calculation_engine.market_value_c import MarketValueCCalculator + from app.controllers.valuation import ValuationController + from app.schemas.valuation import ValuationCalculationStepCreate class FinalValueACalculator: @@ -32,6 +36,7 @@ class FinalValueACalculator: """初始化计算器""" self.model_value_calculator = ModelValueBCalculator() self.market_value_calculator = MarketValueCCalculator() + self.valuation_controller = ValuationController() def calculate_final_value_a(self, model_value_b: float, @@ -64,17 +69,32 @@ class FinalValueACalculator: return final_value - async def calculate_complete_final_value_a(self, input_data: Dict) -> Dict: + async def calculate_complete_final_value_a(self, valuation_id: int, input_data: Dict) -> float: """ - 计算完整的最终估值A,包含所有子模块 + 计算完整的最终估值A,并记录每一步的计算过程。 - input_data: 输入数据字典,包含所有必要的参数 - + 该函数作为最终估值计算的入口,协调调用模型估值B和市场估值C的计算, + 并将计算过程中的关键步骤(如子模块的调用、输入、输出)持久化, + 以便于后续的审计和追溯。 - 包含所有中间计算结果和最终结果的字典 + Args: + valuation_id (int): 本次估值的唯一标识符,用于关联所有计算步骤。 + input_data (Dict): 包含所有计算所需参数的字典,结构如下: + { + 'model_data': { ... }, # 模型估值B所需数据 + 'market_data': { ... } # 市场估值C所需数据 + } + + Returns: + float: 计算得出的最终估值A。 + + Raises: + Exception: 在计算过程中遇到的任何异常都会被重新抛出, + 并在记录最后一步为“计算失败”后终止。 """ import time start_time = time.time() + step_order = 1 # 记录输入参数 logger.info("final_value_a.calculation_start input_data_keys={} model_data_keys={} market_data_keys={}", @@ -82,61 +102,74 @@ class FinalValueACalculator: list(input_data.get('model_data', {}).keys()), list(input_data.get('market_data', {}).keys())) - # 详细记录模型数据参数 - model_data = input_data.get('model_data', {}) - if 'economic_data' in model_data: - economic_data = model_data['economic_data'] - logger.info("final_value_a.economic_data 经济价值B1参数: 近三年机构收益={} 专利分={} 普及地域分={} 侵权分={} 创新投入比={} ESG分={} 专利使用量={} 行业修正系数={}", - economic_data.get('three_year_income'), - economic_data.get('patent_score'), - economic_data.get('popularity_score'), - economic_data.get('infringement_score'), - economic_data.get('innovation_ratio'), - economic_data.get('esg_score'), - economic_data.get('patent_count'), - economic_data.get('industry_coefficient')) - - if 'cultural_data' in model_data: - cultural_data = model_data['cultural_data'] - logger.info("final_value_a.cultural_data 文化价值B2参数: 传承人等级系数={} 跨境深度={} 线下教学次数={} 抖音浏览量={} 快手浏览量={} 哔哩哔哩浏览量={} 结构复杂度={} 归一化信息熵={} 历史传承度={}", - cultural_data.get('inheritor_level_coefficient'), - cultural_data.get('cross_border_depth'), - cultural_data.get('offline_sessions'), - cultural_data.get('douyin_views'), - cultural_data.get('kuaishou_views'), - cultural_data.get('bilibili_views'), - cultural_data.get('structure_complexity'), - cultural_data.get('normalized_entropy'), - cultural_data.get('historical_inheritance')) - - if 'risky_data' in model_data: - risky_data = model_data['risky_data'] - logger.info("final_value_a.risky_data 风险调整B3参数: 最高价={} 最低价={} 诉讼状态={} 传承人年龄={}", - risky_data.get('highest_price'), - risky_data.get('lowest_price'), - risky_data.get('lawsuit_status'), - risky_data.get('inheritor_ages')) - - # 详细记录市场数据参数 - market_data = input_data.get('market_data', {}) - logger.info("final_value_a.market_data 市场估值C参数: 平均交易价={} 手动出价={} 专家估值={} 日浏览量={} 收藏数量={} 发行等级={} 最近市场活动={}", - market_data.get('average_transaction_price'), - market_data.get('manual_bids'), - market_data.get('expert_valuations'), - market_data.get('daily_browse_volume'), - market_data.get('collection_count'), - market_data.get('issuance_level'), - market_data.get('recent_market_activity')) - - # 计算模型估值B - logger.info("final_value_a.calculating_model_value_b 开始计算模型估值B") - model_start_time = time.time() - + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=step_order, + step_name="开始计算最终估值A", + step_description="接收输入参数,准备开始计算。", + input_params=input_data, + status="processing" + ) + ) + step_order += 1 + try: - model_result = self.model_value_calculator.calculate_complete_model_value_b( + # 详细记录模型数据参数 + model_data = input_data.get('model_data', {}) + if 'economic_data' in model_data: + economic_data = model_data['economic_data'] + logger.info("final_value_a.economic_data 经济价值B1参数: 近三年机构收益={} 专利分={} 普及地域分={} 侵权分={} 创新投入比={} ESG分={} 专利使用量={} 行业修正系数={}", + economic_data.get('three_year_income'), + economic_data.get('patent_score'), + economic_data.get('popularity_score'), + economic_data.get('infringement_score'), + economic_data.get('innovation_ratio'), + economic_data.get('esg_score'), + economic_data.get('patent_count'), + economic_data.get('industry_coefficient')) + + if 'cultural_data' in model_data: + cultural_data = model_data['cultural_data'] + logger.info("final_value_a.cultural_data 文化价值B2参数: 传承人等级系数={} 跨境深度={} 线下教学次数={} 抖音浏览量={} 快手浏览量={} 哔哩哔哩浏览量={} 结构复杂度={} 归一化信息熵={} 历史传承度={}", + cultural_data.get('inheritor_level_coefficient'), + cultural_data.get('cross_border_depth'), + cultural_data.get('offline_sessions'), + cultural_data.get('douyin_views'), + cultural_data.get('kuaishou_views'), + cultural_data.get('bilibili_views'), + cultural_data.get('structure_complexity'), + cultural_data.get('normalized_entropy'), + cultural_data.get('historical_inheritance')) + + if 'risky_data' in model_data: + risky_data = model_data['risky_data'] + logger.info("final_value_a.risky_data 风险调整B3参数: 最高价={} 最低价={} 诉讼状态={} 传承人年龄={}", + risky_data.get('highest_price'), + risky_data.get('lowest_price'), + risky_data.get('lawsuit_status'), + risky_data.get('inheritor_ages')) + + # 详细记录市场数据参数 + market_data = input_data.get('market_data', {}) + logger.info("final_value_a.market_data 市场估值C参数: 平均交易价={} 手动出价={} 专家估值={} 日浏览量={} 收藏数量={} 发行等级={} 最近市场活动={}", + market_data.get('average_transaction_price'), + market_data.get('manual_bids'), + market_data.get('expert_valuations'), + market_data.get('daily_browse_volume'), + market_data.get('collection_count'), + market_data.get('issuance_level'), + market_data.get('recent_market_activity')) + + # 计算模型估值B + logger.info("final_value_a.calculating_model_value_b 开始计算模型估值B") + model_start_time = time.time() + + model_result = await self.model_value_calculator.calculate_complete_model_value_b( + valuation_id, input_data['model_data'] ) - model_value_b = model_result['model_value_b'] + model_value_b = model_result if isinstance(model_result, (int, float)) else model_result.get('model_value_b') model_duration = time.time() - model_start_time logger.info("final_value_a.model_value_b_calculated 模型估值B计算完成: 模型估值B={}万元 耗时={}ms 返回字段={}", @@ -144,19 +177,28 @@ class FinalValueACalculator: int(model_duration * 1000), list(model_result.keys())) - except Exception as e: - logger.error("final_value_a.model_value_b_calculation_failed 模型估值B计算失败: 错误={} 输入数据={}", str(e), input_data.get('model_data', {})) - raise - - # 计算市场估值C - logger.info("final_value_a.calculating_market_value_c 开始计算市场估值C") - market_start_time = time.time() - - try: + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=step_order, + step_name="计算模型估值B", + step_description="调用ModelValueBCalculator计算模型估值B。", + input_params=input_data.get('model_data', {}), + output_result=model_result, + status="completed" + ) + ) + step_order += 1 + + # 计算市场估值C + logger.info("final_value_a.calculating_market_value_c 开始计算市场估值C") + market_start_time = time.time() + market_result = await self.market_value_calculator.calculate_complete_market_value_c( + valuation_id, input_data['market_data'] ) - market_value_c = market_result['market_value_c'] + market_value_c = market_result if isinstance(market_result, (int, float)) else market_result.get('market_value_c') market_duration = time.time() - market_start_time logger.info("final_value_a.market_value_c_calculated 市场估值C计算完成: 市场估值C={}万元 耗时={}ms 返回字段={}", @@ -164,15 +206,23 @@ class FinalValueACalculator: int(market_duration * 1000), list(market_result.keys())) - except Exception as e: - logger.error("final_value_a.market_value_c_calculation_failed 市场估值C计算失败: 错误={} 输入数据={}", str(e), input_data.get('market_data', {})) - raise - - # 计算最终估值A - logger.info("final_value_a.calculating_final_value_a 开始计算最终估值A: 模型估值B={}万元 市场估值C={}万元", - model_value_b, market_value_c) - - try: + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=step_order, + step_name="计算市场估值C", + step_description="调用MarketValueCCalculator计算市场估值C。", + input_params=input_data.get('market_data', {}), + output_result=market_result, + status="completed" + ) + ) + step_order += 1 + + # 计算最终估值A + logger.info("final_value_a.calculating_final_value_a 开始计算最终估值A: 模型估值B={}万元 市场估值C={}万元", + model_value_b, market_value_c) + final_value_a = self.calculate_final_value_a( model_value_b, market_value_c @@ -188,16 +238,37 @@ class FinalValueACalculator: int(model_duration * 1000), int(market_duration * 1000)) - except Exception as e: - logger.error("final_value_a.final_value_calculation_failed 最终估值A计算失败: 错误={} 模型估值B={}万元 市场估值C={}万元", - str(e), model_value_b, market_value_c) - raise + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=step_order, + step_name="计算最终估值A", + step_description="最终估值A = 模型估值B × 0.7 + 市场估值C × 0.3", + input_params={"model_value_b": model_value_b, "market_value_c": market_value_c}, + output_result={"final_value_a": final_value_a}, + status="completed" + ) + ) + return { + "model_value_b": model_value_b, + "market_value_c": market_value_c, + "final_value_ab": final_value_a, + } - return { - 'model_value_b': model_value_b, - 'market_value_c': market_value_c, - 'final_value_ab': final_value_a, - } + except Exception as e: + logger.error("final_value_a.calculation_failed 计算失败: 错误={}", str(e)) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=step_order, + step_name="计算失败", + step_description="计算过程中发生错误。", + status="failed", + error_message=str(e) + ) + ) + raise + diff --git a/app/utils/calculation_engine/final_value_ab/model_value_b.py b/app/utils/calculation_engine/final_value_ab/model_value_b.py index 1dbf4dc..decf65a 100644 --- a/app/utils/calculation_engine/final_value_ab/model_value_b.py +++ b/app/utils/calculation_engine/final_value_ab/model_value_b.py @@ -12,10 +12,14 @@ try: # 相对导入(当作为包使用时) from ..economic_value_b1.economic_value_b1 import EconomicValueB1Calculator from ..cultural_value_b2.cultural_value_b2 import CulturalValueB2Calculator + from app.controllers.valuation import ValuationController + from app.schemas.valuation import ValuationCalculationStepCreate except ImportError: # 绝对导入(当直接运行时) from app.utils.calculation_engine.economic_value_b1.economic_value_b1 import EconomicValueB1Calculator from app.utils.calculation_engine.cultural_value_b2.cultural_value_b2 import CulturalValueB2Calculator + from app.controllers.valuation import ValuationController + from app.schemas.valuation import ValuationCalculationStepCreate class ModelValueBCalculator: @@ -26,6 +30,7 @@ class ModelValueBCalculator: self.economic_value_calculator = EconomicValueB1Calculator() self.cultural_value_calculator = CulturalValueB2Calculator() self.risk_adjustment_calculator = RiskAdjustmentB3Calculator() + self.valuation_controller = ValuationController() def calculate_model_value_b(self, economic_value_b1: float, @@ -46,45 +51,127 @@ class ModelValueBCalculator: return model_value - def calculate_complete_model_value_b(self, input_data: Dict) -> Dict: + async def calculate_complete_model_value_b(self, valuation_id: int, input_data: Dict) -> float: """ - 计算完整的模型估值B,包含所有子公式 - + 计算完整的模型估值B,并记录详细的计算步骤。 + + 此函数通过依次调用经济价值B1、文化价值B2和风险调整系数B3的计算器, + 完成模型估值B的全面计算。每一步的计算(包括子模块的调用、输入、输出) + 都会被记录下来,用于后续的分析和审计。 + Args: - input_data: 输入数据字典,包含所有必要的参数 - + valuation_id (int): 估值的唯一标识符,用于关联所有计算步骤。 + input_data (Dict): 包含所有计算所需参数的字典,结构应包含: + { + 'economic_data': { ... }, # 经济价值B1所需数据 + 'cultural_data': { ... }, # 文化价值B2所需数据 + 'risky_data': { ... } # 风险调整系数B3所需数据 + } + Returns: - Dict: 包含所有中间计算结果和最终结果的字典 + float: 计算得出的模型估值B。 + + Raises: + Exception: 在计算过程中遇到的任何异常都会被捕获、记录,然后重新抛出。 """ - # 计算经济价值B1 - economic_result = self.economic_value_calculator.calculate_complete_economic_value_b1( - input_data['economic_data'] + step_order = 1 + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=step_order, + step_name="开始计算模型估值B", + step_description="接收输入参数,准备开始计算。", + input_params=input_data, + status="processing" + ) ) - economic_value_b1 = economic_result['economic_value_b1'] + step_order += 1 - # 计算文化价值B2 - cultural_result = self.cultural_value_calculator.calculate_complete_cultural_value_b2( - input_data['cultural_data'] - ) - cultural_value_b2 = cultural_result['cultural_value_b2'] + try: + # 计算经济价值B1(传入估值ID并等待异步完成) + economic_value_b1 = await self.economic_value_calculator.calculate_complete_economic_value_b1( + valuation_id, + input_data['economic_data'] + ) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=step_order, + step_name="计算经济价值B1", + step_description="调用EconomicValueB1Calculator计算经济价值B1。", + input_params=input_data.get('economic_data', {}), + output_result={"economic_value_b1": economic_value_b1}, + status="completed" + ) + ) + step_order += 1 - risk_value_result = self.risk_adjustment_calculator.calculate_complete_risky_value_b3( - input_data['risky_data'] - ) - risk_value_b3 = risk_value_result['risk_adjustment_b3'] - # 计算模型估值B - model_value_b = self.calculate_model_value_b( - economic_value_b1, - cultural_value_b2, - risk_value_b3 - ) + # 计算文化价值B2(传入估值ID并等待异步完成) + cultural_value_b2 = await self.cultural_value_calculator.calculate_complete_cultural_value_b2( + valuation_id, + input_data['cultural_data'] + ) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=step_order, + step_name="计算文化价值B2", + step_description="调用CulturalValueB2Calculator计算文化价值B2。", + input_params=input_data.get('cultural_data', {}), + output_result={"cultural_value_b2": cultural_value_b2}, + status="completed" + ) + ) + step_order += 1 - return { - 'economic_value_b1': economic_value_b1, - 'cultural_value_b2': cultural_value_b2, - 'risk_value_b3': risk_value_b3, - 'model_value_b': model_value_b, - } + # 计算风险调整系数B3(传入估值ID并等待异步完成) + risk_value_b3 = await self.risk_adjustment_calculator.calculate_complete_risky_value_b3( + valuation_id, + input_data['risky_data'] + ) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=step_order, + step_name="计算风险调整系数B3", + step_description="调用RiskAdjustmentB3Calculator计算风险调整系数B3。", + input_params=input_data.get('risky_data', {}), + output_result={"risk_adjustment_b3": risk_value_b3}, + status="completed" + ) + ) + step_order += 1 + + # 计算模型估值B + model_value_b = self.calculate_model_value_b( + economic_value_b1, + cultural_value_b2, + risk_value_b3 + ) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=step_order, + step_name="计算模型估值B", + step_description="模型估值B = (经济价值B1*0.7+文化价值B2*0.3)*风险调整系数B3", + input_params={"economic_value_b1": economic_value_b1, "cultural_value_b2": cultural_value_b2, "risk_value_b3": risk_value_b3}, + output_result={"model_value_b": model_value_b}, + status="completed" + ) + ) + return model_value_b + except Exception as e: + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=step_order, + step_name="计算失败", + step_description="计算过程中发生错误。", + status="failed", + error_message=str(e) + ) + ) + raise # 示例使用 diff --git a/app/utils/calculation_engine/market_value_c/market_value_c.py b/app/utils/calculation_engine/market_value_c/market_value_c.py index 71752b6..8227420 100644 --- a/app/utils/calculation_engine/market_value_c/market_value_c.py +++ b/app/utils/calculation_engine/market_value_c/market_value_c.py @@ -8,6 +8,9 @@ import logging current_dir = os.path.dirname(os.path.abspath(__file__)) if current_dir not in sys.path: sys.path.append(current_dir) + # 添加项目根目录 + sys.path.append(os.path.join(current_dir, '..', '..', '..')) + try: # 相对导入(当作为包使用时) @@ -16,6 +19,8 @@ try: from .sub_formulas.scarcity_multiplier_c3 import ScarcityMultiplierC3Calculator from .sub_formulas.temporal_decay_c4 import TemporalDecayC4Calculator from .market_data_analyzer import market_data_analyzer + from app.controllers.valuation import ValuationController + from app.schemas.valuation import ValuationCalculationStepCreate except ImportError: # 绝对导入(当直接运行时) from sub_formulas.market_bidding_c1 import MarketBiddingC1Calculator @@ -23,6 +28,8 @@ except ImportError: from sub_formulas.scarcity_multiplier_c3 import ScarcityMultiplierC3Calculator from sub_formulas.temporal_decay_c4 import TemporalDecayC4Calculator from market_data_analyzer import market_data_analyzer + from app.controllers.valuation import ValuationController + from app.schemas.valuation import ValuationCalculationStepCreate logger = logging.getLogger(__name__) @@ -36,6 +43,7 @@ class MarketValueCCalculator: self.heat_coefficient_calculator = HeatCoefficientC2Calculator() self.scarcity_multiplier_calculator = ScarcityMultiplierC3Calculator() self.temporal_decay_calculator = TemporalDecayC4Calculator() + self.valuation_controller = ValuationController() async def _get_dynamic_default_price(self, input_data: Dict) -> float: """ @@ -95,62 +103,109 @@ class MarketValueCCalculator: return market_value - async def calculate_complete_market_value_c(self, input_data: Dict) -> Dict: + async def calculate_complete_market_value_c(self, valuation_id: int, input_data: Dict) -> float: """ - 计算完整的市场估值C,包含所有子公式 - - args: - input_data: 输入数据字典,包含所有必要的参数 - 参数来源标记(用户填写/系统配置/API获取/系统计算): - - average_transaction_price: 系统计算(基于用户填写/API获取) - - market_activity_coefficient: 系统计算(基于用户填写) - - daily_browse_volume: API获取/系统估算 - - collection_count: API获取/系统估算 - - issuance_level: 用户填写 - - recent_market_activity: 用户填写 - - issuance_scarcity/circulation_scarcity/uniqueness_scarcity: 系统配置/系统计算(保留向后兼容) - - return: - Dict: 包含所有中间计算结果和最终结果的字典 - """ - # 计算市场竞价C1 - market_bidding_c1 = self.market_bidding_calculator.calculate_market_bidding_c1( - transaction_data={'weighted_average_price': input_data.get('weighted_average_price', 0)}, - manual_bids=input_data.get('manual_bids', []), - expert_valuations=input_data.get('expert_valuations', []) - ) + 计算完整的市场估值C,并记录每一步的计算过程。 - # 计算热度系数C2 - heat_coefficient_c2 = self.heat_coefficient_calculator.calculate_heat_coefficient_c2( - input_data.get('daily_browse_volume', 500.0), - input_data.get('collection_count', 50) + 该函数通过顺序调用市场竞价C1、热度系数C2、稀缺性乘数C3和时效性衰减C4的计算器, + 最终得出市场估值C。计算过程中的每个子步骤都会被详细记录,以便于审计和跟踪。 + + Args: + valuation_id (int): 估值的唯一标识符,用于关联所有计算步骤。 + input_data (Dict): 包含所有计算所需参数的字典,例如: + { + 'weighted_average_price': 50000.0, # C1 + 'manual_bids': [48000.0, 52000.0], # C1 + 'expert_valuations': [49000.0, 51000.0], # C1 + 'daily_browse_volume': 500.0, # C2 + 'collection_count': 50, # C2 + 'issuance_level': '限量', # C3 + 'recent_market_activity': '2024-01-15' # C4 + } + + Returns: + float: 计算得出的市场估值C。 + + Raises: + Exception: 如果在计算过程中发生任何错误,将记录失败状态并重新抛出异常。 + """ + step = await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=3, + step_name="市场估值C计算", + step_description="开始计算市场估值C,公式为:市场竞价C1 × 热度系数C2 × 稀缺性乘数C3 × 时效性衰减C4", + input_params=input_data, + status="in_progress" + ) ) - - # 计算稀缺性乘数C3 - scarcity_multiplier_c3 = self.scarcity_multiplier_calculator.calculate_scarcity_multiplier_c3( - input_data.get('issuance_level', '限量') - ) - - # 计算时效性衰减C4 - temporal_decay_c4 = self.temporal_decay_calculator.calculate_temporal_decay_c4( - input_data.get('recent_market_activity', '2024-01-15') - ) - - # 计算市场估值C - market_value_c = self.calculate_market_value_c( - market_bidding_c1, - heat_coefficient_c2, - scarcity_multiplier_c3, - temporal_decay_c4 - ) - - return { - 'market_bidding_c1': market_bidding_c1, - 'heat_coefficient_c2': heat_coefficient_c2, - 'scarcity_multiplier_c3': scarcity_multiplier_c3, - 'temporal_decay_c4': temporal_decay_c4, - 'market_value_c': market_value_c - } + try: + # 计算市场竞价C1 + market_bidding_c1 = self.market_bidding_calculator.calculate_market_bidding_c1( + transaction_data={'weighted_average_price': input_data.get('weighted_average_price', 0)}, + manual_bids=input_data.get('manual_bids', []), + expert_valuations=input_data.get('expert_valuations', []) + ) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, step_order=3.1, step_name="市场竞价C1", + output_result={'market_bidding_c1': market_bidding_c1}, status="completed" + ) + ) + + # 计算热度系数C2 + heat_coefficient_c2 = self.heat_coefficient_calculator.calculate_heat_coefficient_c2( + input_data.get('daily_browse_volume', 500.0), + input_data.get('collection_count', 50) + ) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, step_order=3.2, step_name="热度系数C2", + output_result={'heat_coefficient_c2': heat_coefficient_c2}, status="completed" + ) + ) + + # 计算稀缺性乘数C3 + scarcity_multiplier_c3 = self.scarcity_multiplier_calculator.calculate_scarcity_multiplier_c3( + input_data.get('issuance_level', '限量') + ) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, step_order=3.3, step_name="稀缺性乘数C3", + output_result={'scarcity_multiplier_c3': scarcity_multiplier_c3}, status="completed" + ) + ) + + # 计算时效性衰减C4 + temporal_decay_c4 = self.temporal_decay_calculator.calculate_temporal_decay_c4( + input_data.get('recent_market_activity', '2024-01-15') + ) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, step_order=3.4, step_name="时效性衰减C4", + output_result={'temporal_decay_c4': temporal_decay_c4}, status="completed" + ) + ) + + # 计算市场估值C + market_value_c = self.calculate_market_value_c( + market_bidding_c1, + heat_coefficient_c2, + scarcity_multiplier_c3, + temporal_decay_c4 + ) + + await self.valuation_controller.update_calculation_step( + step.id, {"status": "completed", "output_result": {"market_value_c": market_value_c}} + ) + return market_value_c + except Exception as e: + error_message = f"市场估值C计算失败: {e}" + logger.error(error_message, exc_info=True) + await self.valuation_controller.update_calculation_step( + step.id, {"status": "failed", "error_message": error_message} + ) + raise # 示例使用 diff --git a/app/utils/calculation_engine/risk_adjustment_b3/sub_formulas/risk_adjustment_b3.py b/app/utils/calculation_engine/risk_adjustment_b3/sub_formulas/risk_adjustment_b3.py index 4720567..f4c7961 100644 --- a/app/utils/calculation_engine/risk_adjustment_b3/sub_formulas/risk_adjustment_b3.py +++ b/app/utils/calculation_engine/risk_adjustment_b3/sub_formulas/risk_adjustment_b3.py @@ -6,13 +6,25 @@ """ from typing import Dict, List +import sys +import os +# 添加项目根目录到Python路径 +current_dir = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(os.path.join(current_dir, '..', '..', '..', '..')) + +try: + from app.controllers.valuation import ValuationController + from app.schemas.valuation import ValuationCalculationStepCreate +except ImportError: + # 处理可能的导入错误 + pass class RiskAdjustmentB3Calculator: """风险调整系数B3计算器""" def __init__(self): - pass + self.valuation_controller = ValuationController() def calculate_risk_adjustment_b3(self, risk_score_sum: float) -> float: """ @@ -155,22 +167,81 @@ class RiskAdjustmentB3Calculator: return max_score - def calculate_complete_risky_value_b3(self, input_data: Dict) -> Dict: - # 计算各项风险评分 - market_risk = self.calculate_market_risk(input_data["highest_price"], input_data["lowest_price"]) - legal_risk = self.calculate_legal_risk(input_data["lawsuit_status"]) - inheritance_risk = self.calculate_inheritance_risk(input_data["inheritor_ages"]) + async def calculate_complete_risky_value_b3(self, valuation_id: int, input_data: Dict) -> float: + """ + 计算完整的风险调整系数B3,并记录所有计算步骤。 - # 计算风险评分总和R - risk_score_sum = self.calculate_risk_score_sum(market_risk, legal_risk, inheritance_risk) + 该函数通过整合市场风险、法律风险和传承风险的评估, + 计算出风险评分总和R,并最终得出风险调整系数B3。 + 每一步的计算过程都会被记录下来,以确保计算的透明度和可追溯性。 - # 计算风险调整系数B3 - risk_adjustment_b3 = self.calculate_risk_adjustment_b3(risk_score_sum) - return { - 'risk_score_sum': risk_score_sum, - 'risk_adjustment_b3': risk_adjustment_b3 - } + Args: + valuation_id (int): 估值的唯一标识符,用于关联所有计算步骤。 + input_data (Dict): 包含所有计算所需参数的字典,例如: + { + 'highest_price': 340.0, # 市场风险 + 'lowest_price': 300.0, # 市场风险 + 'lawsuit_status': 10.0, # 法律风险 + 'inheritor_ages': [100, 20, 5], # 传承风险 + ... + } + Returns: + float: 计算得出的风险调整系数B3。 + + Raises: + Exception: 在计算过程中遇到的任何异常都会被捕获、记录,并重新抛出。 + """ + step = await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, + step_order=2.3, + step_name="风险调整系数B3计算", + step_description="开始计算风险调整系数B3,公式为:0.8 + 风险评分总和R × 0.4", + input_params=input_data, + status="in_progress" + ) + ) + try: + # 计算各项风险评分 + market_risk = self.calculate_market_risk(input_data["highest_price"], input_data["lowest_price"]) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, step_order=2.31, step_name="市场风险评分", + output_result={'market_risk': market_risk}, status="completed" + ) + ) + legal_risk = self.calculate_legal_risk(input_data["lawsuit_status"]) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, step_order=2.32, step_name="法律风险评分", + output_result={'legal_risk': legal_risk}, status="completed" + ) + ) + inheritance_risk = self.calculate_inheritance_risk(input_data["inheritor_ages"]) + await self.valuation_controller.create_calculation_step( + ValuationCalculationStepCreate( + valuation_id=valuation_id, step_order=2.33, step_name="传承风险评分", + output_result={'inheritance_risk': inheritance_risk}, status="completed" + ) + ) + + # 计算风险评分总和R + risk_score_sum = self.calculate_risk_score_sum(market_risk, legal_risk, inheritance_risk) + + # 计算风险调整系数B3 + risk_adjustment_b3 = self.calculate_risk_adjustment_b3(risk_score_sum) + + await self.valuation_controller.update_calculation_step( + step.id, {"status": "completed", "output_result": {'risk_adjustment_b3': risk_adjustment_b3}} + ) + return risk_adjustment_b3 + except Exception as e: + error_message = f"风险调整系数B3计算失败: {e}" + await self.valuation_controller.update_calculation_step( + step.id, {"status": "failed", "error_message": error_message} + ) + raise # 示例使用 if __name__ == "__main__": diff --git a/app/utils/专利.json b/app/utils/专利.json deleted file mode 100644 index 23ee5c1..0000000 --- a/app/utils/专利.json +++ /dev/null @@ -1,4551 +0,0 @@ -{ - "orderNo": "202510101216340087421", - "data": { - "total": 211108, - "totalPage": 2112, - "dataList": [ - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "B60L3/00", - "FLH": "B60L3/00(2019.01)I;B60L1/00(2006.01)I;B60K35/21(2024.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "时林;王君", - "GKGGH": "CN120735595A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京龙双利达知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "作业;运输——一般车辆——电动车辆动力装置;车辆的磁悬置或悬浮;电动车辆的监控操作变量;电力牵引——电动车辆上安全用电装置;运转变量,例如速度、减速、能量消耗的监测", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "82dd491d0dfa98ffa77e4180cdbafbad", - "FMR": "周晨曦;轩言成;胡晨晖", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/f0/f093350c3e7317bc89991a25537847eb.jpg!xinshuwater", - "SQH": "CN202510803869.8", - "PATNAME": "降低车辆电磁辐射的控制方法、控制器和车辆", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2025-06-16", - "IPC": "作业;运输——一般车辆——电动车辆动力装置;车辆的磁悬置或悬浮;电动车辆的监控操作变量;电力牵引——电动车辆上安全用电装置;运转变量,例如速度、减速、能量消耗的监测", - "ZY": "本申请提供了一种降低车辆电磁辐射的控制方法、控制器和车辆,控制方法用于在低电磁辐射模式启用后控制车辆降低车辆产生的电磁辐射。控制方法包括在车辆运行过程中,在检测到低电磁辐射模式被触发之前,控制车辆的用电器以第一功率运行。在检测到低电磁辐射模式被触发之后控制车辆的显示屏显示指示标识,指示标识用于指示车辆启用低电磁辐射模式。在低电磁辐射模式启用后,控制车辆的用电器以小于第一功率的第二功率运行。其中,用电器件包括车辆的动力总成、制动系统或者低压用电器件中的至少一项。实施该控制方法,可以在车辆低电磁辐射模式启用后限制车辆的用电器的功率以使用电器的电磁辐射下降,从而减小用户受到电磁辐射的安全隐患。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "G06F9/30", - "FLH": "G06F9/30(2006.01)I;G06F9/38(2006.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "申健", - "GKGGH": "CN120752612A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——计算;推算或计数——电数字数据处理——与执行机器指令相关的设计,例如指令译码", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "5b4f6befe25d635a7deb7cc64f6d3177", - "FMR": "希勒尔·阿夫尼;利奥·佩莱德;拉南·萨德;伊丹·李维", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/41/41993ed43ee6aa87244603f3c3ef8d4f.jpg!xinshuwater", - "SQH": "CN202380094786.5", - "PATNAME": "崩溃一致性持久内存设备和方法", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2023-04-19", - "IPC": "物理——计算;推算或计数——电数字数据处理——与执行机器指令相关的设计,例如指令译码", - "ZY": "公开了一种利用持久内存实现崩溃一致性执行的数据处理装置(100)。所述数据处理装置(100)包括用于存储数据的持久内存(105)。此外,所述数据处理装置(100)包括用于执行程序代码(107)的中央处理器(centralprocessingunit,CPU)(101),其中,所述程序代码(107)包括所述CPU(101)的指令集架构(instructionsetarchitecture,ISA)中的一个或多个内存访问指令。所述一个或多个内存访问指令用于访问所述持久内存(105)中的所述数据。所述ISA还包括用于所述CPU(101)的崩溃一致性执行(crash‑consistentexecution,CCE)模式的一个或多个指令,以实现所述一个或多个内存访问指令的CCE,其中,在所述CCE模式下,所述CPU(101)用于在发生读写冲突时,继续实现所述一个或多个内存访问指令的所述CCE。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H01Q1/36申请日:20210524", - "flztggrq": "2022-12-13" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-11-25" - } - ], - "JRGJRQ": "", - "ZFLH": "H01Q1/36", - "FLH": "H01Q1/36(2006.01)I;H01Q1/27(2006.01)I;H01Q1/00(2006.01)I;H05K5/02(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "孙德崇", - "GKGGH": "CN115395207B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京格罗巴尔知识产权代理事务所(普通合伙)", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电气元件——天线,即无线电天线——辐射单元的结构形式,例如锥形、螺旋形、伞形", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "0f7be4319caf6be141dadab102051876", - "FMR": "杨荣广;钟惠婷;刘兵;高建明;赵梦龙;张斌;李惠兰;张攀;苟小刚", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/8b/8be7961fe2cca0656fccc6eff84441cc.jpg!xinshuwater", - "SQH": "CN202110565282.X", - "PATNAME": "穿戴式电子设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2021-05-24", - "IPC": "电学——电气元件——天线,即无线电天线——辐射单元的结构形式,例如锥形、螺旋形、伞形", - "ZY": "本申请提供了一种穿戴式电子设备,其包括外壳和电路板组件。电路板组件位于外壳的内部且相对于外壳固定。外壳包含碳纤维材料,外壳具有内表面。外壳包括设置在内表面上的导电层,导电层使分散在外壳内的碳纤维材料电连接。外壳与电路板组件经由导电层电连接。通过采用上述技术方案,通过外壳的内表面设置的导电层使得外壳整体形成为一个导体,进一步使外壳与电路板组件经由导电层电连接在一起,因而将包含碳纤维材料的外壳本身作为天线的一部分,从而改善采用碳纤维材料制成外壳的穿戴式电子设备的天线性能。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "14-03", - "FLH": "14-03(14)", - "PTYPE": "外观设计", - "GKGGR": "2025-10-03", - "DLR": "李稷芳", - "GKGGH": "CN309530091S", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "录音、通讯或信息再现设备-通讯设备和无线遥控器、无线电放大器", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "0ed2073c535336200c7f2446cd8ff3ff", - "FMR": "徐汀;黄璐;王佳熙;许浩森", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/69/6977a6f76d1046dbaa32bd8eff7c5f43.jpg!xinshuwater", - "SQH": "CN202230466115.5", - "PATNAME": "手机的摄像头及摄像头主体", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2022-07-21", - "IPC": "录音、通讯或信息再现设备-通讯设备和无线遥控器、无线电放大器", - "ZY": "1.本外观设计产品的名称:手机的摄像头及摄像头主体。2.本外观设计产品的用途:本外观设计的整体用于提供网络通信、语音通话、事务处理(数据存储、录音、图片浏览)等,要求保护的局部用于拍摄照片或视频。3.本外观设计产品的设计要点:在于要求保护的局部形状。4.最能表明设计要点的图片或照片:设计1主视图。5.指定设计1为基本设计。6.其他需要说明的情形:图中虚线表示不要求保护的部分。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W76/10", - "FLH": "H04W76/10(2018.01)I;H04W76/15(2018.01)I;G08G5/57(2025.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "邹雅莹", - "GKGGH": "CN120751510A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京同达信恒知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——连接建立", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "5d46a322142c29be28804c2bd1c8cd0c", - "FMR": "臧昕;周润泽", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/db/db7ce95a1e72447d148f7572c788a465.jpg!xinshuwater", - "SQH": "CN202410386045.0", - "PATNAME": "一种通信方法、装置及系统", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2024-03-29", - "IPC": "电学——电通信技术——无线通信网络——连接建立", - "ZY": "本申请涉及通信技术领域,公开了一种通信方法、装置及系统。其中方法包括:第一终端设备向应用服务器发送第一信息,第一信息用于为第一终端设备创建第一通信连接,第一通信连接用于命令与控制C2通信;接收来自应用服务器的第二信息,第二信息用于指示C2通信采用双路通信连接,双路通信连接包括第一通信连接;根据第二信息,发送第三信息,第三信息用于为第一终端设备创建第二通信连接,第二通信连接用于C2通信。如此,在为第一终端设备建立用于C2通信的第一通信连接的过程中,应用服务器可以向第一终端设备发送第二信息,指示C2通信采用双路通信连接,从而通过建立用于C2通信的双路通信连接,保证C2通信的稳定性。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H01R31/00申请日:20220214", - "flztggrq": "2025-02-18" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-01-24" - } - ], - "JRGJRQ": "", - "ZFLH": "H01R31/00", - "FLH": "H01R31/00(2006.01)I;H01R12/71(2011.01)I;H01R13/73(2006.01)I;G06F1/18(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "石翰林", - "GKGGH": "CN119362096B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "深圳市深佳知识产权代理事务所(普通合伙)", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电气元件——导电连接;一组相互绝缘的电连接元件的结构组合;连接装置;集电器——仅通过与配合件协同作用来支承的连接部件", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "a2a658860cba78defb76a6dd101fb31e", - "FMR": "吴桃泉;余文;黄城;彭志勇;郝顺", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/a0/a03e434b053204d12d47ddf102fd984d.jpg!xinshuwater", - "SQH": "CN202411414539.1", - "PATNAME": "连接器组件及电子设备", - "SQR": "华为技术有限公司", - "FASQ": "20221013284122022.02.14", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2022-02-14", - "IPC": "电学——电气元件——导电连接;一组相互绝缘的电连接元件的结构组合;连接装置;集电器——仅通过与配合件协同作用来支承的连接部件", - "ZY": "本申请提供一种连接器组件及电子设备,该连接器组件可以用于插接IO设备。该连接器组件包括:电路板、连接器和连接线组;电路板具有相对的第一表面和第二表面,第一表面具有布线区域;连接器设置于第一表面,沿连接器的长度方向,连接器的两端分别对应有第一边界和第二边界,布线区域位于第一边界和所述第二边界之间;连接线组通过电路板与连接器电连接,且连接线组包括自布线区域引出的第一线组;第一线组远离电路板的一端包括导出部分,导出部分在电路板上的投影落在布线区域内,且导出部分的走线方向平行于连接器的长度方向。该连接器组件的连接线组可以利用IO设备与电路板之间的空间走线,有利于减小连接器组件在连接器高度方向的尺寸。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W28/02申请日:20190430", - "flztggrq": "2022-11-01" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-10-14" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W28/02", - "FLH": "H04W28/02(2009.01)I;H04W72/23(2023.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "时林;王君", - "GKGGH": "CN115190531B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京龙双利达知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——业务量管理,例如流量控制或拥塞控制", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "84d705a2e8efad5ff3b75657c805188b", - "FMR": "卓义斌;戴明增;刘菁;朱元萍;曹振臻", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/82/825af75a1b55c2706e8ffc2a6618022c.jpg!xinshuwater", - "SQH": "CN202210705563.5", - "PATNAME": "数据传输的方法和装置", - "SQR": "华为技术有限公司", - "FASQ": "20191036378472019.04.30", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2019-04-30", - "IPC": "电学——电通信技术——无线通信网络——业务量管理,例如流量控制或拥塞控制", - "ZY": "本申请提供了一种数据传输的方法和装置。在该方法中,第一IAB节点接收宿主基站发送给终端设备的N个数据包,N为大于1的正整数;第一IAB节点从N个数据包中确定第一数据包的PDCPPDUSN,该第一数据包的PDCPPDUSN是N个数据包的PDCPPDUSN中最大的PDCPPDUSN或是N个数据包的PDCPPDUSN按照从小到大的顺序排列且从最小的PDCPPDUSN开始连续PDCPPDUSN中最大的PDCPPDUSN;第一IAB节点向宿主基站发送第一信息,第一信息包括第一数据包的PDCPPDUSN的值。通过以上技术方案,有效地提高数据的传输性能。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):A61B5/1473申请日:20210427", - "flztggrq": "2022-11-15" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-10-28" - } - ], - "JRGJRQ": "", - "ZFLH": "A61B5/1473", - "FLH": "A61B5/1473(2006.01)I;A61B5/00(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "袁方", - "GKGGH": "CN115245332B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "人类生活必需——医学或兽医学;卫生学——诊断;外科;鉴定——侵入式,例如用导管引入体内的", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "129df121578bb698c01bad95a1e891cf", - "FMR": "许刚;解松林;刘翔宇;严家兵;陈文娟", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/c0/c031ee116fda935354c3fc4f531c1d74.jpg!xinshuwater", - "SQH": "CN202110461872.8", - "PATNAME": "电子设备、贴片及检测系统", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2021-04-27", - "IPC": "人类生活必需——医学或兽医学;卫生学——诊断;外科;鉴定——侵入式,例如用导管引入体内的", - "ZY": "本申请实施例提供一种电子设备、贴片及检测系统,涉及电子技术领域,能够简化贴片的结构,降低使用成本。该检测系统,用于检测生理参数,包括贴片和电子设备;其中,该贴片包括:基底、第一电极接口、胶黏层以及第一微针;该第一微针上设置有第一电极,第一电极与第一电极接口耦合;电子设备包括:电化学传感电路以及第一外接接口;该第一外接接口与该电化学传感电路耦合;其中,该电子设备的背面朝向人体佩戴时,该第一外接接口与该第一电极接口电连接,并在该贴片和该电子设备之间传输电信号。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "专利申请权、专利权的转移", - "flztxq": "专利申请权的转移IPC(主分类):H04W4/44登记生效日:20241030变更事项:申请人变更前:华为技术有限公司变更后:深圳引望智能技术有限公司变更事项:国家或地区变更前:中国变更后:中国变更事项:地址变更前:518129 广东省深圳市龙岗区坂田华为总部办公楼变更后:518129 广东省深圳市龙岗区坂田街道万科城社区华为公司华为总部办公楼101", - "flztggrq": "2024-11-15" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W4/44申请日:20210319", - "flztggrq": "2022-12-13" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-09-27" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W4/44", - "FLH": "H04W4/44(2018.01)I;H04W12/00(2021.01)I;H04W12/69(2021.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "朱琳琳", - "GKGGH": "CN115119179B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京同达信恒知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田街道万科城社区华为公司华为总部办公楼101", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——用于车辆和基础设施之间的通信,例如车与云[V2C]或车与家庭", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "a00dace6e1e023b6ed4b9785cff70165", - "FMR": "李明超;潘凯;魏芳", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/3e/3e4cb5041b06f5e7847694780ea0c4f6.jpg!xinshuwater", - "SQH": "CN202110294550.9", - "PATNAME": "一种车辆管理方法及通信装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2021-03-19", - "IPC": "电学——电通信技术——无线通信网络——用于车辆和基础设施之间的通信,例如车与云[V2C]或车与家庭", - "ZY": "一种车辆管理方法及通信装置,涉及智能交通技术领域。该方法中,第一通信装置获取目标车辆的登记信息;根据所述登记信息确定目标算法和目标使用策略,其中,所述目标算法用于生成所述目标车辆的管理身份标识,所述目标使用策略包括用于指示所述目标车辆基于所述管理身份标识与其它通信装置通信的策略,所述管理身份标识用于所述其它通信装置识别所述目标车辆;向所述目标车辆发送所述目标算法和目标使用策略。该方案有助于实现其它通信装置对所述目标车辆的识别和智能管理。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W68/00申请日:20201110", - "flztggrq": "2023-07-28" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2023-07-11" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W68/00", - "FLH": "H04W68/00(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "申健", - "GKGGH": "CN116420393B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——用户通知,例如,用于通信到来的提醒或寻呼,或类似的业务改变", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "ad7b8823e59dcdba80b19a1148bf1342", - "FMR": "葛翠丽;杨艳梅;胡雅婕", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/57/571350d49bd381c304ef7fc02b6cd8d5.jpg!xinshuwater", - "SQH": "CN202080106822.1", - "PATNAME": "标识发送方法和通信装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-11-10", - "IPC": "电学——电通信技术——无线通信网络——用户通知,例如,用于通信到来的提醒或寻呼,或类似的业务改变", - "ZY": "本申请公开了一种标识发送方法和通信装置,涉及通信领域,用于避免网络进行不必要的DNAI变更或UPF装置变更,以节省信令交互资源。标识发送方法包括:会话管理功能装置获取第一信息和应用的流量信息,第一信息用于指示当数据网络接入标识DNAI发生变更并且变更后的目标DNAI对应的边缘数据网络上部署有应用时通知应用功能装置;当DNAI发生变更,会话管理功能装置根据第一信息向应用功能装置发送目标DNAI。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):G06F9/448申请日:20201031", - "flztggrq": "2022-06-07" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-05-20" - } - ], - "JRGJRQ": "", - "ZFLH": "G06F9/448", - "FLH": "G06F9/448(2018.01)I;G06F9/445(2018.01)I;G06F8/61(2018.01)I;G06F8/41(2018.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "陈松浩", - "GKGGH": "CN114518913B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "深圳市深佳知识产权代理事务所(普通合伙)", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——计算;推算或计数——电数字数据处理——执行范例,例如:编程范例的实现", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "d7c5b02b6a37bf0d9086c5afd24b0d00", - "FMR": "刘健平;代雷;钟钜斌", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/1e/1e37458c90bc42393668678a2aea1c4a.jpg!xinshuwater", - "SQH": "CN202011198663.0", - "PATNAME": "程序执行方法、程序处理方法以及相关设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-10-31", - "IPC": "物理——计算;推算或计数——电数字数据处理——执行范例,例如:编程范例的实现", - "ZY": "本申请公开了一种程序执行方法,可以应用于程序处理领域,该方法包括:接收第一程序对小型stub函数的调用请求,根据调用请求加载小型stub函数;根据小型stub函数确定是否加载与小型stub函数对应的互补实函数。其中,第一程序实际可能需要调用的函数为互补实函数,小型stub函数可以理解为加载互补实函数的缓冲。通过增加缓冲,可以控制是否加载互补实函数,从而可以减少第一程序对运行内存的需求。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):G06F21/31申请日:20200829", - "flztggrq": "2022-03-18" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-03-01" - } - ], - "JRGJRQ": "", - "ZFLH": "G06F21/31", - "FLH": "G06F21/31(2013.01)I;G06F21/60(2013.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "熊永强;李稷芳", - "GKGGH": "CN114117367B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——计算;推算或计数——电数字数据处理——用户鉴别", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "4878a86d2ffa907b5f22b50bd0d3ea29", - "FMR": "陆琦玮;张大成;李侃", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/5f/5f58d2c16b077c4d76ba221b31d1153a.jpg!xinshuwater", - "SQH": "CN202010890450.8", - "PATNAME": "一种数据保护方法及电子设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-08-29", - "IPC": "物理——计算;推算或计数——电数字数据处理——用户鉴别", - "ZY": "一种数据保护方法及电子设备。在该方法中,当设备群组中所有电子设备的锁屏状态均为锁定态时,第一电子设备将第一电子设备中受保护的数据置为不可用;当设备群组中任一个电子设备的锁屏状态为解锁态时,第一电子设备将第一电子设备中受保护的数据置为可用。实施本申请提供的技术方案,在对数据进行安全保护的情况下,实现了分布式协同场景下受保护数据的正常使用。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W68/02申请日:20200731", - "flztggrq": "2023-07-28" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-02-18" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W68/02", - "FLH": "H04W68/02(2009.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "申健", - "GKGGH": "CN114071713B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——提高通知或寻呼信道效率的装置", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "3c4824f3ed27efad8a63e330bdda242e", - "FMR": "冯淑兰;刘江华;余政", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/fc/fc42d6f901323e975c2ec7e460495813.jpg!xinshuwater", - "SQH": "CN202010761340.1", - "PATNAME": "寻呼方法及装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-07-31", - "IPC": "电学——电通信技术——无线通信网络——提高通知或寻呼信道效率的装置", - "ZY": "本申请公开了寻呼方法及装置,涉及无线通信领域,通过本申请的方法,终端能够兼容两种寻呼方式。该方法包括:从网络设备接收寻呼物理下行控制信道(physicaldownlinkcontrolchannel,PDCCH);当寻呼PDCCH为第一类型的寻呼PDCCH时,根据第一类型的寻呼PDCCH确定是否被寻呼;当寻呼PDCCH为第二类型的寻呼PDCCH时,接收寻呼物理下行共享信道(physicaldownlinksharedchannel,PDSCH),根据寻呼PDSCH确定是否被寻呼。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W36/00申请日:20200831", - "flztggrq": "2023-07-25" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-03-01" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W36/00", - "FLH": "H04W36/00(2009.01)I;H04W36/08(2009.01)I;H04W36/30(2009.01)I;H04L5/00(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "熊永强;李稷芳", - "GKGGH": "CN114125957B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——切换或重选装置", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "f5fb05843676265b4dba68c5a72ff655", - "FMR": "王锋;胡丹;张旭;曲秉玉", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/79/79cd933654d627b3e14556e69dd4bb8b.jpg!xinshuwater", - "SQH": "CN202010901420.2", - "PATNAME": "一种通信方法、装置及计算机可读存储介质", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-08-31", - "IPC": "电学——电通信技术——无线通信网络——切换或重选装置", - "ZY": "本申请提供了一种通信方法、装置及计算机可读存储介质。其中,该方法包括:确定在终端设备从第二小区切换到第一小区后,在第一小区中终端设备进行上行传输的载波为第一载波;其中,所述第一载波和第二载波属于同一个频段,所述第二载波为所述终端设备在第二小区的载波。通过本申请提供的技术方案,无需随机接入,终端设备可以实现在第一小区进行上行传输。因此,本申请提供的方案,能够有效避免终端设备向第一小区进行需要随机接入的切换过程中可能产生的时延和中断问题,可以提高通信的效率。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "G06T11/20", - "FLH": "G06T11/20(2006.01)I;G06F16/334(2025.01)I;G06F40/30(2020.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "陈霁", - "GKGGH": "CN120747290A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京亿腾知识产权代理事务所(普通合伙)", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——计算;推算或计数——一般的图像数据处理或产生——根据基本元素绘图,例如:直线或圆", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "b703d3fa79045f83019e31d9fee121b6", - "FMR": "胡慧", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/40/40e1a3f6de7170d7440aa4e23828096b.jpg!xinshuwater", - "SQH": "CN202410395581.7", - "PATNAME": "一种图结构生成方法及相关装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2024-03-29", - "IPC": "物理——计算;推算或计数——一般的图像数据处理或产生——根据基本元素绘图,例如:直线或圆", - "ZY": "一种图结构生成方法,包括:获取图像信息,图像信息至少包括多个对象的信息,一个对象的信息包括一个节点的位置、一个节点的文本和一个连接线的位置中的至少一个;根据多个对象的信息,确定目标节点对,目标节点对至少包括第一节点和第二节点;对目标节点对的文本进行语义分析,得到目标节点对的关系,关系表示第一节点和第二节点的关联关系;根据图像信息和目标节点对的关系,生成目标图像的图结构。本申请的方法不需要知道节点间的连接线的方向,便可以知道节点间的关系,可以适用于无导引线方向的图像。即使是复杂的关系,也可以进行详细表述。在遗漏连接线的情况下,分析得到孤立节点与相邻节点的关系,使得图结构的完整性和准确性更高。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H10K59/131申请日:20220823", - "flztggrq": "2024-03-29" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2024-03-12" - } - ], - "JRGJRQ": "", - "ZFLH": "H10K59/131", - "FLH": "H10K59/131(2023.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "张小丽;习冬梅", - "GKGGH": "CN117693243B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "深圳市赛恩倍吉知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——半导体器件;其他类目中不包括的电固体器件——有机电固态器件——互连,例如,布线或端子", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "5636accd452ee60f00f80c6dae5bbd9e", - "FMR": "董毓杰;刘国和", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/e3/e3b2659852aa1c56d90879ab328c5cc4.jpg!xinshuwater", - "SQH": "CN202211012747.X", - "PATNAME": "显示面板和显示装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2022-08-23", - "IPC": "电学——半导体器件;其他类目中不包括的电固体器件——有机电固态器件——互连,例如,布线或端子", - "ZY": "一种显示面板,包括依次层叠的第一电极层、有机发光层、第二电极层。显示面板划分为显示区和边框区。边框区包括相对的第一端边区域和第二端边区域。显示面板包括设置在第一端边区域的第一柔性电路板、设置第二端边区域的第二柔性电路板、两条电源线。第一柔性电路板和第二柔性板通过各自的走线获取供给第一电极层的驱动信号。每一条电源线的一端与第一柔性电路板的走线连接,另一端与第二柔性电路板的走线连接。电源线的电阻率高于第一柔性电路板的走线和第二柔性电路板的走线的电阻率。本申请还提供包括该显示面板的显示装置。利用第二柔性电路板上设置走线连接电源走线,实现低阻抗的电源走线设计,降低电压降。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):G06F21/57申请日:20221107", - "flztggrq": "2025-01-14" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2024-12-27" - } - ], - "JRGJRQ": "", - "ZFLH": "G06F21/57", - "FLH": "G06F21/57(2013.01)I;G06F21/64(2013.01)I;G06F21/60(2013.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "颜晶", - "GKGGH": "CN119203143B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京三高永信知识产权代理有限责任公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——计算;推算或计数——电数字数据处理——确保或维持可信任的计算机平台,例如安全引导或断电、版本控制、系统软件检查、安全更新或评估漏洞", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "3472e076aa83ba2452740a0f79e37e98", - "FMR": "彭琨", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/62/62b6e1b828a367c09d1ebccce38a16b0.jpg!xinshuwater", - "SQH": "CN202411136049.X", - "PATNAME": "安全性验证方法、数据处理系统、存储介质及程序产品", - "SQR": "华为技术有限公司", - "FASQ": "20221138639012022.11.07", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2022-11-07", - "IPC": "物理——计算;推算或计数——电数字数据处理——确保或维持可信任的计算机平台,例如安全引导或断电、版本控制、系统软件检查、安全更新或评估漏洞", - "ZY": "本申请涉及一种安全性验证方法、数据处理系统、存储介质及程序产品,所述方法应用于数据处理系统,所述方法包括:数据处理单元接收用户设备发起的针对可信执行环境的安全性验证请求;数据处理单元响应于安全性验证请求,向用户设备发送包括完整签名的运行环境信息以及可信执行环境的公钥;包括完整签名的运行环境信息以及可信执行环境的公钥用于验证可信执行环境的安全性。该方法可以使用由多个对象的私钥生成的根密钥验证数据处理系统中、包括多个对象的可信执行环境的安全性,同时使得用户不必完全信任多个对象中的任意一个对象,能够提高可信执行环境的可靠性。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W76/11申请日:20200330", - "flztggrq": "2022-12-06" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-11-18" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W76/11", - "FLH": "H04W76/11(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "胡丽平", - "GKGGH": "CN115362747B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——连接标示符的分配或使用", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "ff8ec328d4a7b274a193ae0066b9785f", - "FMR": "雷中定;王海光;康鑫", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/e4/e4938b85afbb0248c1c9819415893bc4.jpg!xinshuwater", - "SQH": "CN202080099352.0", - "PATNAME": "一种终端设备的验证方法及装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-03-30", - "IPC": "电学——电通信技术——无线通信网络——连接标示符的分配或使用", - "ZY": "本申请实施例提供一种终端设备的验证方法及装置,该方法包括:第一网络设备接收来自第一终端设备的第一消息;然后该第一网络设备对第一终端设备和第二终端设备的配对关系进行验证。在对第一终端设备和第二终端设备的配对关系验证通过后,第一网络设备向第一终端设备发送第二消息,该第二消息中可以包括第一指示信息,该第一指示信息用于指示第一终端设备和第二终端设备的配对结果。通过验证第一终端设备和第二终端设备的配对关系,可使得该第一终端设备和该第二终端设备安全的进行配对,提高了第一终端设备和第二终端设备的使用安全性。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W68/02申请日:20191108", - "flztggrq": "2023-02-17" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2023-01-31" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W68/02", - "FLH": "H04W68/02(2009.01)I;H04W76/27(2018.01)I;H04W76/28(2018.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "赵丹", - "GKGGH": "CN115665856B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——提高通知或寻呼信道效率的装置", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "0184c8ab3a470ca6a747322f4f1c9555", - "FMR": "才宇;姚楚婷;金辉;王键", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/27/2783b323a60892e874c8488a65532d9b.jpg!xinshuwater", - "SQH": "CN202211111188.8", - "PATNAME": "一种寻呼方法和装置", - "SQR": "华为技术有限公司", - "FASQ": "20191108996702019.11.08", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2019-11-08", - "IPC": "电学——电通信技术——无线通信网络——提高通知或寻呼信道效率的装置", - "ZY": "本申请实施例提供一种寻呼方法和装置,涉及通信领域,解决了多卡终端设备的多个电话卡的PO在时间上重叠时发生寻呼丢失的问题。具体方案为:终端设备向第一网络中的第一网络设备发送第一指示信息,第一指示信息用于指示需更新第一寻呼时机PO;终端设备接收来自第一网络设备的第二指示信息,第二指示信息用于终端设备确定第二PO。本申请实施例用于UE监听网络侧的寻呼以及网络侧寻呼UE的过程。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):G09G3/20申请日:20201229", - "flztggrq": "2022-08-02" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-07-15" - } - ], - "JRGJRQ": "", - "ZFLH": "G09G3/20", - "FLH": "G09G3/20(2006.01)I;G06F3/14(2006.01)I;G06V40/16(2022.01)I;G06V10/56(2022.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "申健", - "GKGGH": "CN114758601B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——教育;密码术;显示;广告;印鉴——对用静态方法显示可变信息的指示装置进行控制的装置或电路传输数据的装置在数字计算机与显示器之间入G06F3/14;由若干分离源或光控的光电池结合而成的静态指示装置入G09F9/00;由若干光源的组合而构成的静态的指示装置入H01J,H01K,H01L,H05B33/12,H10H;文件或者类似物的扫描、传输或者重现,如传真传输,其零部件入H04N1/00)[3,4,5]——用于显示许多字符的组合,例如用排列成矩阵的单个元件组成系统构成的页面", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "a7604eabc0fa67671ca3d4b453d4cd41", - "FMR": "王凤霞", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/3e/3e03593eab930c5787a1b00006a85cb8.jpg!xinshuwater", - "SQH": "CN202011602885.4", - "PATNAME": "屏幕显示颜色调整方法及电子设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-12-29", - "IPC": "物理——教育;密码术;显示;广告;印鉴——对用静态方法显示可变信息的指示装置进行控制的装置或电路传输数据的装置在数字计算机与显示器之间入G06F3/14;由若干分离源或光控的光电池结合而成的静态指示装置入G09F9/00;由若干光源的组合而构成的静态的指示装置入H01J,H01K,H01L,H05B33/12,H10H;文件或者类似物的扫描、传输或者重现,如传真传输,其零部件入H04N1/00)[3,4,5]——用于显示许多字符的组合,例如用排列成矩阵的单个元件组成系统构成的页面", - "ZY": "本申请提供屏幕显示颜色调整方法及电子设备;涉及终端技术领域,能够在电子设备中预配置屏幕在不同显示角度下,输入颜色参数与输出颜色参数的映射关系。后续,电子设备通过识别屏幕显示角度,根据映射关系,获得需要的输入颜色参数,实现屏幕显示颜色的自适应调整,提高用户的使用体验。该方法包括:获得第一人脸所在平面与电子设备所在平面之间的夹角角度,该夹角角度为显示角度。获得第一像素点的预设颜色参数,利用显示角度和预设颜色参数,确定目标颜色参数,之后在第一像素点利用目标颜色参数进行显示。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04N5/93申请日:20200908", - "flztggrq": "2022-07-26" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-07-08" - } - ], - "JRGJRQ": "", - "ZFLH": "H04N5/93", - "FLH": "H04N5/93(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "王洪", - "GKGGH": "CN114731385B", - "SQGKGGH": "", - "YXQ": "62/905,1262019.09.24US", - "GSDM": "", - "ZLDLJG": "北京润泽恒知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——图像通信,如电视——电视信号或其选择部分的再生", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "1eedc5b0b6b1d186a0da531e9bbecce4", - "FMR": "王业奎", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/f0/f02cd8817409be37d0b2401011c1b694.jpg!xinshuwater", - "SQH": "CN202080066246.2", - "PATNAME": "视频译码中用于多视图的同播层", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-09-08", - "IPC": "电学——电通信技术——图像通信,如电视——电视信号或其选择部分的再生", - "ZY": "本发明公开了一种视频译码机制。所述机制包括对包括编码图像的一个或多个层的码流进行编码。视频参数集(videoparameterset,VPS)也被编码到所述码流中。当由所述VPS指定的所有层被独立编码而不需要层间预测时,所述VPS包括每个层是输出层集(outputlayerset,OLS)标志(each_layer_is_an_ols_flag)。所述each_layer_is_an_ols_flag表示每个OLS是否仅包括一个层。所述码流被存储,以发送给解码器。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W4/029申请日:20210430", - "flztggrq": "2022-12-16" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-11-01" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W4/029", - "FLH": "H04W4/029(2018.01)I;H04W4/10(2009.01)I;H04L67/52(2022.01)I;H04L67/145(2022.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "闫婷婷", - "GKGGH": "CN115278541B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京同达信恒知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——基于位置的管理或跟踪业务", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "008e1f81070c5fdc3985efbe0367b1e4", - "FMR": "葛翠丽;杨艳梅", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/55/55aebd44398b0b5d1c8d7f4bfbf048dc.jpg!xinshuwater", - "SQH": "CN202110481829.8", - "PATNAME": "一种终端信息共享的方法及通信装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2021-04-30", - "IPC": "电学——电通信技术——无线通信网络——基于位置的管理或跟踪业务", - "ZY": "本申请涉及无线通信技术领域,提供了一种终端信息共享的方法及通信装置,可以减少终端资源、空口信令资源的浪费。信息管理网元接收到来自支持第一业务的第一网元的第一请求,以及支持第二业务的第二网元的第三请求,用来请求终端的信息。信息管理网元向所述终端发送第二请求,所述第二请求用于请求所述终端的信息。接下来,信息管理网元接收来自所述终端的所述信息,进而向第一网元和第二网元发送所述信息。在多个分别支持不同业务的网元向信息管理网元请求终端的信息时,信息管理网元只向终端发送一次请求,终端只需针对这一次请求上报终端的信息,可以避免重复上报终端的信息,减少终端资源、空口信令资源的浪费。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04L45/16申请日:20201113", - "flztggrq": "2023-09-01" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-03-29" - } - ], - "JRGJRQ": "", - "ZFLH": "H04L45/16", - "FLH": "H04L45/16(2022.01)I;H04L45/745(2022.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "赵丹", - "GKGGH": "CN114257539B", - "SQGKGGH": "", - "YXQ": "20201095628322020.09.11CN", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——数字信息的传输,例如电报通信——多点路由", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "9ef6d7b6a973b4603b391f15912162f0", - "FMR": "田太徐;夏阳;李吉;李东锋;谢经荣", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/71/71e0af0de610d5774c561cbd6beb17a9.jpg!xinshuwater", - "SQH": "CN202011273084.8", - "PATNAME": "获取下一跳的方法、生成转发表项的方法及装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-11-13", - "IPC": "电学——电通信技术——数字信息的传输,例如电报通信——多点路由", - "ZY": "本申请实施例提供一种获取下一跳的方法,包括:接收位索引显式复制BIER组播报文,BIER组播报文包括第一比特串bitstring,第一bitstirng包括的被置1的比特bit与位转发出口路由器BFER对应;根据第一bitstring中被置1的bit确定转发表项的索引,转发表项包括所述索引和作为下一跳的设备的标识;根据索引直接获得设备的标识。本申请实施例还提供了一种生成转发表项的方法,包括:接收设备发送的设备的标识和BFR‑id;根据设备的标识和BFR‑id,获取转发表项,转发表项的索引对应BFR‑id,转发表项用于根据索引直接获得转发表项包括的作为下一跳的设备的标识。上述方法中的BFR‑id用于标识能够与设备通信的BFER,上述方法中的设备为BFER或者到达BFER所经过的第一中间BFR。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04L5/00申请日:20210630", - "flztggrq": "2023-01-20" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-12-30" - } - ], - "JRGJRQ": "", - "ZFLH": "H04L5/00", - "FLH": "H04L5/00(2006.01)I;H04W72/23(2023.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "聂秀娜", - "GKGGH": "CN115549875B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "深圳市深佳知识产权代理事务所(普通合伙)", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——数字信息的传输,例如电报通信——为传输通道提供多用途的装置", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "5298716e2848098126546bcc6d479c6e", - "FMR": "胡丹;张旭;郭志恒", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/2e/2e176e464d5b19692f6b16f8edac7064.jpg!xinshuwater", - "SQH": "CN202110745368.0", - "PATNAME": "控制信息发送方法、控制信息接收方法以及通信装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2021-06-30", - "IPC": "电学——电通信技术——数字信息的传输,例如电报通信——为传输通道提供多用途的装置", - "ZY": "本申请实施例提供了一种控制信息发送(或接收)方法以及相关通信装置,通过增设用于重复发送控制信息的时频资源,以使得在进入连接态之前的终端设备能够在至少两个时频资源上接收控制信息,即该终端设备可能收到至少两份控制信息。由于,网络设备增加了向终端设备发送控制信息的时频资源,并且,网络设备增加了发送控制信息的次数,因此,能够提高终端设备正确接收控制信息的几率。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):A61B5/0225申请日:20210630", - "flztggrq": "2023-01-20" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-12-30" - } - ], - "JRGJRQ": "", - "ZFLH": "A61B5/0225", - "FLH": "A61B5/0225(2006.01)I;A61B5/00(2006.01)I;A61B5/024(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "赵倩", - "GKGGH": "CN115530785B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "深圳中一联合知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "人类生活必需——医学或兽医学;卫生学——诊断;外科;鉴定——用电信号控制的压力,如由Korotkoff音驱动的", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "f6687083f4113760168ec273c51c5f24", - "FMR": "傅小煜;黄振龙", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/0a/0a68b98b6f85628f20d7fa35a335b249.jpg!xinshuwater", - "SQH": "CN202110747912.5", - "PATNAME": "测量血压的装置与方法", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2021-06-30", - "IPC": "人类生活必需——医学或兽医学;卫生学——诊断;外科;鉴定——用电信号控制的压力,如由Korotkoff音驱动的", - "ZY": "本申请提供了一种测量血压的装置与方法,该装置包括处理器101、第一测量组件102与第二测量组件103,第一测量组件102,用于采集用户的血压值;第二测量组件103,用于采集用户的PPG信号;处理器101,用于控制第一测量组件102采集用户的N组血压值,并控制第二测量组件103采集与N组血压值相对应的N组PPG信号,根据N组血压值和N组PPG信号生成目标模型,目标模型的输入为PPG信号,输出为血压值,在目标模型生成后,控制第二测量组件103采集用户的第一PPG信号,根据目标模型和第一PPG信号确定第一血压值。该装置能够在对用户的血压值进行测量时,提高测量结果的准确性,且操作简便。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W56/00申请日:20200820", - "flztggrq": "2023-07-25" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-02-22" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W56/00", - "FLH": "H04W56/00(2009.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "颜晶", - "GKGGH": "CN114080017B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京三高永信知识产权代理有限责任公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——同步装置", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "eda9d050ef70a47b1e7c948411851ce5", - "FMR": "吕京飞;余芳;范博龄", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/64/6486a510f2ad42daa75d4f96213dc944.jpg!xinshuwater", - "SQH": "CN202010843351.4", - "PATNAME": "处理时间同步故障的方法、装置及系统", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-08-20", - "IPC": "电学——电通信技术——无线通信网络——同步装置", - "ZY": "本申请公开了一种处理时间同步故障的方法及装置,属于通信领域。所述方法包括:当第一网络中的第一转换器的时间未与所述第一网络中的时钟源同步时,停止向与所述第一转换器通信的设备发送与第二网络中的时钟源同步的时间,所述设备位于所述第二网络中。本申请避免获取到错误的时间来同步。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "G06T13/80", - "FLH": "G06T13/80(2011.01)I;G06V10/44(2022.01)I;G06V10/56(2022.01)I;G06T7/90(2017.01)I;G06V40/13(2022.01)I;G06V40/12(2022.01)I;G06T7/13(2017.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "夏峰", - "GKGGH": "CN120747318A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "上海音科专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——计算;推算或计数——一般的图像数据处理或产生——2D〔二维〕动画,如使用精灵sprites", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "bb3f54c36e76a790fe68460b6f17d849", - "FMR": "熊石一", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/a2/a23a8619184f60624ae0868272b14086.jpg!xinshuwater", - "SQH": "CN202510661608.7", - "PATNAME": "电子设备及其功能设置的提示方法、提示文件的播放方法", - "SQR": "华为技术有限公司", - "FASQ": "20201114081092020.10.22", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-10-22", - "IPC": "物理——计算;推算或计数——一般的图像数据处理或产生——2D〔二维〕动画,如使用精灵sprites", - "ZY": "本申请涉及计算机动画技术领域,公开了电子设备及其功能设置的提示方法、提示文件的播放方法和介质。功能设置的提示方法,应用于电子设备,方法包括:响应用户的第一操作,电子设备显示提示文件的第一序列帧图像;响应用户的第二操作,电子设备显示提示文件的第二序列帧图像;其中,提示文件包括播放对象的至少第一部分轮廓信息和第二部分轮廓信息,播放对象的第二部分的轮廓信息生成第二序列帧图像,播放对象的第一部分的轮廓信息生成第一序列帧图像,第二序列帧图像包含第一序列帧图像的内容。如此,可以使用轮廓信息还原出播放对象的或者部分的显示效果,节省了播放指纹提示文件的电子设备中存储指纹提示文件的内存空间。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):G06K9/00申请日:20200827", - "flztggrq": "2021-01-22" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2021-01-05" - } - ], - "JRGJRQ": "", - "ZFLH": "G06V40/13", - "FLH": "G06V40/13(2022.01)I;G06V10/774(2022.01)I;G06V10/74(2022.01)I;G06V10/82(2022.01)I;G06N3/0464(2023.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "申健", - "GKGGH": "CN112183208B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——计算;推算或计数——图像或视频识别或理解——传感器", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "46839ee8289bb3657303fc6fd09c465d", - "FMR": "卢嘉勋;杨春春;邵云峰", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/9e/9e58c4c17b8f216d2b1c3cf1cee29695.jpg!xinshuwater", - "SQH": "CN202010881150.3", - "PATNAME": "一种身份识别方法及设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-08-27", - "IPC": "物理——计算;推算或计数——图像或视频识别或理解——传感器", - "ZY": "本申请实施例提供一种身份识别方法及设备,涉及身份识别领域。终端设备训练获得模板样本组对应的子模型,从而使用子模型确定待处理图像样本与该模板样本组中的模板图像是否匹配,提升终端设备侧的身份识别精度。具体方案为:终端设备根据采集的身份识别数据的多个参考图像样本、第一模型和第一模板样本组训练获得第一子模型;采集待处理图像样本;根据第一子模型,将待处理图像样本与第一模板样本组中的模板图像进行一一比对,获得第一匹配结果;根据第一模型,将待处理图像样本与目标模板样本组以外的模板图像进行一一比对,获得第二匹配结果;根据第一匹配结果和第二匹配结果,确定目标匹配结果。本申请实施例用于身份识别数据匹配的过程。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H10B12/00", - "FLH": "H10B12/00(2023.01)I;H10D30/63(2025.01)I;H10D62/17(2025.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "申健", - "GKGGH": "CN120751695A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——半导体器件;其他类目中不包括的电固体器件——电存储器件——动态随机存取存储器", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "cd75d9a299d3d7f2eae29493aea9e570", - "FMR": "赵万鹏;吴颖;詹士杰;申弘光;崔巍;许俊豪", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/01/01ea7fb5f74f9c961e925c9ff6e158c6.jpg!xinshuwater", - "SQH": "CN202410358183.8", - "PATNAME": "半导体结构及其制备方法、电子设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2024-03-26", - "IPC": "电学——半导体器件;其他类目中不包括的电固体器件——电存储器件——动态随机存取存储器", - "ZY": "本申请实施例属于半导体技术领域,尤其涉及一种半导体结构及其制备方法、电子设备,用于改善相关技术中的垂直晶体管的开启电流较低的问题。半导体结构包括第一电极层、栅极层、第二电极层、沟道层以及第一缓冲层。第一电极层、栅极层以及第二电极层沿第一方向依次层叠设置。沟道层的至少部分位于第一电极层和第二电极层之间,且沿第一方向贯穿栅极层;第一缓冲层的至少部分位于第一电极层和沟道层之间,且第一缓冲层与沟道层接触,第一缓冲层内的N型离子的浓度大于沟道层内的N型离子的浓度。通过上述设置,第一缓冲层内的N型离子扩散至沟道层内,使得沟道层在靠近第一缓冲层处的N型离子的掺杂浓度升高,有利于提高晶体管结构的开启电流。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "G02B6/42", - "FLH": "G02B6/42(2006.01)I;H04B10/40(2013.01)I;H04B10/50(2013.01)I", - "PTYPE": "实用新型", - "GKGGR": "2025-10-03", - "DLR": "庞茜", - "GKGGH": "CN223413514U", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京三高永信知识产权代理有限责任公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——光学在本大类中,下列的词语以指定的含义使用:或红外辐射。——光学元件、系统或仪器——光波导与光电元件的耦合", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "6f3a46bef2bd4d45624d3c5a4ac0f25f", - "FMR": "陈丽芳;谢洪波;李兆明", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/db/db3daed4498ef061d0cce15172f35a02.jpg!xinshuwater", - "SQH": "CN202422787882.2", - "PATNAME": "光发射组件、光收发组件和光通信装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2024-11-14", - "IPC": "物理——光学在本大类中,下列的词语以指定的含义使用:或红外辐射。——光学元件、系统或仪器——光波导与光电元件的耦合", - "ZY": "公开了一种光发射组件、光收发组件和光通信装置,属于光通信技术领域。该光发射组件包括壳体、发光单元和隔离器,所述壳体具有出光口,所述发光单元和所述隔离器位于所述壳体中,且所述壳体位于所述发光单元到所述出光口之间的光路上,所述隔离器的通光孔径小于0.6mm。该发射组件的成本较低。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W76/27申请日:20200929", - "flztggrq": "2022-06-21" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-06-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W76/27", - "FLH": "H04W76/27(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "熊永强;李稷芳", - "GKGGH": "CN114586465B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——在无线资源控制[RRC]状态间的转变", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "1e777d92dd6ad62685ee395fe3fdd725", - "FMR": "谭佳瑶;袁云贺;周小燕;常俊仁;毛颖超", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/c2/c29a65115e95d30ce6cc01e21626050b.jpg!xinshuwater", - "SQH": "CN202080027563.3", - "PATNAME": "一种状态转换的方法及通信装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-09-29", - "IPC": "电学——电通信技术——无线通信网络——在无线资源控制[RRC]状态间的转变", - "ZY": "本申请实施例公开了一种状态转换的方法及通信装置,方法包括:终端接收基站发送的无线资源控制RRC释放消息,指示终端从RRC连接态转换为RRC非激活态;当终端需要从RRC非激活态转换为RRC连接态时,向所述基站发送RRC恢复请求消息;终端接收基站发送的RRC恢复消息,并接收基站为终端分配的上行授权资源;终端基于上行授权资源向基站发送上行消息,上行消息包含无线链路控制确认消息且不包含终端在RRC连接态时缓存的数据包;终端向所述基站发送RRC恢复完成消息。采用本申请实施例,可避免终端与基站RLC不同步的问题,提升了数据传输的效率及用户体验。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W72/04申请日:20201128", - "flztggrq": "2023-07-11" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2023-06-23" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W72/04", - "FLH": "H04W72/04(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "石朝清", - "GKGGH": "CN116326070B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——无线资源分配", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "67912d66f98154613f883e3784a6fc79", - "FMR": "苏立焱", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/61/61513cc5fbe30955fe9503a59fd7fb24.jpg!xinshuwater", - "SQH": "CN202080106253.0", - "PATNAME": "一种通信方法、装置及计算机可读存储介质", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-11-28", - "IPC": "电学——电通信技术——无线通信网络——无线资源分配", - "ZY": "本发明实施例公开一种通信方法、装置及计算机可读存储介质,包括:接收来自网络设备的DCI,所述DCI包括时频资源和第一MCS,所述第一MCS为所述时频资源中第一类RE上承载的数据的MCS;确定第二MCS,所述第二MCS为所述时频资源中第二类RE上承载的数据的MCS;根据所述第一MCS和所述第二MCS对第一数据进行调制编码,得到调制符号;通过所述时频资源向所述网络设备发送所述调制符号。本发明实施例,可以提高传输效率。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H03M13/19申请日:20200703", - "flztggrq": "2023-06-27" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-01-04" - } - ], - "JRGJRQ": "", - "ZFLH": "H03M13/19", - "FLH": "H03M13/19(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "张娜", - "GKGGH": "CN113890545B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电子电路——一般编码、译码或代码转换〔4〕——未应用循环码的特定特性的单个纠错", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "5e9ddc06ca788e3329b7da8f97ad3958", - "FMR": "梁伟光;黄科超;马会肖;肖世尧;耿东玉", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/f5/f55039382e4b6af044aaeef45d75523d.jpg!xinshuwater", - "SQH": "CN202010631750.4", - "PATNAME": "按需译码方法及装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-07-03", - "IPC": "电学——电子电路——一般编码、译码或代码转换〔4〕——未应用循环码的特定特性的单个纠错", - "ZY": "本申请公开了一种译码方法,可应用于城域、骨干网、数据中心互连等多个场景,满足光传输的需求。所述方法包括:获得多个码字中每个码字对应的校正子;对得到的所述校正子进行分组,在每一组校正子中进行优先级排序;根据所述每一组校正子的优先级排序结果,挑选校正子进行译码。由于本译码方法并不对每一个码字都进行相同的译码处理,避免了传统静态译码方案中,无论码字本身是否正确均需要进行同等次数译码的问题,实现按需译码,降低译码资源的需求和功耗。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H05K7/20", - "FLH": "H05K7/20(2006.01)I;H05K5/30(2025.01)I;H05K5/00(2025.01)I;H05K7/14(2006.01)I;H05K9/00(2006.01)I;B60L15/20(2006.01)I;B60L53/20(2019.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "崔文娟", - "GKGGH": "CN120751656A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——其他类目不包含的电技术——印刷电路;电设备的外壳或结构零部件;电气元件组件的制造——便于冷却、通风或加热的改进", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "22a09183dc4555f1e249b0d3e57bebe7", - "FMR": "王双;卞斐;刘少龙;鹿化文", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/c6/c69f7a376099007a86386ea1fa923b1a.jpg!xinshuwater", - "SQH": "CN202510600329.X", - "PATNAME": "车载装置及电动车", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2025-05-09", - "IPC": "电学——其他类目不包含的电技术——印刷电路;电设备的外壳或结构零部件;电气元件组件的制造——便于冷却、通风或加热的改进", - "ZY": "本申请提供一种车载装置及电动车,车载装置的壳体包括电气容纳槽和内部进水通道,内部进水通道的入口分布于电气容纳槽的槽外,内部进水通道的出口分布于电气容纳槽的槽内。其中,电气容纳槽用于容纳第一钣金散热器和屏蔽支架,沿电气容纳槽的槽底与槽口的排列方向屏蔽支架、第一钣金散热器和电气容纳槽的槽底依次层叠,屏蔽支架用于容纳车载装置中的车载充电机的多个磁器件,第一钣金散热器的入口用于连通内部进水通道的出口,第一钣金散热器用于固定屏蔽支架以及用于冷却屏蔽支架所容纳的多个磁器件,第一钣金散热器提升对磁器件的散热效果,并且采用钣金工艺形成的第一钣金散热器的水道凹槽厚度较小,有利于缩小车载装置的整体高度。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "14-03", - "FLH": "14-03(14)", - "PTYPE": "外观设计", - "GKGGR": "2025-10-03", - "DLR": "胡丽平", - "GKGGH": "CN309530084S", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "录音、通讯或信息再现设备-通讯设备和无线遥控器、无线电放大器", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "54d2c28ada24847246d7b78412196927", - "FMR": "徐汀;黄璐;王佳熙;许浩森", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/84/84af38da2ee39b13823856001ce4fa5e.jpg!xinshuwater", - "SQH": "CN202230465743.1", - "PATNAME": "手机的摄像头及摄像头主体", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2022-07-21", - "IPC": "录音、通讯或信息再现设备-通讯设备和无线遥控器、无线电放大器", - "ZY": "1.本外观设计产品的名称:手机的摄像头及摄像头主体。2.本外观设计产品的用途:本外观设计的整体用途为提供网络通信、语音通话、事务处理(数据存储、录音、图片浏览)等,局部用途为拍摄照片或视频。3.本外观设计产品的设计要点:在于要求保护的局部形状。4.最能表明设计要点的图片或照片:设计1主视图。5.指定设计1为基本设计。6.其他需要说明的情形:图中虚线表示不要求保护的部分。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "G06F30/20", - "FLH": "G06F30/20(2020.01)I;G06F15/78(2006.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "朱琳爱义", - "GKGGH": "CN120745140A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京同达信恒知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——计算;推算或计数——电数字数据处理——设计优化、验证或模拟", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "6e44126970b8ca7aeaba2154ecde03cf", - "FMR": "杨立新;刘海新;卓锐;李思恩;黄伯宁", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/77/779c92c18413fc45f6032ff46450262b.jpg!xinshuwater", - "SQH": "CN202510595285.6", - "PATNAME": "一种光伏系统的仿真系统及方法", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2025-05-09", - "IPC": "物理——计算;推算或计数——电数字数据处理——设计优化、验证或模拟", - "ZY": "本申请实施例提供的一种光伏系统的仿真系统及方法,在FPGA中基于标准驱动程序接口,可以精细化的按照待仿真的逆变器真实驱动参数完成PWM发波配置,并按照待仿真的逆变器真实采样信息完成同等的模拟量采样抽取,可以提高仿精度。本申请提供的仿真方法基于高性能异构计算系统架构,充分利用处理器不同内核的算力性能,在不同内核装载不同待仿真的逆变器对应的虚拟控制器模型,可以实现不同模型的并行运算,有利于实现大规模仿真。并且,本申请提供的仿真系统为开放式架构,可以针对所需仿真的逆变器数量扩展所需的CPU数量以满足大规模仿真需求。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04N5/225申请日:20200930", - "flztggrq": "2023-06-13" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-04-12" - } - ], - "JRGJRQ": "", - "ZFLH": "H04N23/57", - "FLH": "H04N23/57(2023.01)I;H04N23/68(2023.01)I;H04M1/02(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "徐丽;许春晓", - "GKGGH": "CN114338964B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "深圳市赛恩倍吉知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——图像通信,如电视——专门适于嵌入其他设备中的相机或相机模块的机械或电子零部件", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "cb93370d37615879155329995409b643", - "FMR": "甄峰;谢俊;王冬立;俞磊;闫志国", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/9a/9a853e2a3e242a44a73ad4e7330a4809.jpg!xinshuwater", - "SQH": "CN202011061826.0", - "PATNAME": "防抖模组及用户设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-09-30", - "IPC": "电学——电通信技术——图像通信,如电视——专门适于嵌入其他设备中的相机或相机模块的机械或电子零部件", - "ZY": "本申请实施例公开了一种防抖模组。所述防抖模组包括可移动元件、用于支撑所述可移动元件的支撑结构及驱动组件。所述可移动元件包括底面。所述支撑结构围设形成容置空间。所述驱动组件收容于所述容置空间,并位于所述可移动元件的底面一侧。所述驱动组件包括多根驱动线。每一所述驱动线的一端连接所述底面,另一端连接所述支撑结构,以驱动所述可移动元件相对所述支撑结构运动。本申请还提供了一种具有所述防抖模组的用户设备。本申请中将所述驱动组件设置于所述可移动元件的底面一侧,以使得防抖模组及搭载所述防抖模组的用户设备实现了大角度的防抖。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H10D89/10", - "FLH": "H10D89/10(2025.01)I;G06F30/392(2020.01)I;G06F30/398(2020.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "石朝清", - "GKGGH": "CN120751770A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——半导体器件;其他类目中不包括的电固体器件——无机电子半导体器件——集成器件布局", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "0c20cfe495cf716852195c95227f2132", - "FMR": "赵春松;刘帅;张肖涵;何超;方利;许俊豪", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/bd/bd6bf9ad313664a26984f08c03f7f18a.jpg!xinshuwater", - "SQH": "CN202410394243.1", - "PATNAME": "一种晶粒、芯片、版图设计方法及相关设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2024-03-30", - "IPC": "电学——半导体器件;其他类目中不包括的电固体器件——无机电子半导体器件——集成器件布局", - "ZY": "本申请涉及半导体技术领域,尤其涉及一种晶粒、芯片、版图设计方法及相关设备,晶粒包括第一逻辑门单元和M个第一CNT组;第一逻辑门单元包括N个第一FET组,每个FET组包括沟道类型相同的多个FET,不同FET组的FET沟道类型不同;每个第一CNT组包括一个或多个CNT;N个第一FET组的FET制备在M个第一CNT组的CNT上;每个第一FET组对应一个或多个第一CNT组,不同FET组对应不同CNT组,同一FET组中的FET制备在该FET组对应的CNT组上,N个第一FET组中至少有一个FET组的多个FET制备在该FET组对应的多个CNT组上。本申请能够降低MCNT对逻辑门单元的影响,提高电路可靠性。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "G06F16/33", - "FLH": "G06F16/33(2006.01)I;G06F16/51(2006.01)I;G06F16/53(2006.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "陈霁", - "GKGGH": "CN120752627A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京亿腾知识产权代理事务所(普通合伙)", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——计算;推算或计数——电数字数据处理——查询", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "b525f38636da8836631d673d8fbc011e", - "FMR": "蒋昊;李高峰;张秦涛;黄振东;杨光", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/b3/b34145fa30a4a4456fc960ad482d7e57.jpg!xinshuwater", - "SQH": "CN202380094792.0", - "PATNAME": "一种检索方法、装置、系统及终端设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2023-04-28", - "IPC": "物理——计算;推算或计数——电数字数据处理——查询", - "ZY": "一种检索方法,应用于终端设备,包括:获取用户输入的查询语句;基于查询语句与向量索引文件中存储的目标特征向量间的相似度,从向量索引文件中筛选出k个目标特征向量,其中,向量索引文件通过服务器对从终端设备处获取的对象的中间特征向量进行处理得到,并由服务器传输至终端设备,中间特征向量为终端设备上神经网络中的一部分网络层的输出结果;展示K个目标特征向量对应的对象。这样,在检索过程中,通过服务器和终端设备协同处理得到向量索引文件,可搜索到与用户的查询语句相匹配的对象。由于向量索引文件是由服务器和终端设备共同处理得到的,所以可减少终端设备的任务量和资源占用,进而实现了利用服务器资源对终端设备检索能力的提升。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):A61B5/0225申请日:20211217", - "flztggrq": "2022-07-19" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-07-01" - } - ], - "JRGJRQ": "", - "ZFLH": "A61B5/0225", - "FLH": "A61B5/0225(2006.01)I;A61B5/024(2006.01)I;A61B5/00(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "李欣", - "GKGGH": "CN114680852B", - "SQGKGGH": "", - "YXQ": "20201163358672020.12.31CN", - "GSDM": "", - "ZLDLJG": "北京同达信恒知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "人类生活必需——医学或兽医学;卫生学——诊断;外科;鉴定——用电信号控制的压力,如由Korotkoff音驱动的", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "34eed59b6ea9c4ba3dd10e814e8dfebf", - "FMR": "杨荣广;李昌明;杨文建;靳俊叶;张雷;何谦;王刚", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/f3/f35e0c4975469f1039f23928fc5e75e8.jpg!xinshuwater", - "SQH": "CN202111549923.9", - "PATNAME": "一种可穿戴设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2021-12-17", - "IPC": "人类生活必需——医学或兽医学;卫生学——诊断;外科;鉴定——用电信号控制的压力,如由Korotkoff音驱动的", - "ZY": "本申请提供了一种可穿戴设备,涉及电子设备技术领域。该可穿戴设备包括表体、气囊以及气囊盖板。其中:气囊与表体的一个端部可拆卸连接,以使气囊对于表体的覆盖面积较小,从而为表体安装其它的模块预留空间。气囊包括相叠置的第一层结构和第二层结构,第一层结构具有第一空腔,第二层结构具有第二空腔,第一空腔与第二空腔相连通,这样可使气囊的充气空间较大。气囊可通过与表体可拆卸连接的气囊盖板固定于表体,以简化气囊与表体的拆装过程。采用本申请提供的可穿戴设备,可有效的提高该可穿戴设备的功能集成化程度,提高血压的测量精度。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H05K7/20申请日:20200324", - "flztggrq": "2021-10-22" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2021-09-28" - } - ], - "JRGJRQ": "", - "ZFLH": "H05K7/20", - "FLH": "H05K7/20(2006.01)I;H05K7/14(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "熊永强;李稷芳", - "GKGGH": "CN113453479B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——其他类目不包含的电技术——印刷电路;电设备的外壳或结构零部件;电气元件组件的制造——便于冷却、通风或加热的改进", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "613c7216ace806950591e437f7b15a28", - "FMR": "孙永富;杨杰;施健", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/4f/4fd5086f0f00f56645ab21abb803728d.jpg!xinshuwater", - "SQH": "CN202010217496.3", - "PATNAME": "移动终端及中框组件", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-03-24", - "IPC": "电学——其他类目不包含的电技术——印刷电路;电设备的外壳或结构零部件;电气元件组件的制造——便于冷却、通风或加热的改进", - "ZY": "本申请提供一种移动终端及其中框组件。中框组件用于承载移动终端内的电子器件,所述电子器件包括热源,所述中框组件包括中框、一个或多个热管以及第一均热板,中框包括对应热源的散热区,第一均热板容置于所述散热区,热管连接至所述第一均热板,用于对第一均热板进行散热。本申请提升了移动终端的散热性能。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H03D7/14申请日:20190430", - "flztggrq": "2021-11-02" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2021-10-15" - } - ], - "JRGJRQ": "", - "ZFLH": "H03D7/14", - "FLH": "H03D7/14(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "熊永强;李稷芳", - "GKGGH": "CN113508523B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电子电路——由一个载频到另一载频对调制进行解调或变换——平衡装置", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "4e16a799be9a54a7fc0ad86556a9e457", - "FMR": "周永丽;金香菊;赖砚", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/74/74cd47c03b214995b009c2b6fa4ab8bf.jpg!xinshuwater", - "SQH": "CN201980093496.2", - "PATNAME": "开关电路、混频器及电子设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2019-04-30", - "IPC": "电学——电子电路——由一个载频到另一载频对调制进行解调或变换——平衡装置", - "ZY": "本申请实施例公开了一种开关电路、混频器及电子设备,该开关电路包括第一金属氧化物半导体MOS管、第二MOS管、第三MOS管以及第四MOS管,该第一MOS管的栅极和该第四MOS管的栅极均连接第一端口,该第二MOS管的栅极和该第三MOS管的栅极均连接第二端口;该第一MOS管的栅极与该第一端口之间的引线的长度、该第二MOS管的栅极与该第二端口之间的引线的长度、该第三MOS管的栅极与该第二端口之间的引线的长度、该第四MOS管的栅极与该第一端口之间的引线的长度均相等;线性度较高。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W28/24申请日:20210421", - "flztggrq": "2022-12-13" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-10-21" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W28/24", - "FLH": "H04W28/24(2009.01)I;H04B7/15(2006.01)I;H04W4/70(2018.01)N", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "王君;肖鹂", - "GKGGH": "CN115226164B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京龙双利达知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——协商SLA〔业务等级协定〕;协商QoS〔服务质量〕", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "bd20a78aca299f807dcf68ede2bef867", - "FMR": "许胜锋", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/c9/c9d602e395a371deb9b8cec3b7573fcc.jpg!xinshuwater", - "SQH": "CN202110428551.8", - "PATNAME": "中继通信方法和装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2021-04-21", - "IPC": "电学——电通信技术——无线通信网络——协商SLA〔业务等级协定〕;协商QoS〔服务质量〕", - "ZY": "本申请提供了一种中继通信方法和装置,该通信方法包括:第一网元获取远端用户设备的数据流的服务质量QoS需求;所述第一网元根据所述数据流的QoS需求为所述数据流生成第一备选参数集合,并发送给第二网元;第二网元根据所述第一备选参数集合向RAN发送备选QoS配置文件,用于RAN节点选择满足当前中继用户设备与用户面网元之间传输数据流的匹配备选QoS配置文件;第二网元根据该匹配备选QoS配置文件确定PC5链路的PC5QoS参数,进而实现PC5链路的QoS参数调整,保障远端用户设备的端到端QoS需求。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H04L47/10", - "FLH": "H04L47/10(2006.01)I;G06N3/02(2006.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "夏欢", - "GKGGH": "CN120752902A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "深圳市深佳知识产权代理事务所(普通合伙)", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——数字信息的传输,例如电报通信——流量控制;拥塞控制", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "0bc9b77d7e77b31067497c4e746c6f08", - "FMR": "杨宇;任晓哲;户忠哲;施少怀", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/96/96c059687d3cb28dbbc1d8f0dc5ddf08.jpg!xinshuwater", - "SQH": "CN202380094866.0", - "PATNAME": "一种数据处理方法以及相关设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2023-04-28", - "IPC": "电学——电通信技术——数字信息的传输,例如电报通信——流量控制;拥塞控制", - "ZY": "一种数据处理方法以及相关设备,可用于人工智能领域的大模型领域中,方法包括:获取第一数据;根据第一参数和/或第二参数,确定r的取值,r用于指示将第一数据切分为r个子数据,r个子数据分别被机器学习模型中的神经网络层处理,第一数据的处理过程被分成r个子数据的发送任务、r个子数据的处理任务和r个子数据的处理结果的发送任务,前述任务所占用的时间之间能够存在重叠;第一参数指示用于发送子数据的通信网络的网络情况,第二参数指示用于处理子数据的通信节点的负载情况,则r的取值是根据网络情况和/或通信节点的负载情况确定出来的,有利于得到符合实际情况的r值,以缩短整个第一数据的一次数据处理过程所消耗的时间。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "G06N5/04", - "FLH": "G06N5/04(2023.01)I;G06F16/35(2025.01)I;G06N20/00(2019.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "范友飞", - "GKGGH": "CN120745792A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "深圳市深佳知识产权代理事务所(普通合伙)", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——计算;推算或计数——基于特定计算模型的计算机系统——推理方法或设备", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "4edac26e38279f29733d0ff9e4c2fa28", - "FMR": "倪运升;刘传建;唐业辉;韩凯;王云鹤", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/fa/fa4b5f82d123c48db081b4337c81ef31.jpg!xinshuwater", - "SQH": "CN202410384904.2", - "PATNAME": "一种数据处理方法及其装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2024-03-29", - "IPC": "物理——计算;推算或计数——基于特定计算模型的计算机系统——推理方法或设备", - "ZY": "一种数据处理方法,应用于人工智能领域,方法包括:获取第一数据,其中,第一数据和第二数据为机器学习模型在对第一batch的数据和第二batch的数据进行并行推理时同一次迭代过程得到的数据,机器学习模型在第一数据之前对第一batch的数据已经推理得到的数据长度为第一数据长度,机器学习模型在第二数据之前对第二batch的数据已经推理得到的数据长度为第二数据长度,第一数据长度小于第二数据长度;在第一存储位置处写入第一数据,其中,第一存储位置为和机器学习模型在第一数据之前对第一batch的数据的最近一次迭代过程得到的数据的相邻位置。本申请可以在保证正常推理的情况下,不需要写入padding属性的数据,从而降低了存储和算力的开销。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H10D30/62", - "FLH": "H10D30/62(2025.01)I;H10D30/01(2025.01)I;H01L23/31(2006.01)I;H01L21/764(2006.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "苑琳琳", - "GKGGH": "CN120751732A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京同达信恒知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——半导体器件;其他类目中不包括的电固体器件——无机电子半导体器件——鳍式场效应晶体管", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "4bade1dbf8bbef2ecaa5437d73878fa3", - "FMR": "刘明山;丁梦璠;董耀旗;许俊豪", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/05/0588462eaa39dfe33cb7c23aab3a6829.jpg!xinshuwater", - "SQH": "CN202410358779.8", - "PATNAME": "一种芯片、制备方法及电子设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2024-03-27", - "IPC": "电学——半导体器件;其他类目中不包括的电固体器件——无机电子半导体器件——鳍式场效应晶体管", - "ZY": "本申请公开了一种芯片、制备方法及电子设备,芯片、制备方法及电子设备,芯片包括衬底、第一电极层、多个垂直沟道、第一绝缘介质层、栅极结构、第二电极层、第二绝缘介质层,可以在芯片中形成多个VTFET,提升堆叠密度。并且,通过设置第一气体间隙可以有效减少栅极导电层与第一电极层之间的寄生电容,减少VTFET的动态功耗和RC延迟时间。以及,通过将第一气体间隙与垂直沟道和栅极结构之间的至少部分区域中设置介质材料,使得第一气体间隙与垂直沟道和栅极结构之间分别存在至少部分介质材料,从而可以提高VTFET器件结构的机械稳定性和可靠性,满足芯片对性能、功耗、面积和成本以及可靠性的要求。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04Q11/00申请日:20220211", - "flztggrq": "2022-07-12" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-06-24" - } - ], - "JRGJRQ": "", - "ZFLH": "H04Q11/00", - "FLH": "H04Q11/00(2006.01)I;H04B10/079(2013.01)I;H04B10/075(2013.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "石翰林", - "GKGGH": "CN114666685B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "深圳市深佳知识产权代理事务所(普通合伙)", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——选择〔1,2009.01〕——多路复用系统的选择装置", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "30399b252b18e83f91dfaca33b8724d3", - "FMR": "董振华;祁彪;董小龙", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/f4/f4dd8306e8ecd51d0bda9210bd30fe95.jpg!xinshuwater", - "SQH": "CN202210130428.2", - "PATNAME": "无源光器件的识别方法、装置和设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2022-02-11", - "IPC": "电学——电通信技术——选择〔1,2009.01〕——多路复用系统的选择装置", - "ZY": "本申请提供了一种无源光器件的识别方法,识别方法应用于检测领域。识别方法包括以下步骤:识别设备获取目标检测信号的目标光谱。目标检测信号是通过无源光器件反射或透射得到的。识别设备确定M个参考光谱中的第一参考光谱和目标光谱是否满足第一条件。M为大于0的整数。若满足第一条件,则识别设备获取与第一参考光谱对应的目标信息。目标信息用于表征无源光器件的相关信息。在本申请中,通过第一参考光谱和目标光谱,识别设备可以远程获取无源光器件的相关信息,从而提高了运维效率。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "14-03", - "FLH": "14-03(14)", - "PTYPE": "外观设计", - "GKGGR": "2025-10-03", - "DLR": "易浩球", - "GKGGH": "CN309530083S", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "录音、通讯或信息再现设备-通讯设备和无线遥控器、无线电放大器", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "1cc61f8b0661f655153c0491c7915567", - "FMR": "徐汀;黄璐;王佳熙;许浩森", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/35/355da2eff2961f1a385126d2f1e1b8fe.jpg!xinshuwater", - "SQH": "CN202230465720.0", - "PATNAME": "手机的摄像头及摄像头主体", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2022-07-21", - "IPC": "录音、通讯或信息再现设备-通讯设备和无线遥控器、无线电放大器", - "ZY": "1.本外观设计产品的名称:手机的摄像头及摄像头主体。2.本外观设计产品的用途:整体用途为用于提供网络通信、语音通话、事务处理(数据存储、录音、图片浏览)等;局部用途为用于拍摄照片或视频。3.本外观设计产品的设计要点:在于要求保护的局部形状。4.最能表明设计要点的图片或照片:设计1立体图1。5.指定设计1为基本设计。6.其他需要说明的情形:图中虚线表示不要求保护的部分。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "14-03", - "FLH": "14-03(14)", - "PTYPE": "外观设计", - "GKGGR": "2025-10-03", - "DLR": "李稷芳", - "GKGGH": "CN309530082S", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "录音、通讯或信息再现设备-通讯设备和无线遥控器、无线电放大器", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "76a9338cc9a426ce0fa972927c725fc1", - "FMR": "徐汀;黄璐;王佳熙;许浩森", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/7a/7aa6708863c06e3e4af049dae3461506.jpg!xinshuwater", - "SQH": "CN202230465702.2", - "PATNAME": "手机的摄像头主体", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2022-07-21", - "IPC": "录音、通讯或信息再现设备-通讯设备和无线遥控器、无线电放大器", - "ZY": "1.本外观设计产品的名称:手机的摄像头主体。2.本外观设计产品的用途:整体用途为主要用于提供网络通信、语音通话、事务处理(数据存储、录音、图片浏览)等,局部用途为用于拍摄照片或视频。3.本外观设计产品的设计要点:在于要求保护的局部形状。4.最能表明设计要点的图片或照片:设计1立体图1。5.指定设计1为基本设计。6.其他需要说明的情形:图中虚线表示不要求保护的部分。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):G01C21/20申请日:20201216", - "flztggrq": "2022-07-05" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-06-17" - } - ], - "JRGJRQ": "", - "ZFLH": "G01C21/20", - "FLH": "G01C21/20(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "申健", - "GKGGH": "CN114636421B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——测量;测试——测量距离、水准或者方位;勘测;导航;陀螺仪;摄影测量学或视频测量学——执行导航计算的仪器", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "348d3bad10c2cdea0a18b9e1eec8970b", - "FMR": "张敏锐;余志文;何其刚", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/6c/6c18e2b5b4495174d67cf7c176fbe154.jpg!xinshuwater", - "SQH": "CN202011491513.9", - "PATNAME": "确定滑雪过程中异常位置的方法及电子设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-12-16", - "IPC": "物理——测量;测试——测量距离、水准或者方位;勘测;导航;陀螺仪;摄影测量学或视频测量学——执行导航计算的仪器", - "ZY": "本申请公开了确定滑雪过程中异常位置的方法及电子设备,涉及电子技术领域,可以实现对滑雪过程中异常位置的确定。本申请提供的方案中,电子设备可以通过分析用户在同一滑道上多次滑行过程中采集的气压数据,以得到用户在该滑道上滑行时的气压变化规律。进而根据该气压变化规律,可以识别用户在上述多次滑行过程中遇到的气压变化速度不符合上述气压变化规律的异常位置。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W52/06", - "FLH": "H04W52/06(2009.01)I;H04W52/54(2009.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "朱琳琳", - "GKGGH": "CN120751469A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京同达信恒知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——TPC算法", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "656a304bb7b9614db756bfd90f3839da", - "FMR": "唐云帅;岳华伟;孙德福;黄博;王蓉", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/2c/2c654b8017d7489c5ef67b68582ad886.jpg!xinshuwater", - "SQH": "CN202410389292.6", - "PATNAME": "一种通信方法及装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2024-03-30", - "IPC": "电学——电通信技术——无线通信网络——TPC算法", - "ZY": "本申请实施例提供一种通信方法及装置,能够兼顾符号的发送功率和符号携带的数据量。本申请实施例提供的通信方法包括:采用第一功率在无线帧的第一符号部分发送控制信令,所述第一符号部分包括至少一个连续的、用于发送控制信令的符号;采用第二功率在无线帧的第二符号部分发送数据,所述第二符号部分包括至少一个连续的、用于发送数据的符号;在所述第一符号部分和所述第二符号部分之间包括第三符号部分,所述第三符号部分用于实现所述网络设备从所述第一功率切换到所述第二功率,所述第三符号部分包括至少一个连续的符号。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "G03B17/08", - "FLH": "G03B17/08(2021.01)I;G03B31/00(2021.01)I;G03B30/00(2021.01)I;H04N23/50(2023.01)I;H04N23/51(2023.01)I", - "PTYPE": "实用新型", - "GKGGR": "2025-10-03", - "DLR": "王洪", - "GKGGH": "CN223413604U", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京润泽恒知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——摄影术;电影术;利用了光波以外其他波的类似技术;电记录术;全息摄影术〔4〕——摄影、放映或观看用的装置或设备;利用了光波以外其他波的类似技术的装置或设备;以及有关的附件——防水机身和外壳", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "e070663cba6a5da511138c18b8221c92", - "FMR": "严泳兴;马志刚;王熠;刘彦君", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/9c/9ca3c2109dbf94a9d9d8a867211e7f99.jpg!xinshuwater", - "SQH": "CN202422587094.9", - "PATNAME": "摄像装置及电子设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2024-10-24", - "IPC": "物理——摄影术;电影术;利用了光波以外其他波的类似技术;电记录术;全息摄影术〔4〕——摄影、放映或观看用的装置或设备;利用了光波以外其他波的类似技术的装置或设备;以及有关的附件——防水机身和外壳", - "ZY": "本申请提供了一种摄像装置及电子设备。其中,摄像装置包括:壳体,壳体上设有凹槽;麦克风,麦克风的至少部分位于凹槽内;密封部,密封部位于麦克风与凹槽的内表面之间;限位结构,限位结构固定于壳体上,限位结构还与麦克风背离凹槽的底面的表面抵接,限位结构被配置为通过麦克风为密封部提供压力,以使密封部在压力的作用下与凹槽的内表面抵接,以及与麦克风的外表面抵接。本申请能够提高摄像装置的生产效率以及降低摄像装置的制造成本。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04L45/00申请日:20200604", - "flztggrq": "2022-11-01" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-10-14" - } - ], - "JRGJRQ": "", - "ZFLH": "H04L45/00", - "FLH": "H04L45/00(2022.01)I;H04L45/28(2022.01)I;H04L67/1095(2022.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "庞茜", - "GKGGH": "CN115190061B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京三高永信知识产权代理有限责任公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——数字信息的传输,例如电报通信——数据交换网络中数据包的路由或寻路", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "0e7594b8ab020065e758d9b16a239a19", - "FMR": "许健彬;郭锋;张旭东;徐海军;赵华", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/58/58760591f729b96172eff711a0360a58.jpg!xinshuwater", - "SQH": "CN202210685994.X", - "PATNAME": "处理路由报文的方法、通信设备、存储介质及系统", - "SQR": "华为技术有限公司", - "FASQ": "20201050027572020.06.04", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-06-04", - "IPC": "电学——电通信技术——数字信息的传输,例如电报通信——数据交换网络中数据包的路由或寻路", - "ZY": "本申请实施例公开了一种处理路由报文的方法、通信设备、存储介质及系统,属于通信技术领域。在本申请实施例中,路由处理单元之间通过报文的形式同步数据,由于报文的格式是标准的、统一的,所以,通过报文的形式同步数据之后,就算各个路由处理单元支持的数据格式不兼容,或者说某个路由处理单元不具有某些数据的收发能力,也不会对路由处理单元进行相应修改,更不会导致路由数据同步失败,也就是说,通过报文的形式同步数据能够保证多数据格式的路由处理单元的兼容性,而且,在路由报文携带新的属性时,路由处理单元在不经过修改的情况下就可以接收,相对来说减少了大量的修改工作。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W72/04申请日:20201102", - "flztggrq": "2023-07-11" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2023-06-23" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W72/04", - "FLH": "H04W72/04(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "时林;王君", - "GKGGH": "CN116326072B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京龙双利达知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——无线资源分配", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "bccc64a00127ac91769704362a499516", - "FMR": "宣一荻;谢信乾;郭志恒", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/55/559b4eeddc338e807d442f33ca1a52f4.jpg!xinshuwater", - "SQH": "CN202080106288.4", - "PATNAME": "通信的方法、装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-11-02", - "IPC": "电学——电通信技术——无线通信网络——无线资源分配", - "ZY": "本申请提供了一种通信的方法和装置,该方法包括:网络设备发送第一信令和第二信令,该第一信令包括用于指示第一频域资源和第一MCS的第一指示信息,该第一频域资源包括第二频域资源;该第二信令包括用于指示该第二频域资源和第二MCS的第二指示信息;终端设备根据该第一信令和第二信令确定在第一时间段内的该第一频域资源上接收第一下行信号,其中,该第一下行信号中承载于该第二频域资源上的部分对应的MCS为该第二MCS,该第一下行信号中承载于第三频域资源上的部分对应的MCS为该第一MCS或第三MCS,该第三MCS基于该第一MCS确定。通过至少一个信令,实现了对单一传输块的多MCS配置。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H01L29/778申请日:20201127", - "flztggrq": "2023-04-07" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2023-03-21" - } - ], - "JRGJRQ": "", - "ZFLH": "H10D30/47", - "FLH": "H10D30/47(2025.01)I;H10D64/00(2025.01)I;H10D64/27(2025.01)I;H10D30/01(2025.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "万欣慰", - "GKGGH": "CN115836394B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "深圳市深佳知识产权代理事务所(普通合伙)", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——半导体器件;其他类目中不包括的电固体器件——无机电子半导体器件——具有二维电荷载流子气沟道,例如纳米带FETs或高电子迁移率晶体管", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "50ff9b66cb3c985546cba5d02aa43f8d", - "FMR": "汤岑;饶进;刘涛;李海军;鲁微;乐伶聪;马俊彩;张志利", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/52/52f5551cb0f72feb009bf73f8d79b2d7.jpg!xinshuwater", - "SQH": "CN202080102939.2", - "PATNAME": "一种半导体器件及其制造方法", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-11-27", - "IPC": "电学——半导体器件;其他类目中不包括的电固体器件——无机电子半导体器件——具有二维电荷载流子气沟道,例如纳米带FETs或高电子迁移率晶体管", - "ZY": "本申请实施例公开了一种半导体器件及其制造方法,半导体器件可以包括衬底、栅极、第二介质层、场板,其中衬底上具有第一介质层,第一介质层在第一区域的厚度大于在第一区域之外的第二区域的厚度,栅极位于衬底上且位于第一区域,栅极包括垂直衬底表面的方向上连接的第一栅极结构和第二栅极结构,第一栅极结构垂直衬底表面的方向上贯穿第一介质层,第二栅极结构形成于第一介质层远离所述衬底的一侧且覆盖部分第一介质层,第二介质层覆盖栅极和第一介质层,场板位于第二介质层上,且同时存在于第一区域和第二区域,这样,第二栅极结构和漏极之间的电容降低,而场板与沟道之间的电容增加,使器件的寄生电容减小,提高器件在高频下的增益特性。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W36/00申请日:20200529", - "flztggrq": "2023-05-26" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2021-12-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W36/00", - "FLH": "H04W36/00(2009.01)I;H04W36/30(2009.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "熊永强;李稷芳", - "GKGGH": "CN113747520B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——切换或重选装置", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "4126db47b150188fa98179e7af223aba", - "FMR": "谢宗慧;王宏;陈磊;李秉肇", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/0a/0a089386e75e93fd6857c8dfe8299fbe.jpg!xinshuwater", - "SQH": "CN202010476646.2", - "PATNAME": "一种小区重选方法及相关装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-05-29", - "IPC": "电学——电通信技术——无线通信网络——切换或重选装置", - "ZY": "本申请提供了一种小区重选方法及相关装置,该方法包括:若承载寻呼消息的频率资源不同于主频率资源,则测量所述频率资源对应的参考信号的质量;根据所述频率资源对应的参考信号的质量,启动邻区测量;根据所述邻区测量的结果,进行小区重选。实现在承载寻呼消息的频率资源不同于主频率资源时,避免终端设备在还未触发小区重选时已经无法解码寻呼消息的问题。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H04L45/16", - "FLH": "H04L45/16(2006.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "赵丹", - "GKGGH": "CN120752901A", - "SQGKGGH": "", - "YXQ": "PCT/CN2023/0811722023.03.13CN", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——数字信息的传输,例如电报通信——多点路由", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "56d6e98883171d3586ab65f22555b8b9", - "FMR": "周伟光;谭丽娟;高飞;王心远;龙华;刘燕", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/7c/7cd2f977803711d22d580d76d65b9f42.jpg!xinshuwater", - "SQH": "CN202380095017.7", - "PATNAME": "传输方法、装置、设备及网络", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2023-11-23", - "IPC": "电学——电通信技术——数字信息的传输,例如电报通信——多点路由", - "ZY": "公开了一种传输方法、装置、设备及网络,方法应用于通信领域。第一设备和第二设备通过端口连接,方法包括:从接收缓冲区获取第一业务流的报文;根据路由器转发表,通过第一设备与第二设备间的第一业务虚拟通道的发送缓冲区传输第一业务流的报文,路由器转发表用于指示基于业务虚拟通道的报文转发规则,业务虚拟通道用于支持两个设备间业务流双向传输。本申请提供的传输方法,设备间通过端口可以自由连接,相互连接的设备间基于业务虚拟通道实现业务流双向传输,以及,设备根据路由器转发表指示的基于业务虚拟通道的报文转发规则转发业务流的报文,从而实现设备间的自由连接和数据传输,提升设备间组网和数据传输的灵活性。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "F16C11/12", - "FLH": "F16C11/12(2006.01)I;G06F1/16(2006.01)I;H04M1/02(2006.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "潘平", - "GKGGH": "CN120752440A", - "SQGKGGH": "", - "YXQ": "20231020905672023.02.27CN", - "GSDM": "", - "ZLDLJG": "北京同达信恒知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "机械工程;照明;加热;武器;爆破——工程元件或部件;为产生和保持机器或设备的有效运行的一般措施;一般绝热——轴;软轴;在挠性护套中传递运动的机械装置;曲轴机构的元件;枢轴;枢轴连接;除传动装置、联轴器、离合器或制动器元件以外的转动工程元件;轴承——包括挠性连接,如片簧", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "f3c2fa03816bb6b3d6244f1a68153275", - "FMR": "林杨明;程正树;张海峰;王松", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/17/1723bd11a25cf83682041bf631a47669.jpg!xinshuwater", - "SQH": "CN202380094661.2", - "PATNAME": "一种转轴机构及电子设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2023-09-20", - "IPC": "机械工程;照明;加热;武器;爆破——工程元件或部件;为产生和保持机器或设备的有效运行的一般措施;一般绝热——轴;软轴;在挠性护套中传递运动的机械装置;曲轴机构的元件;枢轴;枢轴连接;除传动装置、联轴器、离合器或制动器元件以外的转动工程元件;轴承——包括挠性连接,如片簧", - "ZY": "一种转轴机构(1)及电子设备,转轴机构(1)包括第一转动模组(112)、第二转动模组(113)和模组支架(111),第一转动模组(112)与第二转动模组(113)分别设置于模组支架(111)的两侧。第一转动模组(112)包括第一摆臂(1121)、第一支撑臂(1122)、第一壳体支架(1123)、第一门板(1124)及第一连杆(1125),第一门板(1124)用于支撑柔性显示屏,第一摆臂(1121)与第一支撑臂(1122)设置于第一门板(1124)背离柔性显示屏的一面,且两者沿转轴机构(1)的长度方向分布并分别与模组支架(111)转动连接;第一壳体支架(1123)设置于第一摆臂(1121)和第一支撑臂(1122)背离第一门板(1124)的一面,第一摆臂(1121)与第一壳体支架(1123)转动连接,第一支撑臂(1122)与第一壳体支架(1123)滑动连接,第一门板(1124)与第一壳体支架(1123)转动连接;第一连杆(1125)的一端与第一摆臂(1121)转动连接,另一端与第一门板(1124)转动连接。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04L45/74申请日:20211216", - "flztggrq": "2023-07-07" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2023-06-20" - } - ], - "JRGJRQ": "", - "ZFLH": "H04L45/74", - "FLH": "H04L45/74(2022.01)I;H04L45/122(2022.01)I;H04L45/00(2022.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "申健", - "GKGGH": "CN116266821B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——数字信息的传输,例如电报通信——路由地址处理", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "2d21222dad48bdc109dec6c07f2b8aba", - "FMR": "李洪峰;陈哲;王闯;黄小红", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/44/448c75ca8a2cd0156a316ef7e57dc1fd.jpg!xinshuwater", - "SQH": "CN202111542266.5", - "PATNAME": "一种报文转发方法及装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2021-12-16", - "IPC": "电学——电通信技术——数字信息的传输,例如电报通信——路由地址处理", - "ZY": "本申请公开了一种报文转发方法及装置,涉及通信技术领域。该方法能够提高园区网络内节点的转发效率。该方法应用于园区网络中的第一节点。这里,第一节点是该园区网络中具有预设网络结构的子网络中的任一个节点,第一节点在该子网络中的深度是第一深度。该方法包括:接收携带有第一定位信息的第一报文;如果第一深度和第一定位信息中与第一标识号ID对应的第二深度不同,则根据第一深度和第二深度,确定下一跳节点;向该下一跳节点转发第一报文。其中,第一报文携带的第一定位信息用于确定接收第一报文的下一跳节点,该第一定位信息包括用于标识子网络中节点的第一ID。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H02P6/04", - "FLH": "H02P6/04(2016.01)I;B60L15/20(2006.01)I;B60L15/32(2006.01)I;H02P6/08(2016.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "时林;王君", - "GKGGH": "CN120750217A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京龙双利达知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——发电、变电或配电——电动机、发电机或机电变换器的控制或调节;控制变压器、电抗器或扼流圈〔4〕——用于控制或调节多于一个电动机的速度或转矩的装置〔6〕", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "098a7c156860c96a3fe9d3007193977a", - "FMR": "李义;毋超强;李程", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/19/197a7800e85cdd63291a596c569fed78.jpg!xinshuwater", - "SQH": "CN202510807556.X", - "PATNAME": "定子极数可变的动力总成、控制方法和电动车辆", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2025-06-13", - "IPC": "电学——发电、变电或配电——电动机、发电机或机电变换器的控制或调节;控制变压器、电抗器或扼流圈〔4〕——用于控制或调节多于一个电动机的速度或转矩的装置〔6〕", - "ZY": "一种定子极数可变的动力总成、控制方法和电动车辆,涉及新能源汽车领域,可应用于纯电动车辆及混动车辆。该动力总成包括驱动电机和电机控制器,驱动电机包括定子和转子,电机控制器用于向定子的定子绕组输出电流以驱动该驱动电机,电机控制器包括并联的多个逆变电路,每个逆变电路包括并联的三相桥臂,定子绕组包括多组三相绕组。多个逆变电路分别用于向多组三相绕组输出电流以使定子绕组产生磁场,电机控制器用于通过调整多个逆变电路输出的电流以调整定子绕组的磁场极数。根据本方案,实现兼顾同步电机和异步电机的优势,提高电机效率,降低能耗。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):G06F21/44申请日:20201231", - "flztggrq": "2022-07-19" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-07-01" - } - ], - "JRGJRQ": "", - "ZFLH": "G06F21/44", - "FLH": "G06F21/44(2013.01)I;G06F21/60(2013.01)I;G06F21/64(2013.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "张卿;毛威", - "GKGGH": "CN114692119B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京龙双利达知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——计算;推算或计数——电数字数据处理——程序或设备认证", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "14e8d28673a6b8c8a96c063439e224bc", - "FMR": "李洪玺;祁长乐", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/27/273f324db36a351ee3d339dc190771c8.jpg!xinshuwater", - "SQH": "CN202011626815.2", - "PATNAME": "校验应用的方法和电子设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-12-31", - "IPC": "物理——计算;推算或计数——电数字数据处理——程序或设备认证", - "ZY": "本申请提供了一种校验应用的方法和电子设备,该方法应用于包括第一电子设备和第二电子设备的系统中,该第一电子设备中安装有第一应用,该第二电子设备中安装有第二应用,响应于用户的操作,第一电子设备获取第二应用的公钥,并根据可信任公钥和获取的公钥判断该第二应用的真实性,在确定第二应用可信任的情况下,向第二电子设备发送第一数据,第二电子设备调用第二应用处理该第一数据,并向第一电子设备反馈。该技术方案能够实现跨设备校验另一应用的真实性,避免了用户信息的泄露。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "G06F11/3668", - "FLH": "G06F11/3668(2025.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "刘丽萍", - "GKGGH": "CN120743743A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——计算;推算或计数——电数字数据处理——软件测试", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "c0a356cc757690d98645302f314b1ade", - "FMR": "刘一卓", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/58/5817a3c857a039d28cb6633fccb4afa3.jpg!xinshuwater", - "SQH": "CN202410390165.8", - "PATNAME": "一种数据库测试方法、测试系统及相关设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2024-03-29", - "IPC": "物理——计算;推算或计数——电数字数据处理——软件测试", - "ZY": "本申请提供了一种数据库测试方法、测试系统及相关设备,该测试方法通过将待测脚本发布在测试数据库,然后在测试数据库中对待测脚本进行测试,根据待测脚本的测试结果来确定多维数据库的测试结果,由于多维数据库中的每个单元格中的数据,可以理解为是计算脚本生成的,因此,如果计算脚本通过测试,即可证明计算脚本可以输出正确的结果,那么多维数据库中每个单元格中的数据也可以确定为正确的结果,这样通过测试计算脚本的方式对多维数据库进行测试,可以避免对每个单元格中的数据进行测试,进而提高测试效率,解决多维数据库测试困难的问题。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04N23/63申请日:20200530", - "flztggrq": "2023-04-21" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2023-04-04" - } - ], - "JRGJRQ": "", - "ZFLH": "H04N23/63", - "FLH": "H04N23/63(2023.01)I;H04N23/95(2023.01)I;H04N23/62(2023.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "董鑫", - "GKGGH": "CN115914826B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——图像通信,如电视——使用电子取景器", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "65ba8e7ec7ab81caa8ef2e6127412de8", - "FMR": "吴磊;张旭;余明慧;陈玉钢;陈沐春", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/28/28d6b4beca11a5936bafd2dcbdd3822c.jpg!xinshuwater", - "SQH": "CN202211521236.0", - "PATNAME": "一种图像内容的去除方法及相关装置", - "SQR": "华为技术有限公司", - "FASQ": "20201048100752020.05.30", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-05-30", - "IPC": "电学——电通信技术——图像通信,如电视——使用电子取景器", - "ZY": "本申请公开了一种图像内容的去除方法,涉及计算机视觉领域,该方法包括:启动相机应用,显示该相机应用的拍照预览界面。获取摄像头捕捉到的第一预览画面和第一参考帧画面,确定该第一预览画面中的该第一对象为待去除对象。根据该第一参考帧画面,确定该第一预览画面中的待填补内容,其中,该待填补内容为该第一预览画面中,该第二对象被该第一对象遮挡住的图像内容。该终端根据该待填补内容和该第一预览画面,生成第一修复画面。这样,可以去除用户所拍摄的图片或视频中用户不想要的图像内容。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04N5/225申请日:20200701", - "flztggrq": "2023-06-16" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-01-04" - } - ], - "JRGJRQ": "", - "ZFLH": "H04N23/50", - "FLH": "H04N23/50(2023.01)I;H04M1/02(2006.01)I;H04N23/55(2023.01)I;H04N23/58(2023.01)I;H04N23/57(2023.01)I;H04N23/67(2023.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "熊永强;李稷芳", - "GKGGH": "CN113890965B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——图像通信,如电视——结构细节", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "277ee7cd6f24cab4bd8751f67e34bddb", - "FMR": "淡佳鹏;郭利德;卢磊;王昕", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/82/828c857267d65c78172a95912cef69f2.jpg!xinshuwater", - "SQH": "CN202010638092.1", - "PATNAME": "联动装置、摄像模组及电子设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-07-01", - "IPC": "电学——电通信技术——图像通信,如电视——结构细节", - "ZY": "本申请公开了一种联动装置、摄像模组及电子设备。联动装置用于连续变焦的摄像模组中,其包括基座、以及与基座滑动连接的第一载座和第二载座;基座上设有第一定位元件,第一载座上设有第一感应元件和第二定位元件,第二载座上设有第二感应元件;第一感应元件与第一定位元件相对设置,以检测第一载座相对基座的位置;第二感应元件与第二定位元件相对设置,以检测第二载座相对第一载座的位置。通过将第一载座和第二载座的运动关系设置成关联运动,在第一载座运动时,第二载座可以响应第一载座的运动而对应运动,以提高第一载座和第二载座的位置控制精度。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W72/02申请日:20200424", - "flztggrq": "2022-12-13" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-11-25" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W72/02", - "FLH": "H04W72/02(2006.01)I;H04W72/04(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "申健", - "GKGGH": "CN115399007B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——通过用户或终端选择无线资源", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "b860ed2d9f75112f9bb49fc94680e706", - "FMR": "焦春旭;董蕾;卢磊", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/30/303c5b8c750d0bbf9095fd3997c02e8c.jpg!xinshuwater", - "SQH": "CN202080099529.7", - "PATNAME": "资源指示信息的传输方法、设备及系统", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-04-24", - "IPC": "电学——电通信技术——无线通信网络——通过用户或终端选择无线资源", - "ZY": "本申请实施例提供一种资源指示信息的传输方法、设备及系统,适用于车联网、智能网联车、辅助驾驶以及智能驾驶等领域,可以降低终端设备之间的干扰,或者提升资源利用率。该方法中,第一终端设备根据用于第二终端设备传输PSSCH和/或PSCCH的子信道数目,确定X个候选单时间单元资源集合,并向第二终端设备指示该X个候选单时间单元资源集合中,时域位置位于第一时间窗内的K个候选单时间单元资源,以便第二终端设备根据该K个候选单时间单元资源确定第一时频资源。其中,第一时间窗为该X个候选单时间单元资源集合中至少一个候选单时间单元资源集合对应的选择窗口所包括的时间窗。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "G09F9/30", - "FLH": "G09F9/30(2006.01)I;H05K5/02(2006.01)I", - "PTYPE": "实用新型", - "GKGGR": "2025-10-03", - "DLR": "赵丹", - "GKGGH": "CN223413818U", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——教育;密码术;显示;广告;印鉴——显示;广告;标记;标签或铭牌;印鉴——由组合单个部件所形成的符号所需的字符或字符组9/302-9/37的G09F9/302组优先于G09F9/305至G09F9/37的各组", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "f6e22bb237b820a6f6f8d6e2d059b3e7", - "FMR": "朱明超;甄海涛;刘妍", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/65/6564c2ed30d7bef177eb35221e294135.jpg!xinshuwater", - "SQH": "CN202422251349.4", - "PATNAME": "一种电子设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2024-09-12", - "IPC": "物理——教育;密码术;显示;广告;印鉴——显示;广告;标记;标签或铭牌;印鉴——由组合单个部件所形成的符号所需的字符或字符组9/302-9/37的G09F9/302组优先于G09F9/305至G09F9/37的各组", - "ZY": "本申请实施例提供一种电子设备,涉及显示设备领域。旨在降低电子设备的厚度。具体方案包括:该防护结构围设在该盖板的外周,且不覆盖该盖板的出光面。沿电子设备的厚度方向,防护结构至少重叠设置在边框上;电子设备处于展开状态时,沿所述边框的厚度方向,防护结构和边框之间具有第一间隙;所述电子设备从展平状态向折叠状态运动的过程中,第一间隙的宽度减小。防护结构凸出于盖板的尺寸可以为零或接近零,有利于减薄电子设备。第一间隙位于电子设备内部,外部不可视,有利于电子设备美观。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W76/10申请日:20201230", - "flztggrq": "2023-08-08" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2023-07-21" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W74/00", - "FLH": "H04W74/00(2006.01)I;H04W76/10(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "申健", - "GKGGH": "CN116472778B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——无线信道接入", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "cec870bb56ccb2b97598f03aeb7088fe", - "FMR": "李娇娇;谢曦;常俊仁;陈磊;毛颖超", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/66/668a417dbf2c6a2d7807e5f50f1b273a.jpg!xinshuwater", - "SQH": "CN202080106720.X", - "PATNAME": "随机接入方法、装置及系统", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-12-30", - "IPC": "电学——电通信技术——无线通信网络——无线信道接入", - "ZY": "本申请实施例提供随机接入方法、装置及系统,用于解决不同类型终端设备或不同类型业务在随机接入时的RNTI冲突。方法包括:在第一随机接入信道时机RO上向网络设备发送随机接入前导码,第一RO对应于第一设备类型和/或第一业务类型;监听第一RO关联的第一无线网络临时标识RNTI加扰的物理下行控制信息PDCCH;其中,第一RNTI的计算与第一设备类型和/或第一业务类型相关联。本申请适用于通信技术领域。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W4/02", - "FLH": "H04W4/02(2018.01)I;H04W72/044(2023.01)I;H04W72/23(2023.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "赵祎", - "GKGGH": "CN120751332A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京同达信恒知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——利用位置信息的业务", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "1f13e3f6452356373a7351158da19447", - "FMR": "高鑫;黄甦;夏金环", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/15/15b8a911eee5f6d89749a608df0d1ce5.jpg!xinshuwater", - "SQH": "CN202510773706.X", - "PATNAME": "一种通信方法、装置及系统", - "SQR": "华为技术有限公司", - "FASQ": "20211036232202021.04.02", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2021-04-02", - "IPC": "电学——电通信技术——无线通信网络——利用位置信息的业务", - "ZY": "本申请实施例涉及一种通信方法、装置及系统,用以提高定位精度和定位性能。所述方法包括:终端设备接收定位参考信号配置信息;在所述定位参考信号配置信息包含物理小区标识和/或小区全球标识的情况下,若第一条件得到满足,所述终端设备确定所述定位参考信号配置信息对应的定位参考信号来自服务小区。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "发明专利申请更正", - "flztxq": "发明专利申请更正卷=39号=19-02页码=扉页更正项目=PCT国际申请进入国家阶段日误=2023.02.06正=2023.02.07", - "flztggrq": "2024-05-24" - }, - { - "flzt": "发明专利公报更正", - "flztxq": "发明专利公报更正卷=39号=19-02更正项目=PCT国际申请进入国家阶段日误=2023.02.06正=2023.02.07", - "flztggrq": "2024-05-24" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W8/22申请日:20200807", - "flztggrq": "2023-06-06" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2023-05-12" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W8/22", - "FLH": "H04W8/22(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "时林;王君", - "GKGGH": "CN116114276B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京龙双利达知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——终端数据的处理或传送,例如状态或物理能力", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "0cb50fe6a30052123b4eb4c8eb9cc2f2", - "FMR": "陈磊;王宏;谢宗慧;于海凤", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/40/40c57de20f1c88ff53571b5480eb5782.jpg!xinshuwater", - "SQH": "CN202080104142.6", - "PATNAME": "信令传输的方法和装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-08-07", - "IPC": "电学——电通信技术——无线通信网络——终端数据的处理或传送,例如状态或物理能力", - "ZY": "本申请提供了一种信令传输的方法和装置,能够提高信令传输的效率。该方法包括:第一终端设备根据保存的上下文恢复至少一个专用信令无线承载,该第一终端设备处于非激活态;该第一终端设备向第一无线接入网设备发送第一消息,第一消息包括该第一终端设备向核心网设备发送的第一非接入层NAS信令,该第一NAS信令承载于至少一个专用信令无线承载中的一个。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W12/06", - "FLH": "H04W12/06(2021.01)I;H04W12/122(2021.01)I;H04L41/0894(2022.01)I;H04L9/40(2022.01)I;H04L12/46(2006.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "毛威;肖鹂", - "GKGGH": "CN120751381A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京龙双利达知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——鉴权", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "48c5e9da129ea62b421e70d5d4926347", - "FMR": "李赫;雷骜;吴荣", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/6a/6a55491dcc8331a4129e1b1e887f1bf9.jpg!xinshuwater", - "SQH": "CN202510957174.5", - "PATNAME": "通信方法和通信装置", - "SQR": "华为技术有限公司", - "FASQ": "20211019470092021.02.21", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2021-02-21", - "IPC": "电学——电通信技术——无线通信网络——鉴权", - "ZY": "本申请提供了一种通信方法和通信装置,包括:第一网元接收来自第二网元的第一请求消息,该第一请求消息用于请求对第一终端设备执行第一操作;该第一网元根据该第一请求消息确定该第二网元是否有权限请求对该第一终端设备执行所述第一操作。通过验证发送请求消息的网元是否有权限请求执行相关操作,以判断该网元是否是攻击者,从而减少系统业务因为攻击者的请求而受到影响的情况,提高系统的安全性。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W72/04申请日:20200420", - "flztggrq": "2022-12-13" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-11-25" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W72/04", - "FLH": "H04W72/04(2006.01)I;H04W72/02(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "邱青云", - "GKGGH": "CN115399026B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "深圳市深佳知识产权代理事务所(普通合伙)", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——无线资源分配", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "2f3d23f361bbb5ece01cd0c16ba9f1d5", - "FMR": "陈二凯;苏宏家;郭文婷;卢磊", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/ac/acecf595af0eab0d74d0547dcfc8a551.jpg!xinshuwater", - "SQH": "CN202080099811.5", - "PATNAME": "一种通信方法以及通信装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-04-20", - "IPC": "电学——电通信技术——无线通信网络——无线资源分配", - "ZY": "本申请实施例提供了一种通信方法以及通信装置,其中:终端设备确定当前资源侦听的时长小于资源侦听窗口的长度;根据第一参数确定第二参数,第二参数包括以下至少一项:用于资源排除的参考信号接收功率RSRP门限,资源选择窗口的长度,或数据信道的发射功率;终端设备根据第二参数选择发送资源,和/或,终端设备根据第二参数发送终端设备的待发送数据。可以降低和其它终端设备选择相同发送资源的概率,即减少资源选择碰撞,提升传输可靠性和系统资源利用率。本实施例提供的方法可以应用于通信系统,例如V2X、LTE‑V、V2V、车联网、MTC、IoT、LTE‑M,M2M,物联网等。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W28/16申请日:20210730", - "flztggrq": "2022-10-25" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-10-04" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W28/16", - "FLH": "H04W28/16(2009.01)I;H04W28/24(2009.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "熊永强;李稷芳", - "GKGGH": "CN115150897B", - "SQGKGGH": "", - "YXQ": "20211034365592021.03.30CN", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——中央资源管理;协商资源或通信参数,例如协商带宽或QoS", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "45fd2757e1b00d102c59a17bf38070e6", - "FMR": "王皓;姚松平;张志军;李锋;郭兴民;顾燕杰", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/88/88a8cde63c2a041e53040d4301e2bac4.jpg!xinshuwater", - "SQH": "CN202110871914.5", - "PATNAME": "通信资源协作方法及电子设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2021-07-30", - "IPC": "电学——电通信技术——无线通信网络——中央资源管理;协商资源或通信参数,例如协商带宽或QoS", - "ZY": "本申请实施例提供了通信资源协作方法及电子设备。该方法包括:运行有低时延业务的电子设备可以让没有运行低时延业务的设备释放通信资源,例如切换信道、降低数据传输速率等。由于用户对低时延业务的交互卡顿更加敏感,使用本申请实施例提供的通信资源协作方法可以优先保障低时延业务的通信资源,进而降低低时延业务的卡顿,提升用户对低时延业务的体验。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H03L7/099申请日:20190330", - "flztggrq": "2021-11-02" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2021-10-15" - } - ], - "JRGJRQ": "", - "ZFLH": "H03L7/099", - "FLH": "H03L7/099(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "熊永强;李稷芳", - "GKGGH": "CN113508529B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电子电路——电子振荡器或脉冲发生器的自动控制、起振、同步或稳定——主要涉及环路的控制振荡器的", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "e786ce6f3d4527ccb5492bcfed1db0f7", - "FMR": "毛懿鸿;高鹏", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/3d/3d03e9dd37710b9b9c090de260f0a362.jpg!xinshuwater", - "SQH": "CN201980093427.1", - "PATNAME": "多通道多载波收发机", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2019-03-30", - "IPC": "电学——电子电路——电子振荡器或脉冲发生器的自动控制、起振、同步或稳定——主要涉及环路的控制振荡器的", - "ZY": "一种多通道多载波收发机,包括:第一通道,用于传输第一载波;第二通道,用于传输第二载波;第一ADPLL,耦合至第一通道,用于为第一通道提供本振信号;第二ADPLL,耦合至第二通道,用于为第二通道提供本振信号;以及配置电路,分别耦合至第一ADPLL和第二ADPLL,用于根据第一载波和第二载波的组合信息,对第一ADPLL或者第二ADPLL进行配置。通过根据多个载波的组合信息,对锁相环进行配置,使得锁相环可以消除多个载波的频率牵引导致的杂散,提高了多通道多载波通信的可靠性;多个锁相环在版图上可以并排平行放置,不受限于锁相环的环路带宽,并且可以节约面积开销,而自身引入的面积代价很小。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W72/04申请日:20200727", - "flztggrq": "2023-07-25" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-02-01" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W72/20", - "FLH": "H04W72/20(2023.01)I;H04W72/0453(2023.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "李杭", - "GKGGH": "CN114007263B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "深圳市深佳知识产权代理事务所(普通合伙)", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——用于资源管理的控制信道或信令", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "b2e52e4596fcc0a3cfb6bcb767961915", - "FMR": "戴喜增;刘江华;余政;常俊仁", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/ac/ac48692f57d5e215b06d950fd887f0df.jpg!xinshuwater", - "SQH": "CN202010734803.5", - "PATNAME": "一种信息传输方法和通信装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-07-27", - "IPC": "电学——电通信技术——无线通信网络——用于资源管理的控制信道或信令", - "ZY": "本申请实施例公开了一种信息传输方法和通信装置,用于实现对频谱资源的灵活使用。本申请实施例提供一种信息传输方法,包括:接收来自第二设备的第一配置信息,所述第一配置信息用于指示所述第二设备为第一设备配置T个上行载波,所述T的取值集合中至少有一个元素是大于L的正整数,所述L为所述第一设备能够同时进行上行传输的最大上行载波个数;从所述T个上行载波中确定N个上行载波,所述N为小于或等于所述L的正整数;在所述N个上行载波中发送信息。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W72/04申请日:20190930", - "flztggrq": "2022-05-20" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-04-29" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W72/04", - "FLH": "H04W72/04(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "李稷芳", - "GKGGH": "CN114424653B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——无线资源分配", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "fb62d44a14f9da6be32a41e81fb1b050", - "FMR": "樊波", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/bc/bc7942e70ef8c8410808877284bcec8e.jpg!xinshuwater", - "SQH": "CN201980100608.2", - "PATNAME": "物理下行共享信道传输方法及通信装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2019-09-30", - "IPC": "电学——电通信技术——无线通信网络——无线资源分配", - "ZY": "本申请提供一种物理下行共享信道传输方法及通信装置,其中,物理下行共享信道传输方法包括:根据物理下行控制信道的时域资源信息,确定第i次物理下行共享信道传输对应的第一时域范围,所述物理下行控制信道用于调度所述物理下行共享信道,所述第i次物理下行共享信道传输为所述物理下行共享信道的N次重复传输中的一次传输,所述N为大于或者等于2的整数,所述i为大于或者等于1,且小于或者等于N的整数;根据所述第一时域范围,接收所述第i次物理下行共享信道传输对应的内容。实施本申请实施例,可以降低数据传输时延。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H02K11/00", - "FLH": "H02K11/00(2016.01)I;H02K41/02(2006.01)I;H02K33/00(2006.01)I;H02K7/14(2006.01)I;H02K3/50(2006.01)I;H02N2/00(2006.01)I;H02N2/04(2006.01)I;G02B7/09(2021.01)I;G02B7/18(2021.01)I;G02B27/64(2006.01)I;G03B5/00(2021.01)I;G03B13/36(2021.01)I;G03B30/00(2021.01)I;H01R13/02(2006.01)I;H04N23/54(2023.01)I;H04N23/55(2023.01)I;H04N23/57(2023.01)I", - "PTYPE": "实用新型", - "GKGGR": "2025-10-03", - "DLR": "张月婷", - "GKGGH": "CN223414734U", - "SQGKGGH": "", - "YXQ": "20241097897362024.07.19CN", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——发电、变电或配电——电机——电机结构上与电元件或屏蔽设备、监测或保护设备连接", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "ad8208b5ceee401e54d960dc2cd8151e", - "FMR": "李志龙;王德炼;郭利德;田薇;丁睿明;黄金豪", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/c4/c46c861aaf64f068bd99c74f747a67aa.jpg!xinshuwater", - "SQH": "CN202422536758.9", - "PATNAME": "电连接件、马达、摄像模组以及电子设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2024-10-18", - "IPC": "电学——发电、变电或配电——电机——电机结构上与电元件或屏蔽设备、监测或保护设备连接", - "ZY": "本申请提供一种电连接件、马达、摄像模组以及电子设备。马达包括载体、基座、电连接件和驱动单元。沿电连接件的长度方向上,电连接件包括第一端、第二端以及第一形变段,第一形变段固定连接第一端和第二端之间,第一端固定连接基座,第二端固定连接载体,且电连接载体上的电子器件。在第一方向上,第一形变段包括多个电连接层,电连接层固定连接第一端和第二端之间,多个电连接层间隔设置,第一形变段的至少部分位置处的相邻两个电连接层之间镂空设置,第一方向和电连接件的长度方向不同,当载体相对基座运动时,第一端在第一方向上相对第二端靠近或者远离。这样,电连接件的K值较小,电连接件对载体运动的阻碍程度较小。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H05K5/02", - "FLH": "H05K5/02(2006.01)I;H04M1/02(2006.01)I", - "PTYPE": "实用新型", - "GKGGR": "2025-10-03", - "DLR": "穆桂芳;臧建明", - "GKGGH": "CN223415099U", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京同立钧成知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——其他类目不包含的电技术——印刷电路;电设备的外壳或结构零部件;电气元件组件的制造——零部件", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "2fc9735f11aaf586380604597189e0af", - "FMR": "李铭;徐延翔;何奕松;李庆孟;徐逢", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/11/113acf5758ada076317ec6001108dcbf.jpg!xinshuwater", - "SQH": "CN202422501392.1", - "PATNAME": "一种膜片、壳体及电子设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2024-10-15", - "IPC": "电学——其他类目不包含的电技术——印刷电路;电设备的外壳或结构零部件;电气元件组件的制造——零部件", - "ZY": "本申请实施例提供一种膜片、壳体及电子设备,属于电子设备技术领域,膜片包括层叠的连接层、基材层和第一着色层,基材层、第一着色层分别具有第一变色区域和第二变色区域,用于在不同条件下呈现不同的外观色彩,使膜片可以随着环境条件的变化发生外观色彩的变化,赋予膜片色彩可变的特殊外观效果,提升膜片外观效果设计的灵活性,可呈现独特的外观,具有很好的互动性。并且通过位于不同层中的第一变色区域和第二变色区域可以产生立体空间观感,使膜片能够实现空间立体感的视觉效果,使膜片的外观色彩或形状图案等可以呈现近似三度空间中的形状图案等的观感,实现多层次、多维度的外观效果,满足用户对电子设备外观效果的高需求。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04N21/436申请日:20211026", - "flztggrq": "2023-05-16" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2023-04-28" - } - ], - "JRGJRQ": "", - "ZFLH": "H04N21/436", - "FLH": "H04N21/436(2011.01)I;H04N21/4402(2011.01)I;H04N21/41(2011.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "熊永强;李稷芳", - "GKGGH": "CN116033208B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——图像通信,如电视——连接本地分布网络,例如:和另一个STB通信或者在家庭内部通信", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "98564434ecfdae4bf37cd80a07bbc625", - "FMR": "关浔", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/67/67a59e9ab74324f507154c70db833dc0.jpg!xinshuwater", - "SQH": "CN202111247004.6", - "PATNAME": "一种非镜像投屏下多种设备协作方法及设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2021-10-26", - "IPC": "电学——电通信技术——图像通信,如电视——连接本地分布网络,例如:和另一个STB通信或者在家庭内部通信", - "ZY": "本申请实施例公开了一种非镜像投屏下多种设备协作方法及设备,其中方法包括:接收显示设备发送的第一指示信息,其中,所述第一指示信息用于指示所述显示设备存在屏幕旋转事件;响应所述第一指示信息,将虚拟显示标记为已经旋转;在所述虚拟显示标记为已经旋转的情况下,对第一应用的图层进行合成,得到第一待编码图像;对所述第一待编码图像进行编码,得到第一视频码流;向所述显示设备发送所述第一视频码流。本申请实施例可以解决投屏时屏幕旋转遇到的卡屏问题,提高屏幕旋转的流畅性,提升用户体验。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "G06F9/38", - "FLH": "G06F9/38(2006.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "申健", - "GKGGH": "CN120752615A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——计算;推算或计数——电数字数据处理——并行执行指令的,例如,流水线或超前锁定", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "272530904e356146d7c0e41286f199b4", - "FMR": "亨利·高;何塞·纳尔逊·阿马拉尔", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/79/79289dc8c4e6d3cb879617399dd81646.jpg!xinshuwater", - "SQH": "CN202380094784.6", - "PATNAME": "用于平衡计算资源的系统和方法", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2023-05-25", - "IPC": "物理——计算;推算或计数——电数字数据处理——并行执行指令的,例如,流水线或超前锁定", - "ZY": "描述了用于平衡计算资源的方法和系统。根据一个方面,可以提供一种用于平衡计算资源的方法。所述方法包括:生成标量代码的多个向量化循环。所述方法还可以包括:交织所述多个向量化循环中的每个向量化循环,以生成多个交织向量化循环。所述交织可以基于可用向量资源。所述方法还可以包括:将标量循环的一次或多次标量迭代插值到所述多个交织向量化循环中的每个交织向量化循环中,以生成多个交织和标量插值向量化循环。所述方法还可以包括:根据代价模型选择所述多个交织和标量插值向量化循环中的一个交织和标量插值向量化循环。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H03F1/32申请日:20200430", - "flztggrq": "2022-01-21" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2021-12-31" - } - ], - "JRGJRQ": "", - "ZFLH": "H03F1/32", - "FLH": "H03F1/32(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "胡艳娟", - "GKGGH": "CN113875151B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电子电路——放大器——为减少非线性失真对放大器的改进", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "5c31f3b474317841397d749777c170a4", - "FMR": "郭衍;李峰", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/01/01a9b8a353136386fedb1f4d6eab8f70.jpg!xinshuwater", - "SQH": "CN202080001601.8", - "PATNAME": "无线通信装置、系统及信号处理方法", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-04-30", - "IPC": "电学——电子电路——放大器——为减少非线性失真对放大器的改进", - "ZY": "本申请公开了无线通信装置、系统及信号处理方法,涉及通信技术领域,有助于使功率放大器在TDD场景中特性变得稳定,进而有助于提升DPD系统的线性化性能。该无线通信装置包括功率放大器和偏置电路。其中,功率放大器包括信号输入端口,信号输出端口,供电端口和偏置端口,功率放大器用于从供电端口接收电源信号,从偏置端口接收偏置信号,从信号输入端口接收射频信号,并将功率放大后的射频信号通过信号输出端口输出;偏置电路与偏置端口连接,并用于生成偏置信号,其中,偏置信号的时序特征与TDD场景中功率放大器的开关信号的时序特征同步,以补偿TDD场景中功率放大器的非线性变化。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H01L21/02", - "FLH": "H01L21/02(2006.01)I;C30B25/18(2006.01)I;C30B25/20(2006.01)I;C30B29/36(2006.01)I;C30B29/40(2006.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "张月婷", - "GKGGH": "CN120749001A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电气元件——H10类未涵盖的半导体器件——半导体器件或其部件的制造或处理", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "92b6187a6a84e07121646139f8e3ce97", - "FMR": "丁涛;王朋;段焕涛", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/2d/2d8fba591f6ec7b25515651c9b4eca03.jpg!xinshuwater", - "SQH": "CN202410387094.6", - "PATNAME": "半导体衬底、外延片和半导体器件", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2024-03-30", - "IPC": "电学——电气元件——H10类未涵盖的半导体器件——半导体器件或其部件的制造或处理", - "ZY": "本申请实施例提供一种半导体衬底、外延片和半导体器件,所述半导体衬底包括碳化硅基底层,设置于所述碳化硅基底层的碳面上的过渡层,以及设置于所述过渡层的远离所述碳化硅基底层一侧的应力调控层;所述过渡层包括二维材料层,且所述应力调控层包括3C‑SiC层;或者所述过渡层包括多晶3C‑SiC层,且所述应力调控层包括单晶3C‑SiC层。本申请实施例提供的半导体衬底通过在碳化硅基底层上设置过渡层和应力调控层,可以有效改善SiC衬底的面型,以一定程度解决SiC外延工艺过程中由于衬底bow值过大导致的外延层厚度分布不均匀的问题,从而提升外延片制造良率,降低制造成本,提升半导体器件性能。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "G06F16/3329", - "FLH": "G06F16/3329(2025.01)I;G06F16/334(2025.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "龙小翠", - "GKGGH": "CN120744032A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——计算;推算或计数——电数字数据处理——自然语言查询公式", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "24b45e982247eb06c3647ec2f32ec136", - "FMR": "侯津涛;徐建荣;吴登奔", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/80/804fe54207919772948e3514c2ef602d.jpg!xinshuwater", - "SQH": "CN202410384704.7", - "PATNAME": "一种数据检索方法和装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2024-03-29", - "IPC": "物理——计算;推算或计数——电数字数据处理——自然语言查询公式", - "ZY": "公开了一种数据检索方法和装置,涉及计算机领域。计算设备根据第一进入向量的部分邻居向量,预测得到距离查询向量最近的第一向量。计算设备获取另一部分邻居向量与查询向量之间的第五距离,以及根据第一向量和第五距离,确定第j+1次检索的进入向量。如此,计算设备获取第一向量后即可获取第一向量的邻居向量。以及在计算设备确定第j+1次检索的进入向量为第一向量的情况下,可以直接采用第一向量的邻居向量执行下一次检索。无需等待计算设备获取第j+1次检索的进入向量的邻居向量,缩短了等待时长,提高了检索效率。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W24/00", - "FLH": "H04W24/00(2006.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "赵凯莉", - "GKGGH": "CN120752947A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京同达信恒知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——监督,监控或测试装置", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "a41e54ea1008af95ecd6ab185e103e1c", - "FMR": "皇甫幼睿;王坚;李榕", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/a5/a5596cbcaa1f2b0fa98637c9c7ae1d8c.jpg!xinshuwater", - "SQH": "CN202380094554.X", - "PATNAME": "一种通信方法及装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2023-02-23", - "IPC": "电学——电通信技术——无线通信网络——监督,监控或测试装置", - "ZY": "一种通信方法及装置,用于提升预测多径的速度和准确性。该方法包括:接收来自网络设备的第一信号;确定第一信息,第一信息包括第一信号的接收参数,或者第一信息包括第一信号的发送参数和接收参数,第一信息用于确定第一信号对应的路径在预设地理范围内对应的第一网格区域,预设地理范围划分为多个网格区域;向网络设备发送第一信号的测量结果,测量结果包括第一信息。通过将通信系统中的数据发送给网络设备来训练多径预测模型,可以使得多径预测模型可以学习通信系统中多径规律,从而可以提升多径预测的准确性和预测速度,还可以在未知的通信场景中推理出多径特性,避免信道重复测量。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04L9/40申请日:20210826", - "flztggrq": "2023-03-21" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2023-03-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H04L9/40", - "FLH": "H04L9/40(2022.01)I;G06F21/45(2013.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "李杭", - "GKGGH": "CN115733630B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "深圳市深佳知识产权代理事务所(普通合伙)", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——数字信息的传输,例如电报通信——网络安全协议", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "69de90868d61a02322971c23e297b84e", - "FMR": "朱成康;康鑫;王贵林;王海光;李铁岩", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/fe/fe268e84c17c820fadf8d172e5d2ed33.jpg!xinshuwater", - "SQH": "CN202110988997.6", - "PATNAME": "一种身份认证方法及其相关设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2021-08-26", - "IPC": "电学——电通信技术——数字信息的传输,例如电报通信——网络安全协议", - "ZY": "本申请提供一种身份认证方法及其相关设备,可令用户的自主身份和业务身份被隔离开来,分别存放在两个终端中,可有效保护目标用户的自主身份不与业务侧直接接触,即使存放业务身份的终端丢失,自主身份也不会被冒用,可避免潜在的安全问题。本申请的方法包括:第一终端获取来自第一服务器的第一信息,第一信息用于描述目标用户的第一身份;第一终端基于第一信息生成第二信息,第二信息用于描述目标用户的第二身份;第一终端向第二终端发送第二信息,第二信息用于第二终端在第二服务器处实现身份认证。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "14-03", - "FLH": "14-03(14)", - "PTYPE": "外观设计", - "GKGGR": "2025-10-03", - "DLR": "李稷芳", - "GKGGH": "CN309530087S", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "录音、通讯或信息再现设备-通讯设备和无线遥控器、无线电放大器", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "606dfbd1b0a467234911088e0ffa90a4", - "FMR": "徐汀;黄璐;王佳熙;许浩森", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/b7/b71923f3cdabdf8b6aac0d0eceb08c75.jpg!xinshuwater", - "SQH": "CN202230465844.9", - "PATNAME": "手机的摄像头主体", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2022-07-21", - "IPC": "录音、通讯或信息再现设备-通讯设备和无线遥控器、无线电放大器", - "ZY": "1.本外观设计产品的名称:手机的摄像头主体。2.本外观设计产品的用途:整体用途为用于提供网络通信、语音通话、事务处理(数据存储、录音、图片浏览)等;局部用途为用于拍摄照片或视频。3.本外观设计产品的设计要点:在于要求保护的局部形状。4.最能表明设计要点的图片或照片:设计1立体图1。5.指定设计1为基本设计。6.其他需要说明的情形:图中虚线表示不要求保护的部分。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H04M1/02", - "FLH": "H04M1/02(2006.01)I;G06F1/16(2006.01)I;G09F9/33(2006.01)I;G09F9/30(2006.01)I", - "PTYPE": "实用新型", - "GKGGR": "2025-10-03", - "DLR": "陈聪", - "GKGGH": "CN223414893U", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——电话通信——电话机的结构特点", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "26f8150fc6e8df194e6787cc839aa374", - "FMR": "陈博;王维;王海洋;王凯强;马春军", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/78/78c3fc5a596876c035a599f8650561f0.jpg!xinshuwater", - "SQH": "CN202422018178.0", - "PATNAME": "折叠电子设备、显示模组和支撑结构", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2024-08-19", - "IPC": "电学——电通信技术——电话通信——电话机的结构特点", - "ZY": "本申请实施例提供了一种折叠电子设备、显示模组和支撑结构,折叠电子设备包括转轴和显示模组,转轴设置在显示模组的下方,显示模组包括弯折区和非弯折区,弯折区与转轴固定;转轴用于产生机构运动,使显示模组展开或者折叠,转轴包括门板;显示模组包括显示功能层与支撑层,显示功能层与支撑层层叠,所述支撑层包括连接部分,连接部分位于弯折区,连接部分与门板固定,连接部分设有多个孔。本申请实施例能够降低显示模组的弯折区与转轴之间的相互作用力,有利于提升弯折区的可靠性,改善弯折区的功能失效。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "G02B6/44", - "FLH": "G02B6/44(2006.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "赵丹", - "GKGGH": "CN120742505A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——光学在本大类中,下列的词语以指定的含义使用:或红外辐射。——光学元件、系统或仪器——用于为光导纤维提供抗拉强度和外部保护的机械结构,例如,光学传输电缆", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "43c38e173cdf9b24fea6700cc8f650bc", - "FMR": "何伯勇;李唯搏;熊伟", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/28/28042ba35aad80132aca36dfc699b948.jpg!xinshuwater", - "SQH": "CN202510964069.4", - "PATNAME": "一种光缆分纤装置", - "SQR": "华为技术有限公司", - "FASQ": "20201013111262020.02.28", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-02-28", - "IPC": "物理——光学在本大类中,下列的词语以指定的含义使用:或红外辐射。——光学元件、系统或仪器——用于为光导纤维提供抗拉强度和外部保护的机械结构,例如,光学传输电缆", - "ZY": "本申请实施例提供一种光缆分纤装置,涉及光通信设备技术领域,光缆分纤装置包括:分纤箱和收放缆结构以及连接结构,收放缆结构用于收放与分纤箱连接的光缆;收放缆结构包括:具有盘储腔的底座,盘储腔与底座的一个表面相贯通以形成盘储腔开口,底座上开设有与盘储腔相连通的出缆口;配置为盘存光缆的转盘,转盘安装在盘储腔内,分纤箱设置在盘储腔开口一侧,转盘与分纤箱相对固定,转盘和分纤箱能够相对底座同步转动;连接结构包括:第一连接件,设置在分纤箱上;第二连接件,设置在底座上且与第一连接件可拆卸连接。本申请实施提供的光缆分纤装置主要目的是解决现有技术中光缆分纤装置收放缆效率低下、收揽效果差、组装效率低下的技术问题。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):G06F11/30申请日:20200917", - "flztggrq": "2022-04-05" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-03-18" - } - ], - "JRGJRQ": "", - "ZFLH": "G06F11/30", - "FLH": "G06F11/30(2006.01)I;G06F11/34(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "申健", - "GKGGH": "CN114201354B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——计算;推算或计数——电数字数据处理——监控", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "0923bfabf0078e950db4c98b2082bace", - "FMR": "辛洪强;阚彬;卢冬;蔺振超", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/b6/b6459a67ef6c4b575fb26cf715b96e7f.jpg!xinshuwater", - "SQH": "CN202010982738.8", - "PATNAME": "一种应用程序的超时检测方法及终端设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-09-17", - "IPC": "物理——计算;推算或计数——电数字数据处理——监控", - "ZY": "本申请实施例提供一种应用程序的超时检测方法及终端设备,涉及信息处理技术领域,可以检测应用程序APP启动过程的超时。具体方案包括:终端设备在启动第一应用程序APP中的第一Activity时,对第一Activity的启动过程进行计时;若在第一Activity启动成功前,第一Activity的启动过程的计时时长大于第一预设时长,则显示第一提示信息;其中,第一提示信息用于指示第一APP无响应,第一提示信息还包括第一选项和第二选项,第一选项用于触发终端设备继续等待第一APP响应,第二选项用于触发终端设备关闭第一APP。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W68/00申请日:20200228", - "flztggrq": "2023-03-21" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2021-08-31" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W68/00", - "FLH": "H04W68/00(2009.01)I;H04W72/0453(2023.01)I;H04W76/27(2018.01)I;H04W74/0833(2024.01)I;H04W72/0446(2023.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "邹雅莹", - "GKGGH": "CN113329493B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京同达信恒知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——用户通知,例如,用于通信到来的提醒或寻呼,或类似的业务改变", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "adee7463619b9518a5cd219621fd07fd", - "FMR": "酉春华;徐小英;曾清海", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/e4/e47cf6c4b51d1683e6d935a2cc858d89.jpg!xinshuwater", - "SQH": "CN202010129202.1", - "PATNAME": "一种通信方法及装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-02-28", - "IPC": "电学——电通信技术——无线通信网络——用户通知,例如,用于通信到来的提醒或寻呼,或类似的业务改变", - "ZY": "本申请涉及通信技术领域,公开了一种通信方法及装置。其中方法包括:第一网络设备从核心网设备接收终端设备的下行信息后,可以向第二网络设备发送第一消息,第一消息包括终端设备的SUL能力信息,SUL能力信息用于指示终端设备支持SUL载波,终端设备处于非激活态。采用该种方法,由于第一网络设备向第二网络设备发送终端设备的SUL能力信息,从而使得第二网络设备可以获知终端设备的SUL能力;进一步地,第二网络设备可以基于终端设备的SUL能力信息,指示终端设备在SUL载波上进行上行传输,从而为实现非激活态的终端设备在SUL载波上进行上行传输提供了可能。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H01F27/28申请日:20200120", - "flztggrq": "2022-07-26" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-07-08" - } - ], - "JRGJRQ": "", - "ZFLH": "H01F27/28", - "FLH": "H01F27/28(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "申健", - "GKGGH": "CN114730658B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电气元件——磁体;电感;变压器;磁性材料的选择〔2〕——线圈;绕组;导电连接", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "2ada5a304f196e4b3944709960f8ca27", - "FMR": "胡章荣;刘鑫;刘宁;刘铁军", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/a6/a6ab16cb30905a05a0acc8ba2cc489d6.jpg!xinshuwater", - "SQH": "CN202080081842.8", - "PATNAME": "一种电感、电压转换电路及电子设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-01-20", - "IPC": "电学——电气元件——磁体;电感;变压器;磁性材料的选择〔2〕——线圈;绕组;导电连接", - "ZY": "一种电感、电压转换电路及电子设备,涉及电子技术领域。该电感包括磁芯(X)以及绕制在磁芯(X)上的第一线圈(L01)和第二线圈(L02);第一线圈(L01)和第二线圈(L02)串联在电感的第一端和第二端之间,其中,第一线圈(L01)和第二线圈(L02)的一对异名端耦接。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04R3/00申请日:20200904", - "flztggrq": "2023-06-16" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-03-04" - } - ], - "JRGJRQ": "", - "ZFLH": "H04R3/00", - "FLH": "H04R3/00(2006.01)I;H04R23/00(2006.01)I;H04M1/03(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "熊永强;李稷芳", - "GKGGH": "CN114143664B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——扬声器、麦克风、留声机拾音器或类似的声机电换能器;助听器;公共广播系统——用于传感器的电路", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "eb89a97dc53734c82d010e00eae9f2e2", - "FMR": "侯小珂;张世雄;阮盛杰", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/9a/9a53f8e8604652dc43c1e4b035f12da3.jpg!xinshuwater", - "SQH": "CN202010924113.6", - "PATNAME": "激光麦克风和终端", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-09-04", - "IPC": "电学——电通信技术——扬声器、麦克风、留声机拾音器或类似的声机电换能器;助听器;公共广播系统——用于传感器的电路", - "ZY": "本申请实施例提供一种激光麦克风,包括振膜、激光器、控制电路、自混合信号获取装置、信号处理电路;激光器用于向振膜发射光,并接收来自振膜的反馈光信号,反馈光信号与激光器谐振腔内的激光相干涉得到自混合光信号;激光器与振膜之间的间距为30μm‑300μm;控制电路与激光器连接,用于驱动控制激光器发光;自混合信号获取装置与激光器连接,用于获取与自混合光信号相关的目标电压信号;信号处理电路与自混合信号获取装置连接,用于接收自混合信号获取装置输出的目标电压信号,并处理成音频电压信号。该激光麦克风具有高信噪比,可以提升语音识别率和唤醒率,改善远距离拾音效果。本申请实施例还提供包括该激光麦克风的终端。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):G06F9/4401申请日:20250714", - "flztggrq": "2025-08-29" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-08-12" - } - ], - "JRGJRQ": "", - "ZFLH": "G06F9/4401", - "FLH": "G06F9/4401(2018.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "张皎皎", - "GKGGH": "CN120469727B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——计算;推算或计数——电数字数据处理——引导程序", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "7aba35309e3d20131bbccfaa917954d6", - "FMR": "徐羿;陈东辉;孙海龙;蔡小萌;张楠", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/22/22157d837443e9265860ce33366109db.jpg!xinshuwater", - "SQH": "CN202510963923.5", - "PATNAME": "一种参数推荐方法及装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2025-07-14", - "IPC": "物理——计算;推算或计数——电数字数据处理——引导程序", - "ZY": "公开了一种参数推荐方法及装置,涉及计算机领域,以在各种应用场景中,实现推荐不同性能状态下的最优参数,以提升参数推荐的效果和效率。该方法包括:获取调优任务;采集并分析待调对象的性能数据,得到待调对象的性能报告;基于性能报告,对针对待调对象的参数知识库进行检索,确定参数集合。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "G06F16/3329", - "FLH": "G06F16/3329(2025.01)I;G06F16/334(2025.01)I;G06F21/62(2013.01)I;G06N5/04(2023.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "胡丽平", - "GKGGH": "CN120744033A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——计算;推算或计数——电数字数据处理——自然语言查询公式", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "08efa20ebc766cc5241d5f81c309f5b9", - "FMR": "宛烁;卢嘉勋;邵云峰;李秉帅;段广", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/27/27d97cdc86f2b86b28a898a23a02d7c9.jpg!xinshuwater", - "SQH": "CN202410389232.4", - "PATNAME": "一种基于大模型的答案获取方法及相关装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2024-03-30", - "IPC": "物理——计算;推算或计数——电数字数据处理——自然语言查询公式", - "ZY": "本申请实施例提供一种基于大模型的答案获取方法及相关装置,该方法包括:通过第一算法对第一信息中的敏感信息进行脱敏处理,得到第二信息,其中,第一信息包括第一问题和与第一问题相关的第一语料;第二信息包括第二问题和第二语料,第二问题包括第一问题脱敏处理后的问题,第二语料包括第一语料脱敏处理后的语料;向第一设备发送第二信息;接收第一设备发送的第一答案,第一答案为从第二语料中分析出的第二问题的答案;通过第一算法还原第一答案中的敏感信息,得到针对第一问题的第二答案。通过本申请实施例,能够在保障端侧的隐私安全的情况下,减轻端侧获取答案时的计算开销。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W36/00申请日:20210430", - "flztggrq": "2022-12-13" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-11-01" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W36/00", - "FLH": "H04W36/00(2009.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "张娜", - "GKGGH": "CN115278790B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——切换或重选装置", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "b5a7b0d38de588f5204a0b10eff6be3d", - "FMR": "辛婷玉;李秉肇;王燕;曹振臻", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/2d/2df42ed599fd14614e234207933bfec6.jpg!xinshuwater", - "SQH": "CN202110484672.4", - "PATNAME": "一种通信方法及通信装置", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2021-04-30", - "IPC": "电学——电通信技术——无线通信网络——切换或重选装置", - "ZY": "本申请提供了一种通信方法和通信装置,第二接入网设备从第一接入网设备接收第一消息,其中,所述第一消息指示MBS会话的第一状态为激活或去激活;第二接入网设备向所述第一接入网设备发送所述第一消息对应的第一确认消息。该方案中,第一消息用于指示MBS会话的第一状态,第二接入网设备可以根据该第一状态确定是否向核心网请求建立UP隧道。若该MBS会话的第一状态为激活,第二接入网设备可以触发新建UP隧道,保证切换过程中MBS的正常传输;若该MBS会话的第一状态为去激活,第二接入网设备可以不触发新建UP隧道或者请求新建挂起的UP隧道,避免资源浪费问题。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "专利申请权、专利权的转移", - "flztxq": "专利申请权的转移IPC(主分类):G01C21/24登记生效日:20241106变更事项:申请人变更前:华为技术有限公司变更后:深圳引望智能技术有限公司变更事项:国家或地区变更前:中国变更后:中国变更事项:地址变更前:518129 广东省深圳市龙岗区坂田华为总部办公楼变更后:518129 广东省深圳市龙岗区坂田街道万科城社区华为公司华为总部办公楼101", - "flztggrq": "2024-11-19" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):G01C21/24申请日:20200602", - "flztggrq": "2022-10-25" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-09-30" - } - ], - "JRGJRQ": "", - "ZFLH": "G01C21/24", - "FLH": "G01C21/24(2006.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "易浩球", - "GKGGH": "CN115135964B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "广州三环专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田街道万科城社区华为公司华为总部办公楼101", - "SQGKGGR": "", - "ZFLNAME": "物理——测量;测试——测量距离、水准或者方位;勘测;导航;陀螺仪;摄影测量学或视频测量学——专用于宇宙航行的导航", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "f87f575650590c3ec86f83a2835b04f0", - "FMR": "萨米·梅基;穆斯塔法·阿玛拉;原崧育;朱雨桐;魏之暄;彭学明", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/a7/a79b187fc9e8a981fc5e97dd1ea3f434.jpg!xinshuwater", - "SQH": "CN202080096955.5", - "PATNAME": "用于检测道路上的减速带和坑洞的装置、系统和方法", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-06-02", - "IPC": "物理——测量;测试——测量距离、水准或者方位;勘测;导航;陀螺仪;摄影测量学或视频测量学——专用于宇宙航行的导航", - "ZY": "公开了一种用于检测道路上的减速带或坑洞的感知装置(103)。所述感知装置(103)包括遥测感知系统(107),所述遥测感知系统(107)用于通过对路面的遥测感知来收集多个位置矢量,其中,每个位置矢量从公共原点延伸到所述路面上的相应点,并且每个位置矢量具有长度和方向。所述感知装置还包括处理电路(105),所述处理电路用于通过评估所述位置矢量的长度来检测道路平整度异常。因此,提供了一种用于检测道路上的减速带或坑洞等道路不规则结构的改进的装置(103)。所述感知装置(103)在弱光条件下(例如在夜间)以及在存在车辆混响时保持功能。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2025-10-03" - } - ], - "JRGJRQ": "", - "ZFLH": "G06F9/50", - "FLH": "G06F9/50(2006.01)I", - "PTYPE": "发明公告", - "GKGGR": "2025-10-03", - "DLR": "梁一博", - "GKGGH": "CN120743492A", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "深圳中一联合知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "物理——计算;推算或计数——电数字数据处理——资源分配,例如,中央处理单元[CPU]的", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "2c8508ea75a5776784c7da2b3127e5be", - "FMR": "寇振中;李晓晨;张军伟;邢天路;徐源;陈昊;刘思聪", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/a5/a547e3a1e6f41f8cdac5774b5d547319.jpg!xinshuwater", - "SQH": "CN202510646286.9", - "PATNAME": "回收内存的方法及电子设备", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2025-05-19", - "IPC": "物理——计算;推算或计数——电数字数据处理——资源分配,例如,中央处理单元[CPU]的", - "ZY": "本申请提供一种回收内存的方法及电子设备,涉及电子设备领域,其中,该方法包括:获取处理器的负载信息,负载信息指示处理器状态;根据处理器所处的空闲状态或繁忙状态,确定第一水线值和/或第二水线值,其中,第一水线值大于第二水线值,且当处理器处于空闲状态时的第一水线值大于当处理器处于繁忙状态时的第一水线值,当处理器处于空闲状态时的第二水线值大于当处理器处于繁忙状态时的第二水线值;当处理器处于空闲状态且空闲的物理内存小于第二水线值时执行异步回收内存操作,直至空闲的物理内存大于或等于第一水线值时停止执行异步回收内存操作。本申请提供的技术方案提高应用的启动和运行的流畅程度、电子设备的整体性能和用户体验。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W72/12申请日:20190108", - "flztggrq": "2022-08-19" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2022-08-02" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W72/12", - "FLH": "H04W72/12(2023.01)I;H04W72/1268(2023.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "申健", - "GKGGH": "CN114845412B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京中博世达专利商标代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——无线业务量调度", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "8ff6436c46f1ca3a6dde2a43ec1a30b5", - "FMR": "徐海博;肖潇;王君;王键", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/b4/b416c6e6ce670f958517592c5a28d701.jpg!xinshuwater", - "SQH": "CN202210119020.5", - "PATNAME": "触发调度请求的方法、设备及系统", - "SQR": "华为技术有限公司", - "FASQ": "20191001684292019.01.08", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2019-01-08", - "IPC": "电学——电通信技术——无线通信网络——无线业务量调度", - "ZY": "本申请实施例提供触发SR的方法、设备及系统,可以满足NRV2X系统中不同业务的QoS需求,同时避免或减少不必要的SR触发。方法包括:网络设备向终端设备发送第一消息,该第一消息包括第一标识以及与第一标识对应的一个或多个配置参数,其中,第一标识为第一逻辑信道的标识;第一逻辑信道为侧行链路无线承载的逻辑信道。或者,第一标识与第一逻辑信道存在映射关系,如第一标识为第一逻辑信道所属的逻辑信道组的标识。终端设备接收来自网络设备的第一消息之后,若该第一逻辑信道触发了第一常规侧行链路缓存状态报告SLBSR且还未被取消,终端设备根据该第一标识以及与该第一标识对应的一个或多个配置参数触发SR。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W28/24申请日:20210301", - "flztggrq": "2023-04-21" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2023-04-04" - } - ], - "JRGJRQ": "", - "ZFLH": "H04L41/0894", - "FLH": "H04L41/0894(2022.01)I;H04W48/18(2009.01)I;H04W28/24(2009.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "陈霁", - "GKGGH": "CN115918150B", - "SQGKGGH": "", - "YXQ": "PCT/CN2020/1398842020.12.28CN", - "GSDM": "", - "ZLDLJG": "北京亿腾知识产权代理事务所(普通合伙)", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——数字信息的传输,例如电报通信——基于策略的网络配置管理", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "13201f84918f2e1d1aa326d8e5175e9e", - "FMR": "曹龙雨;王耀光;于益俊", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/e2/e212b36e476da3c9ac4f6f0be8836df9.jpg!xinshuwater", - "SQH": "CN202180050992.7", - "PATNAME": "一种策略冲突管理方法、装置及系统", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2021-03-01", - "IPC": "电学——电通信技术——数字信息的传输,例如电报通信——基于策略的网络配置管理", - "ZY": "本申请涉及通信领域,特别涉及一种策略冲突管理方法、装置及系统。该方法用于第一控制器,包括:接收第一策略信息,所述第一策略信息包括核心网为UE配置的第一策略;检测到所述第一策略信息与第二策略信息之间发生策略冲突,所述第二策略信息包括第二控制器为所述UE配置的第二策略、或所述第一控制器基于所述第二策略进行策略决策后得到的第一决策结果;基于所述第一策略与所述第二策略进行策略决策,得到第二决策结果;将所述第二决策结果发送至无线接入网RAN网元,以使所述RAN网元执行所述第二决策结果。因此,本申请避免了有策略冲突的UE策略产生,保证了策略可执行性,提高了策略执行的成功率。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H04W28/02申请日:20191226", - "flztggrq": "2022-12-30" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2021-06-29" - } - ], - "JRGJRQ": "", - "ZFLH": "H04W28/02", - "FLH": "H04W28/02(2009.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "聂秀娜", - "GKGGH": "CN113055935B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "深圳市深佳知识产权代理事务所(普通合伙)", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——电通信技术——无线通信网络——业务量管理,例如流量控制或拥塞控制", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "5cedf9a0cd64203193220bcf32547e69", - "FMR": "黄一宏;周军", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/fe/fe7c61bd36a418919bb9aeb2d09c514e.jpg!xinshuwater", - "SQH": "CN201911368603.6", - "PATNAME": "一种拥塞控制方法、设备和系统", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2019-12-26", - "IPC": "电学——电通信技术——无线通信网络——业务量管理,例如流量控制或拥塞控制", - "ZY": "本申请实施例公开了一种拥塞控制方法、设备和系统,用于通信技术领域。本申请实施例方法包括:接入点AP接收发送设备发送的第一数据报文,所述第一数据报文携带有拥塞标记;所述AP根据所述第一数据报文的拥塞标记,为所述AP已缓存的第一确认ACK报文设置指示标识;所述AP向发送设备发送携带有所述指示标识的所述第一ACK报文,所述指示标识用于指示所述发送设备调整所述第一数据流的发送速率;本申请实施例可以降低网络拥塞信号在无线网线网络侧的传输时延,使得TCP发送设备及时接收到该拥塞信号并调整数据流发送速率,提高吞吐量。" - }, - { - "ZQX": "", - "patentflls": [ - { - "flzt": "授权", - "flztxq": "授权", - "flztggrq": "2025-10-03" - }, - { - "flzt": "实质审查的生效", - "flztxq": "实质审查的生效IPC(主分类):H01L27/15申请日:20200628", - "flztggrq": "2023-02-21" - }, - { - "flzt": "公布", - "flztxq": "公布", - "flztggrq": "2023-02-03" - } - ], - "JRGJRQ": "", - "ZFLH": "H10H20/83", - "FLH": "H10H20/83(2025.01)I;H10H29/30(2025.01)I", - "PTYPE": "发明授权", - "GKGGR": "2025-10-03", - "DLR": "李凤蓉;黄健", - "GKGGH": "CN115699320B", - "SQGKGGH": "", - "YXQ": "", - "GSDM": "", - "ZLDLJG": "北京同立钧成知识产权代理有限公司", - "DZ": "广东省深圳市龙岗区坂田华为总部办公楼", - "SQGKGGR": "", - "ZFLNAME": "电学——半导体器件;其他类目中不包括的电固体器件——具有势垒的无机发光半导体器件——电极", - "GJGB": "", - "GJSQ": "", - "FCFL": "", - "PID": "0c39244cfa028a6ac9c3d75b71980715", - "FMR": "小川刚", - "YZWX": "", - "PIC": "https://oss3.qiyedata.net/patent_img/202510/0b/0b20ba9ead42d3175cdeb6bc24015dc5.jpg!xinshuwater", - "SQH": "CN202080102028.X", - "PATNAME": "器件及其制造方法", - "SQR": "华为技术有限公司", - "FASQ": "", - "ENTNAME": "华为技术有限公司", - "SQRQ": "2020-06-28", - "IPC": "电学——半导体器件;其他类目中不包括的电固体器件——具有势垒的无机发光半导体器件——电极", - "ZY": "提供了一种器件(100),包括:衬底(10);所述衬底(10)上的第一电极(20);通过第一金属布线(28)与所述第一电极(20)电连接的发光元件(30);以及与所述发光元件(30)电连接的第二电极(42),其中所述第一电极(20)与所述发光元件(30)彼此横向分离,并且其中所述发光元件(30)面对所述衬底(10)的一侧连接到所述第一金属布线(28)。" - } - ] - }, - "rc": "0000", - "msg": "查询成功" -} diff --git a/demo_api.py b/demo_api.py deleted file mode 100644 index 9d2691b..0000000 --- a/demo_api.py +++ /dev/null @@ -1,384 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import requests -import json -import random -import time - -# API基础URL -BASE_URL = "http://127.0.0.1:9999/api/v1" - -# 测试数据 -test_phone = f"1380000{random.randint(1000, 9999)}" -test_password = test_phone[-6:] # 默认密码是手机号后6位 -access_token = None -user_id = None -valuation_id = None - -def test_register(): - """测试用户注册功能""" - print("\n===== 测试用户注册 =====") - url = f"{BASE_URL}/app-user/register" - data = { - "phone": test_phone - } - - response = requests.post(url, json=data) - print(f"请求URL: {url}") - print(f"请求数据: {data}") - print(f"响应状态码: {response.status_code}") - print(f"响应内容: {response.text}") - - assert response.status_code == 200, "注册请求失败" - result = response.json() - assert result["code"] == 200, "注册失败" - assert result["data"]["phone"] == test_phone, "返回的手机号不匹配" - assert result["data"]["default_password"] == test_phone[-6:], "默认密码不正确" - - print("✅ 用户注册测试通过") - return result - -def test_login(): - """测试用户登录功能""" - global access_token - - print("\n===== 测试用户登录 =====") - url = f"{BASE_URL}/app-user/login" - data = { - "phone": test_phone, - "password": test_password - } - - response = requests.post(url, json=data) - print(f"请求URL: {url}") - print(f"请求数据: {data}") - print(f"响应状态码: {response.status_code}") - print(f"响应内容: {response.text}") - - assert response.status_code == 200, "登录请求失败" - result = response.json() - assert "access_token" in result, "登录失败,未返回token" - - # 保存token供后续请求使用 - access_token = result["access_token"] - - print("✅ 用户登录测试通过") - return result - -def test_create_valuation(): - """测试创建估值评估申请""" - global access_token - - print("\n===== 测试创建估值评估申请 =====") - url = f"{BASE_URL}/app-valuations/" - - # 准备请求头,包含授权token - headers = { - "Authorization": f"Bearer {access_token}", - "Content-Type": "application/json" - } - - # 估值评估申请数据 - 根据估值字段.txt更新 - data = { - # 02 - 基础信息 - 非遗IP资产的基本信息 - "asset_name": f"蜀绣-{random.randint(1000, 9999)}", # 资产名称:必须是企业全名称 - "institution": "有数", # 所属机构:拥有或管理该非遗IP资产的机构名称 - "industry": "文化艺术", # 所属行业:非遗IP资产所属的行业分类 - - # 03 - 财务状况 - "rd_investment": "5", # :近12个月的研发费用(单位:万元) - "annual_revenue": "100", # 近12个月的营收额(单位:万元),用于计算创新投入比 - "three_year_income": [100, 120, 150], # 近三年的每年收益:资产近3年的年收益数据(单位:万元),用于计算年均收益和增长率 - "funding_status": "国家级资助", # 资金支持: 国家级资助(10分)、省级资助(7分)、无资助(0分) - - # 04 - 非遗等级与技术 - "inheritor_level": "国家级", # 传承人等级 - "inheritor_ages": [60, 42, 35], # 传承人年龄 - "inheritor_certificates": ["http://example.com/国家级非遗传承人证书.jpg"], # 传承人证书:传承人资质证明材料 - "heritage_asset_level": "国家级非遗", # 非遗资产等级 - "patent_remaining_years": "8", # [实际上就是专利号 通过API查询到的 ]专利剩余年限:资产相关专利的剩余保护期,8年对应7分 - "historical_evidence": { # 资产历史证据类型+数量:历史传承的证据材料 - "artifacts": 1, # 出土实物数量 - "ancient_literature": 2, # 古代文献数量 - "inheritor_testimony": 3, # 传承人佐证数量 - "modern_research": 1 # 现代研究数量 - }, - # 专利证书: - "patent_certificates": ["http://example.com/专利证书1.jpg", "http://example.com/专利证书2.jpg"], - "pattern_images": ["pattern1.jpg"], # 纹样图片:资产相关的纹样图片文件 - - # 04 - 非遗应用与推广 - "implementation_stage": "成熟应用", # 非遗资产应用成熟度 - "coverage_area": "区域覆盖", # 非遗资产应用覆盖范围 - "collaboration_type": "品牌联名", # 非遗资产跨界合作深度 - "offline_teaching_count": 12, # 近12个月线下相关演讲活动次数 - "platform_accounts": { # 线上相关宣传账号信息 - "bilibili": { - "followers_count": 8000, # 粉丝数量 - "likes": 1000, # 点赞数 - "comments": 500, # 评论数 - "shares": 500 # 转发数 - }, # B站账号 - "douyin": { - "followers_count": 8000, # 粉丝数量 - "likes": 1000, # 点赞数 - "comments": 500, # 评论数 - "shares": 500 # 转发数 - } # 抖音账号 - }, - - # 06 - 非遗资产衍生商品信息 - #该商品近12个月销售量 - "sales_volume": "1000", # 近12个月销售量:资产衍生商品的近12个月销售量(单位:件) 链接购买量 - # 该商品近12个月的链接浏览量 - "link_views": "10000", # 近12个月链接浏览量:资产衍生商品相关链接的近12个月浏览量(单位:次) 浏览量 - "scarcity_level": "流通", # 稀缺等级:资产的稀缺程度,流通(发行量>1000份)对应0.1分 - "market_activity_time": "近一月", # 市场活动的时间 - "monthly_transaction_amount": "<100万元", # 月交易额:资产衍生商品的月交易额水平,<100万元对应-0.1 - "price_range": { # 资产商品的价格波动率:近30天商品价格的波动情况 - "highest": 239, # 最高价(单位:元) - "lowest": 189 # 最低价(单位:元) - }, - "market_price": 0, # 直接提供的市场价格(单位:万元) 用户输入: 专家审核 或者 系统默认 专家审核 - - # 内置API 计算字段 - "infringement_record": "无侵权记录", # 侵权记录:资产的侵权历史情况,无侵权记录对应10分 - "patent_count": "1", # 专利使用量:资产相关的专利数量,每引用一项专利+2.5分 - "esg_value": "10", # ESG关联价值:根据行业匹配的ESG(环境、社会、治理)关联价值 - "policy_matching": "10", # 政策匹配度:根据行业自动匹配的政策匹配度分值 - "online_course_views": 2000, # 线上课程点击量:抖音/快手播放量按100:1折算为学习人次,B站课程按50:1折算 - "pattern_complexity": "1.459", # 结构复杂度:纹样的结构复杂度值 搞一个默认值: 0.0 - "normalized_entropy": "9.01", # 归一化信息熵:纹样的归一化信息熵值 搞一个默认值: 0.0 - "legal_risk": "无诉讼", # 法律风险-侵权诉讼历史:资产所属机构的诉讼历史,无诉讼对应10分 - # 动态质押率DPR:计算公式=基础质押率*(1+流量修正系数)+政策加成系数-流动性调节因子 - "base_pledge_rate": "50%", # 基础质押率:基础质押率固定值50% - "flow_correction": "0.3", # 流量修正系数:固定值0.3 - } - - response = requests.post(url, headers=headers, json=data) - print(f"请求URL: {url}") - print(f"请求头: {headers}") - print(f"请求数据: {json.dumps(data, ensure_ascii=False, indent=2)}") - print(f"响应状态码: {response.status_code}") - print(f"响应内容: {response.text}") - - assert response.status_code == 200, "创建估值评估申请请求失败" - result = response.json() - assert result["code"] == 200, "创建估值评估申请失败" - - print("✅ 创建估值评估申请测试通过") - return result - -def test_get_profile(): - """测试获取用户个人信息""" - global access_token, user_id - - print("\n===== 测试获取用户个人信息 =====") - url = f"{BASE_URL}/app-user/profile" - - headers = { - "Authorization": f"Bearer {access_token}" - } - - response = requests.get(url, headers=headers) - print(f"请求URL: {url}") - print(f"请求头: {headers}") - print(f"响应状态码: {response.status_code}") - print(f"响应内容: {response.text}") - - assert response.status_code == 200, "获取用户信息请求失败" - result = response.json() - user_id = result["id"] # 保存用户ID供后续使用 - - print("✅ 获取用户个人信息测试通过") - return result - -def test_change_password(): - """测试修改密码""" - global access_token - - print("\n===== 测试修改密码 =====") - url = f"{BASE_URL}/app-user/change-password" - - headers = { - "Authorization": f"Bearer {access_token}", - "Content-Type": "application/json" - } - - new_password = "new" + test_password - data = { - "old_password": test_password, - "new_password": new_password - } - - response = requests.post(url, headers=headers, json=data) - print(f"请求URL: {url}") - print(f"请求头: {headers}") - print(f"请求数据: {data}") - print(f"响应状态码: {response.status_code}") - print(f"响应内容: {response.text}") - - assert response.status_code == 200, "修改密码请求失败" - result = response.json() - assert result["code"] == 200, "修改密码失败" - - print("✅ 修改密码测试通过") - return result - -def test_update_profile(): - """测试更新用户信息""" - global access_token - - print("\n===== 测试更新用户信息 =====") - url = f"{BASE_URL}/app-user/profile" - - headers = { - "Authorization": f"Bearer {access_token}", - "Content-Type": "application/json" - } - - data = { - "nickname": f"测试用户{random.randint(100, 999)}", - "avatar": "https://example.com/avatar.jpg", - "gender": "male", - "email": f"test{random.randint(100, 999)}@example.com" - } - - response = requests.put(url, headers=headers, json=data) - print(f"请求URL: {url}") - print(f"请求头: {headers}") - print(f"请求数据: {data}") - print(f"响应状态码: {response.status_code}") - print(f"响应内容: {response.text}") - - assert response.status_code == 200, "更新用户信息请求失败" - result = response.json() - # 更新用户信息接口直接返回用户对象,不包含code字段 - assert "id" in result, "更新用户信息失败" - - print("✅ 更新用户信息测试通过") - return result - -def test_logout(): - """测试用户登出""" - global access_token - - print("\n===== 测试用户登出 =====") - url = f"{BASE_URL}/app-user/logout" - - headers = { - "Authorization": f"Bearer {access_token}" - } - - response = requests.post(url, headers=headers) - print(f"请求URL: {url}") - print(f"请求头: {headers}") - print(f"响应状态码: {response.status_code}") - print(f"响应内容: {response.text}") - - assert response.status_code == 200, "登出请求失败" - result = response.json() - assert result["code"] == 200, "登出失败" - - print("✅ 用户登出测试通过") - return result - -def test_get_valuation_list(): - """测试获取用户估值列表""" - global access_token - - print("\n===== 测试获取用户估值列表 =====") - url = f"{BASE_URL}/app-valuations/" - - headers = { - "Authorization": f"Bearer {access_token}" - } - - response = requests.get(url, headers=headers) - print(f"请求URL: {url}") - print(f"请求头: {headers}") - print(f"响应状态码: {response.status_code}") - print(f"响应内容: {response.text}") - - assert response.status_code == 200, "获取估值列表请求失败" - result = response.json() - assert result["code"] == 200, "获取估值列表失败" - - print("✅ 获取用户估值列表测试通过") - return result - -def test_get_valuation_detail(): - """测试获取估值详情""" - global access_token, valuation_id - - # 先获取估值列表,获取第一个估值ID - if not valuation_id: - list_result = test_get_valuation_list() - if list_result["data"] and len(list_result["data"]) > 0: - valuation_id = list_result["data"][0]["id"] - else: - print("⚠️ 没有可用的估值记录,跳过估值详情测试") - return None - - print("\n===== 测试获取估值详情 =====") - url = f"{BASE_URL}/app-valuations/{valuation_id}" - - headers = { - "Authorization": f"Bearer {access_token}" - } - - response = requests.get(url, headers=headers) - print(f"请求URL: {url}") - print(f"请求头: {headers}") - print(f"响应状态码: {response.status_code}") - print(f"响应内容: {response.text}") - - assert response.status_code == 200, "获取估值详情请求失败" - result = response.json() - assert result["code"] == 200, "获取估值详情失败" - - print("✅ 获取估值详情测试通过") - return result - -def run_tests(): - """运行所有测试""" - try: - # 测试注册 - test_register() - - # 等待一秒,确保数据已保存 - time.sleep(1) - - # 测试登录 - test_login() - - # 测试获取用户个人信息 - test_get_profile() - - # 测试更新用户信息 - test_update_profile() - - # 测试创建估值评估申请 - test_create_valuation() - - # 测试获取估值列表 - test_get_valuation_list() - - # 测试获取估值详情 - test_get_valuation_detail() - - # 测试修改密码 - test_change_password() - - # 测试登出 - # test_logout() - - print("\n===== 所有测试通过 =====") - except AssertionError as e: - print(f"\n❌ 测试失败: {e}") - except Exception as e: - print(f"\n❌ 发生错误: {e}") - -if __name__ == "__main__": - run_tests() \ No newline at end of file diff --git a/docs/sql/add_transaction_menu.sql b/docs/sql/add_transaction_menu.sql deleted file mode 100644 index 3c15312..0000000 --- a/docs/sql/add_transaction_menu.sql +++ /dev/null @@ -1,24 +0,0 @@ --- 新增交易管理菜单 --- 创建时间: 2025-11-13 - --- 插入一级目录:交易管理 -INSERT INTO menu (id, name, menu_type, icon, path, 'order', parent_id, is_hidden, component, keepalive, redirect, created_at, updated_at) -VALUES - (16, '交易管理', 'catalog', 'carbon:receipt', '/transaction', 3, 0, 0, 'Layout', 0, '/transaction/invoice', datetime('now'), datetime('now')); - --- 插入二级菜单:开票记录 -INSERT INTO menu (id, name, menu_type, icon, path, 'order', parent_id, is_hidden, component, keepalive, redirect, created_at, updated_at) -VALUES - (17, '开票记录', 'menu', 'carbon:document', 'invoice', 1, 16, 0, '/transaction/invoice', 0, NULL, datetime('now'), datetime('now')); - --- 为管理员角色分配菜单权限 -INSERT INTO role_menu (role_id, menu_id) -VALUES - (1, 16), - (1, 17); - --- 为普通用户角色分配菜单权限 -INSERT INTO role_menu (role_id, menu_id) -VALUES - (2, 16), - (2, 17); diff --git a/docs/sql/add_valuation_menu.sql b/docs/sql/add_valuation_menu.sql deleted file mode 100644 index d08691d..0000000 --- a/docs/sql/add_valuation_menu.sql +++ /dev/null @@ -1,24 +0,0 @@ --- 新增估值管理菜单 --- 创建时间: 2025-11-13 - --- 插入一级目录:估值管理 -INSERT INTO menu (id, name, menu_type, icon, path, 'order', parent_id, is_hidden, component, keepalive, redirect, created_at, updated_at) -VALUES - (18, '估值管理', 'catalog', 'carbon:calculator', '/valuation', 4, 0, 0, 'Layout', 0, '/valuation/audit', datetime('now'), datetime('now')); - --- 插入二级菜单:审核列表 -INSERT INTO menu (id, name, menu_type, icon, path, 'order', parent_id, is_hidden, component, keepalive, redirect, created_at, updated_at) -VALUES - (19, '审核列表', 'menu', 'carbon:task-approved', 'audit', 1, 18, 0, '/valuation/audit', 0, NULL, datetime('now'), datetime('now')); - --- 为管理员角色分配菜单权限 -INSERT INTO role_menu (role_id, menu_id) -VALUES - (1, 18), - (1, 19); - --- 为普通用户角色分配菜单权限 -INSERT INTO role_menu (role_id, menu_id) -VALUES - (2, 18), - (2, 19); diff --git a/docs/sql/complete_menu_setup.sql b/docs/sql/complete_menu_setup.sql deleted file mode 100644 index 1d08d3f..0000000 --- a/docs/sql/complete_menu_setup.sql +++ /dev/null @@ -1,86 +0,0 @@ --- 完整菜单初始化SQL --- 创建时间: 2025-11-17 --- 说明: 包含所有新增的菜单项和权限分配 - --- ======================================== --- 1. 工作台菜单 --- ======================================== -INSERT INTO menu (id, name, menu_type, icon, path, 'order', parent_id, is_hidden, component, keepalive, redirect, created_at, updated_at) -VALUES - (22, '工作台', 'menu', 'carbon:dashboard', '/workbench', 1, 0, 0, '/workbench', 1, NULL, datetime('now'), datetime('now')); - --- ======================================== --- 2. 交易管理菜单 --- ======================================== --- 插入一级目录:交易管理 -INSERT INTO menu (id, name, menu_type, icon, path, 'order', parent_id, is_hidden, component, keepalive, redirect, created_at, updated_at) -VALUES - (16, '交易管理', 'catalog', 'carbon:receipt', '/transaction', 3, 0, 0, 'Layout', 0, '/transaction/invoice', datetime('now'), datetime('now')); - --- 插入二级菜单:开票记录 -INSERT INTO menu (id, name, menu_type, icon, path, 'order', parent_id, is_hidden, component, keepalive, redirect, created_at, updated_at) -VALUES - (17, '开票记录', 'menu', 'carbon:document', 'invoice', 1, 16, 0, '/transaction/invoice', 0, NULL, datetime('now'), datetime('now')); - --- ======================================== --- 3. 估值管理菜单 --- ======================================== --- 插入一级目录:估值管理 -INSERT INTO menu (id, name, menu_type, icon, path, 'order', parent_id, is_hidden, component, keepalive, redirect, created_at, updated_at) -VALUES - (18, '估值管理', 'catalog', 'carbon:calculator', '/valuation', 4, 0, 0, 'Layout', 0, '/valuation/audit', datetime('now'), datetime('now')); - --- 插入二级菜单:审核列表 -INSERT INTO menu (id, name, menu_type, icon, path, 'order', parent_id, is_hidden, component, keepalive, redirect, created_at, updated_at) -VALUES - (19, '审核列表', 'menu', 'carbon:task-approved', 'audit', 1, 18, 0, '/valuation/audit', 0, NULL, datetime('now'), datetime('now')); - --- ======================================== --- 4. 用户管理菜单 --- ======================================== --- 插入一级目录:用户管理 -INSERT INTO menu (id, name, menu_type, icon, path, 'order', parent_id, is_hidden, component, keepalive, redirect, created_at, updated_at) -VALUES - (20, '用户管理', 'catalog', 'carbon:user-multiple', '/user-management', 5, 0, 0, 'Layout', 0, '/user-management/user-list', datetime('now'), datetime('now')); - --- 插入二级菜单:用户列表 -INSERT INTO menu (id, name, menu_type, icon, path, 'order', parent_id, is_hidden, component, keepalive, redirect, created_at, updated_at) -VALUES - (21, '用户列表', 'menu', 'carbon:user', 'user-list', 1, 20, 0, '/user-management/user-list', 0, NULL, datetime('now'), datetime('now')); - --- ======================================== --- 角色权限分配 --- ======================================== - --- 为管理员角色(role_id=1)分配所有菜单权限 -INSERT INTO role_menu (role_id, menu_id) -VALUES - (1, 22), -- 工作台 - (1, 16), -- 交易管理 - (1, 17), -- 开票记录 - (1, 18), -- 估值管理 - (1, 19), -- 审核列表 - (1, 20), -- 用户管理 - (1, 21); -- 用户列表 - --- 为普通用户角色(role_id=2)分配基础菜单权限 -INSERT INTO role_menu (role_id, menu_id) -VALUES - (2, 22), -- 工作台 - (2, 16), -- 交易管理 - (2, 17), -- 开票记录 - (2, 18), -- 估值管理 - (2, 19); -- 审核列表 - -- 注意:普通用户不分配用户管理权限 - --- ======================================== --- 验证SQL --- ======================================== --- 查询所有新增的菜单 --- SELECT * FROM menu WHERE id >= 16 ORDER BY parent_id, 'order'; - --- 查询管理员角色的菜单权限 --- SELECT m.name, m.path, m.menu_type FROM menu m --- JOIN role_menu rm ON m.id = rm.menu_id --- WHERE rm.role_id = 1 AND m.id >= 16 --- ORDER BY m.parent_id, m.'order'; diff --git a/node_modules/.vite/deps_temp_e96670e1/package.json b/node_modules/.vite/deps_temp_e96670e1/package.json deleted file mode 100644 index 3dbc1ca..0000000 --- a/node_modules/.vite/deps_temp_e96670e1/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "type": "module" -} diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index c8e06bc..0000000 --- a/package-lock.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "guzhi", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "echarts": "^6.0.0" - } - }, - "node_modules/echarts": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/echarts/-/echarts-6.0.0.tgz", - "integrity": "sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==", - "dependencies": { - "tslib": "2.3.0", - "zrender": "6.0.0" - } - }, - "node_modules/tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - }, - "node_modules/zrender": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/zrender/-/zrender-6.0.0.tgz", - "integrity": "sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==", - "dependencies": { - "tslib": "2.3.0" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index aabad93..0000000 --- a/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dependencies": { - "echarts": "^6.0.0" - } -} diff --git a/pyproject.toml b/pyproject.toml index 301fdbf..35d1843 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,6 +69,11 @@ dependencies = [ "websockets==14.1", "pyproject-toml>=0.1.0", "uvloop==0.21.0 ; sys_platform != 'win32'", + "alibabacloud_dysmsapi20170525==4.1.2", + "alibabacloud_tea_openapi==0.4.1", + "alibabacloud_tea_util==0.3.14", + "pytest==8.3.3", + "pytest-html==4.1.1", ] [tool.black] diff --git a/requirements.txt b/requirements.txt index 3341e96..5957f71 100644 --- a/requirements.txt +++ b/requirements.txt @@ -64,3 +64,7 @@ uvicorn==0.34.0 uvloop==0.21.0 watchfiles==1.0.4 websockets==14.1 +alibabacloud_dysmsapi20170525==4.1.2 +alibabacloud_tea_openapi==0.4.1 +alibabacloud_tea_util==0.3.14 +pytest==8.3.3 diff --git a/run.py b/run.py index 37be141..4364ae4 100644 --- a/run.py +++ b/run.py @@ -10,5 +10,5 @@ if __name__ == "__main__": ] = '%(asctime)s - %(levelname)s - %(client_addr)s - "%(request_line)s" %(status_code)s' LOGGING_CONFIG["formatters"]["access"]["datefmt"] = "%Y-%m-%d %H:%M:%S" - uvicorn.run("app:app", host="0.0.0.0", port=9999, reload=True, log_config=LOGGING_CONFIG) + uvicorn.run("app:app", host="0.0.0.0", port=9991, reload=True, log_config=LOGGING_CONFIG) diff --git a/scripts/admin_flow_test.py b/scripts/admin_flow_test.py new file mode 100644 index 0000000..969d7d9 --- /dev/null +++ b/scripts/admin_flow_test.py @@ -0,0 +1,213 @@ +import os +import json +import time +import uuid +import random +from typing import Dict, Any, List, Optional, Tuple + +import httpx + + +def make_url(base_url: str, path: str) -> str: + if base_url.endswith("/"): + base_url = base_url[:-1] + return f"{base_url}{path}" + + +def now_ms() -> int: + return int(time.time() * 1000) + + +def ensure_dict(obj: Any) -> Dict[str, Any]: + if isinstance(obj, dict): + return obj + return {"raw": str(obj)} + + +async def api_get(client: httpx.AsyncClient, url: str, headers: Optional[Dict[str, str]] = None, params: Optional[Dict[str, Any]] = None) -> Tuple[int, Dict[str, Any]]: + r = await client.get(url, headers=headers or {}, params=params or {}) + try: + parsed = r.json() + except Exception: + parsed = {"raw": r.text} + return r.status_code, ensure_dict(parsed) + + +async def api_post_json(client: httpx.AsyncClient, url: str, payload: Dict[str, Any], headers: Optional[Dict[str, str]] = None) -> Tuple[int, Dict[str, Any]]: + r = await client.post(url, json=payload, headers=headers or {}) + try: + parsed = r.json() + except Exception: + parsed = {"raw": r.text} + return r.status_code, ensure_dict(parsed) + + +async def api_put_json(client: httpx.AsyncClient, url: str, payload: Dict[str, Any], headers: Optional[Dict[str, str]] = None) -> Tuple[int, Dict[str, Any]]: + r = await client.put(url, json=payload, headers=headers or {}) + try: + parsed = r.json() + except Exception: + parsed = {"raw": r.text} + return r.status_code, ensure_dict(parsed) + + +async def api_delete(client: httpx.AsyncClient, url: str, headers: Optional[Dict[str, str]] = None, params: Optional[Dict[str, Any]] = None) -> Tuple[int, Dict[str, Any]]: + r = await client.delete(url, headers=headers or {}, params=params or {}) + try: + parsed = r.json() + except Exception: + parsed = {"raw": r.text} + return r.status_code, ensure_dict(parsed) + + +def write_html_report(filepath: str, title: str, results: List[Dict[str, Any]]) -> None: + rows = [] + for r in results: + color = {"PASS": "#4caf50", "FAIL": "#f44336"}.get(r.get("status"), "#9e9e9e") + rows.append( + f"{r.get('name')}{r.get('status')}{r.get('message','')}
{json.dumps(r.get('detail', {}), ensure_ascii=False, indent=2)}
" + ) + html = f""" + +{title} + + +

{title}

+

生成时间: {time.strftime('%Y-%m-%d %H:%M:%S')}

+ +{''.join(rows)} +
用例结果说明详情
+ +""" + os.makedirs(os.path.dirname(filepath), exist_ok=True) + with open(filepath, "w", encoding="utf-8") as f: + f.write(html) + + +async def test_base(client: httpx.AsyncClient, base: str, token: str, results: List[Dict[str, Any]]): + for path, name in [("/base/userinfo", "admin用户信息"), ("/base/userapi", "admin接口权限"), ("/base/usermenu", "admin菜单")]: + code, data = await api_get(client, make_url(base, path), headers={"token": token}) + ok = (code == 200) + results.append({"name": name, "status": "PASS" if ok else "FAIL", "message": "获取成功" if ok else "获取失败", "detail": {"http": code, "body": data}}) + + +async def test_users_crud(client: httpx.AsyncClient, base: str, token: str, results: List[Dict[str, Any]]): + email = f"admin_{uuid.uuid4().hex[:6]}@test.com" + username = "adm_" + uuid.uuid4().hex[:6] + code, data = await api_post_json(client, make_url(base, "/user/create"), {"email": email, "username": username, "password": "123456", "is_active": True, "is_superuser": False, "role_ids": [], "dept_id": 0}, headers={"token": token}) + results.append({"name": "创建用户", "status": "PASS" if code == 200 and data.get("code") == 200 else "FAIL", "message": data.get("msg"), "detail": {"http": code, "body": data}}) + code, data = await api_get(client, make_url(base, "/user/list"), headers={"token": token}, params={"page": 1, "page_size": 10, "email": email}) + ok = (code == 200 and isinstance(data.get("data"), list)) + uid = None + if ok and data["data"]: + uid = data["data"][0].get("id") + results.append({"name": "查询用户", "status": "PASS" if ok else "FAIL", "message": "获取成功" if ok else "获取失败", "detail": {"http": code, "body": data}}) + if uid: + code, data = await api_post_json(client, make_url(base, "/user/update"), {"id": uid, "email": email, "username": username + "_u", "is_active": True, "is_superuser": False, "role_ids": [], "dept_id": 0}, headers={"token": token}) + results.append({"name": "更新用户", "status": "PASS" if code == 200 and data.get("code") == 200 else "FAIL", "message": data.get("msg"), "detail": {"http": code, "body": data}}) + code, data = await api_delete(client, make_url(base, "/user/delete"), headers={"token": token}, params={"user_id": uid}) + results.append({"name": "删除用户", "status": "PASS" if code == 200 and data.get("code") == 200 else "FAIL", "message": data.get("msg"), "detail": {"http": code, "body": data}}) + + +async def test_roles_menus_apis(client: httpx.AsyncClient, base: str, token: str, results: List[Dict[str, Any]]): + rname = "role_" + uuid.uuid4().hex[:6] + code, data = await api_post_json(client, make_url(base, "/role/create"), {"name": rname, "desc": "测试角色"}, headers={"token": token}) + results.append({"name": "创建角色", "status": "PASS" if code == 200 and data.get("code") == 200 else "FAIL", "message": data.get("msg"), "detail": {"http": code, "body": data}}) + code, data = await api_get(client, make_url(base, "/role/list"), headers={"token": token}, params={"page": 1, "page_size": 10, "role_name": rname}) + ok = (code == 200 and isinstance(data.get("data"), list)) + rid = None + if ok and data["data"]: + rid = data["data"][0].get("id") + results.append({"name": "查询角色", "status": "PASS" if ok else "FAIL", "message": "获取成功" if ok else "获取失败", "detail": {"http": code, "body": data}}) + code, data = await api_post_json(client, make_url(base, "/api/refresh"), {}, headers={"token": token}) + results.append({"name": "刷新API权限表", "status": "PASS" if code == 200 and data.get("code") == 200 else "FAIL", "message": data.get("msg"), "detail": {"http": code, "body": data}}) + code, data = await api_get(client, make_url(base, "/api/list"), headers={"token": token}, params={"page": 1, "page_size": 10}) + ok_apis = (code == 200 and isinstance(data.get("data"), list)) + results.append({"name": "API列表", "status": "PASS" if ok_apis else "FAIL", "message": "获取成功" if ok_apis else "获取失败", "detail": {"http": code, "body": data}}) + if rid and ok_apis: + api_infos = [] + if data["data"]: + first = data["data"][0] + api_infos = [{"path": first.get("path"), "method": first.get("method")}] if first.get("path") and first.get("method") else [] + code, data = await api_post_json(client, make_url(base, "/role/authorized"), {"id": rid, "menu_ids": [], "api_infos": api_infos}, headers={"token": token}) + results.append({"name": "角色授权", "status": "PASS" if code == 200 and data.get("code") == 200 else "FAIL", "message": data.get("msg"), "detail": {"http": code, "body": data}}) + code, data = await api_delete(client, make_url(base, "/role/delete"), headers={"token": token}, params={"role_id": rid}) + results.append({"name": "删除角色", "status": "PASS" if code == 200 and data.get("code") == 200 else "FAIL", "message": data.get("msg"), "detail": {"http": code, "body": data}}) + + +async def test_dept_crud(client: httpx.AsyncClient, base: str, token: str, results: List[Dict[str, Any]]): + dname = "dept_" + uuid.uuid4().hex[:6] + code, data = await api_post_json(client, make_url(base, "/dept/create"), {"name": dname, "desc": "测试部门"}, headers={"token": token}) + results.append({"name": "创建部门", "status": "PASS" if code == 200 and data.get("code") == 200 else "FAIL", "message": data.get("msg"), "detail": {"http": code, "body": data}}) + code, data = await api_get(client, make_url(base, "/dept/list"), headers={"token": token}, params={"page": 1, "page_size": 10}) + ok = (code == 200 and isinstance(data.get("data"), list)) + results.append({"name": "查询部门", "status": "PASS" if ok else "FAIL", "message": "获取成功" if ok else "获取失败", "detail": {"http": code, "body": data}}) + + +async def test_valuations_admin(client: httpx.AsyncClient, base: str, token: str, results: List[Dict[str, Any]]): + payload = {"asset_name": "Admin资产", "institution": "Admin机构", "industry": "行业", "three_year_income": [10, 20, 30]} + code, data = await api_post_json(client, make_url(base, "/valuations/"), payload, headers={"token": token}) + results.append({"name": "创建估值(管理员)", "status": "PASS" if code == 200 and data.get("code") == 200 else "FAIL", "message": data.get("msg"), "detail": {"http": code, "body": data}}) + code, data = await api_get(client, make_url(base, "/valuations/"), headers={"token": token}, params={"page": 1, "size": 5}) + ok = (code == 200 and isinstance(data.get("data"), list)) + results.append({"name": "估值列表(管理员)", "status": "PASS" if ok else "FAIL", "message": "获取成功" if ok else "获取失败", "detail": {"http": code, "body": data}}) + + +async def test_invoice_transactions(client: httpx.AsyncClient, base: str, token: str, results: List[Dict[str, Any]]): + code, data = await api_get(client, make_url(base, "/invoice/list"), headers={"token": token}, params={"page": 1, "page_size": 10}) + ok = (code == 200 and isinstance(data.get("data"), list)) + results.append({"name": "发票列表", "status": "PASS" if ok else "FAIL", "message": "获取成功" if ok else "获取失败", "detail": {"http": code, "body": data}}) + code, data = await api_get(client, make_url(base, "/transactions/receipts"), headers={"token": token}, params={"page": 1, "page_size": 10}) + ok = (code == 200 and isinstance(data.get("data"), list)) + results.append({"name": "对公转账列表", "status": "PASS" if ok else "FAIL", "message": "获取成功" if ok else "获取失败", "detail": {"http": code, "body": data}}) + + +async def perf_benchmark(client: httpx.AsyncClient, base: str, token: str, results: List[Dict[str, Any]]): + endpoints = ["/user/list", "/valuations/", "/invoice/list"] + conc = 20 + metrics = [] + for ep in endpoints: + start = now_ms() + tasks = [api_get(client, make_url(base, ep), headers={"token": token}, params={"page": 1, "page_size": 10}) for _ in range(conc)] + rets = await httpx.AsyncClient.gather(*tasks) if hasattr(httpx.AsyncClient, "gather") else None + # 兼容:无 gather 则顺序执行 + if rets is None: + rets = [] + for _ in range(conc): + rets.append(await api_get(client, make_url(base, ep), headers={"token": token}, params={"page": 1, "page_size": 10})) + dur = now_ms() - start + ok = sum(1 for (code, _) in rets if code == 200) + metrics.append({"endpoint": ep, "concurrency": conc, "duration_ms": dur, "success": ok, "total": conc}) + results.append({"name": "性能基准", "status": "PASS", "message": "并发测试完成", "detail": {"metrics": metrics}}) + + +async def main() -> None: + base = os.getenv("ADMIN_BASE_URL", "http://localhost:9999/api/v1") + token = os.getenv("ADMIN_TOKEN", "dev") + results: List[Dict[str, Any]] = [] + endpoint_list = [ + {"path": "/base/userinfo", "desc": "管理员信息"}, + {"path": "/user/*", "desc": "用户管理"}, + {"path": "/role/*", "desc": "角色管理与授权"}, + {"path": "/api/*", "desc": "API权限管理与刷新"}, + {"path": "/dept/*", "desc": "部门管理"}, + {"path": "/valuations/*", "desc": "估值评估管理"}, + {"path": "/invoice/*", "desc": "发票与抬头"}, + {"path": "/transactions/*", "desc": "对公转账记录"}, + ] + async with httpx.AsyncClient(timeout=10) as client: + await test_base(client, base, token, results) + await test_users_crud(client, base, token, results) + await test_roles_menus_apis(client, base, token, results) + await test_dept_crud(client, base, token, results) + await test_valuations_admin(client, base, token, results) + await test_invoice_transactions(client, base, token, results) + await perf_benchmark(client, base, token, results) + passes = sum(1 for r in results if r.get("status") == "PASS") + print(json.dumps({"total": len(results), "passes": passes, "results": results, "endpoints": endpoint_list}, ensure_ascii=False, indent=2)) + write_html_report("reports/admin_flow_script_report.html", "后台管理员维度接口全流程测试报告", results) + + +if __name__ == "__main__": + import asyncio + asyncio.run(main()) \ No newline at end of file diff --git a/scripts/api_smoke_test.py b/scripts/api_smoke_test.py new file mode 100644 index 0000000..cabc7fa --- /dev/null +++ b/scripts/api_smoke_test.py @@ -0,0 +1,270 @@ +import argparse +import json +import time +from typing import Dict, Any, Optional + +import requests + + +def _print(title: str, payload: Any) -> None: + print(f"\n[{title}]\n{json.dumps(payload, ensure_ascii=False, indent=2)}") + + +def _url(base: str, path: str) -> str: + return f"{base}{path}" + + +class AppClient: + """ + 用户端客户端,会话维持与常用接口封装 + + 参数: + base: API 基础地址,如 http://127.0.0.1:9991/api/v1 + + 属性: + session: requests.Session 会话对象,携带 token + """ + + def __init__(self, base: str) -> None: + self.base = base.rstrip("/") + self.session = requests.Session() + + def set_token(self, token: str) -> None: + """ + 设置用户端 token 到请求头 + + 参数: + token: 登录接口返回的 access_token + 返回: + None + """ + self.session.headers.update({"token": token}) + + def register(self, phone: str) -> Dict[str, Any]: + """ + 用户注册 + + 参数: + phone: 手机号 + 返回: + 注册响应 dict + """ + resp = self.session.post(_url(self.base, "/app-user/register"), json={"phone": phone}) + return _safe_json(resp) + + def login(self, phone: str, password: str) -> Optional[str]: + """ + 用户登录 + + 参数: + phone: 手机号 + password: 密码 + 返回: + access_token 或 None + """ + resp = self.session.post(_url(self.base, "/app-user/login"), json={"phone": phone, "password": password}) + data = _safe_json(resp) + token = data.get("access_token") if isinstance(data, dict) else None + if token: + self.set_token(token) + return token + + def profile(self) -> Dict[str, Any]: + resp = self.session.get(_url(self.base, "/app-user/profile")) + return _safe_json(resp) + + def dashboard(self) -> Dict[str, Any]: + resp = self.session.get(_url(self.base, "/app-user/dashboard")) + return _safe_json(resp) + + def quota(self) -> Dict[str, Any]: + resp = self.session.get(_url(self.base, "/app-user/quota")) + return _safe_json(resp) + + def submit_valuation(self, payload: Dict[str, Any]) -> Dict[str, Any]: + """ + 提交估值评估 + + 参数: + payload: 估值评估输入数据 + 返回: + 提交响应 dict + """ + resp = self.session.post(_url(self.base, "/app-valuations/"), json=payload) + return _safe_json(resp) + + def list_valuations(self) -> Dict[str, Any]: + resp = self.session.get(_url(self.base, "/app-valuations/")) + return _safe_json(resp) + + def valuation_detail(self, valuation_id: int) -> Dict[str, Any]: + resp = self.session.get(_url(self.base, f"/app-valuations/{valuation_id}")) + return _safe_json(resp) + + +class AdminClient: + """ + 后台客户端,会话维持与接口封装 + + 参数: + base: API 基础地址 + """ + + def __init__(self, base: str) -> None: + self.base = base.rstrip("/") + self.session = requests.Session() + + def set_token(self, token: str) -> None: + self.session.headers.update({"token": token}) + + def login(self, username: str, password: str) -> Optional[str]: + resp = self.session.post(_url(self.base, "/base/access_token"), json={"username": username, "password": password}) + data = _safe_json(resp) + token = data.get("data", {}).get("access_token") if isinstance(data, dict) else None + if token: + self.set_token(token) + return token + + def list_valuations(self) -> Dict[str, Any]: + resp = self.session.get(_url(self.base, "/valuations/")) + return _safe_json(resp) + + def valuation_detail(self, valuation_id: int) -> Dict[str, Any]: + resp = self.session.get(_url(self.base, f"/valuations/{valuation_id}")) + return _safe_json(resp) + + def valuation_steps(self, valuation_id: int) -> Dict[str, Any]: + resp = self.session.get(_url(self.base, f"/valuations/{valuation_id}/steps")) + return _safe_json(resp) + + +def _safe_json(resp: requests.Response) -> Dict[str, Any]: + try: + return resp.json() + except Exception: + return {"status_code": resp.status_code, "text": resp.text} + + +def build_sample_payload() -> Dict[str, Any]: + """ + 构建估值评估示例输入(精简版) + + 返回: + dict: 估值评估输入 + """ + # 使用你提供的参数,保持后端计算逻辑不变 + payload = { + "asset_name": "马王堆", + "institution": "成都文化产权交易所", + "industry": "文化艺术业", + "annual_revenue": "10000", + "rd_investment": "6000", + "three_year_income": ["8000", "9000", "9500"], + "funding_status": "省级资助", + "sales_volume": "60000", + "link_views": "350000", + "circulation": "3", + "last_market_activity": "0", + "monthly_transaction": "1", + "price_fluctuation": [402, 445], + "application_maturity": "0", + "application_coverage": "0", + "cooperation_depth": "0", + "offline_activities": "20", + "online_accounts": ["1", "成都文交所", "500000", "89222", "97412"], + "inheritor_level": "省级传承人", + "inheritor_age_count": [200, 68, 20], + "inheritor_certificates": [], + "heritage_level": "2", + "historical_evidence": {"artifacts": "58", "ancient_literature": "789", "inheritor_testimony": "100"}, + "patent_certificates": [], + "pattern_images": [], + "patent_application_no": "", + "heritage_asset_level": "纳入《国家文化数字化战略清单》", + "inheritor_ages": [200, 68, 20], + "implementation_stage": "成熟应用", + "coverage_area": "全球覆盖", + "collaboration_type": "无", + "scarcity_level": "流通:总发行份数 >1000份,或二级市场流通率 ≥ 5%", + "market_activity_time": "近一周", + "monthly_transaction_amount": "月交易额>100万<500万", + "platform_accounts": { + "douyin": {"account": "成都文交所", "likes": "500000", "comments": "89222", "shares": "97412"} + } + } + # 若 application_coverage 为占位,则用 coverage_area 回填 + if payload.get("application_coverage") in (None, "0", "") and payload.get("coverage_area"): + payload["application_coverage"] = payload["coverage_area"] + return payload + + +def main() -> None: + parser = argparse.ArgumentParser(description="估值二期 API 冒烟测试") + parser.add_argument("--base", default="http://127.0.0.1:9991/api/v1", help="API基础地址") + parser.add_argument("--phone", default="13800138001", help="测试手机号") + args = parser.parse_args() + + base = args.base.rstrip("/") + phone = args.phone + default_pwd = phone[-6:] + + app = AppClient(base) + admin = AdminClient(base) + + # 用户注册 + reg = app.register(phone) + _print("用户注册", reg) + + # 用户登录 + token = app.login(phone, default_pwd) + _print("用户登录token", {"access_token": token}) + if not token: + print("登录失败,终止测试") + return + + # 用户相关接口 + _print("用户信息", app.profile()) + _print("首页摘要", app.dashboard()) + _print("剩余估值次数", app.quota()) + + # 提交估值 + payload = build_sample_payload() + submit = app.submit_valuation(payload) + _print("提交估值", submit) + + # 轮询估值列表抓取最新记录 + valuation_id = None + for _ in range(10): + lst = app.list_valuations() + _print("我的估值列表", lst) + try: + items = lst.get("data", []) if isinstance(lst, dict) else [] + if items: + valuation_id = items[0].get("id") or items[-1].get("id") + if valuation_id: + break + except Exception: + pass + time.sleep(0.8) + + if valuation_id: + detail = app.valuation_detail(valuation_id) + _print("估值详情", detail) + else: + print("未获得估值ID,跳过详情") + + # 后台登录 + admin_token = admin.login("admin", "123456") + _print("后台登录token", {"access_token": admin_token}) + if admin_token: + vlist = admin.list_valuations() + _print("后台估值列表", vlist) + if valuation_id: + vdetail = admin.valuation_detail(valuation_id) + _print("后台估值详情", vdetail) + vsteps = admin.valuation_steps(valuation_id) + _print("后台估值计算步骤", vsteps) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/scripts/user_flow_test.py b/scripts/user_flow_test.py new file mode 100644 index 0000000..70025cb --- /dev/null +++ b/scripts/user_flow_test.py @@ -0,0 +1,381 @@ +import os +import sys +import json +import time +import uuid +import random +from typing import Dict, Any, List, Tuple, Optional + +import httpx + + +def now_ms() -> int: + return int(time.time() * 1000) + + +def make_url(base_url: str, path: str) -> str: + if base_url.endswith("/"): + base_url = base_url[:-1] + return f"{base_url}{path}" + + +def write_html_report(filepath: str, title: str, results: List[Dict[str, Any]]) -> None: + """ + 生成HTML测试报告 + 参数: + filepath: 报告输出文件路径 + title: 报告标题 + results: 测试结果列表,包含 name/status/message/detail + 返回: + None + """ + rows = [] + for r in results: + color = {"PASS": "#4caf50", "FAIL": "#f44336"}.get(r.get("status"), "#9e9e9e") + rows.append( + f"{r.get('name')}{r.get('status')}{r.get('message','')}
{json.dumps(r.get('detail', {}), ensure_ascii=False, indent=2)}
" + ) + html = f""" + +{title} + + +

{title}

+

生成时间: {time.strftime('%Y-%m-%d %H:%M:%S')}

+ +{''.join(rows)} +
用例结果说明详情
+ +""" + os.makedirs(os.path.dirname(filepath), exist_ok=True) + with open(filepath, "w", encoding="utf-8") as f: + f.write(html) + + +def _ensure_dict(obj: Any) -> Dict[str, Any]: + if isinstance(obj, dict): + return obj + return {"raw": str(obj)} + + +async def api_post_json(client: httpx.AsyncClient, url: str, payload: Dict[str, Any], headers: Optional[Dict[str, str]] = None) -> Tuple[int, Dict[str, Any]]: + """ + 发送POST JSON请求 + 参数: + client: httpx异步客户端 + url: 完整URL + payload: 请求体JSON + headers: 请求头 + 返回: + (状态码, 响应JSON) + """ + r = await client.post(url, json=payload, headers=headers or {}) + try: + parsed = r.json() + except Exception: + parsed = {"raw": r.text} + if parsed is None: + parsed = {"raw": r.text} + return r.status_code, _ensure_dict(parsed) + + +async def api_get(client: httpx.AsyncClient, url: str, headers: Optional[Dict[str, str]] = None, params: Optional[Dict[str, Any]] = None) -> Tuple[int, Dict[str, Any]]: + """ + 发送GET请求 + 参数: + client: httpx异步客户端 + url: 完整URL + headers: 请求头 + params: 查询参数 + 返回: + (状态码, 响应JSON) + """ + r = await client.get(url, headers=headers or {}, params=params or {}) + try: + parsed = r.json() + except Exception: + parsed = {"raw": r.text} + if parsed is None: + parsed = {"raw": r.text} + return r.status_code, _ensure_dict(parsed) + + +async def api_put_json(client: httpx.AsyncClient, url: str, payload: Dict[str, Any], headers: Optional[Dict[str, str]] = None) -> Tuple[int, Dict[str, Any]]: + """ + 发送PUT JSON请求 + 参数: + client: httpx异步客户端 + url: 完整URL + payload: 请求体JSON + headers: 请求头 + 返回: + (状态码, 响应JSON) + """ + r = await client.put(url, json=payload, headers=headers or {}) + try: + parsed = r.json() + except Exception: + parsed = {"raw": r.text} + if parsed is None: + parsed = {"raw": r.text} + return r.status_code, _ensure_dict(parsed) + + +async def user_register_flow(base_url: str, client: httpx.AsyncClient, phone: str, expect_success: bool = True) -> Dict[str, Any]: + """ + 用户注册流程 + 参数: + base_url: 基础URL(含 /api/v1) + client: httpx客户端 + phone: 手机号 + 返回: + 测试结果字典 + """ + url = make_url(base_url, "/app-user/register") + code, data = await api_post_json(client, url, {"phone": phone}) + rs = {"name": f"注册-{phone}", "status": "FAIL", "message": "", "detail": {"http": code, "body": _ensure_dict(data)}} + body = _ensure_dict(data) + payload = _ensure_dict(body.get("data")) + ok = (body.get("code") == 200 and payload.get("phone") == phone) + # 期望失败场景:重复注册或无效格式 + if not expect_success: + ok = (body.get("code") in (400, 422) or (isinstance(body.get("msg"), str) and "已存在" in body.get("msg"))) + rs["message"] = "注册失败(符合预期)" if ok else "注册失败(不符合预期)" + else: + rs["message"] = "注册成功" if ok else "注册失败" + rs["status"] = "PASS" if ok else "FAIL" + return rs + + +async def user_login_flow(base_url: str, client: httpx.AsyncClient, phone: str, password: str, expect_success: bool = True) -> Tuple[Dict[str, Any], str]: + """ + 用户登录流程 + 参数: + base_url: 基础URL(含 /api/v1) + client: httpx客户端 + phone: 手机号 + password: 密码 + 返回: + (测试结果字典, access_token字符串或空) + """ + url = make_url(base_url, "/app-user/login") + code, data = await api_post_json(client, url, {"phone": phone, "password": password}) + token = "" + is_ok = (code == 200 and isinstance(data, dict) and data.get("access_token")) + if is_ok: + token = data.get("access_token", "") + if not expect_success: + ok = (code in (401, 403)) + rs = {"name": f"登录-{phone}", "status": "PASS" if ok else "FAIL", "message": "登录失败(符合预期)" if ok else "登录失败(不符合预期)", "detail": {"http": code, "body": data}} + else: + rs = {"name": f"登录-{phone}", "status": "PASS" if is_ok else "FAIL", "message": "登录成功" if is_ok else "登录失败", "detail": {"http": code, "body": data}} + return rs, token + + +async def user_profile_flow(base_url: str, client: httpx.AsyncClient, token: str) -> Dict[str, Any]: + """ + 用户资料查看与编辑 + 参数: + base_url: 基础URL(含 /api/v1) + client: httpx客户端 + token: 用户JWT + 返回: + 测试结果字典 + """ + headers = {"token": token} + view_url = make_url(base_url, "/app-user/profile") + v_code, v_data = await api_get(client, view_url, headers=headers) + ok_view = (v_code == 200 and isinstance(v_data, dict) and v_data.get("id")) + upd_url = make_url(base_url, "/app-user/profile") + nickname = "tester-" + uuid.uuid4().hex[:6] + u_code, u_data = await api_put_json(client, upd_url, {"nickname": nickname}, headers=headers) + ok_upd = (u_code == 200 and isinstance(u_data, dict) and u_data.get("nickname") == nickname) + is_ok = ok_view and ok_upd + return {"name": "资料查看与编辑", "status": "PASS" if is_ok else "FAIL", "message": "个人资料操作成功" if is_ok else "个人资料操作失败", "detail": {"view": {"http": v_code, "body": v_data}, "update": {"http": u_code, "body": u_data}}} + + +async def permission_flow(base_url: str, client: httpx.AsyncClient, admin_token: str) -> Dict[str, Any]: + """ + 权限控制验证 + 参数: + base_url: 基础URL(含 /api/v1) + client: httpx客户端 + admin_token: 管理端token头值 + 返回: + 测试结果字典 + """ + protected_url = make_url(base_url, "/user/list") + c1, d1 = await api_get(client, protected_url) + c2, d2 = await api_get(client, protected_url, headers={"token": admin_token}) + ok1 = (c1 in (401, 403, 422)) + ok2 = (c2 in (200, 403)) + is_ok = ok1 and ok2 + return {"name": "权限控制", "status": "PASS" if is_ok else "FAIL", "message": "权限校验完成", "detail": {"no_token": {"http": c1, "body": d1}, "with_token": {"http": c2, "body": d2}}} + + +async def main() -> None: + """ + 主流程 + 参数: + 无 + 返回: + None + """ + base = os.getenv("TEST_BASE_URL", "http://localhost:9991/api/v1") + admin_token = os.getenv("ADMIN_TOKEN", "dev") + results: List[Dict[str, Any]] = [] + endpoint_list = [ + {"path": "/app-user/register", "desc": "用户注册"}, + {"path": "/app-user/login", "desc": "用户登录"}, + {"path": "/app-user/profile", "desc": "获取用户信息(需token)"}, + {"path": "/app-user/profile", "desc": "更新用户信息(需token) PUT"}, + {"path": "/app-user/dashboard", "desc": "用户首页摘要(需token)"}, + {"path": "/app-user/quota", "desc": "剩余估值次数(需token)"}, + {"path": "/app-user/change-password", "desc": "修改密码(需token)"}, + {"path": "/app-user/validate-token", "desc": "验证token(需token)"}, + {"path": "/app-user/logout", "desc": "登出(需token)"}, + {"path": "/upload/file", "desc": "上传文件"}, + {"path": "/app-valuations/", "desc": "创建估值评估(需token)"}, + {"path": "/app-valuations/", "desc": "获取我的估值评估列表(需token)"}, + {"path": "/app-valuations/{id}", "desc": "获取估值评估详情(需token)"}, + {"path": "/app-valuations/statistics/overview", "desc": "获取我的估值统计(需token)"}, + {"path": "/app-valuations/{id}", "desc": "删除估值评估(需token) DELETE"}, + ] + async with httpx.AsyncClient(timeout=10) as client: + def gen_cn_phone() -> str: + second = str(random.choice([3,4,5,6,7,8,9])) + rest = "".join(random.choice("0123456789") for _ in range(9)) + return "1" + second + rest + phone_ok = gen_cn_phone() + r1 = await user_register_flow(base, client, phone_ok, expect_success=True) + results.append(r1) + r2 = await user_register_flow(base, client, phone_ok, expect_success=False) + results.append(r2) + r3 = await user_register_flow(base, client, "abc", expect_success=False) + results.append(r3) + lr_ok, token = await user_login_flow(base, client, phone_ok, phone_ok[-6:], expect_success=True) + results.append(lr_ok) + lr_bad, _ = await user_login_flow(base, client, phone_ok, "wrong", expect_success=False) + results.append(lr_bad) + # token 场景:验证、资料、首页、配额 + if token: + # 验证token + vt_code, vt_data = await api_get(client, make_url(base, "/app-user/validate-token"), headers={"token": token}) + vt_ok = (vt_code == 200 and isinstance(vt_data, dict) and vt_data.get("data", {}).get("user_id")) + results.append({"name": "验证token", "status": "PASS" if vt_ok else "FAIL", "message": "token有效" if vt_ok else "token无效", "detail": {"http": vt_code, "body": vt_data}}) + + # 资料查看与编辑 + pr = await user_profile_flow(base, client, token) + results.append(pr) + + # 首页摘要 + db_code, db_data = await api_get(client, make_url(base, "/app-user/dashboard"), headers={"token": token}) + db_ok = (db_code == 200 and isinstance(db_data, dict)) + results.append({"name": "用户首页摘要", "status": "PASS" if db_ok else "FAIL", "message": "获取成功" if db_ok else "获取失败", "detail": {"http": db_code, "body": db_data}}) + + # 剩余估值次数 + qt_code, qt_data = await api_get(client, make_url(base, "/app-user/quota"), headers={"token": token}) + qt_ok = (qt_code == 200 and isinstance(qt_data, dict)) + results.append({"name": "剩余估值次数", "status": "PASS" if qt_ok else "FAIL", "message": "获取成功" if qt_ok else "获取失败", "detail": {"http": qt_code, "body": qt_data}}) + + # 修改密码并验证新旧密码 + cp_code, cp_data = await api_post_json(client, make_url(base, "/app-user/change-password"), {"old_password": phone_ok[-6:], "new_password": "Npw" + phone_ok[-6:]}, headers={"token": token}) + cp_ok = (cp_code == 200 and isinstance(cp_data, dict) and cp_data.get("code") == 200) + results.append({"name": "修改密码", "status": "PASS" if cp_ok else "FAIL", "message": "修改成功" if cp_ok else "修改失败", "detail": {"http": cp_code, "body": cp_data}}) + + # 旧密码登录应失败 + lr_old, _ = await user_login_flow(base, client, phone_ok, phone_ok[-6:], expect_success=False) + results.append(lr_old) + # 新密码登录成功 + lr_new, token2 = await user_login_flow(base, client, phone_ok, "Npw" + phone_ok[-6:], expect_success=True) + results.append(lr_new) + use_token = token2 or token + + # 上传文件(pdf) + file_url = "" + try: + up_resp = await client.post(make_url(base, "/upload/file"), files={"file": ("demo.pdf", b"%PDF-1.4\n%\xe2\xe3\xcf\xd3\n", "application/pdf")}) + u_code = up_resp.status_code + u_data = _ensure_dict(up_resp.json() if up_resp.headers.get("content-type", "").startswith("application/json") else {"raw": up_resp.text}) + file_url = u_data.get("url", "") + u_ok = (u_code == 200 and file_url) + results.append({"name": "上传文件", "status": "PASS" if u_ok else "FAIL", "message": "上传成功" if u_ok else "上传失败", "detail": {"http": u_code, "body": u_data}}) + except Exception as e: + results.append({"name": "上传文件", "status": "FAIL", "message": "上传异常", "detail": {"error": repr(e)}}) + + # 创建估值评估 + create_payload = { + "asset_name": "测试资产", + "institution": "测试机构", + "industry": "测试行业", + "three_year_income": [100, 120, 140], + "application_coverage": "全国覆盖", + "rd_investment": "10", + "annual_revenue": "100", + "price_fluctuation": [10, 20], + "platform_accounts": {"douyin": {"likes": 1, "comments": 1, "shares": 1}}, + "pattern_images": [], + "report_url": file_url or None, + "certificate_url": file_url or None, + } + cv_code, cv_data = await api_post_json(client, make_url(base, "/app-valuations/"), create_payload, headers={"token": use_token}) + cv_ok = (cv_code == 200 and isinstance(cv_data, dict) and cv_data.get("data", {}).get("task_status") == "queued") + results.append({"name": "创建估值评估", "status": "PASS" if cv_ok else "FAIL", "message": "任务已提交" if cv_ok else "提交失败", "detail": {"http": cv_code, "body": cv_data}}) + + # 等待片刻后获取列表与详情 + import asyncio + await asyncio.sleep(0.3) + gl_code, gl_data = await api_get(client, make_url(base, "/app-valuations/"), headers={"token": use_token}, params={"page": 1, "size": 10}) + gl_ok = (gl_code == 200 and isinstance(gl_data, dict) and isinstance(gl_data.get("data"), list)) + results.append({"name": "估值列表", "status": "PASS" if gl_ok else "FAIL", "message": "获取成功" if gl_ok else "获取失败", "detail": {"http": gl_code, "body": gl_data}}) + vid = None + if gl_ok and gl_data.get("data"): + vid = gl_data["data"][0].get("id") + if vid: + gd_code, gd_data = await api_get(client, make_url(base, f"/app-valuations/{vid}"), headers={"token": use_token}) + gd_ok = (gd_code == 200 and isinstance(gd_data, dict) and gd_data.get("data", {}).get("id") == vid) + results.append({"name": "估值详情", "status": "PASS" if gd_ok else "FAIL", "message": "获取成功" if gd_ok else "获取失败", "detail": {"http": gd_code, "body": gd_data}}) + # 统计 + st_code, st_data = await api_get(client, make_url(base, "/app-valuations/statistics/overview"), headers={"token": use_token}) + st_ok = (st_code == 200 and isinstance(st_data, dict)) + results.append({"name": "估值统计", "status": "PASS" if st_ok else "FAIL", "message": "获取成功" if st_ok else "获取失败", "detail": {"http": st_code, "body": st_data}}) + # 删除 + del_resp = await client.delete(make_url(base, f"/app-valuations/{vid}"), headers={"token": use_token}) + d_code = del_resp.status_code + d_data = _ensure_dict(del_resp.json() if del_resp.headers.get("content-type", "").startswith("application/json") else {"raw": del_resp.text}) + d_ok = (d_code == 200 and isinstance(d_data, dict) and d_data.get("data", {}).get("deleted")) + results.append({"name": "删除估值", "status": "PASS" if d_ok else "FAIL", "message": "删除成功" if d_ok else "删除失败", "detail": {"http": d_code, "body": d_data}}) + + # 登出 + lo_code, lo_data = await api_post_json(client, make_url(base, "/app-user/logout"), {}, headers={"token": use_token}) + lo_ok = (lo_code == 200) + results.append({"name": "登出", "status": "PASS" if lo_ok else "FAIL", "message": "登出成功" if lo_ok else "登出失败", "detail": {"http": lo_code, "body": lo_data}}) + perm = await permission_flow(base, client, admin_token) + results.append(perm) + passes = sum(1 for r in results if r.get("status") == "PASS") + total = len(results) + print(json.dumps({"total": total, "passes": passes, "results": results, "endpoints": endpoint_list}, ensure_ascii=False, indent=2)) + write_html_report("reports/user_flow_script_report.html", "用户维度功能测试报告(脚本)", results) + + +if __name__ == "__main__": + import asyncio + asyncio.run(main()) +async def api_put_json(client: httpx.AsyncClient, url: str, payload: Dict[str, Any], headers: Optional[Dict[str, str]] = None) -> Tuple[int, Dict[str, Any]]: + """ + 发送PUT JSON请求 + 参数: + client: httpx异步客户端 + url: 完整URL + payload: 请求体JSON + headers: 请求头 + 返回: + (状态码, 响应JSON) + """ + r = await client.put(url, json=payload, headers=headers or {}) + data = {} + try: + data = r.json() + except Exception: + data = {"raw": r.text} + return r.status_code, data \ No newline at end of file diff --git a/test_dynamic_default.py b/test_dynamic_default.py deleted file mode 100644 index 0818679..0000000 --- a/test_dynamic_default.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python3 -""" -测试动态默认值计算逻辑 -""" -import sys -import os -import asyncio - -# 添加项目根目录到 Python 路径 -project_root = os.path.dirname(os.path.abspath(__file__)) -sys.path.insert(0, project_root) - -from app.utils.calculation_engine.market_value_c.market_data_analyzer import MarketDataAnalyzer - -async def test_market_data_analyzer(): - """测试市场数据分析器""" - print("=== 测试市场数据分析器 ===") - - analyzer = MarketDataAnalyzer() - - # 测试获取近期交易中位数 - print("\n1. 测试获取近期交易中位数:") - try: - median_price = await analyzer.get_recent_transaction_median(days=30) - print(f" 近30天交易中位数: {median_price}万元") - except Exception as e: - print(f" 获取交易中位数失败: {e}") - - # 测试自适应默认值计算 - print("\n2. 测试自适应默认值计算:") - test_cases = [ - ("限量", "文化艺术"), - ("普通", "科技创新"), - ("稀有", "传统工艺"), - ("", "") # 空值测试 - ] - - for issuance_level, asset_type in test_cases: - try: - adaptive_price = analyzer.calculate_adaptive_default_value(asset_type, issuance_level) - print(f" 发行级别: {issuance_level or '未知'}, 资产类型: {asset_type or '未知'} -> {adaptive_price}万元") - except Exception as e: - print(f" 计算自适应默认值失败 ({issuance_level}, {asset_type}): {e}") - -async def test_market_value_c_integration(): - """测试市场估值C的集成""" - print("\n=== 测试市场估值C集成 ===") - - from app.utils.calculation_engine.market_value_c.market_value_c import MarketValueCCalculator - - calculator = MarketValueCCalculator() - - # 测试数据:没有提供 average_transaction_price - test_data = { - 'daily_browse_volume': 500.0, - 'collection_count': 50, - 'issuance_level': '限量', - 'recent_market_activity': '近一周' - } - - print(f"\n测试输入数据: {test_data}") - - try: - result = await calculator.calculate_complete_market_value_c(test_data) - print(f"\n计算结果:") - print(f" 市场估值C: {result['market_value_c']}万元") - print(f" 市场竞价C1: {result['market_bidding_c1']}万元") - print(f" 热度系数C2: {result['heat_coefficient_c2']}") - print(f" 稀缺性乘数C3: {result['scarcity_multiplier_c3']}") - print(f" 时间衰减C4: {result['temporal_decay_c4']}") - except Exception as e: - print(f"计算失败: {e}") - import traceback - traceback.print_exc() - -async def main(): - """主测试函数""" - print("开始测试动态默认值计算逻辑...") - - await test_market_data_analyzer() - await test_market_value_c_integration() - - print("\n测试完成!") - -if __name__ == "__main__": - asyncio.run(main()) \ No newline at end of file diff --git a/估值字段.txt b/估值字段.txt index 6d00b90..997b46c 100644 --- a/估值字段.txt +++ b/估值字段.txt @@ -37,8 +37,8 @@ export DOCKER_DEFAULT_PLATFORM=linux/amd64 -docker build -t zfc931912343/guzhi-fastapi-admin:v1.4 . -docker push zfc931912343/guzhi-fastapi-admin:v1.4 +docker build -t zfc931912343/bindbox-game:v1.0 . +docker push zfc931912343/bindbox-game:v1.0 # 运行容器