Files
im_server/docs/IM-Kafka接入联调测试文档.md
T

47 KiB
Raw Blame History

IM Kafka 接入联调测试文档

关联文档:IM Kafka 接入方案、IM Kafka 接入范围识别、IM Kafka 基础表 SQL
适用范围:Admin/HotGo 与 IM Server 通过 Kafka 和内部只读 API 联调。
当前约定:Admin 只访问 hotgo,IM Server 只访问租户库;跨系统写操作走 Kafka,跨系统查询走内部只读 API。

1. 联调目标

  1. 验证 Kafka 基础生产、消费、幂等、重试、死信、Outbox 能正常工作。
  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,Kafka 只处理文件元数据和生命周期事件。
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.file.lifecycle IM -> IM 文件上传完成、附件绑定、群文件登记/删除。
im.workbench.event Admin/IM -> IM 工作台事件、红点、系统通知。
im.audit.event IM -> Admin/审计消费者 审计事件。
im.dead.letter IM -> Admin/告警 消费失败死信。

说明:im.message.push 已规划但本轮不联调,离线 Push Kafka 化仍在增强项中。

2.3 数据库准备

租户库必须执行:

SOURCE docs/sql/IM-Kafka基础表SQL.sql;

重点确认租户库存在:

表 用途
im_kafka_consume_log 消费幂等、重试状态。
im_kafka_outbox IM 侧可靠生产 Kafka 事件。
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,用于告警和排障。
im_audit_events Admin 消费 im.audit.event 后落 HotGo,用于 Kafka 审计事件列表。
Admin 配置表/字典表 为 /internal/im-config/* 提供数据源。

Admin/HotGo 侧需执行:

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 环境变量:

{
  "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": 5
  },
  "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、im.workbench.event。
  2. Kafka consumer 消费 im.admin.command.result、im.dead.letter、im.audit.event。
  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 签名。

签名文本:

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 审计表。 关键业务动作生产审计事件。

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 内部查询接口签名通过。

请求:

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 发送:

{
  "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:

{
  "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 封禁/解封用户。

封禁事件:

{
  "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。

事件:

{
  "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_messages。
  5. A 使用同一 x-im-client-mid 重复发送。

预期:

  1. A 立即收到服务端 ack 和 seq_id。
  2. seq_id 为 BIGINT/int64。
  3. 消费成功后 im_messages 有一条记录。
  4. 重复发送不重复落库。
  5. 消费日志 succeeded。

TC-KAFKA-014 群聊消息异步落库

目标:验证群消息写入 im_group_messages,不混入私聊表。

步骤:

  1. 创建群并加入至少两个成员。
  2. 成员发送群消息。
  3. 查询 im_group_messages。
  4. 查询 im_messages。
  5. 重连客户端按 Topic seq 拉取消息。

预期:

  1. 群消息只写 im_group_messages。
  2. seq_id 单调增长,类型为 BIGINT/int64。
  3. 客户端补偿拉取成功。
  4. 24 小时热消息可进入 Redis 热缓存。

TC-KAFKA-015 消息消费失败重试

目标:验证 im.message.persist 消费失败可重试并最终成功。

步骤:

  1. 临时制造租户库写入失败,如锁表或停止库连接。
  2. 发送消息。
  3. 恢复数据库。
  4. 等待重试。

预期:

  1. im_kafka_consume_log.retry_count 增加。
  2. 未超过重试上限时不进入死信。
  3. 数据库恢复后消息最终落库。
  4. 状态变为 succeeded。

TC-KAFKA-016 文件上传完成事件

目标:验证文件上传完成后产生 im.file.lifecycle 事实事件。

步骤:

  1. 客户端上传文件到 OSS。
  2. 上传完成后查询租户库文件记录。
  3. 检查 Kafka outbox 和 im.file.lifecycle。

预期:

  1. 文件字节流只进入 OSS。
  2. Kafka 只包含文件元数据,不包含文件内容。
  3. 上传完成事件可被消费并审计。

TC-KAFKA-017 消息附件绑定

目标:验证消息落库后附件引用由 im.file.lifecycle 绑定。

步骤:

  1. 上传图片或文件。
  2. 发送带附件的私聊消息。
  3. 发送带附件的群聊消息。
  4. 查询附件引用表。

预期:

  1. 私聊附件写入 im_filemsglinks。
  2. 群聊附件写入 im_group_files。
  3. 重复消费不重复绑定。
  4. 文件 ACL 与业务引用可追踪。

TC-KAFKA-018 群文件登记与删除

目标:验证群文件业务事件接入 im.file.lifecycle。

步骤:

  1. 群内登记文件。
  2. 查询 im_group_files。
  3. 删除群文件。
  4. 检查生命周期事件和审计事件。

预期:

  1. 群文件登记成功。
  2. 删除状态正确。
  3. 文件事件可用于后续回收/合规删除。

TC-KAFKA-019 工作台会议事件

目标:验证会议创建、更新、取消、提醒、参会响应产生工作台事件。

步骤:

  1. 创建会议。
  2. 修改会议。
  3. 发送提醒。
  4. 参会人响应。
  5. 取消会议。

预期:

  1. 每个动作产生 im.workbench.event。
  2. 消费后写 im_system_notifications。
  3. 写 im_business_events_outbox。
  4. 在线 C 端收到系统通知或红点刷新。
  5. 审计事件已投递。

Admin 连通性补充:可在「IM管理 -> Kafka命令」点击「发送工作台测试事件」,或调用 POST /admin/imKafkaCommand/workbenchEvent,验证 Admin 具备生产 im.workbench.event 的能力。真实会议、日报/月报、信箱业务结果仍以 IM Server 对应业务用例为准。

TC-KAFKA-020 日报/月报事件

目标:验证日报、月报提交和催办事件。

步骤:

  1. 创建日报草稿。
  2. 保存并提交日报。
  3. 管理员催办日报。
  4. 创建、提交、重开月报。

预期:

  1. 对应事件进入 im.workbench.event。
  2. 通知目标用户正确。
  3. 工作台红点或系统通知可被客户端看到。

TC-KAFKA-021 总经理信箱事件

目标:验证总经理信箱提交事件和审计。

步骤:

  1. 用户提交信箱内容。
  2. 检查 im.workbench.event。
  3. 检查 im.audit.event。
  4. 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 删除缓存。

示例事件:

{
  "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 失效。

事件:

{
  "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 查询死信。

请求:

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 命令处理死信状态。

事件类型:

{
  "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 审计事件

目标:验证关键后台和敏感操作都有审计事件。

覆盖:

  1. 群治理命令。
  2. 用户治理命令。
  3. 文件生命周期。
  4. 工作台状态变化。
  5. 死信生成和处理。

预期:

  1. im.audit.event 收到事件。
  2. 事件包含 tenant_code、actor、biz_type、biz_id、event_type、occurred_at。
  3. Admin 审计消费者可落 HotGo im_audit_events。
  4. Admin 可在「IM管理 -> Kafka审计事件」查看审计事件列表和详情。

TC-KAFKA-030 Outbox 可靠投递

目标:验证 IM 侧 Kafka Outbox 防止业务成功但 Kafka 丢事件。

步骤:

  1. 临时停止 Kafka。
  2. 执行会产生 outbox 的操作,如发送消息、提交工作台事件。
  3. 查询 im_kafka_outbox。
  4. 恢复 Kafka。
  5. 等待 outbox worker 投递。

预期:

  1. Kafka 停止时业务 outbox 记录为 pending/failed。
  2. 恢复后记录变为 sent。
  3. 下游消费者最终收到事件。
  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 im_audit_events,并可在 Kafka审计事件页面查看。
配置接口 /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、outbox worker 正常启动。
租户路由 tenant_code 能路由到正确租户库。
消费幂等 重复 event_id 不重复执行业务。
重试 可重试错误按退避时间重试。
死信 超过上限或不可重试错误进入死信并回 result。
Admin Query API 内部接口验签、鉴权、nonce 防重放生效。
Admin Config Client 可从 Admin 拉租户、注册、第三方配置。
用户治理 封禁/冻结踢下线,解封/恢复可登录,设备踢出只影响指定设备。
群治理 群资料、角色、禁言、移除、转让群主全部生效。
消息落库 私聊写 im_messages,群聊写 im_group_messages。
文件生命周期 附件引用绑定到正确业务表。
工作台事件 生成系统通知和 WS 业务事件。
审计 关键动作进入 im.audit.event。

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_kafka_outbox 是否 sent,im.message.persist 是否消费成功。
附件未绑定 查消息是否带附件、im.file.lifecycle 是否消费成功。
内部 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.file.lifecycle、im.workbench.event 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 用户治理审计事件。 通过 Admin 消费 im.audit.event 链路已验证;正式落表应为 hotgo.im_audit_events。

本轮发现并处理:

问题 现象 处理
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 侧审计模型/DAO 表名映射,所有 im_* 业务表不加 hg_ 前缀。

本轮未覆盖,后续继续:

范围 待测内容
群治理 群资料、治理配置、管理员设置/取消、成员禁言/解禁、移除成员、转让群主。
用户治理完整集 user.ban、user.unban、user.freeze、user.unfreeze、user.device.kick、注销/删除账号。
消息异步落库 私聊 im_messages、群聊 im_group_messages、seq_id BIGINT、重复 client_mid 幂等。
文件生命周期 上传完成、附件绑定、群文件登记/删除、OSS 元数据和引用表。
工作台事件 会议、日报/月报、总经理信箱、红点/系统通知投递。
死信闭环 死信查询、忽略、解决、重放,以及重放后二次成功/失败。
Outbox Kafka 停止/恢复时 im_kafka_outbox、Admin outbox 的可靠投递。
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 群聊消息异步落库。 通过 message.persist.group 消费 succeeded,im_group_messages 写入测试群消息。
TC-KAFKA-016 文件上传完成事件。 通过 file.upload.completed 消费 succeeded。
TC-KAFKA-017 消息附件绑定。 通过 群聊附件绑定写入 im_group_files;私聊附件绑定修复后写入 im_filemsglinks。
TC-KAFKA-018 群文件登记与删除生命周期。 通过 group.file.created/group.file.deleted 消费 succeeded。
TC-KAFKA-019 工作台会议事件。 通过 workbench.meeting.created 消费 succeeded,写入 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;正式落表应为 hotgo.im_audit_events。
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... 格式。