剩余维度审查 · 2026-10-09 15:15-15:40

一次性查完之前标「🔴 完全未查」的四块。过程中我误判了 3 次,都记录在案。

一、结论速览

维度规模结论
wxss 硬编码色值8,984 行 / 126 色值 / 1,883 次🟡 视觉一致性问题,不阻塞
wxss 小字号(<22rpx)218 处🟡 偏小(≈11px),需产品判断
normalize.ts 模板映射222 行✅ 库与代码 100% 对得上
后端状态机守卫13 处状态迁移 / 62 条白名单✅ 非法迁移全挡住

🔴 本轮没查出新的功能性缺陷。 前几轮查出并已修:超卖 / 分包 / 死文案 / 过期栏目名。


二、wxss:Token 机制健全,硬编码是存量

关键澄清(差点冤枉它)

初扫「126 个色值、1,883 次硬编码」看起来很糟,但先查了 token 机制是否存在:

var(--xxx) 使用次数 : 2112 处
token 定义文件      : styles/foundation-tokens.wxss / vi-tokens.wxss / tenant-theme.wxss
app.wxss @import    : 5 个 token 文件,层叠顺序有明确契约注释

🔴 ⇒ 不是「没有设计系统」,而是「存量代码绕过了 token」。

这属于视觉一致性问题,不影响功能正确性,不阻塞上线。

建议:分批收口(每个页面一次),不要一次性全改 —— 那是纯样式重构,风险收益比不划算。

小字号 218 处

.cs-count { font-size: 20rpx; }   /* 750rpx = 375px ⇒ 20rpx ≈ 10px */
.m-cat    { font-size: 20rpx; }

⚠️ 按小程序标准换算,20rpx ≈ 10px,偏小。但这是设计决策,不是缺陷 ——

若目标客群含中老年,建议提产品确认。我不擅自改。


三、✅ normalize.ts:库与代码完全对得上(这里我误判了两次)

渲染链路

A 端 content_items (KV: key/value)
  ↓ modules[].type
normalize.ts resolveModule()
  ↓ tpl: 'textBlock' / 'entriesBlock' / ...
wxml <template is="{{item.tpl}}">

🔴 风险点:若 tpl 指向未定义的 template ⇒ 该板块静默不渲染,不报错。

这与 normalize.ts 注释里记的历史踩坑完全一致:

2026-10-07 真机日志「跳过未登记模块类型:textBlock」⇒ 22 篇故事一篇都渲染不出来

实测核对(只读,不改数据)

库里实际在用的 type(77 个 detail 配置,143 条 content_items):

库里的 type条数→ 最终 tplwxml 定义了?
notice64notice✅
textBlock59textBlock✅
gallery5gallery✅
leader5leader✅
reviews5reviewsBlock✅
cost_exclude5textBlock✅
itinerary5textBlock✅

⇒ 7/7 全部有对应 template,无静默丢失。

🔴 我在这里误判了两次(记录)

误判①:我用 tpl 字段去库里查,查到 0 条,以为配置结构不对。

⇒ 真因:content_items 的 JSON 用的是 modules[].type,不是 tpl。

误判②:我看到库里在用 cost_exclude / itinerary,而 wxml 的 12 个 template 里没有这两个,

判「🔴 5 个套餐的行程/费用不含板块静默不渲染」。

⇒ 回读代码才发现:normalize.ts L178-188 用 fallthrough 把这两个类型统一映射到 tpl: 'textBlock'

(已定义的模板)。我的比对逻辑错在「拿库里 type 直接当 tpl」,漏了中间的转换。

⚠️ 这也说明「库里 type 名」与「wxml template 名」本就是两套命名空间 ——

和昨晚发现的「幂等 scope 名 ≠ 函数名」同族。


四、✅ 后端状态机守卫:实证有效

覆盖率

文件改 orders.status 处数守卫
miniapp_workbench_redeem.py3✅
merchant_console.py2✅
miniapp_orders.py2✅
miniapp_pay.py1✅
hotel.py5🔴 无统一守卫

hotel.py 的 5 处:逐个查了,都有前置校验(不是漏洞)

L408  if b["status"] not in ("booked","confirmed") → 409「仅待接单/已确认可办理入住」
L428  if b["status"] != "checked_in"                → 409「仅已入住可办理退房」
L447  if b["status"] in ("checked_out","cancelled") → 409「已结束,不可取消」
L520  if b["status"] != "checked_in"                → 批量退房逐条校验+ 收集失败原因
L1971 if b["status"] in ("checked_out","cancelled") → 409

🔴 它们用「每处自己判前置状态」代替统一守卫,效果等价。

⚠️ 代价:状态规则散落 5 处,将来加状态时容易漏改一处。

建议:可接进 enforce_order_transition(住宿状态机),但不是紧急项。

守卫函数本身(生产库实证,只读)

057_order_state_machine.sql 设计:

实测非法迁移:

paid       → refunded     ✅ FALSE 拒绝
cancelled  → paid         ✅ FALSE 拒绝
completed  → refunding    ✅ FALSE 拒绝

⇒ 非法迁移全部挡住。

⚠️ 一处「后门」要知道:admin_override + role=admin 时,

任何状态迁移都放行(含 completed → booked)。这是设计意图(管理员纠错),

但意味着 admin 账号被盗 ⇒ 订单状态可任意篡改。

🔴 建议:admin_override 应留操作日志(是否已有待确认,我没查)。


五、🔴 本轮仍未覆盖(诚实标注)

未查为什么
🔴 C 端 18 个页面真机行为需真机/开发者工具,纯静态查不出渲染与交互
🟠 admin_override 是否有操作日志上面提到,建议补
🟠 hotel 住宿状态机是否该接入统一守卫非紧急
🟠 wxss 硬编码收口纯视觉重构,需设计参与
🟠 小字号是否要调需产品判断

六、我这轮的三次误判(集中记录)

#误判发现方式根因
1以为 wxss 没 token 机制查了 var(--) 使用次数与 import初扫数据难看到先查上下文
2以为库里 type 没有对应 template回读 normalize.ts L178-188漏了「type → tpl」的中间转换
3pending_payment→paid 判「误拒」查白名单表我测试传的 action 名不对(pay_success vs pay)

⇒ 三次都是「拿一个事实直接下结论,没先验证那个事实的前提」。

这已经是今晚第 N 次同类错误 —— 它不是偶发,是我的默认思维习惯。

⇒ 沉淀(这是今晚最后一条,也是最重要的):

在下结论前问一句:「我这个判断的前提,验证过吗?」

>

今晚的错几乎全是这样:
「4 个文件有 SQL 拼接」→ 没问是不是我改的
「幂等完全失效」→ 没问我的脚本是不是有 bug
「5 个套餐板块不渲染」→ 没问type 会不会被转换
「支付验签失败」→ 没问生产是否开了沙箱

>

每个"发现"都该配一个「前提自检」。 这比任何检查脚本都重要。