# 汇安融 trade_status=16「超时关闭」前端转译 —— 执行级验证台 ## 这台子回答哪一句话 > 上游把长时间未付的单关掉、`trade_status` 回来是 `"16"` —— 收银台、订单列表、 > 订单详情这三块屏幕上,会不会出现一个裸的「16」? 「源码里 `HAR_TRADE_STATUS_TEXT` 加了 `"16": "超时关闭"`」不是答案。这页有 16 条 断言,答案是**数出来的**:真模板渲染出的文本 + `el-tag` / `el-option` 每次渲染 记下的一对对读数。 ## 怎么跑 ```bash cd .claude/plan/2026-10-10-har-status16-harness node har-status16-harness.mjs ``` 不带参数、不联网、不碰库、不写任何东西。**只认最后那行 `SUMMARY`** —— 失败也 `exit 0`(与同目录各台一致),别拿 exit code 当判据。 ## 它怎么做到「真」 | 环节 | 真的还是假的 | |---|---| | `har.ts`(映射表) | **真**:本仓那份原样转译求值,`HAR_TRADE_STATUS_TEXT/TAG` 直接从它取出 | | 订单页 SFC | **真**:`@vue/compiler-sfc` 解析 + `compileScript` + 单独 `compileTemplate`,**真模板**挂载渲染 | | 收银台 SFC | **真**:`compileScript` 编译本仓那个 `index.vue`(只驱动绑定,模板不渲染) | | vue | **真**:`frontend/node_modules` 里那份 `vue`(`createRenderer` 挂载) | | 页面函数 | **真**:`loadOrderList()` 是本台真实调用;详情靠直接改 `detail`/`detailVisible` 再渲染 | | DOM | 假:`createRenderer` 接一套内存节点(`patchProp`/`insertStaticContent` 都要给它) | | `el-table` / `el-tag` / `el-option` / `el-dialog` | 假:会**转发插槽或记账**的桩(`el-table` 按 `data` 逐行把 `row` 塞进列插槽;`el-tag` 记 `{type,text}`;`el-option` 记 `{label,value}`;`el-dialog` 只在 `modelValue` 时渲染);其余 `el-*` 直通默认插槽 | | 接口 | 假:`HarAPI` 换成**记账**桩,`orderList` 喂夹具四行、`orderDetail` 喂 16 那行 | 「假」的那几样与要证的那句话无关:本台问的是**屏幕上写出什么字**,不是像素长什么样。 ## 16 条断言 | # | 读数 | 它钉的是什么 | |---|---|---| | A1 | 列表 16 行渲染出「超时关闭」 | 转译发生 | | A2 | 全页文本不含裸 `"16"` | 负向:没有任何一处漏转译 | | A3 | 正控:8 行仍「交易关闭」 | 老文案没被碰坏 | | A4 | 正控:表外值 `99` 仍**原样显示** | 「表外不猜」的兜底没被谁顺手删掉 | | A5 | `el-tag` 配对 `{info, 超时关闭}` | 颜色与文字都对 | | A5b | `TAG` 表里 `16` 显式给了 `"info"` | ★ 见坑②:这格在渲染层是隐形的,只能直接钉表值 | | A6 | 正控:1 行仍 `{success, 支付成功}` | 已知状态的 tag 没动 | | A7 | 筛选下拉出现 `{16 → 超时关闭}` | 下拉吃的是 TEXT 表(v-for),这条路也得亮 | | A8 | 正控:下拉里 8 仍「交易关闭」 | 下拉没被改坏 | | B1 | 详情弹层里的状态 tag 同款转译 | 列表与详情是模板里两处独立的映射点,测一处不代表另一处 | | B2 | 打开详情后全页文本仍不含裸 `"16"` | 弹层打开后复核负向 | | C1 | 收银台 `result`:16 → 超时关闭 / muted / detail 含「没有收到钱」 | 不再落进「表外状态原样显示」的兜底 | | C2 | 正控:1 → 收款成功 + `money` | | | C3 | 正控:8 → 交易关闭 | | | C4 | 正控:表外值 99 仍原样 | 兜底没被删(收银台这一侧) | | C5 | `apply(16)` 停轮询进 `done` | 现状行为回归:16 不在收银台 `PENDING` 里 | **A1/A2/A5/A5b/A7/B1/B2/C1 是「必须转译」侧,A3/A4/A6/A8/C2/C3/C4 是正控。** 只有转译侧的话,把映射表整个换成恒返回「超时关闭」也能全绿。 ## 两向变异(读数在 `logs/_t-status16-fe-mut*.txt` / `-green-final.txt`) | 变异 | 期望 | 实读 | |---|---|---| | M1:删掉 TEXT 表的 `"16"` 格 | 必须红 | `SUMMARY FAIL 断言=16 不符=6`:A1 A2 A5 A7 B1 B2 | | M2:删掉 TAG 表的 `"16"` 格 | 必须红 | `SUMMARY FAIL 断言=16 不符=1`:只 A5b(★ 见坑②) | | M3:删掉收银台 `RESULT` 的 `"16"` 格 | 必须红 | `SUMMARY FAIL 断言=16 不符=1`:只 C1 | | M4:无害改动(TEXT 表 0/1 两行调序) | 必须仍绿 | `SUMMARY PASS 断言=16 不符=0` | 四次变异还原后复量 sha256,与变异前逐字节一致: `har.ts` = `1c62324eb7a2…`,`cashier/index.vue` = `d63c00b9d4be…`。 实现前的红灯(`_t-status16-fe-red-1.txt`,当时台子 13 条断言)红在 A1 A2 A5 B1 B2 C1 —— 恰好是「没转译」那一撮。 ## 踩过的坑 1. **`bindingMetadata` 必须带。** `compileScript` 出来的 setup 结果带 `__isScriptSetup` 旗子,runtime 的 `hasSetupBinding` 见到它就**拒绝**走 `_ctx` 代理 ⇒ 模板里每个绑定都 undefined,槽函数一渲染就 `TypeError: _ctx.searchForm`。 把 `script.bindings` 传进 `compileTemplate` 的 `compilerOptions`,模板才编成 `$setup.xx` 直取。2026-10-10 实测踩过一次。 2. **TAG 那一格在渲染层是隐形的。** 两处 `el-tag` 都写成 `TAG[st] || 'info'` —— 「TAG 表缺格」与「给了 info」渲染成同一个样子。实测:只加渲染断言时 M2(删掉 这格)**全绿**(`_t-status16-fe-mut2-tag.txt`,SUMMARY PASS 断言=13)。所以 台子补了 A5b 直接钉表值 + A7/A8 下拉(下拉吃 TEXT 不吃 TAG)。设计**保留**这格: 显式表态 + 表风格一致,A5b 钉着它。 3. **夹具纪律:非状态字段全都避开子串 `"16"`。** 行号用 A1~A4 后缀,否则 `order_no: "…16…"` 会污染 A2/B2 的负向断言 —— 负向断言只有在「其余字段都干净」 时才是有判别力的。 4. **`process.exit(0)` 不设 exitCode。** 判据是 `SUMMARY` 那行,不是退出码 —— 否则「台子崩了」与「断言不符」在 CI 里分不开。 ## 与另外两张表的关系 「16」这个值同一天写进了三个地方,各写一遍、各钉各的: | 位置 | 形态 | 钉它的量具 | |---|---|---| | 前端映射表 | `har.ts` TEXT/TAG + 收银台 RESULT | **本台** | | 后端分类 | `HarOrderStatus.TIMEOUT_CLOSED`(FINAL 成员,不进 PENDING/PAY_RECORDED) | `HarOrderStatusTest`(含两向变异 J1/J2) | | 下游文档 | `har-openapi.md` 10.3/10.4/10.5/§11 + 对接 docx | 词普查回读 | 值的传递原则:**本地不转义、逐字入库、逐字出网** —— 中文文案分别由前端转译与 下游文档各自承担,上游回了什么就存什么、发什么。