feat: batch update - gift card breakdown spec, backend APIs, miniprogram pages, ETL finance recharge, docs & migrations
This commit is contained in:
168
.kiro/specs/rns1-task-performance-api/tasks.md
Normal file
168
.kiro/specs/rns1-task-performance-api/tasks.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# Implementation Plan: RNS1.1 任务与绩效接口
|
||||
|
||||
## Overview
|
||||
|
||||
基于 design.md 的 7 个组件结构,增量扩展现有后端路由和服务层,新增 FDW 查询封装、绩效服务、Pydantic Schema,并完成前端适配。所有 FDW 查询集中在 `fdw_queries.py`,服务层分为 `task_manager.py`(扩展)和 `performance_service.py`(新增)。
|
||||
|
||||
## Tasks
|
||||
|
||||
- [x] 1. Pydantic Schema 定义(组件 6)
|
||||
- [x] 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_
|
||||
- [x] 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_
|
||||
|
||||
- [x] 2. FDW 查询封装服务(组件 3 — 新增 fdw_queries.py)
|
||||
- [x] 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_
|
||||
- [x] 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_
|
||||
- [x] 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_
|
||||
- [x] 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_
|
||||
- [x] 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_
|
||||
- [x] 2.6 实现 `get_service_records_for_task(conn, site_id, assistant_id, member_id, limit)` 查询特定客户的服务记录(TASK-2 用)
|
||||
- _Requirements: 2.1, 2.3_
|
||||
|
||||
|
||||
- [x] 3. Checkpoint — Schema 与 FDW 查询层验证
|
||||
- Ensure all tests pass, ask the user if questions arise.
|
||||
|
||||
- [x] 4. task_manager 服务扩展(组件 4 — 扩展现有 task_manager.py)
|
||||
- [x] 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_
|
||||
- [x] 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_
|
||||
- [x] 4.3 Write property test: 收入趋势计算正确性
|
||||
- **Property 1: 收入趋势计算正确性**
|
||||
- 生成器:st.floats(min_value=0, max_value=1e6) × 2
|
||||
- 验证:incomeTrendDir 方向正确、差值正确、前缀符号一致
|
||||
- **Validates: Requirements 1.4**
|
||||
- [x] 4.4 Write property test: lastVisitDays 计算正确性
|
||||
- **Property 2: lastVisitDays 计算正确性**
|
||||
- 生成器:st.dates(max_value=date.today())
|
||||
- 验证:天数差 = (today - date).days,非负
|
||||
- **Validates: Requirements 1.7**
|
||||
- [x] 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**
|
||||
- [x] 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**
|
||||
|
||||
- [x] 5. performance_service 服务(组件 5 — 新增 performance_service.py)
|
||||
- [x] 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_
|
||||
- [x] 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_
|
||||
- [x] 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**
|
||||
- [x] 5.4 Write property test: DateGroup 分组正确性
|
||||
- **Property 6: DateGroup 分组正确性**
|
||||
- 生成器:st.lists(st.fixed_dictionaries({date, hours, income}))
|
||||
- 验证:日期唯一、组内日期一致、汇总正确、按日期倒序
|
||||
- **Validates: Requirements 3.3, 4.3**
|
||||
- [x] 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**
|
||||
- [x] 5.6 Write property test: 月度汇总聚合正确性
|
||||
- **Property 8: 月度汇总聚合正确性**
|
||||
- 生成器:st.lists(st.fixed_dictionaries({hours, hours_raw, income}))
|
||||
- 验证:totalCount/totalHours/totalHoursRaw/totalIncome 聚合正确
|
||||
- **Validates: Requirements 4.6**
|
||||
|
||||
- [x] 6. Checkpoint — 服务层验证
|
||||
- Ensure all tests pass, ask the user if questions arise.
|
||||
|
||||
- [x] 7. xcx_tasks Router 扩展(组件 1)
|
||||
- [x] 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_
|
||||
- [x] 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_
|
||||
- [x] 7.3 修改 `routers/xcx_tasks.py` 中 `POST .../pin` 和 `POST .../unpin` 端点,响应对齐契约格式 `{ isPinned: bool }`
|
||||
- _Requirements: 5.1, 5.2, 5.3, 5.4, 5.5_
|
||||
- [x] 7.4 修改 `POST /api/xcx/notes` 端点(`xcx_notes.py`),接受请求体中的可选 `score` 字段(number, 1-5),存入 `biz.coach_notes.score` 列;超出 1-5 范围返回 422
|
||||
- _Requirements: 6.3_
|
||||
- [x] 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**
|
||||
- [x] 7.6 Write property test: 权限与数据隔离
|
||||
- **Property 10: 权限与数据隔离**
|
||||
- 生成器:st.sampled_from(INVALID_USER_SCENARIOS)
|
||||
- 验证:未审核/无绑定/无权限/非本人任务 → 所有端点返回 403
|
||||
- **Validates: Requirements 2.13, 8.1, 8.2, 8.4**
|
||||
- [x] 7.7 Write property test: 备注 score 输入验证
|
||||
- **Property 12: 备注 score 输入验证**
|
||||
- 生成器:st.integers()
|
||||
- 验证:1-5 接受、超范围拒绝 422、null 接受
|
||||
- **Validates: Requirements 6.3**
|
||||
|
||||
- [x] 8. xcx_performance Router(组件 2 — 新增)
|
||||
- [x] 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_
|
||||
- [x] 8.2 在 `routers/xcx_performance.py` 中实现 `GET /api/xcx/performance/records` 端点(接受 year/month/page/pageSize 参数),调用 `performance_service.get_records()`
|
||||
- _Requirements: 4.1, 4.2_
|
||||
- [x] 8.3 修改 `apps/backend/app/main.py`,导入并注册 `xcx_performance.router`
|
||||
- _Requirements: 3.1, 4.1_
|
||||
|
||||
- [x] 9. Checkpoint — 后端路由层验证
|
||||
- Ensure all tests pass, ask the user if questions arise.
|
||||
|
||||
- [x] 10. 前端适配(组件 7)
|
||||
- [x] 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_
|
||||
- [x] 10.2 修改 `apps/miniprogram/miniprogram/pages/task-list/task-list.ts`:消费 `TaskListResponse` 新结构(items + performance),`buildPerfData()` 使用 15+ 字段绩效数据,移除 mock 数据
|
||||
- _Requirements: 6.4, 6.5_
|
||||
- [x] 10.3 修改 task-detail 页面:调用 `fetchTaskDetail()`;根据 `balance` 前端本地计算 `storageLevel`,根据 `heartScore` 计算 `relationLevel`/`relationLevelText`/`relationColor`
|
||||
- _Requirements: 6.4, 6.5_
|
||||
- [x] 10.4 修改 `apps/miniprogram/miniprogram/pages/performance/performance.ts`:添加月份切换控件(左右箭头 + 月份标签),实现 `switchMonth()` 调用 `fetchPerformanceOverview`,含加载状态管理
|
||||
- _Requirements: 7.1, 7.2, 7.3_
|
||||
- [x] 10.5 修改 `apps/miniprogram/miniprogram/pages/performance-records/performance-records.ts`:修复 `switchMonth()` 中 page 未重置的 Bug,切换月份时清空记录列表并重新加载(page 重置为 1)
|
||||
- _Requirements: 7.4, 7.5_
|
||||
- [x] 10.6 在 performance 和 performance-records 页面中,使用 `nameToAvatarColor()` 从 `customerName` 计算 `avatarChar`/`avatarColor`,不依赖后端
|
||||
- _Requirements: 7.6_
|
||||
- [x] 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**
|
||||
|
||||
- [x] 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、废单排除
|
||||
Reference in New Issue
Block a user