company-classify-dms-sync.md 6.8 KB

企业信息分类同步(company_info → classify → DMS 1888/1886)

目标

对话返回的 result 事件里带 company_info(非空)时,自动把该企业的分类结果同步到 DMS 的企业基础信息(1888)与企业荣誉信息(1886)两个栏目:没有该企业就新增, 已有就按字段差异更新。全程 fire-and-forget,失败不影响聊天。

依据接口文档:../../reference/company-classification.md(F062)。

现状(2026-09-18 探测所得)

  • classify 接口已部署POST /api/company/classify,与 /api/chat 同一后端)。 但 ⚠️ 实测任何带内容的请求体都返回 422 invalid_request——连文档自己的 合成示例也被拒;只有空对象 {} 返回 200(company_info_empty)。 详见本文件「Step-0 实测」。
  • DMS 侧的字段已经加好了(2026-09-18 17:35–17:36 由 DMS 侧更新模型):
    • 1888(modelId 2036,47 字段)新增 6 个 c_tag_*,别名 = 六个角度名;must 只有 c_credit_code
    • 1886(modelId 2032,12 字段)新增 c_tag_name(别名「荣誉」);must = c_id + c_credit_code
    • 所以不需要再请 DMS 侧加字段(原计划里那份清单已作废)
  • 前端目前拿不到 company_infoapi-chat-coordinator.tscase 'result' 是空分支。
  • api-chat.md还没有 company_info 的契约章节(classify 文档的引用超前); 分类文档给出的结构是 {company, profile:{data, source}, honors}

已确认的决策(用户 2026-09-18)

  1. DMS 读写复用现有路径/dms-api 代理 + src/network/api/dms/client.ts (「原来怎么用现在就怎么用」),不用 121.43.55.7:2101/proxy_dms
  2. 六个角度各存 1888 一个字段(JSON 数组字符串)。
  3. 1886 一个标签一行,c_source='company_info_sync' 作所有权标记【2026-09-18 当天修正,见下】1886 改为以 company_info.honors.records 为准全量对齐
  4. 1888 对比范围 = 身份三字段(企业名/信用代码/法人)+ 6 个标签字段。

📌 修正:1886 的数据源换成 honors.records(用户 2026-09-18 拍板)

起因:用户用宇树科技股份有限公司实测,1886 只有 2 条,觉得「应该不止两条」。 查证后确认同步没写错、是数据源选错了

数量
分类接口的「资质荣誉」标签(原实现的数据源) 2 个(中国独角兽企业国家级高新技术企业)——它是 165 个标签里挑的粗标签
company_info.honors.records(真实荣誉) 31 条,每条含 荣誉名/级别/来源/发布机构/发布日期/证书编号
库里已灌入的行 28 条(同一份企查查荣誉数据的旧快照,字段与 honors.records 一一对应)

新规则

  • 1886 一行 = 一条真实荣誉;荣誉名同时写 c_tag_name 与系统字段 title
  • 与库里该企业的行按「荣誉名+级别+来源全量对齐:补新增、更新差异、删多余
  • 仅当 honors.complete === true 才删(记录集不完整时删 = 丢数据)
  • 灌入的行同样参与对齐(它们就是同一份数据的旧快照),首次对齐会补上 c_tag_name
  • 「资质荣誉」标签仍写进 1888 的 c_tag_honor(标签与明细各归其位,互不冲突)

Step-0 实测(2026-09-18)

探测 结果
classify 空体 {} 200,返回六角度全空 + input_warnings:["company_info_empty"],不调用 LLM
classify 精简/合成载荷(文档里的示例片段 / 纯 ASCII / company:{} / profile:{} / honors:null / company_info:{} 全部 422 invalid_request
classify 真实完整载荷(用户给的真实 company_info) 200,7 秒返回六角度真实分类结果
POST /dms/model/getModelById(form modelId=2036/2032 200:1888 47 字段 / 1886 12 字段,c_tag_* 已存在
1888 must / 1886 must c_credit_codec_id + c_credit_code
1888 / 1886 的实际数据 两栏目都已有数据(2026-09-17 18:12 由 DMS 侧灌入):1888 是完整工商信息行,1886 是真实荣誉行(c_source = 来源通知名)。原文档「仍是空的」已过期
空标签的存储 [] 读回来是字段不存在(语义等同);c_created_at 在 1888 里写了读不回

📌 更正:本轮最初把 classify 的 422 记成「后端坏了」,不准确——它是对载荷完整性的 校验:精简/合成载荷被拒,真实完整载荷正常。已在 api-chat.md 的 company_info 章节写明。

步骤

  1. 纯函数层 src/network/api/dms/classification-sync-utils.ts(身份抽取、编解码、diff、集合同步)
  2. 网络层 src/network/api/company-classification.tsclassifyCompany
  3. 编排层 src/network/api/dms/company-info-sync.ts(查→分类→写/改,串行链)
  4. 协调器捕获 company_info(3 处)+ composable 挂钩(1 处)
  5. client.ts 加 1888/2036、1886/2032 常量;把 findRowBy/enqueueWritechat-sessions-dms.ts 提到 client.ts 共用(避免两处 orderBy 坑各踩一遍)

数据流

result 事件 data.company_info → 协调器暂存 → done → totalResponse 带出
 → useBusinessAssistantChat 监听器(saveTurnToDms 之后、finishTask 之前)
 → void syncCompanyInfoFromChat()  → classify → 1888 upsert + 1886 集合同步

验证

  • 纯函数:harness/tools/verify-company-classify-sync.mjs(打包 _entry-company-classify.ts
  • 真实 DMS:harness/tools/verify-company-classify-dms.mjs(注入假分类响应走编排层, 首写 → 二次改 → 三次清空 → 清理测试数据)
  • 回归:npm run buildvue-tsc --noEmitvalidate-harness、既有 tools 脚本
  • 真实 classify:等后端修好 422 后补测,届时把真实载荷回填 api-chat.md

风险 / 未决

  • classify 422 阻塞真实验证(后端侧);前端实现不受阻
  • c_tag_honor(1888)的 frontType=content(富文本),其余是 text/varchar —— 写 JSON 数组字符串时富文本字段可能被转义或清洗,真实链路验证时要专门核对这一列; 若确实被改写,请 DMS 侧把它改成 text
  • company_info 的真实载荷未知 → 全可选 + profile.data 优先回退 company 的容错读取
  • 1886 的 c_id 必填(整型,源库主键);前端新写行拿不到源库 id,先按 1889 的经验 传 0(实测自动填),若被 214 拒绝再与 DMS 侧确认取值
  • completed + 空数组 = 覆盖清空(会删光我方 1886 行);若业务要保留历史荣誉, 改成「只增不删」即可(一行)