feat: chat integration, tenant admin spec, backend chat service, miniprogram updates, DEMO moved to tmp, XCX-TEST removed, migrations & docs
This commit is contained in:
201
.kiro/specs/rns1-chat-integration/requirements.md
Normal file
201
.kiro/specs/rns1-chat-integration/requirements.md
Normal file
@@ -0,0 +1,201 @@
|
||||
# 需求文档 — RNS1.4:CHAT 对齐与联调收尾
|
||||
|
||||
## 简介
|
||||
|
||||
RNS1.4 是 NS1 小程序后端 API 补全项目的最后一个子 spec,负责 CHAT 模块路径迁移和功能补全、FDW 端到端验证、以及全量前后端联调。本 spec 依赖 RNS1.0-1.3 全部完成,是整个 RNS1 系列的收尾阶段,确保 13 个页面全部连接真实后端运行,无 mock 数据残留。
|
||||
|
||||
### 依赖
|
||||
|
||||
- RNS1.0(基础设施与契约重写)— 全局响应包装中间件、camelCase 转换、重写后的 API 契约
|
||||
- RNS1.1(任务与绩效接口)— TASK-1/2、PERF-1/2、PIN 接口已实现
|
||||
- RNS1.2(客户与助教接口)— CUST-1/2、COACH-1 接口已实现
|
||||
- RNS1.3(三看板接口)— BOARD-1/2/3、CONFIG-1 接口已实现
|
||||
- 后端已有 `xcx_ai.py`(现有 `/api/ai/*` 路由,需迁移)
|
||||
- 前端已有 chat.ts 和 chat-history.ts 页面(P5.2 交付),当前使用 mock 数据和模拟流式输出
|
||||
|
||||
### 来源文档
|
||||
|
||||
- `docs/prd/Neo_Specs/RNS1-split-plan.md` — 拆分计划主文档
|
||||
- `docs/miniprogram-dev/API-contract.md` — API 契约(RNS1.0 T0-5 重写后版本)
|
||||
- `docs/prd/Neo_Specs/storyboard-walkthrough-assistant-view.md` — 助教视角走查报告(GAP-45~51)
|
||||
|
||||
## 术语表
|
||||
|
||||
- **Backend**:FastAPI 后端应用,位于 `apps/backend/`
|
||||
- **Miniprogram**:微信小程序前端应用,位于 `apps/miniprogram/`
|
||||
- **CHAT_1_API**:对话历史列表接口 `GET /api/xcx/chat/history`,返回分页的对话列表
|
||||
- **CHAT_2_API**:对话消息接口 `GET /api/xcx/chat/{chatId}/messages` 或 `GET /api/xcx/chat/messages?customerId={customerId}`,返回指定对话的消息列表
|
||||
- **CHAT_3_API**:发送消息接口 `POST /api/xcx/chat/{chatId}/messages`,发送用户消息并获取 AI 同步回复
|
||||
- **CHAT_4_SSE**:SSE 流式端点 `POST /api/xcx/chat/stream`,通过 Server-Sent Events 逐 token 返回 AI 回复
|
||||
- **SSE**:Server-Sent Events,服务端推送事件协议,用于 AI 流式回复的逐 token 输出
|
||||
- **referenceCard**:引用卡片,消息中附带的结构化上下文数据(类型/标题/摘要/键值对),用于展示客户概览等信息
|
||||
- **FDW**:PostgreSQL Foreign Data Wrapper,用于从业务库 `zqyy_app` 访问 ETL 库 `etl_feiqiu` 的数据
|
||||
- **chat_sessions**:业务库 `zqyy_app` 中的对话会话表,存储对话元数据
|
||||
- **chat_messages**:业务库 `zqyy_app` 中的消息表,存储对话消息内容
|
||||
- **Response_Wrapper**:RNS1.0 实现的全局响应包装中间件,`text/event-stream` 响应自动跳过包装
|
||||
- **items_sum**:DWD-DOC 强制使用的消费金额口径
|
||||
- **联调**:前后端联合调试,验证所有页面使用真实后端数据正常运行
|
||||
|
||||
## 需求
|
||||
|
||||
### 需求 1:CHAT 路径迁移(T4-1)
|
||||
|
||||
**用户故事:** 作为前后端开发者,我希望 CHAT 模块的 API 路径从 `/api/ai/*` 统一迁移到 `/api/xcx/chat/*`,以便与其他小程序接口保持一致的路径命名规范。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE Backend SHALL 将现有 `/api/ai/*` 路由全部迁移到 `/api/xcx/chat/*` 路径下,包括同步端点和 SSE 流式端点
|
||||
2. THE Backend SHALL 在迁移后移除原 `/api/ai/*` 路径,不保留旧路径的兼容映射
|
||||
3. THE Backend SHALL 将迁移后的路由注册到 `xcx_chat` router(或等效命名),与其他 `xcx_*` 路由模块保持一致的组织结构
|
||||
4. THE Miniprogram SHALL 更新 `services/api.ts` 中所有 CHAT 相关的 API 调用路径,从 `/api/ai/*` 改为 `/api/xcx/chat/*`
|
||||
5. WHEN CHAT_4_SSE 端点迁移到 `/api/xcx/chat/stream` 后,THE Response_Wrapper SHALL 继续对 `text/event-stream` 响应跳过包装(RNS1.0 已实现的行为不受路径变更影响)
|
||||
|
||||
### 需求 2:实现 CHAT-1 对话历史列表(T4-2 历史列表部分)
|
||||
|
||||
**用户故事:** 作为助教,我希望在对话历史页面看到所有对话记录(含对话标题和最后消息摘要),以便快速找到并继续之前的对话。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE CHAT_1_API SHALL 实现 `GET /api/xcx/chat/history` 端点,接受 `page`(默认 1)和 `pageSize`(默认 20)查询参数,返回分页的对话历史列表
|
||||
2. THE CHAT_1_API SHALL 为每条对话记录返回以下字段:`id`(对话 ID)、`title`(对话标题)、`customerName`(关联客户姓名,可选)、`lastMessage`(最后一条消息摘要)、`timestamp`(最后消息时间,ISO 8601 格式)、`unreadCount`(未读消息数)
|
||||
3. THE CHAT_1_API SHALL 从 `zqyy_app.chat_sessions` 查询对话列表,按最后消息时间倒序排列
|
||||
4. THE CHAT_1_API SHALL 为 `title` 字段生成对话标题:优先使用对话会话中存储的自定义标题,若无自定义标题则使用关联客户姓名,若均无则使用首条消息内容的前 20 个字符作为标题
|
||||
5. THE CHAT_1_API SHALL 返回标准分页字段:`total`(总记录数)、`page`(当前页码)、`pageSize`(每页条数)
|
||||
6. THE CHAT_1_API SHALL 通过当前登录用户的身份过滤对话列表,确保每位助教只能看到自己的对话记录
|
||||
|
||||
### 需求 3:实现 CHAT-2 对话消息查看(T4-2 消息查看部分)
|
||||
|
||||
**用户故事:** 作为助教,我希望查看指定对话的消息列表,以便回顾与 AI 助手的对话内容。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE CHAT_2_API SHALL 实现两个等效的消息查询端点:`GET /api/xcx/chat/{chatId}/messages`(通过对话 ID 查询)和 `GET /api/xcx/chat/messages?contextType={type}&contextId={id}`(通过上下文类型和 ID 查询)
|
||||
2. THE CHAT_2_API SHALL 接受 `page`(默认 1)和 `pageSize`(默认 50)查询参数,返回分页的消息列表
|
||||
3. THE CHAT_2_API SHALL 为每条消息返回以下字段:`id`(消息 ID)、`role`(`user` 或 `assistant`)、`content`(消息内容)、`createdAt`(创建时间,ISO 8601 格式)、`referenceCard`(引用卡片,可选)
|
||||
4. THE CHAT_2_API SHALL 统一使用 `createdAt` 作为消息时间字段名(替代前端使用的 `timestamp` 和旧契约的 `created_at`,遵循 camelCase 规范)
|
||||
5. THE CHAT_2_API SHALL 从 `zqyy_app.chat_messages` 查询消息列表,按 `createdAt` 正序排列(最早的消息在前)
|
||||
6. THE CHAT_2_API SHALL 在响应中返回 `chatId` 字段,供前端后续发送消息时使用(尤其是通过上下文入口时,前端需要获取对应的 `chatId`)
|
||||
7. THE CHAT_2_API SHALL 返回标准分页字段:`total`(总记录数)、`page`(当前页码)、`pageSize`(每页条数)
|
||||
|
||||
#### 3.2 上下文对话复用规则
|
||||
|
||||
8. WHEN 通过 `contextType` 和 `contextId` 查询参数调用消息端点时,THE CHAT_2_API SHALL 按以下规则查找或创建对话:
|
||||
- `contextType='task'`:查找同一用户、同一 `contextId`(taskId)的已有对话,找到则复用(无时限),找不到则新建
|
||||
- `contextType='customer'` 或 `contextType='coach'`:查找同一用户、同一 `contextId` 的已有对话,若最后消息时间 ≤ 3 天则复用,> 3 天或不存在则新建
|
||||
- `contextType='general'`:始终新建对话
|
||||
9. THE CHAT_2_API SHALL 在 `ai_conversations` 中记录 `context_type` 和 `context_id` 字段,用于后续对话查找
|
||||
10. THE CHAT_2_API SHALL 确保对话复用查找基于 `(user_id, site_id, context_type, context_id)` 组合,不同用户的对话互不影响
|
||||
|
||||
|
||||
### 需求 4:CHAT referenceCard 支持(T4-3)
|
||||
|
||||
**用户故事:** 作为助教,我希望在与 AI 助手对话时,消息中能附带客户概览卡片(含余额、消费、到店频次等键值对数据),以便在对话上下文中快速查看客户关键信息。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE CHAT_2_API SHALL 为消息返回可选的 `referenceCard` 字段,结构包含:`type`(引用类型,`customer` 或 `record` 枚举)、`title`(卡片标题,如 `"张伟 — 消费概览"`)、`summary`(摘要文字)、`data`(`Record<string, string>` 键值对详情,如 `{ "近30天消费": "¥2,380", "到店次数": "8次" }`)
|
||||
2. WHEN AI 助手回复消息涉及特定客户时,THE Backend SHALL 从 FDW 查询该客户的关键指标(余额、近期消费、到店频次等),组装为 `referenceCard` 附加到 AI 回复消息中
|
||||
3. THE Backend SHALL 将 `referenceCard` 数据持久化存储到 `chat_messages` 表中(作为 JSON 字段),以便历史消息查看时仍能展示引用卡片
|
||||
4. THE Miniprogram SHALL 在 chat 页面的消息列表中,检测消息的 `referenceCard` 字段,若存在则渲染为结构化卡片组件(标题 + 摘要 + 键值对列表)
|
||||
|
||||
#### 4.2 多入口参数路由(GAP-50)
|
||||
|
||||
5. THE Miniprogram SHALL 在 chat 页面的 `onLoad(options)` 中实现多入口参数路由逻辑,按以下优先级处理入口参数:
|
||||
- 若 `options.historyId` 存在(从 chat-history 跳转),使用 `historyId` 作为 `chatId` 直接加载历史消息
|
||||
- 若 `options.taskId` 存在(从 task-detail 跳转),使用 `contextType=task` + `contextId=taskId` 调用 CHAT_2_API,由后端查找同一任务的已有对话(始终复用,无时限)
|
||||
- 若 `options.customerId` 存在(从 customer-detail 跳转),使用 `contextType=customer` + `contextId=customerId` 调用 CHAT_2_API,由后端按 3 天时限判断复用或新建
|
||||
- 若 `options.coachId` 存在(从 coach-detail 跳转),使用 `contextType=coach` + `contextId=coachId` 调用 CHAT_2_API,由后端按 3 天时限判断复用或新建
|
||||
6. WHEN 通过上下文入口进入对话后,THE Miniprogram SHALL 将后端返回的 `chatId` 缓存到页面 data 中,后续发送消息和 SSE 流式请求均使用该 `chatId`
|
||||
7. IF chat 页面未收到任何入口参数(`historyId`/`taskId`/`customerId`/`coachId` 均为空),THEN THE Miniprogram SHALL 使用 `contextType=general` 调用 CHAT_2_API 创建一个通用对话
|
||||
|
||||
### 需求 5:CHAT-3 发送消息(T4-2 发送部分)
|
||||
|
||||
**用户故事:** 作为助教,我希望在对话页面发送消息后能立即收到 AI 的同步回复,以便在不支持 SSE 的场景下也能正常使用 AI 助手。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE CHAT_3_API SHALL 实现 `POST /api/xcx/chat/{chatId}/messages` 端点,接受请求体 `{ content: string }`
|
||||
2. THE CHAT_3_API SHALL 将用户消息存入 `chat_messages` 表,调用 AI 服务获取回复,将 AI 回复也存入 `chat_messages` 表
|
||||
3. THE CHAT_3_API SHALL 返回包含用户消息和 AI 回复的响应:`userMessage`(含 `id`/`content`/`createdAt`)和 `aiReply`(含 `id`/`content`/`createdAt`)
|
||||
4. THE CHAT_3_API SHALL 在发送消息后更新 `chat_sessions` 表的 `lastMessage` 和最后消息时间字段
|
||||
5. IF AI 服务调用失败或超时,THEN THE CHAT_3_API SHALL 仍保存用户消息,并返回 AI 回复为错误提示消息(如 `{ content: "抱歉,AI 助手暂时无法回复,请稍后重试" }`),HTTP 状态码保持 200
|
||||
6. THE CHAT_3_API SHALL 验证请求的 `chatId` 属于当前登录助教,不属于时返回 HTTP 403
|
||||
|
||||
### 需求 6:CHAT-4 SSE 流式端点(T4-1 SSE 部分)
|
||||
|
||||
**用户故事:** 作为助教,我希望 AI 助手的回复能以流式方式逐字显示,以便获得更自然的对话体验,减少等待感。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE CHAT_4_SSE SHALL 实现 `POST /api/xcx/chat/stream` 端点,接受请求体 `{ chatId: string, content: string }`,响应内容类型为 `text/event-stream`
|
||||
2. THE CHAT_4_SSE SHALL 发送以下三种 SSE 事件类型:
|
||||
- `event: message` — 逐 token 输出,`data` 为 `{"token": "<文本片段>"}`
|
||||
- `event: done` — 流结束,`data` 为 `{"messageId": "<完整消息ID>", "createdAt": "<ISO 8601>"}`
|
||||
- `event: error` — 错误,`data` 为 `{"message": "<错误描述>"}`
|
||||
3. THE CHAT_4_SSE SHALL 在流开始前将用户消息存入 `chat_messages` 表,在流结束后将完整的 AI 回复存入 `chat_messages` 表
|
||||
4. THE CHAT_4_SSE SHALL 在流结束后更新 `chat_sessions` 表的 `lastMessage` 和最后消息时间字段
|
||||
5. THE Response_Wrapper SHALL 对 `text/event-stream` 响应跳过全局包装,直接透传 SSE 事件流(RNS1.0 已实现)
|
||||
6. THE CHAT_4_SSE SHALL 验证请求的 `chatId` 属于当前登录助教,不属于时返回 HTTP 403(此时响应为普通 JSON 错误,非 SSE)
|
||||
7. THE Miniprogram SHALL 将 chat 页面现有的 `simulateStreamOutput()`(模拟逐字输出)替换为真实的 SSE 连接,通过 `wx.request` 或兼容方案接收 `text/event-stream` 响应
|
||||
|
||||
|
||||
### 需求 7:FDW 端到端验证(T4-4)
|
||||
|
||||
**用户故事:** 作为后端开发者,我希望验证所有 FDW 查询在测试环境链路(`test_zqyy_app` → `test_etl_feiqiu`)上正常工作,以便确保 RNS1.1-1.3 实现的所有接口在真实数据链路上不会因 FDW 连接、权限或性能问题而失败。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE Backend SHALL 验证所有 `fdw_etl.*` 视图在 `test_zqyy_app` 数据库中可正常访问,包括但不限于:`v_dws_assistant_salary_calc`、`v_dwd_assistant_service_log`、`v_dim_member`、`v_dim_assistant`、`v_dws_member_consumption_summary`、`v_dws_member_assistant_relation_index`、`v_dws_finance_*` 系列视图
|
||||
2. THE Backend SHALL 验证每个 FDW 视图的查询响应时间在可接受范围内(单次查询不超过 3 秒),对超时的查询记录慢查询日志并评估是否需要添加索引
|
||||
3. THE Backend SHALL 验证 FDW 查询在带有典型过滤条件(如 `assistant_id`、`member_id`、日期范围)时能正确返回数据,且结果集与直接查询 `test_etl_feiqiu` 的结果一致
|
||||
4. IF 某个 FDW 视图不存在或权限不足,THEN THE Backend SHALL 记录具体的错误信息(视图名、错误类型),并在验证报告中标注需要 DBA 介入修复
|
||||
5. THE Backend SHALL 检查 FDW 链路上的关键索引是否存在:`chat_sessions` 表的 `(assistant_id, customer_id)` 索引、`chat_messages` 表的 `(session_id, created_at)` 索引
|
||||
|
||||
### 需求 8:前端联调修复 — notes 页触底加载(T4-5 F11)
|
||||
|
||||
**用户故事:** 作为助教,我希望在备注列表页面滚动到底部时自动加载更多备注,以便查看全部备注记录而不需要手动翻页。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE Miniprogram SHALL 在 notes 页面实现 `onReachBottom()` 生命周期函数,当用户滚动到页面底部时自动请求下一页备注数据
|
||||
2. WHEN 触底加载触发时,THE Miniprogram SHALL 将 `page` 参数加 1,调用 `fetchNotes({ page, pageSize })` 接口,将返回的备注追加到已有列表末尾
|
||||
3. WHEN 后端返回的 `hasMore` 为 `false` 或返回的备注数量小于 `pageSize` 时,THE Miniprogram SHALL 停止触底加载,显示"没有更多了"提示
|
||||
4. THE Miniprogram SHALL 在触底加载过程中显示加载状态指示器,防止重复触发请求
|
||||
|
||||
### 需求 9:前端联调修复 — customer-service-records 按月请求(T4-5 F10)
|
||||
|
||||
**用户故事:** 作为助教,我希望客户服务记录页面在切换月份时向后端请求对应月份的数据,以便在数据量大时页面仍能快速响应,而不是全量加载后本地过滤。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE Miniprogram SHALL 修改 customer-service-records 页面的月份切换逻辑,从当前的"全量加载后本地过滤"改为"按月请求 API"
|
||||
2. WHEN 用户切换月份时,THE Miniprogram SHALL 使用新的 `year`/`month` 参数调用 `fetchCustomerRecords({ customerId, year, month })` 接口,加载对应月份的服务记录
|
||||
3. THE Miniprogram SHALL 在月份切换时清空已有记录列表,显示加载状态,待新数据返回后渲染
|
||||
4. THE Miniprogram SHALL 在首次加载时默认请求当前月份的数据,而非全量数据
|
||||
5. THE Backend SHALL 确保 CUST-2 接口支持 `year` 和 `month` 查询参数,仅返回指定月份的服务记录(RNS1.2 T2-4 已实现按月查询能力)
|
||||
|
||||
### 需求 10:全量前后端联调(T4-5 联调部分)
|
||||
|
||||
**用户故事:** 作为开发团队,我们希望 13 个小程序页面全部连接真实后端运行,无 mock 数据残留,以便确认整个应用在真实数据环境下功能完整、交互正常。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE Miniprogram SHALL 移除所有页面中的内联 mock 数据和 mock 数据导入(`import { mockXxx } from '../../utils/mock-data'`),全部替换为真实 API 调用
|
||||
2. THE Miniprogram SHALL 确保以下 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`
|
||||
3. WHEN 某个页面的 API 调用失败时,THE Miniprogram SHALL 显示友好的错误提示(如 Toast 或空状态占位),不出现白屏或未捕获异常
|
||||
4. THE Miniprogram SHALL 验证所有页面间的跳转参数传递正确(RNS1.0 T0-6 已修复的跨页面参数),确保目标页面能正确加载对应数据
|
||||
5. THE Miniprogram SHALL 验证 chat 页面从 4 个入口(task-detail、customer-detail、coach-detail、chat-history)进入时均能正确关联上下文并加载对应对话
|
||||
6. IF 联调过程中发现新的 Bug 或数据不一致问题,THEN THE Miniprogram 和 Backend SHALL 在本 spec 范围内修复,修复内容记录到联调问题清单中
|
||||
|
||||
### 需求 11:全局约束与权限控制
|
||||
|
||||
**用户故事:** 作为系统管理员,我希望所有 CHAT 接口遵循统一的权限控制和数据隔离规则,以确保每位助教只能访问自己的对话数据。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE Backend SHALL 对所有 RNS1.4 CHAT 接口(CHAT-1、CHAT-2、CHAT-3、CHAT-4)执行 `require_approved()` 权限检查,确保用户状态为 `approved`
|
||||
2. THE Backend SHALL 通过当前登录用户的身份信息过滤对话数据,确保每位助教只能访问自己创建的或与自己关联的对话
|
||||
3. IF 当前用户未通过审核(状态非 `approved`),THEN THE Backend SHALL 返回 HTTP 403 `{ code: 403, message: "用户未通过审核,无法访问此资源" }`
|
||||
4. THE Backend SHALL 对所有 CHAT 接口的响应字段名统一使用 camelCase 格式(与 RNS1.0 的 CamelCase_Converter 一致)
|
||||
5. THE Backend SHALL 确保 CHAT 模块的错误响应格式与全局异常处理器一致:`{ code: <HTTP状态码>, message: <错误详情> }`
|
||||
6. WHEN CHAT 模块查询 FDW 数据(如为 referenceCard 获取客户指标)时,THE Backend SHALL 遵循 DWD-DOC 强制规则:金额使用 `items_sum` 口径,会员信息通过 `member_id` JOIN `dim_member` 获取
|
||||
Reference in New Issue
Block a user