1131 lines
47 KiB
Markdown
1131 lines
47 KiB
Markdown
# IM Kafka 接入联调测试文档
|
||
|
||
> 关联文档:[IM Kafka 接入方案](./IM-Kafka接入方案.md)、[IM Kafka 接入范围识别](./IM-Kafka接入范围识别.md)、[IM Kafka 基础表 SQL](./sql/IM-Kafka基础表SQL.sql)
|
||
> 适用范围:Admin/HotGo 与 IM Server 通过 Kafka 和内部只读 API 联调。
|
||
> 当前约定:Admin 只访问 `hotgo`,IM Server 只访问租户库;跨系统写操作走 Kafka,跨系统查询走内部只读 API。
|
||
|
||
## 1. 联调目标
|
||
|
||
1. 验证 Kafka 基础生产、消费、幂等、重试、死信能正常工作。
|
||
2. 验证 Admin 后台操作通过 `im.admin.command` 投递到 IM Server,并通过 `im.admin.command.result` 返回最终状态。
|
||
3. 验证 IM Server 私聊/群聊消息异步落库、缓存失效、死信处理、用户治理命令全部可用;文件生命周期和工作台事件同步落库。
|
||
4. 验证 Admin 与 IM Server 不跨库直连:Admin 查询租户业务数据走 IM 内部 API;IM 查询 hotgo 配置走 Admin 内部 API。
|
||
5. 验证 Kafka 异常情况下不会产生重复业务结果,且失败可重试、可进入死信、可人工重放。
|
||
|
||
## 2. 环境准备
|
||
|
||
### 2.1 服务与依赖
|
||
|
||
| 组件 | 要求 |
|
||
| --- | --- |
|
||
| MySQL | `hotgo` 控制库、租户库如 `tinode` 已创建并可连接。 |
|
||
| Redis | IM Server 已配置 Redis,用于在线状态、短状态、热缓存和跨节点 fanout。 |
|
||
| Kafka | Topic 已创建或允许自动创建;建议测试环境显式创建。 |
|
||
| OSS | 文件上传下载仍走 OSS,文件元数据和生命周期小流量动作同步处理。 |
|
||
| Admin/HotGo | 已接入 Kafka producer/consumer、outbox、内部配置查询接口。 |
|
||
| IM Server | 已启用 Kafka、内部 Admin Query API、Admin Internal Config Client。 |
|
||
|
||
### 2.2 Kafka Topic
|
||
|
||
| Topic | 方向 | 用途 |
|
||
| --- | --- | --- |
|
||
| `im.admin.command` | Admin -> IM | 后台群治理、用户治理、死信处理命令。 |
|
||
| `im.admin.command.result` | IM -> Admin | 后台命令执行结果。 |
|
||
| `im.config.event` | Admin -> IM | 租户配置、字典、第三方配置变更通知。 |
|
||
| `im.cache.invalidate` | Admin/IM -> IM | 跨节点缓存失效。 |
|
||
| `im.message.persist` | IM -> IM | 私聊消息异步落库。 |
|
||
| `im.group.message.persist` | IM -> IM | 群聊消息异步落库。 |
|
||
| `im.audit.event` | IM -> Admin/HotGo | 跨系统审计事件。 |
|
||
| `im.dead.letter` | IM -> Admin/告警 | 消费失败死信。 |
|
||
|
||
说明:`im.message.push` 已规划但本轮不联调,离线 Push Kafka 化仍在增强项中。
|
||
|
||
### 2.3 数据库准备
|
||
|
||
租户库必须执行:
|
||
|
||
```sql
|
||
SOURCE docs/sql/IM-Kafka基础表SQL.sql;
|
||
```
|
||
|
||
重点确认租户库存在:
|
||
|
||
| 表 | 用途 |
|
||
| --- | --- |
|
||
| `im_kafka_consume_log` | 消费幂等、重试状态。 |
|
||
| `im_kafka_dead_letters` | 死信记录与人工处理状态。 |
|
||
| `im_messages` | 私聊消息存储。 |
|
||
| `im_group_messages` | 群聊消息存储。 |
|
||
| `im_filemsglinks` | 私聊附件引用。 |
|
||
| `im_group_files` | 群文件和群附件引用。 |
|
||
| `im_system_notifications` | 工作台等系统通知。 |
|
||
| `im_business_events_outbox` | WS 业务事件投递队列。 |
|
||
|
||
Admin/HotGo 侧需具备:
|
||
|
||
| 表/能力 | 用途 |
|
||
| --- | --- |
|
||
| `im_admin_commands` | Admin 操作单,记录后台操作 `pending/running/succeeded/failed/dead`。 |
|
||
| `im_admin_outbox` | Admin outbox,与操作单同事务写入,可靠发送 Kafka。 |
|
||
| `im_kafka_dead_letter_events` | Admin 消费 `im.dead.letter` 后落 HotGo,用于告警和排障。 |
|
||
| Admin 配置表/字典表 | 为 `/internal/im-config/*` 提供数据源。 |
|
||
|
||
Admin/HotGo 侧需执行:
|
||
|
||
```sql
|
||
SOURCE server/storage/data/generate/im_kafka_admin_tables.sql;
|
||
SOURCE server/storage/data/generate/im_kafka_event_tables.sql;
|
||
SOURCE server/storage/data/generate/im_kafka_command_menu.sql;
|
||
SOURCE server/storage/data/generate/im_dead_letter_menu.sql;
|
||
SOURCE server/storage/data/generate/im_kafka_audit_menu.sql;
|
||
```
|
||
|
||
### 2.4 IM Server 配置
|
||
|
||
`tinode.conf` 或 Docker 环境变量:
|
||
|
||
```json
|
||
{
|
||
"kafka": {
|
||
"enabled": true,
|
||
"brokers": ["127.0.0.1:9092"],
|
||
"client_id": "im-server",
|
||
"consumer_group": "im-server",
|
||
"allow_auto_topic_creation": false,
|
||
"max_retry_count": 3
|
||
},
|
||
"internal_api": {
|
||
"enabled": true,
|
||
"time_window_seconds": 300,
|
||
"services": {
|
||
"hotgo-admin": {
|
||
"secret": "same-secret",
|
||
"allow": ["admin_query"]
|
||
}
|
||
}
|
||
},
|
||
"admin_internal_api": {
|
||
"enabled": true,
|
||
"base_url": "http://hotgo-admin:8080",
|
||
"service_name": "im-server",
|
||
"secret": "same-secret",
|
||
"timeout_ms": 3000
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2.5 Admin/HotGo 配置
|
||
|
||
Admin 需要:
|
||
|
||
1. Kafka producer 发送 `im.admin.command`、`im.config.event`、`im.cache.invalidate`。
|
||
2. Kafka consumer 消费 `im.admin.command.result`、`im.dead.letter`。
|
||
3. 内部接口客户端调用 IM `/internal/admin-query/*`。如果 IM API 前缀为 `/v1`,Admin 配置 `imInternalApi.baseUrl` 应包含 `/v1`,例如 `http://im-server:6060/v1`。
|
||
4. 内部接口服务端提供 IM `/internal/im-config/*`。
|
||
5. 内部接口签名与 IM 一致。
|
||
|
||
HMAC 签名请求头:
|
||
|
||
| Header | 说明 |
|
||
| --- | --- |
|
||
| `X-Service-Name` | 调用方服务名,如 `hotgo-admin` 或 `im-server`。 |
|
||
| `X-Timestamp` | Unix 秒级时间戳。 |
|
||
| `X-Nonce` | 一次性随机串。 |
|
||
| `X-Signature` | HMAC-SHA256 hex 签名。 |
|
||
|
||
签名文本:
|
||
|
||
```text
|
||
METHOD + "\n" + RequestURI + "\n" + BodySHA256Hex + "\n" + Timestamp + "\n" + Nonce
|
||
```
|
||
|
||
### 2.6 Admin 与 IM Server 分工
|
||
|
||
| 模块 | Admin/HotGo 负责 | IM Server 负责 |
|
||
| --- | --- | --- |
|
||
| 库访问边界 | 只访问 `hotgo` 控制库,不直连租户库。 | 只访问租户库,不直连 `hotgo`。 |
|
||
| 后台写操作 | 写 Admin 操作单和 Admin outbox,生产 `im.admin.command`。 | 消费命令、执行租户业务、回传 `im.admin.command.result`。 |
|
||
| 后台查询 | 通过 IM `/internal/admin-query/*` 查询租户业务数据。 | 提供白名单只读查询接口,统一 HMAC 鉴权。 |
|
||
| IM 配置读取 | 提供 `/internal/im-config/*`。 | 通过 Admin 内部配置接口读取租户、注册、第三方配置。 |
|
||
| Kafka 死信 | 消费 `im.dead.letter`,展示告警和处理入口。 | 记录死信,提供查询接口,消费处理命令。 |
|
||
| 审计 | 消费 `im.audit.event` 并同步落 HotGo 审计表。 | 关键业务动作生产跨系统审计事件,不在 IM 内部消费落库。 |
|
||
|
||
### 2.7 IM 内部查询接口矩阵
|
||
|
||
Admin 调用以下接口时统一使用 `GET /v1/internal/admin-query/tenants/{tenant_code}/...`,如果部署未配置 `/v1` 前缀,则以实际 IM HTTP 前缀为准。
|
||
|
||
P0 核心页面:
|
||
|
||
| 接口 | 用途 |
|
||
| --- | --- |
|
||
| `/users` | 用户列表。 |
|
||
| `/users/{user_id}` | 用户详情。 |
|
||
| `/users/{user_id}/devices` | 用户设备列表。 |
|
||
| `/groups` | 群列表。 |
|
||
| `/groups/{topic}` | 群详情和治理配置。 |
|
||
| `/groups/{topic}/members` | 群成员分页列表。 |
|
||
| `/groups/{topic}/join-requests` | 入群申请列表。 |
|
||
| `/groups/{topic}/invitations` | 群邀请列表。 |
|
||
| `/groups/{topic}/announcements` | 群公告列表。 |
|
||
| `/groups/{topic}/mutes` | 群禁言列表。 |
|
||
| `/groups/{topic}/admin-logs` | 群管理日志。 |
|
||
| `/groups/{topic}/files` | 群文件列表。 |
|
||
| `/files` | 文件列表。 |
|
||
| `/files/{file_id}` | 文件详情。 |
|
||
| `/files/{file_id}/links` | 文件引用列表。 |
|
||
| `/kafka/dead-letters` | Kafka 死信列表。 |
|
||
| `/kafka/dead-letters/{dead_id}` | Kafka 死信详情。 |
|
||
|
||
P1/P2 运营和完整隔离页面:
|
||
|
||
| 接口 | 用途 |
|
||
| --- | --- |
|
||
| `/audit-logs`、`/audit-logs/{id}` | 审计日志列表和详情。 |
|
||
| `/realname-verifications`、`/realname-verifications/{id}` | 实名审核列表和详情。 |
|
||
| `/abuse-reports`、`/abuse-reports/{id}` | 举报工单列表和详情。 |
|
||
| `/reports/abuse/{report_id}/target` | 举报目标快照。 |
|
||
| `/feedback-submissions`、`/feedback-submissions/{id}` | 意见反馈列表和详情。 |
|
||
| `/outbox/business-events`、`/outbox/group-events` | 业务 Outbox 和群 Outbox 列表。 |
|
||
| `/outbox/business-events/{id}`、`/outbox/group-events/{id}` | Outbox 详情。 |
|
||
| `/org/departments`、`/org/members` | 组织部门和组织成员。 |
|
||
| `/workbench/apps` | 工作台应用配置。 |
|
||
| `/dashboard/summary` | 租户任务看板摘要。 |
|
||
| `/feedback-config`、`/mailbox-config` | 反馈配置、总经理信箱配置。 |
|
||
| `/abuse-reasons`、`/app-releases` | 举报原因、App 发布策略。 |
|
||
|
||
列表接口统一验收 `items/total/page/per_page`,详情接口统一验收 `code=0,data={...}`。用户 ID、雪花 ID、`seq_id`、`dead_id` 等大整数或长 ID 建议按字符串处理,避免前端丢精度。
|
||
|
||
### 2.8 Admin 内部配置接口矩阵
|
||
|
||
IM Server 调 Admin 时统一使用 HMAC 签名,Admin 需提供:
|
||
|
||
| 接口 | 用途 |
|
||
| --- | --- |
|
||
| `GET /internal/im-config/tenants/by-code/{tenant_code}` | 按租户编码查询租户。 |
|
||
| `GET /internal/im-config/tenants/by-id/{tenant_id}` | 按租户 ID 查询租户。 |
|
||
| `GET /internal/im-config/tenants?state=active&limit=1000` | 拉取启用租户列表。 |
|
||
| `GET /internal/im-config/registration/sms` | 读取注册短信配置。 |
|
||
| `GET /internal/im-config/third-party/{group}?keys=a,b` | 读取 OSS、文件安全、声网、极光等第三方配置。 |
|
||
|
||
联调时需要特别确认 OSS 和文件安全配置已经从 Admin 字典项返回,IM 不再依赖本地硬编码。
|
||
|
||
### 2.9 Admin Command 事件矩阵
|
||
|
||
| 事件类型 | 业务 |
|
||
| --- | --- |
|
||
| `group.profile.update` | 后台修改群资料。 |
|
||
| `group.governance.update` | 后台修改群治理配置。 |
|
||
| `group.admin.set`、`group.admin.remove` | 设置或取消群管理员。 |
|
||
| `group.member.mute`、`group.member.unmute` | 成员禁言或解禁。 |
|
||
| `group.member.remove` | 移除群成员。 |
|
||
| `group.owner.transfer` | 转让群主。 |
|
||
| `user.ban`、`user.unban` | 用户封禁和解封。 |
|
||
| `user.freeze`、`user.unfreeze` | 用户冻结和恢复。 |
|
||
| `user.device.kick` | 踢指定设备。 |
|
||
| `user.logout_all` | 全端登出。 |
|
||
| `user.account.cancel`、`user.account.delete` | 注销或删除账号。 |
|
||
| `kafka.dead_letter.ignore` | 忽略死信。 |
|
||
| `kafka.dead_letter.resolve` | 标记死信已解决。 |
|
||
| `kafka.dead_letter.replay` | 重放死信。 |
|
||
|
||
所有 Admin Command 都必须带 `event_id`、`tenant_code`、`event_type`、`biz_type`、`biz_id`、`payload.command_id`、`payload.actor_user_id`。Admin 不应把 Kafka 投递成功当成业务成功,最终状态以 `im.admin.command.result` 为准。
|
||
|
||
## 3. 通用验收规则
|
||
|
||
每个 Kafka 用例都必须检查:
|
||
|
||
1. Kafka 原始事件 `event_id` 全局唯一,同一事件重复投递不会重复产生业务副作用。
|
||
2. `tenant_code` 能正确路由到目标租户库。
|
||
3. 消费成功后 `im_kafka_consume_log.status=succeeded`。
|
||
4. 消费失败后 `retry_count` 增加,并按 `retry_backoff_ms` 重试。
|
||
5. 超过上限后写入 `im_kafka_dead_letters`,并投递 `im.dead.letter`。
|
||
6. Admin Command 成功、失败、死信都必须回传 `im.admin.command.result`。
|
||
7. 业务结果、审计事件、日志里的 `trace_id/event_id/biz_id` 可串联排查。
|
||
|
||
## 4. 联调用例
|
||
|
||
### TC-KAFKA-001 基础启动与 Topic 连通
|
||
|
||
目标:验证 Admin 和 IM 都能连接 Kafka,IM 消费者全部启动。
|
||
|
||
步骤:
|
||
|
||
1. 启动 Kafka、Redis、MySQL。
|
||
2. 启动 Admin/HotGo。
|
||
3. 启动 IM Server。
|
||
4. 查看 IM 日志应出现 Kafka enabled,且无 consumer 启动错误。
|
||
5. Admin 在「IM管理 -> Kafka命令」点击「发送缓存失效测试」,或调用 `POST /admin/imKafkaCommand/cacheInvalidate`,发送一条无业务副作用的测试 `im.cache.invalidate` 事件。
|
||
|
||
预期:
|
||
|
||
1. IM 成功消费测试事件。
|
||
2. `im_kafka_consume_log` 写入一条 succeeded 记录。
|
||
3. 无死信。
|
||
|
||
### TC-KAFKA-002 内部接口鉴权成功
|
||
|
||
目标:验证 Admin 调 IM 内部查询接口签名通过。
|
||
|
||
请求:
|
||
|
||
```http
|
||
GET /v1/internal/admin-query/tenants/default/users/usrxxx
|
||
X-Service-Name: hotgo-admin
|
||
X-Timestamp: <unix_seconds>
|
||
X-Nonce: <random>
|
||
X-Signature: <hmac_sha256_hex>
|
||
```
|
||
|
||
预期:
|
||
|
||
1. HTTP 200。
|
||
2. 响应 `code=0`。
|
||
3. 返回用户基础资料。
|
||
|
||
### TC-KAFKA-003 内部接口鉴权失败
|
||
|
||
目标:验证内部接口拒绝错误签名、过期时间戳、重复 nonce。
|
||
|
||
步骤:
|
||
|
||
1. 使用错误 secret 调用 `/internal/admin-query/*`。
|
||
2. 使用超过时间窗的 timestamp 调用。
|
||
3. 使用同一 nonce 重复调用两次。
|
||
|
||
预期:
|
||
|
||
1. 错误 secret 返回 401。
|
||
2. 过期 timestamp 返回 401。
|
||
3. 重复 nonce 第二次返回 401。
|
||
4. 业务数据不泄露。
|
||
|
||
### TC-KAFKA-004 IM 调 Admin 配置查询
|
||
|
||
目标:验证 IM 通过 Admin `/internal/im-config/*` 获取 hotgo 配置。
|
||
|
||
步骤:
|
||
|
||
1. 开启 IM `admin_internal_api.enabled=true`。
|
||
2. Admin 提供以下接口:
|
||
- `GET /internal/im-config/tenants/by-code/default`
|
||
- `GET /internal/im-config/tenants/by-id/1`
|
||
- `GET /internal/im-config/tenants?state=active&limit=1000`
|
||
- `GET /internal/im-config/registration/sms`
|
||
- `GET /internal/im-config/third-party/im_third_party?keys=...`
|
||
3. 重启 IM。
|
||
4. 执行登录、上传、文件安全配置读取等动作。
|
||
|
||
预期:
|
||
|
||
1. IM 优先请求 Admin 内部配置接口。
|
||
2. Admin 验签通过并返回必要字段。
|
||
3. IM 本地功能正常。
|
||
4. Admin 接口异常时,IM 可回落旧 DB 适配器,日志有 warn。
|
||
|
||
说明:Admin `/internal/im-config/*` 是内部服务接口,不带 `/admin` 后台前缀。
|
||
|
||
### TC-KAFKA-005 Admin 群资料修改命令
|
||
|
||
目标:验证后台群资料修改通过 Kafka 到 IM。
|
||
|
||
Admin 发送:
|
||
|
||
```json
|
||
{
|
||
"event_id": "admin:group:profile:update:001",
|
||
"tenant_code": "default",
|
||
"source_service": "hotgo-admin",
|
||
"target_service": "im-server",
|
||
"event_type": "group.profile.update",
|
||
"biz_type": "group_admin",
|
||
"biz_id": "grpxxx",
|
||
"payload": {
|
||
"command_id": "cmd-group-profile-001",
|
||
"actor_user_id": "usrAdmin",
|
||
"topic": "grpxxx",
|
||
"version": 1,
|
||
"profile": {
|
||
"name": "新群名称",
|
||
"description": "新群描述",
|
||
"avatar_file_id": "file_xxx"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
预期:
|
||
|
||
1. IM 更新群业务表。
|
||
2. `im.admin.command.result` 返回 `status=succeeded`。
|
||
3. C 端在线群成员收到业务刷新事件或可重新拉取看到新资料。
|
||
4. 消费日志 succeeded。
|
||
5. 审计事件已投递。
|
||
|
||
### TC-KAFKA-006 Admin 群治理配置
|
||
|
||
目标:验证后台修改入群审批、全员禁言、成员上传、私聊、邀请开关。
|
||
|
||
事件类型:`group.governance.update`
|
||
|
||
Payload:
|
||
|
||
```json
|
||
{
|
||
"command_id": "cmd-group-govern-001",
|
||
"actor_user_id": "usrAdmin",
|
||
"topic": "grpxxx",
|
||
"version": 2,
|
||
"governance": {
|
||
"join_approval": true,
|
||
"mute_all": true,
|
||
"member_upload_files": false,
|
||
"member_private_chat": false,
|
||
"member_invite": false
|
||
}
|
||
}
|
||
```
|
||
|
||
预期:
|
||
|
||
1. 群设置版本递增或与业务规则一致。
|
||
2. 新配置对成员发送、上传、邀请等入口生效。
|
||
3. Admin 收到成功结果。
|
||
|
||
### TC-KAFKA-007 Admin 群成员治理
|
||
|
||
目标:验证管理员设置、取消、禁言、解禁、移除成员、转让群主。
|
||
|
||
事件类型:
|
||
|
||
| 命令 | 预期 |
|
||
| --- | --- |
|
||
| `group.admin.set` | 成员角色变为 admin。 |
|
||
| `group.admin.remove` | 成员角色恢复 member。 |
|
||
| `group.member.mute` | 成员禁言生效。 |
|
||
| `group.member.unmute` | 成员禁言解除。 |
|
||
| `group.member.remove` | 成员离群,不能继续收发该群消息。 |
|
||
| `group.owner.transfer` | 群主变更,旧群主权限调整。 |
|
||
|
||
每条命令都检查:
|
||
|
||
1. 租户库业务表状态正确。
|
||
2. 在线 C 端收到状态变化。
|
||
3. Admin 操作单从 running 变为 succeeded。
|
||
4. 重复发送同一 `event_id` 不重复执行。
|
||
|
||
### TC-KAFKA-008 用户封禁与解封
|
||
|
||
目标:验证 Admin 可通过 Kafka 封禁/解封用户。
|
||
|
||
封禁事件:
|
||
|
||
```json
|
||
{
|
||
"event_id": "admin:user:ban:001",
|
||
"tenant_code": "default",
|
||
"event_type": "user.ban",
|
||
"biz_type": "user_governance",
|
||
"biz_id": "usrxxx",
|
||
"payload": {
|
||
"command_id": "cmd-user-ban-001",
|
||
"actor_user_id": "usrAdmin",
|
||
"user_id": "usrxxx",
|
||
"reason": "违规"
|
||
}
|
||
}
|
||
```
|
||
|
||
步骤:
|
||
|
||
1. 目标用户保持在线。
|
||
2. Admin 发送 `user.ban`。
|
||
3. 目标用户尝试继续发消息。
|
||
4. 目标用户重新登录。
|
||
5. Admin 发送 `user.unban`。
|
||
6. 目标用户重新登录。
|
||
|
||
预期:
|
||
|
||
1. 封禁后用户状态为 `susp`。
|
||
2. 在线会话被踢下线。
|
||
3. 封禁期间登录被拒绝。
|
||
4. 解封后状态为 `ok`,可正常登录。
|
||
5. 两条命令均回传 result。
|
||
|
||
### TC-KAFKA-009 用户冻结与恢复
|
||
|
||
目标:验证冻结/恢复账号命令。
|
||
|
||
事件类型:`user.freeze`、`user.unfreeze`
|
||
|
||
预期:
|
||
|
||
1. `user.freeze` 设置用户状态 `susp` 并踢下线。
|
||
2. `user.unfreeze` 恢复用户状态 `ok`。
|
||
3. 重复投递同一 `event_id` 不重复产生额外副作用。
|
||
|
||
### TC-KAFKA-010 踢指定设备
|
||
|
||
目标:验证只踢指定设备,不影响同账号其他设备。
|
||
|
||
前置:
|
||
|
||
1. 同一用户使用两个设备登录,设备 ID 分别为 `device-a`、`device-b`。
|
||
|
||
事件:
|
||
|
||
```json
|
||
{
|
||
"event_id": "admin:user:device:kick:001",
|
||
"tenant_code": "default",
|
||
"event_type": "user.device.kick",
|
||
"biz_type": "user_governance",
|
||
"biz_id": "usrxxx:device-a",
|
||
"payload": {
|
||
"command_id": "cmd-device-kick-001",
|
||
"actor_user_id": "usrAdmin",
|
||
"user_id": "usrxxx",
|
||
"device_id": "device-a",
|
||
"reason": "后台踢设备"
|
||
}
|
||
}
|
||
```
|
||
|
||
预期:
|
||
|
||
1. `device-a` 对应会话被踢。
|
||
2. `device-b` 保持在线。
|
||
3. `device-a` 推送设备记录被删除。
|
||
4. Admin 收到 `evicted_sessions` 数量。
|
||
|
||
### TC-KAFKA-011 全端登出
|
||
|
||
目标:验证后台让用户所有设备退出登录。
|
||
|
||
事件类型:`user.logout_all`
|
||
|
||
预期:
|
||
|
||
1. 用户所有在线会话被踢。
|
||
2. 用户状态不改变。
|
||
3. 用户可重新登录。
|
||
|
||
### TC-KAFKA-012 注销与删除账号
|
||
|
||
目标:验证 Admin 可注销或删除租户内账号。
|
||
|
||
事件类型:
|
||
|
||
| 命令 | 预期 |
|
||
| --- | --- |
|
||
| `user.account.cancel` | 默认软删除,`hard=true` 时硬删除。 |
|
||
| `user.account.delete` | 按硬删除处理。 |
|
||
|
||
步骤:
|
||
|
||
1. 目标用户在线。
|
||
2. Admin 发送注销命令。
|
||
3. 查询用户状态。
|
||
4. 尝试重新登录。
|
||
|
||
预期:
|
||
|
||
1. 用户会话被踢。
|
||
2. 用户相关 Topic 被停止。
|
||
3. 用户被删除或标记 `del`。
|
||
4. 登录失败。
|
||
5. Admin 收到 result。
|
||
|
||
### TC-KAFKA-013 私聊消息异步落库
|
||
|
||
目标:验证 Kafka 开启时私聊消息通过 `im.message.persist` 落库。
|
||
|
||
步骤:
|
||
|
||
1. 用户 A 与用户 B 建立私聊。
|
||
2. A 发送带 `x-im-client-mid` 的私聊消息。
|
||
3. 查询 Kafka `im.message.persist`。
|
||
4. 查询租户私聊分表 `im_%03d_messages`。
|
||
5. A 使用同一 `x-im-client-mid` 重复发送。
|
||
|
||
预期:
|
||
|
||
1. A 立即收到服务端 ack 和 `seq_id`。
|
||
2. `seq_id` 为 BIGINT/int64。
|
||
3. 消费成功后对应租户私聊分表有一条记录。
|
||
4. 重复发送不重复落库。
|
||
5. 消费日志 succeeded。
|
||
|
||
### TC-KAFKA-014 群聊消息异步落库
|
||
|
||
目标:验证群消息写入租户月度群消息分表,不混入私聊表。
|
||
|
||
步骤:
|
||
|
||
1. 创建群并加入至少两个成员。
|
||
2. 成员发送群消息。
|
||
3. 查询 `im_%03d_group_messages_YYYYMM`。
|
||
4. 查询 `im_%03d_messages`。
|
||
5. 重连客户端按 Topic seq 拉取消息。
|
||
|
||
预期:
|
||
|
||
1. 群消息只写 `im_group_messages`。
|
||
2. `seq_id` 单调增长,类型为 BIGINT/int64。
|
||
3. 客户端补偿拉取成功。
|
||
4. 24 小时热消息可进入 Redis 热缓存。
|
||
5. 私聊与群聊分别由 `im.message.persist`、`im.group.message.persist` 消费。
|
||
|
||
### TC-KAFKA-015 消息消费失败重试
|
||
|
||
目标:验证私聊和群聊批量落库失败后均按“逐条降级、重试 3 次、死信后提交 offset”处理。
|
||
|
||
步骤:
|
||
|
||
1. 临时制造租户库写入失败,如锁表或停止库连接。
|
||
2. 在批次窗口内连续发送多条私聊和群聊消息。
|
||
3. 恢复数据库,验证逐条重试可成功落库。
|
||
4. 再次持续制造单条失败直至超过重试次数。
|
||
5. 查询 `im_kafka_consume_log`、`im_kafka_dead_letters` 和 `im.dead.letter`。
|
||
|
||
预期:
|
||
|
||
1. `im_kafka_consume_log.retry_count` 增加。
|
||
2. 批量失败后降级为单条写入,每条最多执行 4 次(首次加 3 次重试)。
|
||
3. 未超过重试上限时不进入死信,数据库恢复后消息最终落库并标记 succeeded。
|
||
4. 持续失败的单条消息写入死信表并投递死信 Topic。
|
||
5. 只有批内全部消息达到 succeeded/dead 终态后才提交 offset,不能跳过失败消息。
|
||
|
||
### TC-KAFKA-016 文件上传完成事件
|
||
|
||
目标:验证文件上传完成后不再产生 `im.file.lifecycle` Kafka 事件。
|
||
|
||
步骤:
|
||
|
||
1. 客户端上传文件到 OSS。
|
||
2. 上传完成后查询租户库文件记录。
|
||
3. 确认 Kafka topic 中没有 `im.file.lifecycle` 依赖。
|
||
|
||
预期:
|
||
|
||
1. 文件字节流只进入 OSS。
|
||
2. 上传完成后的文件记录已同步落库。
|
||
|
||
### TC-KAFKA-017 消息附件绑定
|
||
|
||
目标:验证消息落库后附件引用同步绑定。
|
||
|
||
步骤:
|
||
|
||
1. 上传图片或文件。
|
||
2. 发送带附件的私聊消息。
|
||
3. 发送带附件的群聊消息。
|
||
4. 查询附件引用表。
|
||
|
||
预期:
|
||
|
||
1. 私聊附件写入 `im_filemsglinks`。
|
||
2. 群聊附件写入 `im_group_files`。
|
||
3. 重复消费不重复绑定。
|
||
4. 文件 ACL 与业务引用可追踪。
|
||
|
||
### TC-KAFKA-018 群文件登记与删除
|
||
|
||
目标:验证群文件业务由 HTTP 接口同步落库。
|
||
|
||
步骤:
|
||
|
||
1. 群内登记文件。
|
||
2. 查询 `im_group_files`。
|
||
3. 删除群文件。
|
||
4. 检查生命周期事件和审计事件。
|
||
|
||
预期:
|
||
|
||
1. 群文件登记成功。
|
||
2. 删除状态正确。
|
||
3. 文件事件可用于后续回收/合规删除。
|
||
|
||
### TC-KAFKA-019 工作台会议事件
|
||
|
||
目标:验证会议创建、更新、取消、提醒、参会响应产生工作台事件。
|
||
|
||
步骤:
|
||
|
||
1. 创建会议。
|
||
2. 修改会议。
|
||
3. 发送提醒。
|
||
4. 参会人响应。
|
||
5. 取消会议。
|
||
|
||
预期:
|
||
|
||
1. 每个动作同步写 `im_system_notifications`。
|
||
2. 同步写 `im_business_events_outbox`。
|
||
3. 在线 C 端收到系统通知或红点刷新。
|
||
|
||
Admin 连通性补充:工作台事件不再走 Kafka,真实会议、日报/月报、信箱业务结果以 IM Server 对应业务用例为准。
|
||
|
||
### TC-KAFKA-020 日报/月报事件
|
||
|
||
目标:验证日报、月报提交和催办事件。
|
||
|
||
步骤:
|
||
|
||
1. 创建日报草稿。
|
||
2. 保存并提交日报。
|
||
3. 管理员催办日报。
|
||
4. 创建、提交、重开月报。
|
||
|
||
预期:
|
||
|
||
1. 通知目标用户正确。
|
||
2. 工作台红点或系统通知可被客户端看到。
|
||
|
||
### TC-KAFKA-021 总经理信箱事件
|
||
|
||
目标:验证总经理信箱提交事件和审计。
|
||
|
||
步骤:
|
||
|
||
1. 用户提交信箱内容。
|
||
2. 检查同步生成的通知/业务事件。
|
||
3. Admin 后台生成处理单。
|
||
|
||
预期:
|
||
|
||
1. 提交成功后有工作台事件。
|
||
2. 审计事件包含用户、租户、业务 ID。
|
||
3. Admin 可基于事件处理。
|
||
|
||
### TC-KAFKA-022 配置事件失效
|
||
|
||
目标:验证 Admin 配置变更后 IM 清理进程内缓存。
|
||
|
||
事件类型:
|
||
|
||
| 事件 | 预期 |
|
||
| --- | --- |
|
||
| `tenant.updated` | 清租户缓存。 |
|
||
| `tenant.status.changed` | 清租户缓存。 |
|
||
| `tenant.limits.updated` | 清租户配置缓存。 |
|
||
| `tenant.features.updated` | 清租户功能缓存。 |
|
||
| `dictionary.updated` | 清字典/第三方配置缓存。 |
|
||
| `third_party.updated` | 清第三方配置缓存。 |
|
||
| `registration.config.updated` | 清注册短信配置缓存。 |
|
||
|
||
步骤:
|
||
|
||
1. Admin 修改配置。
|
||
2. 发送 `im.config.event`。
|
||
3. IM 再次读取对应配置。
|
||
|
||
预期:
|
||
|
||
1. IM 消费成功。
|
||
2. 下次读取不使用旧缓存。
|
||
3. 可从 Admin 内部配置接口重新获取新值。
|
||
|
||
### TC-KAFKA-023 通用缓存失效
|
||
|
||
目标:验证 `im.cache.invalidate` 可按 namespace 删除缓存。
|
||
|
||
示例事件:
|
||
|
||
```json
|
||
{
|
||
"event_id": "cache:user:001",
|
||
"tenant_code": "default",
|
||
"event_type": "cache.redis_object.invalidate",
|
||
"biz_type": "cache",
|
||
"biz_id": "user:usrxxx",
|
||
"payload": {
|
||
"tenant_code": "default",
|
||
"tenant_id": 1,
|
||
"namespace": "user",
|
||
"keys": ["usrxxx"]
|
||
}
|
||
}
|
||
```
|
||
|
||
预期:
|
||
|
||
1. 用户 Redis 对象缓存被删除。
|
||
2. 后续查询重新读 MySQL 或 Admin 内部配置接口。
|
||
3. 消费幂等。
|
||
|
||
### TC-KAFKA-024 群热消息缓存失效
|
||
|
||
目标:验证群热消息可按 Topic 或 seq range 失效。
|
||
|
||
事件:
|
||
|
||
```json
|
||
{
|
||
"event_id": "cache:group-hot:001",
|
||
"tenant_code": "default",
|
||
"event_type": "cache.group_hot_messages.invalidate",
|
||
"biz_type": "cache",
|
||
"biz_id": "grpxxx",
|
||
"payload": {
|
||
"tenant_id": 1,
|
||
"namespace": "group_hot_messages",
|
||
"topic": "grpxxx",
|
||
"ranges": [{"low": 100, "hi": 120}]
|
||
}
|
||
}
|
||
```
|
||
|
||
预期:
|
||
|
||
1. 指定群热消息范围失效。
|
||
2. 客户端再次拉取时能从最终存储补偿。
|
||
|
||
### TC-KAFKA-025 死信生成
|
||
|
||
目标:验证不可恢复错误进入死信。
|
||
|
||
步骤:
|
||
|
||
1. Admin 发送不存在用户或非法 payload 的命令。
|
||
2. 或临时制造持续失败直到超过最大重试。
|
||
3. 查询 `im_kafka_dead_letters`。
|
||
4. 消费 `im.dead.letter`。
|
||
|
||
预期:
|
||
|
||
1. 死信表出现记录。
|
||
2. 死信 Topic 有 `kafka.dead_letter.created`。
|
||
3. Admin 操作单收到 `status=dead` 的 result。
|
||
4. 死信记录包含原始 envelope、reason、error、retry_count。
|
||
|
||
### TC-KAFKA-026 死信查询
|
||
|
||
目标:验证 Admin 可通过 IM 内部 API 查询死信。
|
||
|
||
请求:
|
||
|
||
```http
|
||
GET /v1/internal/admin-query/tenants/default/kafka/dead-letters?status=dead&limit=50
|
||
GET /v1/internal/admin-query/tenants/default/kafka/dead-letters/{dead_id}
|
||
```
|
||
|
||
预期:
|
||
|
||
1. 列表返回摘要,不返回超大 envelope。
|
||
2. 详情返回原始 envelope。
|
||
3. 支持 `status/biz_type/biz_id/event_type/cursor/limit` 过滤。
|
||
|
||
### TC-KAFKA-027 死信忽略与解决
|
||
|
||
目标:验证 Admin 可通过 Kafka 命令处理死信状态。
|
||
|
||
事件类型:
|
||
|
||
```json
|
||
{
|
||
"event_type": "kafka.dead_letter.ignore",
|
||
"payload": {
|
||
"command_id": "cmd-dead-ignore-001",
|
||
"actor_user_id": "usrAdmin",
|
||
"dead_id": "evt_dead_xxx",
|
||
"note": "测试死信忽略"
|
||
}
|
||
}
|
||
```
|
||
|
||
预期:
|
||
|
||
1. `ignore` 后死信状态为 `ignored`。
|
||
2. `resolve` 后死信状态为 `resolved`。
|
||
3. Admin 收到 result。
|
||
4. 审计事件已投递。
|
||
|
||
### TC-KAFKA-028 死信重放
|
||
|
||
目标:验证死信可人工重放。
|
||
|
||
步骤:
|
||
|
||
1. 修复导致死信的业务数据或配置。
|
||
2. Admin 发送 `kafka.dead_letter.replay`。
|
||
3. 观察原 Topic 是否重新收到 envelope。
|
||
4. 查询死信记录和消费日志。
|
||
|
||
预期:
|
||
|
||
1. IM 将死信改为 `pending_replay`。
|
||
2. 删除原 `im_kafka_consume_log`。
|
||
3. 按原 `source_topic` 重投 envelope。
|
||
4. 重投成功后死信状态为 `replayed`,`replay_count` 增加。
|
||
5. 原业务消费者重新处理并成功。
|
||
|
||
### TC-KAFKA-029 审计事件
|
||
|
||
目标:确认 IM Server 对跨系统审计仍生产 `im.audit.event`,由 Admin/HotGo 消费后同步落审计表。
|
||
|
||
覆盖:
|
||
|
||
1. 群治理命令。
|
||
2. 用户治理命令。
|
||
3. 文件生命周期。
|
||
4. 工作台状态变化。
|
||
5. 死信生成和处理。
|
||
|
||
预期:
|
||
|
||
1. IM Server 生产 `im.audit.event`。
|
||
2. Kafka consumer 启动日志中没有 IM 侧审计事件 consumer。
|
||
3. Admin/HotGo 消费审计事件并同步写入审计表。
|
||
|
||
### TC-KAFKA-030 IM 侧 Kafka 生产失败
|
||
|
||
目标:验证 IM 侧 Kafka 直接生产失败时不会产生 DB outbox,调用方收到发送失败。
|
||
|
||
预期:
|
||
|
||
1. Kafka 停止时 IM 不写 `im_kafka_outbox`,消息发送返回失败。
|
||
2. 恢复后客户端可重新发送。
|
||
3. 重复发送依赖 `client_mid` 和消费日志保持幂等。
|
||
4. 不重复产生业务副作用。
|
||
|
||
## 5. Admin 侧联调检查清单
|
||
|
||
| 检查项 | 验收标准 |
|
||
| --- | --- |
|
||
| 操作单 | 每个后台写操作先写 hotgo 操作单。 |
|
||
| Admin outbox | 操作单与 outbox 同事务写入。 |
|
||
| Command 生产 | `event_id/idempotency_key/tenant_code/biz_id` 稳定可追踪。 |
|
||
| Result 消费 | `im.admin.command.result` 能更新操作单状态。 |
|
||
| 死信消费 | `im.dead.letter` 能生成告警或待处理记录。 |
|
||
| 审计 | 消费 `im.audit.event`,同步落 HotGo 审计表。 |
|
||
| 配置接口 | `/internal/im-config/*` 支持 IM HMAC 调用。 |
|
||
| 查询客户端 | 调 IM `/internal/admin-query/*` 带签名并处理 401/403/404/5xx。 |
|
||
| 页面状态 | 后台页面显示 pending/running/succeeded/failed/dead,不把 Kafka 发送成功当业务成功。 |
|
||
|
||
## 6. IM Server 侧联调检查清单
|
||
|
||
| 检查项 | 验收标准 |
|
||
| --- | --- |
|
||
| Kafka 启动 | producer、consumer 正常启动,IM 侧无 DB outbox worker。 |
|
||
| 租户路由 | `tenant_code` 能路由到正确租户库。 |
|
||
| 消费幂等 | 重复 `event_id` 不重复执行业务。 |
|
||
| 重试 | 可重试错误按退避时间重试。 |
|
||
| 死信 | 超过上限或不可重试错误进入死信并回 result。 |
|
||
| Admin Query API | 内部接口验签、鉴权、nonce 防重放生效。 |
|
||
| Admin Config Client | 可从 Admin 拉租户、注册、第三方配置。 |
|
||
| 用户治理 | 封禁/冻结踢下线,解封/恢复可登录,设备踢出只影响指定设备。 |
|
||
| 群治理 | 群资料、角色、禁言、移除、转让群主全部生效。 |
|
||
| 消息落库 | 私聊写 `im_messages`,群聊写 `im_group_messages`。 |
|
||
| 文件生命周期 | 文件记录、附件引用、群文件同步落到正确业务表。 |
|
||
| 工作台事件 | 同步生成系统通知和 WS 业务事件。 |
|
||
| 审计 | IM 生产 `im.audit.event` 给 Admin/HotGo,IM 本地不消费审计 topic。 |
|
||
|
||
## 7. 回归用例
|
||
|
||
1. Kafka 关闭时,IM 基础登录、私聊、群聊、文件上传下载仍可用。
|
||
2. Redis 异常时,Kafka 消费不应 panic;允许热缓存和 nonce 校验降级按配置表现。
|
||
3. Admin 内部配置接口不可用时,IM 本地旧 DB 读取兜底可用。
|
||
4. IM 内部查询接口关闭时,Admin 查询应收到 404,不泄露数据。
|
||
5. 同一用户在多端登录,`user.device.kick` 只踢指定设备,`user.logout_all` 踢全部设备。
|
||
6. 同一 Admin Command 重复发送 3 次,业务只成功一次,结果可重复查询。
|
||
7. 群消息高并发发送时 `seq_id` 单调递增且无重复。
|
||
8. 文件上传后未发送消息,不应产生错误附件引用。
|
||
9. 工作台事件目标用户为空时,不应产生错误通知。
|
||
10. 死信重放失败后应再次进入死信,不覆盖原处理痕迹。
|
||
|
||
## 8. 联调问题定位
|
||
|
||
| 现象 | 优先排查 |
|
||
| --- | --- |
|
||
| Admin 操作一直 pending | Admin outbox 是否成功投递;`queue.driver` 是否为 `kafka`;Kafka broker 是否可用。 |
|
||
| Admin 操作一直 running | Admin 是否消费 `im.admin.command.result`;IM 是否收到 command;`event_id` 是否重复卡在 processing。 |
|
||
| IM 消费失败 | 查 `im_kafka_consume_log.last_reason/last_error` 和 IM 日志 `event_id`。 |
|
||
| 进入死信 | 查 `im_kafka_dead_letters.envelope/last_error`,修复后用重放命令。 |
|
||
| 用户没有被踢下线 | 查目标 `user_id` 是否是 IM 用户 ID;是否连接在当前 IM 节点;跨节点踢人是否需要后续 Redis fanout 扩展。 |
|
||
| 群治理未生效 | 查 `topic/version/actor_user_id` 是否正确,目标用户是否为群成员。 |
|
||
| 消息未落库 | 私聊查 `im.message.persist`,群聊查 `im.group.message.persist` 是否消费成功,以及 `im_kafka_consume_log.last_reason/last_error`。 |
|
||
| 附件未绑定 | 查消息是否带附件,以及同步绑定逻辑是否返回错误。 |
|
||
| 内部 API 401 | 查 secret、签名文本、RequestURI、timestamp、nonce 是否一致。 |
|
||
| 配置没有刷新 | 查是否发送 `im.config.event` 或 `im.cache.invalidate`,IM 是否清理了对应 namespace。 |
|
||
|
||
## 9. 联调完成标准
|
||
|
||
1. 本文所有 P0/P1 用例通过:Kafka 基础、Admin Command、用户治理、群治理、消息异步落库、文件生命周期同步处理、工作台同步处理、缓存失效、死信。
|
||
2. Admin 操作单状态闭环可视化:pending -> running -> succeeded/failed/dead。
|
||
3. IM 租户库无重复业务记录,Kafka 消费日志无异常堆积。
|
||
4. 死信可查询、可忽略、可解决、可重放。
|
||
5. Admin 与 IM 均不跨库直连,内部查询接口鉴权通过。
|
||
6. 离线 Push Kafka 化未纳入本次验收,继续按增强项跟进。
|
||
|
||
## 10. 本地 Docker 联调记录
|
||
|
||
### 10.1 2026-08-27 冒烟联调
|
||
|
||
本轮不是全量联调,只完成本地 Docker 环境启动和 Kafka/内部接口主链路冒烟。后续仍需按第 4 章逐条补齐。
|
||
|
||
联调环境:
|
||
|
||
| 组件 | 地址/容器 | 结果 |
|
||
| --- | --- | --- |
|
||
| MySQL | `relaxed_cori:3306`,宿主机 `127.0.0.1:3306` | 已连接,本地库包含 `hotgo`、`tinode`。 |
|
||
| Redis | `brave_blackburn:6379`,宿主机 `127.0.0.1:6379` | 已连接。 |
|
||
| Kafka | `im-local-kafka:9092`,宿主机 `127.0.0.1:19092` | 已创建联调 Topic。 |
|
||
| IM Server | `im-local-im:6060`,宿主机 `127.0.0.1:6060` | healthy,Kafka consumer 已启动。 |
|
||
| Admin/HotGo | `im-local-admin:8000`,宿主机 `127.0.0.1:8000` | healthy,Kafka consumer 已启动。 |
|
||
|
||
说明:本地已有 Kafka 容器 `kafka:9092` 的 `advertised.listeners=localhost:9092`,Docker 内服务会回连自身 localhost,故本轮单独启动 `im-local-kafka` 用于容器联调。
|
||
|
||
本轮已测:
|
||
|
||
| 用例/链路 | 测试内容 | 结果 | 证据 |
|
||
| --- | --- | --- | --- |
|
||
| TC-KAFKA-001 | Kafka Topic 创建,IM consumer 启动。 | 通过 | IM 日志显示 `im.admin.command`、`im.config.event`、`im.cache.invalidate`、`im.message.persist`、`im.group.message.persist` consumer started。 |
|
||
| TC-KAFKA-002 | Admin -> IM 内部查询 HMAC。 | 通过 | `GET /internal/admin-query/tenants/default/users?page=1&per_page=1` 返回 `code=0`,`total=29`。 |
|
||
| TC-KAFKA-004 | IM -> Admin 内部配置 HMAC。 | 通过 | `GET /internal/im-config/tenants?limit=1000&state=active` 返回 `code=0` 和 default 租户。 |
|
||
| TC-KAFKA-008/011 | `user.logout_all` 后台用户治理命令。 | 通过 | `tinode.im_kafka_consume_log` 中 `local-logout-fixed-*` 状态为 `succeeded`。 |
|
||
| TC-KAFKA-023 | `im.cache.invalidate` 通用缓存失效。 | 通过 | `local-cache-*` 写入 `im_kafka_consume_log`,状态 `succeeded`,IM 日志显示 `kafka cache invalidate applied`。 |
|
||
| TC-KAFKA-025 | 非法 `user.logout_all` 进入死信。 | 通过 | `local-logout-*` 状态为 `dead`,`im_kafka_dead_letters` 写入 `MALFORMED_COMMAND`。 |
|
||
| TC-KAFKA-029 | 用户治理审计事件。 | 通过 | IM 生产 `im.audit.event`,Admin/HotGo 消费后同步落审计表。 |
|
||
|
||
本轮发现并处理:
|
||
|
||
| 问题 | 现象 | 处理 |
|
||
| --- | --- | --- |
|
||
| Admin Docker 前端构建失败 | `pnpm run build` 非 TTY 环境报 `ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY`。 | Admin `Dockerfile` 增加 `ENV CI=true`。 |
|
||
| Admin Kafka 版本配置不兼容 | Admin 启动时报 `invalid version 2.0.0.0`。 | 本地联调配置临时改为 `version: "2.0.0"`。 |
|
||
| IM 启动缺少 UID key | IM 报 `crypto/xtea: invalid key size 0`。 | Docker 启动显式传入 `UID_ENCRYPTION_KEY`、`API_KEY_SALT`、`AUTH_TOKEN_KEY`。 |
|
||
| HotGo 表前缀不一致 | Admin 内部配置接口错误查询 `hg_im_tenant`,但 IM 业务/配置表正式表名应保持 `im_tenant`。 | 联调时暴露的是 Admin 查询/ORM 表名前缀问题;正式方案必须修 Admin 侧表名映射或 DAO 前缀逻辑,不能新增 `hg_im_*` 兼容表。 |
|
||
| Admin result 消费格式不兼容 | Admin 消费 `im.admin.command.result` 报 `IM命令结果缺少command_id`。 | Admin consumer 已兼容 Kafka Envelope + payload 格式。 |
|
||
| Admin 审计表前缀不一致 | Admin 消费 `im.audit.event` 后错误查询 `hg_im_audit_events`,正式表名应保持 `im_audit_events`。 | Admin/HotGo 同步写审计表时,仍需保证所有 `im_*` 业务表不加 `hg_` 前缀。 |
|
||
|
||
本轮未覆盖,后续继续:
|
||
|
||
| 范围 | 待测内容 |
|
||
| --- | --- |
|
||
| 群治理 | 群资料、治理配置、管理员设置/取消、成员禁言/解禁、移除成员、转让群主。 |
|
||
| 用户治理完整集 | `user.ban`、`user.unban`、`user.freeze`、`user.unfreeze`、`user.device.kick`、注销/删除账号。 |
|
||
| 消息异步落库 | 私聊 `im_messages`、群聊 `im_group_messages`、`seq_id` BIGINT、重复 `client_mid` 幂等。 |
|
||
| 文件生命周期 | 上传完成、附件绑定、群文件登记/删除、OSS 元数据和引用表。 |
|
||
| 工作台事件 | 会议、日报/月报、总经理信箱、红点/系统通知投递。 |
|
||
| 死信闭环 | 死信查询、忽略、解决、重放,以及重放后二次成功/失败。 |
|
||
| Kafka 生产失败 | Kafka 停止/恢复时,确认 IM 不写 `im_kafka_outbox`,接口/WS 返回发送失败,恢复后可重新发送。 |
|
||
| Admin 页面 | 操作单状态从 pending/running 到 succeeded/failed/dead 的页面联调。 |
|
||
|
||
### 10.2 2026-08-27 完整后端链路联调
|
||
|
||
本轮在本地 Docker 环境继续补齐 Kafka 后端链路联调。覆盖方式以 Kafka 直接投递、内部 HTTP HMAC 调用、MySQL 业务表/消费日志核验为主;Admin 前端页面点击未做浏览器人工验收,页面层需要后续按菜单再走一遍。
|
||
|
||
联调环境延续 10.1:
|
||
|
||
| 组件 | 地址/容器 | 结果 |
|
||
| --- | --- | --- |
|
||
| MySQL | `relaxed_cori:3306` | 正常。 |
|
||
| Redis | `brave_blackburn:6379` | 正常。 |
|
||
| Kafka | `im-local-kafka:9092`,宿主机 `127.0.0.1:19092` | 正常;Outbox 用例中曾停止并恢复。 |
|
||
| IM Server | `im-local-im:6060` | 已重建为包含本轮修复的 `im-local-server:dev`,当前 healthy。 |
|
||
| Admin/HotGo | `im-local-admin:8000` | 当前 healthy;Kafka 断开时曾 fatal 退出,见问题记录。 |
|
||
|
||
本轮通过项:
|
||
|
||
| 用例/范围 | 覆盖内容 | 结果 | 证据 |
|
||
| --- | --- | --- | --- |
|
||
| TC-KAFKA-003 | IM 内部查询接口未签名访问。 | 通过 | `GET /internal/admin-query/...` 未带签名返回 401 `AUTH_REQUIRED`。 |
|
||
| TC-KAFKA-004 | Admin 内部配置接口签名成功/失败。 | 通过 | 未签名返回 401;签名访问 `/internal/im-config/tenants?limit=1&state=active` 返回 default 租户。 |
|
||
| TC-KAFKA-005 | 后台群资料修改。 | 通过 | `group.profile.update` 消费 `succeeded`,`im_group_settings.name/description/version` 更新。 |
|
||
| TC-KAFKA-006 | 后台群治理配置。 | 通过 | `group.governance.update` 消费 `succeeded`,`join_approval/mute_all/member_*` 更新。 |
|
||
| TC-KAFKA-007 | 群管理员、禁言、移除、转让群主。 | 通过 | `group.admin.set/remove`、`group.member.mute/unmute/remove`、`group.owner.transfer` 均有成功消费记录;`im_group_members/im_group_mutes` 状态变更正确。 |
|
||
| TC-KAFKA-008 | 用户封禁与解封。 | 通过 | `user.ban/user.unban` 消费 `succeeded`,用户最终恢复 `state=0`。 |
|
||
| TC-KAFKA-009 | 用户冻结与恢复。 | 通过 | `user.freeze/user.unfreeze` 消费 `succeeded`,用户最终恢复 `state=0`。 |
|
||
| TC-KAFKA-010 | 踢指定设备。 | 通过 | `user.device.kick` 消费 `succeeded`,不存在设备按幂等成功处理。 |
|
||
| TC-KAFKA-011 | 全端登出。 | 通过 | `user.logout_all` 消费 `succeeded`。 |
|
||
| TC-KAFKA-012 | 注销与删除账号。 | 通过 | `user.account.cancel/user.account.delete` 使用本地测试账号验证,消费 `succeeded`。 |
|
||
| TC-KAFKA-013 | 私聊消息异步落库。 | 通过 | `message.persist.p2p` 消费 `succeeded`,`im_messages` 写入测试消息。 |
|
||
| TC-KAFKA-014 | 群聊消息异步落库。 | 通过 | `im.group.message.persist` 消费 `message.persist.group`,`im_group_messages` 写入测试群消息。 |
|
||
| TC-KAFKA-016 | 文件上传完成事件。 | 通过 | 文件上传完成不再走 Kafka。 |
|
||
| TC-KAFKA-017 | 消息附件绑定。 | 通过 | 群聊附件绑定写入 `im_group_files`;私聊附件绑定修复后写入 `im_filemsglinks`。 |
|
||
| TC-KAFKA-018 | 群文件登记与删除生命周期。 | 通过 | 群文件登记/删除同步落库。 |
|
||
| TC-KAFKA-019 | 工作台会议事件。 | 通过 | 同步写入 `im_system_notifications` 和 `im_business_events_outbox`。 |
|
||
| TC-KAFKA-022 | 配置事件失效。 | 通过 | `dictionary.updated` 消费 `succeeded`。 |
|
||
| TC-KAFKA-023 | 通用缓存失效。 | 通过 | `cache.redis_object.invalidate` 消费 `succeeded`。 |
|
||
| TC-KAFKA-024 | 群热消息缓存失效。 | 通过 | `cache.group_hot_messages.invalidate` 消费 `succeeded`。 |
|
||
| TC-KAFKA-025 | 死信生成。 | 通过 | 故意发送非法 `user.logout_all`,消费状态 `dead`,写入 `im_kafka_dead_letters`。 |
|
||
| TC-KAFKA-026 | 死信查询。 | 通过 | `/internal/admin-query/tenants/default/kafka/dead-letters` 和详情接口返回 200。 |
|
||
| TC-KAFKA-027 | 死信忽略与解决。 | 通过 | `kafka.dead_letter.ignore/resolve` 消费 `succeeded`,死信状态变为 `ignored/resolved`。 |
|
||
| TC-KAFKA-028 | 死信重放。 | 通过 | `kafka.dead_letter.replay` 消费 `succeeded`,死信状态变为 `replayed`,`replay_count=1`。 |
|
||
| TC-KAFKA-029 | 审计事件。 | 通过 | `im.audit.event` 投递给 Admin/HotGo,消费侧同步落审计表。 |
|
||
| TC-KAFKA-030 | IM Kafka Outbox 可靠投递。 | 通过 | Kafka 停止时 outbox `pending -> failed`;Kafka 恢复并重启 IM 后 `failed -> sent`,下游消费成功。 |
|
||
|
||
本轮消费统计:
|
||
|
||
| 口径 | 数量 |
|
||
| --- | ---: |
|
||
| `codex_it_*` 和本轮修复验证事件总数 | 38 |
|
||
| 成功消费 | 35 |
|
||
| 故意制造死信 | 3 |
|
||
| 修复后仍卡 processing | 0 |
|
||
|
||
代表性事件:
|
||
|
||
| 类型 | Event ID |
|
||
| --- | --- |
|
||
| 用户治理批次 | `codex_it_110224_*` |
|
||
| 群成员移除复测 | `codex_it_remove_110302_271b2ff6` |
|
||
| 私聊/群聊消息落库 | `codex_it_msg_110432_*`、`codex_it_msg_110434_*` |
|
||
| 私聊附件修复验证 | `codex_it_fix_p2p_attach_b93a9a4b` |
|
||
| 工作台事件 | `codex_it_evt_110730_workbench_meeting_created_b764faa3` |
|
||
| Outbox 断 Kafka 恢复 | `codex_it_outbox_7f52e221` |
|
||
| 死信处理 | `codex_it_dead_111524_*`、`codex_it_dead_111528_*`、`codex_it_dead_111531_*` |
|
||
|
||
本轮发现并处理:
|
||
|
||
| 问题 | 影响 | 处理结果 |
|
||
| --- | --- | --- |
|
||
| 私聊附件绑定触发 IM panic | `file.attachments.bind` 对私聊消息调用 `store.Files.LinkAttachments(tenantID, "", msgId, attachments)` 时,空 topic 进入 `types.GetTopicCat(topic)` 导致 `slice bounds out of range`,IM 退出。 | 已修复 `server/store/store.go`:仅当 `topic != ""` 时判断 `TopicCatMe`;重建 IM 后复测私聊附件绑定成功,`im_filemsglinks` 写入。 |
|
||
| Kafka 停止时 Admin 进程 fatal 退出 | Outbox 断 Kafka 用例中停止 `im-local-kafka` 后,Admin consumer 报 `client has run out of available brokers` 并 `FATA` 退出。 | 已记录为 Admin 侧待修复问题:Kafka consumer 断 broker 不应 fatal,应退避重连。 |
|
||
| Kafka 停止/恢复后 IM consumer 自动恢复不稳定 | Kafka 恢复后,IM 仍 healthy,但消费端未立即消费新探针;重启 IM 后积压事件被补偿消费。 | 已记录为 IM 侧增强/修复项:Kafka reader 断 broker 后需要自动重建 reader 并继续消费。 |
|
||
| 备用群业务快照不一致 | 对 `grpEC7ddYMoI5s` 执行 `group.member.remove` 返回 `RESOURCE_NOT_FOUND` 并进入死信。 | 换用业务表已确认成员的 `grpgvTd1mFvM2M` 后移除成功;前一次作为死信生成用例保留。 |
|
||
|
||
未做或需要人工补充:
|
||
|
||
| 范围 | 说明 |
|
||
| --- | --- |
|
||
| Admin 前端页面点击 | 本轮验证了后端接口、Kafka 生产消费和数据库状态;未使用浏览器逐个菜单点选操作单页面、死信页面、审计页面。 |
|
||
| 真实客户端 WS 在线通知 | 后台命令会生成业务事件/系统通知;本轮未启动真实 C 端 WebSocket 客户端观察 UI 实时刷新。 |
|
||
| Admin 查询迁移状态 | 本轮验证 IM Server 内部查询接口可用,但 Admin 查询侧尚未全部切换到内部查询 API,页面查询隔离需要后续迁移。 |
|
||
| 离线 Push | 按既定范围仍放在未实现增强项,本轮不验收。 |
|
||
|
||
### 10.3 2026-08-27 内部查询 API 双向联调
|
||
|
||
本轮在本地 Docker 环境重建 `im-local-server:dev` 和 `im-local-admin:dev` 后,对 Admin 查询 IM Server、IM Server 查询 Admin 两个方向做 HMAC 内部接口联调。IM Server 使用本地 MySQL `tinode`,Admin 使用本地 MySQL `hotgo`;Admin 本地联调容器为了避开队列消费者 Kafka 版本解析退出问题,临时使用 `disk` 队列驱动,不影响本节 HTTP 内部接口结论。
|
||
|
||
双向接口结果:
|
||
|
||
| 方向 | 接口/范围 | 结果 | 证据 |
|
||
| --- | --- | --- | --- |
|
||
| Admin -> IM Server | `/internal/admin-query/tenants/default/users?page=1&per_page=3` | 通过 | 返回 200,首条 `user_id=1565659909440147456`,JSON 类型为 number。 |
|
||
| Admin -> IM Server | `/internal/admin-query/tenants/default/users/{user_id}` | 通过 | 使用数据库 BIGINT 用户 ID 查询详情返回 200。 |
|
||
| Admin -> IM Server | `/internal/admin-query/tenants/default/users?user_id={user_id}` | 通过 | 使用数据库 BIGINT 用户 ID 过滤列表返回 200。 |
|
||
| Admin -> IM Server | `/internal/admin-query/tenants/default/groups/grpgvTd1mFvM2M` | 通过 | 返回 200,`owner_id=1565659909045882880`,JSON 类型为 number。 |
|
||
| Admin -> IM Server | `/internal/admin-query/tenants/default/groups/grpgvTd1mFvM2M/members` | 通过 | 返回 200,成员 `user_id/joined_by` 均为数据库 BIGINT number。 |
|
||
| Admin -> IM Server | `/internal/admin-query/tenants/default/kafka/dead-letters?limit=1` | 通过 | 返回 200。 |
|
||
| Admin -> IM Server | 未签名请求 | 通过 | 返回 401,提示 `internal signature required`。 |
|
||
| IM Server -> Admin | `/internal/im-config/tenants?limit=1&state=active` | 通过 | 返回 200,租户 `default`,业务库 `tinode`。 |
|
||
| IM Server -> Admin | `/internal/im-config/tenants/by-code/default` | 通过 | 返回 200,租户 `default`,业务库 `tinode`。 |
|
||
| IM Server -> Admin | `/internal/im-config/registration/sms` | 通过 | 返回 200,`enabled=true`。 |
|
||
| IM Server -> Admin | `/internal/im-config/third-party/oss?keys=endpoint,bucket,access_key_id` | 通过 | 返回 200,配置 map 正常返回。 |
|
||
| IM Server -> Admin | 未签名请求 | 通过 | 返回 401。 |
|
||
|
||
运行时链路也已确认:`im-local-im` 启动后持续通过 Docker 网络访问 `im-local-admin:8000/internal/im-config/tenants?limit=1000&state=active`,Admin 日志显示每 2 秒一次返回 200,说明不是只通过脚本单独打通。
|
||
|
||
本轮发现并处理:
|
||
|
||
| 问题 | 影响 | 处理结果 |
|
||
| --- | --- | --- |
|
||
| Admin `/internal/im-config/registration/sms` HTTP 200 但业务 `code=53` | IM Server 查询注册短信开关时会拿到业务失败响应。 | 已修复 Admin 控制器扫描目标,将 `g.Map` 改为 `*entity.SysConfig`,重建 Admin 后复测返回 `code=0`。 |
|
||
| Admin 本地容器入口模板缺少新增内部接口配置 | 使用默认入口脚本会覆盖完整 `config.yaml`,导致内部接口密钥配置丢失。 | 本地联调改为挂载完整临时配置并直接启动 `./hotgo`;正式镜像建议同步补齐 `config.template`。 |
|
||
| Admin 本地 Kafka 队列版本解析退出 | Admin 启动队列消费者时 `invalid version 2.0.0.0`,进程 fatal。 | 本地 HTTP 联调临时切到 `disk` 队列驱动;Kafka consumer 退避重连和版本配置仍按增强项跟进。 |
|
||
| Admin 租户表查询仍出现 `hg_im_tenant` | 默认数据库前缀 `hg_` 会污染 `im_tenant/im_tenant_config` 这类 IM 公共表查询。 | 已修复 Admin `imtenant.RegistryDB()`:自动建立/使用 `Prefix` 为空的 `imRegistry` 连接;重建 Admin 后日志确认查询 `hotgo.im_tenant`,不再访问 `hg_im_tenant`。 |
|
||
|
||
ID 口径结论:IM Server 外部聊天协议仍使用 `usr...`;Admin 内部管理/查询接口统一使用数据库 BIGINT/int64,查询返回和写命令入参不再混用 Tinode `usr...` 格式。
|