# 需求文档 — 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` 字段(number,1-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` 表