20 KiB
实施计划:RNS1.4 CHAT 对齐与联调收尾
概述
按照设计文档的 8 个组件,将实施拆分为:DDL 迁移 → 后端 Schema/Service/Router → FDW 验证 → 前端改造 → 联调收尾。每个任务增量构建,确保无孤立代码。属性测试(Hypothesis)和单元测试作为可选子任务紧跟实现步骤。
任务
-
1. DDL 迁移:扩展 ai_conversations 和 ai_messages 表
- 1.1 创建 DDL 迁移脚本
db/zqyy_app/migrations/2026-03-20__rns14_chat_module_extend.sql- ALTER TABLE
biz.ai_conversations新增context_type varchar(20)、context_id varchar(50)、title varchar(200)、last_message text、last_message_at timestamptz五个字段 - ALTER TABLE
biz.ai_messages新增reference_cardjsonb 字段 - 创建索引
idx_ai_conv_contextON(user_id, site_id, context_type, context_id, last_message_at DESC NULLS LAST) WHERE context_type IS NOT NULL - 创建排序索引
idx_ai_conv_last_msgON(user_id, site_id, last_message_at DESC NULLS LAST) - 添加 COMMENT ON COLUMN 注释
- 需求: R2.3, R3.8, R3.10, R4.3, R7.5
- ALTER TABLE
- 1.1 创建 DDL 迁移脚本
-
2. 后端 Pydantic Schema 定义(组件 3)
- 2.1 创建
apps/backend/app/schemas/xcx_chat.py- 继承
CamelModel基类,定义ChatHistoryItem、ChatHistoryResponse、ReferenceCard、ChatMessageItem、ChatMessagesResponse、SendMessageRequest、SendMessageResponse、MessageBrief、ChatStreamRequest - 字段类型和可选性严格遵循设计文档组件 3 定义
- 需求: R2.2, R3.3, R4.1, R5.1, R5.3, R6.1, R11.4
- 继承
- 2.2 编写 Schema 序列化单元测试
apps/backend/tests/unit/test_xcx_chat_schema.py- 验证 ChatHistoryItem / ChatMessageItem / SendMessageResponse 的 camelCase 序列化输出
- 验证 ReferenceCard 可选字段为 None 时不报错
- 需求: R2.2, R3.3, R5.3, R11.4
- 2.1 创建
-
3. 后端 chat_service 业务逻辑层(组件 2)
-
3.1 创建
apps/backend/app/services/chat_service.py- 实现
ChatService类,包含get_chat_history、get_or_create_session、get_messages、send_message_sync、build_reference_card、generate_title方法 get_chat_history:查询biz.ai_conversations,按last_message_at倒序,JOINv_dim_member获取customerName,分页返回get_or_create_session:按(user_id, site_id, context_type, context_id)查找或创建对话。复用规则:task 入口始终复用(无时限);customer/coach 入口 ≤ 3 天复用、> 3 天新建;general 入口始终新建get_messages:查询biz.ai_messages,按created_at正序,验证 chatId 归属当前用户send_message_sync:存入用户消息 → 调用 AI → 存入 AI 回复 → 更新 session 元数据;AI 失败时返回错误提示消息(HTTP 200)- ⚠️ P5 PRD 合规:对话落库必须遵循
docs/prd/specs/P5-miniapp-ai-integration.md数据写入规则:app_id固定为app1_chat- 用户消息发送时即写入
ai_messages(role=user) - 流式完成后完整 assistant 回复写入
ai_messages(role=assistant),含tokens_used - 首条消息为页面上下文 JSON(
current_time/source_page/page_context/screen_content) get_or_create_session仅用于 task/customer/coach 入口的对话复用(task 无时限,customer/coach 3 天时限);general 入口始终新建(保持 P5 PRD 兼容)
build_reference_card:从 FDW 查询客户指标(items_sum口径),组装 referenceCard JSONgenerate_title:自定义标题 > 客户姓名 > 首条消息前 20 字- 需求: R2.1-R2.6, R3.1-R3.10, R4.1-R4.3, R5.1-R5.6, R11.2, R11.6
- 实现
-
3.2 编写属性测试:标题生成优先级
- Property 4: 对话标题生成优先级
- 使用 Hypothesis
st.fixed_dictionaries生成随机 title/customer_name/first_message 组合 - 验证:有 title 用 title,否则用 customer_name,否则用首条消息前 20 字,结果始终非空
- 验证: 需求 R2.4
-
3.3 编写属性测试:对话复用规则正确性
- Property 6: 对话复用规则正确性
- 使用 Hypothesis 生成随机 context_type/context_id/last_message_at 组合
- 验证:task 入口同一 context_id 始终返回同一 chatId;customer/coach 入口 ≤ 3 天复用、> 3 天新建;general 入口每次返回不同 chatId
- 验证: 需求 R3.8, R3.9, R3.10
-
3.4 编写属性测试:referenceCard Round Trip
- Property 7: referenceCard 持久化 Round Trip
- 使用 Hypothesis 生成随机 referenceCard JSON(type/title/summary/data)
- 验证:JSON 序列化→存储→读取→反序列化等于原始对象
- 验证: 需求 R4.1, R4.3
-
3.5 编写属性测试:消息持久化与会话元数据更新
- Property 8: 消息持久化与会话元数据更新
- 使用 Hypothesis
st.text(min_size=1, max_size=500)生成随机消息内容 - 验证:发送后 ai_messages 包含用户消息和 AI 回复,session 的 last_message 和 last_message_at 已更新
- 验证: 需求 R5.2, R5.4, R6.3, R6.4
-
3.6 编写单元测试:AI 失败降级
- 测试文件
apps/backend/tests/unit/test_xcx_chat_ai_fallback.py - 验证 AI 服务超时/异常时,用户消息仍保存,AI 回复为错误提示消息,HTTP 200
- 需求: R5.5
- 测试文件
-
-
4. 后端路由迁移与 CHAT-1/2/3/4 端点实现(组件 1)
-
4.1 创建
apps/backend/app/routers/xcx_chat.py,实现 CHAT-1/2/3/4 五个端点GET /history— CHAT-1 对话历史列表,调用chat_service.get_chat_historyGET /{chat_id}/messages— CHAT-2a 通过 chatId 查询消息GET /messages?contextType=&contextId=— CHAT-2b 通过上下文查询消息(按复用规则自动查找/创建对话)POST /{chat_id}/messages— CHAT-3 发送消息(同步回复)POST /stream— CHAT-4 SSE 流式端点,返回StreamingResponse(media_type="text/event-stream")- 所有端点使用
Depends(require_approved())权限检查 - chatId 归属验证:CHAT-3/4 不属于当前用户返回 HTTP 403
- 需求: R1.1, R1.3, R2.1, R3.1, R5.1, R6.1, R11.1, R11.2
-
4.2 在
apps/backend/app/main.py中注册xcx_chat.router,移除xcx_ai_chat.router- 删除
xcx_ai_chat.py文件(不保留旧路径兼容) - 需求: R1.2, R1.3
- 删除
-
4.3 编写属性测试:SSE 事件类型有效性
- Property 9: SSE 事件类型有效性
- 使用 Hypothesis
st.sampled_from(["message", "done", "error"])+ 对应 data 结构 - 验证:事件类型为三者之一,data 结构符合定义(message→token, done→messageId+createdAt, error→message)
- 验证: 需求 R6.2
-
4.4 编写属性测试:列表排序不变量
- Property 3: 列表排序不变量
- 使用 Hypothesis
st.lists(st.datetimes())生成随机时间戳列表 - 验证:CHAT-1 对话列表按时间倒序,CHAT-2 消息列表按时间正序
- 验证: 需求 R2.3, R3.5
-
4.5 编写单元测试:路由迁移与权限控制
- 测试文件
apps/backend/tests/unit/test_xcx_chat_routes.py - 验证
/api/xcx/chat/history返回 200(需认证);/api/ai/conversations返回 404 - 验证未审核用户收到 403;chatId 不属于当前用户收到 403
- Property 1: 路由迁移完整性 / Property 5: 权限控制与数据隔离
- 验证: 需求 R1.1, R1.2, R5.6, R6.6, R11.1, R11.3
- 测试文件
-
-
5. 检查点 — 后端实现验证
- 确保所有后端测试通过,ask the user if questions arise.
-
6. FDW 端到端验证脚本(组件 7)
- 6.1 创建
scripts/ops/verify_fdw_e2e.py- 验证所有
fdw_etl.*视图在test_zqyy_app中可访问(SELECT 1 FROM ... LIMIT 1) - 验证带典型过滤条件(assistant_id、member_id、日期范围)的查询响应时间 < 3s
- 检查关键索引存在:
chat_sessions(assistant_id, customer_id)、chat_messages(session_id, created_at) - 输出结构化 JSON 报告,失败项标注需 DBA 介入
- 使用
load_dotenv加载根.env,连接test_zqyy_app(遵循 testing-env.md 规范) - 需求: R7.1, R7.2, R7.3, R7.4, R7.5
- 验证所有
- 6.1 创建
-
7. 前端 services/api.ts CHAT 模块对接(组件 6)
- 7.1 修改
apps/miniprogram/miniprogram/services/api.tsfetchChatHistory():调用GET /api/xcx/chat/historyfetchChatMessages(chatId):调用GET /api/xcx/chat/{chatId}/messages- 新增
fetchChatMessagesByContext(contextType, contextId):调用GET /api/xcx/chat/messages?contextType={type}&contextId={id} sendChatMessage(chatId, content):调用POST /api/xcx/chat/{chatId}/messages- 移除所有 CHAT 相关 mock 数据导入,
USE_REAL_API对 CHAT 模块设为true - 需求: R1.4, R10.1
- 7.1 修改
-
8. 前端 chat 页面改造(组件 4)
-
8.1 修改
apps/miniprogram/miniprogram/pages/chat/chat.ts实现多入口参数路由onLoad(options)中按优先级处理:historyId→taskId→customerId→coachId→ 无参数(通用对话)historyId入口:直接用作 chatId 加载历史消息taskId入口:调用fetchChatMessagesByContext('task', taskId),同一 taskId 始终复用同一对话(无时限)customerId入口:调用fetchChatMessagesByContext('customer', customerId),≤ 3 天复用、> 3 天新建coachId入口:调用fetchChatMessagesByContext('coach', coachId),≤ 3 天复用、> 3 天新建- 无参数入口:调用
fetchChatMessagesByContext('general', ''),始终新建 - 需求: R4.5, R4.6, R4.7
-
8.2 修改 chat.ts 将
simulateStreamOutput()替换为真实 SSE 连接- 使用
wx.request+enableChunked: true接收POST /api/xcx/chat/stream的 SSE 响应 - 解析
event: message(逐 token 追加)、event: done(流结束)、event: error(错误处理) - 移除
simulateStreamOutput()和mockAIReplies相关代码 - SSE 连接中断时显示"连接中断"提示,允许重试
- 需求: R6.7, R10.1
- 使用
-
8.3 确保 chat 页面 referenceCard 渲染与真实 API 数据兼容
- 验证
toDataList()和 WXML 模板能正确渲染后端返回的 referenceCard 结构 - 需求: R4.4
- 验证
-
-
9. 前端 chat-history 页面改造(组件 5)
- 9.1 修改
apps/miniprogram/miniprogram/pages/chat-history/chat-history.ts- 移除
mockChatHistory导入,调用fetchChatHistory()获取真实数据 - 响应字段映射:后端
timestamp(ISO 8601)→formatRelativeTime()处理 - 点击对话项跳转 chat 页面时传递
historyId参数 - 需求: R2.1, R10.1, R10.2
- 移除
- 9.1 修改
-
10. 前端联调修复(组件 8)
-
10.1 修改 notes 页面实现触底加载
- 在
apps/miniprogram/miniprogram/pages/notes/notes.ts实现onReachBottom()生命周期函数 - 维护
page状态,触底时page++调用fetchNotes({ page, pageSize }) - 追加数据到已有列表,
hasMore === false时停止加载并显示"没有更多了" - 加载过程中显示加载状态指示器,防止重复触发
- 需求: R8.1, R8.2, R8.3, R8.4
- 在
-
10.2 修改 customer-service-records 页面实现按月请求
- 在
apps/miniprogram/miniprogram/pages/customer-service-records/customer-service-records.ts修改月份切换逻辑 - 月份切换时调用
fetchCustomerRecords({ customerId, year, month }),清空列表 → loading → 渲染 - 首次加载默认当前月份数据
- 需求: R9.1, R9.2, R9.3, R9.4
- 在
-
-
11. 检查点 — 前端改造验证
- 确保所有前端改造完成,chat 页面 4 个入口(task-detail、customer-detail、coach-detail、chat-history)均能正确进入并加载对话。ask the user if questions arise.
-
12. Mock 数据移除与全量联调
-
12.1 移除所有页面中的 mock 数据残留
- 搜索并移除所有
import { mockXxx } from '../../utils/mock-data'引用 - 确保 13 个页面(task-list、task-detail、notes、performance、performance-records、customer-detail、customer-service-records、coach-detail、board-coach、board-customer、board-finance、chat-history、chat)均使用真实 API
- 需求: R10.1, R10.2
- 搜索并移除所有
-
12.2 验证全量联调
- 确保所有页面 API 调用失败时显示友好错误提示(Toast 或空状态占位),不出现白屏
- 验证页面间跳转参数传递正确(RNS1.0 T0-6 已修复的跨页面参数)
- 验证 chat 页面从 4 个入口进入时均能正确关联上下文
- 需求: R10.2, R10.3, R10.4, R10.5
-
-
13. 全链路端到端测试(真实 AI 接口)
-
13.1 后端全链路测试:使用真实百炼 API 验证 CHAT-3(同步)和 CHAT-4(SSE 流式)
- 启动后端服务连接
test_zqyy_app,使用真实测试用户 token - 调用
POST /api/xcx/chat/{chatId}/messages发送真实消息,验证:- AI 回复内容质量(非乱码、语义相关、中文正常)
- 用户消息和 AI 回复均已持久化到
biz.ai_messages biz.ai_conversations的last_message和last_message_at已更新tokens_used字段已记录
- 调用
POST /api/xcx/chat/stream验证 SSE 流式返回:- 逐 token 事件格式正确(
event: message、event: done) - 完整回复拼接后语义通顺
- 流结束后消息已落库
- 逐 token 事件格式正确(
- 手动评估 AI 返回内容质量(至少 3 轮对话),记录评估结果
- 需求: R5.2, R5.4, R6.2, R6.3, R6.4, AC1, AC9
- 启动后端服务连接
-
13.2 前端→后端→AI→数据库全链路验证
- 在微信开发者工具中启动小程序,连接本地后端
- 从 4 个入口(task-detail、customer-detail、coach-detail、chat-history)进入 chat 页面
- 每个入口发送至少 1 条消息,验证:
- SSE 流式逐字显示正常
- 消息发送后页面状态正确(loading → 显示回复)
- 返回 chat-history 页面能看到刚才的对话记录
- referenceCard 在有客户关联时正确渲染(如有数据)
- 验证错误场景:网络断开时的提示、空消息拦截
- 需求: R6.7, R10.5, R4.4
-
13.3 AI 对话落库合规性验证(对照 P5 PRD + 用户确认的复用规则)
- 验证对话复用规则:task 入口同一 taskId 始终复用;customer/coach 入口 ≤ 3 天复用、> 3 天新建;general 入口始终新建
- 验证首条消息格式:应用 1 的首条消息应为页面上下文 JSON(
source_page/page_context/screen_content/current_time),对照 P5 PRD 应用 1 Prompt 数据结构 - 验证
app_id字段:CHAT 模块对话的app_id应为app1_chat - 验证
ai_messages.role值:仅user/assistant/system三种(CHECK 约束) - 验证
tokens_used记录:AI 回复消息应记录 token 消耗量 - 验证
source_page和source_context:从不同入口进入时应正确记录来源页面和上下文 - 验证
context_type和context_id:不同入口写入正确的上下文类型和 ID - 需求: AC9, P5-PRD 数据写入规则
-
-
14. DDL 迁移合并到主 DDL 基线
-
14.1 执行迁移脚本到
test_zqyy_app- 运行
db/zqyy_app/migrations/2026-03-20__rns14_chat_module_extend.sql(任务 1 创建的脚本) - 验证新字段和索引已正确创建(使用 BD 手册中的验证 SQL)
- 需求: R2.3, R3.8, R3.10, R4.3, R7.5
- 运行
-
14.2 合并到主 DDL 基线
docs/database/ddl/zqyy_app__biz.sql- 在
biz.ai_conversations表定义中追加 5 个新字段:context_type varchar(20)、context_id varchar(50)、title varchar(200)、last_message text、last_message_at timestamptz - 在
biz.ai_messages表定义中追加 1 个新字段:reference_card jsonb - 在索引区追加 2 个新索引:
idx_ai_conv_context(条件索引,WHERE context_type IS NOT NULL)、idx_ai_conv_last_msg - 更新文件头部的"生成日期"注释
- 需求: DDL 基线同步
- 在
-
-
15. 文档更新落地
-
15.1 更新 BD 手册
docs/database/BD_Manual_ai_tables.md- 在
biz.ai_conversations字段明细中追加 5 个新字段(context_type/context_id/title/last_message/last_message_at) - 在
biz.ai_messages字段明细中追加 reference_card 字段 - 在约束与索引表中追加 2 个新索引(idx_ai_conv_context、idx_ai_conv_last_msg)
- 更新兼容性影响:标注 RNS1.4 CHAT 模块依赖新字段
- 更新验证 SQL:追加新字段和索引的验证查询
- 更新回滚策略:追加 DROP INDEX 和 ALTER TABLE DROP COLUMN
- 规范: db-docs.md 强制要求
- 在
-
15.2 更新 API 契约
docs/miniprogram-dev/API-contract.md- CHAT 部分路径从
/api/ai/*更新为/api/xcx/chat/* - 补充 CHAT-1(历史列表)、CHAT-2a/2b(消息查询,含 customerId 参数)、CHAT-3(发送消息)端点定义
- 补充 referenceCard 结构定义
- 补充 SSE 事件类型定义(message/done/error)
- 需求: R1.1, R1.4
- CHAT 部分路径从
-
15.3 更新后端 API 参考
apps/backend/docs/API-REFERENCE.md- 新增
xcx_chat路由模块文档(5 个端点) - 移除
xcx_ai_chat路由模块文档(已删除) - 需求: R1.2, R1.3
- 新增
-
15.4 更新后端 README
apps/backend/README.md- 路由模块摘要中:移除
xcx_ai_chat,新增xcx_chat(CHAT-1/2/3/4) - 服务层中:新增
chat_service.py说明 - 需求: R1.3
- 路由模块摘要中:移除
-
15.5 更新文档地图
docs/DOCUMENTATION-MAP.md- 在 3.1 FastAPI 后端部分新增 RNS1.4 模块(xcx_chat.py、chat_service.py、xcx_chat schema)
- 在 5.6 Spec 文件表中新增
rns1-chat-integration条目 - 更新"最后更新"日期
- 规范: doc-map.md 归档规则
-
15.6 更新 RNS1 拆分计划
docs/prd/Neo_Specs/RNS1-split-plan.md- 标注 RNS1.4 状态为"实施中"或"已完成"(视进度)
- 需求: 项目追踪
-
-
16. 最终检查点 — 全量验证
- 确保所有测试通过,13 个页面均连接真实后端运行,无 mock 数据残留
- 确保 DDL 迁移已合并到主基线,BD 手册已同步更新
- 确保 API 契约、后端 README、文档地图均已更新
- 确保 AI 对话落库符合 P5 PRD 规范
- ask the user if questions arise.
备注
- 标记
*的子任务为可选,可跳过以加速 MVP 交付 - 每个任务引用了具体的需求编号(R1-R11)以确保可追溯性
- 属性测试验证通用正确性属性(Property 1-9),单元测试验证具体边界条件
- 检查点任务确保增量验证,避免问题累积
- 后端使用 Python(FastAPI + Pydantic),前端使用 TypeScript(微信小程序)
PRD 合规注意事项
- P5 PRD 数据写入规则(
docs/prd/specs/P5-miniapp-ai-integration.md):- 流式返回完成后,完整 assistant 回复写入
ai_messages(role=assistant) - 用户消息在发送时即写入(role=user)
- 所有 AI 调用记录写入
ai_conversations+ai_messages(含 tokens_used 统计) - 首条消息为页面上下文 JSON(
source_page/page_context/screen_content/current_time) app_id固定为app1_chat
- 流式返回完成后,完整 assistant 回复写入
- 对话复用规则(用户已确认,覆盖 P5 PRD 的"始终新建"规则):
task入口:同一 taskId 始终复用同一对话(无时限)customer/coach入口:最后消息 ≤ 3 天复用,> 3 天新建general入口(无参数):始终新建chat-history入口:直接打开已有对话(传 historyId)
文档更新清单
| 文档 | 更新内容 | 任务 |
|---|---|---|
docs/database/ddl/zqyy_app__biz.sql |
合并 5+1 新字段、2 新索引 | 14.2 |
docs/database/BD_Manual_ai_tables.md |
新字段(context_type/context_id/title/last_message/last_message_at/reference_card)/索引/兼容性/回滚/验证 SQL | 15.1 |
docs/miniprogram-dev/API-contract.md |
CHAT 路径迁移 + 新端点定义 | 15.2 |
apps/backend/docs/API-REFERENCE.md |
xcx_chat 路由模块文档 | 15.3 |
apps/backend/README.md |
路由模块 + 服务层更新 | 15.4 |
docs/DOCUMENTATION-MAP.md |
RNS1.4 模块 + spec 条目 | 15.5 |
docs/prd/Neo_Specs/RNS1-split-plan.md |
RNS1.4 状态更新 | 15.6 |