包含多个会话的累积代码变更: - backend: AI 聊天服务、触发器调度、认证增强、WebSocket、调度器最小间隔 - admin-web: ETL 状态页、任务管理、调度配置、登录优化 - miniprogram: 看板页面、聊天集成、UI 组件、导航更新 - etl: DWS 新任务(finance_area_daily/board_cache)、连接器增强 - tenant-admin: 项目初始化 - db: 19 个迁移脚本(etl_feiqiu 11 + zqyy_app 8) - packages/shared: 枚举和工具函数更新 - tools: 数据库工具、报表生成、健康检查 - docs: PRD/架构/部署/合约文档更新 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
163 lines
6.4 KiB
Markdown
163 lines
6.4 KiB
Markdown
# SPEC: 财务看板 Phase 2 — 144 组合全量验证
|
||
|
||
> 创建日期:2026-03-28
|
||
> 前置 SPEC:`board-finance-phase2`(已完成 T1-T6 + bugfix)
|
||
> 状态:已完成 ✅(2026-03-28)
|
||
> 优先级:P1
|
||
|
||
---
|
||
|
||
## 一、目标
|
||
|
||
遍历财务看板全部 144 种筛选组合(8 时间 × 9 区域 × 2 环比),验证后端 API 返回数据与前端 `page.data` 一致性。
|
||
|
||
## 二、组合矩阵
|
||
|
||
### 时间筛选(8 种)
|
||
| 枚举值 | 显示 | 当期范围 | 上期范围 |
|
||
|--------|------|---------|---------|
|
||
| month | 本月 | 月首~今天 | 上月首~上月同日 |
|
||
| lastMonth | 上月 | 上月首~上月末 | 再上月首~再上月末 |
|
||
| week | 本周 | 周一~今天 | 上周一~上周同天 |
|
||
| lastWeek | 上周 | 上周一~上周日 | 再上周一~再上周日 |
|
||
| quarter3 | 前3个月 | 往前3月首~上月末 | 再往前等长 |
|
||
| quarter | 本季度 | 季首~今天 | 上季首~上季同天 |
|
||
| lastQuarter | 上季度 | 上季首~上季末 | 再上季首~再上季末 |
|
||
| half6 | 最近6个月 | 往前6月首~上月末 | 再往前等长 |
|
||
|
||
### 区域筛选(9 种)
|
||
| 枚举值 | 显示 | 影响板块 |
|
||
|--------|------|---------|
|
||
| all | 全部区域 | 6 板块全显示 |
|
||
| hall | 大厅 | 隐藏:预收资产/现金流出/助教分析 |
|
||
| hallA | A区 | 同上 |
|
||
| hallB | B区 | 同上 |
|
||
| hallC | C区 | 同上 |
|
||
| vip | 台球包厢 | 同上 |
|
||
| snooker | 斯诺克 | 同上 |
|
||
| mahjong | 麻将房 | 同上 |
|
||
| ktv | 团建房 | 同上 |
|
||
|
||
### 环比开关(2 种)
|
||
| 值 | 说明 |
|
||
|----|------|
|
||
| 0 | 关闭,环比字段为空 |
|
||
| 1 | 开启,环比字段有值 |
|
||
|
||
## 三、验证项清单
|
||
|
||
每种组合需验证以下字段(共 6 板块):
|
||
|
||
### 3.1 经营一览(overview)— 始终显示
|
||
| # | 字段 | 验证规则 |
|
||
|---|------|---------|
|
||
| O1 | occurrence | ≥0,数字类型 |
|
||
| O2 | discount | ≥0,数字类型 |
|
||
| O3 | discountRate | 0~1 之间 |
|
||
| O4 | confirmedRevenue | = occurrence - discount |
|
||
| O5 | cashIn | ≥0 |
|
||
| O6 | cashOut | ≥0 |
|
||
| O7 | cashBalance | = cashIn - cashOut |
|
||
| O8 | balanceRate | cashIn>0 时 = cashBalance/cashIn |
|
||
| O9 | area≠all 时 | occurrence/discount/confirmedRevenue 应与 revenue 板块一致 |
|
||
| O10 | compare=1 时 | 8 个 xxxCompare 字段非空,格式为 "X.X%" / "持平" / "新增" |
|
||
| O11 | compare=0 时 | 8 个 xxxCompare 字段为空/null |
|
||
|
||
### 3.2 预收资产(recharge)— 仅 area=all 时返回
|
||
| # | 字段 | 验证规则 |
|
||
|---|------|---------|
|
||
| R1 | area≠all 时 | recharge 为 null |
|
||
| R2 | actualIncome | ≥0 |
|
||
| R3 | firstCharge + renewCharge | ≈ actualIncome |
|
||
| R4 | cardBalance | ≥0(快照值) |
|
||
| R5 | allCardBalance | ≥ cardBalance |
|
||
| R6 | giftRows | 长度 3(新增/消费/余额) |
|
||
| R7 | compare=1 时 | allCardBalanceCompare 非空 |
|
||
|
||
### 3.3 应计收入确认(revenue)— 始终显示
|
||
| # | 字段 | 验证规则 |
|
||
|---|------|---------|
|
||
| V1 | structureRows | 长度 ≥ 3(至少有主行+助教+食品) |
|
||
| V2 | area≠all 时 | structureRows 只含对应区域 |
|
||
| V3 | totalOccurrence | = SUM(structureRows 非 isSub 行的 amount) |
|
||
| V4 | discountTotal | = SUM(discountItems 的 amount) |
|
||
| V5 | confirmedTotal | = totalOccurrence - discountTotal |
|
||
| V6 | discountItems | 长度 5(团购/会员折扣/手动调整/赠送卡/其他) |
|
||
| V7 | channelItems | 长度 3 |
|
||
| V8 | priceItems | 长度 3 |
|
||
| V9 | structureRows 优惠列 | 主行 discount = discountTotal,子行按占比分摊 |
|
||
| V10 | compare=1 时 | totalOccurrenceCompare/confirmedTotalCompare 非空 |
|
||
| V11 | compare=1 时 | structureRows 各行 bookedCompare 非空 |
|
||
|
||
### 3.4 现金流入(cashflow)— 始终显示
|
||
| # | 字段 | 验证规则 |
|
||
|---|------|---------|
|
||
| C1 | consumeItems | 长度 2-3(纸币/线上/团购 或 合并项/团购) |
|
||
| C2 | rechargeItems | 长度 1 |
|
||
| C3 | total | = SUM(consumeItems) + SUM(rechargeItems) |
|
||
| C4 | consumeItems 各项 | desc 非空(柜台现金收款等) |
|
||
| C5 | compare=1 时 | totalCompare 非空,各项 compare 非空 |
|
||
|
||
### 3.5 现金流出(expense)— 仅 area=all 时显示
|
||
| # | 字段 | 验证规则 |
|
||
|---|------|---------|
|
||
| E1 | operationItems | 长度 ≥ 3 |
|
||
| E2 | fixedItems | 长度 ≥ 4 |
|
||
| E3 | coachItems | 长度 ≥ 4 |
|
||
| E4 | platformItems | 长度 ≥ 3 |
|
||
| E5 | total | = SUM(所有 items) |
|
||
|
||
### 3.6 助教分析(coachAnalysis)— 仅 area=all 时显示
|
||
| # | 字段 | 验证规则 |
|
||
|---|------|---------|
|
||
| A1 | basic.rows | 长度 1-4(初/中/高/星) |
|
||
| A2 | basic.totalPay | = SUM(rows.pay) |
|
||
| A3 | basic.totalShare | = SUM(rows.share) |
|
||
| A4 | basic.avgHourly | 13~28 范围(基础课) |
|
||
| A5 | incentive.rows | 长度 0-4 |
|
||
| A6 | compare=1 时 | basic/incentive 各行 payCompare/shareCompare 非空 |
|
||
|
||
## 四、执行方案
|
||
|
||
### 4.1 后端 API 验证(Python 脚本)
|
||
|
||
写 `scripts/ops/validate_board_finance.py`,遍历 144 种组合:
|
||
1. 登录获取 token(dev-login)
|
||
2. 循环调用 `GET /api/xcx/board/finance?time=X&area=Y&compare=Z`
|
||
3. 对每个响应按验证项清单检查
|
||
4. 输出问题清单到 `export/board-finance-validation.md`
|
||
|
||
### 4.2 前端 page.data 验证(微信开发者工具)
|
||
|
||
对后端验证发现问题的组合,用 `evaluate_script` 验证:
|
||
1. 连接开发者工具(ws://127.0.0.1:9420)
|
||
2. `page.setData({ selectedTime, selectedArea, compareEnabled })` + `page._loadData()`
|
||
3. 等待 5 秒后读取 `page.data`
|
||
4. 对比后端 API 返回值与 `page.data` 是否一致
|
||
|
||
### 4.3 优化策略
|
||
|
||
144 种组合中,很多是等价的:
|
||
- area=all 时 6 板块全显示,area≠all 时只有 3 板块(overview/revenue/cashflow)
|
||
- compare=0 时不需要验证环比字段
|
||
- 不同区域的验证逻辑相同,只是数据不同
|
||
|
||
可以分层验证:
|
||
1. 第一层:8 时间 × 2 环比 × area=all = 16 种(全板块)
|
||
2. 第二层:8 时间 × 1 区域(hallA 代表)× 2 环比 = 16 种(验证区域过滤)
|
||
3. 第三层:对剩余 7 个区域,只验证 month × compare=0 = 7 种(验证区域数据差异)
|
||
4. 总计:16 + 16 + 7 = 39 种(覆盖所有逻辑分支)
|
||
|
||
## 五、执行环境
|
||
|
||
- 后端:`cd apps/backend && uvicorn app.main:app --reload`
|
||
- 微信开发者工具:端口 9420
|
||
- 数据库:pg-etl-test(test_etl_feiqiu)
|
||
- 脚本 cwd:项目根目录
|
||
|
||
## 六、产出物
|
||
|
||
- `scripts/ops/validate_board_finance.py` — 验证脚本
|
||
- `export/board-finance-validation.md` — 验证报告(问题清单)
|
||
- 如有问题,修复后重跑验证直到全部通过
|