237 lines
11 KiB
Markdown
237 lines
11 KiB
Markdown
# P12:赠送卡矩阵细分数据 — gift-card-breakdown
|
||
|
||
> 优先级:P1(BOARD-3 财务看板功能缺陷修复)
|
||
> 来源:RNS1 系列审计遗留项 P1-6
|
||
> 预估工作量:中
|
||
> 依赖:无新增依赖(DWD 层数据已就绪)
|
||
|
||
---
|
||
|
||
## 背景
|
||
|
||
BOARD-3 财务看板的「预收资产」板块包含一个赠送卡 3×4 矩阵(3 行:新增/消费/余额;4 列:合计/酒水卡/台费卡/抵用券)。当前矩阵除余额行的 total 列外,所有细分单元格均返回 0。
|
||
|
||
根因:DWS 层 `dws_finance_recharge_summary` 只存储赠送卡总额(`recharge_gift`、`gift_card_balance`),未按卡类型拆分。而 DWD 层 `dim_member_card_account` 已通过 `card_type_id` 区分三种赠送卡类型,数据源完备。
|
||
|
||
---
|
||
|
||
## 需求(Requirements)
|
||
|
||
### 用户故事
|
||
|
||
1. 作为门店管理者,我需要在财务看板中看到赠送卡按用途(酒水/台费/抵用券)拆分的余额、新增、消费数据,以便了解各类赠送卡的使用情况和资金分布。
|
||
|
||
### 验收标准
|
||
|
||
- AC1:`dws_finance_recharge_summary` 新增 6 个字段(3 种卡类型 × 余额+新增),ETL 任务正确填充
|
||
- AC2:BOARD-3 赠送卡矩阵「余额」行的 liquor/table_fee/voucher 列显示正确数值
|
||
- AC3:赠送卡矩阵「新增」行显示各类型赠送卡的新增金额(按充值订单的 `point_amount` 拆分)
|
||
- AC4:赠送卡矩阵「消费」行显示各类型赠送卡的消费金额(如 DWD 层可追溯)
|
||
- AC5:环比数据正确计算(如启用 compare 参数)
|
||
- AC6:RLS 视图自动包含新字段(`CREATE OR REPLACE VIEW` 使用 `SELECT *` 或显式列名)
|
||
- AC7:FDW 外部表通过 `IMPORT FOREIGN SCHEMA` 自动同步
|
||
- AC8:`GET /api/xcx/board/finance` 接口返回的 `gift_rows` 矩阵包含正确的细分数据(非全 0)
|
||
- AC9:小程序 `board-finance` 页面赠送卡矩阵正确渲染后端返回的细分数据(替换 mock)
|
||
|
||
---
|
||
|
||
## 数据源分析
|
||
|
||
### DWD 层现有数据
|
||
|
||
`dwd.dim_member_card_account` 通过 `card_type_id` 区分卡类型:
|
||
|
||
| card_type_id | 类型 | 当前记录数 | 当前余额 |
|
||
|---|---|---|---|
|
||
| 2793249295533893 | 储值卡 | 421 | 128,918.32 |
|
||
| 2791990152417157 | 台费卡(赠送) | 343 | 246,267.50 |
|
||
| 2793266846533445 | 活动抵用券(赠送) | 118 | 24,978.70 |
|
||
| 2794699703437125 | 酒水卡(赠送) | 49 | 3,708.95 |
|
||
| 2791987095408517 | 年卡 | 7 | 7.00 |
|
||
| 2793306611533637 | 月卡 | 12 | 4,938.00 |
|
||
|
||
ETL 任务 `FinanceRechargeTask._extract_card_balances()` 已硬编码三个赠送卡 ID:
|
||
```python
|
||
GIFT_CARD_TYPE_IDS = [2791990152417157, 2793266846533445, 2794699703437125]
|
||
```
|
||
|
||
### 矩阵数据需求
|
||
|
||
| 行 | 数据来源 | 说明 |
|
||
|---|---|---|
|
||
| 余额 | `dim_member_card_account` 按 `card_type_id` 分组 | 当日末快照,已有数据 |
|
||
| 新增 | `dwd_recharge_order.point_amount` 按会员卡类型拆分 | 需要 JOIN `dim_member_card_account` 确定卡类型 |
|
||
| 消费 | `dwd_settlement_head.gift_card_amount` | 总额已有,但无法按卡类型拆分(结算单不记录具体使用哪种赠送卡) |
|
||
|
||
### 关键约束
|
||
|
||
- **消费行拆分可能不可行**:`dwd_settlement_head.gift_card_amount` 是赠送卡消费总额,飞球上游 API 不提供按卡类型拆分的消费明细。需确认是否有 `dwd_settlement_head_ex` 或其他扩展表包含此信息。
|
||
- **新增行拆分**:充值订单 `dwd_recharge_order` 的 `tenant_member_card_id` 可关联 `dim_member_card_account.tenant_member_id`,从而确定充值到哪种卡类型。
|
||
- **card_type_id 硬编码**:当前 ETL 已硬编码,本次扩展沿用同一套 ID。后续可考虑迁移至配置表(feiqiu-data-rules 规则 6 精神)。
|
||
|
||
---
|
||
|
||
## 设计要点
|
||
|
||
### DDL 变更(`dws_finance_recharge_summary`)
|
||
|
||
新增 6 个字段:
|
||
|
||
| 字段名 | 类型 | 说明 |
|
||
|---|---|---|
|
||
| `gift_liquor_balance` | NUMERIC(14,2) | 酒水卡余额(当日末) |
|
||
| `gift_table_fee_balance` | NUMERIC(14,2) | 台费卡余额(当日末) |
|
||
| `gift_voucher_balance` | NUMERIC(14,2) | 抵用券余额(当日末) |
|
||
| `gift_liquor_recharge` | NUMERIC(14,2) | 酒水卡新增充值(赠送部分) |
|
||
| `gift_table_fee_recharge` | NUMERIC(14,2) | 台费卡新增充值(赠送部分) |
|
||
| `gift_voucher_recharge` | NUMERIC(14,2) | 抵用券新增充值(赠送部分) |
|
||
|
||
> 消费行拆分字段暂不新增(待确认上游数据可行性)。消费行 total 可通过 `gift_card_balance` 变化量 + 新增量反推(`consumed = prev_balance + recharge - current_balance`),但精度依赖余额快照的连续性。
|
||
|
||
### ETL 任务修改
|
||
|
||
修改 `FinanceRechargeTask._extract_card_balances()`:
|
||
- 将 `gift_balance` 拆分为 3 个细分余额
|
||
- 新增 `_extract_gift_recharge_breakdown()` 方法,通过 `dwd_recharge_order JOIN dim_member_card_account` 按卡类型拆分赠送金额
|
||
|
||
### 后端接口修改
|
||
|
||
接口路径:`GET /api/xcx/board/finance`(BOARD-3 财务看板)
|
||
- 路由:`apps/backend/app/routers/xcx_board.py` → `get_finance_board()`
|
||
- 权限:`view_board_finance`
|
||
|
||
涉及文件与修改点:
|
||
|
||
| 文件 | 修改点 |
|
||
|---|---|
|
||
| `apps/backend/app/services/fdw_queries.py` | `get_finance_recharge()` — SQL 新增 6 个字段的 SUM,填充 `gift_rows` 矩阵的余额行和新增行细分数据 |
|
||
| `apps/backend/app/services/fdw_queries.py` | `_empty_recharge_data()` — 空默认值同步新增字段 |
|
||
| `apps/backend/app/services/board_service.py` | `_build_recharge()` — 环比计算已覆盖 `gift_rows` 所有 cell,无需额外修改(自动适配) |
|
||
| `apps/backend/app/schemas/xcx_board.py` | `GiftCell` / `GiftRow` / `RechargePanel` — schema 无需修改(已预留 `liquor`/`table_fee`/`voucher` 字段) |
|
||
|
||
> 消费行:如果无法直接拆分,使用 `余额变化量反推法` 或保持 total only。
|
||
|
||
### 小程序页面影响
|
||
|
||
页面路径:`apps/miniprogram/miniprogram/pages/board-finance/`
|
||
|
||
| 文件 | 当前状态 | 修改点 |
|
||
|---|---|---|
|
||
| `board-finance.wxml` (L316-370) | 赠送卡矩阵已渲染 `recharge.giftRows`,3 行 × 4 列(酒水卡/台费卡/抵用券) | 无需修改(模板已就绪,数据绑定字段已对齐) |
|
||
| `board-finance.ts` | 当前使用 mock 数据(`giftRows` 硬编码) | 联调时替换 mock 为真实 API 调用(非本 SPEC 范围,属于联调阶段) |
|
||
| `board-finance.wxss` (L643-717) | 赠送卡表格样式已完成 | 无需修改 |
|
||
|
||
字段映射关系(后端 → 小程序):
|
||
|
||
| 后端字段 | 小程序绑定 |
|
||
|---|---|
|
||
| `GiftRow.total.value` | `item.total` |
|
||
| `GiftRow.liquor.value` | `item.wine` |
|
||
| `GiftRow.table_fee.value` | `item.table` |
|
||
| `GiftRow.voucher.value` | `item.coupon` |
|
||
| `GiftRow.*.compare` | `item.*Compare` |
|
||
|
||
> 小程序页面模板和样式已在 RNS1 阶段完成,本次只需后端返回正确数据即可自动渲染。联调阶段需将 mock 数据替换为真实 API 调用。
|
||
|
||
### 管理后台
|
||
|
||
admin-web 无涉及赠送卡/充值统计的页面,不受影响。
|
||
|
||
### RLS 视图 & FDW
|
||
|
||
- `app.v_dws_finance_recharge_summary` 使用 `CREATE OR REPLACE VIEW`,需要更新列列表
|
||
- FDW 使用 `IMPORT FOREIGN SCHEMA`,需要重新导入(幂等脚本已支持)
|
||
|
||
---
|
||
|
||
## 数据流向
|
||
|
||
```
|
||
DWD 层
|
||
├─ dwd_recharge_order.point_amount ──→ JOIN dim_member_card_account ──→ 按 card_type_id 拆分赠送金额
|
||
├─ dim_member_card_account.balance ──→ 按 card_type_id 分组求和 ──→ 3 种赠送卡余额
|
||
└─ dwd_settlement_head.gift_card_amount ──→ 总额(无法按卡类型拆分)
|
||
|
||
↓ ETL(FinanceRechargeTask)
|
||
|
||
DWS 层:dws_finance_recharge_summary(+6 字段)
|
||
|
||
↓ RLS 视图 + FDW
|
||
|
||
业务库:app.v_dws_finance_recharge_summary
|
||
|
||
↓ fdw_queries.get_finance_recharge()
|
||
|
||
FastAPI:GET /api/xcx/board/finance → RechargePanel.gift_rows
|
||
|
||
↓ 小程序渲染
|
||
|
||
board-finance 页面:赠送卡矩阵 3×4 显示正确数值
|
||
```
|
||
|
||
---
|
||
|
||
## 待确认项
|
||
|
||
1. **消费行拆分**:`dwd_settlement_head_ex` 或其他表是否包含赠送卡消费的卡类型明细?如果没有,消费行只能显示 total,细分列保持 0 或使用反推法。
|
||
2. **card_type_id 2791987095408517 和 2793306611533637**:已确认为年卡和月卡,不纳入赠送卡矩阵。
|
||
3. **环比需求**:赠送卡矩阵的每个 cell 是否需要环比数据(compare 字段)?当前 schema `GiftCell` 已预留 `compare: str | None`。
|
||
|
||
---
|
||
|
||
## 任务清单
|
||
|
||
### 数据层(ETL + DDL)
|
||
|
||
- [ ] T1:DDL 迁移 — `dws_finance_recharge_summary` 新增 6 个字段
|
||
- 迁移脚本:`db/etl_feiqiu/migrations/2026-xx-xx_add_gift_breakdown_fields.sql`
|
||
- DDL 基线同步:`docs/database/ddl/etl_feiqiu__dws.sql`
|
||
- [ ] T2:ETL 修改 — `_extract_card_balances()` 拆分赠送卡余额
|
||
- 文件:`apps/etl/connectors/feiqiu/tasks/dws/finance_recharge_task.py`
|
||
- 返回值新增 `gift_liquor_balance`、`gift_table_fee_balance`、`gift_voucher_balance`
|
||
- [ ] T3:ETL 新增 — `_extract_gift_recharge_breakdown()` 拆分赠送卡新增
|
||
- 文件:同 T2
|
||
- SQL:`dwd_recharge_order JOIN dim_member_card_account` 按 `card_type_id` 分组
|
||
- [ ] T4:ETL transform — `transform()` 方法写入新增 6 个字段到 record dict
|
||
|
||
### 数据库视图层
|
||
|
||
- [ ] T5:RLS 视图更新 — `app.v_dws_finance_recharge_summary` 重建
|
||
- 迁移脚本:`db/etl_feiqiu/migrations/` 或 `db/zqyy_app/migrations/`
|
||
- [ ] T6:FDW 外部表同步 — `IMPORT FOREIGN SCHEMA` 重新导入
|
||
|
||
### 后端接口层
|
||
|
||
- [ ] T7:后端修改 — `fdw_queries.get_finance_recharge()`
|
||
- 文件:`apps/backend/app/services/fdw_queries.py`
|
||
- SQL 新增 6 个字段的 SUM
|
||
- 填充 `gift_rows` 矩阵:余额行 + 新增行的 liquor/table_fee/voucher
|
||
- [ ] T8:后端修改 — `_empty_recharge_data()` 空默认值同步
|
||
|
||
### 小程序页面层
|
||
|
||
- [ ] T9:小程序联调 — `board-finance.ts` 替换 mock 数据为真实 API 调用
|
||
- 文件:`apps/miniprogram/miniprogram/pages/board-finance/board-finance.ts`
|
||
- 当前状态:mock 数据硬编码,模板和样式已就绪
|
||
- 注意:字段映射 `liquor→wine`、`table_fee→table`、`voucher→coupon` 需在数据转换层处理
|
||
|
||
### 验证与文档
|
||
|
||
- [ ] T10:验证 — 跑数后对比 `dim_member_card_account` 余额与 DWS 汇总值
|
||
- [ ] T11:BD 手册更新 — `BD_manual_dws_finance_recharge_summary.md`
|
||
|
||
### 涉及文件汇总
|
||
|
||
| 模块 | 文件路径 | 操作 |
|
||
|---|---|---|
|
||
| DDL | `db/etl_feiqiu/migrations/2026-xx-xx_add_gift_breakdown_fields.sql` | 新增 |
|
||
| DDL 基线 | `docs/database/ddl/etl_feiqiu__dws.sql` | 修改 |
|
||
| ETL | `apps/etl/connectors/feiqiu/tasks/dws/finance_recharge_task.py` | 修改 |
|
||
| RLS 视图 | `db/etl_feiqiu/migrations/` 或 `db/zqyy_app/migrations/` | 新增 |
|
||
| 后端 | `apps/backend/app/services/fdw_queries.py` | 修改 |
|
||
| 小程序 | `apps/miniprogram/miniprogram/pages/board-finance/board-finance.ts` | 修改 |
|
||
| Schema | `apps/backend/app/schemas/xcx_board.py` | 无需修改(已预留) |
|
||
| 路由 | `apps/backend/app/routers/xcx_board.py` | 无需修改 |
|
||
| 服务 | `apps/backend/app/services/board_service.py` | 无需修改(环比自动适配) |
|
||
| BD 手册 | `apps/etl/connectors/feiqiu/docs/database/DWS/main/BD_manual_dws_finance_recharge_summary.md` | 修改 |
|