# 企业信息分类同步(company_info → classify → DMS 1888/1886) ## 目标 对话返回的 `result` 事件里带 `company_info`(非空)时,自动把该企业的分类结果同步到 DMS 的企业基础信息(1888)与企业荣誉信息(1886)两个栏目:没有该企业就新增, 已有就按字段差异更新。全程 fire-and-forget,失败不影响聊天。 依据接口文档:[`../../reference/company-classification.md`](../../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_info**:`api-chat-coordinator.ts` 的 `case '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'` 作所有权标记。 4. 1888 对比范围 = 身份三字段(企业名/信用代码/法人)+ 6 个标签字段。 ## Step-0 实测(2026-09-18) | 探测 | 结果 | |---|---| | classify 空体 `{}` | **200**,返回六角度全空 + `input_warnings:["company_info_empty"]`,不调用 LLM | | classify 任何带键的体(文档示例 / ASCII / 中文 / `company:{}` / `profile:{}` / `honors:null` / `company_info:{}` / `Company:{}`) | **全部 422 `invalid_request`**(响应头有 `x-request-id`,可据此找后端日志) | | `POST /dms/model/getModelById`(form `modelId=2036`/`2032`) | **200**,拿到完整 fieldList:1888 47 字段 / 1886 12 字段,`c_tag_*` 已存在 | | 1888 must | 仅 `c_credit_code` | | 1886 must | `c_id` + `c_credit_code` | > ⚠️ classify 的 422 是**后端问题**(前端已排除编码/引号/键名因素),需后端排查; > 在它修好之前,「真实分类响应」这条链路只能靠**注入假响应**验证。 ## 步骤 1. 纯函数层 `src/network/api/dms/classification-sync-utils.ts`(身份抽取、编解码、diff、集合同步) 2. 网络层 `src/network/api/company-classification.ts`(`classifyCompany`) 3. 编排层 `src/network/api/dms/company-info-sync.ts`(查→分类→写/改,串行链) 4. 协调器捕获 company_info(3 处)+ composable 挂钩(1 处) 5. `client.ts` 加 1888/2036、1886/2032 常量;把 `findRowBy`/`enqueueWrite` 从 `chat-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 build`、`vue-tsc --noEmit`、`validate-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 行);若业务要保留历史荣誉, 改成「只增不删」即可(一行)