2026-10-09建立。每一条规则都来自实测量化,不是凭空定的。
复现:python3 scripts/code_quality_scan.py
昨晚(10-08)一整晚的实测暴露了一件事:这个项目最贵的不是写代码,是漏。
| 实际发生的事 | 漏掉的代价 |
|---|---|
subpackages 少个字母,11 个页面 3.8 天打不开 | 无人察觉;小齐独立数页面数才发现 |
| 详情页编造文案「神山脚下在地工坊手作」上线 | 客人看到商品介绍是编的 |
| 情报包显示「1晚」+ 要求选入住日期 | 交付给客人的是错的信息 |
git fetch 当成 pull 用,服务器停在两天前 | 别人据此质疑了一个已修好的问题 |
| 「主包 0.925MB」是推算不是实测 | 没落地的东西报表上是绿的 |
⇒ 审查的第一目标不是"写得漂亮",是"拦住上面这类东西"。
| 项 | 数量 |
|---|---|
| 后端 Python | 139 文件 / 44,206 行 |
| C 端 TypeScript | 96 文件 / 10,710 行 |
| 超 1000 行的文件 | 10 个(最大 miniapp_orders.py 2035 行) |
| 后端测试文件 | 6 个 |
| C 端测试文件 | 0 个 🔴 |
| CI 配置 | 无 🔴 |
| 已有门禁脚本 | 20+ 个(scripts/*.py),但没接进任何自动流程 |
| git hook | .githooks/pre-commit 存在,但 core.hooksPath 未设置 ⇒ 不生效 |
| # | 规则 | 为什么要这条(对应昨晚/实测的真实教训) |
|---|---|---|
| B1 | SQL 禁止拼接用户可控值,必须用 $1 占位符 | 实测 91 处 f-string 拼 SQL(demand.py、guides.py、admin_goods.py…)。多数 where_sql 是代码自己拼的片段(值走参数化),但必须逐个判"拼接内容是否可控",不能一概放过 |
| B2 | 任何占位/兜底文案不得编造业务事实 | 「神山脚下在地工坊手作」是上线前占位、上线忘清⇒ 客人看到假信息。宁可少一块,不给假信息 |
| B3 | 未接通的功能不许摆可点入口 | 巡检抓到 3 处(语音导览「测试」按钮、护照「继续领取」、评价提交)⇒ 摆一个点不动的入口比不做更糟 |
| B4 | 配置键名/字段名必须机器校验 | subpackages vs subPackages:JSON 合法、编译不报错、文件全在,只有脚本能发现 |
| B5 | 交付前必须真机/真服务验证,不能只看代码 | 「真机验证通过」与「线上生效」是两件事——C端缺陷只有上传小程序才生效 |
| B6 | 金额必须用整数分计算,禁止浮点 | 微信支付踩过:0.1 + 0.2 类问题 |
| B7 | 禁止裸 except: / except: pass | 静默吞错让故障无法定位 |
| # | 规则 | 依据 |
|---|---|---|
| H1 | 改动必须真机/真接口验证并附证据(截图/响应/日志) | 昨晚 5 处漏改,全是"改完就以为好了" |
| H2 | 改一处要扫全部(同类型页面/接口) | 修 4 个详情页时顺手造了 2 处新问题 |
| H3 | 指标必须标注实测 vs 推算 | 「主包 0.925MB」是推算 |
| H4 | 查历史必须用 git log --first-parent | 混入分支提交会得到错误时间线 |
| H5 | 查远端状态必须用 ls-remote/fetch,不能用本地缓存的 origin/xxx | 我差点报"远端没C 端"(实际有456 个文件) |
| H6 | 单文件 >800 行要拆分 | 10 个文件超 1000 行,最大 2035 行 |
| H7 | 删除分支前必须验「已推送 + 已并入主线」 | 7 个未推送分支里3 个内容仅存本地 |
as any(实测256 处)except ValueError 而非 except Exception)console.log(2 处)与 TODO/FIXME(5 处)命名、注释、文件组织等风格问题——只要不误导人,不必在审查里争论。
① 提交前(本地,pre-commit)── 机器秒级拦截
↓
② 合并前(PR/复核)──── 人 + 机器报告
↓
③ 部署后(线上抽验)──── 真机/真服务
先决条件:git config core.hooksPath .githooks(现在没设置,等于没生效)
# ① 类型/语法(注意:node --check 对 .ts 无效,必须用 esbuild)
npx esbuild@0.24.0 <改动的.ts> --outfile=/dev/null
python3 -m py_compile <改动的.py>
# ② 质量扫描(只扫改动的文件,秒级)
python3 scripts/code_quality_scan.py --changed
# ③ app.json 页面注册(这个坑太隐蔽,每次必查)
python3 scripts/check_app_json_pages.py
规则:BLOCKER 一项不过 ⇒ 不许提交(不是"提醒",是拦住)。
复核者要做的三件事(顺序不能乱):
code_quality_scan.py + check_app_json_pages.py复核者必须做「反证」:
| 目标 | 手段 |
|---|---|
| 后端 | 部署后读容器内文件指纹/特征码(不是看仓库 HEAD) |
| C 端 | 只有上传小程序才生效;「真机验证通过 ≠ 线上生效」 |
| 页面数/配置 | 真机逐页打开 + 拼图对比(sweep_all_pages.py + make_contact_sheet.py) |
提交信息说"已修复"不算数。但也不能假设对方错 —— 先确认你看的是哪一份:
昨晚小齐看到「29 页、无分包」,我看到「18 + 11」。
真相:我 22:21 只fetch没pull,服务器停在两天前。两个人都没错,是信息不同步。
义父:「你只是习惯在既定的范围内查问题」——
我只查 4 个"详情页",于是 11 个分包页从未被打开过。
⇒ 用客观清单驱动(app.json 全文、目录遍历),不靠"我以为该看哪里"。
「没落地的东西,报表上是绿的。」——"主包 0.925MB"是按预期结构算的,分包当时根本没生效。
任何自动检查,先证明它自己是对的,再信它的结论。
# scripts/code_quality_scan.py 里已内置
'selfcheck': ('f"SELECT * FROM t WHERE id={x}"', True), # 必须抓到
'selfcheck_neg': ('sql = "SELECT * FROM t WHERE id=$1"', False), # 必须不抓
实测战绩:这条机制在建立当天就抓到我自己写的 except 规则有 bug(多行样本配单行正则,恒不命中)。若没这关,它会报「0 处裸 except」——一个假绿灯。
⇒ 规则:
-test 模式| 阶段 | 动作 | 收益 |
|---|---|---|
| 第1 周 | ① 接上 core.hooksPath ② code_quality_scan.py --changed 进 pre-commit ③ check_app_json_pages.py 进 pre-commit | 拦住 B4 类静默故障(昨晚那 3.8 天) |
| 第 2 周 | ④ 91 处 f-string SQL 逐个判「拼接内容是否可控」,可控的改参数化 | 关闭 B1 |
| 第 3-4 周 | ⑤ 补 C 端测试(当前 0 个)⑥ 超 1000 行的 10 个文件各抽 1 处拆分 | 降低 H6 |
| 持续 | ⑦ 每次线上事故回头补一条规则(昨晚已补 7 条) | 规则来自真实教训,不是想象 |
🔴 BLOCKER: SQL f-string 拼接 94 处(其中 91 处需人工判可控性)
🟠 HIGH: SQL + 拼接 5 处
🟡 MEDIUM: as any 256 处
💭 NIT: console.log 2 / TODO 5
✅ 裸 except 0 / except-pass 0 / @ts-ignore 0 / 非空断言 0
C 端测试 0 个 · CI 无 · pre-commit 未生效
⇒ 这三条是当前最大的结构性缺口。