Przeglądaj źródła

change(company): 荣誉名改写进新建的 c_honor 字段

用户指示「现在没有存具体的荣誉名称,我新建了 c_honor 字段,在这个字段里存荣誉名称」。
探测确认 1886 模型已扩到 13 字段:新增 c_honor(别名「荣誉名称」),同时 c_tag_name 的别名
被改成「荣誉标签」——两个字段语义分开了。

- 荣誉名改写进 c_honor + 系统字段 title(title 供 DMS 列表显示,灌入行本来就有)
- c_tag_name 不再当名字用,只在读取名字时作中间回退项,回退链 c_honor → c_tag_name → title
  (早期同步写进去的值仍在,靠它仍能匹配到同一行,且对齐时会补上 c_honor)
- 新增 HONOR_NAME_FIELD 常量;HONOR_TAG_FIELD 保留并注明只作读取兼容

验证:纯逻辑 66/66(新增「早期只有 c_tag_name 的行也会补上 c_honor」等断言)、
真实 DMS 23/23、e2e 20/20、build 通过、回归抽查全绿。
真实数据:宇树科技的 31 行已全部补上 c_honor(缺该字段的行 = 0,键去重后 31 = 行数)。
⚠️ 修正后尚未再走一遍浏览器。

Co-Authored-By: Claude Code <noreply@anthropic.com>
gongtianxiao 1 dzień temu
rodzic
commit
47d80e708a

+ 1 - 1
README.md

@@ -243,7 +243,7 @@
     一行一家企业(幂等键 `c_credit_code`,字段级 diff,无差异零写入);
     **1886 企业荣誉信息** ← **`company_info.honors.records`**(逐条真实荣誉),一条荣誉一行
   - 1886 与库里该企业的行按「荣誉名+级别+来源」**全量对齐**(补新增、更新差异、删多余),
-    **仅当 `honors.complete === true` 才删**;荣誉名同时写 `c_tag_name` 与系统 `title`。
+    **仅当 `honors.complete === true` 才删**;荣誉名写 `c_honor` 与系统 `title`。
     —— 数据源曾用分类接口的「资质荣誉」标签,实测宇树科技只给 2 个标签而真实荣誉有 31 条,
     按用户要求改为以 `honors.records` 为准(分类标签仍写进 1888 的 `c_tag_honor`)
   - 全程 fire-and-forget:任何失败只 warn,不影响聊天;同企业并发走串行链

+ 3 - 3
harness/docs/exec-plans/active/company-classify-dms-sync.md

@@ -16,7 +16,7 @@ DMS 的企业基础信息(1888)与企业荣誉信息(1886)两个栏目
   详见本文件「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`
+  - 1886(modelId 2032,**13 字段**)新增 `c_tag_name`(荣誉标签)与 `c_honor`(**荣誉名称**,18:17 追加);must = `c_id` + `c_credit_code`
   - 所以**不需要再请 DMS 侧加字段**(原计划里那份清单已作废)
 - 前端目前**拿不到 company_info**:`api-chat-coordinator.ts` 的 `case 'result'` 是空分支。
 - `api-chat.md` 里**还没有 company_info 的契约章节**(classify 文档的引用超前);
@@ -43,10 +43,10 @@ DMS 的企业基础信息(1888)与企业荣誉信息(1886)两个栏目
 | 库里已灌入的行 | 28 条(**同一份企查查荣誉数据的旧快照**,字段与 `honors.records` 一一对应) |
 
 **新规则**:
-- 1886 一行 = 一条真实荣誉;荣誉名同时写 `c_tag_name` 与系统字段 `title`
+- 1886 一行 = 一条真实荣誉;荣誉名写 `c_honor` 与系统字段 `title`
 - 与库里该企业的行按「**荣誉名+级别+来源**」**全量对齐**:补新增、更新差异、删多余
 - **仅当 `honors.complete === true` 才删**(记录集不完整时删 = 丢数据)
-- 灌入的行同样参与对齐(它们就是同一份数据的旧快照),首次对齐会补上 `c_tag_name`
+- 灌入的行同样参与对齐(它们就是同一份数据的旧快照),首次对齐会补上 `c_honor`
 - 「资质荣誉」标签仍写进 1888 的 `c_tag_honor`(标签与明细各归其位,互不冲突)
 
 ## Step-0 实测(2026-09-18)

+ 15 - 9
harness/docs/reference/DMS_COLUMNS.md

@@ -81,18 +81,24 @@
 | `c_dead_line` | 截止日期 | **text** | varchar | |
 | `c_certificate_code` | 证书编号 | text | varchar | |
 | `c_created_at` | 创建时间 | **timestamp** | date_time | |
-| `c_tag_name` | 荣誉 | text | text | |
-
-> `c_tag_name` 由**前端的企业信息分类同步**写入(2026-09-18 DMS 侧新增,12 字段):
-> **一行 = 一条真实荣誉**(数据源是 `company_info.honors.records`),荣誉名同时写进
-> 这一列与系统字段 `title`(DMS 列表用后者显示);别的列照抄后端记录:
-> `c_level`=Level、`c_source`=Source、`c_publish_office`=PublishOffice、
-> `c_publish_date`=PublishDate、`c_beging_date`=BegingDate、`c_dead_line`=DeadLine、
-> `c_certificate_code`=CertificateCode。
+| `c_tag_name` | 荣誉标签 | text | text | |
+| `c_honor` | 荣誉名称 | text | text | |
+
+> **`c_honor`(荣誉名称)与 `c_tag_name`(荣誉标签)由前端的企业信息分类同步写入**
+> (2026-09-18 DMS 侧新增,13 字段):**一行 = 一条真实荣誉**
+> (数据源是 `company_info.honors.records`)——
+> - **`c_honor` 存荣誉名称**(如「独角兽企业」),系统字段 `title` 同时写
+>   (DMS 列表用后者显示,且 09-17 灌入的行本来就有 title)
+> - `c_tag_name` 是同日**先建的同类列**,早期版本把荣誉名写在了这里;
+>   用户 2026-09-18 18:17 新建 `c_honor` 后它**不再当名字用**,只作读取兼容
+> - 别的列照抄后端记录:`c_level`=Level、`c_source`=Source、
+>   `c_publish_office`=PublishOffice、`c_publish_date`=PublishDate、
+>   `c_beging_date`=BegingDate、`c_dead_line`=DeadLine、`c_certificate_code`=CertificateCode
 >
 > 同步按「**荣誉名+级别+来源**」与库里该企业的行全量对齐:补新增、更新有差异的列、
 > 删库里多出来的(**仅当后端 `honors.complete === true`**)。
-> 灌入的行(09-17 那批,只有 `title` 没有 `c_tag_name`)同样参与,首次对齐会补上这一列。
+> 灌入的行(09-17 那批,只有 `title`)与早期同步的行(名字在 `c_tag_name`)同样参与,
+> 首次对齐会把 `c_honor` 补上。
 > 见 [`../exec-plans/active/company-classify-dms-sync.md`](../exec-plans/active/company-classify-dms-sync.md)。
 
 ### 2.3 助手会话 `columnId=1887`

+ 2 - 2
harness/docs/reference/README.md

@@ -30,7 +30,7 @@
 >   DMS 侧 `ams_user` 灌入,不再是空的——此前文档里「仍是空的」已过期)。
 >   1888 的行是完整工商信息(企业名/状态/地址/经营范围等),**没有** `c_tag_*`;
 >   1886 的行是真实荣誉记录(`c_level` 国家级/区级、系统 `title` 存荣誉名、
->   `c_source` 存来源通知名),**没有** `c_tag_name`。
+>   `c_source` 存来源通知名),**没有** `c_honor` / `c_tag_name`(那两列是后来加的)
 > - **前端的企业信息分类同步**会写这两栏目(见
 >   [`company-classification.md`](company-classification.md) 与
 >   [`../exec-plans/active/company-classify-dms-sync.md`](../exec-plans/active/company-classify-dms-sync.md)):
@@ -38,7 +38,7 @@
 >   库里没有的企业才新增;
 >   1886 以 `company_info.honors.records` 为准,与该企业的行按「荣誉名+级别+来源」**全量对齐**
 >   (补新增、更新差异、删多余;`honors.complete=false` 时只增改不删)——
->   **灌入的荣誉行同样参与对齐**(它们就是同一份数据的旧快照),对齐后会被补上 `c_tag_name`
+>   **灌入的荣誉行同样参与对齐**(它们就是同一份数据的旧快照),对齐后会被补上 `c_honor`(荣誉名称)
 > - **1885 页面浏览**:仍是空的(统计埋点已于 2026-09-18 删除,前端不再写)
 
 ## 现行契约:`POST /api/chat`

Plik diff jest za duży
+ 3 - 4
harness/feature_list.json


+ 21 - 0
harness/progress.md

@@ -2589,3 +2589,24 @@
 **顺带修好的真实数据**:宇树科技(91330108MA27YJ5H56)的 1886 已从「28 灌入 + 2 标签」
 对齐为**与后端一致的 31 条**(灌入行补上了 `c_tag_name`,缺的 4 条由 `user_gtx` 写入)。
 
+### 续 3:用户新建 `c_honor` 存荣誉名 → 名字列改用新字段
+
+用户指示:「现在没有存具体的荣誉名称,我新建了 c_honor 字段,在这个字段里存荣誉名称」。
+探测确认:**1886 模型已扩到 13 字段**(18:17 更新),新增 `c_honor`(别名「荣誉名称」,text),
+同时 `c_tag_name` 的别名被改成「**荣誉标签**」——两个字段的语义分开了。
+
+**改动**:
+- 荣誉名**改写进 `c_honor`** + 系统字段 `title`(title 供 DMS 列表显示,灌入行本来就有)
+- `c_tag_name` **不再当名字用**,只在读取名字时作为中间回退项
+  (回退链 `c_honor → c_tag_name → title`,字符串 "null" 仍不算名字)——
+  早期同步写进去的值还在,靠它仍能把已存在的行匹配上,且首次对齐会给这些行**补上 `c_honor`**
+- 新增常量 `HONOR_NAME_FIELD = "c_honor"`;`HONOR_TAG_FIELD` 保留并注明只作兼容
+
+**跑过的验证**:纯逻辑 **66/66**(新增「早期只有 c_tag_name 的行也会补上 c_honor」等断言);
+真实 DMS **23/23**;**e2e 20/20**;`npm run build` 通过;回归抽查(33/24/10/12)全绿。
+
+**真实数据**:宇树科技的 31 行**全部补上了 `c_honor`**(复查「缺 c_honor 的行 = 0」,
+键去重后 31 = 行数,无重复行)。
+
+**仍未做**:浏览器端到端(用户实测后改了两次数据源/字段,尚未再走一遍浏览器)。
+

+ 1 - 0
harness/tools/_entry-company-classify.ts

@@ -10,6 +10,7 @@ export {
   ANGLE_TO_FIELD,
   CLASSIFY_ANGLE_KEYS,
   HONOR_FIELD_MAP,
+  HONOR_NAME_FIELD,
   HONOR_TAG_FIELD,
   buildEnterpriseRow,
   buildHonorKey,

+ 6 - 6
harness/tools/verify-company-classify-dms.mjs

@@ -7,7 +7,7 @@
  *
  * 验证什么:
  *   ① 1888 首写:一行,六个标签字段 = 对应角度的 JSON 数组
- *   ② 1886 首写:**一条荣誉一行**,title 与 c_tag_name 都是荣誉名,级别/来源/机构/日期/证书号各就各位
+ *   ② 1886 首写:**一条荣誉一行**,title 与 c_honor 都是荣誉名,级别/来源/机构/日期/证书号各就各位
  *   ③ 幂等:同样数据再跑一次 → 零新增、行 uuid 不变
  *   ④ 改:1888 标签变化 → 仍是同一行且已更新;1886 少一条/多一条/改字段 →
  *      删多余、补新增、更新差异,行数与后端列表一致
@@ -31,7 +31,7 @@ globalThis.VITE_DMS_API = base;
 const {
   applyCompanyClassification,
   applyCompanyHonors,
-  HONOR_TAG_FIELD,
+  HONOR_NAME_FIELD,
   ANGLE_TO_FIELD,
   DMS_COLUMN_ENTERPRISE,
   DMS_COLUMN_HONOR,
@@ -77,7 +77,7 @@ const findHonors = () =>
   });
 const honorNames = (rows) =>
   rows
-    .map((r) => (typeof r[HONOR_TAG_FIELD] === 'string' && r[HONOR_TAG_FIELD] ? r[HONOR_TAG_FIELD] : r.title))
+    .map((r) => (typeof r[HONOR_NAME_FIELD] === 'string' && r[HONOR_NAME_FIELD] ? r[HONOR_NAME_FIELD] : r.title))
     .sort();
 
 const classifyWith = (categories) => ({
@@ -127,8 +127,8 @@ try {
 
   const honors1 = await findHonors();
   check('1886 行数 = 荣誉记录数(2)', honors1.length === 2, honors1.length);
-  const h1 = honors1.find((r) => r[HONOR_TAG_FIELD] === '高新技术企业') || {};
-  check('1886 title 与 c_tag_name 都写荣誉名', h1.title === '高新技术企业' && h1[HONOR_TAG_FIELD] === '高新技术企业', { title: h1.title, tag: h1[HONOR_TAG_FIELD] });
+  const h1 = honors1.find((r) => r[HONOR_NAME_FIELD] === '高新技术企业') || {};
+  check('1886 title 与 c_honor 都写荣誉名', h1.title === '高新技术企业' && h1[HONOR_NAME_FIELD] === '高新技术企业', { title: h1.title, tag: h1[HONOR_NAME_FIELD] });
   check('1886 级别/来源/机构/日期/证书号落位', h1.c_level === '国家级' && h1.c_source === '某认定公告' && h1.c_publish_office === '科技部' && h1.c_publish_date === '2025-01-02' && h1.c_certificate_code === 'CERT-1', h1);
   check('1886 归属正确', honors1.every((r) => r.c_credit_code === TEST_CREDIT_CODE && r.c_name === TEST_NAME));
 
@@ -177,7 +177,7 @@ try {
   const honors3 = await findHonors();
   check('1886 行数 = 新列表(2)', honors3.length === 2, honors3.length);
   check('1886 名字对齐(新增进、多余的删掉)', JSON.stringify(honorNames(honors3)) === JSON.stringify(['科技小巨人', '高新技术企业']), honorNames(honors3));
-  const h3 = honors3.find((r) => r[HONOR_TAG_FIELD] === '高新技术企业') || {};
+  const h3 = honors3.find((r) => r[HONOR_NAME_FIELD] === '高新技术企业') || {};
   check('1886 同键行的字段差异已更新', h3.c_publish_office === '工信部', h3.c_publish_office);
 
   /* ---------------------------------------------------------------- *

+ 5 - 5
harness/tools/verify-company-classify-e2e.mjs

@@ -43,7 +43,7 @@ const {
   extractIdentityFromCompanyInfo,
   parseHonorRecords,
   readHonorsFromCompanyInfo,
-  HONOR_TAG_FIELD,
+  HONOR_NAME_FIELD,
   ANGLE_TO_FIELD,
   DMS_COLUMN_ENTERPRISE,
   DMS_COLUMN_HONOR,
@@ -166,9 +166,9 @@ check("1886 行数 = 载荷荣誉条数", honors.length === honorParse.records.l
   records: honorParse.records.length,
 });
 check(
-  "每行都有荣誉名(title 与 c_tag_name)",
-  honors.every((r) => typeof r[HONOR_TAG_FIELD] === "string" && r[HONOR_TAG_FIELD] && r.title === r[HONOR_TAG_FIELD]),
-  honors.slice(0, 3).map((r) => ({ title: r.title, tag: r[HONOR_TAG_FIELD] }))
+  "每行都有荣誉名(title 与 c_honor)",
+  honors.every((r) => typeof r[HONOR_NAME_FIELD] === "string" && r[HONOR_NAME_FIELD] && r.title === r[HONOR_NAME_FIELD]),
+  honors.slice(0, 3).map((r) => ({ title: r.title, tag: r[HONOR_NAME_FIELD] }))
 );
 check(
   "级别/来源已写入",
@@ -179,7 +179,7 @@ const sampleRecord = honorParse.records[0];
 check(
   "抽样比对:载荷第一条能在库里按「荣誉名+级别+来源」找到",
   !sampleRecord ||
-    honors.some((r) => r[HONOR_TAG_FIELD] === sampleRecord.name && r.c_level === sampleRecord.level && r.c_source === sampleRecord.source),
+    honors.some((r) => r[HONOR_NAME_FIELD] === sampleRecord.name && r.c_level === sampleRecord.level && r.c_source === sampleRecord.source),
   sampleRecord
 );
 

+ 9 - 7
harness/tools/verify-company-classify-sync.mjs

@@ -7,7 +7,8 @@
  *   ③ 1888 diff:无变化 → null(零写入)、`"[]"` 与 `[]` 不产生假差异、期望为空不清存量
  *   ④ 1886 荣誉对齐:解析 `honors.records`、按「荣誉名+级别+来源」全量对齐
  *      (补新增 / 更新差异 / 删多余)、`complete !== true` 时**不许删**、
- *      荣誉名回退顺序(c_tag_name → title,字符串 "null" 不算名字)
+ *      荣誉名回退顺序(c_honor → c_tag_name → title,字符串 "null" 不算名字)——
+ *      c_honor 是 2026-09-18 晚用户新建的**荣誉名称**列;c_tag_name 只作读取兼容
  *   ⑤ partial / errors 不可解析时的降级语义
  *   ⑥ 分类接口地址拼接(VITE_CHAT_API 的两种写法)
  *
@@ -17,7 +18,7 @@
 
 import {
   ANGLE_TO_FIELD,
-  HONOR_TAG_FIELD,
+  HONOR_NAME_FIELD,
   buildEnterpriseRow,
   buildHonorKey,
   buildHonorRowContent,
@@ -188,7 +189,7 @@ console.log('\n【4】1886 荣誉对齐');
   const seededRow = (id, title, level, source, extra = {}) => ({ id, title, c_level: level, c_source: source, ...extra });
   /** 已对齐过的行:title 与 c_tag_name 都有 */
   const alignedRow = (id, title, level, source, extra = {}) =>
-    seededRow(id, title, level, source, { [HONOR_TAG_FIELD]: title, ...extra });
+    seededRow(id, title, level, source, { [HONOR_NAME_FIELD]: title, ...extra });
 
   const rows = [
     alignedRow('u1', '高新技术企业', '国家级', '某认定公告', { c_publish_office: '科技部', c_publish_date: '2025-01-02', c_certificate_code: 'CERT-1' }),
@@ -210,19 +211,20 @@ console.log('\n【4】1886 荣誉对齐');
     [records[0]],
     { allowDelete: true }
   );
-  check('灌入行首次对齐:只补 c_tag_name,不动其它列', seedOnly.toUpdate.length === 1 && JSON.stringify(Object.keys(seedOnly.toUpdate[0].patch)) === JSON.stringify([HONOR_TAG_FIELD]), seedOnly.toUpdate[0]?.patch);
+  check('灌入行首次对齐:只补 c_honor,不动其它列', seedOnly.toUpdate.length === 1 && JSON.stringify(Object.keys(seedOnly.toUpdate[0].patch)) === JSON.stringify([HONOR_NAME_FIELD]), seedOnly.toUpdate[0]?.patch);
 
   // 差异 → update(只 patch 有差异的列)
   const staleRow = [seededRow('u1', '高新技术企业', '国家级', '某认定公告', { c_publish_office: '旧机构' })];
   const upd = diffHonorRows(staleRow, [records[0]], { allowDelete: true });
   check('同键字段有差异 → toUpdate,只含差异列', upd.toUpdate.length === 1 && upd.toUpdate[0].patch.c_publish_office === '科技部' && upd.toUpdate[0].patch.title === undefined, upd.toUpdate);
   check('title 与荣誉名一致时不重复 patch', upd.toUpdate[0].patch.title === undefined);
-  check('已匹配的行若缺 c_tag_name 也会补上', upd.toUpdate[0].patch[HONOR_TAG_FIELD] === '高新技术企业', upd.toUpdate[0].patch);
+  check('已匹配的行若缺 c_honor 也会补上', upd.toUpdate[0].patch[HONOR_NAME_FIELD] === '高新技术企业', upd.toUpdate[0].patch);
 
-  const titleNull = [seededRow('u9', 'null', '国家级', '某认定公告', { [HONOR_TAG_FIELD]: '高新技术企业' })];
+  const titleNull = [seededRow('u9', 'null', '国家级', '某认定公告', { c_tag_name: '高新技术企业' })];
   const fixTitle = diffHonorRows(titleNull, [records[0]], { allowDelete: true });
   check('title 是字符串 "null" → 用 c_tag_name 认名字,并把 title 修正', fixTitle.toUpdate.length === 1 && fixTitle.toUpdate[0].patch.title === '高新技术企业', fixTitle);
   check('字符串 "null" 不被当成荣誉名(键能对上)', fixTitle.toAdd.length === 0);
+  check('早期同步的行(只有 c_tag_name)也会补上 c_honor', fixTitle.toUpdate[0].patch[HONOR_NAME_FIELD] === '高新技术企业', fixTitle.toUpdate[0].patch);
 
   // 新增
   const empty = diffHonorRows([], records, { allowDelete: true });
@@ -239,7 +241,7 @@ console.log('\n【4】1886 荣誉对齐');
 
   // 内容
   const content = buildHonorRowContent('CODE-X', '合成公司', records[0], '2026-09-18 18:00:00');
-  check('荣誉行内容:title 与 c_tag_name 都写荣誉名', content.title === '高新技术企业' && content[HONOR_TAG_FIELD] === '高新技术企业', content);
+  check('荣誉行内容:title 与 c_honor 都写荣誉名', content.title === '高新技术企业' && content[HONOR_NAME_FIELD] === '高新技术企业', content);
   check('荣誉行内容:级别/来源/机构/日期/证书编号各就各位', content.c_level === '国家级' && content.c_source === '某认定公告' && content.c_publish_office === '科技部' && content.c_publish_date === '2025-01-02' && content.c_certificate_code === 'CERT-1', content);
   check('荣誉行内容:必填与归属', content.c_credit_code === 'CODE-X' && content.c_name === '合成公司' && content.c_id === 0, content);
   check('空字段不写进行(不落空值)', !('c_dead_line' in content) && !('c_beging_date' in content), content);

+ 22 - 6
src/network/api/dms/classification-sync-utils.ts

@@ -43,7 +43,18 @@ export const ANGLE_TO_FIELD: Record<ClassifyAngle, string> = {
   许可认证: "c_tag_license",
 };
 
-/** 1886 里存荣誉名的自有列(DMS 侧 2026-09-18 新增,别名「荣誉」);系统字段 `title` 同时写 */
+/**
+ * 1886 里存**荣誉名称**的列(DMS 侧 2026-09-18 18:17 新增,别名「荣誉名称」)。
+ * 系统字段 `title` 同时写(DMS 列表靠它显示,且灌入行本来就有)。
+ */
+export const HONOR_NAME_FIELD = "c_honor";
+
+/**
+ * 1886 的「荣誉标签」列(同日先建的 `c_tag_name`)。
+ *
+ * ⚠️ 2026-09-18 晚些时候用户新建了 `c_honor` 专存荣誉名称,**这一列不再当名字用**;
+ * 保留只为读取兼容:早先同步写进去的值仍在,读名字时作为中间回退项。
+ */
 export const HONOR_TAG_FIELD = "c_tag_name";
 
 /** 从 company_info 里抽出来的身份三字段(都可能是 null) */
@@ -312,8 +323,13 @@ export const HONOR_FIELD_MAP: Record<keyof Omit<CompanyHonorRecord, "name">, str
   certificateCode: "c_certificate_code",
 };
 
-/** 读出荣誉行里的荣誉名:优先 `c_tag_name`,回退系统字段 `title`(灌入行写在 title) */
+/**
+ * 读出荣誉行里的荣誉名,回退链:`c_honor`(现在的正主)→ `c_tag_name`(早期同步写进去的)
+ * → 系统字段 `title`(DMS 侧灌入的行写在 title)。
+ */
 const readHonorName = (row: DmsRow | null | undefined): string | null => {
+  const honor = pickString(row?.[HONOR_NAME_FIELD]);
+  if (honor) return honor;
   const tag = pickString(row?.[HONOR_TAG_FIELD]);
   if (tag) return tag;
   const title = pickString(row?.title);
@@ -366,9 +382,9 @@ const diffHonorFields = (row: DmsRow, record: CompanyHonorRecord): DmsRow | null
     if (!next) continue; // 载荷没有的字段不写(不清存量)
     if (pickString(row?.[field]) !== next) patch[field] = next;
   }
-  // 荣誉名两处都要有:自有列 `c_tag_name` + 系统字段 `title`(DMS 列表靠后者显示)。
-  // 灌入的行只有 title、没有 c_tag_name —— 首次对齐时要把这一列补上
-  if (pickString(row?.[HONOR_TAG_FIELD]) !== record.name) patch[HONOR_TAG_FIELD] = record.name;
+  // 荣誉名两处都要有:自有列 `c_honor` + 系统字段 `title`(DMS 列表靠后者显示)。
+  // 灌入的行只有 title;早期同步的行写的是 c_tag_name —— 首次对齐时都要补上 `c_honor`
+  if (pickString(row?.[HONOR_NAME_FIELD]) !== record.name) patch[HONOR_NAME_FIELD] = record.name;
   if (pickString(row?.title) !== record.name) patch.title = record.name;
   return Object.keys(patch).length ? patch : null;
 };
@@ -387,7 +403,7 @@ export const buildHonorRowContent = (
     c_id: 0,
     c_credit_code: creditCode,
     c_created_at: createdAt,
-    [HONOR_TAG_FIELD]: record.name,
+    [HONOR_NAME_FIELD]: record.name,
     title: record.name,
   };
   for (const [key, field] of Object.entries(HONOR_FIELD_MAP)) {

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików