22 KiB
22 KiB
Implementation Plan: RNS1.3 三看板接口
Overview
基于 design.md 的模块结构,增量扩展后端路由、服务层和 FDW 查询层,新增 3 个看板端点 + 1 个配置端点,并完成前端筛选修复。BOARD-3 财务看板是最复杂的单个接口(6 板块、200+ 字段、60+ 环比数据点),采用板块级独立查询和独立降级策略。
Tasks
-
1. 通用工具函数(日期范围 + 环比计算)
- 1.1 在
apps/backend/app/services/board_service.py中实现_calc_date_range(time_enum, ref_date=None)工具函数- 支持 BOARD-1 的 6 种时间枚举(
month/quarter/last_month/last_3m/last_quarter/last_6m)和 BOARD-3 的 8 种时间枚举(month/lastMonth/week/lastWeek/quarter3/quarter/lastQuarter/half6) - 返回
(start_date, end_date)元组,date类型 - Requirements: 1.3, 3.2
- 支持 BOARD-1 的 6 种时间枚举(
- 1.2 在
board_service.py中实现_calc_prev_range(start_date, end_date)计算上期日期范围- 上期长度等于当期长度,
prev_end <= start_date - Requirements: 3.3
- 上期长度等于当期长度,
- 1.3 在
board_service.py中实现calc_compare(current: Decimal, previous: Decimal) -> dict环比计算工具- 返回
{ compare: str, is_down: bool, is_flat: bool } - 边界:
previous=0, current≠0→"新增";previous=0, current=0→"持平" - Requirements: 8.11, 8.12, 8.13, 8.14
- 返回
- 1.1 在
-
2. Pydantic Schema 定义
- 2.1 新建
apps/backend/app/schemas/xcx_board.py,定义请求参数枚举CoachSortEnum(6 值)、SkillFilterEnum(5 值)、BoardTimeEnum(6 值)CustomerDimensionEnum(8 值)、ProjectFilterEnum(5 值)FinanceTimeEnum(8 值)、AreaFilterEnum(7 值)- Requirements: 1.1, 2.1, 3.1
- 2.2 在
xcx_board.py中定义 BOARD-1 响应 SchemaCoachSkillItem、CoachBoardItem(扁平结构,含 perf/salary/sv/task 全部维度字段)、CoachBoardResponse(items + dimType)- Requirements: 1.4~1.14, 1.16
- 2.3 在
xcx_board.py中定义 BOARD-2 响应 SchemaCustomerAssistant、CustomerBoardItemBase(基础字段)- 8 个维度专属 Schema:
RecallItem、PotentialItem、BalanceItem、RechargeItem、RecentItem、Spend60Item、Freq60Item、LoyalItem WeeklyVisit(val + pct)、PotentialTag、CoachDetailCustomerBoardResponse(items + total + page + pageSize)- Requirements: 2.3~2.22
- 2.4 在
xcx_board.py中定义 BOARD-3 响应 SchemaOverviewPanel(8 指标 + 各 3 个环比字段,Optional)GiftCell、GiftRow、RechargePanel(储值卡 5 指标 + 赠送卡 3×4 矩阵 + allCardBalance)RevenueStructureRow、RevenueItem、ChannelItem、RevenuePanelCashflowItem、CashflowPanelExpenseItem、ExpensePanel(4 子分组 + total)CoachAnalysisRow、CoachAnalysisTable、CoachAnalysisPanel(basic + incentive)FinanceBoardResponse(overview + recharge|null + revenue + cashflow + expense + coachAnalysis)- Requirements: 3.5
3.12, 4.14.7, 5.1~5.8
- 2.5 新建
apps/backend/app/schemas/xcx_config.py,定义SkillTypeItem(key/label/emoji/cls)- Requirements: 6.1
- 2.1 新建
-
3. FDW 查询层扩展 — BOARD-1
- 3.1 在
apps/backend/app/services/fdw_queries.py中实现get_all_assistants(conn, site_id, skill_filter)- 数据源:
app.v_dim_assistant,按skill筛选 - Requirements: 1.5
- 数据源:
- 3.2 实现
get_salary_calc_batch(conn, site_id, assistant_ids, start_date, end_date)- 数据源:
app.v_dws_assistant_salary_calc,批量查询当期和上期绩效 - ⚠️ 基于已有
get_salary_calc()的 SQL 模式扩展,复用列名映射(salary_month/effective_hours/gross_salary/base_income/bonus_income) - ⚠️ DWD-DOC 规则 1:收入使用 items_sum 口径
- ⚠️ DWD-DOC 规则 2:费用使用 assistant_pd_money + assistant_cx_money
- Requirements: 1.8, 1.10
- 数据源:
- 3.3 实现
get_top_customers_for_coaches(conn, site_id, assistant_ids)- 数据源:
app.v_dws_member_assistant_relation_index+app.v_dim_member - ⚠️ 基于已有
get_relation_index()的 SQL 模式扩展为按助教维度批量查询 - 按亲密度降序取 Top 3,拼接 P6 AC3 四级 emoji(
> 8.5→ 💖,> 7→ 🧡,> 5→ 💛,≤ 5→ 💙) - ⚠️ DQ-6:客户姓名通过 member_id JOIN v_dim_member,取 scd2_is_current=1
- ⚠️ 注意:已有
get_coach_top_customers()按服务次数排序(来自 v_dwd_assistant_service_log),本函数按亲密度排序(来自 v_dws_member_assistant_relation_index),语义不同,不可复用 - Requirements: 1.6
- 数据源:
- 3.4 实现
get_coach_sv_data(conn, site_id, assistant_ids, start_date, end_date)- 数据源:
app.v_dws_assistant_monthly_summary(已按助教维度预聚合,含客源储值额/储值客户数/储值消耗额) - ⚠️ 不使用
v_dws_member_consumption_summary(那是按客户维度的汇总表,需要额外关联助教再聚合,效率低且语义不匹配) - Requirements: 1.12
- 数据源:
- 3.1 在
-
4. FDW 查询层扩展 — BOARD-2(8 维度)
- 4.1 实现
get_customer_board_recall(conn, site_id, project, page, page_size)- 数据源:
app.v_dws_member_winback_index+app.v_dim_member(ETL 已计算 WBI 召回指数,含 ideal_days/elapsed_days/overdue_days/visits_30d/wbi_score) - 按 WBI(wbi_score)降序,LIMIT/OFFSET 分页
- ⚠️ DQ-6:客户姓名通过 member_id JOIN v_dim_member
- ⚠️ 余额通过 JOIN v_dim_member_card_account 获取
- Requirements: 2.6, 2.7
- 数据源:
- 4.2 实现
get_customer_board_potential(conn, site_id, project, page, page_size)- 数据源:
app.v_dws_member_spending_power_index(ETL 已计算 SPI 消费潜力指数,含 potential_tags/spend_30d/avg_visits/avg_spend/spi_score) - 按 SPI(spi_score)降序
- Requirements: 2.8, 2.9
- 数据源:
- 4.3 实现
get_customer_board_balance(conn, site_id, project, page, page_size)- 数据源:
app.v_dim_member_card_account+app.v_dim_member - 按 balance_amount 降序
- ⚠️ DQ-7:余额通过 tenant_member_id JOIN,取 scd2_is_current=1
- Requirements: 2.10, 2.11
- 数据源:
- 4.4 实现
get_customer_board_recharge(conn, site_id, project, page, page_size)- 数据源:
app.v_dwd_recharge_order+app.v_dim_member_card_account(充值记录 + 当前余额) - 按 last_recharge_date 降序
- Requirements: 2.12, 2.13
- 数据源:
- 4.5 实现
get_customer_board_recent(conn, site_id, project, page, page_size)- 数据源:
app.v_dws_member_visit_detail+app.v_dim_member(ETL 已计算到店明细,含 last_visit_date/visit_freq/ideal_days) - 按 last_visit_date 降序
- Requirements: 2.14, 2.15
- 数据源:
- 4.6 实现
get_customer_board_spend60(conn, site_id, project, page, page_size)- 数据源:
app.v_dws_member_consumption_summary(items_sum_60d 已在汇总表中预计算) - ⚠️ DWD-DOC 规则 1:使用 items_sum 口径计算 spend60d
- 按 items_sum_60d 降序
- Requirements: 2.16, 2.17
- 数据源:
- 4.7 实现
get_customer_board_freq60(conn, site_id, project, page, page_size)- 数据源:
app.v_dws_member_consumption_summary(visit_count_60d 已在汇总表中预计算) - 含 weeklyVisits 8 周柱状图计算(pct 相对最大值百分比 0-100)
- ⚠️ weeklyVisits 需从
app.v_dwd_assistant_service_log按周分组统计(汇总表无周粒度数据) - 按 visit_count_60d 降序
- Requirements: 2.18, 2.19, 2.20
- 数据源:
- 4.8 实现
get_customer_board_loyal(conn, site_id, project, page, page_size)- 数据源:
app.v_dws_member_assistant_relation_index - 按 max_rs 降序
- Requirements: 2.21, 2.22
- 数据源:
- 4.9 实现
get_customer_assistants(conn, site_id, member_ids)批量查询客户关联助教列表- 含亲密度计算,当前跟进助教置顶
- Requirements: 2.5
- 4.1 实现
-
5. FDW 查询层扩展 — BOARD-3(6 板块)
- 5.1 实现
get_finance_overview(conn, site_id, start_date, end_date)- 数据源:
app.v_dws_finance_daily_summary(按日期范围 SUM 聚合),返回 8 项核心指标 - ⚠️ DWD-DOC 规则 1:使用 items_sum 口径
- ⚠️ 注意:ETL 中不存在名为
v_dws_finance_overview的视图,实际视图为v_dws_finance_daily_summary - Requirements: 3.5, 3.7
- 数据源:
- 5.2 实现
get_finance_recharge(conn, site_id, start_date, end_date)- 数据源:
app.v_dws_finance_recharge_summary,返回储值卡 5 指标 + 赠送卡 3×4 矩阵 - ⚠️ 注意:ETL 中不存在名为
v_dws_finance_recharge的视图,实际视图为v_dws_finance_recharge_summary - Requirements: 3.8, 3.9, 3.10, 3.12
- 数据源:
- 5.3 实现
get_finance_revenue(conn, site_id, start_date, end_date, area)- 数据源:
app.v_dws_finance_income_structure(收入结构主表)+app.v_dws_finance_discount_detail(优惠明细辅助) - ⚠️ DWD-DOC 规则 2:助教行使用 assistant_pd_money(基础课)+ assistant_cx_money(激励课)
- ⚠️ 注意:ETL 中不存在名为
v_dws_finance_revenue的视图,需组合两个实际视图 - Requirements: 4.1, 4.2, 4.3, 4.4
- 数据源:
- 5.4 实现
get_finance_cashflow(conn, site_id, start_date, end_date)- 数据源:
app.v_dws_finance_daily_summary(消费收款 + 充值收款字段均在财务日报中) - ⚠️ DWD-DOC 规则 7:platform_settlement_amount 和 groupbuy_pay_amount 互斥
- ⚠️ 注意:ETL 中不存在名为
v_dws_finance_cashflow的独立视图,复用财务日报 - Requirements: 4.5, 4.6, 4.7
- 数据源:
- 5.5 实现
get_finance_expense(conn, site_id, start_date, end_date)- 数据源:
app.v_dws_finance_expense_summary(支出明细 4 子分组)+app.v_dws_platform_settlement(平台服务费:汇来米/美团/抖音) - ⚠️ DWD-DOC 规则 2:coachItems 中基础课使用 assistant_pd_money,激励课使用 assistant_cx_money
- ⚠️ 注意:ETL 中不存在名为
v_dws_finance_expense的视图,需组合两个实际视图 - Requirements: 5.1, 5.2, 5.3, 5.4
- 数据源:
- 5.6 实现
get_finance_coach_analysis(conn, site_id, start_date, end_date)- 数据源:
app.v_dws_assistant_salary_calc,按 assistant_level_name 分组聚合 - 返回 basic(基础课/陪打)+ incentive(激励课/超休)两个子表
- Requirements: 5.5, 5.6, 5.7, 5.8
- 数据源:
- 5.7 实现
get_skill_types(conn, site_id)查询技能类型配置- 数据源:ETL cfg 表
- Requirements: 6.2
- 5.1 实现
-
6. Checkpoint — FDW 查询层验证
- All FDW query functions compile and type-check correctly (getDiagnostics: 0 errors).
-
7. 服务层 — BOARD-1 助教看板
- 7.1 在
board_service.py中实现get_coach_board(sort, skill, time, site_id) -> dict- 参数互斥校验:
time=last_6m+sort=sv_desc→ HTTP 400 - 日期范围计算 → 查询助教列表 → 批量查询绩效/Top 客户/储值/任务 → 排序 → 组装扁平响应
- topCustomers 查询失败降级为空列表
- Requirements: 1.1~1.16
- 参数互斥校验:
- 7.2 在
board_service.py中实现_query_coach_tasks(site_id, assistant_ids, start_date, end_date)查询任务完成数- 数据源:
biz.coach_tasks,按 task_type 分类统计 recall/callback - Requirements: 1.13, 1.14
- 数据源:
- 7.1 在
-
8. 服务层 — BOARD-2 客户看板
- 8.1 在
board_service.py中实现get_customer_board(dimension, project, page, page_size, site_id) -> dict- 按 dimension 参数路由到对应 FDW 查询函数
- 批量查询客户关联助教列表
- 组装分页响应(items + total + page + pageSize)
- Requirements: 2.1~2.23
- 8.1 在
-
9. 服务层 — BOARD-3 财务看板
- 9.1 在
board_service.py中实现get_finance_board(time, area, compare, site_id) -> dict- 日期范围计算 → 6 板块独立查询、独立 try/except 降级
area≠all时 recharge 返回 nullcompare=1时计算上期范围并调用 calc_comparecompare=0时环比字段为 None(序列化时排除)- Requirements: 3.1
3.12, 4.14.7, 5.1~5.8, 8.9, 8.10
- 9.2 实现
_build_overview(conn, site_id, date_range, prev_range, compare)经营一览板块构建- Requirements: 3.5, 3.6, 3.7
- 9.3 实现
_build_recharge(conn, site_id, date_range, prev_range, compare)预收资产板块构建- Requirements: 3.8~3.12
- 9.4 实现
_build_revenue(conn, site_id, date_range, area, prev_range, compare)应计收入板块构建- Requirements: 4.1~4.4
- 9.5 实现
_build_cashflow(conn, site_id, date_range, prev_range, compare)现金流入板块构建- Requirements: 4.5~4.7
- 9.6 实现
_build_expense(conn, site_id, date_range, prev_range, compare)现金流出板块构建- Requirements: 5.1~5.4
- 9.7 实现
_build_coach_analysis(conn, site_id, date_range, prev_range, compare)助教分析板块构建- Requirements: 5.5~5.8
- 9.8 实现各板块的
_empty_*()空默认值工厂函数(优雅降级用)- Requirements: 8.9, 8.10
- 9.1 在
-
10. 路由层 + 路由注册
- 10.1 新建
apps/backend/app/routers/xcx_board.py,实现 3 个看板端点GET /api/xcx/board/coaches— require_permission("view_board_coach")GET /api/xcx/board/customers— require_permission("view_board_customer")GET /api/xcx/board/finance— require_permission("view_board_finance"),response_model_exclude_none=True- Requirements: 8.1, 8.2, 8.3
- 10.2 新建
apps/backend/app/routers/xcx_config.py,实现 CONFIG-1 端点GET /api/xcx/config/skill-types— require_approved()- 查询失败降级返回空数组
- Requirements: 6.1~6.4
- 10.3 在
apps/backend/app/main.py中注册xcx_board和xcx_config路由- Requirements: 8.1
- 10.1 新建
-
11. Checkpoint — 后端接口验证
- All backend endpoints compile and type-check correctly (getDiagnostics: 0 errors on all router files and main.py).
-
12. 前端筛选修复 — BOARD-1(T3-7 F1, F6)
- 12.1 修复
apps/miniprogram/miniprogram/pages/board-coach/页面的onSortChange、onSkillChange、onTimeChange事件处理函数- 更新 data 状态后调用
this.loadData()重新请求 API - Requirements: 7.1
- 更新 data 状态后调用
- 12.2 实现
time=last_6m+sort=sv_desc互斥约束- 选择
last_6m时禁用sv_desc选项,或选择sv_desc时禁用last_6m选项 - Requirements: 7.2
- 选择
- 12.1 修复
-
13. 前端筛选修复 — BOARD-2(T3-7 F2, F3)
- 13.1 修复
apps/miniprogram/miniprogram/pages/board-customer/页面的onDimensionChange、onProjectChange事件处理函数- 更新 data 状态后调用
this.loadData()重新请求 API - Requirements: 7.3
- 更新 data 状态后调用
- 13.2 补充分页参数和懒加载逻辑
onReachBottom触发加载下一页,pageSize=20- Requirements: 7.4
- 13.3 修改
services/api.ts中fetchBoardCustomers函数签名,增加page和pageSize参数- Requirements: 7.5
- 13.1 修复
-
14. 前端筛选修复 — BOARD-3(T3-7 F4, F5)
- 14.1 修改
services/api.ts中fetchBoardFinance函数签名- 从
{ date?: string }扩展为{ time: string, area: string, compare: number } - Requirements: 7.6
- 从
- 14.2 修复
apps/miniprogram/miniprogram/pages/board-finance/页面的onTimeChange、onAreaChange事件处理函数- 更新 data 状态后使用新参数调用
fetchBoardFinance - Requirements: 7.7
- 更新 data 状态后使用新参数调用
- 14.3 修复
toggleCompare函数,切换环比开关后使用compare=0/1参数重新请求- Requirements: 7.8
- 14.4
area≠all时隐藏预收资产板块(recharge为 null 时不渲染该 section)- Requirements: 7.9
- 14.1 修改
-
15. Checkpoint — 前端筛选修复验证
- All frontend filter fixes implemented: event handlers call loadData(), API signatures extended, pagination added to BOARD-2, mutual exclusion constraint for BOARD-1.
-
16. 属性测试(Property-Based Testing)
- 16.1 新建
tests/test_board_properties.py,实现 Property 1: 日期范围计算正确性- 生成器:
st.dates()+st.sampled_from(BoardTimeEnum/FinanceTimeEnum) - 验证:
start_date <= end_date,上期prev_end <= start_date,上期长度 = 当期长度 - Validates: Requirements 1.3, 3.2, 3.3 — Design Property 1
- 生成器:
- 16.2 实现 Property 2: BOARD-1 排序不变量
- 生成器:随机助教列表 +
st.sampled_from(CoachSortEnum) - 验证:相邻元素排序字段满足方向约束
- Validates: Requirements 1.15, 9.1, 9.2 — Design Property 2
- 生成器:随机助教列表 +
- 16.3 实现 Property 3: BOARD-2 分页不变量
- 生成器:随机客户列表 + page/pageSize
- 验证:
items.length <= pageSize,total 跨页一致,无交集 - Validates: Requirements 2.2, 9.3, 9.4 — Design Property 3
- 16.4 实现 Property 4: 亲密度 emoji 四级映射
- 生成器:
st.floats(min_value=0, max_value=10) - 验证:
> 8.5→ 💖,> 7→ 🧡,> 5→ 💛,≤ 5→ 💙;边界8.5→ 🧡 - Validates: Requirements 1.6 — Design Property 4
- 生成器:
- 16.5 实现 Property 5: 环比计算公式正确性
- 生成器:
st.decimals(min_value=0, max_value=1e8)× 2 - 验证:公式正确、方向标记正确、"新增"/"持平" 边界
- Validates: Requirements 8.11~8.14 — Design Property 5
- 生成器:
- 16.6 实现 Property 6: 环比开关一致性
- 生成 BOARD-3 mock 数据 + compare=0,序列化后验证 JSON 无 Compare/Down/Flat key
- Validates: Requirements 3.4, 9.8 — Design Property 6
- 16.7 实现 Property 7: 预收资产区域约束
- 生成 area≠all 的请求,验证 recharge=null
- Validates: Requirements 3.11, 9.7 — Design Property 7
- 16.8 实现 Property 8+9: 经营一览恒等式
- 验证
confirmedRevenue ≈ occurrence - abs(discount)(±0.01) - 验证
cashBalance ≈ cashIn - cashOut(±0.01) - Validates: Requirements 9.5, 9.6 — Design Property 8, 9
- 验证
- 16.9 实现 Property 10: 支付渠道恒等式
- 验证
balance_amount = recharge_card_amount + gift_card_amount - Validates: Requirements 8.7, 9.9 — Design Property 10
- 验证
- 16.10 实现 Property 11: 参数互斥约束
- 固定
time=last_6m+sort=sv_desc,验证 HTTP 400 - Validates: Requirements 1.2, 9.11 — Design Property 11
- 固定
- 16.11 实现 Property 13: weeklyVisits 百分比范围
- 生成 8 周到店数据,验证长度=8、pct 0-100、max(pct)=100
- Validates: Requirements 2.20 — Design Property 13
- 16.12 实现 Property 14: 优雅降级不变量
- mock 板块查询抛异常,验证整体 HTTP 200 + 失败板块空默认值
- Validates: Requirements 8.9, 8.10 — Design Property 14
- 16.1 新建
-
17. Final Checkpoint — 全量验证
- Run all property tests:
cd C:\NeoZQYY && pytest tests/test_board_properties.py -v - Ensure all 12 property tests pass. Ask the user if questions arise.
- Run all property tests:
-
18. 前端到数据库全链路测试
- 18.1 启动后端服务,使用测试库(
test_zqyy_app)验证 BOARD-1、BOARD-2、BOARD-3、CONFIG-1 四个端点的完整请求-响应链路- 使用真实 FDW 连接(
test_etl_feiqiu)验证 SQL 查询正确性 - 验证 JSON 响应结构与 Schema 定义一致(camelCase 序列化)
- 验证权限校验(
require_permission()/require_approved())在真实请求中生效 - 验证
SET LOCAL app.current_site_id数据隔离在真实请求中生效
- 使用真实 FDW 连接(
- 18.2 验证 BOARD-3 环比开关行为
compare=0时响应 JSON 中无 Compare/Down/Flat 字段 ✅compare=1时响应 JSON 中包含完整环比数据 ✅area≠all时recharge为 null ✅
- 18.3 验证 BOARD-1 参数互斥
time=last_6m+sort=sv_desc返回 HTTP 400 ✅
- 18.4 验证 BOARD-2 分页行为
page=1, pageSize=20返回正确分页结构 ✅- 不同 page 返回的 total 一致 ✅
- 18.5 小程序前端联调验证
- 前端筛选修复代码已正确接入 API(代码审查确认)
- 待联调清单记录在测试文件注释中(FDW 列名已修复,可联调)
- 18.1 启动后端服务,使用测试库(
-
19. 项目文档更新与落地
- 19.1 更新
docs/contracts/openapi/backend-api.json,补充 BOARD-1、BOARD-2、BOARD-3、CONFIG-1 四个端点的 OpenAPI 定义 - 19.2 更新
docs/architecture/backend-architecture.md,补充新增的board_service模块、xcx_board/xcx_config路由注册说明 - 19.3 更新
docs/database/BD_Manual_biz_tables.md,补充本次引用的biz.coach_tasks表在看板场景下的使用说明(BOARD-1 task 维度查询) - 19.4 更新
docs/DOCUMENTATION-MAP.md,确保新增文档条目已索引 - 19.5 更新
docs/miniprogram-dev/API-contract.md,补充 BOARD-1、BOARD-2、BOARD-3、CONFIG-1 的接口契约(请求参数/响应示例)
- 19.1 更新
-
20. 数据库变更审计与 DDL 合并
- 20.1 审计本次实现中对数据库的改动(新建表、新增字段、新增索引、FDW 映射变更等)
- 结论:无 DDL 变更。全部基于已有
app.v_*RLS 视图的 SELECT 查询,IMPORT FOREIGN SCHEMA app已自动导入所有视图。biz.coach_tasks看板查询走已有idx_coach_tasks_assistant_status索引,无需新增。
- 结论:无 DDL 变更。全部基于已有
- 20.2 将所有数据库变更合并到主 DDL 文件
- 结论:无 DDL 变更需合并。
- 20.3 更新 BD 手册记录变更
docs/database/BD_Manual_biz_tables.md已补充 RNS1.3 看板引用说明(§2.1)- 审计记录:
docs/audit/changes/2026-03-20__rns13-board-apis-e2e-fix.md
- 20.1 审计本次实现中对数据库的改动(新建表、新增字段、新增索引、FDW 映射变更等)