# 〖共创稿事节〗HarmonyOS 7 新特性实战(66):给术语立钉点门禁——「发现系/收录系」26 处逐文件登记,复查从一次性网变常设网

上一篇收尾留了一句话:门禁没盯的角落靠语境复查补位,「给它们补门禁之前,复查是唯一的网」。那 5 处术语漏网——2×2 桌面卡、护照印章格、馆长目标卡两处、探索路线文案——没有任何门禁报红,是当天逐组件人工扫出来的。人工复查有个天然缺陷:抓得到,守不住。它覆盖哪些组件取决于当天扫了哪里;下次有人加一张卡片、改一句文案,复查不会自动跟着跑。

本篇就把这句话兑现:给「发现系/收录系」立逐文件钉点门禁。写作当日落地,全量门禁从 52 项长到 53 项,复跑全绿;HAP 构建同日复跑通过。首跑没有抓到新红——这不是门禁没用,恰恰说明上一篇的人工清扫是干净的;但守未来靠的不再是当天的眼力,是此后每一轮改动都要撞一次的钉点。

术语钉点门禁全景:双语境判定、26 处登记与三层门禁

一、先复述判定标准:语境跟着数据来源走

钉点之前把判定标准写死。第三刀(65 篇)确立的口径是数据来源决定语境,不是词语本身:

语境判定标准典型落点例句
发现系discoveredIds / discovered 等进度状态驱动工具栏筛选、区域进度、桌面卡提示「已发现 17 只神兽」「还有 N 只待发现」
收录系描述馆藏体量与资料范围展厅导览、路线注记、缺位提示「这里收录 N 件展品」「详情页收录原文与注解」

判定标准本身是稳定的,不稳定的是执行。术语回归的特征和上一篇那处权限误删同型:编译不报错、运行不崩、构建绿、门禁绿——当时的 39 项门禁里没有一项管文案口径,只有用户看得见。所以这轮门禁要回答的问题不是「术语对不对」(那是判定标准的事),而是「哪里不许再变」。

二、钉点之前先分治:字面量与注释不是一个风险等级

把 65 篇的结论「每个词只在一个语境出现」直接翻译成计数门禁,会撞上两个坑。第一个坑:一刀切太紧,注释也要吵。首页内容组件里有 6 处「收录」,全部在注释里——是 10-26/10-27 两轮宽屏改造的布局沿革记录(已收录卡固定 262×68、维持 3.86:1 零拉伸之类)。注释是档案,每次重排布局都改注释,裸计数门禁会天天误报。第二个坑:一刀切太松,裁决依据会丢。「已收录」手风琴卡是唯一登记进字面量的收录系进度层级词,它旁边有一行判定注释(「已收录」= 进度卡里的「已发现」那一层)——这行注释是那个合法保留项的裁决依据,被人随手删掉就没人知道为什么留着。

所以门禁用双口径分治:total 数全部出现次数,inString 只数字符串字面量里的次数。字面量直接面向用户,必须逐处裁决;注释只做档案留痕,不拦语义:

const count = (text, term) => (text.match(new RegExp(term, 'g')) || []).length;
// 三个引号段的匹配顺序由正则引擎保证互不重叠,模板串跨行用 s 标志兜住
const stringSpans = text => text.matchAll(/'[^'\n]*'|"[^"\n]*"|`[^`]*`/gs);

判断「这处收录该不该存在」的活儿仍然在人:门禁只保证每次变化都被逼到判定标准面前过一遍。这正是上一篇按钮计数校准的教训换来的纪律——校准值不带出处,下次还是无头案。

三、钉点表:26 处逐文件登记,每条随行出处

钉点表按文件登记两系术语的全部落点,每条 why 字段写明语境出处:

// 每个文件的出现次数都是校准值——校准值不带出处,下次还是无头案。
// inString = 落在字符串字面量里的次数:字面量的收录取舍直接面向用户,必须逐处登记;
// 注释收录只做档案留痕,不拦语义。
const PINS = {
  'common/data/ShanhaiMockData.ets': { total: 7, inString: 7,
    why: '7 条路线注记:详情页收录原文与注解(馆藏范围)' },
  'common/ui/ShanhaiKnowledgeStructureCard.ets': { total: 3, inString: 1,
    why: '首页「已收录」手风琴卡:馆藏体量档位(Text 字面量 ×1 + 沿革注释 ×2)' },
  'feature/home/ShanhaiHomePageContent.ets': { total: 6, inString: 0,
    why: '注释 ×6:已收录卡布局沿革(宽屏改造记录)' },
  'feature/world/ShanhaiExploreWorldPanels.ets': { total: 1, inString: 1,
    why: '该区域暂未收录这一类线索(区域内容覆盖)' },
  // ……共 11 个文件,完整清单见下表
};

11 个文件 26 处的全量登记:

文件处数字面量出处(语境)
common/data/ShanhaiMockData.ets777 条路线注记「详情页收录原文与注解」(馆藏范围)
feature/home/ShanhaiHomePageContent.ets60注释:已收录卡布局沿革(宽屏改造记录)
common/ui/ShanhaiKnowledgeStructureCard.ets31「已收录」手风琴卡 Text ×1 + 判定/沿革注释 ×2
common/data/ShanhaiRichContent.ets22策展话题语料:后世典籍收录叙事(资料正文)
feature/beast/ShanhaiBeastDetailReadingCards.ets22原文定位/摘录缺位提示 ×2(资料缺位)
common/data/ShanhaiContentSources.ets10注释:原文摘录缺位分支说明
common/data/ShanhaiCrossEntryArticles.ets11展厅正文语料:《云笈七签》收录的后世黄帝叙事
common/data/ShanhaiCuratorGuideData.ets11展厅导览语料「这里收录 N 件展品」(展厅体量)
feature/curator/ShanhaiCuratorSourceRelations.ets11「相关原文已收录」(馆藏结果)
common/ui/ShanhaiThemeModeSheet.ets10注释:与已收录展开卡比例对照
feature/world/ShanhaiExploreWorldPanels.ets11「该区域暂未收录这一类线索」(区域内容覆盖)

钉点层的对账逻辑很短:

for (const [file, pin] of Object.entries(PINS)) {
  const text = readFileSync(new URL(file, root), 'utf8');
  const total = count(text, '收录');
  const inString = [...stringSpans(text)].reduce((n, span) => n + count(span[0], '收录'), 0);
  assert.equal(total, pin.total, `${file} 收录出现 ${total} 次 ≠ 钉点 ${pin.total}:${pin.why} —— 改动后先按语境判定(发现系/收录系),再带着出处回填钉点`);
  assert.equal(inString, pin.inString, `${file} 字面量收录 ${inString} 处 ≠ 钉点 ${pin.inString}:${pin.why} —— 新增面向用户的「收录」必须先过语境判定`);
}

报错文案里把该走的流程写全:先判定、再带出处回填。门禁的价值不在拦住,在拦住时把上下文递到眼前——why 字段让下一次对账不用考古。

四、覆盖层与正向钉点:新落点与旧落点都要有人盯着

钉点表只管登记过的文件,还有两个方向没人管:新文件混入与旧落点回退。覆盖层补第一个方向——未登记的文件出现「收录」即红,逼着先归类再登记;禁用形态补第二层保险——「待收录」「收录进度」这两组进度语境组合是事故形态,无论钉点表怎么改都不许在全树出现:

// 覆盖层:未登记文件不许出现「收录」;禁用形态全树扫描(含钉点文件)。
for (const file of files) {
  const key = rel(file);
  const text = readFileSync(file, 'utf8');
  if (!PINS[key]) {
    const hits = count(text, '收录');
    assert.equal(hits, 0, `${key} 新出现 ${hits} 处「收录」:先按判定标准归类——进度状态驱动改「发现」;馆藏体量/资料范围合法,则登记进 PINS 并带出处`);
  }
  for (const { pattern, why } of FORBIDDEN) {
    assert.doesNotMatch(text, pattern, `${key} 命中禁用形态「${pattern.source}」:${why}`);
  }
}

正向钉点管回退方向。发现系的 6 个落点逐一断言存在——有人手滑把某处改回旧词,正向断言比肉眼先知道:

const archive = readFileSync(new URL('feature/beast/BeastArchiveViewModel.ets', root), 'utf8');
assert.match(archive, /BEAST_ARCHIVE_FILTER_DISCOVERED: string = '已发现'/);
assert.match(archive, /BEAST_ARCHIVE_FILTER_UNDISCOVERED: string = '未发现'/);
const toolbar = readFileSync(new URL('common/ui/ShanhaiArchiveToolbar.ets', root), 'utf8');
assert.match(toolbar, /Text\('已发现'\)/);
assert.match(toolbar, /accessibilityText\('已发现' \+ this\.discoveredCount/);

工具栏那条把 accessibilityText 也钉住了:无障碍文本和可见文案是同一句术语口径,可见文案改了、读屏文案没跟,是术语回归里最隐蔽的一种——用户看得见的地方尚且会漏,读屏用户听到的更没人盯。最后一条断言钉的是「裁决依据必须还在」:已收录手风琴卡的判定注释(「已收录」= 进度卡里的「已发现」那一层)一旦被清理注释的顺手一刀删掉,那个合法保留项就变成了无头案。

发现系落点取证:图鉴工具栏「已发现」筛选与无障碍口径

发现系落点取证:探索页区域进度「已发现 x / y」与待发现计数

五、写作当日落地与复跑:52 → 53 项,全绿

落地顺序是先探测后钉点:全树扫一遍两系分布(发现系 106 处、收录系 26 处),对收录系 26 处逐处判定语境、写出处,形成钉点表;发现系只做正向钉点不钉计数——资料正文语料里合法地含有「发现」二字(典籍叙事原文),按文件钉死计数会把语料修订也拦下,正向钉点只锁 UI 落点。

2026-11-10 复跑全量门禁:套件 52 → 53 项(新增 1 项),处置后 53/53 PASS;HAP 构建同日复跑 BUILD SUCCESSFUL。首跑即绿的解读要诚实:没有抓到新红,说明上一篇当日的人工清扫是干净的。但不能由此推出门禁是多余的——人工复查是一次性的网(65 篇靠它抓了 5 处漏网),钉点门禁是常设的网:此后任何一轮改动撞上 26 处钉点、6 处正向落点或 2 组禁用形态,都会当场报红并把判定标准递到眼前。

证据结果说明
全量门禁53/53 PASS52 → 53(新增 verify-terminology-context 一项),处置后全绿
钉点覆盖收录系 26 处 / 11 文件字面量 16 处逐处登记,注释 10 处档案留痕
正向钉点发现系 6 处图鉴过滤器 ×2、工具栏(可见+无障碍)、列表徽标、首页提示、图鉴副标题
禁用形态2 组全树扫描「待收录」「收录进度」零容忍,含钉点文件
HAP 构建BUILD SUCCESSFUL in 14 s33 tasks:15 executed,18 up-to-date;当日复跑取新鲜口径

门禁三层结构:钉点层对账、覆盖层拦新、正向钉点防回退

收个尾

  • 判定标准进代码,语境才不会漂移。 「数据来源决定语境」写在文章里只是一段话,写进门禁才是常设的合同;每次变化都被逼到判定标准面前过一遍。
  • 字面量与注释分治,一刀切两头漏。 字面量直接面向用户,逐处登记;注释是档案留痕,只数总数。双口径让宽屏改造的沿革注释不再制造误报,也让裁决依据丢了会报红。
  • 校准值必须随行出处。 why 字段不是装饰——按钮计数 23→25 的无头案已经演示过一次「数字不带出处,下次还得考古」。
  • 覆盖层与正向钉点双向锁。 钉点表拦不住新文件混入,覆盖层补;钉点表拦不住旧落点回退,正向断言补;禁用形态是零容忍的事故形态,全树扫。
  • 首跑即绿不是门禁没用,是清扫干净了。 守未来靠的是此后每轮都要撞一次的钉点,不是当天的眼力;红在写作当天,比红在发布后便宜得多。

附:本篇证据清单

  • 门禁落地(2026-11-10):app-harmony/scripts/verify-terminology-context.mjs 新增,钉点层(11 文件 26 处对账)+ 覆盖层(未登记文件零出现、禁用形态全树扫描)+ 发现系正向钉点 6 处;全量套件复跑 53/53 PASS(52 → 53)。
  • 构建(2026-11-10):HAP 构建 BUILD SUCCESSFUL in 14 s(33 tasks:15 executed,18 up-to-date)。
  • 验收记录:acceptance/2026-10-16-terminology-shoulu-to-shifaxian/(图鉴工具栏「已发现」与探索页区域进度两图,发现系落点取证)。
  • 术语分布当日探测:全树发现系 106 处、收录系 26 处(11 文件),字面量 16 处;钉点清单见第三节表格。
  • 源码落点:scripts/verify-terminology-context.mjs(本篇唯一新增);钉点涉及的 11 个文件见第三节表格,均为既有文件的文案口径登记,无应用代码改动。
  • 关联稿件:术语语境判定标准与 5 处漏网的人工复查见系列第 65 篇;钉点维护与「实伤/过期」定性见第 64 篇;门禁校准值出处的教训(按钮计数 23→25)同见第 65 篇;无障碍口径的来历见第 48 篇(碰一碰分享的无障碍文本)。
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐