包含多个会话的累积代码变更: - 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>
16 KiB
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 项:
- 团购优惠 →
discount_groupbuy - 手动调整 + 大客户优惠 →
discount_manual + discount_other(adjust_amount 的两个子集) - 赠送卡抵扣(台桌卡+酒水卡+抵用券)→
discount_gift_card - 其他优惠(免单+抹零)→
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 目标:
- 纸币现金(desc: 柜台现金收款)→
cash_paper_amount - 线上收款(desc: 微信/支付宝/刷卡 已扣除平台服务费)→
scan_pay_amount - 团购平台(desc: 美团/抖音回款 已扣除平台服务费)→
groupbuy_pay_amount - 会员充值到账(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 组:
- 进货与运营:食品饮料 / 耗材 / 报销
- 固定支出:房租 / 水电 / 物业 / 人员工资
- 助教薪资:基础课分成 / 激励课分成 / 充值提成 / 额外奖金
- 平台服务费:按平台名分项(汇来米 / 美团 / 抖音)
方案:
- 进货运营 + 固定支出:来自
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 强制规则速查
consume_money禁止直接用于计算 → 用items_sum拆分字段- 助教费用必须拆分:
assistant_pd_money(陪打/基础课)+assistant_cx_money(超休/激励课) - 支付恒等式:
balance_amount = recharge_card_amount + gift_card_amount settle_type IN (1, 3)过滤正向交易- 支付方式拆分从
dwd_payment(payment_method: 2=现金, 4=扫码),不从settlement_head_ex - 快照值(卡余额)禁止 SUM,取最后一天
- 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 |
排除:虚拟台、补时长
九、区域筛选影响板块
| 板块 | 非"全部"时 |
|---|---|
| 经营一览 | 按区域过滤 |
| 预收资产 | 隐藏 |
| 应计收入确认 | 按区域过滤 |
| 现金流入 | 按区域过滤 |
| 现金流出 | 隐藏 |
| 助教分析 | 隐藏 |
十、验证方法
每个任务完成后:
- 用 PostgreSQL Power 直接查询 DWS/DWD 数据验证计算正确性
- 用微信开发者工具 Power 的
evaluate_script检查前端setData后的值 - 用
get_page_snapshot检查 DOM 渲染结果 - 用
screenshot截图对比 Demo 原型
十一、收尾
- 所有改动添加 CHANGE 注释(日期 + Prompt + 直接原因)
- 更新
docs/prd/specs/board-finance-integration.md的状态 - 如涉及 DDL 变更,同步更新
docs/database/ddl/基线和 BD 手册 - 如涉及新的踩坑模式,沉淀到
.kiro/steering/和docs/guides/FRONTEND-BACKEND-INTEGRATION.md