7.7 KiB
7.7 KiB
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 postgres4 并发用例全绿)- 前端:
npm run build✅ 6.32s;npm test✅ 11/11manage.py check0 issues;makemigrations --checkNo changes。
本轮做了什么
P0-1(交接计划 NEXT_PLAN.md §P0-1)收口。接管时工作区已有未提交的半成品
(permissions 超管放行 + 未知租户 400 分层、viewset 未知租户显式 400、
seed_demo/demo 幂等兜底、两个测试侧 membership 修补、exceptions 去 exc_type
- 补
_with_error_code),本轮在其上完成最后闭环:
tests/test_membership.py(新建,8 用例):无 membership→403+code、 跨租户→403、inactive membership→403、未知租户→400、超管放行、 API Key 分支(单元级)+ 集成接线现实、demo token 只读可读、seed_initial_datamembership 幂等。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 续)
- P0 级阻塞修复:
BaseTenantViewSet.acreate()在 async 上下文同步跑serializer.is_valid(),凡带 FK 的创建(收款/付款单等)全 500SynchronousOnlyOperation。修法:包进sync_to_async(与 printing 已有写法对齐)。 验证:收款单 201 / 付款单 201 / 重复编码 400。 落点:backend/apps/core/viewset.py(+3 行注释)。 - 前端财务入口闭环(
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 绿无带崩。
- P1-1 PG 并发复跑:
python scripts/setup_pg.py连通 127.0.0.1:5433;-m postgres4/4 绿;PG 全量 499 绿。 - 日志清理:删
backend/g8060/g8070/g8080/g8090.log(P0-4 现场 g8070 已无保留价值,*.log已在.gitignore)。
本轮新增(2026-09-12 续-2 · 诚实性修复 + P0-3 真 bug)
- 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。 - P0-5 缺头:
check_apikey_rate_limit抛Throttled未传wait, DRF 无wait不回Retry-After(views.py::exception_handler实测)。 修法:wait=APIKEY_WINDOW_SECONDS+ 回归断言Retry-After in 429。 - 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 存在但无人调度。
- dramatiq actor)+
- P1-2 Channel 补校验:
Channel.vue的el-form挂了不存在的formRef/rules(模板引用了未定义的变量,保存直调 API)。 补rules(平台/店铺ID/店铺名必填,App Key 留空合法)+ 保存前 validate。 - 文档数字:README PG 口径
499→514(实跑)。
本轮新增(2026-09-12 续-3 · 附录 A #3–#10 全接入 + P1-2 真机验证)
7 个新页面(路由+菜单已接线,构建 ✅ 6.37s):
| # | 页面 | 路由 | 后端接口 | 真机数据 |
|---|---|---|---|---|
| 3 | 核销记录 Allocations.vue(只读) |
/finance/allocations |
GET /finance/allocations/ |
30 行 |
| 4 | 库存流水 Movements.vue(只读) |
/stocks/movements |
GET /inventory/movements/ |
50 行 |
| 5 | 预警规则 AlertRules.vue(CRUD+开关+init-default) |
/notify/rules |
/notify/rules/ + init-default |
4 行 |
| 6 | 打印模板 PrintTemplates.vue(CRUD) |
/printing/templates |
/printing/templates/ |
空态 200 ✅ |
| 7 | 自定义报表 ReportDefinitions.vue(init-system+run) |
/report/definitions |
definitions/ + init-system + run |
空态 200 ✅ |
| 8 | 库存大盘 | Dashboard 库存卡片点击 → 明细抽屉 | /report/dashboard/inventory-status/ |
2 仓/30734 件/¥131,509.92 |
| 9 | 科目管理 Accounts.vue(init-chart+启用开关) |
/finance/accounts |
/finance/accounts/ + init-chart |
空态 200 ✅ |
| 10 | 价格等级/联系人 PriceLevels.vue(等级 CRUD + 联系人只读) |
/partner/price-levels |
price-levels/ + contacts/ |
空态 200 ✅ |
P1-2 Playwright 真机(dev :18099 + demo 租户,0 console errors):
空提交被拦——Vouchers(请输入摘要)/ AlertRules(编码+名称)/ Channel(店铺ID+店铺名),
dialog 不关闭;抽屉字段曾与后端键名错位(by_warehouse vs 实际 warehouses/
grand_total_*),已按实测键修好并复验出真数据。
最终验证(2026-09-12 终版)
- 后端 SQLite:510 passed / 4 skipped(PG 专用并发用例)
- 后端 PostgreSQL 5433:514 passed / 0 skipped
- 前端:
npm run build✅ 6.37s;npm test✅ 11/11 manage.py check0 issues;makemigrations --checkNo changes- Playwright:Register 空/非法被拦、合法 201 跳 dashboard;9 页渲染全过;3 页空提交被拦;抽屉真数据;全程 0 console errors
遗留(诚实记录)
- P2-2 部署 readiness gate + 上线 192.168.5.7:需 Docker/服务器凭证,未动。
- P2-3/D2 移动开单、P3-1 行业专版:按客户驱动,未动。