feat: batch update - gift card breakdown spec, backend APIs, miniprogram pages, ETL finance recharge, docs & migrations

This commit is contained in:
Neo
2026-03-20 01:43:48 +08:00
parent 075caf067f
commit 79f9a0e1da
437 changed files with 118603 additions and 976 deletions

View File

@@ -0,0 +1,253 @@
# 需求文档 — RNS1.2:客户与助教接口
## 简介
RNS1.2 是 NS1 小程序后端 API 补全项目的第三个子 spec负责实现客户详情CUST-1、客户服务记录CUST-2、助教详情COACH-13 个接口。这三个接口覆盖客户视角和助教视角的详情查看需求,是走查报告中 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`(关系 emojiP6 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` 的记录,验证废单排除规则在所有接口中一致执行