Files
Neo-ZQYY/.kiro/specs/rns1-chat-integration/tasks.md

312 lines
20 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.4 CHAT 对齐与联调收尾
## 概述
按照设计文档的 8 个组件将实施拆分为DDL 迁移 → 后端 Schema/Service/Router → FDW 验证 → 前端改造 → 联调收尾。每个任务增量构建确保无孤立代码。属性测试Hypothesis和单元测试作为可选子任务紧跟实现步骤。
## 任务
- [x] 1. DDL 迁移:扩展 ai_conversations 和 ai_messages 表
- [x] 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_card` jsonb 字段
- 创建索引 `idx_ai_conv_context` ON `(user_id, site_id, context_type, context_id, last_message_at DESC NULLS LAST) WHERE context_type IS NOT NULL`
- 创建排序索引 `idx_ai_conv_last_msg` ON `(user_id, site_id, last_message_at DESC NULLS LAST)`
- 添加 COMMENT ON COLUMN 注释
- _需求: R2.3, R3.8, R3.10, R4.3, R7.5_
- [x] 2. 后端 Pydantic Schema 定义(组件 3
- [x] 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_
- [x] 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_
- [x] 3. 后端 chat_service 业务逻辑层(组件 2
- [x] 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` 倒序JOIN `v_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 JSON
- `generate_title`:自定义标题 > 客户姓名 > 首条消息前 20 字
- _需求: R2.1-R2.6, R3.1-R3.10, R4.1-R4.3, R5.1-R5.6, R11.2, R11.6_
- [x] 3.2 编写属性测试:标题生成优先级
- **Property 4: 对话标题生成优先级**
- 使用 Hypothesis `st.fixed_dictionaries` 生成随机 title/customer_name/first_message 组合
- 验证:有 title 用 title否则用 customer_name否则用首条消息前 20 字,结果始终非空
- **验证: 需求 R2.4**
- [x] 3.3 编写属性测试:对话复用规则正确性
- **Property 6: 对话复用规则正确性**
- 使用 Hypothesis 生成随机 context_type/context_id/last_message_at 组合
- 验证task 入口同一 context_id 始终返回同一 chatIdcustomer/coach 入口 ≤ 3 天复用、> 3 天新建general 入口每次返回不同 chatId
- **验证: 需求 R3.8, R3.9, R3.10**
- [x] 3.4 编写属性测试referenceCard Round Trip
- **Property 7: referenceCard 持久化 Round Trip**
- 使用 Hypothesis 生成随机 referenceCard JSONtype/title/summary/data
- 验证JSON 序列化→存储→读取→反序列化等于原始对象
- **验证: 需求 R4.1, R4.3**
- [x] 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**
- [x] 3.6 编写单元测试AI 失败降级
- 测试文件 `apps/backend/tests/unit/test_xcx_chat_ai_fallback.py`
- 验证 AI 服务超时/异常时用户消息仍保存AI 回复为错误提示消息HTTP 200
- _需求: R5.5_
- [x] 4. 后端路由迁移与 CHAT-1/2/3/4 端点实现(组件 1
- [x] 4.1 创建 `apps/backend/app/routers/xcx_chat.py`,实现 CHAT-1/2/3/4 五个端点
- `GET /history` — CHAT-1 对话历史列表,调用 `chat_service.get_chat_history`
- `GET /{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_
- [x] 4.2 在 `apps/backend/app/main.py` 中注册 `xcx_chat.router`,移除 `xcx_ai_chat.router`
- 删除 `xcx_ai_chat.py` 文件(不保留旧路径兼容)
- _需求: R1.2, R1.3_
- [x] 4.3 编写属性测试SSE 事件类型有效性
- **Property 9: SSE 事件类型有效性**
- 使用 Hypothesis `st.sampled_from(["message", "done", "error"])` + 对应 data 结构
- 验证事件类型为三者之一data 结构符合定义message→token, done→messageId+createdAt, error→message
- **验证: 需求 R6.2**
- [x] 4.4 编写属性测试:列表排序不变量
- **Property 3: 列表排序不变量**
- 使用 Hypothesis `st.lists(st.datetimes())` 生成随机时间戳列表
- 验证CHAT-1 对话列表按时间倒序CHAT-2 消息列表按时间正序
- **验证: 需求 R2.3, R3.5**
- [x] 4.5 编写单元测试:路由迁移与权限控制
- 测试文件 `apps/backend/tests/unit/test_xcx_chat_routes.py`
- 验证 `/api/xcx/chat/history` 返回 200需认证`/api/ai/conversations` 返回 404
- 验证未审核用户收到 403chatId 不属于当前用户收到 403
- **Property 1: 路由迁移完整性** / **Property 5: 权限控制与数据隔离**
- **验证: 需求 R1.1, R1.2, R5.6, R6.6, R11.1, R11.3**
- [x] 5. 检查点 — 后端实现验证
- 确保所有后端测试通过ask the user if questions arise.
- [x] 6. FDW 端到端验证脚本(组件 7
- [x] 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_
- [x] 7. 前端 services/api.ts CHAT 模块对接(组件 6
- [x] 7.1 修改 `apps/miniprogram/miniprogram/services/api.ts`
- `fetchChatHistory()`:调用 `GET /api/xcx/chat/history`
- `fetchChatMessages(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_
- [x] 8. 前端 chat 页面改造(组件 4
- [x] 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_
- [x] 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_
- [x] 8.3 确保 chat 页面 referenceCard 渲染与真实 API 数据兼容
- 验证 `toDataList()` 和 WXML 模板能正确渲染后端返回的 referenceCard 结构
- _需求: R4.4_
- [x] 9. 前端 chat-history 页面改造(组件 5
- [x] 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_
- [x] 10. 前端联调修复(组件 8
- [x] 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_
- [x] 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_
- [x] 11. 检查点 — 前端改造验证
- 确保所有前端改造完成chat 页面 4 个入口task-detail、customer-detail、coach-detail、chat-history均能正确进入并加载对话。ask the user if questions arise.
- [x] 12. Mock 数据移除与全量联调
- [x] 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_
- [x] 12.2 验证全量联调
- 确保所有页面 API 调用失败时显示友好错误提示Toast 或空状态占位),不出现白屏
- 验证页面间跳转参数传递正确RNS1.0 T0-6 已修复的跨页面参数)
- 验证 chat 页面从 4 个入口进入时均能正确关联上下文
- _需求: R10.2, R10.3, R10.4, R10.5_
- [x] 13. 全链路端到端测试(真实 AI 接口)
- [x] 13.1 后端全链路测试:使用真实百炼 API 验证 CHAT-3同步和 CHAT-4SSE 流式)
- 启动后端服务连接 `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`
- 完整回复拼接后语义通顺
- 流结束后消息已落库
- 手动评估 AI 返回内容质量(至少 3 轮对话),记录评估结果
- _需求: R5.2, R5.4, R6.2, R6.3, R6.4, AC1, AC9_
- [x] 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_
- [x] 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 数据写入规则_
- [x] 14. DDL 迁移合并到主 DDL 基线
- [x] 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_
- [x] 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 基线同步_
- [x] 15. 文档更新落地
- [x] 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 强制要求_
- [x] 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_
- [x] 15.3 更新后端 API 参考 `apps/backend/docs/API-REFERENCE.md`
- 新增 `xcx_chat` 路由模块文档5 个端点)
- 移除 `xcx_ai_chat` 路由模块文档(已删除)
- _需求: R1.2, R1.3_
- [x] 15.4 更新后端 README `apps/backend/README.md`
- 路由模块摘要中:移除 `xcx_ai_chat`,新增 `xcx_chat`CHAT-1/2/3/4
- 服务层中:新增 `chat_service.py` 说明
- _需求: R1.3_
- [x] 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 归档规则_
- [x] 15.6 更新 RNS1 拆分计划 `docs/prd/Neo_Specs/RNS1-split-plan.md`
- 标注 RNS1.4 状态为"实施中"或"已完成"(视进度)
- _需求: 项目追踪_
- [x] 16. 最终检查点 — 全量验证
- 确保所有测试通过13 个页面均连接真实后端运行,无 mock 数据残留
- 确保 DDL 迁移已合并到主基线BD 手册已同步更新
- 确保 API 契约、后端 README、文档地图均已更新
- 确保 AI 对话落库符合 P5 PRD 规范
- ask the user if questions arise.
## 备注
- 标记 `*` 的子任务为可选,可跳过以加速 MVP 交付
- 每个任务引用了具体的需求编号R1-R11以确保可追溯性
- 属性测试验证通用正确性属性Property 1-9单元测试验证具体边界条件
- 检查点任务确保增量验证,避免问题累积
- 后端使用 PythonFastAPI + 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`
- **对话复用规则**(用户已确认,覆盖 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 |