包含多个会话的累积代码变更: - 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>
26 KiB
看板 & 详情页差距分析与实施指南
调研日期:2026-03-28 范围:小程序看板(助教/客户列表)、助教详情页、客户详情页 目的:全面梳理前后端完成度差距,为后续联调实施提供完整依据
一、总体完成度
| 模块 | 后端接口 | 前端页面 | 真实数据对接 | 完成度 | 主要差距 |
|---|---|---|---|---|---|
| 助教看板 BOARD-1 | ✅ | ✅ | ❌ Mock | ~60% | 前端用 Mock;skills 为空;环比字段为 None |
| 客户看板 BOARD-2 | ✅ | ✅ | ❌ Mock | ~60% | 前端用 Mock;loyal 缺 coachDetails;排序规则需修正 |
| 助教详情 COACH-1 | ✅ | ✅ | ⚠️ 部分 | ~40% | API 已调用但 Mock 覆盖大部分字段 |
| 客户详情 CUST-1 | ✅ | ✅ | ⚠️ 部分 | ~35% | API 已调用但只映射了 id/name/phone |
| 客户服务记录 CUST-2 | ✅ | ✅ | ✅ 真实 | ~90% | 已完成联调 |
| 看板 Tab 切换 | N/A | ✅ | N/A | 需重构 | 当前是页面跳转,需改为同页切换 |
二、问题清单(按优先级排列)
P1:看板 Tab 切换改为同页切换
现状:三个看板页面(board-finance / board-customer / board-coach)各自独立,通过 wx.navigateTo() 或 wx.switchTab() 跳转。
问题:
board-coach.ts第 245 行onTabChange():wx.navigateTo/wx.switchTabboard-customer.ts第 277 行onTabChange():同上board-finance.ts第 473 行onTabChange():同上
目标:改为同一页面内的 tab 切换(无页面跳转感),类似 wx:if 或 hidden 控制显示/隐藏。
实施方案选项:
- 方案 A:合并为单页:将三个看板合并到一个页面(如
board/board),用wx:if="{{activeTab === 'finance'}}"切换内容区。优点:切换无延迟;缺点:单页代码量大,首次加载慢。 - 方案 B:
wx.redirectTo替代:用wx.redirectTo替代wx.navigateTo,避免页面栈堆积。优点:改动最小;缺点:仍有页面切换闪烁。 - 方案 C:组件化:将三个看板内容抽为自定义组件,在一个容器页面中按 tab 切换。优点:代码隔离好、切换流畅;缺点:需要重构。
建议:方案 C(组件化),兼顾代码隔离和切换体验。
影响范围:
- 前端:
pages/board-finance/、pages/board-customer/、pages/board-coach/→ 抽为组件 - 路由:
app.json页面注册需调整 - 权限守卫:
checkPageAccess需适配新路由 - custom-tab-bar:如果 board-finance 是 tabBar 页面,需要调整
P2:助教详情页 Mock → 真实 API 映射
现状(coach-detail.ts 第 297-370 行 loadData()):
- 已调用
fetchCoachDetail(id)获取后端数据 - 但只映射了
id和name,其余全部用mockCoachDetail覆盖 - 档位节点硬编码为
[0, 100, 130, 160, 190, 220](实际应为[0, 120, 150, 180, 210])
后端 Schema(CoachDetailResponse)已返回的完整字段:
id, name, avatar, level, skills, work_years, customer_count, hire_date,
performance { monthly_hours, monthly_salary, customer_balance, tasks_completed,
perf_current, perf_target },
income { this_month[], last_month[] },
tier_nodes[],
visible_tasks[], hidden_tasks[], abandoned_tasks[],
top_customers[], service_records[], history_months[], notes[]
前端需要映射但当前被 Mock 覆盖的字段:
| 字段 | Mock 变量 | 说明 |
|---|---|---|
tier_nodes |
硬编码 [0, 100, 130, 160, 190, 220] |
档位节点,后端从 cfg_performance_tier 读取 |
visible_tasks |
mockVisibleTasks |
可见任务列表 |
hidden_tasks |
mockHiddenTasks |
隐藏任务列表 |
abandoned_tasks |
mockAbandonedTasks |
已放弃任务列表 |
top_customers |
mockTopCustomers |
TOP 客户列表 |
service_records |
mockServiceRecords |
近期服务记录 |
history_months |
mockHistoryMonths |
历史月份数据 |
notes |
Mock 内嵌 | 备注列表(已部分映射) |
income |
Mock 内嵌 | 收入明细(本月/上月) |
performance 全部子字段 |
mockCoachDetail.performance |
绩效数据 |
实施要点:
- 移除
mockCoachDetail及所有mock*变量 - 直接使用
fetchCoachDetail(id)返回的完整对象 - 注意 camelCase 转换(后端 snake_case → 前端 camelCase 自动转换)
tier_nodes使用后端返回值,fallback 用_FALLBACK_TIER_NODES = [0, 120, 150, 180, 210]- 数值字段传原始数字给
setData,WXML 中用 WXS 格式化(TS 与 WXS 格式化互斥规则) - null 值清洗:
?? 0/?? ''(组件 property 收到 null 不走默认值)
P3:客户详情页 Mock → 真实 API 映射
现状(customer-detail.ts 第 97-120 行 loadDetail()):
- 已调用
fetchCustomerDetail(id)获取后端数据 - 只映射了
id、name、phone三个字段 - 其余模块(Banner、AI 洞察、维客线索、助教任务、最亲密助教、消费记录、备注)全部使用
data中的初始空值
后端 Schema(CustomerDetailResponse)已返回的完整字段:
id, name, phone, phone_full, avatar, member_level, relation_index, tags[],
balance, consumption_60d, ideal_interval, days_since_visit,
ai_insight { summary, strategies[] },
coach_tasks[], favorite_coaches[], retention_clues[],
consumption_records[], notes[]
前端需要映射但当前未映射的字段:
| 字段 | 前端 data key | 说明 |
|---|---|---|
phone_full |
detail.phone |
完整手机号(用于复制) |
avatar |
未使用 | 头像 URL |
member_level |
未使用 | 会员等级 |
relation_index |
未使用 | 关系指数 |
tags |
未使用 | 标签数组 |
balance |
detail.balance |
卡余额 |
consumption_60d |
detail.consumption60d |
60天消费 |
ideal_interval |
detail.idealInterval |
理想到店间隔 |
days_since_visit |
detail.daysSinceVisit |
距上次到店天数 |
ai_insight |
aiInsight |
AI 洞察(暂不处理) |
coach_tasks |
coachTasks |
关联助教任务 |
favorite_coaches |
favoriteCoaches |
最亲密助教 |
retention_clues |
clues |
维客线索 |
consumption_records |
consumptionRecords |
消费记录 |
notes |
sortedNotes |
备注 |
实施要点:
loadDetail中将fetchCustomerDetail(id)返回的完整对象映射到data- AI 相关字段(
ai_insight)暂不处理,保持空值 - Banner 四项指标(balance / consumption60d / idealInterval / daysSinceVisit)直接映射
- 子模块(coachTasks / favoriteCoaches / clues / consumptionRecords / notes)直接映射
- null 值清洗规则同 P2
P4:助教看板 Mock → 真实 API
现状(board-coach.ts 第 195-215 行 loadData()):
- 使用
setTimeout+MOCK_COACHES模拟加载 - 未调用任何 API
后端接口:GET /api/xcx/board/coaches?sort=perf_desc&skill=ALL&time=month
后端返回结构:
{
"items": [
{
"id": 123,
"name": "张三",
"initial": "张",
"avatar_gradient": "",
"level": "senior",
"skills": [],
"top_customers": ["李四", "王五"],
"perf_hours": 156.5,
"perf_hours_before": null,
"perf_gap": null,
"perf_reached": false,
"salary": 18500.0,
"salary_perf_hours": 156.5,
"salary_perf_before": null,
"sv_amount": 45000.0,
"sv_customer_count": 12,
"sv_consume": 8500.0,
"task_recall": 5,
"task_callback": 8
}
],
"dim_type": "perf"
}
实施要点:
- 替换
setTimeout+MOCK_COACHES为真实 API 调用 - 筛选参数(sort / skill / time)传给 API
- 筛选变更时重新请求(当前只在前端过滤 Mock 数据)
- 注意
skills字段当前为空数组(见 P6) perf_hours_before/salary_perf_before当前为 null(见 P7)
P5:客户看板 Mock → 真实 API
现状(board-customer.ts 第 218-235 行 loadData()):
- 使用
setTimeout+MOCK_CUSTOMERS模拟加载 - 未调用任何 API
后端接口:GET /api/xcx/board/customers?dimension=recall&project=ALL&page=1&page_size=20
实施要点:
- 替换 Mock 为真实 API 调用
- 维度切换时重新请求(不同维度调用不同 FDW 查询函数)
- 项目筛选传给 API
- 分页支持(page / page_size)
- 注意前端字段名与后端返回的映射(camelCase 自动转换)
客户看板前端字段 → 后端字段映射:
| 前端字段 | 后端字段 | 维度 |
|---|---|---|
idealDays |
ideal_days |
recall |
elapsedDays |
elapsed_days |
recall |
overdueDays |
overdue_days |
recall |
visits30d |
visits_30d |
recall |
balance |
balance |
recall / balance |
recallIndex |
recall_index |
recall |
spend30d |
spend_30d |
potential |
avgVisits |
avg_visits |
potential |
avgSpend |
avg_spend |
potential |
lastVisit |
last_visit |
balance / recent |
monthlyConsume |
monthly_consume |
balance |
availableMonths |
available_months |
balance |
lastRecharge |
last_recharge |
recharge |
rechargeAmount |
recharge_amount |
recharge |
recharges60d |
recharges_60d |
recharge |
currentBalance |
current_balance |
recharge |
spend60d |
spend_60d |
spend60 |
visits60d |
visits_60d |
spend60 / freq60 |
avgInterval |
avg_interval_days |
freq60 |
weeklyVisits |
weekly_visits |
freq60 |
intimacy |
intimacy |
loyal |
topCoachName |
top_coach_name |
loyal |
topCoachHeart |
top_coach_heart |
loyal |
coachDetails |
❌ 后端未返回 | loyal |
daysAgo |
days_ago |
recent |
assistants |
assistants |
所有维度 |
P6:助教 Skills 字段为空
现状:
- 后端
get_coach_board()返回"skills": [](第 316 行注释:v_dim_assistant 无 skill 列,暂返回空) - 后端
get_coach_detail()同样返回空 skills - 前端 WXML 已有 skills 标签渲染逻辑
调研发现——"技能"的两层含义:
-
课程类型(skill_id):指助教能教的课程类型
- 基础课(陪打/PD):
skill_id = 2791903611396869 - 附加课(超休/CX):
skill_id = 2807440316432197 - 包厢课:归入基础课口径
- 配置表:
cfg_skill_type(skill_id → course_type_code: BASE/BONUS/ROOM) - 这不是看板需要展示的"技能"
- 基础课(陪打/PD):
-
项目类型(area_category):指助教擅长的运动项目
- BILLIARD(🎱 中式/追分)、SNOOKER(斯诺克)、MAHJONG(🀄 麻将/棋牌)、KTV(🎤 团建/K歌)
- 配置表:
cfg_area_category(台桌 → 区域 → 项目类型映射) - 视图:
app.v_cfg_area_category(去重到 category 级别) - 这才是看板需要展示的"技能标签"
看板中 skills 的业务含义:
- 前端
SKILL_CLASS映射:'🎱' → 'skill--chinese'、'斯' → 'skill--snooker'、'🀄' → 'skill--mahjong'、'🎤' → 'skill--karaoke' - 即:助教擅长哪些项目类型(台球/斯诺克/麻将/KTV)
数据来源方案:
- 从
dwd_assistant_service_log按助教聚合历史服务的area_category_code,取去重后的项目类型列表 - SQL 示例:
SELECT DISTINCT asl.area_category_code FROM dwd.dwd_assistant_service_log asl WHERE asl.assistant_id = %s AND asl.is_delete = 0 AND asl.area_category_code IS NOT NULL - 或者:在
v_dim_assistant视图中新增skills列(从服务记录聚合)
实施建议:
- 在
fdw_queries中新增get_assistant_skills_batch(conn, site_id, assistant_ids)函数 - 从
v_dwd_assistant_service_log按助教聚合area_category_code - 映射为前端需要的格式:
["BILLIARD", "SNOOKER"] - 在
get_coach_board()和get_coach_detail()中调用并填充skills字段
P7:助教看板"折前"字段(perf_hours_before / salary_perf_before)
现状:
- 后端
get_coach_board()返回perf_hours_before: None、salary_perf_before: None - 前端 WXML 已有条件渲染:
wx:if="{{item.perfHoursBeforeLabel}}"显示"折前 XX.Xh"
业务含义:
perf_hours= 折算后的定档业绩课时(effective_hours)perf_hours_before= 折算前的原始课时(base_hours + bonus_hours + room_hours)- 折算规则:同一台桌同一时段 >2 名助教重叠挂台时,计算
per_hour_contribution = base_ledger_amount / base_hours / overlap_count,若 < 24 元/小时则按比例扣减penalty_minutes - 豁免条件:
is_exempt = true(客人真实需求,前台核实并绑定客人信息) - 只要
effective_hours != raw_hours就显示折前课时,与新入职无关
注意:这不是"环比",而是"折算前 vs 折算后"的对比。前端 WXML 中的 perfHoursBeforeLabel 显示的是"折前 XX.Xh",不是"上期 XX.Xh"。
数据来源:
dws_assistant_salary_calc表中应有raw_hours(折算前)和effective_hours(折算后)- 需确认 DWS 表是否已有
raw_hours字段 - 如果没有,需要在 DWS 任务中补充计算
P8:客户看板 loyal 维度缺少 coachDetails 子数组
现状:
- 前端 WXML 已有完整的助教服务明细表渲染(
board-customer.wxml第 254-277 行) - 表头:助教 | 次均时长 | 服务次数 | 助教消费 | 关系指数
- 数据绑定:
wx:for="{{item.coachDetails}}" - 但后端
get_customer_board_loyal()只返回了top_assistant_id和top_coach_name,没有返回coach_details数组
用户明确的排列规则:
- 客户-助教对,RSI 从高到低排列
- 客户不重复,排重规则是放在最高对的位置
- 每个卡片展示客户信息 + 该客户对应所有助教 RSI 值从高到低排列
- 右上位置展示最高 RSI 值助教
当前后端实现(fdw_queries.py 第 2272-2340 行):
WITH member_top AS (
SELECT ri.member_id,
MAX(ri.rs_display) AS max_rs,
(ARRAY_AGG(ri.assistant_id ORDER BY ri.rs_display DESC))[1] AS top_assistant_id,
(ARRAY_AGG(ri.rs_display ORDER BY ri.rs_display DESC))[1] AS top_rs
FROM app.v_dws_member_assistant_relation_index ri
GROUP BY ri.member_id
ORDER BY MAX(ri.rs_display) DESC
LIMIT %s OFFSET %s
)
- ✅ 排序正确:按
max_rs DESC(最高 RSI 降序) - ✅ 客户不重复:
GROUP BY ri.member_id - ✅ 右上角最高 RSI 助教:
top_assistant_id+top_coach_name - ❌ 缺少:每个客户对应的所有助教明细
需要补充的后端逻辑:
在 get_customer_board_loyal() 返回 items 后,批量查询每个客户的所有助教 RSI 明细:
SELECT ri.member_id,
ri.assistant_id,
COALESCE(da.real_name, da.nickname, '') AS name,
ri.rs_display,
ri.service_count,
ri.total_hours,
ri.total_income
FROM app.v_dws_member_assistant_relation_index ri
LEFT JOIN app.v_dim_assistant da
ON ri.assistant_id = da.assistant_id AND da.scd2_is_current = 1
WHERE ri.member_id = ANY(%s)
ORDER BY ri.member_id, ri.rs_display DESC
前端 coachDetails 字段结构:
interface CoachDetail {
name: string // 助教姓名
cls: string // 样式类
heartScore: number // RSI 值(0-10)
badge?: string // "跟" / "弃"
avgDuration: string // 次均时长
serviceCount: string // 服务次数
coachSpend: string // 助教消费金额
relationIdx: number // 关系指数
}
计算规则:
avgDuration=total_hours / service_count(次均时长)serviceCount=service_count(服务次数)coachSpend=total_income(助教消费金额,即该客户在该助教处的消费)relationIdx=rs_display(RSI 值)heartScore=rs_display(用于 heart-icon 组件)badge:跟/弃标记来源待确认(可能来自coach_tasks表的任务状态)
P9:客户看板项目筛选枚举不一致
现状:
board-customer.ts的PROJECT_OPTIONS使用旧枚举值:{ value: 'all', text: '全部' }, { value: 'chinese', text: '🎱 中式/追分' }, { value: 'snooker', text: '斯诺克' }, { value: 'mahjong', text: '🀄 麻将/棋牌' }, { value: 'karaoke', text: '🎤 团建/K歌' },board-coach.ts的SKILL_OPTIONS已修正为数据库枚举值(2026-03-20 修复):{ value: 'ALL', text: '不限' }, { value: 'BILLIARD', text: '🎱 中式/追分' }, { value: 'SNOOKER', text: '斯诺克' }, { value: 'MAHJONG', text: '🀄 麻将/棋牌' }, { value: 'KTV', text: '🎤 团建/K歌' },
问题:客户看板的 project 参数值(all/chinese/snooker/mahjong/karaoke)与后端枚举(ALL/BILLIARD/SNOOKER/MAHJONG/KTV)不一致,API 调用会失败。
修复:将 PROJECT_OPTIONS 的 value 改为 ALL/BILLIARD/SNOOKER/MAHJONG/KTV。
三、环比字段分布
根据调研,环比字段的分布如下:
3.1 财务看板(BOARD-3)— 已实现环比
后端 get_finance_board() 接受 compare 参数(0/1),当 compare=1 时计算环比。
环比字段分布(xcx_board.py Schema):
OverviewPanel:8 项核心指标各有*_compare/*_down/*_flat三元组RechargePanel:储值卡各项 + 全类别余额合计RevenuePanel:总发生额 / 优惠总计 / 确认收入CashflowPanel:充值合计ExpensePanel:支出合计CoachAnalysisPanel:pay / share / hourly 各有环比
前端 WXML 使用 fmt.compareText() + fmt.compareClass() WXS 函数渲染。
3.2 助教看板(BOARD-1)— 非环比字段
perf_hours_before / salary_perf_before 不是环比,而是"折算前课时":
- 前端 WXML 显示为"折前 XX.Xh"
- 只有新入职助教才有折算值
- 数据来源:DWS
dws_assistant_salary_calc的raw_hours字段
3.3 客户看板(BOARD-2)— 无环比
当前无环比字段设计。
3.4 助教详情页(COACH-1)— 无环比
当前无环比字段设计。
3.5 客户详情页(CUST-1)— 无环比
当前无环比字段设计。
四、统计规则详解
4.1 助教看板四维度
定档业绩维度(perf)
- 排序字段:
effective_hours(折算后工时) - 升序/降序:
perf_desc/perf_asc - 卡片展示:定档课时、折前课时(新入职才有)、距升档差距、是否达标
- 数据来源:
dws_assistant_salary_calc.effective_hours
工资维度(salary)
- 排序字段:
gross_salary - 计算公式:
assistant_pd_money_total + assistant_cx_money_total + bonus_money + room_income - 卡片展示:工资金额、定档课时、折前课时
- 数据来源:
dws_assistant_salary_calc
客源储值维度(sv)
- 排序字段:
sv_amount(客户余额合计) - 卡片展示:储值金额、储值客户数、储值消耗
- 数据来源:
fdw_queries.get_coach_sv_data() - 互斥规则:
time=last_6m时不支持此维度(HTTP 400)
任务维度(task)
- 排序字段:
task_total(recall + callback) - 卡片展示:回访完成数、召回完成数
- 数据来源:业务库
biz.coach_tasks按task_type分类统计
4.2 客户看板八维度
最应召回(recall)
- 排序:自定义排序(综合理想间隔、已过天数、余额等因素)
- 展示:理想间隔天数、已过天数、逾期天数、30天到店次数、余额、召回指数
- 数据来源:
v_dws_member_consumption_summary+v_dws_member_winback_index
最大消费潜力(potential)
- 排序:自定义评分
- 展示:潜力标签、30天消费、平均到店频率、平均客单价
- 数据来源:
v_dws_member_consumption_summary
最高余额(balance)
- 排序:
total_card_balance DESC(卡余额快照值) - 展示:最后到店日期、月消费、可用月数
- 数据来源:
v_dim_member_card_account(快照值,取最后一天) - ⚠️ 余额是快照值,禁止 SUM
最近充值(recharge)
- 排序:
recharge_amount DESC - 展示:最后充值日期、充值金额、60天充值次数、当前余额
- 数据来源:
v_dws_member_consumption_summary
最近到店(recent)
- 排序:
last_visit_date DESC - 展示:距今天数、60天到店次数
- 数据来源:
v_dws_member_consumption_summary
最高消费 近60天(spend60)
- 排序:
consume_amount_60d DESC - 展示:60天消费金额、60天到店次数、高消费标签
- 数据来源:
v_dws_member_consumption_summary
最频繁 近60天(freq60)
- 排序:
visit_count_60d DESC - 展示:平均间隔天数、8周柱状图
- 数据来源:
v_dws_member_consumption_summary(汇总)+v_dwd_assistant_service_log(周粒度)
8 周柱状图统计规则(已实现,_get_weekly_visits_batch()):
- 时间范围:最近 56 天(8 个自然周)
- 分组:按 ISO 周(
DATE_TRUNC('week', create_time::date)) - 每周统计到店次数(
COUNT(*)) val:该周到店次数pct:相对于 8 周中最高值的百分比(val / max_val * 100)- 固定返回 8 个元素,无数据的周
val=0, pct=0
最专一 近60天(loyal)
- 排序:
max_rs DESC(最高 RSI 降序) - 展示:最高 RSI 助教(右上角)、助教服务明细表
- 数据来源:
v_dws_member_assistant_relation_index
排列规则(用户明确):
- 客户-助教对,RSI 从高到低排列
- 客户不重复,排重规则是放在最高对的位置
- 每个卡片展示客户信息 + 该客户对应所有助教 RSI 值从高到低排列
- 右上位置展示最高 RSI 值助教
RSI(关系指数):
- 存储:
dws_member_assistant_relation_index表 - 展示值:
rs_display(0-10 刻度) - Emoji 四级映射:
>8.5→💖/>7→🧡/>5→💛/≤5→💙 - 计算:由 DWS 层
DWS_RELATION_INDEX任务产出(RS/OS/MS/ML 四个子指数)
4.3 薪酬计算规则(DWS 需求文档 3.2 节)
现行方案(2026-03-01 起):
| 档位 | 总业绩小时数阈值 | 专业课抽成(元/h) | 打赏课抽成 | 次月休假 |
|---|---|---|---|---|
| 0档 淘汰压力 | H < 120 | 28 | 50% | 3天 |
| 1档 及格档 | 120 ≤ H < 150 | 18 | 40% | 4天 |
| 2档 良好档 | 150 ≤ H < 180 | 13 | 35% | 5天 |
| 3档 优秀档 | 180 ≤ H < 210 | 10 | 30% | 6天 |
| 4档 销冠竞争 | H ≥ 210 | 8 | 25% | 休假自由 |
- 过档后所有时长按新档位计算
- 新入职助教:按日均 × 30 折算定档(25日后入职最高定档至 2档)
- 折算仅用于定档,不适用于 Top3 奖
- 档位节点:
[0, 120, 150, 180, 210](从cfg_performance_tier配置表读取)
五、数据层依赖
5.1 DWS 表
| 表名 | 用途 | 使用页面 |
|---|---|---|
dws_assistant_salary_calc |
助教绩效/工资 | BOARD-1、COACH-1 |
dws_member_consumption_summary |
客户消费汇总 | BOARD-2(6个维度) |
dws_member_assistant_relation_index |
客户-助教关系指数 | BOARD-2(loyal)、CUST-1 |
dws_member_winback_index |
客户召回指数 | BOARD-2(recall) |
5.2 DWD 表
| 表名 | 用途 | 使用页面 |
|---|---|---|
dwd_assistant_service_log |
服务记录明细 | BOARD-2(freq60 周粒度)、COACH-1 |
dim_member |
会员维度表 | 所有页面(JOIN 取姓名) |
dim_assistant |
助教维度表 | 所有页面(JOIN 取姓名) |
dim_member_card_account |
会员卡账户 | BOARD-2(balance)、CUST-1 |
5.3 配置表
| 表名 | 用途 |
|---|---|
cfg_performance_tier |
绩效档位节点 |
cfg_bonus_rules |
奖金规则 |
cfg_skill_type |
课程类型映射(skill_id → BASE/BONUS/ROOM) |
cfg_area_category |
区域-项目类型映射(BILLIARD/SNOOKER/MAHJONG/KTV) |
5.4 业务库表
| 表名 | 用途 | 使用页面 |
|---|---|---|
biz.coach_tasks |
助教任务 | BOARD-1(任务维度)、COACH-1、CUST-1 |
biz.notes |
备注 | COACH-1、CUST-1 |
biz.ai_cache |
AI 洞察缓存 | CUST-1(暂不处理) |
public.member_retention_clue |
维客线索 | CUST-1 |
六、实施顺序建议
Phase 1 — 基础联调(前端 Mock → 真实 API)
├── P9: 修复客户看板项目筛选枚举(5 分钟)
├── P4: 助教看板 Mock → 真实 API
├── P5: 客户看板 Mock → 真实 API
├── P2: 助教详情页 Mock → 真实 API
└── P3: 客户详情页 Mock → 真实 API(AI 相关暂跳过)
Phase 2 — 数据补全
├── P6: 助教 skills 字段填充
├── P7: 助教看板折前课时字段
└── P8: 客户看板 loyal 维度 coachDetails 子数组
Phase 3 — 交互优化
└── P1: 看板 Tab 切换改为同页切换
七、风险点
- TS 与 WXS 格式化互斥:替换 Mock 时,
setData必须传原始数字,禁止用formatMoney()预格式化 - null 值清洗:后端返回 null 的字段,前端必须
?? 0/?? ''清洗后再传给组件 - Pydantic 静默丢弃:后端 service 新增返回字段时,必须同步更新 Schema,否则数据被静默丢弃
- 余额快照值:
balance是日末快照,禁止 SUM 聚合 - 档位节点:前端硬编码的
[0, 100, 130, 160, 190, 220]与实际[0, 120, 150, 180, 210]不一致 - 看板 Tab 重构:如果 board-finance 是 tabBar 页面,合并后需要调整
app.json和 custom-tab-bar - 助教姓名:所有助教必须显示昵称/花名(
nickname),禁止显示真实姓名(real_name)。SQL 统一用COALESCE(nickname, real_name, '')