Files
Neo-ZQYY/.kiro/specs/rns1-task-performance-api/requirements.md

215 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 需求文档 — RNS1.1:任务与绩效接口
## 简介
RNS1.1 是 NS1 小程序后端 API 补全项目的第二个子 spec负责实现助教日常使用频率最高的 4 个核心接口任务列表扩展、任务详情、绩效概览、绩效明细、pin/unpin 端点、以及对应的前端适配。本 spec 覆盖助教视角的核心工作流,是助教登录后最先接触的功能集合。
### 依赖
- RNS1.0基础设施与契约重写必须先完成全局响应包装中间件、camelCase 转换、重写后的 API 契约
- 前端已有 13 个页面P5.2 交付),当前使用 mock 数据
- 后端已有 `xcx_tasks.py`(需扩展)、`xcx_notes.py``xcx_auth.py`
### 来源文档
- `docs/prd/Neo_Specs/RNS1-split-plan.md` — 拆分计划主文档
- `docs/miniprogram-dev/API-contract.md` — API 契约RNS1.0 T0-5 重写后版本)
- `docs/prd/Neo_Specs/storyboard-walkthrough-assistant-view.md` — 助教视角走查报告GAP-02~22
- `docs/reports/DWD-DOC/` — 金额口径与字段语义权威标杆文档
## 术语表
- **Backend**FastAPI 后端应用,位于 `apps/backend/`
- **Miniprogram**:微信小程序前端应用,位于 `apps/miniprogram/`
- **TASK_1_API**:任务列表接口 `GET /api/xcx/tasks`,返回任务列表 + 绩效概览
- **TASK_2_API**:任务详情接口 `GET /api/xcx/tasks/{taskId}`,返回单个任务的完整详情
- **PERF_1_API**:绩效概览接口 `GET /api/xcx/performance`,返回助教当月绩效汇总
- **PERF_2_API**:绩效明细接口 `GET /api/xcx/performance/records`,返回按日期分组的服务记录
- **PIN_API**:任务置顶/取消置顶接口 `POST /api/xcx/tasks/{id}/pin``POST /api/xcx/tasks/{id}/unpin`
- **PerformanceSummary**任务列表响应中附带的绩效概览数据结构15+ 字段)
- **DateGroup**:按日期分组的数据结构,包含日期标签、当日汇总、记录列表
- **FDW**PostgreSQL Foreign Data Wrapper用于从业务库 `zqyy_app` 访问 ETL 库 `etl_feiqiu` 的数据
- **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 要求的拆分字段
- **enrichTask**:前端 `task-list` 页面中对原始任务数据进行扩展的函数,补充 `lastVisitDays`/`balance`/`aiSuggestion` 等字段
- **buildPerfData**:前端 `task-list` 页面中构建绩效进度条数据的函数,消费 `PerformanceSummary` 的 15+ 字段
- **courseTypeClass**:服务记录中课程类型的样式标识,统一使用 `basic`/`vip`/`tip` 枚举(不带 `tag-` 前缀)
- **user_assistant_binding**:业务库中用户与助教身份的绑定关系表,用于数据隔离
- **ai_cache**:业务库中 AI 分析结果的缓存表,按 `cache_type` 区分不同类型的 AI 输出
## 需求
### 需求 1扩展 TASK-1 任务列表绩效概览T1-1
**用户故事:** 作为助教,我希望在任务列表页面看到完整的绩效进度条(含档位节点、基础/激励课时、升档奖金、收入趋势),以便快速了解本月绩效状态和距升档的差距。
#### 验收标准
1. THE TASK_1_API SHALL 在响应的 `performance` 字段中返回以下扩展字段:`tierNodes`(档位节点数组,如 `[0, 100, 130, 160, 190, 220]`)、`basicHours`(基础课时)、`bonusHours`(激励课时)、`currentTier`当前档位索引0-based`nextTierHours`(下一档位工时阈值)、`tierCompleted`(是否已达最高档)、`bonusMoney`(升档奖金,元)、`incomeTrend`(收入趋势描述,如 `"↓368"`)、`incomeTrendDir``up``down`)、`prevMonth`(上月标签,如 `"1月"`)、`currentTierLabel`(当前档位名称,如 `"初级"`
2. THE TASK_1_API SHALL 从 `fdw_etl.v_dws_assistant_salary_calc` 查询当前助教的绩效和档位数据,通过 `user_assistant_binding` 获取 `assistant_id` 进行数据隔离
3. THE TASK_1_API SHALL 使用 `items_sum` 口径计算所有收入金额DWD-DOC 强制规则 1使用 `assistant_pd_money`(基础课)和 `assistant_cx_money`激励课拆分助教费用DWD-DOC 强制规则 2
4. THE TASK_1_API SHALL 通过对比当月和上月的收入数据计算 `incomeTrend``incomeTrendDir` 字段
5. WHEN `tierCompleted``true`THE TASK_1_API SHALL 将 `bonusMoney` 设为 0`nextTierHours` 设为当前档位工时阈值
#### 1.2 任务项扩展字段GAP-03
6. THE TASK_1_API SHALL 为每个任务 item 返回以下可选扩展字段:`lastVisitDays`距上次到店天数integer`balance`客户余额number`aiSuggestion`AI 建议摘要string
7. THE TASK_1_API SHALL 从 `fdw_etl.dwd.dwd_settlement_head` 查询客户最后到店日期,计算 `lastVisitDays`(当前日期与 `MAX(settle_time)` 的天数差)
8. THE TASK_1_API SHALL 从 `fdw_etl.dwd.dim_member_card_account` 查询客户储值卡余额作为 `balance` 字段值
9. THE TASK_1_API SHALL 从 `biz.ai_cache` 查询 `cache_type` 对应的 AI 建议摘要作为 `aiSuggestion` 字段值
10. IF 某个扩展字段的数据源查询失败或无数据THEN THE TASK_1_API SHALL 对该字段返回 `null`,不影响其他字段和整体响应
### 需求 2实现 TASK-2 任务详情完整版T1-2
**用户故事:** 作为助教我希望点击任务后能看到完整的任务详情包括服务记录、维客线索、AI 分析、备注),以便全面了解客户情况并制定跟进策略。
#### 验收标准
1. THE TASK_2_API SHALL 返回完整的任务详情响应,包含基础信息、维客线索(`retentionClues`)、话术参考(`talkingPoints`)、服务记录摘要(`serviceSummary`)、服务记录列表(`serviceRecords`、AI 分析(`aiAnalysis`)、备注列表(`notes`
2. THE TASK_2_API SHALL 在响应中包含 `customerId` 字段(客户唯一 ID供前端跳转 chat 和 customer-service-records 页面使用
3. WHEN 请求任务详情时THE TASK_2_API SHALL 对服务记录(`serviceRecords`)和备注(`notes`)各返回最多 20 条,按时间倒序排列,支持前端懒加载
#### 2.2 维客线索格式统一GAP-06~07
4. THE TASK_2_API SHALL 返回维客线索的 `tag` 字段为纯文本字符串(不含换行符 `\n`),多行标签使用空格分隔
5. THE TASK_2_API SHALL 返回维客线索的 `source` 字段为以下枚举值之一:`manual`(手动创建)、`ai_consumption`AI 消费分析生成)、`ai_note`AI 备注分析生成)
6. THE TASK_2_API SHALL 从 `public.member_retention_clue` 查询维客线索数据,按 `created_at` 倒序排列
#### 2.3 AI 分析 cache_type 映射GAP-08
7. THE TASK_2_API SHALL 从 `biz.ai_cache` 查询 AI 分析数据,使用以下 `cache_type` 映射:`aiAnalysis.summary` 来自 `app4_analysis``talkingPoints` 来自 `app5_talking_points`
8. IF `biz.ai_cache` 中无对应 `cache_type` 的缓存记录THEN THE TASK_2_API SHALL 对 `aiAnalysis` 返回 `{ summary: "", suggestions: [] }`,对 `talkingPoints` 返回空数组
#### 2.4 服务记录字段GAP-06
9. THE TASK_2_API SHALL 为每条服务记录返回 `courseTypeClass` 字段,使用统一枚举值:`basic`(基础课)、`vip`(包厢课)、`tip`(激励课)、`recharge`(充值)、`incentive`(激励),不带 `tag-` 前缀
10. THE TASK_2_API SHALL 为每条服务记录返回可选字段 `recordType``course``recharge`)和 `isEstimate`是否预估数据boolean
11. THE TASK_2_API SHALL 使用 `items_sum` 口径计算服务记录中的 `income` 字段DWD-DOC 强制规则 1
12. THE TASK_2_API SHALL 使用 `dwd_assistant_service_log_ex.is_trash` 排除废单记录
#### 2.5 权限与数据隔离
13. THE TASK_2_API SHALL 验证请求的 `taskId` 属于当前登录助教(通过 `user_assistant_binding` 获取 `assistant_id`,校验 `coach_tasks.assistant_id` 匹配)
14. IF 请求的 `taskId` 不属于当前助教THEN THE TASK_2_API SHALL 返回 HTTP 403 `{ code: 403, message: "无权访问该任务" }`
### 需求 3实现 PERF-1 绩效概览T1-3
**用户故事:** 作为助教,我希望查看本月绩效概览(包括收入明细、档位进度、服务记录按日期分组、新客和常客列表),以便了解本月工作成果和收入构成。
#### 验收标准
1. THE PERF_1_API SHALL 接受 `year``month` 查询参数,返回指定月份的绩效概览数据
2. THE PERF_1_API SHALL 通过 `user_assistant_binding` 获取当前助教的 `assistant_id`,仅查询该助教自己的绩效数据
#### 3.2 本月服务记录 DateGroup 结构GAP-12
3. THE PERF_1_API SHALL 将 `thisMonthRecords` 以 DateGroup 结构返回:每组包含 `date`(日期标签,如 `"2月7日"`)、`totalHours`(当日总工时,格式化字符串)、`totalIncome`(当日总收入,格式化字符串)、`records`(记录列表)
4. THE PERF_1_API SHALL 为 DateGroup 中每条记录返回以下字段:`customerName``avatarChar`(姓氏首字)、`avatarColor`(头像渐变色)、`timeRange`(时间段,如 `"20:00-22:00"`)、`hours`(工时,格式化字符串)、`courseType`(课程类型,如 `"基础课"`)、`courseTypeClass`(样式标识:`basic`/`vip`/`tip`,不带 `tag-` 前缀)、`location`(台桌/包厢名)、`income`(收入,格式化字符串)
5. THE PERF_1_API SHALL 从 `fdw_etl.v_dwd_assistant_service_log` 查询服务记录,按 `settle_time` 日期分组,每组内按时间倒序排列
#### 3.3 收入档位数据GAP-13
6. THE PERF_1_API SHALL 返回收入档位数据:`currentTier`(当前档,含 `basicRate``incentiveRate`)、`nextTier`(下一档,含 `basicRate``incentiveRate`)、`upgradeHoursNeeded`距升档所需工时number`upgradeBonus`升档奖金number
7. THE PERF_1_API SHALL 从 `fdw_etl.v_dws_assistant_salary_calc` 查询档位和费率数据
#### 3.4 上月收入与收入明细GAP-14~15
8. THE PERF_1_API SHALL 返回 `lastMonthIncome` 字段(上月收入,格式化字符串,如 `"¥16,880"`
9. THE PERF_1_API SHALL 为 `incomeItems` 每项返回 `desc` 字段(费率×工时的拆分描述,如 `"80元/h × 75h"`),由后端根据费率和工时数据计算生成
10. THE PERF_1_API SHALL 使用 `items_sum` 口径计算所有收入金额DWD-DOC 强制规则 1使用 `assistant_pd_money``assistant_cx_money` 拆分助教费用DWD-DOC 强制规则 2
#### 3.5 新客与常客列表GAP-16
11. THE PERF_1_API SHALL 为 `newCustomers` 每项返回 `lastService`(最后服务日期,如 `"2月7日"`)和 `count`服务次数number字段
12. THE PERF_1_API SHALL 为 `regularCustomers` 每项返回 `hours`总工时number`income`(总收入,格式化字符串)字段
13. THE PERF_1_API SHALL 通过 `member_id` JOIN `fdw_etl.dwd.dim_member`(取 `scd2_is_current=1`获取客户姓名DWD-DOC 强制规则 DQ-6禁止直接使用 `member_phone``member_name`
### 需求 4实现 PERF-2 绩效明细T1-4
**用户故事:** 作为助教,我希望查看指定月份的绩效明细(按日期分组的服务记录列表),以便回顾每天的工作详情。
#### 验收标准
1. THE PERF_2_API SHALL 接受 `year``month``page``pageSize` 查询参数,返回指定月份的绩效明细数据
2. THE PERF_2_API SHALL 每页返回最多 20 条记录(默认 `pageSize=20`),按日期分组为 `dateGroups` 结构,并返回 `hasMore` 标记指示是否有更多数据
#### 4.2 按日期分组GAP-19~20
3. THE PERF_2_API SHALL 将服务记录按日期分组,每组包含 `date`(日期标签)、`totalHours`(当日总工时)、`totalIncome`(当日总收入)、`records`(记录列表)
4. THE PERF_2_API SHALL 为每条记录返回 `courseTypeClass` 字段,使用统一枚举值 `basic`/`vip`/`tip`(不带 `tag-` 前缀)
5. THE PERF_2_API SHALL 不返回 `avatarChar``avatarColor` 字段(前端通过 `nameToAvatarColor()` 工具函数从 `customerName` 自行计算)
#### 4.3 月度汇总
6. THE PERF_2_API SHALL 返回月度汇总数据 `summary``totalCount`(总记录数)、`totalHours`(总工时)、`totalHoursRaw`(原始工时,未折算)、`totalIncome`(总收入)
7. THE PERF_2_API SHALL 使用 `items_sum` 口径计算 `totalIncome` 和每条记录的 `income`DWD-DOC 强制规则 1
8. THE PERF_2_API SHALL 通过 `member_id` JOIN `fdw_etl.dwd.dim_member`(取 `scd2_is_current=1`获取客户姓名DWD-DOC 强制规则 DQ-6
### 需求 5实现 pin/unpin API 端点T1-5
**用户故事:** 作为助教,我希望将重要任务置顶或取消置顶,以便优先处理关键客户的跟进任务。
#### 验收标准
1. THE Backend SHALL 实现 `POST /api/xcx/tasks/{taskId}/pin` 端点,将指定任务的 `is_pinned` 字段设为 `true`
2. THE Backend SHALL 实现 `POST /api/xcx/tasks/{taskId}/unpin` 端点,将指定任务的 `is_pinned` 字段设为 `false`
3. WHEN pin 或 unpin 操作成功时THE PIN_API SHALL 返回 `{ isPinned: true }``{ isPinned: false }`
4. THE PIN_API SHALL 验证请求的 `taskId` 属于当前登录助教(通过 `user_assistant_binding` 校验),不属于时返回 HTTP 403
5. IF 请求的 `taskId` 不存在THEN THE PIN_API SHALL 返回 HTTP 404 `{ code: 404, message: "任务不存在" }`
6. THE Miniprogram SHALL 在 `services/api.ts` 中新增 `pinTask(taskId: string)``unpinTask(taskId: string)` 函数,分别调用 pin 和 unpin 端点
7. WHEN pin/unpin API 调用成功后THE Miniprogram SHALL 更新本地任务列表状态(将任务移入/移出置顶分组),无需重新请求完整任务列表
### 需求 6前端适配 — 任务页面T1-6 任务部分)
**用户故事:** 作为助教,我希望任务列表和任务详情页面能正确展示后端返回的真实数据(替代当前的 mock 数据),以便看到真实的客户信息和绩效状态。
#### 验收标准
#### 6.1 createNote 补充用户手动评分参数GAP-05
1. THE Miniprogram SHALL 修改 `services/api.ts``createNote` 函数的签名,增加可选参数 `manualScore?: number`用户手动评分1-5 星)
2. WHEN 用户在备注弹窗中提交备注时THE Miniprogram SHALL 将 `manualScore` 字段一并传递给 `POST /api/xcx/notes` 端点
3. THE Backend SHALL 修改 `POST /api/xcx/notes` 端点,接受请求体中的可选 `manualScore` 字段number1-5并存入 `biz.notes.score` 列。注意:此字段为用户手动评分(再次服务意愿 + 再来店可能性),与 AI 应用 6 的 `aiScore`1-10 分,展示用)语义不同
#### 6.2 storageLevel/relationLevel 前端计算GAP-11
4. THE Miniprogram SHALL 在 task-detail 页面根据后端返回的 `balance` 字段值,在前端本地计算 `storageLevel`(储值等级,如 "非常多"/"较多"/"一般"/"较少"
5. THE Miniprogram SHALL 在 task-detail 页面根据后端返回的 `heartScore` 字段值0-10 范围),在前端本地计算 `relationLevel``relationLevelText``relationColor`,阈值遵循 P6 AC3 四级映射(>8.5 / >7 / >5 / ≤5
### 需求 7前端适配 — 绩效页面T1-6 绩效部分)
**用户故事:** 作为助教,我希望绩效概览和绩效明细页面支持月份切换,以便查看历史月份的绩效数据。
#### 验收标准
#### 7.1 绩效概览月份切换F8, GAP-18
1. THE Miniprogram SHALL 在 performance 页面添加月份切换控件(左右箭头 + 月份标签),允许用户在当前月和历史月份之间切换
2. WHEN 用户切换月份时THE Miniprogram SHALL 使用新的 `year`/`month` 参数重新调用 `fetchPerformanceOverview` 接口,加载对应月份的绩效数据
3. THE Miniprogram SHALL 在月份切换期间显示加载状态,防止用户重复操作
#### 7.2 绩效明细月份切换重置分页F9, GAP-21
4. WHEN 用户在 performance-records 页面切换月份时THE Miniprogram SHALL 将 `page` 重置为 1清空已加载的记录列表重新从第一页加载
5. THE Miniprogram SHALL 修复 `switchMonth()` 函数中 `page` 未重置的 Bug
#### 7.3 avatarChar/avatarColor 前端计算GAP-19 决策)
6. THE Miniprogram SHALL 在 performance 和 performance-records 页面,使用 `nameToAvatarColor()` 工具函数从 `customerName` 计算 `avatarChar`(姓氏首字)和 `avatarColor`(头像渐变色),不依赖后端返回这两个字段
### 需求 8全局约束与数据隔离
**用户故事:** 作为系统管理员,我希望所有任务和绩效接口都遵循统一的权限控制和数据隔离规则,以确保每位助教只能访问自己的数据。
#### 验收标准
1. THE Backend SHALL 对所有 RNS1.1 接口TASK-1、TASK-2、PERF-1、PERF-2、PIN/UNPIN执行 `require_approved()` 权限检查,确保用户状态为 `approved`
2. THE Backend SHALL 对所有 RNS1.1 接口验证用户具有 `view_tasks` 权限
3. THE Backend SHALL 通过 `user_assistant_binding` 表获取当前用户对应的 `assistant_id`,所有数据查询均以该 `assistant_id` 作为过滤条件
4. IF 当前用户在 `user_assistant_binding` 中无绑定记录THEN THE Backend SHALL 返回 HTTP 403 `{ code: 403, message: "未绑定助教身份" }`
5. THE Backend SHALL 对所有涉及金额的字段统一使用 `items_sum` 口径DWD-DOC 强制规则 1禁止使用 `consume_money`
6. THE Backend SHALL 对所有涉及助教费用的字段使用 `assistant_pd_money`(陪打/基础课)+ `assistant_cx_money`(超休/激励课拆分DWD-DOC 强制规则 2禁止使用 `service_fee`
7. THE Backend SHALL 对所有涉及会员信息的查询通过 `member_id` LEFT JOIN `fdw_etl.dwd.dim_member`(取 `scd2_is_current=1`获取姓名和手机号DWD-DOC 强制规则 DQ-6禁止直接使用 `settlement_head.member_phone``member_name`
8. THE Backend SHALL 使用 `dwd_assistant_service_log_ex.is_trash` 排除废单记录,禁止使用已废弃的 `dwd_assistant_trash_event`