版本说明 · 2026-10-09 晚间(20:55 → 01:05)

基线:a74efa11
当前:7d98dc79
跨度:约 4 小时 · 20 个提交 · 33 个源文件

一句话概括

把A 端「运营找不到配置」和后端「一个慢请求就全线卡死」这两个问题解决了,

顺手把下单链路的幂等补上、测试数据清干净、埋点补上 IP/UA/session。


一、A 端(已上线 · https://doc.kailashi.cn/console/)

① 首页Dashboard —— 从无到有 ✅

项说明
改动新增 DashboardPage.tsx +菜单「首页 → 看板」放在第一位
内容6 张指标卡 · 时间范围切换(今日/7天/30 天)· 待办卡 · 埋点事件分类
数据源/api/admin/stats/overview(已有端点,未新增接口)

🔴 动因:实测发现 menu.ts 里「首页」组下只有「主视觉」一个子项

⇒ 运营打开 A 端看到的是改图文的表单,看不到任何「今天怎么样」的信息。

② 趋势图 ✅

项说明
改动新增 TrendCharts.tsx(纯 SVG)+ 后端新增 /api/admin/stats/trend
体积8.7 KB(引入 echarts 约 +300KB gzip,没引)
设计三序列独立缩放 —— cs_click 峰值195 / phone 7 / orders 0,差 200 倍,共用 Y 轴会把小的压成直线

③ 三态补齐 ✅

项说明
改动DataTablePage 加重试按钮 + 骨架屏;EmptyState 组件
空态覆盖19/19(改前 16/20)

🔴 口径:我第一版给底座加了 3 个新 props,隔一轮才发现项目早有 emptyHint ⇒ 已撤回,避免在既有设计上叠一层。

④ 菜单按C 端视角改名 ✅

10 个一级分组改成客人的词(「商品与交易」→「买什么」等)+ 悬停显示 C 端对照。

⚠️ 刻意没动叶子项:语音/知识库/场景卡对应 C 端哪里我并不确定,

归错类比不重排更糟(运营按新分类找、东西不在那儿更迷惑)。


二、后端(已上线 · rev=3d721c9d)

架构阶段一:稳定性 ✅

改动内容
多 worker--workers 4(原单进程)· --timeout-keep-alive 90 · --limit-concurrency 200
连接池PG_POOL_MIN/MAX + PG_COMMAND_TIMEOUT=15(原硬编码且无超时)
慢请求日志新增 SlowRequestMiddleware(>500ms 记,带 request_id)

连接预算公式(写进代码注释防将来扩容踩坑):

实例数 × worker 数 × POOL_MAX ≤ max_connections × 0.8 ⇒ 当前 1×4×10=40 ≤ 80 ✅

🔴 为什么先做多 worker:单进程单事件循环下,一个慢请求/一个死循环就全线卡死

—— 这不是"扛不住流量",是"一个点的问题扩散成全站不可用"。

幂等链路 ✅

层改动
C 端新增 utils/idempotency.ts,四处下单全部带上幂等键
后端新增 order_idempotency_guard.py,撞唯一约束时返回已建订单而非 500

🔴 查出的大问题:后端早就支持 idempotencyKey,但 C 端从未传过

⇒ 弱网重复点「立即预订」会创建两笔订单。

⚠️ 关键设计:键必须在意图产生时生成(onLoad 重置 +惰性生成),不是每次点击现生成。

埋点补采 ✅

列改前改后
ip_addr308 条全空✅ 采集(实测 101.249.218.212)
user_agent308 条全空✅ 采集
session_id308 条全空✅ C 端已补(待上传生效)

🔴 关键:request.client.host 在 nginx 反代下拿到的是 nginx 自己的地址

⇒ 必须读 X-Real-IP,其次取 XFF 第一段(不是整段)。


三、数据清理 ✅

表清理前清理后
orders130
order_items400
redemption_codes580
fulfillment_items320

备份:/srv/kailash/backups/orders-clean-20261009/(3 份 pg_dump)

⚠️ 你说是 12 笔,实际 13 笔(is_test=true 仅 3 笔,另 10 笔是 cancelled)。


四、两个「有意不修」的地方

###① 退款分摊「重复实现」不能合并(a0ad1ba6)

我原以为该抽成共用函数,实测发现两处 pending_fen 兜底刻意不同:

申请退款执行退款
无履约项时pending=total ⇒ 仍可申请pending=0 ⇒ 直接 422

⇒ 「申请宽、执行严」的有意设计。合并会抹平差异,

而下一个人很可能误当成 bug 去"修成一致" ⇒ 已加警示注释,仅注释改动 0 行逻辑。

② 118 迁移(cde94815)

它是一次性历史迁移:搬数据段要读的 12 个列,

正是它自己第 4 部分删掉的,而建表迁移 106 也不产生它们

⇒ 全新库必然跑不了。处理:文件头加警示 + staging 补台账记录。

⇒ deploy staging 从此不再失败(跳过 134 个迁移、health 200)。


五、🔴 事故记录:我把服务搞挂了 4 分钟

from .services.order_idempotency_guard import ...   ❌ 应为 ..services
ModuleNotFoundError: No module named 'app.routers.services'
⇒ 8 个 worker 全部启动即崩 · health 000

根因:py_compile 只做语法分析、不解析导入路径 ⇒ 它当时报 OK。

教训:加/改 import 后,必须真 import 一次才算验证过。

⚠️ 今晚同类错误犯了两次,另一次(from .db import)只会让定时任务全部 ImportError,

同样是 py_compile 放过的。


六、⚠️ 待你处理

① 上传小程序(唯一改了但用户享受不到的)

miniprogram-v3/utils/idempotency.ts          ← 下单幂等(防重复下单)
miniprogram-v3/utils/track-session.ts       ← 埋点会话
miniprogram-v3/utils/concierge.ts
miniprogram-v3/pages/product-detail/product-detail.ts
miniprogram-v3/pages/item-detail/item-detail.ts

我不碰上传(你的红线)。

② 我造成的一个问题:构建产物进了git

.gitignore 只排了 dist/,但我今晚构建用的目录叫

dist-1009-2306 / dist-old-231749 ⇒ 不匹配 ⇒ 733 个构建产物被跟踪。

影响:仓库膨胀(单个 dist 目录 2.2MB),且每次构建都会再进来一份。

修复:git rm -r --cached + .gitignore 加 dist-* / dist-old-*。

要我做吗?


七、回滚

想撤什么怎么做
后端全部git revert 或部署旧指纹(a74efa11)
只撤 A 端bash scripts/deploy_frontend.sh(部署上一个 dist)
只撤后端bash scripts/deploy.sh(会重建容器到当前 commit)

⚠️ 数据库无回滚:

119_app_messages_dedup_key.sql 只加列+索引(可空、不影响存量),

dedup_key 只在 D7 站内信用,删掉索引即可回到旧行为。