# 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
- 数据库：PostgreSQL（ETL 测试库 `test_etl_feiqiu`、业务测试库 `test_zqyy_app`）
- ETL：uv 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 Schema（FinanceBoardResponse） |
| `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`
