波波 2026-09-25 · 范围:A 文案字典(白标)/ B 藏汉双语 A端 可配 / C 平台参数页
验收口径:本地通过不算,staging 真调 + 真 Chrome 渲染才算。
| 项 | 交付 | 验收证据 |
|---|---|---|
| A 文案字典 | TextDictionary.tsx + 3069 键目录(esbuild 真打包,零改 C端) | 本地真渲染搜索/编辑/孤儿项全通 |
| B 藏汉双语 | 后端 4 键 + 095 迁移 + B端 i18n 运行时 + A端 4 开关 | staging 全链路 PUT→库→B端 GET 实测;双闸生效 |
| C 平台参数页 | 39 可配 / 16 只读 / 服务端脱敏 | staging 真数据 + 假私钥脱敏实测 |
| 门禁 | 四路→六路接线门禁 + 词典门禁 + 3 个反证脚本(23 错例) | 全部反证命中 |
已推送:2901674e(主体)、f1c1d591(A端 渲染验收)。staging 已自动部署,smoke 26/26 全绿。
未部署:B端 与 A端 的 dist 均未上 prod(prod 前端需人工部署,等你令)。
通道选择(本轮最关键的判断):C端 v2 实际消费的是 content_items,不是 site_texts。
链路:miniprogram-v2/app.ts:17 → loadSiteTexts() → GET /api/miniapp/content/items
→ 过滤 texts.<page>.<key> → applySiteTexts() → pageTexts() 合并。
site_texts(064 迁移、WIP-289)是 B端 SiteTexts.tsx 用的另一套,C端 不读。
⇒ 走 content_items 才能"改完立刻生效",不是半拉子工程。
目录怎么来的:admin-console/scripts/extract-texts-catalog.mjs
用 esbuild 真打包 C端 utils/texts.ts 并调 pageTexts() 取运行时真值(该常量没 export,
正则提取会切碎含冒号/引号/换行的条目)。产出 64 页 / 3069 键,与源码逐条比对 0 偏差。
页面设计:核心是「按内容搜索」(搜"神山秘境"揪出所有品牌名文案),
刻意不做批量自动替换——批量替换会把禁词/术语/品牌名一起误伤。
孤儿改写(库里有、目录里已无此键)不会静默丢弃,标注「C 端代码已无此默认值」。
架构:A端 配 → system_settings 存 → B端 读,运营改完无需发版。
backend/app/routers/admin_platform_params.py:服务端白名单 + 脱敏 (settings.py 是无过滤全表返回,会把 wx_pay_private_key 明文给前端,
我不做它的第一个调用方;加固属渔佳新媒体域,已单独上报未代改)
sql/095_b_locale_config.sql:4 键入库,ON CONFLICT DO NOTHING(不覆盖运营已改的值)merchant_console.py: GET /locale-config(公开,登录页要用;只返回 4 个开关,无泄漏面)b-workbench/src/i18n/{dict,locale}.ts:147 词条,bo 全空(藏文不编造)三条铁律:① bo 空一律回落 zh(绝不返回空串)② 拉取失败不阻断首屏(弱网)
③ showTibetan = bilingualEnabled AND boReady 双闸,前端不自行判断。
翻译进度不靠人填:extract-locale-progress.mjs 真打包 dict.ts 算出
147 条 / 已译 0 / 0%,A端 语言配置页直接显示,并在「进度未满却开了 boReady」时硬告警。
staging 实测返回:可配置 39 · 只读 16 · 脱敏 0 · 未登记 0 · 库内 55 行,
分组 = 福钥 14 / 分销 10 / 订单 6 / 平台 5 / 语言 4。
脱敏实测(安全关键路径,不靠推理):在 staging 插入假值
wx_pay_private_key = FAKE-SECRET-DO-NOT-LEAK → 端点返回
redacted: ['wx_pay_private_key'],响应体里搜不到明文(键名如实报出,值不出库)。
服务端强校验实测:b_locale_default=zh-CN → 422「只能是 zh / bo / both」;
b_locale_bo_ready=maybe → 422「只能是 true / false」。资金/语言口径不能只靠前端。
prod 安全现状复核:5 个敏感键(wx_pay_private_key/apiv3_key/serial_no/mchid/weather_api_key)
在 prod 确实存在且值为空串(长度 0) ⇒ 是隐患不是现行泄漏,与上一轮表述一致。
staging 这 5 键根本不存在,所以 redacted:0 是如实报出,不是漏报。
/retail-products(已删)真 Chrome 渲染实测:侧边栏只有 38 项,缺 3 项。查证后发现
/retail-products 在 App.tsx 无路由、pages/ 下也无组件,
商户点了被 <Route path="*"> 兜底弹回工作台 —— tsc 与 vite build 全程一声不吭。
是我上一轮补菜单时加错的。商品管理实际由 /supplier-products(供货商品)承担,已删除。
旧门禁只做「包白名单 → 入口」的正向检查,查不出"菜单里有、路由里没有"。
新增⑥反向校验:菜单项必须「有路由」且「能被行业包 ∪ 基础包 ∪ DEFERRED 放行」。
判据收紧过程(重要):⑥一上来自报 16 项红,其中 14 项是误报——
PACKS 只写了 4 个业态漏了 scenic/other,且没把基础包恒开(BASE_ROUTES)算进放行集合。
补齐后收敛到 2 项真红。
| 脚本 | 错例数 | 覆盖 |
|---|---|---|
scripts/revproof_pack_wiring.py | 8 | 门禁漏项/无路由/入口消失/三处全无/详情页跳转被删/幽灵菜单/DEFERRED 被摘/模板串归一 |
scripts/revproof_locale_dict.py | 8 | R1 元组少项 / R2 中文空 / R3 假翻译 / R4 key 重复 / R5 Layout 引用缺失 / R6 菜单无映射 / R0 判据静默失效 / 基线 |
scripts/revproof_locale_progress.py | 4 | 填 3 条→进度变 3、假翻译计入 4、R3 拦截、还原 |
词典门禁特意加了 R0:解析不出 PATH_KEY 块时报红而不是跳过——
跳过=判据静默失效,这是我自己踩过的坑(正则漏了 Record<...,string> 的 >)。
b-workbench/scripts/verify_locale_render.mjs:全通 ⇒ 38/38 菜单项仍为中文,不空白、不出现 undefined/NaN、不漏 nav.* key
admin-console/scripts/verify_admin_render_local.mjs:全通(两页零控制台错误)两次自我纠错留痕:
/#/xxx 被弹回 /dashboard,报错其实是 Dashboard 对我假数据的反应。
改成按真实用法搜索 ghost,找得到且标注正确。
| # | 事项 | 现状 | 建议 |
|---|---|---|---|
| 1 | /supplier-questionnaire | 有路由、在 retail 包,三处入口全无(真孤儿,只能 URL 直达)。语义属入驻前流程 | 我判其应归 C端,但 retail 包归我、C端归小齐 ⇒ 跨域不擅动。三选一:删页面 / 挂菜单 / 移出 B端 |
| 2 | /retail-orders 菜单文案「收银台」 | 易被误读成"平台收钱" | ✅ 已办(同日 56e8e34a):改「收款记录」,并连带把收银/结账口径在零售·餐饮·车队三业态统一为「登记收款/记录收款」;酒店客账「结账」按业务实际保留。详见下方 §追加 |
| 3 | prod 前端部署 | B端/A端 dist 都已构建通过,未上 prod | 等你令;prod 迁移需 095 + 人工 DEPLOY_WITH_MIGRATIONS=1 |
上一节把「收银台」列为待裁定第 2 条,义父批「改」,已办(commit 56e8e34a)。
| 场景 | 后端实际行为 | 文案 |
|---|---|---|
| 现场即时记账(零售 / 餐饮 / 车队) | settle 只写 pay_method + paid_fen + paid_at,登记即收款、无待支付中间态 | 收款记录 / 登记收款 / 记录收款 |
| 酒店客账(Folio / 交接班) | 押金、挂账、多笔收款、离店结清 | 保留「结账」(改了反而不准确) |
一刀切把酒店「结账」也改掉,属于为了口径统一牺牲准确性——这条用脚本
must_keep 判据锁住(HotelFolios.tsx 9 处、ShiftHandover.tsx 3 处必须仍在),
并在渲染层做了反向证明:打开 /folios 断言页面仍含「结账」,失败即说明改过头。
nav.retailOrders → 收款记录h2「零售订单」→「收款记录」(与入口一致;否则点「收款记录」进去看到「零售订单」)收银开单→登记收款、结账→记录收款、确认开单→确认登记待结账/已结账 → 待收款/已收款(零售 4 + 餐饮 1)今日已结账订单→今日已收款订单(零售 + 餐饮)、待结账→待收款b-workbench/scripts/verify_wording_local.mjs(新增,13 项全绿)打本地 dist + 拦截 API 造假数据 + 真 Chrome,不碰 prod。
为什么不复用 verify_retail_render.mjs:那个打线上 b.kailashi.cn 且要真商户 token,
本轮前端尚未部署 ⇒ 打本地才能现在就验,不必等发布。
断言覆盖:新词在位、旧词(收银台/收银开单/结账)清零、控制台零错误、
侧边栏可见入口、以及 /folios 仍含「结账」 的反向证明。
回归:verify_locale_render.mjs 全绿、locale-dict-test.mjs 29/29、接线/词典两门禁绿
(接线门禁仍剩 /supplier-questionnaire 真孤儿,跨域待裁定,未擅动)。
看截断输出就下"清单完整"结论,连漏 4 批。 第一批我用 head -40 / tail -60
看扫描结果,中间的 services/retail.ts、FleetRides.tsx、ProductEdit.tsx、
RetailDashboard.tsx 根本没进视野 ⇒ 以为 12 处就完事,实际 30 处。
全称结论必须跑全量不截断复扫,且复扫判据要同时包含「该清零」和「该保留」两类,
前者防漏改、后者防改过头。
de4fafd6)义父令「继续」⇒ 把已交付的东西走完最后一公里:真实环境验收。
B端 前端部署在本人全权范围内,已执行:
| 步骤 | 结果 |
|---|---|
| 备份原目录 | /srv/kailash/nginx/html/bworkbench.bak.20260925102429(可整目录回滚) |
| 打包上传 | COPYFILE_DISABLE=1 tar czf(禁 rsync,避中文名乱码) |
| 解包 | ._* 残留 0 |
| 同步根级 assets | index.html 引用的是绝对路径 /assets/,只解包到 bworkbench/assets/ 会白屏 —— 必须再 cp 到 html/assets/ |
| reload | nginx -t 通过后 -s reload,js/css/首页三个资源均 200 |
b-workbench/scripts/verify_prod_render.mjs(新增,全绿)为什么必须有这一层:本地 dist + 假数据只证明「前端没写错」,中间还隔着
nginx、资源 hash、后端真实数据三件事,任一出问题现象都是白屏而本地全绿。
本轮运气好,prod 上有真数据可验:商户 8001 开了 restaurant,库里
dining_orders 有 4 条真实订单(created/preparing/served/paid 四态齐全),
所以直接渲染出真实 UI,证据强于本地:
Tabs : 待处理 / 备餐中 / 已上菜 / 已收款 / 已取消
行按钮: 备餐 / 记录收款 / 取消
页面 : 无「结账」、无「收银」
/restaurant-dashboard、/workbench 同步验收,无 PAGEERROR,无词典 key 泄漏。
验收脚本只打印侧边栏前 12 项,我看到全是「住宿·房务」,一度以为 8001 的餐饮菜单
没显示、怀疑是第二个「桌面不可见」缺陷。补诊断脚本打印全部 27 项后发现餐饮四项
都在(🍽️餐饮概览 / 📖菜单管理 / 🪑桌台管理 / 🧾点餐订单,分组也有「餐饮」)。
是切片打印导致看漏,不是缺陷。 与上午「head/tail 漏 4 批」同源——
报缺陷前一定先看全量数据。
| # | 事项 | 为什么现在不做 |
|---|---|---|
| 1 | prod 执行 095 迁移(b_locale_* 四键,现状无) | 词典藏文 0% 未译 + 双闸 ⇒ 开了也显示中文,对用户零差别;且 A端 未部署,单跑迁移就是半成品。建议真要上藏文时一次性做完 |
| 2 | A端 dist 部署到 /srv/kailash/nginx/html/adminconsole/ | 权限归属不明确,未擅动 |
| 3 | prod retail 包零商户开通 | 目前只有 merchant 1(冈仁波齐城堡酒店,唯一真实)开 hotel;8001 开 hotel+restaurant;8021 开 vehicle ⇒ 零售 UI 暂无真实承载商户,只能靠本地 dist 验收覆盖 |
feaa58de)义父令「藏文可以先借用 AI 翻译,后期在 A端 人工修改」。
关键判断:只翻译、不建通道 = 改一个词要发一次版 = 等于不可维护。
所以两件事必须一起做,否则"后期可改"是句空话。
| 项 | 内容 |
|---|---|
| 翻译 | dict.ts 147/147 全部填入。UI 常用词多属标准对应;业务术语(夜审/核销/RevPAR/核载)为意译。未经藏族母语者校对 |
| 来源标注 | dict.ts 顶部显著写明「AI 初稿,信任等级:可看、不可当作最终稿」 |
| 双闸 | b_locale_bo_ready 仍默认 false ⇒ 不打开就不显示藏文 |
| 覆盖层 | A端 改 content_items 的 b_dict.<key> → GET /merchant-console/bo-dict → B端 同名覆盖,改词不用发版 |
覆盖层三条约定(与内置词典同款):空值不覆盖(否则运营清空 ⇒ 变空白)、
拉取失败不阻断首屏、形状异常静默退回内置。复用 content_items 不新增表。
"藏文不编造"这条铁律没作废,只是换了防线位置:从"bo 必须留空"
变成"来源如实标注 + 双闸把关 + 随时可改"。
-U yos, role 不是 kailash。locale 组出现,B端 公开端点 configured:true。
a.kailashi.cn。同样踩 /assets/ 绝对路径坑; 另有 16 个 AppleDouble ._* 残留需清理。
verify_admin_prod.mjs 全绿:platform-params 6835 字符、locale 组在位、脱敏区在位、进度卡在位;text-dictionary 3069 键目录无白屏。
api.ts 两组 listContentItems/deleteContentItem 并存 ⇒ tsc 报重复声明,
运行时后声明覆盖先声明,一直跑的是另一组。而我那组还漏了 API_BASE
(写成字面量 '/admin/content/items',正确应为 CONTENT 常量)——
错路径没被暴露,正因为它从未被调用。已删我这组、保留正确那组,
并给保留组补形状校验(防 .map 崩白屏)。
| 原判据 | 新判据 |
|---|---|
| bo 必须全空 / countTranslated()=0 / 进度 0% | 不许半套:要么全译要么全无(一半藏文一半空 ⇒ 双语界面半中文半藏文,比纯中文更糟) |
| 渲染:词典未译必须回落中文 | 拆两个方向:② 强开时真显示藏文(38/38、无中藏混排);②b 双闸关闭时必须回落中文(残留 0) |
🏠ལས་སྟེགས / 📋མངགས་ཉོ / 📢དགོས་མཁོའི་ཁང་ཆེན(工作台/订单/需求大厅)is_test 口径,工作量独立,本轮来不及。
/bo-dict(上次构建 03:08,在本改动之前)⇒ 覆盖层需后端下次构建才在 prod 生效;在此之前 B端 用内置译文正常工作,不报错。