# 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`