Files
dealerhub/PROGRESS_AGI_ITERATION_6.md
T

6.3 KiB
Raw Blame History

PROGRESS_AGI_ITERATION_6 · P0 收口 + 财务闭环 + PG 并发复跑

实跑基线(2026-09-12,pytest 全量口径):

  • SQLite:509 passed / 4 skipped(4 skipped 均为 tests/test_concurrency.py 的 PG 专用用例)
  • PostgreSQL(127.0.0.1:5433,config.settings.pgtest):499 passed / 0 skipped(含 -m postgres 4 并发用例全绿)
  • 前端:npm run build ✅ 6.32s;npm test ✅ 11/11 manage.py check 0 issues;makemigrations --check No changes。

本轮做了什么

P0-1(交接计划 NEXT_PLAN.md §P0-1)收口。接管时工作区已有未提交的半成品 (permissions 超管放行 + 未知租户 400 分层、viewset 未知租户显式 400、 seed_demo/demo 幂等兜底、两个测试侧 membership 修补、exceptions 去 exc_type

  • 补 _with_error_code),本轮在其上完成最后闭环:
  1. tests/test_membership.py(新建,8 用例):无 membership→403+code、 跨租户→403、inactive membership→403、未知租户→400、超管放行、 API Key 分支(单元级)+ 集成接线现实、demo token 只读可读、 seed_initial_data membership 幂等。
  2. tests/test_exception_handler.py::test_handler_returns_structured_500_for_unknown: 期望从 exc_type == "RuntimeError" 改为 "exc_type" not in resp.data, 对齐 P0-4 已落地的去泄露修复(实现先于测试更新)。

语义决策(理由)

  • 未知租户 → 400 而非 403:参数错误与授权失败分离,未知 code 走视图层 ValidationError({"tenant": "无法识别租户"}),不泄露租户存在性判断给授权层。
  • 超管直通:与 seed_initial_data 的 bootstrap 契约一致 ("existing superusers are operators for every seeded tenant")。
  • demo 幂等兜底:demo token 流 bypass 正常登录路径,enter_demo 内 get_or_create 保证手工建的 demo 租户也能进。
  • API Key 分支的接线现实(实测,非假设): TenantMembershipPermission 的 APIKey 分支目前没有视图同时接线—— BaseTenantViewSet 系用 JWT/Session,Open* 系用 require_scope。 所以用例 6 按"单元级分支验证 + 集成级现实记录"写,不伪造 403。 若后续要把 APIKey 接进租户系视图,再把该用例升级为端到端。

踩坑

  • test_demo.py、test_printing.py 的 membership 修补在接管时已在工作区, 本轮只验证了它们变绿,没有重写。
  • pytest -q 不打印 passed 汇总行(只有 dots + warnings + FAILED/SKIPPED), 用例数用 --collect-only | grep -c "::" 拿:全量 490(tests/ 476 + core 14)。
  • 交接计划写的"482 全量 / 477 passed"是旧基线数字,本轮实测已是 490, 差异来自期间新增用例;文档数字以本轮实跑为准。

本轮新增(2026-09-12 续)

  1. P0 级阻塞修复:BaseTenantViewSet.acreate() 在 async 上下文同步跑 serializer.is_valid(),凡带 FK 的创建(收款/付款单等)全 500 SynchronousOnlyOperation。修法:包进 sync_to_async(与 printing 已有写法对齐)。 验证:收款单 201 / 付款单 201 / 重复编码 400。 落点:backend/apps/core/viewset.py(+3 行注释)。
  2. 前端财务入口闭环(NEXT_PLAN.md §附录 A #1#2 + f7eb4325"补前端财务入口与错误状态"):
    • Receivables.vue:"+登记收款"按钮 → 收款单创建 dialog(客户下拉/单号留空自生成/日期/金额>0/方式/备注)
      • 每行"核销"按钮 → 收款单选择 + 金额 dialog(默认未结余额,超额后端 400 透出)。
    • Payables.vue:镜像实现(登记付款 + 核销应付)。
    • 后端字段级错误(customer/bill_no/amount/allocations/detail)本地透出 ElMessage.error, 全局拦截器兜底其余状态。
    • 验证:npm run build ✅ 6.32s;后端 finance 22 用例绿;全量 509 绿无带崩。
  3. P1-1 PG 并发复跑:python scripts/setup_pg.py 连通 127.0.0.1:5433; -m postgres 4/4 绿;PG 全量 499 绿。
  4. 日志清理:删 backend/g8060/g8070/g8080/g8090.log(P0-4 现场 g8070 已无保留价值, *.log 已在 .gitignore)。

本轮新增(2026-09-12 续-2 · 诚实性修复 + P0-3 真 bug)

  1. P0-3 真 bug:frontend/src/router/index.js 的 PUBLIC_ROUTES 只放行 login/storefront,匿名进 /register 被守卫踢回 /login—— 注册页存在但不可达,闭环是断的。修法:PUBLIC_ROUTES 加 register。 Playwright 真机(dev :18099 + 新 build): 空表单提交被拦(4 条必填标红)/ 非法值被拦(手机号格式/密码至少8位/ 两次密码不一致)/ 合法值 201 并跳 #/dashboard / 0 console errors。
  2. P0-5 缺头:check_apikey_rate_limit 抛 Throttled 未传 wait, DRF 无 wait 不回 Retry-After(views.py::exception_handler 实测)。 修法:wait=APIKEY_WINDOW_SECONDS + 回归断言 Retry-After in 429。
  3. P1-3 定时接线:apps/core/tasks.py(新建,purge_expired_audit_logs
    • dramatiq actor)+ config/dramatiq.py 每日 04:00 cron + test_purge_actor_registered_and_runs。此前 command 存在但无人调度。
  4. P1-2 Channel 补校验:Channel.vue 的 el-form 挂了不存在的 formRef/rules(模板引用了未定义的变量,保存直调 API)。 补 rules(平台/店铺ID/店铺名必填,App Key 留空合法)+ 保存前 validate。
  5. 文档数字:README PG 口径 499 → 514(实跑)。

最终验证(2026-09-12)

  • 后端 SQLite:510 passed / 4 skipped(PG 专用并发用例)
  • 后端 PostgreSQL 5433:514 passed / 0 skipped
  • 前端:npm run build ✅ 6.50s;npm test ✅ 11/11
  • manage.py check 0 issues;makemigrations --check No changes
  • Playwright Register 真机:空/非法被拦、合法 201 跳 dashboard、0 console errors

遗留(诚实记录)

  • §附录 A #4–#10(库存流水/预警规则/打印模板/自定义报表/库存大盘/科目管理/价格等级页) 仍是后端有、前端无——超出本轮 27 条真源范围,未动。
  • P2-2 部署 readiness gate + 上线 192.168.5.7:需 Docker/服务器凭证,未动。
  • P2-3/D2 移动开单、P3-1 行业专版:按客户驱动,未动。