Files
Neo-ZQYY/.kiro/specs/rns1-infra-contract-rewrite/tasks.md

252 lines
13 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.0 基础设施与契约重写
## 概述
按依赖关系排序实现 6 个主任务T0-1全局中间件和 T0-2CamelModel为基础设施层必须先行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 统一 camelCaseT0-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
- 定义请求参数枚举time8 种、area7 种、compare0/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[]`
- 定义请求参数枚举sort6 种、skill5 种、time6 种)
- 标注交叉约束:`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 等字段
- 定义请求参数dimension8 种、project5 种、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 助教详情契约
- 补充 performance6 指标)/ income本月/上月各 4 项)/ tierNodes / historyMonths
- 扩展 topCustomersheartEmoji/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、金额口径标注正确
- 确认契约文档可作为后续子 specRNS1.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`),当前先修改跳转代码,待后端实现后联调验证