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>
This commit is contained in:
Neo
2026-04-06 00:03:48 +08:00
parent 70324d8542
commit 6f8f12314f
515 changed files with 76604 additions and 7456 deletions

View File

@@ -0,0 +1,384 @@
# 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`