13 KiB
实现计划:RNS1.0 基础设施与契约重写
概述
按依赖关系排序实现 6 个主任务:T0-1(全局中间件)和 T0-2(CamelModel)为基础设施层,必须先行;T0-3(路由修正)和 T0-4(前端解包)为适配层;T0-5(契约重写)为文档层;T0-6(参数修复)为前端修复层。属性测试使用 Hypothesis,单元测试使用 pytest。
任务
-
1. 全局响应包装中间件 + 异常处理器(T0-1)
-
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
- 创建
-
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
- 在同一文件中实现
-
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
-
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
-
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
-
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
-
1.7 编写单元测试:中间件跳过逻辑
- 测试文件:
apps/backend/tests/unit/test_response_wrapper.py - 用例:SSE 端点不包装;health 端点包装;非 2xx 不二次包装;中间件自身异常时透传原始响应
- 需求: 1.5, 1.6
- 测试文件:
-
-
2. Pydantic Schema 统一 camelCase(T0-2)
-
2.1 创建 CamelModel 基类
- 创建
apps/backend/app/schemas/base.py - 实现
CamelModel(BaseModel)配置alias_generator=to_camel、populate_by_name=True、from_attributes=True - 需求: 2.1
- 创建
-
2.2 迁移现有 schema 到 CamelModel
schemas/xcx_tasks.py:TaskListItem、AbandonRequest等所有类的BaseModel→CamelModelschemas/xcx_auth.py:所有 Auth schema 迁移schemas/xcx_notes.py:所有 Notes schema 迁移- 确认路由函数
response_model无需修改(Pydantic 自动使用 alias 序列化) - 需求: 2.2, 2.3
-
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
-
2.4 编写单元测试:CamelModel 基类
- 测试文件:
apps/backend/tests/unit/test_camel_model.py - 用例:
TaskListItem序列化输出 camelCase 字段名;反序列化同时接受 snake_case 和 camelCase - 需求: 2.2, 2.4
- 测试文件:
-
-
3. 检查点 — 基础设施层验证
- 确保所有测试通过,ask the user if questions arise.
- 验证现有 16 个端点在中间件 + CamelModel 迁移后行为正常
- 确认 JSON 响应格式为
{ code: 0, data: { camelCaseFields... } }
-
4. 后端路由路径修正(T0-3)
-
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
- 文件:
-
4.2 编写单元测试:路由路径修正
- 测试文件:
apps/backend/tests/unit/test_xcx_tasks_route.py - 用例:
POST /restore返回 200;POST /cancel-abandon返回 404/405 - 需求: 3.1, 3.3
- 测试文件:
-
-
5. 前端 request() 解包适配(T0-4)
-
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
- 文件:
-
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
-
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
-
-
6. 检查点 — 适配层验证
- 确保所有测试通过,ask the user if questions arise.
- 验证后端路由
/restore可正常调用 - 验证前端 request() 解包逻辑对成功/错误/非标准格式三种场景均正确
-
7. API 契约完全重写(T0-5)
-
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
- 文件:
-
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
-
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
-
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
-
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
-
7.6 重写 PERF-1 绩效概览契约
- thisMonthRecords 改为 DateGroup 分组结构
- 补充收入档位数据:currentTier/nextTier/upgradeHoursNeeded/upgradeBonus
- 补充 lastMonthIncome、incomeItems.desc
- 补充 newCustomers 和 regularCustomers 扩展字段
- 需求: 5.6.1 ~ 5.6.5, 7.1
-
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
-
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
- 统一时间字段名为
-
7.9 契约一致性审查
- 确保所有字段名统一 camelCase
- 确保所有金额字段标注
items_sum口径 - 确保所有助教费用标注
assistant_pd_money+assistant_cx_money拆分 - 确保所有会员信息标注
member_idJOINdim_member规则 - 每个接口标注数据源(FDW 表名或业务表名)
- 对照前端 mock 数据结构逐字段比对,确保无遗漏或类型不匹配
- 需求: 7.1 ~ 7.6
-
-
8. 检查点 — 契约文档验证
- 确保所有测试通过,ask the user if questions arise.
- 确认 8 个接口契约定义完整、字段名 camelCase、金额口径标注正确
- 确认契约文档可作为后续子 spec(RNS1.1-1.4)的唯一实现基准
-
9. 前端跨页面参数修复(T0-6)
-
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
- 文件:
-
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
- 文件:
-
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
- 文件:
-
9.4 修复 performance 页面跳转参数
- 文件:
apps/miniprogram/miniprogram/pages/performance/performance.ts - 跳转 task-detail 时传
id={taskId}(而非customerName={name}) - 依赖 PERF-1 响应中记录包含
taskId字段 - 需求: 6.4.1
- 文件:
-
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
- 文件:
-
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
- 文件:
-
-
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),当前先修改跳转代码,待后端实现后联调验证