feat: batch update - gift card breakdown spec, backend APIs, miniprogram pages, ETL finance recharge, docs & migrations
This commit is contained in:
251
.kiro/specs/rns1-infra-contract-rewrite/tasks.md
Normal file
251
.kiro/specs/rns1-infra-contract-rewrite/tasks.md
Normal file
@@ -0,0 +1,251 @@
|
||||
# 实现计划:RNS1.0 基础设施与契约重写
|
||||
|
||||
## 概述
|
||||
|
||||
按依赖关系排序实现 6 个主任务:T0-1(全局中间件)和 T0-2(CamelModel)为基础设施层,必须先行;T0-3(路由修正)和 T0-4(前端解包)为适配层;T0-5(契约重写)为文档层;T0-6(参数修复)为前端修复层。属性测试使用 Hypothesis,单元测试使用 pytest。
|
||||
|
||||
## 任务
|
||||
|
||||
- [x] 1. 全局响应包装中间件 + 异常处理器(T0-1)
|
||||
- [x] 1.1 实现 ResponseWrapperMiddleware ASGI 中间件
|
||||
- 创建 `apps/backend/app/middleware/response_wrapper.py`
|
||||
- 实现 ASGI 中间件类,拦截 `http.response.start` 和 `http.response.body`
|
||||
- JSON 成功响应(2xx + application/json)包装为 `{ "code": 0, "data": <原始body> }`
|
||||
- 跳过条件:`text/event-stream`(SSE)、非 `application/json`、非 2xx 状态码
|
||||
- _需求: 1.1, 1.5, 1.6_
|
||||
|
||||
- [x] 1.2 实现异常处理器函数
|
||||
- 在同一文件中实现 `http_exception_handler` 和 `unhandled_exception_handler`
|
||||
- HTTPException → `{ "code": <status_code>, "message": <detail> }`
|
||||
- 未捕获异常 → `{ "code": 500, "message": "Internal Server Error" }` + `logger.exception` 写入完整堆栈
|
||||
- _需求: 1.2, 1.3_
|
||||
|
||||
- [x] 1.3 在 main.py 中注册中间件和异常处理器
|
||||
- `app.add_exception_handler(HTTPException, http_exception_handler)`
|
||||
- `app.add_exception_handler(Exception, unhandled_exception_handler)`
|
||||
- `app.add_middleware(ResponseWrapperMiddleware)`(在 CORS 之后添加)
|
||||
- 验证现有 16 个端点(Auth/Tasks/Notes/AI Chat)在包装后行为正常
|
||||
- _需求: 1.4_
|
||||
|
||||
- [x] 1.4 编写属性测试:响应包装-解包 Round Trip
|
||||
- **Property 1: 响应包装-解包 Round Trip**
|
||||
- 测试文件:`tests/test_rns1_properties.py`
|
||||
- 生成器:`st.from_type(dict|list|str|int|float|bool|None)` 生成随机 JSON 可序列化数据
|
||||
- 验证:包装后 JSON 包含 `code=0` 和 `data`;解包 `data` 等于原始输入
|
||||
- **验证需求: 1.1, 4.1**
|
||||
|
||||
- [x] 1.5 编写属性测试:异常响应保持 code 和 message
|
||||
- **Property 2: 异常响应保持 code 和 message**
|
||||
- 测试文件:`tests/test_rns1_properties.py`
|
||||
- 生成器:`st.integers(min_value=400, max_value=599)` × `st.text(min_size=1)`
|
||||
- 验证:输出 JSON 的 `code` 等于输入状态码,`message` 等于输入 detail
|
||||
- **验证需求: 1.2, 1.3**
|
||||
|
||||
- [x] 1.6 编写属性测试:非 JSON 响应透传
|
||||
- **Property 3: 非 JSON 响应透传**
|
||||
- 测试文件:`tests/test_rns1_properties.py`
|
||||
- 生成器:`st.sampled_from(["text/event-stream", "application/octet-stream", "text/html"])` × `st.binary()`
|
||||
- 验证:中间件输出的响应体与输入完全相同
|
||||
- **验证需求: 1.5, 1.6**
|
||||
|
||||
- [x] 1.7 编写单元测试:中间件跳过逻辑
|
||||
- 测试文件:`apps/backend/tests/unit/test_response_wrapper.py`
|
||||
- 用例:SSE 端点不包装;health 端点包装;非 2xx 不二次包装;中间件自身异常时透传原始响应
|
||||
- _需求: 1.5, 1.6_
|
||||
|
||||
- [x] 2. Pydantic Schema 统一 camelCase(T0-2)
|
||||
- [x] 2.1 创建 CamelModel 基类
|
||||
- 创建 `apps/backend/app/schemas/base.py`
|
||||
- 实现 `CamelModel(BaseModel)` 配置 `alias_generator=to_camel`、`populate_by_name=True`、`from_attributes=True`
|
||||
- _需求: 2.1_
|
||||
|
||||
- [x] 2.2 迁移现有 schema 到 CamelModel
|
||||
- `schemas/xcx_tasks.py`:`TaskListItem`、`AbandonRequest` 等所有类的 `BaseModel` → `CamelModel`
|
||||
- `schemas/xcx_auth.py`:所有 Auth schema 迁移
|
||||
- `schemas/xcx_notes.py`:所有 Notes schema 迁移
|
||||
- 确认路由函数 `response_model` 无需修改(Pydantic 自动使用 alias 序列化)
|
||||
- _需求: 2.2, 2.3_
|
||||
|
||||
- [x] 2.3 编写属性测试:camelCase 转换 Round Trip
|
||||
- **Property 4: camelCase 转换 Round Trip**
|
||||
- 测试文件:`tests/test_rns1_properties.py`
|
||||
- 生成器:`st.fixed_dictionaries` 生成随机 snake_case 字段名和值
|
||||
- 验证:`model_dump(by_alias=True)` → `Model(**camel_dict)` → 等于原始实例
|
||||
- **验证需求: 2.2, 2.4**
|
||||
|
||||
- [x] 2.4 编写单元测试:CamelModel 基类
|
||||
- 测试文件:`apps/backend/tests/unit/test_camel_model.py`
|
||||
- 用例:`TaskListItem` 序列化输出 camelCase 字段名;反序列化同时接受 snake_case 和 camelCase
|
||||
- _需求: 2.2, 2.4_
|
||||
|
||||
- [x] 3. 检查点 — 基础设施层验证
|
||||
- 确保所有测试通过,ask the user if questions arise.
|
||||
- 验证现有 16 个端点在中间件 + CamelModel 迁移后行为正常
|
||||
- 确认 JSON 响应格式为 `{ code: 0, data: { camelCaseFields... } }`
|
||||
|
||||
- [x] 4. 后端路由路径修正(T0-3)
|
||||
- [x] 4.1 修改路由路径和函数名
|
||||
- 文件:`apps/backend/app/routers/xcx_tasks.py`
|
||||
- `@router.post("/{task_id}/cancel-abandon")` → `@router.post("/{task_id}/restore")`
|
||||
- 函数名 `cancel_abandon` → `restore_task`(业务逻辑不变)
|
||||
- 移除旧路径,不保留兼容映射
|
||||
- _需求: 3.1, 3.2, 3.3_
|
||||
|
||||
- [x] 4.2 编写单元测试:路由路径修正
|
||||
- 测试文件:`apps/backend/tests/unit/test_xcx_tasks_route.py`
|
||||
- 用例:`POST /restore` 返回 200;`POST /cancel-abandon` 返回 404/405
|
||||
- _需求: 3.1, 3.3_
|
||||
|
||||
- [x] 5. 前端 request() 解包适配(T0-4)
|
||||
- [x] 5.1 修改 request() 函数添加 .data 解包逻辑
|
||||
- 文件:`apps/miniprogram/miniprogram/utils/request.ts`(或 `services/api.ts`,视实际位置)
|
||||
- `code === 0` → 返回 `res.data.data`(业务数据)
|
||||
- `code !== 0` → 抛出 `{ code, message }` 错误对象
|
||||
- 无 `code` 字段 → 直接返回原始响应体(SSE 等非标准格式)
|
||||
- 确保所有现有 API 调用(Auth/Tasks/Notes/AI Chat)解包后行为不变
|
||||
- _需求: 4.1, 4.2, 4.3, 4.4_
|
||||
|
||||
- [x] 5.2 编写属性测试:错误码解包抛出
|
||||
- **Property 5: 错误码解包抛出**
|
||||
- 测试文件:`tests/test_rns1_properties.py`
|
||||
- 生成器:`st.integers().filter(lambda x: x != 0)` × `st.text()`
|
||||
- 验证:解包函数对 `{ code: n, message: m }` 抛出异常,异常包含 code 和 message
|
||||
- **验证需求: 4.2**
|
||||
|
||||
- [x] 5.3 编写属性测试:非标准格式响应透传
|
||||
- **Property 6: 非标准格式响应透传**
|
||||
- 测试文件:`tests/test_rns1_properties.py`
|
||||
- 生成器:`st.dictionaries(st.text(), st.text()).filter(lambda d: "code" not in d)`
|
||||
- 验证:解包函数直接返回原始 dict
|
||||
- **验证需求: 4.3**
|
||||
|
||||
- [x] 6. 检查点 — 适配层验证
|
||||
- 确保所有测试通过,ask the user if questions arise.
|
||||
- 验证后端路由 `/restore` 可正常调用
|
||||
- 验证前端 request() 解包逻辑对成功/错误/非标准格式三种场景均正确
|
||||
|
||||
- [x] 7. API 契约完全重写(T0-5)
|
||||
- [x] 7.1 重写 BOARD-3 财务看板契约
|
||||
- 文件:`docs/miniprogram-dev/API-contract.md`
|
||||
- 定义 6 板块嵌套结构:overview / recharge / revenue / cashflow / expense / coachAnalysis
|
||||
- 定义请求参数枚举:time(8 种)、area(7 种)、compare(0/1)
|
||||
- 标注 `area≠all` 时 recharge 不返回;标注 `items_sum` 口径和助教费用拆分规则
|
||||
- 每个指标附带环比字段(`xxxCompare` + `isDown` + `isFlat`)
|
||||
- _需求: 5.1.1 ~ 5.1.10, 7.1 ~ 7.5_
|
||||
|
||||
- [x] 7.2 重写 BOARD-1 助教看板契约
|
||||
- 定义基础字段 + 4 维度专属字段(perf/salary/sv/task)
|
||||
- `skills` 类型为 `Array<{ text, cls }>`,`topCustomers` 类型为 `string[]`
|
||||
- 定义请求参数枚举:sort(6 种)、skill(5 种)、time(6 种)
|
||||
- 标注交叉约束:`time=last_6m` 与 `sort=sv_desc` 不兼容 → HTTP 400
|
||||
- _需求: 5.2.1 ~ 5.2.8, 7.1, 7.6_
|
||||
|
||||
- [x] 7.3 重写 BOARD-2 客户看板契约
|
||||
- 定义基础字段 + 8 维度专属字段(recall/potential/balance/recharge/recent/spend60/freq60/loyal)
|
||||
- `assistants` 含 heartScore/badge 等字段
|
||||
- 定义请求参数:dimension(8 种)、project(5 种)、page/pageSize(分页)
|
||||
- _需求: 5.3.1 ~ 5.3.11, 7.1_
|
||||
|
||||
- [x] 7.4 重写 CUST-1 客户详情契约
|
||||
- 补充 Banner 字段:balance/consumption60d/idealInterval/daysSinceVisit
|
||||
- 补充 aiInsight / coachTasks / favoriteCoaches / notes 模块
|
||||
- 消费记录重写为嵌套结构(含 coaches 子数组、tableFee/foodAmount 分项)
|
||||
- 标注会员信息 JOIN 规则(DQ-6)和 `items_sum` 口径
|
||||
- _需求: 5.4.1 ~ 5.4.7, 7.1 ~ 7.4_
|
||||
|
||||
- [x] 7.5 重写 COACH-1 助教详情契约
|
||||
- 补充 performance(6 指标)/ income(本月/上月各 4 项)/ tierNodes / historyMonths
|
||||
- 扩展 topCustomers(heartEmoji/score/balance)
|
||||
- 补充近期服务明细 perfHours/customerId
|
||||
- 任务分组补充 noteCount/pinned/notes/reason
|
||||
- 补充 notes 模块
|
||||
- _需求: 5.5.1 ~ 5.5.9, 7.1 ~ 7.4_
|
||||
|
||||
- [x] 7.6 重写 PERF-1 绩效概览契约
|
||||
- thisMonthRecords 改为 DateGroup 分组结构
|
||||
- 补充收入档位数据:currentTier/nextTier/upgradeHoursNeeded/upgradeBonus
|
||||
- 补充 lastMonthIncome、incomeItems.desc
|
||||
- 补充 newCustomers 和 regularCustomers 扩展字段
|
||||
- _需求: 5.6.1 ~ 5.6.5, 7.1_
|
||||
|
||||
- [x] 7.7 重写 TASK-1 绩效概览字段契约
|
||||
- performance 从 4 字段扩展为 15+ 字段
|
||||
- 补充 tierNodes/basicHours/bonusHours/currentTier/nextTierHours/tierCompleted/bonusMoney/incomeTrend/incomeTrendDir/prevMonth
|
||||
- 任务 item 补充 lastVisitDays/balance/aiSuggestion
|
||||
- _需求: 5.7.1, 5.7.2_
|
||||
|
||||
- [x] 7.8 重写 CHAT-1/2 对话模块契约
|
||||
- 统一时间字段名为 `createdAt`
|
||||
- CHAT-1 补充 `title` 字段
|
||||
- CHAT-2 补充 `referenceCard` 可选字段
|
||||
- 补充 SSE 流式端点定义:`POST /api/xcx/chat/stream`
|
||||
- 标注 `customerId` 查询参数支持
|
||||
- _需求: 5.8.1 ~ 5.8.5_
|
||||
|
||||
- [x] 7.9 契约一致性审查
|
||||
- 确保所有字段名统一 camelCase
|
||||
- 确保所有金额字段标注 `items_sum` 口径
|
||||
- 确保所有助教费用标注 `assistant_pd_money` + `assistant_cx_money` 拆分
|
||||
- 确保所有会员信息标注 `member_id` JOIN `dim_member` 规则
|
||||
- 每个接口标注数据源(FDW 表名或业务表名)
|
||||
- 对照前端 mock 数据结构逐字段比对,确保无遗漏或类型不匹配
|
||||
- _需求: 7.1 ~ 7.6_
|
||||
|
||||
- [x] 8. 检查点 — 契约文档验证
|
||||
- 确保所有测试通过,ask the user if questions arise.
|
||||
- 确认 8 个接口契约定义完整、字段名 camelCase、金额口径标注正确
|
||||
- 确认契约文档可作为后续子 spec(RNS1.1-1.4)的唯一实现基准
|
||||
|
||||
- [x] 9. 前端跨页面参数修复(T0-6)
|
||||
- [x] 9.1 修复 task-detail 页面跳转参数
|
||||
- 文件:`apps/miniprogram/miniprogram/pages/task-detail/task-detail.ts`
|
||||
- 跳转 chat 时传 `customerId={detail.customerId}`(而非 `detail.id`)
|
||||
- 跳转 customer-service-records 时传 `customerId={detail.customerId}`(而非 `detail.id`)
|
||||
- _需求: 6.1.1, 6.1.2_
|
||||
|
||||
- [x] 9.2 修复 customer-detail 页面跳转参数和 ID 获取方式
|
||||
- 文件:`apps/miniprogram/miniprogram/pages/customer-detail/customer-detail.ts`
|
||||
- 跳转 customer-service-records 时传 `customerId={detail.id}`
|
||||
- 跳转 chat 时传 `customerId={detail.id}`
|
||||
- `loadDetail()` 从 `onLoad(options)` 的 `options.id` 获取客户 ID,替代 `__route__` 解析
|
||||
- _需求: 6.2.1, 6.2.2, 6.2.3_
|
||||
|
||||
- [x] 9.3 修复 coach-detail 页面跳转参数
|
||||
- 文件:`apps/miniprogram/miniprogram/pages/coach-detail/coach-detail.ts`
|
||||
- 任务项跳转 customer-detail 时传 `id={customerId}`(而非 `name={customerName}`)
|
||||
- 依赖 COACH-1 响应中 TaskItem 包含 `customerId` 字段
|
||||
- _需求: 6.3.1_
|
||||
|
||||
- [x] 9.4 修复 performance 页面跳转参数
|
||||
- 文件:`apps/miniprogram/miniprogram/pages/performance/performance.ts`
|
||||
- 跳转 task-detail 时传 `id={taskId}`(而非 `customerName={name}`)
|
||||
- 依赖 PERF-1 响应中记录包含 `taskId` 字段
|
||||
- _需求: 6.4.1_
|
||||
|
||||
- [x] 9.5 实现 chat 页面多入口参数路由
|
||||
- 文件:`apps/miniprogram/miniprogram/pages/chat/chat.ts`
|
||||
- 支持 `customerId` 入口:使用 `customerId` 查询参数调用 CHAT 端点
|
||||
- 支持 `historyId` 入口:使用 `historyId` 作为 `chatId` 加载历史消息
|
||||
- 支持 `coachId` 入口:使用 `coachId` 作为上下文参数
|
||||
- _需求: 6.5.1, 6.5.2, 6.5.3_
|
||||
|
||||
- [x] 9.6 扩展 globalData.authUser 字段
|
||||
- 文件:`apps/miniprogram/miniprogram/app.ts`
|
||||
- 登录成功后将 `role`/`storeName`/`coachLevel`/`avatar` 存入 `globalData.authUser`
|
||||
- 各页面 Banner 直接从 `globalData.authUser` 读取,不再单独请求 `/me`
|
||||
- _需求: 6.6.1, 6.6.2_
|
||||
|
||||
- [x] 10. 最终检查点 — 全量验证
|
||||
- 确保所有测试通过,ask the user if questions arise.
|
||||
- 验证后端:中间件包装 + CamelModel + 路由修正 + 异常处理器全部生效
|
||||
- 验证前端:request() 解包 + 8 个跳转场景参数正确 + globalData 字段完整
|
||||
- 验证契约:8 个接口定义完整,可作为 RNS1.1-1.4 实现基准
|
||||
|
||||
## 备注
|
||||
|
||||
- 标记 `*` 的子任务为可选测试任务,可跳过以加速 MVP
|
||||
- 每个任务引用具体需求编号,确保可追溯
|
||||
- 属性测试使用 Hypothesis(`tests/test_rns1_properties.py`),单元测试使用 pytest(`apps/backend/tests/unit/`)
|
||||
- 检查点确保增量验证,避免问题累积
|
||||
- T0-5 契约重写为文档任务,产出物为 `docs/miniprogram-dev/API-contract.md`
|
||||
- T0-6 前端参数修复中部分跳转依赖后续子 spec 的 API 响应字段(如 COACH-1 的 `customerId`、PERF-1 的 `taskId`),当前先修改跳转代码,待后端实现后联调验证
|
||||
Reference in New Issue
Block a user