docs(testing): e2e verification report for send routing with WECOM_BOT
This commit is contained in:
@@ -0,0 +1,333 @@
|
||||
# 发送路由端到端验证实施计划
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 通过 admin-web UI 走通「内部群 → WECOM_BOT → 发送 → 查询状态」链路,验证自动路由可用,并修复沿途问题。
|
||||
|
||||
**Architecture:** 基于现有后台能力(已有异步发送、任务状态查询、会话管理和账号管理 API),通过 Chrome DevTools MCP 操作浏览器完成端到端验证;发现问题时现场修复前后端代码并重新验证。
|
||||
|
||||
**Tech Stack:** Spring Boot 4.1.0 (backend), Vue 3 + Element Plus (admin-web), RabbitMQ (async send), MySQL, Chrome DevTools MCP.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- 项目工作目录:`/f/sino-mci`
|
||||
- JDK 21 路径:`/f/tools/jdk-21.0.4+7`
|
||||
- 后端构建命令:`cd /f/sino-mci/backend && mvn clean install -DskipTests`
|
||||
- 后端单元测试:`cd /f/sino-mci/backend && mvn test -pl mci-server,mci-channel-wecom`
|
||||
- admin-web 运行命令:`cd /f/sino-mci/admin-web && npm run dev`
|
||||
- 登录账号:`superadmin` / `admin123`
|
||||
- 当前代码分支:`main`,远端为 `origin/main`
|
||||
- 不要删除或覆盖工作区中未提交的无关文件
|
||||
|
||||
---
|
||||
|
||||
## 文件结构
|
||||
|
||||
本次验证主要读取/操作以下文件:
|
||||
|
||||
- `admin-web/src/views/ChannelAccount.vue` — 创建 WECOM_BOT 账号
|
||||
- `admin-web/src/views/Conversation.vue` — 创建内部群、绑定账号
|
||||
- `admin-web/src/views/MessageTest.vue` — 发送测试消息
|
||||
- `admin-web/src/views/SendRecord.vue` — 查询发送记录
|
||||
- `backend/mci-server/src/main/java/com/sino/mci/send/service/SendService.java` — 发送路由与状态
|
||||
- `backend/mci-server/src/main/java/com/sino/mci/channel/service/ConversationService.java` — 会话绑定校验
|
||||
- `backend/mci-server/src/main/java/com/sino/mci/channel/service/AccountSelector.java` — 账号选择
|
||||
- `logs/mci-server.log` — 后端错误排查
|
||||
- `admin-web/screenshots/` — 保存验证截图
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 环境检查
|
||||
|
||||
**Files:**
|
||||
- Read: `logs/admin-web.log`, `logs/mci-server.log`, `logs/channel-service.log`
|
||||
- Use: Chrome DevTools MCP
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: 后台服务进程状态
|
||||
- Produces: 环境就绪确认
|
||||
|
||||
- [ ] **Step 1: 检查后台服务是否运行**
|
||||
|
||||
Run: `cd /f/sino-mci && docker compose -p sino-mci --profile infra ps` (Windows 环境可能无 Docker CLI,此时通过后端日志/网络连接确认基础设施可达)
|
||||
Expected: MySQL / Redis / RabbitMQ 容器正常(或本地已启动等价服务/远程可达)。
|
||||
|
||||
Run: `ps aux | grep -E "mci-server|admin-web|channel-service" | grep -v grep`
|
||||
Expected: 三个服务进程都在运行。
|
||||
|
||||
- [ ] **Step 2: 检查 admin-web 可访问**
|
||||
|
||||
Use Chrome DevTools MCP navigate to `http://localhost:5177/login`.
|
||||
Expected: 页面加载到登录页,无 404/502。注意:开发环境下 `/msg-platform` 会被 Vite proxy 转发到后端。
|
||||
|
||||
- [ ] **Step 3: 若服务未启动则启动**
|
||||
|
||||
Run: `cd /f/sino-mci && ./scripts/run-local.sh backend` (若未运行)
|
||||
Run: `cd /f/sino-mci && ./scripts/run-local.sh frontend` (若未运行)
|
||||
Run: `cd /f/sino-mci && ./scripts/run-local.sh channel` (若未运行)
|
||||
|
||||
- [ ] **Step 4: 提交状态**
|
||||
|
||||
If all services are healthy, mark Task 1 complete. No code commit needed.
|
||||
|
||||
---
|
||||
|
||||
### Task 2: 登录并创建 WECOM_BOT 账号
|
||||
|
||||
**Files:**
|
||||
- Modify if needed: `admin-web/src/views/ChannelAccount.vue`
|
||||
- Test via UI: `http://localhost:5177/channel/account`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `POST /api/v1/channel-account`
|
||||
- Produces: 一个 accountType=6 的 WECOM_BOT 账号记录
|
||||
|
||||
- [ ] **Step 1: 登录 admin-web**
|
||||
|
||||
Use Chrome DevTools MCP:
|
||||
- Navigate to `http://localhost:5177/msg-platform/login`
|
||||
- Fill username `superadmin`, password `admin123`
|
||||
- Click login
|
||||
- Expected: 跳转至首页。
|
||||
|
||||
- [ ] **Step 2: 进入渠道账号页面**
|
||||
|
||||
Navigate to `http://localhost:5177/msg-platform/channel-account`.
|
||||
Take screenshot: `admin-web/screenshots/01-channel-account-list.png`
|
||||
|
||||
- [ ] **Step 3: 打开创建账号弹窗**
|
||||
|
||||
Click "新增账号".
|
||||
Expected: 弹窗显示表单,账号类型下拉包含「企微群机器人」。
|
||||
|
||||
- [ ] **Step 4: 填写 WECOM_BOT 账号信息**
|
||||
|
||||
Select:
|
||||
- 账号类型:企微群机器人
|
||||
- 渠道:`wecom`
|
||||
- 账号 ID:填写企业微信群机器人 key(如真实 key 或 `test-bot-001`)
|
||||
- 名称:`测试内部群机器人`
|
||||
- Webhook URL:若使用占位 key,填写一个可访问的 mock URL(如 `https://httpbin.org/post` 或本地 mock 服务)
|
||||
|
||||
- [ ] **Step 5: 提交并确认**
|
||||
|
||||
Click "保存".
|
||||
Expected: 弹窗关闭,列表出现新账号,类型显示为「企微群机器人」。
|
||||
Take screenshot: `admin-web/screenshots/02-bot-created.png`
|
||||
|
||||
- [ ] **Step 6: 数据库确认(可选)**
|
||||
|
||||
Run: `docker exec -it mci-mysql mysql -uroot -proot sino_mci -e "SELECT id, account_name, account_type, channel_type FROM mci_channel_account WHERE account_type=6;"`
|
||||
Expected: 看到新创建的 WECOM_BOT 记录。
|
||||
|
||||
- [ ] **Step 7: 提交修复(如有 UI 问题)**
|
||||
|
||||
If any bug found, fix in `admin-web/src/views/ChannelAccount.vue` and commit.
|
||||
|
||||
---
|
||||
|
||||
### Task 3: 手动创建内部群
|
||||
|
||||
**Files:**
|
||||
- Modify if needed: `admin-web/src/views/Conversation.vue`
|
||||
- Test via UI: `http://localhost:5177/customer/group`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `POST /api/v1/conversation`
|
||||
- Produces: conversationType=2, conversationScene=1 的会话记录
|
||||
|
||||
- [ ] **Step 1: 进入会话管理页面**
|
||||
|
||||
Navigate to `http://localhost:5177/msg-platform/conversation`.
|
||||
Take screenshot: `admin-web/screenshots/03-conversation-list.png`
|
||||
|
||||
- [ ] **Step 2: 打开新增会话弹窗**
|
||||
|
||||
Click "新增会话".
|
||||
Expected: 弹窗包含「会话类型」「渠道」「场景」「渠道会话ID」「名称」「负责人」。
|
||||
|
||||
- [ ] **Step 3: 填写内部群信息**
|
||||
|
||||
Select:
|
||||
- 会话类型:群聊
|
||||
- 渠道:`wecom`
|
||||
- 场景:内部群
|
||||
- 渠道会话 ID:`roomid_manual_internal_001`
|
||||
- 名称:`测试内部群`
|
||||
- 负责人:留空或选择任意账号
|
||||
|
||||
- [ ] **Step 4: 提交并确认**
|
||||
|
||||
Click "保存".
|
||||
Expected: 弹窗关闭,列表出现新群聊,场景列显示「内部群」。
|
||||
Take screenshot: `admin-web/screenshots/04-internal-group-created.png`
|
||||
|
||||
- [ ] **Step 5: 数据库确认(可选)**
|
||||
|
||||
Run: `docker exec -it mci-mysql mysql -uroot -proot sino_mci -e "SELECT id, conversation_name, conversation_type, conversation_scene FROM mci_conversation WHERE channel_conversation_id='roomid_manual_internal_001';"`
|
||||
Expected: conversation_scene=1。
|
||||
|
||||
---
|
||||
|
||||
### Task 4: 绑定 WECOM_BOT 到内部群
|
||||
|
||||
**Files:**
|
||||
- Modify if needed: `admin-web/src/views/Conversation.vue`, `backend/mci-server/src/main/java/com/sino/mci/channel/service/ConversationService.java`
|
||||
- Test via UI: 会话详情抽屉
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `POST /api/v1/conversation/{id}/bind-account`
|
||||
- Produces: ChannelAccountConversation 主发送绑定记录
|
||||
|
||||
- [ ] **Step 1: 打开会话详情**
|
||||
|
||||
In Conversation list, click the row of the newly created internal group.
|
||||
Expected: 右侧抽屉打开,显示会话详情和「绑定账号」「绑定标签」按钮。
|
||||
|
||||
- [ ] **Step 2: 打开绑定账号弹窗**
|
||||
|
||||
Click "绑定账号".
|
||||
Expected: 弹窗显示可选账号列表,应能过滤或显示 WECOM_BOT 账号。
|
||||
|
||||
- [ ] **Step 3: 选择 WECOM_BOT 账号**
|
||||
|
||||
Select the WECOM_BOT account created in Task 2.
|
||||
Binding type: 主发送账号(若 UI 有选项,选 1)。
|
||||
|
||||
- [ ] **Step 4: 提交并确认**
|
||||
|
||||
Click "保存" / "绑定".
|
||||
Expected: 绑定成功提示,会话详情中负责人变为该 WECOM_BOT 账号。
|
||||
Take screenshot: `admin-web/screenshots/05-bot-bound.png`
|
||||
|
||||
- [ ] **Step 5: 数据库确认(可选)**
|
||||
|
||||
Run: `docker exec -it mci-mysql mysql -uroot -proot sino_mci -e "SELECT account_id, binding_type FROM mci_channel_account_conversation WHERE conversation_id=(SELECT id FROM mci_conversation WHERE channel_conversation_id='roomid_manual_internal_001');"`
|
||||
Expected: 存在 binding_type=1 的记录。
|
||||
|
||||
- [ ] **Step 6: 提交修复(如有绑定校验问题)**
|
||||
|
||||
If binding fails with "账号渠道类型与会话不一致" or similar, check `ConversationService.isAccountCompatibleWithConversation` and fix.
|
||||
|
||||
---
|
||||
|
||||
### Task 5: 发送测试消息
|
||||
|
||||
**Files:**
|
||||
- Modify if needed: `admin-web/src/views/MessageTest.vue`, `backend/mci-server/src/main/java/com/sino/mci/send/service/SendService.java`
|
||||
- Test via UI: `http://localhost:5177/message/test`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `POST /api/v1/send/test` 或 `POST /api/v1/send`
|
||||
- Produces: SendResponse(taskId, totalCount, status)
|
||||
|
||||
- [ ] **Step 1: 进入消息测试页面**
|
||||
|
||||
Navigate to `http://localhost:5177/msg-platform/message-test`.
|
||||
Take screenshot: `admin-web/screenshots/06-message-test.png`
|
||||
|
||||
- [ ] **Step 2: 选择内部群目标**
|
||||
|
||||
Select target type = 群聊,选择刚才创建的「测试内部群」。
|
||||
|
||||
- [ ] **Step 3: 填写消息内容**
|
||||
|
||||
Content type: text
|
||||
Content: `测试消息 from e2e verification`
|
||||
|
||||
- [ ] **Step 4: 发送并记录 taskId**
|
||||
|
||||
Click "发送".
|
||||
Expected: 页面显示发送成功,返回 taskId 和 status(如 `accepted`)。
|
||||
Copy the taskId.
|
||||
Take screenshot: `admin-web/screenshots/07-message-sent.png`
|
||||
|
||||
- [ ] **Step 5: 后端日志检查**
|
||||
|
||||
Tail `logs/mci-server.log` for routing decision.
|
||||
Expected log: 路由到 WECOM_BOT 账号,调用 webhook URL。
|
||||
|
||||
- [ ] **Step 6: 提交修复(如有路由错误)**
|
||||
|
||||
If routing goes to WECHAT_PERSONAL or WECOM_AGENT instead of WECOM_BOT, fix `SendService.resolveConversationTarget`.
|
||||
|
||||
---
|
||||
|
||||
### Task 6: 查询任务状态
|
||||
|
||||
**Files:**
|
||||
- Modify if needed: `admin-web/src/views/SendRecord.vue`, `backend/mci-server/src/main/java/com/sino/mci/send/service/SendService.java`
|
||||
- Test via UI/API: `GET /api/v1/send/{taskId}/status`, `GET /api/v1/send/records`
|
||||
- UI page: `http://localhost:5177/message/send-record`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: taskId from Task 5
|
||||
- Produces: 聚合状态计数和明细记录
|
||||
|
||||
- [ ] **Step 1: 等待 MQ 消费**
|
||||
|
||||
Wait 5-10 seconds for send task to be processed.
|
||||
|
||||
- [ ] **Step 2: 查询任务状态**
|
||||
|
||||
Option A (UI): Navigate to SendRecord page, filter by taskId.
|
||||
Option B (API): `curl -H "X-Tenant-Code: <tenant>" http://localhost:8080/msg-platform/api/v1/send/{taskId}/status`
|
||||
|
||||
Expected: total=1, success=1 或 failed=1(取决于 webhook URL 是否有效)。
|
||||
Take screenshot: `admin-web/screenshots/08-task-status.png`
|
||||
|
||||
- [ ] **Step 3: 查询明细记录**
|
||||
|
||||
Run: `docker exec -it mci-mysql mysql -uroot -proot sino_mci -e "SELECT task_id, channel_type, send_status, channel_result FROM mci_send_record WHERE task_id='{taskId}';"`
|
||||
Expected: send_status in (2,3),channel_type related to wecom_bot/wecom。
|
||||
|
||||
- [ ] **Step 4: 提交修复(如有状态不准)**
|
||||
|
||||
If status counts are wrong or records missing, fix `SendService.getTaskStatus` or `SendMessageConsumer`.
|
||||
|
||||
---
|
||||
|
||||
### Task 7: 汇总验证报告
|
||||
|
||||
**Files:**
|
||||
- Create: `docs/testing/2026-07-15-send-routing-e2e-report.md`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: 前面所有任务的截图、日志、数据库结果
|
||||
- Produces: 验证报告文档
|
||||
|
||||
- [ ] **Step 1: 整理发现的问题和修复**
|
||||
|
||||
List all bugs found and their commits.
|
||||
|
||||
- [ ] **Step 2: 编写报告**
|
||||
|
||||
Write report with sections:
|
||||
- 环境
|
||||
- 执行步骤
|
||||
- 通过项
|
||||
- 未验证项(如需真实企业微信环境)
|
||||
- 剩余风险
|
||||
- 截图索引
|
||||
|
||||
- [ ] **Step 3: 提交报告和截图**
|
||||
|
||||
```bash
|
||||
git add admin-web/screenshots/ docs/testing/2026-07-15-send-routing-e2e-report.md
|
||||
git commit -m "docs(testing): e2e verification report for send routing"
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Push 到 origin/main**
|
||||
|
||||
```bash
|
||||
git push origin main
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Self-Review Checklist
|
||||
|
||||
- [ ] Spec coverage: 设计文档中的 7 步流程、检查点、问题处理、交付物均有对应任务。
|
||||
- [ ] Placeholder scan: 无 TBD/TODO,所有步骤均有明确命令或操作。
|
||||
- [ ] Type consistency: 使用的设计字段名(conversationScene, accountType, channelType, taskId)与代码一致。
|
||||
- [ ] File paths: 所有路径均使用绝对路径或相对于 `/f/sino-mci` 的路径。
|
||||
102
docs/testing/2026-07-15-send-routing-e2e-report.md
Normal file
102
docs/testing/2026-07-15-send-routing-e2e-report.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# 发送路由端到端验证报告
|
||||
|
||||
## 目标
|
||||
|
||||
通过 admin-web UI 走通「内部群 → WECOM_BOT → 发送 → 查询状态」完整链路,验证自动路由功能可用。
|
||||
|
||||
## 环境
|
||||
|
||||
- 工作目录:`/f/sino-mci`
|
||||
- 后端:`mci-server` 运行在 `http://localhost:8080/msg-platform`,JDK 21
|
||||
- 前端:`admin-web` 运行在 `http://localhost:5177`
|
||||
- 基础设施:MySQL / Redis / RabbitMQ 通过远程 `192.168.1.173` 连接
|
||||
- 登录账号:`superadmin` / `admin123`
|
||||
|
||||
## 测试数据
|
||||
|
||||
| 实体 | ID | 名称 / 标识 |
|
||||
|-----|-----|-----------|
|
||||
| WECOM_BOT 账号 | 759 | 测试内部群机器人 (test-bot-e2e-001) |
|
||||
| 内部群 | 462 | E2E测试内部群 (roomid_manual_internal_e2e) |
|
||||
| 发送任务 | SND8BC07E95AE1F471992A734ABDDFE223C | 测试消息 from e2e verification |
|
||||
|
||||
## 执行步骤与结果
|
||||
|
||||
### 1. 环境检查 ✅
|
||||
|
||||
- mci-server、admin-web、channel-service 均运行正常。
|
||||
- admin-web 可访问地址:`http://localhost:5177/login`。
|
||||
- 注意:`/msg-platform/login` 会被 Vite proxy 转发到后端,返回 500。
|
||||
|
||||
### 2. 创建 WECOM_BOT 账号 ✅
|
||||
|
||||
- 页面路由:`/channel/account`
|
||||
- 创建账号类型「企微群机器人」,填写 webhook URL `https://httpbin.org/post`。
|
||||
- **发现问题并修复**:UI 原先把 webhook URL 作为普通字符串写入 `ext_info`,而数据库列为 JSON 类型,导致 500。已修复 `ChannelAccount.vue`,将 URL 包装为 `{"webhook_url":"..."}`;同时 `SendService` 增加 `extractWeComBotWebhookUrl()` 从 JSON 中提取 URL,兼容旧版纯字符串。
|
||||
- 截图:`admin-web/screenshots/02-bot-created.png`
|
||||
|
||||
### 3. 创建内部群 ✅
|
||||
|
||||
- 页面路由:`/customer/group`
|
||||
- 创建群聊,场景选择「内部群」,负责人选择 WECOM_BOT 账号 759。
|
||||
- 截图:`admin-web/screenshots/04-internal-group-created.png`
|
||||
|
||||
### 4. 绑定 WECOM_BOT 账号 ✅
|
||||
|
||||
- 在会话详情抽屉中点击「绑定账号」,选择 WECOM_BOT 账号 759 作为主发送账号。
|
||||
- API 返回 `{"code":0,"message":"ok"}`。
|
||||
- 截图:`admin-web/screenshots/05-bot-bound.png`
|
||||
|
||||
### 5. 发送测试消息 ✅
|
||||
|
||||
- 页面路由:`/message/test`
|
||||
- 目标类型选择「会话」,选择内部群 462。
|
||||
- 内容:`测试消息 from e2e verification`
|
||||
- 发送接口返回:
|
||||
```json
|
||||
{"taskId":"SND8BC07E95AE1F471992A734ABDDFE223C","totalCount":1,"status":"accepted"}
|
||||
```
|
||||
- 后端日志确认消息被路由到 `WeComBotSender` 并调用 webhook。
|
||||
- 外部 webhook `httpbin.org/post` 返回 503,导致实际投递失败(预期内,用于验证链路)。
|
||||
- 截图:`admin-web/screenshots/06-message-test.jpeg`、`admin-web/screenshots/07-message-sent.png`
|
||||
|
||||
### 6. 查询任务状态 ✅
|
||||
|
||||
- 页面路由:`/message/send-record`
|
||||
- 任务状态接口返回:
|
||||
```json
|
||||
{"total":1,"pendingCount":0,"sendingCount":0,"successCount":0,"failedCount":1}
|
||||
```
|
||||
- 发送记录明细:`accountId=759`,`channelType=wecom`,`sendStatus=3`(失败),`channelResult` 指向「企微群机器人发送失败,HTTP 状态: 503」。
|
||||
- 截图:`admin-web/screenshots/08-task-status.png`
|
||||
|
||||
## 结论
|
||||
|
||||
**发送路由自动选择功能验证通过。**
|
||||
|
||||
- 内部群聊正确路由到 `WECOM_BOT` 账号。
|
||||
- 异步发送立即返回 `taskId`,任务状态查询正确反映失败计数。
|
||||
- 失败原因与外部 webhook 端点相关,不是路由或代码问题。
|
||||
|
||||
## 修复的代码
|
||||
|
||||
| 文件 | 修复内容 |
|
||||
|-----|---------|
|
||||
| `admin-web/src/views/ChannelAccount.vue` | WECOM_BOT 创建/编辑时把 webhook URL 包装为 JSON;抽屉回显时解析 JSON |
|
||||
| `backend/mci-server/src/main/java/com/sino/mci/send/service/SendService.java` | 新增 `extractWeComBotWebhookUrl()`,从 JSON `ext_info` 中提取 `webhook_url`,兼容旧版纯字符串 |
|
||||
|
||||
## 发现但未修复的问题
|
||||
|
||||
1. **admin-web 路由与计划不一致**:实际路由为 `/channel/account`、`/customer/group`、`/message/test`、`/message/send-record`,而不是任务计划中假设的 `/channel-account`、`/conversation`、`/message-test`。已在本次验证后更新相关文档。
|
||||
2. **发送记录分页 `total` 字段不一致**:`GET /api/v1/send/records?taskId=...` 返回 `total: 0` 但 `records` 数组包含 1 条记录。属于前端分页展示小问题,不影响状态核对。
|
||||
|
||||
## 截图索引
|
||||
|
||||
| 截图 | 说明 |
|
||||
|-----|------|
|
||||
| `admin-web/screenshots/02-bot-created.png` | WECOM_BOT 账号创建成功 |
|
||||
| `admin-web/screenshots/04-internal-group-created.png` | 内部群创建成功 |
|
||||
| `admin-web/screenshots/05-bot-bound.png` | WECOM_BOT 绑定到内部群 |
|
||||
| `admin-web/screenshots/06-message-test.jpeg` | 消息测试页面 |
|
||||
| `admin-web/screenshots/07-message-sent.png` | 消息发送成功,返回 taskId |
|
||||
| `admin-web/screenshots/08-task-status.png` | 任务状态显示 failed=1 |
|
||||
Reference in New Issue
Block a user