feat: batch update - gift card breakdown spec, backend APIs, miniprogram pages, ETL finance recharge, docs & migrations
This commit is contained in:
1
.kiro/specs/rns1-customer-coach-api/.config.kiro
Normal file
1
.kiro/specs/rns1-customer-coach-api/.config.kiro
Normal file
@@ -0,0 +1 @@
|
||||
{"specId": "b2f4e8a1-3c7d-4f9b-a6e2-8d5c1b3f7a9e", "workflowType": "requirements-first", "specType": "feature"}
|
||||
1068
.kiro/specs/rns1-customer-coach-api/design.md
Normal file
1068
.kiro/specs/rns1-customer-coach-api/design.md
Normal file
File diff suppressed because it is too large
Load Diff
253
.kiro/specs/rns1-customer-coach-api/requirements.md
Normal file
253
.kiro/specs/rns1-customer-coach-api/requirements.md
Normal file
@@ -0,0 +1,253 @@
|
||||
# 需求文档 — RNS1.2:客户与助教接口
|
||||
|
||||
## 简介
|
||||
|
||||
RNS1.2 是 NS1 小程序后端 API 补全项目的第三个子 spec,负责实现客户详情(CUST-1)、客户服务记录(CUST-2)、助教详情(COACH-1)3 个接口。这三个接口覆盖客户视角和助教视角的详情查看需求,是走查报告中 Gap 最集中的区域(GAP-23~30、GAP-32~35、GAP-38~44),数据结构最为复杂。
|
||||
|
||||
### 依赖
|
||||
|
||||
- RNS1.0(基础设施与契约重写)已完成:全局响应包装中间件(`ResponseWrapperMiddleware`)、camelCase 转换(`CamelModel`)、重写后的 API 契约
|
||||
- RNS1.1(任务与绩效接口)可并行开发,无直接依赖
|
||||
- 后端已有 `fdw_queries.py`(FDW 查询集中封装)、`task_manager.py`、`note_service.py`
|
||||
- 前端已有 13 个页面(P5.2 交付),当前使用 mock 数据
|
||||
|
||||
### 来源文档
|
||||
|
||||
- `docs/prd/Neo_Specs/RNS1-split-plan.md` — 拆分计划主文档(RNS1.2 章节)
|
||||
- `docs/miniprogram-dev/API-contract.md` — API 契约(CUST-1、CUST-2、COACH-1 完整定义)
|
||||
- `docs/reports/storyboard-walkthrough-assistant-view.md` — 助教视角走查报告(GAP-23~30、GAP-32~35、GAP-38~44)
|
||||
- `docs/reports/miniprogram-storyboard-walkthrough-gaps.md` — 管理层视角走查报告(G4、G5)
|
||||
- `docs/reports/DWD-DOC/` — 金额口径与字段语义权威标杆文档
|
||||
- `docs/architecture/backend-architecture.md` — 后端架构文档
|
||||
|
||||
## 术语表
|
||||
|
||||
- **Backend**:FastAPI 后端应用,位于 `apps/backend/`
|
||||
- **Miniprogram**:微信小程序前端应用,位于 `apps/miniprogram/`
|
||||
- **CUST_1_API**:客户详情接口 `GET /api/xcx/customers/{customerId}`,返回客户完整详情(Banner 概览、AI 洞察、关联助教任务、最亲密助教、消费记录、备注)
|
||||
- **CUST_2_API**:客户服务记录接口 `GET /api/xcx/customers/{customerId}/records`,返回按月查询的服务记录列表
|
||||
- **COACH_1_API**:助教详情接口 `GET /api/xcx/coaches/{coachId}`,返回助教完整详情(绩效、收入、档位、TOP 客户、历史月份、任务分组、备注)
|
||||
- **FDW**:PostgreSQL Foreign Data Wrapper,后端通过直连 ETL 库查询 `app.v_*` RLS 视图
|
||||
- **items_sum**:DWD-DOC 强制使用的消费金额口径,= `table_charge_money + goods_money + assistant_pd_money + assistant_cx_money + electricity_money`
|
||||
- **assistant_pd_money**:助教陪打费用(基础课),DWD-DOC 强制规则 2 要求的拆分字段
|
||||
- **assistant_cx_money**:助教超休费用(激励课),DWD-DOC 强制规则 2 要求的拆分字段
|
||||
- **v_dim_member**:ETL RLS 视图,提供会员基本信息(nickname、mobile),通过 `member_id` 关联,取 `scd2_is_current=1`
|
||||
- **v_dim_member_card_account**:ETL RLS 视图,提供会员卡余额,通过 `tenant_member_id` 关联,取 `scd2_is_current=1`
|
||||
- **v_dwd_assistant_service_log**:ETL RLS 视图,提供助教服务记录明细(基于 `dwd_assistant_service_log` 基表,废单字段为 `is_delete`)
|
||||
- **v_dws_member_assistant_relation_index**:ETL RLS 视图,提供会员与助教的关系指数
|
||||
- **v_dws_member_consumption_summary**:ETL RLS 视图,提供会员消费汇总
|
||||
- **v_dws_assistant_salary_calc**:ETL RLS 视图,提供助教绩效/档位/收入数据
|
||||
- **v_dim_assistant**:ETL RLS 视图,提供助教基本信息
|
||||
- **v_dwd_table_fee_log**:ETL RLS 视图,提供台费明细
|
||||
- **ai_cache**:业务库 `biz.ai_cache` 表,按 `cache_type` 存储不同类型的 AI 分析缓存
|
||||
- **coach_tasks**:业务库 `biz.coach_tasks` 表,存储助教任务分配与状态
|
||||
- **member_retention_clue**:业务库 `public.member_retention_clue` 表,存储维客线索
|
||||
- **user_assistant_binding**:认证库 `auth.user_assistant_binding` 表,映射小程序用户与助教身份
|
||||
- **settle_type**:结算类型字段,正向交易取 `IN (1, 3)`
|
||||
- **is_delete**:RLS 视图中的废单标记字段(整数类型,0=正常),对应 design.md 中的 `is_trash`
|
||||
|
||||
## 需求
|
||||
|
||||
### 需求 1:实现 CUST-1 客户详情 Banner 概览(T2-1 基础部分)
|
||||
|
||||
**用户故事:** 作为管理者或助教,我希望在客户详情页顶部看到客户的基本信息和关键指标(余额、近期消费、到店间隔、距上次到店天数),以便快速评估客户价值和活跃度。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE CUST_1_API SHALL 返回客户基础信息字段:`id`(客户唯一 ID)、`name`(客户姓名)、`phone`(脱敏手机号,如 `"139****5678"`)、`phoneFull`(完整手机号)、`avatar`(头像 URL)、`memberLevel`(会员等级)、`relationIndex`(关系指数)、`tags`(客户标签列表)
|
||||
2. THE CUST_1_API SHALL 通过 `member_id` LEFT JOIN `v_dim_member`(取 `scd2_is_current=1`)获取 `name`(`nickname` 字段)和 `phone`/`phoneFull`(`mobile` 字段),禁止使用 `settlement_head.member_phone`(DQ-6)
|
||||
3. THE CUST_1_API SHALL 通过 `member_id` LEFT JOIN `v_dim_member_card_account`(`tenant_member_id=member_id`,取 `scd2_is_current=1`)获取 `memberLevel`,禁止使用 `member_card_type_name`(DQ-7)
|
||||
4. THE CUST_1_API SHALL 返回 Banner 概览字段:`balance`(客户余额,元)、`consumption60d`(近 60 天消费金额,元)、`idealInterval`(理想到店间隔,天)、`daysSinceVisit`(距上次到店天数)
|
||||
5. THE CUST_1_API SHALL 使用 `items_sum` 口径计算 `balance` 和 `consumption60d`(DWD-DOC 强制规则 1),禁止使用 `consume_money`
|
||||
6. THE CUST_1_API SHALL 从 `v_dwd_assistant_service_log` 查询客户最后到店日期(`MAX(create_time)`,过滤 `is_delete=0`),计算 `daysSinceVisit`(当前日期与最后到店日期的天数差)
|
||||
7. IF 某个 Banner 字段的数据源查询失败或无数据,THEN THE CUST_1_API SHALL 对该字段返回 `null`,不影响其他字段和整体响应
|
||||
|
||||
### 需求 2:实现 CUST-1 AI 洞察模块(T2-1 AI 部分)
|
||||
|
||||
**用户故事:** 作为管理者或助教,我希望在客户详情页看到 AI 生成的客户分析洞察和策略建议,以便制定针对性的服务策略。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE CUST_1_API SHALL 返回 `aiInsight` 字段,包含 `summary`(AI 分析摘要文本)和 `strategies`(策略建议列表,每项含 `color` 和 `text`)
|
||||
2. THE CUST_1_API SHALL 从 `biz.ai_cache` 查询 `cache_type='app4_analysis'` 且 `target_id=customerId` 的缓存记录,解析 `cache_value` JSON 生成 `aiInsight` 数据
|
||||
3. IF `biz.ai_cache` 中无对应缓存记录,THEN THE CUST_1_API SHALL 返回 `aiInsight: { summary: "", strategies: [] }`
|
||||
|
||||
### 需求 3:实现 CUST-1 维客线索与备注(T2-1 线索与备注部分)
|
||||
|
||||
**用户故事:** 作为管理者或助教,我希望在客户详情页看到维客线索和历史备注,以便了解客户的留存风险和过往沟通记录。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE CUST_1_API SHALL 返回 `retentionClues` 字段(维客线索列表),从 `public.member_retention_clue` 查询,按 `created_at` 倒序排列,格式与 TASK-2 一致
|
||||
2. THE CUST_1_API SHALL 返回 `notes` 字段(备注列表),从 `biz.notes` 查询 `target_type='member'` 且 `target_id=customerId` 的记录,每项含 `id`、`tagLabel`、`createdAt`、`content`,按 `created_at` 倒序排列,最多返回 20 条
|
||||
|
||||
### 需求 4:实现 CUST-1 消费记录嵌套结构(T2-1 消费记录部分)
|
||||
|
||||
**用户故事:** 作为管理者或助教,我希望在客户详情页看到消费记录的完整拆分(台费、酒水、助教服务明细),以便分析客户的消费构成和偏好。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE CUST_1_API SHALL 返回 `consumptionRecords` 字段(消费记录列表),每条记录包含嵌套结构:`id`、`type`(`table`/`shop`/`recharge`)、`date`、`tableName`、`startTime`、`endTime`、`duration`、`tableFee`、`tableOrigPrice`、`coaches`(助教服务子数组)、`foodAmount`、`foodOrigPrice`、`totalAmount`、`totalOrigPrice`、`payMethod`、`rechargeAmount`
|
||||
2. THE CUST_1_API SHALL 为每条消费记录的 `coaches` 子数组返回助教服务明细,每项含 `name`、`level`、`levelColor`、`courseType`(`"基础课"` 或 `"激励课"`)、`hours`(服务时长)、`perfHours`(折算工时,可选)、`fee`(服务费用)
|
||||
3. THE CUST_1_API SHALL 对 `coaches` 子数组中的 `fee` 字段使用 `assistant_pd_money`(基础课)和 `assistant_cx_money`(激励课)拆分(DWD-DOC 强制规则 2),禁止使用 `service_fee`
|
||||
4. THE CUST_1_API SHALL 对 `totalAmount` 使用 `items_sum` 口径(DWD-DOC 强制规则 1),对 `tableFee` 使用 `table_charge_money`,对 `foodAmount` 使用 `goods_money`
|
||||
5. THE CUST_1_API SHALL 仅查询正向交易记录(`settle_type IN (1, 3)`)
|
||||
6. THE CUST_1_API SHALL 使用 `v_dwd_assistant_service_log` 的 `is_delete=0` 排除废单记录
|
||||
|
||||
### 需求 5:实现 CUST-1 coachTasks 模块(T2-2)
|
||||
|
||||
**用户故事:** 作为管理者或助教,我希望在客户详情页看到所有关联助教的任务信息和近期服务统计,以便了解该客户被哪些助教跟进、服务频率和质量如何。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE CUST_1_API SHALL 返回 `coachTasks` 字段(关联助教任务列表),从 `biz.coach_tasks` 查询该客户(`member_id=customerId`)的所有任务记录
|
||||
2. THE CUST_1_API SHALL 为每位关联助教返回以下字段:`name`(助教姓名)、`level`(助教等级:`star`/`senior`/`middle`/`junior`)、`levelColor`(等级对应颜色)、`taskType`(任务类型,如 `"回访"`、`"召回"`)、`taskColor`(任务类型对应颜色)、`bgClass`(背景样式类)、`status`(任务状态)、`lastService`(最后服务日期)、`metrics`(指标列表)
|
||||
3. THE CUST_1_API SHALL 为每位助教的 `metrics` 返回近 60 天统计:服务次数、总时长、次均时长,从 `v_dwd_assistant_service_log` 按助教+客户聚合近 60 天数据(过滤 `is_delete=0`)
|
||||
4. THE CUST_1_API SHALL 从 `v_dws_assistant_salary_calc` 获取助教等级(`assistant_level_name`),从 `v_dim_member` 获取助教姓名(DQ-6)
|
||||
|
||||
### 需求 6:实现 CUST-1 favoriteCoaches 模块(T2-3)
|
||||
|
||||
**用户故事:** 作为管理者或助教,我希望在客户详情页看到该客户最亲密的助教排名和详细服务统计,以便了解客户的助教偏好和关系深度。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE CUST_1_API SHALL 返回 `favoriteCoaches` 字段(最亲密助教列表),从 `v_dws_member_assistant_relation_index` 获取关系指数,按关系指数降序排列
|
||||
2. THE CUST_1_API SHALL 为每位亲密助教返回:`emoji`(亲密度 emoji)、`name`(助教姓名)、`relationIndex`(关系指数,如 `"0.92"`)、`indexColor`(关系指数对应颜色)、`bgClass`(背景样式类)、`stats`(统计指标列表)
|
||||
3. THE CUST_1_API SHALL 为 `stats` 返回 4 项指标:基础课时(对应 `assistant_pd_money`)、激励课时(对应 `assistant_cx_money`)、上课次数、充值金额,使用 DWD-DOC 强制规则 2 拆分助教费用
|
||||
4. THE CUST_1_API SHALL 根据关系指数(`rs_display`,0-10 范围)阈值映射 `emoji`(P6 AC3 四级映射):`> 8.5` → `"💖"`,`> 7` → `"🧡"`,`> 5` → `"💛"`,`≤ 5` → `"💙"`,复用后端 `compute_heart_icon()` 函数
|
||||
|
||||
### 需求 7:实现 CUST-2 客户服务记录(T2-4)
|
||||
|
||||
**用户故事:** 作为管理者或助教,我希望按月查看客户的服务记录(替代前端全量加载本地过滤),以便高效浏览大量历史数据并查看月度统计汇总。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE CUST_2_API SHALL 接受 `year`、`month`、`table`(可选,台桌筛选)查询参数,返回指定月份的客户服务记录
|
||||
2. THE CUST_2_API SHALL 返回客户基础信息:`customerName`(客户姓名)、`customerPhone`(脱敏手机号)、`customerPhoneFull`(完整手机号)、`relationIndex`(关系指数)、`tables`(可选台桌列表)
|
||||
3. THE CUST_2_API SHALL 通过 `member_id` LEFT JOIN `v_dim_member`(取 `scd2_is_current=1`)获取 `customerName` 和 `customerPhone`/`customerPhoneFull`(DQ-6)
|
||||
4. THE CUST_2_API SHALL 返回 `totalServiceCount`(累计服务总次数,跨所有月份)
|
||||
5. THE CUST_2_API SHALL 为每条服务记录返回 `recordType`(`course` 或 `recharge`)和 `isEstimate`(是否预估数据,boolean)字段
|
||||
6. THE CUST_2_API SHALL 返回月度统计汇总:`monthCount`(当月服务次数)和 `monthHours`(当月总工时)
|
||||
7. THE CUST_2_API SHALL 使用 `items_sum` 口径计算服务记录中的 `income` 字段(DWD-DOC 强制规则 1)
|
||||
8. THE CUST_2_API SHALL 使用 `is_delete=0` 排除废单记录
|
||||
9. THE CUST_2_API SHALL 按 `create_time` 倒序排列服务记录,返回 `hasMore` 标记指示是否有更多数据
|
||||
|
||||
|
||||
### 需求 8:实现 COACH-1 助教详情基础信息与绩效(T2-5 基础部分)
|
||||
|
||||
**用户故事:** 作为管理者,我希望在助教详情页看到助教的基本信息和 6 项绩效指标,以便快速评估助教的工作表现和产出。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE COACH_1_API SHALL 返回助教基础信息字段:`id`、`name`、`avatar`、`level`(初级/中级/高级/星级)、`skills`(技能标签列表)、`workYears`(工龄,年)、`customerCount`(客户数)、`hireDate`(入职日期)
|
||||
2. THE COACH_1_API SHALL 从 `v_dim_assistant` 获取助教基本信息,从 `v_dws_assistant_salary_calc` 获取等级(`assistant_level_name`)
|
||||
3. THE COACH_1_API SHALL 返回 `performance` 字段,包含 6 项绩效指标:`monthlyHours`(本月定档工时)、`monthlySalary`(本月工资预估)、`customerBalance`(客源储值余额合计)、`tasksCompleted`(本月任务完成数)、`perfCurrent`(当前绩效值)、`perfTarget`(绩效目标值)
|
||||
4. THE COACH_1_API SHALL 从 `v_dws_assistant_salary_calc` 查询 `monthlyHours`(`effective_hours`)和 `monthlySalary`(`gross_salary`),使用 `items_sum` 口径(DWD-DOC 强制规则 1)
|
||||
5. THE COACH_1_API SHALL 从 `biz.coach_tasks` 查询 `tasksCompleted`(当月 `status='completed'` 的任务数)
|
||||
|
||||
### 需求 9:实现 COACH-1 收入明细与档位节点(T2-5 收入部分)
|
||||
|
||||
**用户故事:** 作为管理者,我希望在助教详情页看到本月和上月的收入明细拆分(基础课时费、激励课时费、充值提成、酒水提成)以及档位进度节点,以便了解助教的收入构成和升档进度。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE COACH_1_API SHALL 返回 `income` 字段,包含 `thisMonth` 和 `lastMonth` 两个子数组,各含 4 项收入分类:基础课时费(`assistant_pd_money`)、激励课时费(`assistant_cx_money`)、充值提成、酒水提成
|
||||
2. THE COACH_1_API SHALL 使用 `assistant_pd_money`(基础课/陪打)和 `assistant_cx_money`(激励课/超休)拆分助教费用(DWD-DOC 强制规则 2),禁止使用 `service_fee`
|
||||
3. THE COACH_1_API SHALL 从 `v_dws_assistant_salary_calc` 分别查询当月和上月的收入数据(`salary_month` 字段为 date 类型,存储为 `YYYY-MM-01`)
|
||||
4. THE COACH_1_API SHALL 返回 `tierNodes` 字段(档位节点数组,如 `[0, 100, 130, 160, 190, 220]`),供前端绩效进度条组件使用
|
||||
|
||||
### 需求 10:实现 COACH-1 TOP 客户与近期服务记录(T2-5 客户部分)
|
||||
|
||||
**用户故事:** 作为管理者,我希望在助教详情页看到该助教的 TOP 客户排名(含关系指数、余额、消费)和近期服务明细(含折算工时),以便评估助教的客户关系质量和服务产出。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE COACH_1_API SHALL 返回 `topCustomers` 字段(TOP 客户列表,最多 20 条),每项含扩展字段:`id`、`name`、`initial`(姓氏首字)、`avatarGradient`(头像渐变色)、`heartEmoji`(关系 emoji,P6 AC3 四级映射:💖/🧡/💛/💙)、`relationScore`(关系指数,0-10)、`scoreColor`(分数颜色)、`serviceCount`(服务次数)、`balance`(余额,格式化)、`consume`(消费总额,格式化)
|
||||
2. THE COACH_1_API SHALL 对 `topCustomers[].consume` 使用 `items_sum` 口径(DWD-DOC 强制规则 1)
|
||||
3. THE COACH_1_API SHALL 对 `topCustomers[].balance` 通过 `member_id` LEFT JOIN `v_dim_member_card_account`(取 `scd2_is_current=1`)获取(DQ-7)
|
||||
4. THE COACH_1_API SHALL 通过 `member_id` LEFT JOIN `v_dim_member`(取 `scd2_is_current=1`)获取客户姓名(DQ-6)
|
||||
5. THE COACH_1_API SHALL 返回 `serviceRecords` 字段(近期服务记录列表),每项含 `customerId`、`customerName`、`initial`、`avatarGradient`、`type`(课程类型)、`typeClass`(样式类)、`table`(台桌名)、`duration`(时长)、`income`(收入)、`date`(日期时间)、`perfHours`(折算工时,可选)
|
||||
6. THE COACH_1_API SHALL 对 `serviceRecords[].income` 使用 `ledger_amount`(对应 `items_sum` 口径,DWD-DOC 强制规则 1)
|
||||
7. THE COACH_1_API SHALL 使用 `is_delete=0` 排除废单记录
|
||||
|
||||
### 需求 11:实现 COACH-1 任务分组与备注(T2-5 任务部分)
|
||||
|
||||
**用户故事:** 作为管理者,我希望在助教详情页看到该助教的任务按状态分组展示(进行中/已过期/已放弃),每个任务含关联备注和放弃原因,以便全面了解助教的任务执行情况。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE COACH_1_API SHALL 返回任务分为三组:`visibleTasks`(active 状态任务)、`hiddenTasks`(inactive 状态任务)、`abandonedTasks`(abandoned 状态任务),从 `biz.coach_tasks` 查询该助教的所有任务
|
||||
2. THE COACH_1_API SHALL 为 `visibleTasks` 和 `hiddenTasks` 每项返回:`typeLabel`(任务类型标签)、`typeClass`(样式类)、`customerName`(客户姓名)、`customerId`(客户 ID,用于跳转)、`noteCount`(备注数量)、`pinned`(是否置顶)、`notes`(备注列表,可选,每项含 `pinned`、`text`、`date`)
|
||||
3. THE COACH_1_API SHALL 为 `abandonedTasks` 每项返回:`customerName`(客户姓名)、`reason`(放弃原因,来自 `coach_tasks.abandon_reason`)
|
||||
4. THE COACH_1_API SHALL 从 `biz.notes` 查询每个任务关联的备注(`task_id` 关联),按 `created_at` 倒序排列
|
||||
5. THE COACH_1_API SHALL 返回 `notes` 字段(助教相关备注列表),每项含 `id`、`content`、`timestamp`、`score`、`customerName`、`tagLabel`、`createdAt`,按 `created_at` 倒序排列,最多返回 20 条
|
||||
|
||||
### 需求 12:实现 COACH-1 historyMonths 模块(T2-6)
|
||||
|
||||
**用户故事:** 作为管理者,我希望在助教详情页看到该助教最近 5 个以上月份的历史统计(客户数、工时、工资、回访/召回完成数),以便追踪助教的长期表现趋势。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE COACH_1_API SHALL 返回 `historyMonths` 字段(历史月份统计列表),包含最近 5 个以上月份的汇总数据,第一条为本月
|
||||
2. THE COACH_1_API SHALL 为每个月份返回:`month`(月份标签,如 `"本月"`、`"上月"`、`"4月"`)、`estimated`(是否为预估数据,boolean)、`customers`(客户数,格式化,如 `"22人"`)、`hours`(工时,格式化,如 `"87.5h"`)、`salary`(工资,格式化,如 `"¥6,950"`)、`callbackDone`(回访任务完成数)、`recallDone`(召回任务完成数)
|
||||
3. THE COACH_1_API SHALL 从 `v_dws_assistant_salary_calc` 查询各月的工时(`effective_hours`)和工资(`gross_salary`)数据
|
||||
4. THE COACH_1_API SHALL 从 `biz.coach_tasks` 查询各月的回访完成数(`task_type='follow_up_visit' AND status='completed'`)和召回完成数(`task_type IN ('high_priority_recall', 'priority_recall') AND status='completed'`)
|
||||
5. THE COACH_1_API SHALL 将本月标记为 `estimated: true`(预估数据),历史月份标记为 `estimated: false`
|
||||
6. THE COACH_1_API SHALL 从 `v_dwd_assistant_service_log` 按月统计不重复的 `tenant_member_id` 数量作为客户数(过滤 `is_delete=0`)
|
||||
|
||||
### 需求 13:全局约束与数据隔离
|
||||
|
||||
**用户故事:** 作为系统管理员,我希望所有客户和助教接口都遵循统一的权限控制、数据隔离和数据质量规则,以确保数据安全和口径一致。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
#### 13.1 权限与认证
|
||||
|
||||
1. THE Backend SHALL 对所有 RNS1.2 接口(CUST_1_API、CUST_2_API、COACH_1_API)执行 `require_approved()` 权限检查,确保用户状态为 `approved`
|
||||
2. THE Backend SHALL 对所有 RNS1.2 接口通过 `SET LOCAL app.current_site_id` 实现门店级数据隔离(FDW 查询通过 `_fdw_context` 上下文管理器统一执行)
|
||||
|
||||
#### 13.2 DWD-DOC 强制规则
|
||||
|
||||
3. THE Backend SHALL 对所有涉及金额的字段统一使用 `items_sum` 口径(DWD-DOC 强制规则 1),禁止使用 `consume_money`
|
||||
4. THE Backend SHALL 对所有涉及助教费用的字段使用 `assistant_pd_money`(陪打/基础课)+ `assistant_cx_money`(超休/激励课)拆分(DWD-DOC 强制规则 2),禁止使用 `service_fee`
|
||||
5. THE Backend SHALL 对所有涉及会员信息的查询通过 `member_id` LEFT JOIN `v_dim_member`(取 `scd2_is_current=1`)获取姓名和手机号(DWD-DOC 强制规则 DQ-6),禁止直接使用 `settlement_head.member_phone` 或 `member_name`
|
||||
6. THE Backend SHALL 对所有涉及会员卡信息的查询通过 `member_id` LEFT JOIN `v_dim_member_card_account`(`tenant_member_id=member_id`,取 `scd2_is_current=1`)获取(DWD-DOC 强制规则 DQ-7),禁止使用 `member_card_type_name`
|
||||
7. THE Backend SHALL 使用 `v_dwd_assistant_service_log` 的 `is_delete=0` 排除废单记录,禁止使用已废弃的 `dwd_assistant_trash_event` 表
|
||||
|
||||
#### 13.3 优雅降级
|
||||
|
||||
8. IF 某个扩展模块(`aiInsight`/`coachTasks`/`favoriteCoaches`/`historyMonths`)的数据源查询失败,THEN THE Backend SHALL 对该模块返回空默认值(空数组或空对象),不影响其他模块和整体响应
|
||||
9. THE Backend SHALL 对所有 FDW 查询异常进行捕获和日志记录,返回降级响应而非 HTTP 500
|
||||
|
||||
#### 13.4 列名映射
|
||||
|
||||
10. THE Backend SHALL 在 SQL 中使用 AS 别名将 RLS 视图原始列名转换为代码语义名(如 `site_assistant_id AS assistant_id`、`tenant_member_id AS member_id`、`create_time AS settle_time`、`ledger_amount AS income`、`income_seconds / 3600.0 AS service_hours`),统一在 `fdw_queries.py` 中封装
|
||||
|
||||
### 需求 14:正确性属性(Property-Based Testing)
|
||||
|
||||
**用户故事:** 作为开发者,我希望通过属性测试验证接口的数据一致性和业务规则正确性,以便在开发阶段发现口径错误和数据异常。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
#### 14.1 金额口径不变量
|
||||
|
||||
1. FOR ALL 消费记录,THE CUST_1_API 返回的 `totalAmount` SHALL 等于 `tableFee + foodAmount + SUM(coaches[].fee)`(在浮点精度范围内),验证 `items_sum` 口径拆分的一致性
|
||||
2. FOR ALL 助教费用拆分,`coaches[].fee` 中基础课记录 SHALL 对应 `assistant_pd_money`,激励课记录 SHALL 对应 `assistant_cx_money`,两者之和 SHALL 等于该结算单的助教费用总额
|
||||
|
||||
#### 14.2 数据隔离不变量
|
||||
|
||||
3. FOR ALL CUST_1_API 响应中的 `coachTasks`,每条任务的 `member_id` SHALL 等于请求路径中的 `customerId`,验证客户-任务关联的正确性
|
||||
4. FOR ALL COACH_1_API 响应中的 `serviceRecords`,每条记录的 `assistant_id` SHALL 等于请求路径中的 `coachId`,验证助教数据隔离
|
||||
|
||||
#### 14.3 排序与分组不变量
|
||||
|
||||
5. FOR ALL CUST_1_API 响应中的 `favoriteCoaches`,列表 SHALL 按 `relationIndex` 降序排列(前一项的 `relationIndex` ≥ 后一项的 `relationIndex`)
|
||||
6. FOR ALL COACH_1_API 响应中的 `historyMonths`,列表 SHALL 按月份降序排列(最近月份在前),且第一条的 `estimated` SHALL 为 `true`
|
||||
|
||||
#### 14.4 幂等性
|
||||
|
||||
7. FOR ALL 相同参数的 CUST_2_API 请求(相同 `customerId`、`year`、`month`),在数据未变更的情况下,两次请求 SHALL 返回相同的 `monthCount` 和 `monthHours` 值
|
||||
|
||||
#### 14.5 废单排除一致性
|
||||
|
||||
8. FOR ALL 服务记录查询,返回的记录集合中 SHALL 不包含 `is_delete != 0` 的记录,验证废单排除规则在所有接口中一致执行
|
||||
271
.kiro/specs/rns1-customer-coach-api/tasks.md
Normal file
271
.kiro/specs/rns1-customer-coach-api/tasks.md
Normal file
@@ -0,0 +1,271 @@
|
||||
# Implementation Plan: RNS1.2 客户与助教接口
|
||||
|
||||
## Overview
|
||||
|
||||
基于 design.md 架构,按 T2-1 ~ T2-6 任务结构增量实现 CUST-1、CUST-2、COACH-1 三个接口。先扩展 FDW 查询层,再逐步构建 service → router → 集成测试 → 属性测试。所有金额使用 `items_sum` 口径,助教费用使用 `assistant_pd_money` + `assistant_cx_money` 拆分,会员信息通过 `member_id` JOIN `v_dim_member`。
|
||||
|
||||
## Tasks
|
||||
|
||||
- [x] 1. Pydantic Schema 定义与项目结构搭建
|
||||
- [x] 1.1 创建 `apps/backend/app/schemas/xcx_customers.py`,定义 CUST-1 和 CUST-2 所有响应模型
|
||||
- `CustomerDetailResponse`、`CustomerRecordsResponse` 及所有嵌套模型(`AiInsight`、`AiStrategy`、`MetricItem`、`CoachTask`、`FavoriteCoach`、`CoachServiceItem`、`ConsumptionRecord`、`RetentionClue`、`CustomerNote`、`ServiceRecordItem`)
|
||||
- 所有模型继承 `CamelModel`,确保 camelCase 序列化
|
||||
- _Requirements: 1.1-1.7, 2.1-2.3, 3.1-3.2, 4.1-4.6, 5.1-5.4, 6.1-6.4, 7.1-7.9_
|
||||
- [x] 1.2 创建 `apps/backend/app/schemas/xcx_coaches.py`,定义 COACH-1 所有响应模型
|
||||
- `CoachDetailResponse` 及所有嵌套模型(`PerformanceMetrics`、`IncomeItem`、`IncomeSection`、`CoachTaskItem`、`AbandonedTask`、`TopCustomer`、`CoachServiceRecord`、`HistoryMonth`、`CoachNoteItem`)
|
||||
- _Requirements: 8.1-8.5, 9.1-9.4, 10.1-10.7, 11.1-11.5, 12.1-12.6_
|
||||
|
||||
- [x] 2. FDW 查询层扩展(T2-1 基础)
|
||||
- [x] 2.1 在 `apps/backend/app/services/fdw_queries.py` 新增客户相关查询函数
|
||||
- `get_consumption_60d(conn, site_id, member_id)` — 近 60 天消费,使用 `ledger_amount`(items_sum),过滤 `is_delete=0`
|
||||
- `get_relation_index(conn, site_id, member_id)` — 关系指数列表,来源 `v_dws_member_assistant_relation_index`,按 `relation_index` 降序
|
||||
- `get_consumption_records(conn, site_id, member_id, limit, offset)` — 消费记录嵌套查询,JOIN `v_dim_assistant`,过滤 `settle_type IN (1,3)` + `is_delete=0`
|
||||
- `get_total_service_count(conn, site_id, member_id)` — 累计服务总次数
|
||||
- `get_coach_60d_stats(conn, site_id, assistant_id, member_id)` — 特定助教对特定客户近 60 天统计
|
||||
- 所有 SQL 使用 AS 别名映射(design.md 列名映射表)
|
||||
- _Requirements: 1.5, 1.6, 4.3-4.6, 5.3, 6.1, 7.4, 7.7-7.8, 13.2-13.7, 13.10_
|
||||
- [x] 2.2 在 `apps/backend/app/services/fdw_queries.py` 新增助教相关查询函数
|
||||
- `get_assistant_info(conn, site_id, assistant_id)` — 助教基本信息,来源 `v_dim_assistant`
|
||||
- `get_salary_calc_multi_months(conn, site_id, assistant_id, months)` — 批量多月绩效数据
|
||||
- `get_monthly_customer_count(conn, site_id, assistant_id, months)` — 各月不重复客户数,`COUNT(DISTINCT tenant_member_id)`,过滤 `is_delete=0`
|
||||
- `get_coach_top_customers(conn, site_id, assistant_id, limit=20)` — TOP 客户,JOIN `v_dim_member`(DQ-6)+ `v_dim_member_card_account`(DQ-7),consume 使用 `ledger_amount`
|
||||
- `get_customer_service_records(conn, site_id, member_id, year, month, table, limit, offset)` — 按月服务记录 + 月度统计汇总
|
||||
- _Requirements: 8.2, 8.4, 9.3, 10.2-10.4, 10.6-10.7, 12.3, 12.6, 13.5-13.7, 13.10_
|
||||
- [x] 2.3 为新增 FDW 查询函数编写单元测试
|
||||
- 测试文件:`apps/backend/tests/unit/test_fdw_queries_rns12.py`
|
||||
- 验证 DQ-6 JOIN 正确性、DQ-7 余额查询、`is_delete=0` 排除、`items_sum` 口径
|
||||
- _Requirements: 13.2-13.7_
|
||||
|
||||
- [x] 3. CUST-1 客户详情 Service + Router(T2-1 ~ T2-3)
|
||||
- [x] 3.1 创建 `apps/backend/app/services/customer_service.py`,实现 `get_customer_detail()`
|
||||
- 核心字段:调用 `fdw_queries.get_member_info()` → 基础信息,`get_member_balance()` → balance,`get_consumption_60d()` → consumption60d,`get_last_visit_days()` → daysSinceVisit
|
||||
- 手机号脱敏逻辑(`"139****5678"` 格式)
|
||||
- Banner 字段查询失败返回 `null`(需求 1.7)
|
||||
- _Requirements: 1.1-1.7, 13.1-13.2_
|
||||
- [x] 3.2 在 `customer_service.py` 实现 `_build_ai_insight()` 和 `_build_retention_clues()` 和 `_build_notes()`
|
||||
- aiInsight:查询 `biz.ai_cache` WHERE `cache_type='app4_analysis'` AND `target_id=customerId`,解析 `cache_value` JSON
|
||||
- retentionClues:查询 `public.member_retention_clue`,按 `created_at` 倒序
|
||||
- notes:查询 `biz.notes` WHERE `target_type='member'`,最多 20 条,按 `created_at` 倒序
|
||||
- 每个模块独立 try/except 优雅降级
|
||||
- _Requirements: 2.1-2.3, 3.1-3.2, 13.8-13.9_
|
||||
- [x] 3.3 在 `customer_service.py` 实现 `_build_consumption_records()`
|
||||
- 调用 `fdw_queries.get_consumption_records()` 获取结算单列表
|
||||
- 构建 coaches 子数组:`fee` 使用 `assistant_pd_money`(基础课)/ `assistant_cx_money`(激励课)
|
||||
- `totalAmount` 使用 `items_sum` 口径,`tableFee` 使用 `table_charge_money`,`foodAmount` 使用 `goods_money`
|
||||
- 过滤 `settle_type IN (1, 3)` + `is_delete=0`
|
||||
- _Requirements: 4.1-4.6, 13.3-13.4, 13.7_
|
||||
- [x] 3.4 在 `customer_service.py` 实现 `_build_coach_tasks()`(T2-2)
|
||||
- 查询 `biz.coach_tasks` WHERE `member_id=customerId`
|
||||
- 对每位助教:`fdw_queries.get_salary_calc()` 获取等级,`get_coach_60d_stats()` 获取近 60 天统计
|
||||
- 映射 `levelColor`/`taskColor`/`bgClass`
|
||||
- metrics 返回:服务次数、总时长、次均时长
|
||||
- _Requirements: 5.1-5.4_
|
||||
- [x] 3.5 在 `customer_service.py` 实现 `_build_favorite_coaches()`(T2-3)
|
||||
- 调用 `fdw_queries.get_relation_index()` 获取关系指数列表,按降序排列
|
||||
- emoji 映射:`relationIndex >= 0.7` → `"💖"`,`< 0.7` → `"💛"`
|
||||
- stats 4 项指标:基础课时(`assistant_pd_money`)、激励课时(`assistant_cx_money`)、上课次数、充值金额
|
||||
- _Requirements: 6.1-6.4_
|
||||
- [x] 3.6 创建 `apps/backend/app/routers/xcx_customers.py`,注册 CUST-1 端点
|
||||
- `GET /{customer_id}` → `customer_service.get_customer_detail()`
|
||||
- `Depends(require_approved())` 权限检查
|
||||
- 在 `main.py` 注册 router
|
||||
- _Requirements: 13.1_
|
||||
- [x] 3.7 为 CUST-1 编写单元测试
|
||||
- 测试文件:`apps/backend/tests/unit/test_customer_detail.py`
|
||||
- 验证完整响应结构、Banner 字段、aiInsight 降级、consumptionRecords 嵌套、coachTasks metrics、favoriteCoaches 排序
|
||||
- _Requirements: 1.1-6.4_
|
||||
|
||||
- [x] 4. Checkpoint — 确保 CUST-1 所有测试通过
|
||||
- 确保所有测试通过,ask the user if questions arise.
|
||||
|
||||
- [x] 5. CUST-2 客户服务记录 Service + Router(T2-4)
|
||||
- [x] 5.1 在 `customer_service.py` 实现 `get_customer_records()`
|
||||
- 接受 `year`、`month`、`table`(可选)、`page`、`page_size` 参数
|
||||
- 调用 `fdw_queries.get_member_info()` → customerName/customerPhone(DQ-6)
|
||||
- 调用 `fdw_queries.get_customer_service_records()` → 按月分页记录
|
||||
- 聚合 `monthCount`/`monthHours`
|
||||
- 调用 `fdw_queries.get_total_service_count()` → totalServiceCount(跨月)
|
||||
- 每条记录含 `recordType`(`course`/`recharge`)和 `isEstimate`
|
||||
- income 使用 `items_sum` 口径,排除 `is_delete!=0`
|
||||
- 按 `create_time` 倒序,返回 `hasMore`
|
||||
- _Requirements: 7.1-7.9_
|
||||
- [x] 5.2 在 `xcx_customers.py` router 注册 CUST-2 端点
|
||||
- `GET /{customer_id}/records` → `customer_service.get_customer_records()`
|
||||
- Query 参数:`year: int`、`month: int (ge=1, le=12)`、`table: str | None`、`page: int (ge=1)`、`page_size: int (ge=1, le=100)`
|
||||
- `Depends(require_approved())` 权限检查
|
||||
- _Requirements: 7.1, 13.1_
|
||||
- [x] 5.3 为 CUST-2 编写单元测试
|
||||
- 测试文件:`apps/backend/tests/unit/test_customer_records.py`
|
||||
- 验证按月查询、monthCount/monthHours 汇总、totalServiceCount 跨月、hasMore 分页、recordType/isEstimate
|
||||
- _Requirements: 7.1-7.9_
|
||||
|
||||
- [x] 6. COACH-1 助教详情 Service + Router(T2-5)
|
||||
- [x] 6.1 创建 `apps/backend/app/services/coach_service.py`,实现 `get_coach_detail()`
|
||||
- 基础信息:`fdw_queries.get_assistant_info()` → name/avatar/skills/workYears/hireDate
|
||||
- 绩效:`fdw_queries.get_salary_calc()` → monthlyHours(`effective_hours`)/monthlySalary(`gross_salary`)/perfCurrent/perfTarget
|
||||
- customerBalance:`fdw_queries.get_member_balance()` 聚合该助教所有客户余额
|
||||
- tasksCompleted:`biz.coach_tasks` 当月 `status='completed'` 计数
|
||||
- _Requirements: 8.1-8.5_
|
||||
- [x] 6.2 在 `coach_service.py` 实现 `_build_income()` 和 `_build_tier_nodes()`
|
||||
- income:`thisMonth`/`lastMonth` 各含 4 项(基础课时费 `assistant_pd_money`/`base_income`、激励课时费 `assistant_cx_money`/`bonus_income`、充值提成、酒水提成)
|
||||
- 从 `v_dws_assistant_salary_calc` 分别查询当月和上月(`salary_month` 为 `YYYY-MM-01`)
|
||||
- tierNodes:档位节点数组(如 `[0, 100, 130, 160, 190, 220]`)
|
||||
- _Requirements: 9.1-9.4_
|
||||
- [x] 6.3 在 `coach_service.py` 实现 `_build_top_customers()` 和 `_build_service_records()`
|
||||
- topCustomers:调用 `fdw_queries.get_coach_top_customers()`,最多 20 条
|
||||
- heartEmoji 三级映射:`score >= 0.7` → `"❤️"`,`0.3 <= score < 0.7` → `"💛"`,`score < 0.3` → `"🤍"`
|
||||
- consume 使用 `items_sum` 口径,balance 通过 `v_dim_member_card_account`(DQ-7),客户姓名通过 `v_dim_member`(DQ-6)
|
||||
- serviceRecords:近期服务记录,income 使用 `ledger_amount`,排除 `is_delete!=0`
|
||||
- _Requirements: 10.1-10.7_
|
||||
- [x] 6.4 在 `coach_service.py` 实现 `_build_task_groups()` 和 `_build_notes()`
|
||||
- 查询 `biz.coach_tasks` WHERE `assistant_id=coachId`
|
||||
- 按 status 分组:`active` → visibleTasks,`inactive` → hiddenTasks,`abandoned` → abandonedTasks
|
||||
- visible/hidden:关联 `biz.notes` 获取备注列表(`task_id` 关联,按 `created_at` 倒序)
|
||||
- abandoned:取 `abandon_reason`
|
||||
- notes:助教相关备注,最多 20 条
|
||||
- _Requirements: 11.1-11.5_
|
||||
- [x] 6.5 在 `coach_service.py` 实现 `_build_history_months()`(T2-6)
|
||||
- `fdw_queries.get_salary_calc_multi_months()` → 最近 6 个月工时/工资
|
||||
- `fdw_queries.get_monthly_customer_count()` → 各月客户数
|
||||
- `biz.coach_tasks` → 各月回访完成数(`task_type='follow_up_visit' AND status='completed'`)和召回完成数(`task_type IN ('high_priority_recall', 'priority_recall') AND status='completed'`)
|
||||
- 本月 `estimated=True`,历史月份 `estimated=False`
|
||||
- 格式化:customers → `"22人"`,hours → `"87.5h"`,salary → `"¥6,950"`
|
||||
- _Requirements: 12.1-12.6_
|
||||
- [x] 6.6 创建 `apps/backend/app/routers/xcx_coaches.py`,注册 COACH-1 端点
|
||||
- `GET /{coach_id}` → `coach_service.get_coach_detail()`
|
||||
- `Depends(require_approved())` 权限检查
|
||||
- 在 `main.py` 注册 router
|
||||
- _Requirements: 13.1_
|
||||
- [x] 6.7 为 COACH-1 编写单元测试
|
||||
- 测试文件:`apps/backend/tests/unit/test_coach_detail.py`
|
||||
- 验证完整响应结构、performance 6 指标、income 本月/上月、topCustomers heartEmoji、historyMonths 排序与 estimated、任务分组
|
||||
- _Requirements: 8.1-12.6_
|
||||
|
||||
- [x] 7. Checkpoint — 确保 CUST-1 + CUST-2 + COACH-1 所有测试通过
|
||||
- 确保所有测试通过,ask the user if questions arise.
|
||||
|
||||
- [x] 8. 优雅降级与权限校验测试
|
||||
- [x] 8.1 为优雅降级编写单元测试
|
||||
- 测试文件:`apps/backend/tests/unit/test_degradation_rns12.py`
|
||||
- 验证 aiInsight/coachTasks/favoriteCoaches/consumptionRecords/historyMonths 各模块查询失败时返回空默认值,不影响 HTTP 200
|
||||
- _Requirements: 13.8-13.9_
|
||||
- [x] 8.2 为权限校验编写单元测试
|
||||
- 测试文件:`apps/backend/tests/unit/test_auth_rns12.py`
|
||||
- 验证未审核用户 403、客户不存在 404、助教不存在 404
|
||||
- _Requirements: 13.1_
|
||||
|
||||
- [x] 9. 属性测试(T2-6 PBT)
|
||||
- [x] 9.1 编写属性测试:消费记录金额拆分不变量
|
||||
- **Property 1: 消费记录金额拆分不变量**
|
||||
- 测试文件:`tests/test_rns12_properties.py`
|
||||
- 生成器:`st.floats(min_value=0, max_value=1e5)` 生成 tableFee/foodAmount/coachFees
|
||||
- 验证:`abs(totalAmount - (tableFee + foodAmount + sum(fees))) < 0.01`
|
||||
- **Validates: Requirements 14.1, 4.4**
|
||||
- [x] 9.2 编写属性测试:废单排除一致性
|
||||
- **Property 2: 废单排除一致性**
|
||||
- 生成器:`st.lists(st.fixed_dictionaries({is_delete: st.integers(0,2), ...}))`
|
||||
- 验证:过滤后结果中所有 `is_delete == 0`
|
||||
- **Validates: Requirements 14.8, 4.6, 7.8, 10.7**
|
||||
- [x] 9.3 编写属性测试:助教费用拆分正确性
|
||||
- **Property 3: 助教费用拆分正确性**
|
||||
- 生成器:`st.floats` 生成 pd_money/cx_money + `st.sampled_from(["基础课","激励课"])`
|
||||
- 验证:基础课 → pd_money,激励课 → cx_money,两者之和 = 总额
|
||||
- **Validates: Requirements 14.2, 4.3, 9.2**
|
||||
- [x] 9.4 编写属性测试:favoriteCoaches 排序不变量
|
||||
- **Property 4: favoriteCoaches 排序不变量**
|
||||
- 生成器:`st.lists(st.floats(0, 1))` 生成 relationIndex 列表
|
||||
- 验证:排序后每项 ≥ 下一项
|
||||
- **Validates: Requirements 14.5, 6.1**
|
||||
- [x] 9.5 编写属性测试:historyMonths 排序与预估标记
|
||||
- **Property 5: historyMonths 排序与预估标记**
|
||||
- 生成器:`st.lists(st.dates(), min_size=1)` 生成月份列表
|
||||
- 验证:降序排列,首项 `estimated=True`,其余 `False`
|
||||
- **Validates: Requirements 14.6, 12.5**
|
||||
- [x] 9.6 编写属性测试:列表上限约束
|
||||
- **Property 6: 列表上限约束**
|
||||
- 生成器:`st.integers(0, 100)` 生成记录数
|
||||
- 验证:notes ≤ 20,topCustomers ≤ 20
|
||||
- **Validates: Requirements 3.2, 10.1, 11.5**
|
||||
- [x] 9.7 编写属性测试:月度汇总聚合正确性
|
||||
- **Property 7: 月度汇总聚合正确性**
|
||||
- 生成器:`st.lists(st.fixed_dictionaries({hours: st.floats(0,10), income: st.floats(0,1e4)}))`
|
||||
- 验证:count=len,monthHours=sum(hours)
|
||||
- **Validates: Requirements 7.6, 5.3**
|
||||
- [x] 9.8 编写属性测试:daysSinceVisit 计算正确性
|
||||
- **Property 8: daysSinceVisit 计算正确性**
|
||||
- 生成器:`st.dates(max_value=date.today())`
|
||||
- 验证:days = (today - date).days,非负整数
|
||||
- **Validates: Requirements 1.6**
|
||||
- [x] 9.9 编写属性测试:emoji 映射正确性
|
||||
- **Property 9: emoji 映射正确性**
|
||||
- 生成器:`st.floats(0, 1)` 生成 relationIndex
|
||||
- 验证:CUST-1 两级映射(≥0.7→💖,<0.7→💛);COACH-1 三级映射(≥0.7→❤️,0.3-0.7→💛,<0.3→🤍)
|
||||
- **Validates: Requirements 6.4**
|
||||
- [x] 9.10 编写属性测试:优雅降级
|
||||
- **Property 10: 优雅降级**
|
||||
- 生成器:`st.sampled_from(MODULES)` 选择失败模块
|
||||
- 验证:失败模块返回空默认值,其他模块正常,HTTP 200
|
||||
- **Validates: Requirements 1.7, 13.8**
|
||||
- [x] 9.11 编写属性测试:任务分组正确性
|
||||
- **Property 11: 任务分组正确性**
|
||||
- 生成器:`st.lists(st.fixed_dictionaries({status: st.sampled_from(STATUSES)}))`
|
||||
- 验证:active→visible,inactive→hidden,abandoned→abandoned,无交集,并集=原集合
|
||||
- **Validates: Requirements 11.1**
|
||||
- [x] 9.12 编写属性测试:数据隔离不变量
|
||||
- **Property 12: 数据隔离不变量**
|
||||
- 生成器:`st.integers(1, 1000)` 生成 customerId/coachId
|
||||
- 验证:coachTasks 每条 member_id=customerId,serviceRecords 每条 assistant_id=coachId
|
||||
- **Validates: Requirements 14.3, 14.4**
|
||||
- [x] 9.13 编写属性测试:分页与 hasMore 正确性
|
||||
- **Property 13: 分页与 hasMore 正确性**
|
||||
- 生成器:`st.integers(1,100)` total + `st.integers(1,10)` page/pageSize
|
||||
- 验证:记录数 ≤ pageSize,hasMore = (total > page*pageSize)
|
||||
- **Validates: Requirements 7.9**
|
||||
- [x] 9.14 编写属性测试:幂等性
|
||||
- **Property 14: 幂等性**
|
||||
- 生成器:`st.integers(1,12)` month + `st.integers(2020,2026)` year
|
||||
- 验证:f(x) == f(x) 对 monthCount/monthHours
|
||||
- **Validates: Requirements 14.7**
|
||||
|
||||
- [x] 10. Final checkpoint — 确保所有测试通过
|
||||
- 确保所有测试通过,ask the user if questions arise.
|
||||
|
||||
- [x] 11. 前端到数据库全链路测试
|
||||
- [x] 11.1 启动后端服务,使用测试库(`test_zqyy_app`)验证 CUST-1、CUST-2、COACH-1 三个端点的完整请求-响应链路
|
||||
- 使用真实 FDW 连接(`test_etl_feiqiu`)验证 SQL 查询正确性
|
||||
- 验证 JSON 响应结构与 Schema 定义一致(camelCase 序列化)
|
||||
- 验证权限校验(`require_approved()`)在真实请求中生效
|
||||
- [x] 11.2 小程序前端联调验证(如已有对应页面)
|
||||
- 确认前端页面能正确调用新增 API 并渲染数据
|
||||
- 验证空数据/降级场景下前端不崩溃
|
||||
- 如前端页面尚未开发,记录待联调清单供后续 RNS 任务使用
|
||||
|
||||
- [x] 12. 项目文档更新与落地
|
||||
- [x] 12.1 更新 `docs/contracts/openapi/backend-api.json`,补充 CUST-1、CUST-2、COACH-1 三个端点的 OpenAPI 定义
|
||||
- [x] 12.2 更新 `docs/architecture/backend-architecture.md`,补充新增的 `customer_service`、`coach_service` 模块及路由注册说明
|
||||
- [x] 12.3 更新 `docs/database/BD_Manual_biz_tables.md`,补充本次新增/引用的 `biz.coach_tasks`、`biz.notes`、`biz.ai_cache` 表的使用说明(如有新增字段或新用法)
|
||||
- [x] 12.4 更新 `docs/DOCUMENTATION-MAP.md`,确保新增文档条目已索引
|
||||
- [x] 12.5 更新 `docs/miniprogram-dev/API-contract.md`,补充 CUST-1、CUST-2、COACH-1 的接口契约(请求/响应示例)
|
||||
|
||||
- [x] 13. 数据库变更审计与 DDL 合并
|
||||
- [x] 13.1 审计本次实现中对数据库的改动(新建表、新增字段、新增索引、FDW 映射变更等)
|
||||
- 检查 `biz.coach_tasks`、`biz.notes`、`biz.ai_cache`、`public.member_retention_clue` 是否需要新建或变更
|
||||
- 检查 FDW 外部表映射是否需要更新(新增视图引用等)
|
||||
- [x] 13.2 将所有数据库变更合并到主 DDL 文件
|
||||
- 业务库变更 → `db/zqyy_app/` 对应 DDL 文件
|
||||
- FDW 变更 → `db/fdw/` 对应 DDL 文件
|
||||
- 编写日期前缀迁移脚本(如有 schema 变更)
|
||||
- [x] 13.3 更新 BD 手册记录变更
|
||||
- 业务库 → `docs/database/BD_Manual_biz_tables.md`
|
||||
- FDW → `docs/database/BD_Manual_fdw.md`(如有变更)
|
||||
- 记录变更原因、影响范围、回滚 SQL
|
||||
|
||||
## Notes
|
||||
|
||||
- Tasks marked with `*` are optional and can be skipped for faster MVP
|
||||
- 所有金额字段统一使用 `items_sum` 口径(DWD-DOC 强制规则 1),禁止 `consume_money`
|
||||
- 助教费用使用 `assistant_pd_money` + `assistant_cx_money` 拆分(DWD-DOC 强制规则 2),禁止 `service_fee`
|
||||
- 会员信息通过 `member_id` JOIN `v_dim_member`(DQ-6),余额通过 `v_dim_member_card_account`(DQ-7)
|
||||
- 废单排除统一使用 `is_delete=0`,禁止引用已废弃的 `dwd_assistant_trash_event`
|
||||
- Property tests validate universal correctness properties from design.md
|
||||
- Checkpoints ensure incremental validation
|
||||
Reference in New Issue
Block a user