Files
live-hub-py/AGENTS.md
T
yml2213 5405d4b1d1 fix(overview): 纠正高价值套装以商城 status==1 为最高准入裁判
- 根因:斗鱼官方商城接口 status==1 代表「已达兑换上限」,历史代码误以为商品列表全为全局字段而单纯依赖常被官方清空的 exchange_records,导致已兑换账号被误判为可兑换
- 修复:extract_high_value_goods 结合商城完整列表 status==1 现场判定已兑换,exchange_records 作为历史展示与兜底
- 前端:优化工作台与一键查询展示,明确展示「已达上限 N 个: ...」与「未达上限」,消除反向歧义
- 文档:沉淀坑 #9 到 AGENTS.md,并更正设计-账号一键查询.md
2026-10-07 14:51:51 +08:00

9.2 KiB
Raw Blame History

协作约定(给 AI 助手 / 协作者)

用户明确表达过的偏好,加上已经用事故买来的教训。每次在这个仓库里干活前先读一遍。 每条都对应一个真实踩过的坑 —— 删之前先确认那个坑不会再犯。

1. 分支与提交

  • main = 已发布(生产跑的就是它)。功能开发从 main 开分支: git checkout -b feat/<功能名> main,做完合回 main。
  • dev 是历史工作分支,已与 main 分叉(dev 上有未上线的账号自动分配功能)。 ⚠️ 旧的「推完 main 再 dev merge --ff-only main」约定已作废 —— 分叉后这么做会失败;以后要同步 dev 只能真合并,并且必须显式确认。
  • 不要改一点就提交、推送。 小改动先攒着、攒够一批再提交;只有用户明确说 「提交 / 推送」,或到了一个自然的里程碑(一个完整功能做完、一轮反馈全部改完) 才 commit / push。一次提交要整合相关改动,别拆成一堆碎提交。
  • 远端名不统一:本地是 gitea,生产服务器上的同一个仓库叫 origin(URL 相同)。

2. 功能改动

  • 新功能是「增加」,不是「替换」。 旧路径要完整保留(例:账号检测保留了手动粘贴, 新增「按账号选择」;旧的手动输入逻辑一行未动)。做新功能时先想清楚旧路径会不会被影响, 需要扩展的地方用可选参数 / 并集,不要改默认行为。
  • 改动要务实、克制:不做没人要的重构,不加用不上的抽象。
  • 不要重复展示同一份数据。 数据库里已有的字段(如 accounts.points / gold_balance / elite_opened)就让已有界面显示,查询去刷新它们,不要另开一份副本 —— 同一屏两份会让人分不清哪份是准的。
  • 单项外部调用失败不要让整个流程失败,但要如实暴露(错误写进 errors / 返回值 并在 UI 展示),别让「没查到」被读成「没有」。
  • 旁路动作(写事件、写审计、释放资源)绝不能带崩主流程:单独 try/except + 记 warning, 但要留兜底(如定时作业)把漏掉的补上。

3. 验证要求(怎么证明你做对了)

  • 结论要有验证:能实测就实测(有生产镜像库 douyu_login_prod 可只读查), 关键是反向验证 —— 把 bug 注入回去,确认用例真的会变红,再还原。 「跑了一下没报错」不算验证。
  • 提交/推送会跑钩子,所以本地必须按同样标准自查:
    • pre-commit:ruff format --check(只跑 ruff check 不够,格式不对会直接拦下提交)
      • 前端 tsc -b;
    • pre-push:上面这些 + 全量 pytest。
  • 断言要看 model_dump()(序列化结果),只看属性会假绿(原因见坑 #2)。
  • 别信「测试通过」 —— 用例可能是假绿的(原因见坑 #3)。

4. 数据库与迁移

  • 迁移编号按落地顺序取,不预留;已应用的迁移不能改名。
  • 加列/加表后,测试库不会自己长出新结构 —— tests/support/db.py 用的是 Base.metadata.create_all,它只建缺失的表,不会 ALTER 已有表。 症状是 Unknown column,处理办法:重建测试库(DROP/CREATE douyu_login_test)。
  • 本地 dev.sh 在跑时,改迁移文件会被它自动应用到你正在用的库。 所以切分支前先确认「库的迁移版本 == 目标分支的 head」,否则应用会因为 alembic 找不到 revision 直接起不来(run_migrations 没有兜底,会带崩 lifespan)。
  • 本地库:douyu_login(dev)、douyu_login_test(测试,不要拿别的库跑测试套件)、 douyu_login_prod(生产镜像)。 另有 douyu_login_prodclone(停在 20261003_0040)与 douyu_login_rehearsal (停在 20261004_0049):是 S1–S7 上线演练留下的生产克隆快照(各 15,182 账号), 代码里没有任何引用 —— 只读参考,别当测试库用。

5. 部署与生产

生产:SSH 别名 order+douyu_huya,目录 /root/live-hub-py,跑的就是 main。 完整清单见 docs/上线清单-S1-S7部署.md,核心步骤:

# ① 部署前(缺一不可):备份 + 采基线
docker exec live-hub-py-mysql-1 sh -c 'mysqldump ... douyu_login' | gzip > /root/backup_$(date +%m%d_%H%M).sql.gz
gzip -t /root/backup_*.sql.gz
# 采 CK 基线:样本解密长度 + 持有 CK 账号数(部署后要一字不差地对比)
# 查在途任务:正在跑的批次会被重启打断,先看能不能等

# ② 执行:先停 → 再迁移 → 再起
docker compose stop douyu-login
docker compose run --rm -T douyu-login alembic upgrade head </dev/null
docker compose up -d douyu-login

# ③ 部署后:版本 / CK 基线 / 新接口 / 前端产物 / 日志无 ERROR,逐条核对
  • 部署前必须备份(即使这次零迁移);CK 检查是唯一不能省的功能性验证 (CK 丢了补不回来)。
  • docker compose build douyu-login 内部会 npm run build 前端 —— 改了前端不用手工构建, 但部署后要去产物里确认新代码真在里面(grep 一个新增的界面文案)。
  • 只动 app 容器,别碰 mysql(历史上 up -d --force-recreate 把 mysql 一起重建, 每次部署 90 秒 502)。

6. 汇报

  • 如实报告,包括自己的错误、误判和局限。 不确定就说不确定,别猜; 报错了要主动更正(数字尤其要能复现:说「新增 N 个用例 / 基线 M」之前先跑一遍数出来)。
  • 用中文回答。

7. 已踩过的坑(速查)

# 坑 应对
1 别拿不同时刻的快照做对比 —— 曾把相隔两个月的两条记录对比,把「时间差异」当成「账号差异」,得出错误规则写进设计文档 对比必须在同一时刻;时间跨度大就重新采数
2 Pydantic 静默丢字段(本仓库踩过三次,表现各不相同):① 构造函数传了、模型没声明 → extra='ignore' 丢掉;② 模型声明了、手写 @model_serializer 里没列 → 属性有值、JSON 没有;③ 字段名与 schema 不一致(elite_bind_status vs role_bind_status) 断言看 model_dump();改完接口先在 Python 里打一遍确认字段真在里面
3 用例假绿:缺 session.commit() 时 MySQL REPEATABLE READ 让本会话看不到别的会话刚提交的行(脚本改没改都显示没改) 跨会话断言前先 rollback()/commit() 结束当前事务再读;关键用例做反向验证
4 用「从 A 到 B」的区间删除删代码块很危险,会连带删掉中间的行(曾把 **bind_payload(acc), 一起删掉) 改完读回来确认;能用精确替换就别用区间
5 docker compose run 默认读 stdin:用 ssh 'bash -s' < 脚本 喂进去时,它会把脚本剩下的行吃掉 用 docker compose run -T --rm ... </dev/null
6 镜像同步脚本会白等 15 分钟:pgrep -f 'mysqldump.*douyu_login' 匹配到了自己所在的 shell,等待循环永不提前退出 判定远端进程存活别用会自匹配的 pgrep -f 模式
7 set -e + 输出过滤(管道接 grep -v)会半途静默中止:过滤把输出全吃掉时 grep 返回 1,被 set -e 当成失败(曾在「已删库、还没导入」处中止,镜像库一度是空的) 过滤输出后别让 set -e 接管退出码;关键步骤显式检查成功
8 ruff check 通过 ≠ 能提交:钩子查的是 ruff format 提交前本地跑一遍 .githooks/pre-commit
9 偷换概念把「账号级状态」当成「全局字段」:斗鱼商城 storehome 的 count(库存)确实是全局的,但 status 字段是强账号维度的!status=1 就是「已达兑换上限」(限量 1 件已满/已兑换)。曾把两者混为一谈,误以为商品列表全不可信,跑去依赖早已被官方清空的 exchangeList,导致已兑换的账号被错误打上「两个都可兑换」绿标 判定高价值资产准入:以 storehome.status==1 为最高裁判;exchangeList 仅做历史展示兜底,绝不可当作否定已拥有的依据

8. 仓库事实(速查)

分支 main = 已发布(生产跑它);dev = 已分叉的历史工作分支;新功能从 main 开 feat/*
生产 SSH order+douyu_huya(47.114.114.225),目录 /root/live-hub-py,容器 live-hub-py-douyu-login-1
本地服务 ./dev.sh(8800 端口,--reload);启动时会自动跑迁移
本地 MySQL 容器 live-hub-py-mysql-1,端口 3307;库见 §4
生产镜像库 douyu_login_prod(scripts/refresh_local_prod_mirror.sh 同步,只读用)
迁移 head main = 20261004_0051;dev = 20261007_0052(两侧不一致,切分支前先对齐库)
前端 tsc -b 开了 noUnusedLocals,死代码/未用导入直接报 TS6133;镜像构建时会 npm run build
测试 pytest tests/ -q(用 douyu_login_test);ruff format + ruff check