feat: batch update - gift card breakdown spec, backend APIs, miniprogram pages, ETL finance recharge, docs & migrations
This commit is contained in:
997
docs/prd/Neo_Specs/NS1-xcx-backend-api.md
Normal file
997
docs/prd/Neo_Specs/NS1-xcx-backend-api.md
Normal file
@@ -0,0 +1,997 @@
|
||||
# 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_id` JOIN `dim_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_summary`
|
||||
- `fdw_etl.v_dws_finance_income_structure`
|
||||
- `fdw_etl.v_dws_finance_recharge_summary`
|
||||
- `fdw_etl.v_dws_finance_discount_detail`
|
||||
- `fdw_etl.v_dws_finance_expense_summary`
|
||||
- `fdw_etl.v_dws_platform_settlement`
|
||||
- `biz.ai_cache`(app2_finance 缓存,AI 洞察)
|
||||
|
||||
### 3.7 CONFIG-1:技能类型列表
|
||||
|
||||
静态配置接口,返回助教技能类型列表。数据来源待定(硬编码 or cfg 表)。
|
||||
|
||||
### 3.8 CHAT 路径对齐
|
||||
|
||||
现有 `xcx_ai_chat.py` 路径:
|
||||
- `POST /api/ai/chat/stream`
|
||||
- `GET /api/ai/conversations`
|
||||
- `GET /api/ai/conversations/{id}/messages`
|
||||
|
||||
契约定义路径:
|
||||
- `GET /api/xcx/chat/history`
|
||||
- `GET /api/xcx/chat/{chatId}/messages`
|
||||
- `POST /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 表。
|
||||
|
||||
```sql
|
||||
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 表。
|
||||
|
||||
```sql
|
||||
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`(财务看板快照)
|
||||
|
||||
用途:预聚合财务看板数据 + 环比计算结果。
|
||||
|
||||
```sql
|
||||
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+ 张表聚合,首次查询后缓存结果,后续读缓存。
|
||||
|
||||
```sql
|
||||
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 验证
|
||||
|
||||
```sql
|
||||
-- 设置门店隔离
|
||||
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 联调步骤
|
||||
|
||||
1. 后端接口开发完成后,逐个验证响应格式与 `API-contract.md` 一致
|
||||
2. 小程序 `services/api.ts` 中 `USE_REAL_API = true`
|
||||
3. 逐页面联调:login → task-list → task-detail → performance → board → customer-detail → chat
|
||||
4. 修复 snake_case → camelCase 映射差异
|
||||
5. 处理 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 接口设计
|
||||
|
||||
1. **响应包装格式**:现有后端直接返回数据对象,契约要求 `{ code: 0, data: ... }` 包装。是全局统一改造(中间件),还是仅新接口使用包装格式?
|
||||
2. **CHAT 路径对齐**:是修改后端路径从 `/api/ai/*` 改为 `/api/xcx/chat/*`,还是在前端 service 层适配现有路径?
|
||||
3. **分页策略**:TASK-1 契约要求分页,但现有实现返回全量列表。是否所有列表接口都需要分页?看板接口(前 100 名)是否需要分页?
|
||||
4. **CONFIG-1 技能类型**:数据来源是硬编码还是从 `cfg_*` 表读取?如果硬编码,具体的技能类型列表是什么?
|
||||
|
||||
### 8.2 数据库
|
||||
|
||||
5. **快照表刷新策略**:3 个看板快照表(coach/customer/finance)的刷新频率?是 ETL 数据更新后触发,还是每日定时刷新,还是请求时按需刷新?
|
||||
6. **任务详情缓存**:`task_detail_cache` 的过期时间设多久?缓存失效触发条件是否完整(备注新增、AI 缓存更新、任务状态变更)?
|
||||
7. **FDW 外部表命名**:现有代码中使用 `fdw_etl.v_dim_member`(带 `v_` 前缀),需确认所有 FDW 外部表的实际命名是否一致。
|
||||
|
||||
### 8.3 业务逻辑
|
||||
|
||||
8. **TASK-2 服务记录范围**:近期服务记录取最近多少条?最近 30 天还是最近 10 条?
|
||||
9. **BOARD-2 客户看板**:8 个维度排序是否都取前 100 名?是否需要支持类型筛选(如按会员等级筛选)?
|
||||
10. **BOARD-3 财务看板**:环比计算的基准是什么?日环比、周环比、月环比都需要吗?
|
||||
11. **CUST-1 消费记录**:3 种消费类型(台桌/商城/充值)是混合排序还是分类展示?分页策略?
|
||||
12. **COACH-1 任务统计**:可见/隐藏/已放弃的分类逻辑是什么?"隐藏"指什么状态?
|
||||
|
||||
### 8.4 性能与安全
|
||||
|
||||
13. **FDW 查询性能**:看板接口涉及多张 FDW 表的聚合查询,是否需要设置查询超时?超时后返回什么?
|
||||
14. **维客线索脱敏**:TASK-2 返回维客线索时,后端脱敏规则是否与 P6 spec 中定义的一致(移除 recorded_by_assistant_id/name)?
|
||||
15. **金额口径确认**:所有涉及消费金额的接口,确认使用 `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`)
|
||||
|
||||
实施路径:
|
||||
1. 新增全局响应包装中间件(或 `response_model` 基类),成功响应统一 `{ code: 0, data: ... }`
|
||||
2. 新增全局异常处理器,`HTTPException` → `{ code: status_code, message: detail }`
|
||||
3. 所有 Pydantic schema 加 `model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)`
|
||||
4. 前端 `request()` 工具函数加 `.data` 解包(一处改动)
|
||||
5. 已有接口(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.ts` service 层交叉验证。
|
||||
|
||||
### 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`)
|
||||
```typescript
|
||||
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`)
|
||||
```typescript
|
||||
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` 签名:
|
||||
```typescript
|
||||
fetchBoardFinance({ date?: string }) // 仅 date 参数
|
||||
```
|
||||
|
||||
前端 `onTimeChange`/`onAreaChange`/`toggleCompare` 仅更新本地 data 状态,**不重新调用 API**。mock 模式下不需要重新请求(数据内联),但联调后必须:
|
||||
|
||||
1. `fetchBoardFinance` 签名扩展为 `{ time, area, compareEnabled }` 三个参数
|
||||
2. 筛选变更后重新调用 `fetchBoardFinance`
|
||||
3. 后端根据 `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` | ✅ 硬编码 | — |
|
||||
|
||||
月份切换详细交互:
|
||||
1. 页面顶部显示 `{year}年{month}月` 标签,左右各有箭头按钮
|
||||
2. 点击左箭头 → `currentMonth--`(跨年自动 `currentYear--`,`currentMonth=12`)
|
||||
3. 点击右箭头 → `currentMonth++`(跨年自动 `currentYear++`,`currentMonth=1`)
|
||||
4. 边界:`canGoNext = false` 当 `currentYear == nowYear && currentMonth == nowMonth`(不可超过当前月);`canGoPrev = true`(无下限)
|
||||
5. 月份变更后 → 调用 `loadData()` → 重新请求 API `fetchPerformanceRecords({ year, month, page, pageSize })`
|
||||
6. ⚠️ 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 支持但页面未使用 |
|
||||
|
||||
月份切换详细交互:
|
||||
1. 页面顶部显示 `{year}年{month}月` 标签,左右各有箭头按钮
|
||||
2. 点击左箭头 → `currentMonth--`(跨年处理同 L3)
|
||||
3. 点击右箭头 → `currentMonth++`(跨年处理同 L3)
|
||||
4. 边界:`canPrev` = `yearMonth > minYearMonth`(最早 12 个月前);`canNext` = `yearMonth < maxYearMonth`(不超过当前月)
|
||||
5. 月份变更后 → 调用 `updateMonthView()` → **仅本地过滤**(从 `allRecords` 中按 `date.startsWith('{year}-{month}')` 筛选)
|
||||
6. ⚠️ 设计问题:首次 `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 |
|
||||
|
||||
### 核心发现
|
||||
|
||||
1. **契约与前端严重脱节**:BOARD-1/2/3、CUST-1、COACH-1、PERF-1、TASK-1 performance 的契约响应定义与前端实际需求严重不匹配,需完全重写
|
||||
2. **BOARD-3 财务看板**:契约定义扁平 `metrics` 数组,前端需要 6 个独立板块(overview/recharge/revenue/cashflow/expense/coachAnalysis)的深度嵌套结构,含 200+ 字段
|
||||
3. **CUST-1 客户详情**:缺少 5 大模块(aiInsight/coachTasks/favoriteCoaches/消费记录嵌套结构/备注列表)
|
||||
4. **COACH-1 助教详情**:缺少 6 大模块(performance/income/tierNodes/historyMonths/topCustomers 扩展/备注列表)
|
||||
5. **跨页面参数传递**:多处 taskId/customerId 混淆、未传参数、用 name 代替 id 跳转(用户决策:统一用唯一 ID)
|
||||
6. **CHAT 模块**:referenceCard 未定义、customerId→chatId 映射缺失、SSE 端点未在契约中定义
|
||||
|
||||
### 处置方案
|
||||
|
||||
全部 Gap 已纳入 **[RNS1 拆分计划](./RNS1-split-plan.md)**,分 5 个子 Spec 逐步实施。Gap 追溯矩阵见拆分计划末尾。
|
||||
|
||||
---
|
||||
|
||||
## 九、任务清单
|
||||
|
||||
> ⚠️ **已拆分**:原始任务清单(T0-1 ~ T18)已被 Storyboard 走查发现的 80+ 个 Gap 大幅扩展,体量超出单个 SPEC 可控范围。
|
||||
>
|
||||
> 完整的拆分计划见 **[RNS1-split-plan.md](./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 已在拆分计划的追溯矩阵中映射到具体任务。
|
||||
|
||||
### 原始任务清单(归档参考)
|
||||
|
||||
<details>
|
||||
<summary>点击展开原始 Batch 0~C 任务清单(已被 RNS1 拆分计划替代)</summary>
|
||||
|
||||
#### 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-3 `time`/`area`/`compare` 参数)
|
||||
- [ ] T15:FDW 端到端验证
|
||||
- [ ] T16:前端懒加载改造(TASK-2 服务记录/备注、BOARD-2 客户列表)
|
||||
- [ ] T17:前端看板筛选修复(F1-F6,详见八¾节)
|
||||
- [ ] T18:前后端联调 + 修复
|
||||
|
||||
</details>
|
||||
419
docs/prd/Neo_Specs/NS2-ai-prompt-refinement.md
Normal file
419
docs/prd/Neo_Specs/NS2-ai-prompt-refinement.md
Normal file
@@ -0,0 +1,419 @@
|
||||
# NS2:AI Prompt 细化 — ai-prompt-refinement
|
||||
|
||||
> 优先级:高(NS1 完成后立即执行,Prompt 细化是 AI 功能闭环的关键)
|
||||
> 预估工作量:中等
|
||||
> 前置条件:P5-A 已完成(AI 骨架就绪)、NS1 已完成(后端 API 数据结构确定)
|
||||
> 参考基准:`docs/prd/specs/P5-miniapp-ai-integration.md`(P5-B 阶段定义)
|
||||
|
||||
---
|
||||
|
||||
## 一、背景与目标
|
||||
|
||||
P5-A 阶段已交付 AI 集成管道:百炼封装、缓存 API、SSE 框架、应用 2/8 完整实现、应用 1/3/4/5/6/7 的触发机制和调用骨架。但应用 1/3/4/5/6/7 的 `build_prompt()` 函数仍为占位骨架,核心数据字段(`consumption_records`、`service_history`、`objective_data` 等)标记为 TODO。
|
||||
|
||||
本 SPEC 目标:将 6 个应用的 Prompt 拼接函数从骨架升级为完整实现,使 AI 应用能基于真实数据生成有价值的分析结果。
|
||||
|
||||
### 当前骨架状态
|
||||
|
||||
| 应用 | 文件 | 骨架状态 | 待细化字段 |
|
||||
|------|------|---------|-----------|
|
||||
| 应用 1 | `app1_chat.py` | 页面上下文文本化工具留接口 | `page_context`、`screen_content` 各页面文本化 |
|
||||
| 应用 3 | `app3_clue.py` | `build_prompt()` 占位 | `consumption_records`(DWD+DWS 订单明细全维度) |
|
||||
| 应用 4 | `app4_analysis.py` | `build_prompt()` 占位 | `service_history`、`assistant_info` |
|
||||
| 应用 5 | `app5_tactics.py` | `build_prompt()` 占位 | `service_history`、`assistant_info`(同应用 4) |
|
||||
| 应用 6 | `app6_note.py` | `build_prompt()` 占位 | `consumption_data`(同应用 3 的 main_data) |
|
||||
| 应用 7 | `app7_customer.py` | `build_prompt()` 占位 | `objective_data`(同应用 3 的 main_data) |
|
||||
|
||||
---
|
||||
|
||||
## 二、技术架构
|
||||
|
||||
### 2.1 数据获取层(新建共享模块)
|
||||
|
||||
Prompt 细化的核心是从数据库获取真实数据并格式化为 AI 可读的 JSON。多个应用共享相同的数据获取逻辑,应抽取为共享模块:
|
||||
|
||||
```
|
||||
apps/backend/app/ai/
|
||||
├── data_fetchers/ 🆕 新建
|
||||
│ ├── __init__.py
|
||||
│ ├── member_data.py # 客户数据获取(消费记录、会员卡、余额等)
|
||||
│ ├── assistant_data.py # 助教数据获取(基本信息、服务历史)
|
||||
│ ├── service_history.py # 服务记录获取(助教-客户维度)
|
||||
│ └── page_context.py # 页面上下文文本化(应用 1 专用)
|
||||
├── apps/
|
||||
│ ├── app1_chat.py 🔧 补充页面文本化调用
|
||||
│ ├── app3_clue.py 🔧 补充 consumption_records 拼接
|
||||
│ ├── app4_analysis.py 🔧 补充 service_history + assistant_info
|
||||
│ ├── app5_tactics.py 🔧 补充 service_history + assistant_info
|
||||
│ ├── app6_note.py 🔧 补充 consumption_data
|
||||
│ └── app7_customer.py 🔧 补充 objective_data
|
||||
```
|
||||
|
||||
### 2.2 数据获取函数设计
|
||||
|
||||
#### `member_data.py` — 客户数据获取(应用 3/6/7 共用)
|
||||
|
||||
```python
|
||||
async def fetch_member_consumption_data(
|
||||
site_id: int, member_id: int, months: int = 3
|
||||
) -> dict:
|
||||
"""获取客户近 N 个月消费数据(DWD+DWS 全维度)。
|
||||
|
||||
返回结构对应 P5 spec 中 main_data:
|
||||
- consumption_records: 台桌结账 + 商城订单明细列表
|
||||
- member_cards: 会员卡明细
|
||||
- card_balance_total: 储值卡余额合计
|
||||
- stored_value_balance_total: 储值余额合计
|
||||
- expected_visit_date: 预计到店日期
|
||||
- days_since_last_visit: 距上次到店天数
|
||||
"""
|
||||
```
|
||||
|
||||
数据源(全部通过 FDW):
|
||||
- `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`(充值记录)
|
||||
- `fdw_etl.v_dim_member_card_account`(会员卡明细)
|
||||
- `fdw_etl.v_dws_member_consumption_summary`(消费汇总 + 余额)
|
||||
- `fdw_etl.v_dws_member_visit_detail`(到店明细 → 计算预计到店日期)
|
||||
|
||||
> ⚠️ 金额口径:使用 `items_sum`(= table_charge_money + goods_money + assistant_pd_money + assistant_cx_money + electricity_money),禁止 `consume_money`
|
||||
> ⚠️ 会员字段断档(DQ-6/DQ-7):member_phone/member_name 通过 member_id JOIN dim_member
|
||||
|
||||
#### `assistant_data.py` — 助教数据获取(应用 4/5 共用)
|
||||
|
||||
```python
|
||||
async def fetch_assistant_info(
|
||||
site_id: int, assistant_id: int
|
||||
) -> dict:
|
||||
"""获取助教基本信息(花名、级别、工龄等)。"""
|
||||
|
||||
async def fetch_service_history(
|
||||
site_id: int, assistant_id: int, member_id: int, months: int = 3
|
||||
) -> list[dict]:
|
||||
"""获取助教服务该客户的历史记录。"""
|
||||
```
|
||||
|
||||
数据源:
|
||||
- `fdw_etl.v_dim_assistant`(基本信息)
|
||||
- `fdw_etl.v_dwd_assistant_service_log`(服务记录,按助教+客户筛选)
|
||||
- `fdw_etl.v_dws_member_assistant_relation_index`(关系指数)
|
||||
- `fdw_etl.v_dws_member_assistant_intimacy`(亲密度)
|
||||
|
||||
#### `page_context.py` — 页面上下文文本化(应用 1 专用)
|
||||
|
||||
```python
|
||||
async def build_page_text(
|
||||
source_page: str, context_data: dict, site_id: int
|
||||
) -> str:
|
||||
"""将页面数据转换为 AI 可读的结构化文本。
|
||||
|
||||
根据 source_page 类型,从数据库获取对应数据并格式化。
|
||||
"""
|
||||
```
|
||||
|
||||
支持的页面类型及数据获取:
|
||||
| source_page | 数据获取 | 文本化内容 |
|
||||
|-------------|---------|-----------|
|
||||
| `task-detail` | coach_tasks + member + notes + ai_cache | 任务信息 + 客户信息 + 备注 + AI 分析 |
|
||||
| `customer-detail` | member + consumption + clues | 客户全信息 + 消费记录 + 维客线索 |
|
||||
| `coach-detail` | assistant + tasks + notes | 助教信息 + 任务统计 + 备注 |
|
||||
| `board-finance` | finance DWS 汇总 | 财务数据摘要 |
|
||||
| `board-customer` | 当前筛选维度 top 列表 | 客户排名摘要 |
|
||||
| `board-coach` | 当前筛选维度排名 | 助教排名摘要 |
|
||||
| `performance` | salary_calc + daily_detail | 绩效数据摘要 |
|
||||
| `my-profile` | user info + assistant binding | 个人信息摘要 |
|
||||
|
||||
### 2.3 数据库连接模式
|
||||
|
||||
- 所有 FDW 查询通过 `get_etl_readonly_connection(site_id)` 或 `fdw_etl.*` 视图
|
||||
- 业务库查询通过 `get_connection()`
|
||||
- 查询前必须 `SET LOCAL app.current_site_id`(RLS 隔离)
|
||||
|
||||
---
|
||||
|
||||
## 三、各应用 Prompt 细化详细设计
|
||||
|
||||
### 3.1 应用 3:客户数据维客线索分析
|
||||
|
||||
`build_prompt()` 需要拼接的完整 JSON 结构(参考 P5 spec):
|
||||
|
||||
```json
|
||||
{
|
||||
"current_time": "2026-03-08 14:30:25",
|
||||
"member_nickname": "客户昵称",
|
||||
"main_data": {
|
||||
"consumption_records": [
|
||||
{
|
||||
"settle_date": "2026-03-05",
|
||||
"settle_type": 1,
|
||||
"items_sum": 280.00,
|
||||
"table_charge_money": 180.00,
|
||||
"assistant_pd_money": 80.00,
|
||||
"assistant_cx_money": 0,
|
||||
"goods_money": 20.00,
|
||||
"room_name": "VIP-3",
|
||||
"duration_minutes": 120,
|
||||
"assistant_names": ["张助教"]
|
||||
}
|
||||
],
|
||||
"member_cards": [
|
||||
{"card_type": "金卡", "balance": 1500.00, "gift_balance": 200.00}
|
||||
],
|
||||
"card_balance_total": 1700.00,
|
||||
"stored_value_balance_total": 1700.00,
|
||||
"expected_visit_date": "2026-03-10",
|
||||
"days_since_last_visit": 15
|
||||
},
|
||||
"reference": {
|
||||
"app6_clues": [...],
|
||||
"app8_history": [
|
||||
{"generated_at": "2026-03-01", "clues": [...]},
|
||||
{"generated_at": "2026-02-15", "clues": [...]}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
实现要点:
|
||||
- `consumption_records` 从 `v_dwd_settlement_head` + `v_dwd_table_fee_log` 获取,settle_type IN (1,3)
|
||||
- 金额字段逐项拆分(table_charge_money / assistant_pd_money / assistant_cx_money / goods_money),不使用 consume_money
|
||||
- `expected_visit_date` 从 `v_dws_member_visit_detail` 的到店间隔推算
|
||||
- reference 从 `ai_cache` 获取 app6 + app8 最近 2 套历史
|
||||
|
||||
### 3.2 应用 4:关系分析/任务建议
|
||||
|
||||
`build_prompt()` 需要拼接的完整 JSON 结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"current_time": "2026-03-08 14:30:25",
|
||||
"assistant_info": {
|
||||
"nickname": "张助教",
|
||||
"level": "高级",
|
||||
"hire_date": "2024-06-01",
|
||||
"tenure_months": 21,
|
||||
"monthly_customers": 45,
|
||||
"performance_tier": "A档"
|
||||
},
|
||||
"service_history": [
|
||||
{
|
||||
"service_date": "2026-03-01",
|
||||
"duration_minutes": 90,
|
||||
"items_sum": 250.00,
|
||||
"room_name": "VIP-3",
|
||||
"is_pd": true
|
||||
}
|
||||
],
|
||||
"task_assignment_basis": "优先召回",
|
||||
"customer_data": {
|
||||
"system_data": { /* 同应用 3 的 main_data */ },
|
||||
"notes": [
|
||||
{"recorded_by": "张助教", "content": "客户喜欢打斯诺克", "created_at": "2026-02-28"}
|
||||
]
|
||||
},
|
||||
"reference": {
|
||||
"app8_current": {...},
|
||||
"app8_history": [...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
实现要点:
|
||||
- `assistant_info` 从 `v_dim_assistant` + `v_dws_assistant_salary_calc` 获取
|
||||
- `service_history` 从 `v_dwd_assistant_service_log` 按助教+客户筛选
|
||||
- `customer_data.system_data` 复用 `member_data.fetch_member_consumption_data()`
|
||||
- `customer_data.notes` 从 `biz.notes` 获取该客户的全部备注
|
||||
- reference 从 `ai_cache` 获取 app8 最新 + 最近 2 套历史
|
||||
|
||||
### 3.3 应用 5:话术参考
|
||||
|
||||
结构与应用 4 基本一致,额外增加 `task_suggestion` 字段(应用 4 的返回结果)。
|
||||
|
||||
实现要点:
|
||||
- 复用应用 4 的 `assistant_info` + `service_history` + `customer_data` 获取逻辑
|
||||
- `task_suggestion` 从应用 4 的 `run()` 返回值传入(调用链:应用 4 → 应用 5)
|
||||
|
||||
### 3.4 应用 6:备注分析
|
||||
|
||||
```json
|
||||
{
|
||||
"current_time": "2026-03-08 14:30:25",
|
||||
"current_note": {
|
||||
"content": "客户说下周要带朋友来打球",
|
||||
"recorded_by": "张助教",
|
||||
"created_at": "2026-03-08 14:25:00"
|
||||
},
|
||||
"reference": {
|
||||
"member_nickname": "王先生",
|
||||
"consumption_data": { /* 同应用 3 的 main_data */ },
|
||||
"all_notes": [...],
|
||||
"app3_clues": [...],
|
||||
"app8_history": [...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
实现要点:
|
||||
- `current_note` 从触发上下文获取(备注提交事件传入)
|
||||
- `consumption_data` 复用 `member_data.fetch_member_consumption_data()`
|
||||
- `all_notes` 从 `biz.notes` 获取所有助教对该客户的全部备注
|
||||
- reference 从 `ai_cache` 获取 app3 + app8 历史
|
||||
|
||||
### 3.5 应用 7:客户分析
|
||||
|
||||
```json
|
||||
{
|
||||
"current_time": "2026-03-08 14:30:25",
|
||||
"member_id": 12345,
|
||||
"member_nickname": "王先生",
|
||||
"objective_data": { /* 同应用 3 的 main_data */ },
|
||||
"subjective_data": {
|
||||
"notes": [
|
||||
{"recorded_by": "张助教", "content": "...", "created_at": "..."}
|
||||
]
|
||||
},
|
||||
"reference": {
|
||||
"app8_current": {...},
|
||||
"app8_history": [...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
实现要点:
|
||||
- `objective_data` 复用 `member_data.fetch_member_consumption_data()`
|
||||
- `subjective_data.notes` 从 `biz.notes` 获取全部备注,标注创建者
|
||||
- 主观信息需在 Prompt 中标注【来源:XXX,请甄别信息真实性】
|
||||
- reference 从 `ai_cache` 获取 app8 最新 + 最近 2 套历史
|
||||
|
||||
### 3.6 应用 1:页面上下文文本化
|
||||
|
||||
应用 1 的 `_build_page_context()` 和 `_build_source_context()` 需要根据 `source_page` 类型,调用对应的数据获取函数并格式化为文本。
|
||||
|
||||
实现要点:
|
||||
- 每个页面类型对应一个文本化函数
|
||||
- 文本化输出为结构化中文描述(非 JSON),便于 AI 理解
|
||||
- 数据量控制:每个页面上下文不超过 2000 字符,避免 token 浪费
|
||||
- 敏感信息脱敏:不传入 member_phone 等断档字段
|
||||
|
||||
---
|
||||
|
||||
## 四、数据库审查
|
||||
|
||||
### 4.1 数据获取涉及的表
|
||||
|
||||
| 数据获取函数 | 涉及表 | 连接方式 |
|
||||
|-------------|--------|---------|
|
||||
| `fetch_member_consumption_data` | v_dwd_settlement_head, v_dwd_table_fee_log, v_dwd_store_goods_sale, v_dwd_recharge_order, v_dim_member_card_account, v_dws_member_consumption_summary, v_dws_member_visit_detail | FDW |
|
||||
| `fetch_assistant_info` | v_dim_assistant, v_dws_assistant_salary_calc | FDW |
|
||||
| `fetch_service_history` | v_dwd_assistant_service_log, v_dws_member_assistant_relation_index, v_dws_member_assistant_intimacy | FDW |
|
||||
| `build_page_text` | 以上全部 + biz.coach_tasks, biz.notes, biz.ai_cache, public.member_retention_clue | FDW + 业务库 |
|
||||
|
||||
### 4.2 无需新建表
|
||||
|
||||
本 SPEC 不需要新建数据库表。所有数据获取基于 NS1 已建立的 FDW 映射和 P4/P5-A 已建立的业务表。
|
||||
|
||||
### 4.3 性能考虑
|
||||
|
||||
- `fetch_member_consumption_data` 涉及 7 张 FDW 表查询,建议:
|
||||
- 消费记录限制最近 3 个月(WHERE settle_date >= NOW() - INTERVAL '3 months')
|
||||
- 单次查询最多返回 100 条记录
|
||||
- 考虑使用 NS1 中建议的 `biz.task_detail_cache` 缓存聚合结果
|
||||
- 页面文本化(应用 1)需要实时获取数据,建议设置 5 秒查询超时
|
||||
|
||||
---
|
||||
|
||||
## 五、调用链与数据流
|
||||
|
||||
### 5.1 消费事件链
|
||||
|
||||
```
|
||||
新结算单到达
|
||||
→ 应用 3.build_prompt(member_data) → 百炼 API → ai_cache
|
||||
→ 应用 8.run() → member_retention_clue
|
||||
→ 应用 7.build_prompt(member_data + notes) → 百炼 API → ai_cache
|
||||
→ 应用 4.build_prompt(assistant_data + member_data) → 百炼 API → ai_cache
|
||||
→ 应用 5.build_prompt(app4_result) → 百炼 API → ai_cache
|
||||
```
|
||||
|
||||
### 5.2 备注提交事件链
|
||||
|
||||
```
|
||||
备注提交
|
||||
→ 应用 6.build_prompt(note + member_data) → 百炼 API → ai_cache
|
||||
→ 应用 8.run() → member_retention_clue
|
||||
```
|
||||
|
||||
### 5.3 应用 1 对话流
|
||||
|
||||
```
|
||||
用户点击 AI 入口
|
||||
→ 前端传入 source_page + context_data
|
||||
→ 后端 build_page_text(source_page, context_data)
|
||||
→ 拼接为首条 user message
|
||||
→ SSE 流式调用百炼 API
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、参考文档
|
||||
|
||||
| 文档 | 路径 | 用途 |
|
||||
|------|------|------|
|
||||
| P5 AI 集成 spec | `docs/prd/specs/P5-miniapp-ai-integration.md` | Prompt JSON 结构定义、调用链时序 |
|
||||
| API 契约 | `docs/miniprogram-dev/API-contract.md` | 接口响应格式(决定数据结构) |
|
||||
| DWD-DOC 标杆 | `docs/reports/DWD-DOC/` | 金额口径、字段语义权威参考 |
|
||||
| AI 应用骨架代码 | `apps/backend/app/ai/apps/app*.py` | 当前骨架实现 |
|
||||
| 百炼客户端 | `apps/backend/app/ai/bailian_client.py` | API 调用封装 |
|
||||
| 缓存服务 | `apps/backend/app/ai/cache_service.py` | ai_cache 读写 |
|
||||
| NS1 接口设计 | `docs/prd/Neo_Specs/NS1-xcx-backend-api.md` | 后端数据结构参考 |
|
||||
|
||||
---
|
||||
|
||||
## 七、预审查清单(SPEC 启动前确认)
|
||||
|
||||
### 7.1 数据结构
|
||||
|
||||
1. **消费记录字段范围**:`consumption_records` 中每条记录需要包含哪些字段?是否需要包含折扣信息(discount_manual/discount_other)?是否需要包含支付方式明细(balance_pay/cash_pay/online_pay)?
|
||||
2. **服务记录字段范围**:`service_history` 中每条记录需要包含哪些字段?是否需要包含台桌类型(room_category)和客户评价?
|
||||
3. **备注内容截断**:`all_notes` 中每条备注是否需要全文传入?长备注是否截断?截断长度?
|
||||
4. **会员卡明细粒度**:`member_cards` 是否需要包含卡号、开卡日期、有效期等详细信息,还是只需要卡类型和余额?
|
||||
|
||||
### 7.2 Prompt 优化
|
||||
|
||||
5. **Token 预算**:每个应用的首条 Prompt 的 token 上限是多少?百炼 API 的单次请求 token 限制?
|
||||
6. **数据时间窗口**:消费记录默认取近 3 个月,是否需要可配置?不同应用是否需要不同时间窗口?
|
||||
7. **空数据处理**:当客户无消费记录/无备注/无服务历史时,Prompt 如何处理?是否需要特殊提示词?
|
||||
|
||||
### 7.3 页面文本化(应用 1)
|
||||
|
||||
8. **文本化格式**:页面上下文是输出为结构化中文文本还是 JSON?AI 对哪种格式理解更好?
|
||||
9. **数据量控制**:每个页面上下文的字符上限?是否需要根据页面类型动态调整?
|
||||
10. **实时性要求**:应用 1 的页面上下文是否需要实时获取最新数据?还是可以使用缓存(如 task_detail_cache)?
|
||||
|
||||
### 7.4 性能与安全
|
||||
|
||||
11. **FDW 查询并发**:多个数据获取函数是否可以并发执行(asyncio.gather)?FDW 连接池是否支持?
|
||||
12. **数据脱敏**:传入百炼 API 的数据中,哪些字段需要脱敏?member_phone 已断档不传,还有其他敏感字段吗?
|
||||
13. **错误降级**:某个数据获取函数失败时(如 FDW 超时),是否跳过该部分继续生成 Prompt?还是整体失败?
|
||||
|
||||
---
|
||||
|
||||
## 八、任务清单(草案,SPEC 细化后调整)
|
||||
|
||||
### Batch A:共享数据获取层
|
||||
- [ ] T1:创建 `data_fetchers/member_data.py`(客户消费数据获取,应用 3/6/7 共用)
|
||||
- [ ] T2:创建 `data_fetchers/assistant_data.py`(助教信息 + 服务历史获取,应用 4/5 共用)
|
||||
- [ ] T3:创建 `data_fetchers/page_context.py`(页面上下文文本化框架,应用 1 专用)
|
||||
|
||||
### Batch B:Prompt 拼接实现
|
||||
- [ ] T4:完善 `app3_clue.py` 的 `build_prompt()`(客户消费数据 → 维客线索分析)
|
||||
- [ ] T5:完善 `app4_analysis.py` 的 `build_prompt()`(助教+客户数据 → 关系分析)
|
||||
- [ ] T6:完善 `app5_tactics.py` 的 `build_prompt()`(复用应用 4 数据 + task_suggestion)
|
||||
- [ ] T7:完善 `app6_note.py` 的 `build_prompt()`(备注+客户数据 → 备注分析)
|
||||
- [ ] T8:完善 `app7_customer.py` 的 `build_prompt()`(客户全量数据 → 运营策略)
|
||||
|
||||
### Batch C:应用 1 页面文本化 + 联调
|
||||
- [ ] T9:实现各页面类型的文本化函数(task-detail/customer-detail/board-*/performance 等)
|
||||
- [ ] T10:补充 `app1_chat.py` 的 `_build_page_context()` 调用文本化函数
|
||||
- [ ] T11:端到端联调(触发事件 → Prompt 拼接 → 百炼调用 → 缓存写入 → 前端展示)
|
||||
305
docs/prd/Neo_Specs/NS3-mcp-server-ai-extension.md
Normal file
305
docs/prd/Neo_Specs/NS3-mcp-server-ai-extension.md
Normal file
@@ -0,0 +1,305 @@
|
||||
# NS3:MCP Server 扩展 — mcp-server-ai-extension
|
||||
|
||||
> 优先级:中(可与 NS1/NS2 并行,批次 A 无前置依赖)
|
||||
> 预估工作量:中等
|
||||
> 前置条件:批次 A 无依赖;批次 B 依赖 P5-A(biz 表已建);批次 C 依赖批次 B
|
||||
> 参考基准:`docs/prd/specs/P5.1-mcp-server-ai-extension.md`
|
||||
|
||||
---
|
||||
|
||||
## 一、背景与目标
|
||||
|
||||
当前 MCP Server(`apps/mcp-server/server.py`)仅连接 `etl_feiqiu` 数据仓库,提供 4 个工具(list_tables、describe_table、describe_schemas、query_sql),schema 白名单为 ods/dwd/dws/core/meta/app。SQL 安全通过正则禁词检测实现只读保护。
|
||||
|
||||
查库手册(`docs/mcp/AI-DATABASE-QUERY-MANUAL.md`)内容陈旧,DWS 层 34 张表缺少完整字段说明,业务库(zqyy_app)完全未覆盖。
|
||||
|
||||
本 SPEC 目标:
|
||||
1. MCP Server 新增 `zqyy_app` 业务库连接,支持多数据库路由
|
||||
2. 实现业务库敏感字段脱敏策略
|
||||
3. 重写查库手册(ETL 全字段 + 业务库全字段 + 常用查询模式)
|
||||
4. 手册上传百炼平台验证 AI 引用效果
|
||||
|
||||
### 当前 MCP Server 能力
|
||||
|
||||
| 工具 | 功能 | 限制 |
|
||||
|------|------|------|
|
||||
| `list_tables` | 列出指定 schema 下的表 | 仅 etl_feiqiu |
|
||||
| `describe_table` | 查看表结构(列名、类型、注释) | 仅 etl_feiqiu |
|
||||
| `describe_schemas` | 列出可用 schema 及表数量 | 仅 ods/dwd/dws/core/meta/app |
|
||||
| `query_sql` | 执行只读 SQL 查询 | 正则禁词检测,单 schema 限制 |
|
||||
|
||||
---
|
||||
|
||||
## 二、技术架构
|
||||
|
||||
### 2.1 多数据库连接
|
||||
|
||||
```
|
||||
apps/mcp-server/
|
||||
├── server.py 🔧 扩展:多连接池 + schema 路由
|
||||
├── db_pool.py 🆕 新建:连接池管理(etl_feiqiu + zqyy_app)
|
||||
├── security.py 🆕 新建:敏感字段脱敏策略
|
||||
└── config.py 🆕 新建:数据库配置(DSN、schema 白名单)
|
||||
```
|
||||
|
||||
### 2.2 连接池设计
|
||||
|
||||
```python
|
||||
# db_pool.py
|
||||
class DatabasePool:
|
||||
"""管理两个数据库的连接池。"""
|
||||
|
||||
pools = {
|
||||
"etl": {
|
||||
"dsn_env": "PG_DSN", # etl_feiqiu / test_etl_feiqiu
|
||||
"schemas": ["ods", "dwd", "dws", "core", "meta", "app"],
|
||||
"readonly": True,
|
||||
},
|
||||
"biz": {
|
||||
"dsn_env": "APP_DB_DSN", # zqyy_app / test_zqyy_app
|
||||
"schemas": ["auth", "biz", "public"],
|
||||
"readonly": True, # MCP 只读,写操作走后端 API
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 Schema 自动路由
|
||||
|
||||
工具参数中不再需要显式指定 `database`,而是根据 schema 名称自动路由:
|
||||
|
||||
| Schema | 路由目标 | 说明 |
|
||||
|--------|---------|------|
|
||||
| `ods` / `dwd` / `dws` / `core` / `meta` / `app` | etl_feiqiu | ETL 数据仓库 |
|
||||
| `auth` / `biz` / `public` | zqyy_app | 业务库 |
|
||||
|
||||
路由逻辑:
|
||||
```python
|
||||
def resolve_database(schema: str) -> str:
|
||||
ETL_SCHEMAS = {"ods", "dwd", "dws", "core", "meta", "app"}
|
||||
BIZ_SCHEMAS = {"auth", "biz", "public"}
|
||||
if schema in ETL_SCHEMAS:
|
||||
return "etl"
|
||||
elif schema in BIZ_SCHEMAS:
|
||||
return "biz"
|
||||
else:
|
||||
raise ValueError(f"未知 schema: {schema}")
|
||||
```
|
||||
|
||||
### 2.4 工具扩展
|
||||
|
||||
4 个现有工具的参数不变,内部根据 schema 参数自动选择连接池:
|
||||
|
||||
| 工具 | 变更 |
|
||||
|------|------|
|
||||
| `list_tables(schema)` | schema 参数扩展接受 auth/biz/public |
|
||||
| `describe_table(table, schema)` | 同上 |
|
||||
| `describe_schemas()` | 返回结果增加 auth/biz/public 三个 schema |
|
||||
| `query_sql(schema, sql)` | 根据 schema 路由到对应连接池 |
|
||||
|
||||
### 2.5 跨库查询限制
|
||||
|
||||
- 单次 `query_sql` 调用只能查询单个数据库(由 schema 参数决定)
|
||||
- 禁止在 SQL 中引用其他数据库的 schema(现有 `_reject_cross_schema` 逻辑扩展)
|
||||
- 如需跨库关联,AI 应分两次查询后在应用层组合
|
||||
|
||||
---
|
||||
|
||||
## 三、敏感字段脱敏策略
|
||||
|
||||
### 3.1 脱敏规则
|
||||
|
||||
| Schema | 表 | 敏感字段 | 脱敏方式 |
|
||||
|--------|-----|---------|---------|
|
||||
| `auth` | `users` | `wx_openid` | 前 4 后 4 保留,中间 `***` |
|
||||
| `auth` | `users` | `phone` | 前 3 后 4 保留,中间 `****` |
|
||||
| `auth` | `users` | `wx_session_key` | 完全隐藏,返回 `[REDACTED]` |
|
||||
| `biz` | `ai_messages` | `content`(role=user) | 不脱敏(AI 需要理解对话内容) |
|
||||
| `biz` | `ai_conversations` | — | 无敏感字段 |
|
||||
| `public` | `member_retention_clue` | `recorded_by_assistant_id` | 不脱敏(非 PII) |
|
||||
|
||||
### 3.2 实现方式
|
||||
|
||||
在 `query_sql` 返回结果时,对命中脱敏规则的列进行后处理:
|
||||
|
||||
```python
|
||||
# security.py
|
||||
MASKING_RULES = {
|
||||
"auth.users": {
|
||||
"wx_openid": lambda v: f"{v[:4]}***{v[-4:]}" if v and len(v) > 8 else "[REDACTED]",
|
||||
"phone": lambda v: f"{v[:3]}****{v[-4:]}" if v and len(v) >= 11 else "[REDACTED]",
|
||||
"wx_session_key": lambda v: "[REDACTED]",
|
||||
},
|
||||
}
|
||||
|
||||
def mask_row(schema: str, table: str, columns: list[str], row: tuple) -> tuple:
|
||||
"""对查询结果行应用脱敏规则。"""
|
||||
```
|
||||
|
||||
### 3.3 脱敏绕过防护
|
||||
|
||||
- `SELECT *` 查询 auth.users 时自动应用脱敏
|
||||
- 禁止通过 `CAST`、`CONCAT`、子查询等方式绕过脱敏(在 SQL 解析阶段检测)
|
||||
- 对 auth.users 的查询结果始终应用列级脱敏,无论 SQL 写法
|
||||
|
||||
---
|
||||
|
||||
## 四、查库手册重写
|
||||
|
||||
### 4.1 手册结构
|
||||
|
||||
重写后的手册分为两个文件,便于维护和上传:
|
||||
|
||||
```
|
||||
docs/mcp/
|
||||
├── AI-DATABASE-QUERY-MANUAL.md 🔧 重写(总览 + ETL 库)
|
||||
└── AI-DATABASE-QUERY-MANUAL-BIZ.md 🆕 新建(业务库)
|
||||
```
|
||||
|
||||
### 4.2 ETL 库手册内容(重写)
|
||||
|
||||
保留现有架构流程和工具说明,重点补充:
|
||||
|
||||
1. **DWS 层完整表清单**(34 张表按业务域分组)
|
||||
- 每张表列出全部字段(字段名、类型、中文说明)
|
||||
- 标注关键字段的业务含义和使用注意事项
|
||||
- 按业务域分组:会员域、助教域、财务域、配置域
|
||||
|
||||
2. **金额口径专章**
|
||||
- `consume_money` 三种历史口径说明(A/B/C),明确标注"禁止直接使用"
|
||||
- `items_sum` 定义和计算公式
|
||||
- settle_type 枚举映射表(1=台桌结账 78.6%、3=商城订单 21.4%、5=充值、7=充值退款、6=结算退款)
|
||||
- 助教费用拆分规则(assistant_pd_money 陪打 / assistant_cx_money 超休)
|
||||
- 支付渠道恒等式(balance_amount = recharge_card_amount + gift_card_amount)
|
||||
|
||||
3. **会员字段断档说明**
|
||||
- DQ-6:member_phone/member_name 自 2025-12 起 NULL,需 JOIN dim_member
|
||||
- DQ-7:member_card_type_name 自 2025-07-21 起 NULL,需 JOIN dim_member_card_account
|
||||
|
||||
4. **常用查询模式**(ETL 侧)
|
||||
- 客户消费汇总查询
|
||||
- 助教绩效查询
|
||||
- 财务日报查询
|
||||
- 指数排名查询
|
||||
|
||||
5. **数据量统计与性能建议**
|
||||
- 各表的大致数据量级
|
||||
- 推荐的 WHERE 条件(时间范围、site_id)
|
||||
- 避免全表扫描的建议
|
||||
|
||||
### 4.3 业务库手册内容(新建)
|
||||
|
||||
1. **Schema 概览**
|
||||
- `auth`:用户认证(users、user_applications、site_code_mapping、user_assistant_binding)
|
||||
- `biz`:业务数据(coach_tasks、notes、ai_conversations、ai_messages、ai_cache、trigger_jobs、salary_adjustments、excel_upload_log)
|
||||
- `public`:共享数据(member_retention_clue)
|
||||
|
||||
2. **每张表完整字段说明**
|
||||
- 字段名、类型、是否可空、默认值、中文说明
|
||||
- 关键枚举值说明(如 coach_tasks.task_type、notes.type、ai_cache.cache_type)
|
||||
- 外键关系和关联查询建议
|
||||
|
||||
3. **常用查询模式**(业务侧)
|
||||
- 维客线索查询(按客户、按来源、按标签)
|
||||
- 任务系统查询(按助教、按状态、按类型)
|
||||
- 备注查询(按客户、按助教、含星星评分)
|
||||
- AI 缓存查询(按应用类型、按 target_id)
|
||||
- Excel 上传记录查询
|
||||
|
||||
4. **跨库关联指南**
|
||||
- 业务库 member_id → ETL 库 dim_member 的关联方式
|
||||
- 业务库 assistant_id → ETL 库 dim_assistant 的关联方式
|
||||
- 注意:MCP 不支持单次跨库 JOIN,需分两次查询
|
||||
|
||||
### 4.4 数据字段权威参考
|
||||
|
||||
手册中涉及的 DWD/DWS 层字段来源、金额口径、业务逻辑,以 `docs/reports/DWD-DOC/` 校准文档为准(数据快照 2026-03-06)。
|
||||
|
||||
---
|
||||
|
||||
## 五、百炼平台上传与验证
|
||||
|
||||
### 5.1 上传方式
|
||||
|
||||
- 将重写后的手册作为知识库文档上传至百炼平台
|
||||
- 关联到应用 1(通用对话),使 AI 在回答数据查询问题时可引用手册
|
||||
|
||||
### 5.2 验证场景
|
||||
|
||||
| 测试场景 | 预期行为 |
|
||||
|---------|---------|
|
||||
| "查询本月营业额" | AI 引用手册中的 dws_finance_daily_summary 表,使用 items_sum 口径 |
|
||||
| "查看客户王先生的消费记录" | AI 引用 dim_member + dwd_settlement_head,通过 member_id 关联 |
|
||||
| "助教张三的本月绩效" | AI 引用 dws_assistant_salary_calc,正确使用 assistant_pd_money 拆分 |
|
||||
| "查看维客线索" | AI 引用业务库 member_retention_clue 表 |
|
||||
| "查看用户申请列表" | AI 引用 auth.user_applications,对 phone 字段脱敏 |
|
||||
|
||||
### 5.3 验证标准
|
||||
|
||||
- AI 不再频繁调用 `describe_table`(手册已提供完整字段信息)
|
||||
- AI 生成的 SQL 使用正确的金额口径(items_sum 而非 consume_money)
|
||||
- AI 查询 auth.users 时返回结果中敏感字段已脱敏
|
||||
- AI 能正确区分 ETL 库和业务库的查询路由
|
||||
|
||||
---
|
||||
|
||||
## 六、参考文档
|
||||
|
||||
| 文档 | 路径 | 用途 |
|
||||
|------|------|------|
|
||||
| P5.1 原始 spec | `docs/prd/specs/P5.1-mcp-server-ai-extension.md` | 需求定义基准 |
|
||||
| 现有 MCP Server | `apps/mcp-server/server.py` | 当前实现参考 |
|
||||
| 现有查库手册 | `docs/mcp/AI-DATABASE-QUERY-MANUAL.md` | 重写基础 |
|
||||
| DWD-DOC 标杆 | `docs/reports/DWD-DOC/` | 字段语义、金额口径权威参考 |
|
||||
| BD 手册-业务表 | `docs/database/BD_Manual_biz_tables.md` | biz schema 表结构 |
|
||||
| BD 手册-认证表 | `docs/database/BD_Manual_auth_tables.md` | auth schema 表结构 |
|
||||
| ETL BD 手册 | `apps/etl/connectors/feiqiu/docs/database/` | DWD/DWS 表结构 |
|
||||
|
||||
---
|
||||
|
||||
## 七、预审查清单(SPEC 启动前确认)
|
||||
|
||||
### 7.1 多数据库连接
|
||||
|
||||
1. **连接池大小**:zqyy_app 连接池的 min/max 连接数?与 etl_feiqiu 共享还是独立?MCP Server 的并发查询量预估?
|
||||
2. **DSN 环境变量**:MCP Server 运行环境中 `APP_DB_DSN` 是否已配置?测试环境使用 `test_zqyy_app` 还是 `zqyy_app`?
|
||||
3. **RLS 隔离**:MCP 查询 zqyy_app 时是否需要 `SET LOCAL app.current_site_id`?如果需要,site_id 从哪里获取(MCP 调用方传入?固定值?)?
|
||||
4. **连接超时**:FDW 查询和业务库查询的超时时间是否需要不同设置?
|
||||
|
||||
### 7.2 安全与脱敏
|
||||
|
||||
5. **脱敏范围确认**:除 auth.users 外,还有哪些表/字段需要脱敏?biz.notes 的备注内容是否需要脱敏?
|
||||
6. **脱敏粒度**:是否需要根据 MCP 调用方身份区分脱敏级别?(如管理员可看完整数据,AI 应用只看脱敏数据)
|
||||
7. **SQL 注入防护**:现有正则禁词检测是否足够?是否需要升级为 SQL 解析器(如 sqlparse)?
|
||||
8. **审计日志**:MCP 查询是否需要记录审计日志(谁查了什么表、什么时间)?
|
||||
|
||||
### 7.3 查库手册
|
||||
|
||||
9. **手册格式**:百炼平台对知识库文档的格式要求?Markdown 是否直接支持?是否需要转换为 PDF/TXT?
|
||||
10. **手册拆分**:ETL 库和业务库是否拆分为两个知识库文档?还是合并为一个?
|
||||
11. **手册更新机制**:表结构变更后,手册如何同步更新?是否需要自动化脚本从数据库 DDL 生成手册?
|
||||
12. **DWD 层覆盖**:手册是否需要覆盖 DWD 层全部表的完整字段?还是只覆盖 DWS + 常用 DWD 表?
|
||||
|
||||
### 7.4 部署与运维
|
||||
|
||||
13. **MCP Server 重启**:新增连接池后,MCP Server 是否需要重启?是否支持热加载配置?
|
||||
14. **监控指标**:是否需要监控 MCP 查询的响应时间、错误率、查询频次?
|
||||
15. **降级策略**:zqyy_app 连接失败时,是否影响 etl_feiqiu 的查询?两个连接池是否需要独立故障隔离?
|
||||
|
||||
---
|
||||
|
||||
## 八、任务清单(草案,SPEC 细化后调整)
|
||||
|
||||
### 批次 A:查库手册 — ETL 库(无前置依赖)
|
||||
- [ ] T1:重写 `AI-DATABASE-QUERY-MANUAL.md` — DWS 层 34 张表全字段说明(按业务域分组)
|
||||
- [ ] T2:补充金额口径专章(consume_money 禁用说明 + items_sum 定义 + settle_type 映射)
|
||||
- [ ] T3:补充会员字段断档说明(DQ-6/DQ-7)+ 常用 ETL 查询模式
|
||||
|
||||
### 批次 B:MCP Server 扩展 + 业务库手册(依赖 P5-A)
|
||||
- [ ] T4:创建 `db_pool.py` — 双连接池管理(etl_feiqiu + zqyy_app)
|
||||
- [ ] T5:扩展 `server.py` — 4 个工具支持 schema 自动路由
|
||||
- [ ] T6:创建 `security.py` — 敏感字段脱敏策略实现
|
||||
- [ ] T7:创建 `AI-DATABASE-QUERY-MANUAL-BIZ.md` — 业务库全字段说明 + 常用查询模式
|
||||
|
||||
### 批次 C:百炼平台验证
|
||||
- [ ] T8:手册上传百炼平台并关联 AI 应用
|
||||
- [ ] T9:执行验证场景测试(5 个场景),确认 AI 引用效果
|
||||
532
docs/prd/Neo_Specs/NS4-tenant-admin-web.md
Normal file
532
docs/prd/Neo_Specs/NS4-tenant-admin-web.md
Normal file
@@ -0,0 +1,532 @@
|
||||
# NS4:租户管理后台 — tenant-admin-web
|
||||
|
||||
> 优先级:中(可与 NS1/NS2 并行,依赖 P1+P3)
|
||||
> 预估工作量:大
|
||||
> 前置条件:P1(数据库基础)、P3(用户认证体系)、admin-web-console 需求 11(租户管理员账号管理)
|
||||
> 参考基准:`docs/prd/specs/P10-tenant-admin-web.md`
|
||||
|
||||
---
|
||||
|
||||
## 一、背景与目标
|
||||
|
||||
当前系统缺少面向租户管理员的独立管理界面。用户审核、Excel 数据上传、维客线索管理等运营操作无法自助完成,依赖开发人员手动操作数据库。
|
||||
|
||||
本 SPEC 目标:构建独立的租户管理 Web 应用,提供:
|
||||
1. 用户审核与管理(申请审核、身份编辑、店铺归属、助教/员工绑定)
|
||||
2. Excel 数据上传(4 种模板:财务支出/团购收入/助教奖罚/充值业绩归属)
|
||||
3. 维客线索管理(查看、修改、删除、隐藏)
|
||||
|
||||
### 与现有系统的关系
|
||||
|
||||
| 系统 | 用途 | 用户 |
|
||||
|------|------|------|
|
||||
| `apps/admin-web/`(系统管理后台) | 平台级管理(Operator 操作) | 系统管理员 |
|
||||
| `apps/tenant-admin/`(本 SPEC) | 租户级管理 | 租户管理员 |
|
||||
| `apps/miniprogram/`(小程序) | C 端业务 | 助教/管理者 |
|
||||
|
||||
租户管理员账号由系统管理后台(`apps/admin-web/`)的 Operator 创建,租户管理员不可自行注册。
|
||||
|
||||
---
|
||||
|
||||
## 二、技术架构
|
||||
|
||||
### 2.1 前端
|
||||
|
||||
- 独立 Web 应用:React + Vite + Ant Design(与 `apps/admin-web/` 同技术栈)
|
||||
- 部署路径:`apps/tenant-admin/`
|
||||
- 独立登录入口,与系统管理后台完全隔离
|
||||
|
||||
```
|
||||
apps/tenant-admin/
|
||||
├── src/
|
||||
│ ├── pages/
|
||||
│ │ ├── Login/ # 登录页
|
||||
│ │ ├── UserApproval/ # 用户审核
|
||||
│ │ ├── UserManagement/ # 用户管理
|
||||
│ │ ├── ExcelUpload/ # Excel 上传(4 种模板)
|
||||
│ │ └── RetentionClues/ # 维客线索管理
|
||||
│ ├── components/
|
||||
│ │ ├── DiffTable/ # 冲突 diff 交互组件
|
||||
│ │ └── ClueEditor/ # 线索编辑组件
|
||||
│ ├── services/
|
||||
│ │ └── api.ts # API 调用封装
|
||||
│ ├── hooks/
|
||||
│ ├── utils/
|
||||
│ └── App.tsx
|
||||
├── package.json
|
||||
├── vite.config.ts
|
||||
└── tsconfig.json
|
||||
```
|
||||
|
||||
### 2.2 后端
|
||||
|
||||
复用 `apps/backend/` 的 FastAPI,新增租户管理路由模块:
|
||||
|
||||
```
|
||||
apps/backend/app/routers/
|
||||
├── tenant_auth.py 🆕 租户管理员登录/鉴权
|
||||
├── tenant_users.py 🆕 用户审核 + 用户管理
|
||||
├── tenant_excel.py 🆕 Excel 上传/校验/冲突处理
|
||||
└── tenant_clues.py 🆕 维客线索管理
|
||||
```
|
||||
|
||||
### 2.3 认证体系
|
||||
|
||||
- 独立凭据:用户名 + 密码(非微信登录)
|
||||
- JWT 签发:与小程序 JWT 独立(不同 issuer 或 audience)
|
||||
- 账号创建:由系统管理后台 Operator 创建,指定用户名、初始密码、所属租户、管辖 site_id 列表
|
||||
- 权限级别:
|
||||
- 租户级管理员:管辖该租户下所有店铺
|
||||
- 店铺级管理员:只能管理 Operator 分配的 site_id 列表内的店铺
|
||||
|
||||
### 2.4 数据隔离
|
||||
|
||||
- 所有查询附加 `site_id IN (管辖列表)` 条件
|
||||
- FDW 查询需 `SET LOCAL app.current_site_id`(单店铺场景)
|
||||
- 多店铺场景下,逐 site_id 查询后合并结果
|
||||
|
||||
---
|
||||
|
||||
## 三、功能详细设计
|
||||
|
||||
### 3.1 用户审核页面
|
||||
|
||||
#### 页面功能
|
||||
|
||||
- 申请列表:展示所有待审核/已审核的用户申请
|
||||
- 状态筛选:全部 / 待审核(pending) / 已通过(approved) / 已拒绝(rejected)
|
||||
- 关联建议:根据申请中的球房 ID + 手机号,同时在助教表和员工信息表中匹配
|
||||
- 审核操作:通过(分配身份+关联助教/员工)/ 拒绝(填写原因)
|
||||
|
||||
#### 关联匹配逻辑
|
||||
|
||||
```
|
||||
用户申请(球房ID + 手机号)
|
||||
→ site_code_mapping 查 site_id
|
||||
→ 并行匹配:
|
||||
├── fdw_etl.v_dim_assistant(phone 匹配,scd2_is_current=1)
|
||||
└── fdw_etl.v_dim_staff + v_dim_staff_ex(phone 匹配)
|
||||
→ 返回匹配建议列表(可能多条)
|
||||
→ 管理员选择关联目标
|
||||
```
|
||||
|
||||
#### 审核通过后操作
|
||||
|
||||
1. 更新 `auth.users.status = 'approved'`
|
||||
2. 分配角色(助教/管理者)→ 写入 `auth.user_roles`
|
||||
3. 关联助教 → 写入 `auth.user_assistant_binding`(含 staff_id)
|
||||
4. 分配 site_id → 更新 `auth.users.site_id`
|
||||
|
||||
#### 接口设计
|
||||
|
||||
| 接口 | 方法 | 路径 | 说明 |
|
||||
|------|------|------|------|
|
||||
| 申请列表 | GET | `/api/tenant/applications` | 支持 status 筛选、分页 |
|
||||
| 关联建议 | GET | `/api/tenant/applications/{id}/match-suggestions` | 返回助教+员工匹配结果 |
|
||||
| 审核通过 | POST | `/api/tenant/applications/{id}/approve` | body: role, assistant_id, staff_id |
|
||||
| 审核拒绝 | POST | `/api/tenant/applications/{id}/reject` | body: reason |
|
||||
|
||||
### 3.2 用户管理页面
|
||||
|
||||
#### 页面功能
|
||||
|
||||
- 用户列表:展示已通过审核的用户(姓名、角色、关联助教、店铺、状态)
|
||||
- 身份编辑:修改角色(助教↔管理者)
|
||||
- 店铺归属:修改用户的 site_id
|
||||
- 关联助教/员工:修改绑定关系
|
||||
- 禁用/启用:冻结用户账号
|
||||
|
||||
#### 接口设计
|
||||
|
||||
| 接口 | 方法 | 路径 | 说明 |
|
||||
|------|------|------|------|
|
||||
| 用户列表 | GET | `/api/tenant/users` | 支持角色筛选、搜索、分页 |
|
||||
| 编辑用户 | PATCH | `/api/tenant/users/{id}` | body: role, site_id, status |
|
||||
| 修改绑定 | PUT | `/api/tenant/users/{id}/binding` | body: assistant_id, staff_id |
|
||||
|
||||
### 3.3 Excel 上传
|
||||
|
||||
#### 4 种模板
|
||||
|
||||
##### 模板 1:财务支出(按月)
|
||||
|
||||
| 列名 | 类型 | 必填 | 校验规则 |
|
||||
|------|------|------|---------|
|
||||
| 月份 | YYYY-MM | 是 | 格式校验,不超过当前月 |
|
||||
| 支出类别 | 文本 | 是 | 枚举:房租/水电/物业/食品饮料进货/耗材/报销/固定人员工资/其他费用 |
|
||||
| 金额 | 数值(2) | 是 | > 0,精度 2 位小数 |
|
||||
| 备注 | 文本 | 否 | 最长 500 字符 |
|
||||
|
||||
主键:月份 + 支出类别
|
||||
写入目标:`dws.dws_finance_expense_summary`(通过后端 API 写入 ETL 库,或写入业务库 staging 表后由 ETL 同步)
|
||||
|
||||
##### 模板 2:团购平台收入(按月)
|
||||
|
||||
| 列名 | 类型 | 必填 | 校验规则 |
|
||||
|------|------|------|---------|
|
||||
| 月份 | YYYY-MM | 是 | 格式校验 |
|
||||
| 平台名称 | 文本 | 是 | 非空 |
|
||||
| 收入金额 | 数值(2) | 是 | > 0 |
|
||||
| 备注 | 文本 | 否 | 最长 500 字符 |
|
||||
|
||||
主键:月份 + 平台名称
|
||||
写入目标:`dws.dws_platform_settlement`(或业务库 staging 表)
|
||||
|
||||
##### 模板 3:助教奖罚(按月)
|
||||
|
||||
| 列名 | 类型 | 必填 | 校验规则 |
|
||||
|------|------|------|---------|
|
||||
| 月份 | YYYY-MM | 是 | 格式校验 |
|
||||
| 助教姓名 | 文本 | 是 | 非空 |
|
||||
| 助教编号 | 文本 | 是 | 非空 |
|
||||
| 类型 | 文本 | 是 | 枚举:扣款/奖金 |
|
||||
| 金额 | 数值(2) | 是 | > 0 |
|
||||
| 原因 | 文本 | 是 | 非空,最长 200 字符 |
|
||||
|
||||
主键:月份 + 助教姓名 + 助教编号 + 类型 + 原因(同一助教同月可多笔)
|
||||
写入目标:`biz.salary_adjustments`
|
||||
|
||||
##### 模板 4:充值业绩归属(按月)
|
||||
|
||||
| 列名 | 类型 | 必填 | 校验规则 |
|
||||
|------|------|------|---------|
|
||||
| 充值日期 | YYYY-MM-DD | 是 | 格式校验 |
|
||||
| 会员名称 | 文本 | 是 | 非空 |
|
||||
| 充值金额 | 数值(2) | 是 | > 0 |
|
||||
| 归属助教 | 文本 | 是 | 非空 |
|
||||
| 奖励金额 | 数值(2) | 是 | ≥ 0 |
|
||||
|
||||
主键:充值日期 + 会员名称 + 归属助教
|
||||
写入目标:`dws.dws_assistant_recharge_commission`(或业务库 staging 表)
|
||||
|
||||
#### 人员匹配校验(模板 3/4)
|
||||
|
||||
上传助教奖罚和充值业绩归属时,需校验助教姓名+编号是否存在:
|
||||
|
||||
```
|
||||
助教姓名 + 助教编号
|
||||
→ fdw_etl.v_dim_assistant(nickname + assistant_number 匹配,scd2_is_current=1)
|
||||
→ 如不匹配,尝试 fdw_etl.v_dim_staff + v_dim_staff_ex(name + staff_number 匹配)
|
||||
→ 匹配失败:标记为校验警告(不阻断上传,但提示管理员确认)
|
||||
```
|
||||
|
||||
#### 冲突处理流程
|
||||
|
||||
```
|
||||
上传 Excel
|
||||
→ 后端解析 + 格式校验
|
||||
→ 返回校验结果:
|
||||
├── 格式错误行 → 前端标红,要求修正后重新上传
|
||||
├── 无冲突行 → 标记为"待写入"
|
||||
└── 冲突行(主键已存在)→ 返回 diff 数据(旧值 vs 新值)
|
||||
→ 前端展示 diff 交互表格:
|
||||
- 每行显示:字段名 | 旧值 | 新值 | 操作(替换/保留)
|
||||
- 支持"全部替换"/"全部保留"快捷操作
|
||||
→ 用户确认后提交
|
||||
→ 后端按选择写入
|
||||
```
|
||||
|
||||
#### 接口设计
|
||||
|
||||
| 接口 | 方法 | 路径 | 说明 |
|
||||
|------|------|------|------|
|
||||
| 上传解析 | POST | `/api/tenant/excel/upload` | multipart/form-data,返回校验结果+冲突列表 |
|
||||
| 确认写入 | POST | `/api/tenant/excel/confirm` | body: upload_id, resolutions[] |
|
||||
| 上传记录 | GET | `/api/tenant/excel/logs` | 历史上传记录列表 |
|
||||
| 模板下载 | GET | `/api/tenant/excel/template/{type}` | 下载空白模板 |
|
||||
|
||||
### 3.4 维客线索管理
|
||||
|
||||
#### 页面功能
|
||||
|
||||
- 客户搜索:按客户姓名/手机号搜索(姓名从 dim_member.nickname,手机从 dim_member.mobile)
|
||||
- 门店筛选:按管辖 site_id 筛选
|
||||
- 线索列表:展示该客户的全部维客线索
|
||||
- 标签(大类枚举:客户基础/消费习惯/玩法偏好/促销偏好/社交关系/重要反馈)
|
||||
- 摘要(含 Emoji 前缀)
|
||||
- 详情
|
||||
- 提供人(recorded_by_name)
|
||||
- 来源(manual / ai_consumption / ai_note)
|
||||
- 记录时间
|
||||
- 隐藏状态
|
||||
- 操作:
|
||||
- 修改:编辑标签、摘要、详情
|
||||
- 删除:二次确认后物理删除
|
||||
- 隐藏/显示:切换 `is_hidden` 状态
|
||||
|
||||
#### 数据源
|
||||
|
||||
- `zqyy_app.public.member_retention_clue`(线索数据)
|
||||
- `fdw_etl.v_dim_member`(客户信息:nickname、mobile,通过 member_id 关联)
|
||||
|
||||
> ⚠️ 会员字段断档(DQ-6):客户姓名/手机必须从 dim_member 获取,不可使用结算单上的冗余字段
|
||||
|
||||
#### 接口设计
|
||||
|
||||
| 接口 | 方法 | 路径 | 说明 |
|
||||
|------|------|------|------|
|
||||
| 客户搜索 | GET | `/api/tenant/customers/search` | query: keyword, site_id |
|
||||
| 线索列表 | GET | `/api/tenant/customers/{member_id}/clues` | 该客户全部线索 |
|
||||
| 修改线索 | PATCH | `/api/tenant/clues/{id}` | body: category, summary, detail |
|
||||
| 删除线索 | DELETE | `/api/tenant/clues/{id}` | 二次确认后物理删除 |
|
||||
| 隐藏/显示 | PATCH | `/api/tenant/clues/{id}/visibility` | body: is_hidden |
|
||||
|
||||
---
|
||||
|
||||
## 四、数据库审查与新增表
|
||||
|
||||
### 4.1 现有表满足度
|
||||
|
||||
| 功能 | 现有表 | 是否满足 | 缺口 |
|
||||
|------|--------|---------|------|
|
||||
| 用户审核 | auth.users, auth.user_applications | ✅ 满足 | 无 |
|
||||
| 用户管理 | auth.users, auth.user_roles, auth.user_assistant_binding | ✅ 满足 | 无 |
|
||||
| 维客线索 | public.member_retention_clue | ⚠️ 部分 | 缺 is_hidden 字段 |
|
||||
| 助教奖罚 | — | ❌ 不满足 | 需新建 biz.salary_adjustments |
|
||||
| 上传记录 | — | ❌ 不满足 | 需新建 biz.excel_upload_log |
|
||||
| 财务支出/团购收入/充值归属 | DWS 表 | ⚠️ 待定 | 可能需要 staging 表 |
|
||||
|
||||
### 4.2 需新建的表
|
||||
|
||||
#### 表 1:`biz.salary_adjustments`(助教奖罚明细)
|
||||
|
||||
```sql
|
||||
CREATE TABLE biz.salary_adjustments (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
site_id BIGINT NOT NULL,
|
||||
assistant_id BIGINT, -- 匹配到的助教 ID(可空,匹配失败时为 NULL)
|
||||
assistant_name VARCHAR(100) NOT NULL,
|
||||
assistant_number VARCHAR(50) NOT NULL,
|
||||
salary_month VARCHAR(7) NOT NULL, -- YYYY-MM
|
||||
adjustment_type VARCHAR(20) NOT NULL CHECK (adjustment_type IN ('deduction', 'bonus')),
|
||||
amount NUMERIC(12,2) NOT NULL CHECK (amount > 0),
|
||||
reason VARCHAR(200) NOT NULL,
|
||||
upload_batch_id BIGINT REFERENCES biz.excel_upload_log(id),
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
created_by BIGINT -- 上传人(租户管理员 ID)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_salary_adj_site_month ON biz.salary_adjustments(site_id, salary_month);
|
||||
CREATE INDEX idx_salary_adj_assistant ON biz.salary_adjustments(assistant_id, salary_month);
|
||||
```
|
||||
|
||||
#### 表 2:`biz.excel_upload_log`(Excel 上传记录)
|
||||
|
||||
```sql
|
||||
CREATE TABLE biz.excel_upload_log (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
site_id BIGINT NOT NULL,
|
||||
upload_type VARCHAR(30) NOT NULL CHECK (upload_type IN (
|
||||
'expense', 'platform_income', 'salary_adj', 'recharge_commission'
|
||||
)),
|
||||
file_name VARCHAR(255) NOT NULL,
|
||||
uploaded_by BIGINT NOT NULL, -- 租户管理员 ID
|
||||
row_count INTEGER NOT NULL DEFAULT 0,
|
||||
conflict_count INTEGER NOT NULL DEFAULT 0,
|
||||
resolved_count INTEGER NOT NULL DEFAULT 0,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'pending' CHECK (status IN (
|
||||
'pending', 'confirmed', 'failed'
|
||||
)),
|
||||
error_detail JSONB, -- 校验错误详情
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
confirmed_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX idx_excel_log_site ON biz.excel_upload_log(site_id, created_at DESC);
|
||||
```
|
||||
|
||||
### 4.3 需变更的表
|
||||
|
||||
#### `public.member_retention_clue` — 新增字段
|
||||
|
||||
```sql
|
||||
ALTER TABLE public.member_retention_clue
|
||||
ADD COLUMN is_hidden BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
COMMENT ON COLUMN public.member_retention_clue.is_hidden
|
||||
IS '是否隐藏(true=管理后台保留但小程序不展示)';
|
||||
```
|
||||
|
||||
> 小程序端查询线索时需增加 `WHERE is_hidden = false` 条件
|
||||
|
||||
#### `public.member_retention_clue` — 确认 source 字段
|
||||
|
||||
P10 spec 中提到需新增 `source` 字段,需确认该字段是否已在 P4/P5 阶段建立。如未建立:
|
||||
|
||||
```sql
|
||||
ALTER TABLE public.member_retention_clue
|
||||
ADD COLUMN source VARCHAR(20) NOT NULL DEFAULT 'manual'
|
||||
CHECK (source IN ('manual', 'ai_consumption', 'ai_note'));
|
||||
|
||||
COMMENT ON COLUMN public.member_retention_clue.source
|
||||
IS '线索来源:manual=人工录入, ai_consumption=应用3消费分析, ai_note=应用6备注分析';
|
||||
```
|
||||
|
||||
### 4.4 Excel 写入目标表的 staging 策略
|
||||
|
||||
财务支出、团购收入、充值业绩归属三种模板的数据最终需要进入 DWS 层。有两种策略:
|
||||
|
||||
**方案 A:直接写入 DWS 表**(通过后端 API 直连 ETL 库写入)
|
||||
- 优点:数据即时可用
|
||||
- 缺点:绕过 ETL 流程,数据一致性风险
|
||||
|
||||
**方案 B:写入业务库 staging 表,ETL 定时同步**
|
||||
- 优点:数据经过 ETL 标准流程,一致性有保障
|
||||
- 缺点:数据有延迟(取决于 ETL 调度频率)
|
||||
|
||||
建议采用方案 B,需新建 3 张 staging 表:
|
||||
|
||||
```sql
|
||||
-- biz.stg_finance_expense(财务支出 staging)
|
||||
CREATE TABLE biz.stg_finance_expense (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
site_id BIGINT NOT NULL,
|
||||
expense_month VARCHAR(7) NOT NULL,
|
||||
category VARCHAR(50) NOT NULL,
|
||||
amount NUMERIC(12,2) NOT NULL,
|
||||
remark TEXT,
|
||||
upload_batch_id BIGINT REFERENCES biz.excel_upload_log(id),
|
||||
synced_at TIMESTAMPTZ, -- ETL 同步时间(NULL=未同步)
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- biz.stg_platform_income(团购收入 staging)
|
||||
CREATE TABLE biz.stg_platform_income (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
site_id BIGINT NOT NULL,
|
||||
income_month VARCHAR(7) NOT NULL,
|
||||
platform_name VARCHAR(100) NOT NULL,
|
||||
amount NUMERIC(12,2) NOT NULL,
|
||||
remark TEXT,
|
||||
upload_batch_id BIGINT REFERENCES biz.excel_upload_log(id),
|
||||
synced_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- biz.stg_recharge_commission(充值业绩归属 staging)
|
||||
CREATE TABLE biz.stg_recharge_commission (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
site_id BIGINT NOT NULL,
|
||||
recharge_date DATE NOT NULL,
|
||||
member_name VARCHAR(100) NOT NULL,
|
||||
recharge_amount NUMERIC(12,2) NOT NULL,
|
||||
assigned_assistant VARCHAR(100) NOT NULL,
|
||||
reward_amount NUMERIC(12,2) NOT NULL,
|
||||
upload_batch_id BIGINT REFERENCES biz.excel_upload_log(id),
|
||||
synced_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、租户管理员账号体系
|
||||
|
||||
### 5.1 账号存储
|
||||
|
||||
租户管理员账号存储在 `auth.tenant_admins` 表(需新建):
|
||||
|
||||
```sql
|
||||
CREATE TABLE auth.tenant_admins (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
username VARCHAR(50) NOT NULL UNIQUE,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
display_name VARCHAR(100),
|
||||
tenant_id BIGINT NOT NULL, -- 所属租户
|
||||
managed_site_ids BIGINT[] NOT NULL, -- 管辖的 site_id 列表
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
created_by BIGINT, -- 创建该账号的 Operator ID
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
last_login_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX idx_tenant_admin_tenant ON auth.tenant_admins(tenant_id);
|
||||
```
|
||||
|
||||
### 5.2 与小程序用户体系的隔离
|
||||
|
||||
- 租户管理员使用 `auth.tenant_admins` 表,小程序用户使用 `auth.users` 表
|
||||
- JWT 签发时使用不同的 `aud`(audience)字段区分
|
||||
- 后端路由通过不同的认证依赖注入区分(`require_tenant_admin()` vs `require_approved()`)
|
||||
|
||||
---
|
||||
|
||||
## 六、参考文档
|
||||
|
||||
| 文档 | 路径 | 用途 |
|
||||
|------|------|------|
|
||||
| P10 原始 spec | `docs/prd/specs/P10-tenant-admin-web.md` | 需求定义基准 |
|
||||
| admin-web 现有代码 | `apps/admin-web/` | 技术栈参考(React + Vite + Ant Design) |
|
||||
| BD 手册-认证表 | `docs/database/BD_Manual_auth_tables.md` | auth schema 表结构 |
|
||||
| BD 手册-业务表 | `docs/database/BD_Manual_biz_tables.md` | biz schema 表结构 |
|
||||
| 权限矩阵 | `docs/permission_matrix/` | 角色-权限映射参考 |
|
||||
| DWD-DOC 标杆 | `docs/reports/DWD-DOC/` | 金额口径权威参考 |
|
||||
| 数据依赖矩阵 | `docs/prd/specs/00-数据依赖矩阵.md` | 租户管理后台数据源映射 |
|
||||
| member_retention_clue DDL | `db/zqyy_app/` | 维客线索表结构 |
|
||||
|
||||
---
|
||||
|
||||
## 七、预审查清单(SPEC 启动前确认)
|
||||
|
||||
### 7.1 账号与认证
|
||||
|
||||
1. **租户管理员账号模型**:是否需要独立的 `auth.tenant_admins` 表?还是复用 `auth.users` 表增加 `user_type` 字段区分?
|
||||
2. **密码策略**:初始密码是否需要强制修改?密码复杂度要求?是否需要密码过期机制?
|
||||
3. **多租户隔离**:一个管理员是否可以管辖多个租户?还是严格一对一?
|
||||
4. **会话管理**:JWT 过期时间?是否需要 refresh token?是否支持多设备同时登录?
|
||||
|
||||
### 7.2 用户审核
|
||||
|
||||
5. **关联匹配优先级**:助教表和员工信息表同时匹配到时,优先展示哪个?是否需要合并展示?
|
||||
6. **审核拒绝后**:用户是否可以重新申请?重新申请是新建记录还是更新原记录?
|
||||
7. **批量审核**:是否需要支持批量通过/拒绝?
|
||||
8. **审核通知**:审核结果是否需要通知用户(小程序消息/微信模板消息)?
|
||||
|
||||
### 7.3 Excel 上传
|
||||
|
||||
9. **写入策略**:财务支出/团购收入/充值归属是直接写入 DWS 表(方案 A)还是写入 staging 表由 ETL 同步(方案 B)?
|
||||
10. **文件大小限制**:单次上传的 Excel 文件大小上限?行数上限?
|
||||
11. **历史数据**:是否允许上传历史月份的数据?是否有时间范围限制?
|
||||
12. **模板版本**:Excel 模板是否需要版本管理?表头变更时如何兼容旧模板?
|
||||
13. **助教匹配失败处理**:模板 3/4 中助教姓名+编号匹配失败时,是阻断上传还是允许上传但标记警告?
|
||||
|
||||
### 7.4 维客线索
|
||||
|
||||
14. **隐藏 vs 删除**:隐藏的线索是否可以恢复显示?删除是否需要软删除(保留记录但标记删除)?
|
||||
15. **AI 线索保护**:AI 生成的线索(source=ai_consumption/ai_note)是否允许管理员修改/删除?修改后 source 是否变更为 manual?
|
||||
16. **线索审计**:线索的修改/删除操作是否需要记录操作日志(谁在什么时间做了什么操作)?
|
||||
17. **批量操作**:是否需要支持批量隐藏/删除线索?
|
||||
|
||||
### 7.5 部署与运维
|
||||
|
||||
18. **部署方式**:租户管理后台是独立部署还是与系统管理后台共享服务器?域名/路径如何规划?
|
||||
19. **前端构建**:是否与 admin-web 共享 pnpm workspace?还是完全独立的 package.json?
|
||||
20. **监控**:是否需要独立的访问日志和错误监控?
|
||||
|
||||
---
|
||||
|
||||
## 八、任务清单(草案,SPEC 细化后调整)
|
||||
|
||||
### Batch A:基础设施
|
||||
- [ ] T1:创建 `apps/tenant-admin/` 项目骨架(React + Vite + Ant Design)
|
||||
- [ ] T2:创建 `auth.tenant_admins` 表 + DDL 迁移脚本
|
||||
- [ ] T3:实现租户管理员登录 API(`tenant_auth.py`:登录/JWT 签发/鉴权中间件)
|
||||
- [ ] T4:创建 `biz.salary_adjustments` + `biz.excel_upload_log` 表 + DDL 迁移脚本
|
||||
|
||||
### Batch B:用户审核与管理
|
||||
- [ ] T5:实现用户审核后端 API(申请列表/关联建议/审核通过/审核拒绝)
|
||||
- [ ] T6:实现用户审核前端页面(申请列表 + 状态筛选 + 关联建议展示 + 审核操作)
|
||||
- [ ] T7:实现用户管理后端 API(用户列表/编辑/绑定修改)
|
||||
- [ ] T8:实现用户管理前端页面(用户列表 + 身份编辑 + 店铺归属)
|
||||
|
||||
### Batch C:Excel 上传
|
||||
- [ ] T9:实现 Excel 解析+校验后端(4 种模板的格式校验 + 人员匹配校验)
|
||||
- [ ] T10:实现冲突检测后端(主键匹配 + diff 数据生成)
|
||||
- [ ] T11:实现 Excel 上传前端(模板下载 + 上传 + 校验结果展示 + diff 交互 + 确认)
|
||||
- [ ] T12:创建 staging 表(如采用方案 B)+ 写入逻辑
|
||||
|
||||
### Batch D:维客线索管理
|
||||
- [ ] T13:`member_retention_clue` 新增 `is_hidden` 字段 + DDL 迁移
|
||||
- [ ] T14:实现维客线索后端 API(客户搜索/线索列表/修改/删除/隐藏)
|
||||
- [ ] T15:实现维客线索前端页面(客户搜索 + 线索列表 + 编辑/删除/隐藏操作)
|
||||
- [ ] T16:小程序端线索查询增加 `WHERE is_hidden = false` 条件
|
||||
300
docs/prd/Neo_Specs/RNS1-split-plan.md
Normal file
300
docs/prd/Neo_Specs/RNS1-split-plan.md
Normal file
@@ -0,0 +1,300 @@
|
||||
# RNS1 拆分计划 — NS1 小程序后端 API 补全
|
||||
|
||||
> 创建时间:2026-03-18
|
||||
> 来源:NS1 原始 spec + 两份 Storyboard 走查报告(助教视角 51 Gap + 管理层视角 31 Gap)
|
||||
> 拆分原则:每个子 spec 控制在 5-8 个任务、可独立交付验证、依赖关系清晰
|
||||
|
||||
---
|
||||
|
||||
## 拆分总览
|
||||
|
||||
| 子 Spec | 名称 | 任务数 | 依赖 | 交付物 |
|
||||
|---------|------|:------:|------|--------|
|
||||
| RNS1.0 | 基础设施与契约重写 | 6 | 无 | 全局中间件 + 重写后的 API 契约 + 前端跨页面参数修复 |
|
||||
| RNS1.1 | 任务与绩效接口 | 6 | RNS1.0 | TASK-1 扩展 + TASK-2 + PERF-1 + PERF-2 + 前端适配 |
|
||||
| RNS1.2 | 客户与助教接口 | 6 | RNS1.0 | CUST-1 + CUST-2 + COACH-1 + 前端适配 |
|
||||
| RNS1.3 | 三看板接口 | 7 | RNS1.0 | BOARD-1 + BOARD-2 + BOARD-3 + CONFIG-1 + 前端筛选修复 |
|
||||
| RNS1.4 | CHAT 对齐与联调收尾 | 5 | RNS1.1-1.3 | CHAT 迁移 + FDW 验证 + 前后端联调 |
|
||||
|
||||
总计:30 个任务,5 个子 spec。
|
||||
|
||||
---
|
||||
|
||||
## RNS1.0:基础设施与契约重写
|
||||
|
||||
> 阻塞所有后续子 spec,必须最先完成。
|
||||
> 预估:2-3 天
|
||||
|
||||
### 目标
|
||||
1. 后端全局响应包装 + camelCase 转换(R2 决策)
|
||||
2. API 契约按走查报告发现完全重写(BOARD-1/2/3、CUST-1、COACH-1、PERF-1、TASK-1 performance)
|
||||
3. 前端跨页面参数传递统一用唯一 ID
|
||||
|
||||
### 任务清单
|
||||
|
||||
| # | 任务 | 说明 | 产出 |
|
||||
|---|------|------|------|
|
||||
| T0-1 | 全局响应包装中间件 | `{ code: 0, data }` + 异常处理器 `{ code, message }` | `apps/backend/app/middleware/` |
|
||||
| T0-2 | Pydantic schema 统一 camelCase | `alias_generator=to_camel`,所有现有 + 新增 schema | `apps/backend/app/schemas/` |
|
||||
| T0-3 | 后端 TASK-4 路径 `/cancel-abandon` → `/restore` | 对齐契约(R1-3) | `xcx_tasks.py` |
|
||||
| T0-4 | 前端 `request()` 加 `.data` 解包 | 适配全局包装 | `api.ts` |
|
||||
| T0-5 | **API 契约完全重写** | 按走查报告重写全部接口响应定义(详见下方) | `API-contract.md` |
|
||||
| T0-6 | **前端跨页面参数修复** | 统一用唯一 ID 传参(详见下方) | 多个页面 `.ts` |
|
||||
|
||||
#### T0-5 契约重写范围
|
||||
|
||||
需要完全重写响应定义的接口:
|
||||
|
||||
| 接口 | 问题 | 重写要点 |
|
||||
|------|------|---------|
|
||||
| BOARD-3 | 扁平 metrics → 6 板块嵌套 | overview/recharge/revenue/cashflow/expense/coachAnalysis 完整字段定义,赠送卡 3×4 矩阵,收入结构表 9 行含子行,助教分析按等级分行,现金流出 4 子分组,环比格式(xxxCompare + isDown/isFlat) |
|
||||
| BOARD-1 | 10 通用字段 → 20 字段含 4 维度 | skills 改为 `Array<{text,cls}>`,补充 topCustomers,4 维度专属字段(perf/salary/sv/task),sort 参数改为枚举 |
|
||||
| BOARD-2 | 8 通用字段 → 40+ 维度专属字段 | 8 维度各自字段集,补充 assistants 关联助教列表,weeklyVisits 柱状图,coachDetails 明细表,potentialTags |
|
||||
| CUST-1 | 缺少 5 大模块 | 补充 balance/consumption60d/idealInterval/daysSinceVisit,aiInsight,coachTasks,favoriteCoaches,消费记录改为嵌套结构(含 coaches 子数组),备注列表 |
|
||||
| COACH-1 | 缺少 6 大模块 | 补充 performance(6 指标),income(本月/上月各 4 项),tierNodes,historyMonths,topCustomers 扩展(heartEmoji/score/balance),备注列表 |
|
||||
| PERF-1 | 扁平数组 → DateGroup | this_month_records 改为按日期分组结构,补充收入档位(currentTier/nextTier/upgradeHoursNeeded/upgradeBonus),lastMonthIncome,incomeItems.desc |
|
||||
| TASK-1 perf | 4 字段 → 15+ 字段 | 补充 tierNodes/basicHours/bonusHours/currentTier/nextTierHours/tierCompleted/bonusMoney/incomeTrend/incomeTrendDir/prevMonth |
|
||||
| CHAT-1/2 | 字段名不一致 + 缺失 | timestamp→created_at 统一,补充 title,referenceCard 结构,SSE 端点定义,customerId→chatId 映射 |
|
||||
|
||||
#### T0-6 前端跨页面参数修复范围
|
||||
|
||||
用户决策:统一用唯一 ID 传参。
|
||||
|
||||
| 来源页面 | 目标页面 | 当前问题 | 修复方案 |
|
||||
|---------|---------|---------|---------|
|
||||
| task-detail | chat | 传 `detail.id`(taskId)当 customerId | TASK-2 响应增加 `customer_id`,跳转改为 `?customerId={detail.customerId}` |
|
||||
| task-detail | customer-service-records | 同上 | 同上 |
|
||||
| customer-detail | customer-service-records | 未传 customerId | 跳转加 `?customerId={detail.id}` |
|
||||
| customer-detail | chat | 未传 customerId | 跳转加 `?customerId={detail.id}` |
|
||||
| coach-detail | customer-detail | 任务项用 name 跳转 | 改为 `?id={customerId}`(需 TaskItem 含 customerId) |
|
||||
| performance | task-detail | 传 customerName | 改为 `?id={taskId}`(需记录含 taskId) |
|
||||
| customer-detail | — | loadDetail() 用 `__route__` 解析 id | 改为从 `onLoad(options)` 获取 |
|
||||
| chat | API | 传 customerId 但 API 需 chatId | 后端支持 `?customerId=` 查询参数自动查找/创建对话 |
|
||||
|
||||
---
|
||||
|
||||
## RNS1.1:任务与绩效接口
|
||||
|
||||
> 助教日常使用频率最高的 4 个接口。
|
||||
> 依赖:RNS1.0(全局中间件 + 契约)
|
||||
> 预估:3-4 天
|
||||
|
||||
### 目标
|
||||
实现任务列表扩展、任务详情、绩效概览、绩效明细 4 个接口,覆盖助教视角的核心工作流。
|
||||
|
||||
### 任务清单
|
||||
|
||||
| # | 任务 | 说明 | 涉及 Gap |
|
||||
|---|------|------|---------|
|
||||
| T1-1 | 扩展 TASK-1(任务列表 + 绩效概览) | performance 从 4 字段扩展到 15+ 字段(tierNodes/basicHours/bonusHours/currentTier/nextTierHours/tierCompleted/bonusMoney/incomeTrend/incomeTrendDir/prevMonth);enrichTask 补充 lastVisitDays/balance/aiSuggestion | GAP-02, GAP-03 |
|
||||
| T1-2 | 实现 TASK-2(任务详情完整版) | 服务记录/备注各 20 条懒加载;响应含 `customer_id`;维客线索统一 tag 格式和 source 枚举;aiAnalysis 明确 cache_type 映射 | GAP-06~11 |
|
||||
| T1-3 | 实现 PERF-1(绩效概览) | this_month_records 按 DateGroup 分组;收入档位数据(currentTier/nextTier/upgradeHoursNeeded/upgradeBonus);lastMonthIncome;incomeItems 含 desc | GAP-12~16 |
|
||||
| T1-4 | 实现 PERF-2(绩效明细) | 20 条懒加载;按日期分组;courseTypeClass 枚举统一(`basic`/`vip`/`tip`,不带 `tag-` 前缀) | GAP-19~20 |
|
||||
| T1-5 | 实现 pin/unpin API 端点 | `POST /tasks/{id}/pin` + `POST /tasks/{id}/unpin`;前端 api.ts 补充对应函数 | GAP-04 |
|
||||
| T1-6 | 前端适配(任务+绩效页面) | createNote 补充 score 参数;performance 页添加月份切换(F8);performance-records 月份切换重置分页(F9);avatarChar/avatarColor 前端自行计算(GAP-19 决策) | GAP-05, F8, F9 |
|
||||
|
||||
### 数据源
|
||||
- `dws_assistant_salary_calc` via FDW → 绩效/档位/收入
|
||||
- `dwd_assistant_service_log` via FDW → 服务记录
|
||||
- `biz.coach_tasks` → 任务列表
|
||||
- `biz.ai_cache` → AI 分析/话术
|
||||
- `public.member_retention_clue` → 维客线索
|
||||
|
||||
### 验收标准
|
||||
- 助教登录后可看到真实任务列表 + 绩效卡片
|
||||
- 点击任务可查看完整详情(服务记录、维客线索、AI 分析、备注)
|
||||
- 绩效概览展示真实档位进度和收入明细
|
||||
- 绩效明细按日期分组展示,支持月份切换
|
||||
|
||||
---
|
||||
|
||||
## RNS1.2:客户与助教接口
|
||||
|
||||
> 详情页是 Gap 最集中的区域,数据结构最复杂。
|
||||
> 依赖:RNS1.0(全局中间件 + 契约)
|
||||
> 预估:3-4 天
|
||||
|
||||
### 目标
|
||||
实现客户详情、客户服务记录、助教详情 3 个接口,覆盖两个角色视角的详情查看需求。
|
||||
|
||||
### 任务清单
|
||||
|
||||
| # | 任务 | 说明 | 涉及 Gap |
|
||||
|---|------|------|---------|
|
||||
| T2-1 | 实现 CUST-1(客户详情) | 含 balance/consumption60d/idealInterval/daysSinceVisit(Banner);aiInsight(biz.ai_cache app4);维客线索;消费记录嵌套结构(含 coaches 子数组、tableFee/foodAmount 分项);备注列表 | GAP-23~30 |
|
||||
| T2-2 | 实现 CUST-1 coachTasks 模块 | 关联助教任务列表:从 biz.coach_tasks + FDW 聚合,每位助教含任务类型/状态/最后服务时间/近60天服务次数/总时长/次均时长 | GAP-25 |
|
||||
| T2-3 | 实现 CUST-1 favoriteCoaches 模块 | 最亲密助教:从 `v_dws_member_assistant_relation_index` 获取关系指数,聚合基础课时/激励课时/上课次数/充值金额 | GAP-26 |
|
||||
| T2-4 | 实现 CUST-2(客户服务记录) | 按月查询(改掉前端全量加载本地过滤);补充 recordType/isEstimate;月度统计汇总(monthCount/monthHours);customerPhoneFull;totalServiceCount | GAP-32~35 |
|
||||
| T2-5 | 实现 COACH-1(助教详情) | performance(6 指标);income(本月/上月各 4 项);tierNodes;topCustomers 扩展(heartEmoji/score/balance/consume);近期服务明细含 perfHours;任务分组(active/inactive/abandoned 含 notes 子数组和 reason);备注列表 | GAP-38~44 |
|
||||
| T2-6 | 实现 COACH-1 historyMonths 模块 | 历史月份统计(最近 5+ 个月):客户数/工时/工资/回访完成数/召回完成数/是否预估 | GAP-41 |
|
||||
|
||||
### 数据源
|
||||
- `fdw_etl.v_dim_member` → 客户基本信息(DQ-6:member_phone 不可靠,用 dim_member.mobile)
|
||||
- `fdw_etl.v_dws_member_consumption_summary` → 消费汇总/余额
|
||||
- `fdw_etl.v_dws_member_assistant_relation_index` → 关系指数
|
||||
- `fdw_etl.v_dwd_assistant_service_log` → 服务记录明细
|
||||
- `fdw_etl.v_dwd_table_fee_log` → 台费明细
|
||||
- `fdw_etl.v_dim_assistant` → 助教基本信息
|
||||
- `fdw_etl.v_dws_assistant_salary_calc` → 助教绩效/收入
|
||||
- `biz.coach_tasks` → 任务关联
|
||||
- `biz.ai_cache` → AI 洞察
|
||||
- `public.member_retention_clue` → 维客线索
|
||||
|
||||
### 关键约束
|
||||
- 金额口径:统一使用 `items_sum`,禁用 `consume_money`(DWD-DOC 强制规则 1)
|
||||
- 助教费用拆分:`assistant_pd_money`(陪打)+ `assistant_cx_money`(超休),禁用 `service_fee`(DWD-DOC 强制规则 2)
|
||||
- 会员信息:通过 `member_id` JOIN `dim_member`,禁用 `settlement_head.member_phone`(DQ-6)
|
||||
|
||||
### 验收标准
|
||||
- 客户详情页展示真实余额、消费、AI 洞察、关联助教、最亲密助教、消费记录(含助教明细)、备注
|
||||
- 客户服务记录支持按月切换,展示真实数据
|
||||
- 助教详情页展示真实绩效、收入明细、档位进度、TOP20 客户、历史月份统计、任务分组
|
||||
|
||||
---
|
||||
|
||||
## RNS1.3:三看板接口
|
||||
|
||||
> 看板是管理层视角的核心,BOARD-3 财务看板是全项目最复杂的单个接口。
|
||||
> 依赖:RNS1.0(全局中间件 + 契约)
|
||||
> 预估:4-5 天
|
||||
|
||||
### 目标
|
||||
实现 3 个看板接口 + 技能配置接口,修复前端看板筛选 Bug。
|
||||
|
||||
### 任务清单
|
||||
|
||||
| # | 任务 | 说明 | 涉及 Gap |
|
||||
|---|------|------|---------|
|
||||
| T3-1 | 实现 BOARD-1(助教看板) | 4 维度专属字段(perf/salary/sv/task);skills 为 `Array<{text,cls}>`;topCustomers;sort 枚举参数;time 参数日期范围计算 | G2, 八¾ B1 |
|
||||
| T3-2 | 实现 BOARD-2(客户看板) | 8 维度各自字段集;assistants 关联助教列表;weeklyVisits 柱状图(freq60);coachDetails 明细表(loyal);potentialTags(potential);20 条懒加载 | G3, 八¾ B2 |
|
||||
| T3-3 | 实现 BOARD-3 经营一览 + 预收资产 | overview(8 指标 + 8 环比);recharge(储值卡 5 指标 + 赠送卡 3×4 矩阵 = 24 字段 + 24 环比);area≠all 时预收资产隐藏 | G1, G6, G10 |
|
||||
| T3-4 | 实现 BOARD-3 应计收入 + 现金流 | revenue(收入结构表 9 行含子行 + 正价/优惠/渠道明细);cashflow(消费收款 + 充值收款 + 合计) | G7 |
|
||||
| T3-5 | 实现 BOARD-3 现金流出 + 助教分析 | expense(4 子分组:经营/固定/助教分成/平台服务费);coachAnalysis(基础课 + 激励课各按 4 等级分行) | G8, G9 |
|
||||
| T3-6 | 实现 CONFIG-1(技能类型列表) | 从 ETL cfg 表读取,前端 api.ts 硬编码作为 mock 回退 | R8-1 |
|
||||
| T3-7 | 前端看板筛选修复 | F1-F6(筛选变更触发重新请求);BOARD-2 补充分页参数;BOARD-3 fetchBoardFinance 签名扩展为 `{time,area,compare}` | F1~F6 |
|
||||
|
||||
### BOARD-3 环比计算说明
|
||||
- 所有环比为月环比(与上一个相同时间周期对比)
|
||||
- 后端根据 `time` 参数计算当期和上期日期范围,分别查询后计算百分比
|
||||
- 返回格式:每个值旁有 `xxxCompare: string`(如 `"+12.5%"`)+ `isDown: boolean` + `isFlat: boolean`
|
||||
- `compare=0` 时不计算环比,减少查询开销
|
||||
|
||||
### 数据源
|
||||
- BOARD-1:`v_dws_assistant_salary_calc` + `v_dws_assistant_monthly_summary` + `v_dim_assistant`
|
||||
- BOARD-2:8 个维度对应不同 FDW 表(详见八¾ B2 维度映射)
|
||||
- BOARD-3:6 个 `v_dws_finance_*` 表 + `v_dws_assistant_salary_calc`(助教分析)
|
||||
- CONFIG-1:ETL cfg 表
|
||||
|
||||
### 验收标准
|
||||
- 助教看板 4 维度切换正常,筛选变更触发重新请求
|
||||
- 客户看板 8 维度切换正常,懒加载 20 条
|
||||
- 财务看板 6 板块数据正确,环比开关工作,区域筛选隐藏预收资产
|
||||
- 技能类型从 cfg 表读取
|
||||
|
||||
---
|
||||
|
||||
## RNS1.4:CHAT 对齐与联调收尾
|
||||
|
||||
> 最后阶段,依赖前面所有子 spec 完成。
|
||||
> 预估:2-3 天
|
||||
|
||||
### 目标
|
||||
CHAT 模块路径迁移和功能补全,FDW 端到端验证,全量前后端联调。
|
||||
|
||||
### 任务清单
|
||||
|
||||
| # | 任务 | 说明 | 涉及 Gap |
|
||||
|---|------|------|---------|
|
||||
| T4-1 | CHAT 路径迁移 | `/api/ai/*` → `/api/xcx/chat/*`;保留 SSE 流式端点 | R3, GAP-51 |
|
||||
| T4-2 | 新增 CHAT-1/2 同步端点 | 历史列表(含 title 字段)+ 消息查看(timestamp→created_at 统一);支持 `?customerId=` 查询参数自动查找/创建对话 | GAP-45~50 |
|
||||
| T4-3 | CHAT referenceCard 支持 | 消息中附带结构化引用卡片(type/title/summary/data);多入口参数路由(customerId/historyId/coachId) | GAP-45, GAP-50 |
|
||||
| T4-4 | FDW 端到端验证 | 验证所有 FDW 查询在 test_zqyy_app → test_etl_feiqiu 链路上正常工作;检查索引和查询性能 | — |
|
||||
| T4-5 | 前后端联调 + 修复 | 全量联调 13 个页面;修复联调中发现的问题;notes 页补充触底加载(F11);customer-service-records 改为按月请求 API(F10) | F10, F11 |
|
||||
|
||||
### 验收标准
|
||||
- CHAT 模块 SSE 流式 + 同步端点均可用
|
||||
- 从任意入口进入 chat 页面均能正确关联上下文
|
||||
- 13 个页面全部连接真实后端运行,无 mock 数据残留
|
||||
|
||||
---
|
||||
|
||||
## 执行顺序与依赖关系
|
||||
|
||||
```
|
||||
RNS1.0(基础设施+契约)
|
||||
├── RNS1.1(任务+绩效)──┐
|
||||
├── RNS1.2(客户+助教)──┼── RNS1.4(CHAT+联调)
|
||||
└── RNS1.3(三看板)─────┘
|
||||
```
|
||||
|
||||
- RNS1.0 必须最先完成(阻塞所有后续)
|
||||
- RNS1.1 / RNS1.2 / RNS1.3 可并行(无相互依赖)
|
||||
- RNS1.4 依赖 RNS1.1-1.3 全部完成
|
||||
|
||||
---
|
||||
|
||||
## 与 NS1 原始 spec 的关系
|
||||
|
||||
NS1 原始 spec(`NS1-xcx-backend-api.md`)保留为历史参考文档,其中:
|
||||
- 一~七章:技术架构和接口设计(被 RNS1.0 T0-5 契约重写替代)
|
||||
- 八½:前置审查发现 R1-R8(决策仍有效,分散到各子 spec 执行)
|
||||
- 八¾:看板筛选交叉矩阵(仍为权威参考,RNS1.3 直接引用)
|
||||
- 九:原始任务清单(被本拆分计划替代)
|
||||
|
||||
两份走查报告保留在 `docs/reports/` 作为需求追溯依据:
|
||||
- `storyboard-walkthrough-assistant-view.md`(助教视角,51 Gap)
|
||||
- `miniprogram-storyboard-walkthrough-gaps.md`(管理层视角,31 Gap)
|
||||
|
||||
---
|
||||
|
||||
## Gap 追溯矩阵
|
||||
|
||||
### 助教视角报告 Gap → 子 Spec 映射
|
||||
|
||||
| Gap | 描述 | 子 Spec |
|
||||
|-----|------|---------|
|
||||
| GAP-01 | globalData.authUser 缺少 role/store_name/coach_level/avatar | RNS1.0 T0-6(前端修复,/me 已返回这些字段) |
|
||||
| GAP-02 | TASK-1 performance 字段不足(4→15+) | RNS1.1 T1-1 |
|
||||
| GAP-03 | enrichTask 需要 lastVisitDays/balance/aiSuggestion | RNS1.1 T1-1 |
|
||||
| GAP-04 | pin/unpin API 端点未定义 | RNS1.1 T1-5 |
|
||||
| GAP-05 | createNote 缺少 score 参数 | RNS1.1 T1-6 |
|
||||
| GAP-06~08 | 维客线索 tag/source 格式 + aiAnalysis cache_type | RNS1.1 T1-2 |
|
||||
| GAP-09~10 | taskId/customerId 混淆 | RNS1.0 T0-6 |
|
||||
| GAP-11 | storageLevel/relationLevel 计算逻辑 | RNS1.1 T1-6(前端自行计算) |
|
||||
| GAP-12~16 | PERF-1 DateGroup/档位/lastMonthIncome/incomeItems | RNS1.1 T1-3 |
|
||||
| GAP-17 | performance→task-detail 传参不匹配 | RNS1.0 T0-6 |
|
||||
| GAP-18 | performance 无月份切换 | RNS1.1 T1-6(F8) |
|
||||
| GAP-19~22 | PERF-2 avatarChar/courseTypeClass/分页重置/Banner | RNS1.1 T1-4 + T1-6 |
|
||||
| GAP-23~30 | CUST-1 五大缺失模块 | RNS1.2 T2-1~T2-3 |
|
||||
| GAP-31 | customer-detail 跳转未传 customerId | RNS1.0 T0-6 |
|
||||
| GAP-32~35 | CUST-2 扩展字段 + 按月查询 | RNS1.2 T2-4 |
|
||||
| GAP-36~37 | notes 触底加载 + tagType 语义 | RNS1.4 T4-5 |
|
||||
| GAP-38~44 | COACH-1 六大缺失模块 | RNS1.2 T2-5~T2-6 |
|
||||
| GAP-45~51 | CHAT 模块全部 Gap | RNS1.4 T4-1~T4-3 |
|
||||
|
||||
### 管理层视角报告 Gap → 子 Spec 映射
|
||||
|
||||
| Gap | 描述 | 子 Spec |
|
||||
|-----|------|---------|
|
||||
| G1 | BOARD-3 响应结构完全不匹配 | RNS1.3 T3-3~T3-5 |
|
||||
| G2 | BOARD-1 字段严重不足 | RNS1.3 T3-1 |
|
||||
| G3 | BOARD-2 字段严重不足 | RNS1.3 T3-2 |
|
||||
| G4 | COACH-1 缺少多个核心区域 | RNS1.2 T2-5~T2-6 |
|
||||
| G5 | CUST-1 缺少管理层核心数据 | RNS1.2 T2-1~T2-3 |
|
||||
| G6~G10 | BOARD-3 各子模块缺失 + 环比格式 | RNS1.3 T3-3~T3-5 |
|
||||
| G11 | customer-detail→customer-service-records 未传 customerId | RNS1.0 T0-6 |
|
||||
| G12 | coach-detail 任务项用 name 跳转 | RNS1.0 T0-6 |
|
||||
| G13~G14 | api.ts 返回类型不匹配 | RNS1.0 T0-5(契约重写时同步更新类型) |
|
||||
| G15~G26 | 各接口中等严重度缺陷 | 分散到对应子 Spec |
|
||||
| G27~G31 | 低严重度设计决策 | 分散到对应子 Spec |
|
||||
|
||||
### 前端修复项 → 子 Spec 映射
|
||||
|
||||
| 修复项 | 描述 | 子 Spec |
|
||||
|--------|------|---------|
|
||||
| F1~F6 | 看板筛选变更不触发重新请求 | RNS1.3 T3-7 |
|
||||
| F7 | task-list status 筛选 UI 未实现 | RNS1.1 T1-6(低优先级,可后续) |
|
||||
| F8 | performance 无月份切换 | RNS1.1 T1-6 |
|
||||
| F9 | performance-records 月份切换未重置分页 | RNS1.1 T1-6 |
|
||||
| F10 | customer-service-records 本地筛选改 API 请求 | RNS1.4 T4-5 |
|
||||
| F11 | notes 无触底加载 | RNS1.4 T4-5 |
|
||||
813
docs/prd/Neo_Specs/miniprogram-storyboard-walkthrough-gaps.md
Normal file
813
docs/prd/Neo_Specs/miniprogram-storyboard-walkthrough-gaps.md
Normal file
@@ -0,0 +1,813 @@
|
||||
# 小程序管理层视角 Storyboard 走查报告 — API 需求 Gap 分析
|
||||
|
||||
> 走查日期:2026-03-18
|
||||
> 走查角色:管理层/店长
|
||||
> 走查路线:3 个看板 → 助教详情 → 客户详情 → 服务记录
|
||||
> 对比基准:前端内联 mock 数据 vs API 契约 (`API-contract.md`) vs 八¾节 (`NS1-xcx-backend-api.md`)
|
||||
|
||||
---
|
||||
|
||||
## 场景 1:助教看板 → 助教详情 → 客户详情
|
||||
|
||||
### 1.1 board-coach(`pages/board-coach/board-coach.ts`)
|
||||
|
||||
#### API 调用
|
||||
- `fetchBoardCoaches({ skill, sort, time })` → `CoachItem[]`
|
||||
- 页面 `onLoad()` → `loadData()` → `fetchBoardCoaches()`
|
||||
- 下拉刷新 `onPullDownRefresh()` → `loadData()`
|
||||
- ⚠️ 筛选变更(`onSortChange`/`onSkillChange`/`onTimeChange`)仅更新 data 状态,**不调用 `loadData()`**(八¾节 F1 已记录)
|
||||
|
||||
#### 前端期望字段(从内联 MOCK_COACHES 的 CoachItem 接口提取,共 18 个字段)
|
||||
|
||||
| 字段 | 类型 | 说明 | 数据源 |
|
||||
|------|------|------|--------|
|
||||
| `id` | string | 助教 ID | dim_assistant |
|
||||
| `name` | string | 助教姓名 | dim_assistant |
|
||||
| `initial` | string | 姓名首字(头像占位) | 前端可计算 |
|
||||
| `avatarGradient` | string | 头像渐变色 (blue/green/pink/amber/violet/cyan) | 前端可计算或后端分配 |
|
||||
| `level` | string | 等级 key: star/senior/middle/junior | dim_assistant |
|
||||
| `levelClass` | string | 等级样式类 | 前端可映射(LEVEL_CLASS 常量) |
|
||||
| `skills` | `Array<{text: string, cls: string}>` | 技能列表含 emoji 和样式类 | cfg 表 |
|
||||
| `topCustomers` | `string[]` | Top 客户列表含 emoji 前缀(如 `'💖 王先生'`) | dws_member_assistant_relation_index |
|
||||
| `perfHours` | number | 当期定档工时 | dws_assistant_salary_calc |
|
||||
| `perfHoursBefore` | number? | 上期定档工时(可选) | dws_assistant_salary_calc (上期) |
|
||||
| `perfGap` | string? | 距升档差距描述(如 `"距升档 13.8h"`,已达标时不返回) | 后端计算 |
|
||||
| `perfReached` | boolean | 是否已达标 | 后端计算 |
|
||||
| `salary` | number | 工资总额(元) | dws_assistant_salary_calc |
|
||||
| `salaryPerfHours` | number | 定档工时 | 同 perfHours |
|
||||
| `salaryPerfBefore` | number? | 上期定档工时 | 同 perfHoursBefore |
|
||||
| `svAmount` | number | 客源储值总额(元) | dws_member_consumption_summary |
|
||||
| `svCustomerCount` | number | 储值客户数 | dws_member_consumption_summary |
|
||||
| `svConsume` | number | 储值消耗额(元) | dws_member_consumption_summary |
|
||||
| `taskRecall` | number | 召回任务完成数 | biz.coach_tasks |
|
||||
| `taskCallback` | number | 回访任务完成数 | biz.coach_tasks |
|
||||
|
||||
#### 筛选参数映射
|
||||
|
||||
| 筛选项 | 前端参数 | 后端参数 | 查询影响 |
|
||||
|--------|---------|---------|---------|
|
||||
| 排序维度 | `sort`: perf_desc/perf_asc/salary_desc/salary_asc/sv_desc/task_desc | `sort` | 决定排序字段和方向 + 卡片模板(dimType) |
|
||||
| 技能筛选 | `skill`: all/chinese/snooker/mahjong/karaoke | `skill` | WHERE 过滤助教技能类型 |
|
||||
| 时间范围 | `time`: month/quarter/last_month/last_3m/last_quarter/last_6m | `time` | 决定统计日期区间 |
|
||||
|
||||
交叉约束:`time=last_6m` + `sort=sv_desc` 不兼容(TIME_OPTIONS 注释标注)
|
||||
|
||||
#### 页面跳转
|
||||
- 点击助教卡片 `onCoachTap` → `/pages/coach-detail/coach-detail?id={coachId}`
|
||||
- Tab 切换 → `board-finance`(switchTab)/ `board-customer`(navigateTo)
|
||||
|
||||
#### API 契约 vs Mock 数据 vs 八¾节 差异
|
||||
|
||||
| 差异项 | API 契约 (BOARD-1) | 前端 Mock (CoachItem) | 八¾节 (B1) | 严重度 |
|
||||
|--------|--------------------|-----------------------|------------|--------|
|
||||
| 字段结构 | 扁平简单:`id, name, avatar, level, level_class, skills(string[]), total_hours, total_income, customer_count, satisfaction` | 丰富结构:4 维度 20 个字段,skills 为对象数组,含 topCustomers | 与 Mock 一致,详细列出 4 维度字段 | 🔴 高 |
|
||||
| `initial` 字段 | ❌ 无 | ✅ 有 | ✅ 有 | 🟠 中 |
|
||||
| `avatarGradient` 字段 | ❌ 无(仅 `avatar` URL) | ✅ 有 | ✅ 有 | 🟠 中 |
|
||||
| `skills` 类型 | `string[]` | `Array<{text, cls}>` | `Array<{text, cls}>` | 🔴 高 |
|
||||
| `topCustomers` 字段 | ❌ 无 | ✅ 有 | ✅ 有 | 🔴 高 |
|
||||
| 4 维度专属字段 | ❌ 全部缺失(仅 total_hours/total_income/customer_count/satisfaction) | ✅ 完整(perf/salary/sv/task 各 2-4 个字段) | ✅ 完整 | 🔴 高 |
|
||||
| `sort` 参数格式 | `sort=field:direction` | `sort=perf_desc` 等枚举 | `sort=perf_desc` 等枚举 | 🟠 中 |
|
||||
| 分页 | 无分页定义 | 无分页(全量返回) | 无分页 | 🟡 低 |
|
||||
|
||||
#### spec 未记录的 Gap
|
||||
1. 🔴 API 契约 BOARD-1 响应字段与前端实际需求严重不匹配 — 契约仅定义 10 个通用字段,前端需要 20 个含 4 维度专属字段
|
||||
2. 🔴 `skills` 类型不一致 — 契约为 `string[]`,前端需要 `Array<{text, cls}>`(含 emoji 和样式类)
|
||||
3. 🔴 `topCustomers` 字段契约完全缺失 — 前端每个助教卡片展示 Top 3 客户
|
||||
4. 🟠 `sort` 参数格式不一致 — 契约用 `field:direction`,前端用枚举值 `perf_desc`
|
||||
5. 🟠 `initial`/`avatarGradient` 字段契约缺失 — 前端用于头像渲染,需确认由前端计算还是后端返回
|
||||
|
||||
---
|
||||
|
||||
### 1.2 coach-detail(`pages/coach-detail/coach-detail.ts`)
|
||||
|
||||
#### API 调用
|
||||
- `fetchCoachDetail(coachId)` → `CoachCard | null`(api.ts 返回类型过于简单)
|
||||
- 页面 `onLoad({ id })` → `loadData(id)` → `fetchCoachDetail(id)`
|
||||
- 实际使用内联 mock 数据组装 CoachDetail 对象
|
||||
|
||||
#### 前端期望字段(从 mockCoachDetail + 各 mock 数组提取)
|
||||
|
||||
**CoachDetail 基础信息:**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | string | 助教 ID |
|
||||
| `name` | string | 助教姓名 |
|
||||
| `avatar` | string | 头像 URL |
|
||||
| `level` | string | 等级(中文:星级/高级/中级/初级) |
|
||||
| `skills` | string[] | 技能列表 |
|
||||
| `workYears` | number | 工龄 |
|
||||
| `customerCount` | number | 客户数 |
|
||||
| `hireDate` | string | 入职日期 |
|
||||
|
||||
**performance 绩效指标:**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `performance.monthlyHours` | number | 本月定档业绩工时 |
|
||||
| `performance.monthlySalary` | number | 本月工资(预估) |
|
||||
| `performance.customerBalance` | number | 客源储值余额 |
|
||||
| `performance.tasksCompleted` | number | 本月任务完成数 |
|
||||
| `performance.perfCurrent` | number | 当前绩效值 |
|
||||
| `performance.perfTarget` | number | 目标绩效值 |
|
||||
|
||||
**income 收入明细(本月/上月 Tab 切换):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `income.thisMonth` | `IncomeItem[]` | 本月收入明细(基础课时费/激励课时费/充值提成/酒水提成) |
|
||||
| `income.lastMonth` | `IncomeItem[]` | 上月收入明细 |
|
||||
| IncomeItem: `label` | string | 收入项名称 |
|
||||
| IncomeItem: `amount` | string | 金额(格式化,如 `"¥3,500"`) |
|
||||
| IncomeItem: `color` | string | 颜色标识 |
|
||||
|
||||
**任务执行区域(管理层视角核心):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `visibleTasks` | `TaskItem[]` | 可见任务(active 状态) |
|
||||
| `hiddenTasks` | `TaskItem[]` | 隐藏任务(inactive 状态,被顶替但未过期) |
|
||||
| `abandonedTasks` | `AbandonedTask[]` | 已放弃任务 |
|
||||
| TaskItem: `typeLabel` | string | 任务类型标签(高优先召回/优先召回/关系构建/客户回访) |
|
||||
| TaskItem: `typeClass` | string | 样式类(high-priority/priority/relationship/callback) |
|
||||
| TaskItem: `customerName` | string | 客户姓名 |
|
||||
| TaskItem: `noteCount` | number | 备注数量 |
|
||||
| TaskItem: `pinned` | boolean | 是否置顶 |
|
||||
| TaskItem: `notes` | `Array<{pinned?, text, date}>` | 备注列表(可选) |
|
||||
| AbandonedTask: `customerName` | string | 客户姓名 |
|
||||
| AbandonedTask: `reason` | string | 放弃原因 |
|
||||
|
||||
**客户关系 TOP20(管理层关注指标):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| TopCustomer: `id` | string | 客户 ID |
|
||||
| TopCustomer: `name` | string | 客户姓名 |
|
||||
| TopCustomer: `initial` | string | 姓名首字 |
|
||||
| TopCustomer: `avatarGradient` | string | 头像渐变色 |
|
||||
| TopCustomer: `heartEmoji` | string | 爱心 emoji(❤️/💛/🤍) |
|
||||
| TopCustomer: `score` | string | 关系指数 |
|
||||
| TopCustomer: `scoreColor` | string | 分数颜色(success/warning/gray) |
|
||||
| TopCustomer: `serviceCount` | number | 服务次数 |
|
||||
| TopCustomer: `balance` | string | 余额(格式化) |
|
||||
| TopCustomer: `consume` | string | 消费额(格式化) |
|
||||
|
||||
**近期服务明细:**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| ServiceRecord: `customerId` | string? | 客户 ID |
|
||||
| ServiceRecord: `customerName` | string | 客户姓名 |
|
||||
| ServiceRecord: `initial` | string | 姓名首字 |
|
||||
| ServiceRecord: `avatarGradient` | string | 头像渐变色 |
|
||||
| ServiceRecord: `type` | string | 课程类型(基础课/激励课) |
|
||||
| ServiceRecord: `typeClass` | string | 样式类(basic/incentive) |
|
||||
| ServiceRecord: `table` | string | 台桌号 |
|
||||
| ServiceRecord: `duration` | string | 时长(格式化) |
|
||||
| ServiceRecord: `income` | string | 收入(格式化) |
|
||||
| ServiceRecord: `date` | string | 日期时间 |
|
||||
| ServiceRecord: `perfHours` | string? | 定档工时(可选) |
|
||||
|
||||
**历史月份统计(管理层评估助教表现):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| HistoryMonth: `month` | string | 月份标签(本月/上月/4月...) |
|
||||
| HistoryMonth: `estimated` | boolean | 是否预估 |
|
||||
| HistoryMonth: `customers` | string | 客户数(格式化,如 `"22人"`) |
|
||||
| HistoryMonth: `hours` | string | 工时(格式化,如 `"87.5h"`) |
|
||||
| HistoryMonth: `salary` | string | 工资(格式化) |
|
||||
| HistoryMonth: `callbackDone` | number | 回访完成数 |
|
||||
| HistoryMonth: `recallDone` | number | 召回完成数 |
|
||||
|
||||
**备注列表:**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| NoteItem: `id` | string | 备注 ID |
|
||||
| NoteItem: `content` | string | 备注内容 |
|
||||
| NoteItem: `timestamp` | string | 时间戳 |
|
||||
| NoteItem: `score` | number | 评分 |
|
||||
| NoteItem: `customerName` | string | 关联客户/管理员名 |
|
||||
| NoteItem: `tagLabel` | string | 标签 |
|
||||
| NoteItem: `createdAt` | string | 创建时间(格式化) |
|
||||
|
||||
**绩效进度条数据(前端需要后端提供):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `tierNodes` | number[] | 档位节点数组(如 `[0, 100, 130, 160, 190, 220]`) |
|
||||
| `maxHours` | number | 最大工时(进度条满值) |
|
||||
|
||||
#### 筛选参数映射
|
||||
- 无筛选项,单次加载全部数据
|
||||
- 收入明细有本月/上月 Tab 切换(纯前端切换,数据一次性返回)
|
||||
|
||||
#### 页面跳转
|
||||
- 点击任务项 `onTaskItemTap` → `/pages/customer-detail/customer-detail?name={customerName}`(⚠️ 用 name 而非 id)
|
||||
- 点击客户卡片 `onCustomerTap` → `/pages/customer-detail/customer-detail?id={customerId}`
|
||||
- 点击服务记录 `onSvcCardTap` → `/pages/customer-detail/customer-detail?id={customerId}`
|
||||
- 查看更多服务记录 `onViewMoreRecords` → `/pages/performance-records/performance-records?coachId={coachId}`
|
||||
- 问问助手 `onStartChat` → `/pages/chat/chat?coachId={coachId}`
|
||||
|
||||
#### API 契约 vs Mock 数据 vs 八¾节 差异
|
||||
|
||||
| 差异项 | API 契约 (COACH-1) | 前端 Mock | 八¾节 | 严重度 |
|
||||
|--------|--------------------|-----------|----|--------|
|
||||
| 返回类型 | api.ts 返回 `CoachCard`(仅 id/name/avatar/level/keyMetrics) | 实际需要 CoachDetail(含 performance/income/tasks/topCustomers/serviceRecords/historyMonths/notes) | 仅提到基本信息+任务分组+Top客户+服务记录 | 🔴 高 |
|
||||
| `income` 本月/上月明细 | ❌ 无 | ✅ 有(4 项收入分类 × 2 月) | ❌ 无 | 🔴 高 |
|
||||
| `topCustomers` TOP20 | 契约仅 `top_customers: Array<{id, name, avatar, total_spend, visit_count}>` | Mock 含 20 条,每条含 heartEmoji/score/scoreColor/serviceCount/balance/consume | ❌ 未详细定义字段 | 🔴 高 |
|
||||
| `historyMonths` 历史月份 | ❌ 完全缺失 | ✅ 有(5 个月,含 estimated/customers/hours/salary/callbackDone/recallDone) | ❌ 完全缺失 | 🔴 高 |
|
||||
| `tierNodes` 档位节点 | ❌ 缺失 | ✅ 有(进度条组件需要) | ❌ 缺失 | 🟠 中 |
|
||||
| 任务分组 | `visible_tasks`/`hidden_tasks`/`abandoned_tasks` | 同,但 TaskItem 结构含 notes 子数组 | ✅ 已确认 active/inactive/abandoned 映射 | 🟡 低 |
|
||||
| 备注列表 | ❌ 未在 COACH-1 定义 | ✅ 有(含 score 字段) | ❌ 未提及 | 🟠 中 |
|
||||
|
||||
#### spec 未记录的 Gap
|
||||
1. 🔴 COACH-1 响应缺少 `income` 收入明细(本月/上月 4 项分类)— 管理层评估助教收入结构的核心数据
|
||||
2. 🔴 COACH-1 响应缺少 `historyMonths` 历史月份统计 — 管理层评估助教长期表现趋势
|
||||
3. 🔴 `topCustomers` 字段结构不完整 — 契约仅 5 个字段,前端需要 10 个字段(含 heartEmoji/scoreColor/balance/consume)
|
||||
4. 🟠 COACH-1 响应缺少 `tierNodes`/`maxHours` — 绩效进度条组件需要档位节点数据
|
||||
5. 🟠 COACH-1 响应缺少备注列表 — 管理层需要查看助教相关备注
|
||||
6. 🟠 `onTaskItemTap` 用 `name` 而非 `id` 跳转客户详情 — 前端 Bug,name 不唯一
|
||||
7. 🟡 api.ts 中 `fetchCoachDetail` 返回类型为 `CoachCard`(mock-data.ts 中的简单类型),与实际需要的 CoachDetail 不匹配
|
||||
|
||||
---
|
||||
|
||||
## 场景 2:客户看板 → 客户详情 → 客户服务记录
|
||||
|
||||
### 2.1 board-customer(`pages/board-customer/board-customer.ts`)
|
||||
|
||||
#### API 调用
|
||||
- `fetchBoardCustomers({ dimension, project })` → `CustomerItem[]`
|
||||
- 页面 `onLoad()` → `loadData()` → `fetchBoardCustomers()`
|
||||
- 下拉刷新 `onPullDownRefresh()` → `loadData()`
|
||||
- ⚠️ 筛选变更(`onDimensionChange`/`onProjectChange`)仅更新 data 状态,**不调用 `loadData()`**(八¾节 F2 已记录)
|
||||
- ⚠️ 缺少分页参数 `page`/`pageSize`(八¾节 F3 已记录)
|
||||
|
||||
#### 前端期望字段(从内联 MOCK_CUSTOMERS 的 CustomerItem 接口提取)
|
||||
|
||||
**所有维度共享基础字段(每个 item 都返回):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | string | 客户 member_id |
|
||||
| `name` | string | 客户姓名 |
|
||||
| `initial` | string | 姓名首字 |
|
||||
| `avatarCls` | string | 头像样式类(avatar--amber/pink/blue 等) |
|
||||
| `assistants` | `AssistantInfo[]` | 关联助教列表 |
|
||||
|
||||
**AssistantInfo 结构:**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `name` | string | 助教姓名 |
|
||||
| `cls` | string | 样式类(assistant--assignee/abandoned/normal) |
|
||||
| `heartScore` | number | 爱心分 0-10 |
|
||||
| `badge` | string? | 标记(跟/弃) |
|
||||
| `badgeCls` | string? | 标记样式类 |
|
||||
|
||||
**8 维度专属字段(按 dimension 参数返回对应字段):**
|
||||
|
||||
| 维度 | 专属字段 | 类型 | 说明 |
|
||||
|------|---------|------|------|
|
||||
| recall | `idealDays` | number | 理想到店间隔(天) |
|
||||
| | `elapsedDays` | number | 已过天数 |
|
||||
| | `overdueDays` | number | 超期天数 |
|
||||
| | `visits30d` | number | 近30天到店次数 |
|
||||
| | `balance` | string | 余额(格式化) |
|
||||
| | `recallIndex` | string | 召回指数 |
|
||||
| potential | `potentialTags` | `Array<{text, theme}>` | 潜力标签 |
|
||||
| | `spend30d` | string | 近30天消费 |
|
||||
| | `avgVisits` | string | 月均到店 |
|
||||
| | `avgSpend` | string | 次均消费 |
|
||||
| balance | `balance` | string | 当前余额 |
|
||||
| | `lastVisit` | string | 最近到店 |
|
||||
| | `monthlyConsume` | string | 月均消耗 |
|
||||
| | `availableMonths` | string | 可用月数 |
|
||||
| recharge | `lastRecharge` | string | 最后充值日期 |
|
||||
| | `rechargeAmount` | string | 充值金额 |
|
||||
| | `recharges60d` | string | 近60天充值次数 |
|
||||
| | `currentBalance` | string | 当前余额 |
|
||||
| recent | `daysAgo` | number | 距今天数 |
|
||||
| | `visitFreq` | string | 到店频率 |
|
||||
| | `idealDays` | number | 理想间隔 |
|
||||
| | `visits30d` | number | 近30天到店 |
|
||||
| | `avgSpend` | string | 次均消费 |
|
||||
| spend60 | `spend60d` | string | 近60天消费总额 |
|
||||
| | `visits60d` | string | 近60天到店次数 |
|
||||
| | `highSpendTag` | boolean | 是否高消费标签 |
|
||||
| | `avgSpend` | string | 次均消费 |
|
||||
| freq60 | `visits60d` | string | 近60天到店次数 |
|
||||
| | `avgInterval` | string | 平均到店间隔 |
|
||||
| | `weeklyVisits` | `Array<{val, pct}>` | 8周到店柱状图 |
|
||||
| | `spend60d` | string | 近60天消费 |
|
||||
| loyal | `intimacy` | string | 亲密度指数 |
|
||||
| | `topCoachName` | string | 最亲密助教姓名 |
|
||||
| | `topCoachHeart` | number | 最亲密助教爱心分 |
|
||||
| | `topCoachScore` | string | 最亲密助教关系指数 |
|
||||
| | `coachName` | string | 主助教姓名 |
|
||||
| | `coachRatio` | string | 主助教占比 |
|
||||
| | `coachDetails` | `Array<{name, cls, heartScore, badge?, avgDuration, serviceCount, coachSpend, relationIdx}>` | 助教服务明细表 |
|
||||
|
||||
#### 筛选参数映射
|
||||
|
||||
| 筛选项 | 前端参数 | 后端参数 | 查询影响 |
|
||||
|--------|---------|---------|---------|
|
||||
| 维度切换 | `dimension`: recall/potential/balance/recharge/recent/spend60/freq60/loyal | `dimension` | 决定查询的 FDW 表和排序字段 |
|
||||
| 项目筛选 | `project`: all/chinese/snooker/mahjong/karaoke | `project` | WHERE 过滤客户偏好项目 |
|
||||
|
||||
无交叉约束,8×5=40 种有效组合。
|
||||
|
||||
#### 页面跳转
|
||||
- 点击客户卡片 `onCustomerTap` → `/pages/customer-detail/customer-detail?id={customerId}`
|
||||
- Tab 切换 → `board-finance`(switchTab)/ `board-coach`(navigateTo)
|
||||
|
||||
#### API 契约 vs Mock 数据 vs 八¾节 差异
|
||||
|
||||
| 差异项 | API 契约 (BOARD-2) | 前端 Mock (CustomerItem) | 八¾节 (B2) | 严重度 |
|
||||
|--------|--------------------|-----------------------|------------|--------|
|
||||
| 字段结构 | 扁平简单:`id, name, avatar, tags(string[]), total_spend, visit_count, last_visit, relation_index` | 丰富结构:8 维度 40+ 专属字段 + assistants 对象数组 | 与 Mock 一致 | 🔴 高 |
|
||||
| `assistants` 字段 | ❌ 无 | ✅ 有(含 name/cls/heartScore/badge/badgeCls) | ✅ 有 | 🔴 高 |
|
||||
| `avatarCls` 字段 | ❌ 无(仅 `avatar` URL) | ✅ 有 | ✅ 有 | 🟠 中 |
|
||||
| `initial` 字段 | ❌ 无 | ✅ 有 | ✅ 有 | 🟠 中 |
|
||||
| 8 维度专属字段 | ❌ 全部缺失 | ✅ 完整 | ✅ 完整 | 🔴 高 |
|
||||
| `weeklyVisits` 柱状图 | ❌ 无 | ✅ 有(freq60 维度,8 周数据) | ✅ 有 | 🔴 高 |
|
||||
| `coachDetails` 明细表 | ❌ 无 | ✅ 有(loyal 维度,助教服务明细) | ✅ 有 | 🔴 高 |
|
||||
| `potentialTags` 标签 | ❌ 无 | ✅ 有(potential 维度,含 text+theme) | ✅ 有 | 🟠 中 |
|
||||
| 分页 | ✅ 有(page/pageSize) | ❌ 无(全量返回 3 条 mock) | ✅ 已确认 20 条懒加载 | 🟡 低 |
|
||||
|
||||
#### spec 未记录的 Gap
|
||||
1. 🔴 API 契约 BOARD-2 响应字段与前端实际需求严重不匹配 — 契约仅 8 个通用字段,前端需要 40+ 维度专属字段
|
||||
2. 🔴 `assistants` 关联助教列表契约完全缺失 — 每个客户卡片展示关联助教及其爱心分/跟弃状态
|
||||
3. 🔴 `weeklyVisits` 8 周柱状图数据契约缺失 — freq60 维度的核心可视化数据
|
||||
4. 🔴 `coachDetails` 助教服务明细表契约缺失 — loyal 维度展示每个助教的详细服务数据
|
||||
5. 🟠 `potentialTags` 标签结构契约缺失 — potential 维度展示客户潜力标签
|
||||
|
||||
---
|
||||
|
||||
### 2.2 customer-detail(`pages/customer-detail/customer-detail.ts`)
|
||||
|
||||
#### API 调用
|
||||
- `fetchCustomerDetail(customerId)` → `MockCustomerDetail | null`
|
||||
- 页面 `onLoad()` → `loadDetail()` → `fetchCustomerDetail(id)`
|
||||
- ⚠️ `loadDetail()` 中 id 获取方式有 Bug:`(this as any).__route__?.split('?')[1]` 无法正确解析 query 参数
|
||||
|
||||
#### 前端期望字段(从内联 data 提取,管理层视角)
|
||||
|
||||
**客户基础信息:**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `detail.id` | string | 客户 ID |
|
||||
| `detail.name` | string | 客户姓名 |
|
||||
| `detail.avatarChar` | string | 头像字符 |
|
||||
| `detail.phone` | string | 完整手机号(前端控制脱敏显示) |
|
||||
| `detail.balance` | string | 余额(格式化) |
|
||||
| `detail.consumption60d` | string | 近60天消费(格式化) |
|
||||
| `detail.idealInterval` | string | 理想到店间隔 |
|
||||
| `detail.daysSinceVisit` | string | 距上次到店天数 |
|
||||
|
||||
**AI 洞察(管理层视角特有):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `aiInsight.summary` | string | AI 分析摘要 |
|
||||
| `aiInsight.strategies` | `Array<{color, text}>` | 策略建议列表 |
|
||||
|
||||
**维客线索(clues):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `clues[].category` | string | 线索大类(含换行符格式化) |
|
||||
| `clues[].categoryColor` | string | 分类颜色 |
|
||||
| `clues[].text` | string | 线索文本(含 emoji) |
|
||||
| `clues[].source` | string | 来源(系统/助教名) |
|
||||
| `clues[].detail` | string? | 详情(可选,展开显示) |
|
||||
|
||||
**关联助教 coachTasks(管理层关注多助教对同一客户的服务):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `coachTasks[].name` | string | 助教姓名 |
|
||||
| `coachTasks[].level` | string | 等级 |
|
||||
| `coachTasks[].levelColor` | string | 等级颜色 |
|
||||
| `coachTasks[].taskType` | string | 任务类型标签 |
|
||||
| `coachTasks[].taskColor` | string | 任务颜色 |
|
||||
| `coachTasks[].bgClass` | string | 背景样式类 |
|
||||
| `coachTasks[].status` | string | 状态(normal/pinned/abandoned) |
|
||||
| `coachTasks[].lastService` | string | 最后服务时间 |
|
||||
| `coachTasks[].metrics` | `Array<{label, value, color?}>` | 指标列表(近60天次数/总时长/次均时长) |
|
||||
|
||||
**喜好助教 favoriteCoaches:**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `favoriteCoaches[].emoji` | string | 爱心 emoji |
|
||||
| `favoriteCoaches[].name` | string | 助教姓名 |
|
||||
| `favoriteCoaches[].relationIndex` | string | 关系指数 |
|
||||
| `favoriteCoaches[].indexColor` | string | 指数颜色 |
|
||||
| `favoriteCoaches[].bgClass` | string | 背景样式类 |
|
||||
| `favoriteCoaches[].stats` | `Array<{label, value, color?}>` | 统计(基础/激励/上课/充值) |
|
||||
|
||||
**消费记录 consumptionRecords(3 种类型,管理层关注消费结构):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `ConsumptionRecord.id` | string | 记录 ID |
|
||||
| `ConsumptionRecord.type` | `"table" \| "shop" \| "recharge"` | 消费类型 |
|
||||
| `ConsumptionRecord.date` | string | 日期 |
|
||||
| `ConsumptionRecord.tableName` | string? | 台桌名(table 类型) |
|
||||
| `ConsumptionRecord.startTime` | string? | 开始时间 |
|
||||
| `ConsumptionRecord.endTime` | string? | 结束时间 |
|
||||
| `ConsumptionRecord.duration` | string? | 时长 |
|
||||
| `ConsumptionRecord.tableFee` | number? | 台费(优惠后) |
|
||||
| `ConsumptionRecord.tableOrigPrice` | number? | 台费原价 |
|
||||
| `ConsumptionRecord.coaches` | `Array<{name, level, levelColor, courseType, hours, perfHours?, fee}>` | 助教服务明细 |
|
||||
| `ConsumptionRecord.foodAmount` | number? | 食品酒水金额(优惠后) |
|
||||
| `ConsumptionRecord.foodOrigPrice` | number? | 食品酒水原价 |
|
||||
| `ConsumptionRecord.totalAmount` | number? | 总金额 |
|
||||
| `ConsumptionRecord.totalOrigPrice` | number? | 总原价 |
|
||||
| `ConsumptionRecord.payMethod` | string? | 支付方式 |
|
||||
| `ConsumptionRecord.rechargeAmount` | number? | 充值金额(recharge 类型) |
|
||||
|
||||
**备注列表:**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `sortedNotes[].id` | string | 备注 ID |
|
||||
| `sortedNotes[].tagLabel` | string | 标签(管理员/助教名) |
|
||||
| `sortedNotes[].createdAt` | string | 创建时间 |
|
||||
| `sortedNotes[].content` | string | 备注内容 |
|
||||
|
||||
#### 页面跳转
|
||||
- 查看服务记录 `onViewServiceRecords` → `/pages/customer-service-records/customer-service-records`(⚠️ 未传 customerId 参数!)
|
||||
- 问问助手 `onStartChat` → `/pages/chat/chat`(⚠️ 未传 customerId 参数!)
|
||||
|
||||
#### API 契约 vs Mock 数据 差异
|
||||
|
||||
| 差异项 | API 契约 (CUST-1) | 前端 Mock | 严重度 |
|
||||
|--------|--------------------|-----------| --------|
|
||||
| `aiInsight` AI 洞察 | ❌ 完全缺失 | ✅ 有(summary + strategies) | 🔴 高 |
|
||||
| `coachTasks` 关联助教任务 | ❌ 完全缺失 | ✅ 有(4 个助教,含 metrics) | 🔴 高 |
|
||||
| `favoriteCoaches` 喜好助教 | ❌ 完全缺失 | ✅ 有(含 stats 统计) | 🔴 高 |
|
||||
| `clues` 维客线索 | 契约有 `retention_clues` 但结构不同 | 前端用 category/categoryColor/text/source/detail | 🟠 中 |
|
||||
| `consumptionRecords` 结构 | 契约为扁平 `{id, date, type, type_class, amount, table, duration}` | 前端为嵌套结构(含 coaches 子数组、tableFee/foodAmount 分项) | 🔴 高 |
|
||||
| `balance`/`consumption60d`/`idealInterval`/`daysSinceVisit` | ❌ 缺失 | ✅ 有 | 🟠 中 |
|
||||
| `avatarChar` | ❌ 无(仅 avatar URL) | ✅ 有 | 🟡 低 |
|
||||
| 备注列表 | ❌ 未在 CUST-1 定义 | ✅ 有 | 🟠 中 |
|
||||
|
||||
#### spec 未记录的 Gap
|
||||
1. 🔴 CUST-1 缺少 `aiInsight` AI 洞察 — 管理层查看客户时的核心决策辅助信息(来源:biz.ai_cache app4_analysis)
|
||||
2. 🔴 CUST-1 缺少 `coachTasks` 关联助教任务区域 — 管理层关注多助教对同一客户的服务情况
|
||||
3. 🔴 CUST-1 缺少 `favoriteCoaches` 喜好助教 — 展示客户与各助教的关系指数和服务统计
|
||||
4. 🔴 CUST-1 消费记录结构严重不匹配 — 契约为扁平结构,前端需要嵌套结构(含助教明细、台费/食品分项、原价/优惠后价格)
|
||||
5. 🟠 `onViewServiceRecords` 跳转未传 `customerId` — 前端 Bug
|
||||
6. 🟠 `onStartChat` 跳转未传 `customerId` — 前端 Bug
|
||||
7. 🟠 `loadDetail()` 中 id 解析方式有 Bug — 应从 `onLoad(options)` 的 options 中获取
|
||||
|
||||
---
|
||||
|
||||
### 2.3 customer-service-records(`pages/customer-service-records/customer-service-records.ts`)
|
||||
|
||||
#### API 调用
|
||||
- `fetchCustomerRecords({ customerId })` → `{ records, hasMore }`
|
||||
- 页面 `onLoad({ customerId, id })` → `loadData(id)` → `fetchCustomerRecords({ customerId: id })`
|
||||
- ⚠️ 首次加载拉取全部记录,月份切换仅本地过滤(八¾节 F10/L4 已记录)
|
||||
|
||||
#### 前端期望字段
|
||||
|
||||
**页面头部信息:**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `customerName` | string | 客户姓名 |
|
||||
| `customerPhone` | string | 脱敏手机号 |
|
||||
| `customerPhoneFull` | string | 完整手机号 |
|
||||
| `totalServiceCount` | number | 累计服务次数 |
|
||||
| `relationIndex` | string | 关系指数 |
|
||||
|
||||
**ServiceRecord 结构(转换后):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `table` | string | 台桌号 |
|
||||
| `type` | string | 课程类型标签 |
|
||||
| `typeClass` | `'basic' \| 'vip' \| 'tip' \| 'recharge'` | 样式类 |
|
||||
| `recordType` | `'course' \| 'recharge'` | 记录类型 |
|
||||
| `duration` | number | 折算后小时数 |
|
||||
| `durationRaw` | number | 折算前小时数 |
|
||||
| `income` | number | 到手金额 |
|
||||
| `isEstimate` | boolean | 是否预估 |
|
||||
| `drinks` | string | 饮品描述 |
|
||||
| `date` | string | 显示日期+时间段 |
|
||||
|
||||
#### 筛选参数映射
|
||||
|
||||
| 筛选项 | 前端参数 | 后端参数 | 查询影响 |
|
||||
|--------|---------|---------|---------|
|
||||
| 月份切换 | `currentYear`/`currentMonth` | `year`/`month` | 按月筛选记录(当前为本地过滤) |
|
||||
| 客户 ID | `customerId` | `customerId` | WHERE 过滤 |
|
||||
|
||||
#### 页面跳转
|
||||
- 无跳转(详情页,仅返回上一页)
|
||||
|
||||
#### API 契约 vs Mock 数据 差异
|
||||
|
||||
| 差异项 | API 契约 (CUST-2) | 前端 Mock | 严重度 |
|
||||
|--------|--------------------|-----------| --------|
|
||||
| 源数据结构 | 契约返回 ServiceRecord 格式 | Mock 使用 ConsumptionRecord(来自 mock-data.ts),前端转换为 ServiceRecord | 🟠 中 |
|
||||
| `customerPhone`/`customerPhoneFull` | 契约有 `customer_phone` | Mock 硬编码 | 🟡 低 |
|
||||
| `tables` 台桌列表 | 契约有 `tables: Array<{id, name}>` | Mock 无(前端模拟台号) | 🟡 低 |
|
||||
| `relationIndex` | 契约有 `relation_index` | Mock 硬编码 `"0.85"` | 🟡 低 |
|
||||
| 月度统计 | ❌ 契约无 | ✅ 前端本地计算 `monthCount`/`monthHours`/`monthRelation` | 🟠 中 |
|
||||
|
||||
#### spec 未记录的 Gap
|
||||
1. 🟠 CUST-2 响应缺少月度统计汇总 — 前端需要 `monthCount`/`monthHours`,当前本地计算,联调后应由后端返回
|
||||
2. 🟠 首次加载全量 vs 按月分页 — 当前设计一次拉全部记录本地过滤,数据量大时需改为按月请求
|
||||
|
||||
---
|
||||
|
||||
## 场景 3:财务看板(最复杂)
|
||||
|
||||
### 3.1 board-finance(`pages/board-finance/board-finance.ts`)
|
||||
|
||||
#### API 调用
|
||||
- `fetchBoardFinance()` → `BoardFinanceData`(⚠️ 当前签名仅 `{ date?: string }`,缺少 time/area/compare 参数)
|
||||
- 页面 `onLoad()` → `fetchBoardFinance()` → 设置 `pageState: 'normal'`
|
||||
- ⚠️ 筛选变更(`onTimeChange`/`onAreaChange`/`toggleCompare`)仅更新 data 状态,**不调用 API**(八¾节 F4/F5 已记录)
|
||||
- ⚠️ 所有 6 个板块数据完全内联在 `data` 中,`fetchBoardFinance` 返回的 `_data` 未使用
|
||||
|
||||
#### 前端期望字段(从内联 data 提取,6 个板块完整结构)
|
||||
|
||||
**板块 1:经营一览 (overview) — 8 个指标 + 8 个环比值**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `overview.occurrence` | string | 发生额/正价(格式化,如 `"¥823,456"`) |
|
||||
| `overview.occurrenceCompare` | string | 环比百分比(如 `"12.5%"`) |
|
||||
| `overview.discount` | string | 总优惠(负值,如 `"-¥113,336"`) |
|
||||
| `overview.discountCompare` | string | 环比 |
|
||||
| `overview.discountRate` | string | 优惠率(如 `"13.8%"`) |
|
||||
| `overview.discountRateCompare` | string | 环比 |
|
||||
| `overview.confirmedRevenue` | string | 成交/确认收入 |
|
||||
| `overview.confirmedCompare` | string | 环比 |
|
||||
| `overview.cashIn` | string | 实收/现金流入 |
|
||||
| `overview.cashInCompare` | string | 环比 |
|
||||
| `overview.cashOut` | string | 现金支出 |
|
||||
| `overview.cashOutCompare` | string | 环比 |
|
||||
| `overview.cashBalance` | string | 现金结余 |
|
||||
| `overview.cashBalanceCompare` | string | 环比 |
|
||||
| `overview.balanceRate` | string | 结余率 |
|
||||
| `overview.balanceRateCompare` | string | 环比 |
|
||||
|
||||
**板块 2:预收资产 (recharge) — 储值卡 + 赠送卡矩阵**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `recharge.actualIncome` | string | 储值卡充值实收 |
|
||||
| `recharge.actualCompare` | string | 环比 |
|
||||
| `recharge.firstCharge` | string | 首充 |
|
||||
| `recharge.firstChargeCompare` | string | 环比 |
|
||||
| `recharge.renewCharge` | string | 续费 |
|
||||
| `recharge.renewChargeCompare` | string | 环比 |
|
||||
| `recharge.consumed` | string | 消耗 |
|
||||
| `recharge.consumedCompare` | string | 环比 |
|
||||
| `recharge.cardBalance` | string | 储值卡总余额 |
|
||||
| `recharge.cardBalanceCompare` | string | 环比 |
|
||||
| `recharge.giftRows` | `Array<{label, total, totalCompare, wine, wineCompare, table, tableCompare, coupon, couponCompare}>` | 赠送卡矩阵(3行:新增/消费/余额 × 4列:合计/酒水卡/台费卡/抵用券) |
|
||||
| `recharge.allCardBalance` | string | 全类别会员卡余额合计 |
|
||||
| `recharge.allCardBalanceCompare` | string | 环比 |
|
||||
|
||||
赠送卡矩阵结构(3×4 = 12 个数据单元 + 12 个环比值 = 24 个字段):
|
||||
- 行:新增 / 消费 / 余额
|
||||
- 列:合计(total) / 酒水卡(wine) / 台费卡(table) / 抵用券(coupon)
|
||||
|
||||
**板块 3:应计收入确认 (revenue) — 4 个子表**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `revenue.structureRows` | `Array<{id, name, desc?, amount, discount, booked, bookedCompare, isSub?}>` | 收入结构表(9行,含子行标记) |
|
||||
| `revenue.priceItems` | `Array<{name, value, compare}>` | 正价明细(4项) |
|
||||
| `revenue.totalOccurrence` | string | 正价合计 |
|
||||
| `revenue.totalOccurrenceCompare` | string | 环比 |
|
||||
| `revenue.discountItems` | `Array<{name, desc?, value, compare}>` | 优惠明细(4项) |
|
||||
| `revenue.confirmedTotal` | string | 确认收入合计 |
|
||||
| `revenue.confirmedTotalCompare` | string | 环比 |
|
||||
| `revenue.channelItems` | `Array<{name, desc?, value, compare}>` | 渠道明细(3项) |
|
||||
|
||||
收入结构表行结构:
|
||||
- 开台与包厢(含 A区/B区/C区/团建区/麻将区 5 个子行)
|
||||
- 助教基础课
|
||||
- 助教激励课
|
||||
- 食品酒水
|
||||
|
||||
**板块 4:现金流入 (cashflow)**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `cashflow.consumeItems` | `Array<{name, desc, value, compare, isDown}>` | 消费收款(3项:纸币现金/线上收款/团购平台) |
|
||||
| `cashflow.rechargeItems` | `Array<{name, desc, value, compare}>` | 充值收款(1项:会员充值到账) |
|
||||
| `cashflow.total` | string | 现金流入合计 |
|
||||
| `cashflow.totalCompare` | string | 环比 |
|
||||
|
||||
**板块 5:现金流出 (expense) — 4 个子分组**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `expense.operationItems` | `Array<{name, value, compare, isDown}>` | 经营支出(3项:食品饮料/耗材/报销) |
|
||||
| `expense.fixedItems` | `Array<{name, value, compare, isFlat}>` | 固定支出(4项:房租/水电/物业/人员工资) |
|
||||
| `expense.coachItems` | `Array<{name, value, compare, isDown}>` | 助教分成(4项:基础课/激励课/充值提成/额外奖金) |
|
||||
| `expense.platformItems` | `Array<{name, value, compare}>` | 平台服务费(3项:汇来米/美团/抖音) |
|
||||
| `expense.total` | string | 现金流出合计 |
|
||||
| `expense.totalCompare` | string | 环比 |
|
||||
|
||||
**板块 6:助教分析 (coachAnalysis) — 基础课 + 激励课两个子表**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `coachAnalysis.basic.totalPay` | string | 基础课总收入 |
|
||||
| `coachAnalysis.basic.totalPayCompare` | string | 环比 |
|
||||
| `coachAnalysis.basic.totalShare` | string | 基础课总分成 |
|
||||
| `coachAnalysis.basic.totalShareCompare` | string | 环比 |
|
||||
| `coachAnalysis.basic.avgHourly` | string | 平均时薪 |
|
||||
| `coachAnalysis.basic.avgHourlyCompare` | string | 环比 |
|
||||
| `coachAnalysis.basic.rows` | `Array<{level, pay, payCompare, share, shareCompare, hourly, hourlyCompare, hourlyFlat?, payDown?, shareDown?}>` | 按等级分行(初级/中级/高级/星级) |
|
||||
| `coachAnalysis.incentive.*` | 同 basic | 激励课(结构完全相同) |
|
||||
|
||||
每行字段:
|
||||
- `level`: string — 等级名
|
||||
- `pay`: string — 收入
|
||||
- `payCompare`: string — 收入环比
|
||||
- `payDown`: boolean? — 收入是否下降
|
||||
- `share`: string — 分成
|
||||
- `shareCompare`: string — 分成环比
|
||||
- `shareDown`: boolean? — 分成是否下降
|
||||
- `hourly`: string — 时薪
|
||||
- `hourlyCompare`: string — 时薪环比
|
||||
- `hourlyFlat`: boolean? — 时薪是否持平
|
||||
|
||||
#### 筛选参数映射
|
||||
|
||||
| 筛选项 | 前端参数 | 后端参数 | 查询影响 |
|
||||
|--------|---------|---------|---------|
|
||||
| 时间范围 | `selectedTime`: month/lastMonth/week/lastWeek/quarter3/quarter/lastQuarter/half6 | `time` | 决定统计日期区间 |
|
||||
| 区域筛选 | `selectedArea`: all/hall/hallA/hallB/hallC/mahjong/teamBuilding | `area` | WHERE 过滤区域;area≠all 时预收资产板块隐藏 |
|
||||
| 环比开关 | `compareEnabled`: true/false | `compare`: 0/1 | 控制是否返回环比数据 |
|
||||
|
||||
时间范围→日期区间映射(后端需实现):
|
||||
- `month` → 当月1日 ~ 当月末日
|
||||
- `lastMonth` → 上月1日 ~ 上月末日
|
||||
- `week` → 本周一 ~ 本周日
|
||||
- `lastWeek` → 上周一 ~ 上周日
|
||||
- `quarter3` → 前3个月(不含本月)
|
||||
- `quarter` → 本季度第1天 ~ 本季度末日
|
||||
- `lastQuarter` → 上季度
|
||||
- `half6` → 最近6个月(不含本月)
|
||||
|
||||
#### 目录导航 (sectionNav)
|
||||
- 6 个板块:经营一览/预收资产/应计收入确认/现金流入/现金流出/助教分析
|
||||
- 每个板块有 emoji + 标题 + sectionId
|
||||
- 吸顶头显示当前板块 emoji + 标题 + 描述
|
||||
- 板块描述(`_sectionDescs`):6 段固定文案
|
||||
|
||||
#### 帮助图标 (helpTip)
|
||||
- 7 个指标解释:occurrence/discount/confirmed/cashIn/cashOut/balance/rechargeActual/firstCharge/renewCharge/consume/cardBalance/allCardBalance
|
||||
- 每个含 `title` + `content`(多行文本)
|
||||
|
||||
#### API 契约 vs Mock 数据 vs 八¾节 差异
|
||||
|
||||
| 差异项 | API 契约 (BOARD-3) | 前端 Mock (内联 data) | 八¾节 (B3) | 严重度 |
|
||||
|--------|--------------------|-----------------------|------------|--------|
|
||||
| 响应结构 | 扁平 `metrics: Array<{key, label, value, unit, trend, compareValue, sub_items}>` | 6 个板块独立对象,深度嵌套结构 | 仅定义了参数,未定义完整响应结构 | 🔴 高 |
|
||||
| 板块数量 | 未区分板块(单一 metrics 数组) | 6 个独立板块(overview/recharge/revenue/cashflow/expense/coachAnalysis) | 提到 6 个板块但未定义结构 | 🔴 高 |
|
||||
| 环比数据格式 | `trend: 'up'\|'down'\|'flat'` + `compareValue?: string` | 每个值旁有独立的 `xxxCompare` 字段(百分比字符串) + `isDown`/`isFlat` 布尔标记 | 已确认需补充 `compareValue` 字段 | 🔴 高 |
|
||||
| 赠送卡矩阵 | ❌ 完全缺失 | ✅ 3×4 矩阵(24 个字段) | ❌ 未详细定义 | 🔴 高 |
|
||||
| 收入结构表 | ❌ 缺失 | ✅ 9 行含子行标记 | ❌ 未详细定义 | 🔴 高 |
|
||||
| 助教分析子表 | ❌ 缺失 | ✅ 基础课+激励课各 4 行(按等级) | ❌ 未详细定义 | 🔴 高 |
|
||||
| 现金流出 4 子分组 | ❌ 缺失 | ✅ 经营支出/固定支出/助教分成/平台服务费 | ❌ 未详细定义 | 🔴 高 |
|
||||
| `time`/`area`/`compare` 参数 | 八¾节末尾补充了参数定义 | 前端未传参(Bug) | ✅ 已定义 | 🟠 中(八¾节已补充) |
|
||||
| `isDown`/`isFlat` 标记 | ❌ 无 | ✅ 有(控制箭头颜色方向) | ❌ 无 | 🟠 中 |
|
||||
| 帮助图标内容 | ❌ 完全缺失 | ✅ 12 个指标解释 | ❌ 未提及 | 🟡 低 |
|
||||
|
||||
#### spec 未记录的 Gap
|
||||
1. 🔴 BOARD-3 响应结构与前端实际需求完全不匹配 — 契约为扁平 metrics 数组,前端需要 6 个独立板块的深度嵌套结构
|
||||
2. 🔴 赠送卡矩阵(3×4=12 单元 + 12 环比)完全未定义 — 预收资产板块的核心数据
|
||||
3. 🔴 收入结构表(9 行含子行层级)完全未定义 — 应计收入确认板块的核心数据
|
||||
4. 🔴 助教分析子表(基础课+激励课各 4 行按等级)完全未定义
|
||||
5. 🔴 现金流出 4 个子分组(经营/固定/助教分成/平台服务费)完全未定义
|
||||
6. 🔴 环比数据格式不匹配 — 契约仅 `trend` + `compareValue`,前端需要每个值旁独立的 `xxxCompare` 字段 + `isDown`/`isFlat` 布尔标记
|
||||
7. 🟠 `isDown`/`isFlat` 布尔标记未定义 — 前端用于控制环比箭头方向和颜色
|
||||
8. 🟡 帮助图标内容(12 个指标解释文案)未定义 — 可硬编码在前端,但后端可提供配置化
|
||||
|
||||
---
|
||||
|
||||
## 场景 4:跨页面数据关联
|
||||
|
||||
### 4.1 board-coach → coach-detail → customer-detail → customer-service-records
|
||||
|
||||
**参数传递链:**
|
||||
1. `board-coach` 点击助教卡片 → `coach-detail?id={coachId}`
|
||||
2. `coach-detail` 点击客户卡片 → `customer-detail?id={customerId}`
|
||||
3. `coach-detail` 点击任务项 → `customer-detail?name={customerName}`(⚠️ 用 name 而非 id)
|
||||
4. `customer-detail` 查看服务记录 → `customer-service-records`(⚠️ 未传 customerId)
|
||||
5. `coach-detail` 查看更多服务记录 → `performance-records?coachId={coachId}`
|
||||
6. `coach-detail` 问问助手 → `chat?coachId={coachId}`
|
||||
|
||||
**跨页面 Gap:**
|
||||
- 🔴 步骤 3 用 `name` 跳转,name 不唯一,应改为 `id`
|
||||
- 🔴 步骤 4 未传 `customerId`,`customer-service-records` 无法知道查哪个客户
|
||||
- 🟠 `customer-detail` 的 `onStartChat` 未传 `customerId`,chat 页面无法关联客户上下文
|
||||
|
||||
### 4.2 board-customer → customer-detail → chat
|
||||
|
||||
**参数传递链:**
|
||||
1. `board-customer` 点击客户卡片 → `customer-detail?id={customerId}`
|
||||
2. `customer-detail` 问问助手 → `chat`(⚠️ 未传参数)
|
||||
|
||||
**跨页面 Gap:**
|
||||
- 🟠 `chat` 页面无法获取客户上下文(customerId/customerName)
|
||||
|
||||
### 4.3 board-finance → coach-detail 入口
|
||||
|
||||
**分析结果:**
|
||||
- board-finance 的助教分析板块(coachAnalysis)展示按等级汇总的数据,**无跳转到 coach-detail 的入口**
|
||||
- 这是合理的设计 — 财务看板关注的是汇总数据而非个体
|
||||
|
||||
---
|
||||
|
||||
## 汇总 Gap 清单(按严重度排序)
|
||||
|
||||
### 🔴 高严重度(阻塞联调,必须在后端开发前解决)
|
||||
|
||||
| # | 类别 | Gap 描述 | 涉及接口 | 涉及页面 |
|
||||
|---|------|---------|---------|---------|
|
||||
| G1 | 契约缺陷 | **BOARD-3 响应结构与前端完全不匹配** — 契约为扁平 metrics 数组,前端需要 6 个独立板块的深度嵌套结构(overview/recharge/revenue/cashflow/expense/coachAnalysis),含 200+ 个字段 | BOARD-3 | board-finance |
|
||||
| G2 | 契约缺陷 | **BOARD-1 响应字段严重不足** — 契约仅 10 个通用字段,前端需要 20 个含 4 维度专属字段(perf/salary/sv/task),且 skills 类型不匹配(string[] vs Array<{text,cls}>) | BOARD-1 | board-coach |
|
||||
| G3 | 契约缺陷 | **BOARD-2 响应字段严重不足** — 契约仅 8 个通用字段,前端需要 40+ 维度专属字段(8 个维度各有独立字段集),且缺少 assistants 关联助教列表 | BOARD-2 | board-customer |
|
||||
| G4 | 契约缺陷 | **COACH-1 响应缺少多个核心区域** — 缺少 income 收入明细(本月/上月)、historyMonths 历史月份统计、tierNodes 档位节点、备注列表;topCustomers 字段结构不完整(契约 5 字段 vs 前端 10 字段) | COACH-1 | coach-detail |
|
||||
| G5 | 契约缺陷 | **CUST-1 响应缺少管理层核心数据** — 缺少 aiInsight AI 洞察、coachTasks 关联助教任务、favoriteCoaches 喜好助教;消费记录结构严重不匹配(契约扁平 vs 前端嵌套含助教明细) | CUST-1 | customer-detail |
|
||||
| G6 | 契约缺陷 | **BOARD-3 赠送卡矩阵完全未定义** — 预收资产板块的 3×4 矩阵(新增/消费/余额 × 合计/酒水卡/台费卡/抵用券)= 24 个数据字段 + 24 个环比字段 | BOARD-3 | board-finance |
|
||||
| G7 | 契约缺陷 | **BOARD-3 收入结构表完全未定义** — 9 行含子行层级(开台与包厢含 5 个区域子行 + 助教基础课 + 助教激励课 + 食品酒水),每行含 amount/discount/booked/bookedCompare | BOARD-3 | board-finance |
|
||||
| G8 | 契约缺陷 | **BOARD-3 助教分析子表完全未定义** — 基础课+激励课各含汇总行 + 4 个等级行(初级/中级/高级/星级),每行含 pay/share/hourly 及各自环比 | BOARD-3 | board-finance |
|
||||
| G9 | 契约缺陷 | **BOARD-3 现金流出 4 子分组完全未定义** — 经营支出(3项)/固定支出(4项)/助教分成(4项)/平台服务费(3项) | BOARD-3 | board-finance |
|
||||
| G10 | 契约缺陷 | **环比数据格式不匹配** — 契约仅 `trend` + `compareValue`,前端需要每个值旁独立的 `xxxCompare` 百分比字段 + `isDown`/`isFlat` 布尔标记 | BOARD-3 | board-finance |
|
||||
| G11 | 前端 Bug | **customer-detail 跳转 customer-service-records 未传 customerId** — 服务记录页无法知道查哪个客户 | CUST-2 | customer-detail → customer-service-records |
|
||||
| G12 | 前端 Bug | **coach-detail 任务项跳转用 name 而非 id** — `onTaskItemTap` 传 `name={customerName}`,name 不唯一 | — | coach-detail → customer-detail |
|
||||
| G13 | 类型不匹配 | **api.ts 中 fetchCoachDetail 返回类型为 CoachCard** — 实际需要完整的 CoachDetail(含 performance/income/tasks/topCustomers/serviceRecords/historyMonths/notes),类型定义严重不足 | COACH-1 | coach-detail |
|
||||
| G14 | 类型不匹配 | **mock-data.ts 中的类型与页面内联 mock 完全脱节** — BoardFinanceData 仅含 4 个 metrics,实际页面需要 6 个板块 200+ 字段;CustomerCard/CoachCard 过于简单 | 全部看板 | 全部看板页 |
|
||||
|
||||
### 🟠 中严重度(影响功能完整性,联调前需解决)
|
||||
|
||||
| # | 类别 | Gap 描述 | 涉及接口 | 涉及页面 |
|
||||
|---|------|---------|---------|---------|
|
||||
| G15 | 契约缺陷 | **BOARD-1 sort 参数格式不一致** — 契约用 `field:direction`,前端用枚举值 `perf_desc` | BOARD-1 | board-coach |
|
||||
| G16 | 契约缺陷 | **BOARD-1/2 缺少 initial/avatarGradient/avatarCls 字段** — 前端用于头像渲染,需确认由前端计算还是后端返回 | BOARD-1/2 | board-coach/board-customer |
|
||||
| G17 | 契约缺陷 | **CUST-1 缺少 balance/consumption60d/idealInterval/daysSinceVisit** — 客户详情页头部展示的核心指标 | CUST-1 | customer-detail |
|
||||
| G18 | 契约缺陷 | **CUST-1 维客线索结构不匹配** — 契约用 tag/tag_color/emoji/text/source/desc,前端用 category/categoryColor/text/source/detail | CUST-1 | customer-detail |
|
||||
| G19 | 契约缺陷 | **CUST-1 缺少备注列表** — 客户详情页展示关联备注 | CUST-1 | customer-detail |
|
||||
| G20 | 契约缺陷 | **COACH-1 缺少备注列表** — 助教详情页展示关联备注 | COACH-1 | coach-detail |
|
||||
| G21 | 契约缺陷 | **CUST-2 缺少月度统计汇总** — 前端需要 monthCount/monthHours,当前本地计算 | CUST-2 | customer-service-records |
|
||||
| G22 | 契约缺陷 | **BOARD-3 isDown/isFlat 布尔标记未定义** — 前端用于控制环比箭头方向和颜色 | BOARD-3 | board-finance |
|
||||
| G23 | 前端 Bug | **customer-detail 的 onStartChat 未传 customerId** — chat 页面无法关联客户上下文 | CHAT | customer-detail → chat |
|
||||
| G24 | 前端 Bug | **customer-detail 的 loadDetail() 中 id 解析方式有 Bug** — 应从 onLoad(options) 获取,而非 `__route__` 解析 | CUST-1 | customer-detail |
|
||||
| G25 | 设计缺陷 | **BOARD-2 potentialTags 标签结构契约缺失** — potential 维度展示客户潜力标签(含 text+theme) | BOARD-2 | board-customer |
|
||||
| G26 | 设计缺陷 | **COACH-1 tierNodes/maxHours 缺失** — 绩效进度条组件需要档位节点数据,当前硬编码 `[0, 100, 130, 160, 190, 220]` | COACH-1 | coach-detail |
|
||||
|
||||
### 🟡 低严重度(不阻塞联调,可后续优化)
|
||||
|
||||
| # | 类别 | Gap 描述 | 涉及接口 | 涉及页面 |
|
||||
|---|------|---------|---------|---------|
|
||||
| G27 | 设计决策 | **BOARD-1 无分页定义** — 当前全量返回,助教数量少(<50)可接受 | BOARD-1 | board-coach |
|
||||
| G28 | 设计决策 | **BOARD-3 帮助图标内容未定义** — 12 个指标解释文案,可硬编码在前端 | BOARD-3 | board-finance |
|
||||
| G29 | 设计决策 | **customer-service-records 首次加载全量 vs 按月分页** — 数据量小时可接受,量大时需改为按月请求 | CUST-2 | customer-service-records |
|
||||
| G30 | 类型不匹配 | **mock-data.ts ConsumptionRecord 结构过于简单** — 仅 6 个字段,customer-detail 内联 mock 有 16 个字段(含嵌套 coaches 数组) | CUST-1 | customer-detail |
|
||||
| G31 | 契约缺陷 | **CUST-2 tables 台桌列表** — 契约定义了但前端未使用(前端模拟台号) | CUST-2 | customer-service-records |
|
||||
|
||||
---
|
||||
|
||||
## 行动建议
|
||||
|
||||
### 优先级 1:更新 API 契约(阻塞后端开发)
|
||||
|
||||
以下接口的契约需要**完全重写**,以匹配前端实际需求:
|
||||
|
||||
1. **BOARD-3 财务看板** — 从扁平 metrics 改为 6 板块嵌套结构,补充赠送卡矩阵、收入结构表、助教分析子表、现金流出子分组的完整字段定义
|
||||
2. **BOARD-1 助教看板** — 从 10 个通用字段扩展为 20 个含 4 维度专属字段,修正 skills 类型,补充 topCustomers
|
||||
3. **BOARD-2 客户看板** — 从 8 个通用字段扩展为 40+ 维度专属字段,补充 assistants/weeklyVisits/coachDetails/potentialTags
|
||||
4. **COACH-1 助教详情** — 补充 income/historyMonths/tierNodes/备注列表,扩展 topCustomers 字段
|
||||
5. **CUST-1 客户详情** — 补充 aiInsight/coachTasks/favoriteCoaches/备注列表,重写消费记录为嵌套结构
|
||||
|
||||
### 优先级 2:修复前端 Bug(联调前必须完成)
|
||||
|
||||
1. G11: customer-detail → customer-service-records 传 customerId
|
||||
2. G12: coach-detail onTaskItemTap 改用 id 而非 name
|
||||
3. G23: customer-detail → chat 传 customerId
|
||||
4. G24: customer-detail loadDetail() 修复 id 解析
|
||||
5. F1-F6: 看板筛选变更触发重新请求(八¾节已记录)
|
||||
|
||||
### 优先级 3:统一 mock-data.ts 类型定义
|
||||
|
||||
当前 `mock-data.ts` 中的类型(BoardFinanceData/CustomerCard/CoachCard/CustomerDetail)与页面内联 mock 数据严重脱节。建议:
|
||||
- 将页面内联的完整接口定义(CoachItem/CustomerItem/CoachDetail 等)提取到 `mock-data.ts` 或独立的 `types.ts`
|
||||
- 更新 `api.ts` 中各函数的返回类型
|
||||
- 确保类型定义与更新后的 API 契约一致
|
||||
826
docs/prd/Neo_Specs/storyboard-walkthrough-assistant-view.md
Normal file
826
docs/prd/Neo_Specs/storyboard-walkthrough-assistant-view.md
Normal file
@@ -0,0 +1,826 @@
|
||||
# 小程序 Storyboard 走查报告 — 助教视角(小燕)
|
||||
|
||||
> 走查日期:2026-03-18
|
||||
> 走查角色:助教"小燕",已登录,status=approved
|
||||
> 对照文档:`docs/miniprogram-dev/API-contract.md`(契约)、`docs/prd/Neo_Specs/NS1-xcx-backend-api.md`(spec)
|
||||
> 目标:提取 spec 未记录的接口需求细节
|
||||
|
||||
---
|
||||
|
||||
## 场景 1:登录 → 任务列表(app.ts)
|
||||
|
||||
### API 调用
|
||||
- `checkAuthStatus()` → `GET /api/xcx/me` → 返回 `ApiUserInfo`
|
||||
- 参数来源:token 从 `wx.getStorageSync('token')` 读取
|
||||
- 成功后根据 `data.status` 路由:`approved` → `reLaunch('/pages/task-list/task-list')`
|
||||
|
||||
### 前端期望字段(/api/xcx/me 响应)
|
||||
| 字段 | 类型 | 说明 | 契约定义 |
|
||||
|------|------|------|---------|
|
||||
| `user_id` | number | 用户 ID | ✅ |
|
||||
| `status` | enum | `new/pending/approved/rejected/disabled` | ✅ |
|
||||
| `nickname` | string | 昵称 | ✅ |
|
||||
| `role` | string | 角色 | ✅ REQ-4 |
|
||||
| `store_name` | string | 门店名 | ✅ REQ-4 |
|
||||
| `coach_level` | string? | 助教等级 | ✅ REQ-4 |
|
||||
| `avatar` | string? | 头像 URL | ✅ REQ-4 |
|
||||
|
||||
### globalData 结构
|
||||
```typescript
|
||||
globalData: {
|
||||
token?: string
|
||||
refreshToken?: string
|
||||
authUser?: { userId: number, status: string, nickname: string }
|
||||
}
|
||||
```
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-01**:`globalData.authUser` 未存储 `role`、`store_name`、`coach_level`、`avatar`,但下游页面(task-list banner、performance banner、performance-records banner)需要这些字段。前端需扩展 `globalData.authUser` 或各页面单独请求 `/me`。
|
||||
|
||||
---
|
||||
|
||||
## 场景 2:任务列表页(task-list.ts)
|
||||
|
||||
### API 调用
|
||||
- `fetchTasks(params?)` → `GET /api/xcx/tasks?status=&page=&pageSize=`
|
||||
- 返回:`{ tasks: Task[], performance: PerformanceData, total: number, hasMore: boolean }`
|
||||
- 页面加载时无参数调用(获取全部任务)
|
||||
|
||||
### 前端期望字段 — Task(任务卡片)
|
||||
| 字段 | 类型 | 说明 | 契约 TASK-1 |
|
||||
|------|------|------|------------|
|
||||
| `id` | string | 任务 ID | ✅ |
|
||||
| `customerName` | string | 客户姓名 | ✅ |
|
||||
| `customerAvatar` | string | 客户头像 | ✅ |
|
||||
| `taskType` | enum | `callback/priority_recall/relationship/high_priority` | ✅ |
|
||||
| `taskTypeLabel` | string | 类型中文标签 | ✅ |
|
||||
| `deadline` | string | 截止日期 ISO 8601 | ✅ |
|
||||
| `heartScore` | number | 爱心评分 0-10 | ✅ |
|
||||
| `hobbies` | string[] | 爱好标签 | ✅ |
|
||||
| `isPinned` | boolean | 是否置顶 | ✅ |
|
||||
| `hasNote` | boolean | 是否有备注 | ✅ |
|
||||
| `status` | enum | `pending/completed/abandoned` | ✅ |
|
||||
|
||||
### 前端期望字段 — PerformanceData(绩效概览卡片)
|
||||
| 字段 | 类型 | 说明 | 契约 TASK-1 performance | 前端实际使用 |
|
||||
|------|------|------|------------------------|-------------|
|
||||
| `monthlyIncome` | number | 月收入 | ❌ 契约仅有 `total_income` | `buildPerfData()` 用 |
|
||||
| `incomeChange` | number | 收入变化百分比 | ❌ 契约无 | `incomeTrend` 展示 |
|
||||
| `currentTier` | string | 当前档位名称 | ❌ 契约无 | `bannerTitle` |
|
||||
| `nextTierGap` | number | 距下一档差距 | ❌ 契约无 | `remainHours` |
|
||||
| `todayServiceCount` | number | 今日服务次数 | ❌ 契约无 | 未使用 |
|
||||
| `weekServiceCount` | number | 本周服务次数 | ❌ 契约无 | 未使用 |
|
||||
| `monthServiceCount` | number | 本月服务次数 | ❌ 契约无 | 未使用 |
|
||||
|
||||
### 前端期望字段 — buildPerfData() 需要后端返回的绩效字段
|
||||
| 字段 | 类型 | 说明 | 契约/spec 定义 |
|
||||
|------|------|------|---------------|
|
||||
| `tierNodes` | number[] | 档位节点数组,如 `[0,100,130,160,190,220]` | ❌ 未定义 |
|
||||
| `totalHours` | number | 当月总工时(折算后) | 契约有 `total_hours` |
|
||||
| `basicHours` | number | 基础课时 | ❌ 未定义 |
|
||||
| `bonusHours` | number | 激励课时 | ❌ 未定义 |
|
||||
| `currentTier` | number | 当前档位索引 | ❌ 未定义 |
|
||||
| `nextTierHours` | number | 下一档位工时阈值 | ❌ 未定义 |
|
||||
| `tierCompleted` | boolean | 是否已达标(满档) | ❌ 未定义 |
|
||||
| `bonusMoney` | number | 升档奖金 | ❌ 未定义 |
|
||||
| `incomeFormatted` | string | 格式化收入 | 契约有 `total_income` |
|
||||
| `incomeTrend` | string | 收入趋势(如 `↓368`) | ❌ 未定义 |
|
||||
| `incomeTrendDir` | enum | `up/down` | ❌ 未定义 |
|
||||
| `incomeMonth` | string | 当前月份标签 | 契约有 `month_label` |
|
||||
| `prevMonth` | string | 上月标签 | ❌ 未定义 |
|
||||
|
||||
### enrichTask() 需要后端返回的扩展字段
|
||||
| 字段 | 类型 | 说明 | 契约/spec 定义 |
|
||||
|------|------|------|---------------|
|
||||
| `lastVisitDays` | number | 距上次到店天数 | ❌ 前端 mock 计算,后端需返回 |
|
||||
| `balance` | number | 客户余额 | ❌ 未定义在 TASK-1 |
|
||||
| `aiSuggestion` | string | AI 建议摘要 | ❌ 未定义在 TASK-1 |
|
||||
|
||||
### 页面跳转
|
||||
| 操作 | 目标 | 参数 |
|
||||
|------|------|------|
|
||||
| 点击任务卡片 | `/pages/task-detail/task-detail` | `?id={taskId}` |
|
||||
| 点击绩效卡片 | `/pages/performance/performance` | 无参数 |
|
||||
| 长按→AI助手 | `/pages/ai-chat/ai-chat` | `?taskId={id}&customerName={name}` |
|
||||
|
||||
### 写操作
|
||||
| 操作 | API | 请求体 | 成功后行为 |
|
||||
|------|-----|--------|-----------|
|
||||
| 长按→置顶/取消置顶 | `POST /tasks/{id}/pin` 或 `/unpin` | 无 | 本地更新 `isPinned`,重新分组 |
|
||||
| 长按→放弃 | `POST /tasks/{id}/abandon` | `{ reason: string }` | 本地移至 abandonedTasks |
|
||||
| 长按→取消放弃 | `POST /tasks/{id}/restore` | 无 | 本地移至 normalTasks |
|
||||
| 长按→添加备注 | `POST /notes` | `{ content, taskId?, customerId? }` | Toast 提示 |
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-02**:契约 TASK-1 `performance` 仅有 4 个字段(`total_hours/total_income/total_customers/month_label`),但前端 `buildPerfData()` 需要 15+ 个字段(`tierNodes`、`basicHours`、`bonusHours`、`currentTier`、`nextTierHours`、`tierCompleted`、`bonusMoney`、`incomeTrend`、`incomeTrendDir`、`prevMonth` 等)。这是最大的 Gap。
|
||||
- **GAP-03**:`enrichTask()` 需要 `lastVisitDays`(距上次到店天数)、`balance`(客户余额)、`aiSuggestion`(AI 建议),这 3 个字段在 TASK-1 契约中未定义。后端需在任务列表 item 中附带这些字段,或前端改为进入详情页后再加载。
|
||||
- **GAP-04**:`pin/unpin` 操作的 API 端点在契约中未定义。spec 提到已实现 `pin/unpin`,但 `api.ts` 中无对应函数,前端仅做本地状态更新。需确认 API 路径(`POST /tasks/{id}/pin` 和 `POST /tasks/{id}/unpin`)。
|
||||
- **GAP-05**:`createNote` 的 `score` 参数 — 前端备注弹窗 `onNoteConfirm` 传递 `{ score, content }`,但 `api.ts` 的 `createNote` 签名仅有 `{ content, taskId?, customerId? }`,缺少 `score` 字段。
|
||||
|
||||
---
|
||||
|
||||
## 场景 3:任务详情页(task-detail.ts)
|
||||
|
||||
### API 调用
|
||||
- `fetchTaskDetail(taskId)` → `GET /api/xcx/tasks/{taskId}`
|
||||
- 参数来源:`options.id`(从 task-list 跳转传入)
|
||||
- `deleteNote(noteId)` → `DELETE /api/xcx/notes/{noteId}`
|
||||
|
||||
### 前端期望字段 — TaskDetail(完整清单)
|
||||
|
||||
#### 基础信息(继承 Task)
|
||||
同 TASK-1 item 字段,契约已覆盖。
|
||||
|
||||
#### 维客线索 retentionClues(内联 mock,8 条)
|
||||
| 字段 | 类型 | 说明 | 契约 TASK-2 |
|
||||
|------|------|------|------------|
|
||||
| `tag` | string | 线索大类标签(如 `"客户\n基础"`) | ❌ 契约用 `tag` 但格式不同 |
|
||||
| `tagColor` | enum | `primary/success/purple/error` | ✅ `tag_color` |
|
||||
| `emoji` | string | 表情符号 | ✅ |
|
||||
| `text` | string | 线索摘要 | ✅ |
|
||||
| `source` | string | 来源(如 `"By:系统"`、`"By:小燕"`) | ⚠️ 契约有 `source` 但格式不同 |
|
||||
| `desc` | string? | 详细描述(展开后显示) | ✅ |
|
||||
| `expanded` | boolean | 展开状态(前端本地) | — |
|
||||
|
||||
#### 话术参考 talkingPoints(内联 mock,5 条)
|
||||
| 字段 | 类型 | 说明 | 契约 TASK-2 |
|
||||
|------|------|------|------------|
|
||||
| `talkingPoints` | string[] | 话术文本数组 | ✅ |
|
||||
|
||||
#### 服务记录 serviceRecords(内联 mock,4 条)
|
||||
| 字段 | 类型 | 说明 | 契约 TASK-2 |
|
||||
|------|------|------|------------|
|
||||
| `table` | string | 台桌号 | ✅ |
|
||||
| `type` | string | 课程类型标签 | ✅ |
|
||||
| `typeClass` | enum | `basic/vip/tip/recharge/incentive` | ✅ `type_class` |
|
||||
| `recordType` | enum? | `course/recharge` | ✅ `record_type` |
|
||||
| `duration` | number | 折算后课时(小时) | ✅ |
|
||||
| `durationRaw` | number? | 折算前课时(小时) | ✅ `duration_raw` |
|
||||
| `income` | number | 收入(元) | ✅ |
|
||||
| `isEstimate` | boolean? | 是否预估金额 | ✅ `is_estimate` |
|
||||
| `drinks` | string | 酒水描述 | ✅ |
|
||||
| `date` | string | 日期(格式化后) | ✅ |
|
||||
|
||||
#### 服务汇总 serviceSummary
|
||||
| 字段 | 类型 | 说明 | 契约 TASK-2 |
|
||||
|------|------|------|------------|
|
||||
| `totalHours` | number | 总工时 | ✅ |
|
||||
| `totalIncome` | number | 总收入 | ✅ |
|
||||
| `avgIncome` | number | 平均收入 | ✅ |
|
||||
|
||||
#### AI 分析 aiAnalysis
|
||||
| 字段 | 类型 | 说明 | 契约 TASK-2 |
|
||||
|------|------|------|------------|
|
||||
| `summary` | string | AI 分析摘要 | ✅ |
|
||||
| `suggestions` | string[] | AI 建议列表 | ✅ |
|
||||
|
||||
#### 备注 notes(内联 mock,5 条)
|
||||
| 字段 | 类型 | 说明 | 契约 TASK-2 |
|
||||
|------|------|------|------------|
|
||||
| `id` | string | 备注 ID | ✅ |
|
||||
| `content` | string | 备注内容 | ✅ |
|
||||
| `tagType` | enum | `customer/coach/system` | ✅ `tag_type` |
|
||||
| `tagLabel` | string | 标签文案 | ✅ `tag_label` |
|
||||
| `createdAt` | string | 创建时间 | ✅ `created_at` |
|
||||
| `score` | number? | 满意度评分 0-10 | ✅ |
|
||||
|
||||
### 页面跳转
|
||||
| 操作 | 目标 | 参数 |
|
||||
|------|------|------|
|
||||
| 问问助手 | `/pages/chat/chat` | `?customerId={detail.id}` |
|
||||
| 查看全部服务记录 | `/pages/customer-service-records/customer-service-records` | `?customerId={detail.id}` |
|
||||
|
||||
### 写操作
|
||||
| 操作 | API | 请求体 | 成功后行为 |
|
||||
|------|-----|--------|-----------|
|
||||
| 添加备注 | `POST /notes` | `{ content }` | 本地 prepend 到 sortedNotes |
|
||||
| 删除备注 | `DELETE /notes/{noteId}` | 无 | 本地 filter 移除 |
|
||||
| 放弃任务 | `POST /tasks/{id}/abandon` | `{ reason }` | 更新 `detail.status='abandoned'` |
|
||||
| 取消放弃 | `POST /tasks/{id}/restore` | 无 | 更新 `detail.status='pending'` |
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-06**:维客线索的 `tag` 字段格式不一致 — 前端内联 mock 使用 `"客户\n基础"` 格式(含换行符),契约定义为普通 string。后端需明确 tag 的取值枚举和格式。
|
||||
- **GAP-07**:维客线索的 `source` 字段格式不一致 — 前端显示 `"By:系统"`、`"By:小燕"`,但 mock-data.ts 中 `RetentionClue.source` 定义为 `'manual' | 'ai_consumption' | 'ai_note'`。前端内联 mock 与类型定义不匹配,后端需明确返回格式。
|
||||
- **GAP-08**:`aiAnalysis` 数据来源 — spec §3.2 提到来自 `biz.ai_cache`(app4/app5/app6/app7),但契约未说明 `cache_type` 映射关系。需明确:`ai_analysis.summary` 来自哪个 `cache_type`?`talking_points` 来自哪个 `cache_type`?
|
||||
- **GAP-09**:task-detail 跳转 chat 时传 `customerId={detail.id}`,但 `detail.id` 实际是 **taskId** 不是 customerId。前端需要一个 `customerId` 字段(TASK-2 响应中需包含 `customer_id`)。
|
||||
- **GAP-10**:task-detail 跳转 customer-service-records 时同样传 `customerId={detail.id}`,存在同样的 taskId/customerId 混淆问题。TASK-2 响应需包含 `customer_id`。
|
||||
- **GAP-11**:`storageLevel`(储值等级,如"非常多")和 `relationLevel/relationLevelText/relationColor`(关系等级)在前端本地计算,但后端未定义这些字段。后端是否需要返回,还是前端根据 `heartScore` 自行计算?
|
||||
|
||||
---
|
||||
|
||||
## 场景 4:绩效概览页(performance.ts)
|
||||
|
||||
### API 调用
|
||||
- `fetchPerformanceOverview({ year, month })` → `GET /api/xcx/performance?year=&month=`
|
||||
- 参数来源:`new Date()` 取当前年月
|
||||
- 返回类型:`PerformanceData`(mock-data.ts 定义)
|
||||
|
||||
### 前端期望字段 — 完整清单(对比契约 PERF-1)
|
||||
|
||||
#### Banner 数据(内联 mock,非 API 返回)
|
||||
| 字段 | 类型 | 说明 | 契约 PERF-1 | 数据源 |
|
||||
|------|------|------|------------|--------|
|
||||
| `coachName` | string | 助教姓名 `'小燕'` | ❌ 未定义 | 应从 globalData.authUser 或 /me |
|
||||
| `coachRole` | string | 角色 `'助教'` | ❌ 未定义 | 同上 |
|
||||
| `storeName` | string | 门店名 `'广州朗朗桌球'` | ❌ 未定义 | 同上 |
|
||||
| `monthlyIncome` | string | 本月收入 `'¥6,206'` | ⚠️ 契约有 `total_income` 但是 number | 需格式化 |
|
||||
| `lastMonthIncome` | string | 上月收入 `'¥16,880'` | ❌ 未定义 | 需后端额外返回 |
|
||||
|
||||
#### 收入档位(内联 mock)
|
||||
| 字段 | 类型 | 说明 | 契约 PERF-1 |
|
||||
|------|------|------|------------|
|
||||
| `currentTier.basicRate` | number | 当前档基础费率 80 | ❌ 未定义 |
|
||||
| `currentTier.incentiveRate` | number | 当前档激励费率 95 | ❌ 未定义 |
|
||||
| `nextTier.basicRate` | number | 下一档基础费率 90 | ❌ 未定义 |
|
||||
| `nextTier.incentiveRate` | number | 下一档激励费率 114 | ❌ 未定义 |
|
||||
| `upgradeHoursNeeded` | number | 距升档所需工时 15 | ❌ 未定义 |
|
||||
| `upgradeBonus` | number | 升档奖金 800 | ❌ 未定义 |
|
||||
|
||||
#### 收入明细 incomeItems(内联 mock,4 项)
|
||||
| 项目 | icon | desc 格式 | 契约 PERF-1 |
|
||||
|------|------|----------|------------|
|
||||
| 基础课 | 🎱 | `80元/h × 75h` | ⚠️ 契约有 `income_items` 但字段不同 |
|
||||
| 激励课 | ⭐ | `95.05元/h × 10h` | 同上 |
|
||||
| 充值激励 | 💰 | `客户充值返佣` | 同上 |
|
||||
| TOP3 销冠奖 | 🏆 | `全店业绩前三名奖励` | 同上 |
|
||||
|
||||
契约 `income_items` 定义:`Array<{ label, amount, icon }>`,缺少 `desc` 字段。
|
||||
|
||||
#### 服务记录 thisMonthRecords(内联 mock,按日期分组)
|
||||
| 字段 | 类型 | 说明 | 契约 PERF-1 |
|
||||
|------|------|------|------------|
|
||||
| `date` | string | 日期标签 `'2月7日'` | ❌ 契约 `this_month_records` 结构完全不同 |
|
||||
| `totalHours` | string | 当日总工时 `'4.0h'` | ❌ |
|
||||
| `totalIncome` | string | 当日总收入 `'¥350'` | ❌ |
|
||||
| `records[].customerName` | string | 客户名 | ⚠️ 契约有 `customer_name` |
|
||||
| `records[].avatarChar` | string | 头像首字 | ❌ |
|
||||
| `records[].avatarColor` | string | 头像颜色 | ❌ |
|
||||
| `records[].timeRange` | string | 时间段 `'20:00-22:00'` | ❌ |
|
||||
| `records[].hours` | string | 工时 `'2.0h'` | ⚠️ 契约有 `hours` 但是 number |
|
||||
| `records[].courseType` | string | 课程类型 | ❌ |
|
||||
| `records[].courseTypeClass` | string | 样式类 | ❌ |
|
||||
| `records[].location` | string | 台号 | ❌ |
|
||||
| `records[].income` | string | 收入 `'¥160'` | ⚠️ 契约有 `income` 但是 number |
|
||||
|
||||
#### 新客列表 newCustomers
|
||||
| 字段 | 类型 | 说明 | 契约 PERF-1 |
|
||||
|------|------|------|------------|
|
||||
| `name` | string | 客户名 | ✅ |
|
||||
| `avatarChar` | string | 头像首字 | ❌ 契约有 `avatar_char` |
|
||||
| `avatarColor` | string | 头像颜色 | ❌ 未定义 |
|
||||
| `lastService` | string | 最后服务日期 | ❌ 未定义 |
|
||||
| `count` | number | 服务次数 | ❌ 未定义 |
|
||||
|
||||
#### 常客列表 regularCustomers
|
||||
| 字段 | 类型 | 说明 | 契约 PERF-1 |
|
||||
|------|------|------|------------|
|
||||
| `name` | string | 客户名 | ✅ |
|
||||
| `avatarChar` | string | 头像首字 | ❌ 契约有 `avatar_char` |
|
||||
| `avatarColor` | string | 头像颜色 | ❌ 未定义 |
|
||||
| `hours` | number | 总工时 | ❌ 未定义 |
|
||||
| `income` | string | 总收入 | ❌ 未定义 |
|
||||
| `count` | number | 到店次数 | ⚠️ 契约有 `visits` |
|
||||
|
||||
### 页面跳转
|
||||
| 操作 | 目标 | 参数 |
|
||||
|------|------|------|
|
||||
| 查看全部记录 | `/pages/performance-records/performance-records` | 无参数 |
|
||||
| 点击客户卡片 | `/pages/task-detail/task-detail` | `?customerName={name}` |
|
||||
| 点击服务记录 | `/pages/task-detail/task-detail` | `?customerName={name}&taskId={taskId}` |
|
||||
| 点击收入概览 | `/pages/performance-records/performance-records` | 无参数 |
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-12**:契约 PERF-1 `this_month_records` 是扁平数组 `Array<{ customer_name, hours, income, date }>`,但前端需要 **按日期分组的 DateGroup 结构**(含 `date`、`totalHours`、`totalIncome`、`records[]`),且每条记录需要 `timeRange`、`courseType`、`courseTypeClass`、`location` 等字段。契约需大幅扩展。
|
||||
- **GAP-13**:收入档位数据(`currentTier`、`nextTier`、`upgradeHoursNeeded`、`upgradeBonus`)在契约 PERF-1 中完全未定义。这些数据来自 `dws_assistant_salary_calc`,后端需返回。
|
||||
- **GAP-14**:`lastMonthIncome`(上月收入)在契约中未定义,前端 Banner 需要展示。
|
||||
- **GAP-15**:`incomeItems` 的 `desc` 字段(如 `"80元/h × 75h"`)在契约中未定义。后端需返回费率和工时的拆分数据,或直接返回格式化的 desc 字符串。
|
||||
- **GAP-16**:`newCustomers` 缺少 `lastService`(最后服务日期)和 `count`(服务次数)字段;`regularCustomers` 缺少 `hours`(总工时)和 `income`(总收入)字段。
|
||||
- **GAP-17**:跳转 task-detail 时传 `customerName` 而非 `taskId`,但 task-detail 页面 `onLoad` 读取的是 `options.id`。参数传递不匹配,联调时会导致页面无法加载正确数据。
|
||||
- **GAP-18**:页面无月份切换功能(F8),API 支持 `year/month` 参数但页面固定当前月。
|
||||
|
||||
---
|
||||
|
||||
## 场景 5:绩效明细页(performance-records.ts)
|
||||
|
||||
### API 调用
|
||||
- `fetchPerformanceRecords({ year, month })` → `GET /api/xcx/performance/records?year=&month=&page=&pageSize=`
|
||||
- 参数来源:`currentYear`/`currentMonth`(页面 data,支持月份切换)
|
||||
- Banner 数据从 `getApp().globalData.authUser` 读取
|
||||
|
||||
### 前端期望字段 — summary
|
||||
| 字段 | 类型 | 说明 | 契约 PERF-2 |
|
||||
|------|------|------|------------|
|
||||
| `totalCount` | number | 总笔数 32 | ✅ `total_count` |
|
||||
| `totalHours` | number | 总工时 59.0 | ✅ `total_hours` |
|
||||
| `totalHoursRaw` | number | 折算前总工时 63.5 | ✅ `total_hours_raw` |
|
||||
| `totalIncome` | number | 总收入 4720 | ✅ `total_income` |
|
||||
|
||||
### 前端期望字段 — dateGroups(按日期分组)
|
||||
| 字段 | 类型 | 说明 | 契约 PERF-2 |
|
||||
|------|------|------|------------|
|
||||
| `date` | string | 日期标签 `'2月7日'` | ✅ |
|
||||
| `totalHours` | number | 当日总工时 | ✅ |
|
||||
| `totalIncome` | number | 当日总收入 | ✅ |
|
||||
| `records[].id` | string | 记录 ID | ✅ |
|
||||
| `records[].customerName` | string | 客户名 | ✅ |
|
||||
| `records[].avatarChar` | string | 头像首字 | ❌ 未定义 |
|
||||
| `records[].avatarColor` | string | 头像颜色 | ❌ 未定义 |
|
||||
| `records[].timeRange` | string | 时间段 | ✅ |
|
||||
| `records[].hours` | number | 折算后工时 | ✅ |
|
||||
| `records[].hoursRaw` | number? | 折算前工时 | ✅ |
|
||||
| `records[].courseType` | string | 课程类型 | ✅ |
|
||||
| `records[].courseTypeClass` | string | 样式类 | ✅ |
|
||||
| `records[].location` | string | 台号 | ✅ |
|
||||
| `records[].income` | number | 收入 | ✅ |
|
||||
|
||||
### courseTypeClass 完整枚举
|
||||
前端使用的样式类:`tag-basic`(基础课)、`tag-vip`(包厢课)、`tag-tip`(打赏课)。
|
||||
契约定义的 `course_type_class`:未明确枚举值。
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-19**:`avatarChar`(头像首字)和 `avatarColor`(头像颜色)在契约 PERF-2 中未定义。前端通过 `nameToAvatarColor()` 工具函数从姓名生成,但后端是否需要返回?建议前端自行计算。
|
||||
- **GAP-20**:`courseTypeClass` 的完整枚举值未在契约中明确。前端使用 `tag-basic`/`tag-vip`/`tag-tip`,但后端返回的是 `basic`/`vip`/`tip` 还是带 `tag-` 前缀?需统一。
|
||||
- **GAP-21**:月份切换时未重置分页(F9 bug),`switchMonth()` 中 `page` 未重置为 1。
|
||||
- **GAP-22**:Banner 字段 `coachName`/`coachLevel`/`storeName` 从 `globalData.authUser` 读取,但 `authUser` 当前未存储 `coachLevel` 和 `storeName`(见 GAP-01)。
|
||||
|
||||
---
|
||||
|
||||
## 场景 6:客户详情页(customer-detail.ts)
|
||||
|
||||
### API 调用
|
||||
- `fetchCustomerDetail(customerId)` → `GET /api/xcx/customers/{customerId}`
|
||||
- 参数来源:页面路由参数(但当前代码从 `__route__` 解析,有 bug)
|
||||
|
||||
### 前端期望字段 — 完整清单
|
||||
|
||||
#### 客户基本信息(内联 mock)
|
||||
| 字段 | 类型 | 说明 | 契约 CUST-1 |
|
||||
|------|------|------|------------|
|
||||
| `id` | string | 客户 ID | ✅ |
|
||||
| `name` | string | 客户名 | ✅ |
|
||||
| `avatarChar` | string | 头像首字 | ❌ 未定义 |
|
||||
| `phone` | string | 手机号(完整) | ⚠️ 契约有 `phone`(脱敏)和 `phone_full`(完整) |
|
||||
| `balance` | string | 余额 `'8,600'` | ❌ 未定义 |
|
||||
| `consumption60d` | string | 近60天消费 `'2,800'` | ❌ 未定义 |
|
||||
| `idealInterval` | string | 理想到店间隔 `'7天'` | ❌ 未定义 |
|
||||
| `daysSinceVisit` | string | 距上次到店 `'12天'` | ❌ 未定义 |
|
||||
|
||||
#### AI 洞察 aiInsight(内联 mock)
|
||||
| 字段 | 类型 | 说明 | 契约 CUST-1 |
|
||||
|------|------|------|------------|
|
||||
| `summary` | string | AI 分析摘要 | ❌ 未定义 |
|
||||
| `strategies` | Array<{color, text}> | 策略建议列表 | ❌ 未定义 |
|
||||
|
||||
#### 维客线索 clues(内联 mock,7 条)
|
||||
| 字段 | 类型 | 说明 | 契约 CUST-1 |
|
||||
|------|------|------|------------|
|
||||
| `category` | string | 线索大类(含换行符) | ⚠️ 契约说"同 TASK-2 格式" |
|
||||
| `categoryColor` | string | 颜色 | ⚠️ |
|
||||
| `text` | string | 含 emoji 的摘要 | ⚠️ |
|
||||
| `source` | string | 来源 `'系统'`/`'小燕'` | ⚠️ |
|
||||
| `detail` | string? | 详细描述 | ⚠️ |
|
||||
|
||||
#### 关联助教 coachTasks(内联 mock,4 位助教)
|
||||
| 字段 | 类型 | 说明 | 契约 CUST-1 |
|
||||
|------|------|------|------------|
|
||||
| `name` | string | 助教名 | ❌ 完全未定义 |
|
||||
| `level` | string | 等级 `senior/middle/junior` | ❌ |
|
||||
| `levelColor` | string | 等级颜色 | ❌ |
|
||||
| `taskType` | string | 任务类型标签 | ❌ |
|
||||
| `taskColor` | string | 任务颜色 | ❌ |
|
||||
| `bgClass` | string | 背景样式类 | ❌ |
|
||||
| `status` | string | 状态 `normal/pinned/abandoned` | ❌ |
|
||||
| `lastService` | string | 最后服务 `'02-20 21:30 · 2.5h'` | ❌ |
|
||||
| `metrics` | Array<{label,value,color}> | 指标(近60天次数/总时长/次均时长) | ❌ |
|
||||
|
||||
#### 最亲密助教 favoriteCoaches(内联 mock,2 位)
|
||||
| 字段 | 类型 | 说明 | 契约 CUST-1 |
|
||||
|------|------|------|------------|
|
||||
| `emoji` | string | 爱心 emoji | ❌ 完全未定义 |
|
||||
| `name` | string | 助教名 | ❌ |
|
||||
| `relationIndex` | string | 关系指数 `'9.2'` | ❌ |
|
||||
| `indexColor` | string | 指数颜色 | ❌ |
|
||||
| `bgClass` | string | 背景样式类 | ❌ |
|
||||
| `stats` | Array<{label,value,color}> | 统计(基础/激励/上课/充值) | ❌ |
|
||||
|
||||
#### 消费记录 consumptionRecords(内联 mock,3 条,3 种类型)
|
||||
| 字段 | 类型 | 说明 | 契约 CUST-1 |
|
||||
|------|------|------|------------|
|
||||
| `id` | string | 记录 ID | ✅ |
|
||||
| `type` | enum | `table/shop/recharge` | ⚠️ 契约有 `type` 但枚举不同 |
|
||||
| `date` | string | 日期 | ✅ |
|
||||
| `tableName` | string? | 台桌名 | ❌ 契约有 `table` |
|
||||
| `startTime` | string? | 开始时间 | ❌ 未定义 |
|
||||
| `endTime` | string? | 结束时间 | ❌ 未定义 |
|
||||
| `duration` | string? | 时长 `'3h 20min'` | ⚠️ 契约有 `duration` 但是 number |
|
||||
| `tableFee` | number? | 台费 | ❌ 未定义 |
|
||||
| `tableOrigPrice` | number? | 台费原价 | ❌ 未定义 |
|
||||
| `coaches` | Array<{name,level,levelColor,courseType,hours,perfHours?,fee}> | 助教服务明细 | ❌ 完全未定义 |
|
||||
| `foodAmount` | number? | 酒水金额 | ❌ 未定义 |
|
||||
| `foodOrigPrice` | number? | 酒水原价 | ❌ 未定义 |
|
||||
| `totalAmount` | number? | 总金额 | ⚠️ 契约有 `amount` |
|
||||
| `totalOrigPrice` | number? | 总原价 | ❌ 未定义 |
|
||||
| `payMethod` | string? | 支付方式 | ❌ 未定义 |
|
||||
| `rechargeAmount` | number? | 充值金额 | ❌ 未定义 |
|
||||
|
||||
#### 备注 sortedNotes(内联 mock,3 条)
|
||||
| 字段 | 类型 | 说明 | 契约 CUST-1 |
|
||||
|------|------|------|------------|
|
||||
| `id` | string | 备注 ID | ❌ 契约无备注字段 |
|
||||
| `tagLabel` | string | 标签 | ❌ |
|
||||
| `createdAt` | string | 创建时间 | ❌ |
|
||||
| `content` | string | 内容 | ❌ |
|
||||
|
||||
### 页面跳转
|
||||
| 操作 | 目标 | 参数 |
|
||||
|------|------|------|
|
||||
| 查看服务记录 | `/pages/customer-service-records/customer-service-records` | 无参数(⚠️ 未传 customerId) |
|
||||
| 问问助手 | `/pages/chat/chat` | 无参数(⚠️ 未传 customerId) |
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-23**:`balance`(余额)、`consumption60d`(近60天消费)、`idealInterval`(理想到店间隔)、`daysSinceVisit`(距上次到店天数)在契约 CUST-1 中完全未定义。这些是客户详情页 Banner 的核心展示字段。
|
||||
- **GAP-24**:`aiInsight`(AI 洞察:summary + strategies)在契约 CUST-1 中完全未定义。数据来源应为 `biz.ai_cache`,但 `cache_type` 未明确(可能是 `app4_analysis` 或新增类型)。
|
||||
- **GAP-25**:`coachTasks`(关联助教任务列表)在契约 CUST-1 中完全未定义。这是一个复杂的数据结构,需要从 `biz.coach_tasks` + `fdw_etl.v_dwd_assistant_service_log` 聚合。每位助教需返回:任务类型、状态、最后服务时间、近60天服务次数/总时长/次均时长。
|
||||
- **GAP-26**:`favoriteCoaches`(最亲密助教)在契约 CUST-1 中完全未定义。需要从 `fdw_etl.v_dws_member_assistant_relation_index` 获取关系指数,并聚合基础课时/激励课时/上课次数/充值金额。
|
||||
- **GAP-27**:消费记录的 `coaches` 子数组(助教服务明细,含 `perfHours` 折算工时)在契约中完全未定义。这需要从 `fdw_etl.v_dwd_assistant_service_log` 按结算单关联查询。
|
||||
- **GAP-28**:消费记录的 `tableFee`/`tableOrigPrice`/`foodAmount`/`foodOrigPrice`/`totalOrigPrice`/`payMethod` 等拆分字段在契约中未定义。契约仅有 `amount`(总金额)。
|
||||
- **GAP-29**:消费记录的 `type` 枚举不一致 — 前端使用 `table/shop/recharge`(3 种),契约使用 `type_class`(未明确枚举)。
|
||||
- **GAP-30**:备注列表在契约 CUST-1 中未定义。前端需要展示客户相关的备注。
|
||||
- **GAP-31**:跳转 customer-service-records 和 chat 时未传 `customerId` 参数,联调时目标页面无法获取客户 ID。
|
||||
|
||||
---
|
||||
|
||||
## 场景 7:客户服务记录页(customer-service-records.ts)
|
||||
|
||||
### API 调用
|
||||
- `fetchCustomerRecords({ customerId, year?, month?, table? })` → `GET /api/xcx/customers/{customerId}/records`
|
||||
- 参数来源:`options.customerId` 或 `options.id`
|
||||
- 当前实现:首次加载拉取全部记录,月份切换仅本地过滤
|
||||
|
||||
### 前端期望字段 — ServiceRecord
|
||||
| 字段 | 类型 | 说明 | 契约 CUST-2 |
|
||||
|------|------|------|------------|
|
||||
| `table` | string | 台桌号 | ✅ |
|
||||
| `type` | string | 课程类型标签 | ✅ |
|
||||
| `typeClass` | enum | `basic/vip/tip/recharge` | ✅ `type_class` |
|
||||
| `recordType` | enum | `course/recharge` | ❌ 未定义 |
|
||||
| `duration` | number | 折算后小时 | ✅ |
|
||||
| `durationRaw` | number | 折算前小时 | ✅ `duration_raw` |
|
||||
| `income` | number | 到手金额 | ✅ |
|
||||
| `isEstimate` | boolean | 是否预估 | ❌ 未定义 |
|
||||
| `drinks` | string | 饮品描述 | ✅ |
|
||||
| `date` | string | 显示日期 | ✅ |
|
||||
|
||||
### 页面头部字段
|
||||
| 字段 | 类型 | 说明 | 契约 CUST-2 |
|
||||
|------|------|------|------------|
|
||||
| `customerName` | string | 客户名 | ✅ |
|
||||
| `customerPhone` | string | 脱敏手机号 | ✅ `customer_phone` |
|
||||
| `customerPhoneFull` | string | 完整手机号 | ❌ 未定义(需从 CUST-1 获取) |
|
||||
| `relationIndex` | string | 关系指数 | ✅ |
|
||||
| `totalServiceCount` | number | 累计服务次数 | ❌ 未定义 |
|
||||
| `monthCount` | string | 当月次数 | ❌ 需前端计算 |
|
||||
| `monthHours` | string | 当月工时 | ❌ 需前端计算 |
|
||||
| `monthRelation` | string | 当月关系指数 | ❌ 未定义 |
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-32**:`recordType`(`course/recharge`)和 `isEstimate`(是否预估)在契约 CUST-2 中未定义。
|
||||
- **GAP-33**:`customerPhoneFull`(完整手机号)在 CUST-2 响应中未定义,前端需要点击查看完整号码。
|
||||
- **GAP-34**:`totalServiceCount`(累计服务次数)在 CUST-2 中未定义。
|
||||
- **GAP-35**:月份切换采用本地筛选(F10),联调后数据量大时需改为按月请求 API。
|
||||
|
||||
---
|
||||
|
||||
## 场景 8:备注列表页(notes.ts)
|
||||
|
||||
### API 调用
|
||||
- `fetchNotes({ page?, pageSize? })` → `GET /api/xcx/notes?page=&pageSize=`
|
||||
- `deleteNote(noteId)` → `DELETE /api/xcx/notes/{noteId}`
|
||||
|
||||
### 前端期望字段 — Note
|
||||
| 字段 | 类型 | 说明 | 契约 NOTE-1 |
|
||||
|------|------|------|------------|
|
||||
| `id` | string | 备注 ID | ✅ |
|
||||
| `content` | string | 备注内容 | ✅ |
|
||||
| `tagType` | enum | `customer/coach/system` | ✅ `tag_type` |
|
||||
| `tagLabel` | string | 标签文案 | ✅ `tag_label` |
|
||||
| `createdAt` | string | 创建时间 | ✅ `created_at` |
|
||||
| `score` | number? | 满意度评分 | ✅ |
|
||||
| `timeLabel` | string | 格式化时间(前端计算) | — |
|
||||
|
||||
### 写操作
|
||||
| 操作 | API | 成功后行为 |
|
||||
|------|-----|-----------|
|
||||
| 删除备注 | `DELETE /notes/{noteId}` | 本地 filter 移除 + Toast |
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-36**:无触底加载逻辑(F11),API 支持分页但 `onReachBottom()` 未实现。
|
||||
- **GAP-37**:`tagType` 枚举 — mock 数据中有 `'system'` 类型,但契约 NOTE-1 仅定义 `customer/coach/system`。需确认 `system` 类型的备注由谁创建、如何展示。
|
||||
|
||||
---
|
||||
|
||||
## 场景 9:助教详情页(coach-detail.ts)
|
||||
|
||||
### API 调用
|
||||
- `fetchCoachDetail(coachId)` → `GET /api/xcx/coaches/{coachId}`
|
||||
- 参数来源:`options.id`
|
||||
- 返回类型:`CoachCard`(api.ts 定义),但前端实际需要远超 `CoachCard` 的数据
|
||||
|
||||
### 前端期望字段 — CoachDetail(完整清单)
|
||||
|
||||
#### 基本信息
|
||||
| 字段 | 类型 | 说明 | 契约 COACH-1 |
|
||||
|------|------|------|-------------|
|
||||
| `id` | string | 助教 ID | ✅ |
|
||||
| `name` | string | 助教名 | ✅ |
|
||||
| `avatar` | string | 头像 | ✅ |
|
||||
| `level` | string | 等级 `'星级'` | ✅ |
|
||||
| `skills` | string[] | 技能 | ✅ |
|
||||
| `workYears` | number | 工龄 3 | ❌ 未定义 |
|
||||
| `customerCount` | number | 客户数 68 | ❌ 未定义 |
|
||||
| `hireDate` | string | 入职日期 | ❌ 未定义 |
|
||||
| `store_name` | string | 门店名 | ✅ |
|
||||
|
||||
#### 绩效指标 performance(6 个指标)
|
||||
| 字段 | 类型 | 说明 | 契约 COACH-1 |
|
||||
|------|------|------|-------------|
|
||||
| `monthlyHours` | number | 本月工时 87.5 | ❌ 未定义 |
|
||||
| `monthlySalary` | number | 本月工资 6950 | ❌ 未定义 |
|
||||
| `customerBalance` | number | 客源储值余额 86200 | ❌ 未定义 |
|
||||
| `tasksCompleted` | number | 任务完成数 38 | ❌ 未定义 |
|
||||
| `perfCurrent` | number | 当前绩效 80 | ❌ 未定义 |
|
||||
| `perfTarget` | number | 目标绩效 100 | ❌ 未定义 |
|
||||
|
||||
#### 绩效指标卡片 perfCards(4 张)
|
||||
| 卡片 | 需要字段 | 契约 |
|
||||
|------|---------|------|
|
||||
| 本月定档业绩 | `monthlyHours` + 折算前工时 | ❌ |
|
||||
| 本月工资(预估) | `monthlySalary` | ❌ |
|
||||
| 客源储值余额 | `customerBalance` + `customerCount` | ❌ |
|
||||
| 本月任务完成 | `tasksCompleted` + 覆盖客户数 | ❌ |
|
||||
|
||||
#### 进度条数据
|
||||
| 字段 | 类型 | 说明 | 契约 |
|
||||
|------|------|------|------|
|
||||
| `tierNodes` | number[] | 档位节点 `[0,100,130,160,190,220]` | ❌ 未定义 |
|
||||
|
||||
#### 收入明细 income(本月/上月各 4 项)
|
||||
| 项目 | 说明 | 契约 |
|
||||
|------|------|------|
|
||||
| 基础课时费 | `¥3,500` | ❌ 未定义 |
|
||||
| 激励课时费 | `¥1,800` | ❌ 未定义 |
|
||||
| 充值提成 | `¥1,200` | ❌ 未定义 |
|
||||
| 酒水提成 | `¥450` | ❌ 未定义 |
|
||||
|
||||
#### 任务执行
|
||||
| 字段 | 类型 | 说明 | 契约 COACH-1 |
|
||||
|------|------|------|-------------|
|
||||
| `visibleTasks` | TaskItem[] | 可见任务(active) | ⚠️ 契约说"同 TASK-1 item 格式"但实际需要更多字段 |
|
||||
| `hiddenTasks` | TaskItem[] | 隐藏任务(inactive) | ⚠️ |
|
||||
| `abandonedTasks` | AbandonedTask[] | 已放弃任务 | ⚠️ |
|
||||
|
||||
TaskItem 需要的字段(超出 TASK-1 item):
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `typeLabel` | string | 任务类型标签 |
|
||||
| `typeClass` | string | 样式类 `high-priority/priority/relationship/callback` |
|
||||
| `customerName` | string | 客户名 |
|
||||
| `noteCount` | number | 备注数量 |
|
||||
| `pinned` | boolean | 是否置顶 |
|
||||
| `notes` | Array<{pinned?,text,date}> | 备注列表(含置顶标记) |
|
||||
|
||||
AbandonedTask 字段:
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `customerName` | string | 客户名 |
|
||||
| `reason` | string | 放弃原因 |
|
||||
|
||||
#### 客户关系 TOP20 — TopCustomer
|
||||
| 字段 | 类型 | 说明 | 契约 COACH-1 |
|
||||
|------|------|------|-------------|
|
||||
| `id` | string | 客户 ID | ✅ |
|
||||
| `name` | string | 客户名 | ✅ |
|
||||
| `initial` | string | 姓名首字 | ❌ |
|
||||
| `avatarGradient` | string | 头像渐变色 | ❌ |
|
||||
| `heartEmoji` | string | 爱心 emoji `❤️/💛/🤍` | ❌ |
|
||||
| `score` | string | 关系指数 `'9.5'` | ❌ 契约有 `total_spend`/`visit_count` 但无 score |
|
||||
| `scoreColor` | string | 指数颜色 | ❌ |
|
||||
| `serviceCount` | number | 服务次数 | ⚠️ 契约有 `visit_count` |
|
||||
| `balance` | string | 余额 `'¥8,600'` | ❌ 未定义 |
|
||||
| `consume` | string | 消费 `'¥12,800'` | ⚠️ 契约有 `total_spend` |
|
||||
|
||||
#### 近期服务明细 — ServiceRecord
|
||||
| 字段 | 类型 | 说明 | 契约 COACH-1 |
|
||||
|------|------|------|-------------|
|
||||
| `customerId` | string? | 客户 ID | ❌ |
|
||||
| `customerName` | string | 客户名 | ⚠️ |
|
||||
| `initial` | string | 姓名首字 | ❌ |
|
||||
| `avatarGradient` | string | 头像渐变色 | ❌ |
|
||||
| `type` | string | 课程类型 | ⚠️ |
|
||||
| `typeClass` | string | 样式类 | ⚠️ |
|
||||
| `table` | string | 台号 | ⚠️ |
|
||||
| `duration` | string | 时长 `'2.5h'` | ⚠️ |
|
||||
| `income` | string | 收入 `'¥200'` | ⚠️ |
|
||||
| `date` | string | 日期时间 | ⚠️ |
|
||||
| `perfHours` | string? | 折算工时 `'2h'` | ❌ 未定义 |
|
||||
|
||||
#### 历史月份统计 — HistoryMonth
|
||||
| 字段 | 类型 | 说明 | 契约 COACH-1 |
|
||||
|------|------|------|-------------|
|
||||
| `month` | string | 月份标签 `'本月'`/`'上月'`/`'4月'` | ❌ 完全未定义 |
|
||||
| `estimated` | boolean | 是否预估 | ❌ |
|
||||
| `customers` | string | 客户数 `'22人'` | ❌ |
|
||||
| `hours` | string | 工时 `'87.5h'` | ❌ |
|
||||
| `salary` | string | 工资 `'¥6,950'` | ❌ |
|
||||
| `callbackDone` | number | 回访完成数 | ❌ |
|
||||
| `recallDone` | number | 召回完成数 | ❌ |
|
||||
|
||||
### 页面跳转
|
||||
| 操作 | 目标 | 参数 |
|
||||
|------|------|------|
|
||||
| 点击任务项 | `/pages/customer-detail/customer-detail` | `?name={customerName}` |
|
||||
| 点击客户卡片 | `/pages/customer-detail/customer-detail` | `?id={customerId}` |
|
||||
| 点击服务记录 | `/pages/customer-detail/customer-detail` | `?id={customerId}` |
|
||||
| 查看更多服务记录 | `/pages/performance-records/performance-records` | `?coachId={coachId}` |
|
||||
| 问问助手 | `/pages/chat/chat` | `?coachId={coachId}` |
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-38**:契约 COACH-1 严重不完整。前端需要 `performance`(6 个绩效指标)、`income`(本月/上月各 4 项收入明细)、`tierNodes`(档位节点数组),契约均未定义。
|
||||
- **GAP-39**:`TopCustomer` 需要 `heartEmoji`、`score`(关系指数)、`scoreColor`、`balance`(余额)、`consume`(消费),契约仅有 `total_spend`/`visit_count`。
|
||||
- **GAP-40**:`ServiceRecord.perfHours`(折算工时)在契约中未定义。
|
||||
- **GAP-41**:`HistoryMonth`(历史月份统计)在契约中完全未定义。需要返回最近 N 个月的汇总数据(客户数、工时、工资、回访/召回完成数)。
|
||||
- **GAP-42**:`TaskItem.notes`(任务关联的备注列表,含置顶标记)在契约中未定义。契约的 `visible_tasks` 说"同 TASK-1 item 格式",但前端需要额外的 `noteCount`、`pinned`、`notes[]` 字段。
|
||||
- **GAP-43**:`AbandonedTask.reason`(放弃原因)在契约中未定义。
|
||||
- **GAP-44**:`workYears`(工龄)、`hireDate`(入职日期)在契约中未定义。
|
||||
|
||||
---
|
||||
|
||||
## 场景 10:对话页面(chat.ts + chat-history.ts)
|
||||
|
||||
### chat.ts — API 调用
|
||||
- `fetchChatMessages(customerId)` → `GET /api/xcx/chat/{chatId}/messages`
|
||||
- ⚠️ 注意:前端传入的是 `customerId`(或 `'default'`),但 API 路径参数是 `chatId`。参数语义不匹配。
|
||||
- `sendChatMessage(chatId, content)` → `POST /api/xcx/chat/{chatId}/messages`
|
||||
- 页面参数:`options.customerId`
|
||||
|
||||
### chat.ts — 前端期望字段 — ChatMessage
|
||||
| 字段 | 类型 | 说明 | 契约 CHAT-2 |
|
||||
|------|------|------|------------|
|
||||
| `id` | string | 消息 ID | ✅ |
|
||||
| `role` | enum | `user/assistant` | ✅ |
|
||||
| `content` | string | 消息内容 | ✅ |
|
||||
| `timestamp` | string | 时间戳 | ⚠️ 契约用 `created_at` |
|
||||
| `referenceCard` | object? | 引用卡片 | ❌ 完全未定义 |
|
||||
| `referenceCard.type` | enum | `customer/record` | ❌ |
|
||||
| `referenceCard.title` | string | 卡片标题 | ❌ |
|
||||
| `referenceCard.summary` | string | 卡片摘要 | ❌ |
|
||||
| `referenceCard.data` | Record<string,string> | 键值对数据 | ❌ |
|
||||
|
||||
### chat.ts — SSE 流式回复
|
||||
- 前端 `simulateStreamOutput()` 模拟流式输出(逐字显示)
|
||||
- 联调时需替换为真实 SSE 连接(`POST /api/xcx/chat/stream`)
|
||||
- spec R3 已确认保留 SSE 流式端点
|
||||
|
||||
### chat.ts — 页面参数
|
||||
| 来源页面 | 传递参数 | 用途 |
|
||||
|---------|---------|------|
|
||||
| task-detail | `?customerId={taskId}` | ⚠️ 实际传的是 taskId |
|
||||
| customer-detail | 无参数 | ⚠️ 未传 customerId |
|
||||
| coach-detail | `?coachId={coachId}` | 助教 ID |
|
||||
| chat-history | `?historyId={id}` | 历史对话 ID |
|
||||
|
||||
### chat-history.ts — API 调用
|
||||
- `fetchChatHistory({ page?, pageSize? })` → `GET /api/xcx/chat/history`
|
||||
- 返回:`{ items: ChatHistoryItem[], total, hasMore }`
|
||||
|
||||
### chat-history.ts — 前端期望字段 — ChatHistoryItem
|
||||
| 字段 | 类型 | 说明 | 契约 CHAT-1 |
|
||||
|------|------|------|------------|
|
||||
| `id` | string | 对话 ID | ✅ |
|
||||
| `title` | string | 对话标题 | ❌ 契约无 `title` |
|
||||
| `lastMessage` | string | 最后消息 | ✅ `last_message` |
|
||||
| `timestamp` | string | 时间戳 | ⚠️ 契约用 `last_time` |
|
||||
| `customerName` | string? | 关联客户名 | ✅ |
|
||||
|
||||
### chat-history.ts — 页面跳转
|
||||
| 操作 | 目标 | 参数 |
|
||||
|------|------|------|
|
||||
| 点击对话记录 | `/pages/chat/chat` | `?historyId={id}` |
|
||||
|
||||
### spec 未记录的 Gap
|
||||
- **GAP-45**:`ChatMessage.referenceCard`(引用卡片)在契约 CHAT-2 中完全未定义。前端需要展示客户概览卡片(含键值对数据),后端需在消息中附带结构化引用数据。
|
||||
- **GAP-46**:`ChatMessage.timestamp` vs 契约 `created_at` — 字段名不一致,前端使用 `timestamp`,契约定义 `created_at`。
|
||||
- **GAP-47**:`ChatHistoryItem.title`(对话标题)在契约 CHAT-1 中未定义。契约仅有 `customer_name`/`last_message`/`last_time`/`unread_count`。
|
||||
- **GAP-48**:`ChatHistoryItem.timestamp` vs 契约 `last_time` — 字段名不一致。
|
||||
- **GAP-49**:chat 页面接收 `customerId` 参数但 API 路径需要 `chatId`。前端需要一个"根据 customerId 查找或创建对话"的机制,或 API 支持 `customerId` 作为查询参数。
|
||||
- **GAP-50**:chat 页面还接收 `historyId`(从 chat-history 跳转)和 `coachId`(从 coach-detail 跳转),但 `loadMessages()` 仅使用 `customerId`。多种入口参数的路由逻辑未实现。
|
||||
- **GAP-51**:SSE 流式端点 `POST /api/xcx/chat/stream` 在契约中未定义(spec R3 已确认需补充)。
|
||||
|
||||
---
|
||||
|
||||
## 汇总:Gap 清单
|
||||
|
||||
### 一、架构级 Gap(影响多个页面)
|
||||
|
||||
| # | Gap | 严重度 | 影响页面 | 说明 |
|
||||
|---|-----|--------|---------|------|
|
||||
| GAP-01 | globalData.authUser 缺少 role/store_name/coach_level/avatar | 🟠 中 | task-list, performance, performance-records | 多个页面 Banner 需要这些字段,当前硬编码 |
|
||||
| GAP-04 | pin/unpin API 端点未在契约中定义 | 🔴 高 | task-list | api.ts 无对应函数,前端仅本地更新 |
|
||||
| GAP-05 | createNote 缺少 score 参数 | 🟠 中 | task-list, task-detail | 前端传 score 但 api.ts 签名无此字段 |
|
||||
| GAP-09/10 | task-detail 跳转 chat/customer-service-records 时传 taskId 而非 customerId | 🔴 高 | task-detail → chat/customer-service-records | TASK-2 响应需包含 customer_id |
|
||||
| GAP-31 | customer-detail 跳转时未传 customerId | 🔴 高 | customer-detail → chat/customer-service-records | 联调时目标页面无法获取客户 ID |
|
||||
| GAP-17 | performance 跳转 task-detail 传 customerName 而非 taskId | 🟠 中 | performance → task-detail | 参数不匹配 |
|
||||
|
||||
### 二、TASK-1 扩展 Gap(任务列表绩效卡片)
|
||||
|
||||
| # | Gap | 严重度 | 说明 |
|
||||
|---|-----|--------|------|
|
||||
| GAP-02 | performance 字段严重不足(需 15+ 字段,契约仅 4 个) | 🔴 高 | tierNodes、basicHours、bonusHours、currentTier、nextTierHours、tierCompleted、bonusMoney、incomeTrend、incomeTrendDir、prevMonth 等 |
|
||||
| GAP-03 | enrichTask 需要 lastVisitDays/balance/aiSuggestion | 🟠 中 | 任务卡片扩展字段 |
|
||||
|
||||
### 三、PERF-1 Gap(绩效概览)
|
||||
|
||||
| # | Gap | 严重度 | 说明 |
|
||||
|---|-----|--------|------|
|
||||
| GAP-12 | this_month_records 需要 DateGroup 分组结构 | 🔴 高 | 契约是扁平数组,前端需按日期分组 + timeRange/courseType/location |
|
||||
| GAP-13 | 收入档位数据完全未定义 | 🔴 高 | currentTier/nextTier/upgradeHoursNeeded/upgradeBonus |
|
||||
| GAP-14 | lastMonthIncome 未定义 | 🟠 中 | Banner 需展示上月收入 |
|
||||
| GAP-15 | incomeItems.desc 未定义 | 🟠 中 | 需费率×工时的拆分描述 |
|
||||
| GAP-16 | newCustomers/regularCustomers 缺少多个字段 | 🟠 中 | lastService/count/hours/income |
|
||||
| GAP-18 | 页面无月份切换功能 | 🟡 低 | F8 前端修复项 |
|
||||
|
||||
### 四、CUST-1 Gap(客户详情 — 最大 Gap 集中区)
|
||||
|
||||
| # | Gap | 严重度 | 说明 |
|
||||
|---|-----|--------|------|
|
||||
| GAP-23 | balance/consumption60d/idealInterval/daysSinceVisit 未定义 | 🔴 高 | Banner 核心展示字段 |
|
||||
| GAP-24 | aiInsight (summary + strategies) 未定义 | 🔴 高 | AI 洞察模块 |
|
||||
| GAP-25 | coachTasks(关联助教任务列表)未定义 | 🔴 高 | 复杂数据结构,需多表聚合 |
|
||||
| GAP-26 | favoriteCoaches(最亲密助教)未定义 | 🔴 高 | 需关系指数 + 服务统计 |
|
||||
| GAP-27 | 消费记录 coaches 子数组(助教服务明细)未定义 | 🔴 高 | 含 perfHours 折算工时 |
|
||||
| GAP-28 | 消费记录拆分字段(tableFee/foodAmount/totalOrigPrice 等)未定义 | 🟠 中 | 契约仅有 amount |
|
||||
| GAP-29 | 消费记录 type 枚举不一致 | 🟠 中 | 前端 table/shop/recharge vs 契约未明确 |
|
||||
| GAP-30 | 备注列表未定义 | 🟠 中 | 客户详情需展示备注 |
|
||||
|
||||
### 五、COACH-1 Gap(助教详情 — 第二大 Gap 集中区)
|
||||
|
||||
| # | Gap | 严重度 | 说明 |
|
||||
|---|-----|--------|------|
|
||||
| GAP-38 | performance/income/tierNodes 未定义 | 🔴 高 | 6 个绩效指标 + 本月/上月各 4 项收入 + 档位节点 |
|
||||
| GAP-39 | TopCustomer 缺少 heartEmoji/score/scoreColor/balance | 🔴 高 | 客户关系 TOP20 核心字段 |
|
||||
| GAP-40 | ServiceRecord.perfHours 未定义 | 🟠 中 | 折算工时 |
|
||||
| GAP-41 | HistoryMonth 完全未定义 | 🔴 高 | 历史月份统计(5+ 个月) |
|
||||
| GAP-42 | TaskItem.notes(含置顶标记)未定义 | 🟠 中 | 任务关联备注 |
|
||||
| GAP-43 | AbandonedTask.reason 未定义 | 🟡 低 | 放弃原因 |
|
||||
| GAP-44 | workYears/hireDate 未定义 | 🟡 低 | 助教基本信息 |
|
||||
|
||||
### 六、CHAT 模块 Gap
|
||||
|
||||
| # | Gap | 严重度 | 说明 |
|
||||
|---|-----|--------|------|
|
||||
| GAP-45 | ChatMessage.referenceCard 未定义 | 🔴 高 | 引用卡片(客户概览数据) |
|
||||
| GAP-46 | timestamp vs created_at 字段名不一致 | 🟠 中 | 前端用 timestamp,契约用 created_at |
|
||||
| GAP-47 | ChatHistoryItem.title 未定义 | 🟠 中 | 对话标题 |
|
||||
| GAP-48 | ChatHistoryItem.timestamp vs last_time 不一致 | 🟠 中 | 字段名不一致 |
|
||||
| GAP-49 | customerId → chatId 映射机制缺失 | 🔴 高 | 前端传 customerId 但 API 需要 chatId |
|
||||
| GAP-50 | 多入口参数路由逻辑未实现 | 🟠 中 | historyId/coachId/customerId 三种入口 |
|
||||
| GAP-51 | SSE 流式端点未在契约中定义 | 🟠 中 | spec 已确认需补充 |
|
||||
|
||||
### 七、其他 Gap
|
||||
|
||||
| # | Gap | 严重度 | 说明 |
|
||||
|---|-----|--------|------|
|
||||
| GAP-06 | 维客线索 tag 格式不一致(含换行符) | 🟡 低 | 需统一 |
|
||||
| GAP-07 | 维客线索 source 格式不一致 | 🟡 低 | mock 用 `'By:系统'`,类型定义用 `'manual'` |
|
||||
| GAP-08 | aiAnalysis/talkingPoints 的 ai_cache cache_type 未明确 | 🟠 中 | 需明确映射 |
|
||||
| GAP-11 | storageLevel/relationLevel 计算逻辑未明确 | 🟡 低 | 前端本地计算 |
|
||||
| GAP-19 | avatarChar/avatarColor 是否后端返回 | 🟡 低 | 建议前端自行计算 |
|
||||
| GAP-20 | courseTypeClass 枚举值前缀不统一 | 🟡 低 | `tag-basic` vs `basic` |
|
||||
| GAP-21 | 月份切换未重置分页 | 🟡 低 | F9 前端 bug |
|
||||
| GAP-22 | performance-records Banner 字段来源 | 🟡 低 | 依赖 GAP-01 |
|
||||
| GAP-32 | recordType/isEstimate 未定义 | 🟡 低 | CUST-2 扩展字段 |
|
||||
| GAP-33 | customerPhoneFull 未定义 | 🟡 低 | CUST-2 需完整手机号 |
|
||||
| GAP-34 | totalServiceCount 未定义 | 🟡 低 | CUST-2 累计次数 |
|
||||
| GAP-35 | 月份切换本地筛选需改 API 请求 | 🟡 低 | F10 前端修复 |
|
||||
| GAP-36 | notes 页无触底加载 | 🟡 低 | F11 前端修复 |
|
||||
| GAP-37 | Note.tagType 'system' 类型语义 | 🟡 低 | 需确认 |
|
||||
|
||||
---
|
||||
|
||||
### 统计
|
||||
|
||||
| 严重度 | 数量 | 说明 |
|
||||
|--------|------|------|
|
||||
| 🔴 高 | 17 | 阻塞联调,必须在后端开发前解决 |
|
||||
| 🟠 中 | 19 | 影响功能完整性,需在联调前补充 |
|
||||
| 🟡 低 | 15 | 可联调后修复或前端自行处理 |
|
||||
| **合计** | **51** | |
|
||||
|
||||
### 最高优先级修复建议
|
||||
|
||||
1. **契约大幅扩展 CUST-1**:客户详情是 Gap 最集中的页面(GAP-23~30),需新增 `balance`、`consumption60d`、`idealInterval`、`daysSinceVisit`、`aiInsight`、`coachTasks`、`favoriteCoaches`、消费记录拆分字段、备注列表。
|
||||
2. **契约大幅扩展 COACH-1**:助教详情是第二大 Gap 集中区(GAP-38~44),需新增 `performance`(6 指标)、`income`(8 项)、`tierNodes`、`TopCustomer` 扩展字段、`HistoryMonth`、`ServiceRecord.perfHours`。
|
||||
3. **契约扩展 PERF-1**:绩效概览需要 DateGroup 分组结构(GAP-12)和收入档位数据(GAP-13)。
|
||||
4. **契约扩展 TASK-1 performance**:绩效卡片需要 15+ 字段(GAP-02)。
|
||||
5. **修复 customerId/taskId 混淆**:TASK-2 响应需包含 `customer_id`(GAP-09/10),customer-detail 跳转需传参(GAP-31)。
|
||||
6. **补充 CHAT 模块**:referenceCard(GAP-45)、customerId→chatId 映射(GAP-49)、SSE 端点定义(GAP-51)。
|
||||
Reference in New Issue
Block a user