13 KiB
13 KiB
Implementation Plan: RNS1.1 任务与绩效接口
Overview
基于 design.md 的 7 个组件结构,增量扩展现有后端路由和服务层,新增 FDW 查询封装、绩效服务、Pydantic Schema,并完成前端适配。所有 FDW 查询集中在 fdw_queries.py,服务层分为 task_manager.py(扩展)和 performance_service.py(新增)。
Tasks
-
1. Pydantic Schema 定义(组件 6)
- 1.1 扩展
apps/backend/app/schemas/xcx_tasks.py,新增PerformanceSummary(15+ 字段:tierNodes, basicHours, bonusHours, currentTier, nextTierHours, tierCompleted, bonusMoney, incomeTrend, incomeTrendDir, prevMonth, currentTierLabel 等)、TaskItem(扩展版,含 lastVisitDays, balance, aiSuggestion 可选字段)、TaskListResponse(items + total + page + pageSize + performance)- 新增
RetentionClue、ServiceRecord(含 courseTypeClass 枚举)、AiAnalysis、NoteItem(含 score 可选字段)、ServiceSummary、TaskDetailResponse模型 - Requirements: 1.1, 1.6, 2.1, 2.2, 2.4, 2.9
- 新增
- 1.2 新建
apps/backend/app/schemas/xcx_performance.py,定义DateGroupRecord(含可选 avatarChar/avatarColor)、DateGroup、TierInfo、IncomeItem(含 desc)、CustomerSummary、NewCustomer、RegularCustomer、PerformanceOverviewResponse模型- 定义
RecordsSummary(totalCount, totalHours, totalHoursRaw, totalIncome)、PerformanceRecordsResponse(summary + dateGroups + hasMore)模型 - Requirements: 3.1, 3.3, 3.4, 3.5, 4.1, 4.2, 4.3
- 定义
- 1.1 扩展
-
2. FDW 查询封装服务(组件 3 — 新增 fdw_queries.py)
- 2.1 新建
apps/backend/app/services/fdw_queries.py,实现_fdw_context(conn, site_id)上下文管理器(BEGIN + SET LOCAL app.current_site_id)和get_member_info(conn, site_id, member_ids)批量查询会员信息- ⚠️ DQ-6:通过 member_id JOIN fdw_etl.v_dim_member,取 scd2_is_current=1
- Requirements: 8.7
- 2.2 实现
get_member_balance(conn, site_id, member_ids)批量查询会员储值卡余额- ⚠️ DQ-7:通过 member_id JOIN fdw_etl.v_dim_member_card_account,取 scd2_is_current=1
- Requirements: 1.8, 8.7
- 2.3 实现
get_last_visit_days(conn, site_id, member_ids)批量查询客户距上次到店天数- 来源:fdw_etl.v_dwd_assistant_service_log,WHERE is_trash = false
- Requirements: 1.7
- 2.4 实现
get_salary_calc(conn, site_id, assistant_id, year, month)查询助教绩效/档位/收入数据- ⚠️ DWD-DOC 规则 1:收入使用 items_sum 口径
- ⚠️ DWD-DOC 规则 2:费用使用 assistant_pd_money + assistant_cx_money
- Requirements: 1.2, 1.3, 3.7, 8.5, 8.6
- 2.5 实现
get_service_records(conn, site_id, assistant_id, year, month, limit, offset)查询助教服务记录明细- ⚠️ 废单排除:WHERE is_trash = false;DQ-6:客户姓名通过 member_id JOIN dim_member
- Requirements: 2.11, 2.12, 3.5, 4.7, 4.8, 8.8
- 2.6 实现
get_service_records_for_task(conn, site_id, assistant_id, member_id, limit)查询特定客户的服务记录(TASK-2 用)- Requirements: 2.1, 2.3
- 2.1 新建
-
3. Checkpoint — Schema 与 FDW 查询层验证
- Ensure all tests pass, ask the user if questions arise.
-
4. task_manager 服务扩展(组件 4 — 扩展现有 task_manager.py)
- 4.1 在
apps/backend/app/services/task_manager.py中实现get_task_list_v2(user_id, site_id, status, page, page_size)- 逻辑:_get_assistant_id() → 查询 coach_tasks 带分页 → fdw_queries 批量获取会员信息/余额/lastVisitDays → fdw_queries.get_salary_calc() 获取绩效概览 → 查询 ai_cache 获取 aiSuggestion → 组装 TaskListResponse
- 扩展字段(lastVisitDays/balance/aiSuggestion)采用优雅降级:单个查询失败返回 null,不影响整体响应
- Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 1.10
- 4.2 在
apps/backend/app/services/task_manager.py中实现get_task_detail(task_id, user_id, site_id)- 逻辑:_get_assistant_id() + _verify_task_ownership() 权限校验 → 查询 coach_tasks 基础信息 → 查询 member_retention_clue 维客线索 → 查询 ai_cache(app5_talking_points → talkingPoints, app4_analysis → aiAnalysis)→ fdw_queries.get_service_records_for_task() 服务记录(最多 20 条)→ 查询 notes(最多 20 条)→ 组装 TaskDetailResponse
- tag 字段净化:去除换行符 \n,多行标签使用空格分隔
- Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 2.10, 2.11, 2.12, 2.13, 2.14
- 4.3 Write property test: 收入趋势计算正确性
- Property 1: 收入趋势计算正确性
- 生成器:st.floats(min_value=0, max_value=1e6) × 2
- 验证:incomeTrendDir 方向正确、差值正确、前缀符号一致
- Validates: Requirements 1.4
- 4.4 Write property test: lastVisitDays 计算正确性
- Property 2: lastVisitDays 计算正确性
- 生成器:st.dates(max_value=date.today())
- 验证:天数差 = (today - date).days,非负
- Validates: Requirements 1.7
- 4.5 Write property test: 维客线索 tag 净化与 source 枚举
- Property 3: 维客线索 tag 净化与 source 枚举
- 生成器:st.text() 生成含 \n 的 tag + st.sampled_from source
- 验证:tag 无换行、source 在枚举内
- Validates: Requirements 2.4, 2.5
- 4.6 Write property test: courseTypeClass 枚举映射
- Property 4: courseTypeClass 枚举映射
- 生成器:st.sampled_from(ALL_COURSE_TYPES)
- 验证:结果在 {basic, vip, tip, recharge, incentive} 内、无 tag- 前缀
- Validates: Requirements 2.9, 4.4
- 4.1 在
-
5. performance_service 服务(组件 5 — 新增 performance_service.py)
- 5.1 新建
apps/backend/app/services/performance_service.py,实现get_overview(user_id, site_id, year, month)- 逻辑:获取 assistant_id → fdw_queries.get_salary_calc() 档位/收入/费率 → fdw_queries.get_service_records() 按日期分组为 DateGroup → 聚合新客/常客列表 → 计算 incomeItems(含 desc 费率描述)→ 查询上月收入 lastMonthIncome
- Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13
- 5.2 实现
get_records(user_id, site_id, year, month, page, page_size)- 逻辑:获取 assistant_id → fdw_queries.get_service_records() 带分页 → 按日期分组为 dateGroups → 计算 summary 汇总 → 返回 { summary, dateGroups, hasMore }
- PERF-2 不返回 avatarChar/avatarColor(前端自行计算)
- Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8
- 5.3 Write property test: 分页与排序
- Property 5: 列表分页与排序
- 生成器:st.lists(st.fixed_dictionaries(...)) + st.integers(1,10) page/pageSize
- 验证:记录数 ≤ pageSize、hasMore 正确、排序正确
- Validates: Requirements 2.3, 4.2
- 5.4 Write property test: DateGroup 分组正确性
- Property 6: DateGroup 分组正确性
- 生成器:st.lists(st.fixed_dictionaries({date, hours, income}))
- 验证:日期唯一、组内日期一致、汇总正确、按日期倒序
- Validates: Requirements 3.3, 4.3
- 5.5 Write property test: incomeItems desc 格式化
- Property 7: incomeItems desc 格式化
- 生成器:st.floats(min_value=0.01) rate × st.floats(min_value=0) hours
- 验证:desc 包含费率值和工时值,格式为 "{rate}元/h × {hours}h"
- Validates: Requirements 3.9
- 5.6 Write property test: 月度汇总聚合正确性
- Property 8: 月度汇总聚合正确性
- 生成器:st.lists(st.fixed_dictionaries({hours, hours_raw, income}))
- 验证:totalCount/totalHours/totalHoursRaw/totalIncome 聚合正确
- Validates: Requirements 4.6
- 5.1 新建
-
6. Checkpoint — 服务层验证
- Ensure all tests pass, ask the user if questions arise.
-
7. xcx_tasks Router 扩展(组件 1)
- 7.1 修改
apps/backend/app/routers/xcx_tasks.py:将GET /api/xcx/tasks响应从list[TaskListItem]改为TaskListResponse(含 items + performance + 分页),新增 status/page/pageSize 查询参数,调用task_manager.get_task_list_v2()- Requirements: 1.1, 1.2
- 7.2 在
routers/xcx_tasks.py中新增GET /api/xcx/tasks/{task_id}端点,调用task_manager.get_task_detail(),含 require_approved() 权限校验- Requirements: 2.1, 2.13, 2.14
- 7.3 修改
routers/xcx_tasks.py中POST .../pin和POST .../unpin端点,响应对齐契约格式{ isPinned: bool }- Requirements: 5.1, 5.2, 5.3, 5.4, 5.5
- 7.4 修改
POST /api/xcx/notes端点(xcx_notes.py),接受请求体中的可选score字段(number, 1-5),存入biz.coach_notes.score列;超出 1-5 范围返回 422- Requirements: 6.3
- 7.5 Write property test: Pin/Unpin 状态往返
- Property 9: Pin/Unpin 状态往返
- 生成器:st.booleans() 初始状态
- 验证:pin→true、unpin→false、往返恢复
- Validates: Requirements 5.1, 5.2, 5.3
- 7.6 Write property test: 权限与数据隔离
- Property 10: 权限与数据隔离
- 生成器:st.sampled_from(INVALID_USER_SCENARIOS)
- 验证:未审核/无绑定/无权限/非本人任务 → 所有端点返回 403
- Validates: Requirements 2.13, 8.1, 8.2, 8.4
- 7.7 Write property test: 备注 score 输入验证
- Property 12: 备注 score 输入验证
- 生成器:st.integers()
- 验证:1-5 接受、超范围拒绝 422、null 接受
- Validates: Requirements 6.3
- 7.1 修改
-
8. xcx_performance Router(组件 2 — 新增)
- 8.1 新建
apps/backend/app/routers/xcx_performance.py,实现GET /api/xcx/performance端点(接受 year/month 参数),调用performance_service.get_overview(),含 require_approved() 权限校验- Requirements: 3.1, 3.2
- 8.2 在
routers/xcx_performance.py中实现GET /api/xcx/performance/records端点(接受 year/month/page/pageSize 参数),调用performance_service.get_records()- Requirements: 4.1, 4.2
- 8.3 修改
apps/backend/app/main.py,导入并注册xcx_performance.router- Requirements: 3.1, 4.1
- 8.1 新建
-
9. Checkpoint — 后端路由层验证
- Ensure all tests pass, ask the user if questions arise.
-
10. 前端适配(组件 7)
- 10.1 修改
apps/miniprogram/miniprogram/services/api.ts:新增pinTask()、unpinTask()、fetchTaskDetail()、fetchPerformanceOverview(year, month)、fetchPerformanceRecords(year, month, page)函数;createNote()增加可选score参数- Requirements: 5.6, 6.1, 6.2, 7.1
- 10.2 修改
apps/miniprogram/miniprogram/pages/task-list/task-list.ts:消费TaskListResponse新结构(items + performance),buildPerfData()使用 15+ 字段绩效数据,移除 mock 数据- Requirements: 6.4, 6.5
- 10.3 修改 task-detail 页面:调用
fetchTaskDetail();根据balance前端本地计算storageLevel,根据heartScore计算relationLevel/relationLevelText/relationColor- Requirements: 6.4, 6.5
- 10.4 修改
apps/miniprogram/miniprogram/pages/performance/performance.ts:添加月份切换控件(左右箭头 + 月份标签),实现switchMonth()调用fetchPerformanceOverview,含加载状态管理- Requirements: 7.1, 7.2, 7.3
- 10.5 修改
apps/miniprogram/miniprogram/pages/performance-records/performance-records.ts:修复switchMonth()中 page 未重置的 Bug,切换月份时清空记录列表并重新加载(page 重置为 1)- Requirements: 7.4, 7.5
- 10.6 在 performance 和 performance-records 页面中,使用
nameToAvatarColor()从customerName计算avatarChar/avatarColor,不依赖后端- Requirements: 7.6
- 10.7 Write property test: 前端派生字段计算
- Property 11: 前端派生字段计算
- 生成器:st.text(min_size=1) name + st.floats(0, 1e6) balance + st.floats(0, 10) heartScore
- 验证:avatarChar=name[0]、确定性、storageLevel/relationLevel 单调性
- Validates: Requirements 6.4, 6.5, 7.6
- 10.1 修改
-
11. Final checkpoint — 全量验证
- Ensure all tests pass, ask the user if questions arise.
Notes
- Tasks marked with
*are optional and can be skipped for faster MVP - 组件 3(fdw_queries.py)是所有 FDW 查询的集中封装,组件 4/5 通过调用它访问 ETL 数据
- 组件 4 扩展现有 task_manager.py(不新建 task_perf_service.py),组件 5 新建 performance_service.py
- Schema 文件命名:
xcx_tasks.py(扩展)+xcx_performance.py(新增),与 design.md 一致 - 所有 12 个正确性属性(P1-P12)均有对应的属性测试任务
- DWD-DOC 强制规则在 fdw_queries.py 中统一实施:items_sum 口径、费用拆分、DQ-6/DQ-7 JOIN、废单排除