Appearance
系统架构
整体架构
IM 系统采用前后端分离的架构设计。
┌──────────────────────────────────────────────────────────────┐
│ 客户端 (UniApp) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐ │
│ │ login │ │ home │ │ chat │ │ partner │ │
│ └──────────┘ └──────────┘ └──────────┘ └────────────┘ │
│ │
│ ┌─────────────────────┐ ┌────────────────────────────┐ │
│ │ im_page_mixin.js │ │ im-push-listener.vue │ │
│ │ 公共逻辑层 │ │ 全局 WebSocket 推送监听 │ │
│ └─────────────────────┘ └────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ middleware (this.middle) │ │
│ │ http.js → 自动 Token 注入 + 401 处理 + loading 管理 │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────┬─────────────────────────────┬─────────────────┘
│ HTTP (REST API) │ WebSocket
▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ IM 后端 (Express + ws) │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌───────────────────┐ │
│ │ REST API │ │ WebSocket │ │ 静态文件服务 │ │
│ │ 49 个接口 │ │ 17 种事件 │ │ (server/public/) │ │
│ └──────┬──────┘ └──────┬───────┘ └───────────────────┘ │
│ │ │ │
│ ┌──────┴────────────────┴───────────────────────────────┐ │
│ │ 中间件层 │ │
│ │ CORS → JSON 解析 → 频率限制 → JWT 验证 → 路由处理 │ │
│ └──────────────────────┬────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────┴────────────────────────────────┐ │
│ │ 数据库层 (db.js) │ │
│ │ sql.js (SQLite WASM) → 内存数据库 → 防抖写入磁盘 │ │
│ │ WAL 模式 | 8MB 缓存 | NORMAL 同步 | 5s 超时 │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘认证体系
系统使用两种认证方案:
JWT Bearer Token:面向终端用户。登录或注册后返回 JWT Token,有效期 7 天。用户请求通过 Authorization: Bearer <token> 头认证。WebSocket 连接支持查询参数 ?token=xxx 或 auth 消息两种认证方式。
API Key Bearer Token:面向管理员和第三方系统。首次启动自动生成,存储在数据库 configs 表中。所有 /api/admin/* 接口使用此认证。
安全提示
API Key 是管理员接口的唯一凭证,请妥善保管,不要泄露到客户端代码中。
数据库设计
数据库使用 sql.js(SQLite 的 WebAssembly 编译版本),整个数据库加载到内存中运行,通过防抖机制定期写入磁盘文件。
性能优化:WAL 日志模式减少写锁冲突,8MB 内存缓存加速查询,NORMAL 同步级别平衡性能与安全,10 个索引覆盖高频查询场景。
写入策略:每次写操作触发 100ms 防抖定时器,多个快速写入合并为一次磁盘 IO。进程关闭时通过 forceSave() 立即落盘确保数据不丢失。
数据库详情
详细的数据库表结构请参考 数据库结构。
消息投递流程
发送方 IM 后端 接收方
│ │ │
│── WS message ────────▶│ │
│ │── 参数校验 │
│ │── 屏蔽/禁言检查 │
│ │── 保存消息到 DB │
│ │── 更新未读计数 │
│◀── WS ack ───────────│ │
│ │ │
│ │── 检查接收方是否在线 ───▶│
│ │ │ │
│ │ ├── 在线 → WS 推送 │
│ │ └── 离线 → Web Push │
│ │ │- 发送方通过 WebSocket 发送
message类型消息 - 后端校验参数、检查屏蔽/禁言状态
- 消息持久化到 SQLite,更新未读计数
- 向发送方返回
ack确认 - 检查接收方 WebSocket 连接状态
- 在线则通过 WebSocket 实时推送,离线则触发 Web Push 通知
实时推送机制
除了聊天页面自身的 WebSocket 连接外,系统还提供了 im-push-listener 全局组件。该组件在 home.vue 等页面中嵌入,维护一个独立的 WebSocket 连接用于接收实时通知:
- 新消息到达时触发
im-push-message全局事件 - 好友请求到达时触发
im-push-notification全局事件 - 连接断开后自动重连(5 秒间隔)
- 25 秒心跳保活
这确保了用户在浏览不同页面时都能及时收到消息通知。