Files
Neo-ZQYY/docs/prd/specs/board-finance-phase2.md
Neo 6f8f12314f feat: 累积功能变更 — 聊天集成、租户管理、小程序更新、ETL 增强、迁移脚本
包含多个会话的累积代码变更:
- 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>
2026-04-06 00:03:48 +08:00

385 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SPEC: 财务看板第二阶段 — Demo 对齐与数据修正board-finance-phase2
> 创建日期2026-03-27
> 前置 SPEC`board-finance-integration`(已完成,实现了基础数据绑定和筛选联动)
> 状态:执行中
> 优先级P0
---
## 一、背景与前提
### 1.1 已完成的工作Phase 1
Phase 1`board-finance-integration` SPEC已完成
- 前端 `_loadData()` 绑定全部 6 个板块数据
- 筛选联动(时间/区域/环比切换触发重新加载)
- 区域筛选枚举重建9 项)
- DWS 新增 `cash_paper_amount`/`scan_pay_amount` 字段(支付方式拆分)
- 预收资产卡余额从 SUM 改为取最后一天快照
- consumed 字段从硬编码 0 改为从 `card_consume_total` 取值
- 助教排序改为初→中→高→星
- 百分比字段 ×100 修复
### 1.2 当前状态
- 后端 API 200 正常返回6 个板块有数据
- DWS 已重跑(含 `DWS_SALARY_ALLOW_OUT_OF_CYCLE=true`),激励课有数据
- 前端数据绑定正常WXS 格式化正常
- 微信开发者工具 Power 已连接(`ws://127.0.0.1:9420`
### 1.3 遗留问题清单(本 SPEC 要解决的)
| # | 问题 | 板块 | 严重程度 |
|---|------|------|---------|
| P1 | 收入结构分类不对,需按区域筛选体系分类 | 应计收入确认 | 高 |
| P2 | 优惠减扣分项名称/结构与 Demo 不一致 | 应计收入确认 | 高 |
| P3 | 优惠减扣总计未在发生额右侧齐平展示 | 应计收入确认 | 中 |
| P4 | 现金流入各项名称/描述与 Demo 不一致 | 现金流入 | 中 |
| P5 | 现金流出各项名称为空(需固定项名对齐 Demo | 现金流出 | 中 |
| P6 | 助教分析数据不准,需从订单级绩效方案重算 | 助教分析 | 高 |
---
## 二、开发环境
### 2.1 技术栈
- 后端Python 3.10+ / FastAPI / psycopg2原生 SQL
- 前端:微信小程序 TypeScript / WXML / WXSS / WXS
- 数据库PostgreSQLETL 测试库 `test_etl_feiqiu`、业务测试库 `test_zqyy_app`
- ETLuv workspace`apps/etl/connectors/feiqiu/`
### 2.2 可用工具
- **微信开发者工具 Power**`ws://127.0.0.1:9420`支持页面导航、截图、JS 执行、元素快照、网络监控
- **PostgreSQL Power**`pg-etl-test`ETL 库)和 `pg-app-test`(业务库),支持 SQL 执行、表结构查看
- **OpenAPI Power**:后端 API 测试
### 2.3 关键文件路径
| 文件 | 说明 |
|------|------|
| `apps/backend/app/services/fdw_queries.py` | FDW 查询层6 个财务看板查询函数) |
| `apps/backend/app/services/board_service.py` | Service 编排层6 个 _build_* 函数 + 环比计算) |
| `apps/backend/app/schemas/xcx_board.py` | Pydantic SchemaFinanceBoardResponse |
| `apps/miniprogram/miniprogram/pages/board-finance/board-finance.ts` | 前端页面逻辑 |
| `apps/miniprogram/miniprogram/pages/board-finance/board-finance.wxml` | 前端模板 |
| `apps/miniprogram/miniprogram/pages/board-finance/board-finance.wxss` | 前端样式 |
| `apps/miniprogram/miniprogram/utils/format.wxs` | WXS 格式化函数 |
| `apps/demo-miniprogram/miniprogram/pages/board-finance/` | Demo 原型(对齐目标) |
### 2.4 数据库关键表/视图
| 表/视图 | 说明 |
|--------|------|
| `app.v_dws_finance_daily_summary` | 财务日报RLS 视图) |
| `app.v_dws_finance_income_structure` | 收入结构RLS 视图) |
| `app.v_dws_finance_discount_detail` | 优惠明细RLS 视图) |
| `app.v_dws_finance_expense_summary` | 支出汇总RLS 视图Excel 导入) |
| `app.v_dws_platform_settlement` | 平台结算RLS 视图Excel 导入) |
| `app.v_dws_assistant_salary_calc` | 助教工资计算RLS 视图) |
| `dwd.dwd_settlement_head` / `_ex` | 结算单头表/扩展表 |
| `dwd.dwd_assistant_service_log` / `_ex` | 助教服务流水 |
| `dwd.dwd_payment` | 支付流水payment_method: 2=现金, 4=扫码) |
| `dwd.dwd_groupbuy_redemption` | 团购核销 |
| `ods.site_tables_master` | 桌台主数据areaname 字段) |
---
## 三、Demo 原型数据结构(对齐目标)
### 3.1 应计收入确认
**收入结构表structureRows**
```
开台与包厢 ¥358,600 -¥45,200 ¥313,400
A区 ¥118,200 -¥11,600 ¥106,600 (isSub)
B区 ¥95,800 -¥11,200 ¥84,600 (isSub)
C区 ¥72,600 -¥11,100 ¥61,500 (isSub)
团建区 ¥48,200 -¥6,800 ¥41,400 (isSub)
麻将区 ¥23,800 -¥4,500 ¥19,300 (isSub)
助教 基础课 ¥232,500 - ¥232,500
助教 激励课 ¥112,800 - ¥112,800
食品酒水 ¥119,556 -¥68,136 ¥51,420
```
**发生额构成priceItems**
```
开台消费 ¥358,600
酒水商品 ¥186,420
包厢费用 ¥165,636
助教服务 ¥112,800
```
**优惠减扣discountItems**
```
团购优惠 -¥56,200
手动调整 + 大客户优惠 -¥34,800
赠送卡抵扣 (台桌卡+酒水卡+抵用券) -¥22,336
其他优惠 (免单+抹零) -¥0
```
**收款渠道channelItems**
```
储值卡结算冲销 ¥238,200
现金/线上支付 ¥345,800
团购核销确认收入 (团购成交价) ¥126,120
```
### 3.2 现金流入
```
纸币现金 (柜台现金收款) ¥85,600
线上收款 (微信/支付宝/刷卡 已扣除平台服务费) ¥260,200
团购平台 (美团/抖音回款 已扣除平台服务费) ¥126,120
会员充值到账 (首充/续费实收) ¥352,800
合计 ¥824,720
```
### 3.3 现金流出
```
进货与运营:食品饮料 ¥108,200 / 耗材 ¥21,850 / 报销 ¥10,920
固定支出:房租 ¥125,000 / 水电 ¥24,200 / 物业 ¥11,500 / 人员工资 ¥112,000
助教薪资:基础课分成 ¥116,250 / 激励课分成 ¥23,840 / 充值提成 ¥12,640 / 额外奖金 ¥11,500
平台服务费:汇来米 ¥10,680 / 美团 ¥11,240 / 抖音 ¥10,580
合计 ¥600,400
```
### 3.4 助教分析
```
基础课:
totalPay(客户支付) / totalShare(球房抽成) / avgHourly(小时平均)
初级: pay ¥68,600 / share ¥34,300 / hourly ¥20/h
中级: pay ¥82,400 / share ¥41,200 / hourly ¥25/h
高级: pay ¥57,800 / share ¥28,900 / hourly ¥30/h
星级: pay ¥23,700 / share ¥11,850 / hourly ¥35/h
激励课:同结构
```
---
## 四、任务清单
### T1应计收入确认 — 收入结构按区域分类
**问题**:当前 `get_finance_revenue()``v_dws_finance_income_structure` 读取,按 INCOME_TYPE/AREA 分类。需要改为按物理区域分类。
**方案**:不依赖 `dws_finance_income_structure`,直接从 `dwd_settlement_head` + 桌台维度表聚合。
**数据来源**
- `dwd_settlement_head.table_id``ods.site_tables_master.areaname` 获取物理区域
- 按区域聚合 `table_charge_money`(台费)为"开台与包厢"主行
- 按区域拆分子行A区/B区/C区/团建区/麻将区等)
- 助教行:`assistant_pd_money`(基础课)+ `assistant_cx_money`(激励课)
- 食品酒水行:`goods_money`
**区域映射**(与财务看板区域筛选对照表一致):
```
A区 → A区
B区 → B区
C区 + TV台 + 美洲豹赛台 → C区
VIP包厢 → 台球包厢
斯诺克区 → 斯诺克
麻将房 + M7 + M8 + 666 + 发财 → 麻将区
K包 + k包活动区 + 幸会158 → 团建区
虚拟台 + 补时长 → 排除
```
**涉及文件**
- `apps/backend/app/services/fdw_queries.py``get_finance_revenue()` 重写
- 可能需要在 ETL 库中创建区域映射视图或在后端 SQL 中内联 CASE WHEN
**注意**
- 营业日转换:使用 `biz_date_sql_expr` 或后端等效逻辑
- `settle_type IN (1, 3)` 过滤
- 优惠按区域拆分可能不可行(优惠是订单级的,不是区域级的),需确认
### T2应计收入确认 — 优惠减扣对齐 Demo
**问题**:当前 discountItems 从 `v_dws_finance_discount_detail` 读取,分项名称与 Demo 不一致。
**Demo 目标 4 项**
1. 团购优惠 → `discount_groupbuy`
2. 手动调整 + 大客户优惠 → `discount_manual + discount_other`adjust_amount 的两个子集)
3. 赠送卡抵扣(台桌卡+酒水卡+抵用券)→ `discount_gift_card`
4. 其他优惠(免单+抹零)→ `discount_rounding`
**方案**:不从 `v_dws_finance_discount_detail` 读取,直接从 `v_dws_finance_daily_summary` 的 6 个 discount_* 字段聚合,按 Demo 的 4 项重新组合。
**涉及文件**
- `apps/backend/app/services/fdw_queries.py``get_finance_revenue()` 中 discount 部分重写
### T3应计收入确认 — 优惠总计展示位置
**问题**:优惠减扣总计需在发生额右侧齐平展示。
**方案**WXML 布局调整,在 `totalOccurrence` 行下方添加优惠总计行,右侧对齐。
**涉及文件**
- `apps/miniprogram/miniprogram/pages/board-finance/board-finance.wxml`
- 可能需要 WXSS 调整
### T4现金流入对齐 Demo
**问题**:当前项名和描述与 Demo 不一致。
**Demo 目标**
1. 纸币现金desc: 柜台现金收款)→ `cash_paper_amount`
2. 线上收款desc: 微信/支付宝/刷卡 已扣除平台服务费)→ `scan_pay_amount`
3. 团购平台desc: 美团/抖音回款 已扣除平台服务费)→ `groupbuy_pay_amount`
4. 会员充值到账desc: 首充/续费实收)→ `recharge_cash_inflow`
**方案**:修改 `get_finance_cashflow()` 返回的 label 和 desc 字段。
**注意**
- "线上收款"的 desc 说"已扣除平台服务费",但当前 `scan_pay_amount` 是扫码支付原始金额,未扣除平台服务费。需确认是否需要扣除 `platform_fee_amount`
- "团购平台"同理,`groupbuy_pay_amount` 是团购实付金额,是否已扣除平台服务费取决于 `platform_settlement_amount` 是否有值
**涉及文件**
- `apps/backend/app/services/fdw_queries.py``get_finance_cashflow()`
- `apps/miniprogram/miniprogram/pages/board-finance/board-finance.ts``_loadData()` 中 cashflow 映射
### T5现金流出对齐 Demo
**问题**:当前支出数据来自 Excel 导入(`dws_finance_expense_summary` + `dws_platform_settlement`),数据全为 0。但项名和分组结构需要与 Demo 对齐。
**Demo 目标 4 组**
1. 进货与运营:食品饮料 / 耗材 / 报销
2. 固定支出:房租 / 水电 / 物业 / 人员工资
3. 助教薪资:基础课分成 / 激励课分成 / 充值提成 / 额外奖金
4. 平台服务费:按平台名分项(汇来米 / 美团 / 抖音)
**方案**
- 进货运营 + 固定支出:来自 `dws_finance_expense_summary`Excel 导入),按 `expense_category` 分组
- 助教薪资:可从 `dws_assistant_salary_calc` 聚合(基础课分成 = SUM(base_income 对应的球房抽成),激励课分成 = SUM(bonus 对应的球房抽成),充值提成 = SUM(recharge_commission),额外奖金 = SUM(sprint_bonus + top_rank_bonus + other_bonus)
- 平台服务费:来自 `dws_platform_settlement`Excel 导入),按 `platform_name` 分组
**涉及文件**
- `apps/backend/app/services/fdw_queries.py``get_finance_expense()` 重写
- 前端 `_loadData()` 中 expense 映射
### T6助教分析 — 从订单级绩效方案重算
**问题**:当前从 `dws_assistant_salary_calc` 聚合,该表是月度粒度的工资计算结果,使用的是配置表中的标准费率。用户要求从每笔订单的实际绩效方案计算。
**用户要求**
- 客户支付:每笔订单中客户实际支付的助教费用
- 球房抽成:每笔订单中球房实际抽取的金额
- 助教到手 = 客户支付 - 球房抽成
- 小时平均 = 球房抽成 / 总小时数
**数据来源**
- `dwd_assistant_service_log`每笔助教服务记录assistant_id, skill_id, 时长)
- `dwd_settlement_head`每笔结算单assistant_pd_money=基础课客户支付, assistant_cx_money=激励课客户支付)
- 球房抽成需要从配置表(`cfg_assistant_level_price` + `cfg_performance_tier`)按每笔订单的助教等级和档位计算
**方案选择**
- 方案 A直接从 `dwd_assistant_service_log` + `dwd_settlement_head` 关联计算(绕过 DWS
- 方案 B继续用 `dws_assistant_salary_calc`,但验证其数据与订单级计算一致
**建议**:先用方案 B 验证数据一致性。如果一致,保持现有逻辑;如果不一致,再改为方案 A。
**验证方法**
-`dwd_settlement_head` 聚合 `SUM(assistant_pd_money)``SUM(assistant_cx_money)`
-`dws_assistant_salary_calc``SUM(base_hours * base_course_price)``SUM(bonus_hours * bonus_course_price)` 对比
- 如果一致,说明 DWS 数据可靠
**涉及文件**
- `apps/backend/app/services/fdw_queries.py``get_finance_coach_analysis()`
- 可能需要新增 FDW 查询函数
**参考文件**
- `tmp/助理教练流水_朗朗桌球_20260301_20260327.xlsx`(飞球系统导出的助教流水,用于交叉验证)
---
## 五、依赖关系
```
T1收入结构按区域→ 独立,可先做
T2优惠减扣对齐→ 独立,可先做
T3优惠总计展示→ 依赖 T2 完成
T4现金流入对齐→ 独立,可先做
T5现金流出对齐→ 独立,可先做
T6助教分析重算→ 独立,可先做
```
建议执行顺序T6 → T1 → T2 → T3 → T4 → T5先解决数据准确性再对齐展示
---
## 六、权威文档引用
| 文档 | 路径 | 用途 |
|------|------|------|
| DWD-DOC 权威规则 | `.kiro/steering/dwd-doc-authority.md` | consume_money 禁用、支付恒等式、会员字段断档等 |
| DWS 权威规范 | `.kiro/steering/dws-doc-authority.md` | 幂等策略、课程定价、绩效档位等 |
| 前后端联调规范 | `docs/guides/FRONTEND-BACKEND-INTEGRATION.md` | 数据契约、WXS 格式化、快照值 vs 流量值等 |
| 财务看板 Phase 1 SPEC | `docs/prd/specs/board-finance-integration.md` | 已完成的工作和区域对照表 |
| Demo 原型 | `apps/demo-miniprogram/miniprogram/pages/board-finance/` | 对齐目标 |
| 支付方式 BD 手册 | `apps/etl/connectors/feiqiu/docs/database/DWD/main/BD_manual_dwd_payment.md` | payment_method 枚举 |
---
## 七、DWD-DOC 强制规则速查
1. `consume_money` 禁止直接用于计算 → 用 `items_sum` 拆分字段
2. 助教费用必须拆分:`assistant_pd_money`(陪打/基础课)+ `assistant_cx_money`(超休/激励课)
3. 支付恒等式:`balance_amount = recharge_card_amount + gift_card_amount`
4. `settle_type IN (1, 3)` 过滤正向交易
5. 支付方式拆分从 `dwd_payment`payment_method: 2=现金, 4=扫码),不从 `settlement_head_ex`
6. 快照值(卡余额)禁止 SUM取最后一天
7. TS 层传原始数字WXS 负责格式化(禁止双重格式化)
---
## 八、区域筛选对照表
| code | 前端显示 | 包含的物理区域areaname |
|------|---------|--------------------------|
| all | 全部区域 | 所有 |
| hall | 大厅 | A区+B区+C区+TV台+美洲豹赛台 |
| hallA | A区 | A区 |
| hallB | B区 | B区 |
| hallC | C区 | C区+TV台+美洲豹赛台 |
| vip | 台球包厢 | VIP包厢 |
| snooker | 斯诺克 | 斯诺克区 |
| mahjong | 麻将房 | 麻将房+M7+M8+666+发财 |
| ktv | 团建房 | K包+k包活动区+幸会158 |
排除:虚拟台、补时长
---
## 九、区域筛选影响板块
| 板块 | 非"全部"时 |
|------|-----------|
| 经营一览 | 按区域过滤 |
| 预收资产 | 隐藏 |
| 应计收入确认 | 按区域过滤 |
| 现金流入 | 按区域过滤 |
| 现金流出 | 隐藏 |
| 助教分析 | 隐藏 |
---
## 十、验证方法
每个任务完成后:
1. 用 PostgreSQL Power 直接查询 DWS/DWD 数据验证计算正确性
2. 用微信开发者工具 Power 的 `evaluate_script` 检查前端 `setData` 后的值
3.`get_page_snapshot` 检查 DOM 渲染结果
4.`screenshot` 截图对比 Demo 原型
---
## 十一、收尾
- 所有改动添加 CHANGE 注释(日期 + Prompt + 直接原因)
- 更新 `docs/prd/specs/board-finance-integration.md` 的状态
- 如涉及 DDL 变更,同步更新 `docs/database/ddl/` 基线和 BD 手册
- 如涉及新的踩坑模式,沉淀到 `.kiro/steering/``docs/guides/FRONTEND-BACKEND-INTEGRATION.md`