Files
im_server/docs/IM-工作台服务拆分迁移稳定方案.md
T

483 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# IM 工作台服务拆分迁移稳定方案
> 目标:将 `im_server` 中的工作台业务拆到独立仓库 `/Users/youshunlai/go_workspace/im_workbench`,先形成独立服务和独立发布边界;数据库暂不迁移,等接口全部稳定后再单独做工作台库迁移。
## 1. 结论
建议采用“独立仓库 + 分阶段迁移 + Nginx 分流 + Redis 统一 Token 鉴权”的方式:
1. `im_server` 保留 IM 核心能力:登录态校验、聊天协议、单聊/群聊、消息存储、长连接、在线状态、Redis 实时 fanout、Kafka 消息消费。
2. `im_workbench` 承接工作台能力:工作台入口、组织架构、考勤、会议、会议笔记、日报、月报、总经理信箱、工作台红点。
3. 当前阶段不迁移数据库,`im_workbench` 先连接现有租户库读取和写入工作台相关表。
4. 等 `im_workbench` 接口、鉴权、Kafka、监控全部稳定后,再单独启动数据库迁移专项。
5. 工作台接口直接切换为 `/v1/workbench/*`,由 Nginx/API Gateway 按路径分发到 `im_workbench`;`im_server` 不再代理工作台请求。
6. 迁移采用“先复制迁出、旧代码保留”的方式;只有 `im_workbench` 全部接口、数据库、Kafka、监控联调通过后,才删除 `im_server` 中的工作台旧代码。
一句话:网关分流,Redis 鉴权,先拆服务和接口,数据库后迁,最后清理 `im_server` 中的工作台旧代码。
## 2. 拆分范围
### 2.1 迁出到 im_workbench
以下业务从 `im_server` 迁移到 `im_workbench`:
| 模块 | 当前接口示例 | 迁移归属 |
|---|---|---|
| 工作台入口 | `GET /v1/workbench` | im_workbench |
| 组织架构 | `/v1/workbench/organization/*` | im_workbench |
| 考勤打卡 | `/v1/workbench/attendance/*` | im_workbench |
| 会议通知 | `/v1/workbench/meetings*` | im_workbench |
| 会议笔记 | `/v1/workbench/meeting-notes`、`/v1/workbench/meetings/{id}/notes*` | im_workbench |
| 日报 | `/v1/workbench/reports/daily*` | im_workbench |
| 月报 | `/v1/workbench/reports/monthly*` | im_workbench |
| 总经理信箱 | `/v1/workbench/mailbox*` | im_workbench |
| 工作台红点 | `workbench_total_unread`、模块未读 | im_workbench 计算,im_server 聚合透出 |
### 2.2 留在 im_server
以下能力继续留在 `im_server`:
| 模块 | 说明 |
|---|---|
| 登录与 Token 签发 | C 端登录仍由 IM 负责,登录成功后把 Token 会话写入 Redis。 |
| 聊天消息 | 单聊、群聊、消息 seq、消息落库、Redis fanout、WS 下发。 |
| 群治理 | 群资料、群成员、禁言、进群审批、群公告、群文件。 |
| 文件上传入口 | `POST /v0/file/u/` 暂留 im_server,工作台只保存业务引用;后续可单独拆文件服务。 |
| 系统通知投递 | 最终给 C 端的在线通知仍由 im_server 通过 WS 下发。 |
| Kafka 消费到 C 端 | Admin 或 Workbench 产生的业务事件,由 im_server 消费后找在线用户下发。 |
## 3. 目标架构
```text
C 端 App
|
| HTTPS
v
Nginx / API Gateway
|-- /v1/im/*、/v0/channels、/v0/file/u/ -> im_server
|-- /v1/workbench/* -> im_workbench
im_workbench
|
| MySQL
v
现有租户库中的工作台表
im_workbench
|
| Kafka: workbench 业务事件
v
im_server
|
| WebSocket
v
C 端在线用户
```
稳定边界:
1. `im_workbench` 不直接操作 IM 消息表、在线连接、WebSocket。
2. `im_server` 不直接写工作台业务表,也不代理工作台 HTTP 请求。
3. `im_server` 登录成功后将 Token 会话写入 Redis,`im_workbench` 每次请求从 Redis 校验 Token。
4. 两边共享用户 ID 口径:内部管理和工作台接口使用数据库 `BIGINT/int64`;聊天协议层仍可使用 `usr...`。
5. 工作台需要通知 C 端时投 Kafka 事件,由 `im_server` 统一下发。
## 4. 数据库处理方案
### 4.0 当前阶段原则
当前阶段数据库先不迁移。`im_workbench` 独立服务启动后,仍访问现有租户库中的工作台相关表,先保证新服务接口、Redis Token 鉴权、Nginx 路由、Kafka 事件和监控全部跑通。
代码层面禁止服务启动时自动建库建表。`im_server` 和 `im_workbench` 都只负责连接已有数据库和执行正常业务 SQL;所有 `CREATE DATABASE`、`CREATE TABLE`、`ALTER TABLE`、索引变更、初始化字典和种子数据,统一整理为 SQL 文件,经审核后由人工或运维发布流程执行。
数据库迁移作为后置专项处理,触发条件是:
1. `/v1/workbench/*` 全部接口在本地、测试环境、前端联调中稳定。
2. `im_server` 中工作台旧接口确认没有客户端继续访问。
3. 工作台事件、通知、红点、文件引用链路全部验收通过。
4. 运维确认新库命名、备份、监控、账号权限和迁移窗口。
### 4.1 当前工作台相关表
当前这些表仍保留在租户库 `tinode` 中,暂不迁移;后续数据库专项时再纳入迁移范围:
| 表 | 用途 | 迁移建议 |
|---|---|---|
| `im_workbench_apps` | 工作台模块配置、排序、入口 | 暂留租户库;后续迁到工作台库或由 Admin 配置后同步。 |
| `im_org_departments` | 组织部门树 | 暂留租户库;后续迁到工作台库。 |
| `im_org_members` | 组织成员、岗位、联系方式 | 暂留租户库;保留 `user_id BIGINT` 关联 IM 用户。 |
| `im_org_visibility_overrides` | 组织可见范围覆盖 | 暂留租户库。 |
| `im_attendance_rules` | 考勤规则 | 暂留租户库。 |
| `im_attendance_shifts` | 考勤班次 | 暂留租户库。 |
| `im_attendance_records` | 考勤日记录 | 暂留租户库。 |
| `im_attendance_checkins` | 打卡流水 | 暂留租户库。 |
| `im_attendance_appeals` | 补卡/异常申诉 | 暂留租户库。 |
| `im_meetings` | 会议主表 | 暂留租户库。 |
| `im_meeting_participants` | 会议参与人 | 暂留租户库。 |
| `im_meeting_note_files` | 会议笔记文件引用 | 暂留租户库;文件实体仍在文件表/OSS。 |
| `im_daily_reports` | 日报 | 暂留租户库。 |
| `im_monthly_reports` | 月报 | 暂留租户库。 |
| `im_mailbox_config` | 总经理信箱配置 | 暂留租户库;后续可迁到工作台库或 Admin 公共库配置后同步。 |
| `im_mailbox_submissions` | 总经理信箱提交 | 暂留租户库。 |
### 4.2 不迁出的表
| 表/数据 | 原因 |
|---|---|
| `users`、认证、会话、设备 | 属于 IM 登录和账号基础能力。 |
| `topics`、`subscriptions`、`messages`、`im_group_messages` | 属于聊天主链路。 |
| 好友、黑名单、群成员、群治理表 | 属于 IM 社交关系和群聊能力。 |
| 文件实体表、OSS 元数据 | 暂不迁移,工作台只保存 `file_id` 引用。 |
| `im_system_notifications` | 建议先留在 im_server,作为统一 C 端通知中心。后续如要拆,可单独设计通知服务。 |
| `im_business_events_outbox` | 已废弃,不纳入迁移;当前代码不再自动建表,工作台拆分后事件统一走 Kafka 或实时投递链路。 |
### 4.3 后续目标库建议
后续接口稳定后,再给工作台单独库,命名按环境区分:
```text
本地:im_workbench
测试:im_workbench_test 或按运维规范命名
生产:im_workbench
```
如果继续保持“一个租户一个库”,则工作台也应支持租户库路由:
```text
hotgo.im_tenant
- code
- im_db_name
- workbench_db_name
- im_internal_base_url
- workbench_internal_base_url
```
正式方向建议在 `hotgo.im_tenant` 或租户配置表里增加工作台库配置,而不是让 `im_server` 读取工作台库 DSN。
### 4.4 后续数据迁移顺序
迁库等接口全部稳定后再做,不要和本轮服务拆分混在一起。后续建议分四步:
1. 建新库新表:在 `im_workbench` 库手动执行审核后的 SQL,创建同结构表,先不接线上流量。
2. 全量复制:从租户库复制历史工作台表数据到工作台库。
3. 增量同步:迁移窗口内对工作台写操作做双写,或由旧库 binlog/脚本同步到新库。
4. 切读切写:先灰度读新库,再切写新库,最后关闭旧库工作台写入。
迁移校验至少包括:
| 校验项 | 说明 |
|---|---|
| 行数校验 | 每张表按租户统计行数。 |
| 主键/业务唯一键校验 | `meeting_id`、`report_id`、`submission_id`、`department_id` 等必须一致。 |
| 更新时间校验 | 对比 `updated_at` 最大值,确认增量无遗漏。 |
| 抽样内容校验 | 抽样会议、日报、月报、考勤记录详情。 |
| 外键引用校验 | 会议参与人、会议笔记文件、组织成员部门引用不能断。 |
### 4.5 回滚原则
当前阶段没有数据库迁移,回滚以服务和路由为主。后续真正迁库时,旧库数据至少保留一个发版周期,不删除旧表。
回滚策略:
1. 如果服务拆分异常:关闭 `/v1/workbench/*` 路由,前端回退旧路径或旧版本。
2. 如果 `im_workbench` 业务异常:保留 `im_server` 旧工作台代码作为临时回退能力。
3. 如果 Redis 鉴权异常:停止工作台新路由,优先恢复登录态校验。
4. 如果 Kafka 通知异常:业务写入不回滚,只降级为客户端主动拉取和后台补偿推送。
5. 后续如果新库读写异常:再按迁库专项方案切回旧库。
## 5. 路由与鉴权迁移方案
### 5.1 Nginx 分流规则
工作台接口直接使用新路径 `/v1/workbench/*`,Nginx/API Gateway 按路径分流:
```text
/v1/im/* -> im_server
/v0/channels -> im_server
/v0/file/u/ -> im_server
/v1/workbench/* -> im_workbench
```
建议工作台目标路由:
| 原 IM 路径 | 新 Workbench 路径 |
|---|---|
| `GET /v1/im/workbench` | `GET /v1/workbench` |
| `/v1/im/organization/*` | `/v1/workbench/organization/*` |
| `/v1/im/attendance/*` | `/v1/workbench/attendance/*` |
| `/v1/im/meetings*` | `/v1/workbench/meetings*` |
| `/v1/im/meeting-notes*` | `/v1/workbench/meeting-notes*` |
| `/v1/im/meetings/{id}/notes*` | `/v1/workbench/meetings/{id}/notes*` |
| `/v1/im/reports/daily*` | `/v1/workbench/reports/daily*` |
| `/v1/im/reports/monthly*` | `/v1/workbench/reports/monthly*` |
| `/v1/im/mailbox*` | `/v1/workbench/mailbox*` |
不建议让 `im_server` 继续代理工作台请求。路由由 Nginx 分流,服务各自鉴权、各自访问自己的数据库。
### 5.2 Redis Token 鉴权
登录仍由 `im_server` 负责。登录成功后,`im_server` 将 Token 会话写入 Redis,`im_workbench` 直接从 Redis 校验登录态。
Redis Key 建议:
```text
im:session:token:{token_hash}
```
Value 建议使用 JSON:
```json
{
"user_id": 42,
"tenant_id": 1,
"tenant_code": "default",
"device_id": "ios-device-1",
"auth_level": "auth",
"state": "active",
"expires_at": "2026-09-13T18:00:00+08:00",
"issued_at": "2026-09-13T10:00:00+08:00"
}
```
请求 Header 保持 C 端统一:
| Header | 说明 |
|---|---|
| `Authorization` | `Bearer {token}`,两个服务都从 Redis 校验。 |
| `X-MeetingIM-APIKey` | 应用 API Key。 |
| `X-Tenant-Code` | 租户编码。 |
| `X-IM-Schema` | 协议版本。 |
| `X-Request-ID` | 链路追踪 ID。 |
校验流程:
1. 从 `Authorization` 取 Bearer Token,先做格式校验。
2. 对 Token 做 hash 后查 Redis,避免 Redis 明文 Key 暴露原始 Token。
3. 校验会话存在、未过期、`state=active`。
4. 校验 `X-Tenant-Code` 与 Redis 会话中的 `tenant_code` 一致。
5. 校验 API Key 是否可用于当前租户和客户端类型。
6. 将 `tenant_id`、`tenant_code`、`user_id`、`device_id` 写入请求上下文。
Token 失效场景:
| 场景 | 处理 |
|---|---|
| 用户主动退出 | 删除当前 Token Key。 |
| 踢设备 | 删除指定 `device_id` 对应 Token Key。 |
| 冻结/封禁账号 | 删除用户全部 Token Key,并通过 Kafka 通知在线连接断开。 |
| 修改密码 | 删除用户全部 Token Key。 |
| Token 过期 | Redis TTL 自动过期。 |
### 5.3 共享鉴权代码
建议抽一个轻量公共包,两个服务复用:
```text
meetingim_common/auth/session
```
公共包只放稳定协议:
1. Redis Key 生成。
2. Token hash 规则。
3. Session JSON 结构。
4. Header 解析。
5. 错误码定义。
不要把数据库访问、HTTP handler、业务权限放进公共包,避免公共包变成新的耦合点。
## 6. 事件与通知
工作台拆出后,工作台业务变更不再直接调用 `im_server` 代码函数,而是投 Kafka。
建议 Topic:
| Topic | 生产者 | 消费者 | 用途 |
|---|---|---|---|
| `im.workbench.event` | im_workbench | im_server | 工作台事件、红点、系统通知、WS 下发。 |
| `im.cache.invalidate` | im_workbench/Admin | im_server/im_workbench | 组织、工作台模块、配置变更后的缓存失效。 |
| `im.audit.event` | im_workbench | Admin/审计消费者 | 工作台敏感操作审计。 |
`im.workbench.event` 消息建议字段:
```json
{
"event_id": "wb_default_meeting_mtg_1_created_v1",
"tenant_id": 1,
"tenant_code": "default",
"actor_user_id": 42,
"module": "meeting",
"event_type": "meeting.created",
"resource_id": "mtg_1",
"target_user_ids": [42, 43],
"title": "会议通知",
"content": "项目周会",
"badge_delta": 1,
"payload": {
"meeting_id": "mtg_1",
"version": 1
},
"occurred_at": "2026-09-13T13:30:00+08:00"
}
```
消费原则:
1. `event_id` 全局幂等,消费端记录处理日志。
2. 系统通知写入失败可重试,超过次数进入死信。
3. WS 下发失败不影响业务写入,客户端重连后通过工作台接口和 bootstrap 补偿。
4. 工作台红点以服务端查询为准,Kafka 只做实时增量提醒。
## 7. 代码迁移步骤
### 7.1 第一阶段:服务骨架
在 `/Users/youshunlai/go_workspace/im_workbench` 新建 Go 服务:
```text
im_workbench/
cmd/workbench/
internal/http/
internal/store/
internal/kafka/
internal/config/
docs/
sql/
```
第一阶段只做:
1. 配置文件、日志、MySQL、Redis、Kafka 初始化。
2. 健康检查:`GET /healthz`、`GET /readyz`。
3. Redis Token 鉴权中间件。
4. 租户上下文解析。
5. 复制工作台接口 DTO 和响应结构。
### 7.2 第二阶段:迁移业务代码
从 `im_server` 迁出以下代码:
| 当前位置 | 迁移内容 |
|---|---|
| `server/im_http.go` | `serveIMWorkbench`、Organization、Attendance、Meetings、MeetingNotes、DailyReports、MonthlyReports、Mailbox 相关 handler。 |
| `server/store/store.go` | 工作台相关 Persistence Interface 和 mapper。 |
| `server/store/types/*.go` | `workbench.go`、`organization.go`、`attendance.go`、`meeting.go`、`reports.go`、`mailbox.go` 等类型。 |
| `server/db/mysql/tenant_adapter.go` | 工作台相关 SQL 实现。 |
| `server/im_business_events.go` | 工作台事件生产逻辑拆到 `im_workbench`,只保留 IM 消费下发。 |
| `server/kafka_workbench_audit.go` | 工作台事件生产侧迁出,IM 侧保留消费或下发处理。 |
迁移时要保留测试:
1. 原 handler 单测迁到 `im_workbench`。
2. `im_server` 原有工作台测试暂时保留,作为旧链路回归保护。
3. `im_workbench` 新增同等接口测试,确认新路径行为一致。
4. Kafka 事件用例拆为 Workbench 生产端测试和 IM 消费端测试。
### 7.3 第三阶段:Nginx 路由切换
`im_server` 中原工作台 handler 暂时不删除,不做主链路代理。新请求由 Nginx/API Gateway 直接分流到 `im_workbench`:
```text
客户端请求 /v1/workbench
-> Nginx/API Gateway
-> im_workbench Redis Token 鉴权
-> return response
```
路由层必须支持:
1. `/v1/workbench/*` 全量转发到 `im_workbench`。
2. `/v1/im/*`、`/v0/channels`、`/v0/file/u/` 继续转发到 `im_server`。
3. 透传 `Authorization`、`X-MeetingIM-APIKey`、`X-Tenant-Code`、`X-IM-Schema`、`X-Request-ID`。
4. 工作台服务超时建议 3 到 5 秒。
5. 工作台不可用时返回明确 `WORKBENCH_UNAVAILABLE` 或网关 502/504,不影响聊天主链路。
旧代码保留策略:
1. `im_server` 工作台旧 handler、store、SQL 先保留。
2. 前端切新路径后,测试环境持续观察一个发版周期。
3. `im_workbench` 全部接口、数据库、Kafka、监控确认稳定后,再从 `im_server` 删除旧工作台代码。
4. 删除前要确保没有客户端继续访问 `/v1/im/workbench`、`/v1/im/meetings*` 等旧路径。
### 7.4 第四阶段:接口稳定验收
当前阶段只做接口稳定验收,不做数据库切换。建议保留以下服务开关:
| 配置 | 说明 |
|---|---|
| `workbench.service.enabled` | 是否启用工作台独立服务。 |
| `workbench.route.enabled` | Nginx/API Gateway 是否启用 `/v1/workbench/*` 路由。 |
| `workbench.kafka.enabled` | 是否启用工作台事件 Kafka。 |
后续迁库专项再增加数据库切换开关:
```text
workbench.db.read_from=old|new|shadow
workbench.db.write_to=old|dual|new
```
## 8. 发布与回归
### 8.1 发布前检查
1. `im_workbench` 健康检查通过。
2. `im_workbench` 可连接现有租户库,并只访问工作台相关表。
3. Redis Token 鉴权与 `im_server` 登录态一致。
4. Nginx/API Gateway 工作台路由灰度开关默认关闭或仅测试环境开放。
5. Kafka Topic、消费组、死信队列已建。
6. 监控已覆盖 HTTP 成功率、耗时、Kafka 积压、DB 错误、网关转发失败。
7. 本阶段不要求新工作台库建表和数据复制。
### 8.2 必测用例
| 模块 | 用例 |
|---|---|
| 工作台入口 | 登录后返回模块排序、开关、未读角标。 |
| 组织架构 | 普通用户、手机号注册用户、邀请用户、测试账号可见范围正确。 |
| 考勤 | 配置查询、打卡、重复幂等、记录列表、申诉创建。 |
| 会议 | 创建、列表、详情、修改、取消、提醒、参会响应。 |
| 会议笔记 | 上传文件、绑定文件、详情、替换、删除、权限校验。 |
| 日报 | 创建草稿、保存、提交、列表、统计、提醒。 |
| 月报 | 创建、保存、提交、reopen、详情、统计。 |
| 总经理信箱 | 配置查询、匿名/实名提交、附件引用。 |
| 通知 | 工作台事件生成系统通知,在线用户收到 WS,离线用户重登可补偿。 |
| 回滚 | 关闭 `/v1/workbench/*` 路由后,前端回退旧版本或网关明确降级;旧代码未删前可切回旧路径。 |
## 9. 风险点
| 风险 | 处理 |
|---|---|
| 一次性拆代码和迁库导致回归面过大 | 本阶段不迁库,只拆服务和接口;数据库等接口稳定后单独迁移。 |
| 工作台依赖 IM 用户资料 | 通过 IM 内部查询 API 获取用户摘要,禁止直连 IM 库。 |
| IM 需要工作台红点 | 通过工作台内部接口或 Kafka 事件聚合,禁止 im_server 查工作台库。 |
| 文件上传仍在 im_server | 工作台保存 `file_id` 引用,文件服务后续再独立拆。 |
| Kafka 事件丢失 | 生产端直接投 Kafka,必要时使用业务失败表补偿;消费端使用幂等和死信队列。 |
| 前端路径切换风险 | 前端切到 `/v1/workbench/*` 前必须完成接口对照、灰度和回归。 |
| 数据不一致 | 当前阶段继续读写同一套租户库,避免双写不一致;后续迁库再做影子读和校验。 |
## 10. 推荐落地顺序
1. 在 `im_workbench` 建服务骨架、配置、健康检查、Redis Token 鉴权。
2. 迁移工作台类型、store interface、MySQL 实现和单测。
3. 迁移 `/v1/workbench`、组织架构、考勤接口。
4. 迁移会议、会议笔记、日报、月报、总经理信箱接口。
5. Nginx/API Gateway 增加 `/v1/workbench/*` 路由,默认关闭或只对测试环境开放。
6. 本地环境用新路径跑通全部工作台接口。
7. 测试环境启用新路由灰度,只切少量测试账号。
8. 全量接口稳定后,确认没有客户端访问旧 `/v1/im/*` 工作台路径。
9. 清理 `im_server` 内工作台业务实现,只保留 IM 主链路。
10. 单独启动数据库迁移专项,新建工作台库、复制数据、影子读、切读写。
## 11. 当前建议先做的第一步
第一步不要急着删 `im_server` 代码,建议先在 `im_workbench` 完成最小可运行服务:
1. 监听端口建议:`7070`。
2. 支持 `GET /healthz`、`GET /readyz`。
3. 支持 Redis Token 鉴权和统一 Header 校验。
4. 接入本地 MySQL、Redis、Kafka 配置。
5. 先迁 `GET /v1/workbench` 一个读接口做样板。
6. Nginx/API Gateway 加 `/v1/workbench` 测试路由,只开放样板接口。
这样第一步上线风险最低,也能验证服务拆分、Redis 鉴权、链路追踪、配置和部署是否顺畅。