54 KiB
NS1:小程序后端 API 补全 — xcx-backend-api
优先级:最高(上线关键路径) 预估工作量:大 前置条件:P1-P5 已完成(数据库、认证、任务、备注、AI 骨架均就绪) 契约基准:
docs/miniprogram-dev/API-contract.md
一、背景与目标
小程序前端 13 个业务页面已全部完成(P5.2 交付),services/api.ts 已统一封装 mock → real 切换机制(USE_REAL_API 开关)。但后端 23 个接口中仅 16 个已实现,剩余 12 个业务接口缺失,是前后端联调的最大阻塞。
本 SPEC 目标:补全全部缺失接口 + FDW 端到端验证 + 前后端联调修复,使小程序可以连接真实后端运行。
已实现的接口(16 个)
| 模块 | 接口 | 路由文件 |
|---|---|---|
| Auth(5) | AUTH-1~5(登录/dev-login/me/refresh/apply) | xcx_auth.py |
| Tasks(5) | GET tasks + pin/unpin/abandon/cancel-abandon | xcx_tasks.py |
| Notes(3) | POST/GET/DELETE notes | xcx_notes.py |
| AI Chat(3) | SSE stream + conversations + messages | xcx_ai_chat.py |
缺失的接口(12 个)
| 接口 ID | 端点 | 页面依赖 | 数据源 |
|---|---|---|---|
| TASK-1 扩展 | GET /tasks(增加 performance 附带字段) | task-list | dws_assistant_salary_calc via FDW |
| TASK-2 | GET /tasks/{id}(完整详情) | task-detail | biz.coach_tasks + FDW 多表 + ai_cache |
| PERF-1 | GET /performance | performance | dws_assistant_salary_calc + dws_assistant_daily_detail via FDW |
| PERF-2 | GET /performance/records | performance-records | dwd_assistant_service_log via FDW |
| CUST-1 | GET /customers/{id} | customer-detail | dim_member + dws_member_consumption_summary via FDW |
| CUST-2 | GET /customers/{id}/records | customer-service-records | dwd_assistant_service_log + dwd_table_fee_log via FDW |
| COACH-1 | GET /coaches/{id} | coach-detail | dim_assistant + dws_member_assistant_relation_index via FDW |
| BOARD-1 | GET /board/coaches | board-coach | dws_assistant_salary_calc + dws_assistant_monthly_summary via FDW |
| BOARD-2 | GET /board/customers | board-customer | 多个 DWS 指数表 via FDW |
| BOARD-3 | GET /board/finance | board-finance | 6 个 dws_finance_* 表 via FDW |
| CONFIG-1 | GET /config/skill-types | board-coach | 静态配置或 cfg 表 |
| CHAT 路径对齐 | 对齐 CHAT-1/2/3 路径与契约 | chat/chat-history | 现有 xcx_ai_chat.py 路径调整 |
二、技术架构
2.1 后端代码模式(沿用现有模式)
apps/backend/app/
├── routers/ # FastAPI 路由(xcx_*.py)
│ ├── xcx_auth.py ✅ 已有
│ ├── xcx_tasks.py ✅ 已有(需扩展 TASK-1/TASK-2)
│ ├── xcx_notes.py ✅ 已有
│ ├── xcx_ai_chat.py ✅ 已有(需路径对齐)
│ ├── xcx_performance.py 🆕 新建
│ ├── xcx_customers.py 🆕 新建
│ ├── xcx_coaches.py 🆕 新建
│ ├── xcx_board.py 🆕 新建
│ └── xcx_config.py 🆕 新建
├── services/ # 业务逻辑层
│ ├── task_manager.py ✅ 已有(需扩展)
│ ├── note_service.py ✅ 已有
│ ├── perf_service.py 🆕 新建
│ ├── customer_service.py 🆕 新建
│ ├── coach_service.py 🆕 新建
│ └── board_service.py 🆕 新建
├── schemas/ # Pydantic 响应模型
└── auth/ # 认证依赖注入 ✅ 已有
2.2 数据库连接模式
- 业务库(zqyy_app):
get_connection()→ psycopg2 直连,读写 - ETL 库(通过 FDW):
fdw_etl.*schema 查询,需先SET LOCAL app.current_site_id - ETL 库(直连只读):
get_etl_readonly_connection(site_id)→ 用于复杂聚合查询
2.3 权限控制
- 所有接口需 JWT(
require_approved()) - 看板接口需额外权限检查(
require_permission("view_board_finance")等) - 权限种子数据已就绪(auth.role_permissions,14 条映射)
三、接口详细设计
3.1 TASK-1 扩展:任务列表增加绩效概览
现有 GET /api/xcx/tasks 返回 list[TaskListItem],需扩展为:
- 增加
performance字段(当月工时/收入/客户数/月份标签) - 增加分页支持(
page/pageSize参数) - 增加
status筛选参数
数据源:
- 任务列表:
biz.coach_tasks+fdw_etl.v_dim_member(客户信息)+fdw_etl.v_dws_member_winback_index/v_dws_member_newconv_index(指数) - 绩效概览:
fdw_etl.v_dws_assistant_salary_calc(当月薪资计算快照)
3.2 TASK-2:任务详情(完整版)
现有 xcx_tasks.py 无 GET /tasks/{id} 端点,需新增。
数据源(多表聚合):
- 基础信息:
biz.coach_tasks - 客户信息:
fdw_etl.v_dim_member+fdw_etl.v_dws_member_consumption_summary - 亲密度:
fdw_etl.v_dws_member_assistant_intimacy - 近期服务记录:
fdw_etl.v_dwd_assistant_service_log(最近 N 条) - 备注:
biz.notes(最近 N 条,含 ai_score) - 维客线索:
public.member_retention_clue - AI 缓存:
biz.ai_cache(app4/app5/app6/app7 缓存) - 喜好标签:
fdw_etl.v_dwd_table_fee_log(按房间类型统计)
⚠️ 这是最复杂的接口,涉及 8+ 张表的聚合。建议拆分为多个 service 函数组合。
3.3 PERF-1/PERF-2:绩效模块
PERF-1(绩效概览)数据源:
fdw_etl.v_dws_assistant_salary_calc(收入/档位/工资)fdw_etl.v_dwd_assistant_service_log(当月服务记录明细)fdw_etl.v_dws_assistant_customer_stats(新客/常客统计)
PERF-2(绩效明细)数据源:
fdw_etl.v_dwd_assistant_service_log(按月筛选,按日分组)fdw_etl.v_dws_assistant_daily_detail(定档折算惩罚字段)
3.4 CUST-1/CUST-2:客户模块
CUST-1(客户详情)数据源:
fdw_etl.v_dim_member+fdw_etl.v_dim_member_card_account(基本信息 + 会员卡)fdw_etl.v_dws_member_consumption_summary(消费汇总)fdw_etl.v_dws_member_assistant_relation_index(关系指数)public.member_retention_clue(维客线索)- 消费记录:
fdw_etl.v_dwd_settlement_head+fdw_etl.v_dwd_table_fee_log+fdw_etl.v_dwd_store_goods_sale+fdw_etl.v_dwd_recharge_order
⚠️ 会员字段断档规则(DQ-6/DQ-7):
member_phone/member_name/member_card_type_name不可靠,必须通过member_idJOINdim_member/dim_member_card_account
CUST-2(客户服务记录)数据源:
fdw_etl.v_dwd_assistant_service_log(按客户+助教筛选)fdw_etl.v_dwd_table_fee_log(台桌信息)
3.5 COACH-1:助教详情
数据源:
fdw_etl.v_dim_assistant(基本信息)fdw_etl.v_dws_member_assistant_relation_index(客户数统计,RS>2)biz.coach_tasks(任务统计:可见/隐藏/已放弃)biz.notes(备注列表)
3.6 BOARD-1/2/3:看板模块
BOARD-1(助教看板)数据源:
fdw_etl.v_dws_assistant_salary_calc(定档业绩/工资)fdw_etl.v_dws_assistant_monthly_summary(月度汇总)biz.coach_tasks(任务完成数统计)
BOARD-2(客户看板)数据源(8 个维度排序):
- 最应召回:
fdw_etl.v_dws_member_winback_index(WBI 降序) - 最大消费潜力:
fdw_etl.v_dws_member_spending_power_index(SPI 降序) - 最高余额:
fdw_etl.v_dws_member_consumption_summary - 最近充值:
fdw_etl.v_dwd_recharge_order - 最高消费 60 天:
fdw_etl.v_dws_member_consumption_summary(基于items_sum) - 最频繁 60 天:
fdw_etl.v_dws_member_consumption_summary - 最近到店:
fdw_etl.v_dws_member_visit_detail - 最专一:
fdw_etl.v_dws_member_assistant_relation_index(RS 最大值)
BOARD-3(财务看板)数据源:
fdw_etl.v_dws_finance_daily_summaryfdw_etl.v_dws_finance_income_structurefdw_etl.v_dws_finance_recharge_summaryfdw_etl.v_dws_finance_discount_detailfdw_etl.v_dws_finance_expense_summaryfdw_etl.v_dws_platform_settlementbiz.ai_cache(app2_finance 缓存,AI 洞察)
3.7 CONFIG-1:技能类型列表
静态配置接口,返回助教技能类型列表。数据来源待定(硬编码 or cfg 表)。
3.8 CHAT 路径对齐
现有 xcx_ai_chat.py 路径:
POST /api/ai/chat/streamGET /api/ai/conversationsGET /api/ai/conversations/{id}/messages
契约定义路径:
GET /api/xcx/chat/historyGET /api/xcx/chat/{chatId}/messagesPOST /api/xcx/chat/{chatId}/messages
需要对齐路径,或在前端 service 层适配。
四、数据库审查与新增表建议
4.1 现有表结构满足度
| 接口 | 现有表是否满足 | 缺口 |
|---|---|---|
| TASK-1/2 | ✅ 基本满足 | 需 FDW 多表 JOIN |
| PERF-1/2 | ✅ 满足 | 无 |
| CUST-1/2 | ✅ 满足 | 消费记录需 3 种类型 UNION |
| COACH-1 | ✅ 满足 | 无 |
| BOARD-1 | ⚠️ 部分 | 任务完成数需实时统计 |
| BOARD-2 | ⚠️ 部分 | 8 维度排序需多表查询,性能风险 |
| BOARD-3 | ✅ 满足 | 环比需后端计算 |
4.2 建议新增的中间表/物化视图
表 1:biz.board_coach_snapshot(助教看板快照)
用途:预聚合助教看板数据,避免每次请求实时 JOIN 多张 FDW 表。
CREATE TABLE biz.board_coach_snapshot (
id BIGSERIAL PRIMARY KEY,
site_id BIGINT NOT NULL,
assistant_id BIGINT NOT NULL,
snapshot_date DATE NOT NULL,
-- 定档业绩
performance_tier VARCHAR(20),
tier_revenue NUMERIC(12,2),
-- 工资
salary_total NUMERIC(12,2),
-- 客户数
customer_count INTEGER,
-- 高客源储值额
high_value_balance NUMERIC(12,2),
-- 任务完成数
task_completed_count INTEGER,
-- 服务时长
total_hours NUMERIC(8,2),
total_income NUMERIC(12,2),
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE (site_id, assistant_id, snapshot_date)
);
刷新策略:ETL 数据更新后触发(trigger_jobs 事件驱动),或每日凌晨批量刷新。
表 2:biz.board_customer_snapshot(客户看板快照)
用途:预聚合客户看板 8 个维度的排序数据,避免实时查询 8 张 FDW 表。
CREATE TABLE biz.board_customer_snapshot (
id BIGSERIAL PRIMARY KEY,
site_id BIGINT NOT NULL,
member_id BIGINT NOT NULL,
snapshot_date DATE NOT NULL,
-- 8 个维度排序值
wbi_score NUMERIC(8,4), -- 最应召回
spi_score NUMERIC(8,4), -- 最大消费潜力
balance_amount NUMERIC(12,2), -- 最高余额
last_recharge_date DATE, -- 最近充值
spend_60d NUMERIC(12,2), -- 最高消费 60 天(items_sum 口径)
visit_count_60d INTEGER, -- 最频繁 60 天
last_visit_date DATE, -- 最近到店
max_rs_score NUMERIC(8,4), -- 最专一
-- 展示字段
member_name VARCHAR(100),
member_avatar VARCHAR(500),
member_tags TEXT[], -- 喜好标签
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE (site_id, member_id, snapshot_date)
);
表 3:biz.board_finance_snapshot(财务看板快照)
用途:预聚合财务看板数据 + 环比计算结果。
CREATE TABLE biz.board_finance_snapshot (
id BIGSERIAL PRIMARY KEY,
site_id BIGINT NOT NULL,
biz_date DATE NOT NULL,
-- 核心指标(JSON 存储,灵活扩展)
metrics JSONB NOT NULL,
-- 环比数据
prev_day_metrics JSONB,
prev_week_metrics JSONB,
prev_month_metrics JSONB,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE (site_id, biz_date)
);
表 4:biz.task_detail_cache(任务详情缓存)
用途:TASK-2 接口涉及 8+ 张表聚合,首次查询后缓存结果,后续读缓存。
CREATE TABLE biz.task_detail_cache (
id BIGSERIAL PRIMARY KEY,
task_id BIGINT NOT NULL REFERENCES biz.coach_tasks(id),
site_id BIGINT NOT NULL,
detail_json JSONB NOT NULL,
version INTEGER NOT NULL DEFAULT 1,
expires_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE (task_id)
);
失效策略:备注新增/AI 缓存更新/任务状态变更时,DELETE 对应缓存行。
五、FDW 端到端验证
5.1 验证范围
需验证 fdw_etl schema 下所有外部表可正常 SELECT,重点关注:
| 外部表 | 用途 | 验证 SQL |
|---|---|---|
v_dim_member |
客户信息 | SELECT count(*) FROM fdw_etl.v_dim_member |
v_dim_assistant |
助教信息 | SELECT count(*) FROM fdw_etl.v_dim_assistant |
v_dws_assistant_salary_calc |
薪资计算 | SELECT * FROM fdw_etl.v_dws_assistant_salary_calc LIMIT 5 |
v_dws_finance_daily_summary |
财务日报 | SELECT * FROM fdw_etl.v_dws_finance_daily_summary LIMIT 5 |
v_dws_member_winback_index |
WBI 指数 | SELECT * FROM fdw_etl.v_dws_member_winback_index LIMIT 5 |
v_dws_member_spending_power_index |
SPI 指数 | SELECT * FROM fdw_etl.v_dws_member_spending_power_index LIMIT 5 |
5.2 RLS 验证
-- 设置门店隔离
SET LOCAL app.current_site_id = '目标 site_id';
-- 验证只返回该门店数据
SELECT site_id, count(*) FROM fdw_etl.v_dim_member GROUP BY site_id;
-- 预期:只有一个 site_id
六、前后端联调
6.1 联调步骤
- 后端接口开发完成后,逐个验证响应格式与
API-contract.md一致 - 小程序
services/api.ts中USE_REAL_API = true - 逐页面联调:login → task-list → task-detail → performance → board → customer-detail → chat
- 修复 snake_case → camelCase 映射差异
- 处理 inline-mock 页面的数据替换(7 个页面有 TODO 标记)
6.2 已知需要对齐的差异
- TASK-1 响应格式:现有返回
list[TaskListItem],契约要求{ items, total, page, pageSize, performance } - CHAT 路径:现有
/api/ai/*,契约要求/api/xcx/chat/* - 响应包装:现有直接返回数据,契约要求
{ code: 0, data: ... }包装
七、参考文档
| 文档 | 路径 | 用途 |
|---|---|---|
| API 契约 | docs/miniprogram-dev/API-contract.md |
接口定义基准 |
| 前端 Service 层 | apps/miniprogram/miniprogram/services/api.ts |
前端期望的响应格式 |
| BD 手册-认证表 | docs/database/BD_Manual_auth_tables.md |
auth schema 表结构 |
| BD 手册-业务表 | docs/database/BD_Manual_biz_tables.md |
biz schema 表结构 |
| DWD-DOC 标杆 | docs/reports/DWD-DOC/ |
金额口径、字段语义权威参考 |
| 数据依赖矩阵 | docs/prd/specs/00-数据依赖矩阵.md |
页面→数据表映射 |
| 现有 task_manager | apps/backend/app/services/task_manager.py |
任务服务层模式参考 |
| 现有 note_service | apps/backend/app/services/note_service.py |
备注服务层模式参考 |
| 权限中间件 | apps/backend/app/middleware/permission.py |
权限检查模式参考 |
| 数据库连接 | apps/backend/app/database.py |
连接方式参考 |
| FDW 配置 | db/fdw/setup_fdw_test.sql |
FDW 外部表映射 |
八、预审查清单(SPEC 启动前确认)
以下问题需要在启动 SPEC 前与用户确认,确保需求无歧义:
8.1 接口设计
- 响应包装格式:现有后端直接返回数据对象,契约要求
{ code: 0, data: ... }包装。是全局统一改造(中间件),还是仅新接口使用包装格式? - CHAT 路径对齐:是修改后端路径从
/api/ai/*改为/api/xcx/chat/*,还是在前端 service 层适配现有路径? - 分页策略:TASK-1 契约要求分页,但现有实现返回全量列表。是否所有列表接口都需要分页?看板接口(前 100 名)是否需要分页?
- CONFIG-1 技能类型:数据来源是硬编码还是从
cfg_*表读取?如果硬编码,具体的技能类型列表是什么?
8.2 数据库
- 快照表刷新策略:3 个看板快照表(coach/customer/finance)的刷新频率?是 ETL 数据更新后触发,还是每日定时刷新,还是请求时按需刷新?
- 任务详情缓存:
task_detail_cache的过期时间设多久?缓存失效触发条件是否完整(备注新增、AI 缓存更新、任务状态变更)? - FDW 外部表命名:现有代码中使用
fdw_etl.v_dim_member(带v_前缀),需确认所有 FDW 外部表的实际命名是否一致。
8.3 业务逻辑
- TASK-2 服务记录范围:近期服务记录取最近多少条?最近 30 天还是最近 10 条?
- BOARD-2 客户看板:8 个维度排序是否都取前 100 名?是否需要支持类型筛选(如按会员等级筛选)?
- BOARD-3 财务看板:环比计算的基准是什么?日环比、周环比、月环比都需要吗?
- CUST-1 消费记录:3 种消费类型(台桌/商城/充值)是混合排序还是分类展示?分页策略?
- COACH-1 任务统计:可见/隐藏/已放弃的分类逻辑是什么?"隐藏"指什么状态?
8.4 性能与安全
- FDW 查询性能:看板接口涉及多张 FDW 表的聚合查询,是否需要设置查询超时?超时后返回什么?
- 维客线索脱敏:TASK-2 返回维客线索时,后端脱敏规则是否与 P6 spec 中定义的一致(移除 recorded_by_assistant_id/name)?
- 金额口径确认:所有涉及消费金额的接口,确认使用
items_sum口径(= table_charge_money + goods_money + assistant_pd_money + assistant_cx_money + electricity_money),不使用consume_money。
八½、前置审查发现(2026-03-18 Kiro 审查)
审查方法:对照现有代码(
xcx_auth.py、xcx_tasks.py、xcx_ai_chat.py、task_manager.py、permission.py、database.py)、API 契约(API-contract.md)、前端 Service 层(api.ts)、FDW 配置(setup_fdw_test.sql)、BD 手册、数据依赖矩阵、DWD-DOC 标杆文档,逐项交叉验证。
R1:接口清单准确性
| # | 问题 | 严重度 | 说明 |
|---|---|---|---|
| R1-1 | 接口总数算术错误 | 🟡 低 | spec 称"23 个接口中仅 16 个已实现,剩余 12 个缺失",16+12=28≠23。需修正总数或重新盘点 |
| R1-2 | Auth 已实现接口数低估 | 🟡 低 | spec 列 Auth 5 个,实际 xcx_auth.py 生产端点 7 个(login/apply/me/me-sites/switch-site/refresh/dev-login),含 dev 端点共 11 个 |
| R1-3 | TASK-4 路径不一致 | 🔴 高 | API 契约定义 POST /tasks/{taskId}/restore,前端 api.ts 调用 /tasks/${taskId}/restore,但后端实现的是 /tasks/{taskId}/cancel-abandon。联调必然失败 |
R1-3 ✅ 已确认:统一为 /restore(改后端路由路径,契约和前端不动)。后端 xcx_tasks.py 中 /cancel-abandon 端点已实现,需改路径为 /restore。
R2:响应格式(全局架构决策,阻塞所有新接口开发)
| # | 问题 | 严重度 | 说明 |
|---|---|---|---|
| R2-1 | 成功响应无包装 | 🔴 高 | 契约要求 { code: 0, data: ... },现有后端直接返回 Pydantic model。全局改造影响所有已实现接口 |
| R2-2 | 错误响应格式不一致 | 🔴 高 | 契约要求 { code: number, message: string },现有 FastAPI 默认 { detail: "..." }。需自定义异常处理器 |
| R2-3 | snake_case vs camelCase | 🔴 高 | 后端返回 snake_case,前端期望 camelCase。api.ts 有 TODO 标记但未实现转换。需决定转换层位置 |
R2 ✅ 已确认:采用方案 A(后端全局包装 { code: 0, data: ... } + Pydantic alias_generator=to_camel)
实施路径:
- 新增全局响应包装中间件(或
response_model基类),成功响应统一{ code: 0, data: ... } - 新增全局异常处理器,
HTTPException→{ code: status_code, message: detail } - 所有 Pydantic schema 加
model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True) - 前端
request()工具函数加.data解包(一处改动) - 已有接口(Auth/Tasks/Notes/AI Chat)逐个验证兼容性
R3:CHAT 模块路径与功能差异
| # | 问题 | 严重度 | 说明 |
|---|---|---|---|
| R3-1 | 路径前缀不同 | 🟠 中 | 现有 /api/ai/*,契约 /api/xcx/chat/* |
| R3-2 | 功能模式不同 | 🔴 高 | 现有是 SSE 流式(POST /chat/stream),契约是同步 JSON(POST /chat/{chatId}/messages 返回 { userMessage, aiReply })。两者不可互替 |
| R3-3 | 历史列表字段不同 | 🟠 中 | 现有返回 { id, app_id, source_page, first_message_preview },契约要求 { id, customer_name, last_message, last_time, unread_count } |
R3 ✅ 已确认:采用选项 A(保留 SSE 流式 + 新增同步端点)
实施方案:
- 保留
POST /api/xcx/chat/stream(SSE 流式对话,路径从/api/ai/chat/stream迁移) - 新增
GET /api/xcx/chat/history(历史对话列表,替代/api/ai/conversations) - 新增
GET /api/xcx/chat/{chatId}/messages(消息查看,替代/api/ai/conversations/{id}/messages) - 契约补充 SSE 端点定义
ai_conversations表无unread_count字段,CHAT-1 响应中此字段暂返回 0(或后续按需新增)
R4:数据库新增表设计
| # | 问题 | 严重度 | 说明 |
|---|---|---|---|
| R4-1 | 快照表刷新时机未定义 | 🟠 中 | 3 个 board_*_snapshot 表的刷新策略(ETL 后触发 / 每日定时 / 按需)直接影响数据新鲜度和实现复杂度 |
| R4-2 | task_detail_cache 命中率存疑 | 🟡 低 | TASK-2 涉及 8+ 张表,备注/AI 缓存/服务记录变更频繁,缓存频繁失效。建议评估:优化 SQL + 并行查询是否足够,不一定需要缓存表 |
| R4-3 | board_finance_snapshot.metrics 缺 schema | 🟡 低 | JSONB 灵活但无约束,建议在 spec 中定义 metrics 的 key 清单(如 total_revenue、total_expense、net_income 等) |
| R4-4 | board_customer_snapshot.spend_60d 窗口定义 | 🟡 低 | "60 天"是 snapshot_date - 60 到 snapshot_date,还是自然月?需明确 |
| R4-5 | 快照表是否真的需要 | 🟠 中 | 看板数据量不大(单店助教 < 50 人、活跃客户 < 5000 人),FDW 直查 + 合理索引可能已够用。快照表增加了维护复杂度(刷新逻辑、数据一致性)。建议先不建快照表,性能不足时再加 |
R4 ✅ 已确认:采用选项 A(先直查 FDW,做好索引和查询优化)
- 不建
board_coach_snapshot、board_customer_snapshot、board_finance_snapshot三张快照表 - 不建
task_detail_cache缓存表 - 看板接口直接查 FDW 表,通过合理索引 + SQL 优化保证性能
- 如后续出现性能瓶颈,再按需引入快照/缓存机制
- spec 第四章中的 4 张新增表建议全部移除
R5:权限模型
| # | 问题 | 严重度 | 说明 |
|---|---|---|---|
| R5-1 | 看板接口权限映射未明确 | 🟠 中 | BOARD-1 → view_board_coach?BOARD-2 → view_board_customer?BOARD-3 → view_board_finance?需确认 |
| R5-2 | PERF/CUST/COACH 权限未定义 | 🟠 中 | 绩效/客户/助教模块是否只需 require_approved()?助教只能看自己的绩效,管理员能看所有人的? |
| R5-3 | 数据隔离粒度 | 🟠 中 | 助教只能看自己的任务/绩效/客户(通过 assistant_id 过滤),管理员能看全店。这个逻辑在 service 层实现还是中间件层? |
R5 ✅ 已确认:现有 5 个权限 code 够用,从上游入口控制即可。
view_tasks:任务模块(TASK-1/2、PERF-1/2、CUST-1/2、COACH-1 均通过此权限 +require_approved()控制)view_board+view_board_finance/view_board_customer/view_board_coach:看板模块- 数据隔离在 service 层实现:助教通过
user_assistant_binding获取assistant_id,只查自己的数据;管理员角色可查全店 - 权限选项固定,角色-权限映射通过租户管理后台灵活配置
- 不新增权限 code
R6:数据源与口径
| # | 问题 | 严重度 | 说明 |
|---|---|---|---|
| R6-1 | TASK-1 performance.total_customers 来源不明 | 🟡 低 | dws_assistant_salary_calc 是否有客户数字段?可能需要从 dws_assistant_customer_stats 单独查 |
| R6-2 | CUST-1 消费记录混合 vs 分类 | 🟡 低 | 3 种消费类型(台桌/商城/充值)在 API 契约中是单一 consumption_records 数组,暗示混合排序。确认? |
| R6-3 | BOARD-2 维度切换方式 | 🟡 低 | API 契约通过 dimension 参数切换 8 个维度,一次返回一个维度的排序结果。确认? |
R7:spec 中引用但未定义的表
| # | 表名 | 引用位置 | 说明 |
|---|---|---|---|
| R7-1 | biz.ai_cache |
TASK-2(app4/5/6/7)、BOARD-3(app2) | 多处引用但 BD 手册无定义。需确认表结构或补充 BD 手册 |
| R7-2 | biz.ai_conversations + biz.ai_messages |
CHAT 模块 | 现有 xcx_ai_chat.py 通过 ConversationService 操作,但 spec 未引用其结构 |
| R7-3 | public.member_retention_clue |
TASK-2、CUST-1 | 维客线索表,spec 未描述字段结构和脱敏规则 |
R7 ✅ 已确认(查库验证):4 张表全部已建,无需新增 DDL。
| 表名 | Schema | 字段数 | 关键字段 |
|---|---|---|---|
ai_cache |
biz | 9 | cache_type(app2_finance/app3_clue/app4_analysis/app5_script/app6_note/app7_task)、target_id、result_json(JSONB)、expires_at |
ai_conversations |
biz | 8 | user_id(varchar)、app_id、site_id、source_page、source_context(JSONB) |
ai_messages |
biz | 6 | conversation_id(FK)、role、content、tokens_used |
member_retention_clue |
public | 10 | member_id、category、summary、detail、recorded_by_assistant_id、recorded_by_name、source(默认 'manual') |
脱敏规则:TASK-2/CUST-1 返回维客线索时,后端应排除 recorded_by_assistant_id 和 recorded_by_name 字段(仅返回 category、summary、detail、source、recorded_at)。
R8:其他待确认项
| # | 问题 | 来源 | 状态 |
|---|---|---|---|
| R8-1 | CONFIG-1 技能类型 | spec §3.7 + api.ts | ✅ 使用 cfg 表 |
| R8-2 | TASK-2 近期服务记录分页 | spec §3.2 | ✅ 一批 20 条,懒加载 |
| R8-3 | TASK-2 备注分页 | spec §3.2 | ✅ 一批 20 条,懒加载 |
| R8-4 | BOARD-2 每维度取前多少名 | spec §3.6 | ✅ 一批 20 条,懒加载,无上限 |
| R8-5 | BOARD-3 环比 | spec §3.6 | ✅ 全部月环比(详见下方) |
| R8-6 | COACH-1 "隐藏任务" | spec §3.5 + API 契约 | ✅ inactive 状态(详见下方) |
| R8-7 | FDW 查询超时 | 性能风险 | 待定(实施时按需设置) |
| R8-8 | 金额口径 | DWD-DOC 标杆 | ✅ items_sum,禁用 consume_money |
R8-1 ✅ 已确认:使用 cfg 表。后端从 ETL 库的 cfg 表读取技能类型列表,前端 api.ts 中的硬编码仅作为 mock 回退。
R8-2/3 ✅ 已确认:一批 20 条,懒加载。
- 前端修改需求:TASK-2 页面的服务记录区域和备注区域需从一次性渲染改为懒加载模式(
page+pageSize=20参数 + 滚动到底部触发"加载更多")
R8-4 ✅ 已确认:一批 20 条,懒加载,无上限。
- 前端修改需求:BOARD-2 客户看板需从一次性渲染改为懒加载模式
R8-5 ✅ 已确认(环比调研完成):
- 所有环比均为月环比(与上月同期对比)
- 共 60+ 个环比数据点,覆盖 6 个板块:经营一览(8 项)、预收资产(6 项 + 赠送卡矩阵 12 项)、应计收入确认(收入结构 9 项 + 正价 4 项 + 优惠 4 项 + 渠道 3 项)、现金流入(5 项)、现金流出(15 项)、助教分析(基础课 + 激励课各 6 项)
- 前端已实现环比开关(
compareEnabled),环比值格式为百分比字符串(如"+12.5%") - API 契约缺陷:BOARD-3 响应中
trend字段仅支持up/down/flat,未定义环比百分比值,需补充compareValue: string(如"12.5%")字段 - 后端需计算所有指标的月环比值(当前期 vs 上期),返回百分比和方向
- 页面筛选支持:本月、上月、本周、上周、前3个月、本季度、上季度、最近6个月
R8-6 ✅ 已确认:"隐藏任务"= 回访任务被顶替后,仍有一段生效时间但前端不显示。
- 映射关系:
hidden_tasks对应coach_tasks.status = 'inactive'(被新任务顶替但未过期) - 后端在 COACH-1 接口中按 status 分组返回:
active→visible_tasks,inactive→hidden_tasks,abandoned→abandoned_tasks - 前端不展示
hidden_tasks,但后端仍需返回(管理端可能需要查看)
八¾、看板筛选项交叉矩阵(2026-03-18 Kiro 调研)
调研方法:逐文件阅读
board-coach.ts、board-customer.ts、board-finance.ts的完整源码,提取所有筛选项定义、可选值、默认值、交叉约束、前端传参方式,并与 API 契约和api.tsservice 层交叉验证。
B1:BOARD-1 助教看板(board-coach)
筛选项清单
| 参数名 | 类型 | 可选值 | 默认值 | 说明 |
|---|---|---|---|---|
sort |
排序维度 | perf_desc、perf_asc、salary_desc、salary_asc、sv_desc、task_desc |
perf_desc |
6 种排序,决定卡片模板 |
skill |
技能筛选 | all、chinese、snooker、mahjong、karaoke |
all |
不限、中式/追分、斯诺克、麻将/棋牌、团建/K歌 |
time |
时间范围 | month、quarter、last_month、last_3m、last_quarter、last_6m |
month |
本月、本季度、上月、前3个月、上季度、最近6个月 |
排序→卡片模板映射(SORT_TO_DIM)
| sort 值 | dimType | 卡片显示内容 |
|---|---|---|
perf_desc / perf_asc |
perf |
定档业绩工时、上期工时、距升档差距、是否达标 |
salary_desc / salary_asc |
salary |
工资总额、定档工时、上期工时 |
sv_desc |
sv |
客源储值额、储值客户数、储值消耗 |
task_desc |
task |
召回任务完成数、回访任务完成数 |
各维度卡片所需字段(后端响应必须包含)
所有维度共享的基础字段(每个 item 都返回):
| 字段 | 类型 | 说明 |
|---|---|---|
id |
string | 助教 ID |
name |
string | 助教姓名 |
initial |
string | 姓名首字(头像占位) |
avatarGradient |
string | 头像渐变色(blue/green/pink/amber/violet/cyan) |
level |
string | 等级 key:star/senior/middle/junior |
levelClass |
string | 等级样式类(前端可自行映射,后端可不返回) |
skills |
array | 技能列表 [{ text: '🎱', cls: 'skill--chinese' }] |
topCustomers |
string[] | Top 客户列表(如 ['💖 王先生', '💛 李女士']) |
各维度专属字段:
| dimType | 维度名 | 专属字段 | 类型 | 说明 |
|---|---|---|---|---|
perf |
定档业绩 | perfHours |
number | 当期定档工时 |
perfHoursBefore |
number? | 上期定档工时(可选,无上期数据时不返回) | ||
perfGap |
string? | 距升档差距描述(如 "距升档 13.8h",已达标时不返回) |
||
perfReached |
boolean | 是否已达标 | ||
salary |
工资 | salary |
number | 工资总额(元) |
salaryPerfHours |
number | 定档工时 | ||
salaryPerfBefore |
number? | 上期定档工时 | ||
sv |
客源储值 | svAmount |
number | 客源储值总额(元) |
svCustomerCount |
number | 储值客户数 | ||
svConsume |
number | 储值消耗额(元) | ||
task |
任务完成 | taskRecall |
number | 召回任务完成数 |
taskCallback |
number | 回访任务完成数 |
后端设计建议:所有维度的字段统一返回在同一个 item 对象中(扁平结构),前端根据当前
dimType选择性渲染。这样切换维度时无需重新请求(数据已在本地),仅切换卡片模板即可。如果数据量大或查询开销高,也可按sort参数只返回当前维度所需字段。
交叉约束
| 约束 | 说明 |
|---|---|
time=last_6m + sort=sv_desc |
⚠️ 不兼容。前端 TIME_OPTIONS 注释标注"不支持客源储值最高"。后端需返回 400 或忽略 |
| 其他组合 | 无限制,3 参数自由组合 |
交叉组合总数
- sort(6) × skill(5) × time(6) = 180 种(减去 1 个不兼容 = 175 种有效组合)
前端传参(api.ts)
fetchBoardCoaches({ skill, sort, time })
⚠️ 前端 Bug:筛选变更不触发重新请求
onSortChange/onSkillChange/onTimeChange 仅更新 data 状态(selectedSort/selectedSkill/selectedTime),未调用 loadData()。联调时前端需修复:筛选变更后重新调用 loadData()。
B2:BOARD-2 客户看板(board-customer)
筛选项清单
| 参数名 | 类型 | 可选值 | 默认值 | 说明 |
|---|---|---|---|---|
dimension |
维度切换 | recall、potential、balance、recharge、recent、spend60、freq60、loyal |
recall |
8 个维度,决定卡片模板和排序逻辑 |
project |
项目筛选 | all、chinese、snooker、mahjong、karaoke |
all |
全部、中式/追分、斯诺克、麻将/棋牌、团建/K歌 |
page |
分页页码 | 正整数 | 1 |
前端待补充(R8-4 决策:20 条懒加载) |
pageSize |
每页条数 | 正整数 | 20 |
前端待补充 |
8 维度→卡片模板映射(DIMENSION_TO_DIM)
| dimension | 中文名 | 排序依据(后端) | 数据源(FDW 表) | 卡片关键字段 |
|---|---|---|---|---|
recall |
最应召回 | WBI 降序 | v_dws_member_winback_index |
idealDays、elapsedDays、overdueDays、visits30d、balance、recallIndex |
potential |
最大消费潜力 | SPI 降序 | v_dws_member_spending_power_index |
potentialTags、spend30d、avgVisits、avgSpend |
balance |
最高余额 | balance_amount 降序 | v_dws_member_consumption_summary |
lastVisit、monthlyConsume、availableMonths |
recharge |
最近充值 | last_recharge_date 降序 | v_dwd_recharge_order |
lastRecharge、rechargeAmount、recharges60d、currentBalance |
recent |
最近到店 | last_visit_date 降序 | v_dws_member_visit_detail |
daysAgo、visitFreq、idealDays |
spend60 |
最高消费 近60天 | items_sum_60d 降序 | v_dws_member_consumption_summary |
spend60d、visits60d、highSpendTag |
freq60 |
最频繁 近60天 | visit_count_60d 降序 | v_dws_member_consumption_summary |
avgInterval、weeklyVisits(8 周柱状图) |
loyal |
最专一 近60天 | max_rs 降序 | v_dws_member_assistant_relation_index |
intimacy、topCoachName、coachDetails(助教明细表) |
各维度卡片所需字段(后端响应必须包含)
所有维度共享的基础字段(每个 item 都返回):
| 字段 | 类型 | 说明 |
|---|---|---|
id |
string | 客户 member_id |
name |
string | 客户姓名 |
initial |
string | 姓名首字(头像占位) |
avatarCls |
string | 头像样式类(avatar--amber/pink/blue 等) |
assistants |
array | 关联助教列表 [{ name, cls, heartScore, badge?, badgeCls? }] |
各维度专属字段:
| dimType | 维度名 | 专属字段 | 类型 | 说明 |
|---|---|---|---|---|
recall |
最应召回 | idealDays |
number | 理想到店间隔(天) |
elapsedDays |
number | 已过天数 | ||
overdueDays |
number | 超期天数(= elapsedDays - idealDays) | ||
visits30d |
number | 近 30 天到店次数 | ||
balance |
string | 余额(格式化,如 "¥2,680") |
||
recallIndex |
string | 召回指数(如 "9.2") |
||
potential |
最大消费潜力 | potentialTags |
array | 潜力标签 [{ text: '高频', theme: 'primary' }] |
spend30d |
string | 近 30 天消费 | ||
avgVisits |
string | 月均到店(如 "6.2次") |
||
avgSpend |
string | 次均消费 | ||
balance |
最高余额 | balance |
string | 当前余额 |
lastVisit |
string | 最近到店(如 "3天前") |
||
monthlyConsume |
string | 月均消耗 | ||
availableMonths |
string | 可用月数(如 "约0.8个月") |
||
recharge |
最近充值 | lastRecharge |
string | 最后充值日期(如 "2月15日") |
rechargeAmount |
string | 充值金额 | ||
recharges60d |
string | 近 60 天充值次数 | ||
currentBalance |
string | 当前余额 | ||
recent |
最近到店 | daysAgo |
number | 距今天数(右上角大字) |
visitFreq |
string | 到店频率(如 "6.2次/月") |
||
idealDays |
number | 理想间隔 | ||
visits30d |
number | 近 30 天到店 | ||
avgSpend |
string | 次均消费 | ||
spend60 |
最高消费 近60天 | spend60d |
string | 近 60 天消费总额 |
visits60d |
string | 近 60 天到店次数 | ||
highSpendTag |
boolean | 是否高消费标签 | ||
avgSpend |
string | 次均消费 | ||
freq60 |
最频繁 近60天 | visits60d |
string | 近 60 天到店次数(右上角大字) |
avgInterval |
string | 平均到店间隔(如 "5.0天") |
||
weeklyVisits |
array | 8 周到店柱状图 [{ val: number, pct: number }] |
||
spend60d |
string | 近 60 天消费 | ||
loyal |
最专一 近60天 | intimacy |
string | 亲密度指数 |
topCoachName |
string | 最亲密助教姓名 | ||
topCoachHeart |
number | 最亲密助教爱心分 | ||
topCoachScore |
string | 最亲密助教关系指数 | ||
coachName |
string | 主助教姓名 | ||
coachRatio |
string | 主助教占比(如 "78%") |
||
coachDetails |
array | 助教服务明细表 [{ name, cls, heartScore, badge?, avgDuration, serviceCount, coachSpend, relationIdx }] |
后端设计建议:与助教看板不同,客户看板 8 个维度的字段差异很大,且数据来源不同(8 张不同的 FDW 表)。建议后端按
dimension参数只查询和返回当前维度所需字段,切换维度时前端重新请求。这样避免一次查询 8 张表的性能开销。
交叉约束
- 无交叉限制:dimension 和 project 完全独立,可自由组合
交叉组合总数
- dimension(8) × project(5) = 40 种有效组合
前端传参(api.ts)
fetchBoardCustomers({ dimension, project, sort })
// 注意:sort 参数在 api.ts 签名中存在但前端未使用(无排序筛选 UI)
// page/pageSize 参数缺失,需前端补充(R8-4 决策)
⚠️ 前端 Bug:筛选变更不触发重新请求
onDimensionChange/onProjectChange 仅更新 data 状态,未调用 loadData()。联调时前端需修复。
⚠️ 前端缺失:分页参数
R8-4 已确认 20 条懒加载,但 fetchBoardCustomers 签名和 board-customer.ts 均无 page/pageSize 参数和"加载更多"逻辑。联调时前端需补充。
B3:BOARD-3 财务看板(board-finance)
筛选项清单
| 参数名 | 类型 | 可选值 | 默认值 | 说明 |
|---|---|---|---|---|
time |
时间范围 | month、lastMonth、week、lastWeek、quarter3、quarter、lastQuarter、half6 |
month |
8 种时间范围 |
area |
区域筛选 | all、hall、hallA、hallB、hallC、mahjong、teamBuilding |
all |
7 种区域 |
compareEnabled |
环比开关 | true / false |
false |
控制环比数据显示/隐藏 |
6 个板块及筛选影响
| 板块 | sectionId | 受 time 影响 | 受 area 影响 | 受 compare 影响 | 特殊规则 |
|---|---|---|---|---|---|
| 经营一览 | section-overview | ✅ | ✅ | ✅ | 无 |
| 预收资产 | section-recharge | ✅ | ⚠️ | ✅ | 仅 area=all 时显示,选中具体区域时整个板块隐藏 |
| 应计收入确认 | section-revenue | ✅ | ✅ | ✅ | 含收入结构表(按区域分行)、正价明细、优惠明细、渠道明细 |
| 现金流入 | section-cashflow | ✅ | ✅ | ✅ | 无 |
| 现金流出 | section-expense | ✅ | ✅ | ✅ | 含经营支出、固定支出、助教分成、平台服务费 4 个子分组 |
| 助教分析 | section-coach | ✅ | ✅ | ✅ | 含基础课、激励课两个子表(各按等级分行) |
环比开关行为
| 状态 | 行为 |
|---|---|
compareEnabled=false(默认) |
仅显示当期数值,隐藏所有环比箭头和百分比 |
compareEnabled=true |
每个数据单元格下方显示环比方向+百分比 |
环比样式规则:
- 上升:绿色 ↑ + 百分比(如
↑12.5%) - 下降:红色 ↓ + 百分比(如
↓3.2%) - 持平:灰色文字
持平
环比计算基准:与上一个相同时间周期对比(本月 vs 上月、本周 vs 上周、本季度 vs 上季度等)
交叉约束
| 约束 | 说明 |
|---|---|
area ≠ all → 预收资产板块隐藏 |
储值卡数据不按区域拆分,选中具体区域时无意义 |
compareEnabled 独立 |
与 time/area 无交叉限制 |
| time + area 自由组合 | 无其他限制 |
交叉组合总数
- time(8) × area(7) × compare(2) = 112 种组合
- 其中 area≠all 时预收资产板块隐藏(不影响请求参数,仅影响前端渲染)
⚠️ 重大设计缺陷:筛选参数不传后端
当前 fetchBoardFinance 签名:
fetchBoardFinance({ date?: string }) // 仅 date 参数
前端 onTimeChange/onAreaChange/toggleCompare 仅更新本地 data 状态,不重新调用 API。mock 模式下不需要重新请求(数据内联),但联调后必须:
fetchBoardFinance签名扩展为{ time, area, compareEnabled }三个参数- 筛选变更后重新调用
fetchBoardFinance - 后端根据
time计算日期范围、根据area过滤区域、根据compareEnabled决定是否返回环比数据
后端 API 参数设计建议
GET /api/xcx/board/finance?time={time}&area={area}&compare={0|1}
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
time |
string | 否 | month |
时间范围枚举 |
area |
string | 否 | all |
区域枚举 |
compare |
0/1 | 否 | 0 |
是否返回环比数据(减少不需要时的计算开销) |
前端修改需求汇总(联调前必须完成)
看板页修复(F1-F6)
| # | 页面 | 问题 | 修改内容 |
|---|---|---|---|
| F1 | board-coach | 筛选变更不触发重新请求 | onSortChange/onSkillChange/onTimeChange 末尾加 this.loadData() |
| F2 | board-customer | 筛选变更不触发重新请求 | onDimensionChange/onProjectChange 末尾加 this.loadData() |
| F3 | board-customer | 缺少分页参数 | fetchBoardCustomers 签名加 page/pageSize,页面加"加载更多"逻辑 |
| F4 | board-finance | 筛选参数不传后端 | fetchBoardFinance 签名扩展为 { time, area, compare },筛选变更后重新调用 |
| F5 | board-finance | 筛选变更不触发重新请求 | onTimeChange/onAreaChange 末尾加 this.loadData() |
| F6 | board-coach | time=last_6m + sort=sv_desc 不兼容 |
前端在选择 last_6m 时禁用 sv_desc 选项,或选择 sv_desc 时禁用 last_6m |
列表页修复(F7-F11)
| # | 页面 | 问题 | 修改内容 |
|---|---|---|---|
| F7 | task-list | status 筛选 UI 未实现 | API 支持 status 参数但页面无筛选控件,需添加 Tab 或筛选栏 |
| F8 | performance | 无月份切换功能 | API 支持 year/month 参数但页面固定当前月,需添加月份切换按钮 |
| F9 | performance-records | 月份切换时未重置分页 | switchMonth() 中需将 page 重置为 1 |
| F10 | customer-service-records | 月份切换采用本地筛选 | updateMonthView() 仅本地过滤,联调后需改为重新调用 API |
| F11 | notes | 无触底加载逻辑 | API 支持分页但页面未实现 onReachBottom() |
非看板列表页筛选矩阵
L1:task-list(任务列表)
| 参数名 | 类型 | 可选值 | 默认值 | UI 实现 | 说明 |
|---|---|---|---|---|---|
status |
string | pending、completed、abandoned |
无(全部) | ❌ 缺失 | API 支持但页面无筛选控件 |
page |
number | 1+ | 1 |
✅ 自动 | 触底加载 |
pageSize |
number | 1+ | 20 |
✅ 硬编码 | — |
交互:下拉刷新重置 page=1;触底 page++。前端将任务分为 pinnedTasks / normalTasks / abandonedTasks 三组渲染。
L2:performance(绩效概览)
| 参数名 | 类型 | 可选值 | 默认值 | UI 实现 | 说明 |
|---|---|---|---|---|---|
year |
number | 当前年 ± N | 当前年 | ❌ 缺失 | 固定当前年 |
month |
number | 1-12 | 当前月 | ❌ 缺失 | 固定当前月 |
交互:无筛选 UI,页面加载时固定请求当前年月。无分页。
L3:performance-records(绩效明细)
| 参数名 | 类型 | 可选值 | 默认值 | UI 实现 | 说明 |
|---|---|---|---|---|---|
year |
number | 当前年 ± N | 当前年 | ✅ 月份切换按钮 | 上月/下月箭头 |
month |
number | 1-12 | 当前月 | ✅ 月份切换按钮 | 不可超过当前月 |
page |
number | 1+ | 1 |
✅ 自动 | 触底加载 |
pageSize |
number | 1+ | 20 |
✅ 硬编码 | — |
月份切换详细交互:
- 页面顶部显示
{year}年{month}月标签,左右各有箭头按钮 - 点击左箭头 →
currentMonth--(跨年自动currentYear--,currentMonth=12) - 点击右箭头 →
currentMonth++(跨年自动currentYear++,currentMonth=1) - 边界:
canGoNext = false当currentYear == nowYear && currentMonth == nowMonth(不可超过当前月);canGoPrev = true(无下限) - 月份变更后 → 调用
loadData()→ 重新请求 APIfetchPerformanceRecords({ year, month, page, pageSize }) - ⚠️ Bug:
switchMonth()中未将page重置为 1,月份切换后可能请求第 N 页数据
后端响应结构(按月返回):
summary:当月汇总(总笔数、总工时、折算前总工时、总收入)date_groups:按日期降序分组,每组含日期标签、当日总工时/总收入、记录列表- 每条记录字段:
id、customerName、timeRange(如"20:00-22:00")、hours(折算后)、hoursRaw(折算前,可选)、courseType、courseTypeClass(tag-basic/tag-vip/tag-tip)、location(台号)、income
后端时间范围处理:
- 接收
year+month→ 转换为biz_date BETWEEN '{year}-{month}-01' AND '{year}-{month}-{lastDay}' - 数据源:
fdw_etl.v_dwd_assistant_service_log(按assistant_id+biz_date范围查询)
L4:customer-service-records(客户服务记录)
| 参数名 | 类型 | 可选值 | 默认值 | UI 实现 | 说明 |
|---|---|---|---|---|---|
customerId |
string | 客户 ID | 从 options 读取 | ✅ 自动 | 页面参数 |
year |
number | 当前年 ± N | 当前年 | ✅ 月份切换按钮 | 上月/下月箭头 |
month |
number | 1-12 | 当前月 | ✅ 月份切换按钮 | 不可超过当前月 |
table |
string | 台号 | 无 | ❌ 缺失 | API 支持但页面未使用 |
月份切换详细交互:
- 页面顶部显示
{year}年{month}月标签,左右各有箭头按钮 - 点击左箭头 →
currentMonth--(跨年处理同 L3) - 点击右箭头 →
currentMonth++(跨年处理同 L3) - 边界:
canPrev=yearMonth > minYearMonth(最早 12 个月前);canNext=yearMonth < maxYearMonth(不超过当前月) - 月份变更后 → 调用
updateMonthView()→ 仅本地过滤(从allRecords中按date.startsWith('{year}-{month}')筛选) - ⚠️ 设计问题:首次
loadData()一次性拉取全部记录,月份切换仅本地过滤。联调后如果数据量大(单客户数百条记录),需改为按月请求 API
本地筛选逻辑:
allRecords(首次加载时获取全部)→ 按date字段前缀{year}-{month}过滤 → 转换为ServiceRecord展示格式- 月度统计:
monthCount(当月记录数)、monthHours(当月总时长) - 记录字段:
table(台号)、type(课程类型标签)、typeClass(basic/vip/tip/recharge)、recordType(course/recharge)、duration(折算后小时)、income(到手金额)、drinks(饮品描述)、date(显示日期+时间段)
后端时间范围处理(联调后建议改为按月请求):
- 当前方案:一次返回全部记录,前端本地按月过滤
- 建议方案:
fetchCustomerRecords({ customerId, year, month })→ 后端按月查询fdw_etl.v_dwd_assistant_service_log(按member_id+assistant_id+biz_date范围)
L5:notes(备注列表)
| 参数名 | 类型 | 可选值 | 默认值 | UI 实现 | 说明 |
|---|---|---|---|---|---|
page |
number | 1+ | 1 |
✅ 自动 | 下拉刷新重置 |
pageSize |
number | 1+ | 20 |
✅ 硬编码 | — |
交互:下拉刷新重置 page=1。⚠️ 无触底加载(onReachBottom() 未实现)。
L6:customer-detail / coach-detail(详情页)
无筛选项,单次加载全部数据。
八⅞、Storyboard 走查 Gap 汇总(2026-03-18)
走查方法:两个子代理分别以助教视角和管理层视角,逐页面对照前端内联 mock 数据 vs API 契约 vs 八¾节字段定义,提取所有未记录的接口需求 Gap。 完整报告:
docs/reports/storyboard-walkthrough-assistant-view.md(助教视角,51 Gap)+docs/reports/miniprogram-storyboard-walkthrough-gaps.md(管理层视角,31 Gap)
统计
| 严重度 | 助教视角 | 管理层视角 | 去重后合计 |
|---|---|---|---|
| 🔴 高(阻塞联调) | 17 | 14 | ~20 |
| 🟠 中(影响完整性) | 19 | 12 | ~18 |
| 🟡 低(可后续修复) | 15 | 5 | ~12 |
核心发现
- 契约与前端严重脱节:BOARD-1/2/3、CUST-1、COACH-1、PERF-1、TASK-1 performance 的契约响应定义与前端实际需求严重不匹配,需完全重写
- BOARD-3 财务看板:契约定义扁平
metrics数组,前端需要 6 个独立板块(overview/recharge/revenue/cashflow/expense/coachAnalysis)的深度嵌套结构,含 200+ 字段 - CUST-1 客户详情:缺少 5 大模块(aiInsight/coachTasks/favoriteCoaches/消费记录嵌套结构/备注列表)
- COACH-1 助教详情:缺少 6 大模块(performance/income/tierNodes/historyMonths/topCustomers 扩展/备注列表)
- 跨页面参数传递:多处 taskId/customerId 混淆、未传参数、用 name 代替 id 跳转(用户决策:统一用唯一 ID)
- CHAT 模块:referenceCard 未定义、customerId→chatId 映射缺失、SSE 端点未在契约中定义
处置方案
全部 Gap 已纳入 RNS1 拆分计划,分 5 个子 Spec 逐步实施。Gap 追溯矩阵见拆分计划末尾。
九、任务清单
⚠️ 已拆分:原始任务清单(T0-1 ~ T18)已被 Storyboard 走查发现的 80+ 个 Gap 大幅扩展,体量超出单个 SPEC 可控范围。
完整的拆分计划见 RNS1-split-plan.md,将 NS1 拆为 5 个子 Spec:
子 Spec 名称 任务数 状态 RNS1.0 基础设施与契约重写 6 待启动 RNS1.1 任务与绩效接口 6 待启动 RNS1.2 客户与助教接口 6 待启动 RNS1.3 三看板接口 7 待启动 RNS1.4 CHAT 对齐与联调收尾 5 待启动 执行顺序:RNS1.0 → (RNS1.1 ∥ RNS1.2 ∥ RNS1.3) → RNS1.4
需求追溯:两份走查报告(
docs/reports/storyboard-walkthrough-assistant-view.md+docs/reports/miniprogram-storyboard-walkthrough-gaps.md)中的全部 Gap 已在拆分计划的追溯矩阵中映射到具体任务。
原始任务清单(归档参考)
点击展开原始 Batch 0~C 任务清单(已被 RNS1 拆分计划替代)
Batch 0:基础设施改造(阻塞所有新接口)
- T0-1:全局响应包装中间件(
{ code: 0, data: ... }+ 异常处理器{ code, message }) - T0-2:Pydantic schema 统一 camelCase(
alias_generator=to_camel,所有现有 + 新增 schema) - T0-3:后端 TASK-4 路径从
/cancel-abandon改为/restore(对齐契约) - T0-4:前端
request()工具函数加.data解包适配
Batch A:核心业务接口
- T1:扩展 TASK-1(任务列表 + 绩效概览 + 分页 + 懒加载)
- T2:实现 TASK-2(任务详情完整版,服务记录/备注各 20 条懒加载)
- T3:实现 PERF-1(绩效概览)
- T4:实现 PERF-2(绩效明细,20 条懒加载)
- T5:实现 CUST-1(客户详情,消费记录 3 类混合排序 + 懒加载)
- T6:实现 CUST-2(客户服务记录,20 条懒加载)
- T7:实现 COACH-1(助教详情,任务按 active/inactive/abandoned 分组)
Batch B:看板 + 配置接口
- T8:实现 BOARD-1(助教看板,FDW 直查)
- T9:实现 BOARD-2(客户看板,8 维度切换,20 条懒加载,FDW 直查)
- T10:实现 BOARD-3(财务看板,60+ 指标月环比计算,FDW 直查)
- T11:实现 CONFIG-1(技能类型列表,从 cfg 表读取)
Batch C:CHAT 对齐、前端修复与联调
- T12:CHAT 路径迁移(
/api/ai/*→/api/xcx/chat/*,保留 SSE 流式端点) - T13:新增 CHAT-1/2 同步端点(历史列表 + 消息查看)
- T14:API 契约补充(SSE 端点定义 + BOARD-3
compareValue字段 + BOARD-3time/area/compare参数) - T15:FDW 端到端验证
- T16:前端懒加载改造(TASK-2 服务记录/备注、BOARD-2 客户列表)
- T17:前端看板筛选修复(F1-F6,详见八¾节)
- T18:前后端联调 + 修复