# EchoChat 音视频会议直播系统 - 整体设计方案

> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

**目标：** 构建一套跨端可用、可扩展、可演进的实时音视频会议直播系统，第一期（MVP）实现用户体系、即时聊天、好友管理、多人音视频会议、消息通知五大核心功能。

**架构：** 精简单体 + 媒体微服务。Go 单体模块化服务处理所有业务逻辑，mediasoup Node 服务独立管理媒体资源。模块间按微服务边界组织代码，预留后期拆分能力。

**技术栈：** Go (Gin + GORM + Wire) / Node.js + mediasoup / uniapp (Vue 3) / Vue 3 + Element Plus / PostgreSQL / Redis / Docker Compose

---

## 一、需求确认汇总

| 维度 | 决策 |
|------|------|
| 后端 | 纯 Go 语言（移除 PHP） |
| 前台前端 | uniapp (Vue 3) + mediasoup-client |
| 后台前端 | 独立 Vue 3 + Vite + Element Plus（PC 端专注） |
| 媒体层 | Node.js + mediasoup（保留官方 Node 中间层） |
| 存储 | PostgreSQL 17 + Redis 7 |
| 认证 | 邮箱+密码、用户名+密码（微信授权后期迭代） |
| MVP 功能 | 用户体系、即时聊天、好友管理、音视频会议、消息通知 |
| 多端优先级 | H5 > 桌面端 > 微信小程序 > Android |
| 并发规模 | 中型（单场50人，总用户千级） |
| 部署 | Docker Compose（初期），预留 K8s 编排 |
| 团队 | 个人 + AI 辅助开发 |

---

## 二、系统架构总览

```
                    ┌─────────────────────────────────────┐
                    │           负载均衡 (Nginx)            │
                    └──────┬──────────────┬───────────────┘
                           │              │
              ┌────────────┴───┐   ┌──────┴────────────┐
              │  前台用户端     │   │  后台管理端         │
              │  uniapp        │   │  Vue3+ElementPlus  │
              │  (H5/App/小程序)│   │  (PC Web)          │
              └────────┬───────┘   └──────┬─────────────┘
                       │                  │
          WebSocket + HTTPS          HTTPS (RESTful)
                       │                  │
              ┌────────┴──────────────────┴─────────────┐
              │         Go 单体服务（模块化）              │
              │                                          │
              │  ┌────────┐ ┌──────┐ ┌────────────────┐  │
              │  │ auth   │ │  im  │ │  meeting       │  │
              │  │ 用户鉴权│ │ 聊天 │ │  会议/信令     │  │
              │  ├────────┤ ├──────┤ ├────────────────┤  │
              │  │contact │ │notify│ │  admin         │  │
              │  │ 联系人  │ │ 通知 │ │  后台管理      │  │
              │  └────────┘ └──────┘ └────────────────┘  │
              │          │              │                  │
              │     PostgreSQL       Redis                │
              └──────────┼──────────────┼─────────────────┘
                         │         HTTP/gRPC
              ┌──────────┴──────────────┴─────────────────┐
              │         mediasoup Node 服务                 │
              │  ┌──────────┐  ┌──────────┐               │
              │  │ Worker 1 │  │ Worker N │  (C++ SFU)    │
              │  └──────────┘  └──────────┘               │
              └────────────────────────────────────────────┘
```

### 架构选型：精简单体 + 媒体微服务

- **方案一（已采用）：** Go 单体模块化服务 + mediasoup Node 独立服务
- **演进路径：** 单体 → 分组服务（实时/业务分离） → 完全微服务
- **微服务预留规则：**
  - 模块间零直接引用，通过 interface 通信
  - 每个模块有独立的路由注册
  - 数据库表按模块前缀命名
  - Redis key 按模块命名空间

### 用户体系设计

采用统一用户表 + RBAC 权限体系，认证入口分离：

- 前台用户和后台管理员共用 `auth_users` 表
- 通过 `auth_roles` 和 `auth_user_roles` 区分角色（user / admin / super_admin）
- `auth_roles.level` 字段实现角色等级层级管控（值越小权限越高：1=超管, 10=管理员, 100=用户）
- 管理操作强制执行"高等级管理低等级"规则（操作者 level 必须 < 目标用户 level）
- 前台 API 路由：`/api/v1/auth/*` — 仅需 JWT 验证
- 后台 API 路由：`/api/v1/admin/*` — JWT 验证 + 角色检查双重中间件

---

## 三、项目目录结构

```
EchoChat/
├── frontend/                          # 前台用户端 (uniapp)
│   ├── src/
│   │   ├── api/                       # API 请求封装
│   │   ├── components/                # 公共组件
│   │   ├── composables/               # 组合式函数
│   │   ├── pages/                     # 页面
│   │   │   ├── auth/                  # 登录/注册
│   │   │   ├── chat/                  # 聊天相关
│   │   │   ├── contact/               # 联系人
│   │   │   ├── meeting/               # 会议相关
│   │   │   ├── notification/          # 通知中心
│   │   │   └── profile/               # 个人中心
│   │   ├── store/                     # Pinia 状态管理
│   │   ├── utils/                     # 工具函数
│   │   ├── services/                  # 业务服务（WebSocket、mediasoup-client）
│   │   └── static/                    # 静态资源
│   └── package.json
│
├── admin/                             # 后台管理端 (Vue 3 + Element Plus)
│   ├── src/
│   │   ├── api/
│   │   ├── components/
│   │   ├── views/
│   │   │   ├── dashboard/             # 数据看板
│   │   │   ├── user/                  # 用户管理
│   │   │   ├── meeting/               # 会议监控
│   │   │   ├── permission/            # 权限管理
│   │   │   ├── system/                # 系统配置
│   │   │   └── layout/                # 布局框架
│   │   ├── router/
│   │   └── store/
│   └── package.json
│
├── backend/
│   └── go-service/                    # Go 单体服务
│       ├── cmd/
│       │   └── server/main.go         # 入口
│       ├── config/                    # 配置
│       ├── app/
│       │   ├── auth/                  # 用户鉴权模块
│       │   │   ├── controller/
│       │   │   │   ├── auth_controller.go
│       │   │   │   └── admin_auth_controller.go
│       │   │   ├── service/
│       │   │   ├── dao/
│       │   │   └── model/
│       │   ├── im/                    # 即时通讯模块
│       │   ├── contact/               # 联系人模块
│       │   ├── meeting/               # 会议模块
│       │   ├── notify/                # 通知模块
│       │   ├── admin/                 # 后台管理模块
│       │   ├── constants/             # 常量定义
│       │   └── dto/                   # 数据传输对象
│       ├── pkg/                       # 公共工具包
│       │   ├── db/                    # 数据库连接
│       │   ├── redis/                 # Redis 客户端
│       │   ├── ws/                    # WebSocket 管理
│       │   ├── middleware/            # 中间件
│       │   ├── utils/                 # 工具函数
│       │   └── logs/                  # 日志
│       ├── go.mod
│       └── go.sum
│
├── media-server/                      # mediasoup Node 服务
│   ├── src/
│   │   ├── worker-manager.js          # Worker 管理
│   │   ├── room.js                    # 房间媒体资源管理
│   │   ├── peer.js                    # 参与者媒体管理
│   │   └── api.js                     # 对外 HTTP API
│   ├── config/
│   └── package.json
│
├── deploy/                            # 部署配置
│   ├── docker/                        # Dockerfile 集合
│   ├── docker-compose.yml
│   ├── docker-compose.dev.yml
│   ├── nginx/
│   └── k8s/                           # 预留 K8s 配置
│
├── docs/                              # 文档
│   ├── plans/                         # 设计方案与实施计划
│   ├── api/                           # API 文档
│   └── architecture/                  # 架构文档
│
├── scripts/                           # 脚本
│   ├── init-db.sql
│   └── dev-setup.sh
│
└── README.md
```

---

## 四、数据库设计

### 4.1 PostgreSQL 核心表

> 所有表和字段均添加 `COMMENT` 注释，枚举类字段详细标注各值含义。

#### auth 模块 — 用户与权限

```sql
-- ============================================================
-- auth_users: 用户主表
-- 存储系统所有用户（包括普通用户和管理员），通过角色表区分权限
-- ============================================================
CREATE TABLE auth_users (
    id              BIGSERIAL PRIMARY KEY,
    username        VARCHAR(50)  UNIQUE NOT NULL,
    email           VARCHAR(100) UNIQUE NOT NULL,
    password_hash   VARCHAR(255) NOT NULL,
    nickname        VARCHAR(50)  NOT NULL DEFAULT '',
    avatar          VARCHAR(500) NOT NULL DEFAULT '',
    gender          SMALLINT     NOT NULL DEFAULT 0,
    phone           VARCHAR(20)  DEFAULT NULL,
    status          SMALLINT     NOT NULL DEFAULT 1,
    last_login_at   TIMESTAMP  DEFAULT NULL,
    last_login_ip   VARCHAR(50)  DEFAULT NULL,
    created_at      TIMESTAMP  NOT NULL DEFAULT NOW(),
    updated_at      TIMESTAMP  NOT NULL DEFAULT NOW()
);

COMMENT ON TABLE  auth_users                IS '用户主表，存储所有用户信息（普通用户与管理员共用）';
COMMENT ON COLUMN auth_users.id             IS '用户唯一标识，自增主键';
COMMENT ON COLUMN auth_users.username       IS '用户名，全局唯一，用于登录';
COMMENT ON COLUMN auth_users.email          IS '邮箱地址，全局唯一，用于登录和通知';
COMMENT ON COLUMN auth_users.password_hash  IS '密码哈希值，使用 bcrypt 加密存储';
COMMENT ON COLUMN auth_users.nickname       IS '用户昵称，用于前端显示';
COMMENT ON COLUMN auth_users.avatar         IS '头像 URL 地址';
COMMENT ON COLUMN auth_users.gender         IS '性别：0=未知，1=男，2=女';
COMMENT ON COLUMN auth_users.phone          IS '手机号码，可选';
COMMENT ON COLUMN auth_users.status         IS '账号状态：1=正常，2=禁用（管理员封禁），3=注销（用户主动注销）';
COMMENT ON COLUMN auth_users.last_login_at  IS '最后一次登录时间';
COMMENT ON COLUMN auth_users.last_login_ip  IS '最后一次登录 IP 地址';
COMMENT ON COLUMN auth_users.created_at     IS '账号创建时间';
COMMENT ON COLUMN auth_users.updated_at     IS '信息最后更新时间';

-- ============================================================
-- auth_roles: 角色表
-- 系统预置角色，用于 RBAC 权限控制
-- ============================================================
CREATE TABLE auth_roles (
    id          SERIAL PRIMARY KEY,
    code        VARCHAR(50) UNIQUE NOT NULL,
    name        VARCHAR(50) NOT NULL,
    level       INT NOT NULL DEFAULT 100,
    description VARCHAR(200) DEFAULT '',
    created_at  TIMESTAMP NOT NULL DEFAULT NOW()
);

COMMENT ON TABLE  auth_roles             IS '角色表，定义系统中所有角色类型';
COMMENT ON COLUMN auth_roles.id          IS '角色唯一标识，自增主键';
COMMENT ON COLUMN auth_roles.code        IS '角色代码，唯一标识：user=普通用户，admin=管理员，super_admin=超级管理员';
COMMENT ON COLUMN auth_roles.name        IS '角色显示名称，如"普通用户""管理员""超级管理员"';
COMMENT ON COLUMN auth_roles.level       IS '角色等级，值越小权限越高：1=超管, 10=管理员, 100=普通用户，预留间隔便于扩展';
COMMENT ON COLUMN auth_roles.description IS '角色描述说明';
COMMENT ON COLUMN auth_roles.created_at  IS '创建时间';

-- 预置角色数据（level 值越小权限越高）
INSERT INTO auth_roles (code, name, level, description) VALUES
    ('user',        '普通用户',   100, '系统普通用户，可以使用聊天、会议等功能'),
    ('admin',       '管理员',     10,  '后台管理员，可以管理用户、监控会议等'),
    ('super_admin', '超级管理员', 1,   '最高权限管理员，可以管理角色和系统配置');

-- ============================================================
-- auth_user_roles: 用户角色关联表
-- 多对多关系，一个用户可拥有多个角色
-- ============================================================
CREATE TABLE auth_user_roles (
    user_id    BIGINT NOT NULL REFERENCES auth_users(id),
    role_id    INT    NOT NULL REFERENCES auth_roles(id),
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    PRIMARY KEY (user_id, role_id)
);

#### 权限扩展规划（后期迭代）

> 当前已实现：3 角色 + level 等级层级管控（super_admin=1, admin=10, user=100），所有管理操作受层级约束。
> 后期管理员增多、需要区分职责时，引入细粒度权限点机制，扩展如下两张表（level 字段天然支持排序和层级融合）：

```sql
-- 预留：权限点表（后期扩展）
-- CREATE TABLE auth_permissions (
--     id          SERIAL PRIMARY KEY,
--     code        VARCHAR(100) UNIQUE NOT NULL,     -- 权限点代码，如 admin:user:list
--     name        VARCHAR(100) NOT NULL,            -- 权限点名称，如"查看用户列表"
--     module      VARCHAR(50)  NOT NULL,            -- 所属模块：user / meeting / system
--     description VARCHAR(200) DEFAULT '',
--     created_at  TIMESTAMP NOT NULL DEFAULT NOW()
-- );
--
-- 预留：角色权限关联表（后期扩展）
-- CREATE TABLE auth_role_permissions (
--     role_id       INT NOT NULL REFERENCES auth_roles(id),
--     permission_id INT NOT NULL REFERENCES auth_permissions(id),
--     PRIMARY KEY (role_id, permission_id)
-- );
```

扩展时的权限点示例：
- `admin:user:list` / `admin:user:detail` / `admin:user:disable` / `admin:user:role`
- `admin:meeting:list` / `admin:meeting:close` / `admin:meeting:stats`
- `admin:system:config` / `admin:log:list` / `admin:dashboard`

中间件从 `RequireRole("admin")` 扩展为 `RequirePermission("admin:user:list")`，前端管理端菜单根据权限点动态渲染。

COMMENT ON TABLE  auth_user_roles            IS '用户角色关联表，建立用户与角色的多对多关系';
COMMENT ON COLUMN auth_user_roles.user_id    IS '关联的用户 ID';
COMMENT ON COLUMN auth_user_roles.role_id    IS '关联的角色 ID';
COMMENT ON COLUMN auth_user_roles.created_at IS '角色分配时间';
```

#### contact 模块 — 联系人与好友

```sql
-- ============================================================
-- contact_friendships: 好友关系表
-- 双向存储：A→B 和 B→A 各一条记录，便于查询"我的好友列表"
-- ============================================================
CREATE TABLE contact_friendships (
    id          BIGSERIAL PRIMARY KEY,
    user_id     BIGINT   NOT NULL REFERENCES auth_users(id),
    friend_id   BIGINT   NOT NULL REFERENCES auth_users(id),
    remark      VARCHAR(50) DEFAULT '',
    group_id    BIGINT   DEFAULT NULL,
    status      SMALLINT NOT NULL DEFAULT 0,
    created_at  TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMP NOT NULL DEFAULT NOW(),
    UNIQUE (user_id, friend_id)
);

COMMENT ON TABLE  contact_friendships            IS '好友关系表，双向存储（A→B和B→A各一条记录）';
COMMENT ON COLUMN contact_friendships.id         IS '记录唯一标识';
COMMENT ON COLUMN contact_friendships.user_id    IS '发起方用户 ID';
COMMENT ON COLUMN contact_friendships.friend_id  IS '好友用户 ID';
COMMENT ON COLUMN contact_friendships.remark     IS '好友备注名，仅对当前用户可见';
COMMENT ON COLUMN contact_friendships.group_id   IS '所属好友分组 ID，关联 contact_groups 表';
COMMENT ON COLUMN contact_friendships.status     IS '好友关系状态：0=待确认（已发送申请），1=已接受（互为好友），2=已拒绝，3=已拉黑';
COMMENT ON COLUMN contact_friendships.created_at IS '记录创建时间（申请发送时间）';
COMMENT ON COLUMN contact_friendships.updated_at IS '最后更新时间（状态变更时间）';

-- ============================================================
-- contact_groups: 好友分组表
-- 每个用户可自定义好友分组
-- ============================================================
CREATE TABLE contact_groups (
    id         BIGSERIAL PRIMARY KEY,
    user_id    BIGINT      NOT NULL REFERENCES auth_users(id),
    name       VARCHAR(50) NOT NULL,
    sort_order INT         NOT NULL DEFAULT 0,
    created_at TIMESTAMP NOT NULL DEFAULT NOW()
);

COMMENT ON TABLE  contact_groups            IS '好友分组表，用户可自定义分组管理好友';
COMMENT ON COLUMN contact_groups.id         IS '分组唯一标识';
COMMENT ON COLUMN contact_groups.user_id    IS '所属用户 ID';
COMMENT ON COLUMN contact_groups.name       IS '分组名称，如"同事""家人""朋友"等';
COMMENT ON COLUMN contact_groups.sort_order IS '排序权重，数值越小越靠前';
COMMENT ON COLUMN contact_groups.created_at IS '创建时间';
```

#### im 模块 — 即时通讯（统一会话模型）✅ Phase 2b 已实现

单聊和群聊统一抽象为"会话"，本质都是"一组人在一个空间里收发消息"。这是微信、钉钉、Slack 等主流 IM 的标准模型。

> **Phase 2b 实际实现说明：** 当前已实现单聊功能，表结构已针对实际需求进行了优化（冗余 last_msg_* 字段提升查询效率，新增 clear_before_msg_id 实现个人视图清空，新增 client_msg_id 实现消息幂等）。群聊相关字段（name/avatar/owner_id/max_members/role/nickname/is_muted）保留在总体设计中，将在 Phase 2c 实现。

```sql
-- ============================================================
-- im_conversations: 会话表（Phase 2b 实际实现版本）
-- 统一抽象单聊和群聊，含冗余 last_msg_* 字段避免 JOIN 查询
-- ============================================================
CREATE TABLE im_conversations (
    id                 BIGSERIAL PRIMARY KEY,
    type               SMALLINT NOT NULL DEFAULT 1,
    creator_id         BIGINT NOT NULL,
    last_message_id    BIGINT DEFAULT NULL,
    last_msg_content   TEXT DEFAULT '',
    last_msg_time      TIMESTAMP WITH TIME ZONE,
    last_msg_sender_id BIGINT DEFAULT NULL,
    created_at         TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at         TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

COMMENT ON TABLE  im_conversations                    IS '会话表，统一管理单聊和群聊会话';
COMMENT ON COLUMN im_conversations.id                 IS '会话唯一标识';
COMMENT ON COLUMN im_conversations.type               IS '会话类型：1=单聊，2=群聊（Phase 2c）';
COMMENT ON COLUMN im_conversations.creator_id         IS '会话创建者用户 ID';
COMMENT ON COLUMN im_conversations.last_message_id    IS '最后一条消息 ID（冗余字段，避免 JOIN）';
COMMENT ON COLUMN im_conversations.last_msg_content   IS '最后消息预览文本（冗余字段）';
COMMENT ON COLUMN im_conversations.last_msg_time      IS '最后消息时间（冗余字段，用于排序）';
COMMENT ON COLUMN im_conversations.last_msg_sender_id IS '最后消息发送者 ID（冗余字段）';

CREATE INDEX idx_im_conversations_updated ON im_conversations(updated_at DESC);

-- ============================================================
-- im_conversation_members: 会话成员表（Phase 2b 实际实现版本）
-- 记录每个会话中的参与成员及其个人视图设置
-- ============================================================
CREATE TABLE im_conversation_members (
    id                  BIGSERIAL PRIMARY KEY,
    conversation_id     BIGINT NOT NULL REFERENCES im_conversations(id),
    user_id             BIGINT NOT NULL,
    is_pinned           BOOLEAN DEFAULT FALSE,
    is_deleted          BOOLEAN DEFAULT FALSE,
    unread_count        INT DEFAULT 0,
    last_read_msg_id    BIGINT DEFAULT 0,
    clear_before_msg_id BIGINT DEFAULT 0,
    created_at          TIMESTAMP(0) NOT NULL DEFAULT NOW(),
    updated_at          TIMESTAMP(0) NOT NULL DEFAULT NOW(),
    UNIQUE(conversation_id, user_id)
);

COMMENT ON TABLE  im_conversation_members                      IS '会话成员表，记录成员列表及个人视图设置';
COMMENT ON COLUMN im_conversation_members.id                   IS '记录唯一标识';
COMMENT ON COLUMN im_conversation_members.conversation_id      IS '所属会话 ID';
COMMENT ON COLUMN im_conversation_members.user_id              IS '成员用户 ID';
COMMENT ON COLUMN im_conversation_members.is_pinned            IS '是否置顶（个人设置）';
COMMENT ON COLUMN im_conversation_members.is_deleted           IS '是否软删除（个人视图，不影响对方）';
COMMENT ON COLUMN im_conversation_members.unread_count         IS '未读消息数';
COMMENT ON COLUMN im_conversation_members.last_read_msg_id     IS '最后已读消息 ID';
COMMENT ON COLUMN im_conversation_members.clear_before_msg_id  IS '清空记录截止消息 ID（个人视图，查询时过滤 id <= 此值的消息）';

CREATE INDEX idx_im_conv_members_user ON im_conversation_members(user_id, is_deleted);
CREATE INDEX idx_im_conv_members_conv ON im_conversation_members(conversation_id);

-- ============================================================
-- im_messages: 消息表（Phase 2b 实际实现版本）
-- 系统数据量最大的表，含 client_msg_id 实现幂等 + GIN 全文搜索索引
-- ============================================================
CREATE TABLE im_messages (
    id              BIGSERIAL PRIMARY KEY,
    conversation_id BIGINT NOT NULL REFERENCES im_conversations(id),
    sender_id       BIGINT NOT NULL,
    type            SMALLINT NOT NULL DEFAULT 1,
    content         TEXT NOT NULL,
    extra           JSONB DEFAULT NULL,
    status          SMALLINT NOT NULL DEFAULT 1,
    client_msg_id   VARCHAR(64) DEFAULT '',
    created_at      TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

COMMENT ON TABLE  im_messages                 IS '聊天消息表，存储所有会话的消息记录';
COMMENT ON COLUMN im_messages.id              IS '消息唯一标识，全局自增';
COMMENT ON COLUMN im_messages.conversation_id IS '所属会话 ID';
COMMENT ON COLUMN im_messages.sender_id       IS '发送者用户 ID';
COMMENT ON COLUMN im_messages.type            IS '消息类型：1=文本（预留 2=图片 3=语音 4=文件 5=系统通知）';
COMMENT ON COLUMN im_messages.content         IS '消息内容';
COMMENT ON COLUMN im_messages.extra           IS '附加数据（JSON），预留扩展';
COMMENT ON COLUMN im_messages.status          IS '消息状态：1=正常，2=已撤回，3=已删除';
COMMENT ON COLUMN im_messages.client_msg_id   IS '客户端消息唯一 ID，用于幂等去重';
COMMENT ON COLUMN im_messages.created_at      IS '消息发送时间';

CREATE INDEX idx_im_messages_conv_time ON im_messages(conversation_id, created_at DESC);
CREATE INDEX idx_im_messages_conv_id ON im_messages(conversation_id, id DESC);
CREATE INDEX idx_im_messages_content_search ON im_messages USING gin(to_tsvector('simple', content));
```

#### meeting 模块 — 音视频会议

> **📘 Phase 2e-2 实施修订说明**（2026-04-21）：以下为总设计首版 DDL，Phase 2e-2 实施阶段将做 3 处修订，详见 [`2026-04-21-phase2e-2-design.md` §5.1](./2026-04-21-phase2e-2-design.md)：
> - `password VARCHAR(50)` → `password_hash VARCHAR(255)`（bcrypt 哈希替换明文）
> - `meeting_rooms` 新增 `ended_reason VARCHAR(20)`、`meeting_participants` 新增 `left_reason VARCHAR(20)`
> - 新增独立表 `meeting_chats`（会议内聊天，24 小时后清理，不写入 `im_messages`）
> - MVP 阶段 `max_members` 应用层强制 8（schema 默认值保留 50 供后续扩展）

```sql
-- ============================================================
-- meeting_rooms: 会议房间表
-- 存储所有会议信息，支持即时会议和预约会议两种类型
-- ============================================================
CREATE TABLE meeting_rooms (
    id            BIGSERIAL PRIMARY KEY,
    room_code     VARCHAR(20) UNIQUE NOT NULL,
    title         VARCHAR(200) NOT NULL,
    host_id       BIGINT      NOT NULL REFERENCES auth_users(id),
    type          SMALLINT    NOT NULL DEFAULT 1,
    password      VARCHAR(50) DEFAULT NULL,
    max_members   INT         NOT NULL DEFAULT 50,
    status        SMALLINT    NOT NULL DEFAULT 0,
    scheduled_at  TIMESTAMP DEFAULT NULL,
    started_at    TIMESTAMP DEFAULT NULL,
    ended_at      TIMESTAMP DEFAULT NULL,
    settings      JSONB       DEFAULT '{}',
    created_at    TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at    TIMESTAMP NOT NULL DEFAULT NOW()
);

COMMENT ON TABLE  meeting_rooms               IS '会议房间表，存储所有会议的基本信息和状态';
COMMENT ON COLUMN meeting_rooms.id            IS '会议唯一标识，自增主键';
COMMENT ON COLUMN meeting_rooms.room_code     IS '会议号（用户可见），如"123-456-789"，用于分享和加入会议';
COMMENT ON COLUMN meeting_rooms.title         IS '会议标题';
COMMENT ON COLUMN meeting_rooms.host_id       IS '会议创建者/主持人用户 ID';
COMMENT ON COLUMN meeting_rooms.type          IS '会议类型：1=即时会议（立即创建立即开始），2=预约会议（设定未来时间）';
COMMENT ON COLUMN meeting_rooms.password      IS '会议密码，NULL 表示无密码，任何人可直接加入';
COMMENT ON COLUMN meeting_rooms.max_members   IS '最大参会人数，默认50人';
COMMENT ON COLUMN meeting_rooms.status        IS '会议状态：0=未开始（仅预约会议），1=进行中，2=已结束';
COMMENT ON COLUMN meeting_rooms.scheduled_at  IS '预约时间，仅预约会议有值，即时会议为 NULL';
COMMENT ON COLUMN meeting_rooms.started_at    IS '实际开始时间';
COMMENT ON COLUMN meeting_rooms.ended_at      IS '实际结束时间';
COMMENT ON COLUMN meeting_rooms.settings      IS '会议设置（JSON），如 {mute_on_join: true, allow_recording: false, auto_start: true}';
COMMENT ON COLUMN meeting_rooms.created_at    IS '会议创建时间';
COMMENT ON COLUMN meeting_rooms.updated_at    IS '信息最后更新时间';

-- ============================================================
-- meeting_participants: 会议参与者表
-- 记录每场会议的参与者及其参会信息
-- ============================================================
CREATE TABLE meeting_participants (
    id           BIGSERIAL PRIMARY KEY,
    room_id      BIGINT   NOT NULL REFERENCES meeting_rooms(id),
    user_id      BIGINT   NOT NULL REFERENCES auth_users(id),
    role         SMALLINT NOT NULL DEFAULT 0,
    joined_at    TIMESTAMP DEFAULT NULL,
    left_at      TIMESTAMP DEFAULT NULL,
    duration     INT      DEFAULT 0,
    UNIQUE (room_id, user_id)
);

COMMENT ON TABLE  meeting_participants           IS '会议参与者表，记录每场会议的所有参与者信息';
COMMENT ON COLUMN meeting_participants.id        IS '记录唯一标识';
COMMENT ON COLUMN meeting_participants.room_id   IS '所属会议 ID';
COMMENT ON COLUMN meeting_participants.user_id   IS '参与者用户 ID';
COMMENT ON COLUMN meeting_participants.role      IS '参会角色：0=普通参与者，1=主持人（会议创建者），2=联合主持人（主持人指定）';
COMMENT ON COLUMN meeting_participants.joined_at IS '加入会议的时间';
COMMENT ON COLUMN meeting_participants.left_at   IS '离开会议的时间，NULL 表示仍在会议中';
COMMENT ON COLUMN meeting_participants.duration  IS '累计参会时长（秒），离开时自动计算';
```

会议类型状态流转：
- **即时会议**（type=1）：创建 → 进行中(status=1) → 已结束(status=2)
- **预约会议**（type=2）：创建 → 未开始(status=0) → 进行中(status=1) → 已结束(status=2)

#### notify 模块 — 消息通知

```sql
-- ============================================================
-- notify_notifications: 通知表
-- 存储所有推送给用户的通知消息
-- ============================================================
CREATE TABLE notify_notifications (
    id          BIGSERIAL PRIMARY KEY,
    user_id     BIGINT      NOT NULL REFERENCES auth_users(id),
    type        VARCHAR(50) NOT NULL,
    title       VARCHAR(200) NOT NULL,
    content     TEXT        DEFAULT '',
    extra       JSONB       DEFAULT '{}',
    is_read     BOOLEAN     NOT NULL DEFAULT FALSE,
    created_at  TIMESTAMP NOT NULL DEFAULT NOW()
);

COMMENT ON TABLE  notify_notifications            IS '通知消息表，存储推送给用户的所有类型通知';
COMMENT ON COLUMN notify_notifications.id         IS '通知唯一标识';
COMMENT ON COLUMN notify_notifications.user_id    IS '接收通知的用户 ID';
COMMENT ON COLUMN notify_notifications.type       IS '通知类型：meeting_invite=会议邀请，friend_request=好友申请，friend_accepted=好友已接受，meeting_reminder=会议提醒，system=系统通知';
COMMENT ON COLUMN notify_notifications.title      IS '通知标题';
COMMENT ON COLUMN notify_notifications.content    IS '通知正文内容';
COMMENT ON COLUMN notify_notifications.extra      IS '附加数据（JSON），如会议邀请存 {room_code, room_title}，好友申请存 {from_user_id, from_username}';
COMMENT ON COLUMN notify_notifications.is_read    IS '是否已读：false=未读，true=已读';
COMMENT ON COLUMN notify_notifications.created_at IS '通知创建时间';

CREATE INDEX idx_notify_user_read ON notify_notifications(user_id, is_read, created_at DESC);
COMMENT ON INDEX idx_notify_user_read IS '用户未读通知索引，优化"获取未读通知列表"查询';
```

#### admin 模块 — 管理操作日志

```sql
-- ============================================================
-- admin_operation_logs: 管理操作日志表
-- 记录后台管理员的所有操作行为，用于审计和追踪
-- ============================================================
CREATE TABLE admin_operation_logs (
    id          BIGSERIAL PRIMARY KEY,
    admin_id    BIGINT      NOT NULL REFERENCES auth_users(id),
    module      VARCHAR(50) NOT NULL,
    action      VARCHAR(50) NOT NULL,
    target_type VARCHAR(50) DEFAULT '',
    target_id   BIGINT      DEFAULT NULL,
    detail      JSONB       DEFAULT '{}',
    ip          VARCHAR(50) DEFAULT '',
    created_at  TIMESTAMP NOT NULL DEFAULT NOW()
);

COMMENT ON TABLE  admin_operation_logs             IS '管理操作日志表，记录所有后台管理员的操作行为';
COMMENT ON COLUMN admin_operation_logs.id          IS '日志唯一标识';
COMMENT ON COLUMN admin_operation_logs.admin_id    IS '操作管理员的用户 ID';
COMMENT ON COLUMN admin_operation_logs.module      IS '操作所属模块：user=用户管理，meeting=会议管理，permission=权限管理，system=系统配置';
COMMENT ON COLUMN admin_operation_logs.action      IS '操作类型：create=创建，update=修改，delete=删除，disable=禁用，enable=启用，close=关闭';
COMMENT ON COLUMN admin_operation_logs.target_type IS '操作目标类型：user=用户，meeting=会议，role=角色，config=配置';
COMMENT ON COLUMN admin_operation_logs.target_id   IS '操作目标 ID，关联对应表的主键';
COMMENT ON COLUMN admin_operation_logs.detail      IS '操作详情（JSON），如 {before: {...}, after: {...}} 记录变更前后数据';
COMMENT ON COLUMN admin_operation_logs.ip          IS '操作者 IP 地址';
COMMENT ON COLUMN admin_operation_logs.created_at  IS '操作时间';
```

### 4.2 Redis 数据结构

```
# 用户认证（按 client_type 隔离前台和管理端）
echo:auth:token:{client_type}:{user_id}     → JWT Access Token (STRING, TTL 由配置决定)
echo:auth:refresh:{client_type}:{user_id}   → Refresh Token (STRING, TTL 由配置决定)
# client_type: frontend（前台用户端）/ admin（后台管理端）

# 用户在线状态
echo:user:online                    → 在线用户集合 (SET)
echo:user:status:{user_id}          → 用户状态 JSON (STRING, TTL 自动过期)

# 即时通讯（Phase 2b 实际实现）
echo:im:unread:{user_id}            → 全局未读消息总数 (STRING，Lua 脚本原子递减，下限 0)

# 会议实时状态
echo:meeting:room:{room_code}       → 房间实时状态 JSON (STRING)
echo:meeting:members:{room_code}    → 房间在线成员 (HASH: user_id → 状态JSON)
echo:meeting:transport:{room_code}  → Transport/Producer 映射 (HASH)

# WebSocket 连接映射
echo:ws:user:{user_id}              → 连接所在的服务实例ID (STRING)
echo:ws:conn:{conn_id}              → 连接信息 JSON (STRING)
```

---

## 五、通信协议与 API 设计

### 5.1 通信方式总览

| 通信路径 | 协议 | 用途 |
|---------|------|------|
| 客户端 ↔ Go 服务 | WebSocket | 实时消息、信令、状态同步 |
| 客户端 ↔ Go 服务 | HTTPS RESTful | 用户操作、数据查询、管理功能 |
| 管理端 ↔ Go 服务 | HTTPS RESTful | 后台管理 CRUD |
| Go 服务 ↔ mediasoup Node | HTTP | 媒体资源创建/管理 |
| 客户端 ↔ mediasoup Worker | WebRTC (DTLS/RTP) | 音视频媒体流直连 |

### 5.2 WebSocket 消息协议

统一 JSON 格式：

```json
{
    "event": "im.message.send",
    "seq": 1001,
    "data": {},
    "time": "2026-02-27 18:06:40"
}
```

事件命名规范：`{模块}.{对象}.{动作}`

```
# 即时通讯
im.message.send / im.message.new / im.message.revoke / im.message.read
im.typing.start / im.typing.stop

# 会议信令
meeting.room.join / meeting.room.leave / meeting.room.info
meeting.member.join / meeting.member.leave / meeting.member.mute / meeting.member.video

# mediasoup 信令
meeting.transport.create / meeting.transport.connect
meeting.produce.start / meeting.produce.stop
meeting.consume.start / meeting.consume.resume

# 用户状态
user.status.online / user.status.offline

# 通知
notify.new / notify.meeting.invite / notify.friend.request
```

服务端响应格式：

```json
{
    "event": "im.message.send.ack",
    "seq": 1001,
    "code": 0,
    "message": "ok",
    "data": { "msg_id": 10086 }
}
```

### 5.3 前台用户 RESTful API

```
# 认证模块
POST   /api/v1/auth/register
POST   /api/v1/auth/login
POST   /api/v1/auth/logout
POST   /api/v1/auth/refresh-token
GET    /api/v1/auth/profile
PUT    /api/v1/auth/profile
PUT    /api/v1/auth/password

# 联系人模块（Phase 2a 已实现）
GET    /api/v1/contacts                   好友列表（含在线状态）
POST   /api/v1/contacts/request           发送好友申请
POST   /api/v1/contacts/accept            接受申请
POST   /api/v1/contacts/reject            拒绝申请
DELETE /api/v1/contacts/:id               删除好友
PUT    /api/v1/contacts/:id/remark        设置备注
GET    /api/v1/contacts/requests          待处理申请列表

# 好友分组
GET    /api/v1/contacts/groups            分组列表
POST   /api/v1/contacts/groups            创建分组
PUT    /api/v1/contacts/groups/:id        修改分组
DELETE /api/v1/contacts/groups/:id        删除分组
PUT    /api/v1/contacts/:id/group         移动好友到分组

# 黑名单
POST   /api/v1/contacts/block             拉黑用户
DELETE /api/v1/contacts/block/:user_id    取消拉黑
GET    /api/v1/contacts/block             黑名单列表

# 搜索与推荐
GET    /api/v1/users/search               搜索用户
GET    /api/v1/contacts/recommend         好友推荐

# 在线状态
GET    /api/v1/contacts/online            批量查询好友在线状态

# 即时通讯模块（Phase 2b 已实现）
GET    /api/v1/im/conversations                  会话列表（含未读数、对方信息）
GET    /api/v1/im/messages                       历史消息（游标分页 before_id + limit）
PUT    /api/v1/im/conversations/:id/pin          置顶/取消置顶
DELETE /api/v1/im/conversations/:id              删除会话（软删除）
DELETE /api/v1/im/conversations/:id/messages     清空聊天记录（个人视图）
GET    /api/v1/im/messages/search                全局消息搜索（GIN 全文索引）
GET    /api/v1/im/unread                         全局未读消息总数

# 群聊管理模块（✅ Phase 2c 完成）
POST   /api/v1/groups                              创建群聊
GET    /api/v1/groups/:id                           群详情
PUT    /api/v1/groups/:id                           更新群信息
DELETE /api/v1/groups/:id                           解散群聊
GET    /api/v1/groups/:id/members                   成员列表
POST   /api/v1/groups/:id/members                   邀请入群
DELETE /api/v1/groups/:id/members/:uid              踢人
PUT    /api/v1/groups/:id/members/:uid/role         设置/取消管理员
PUT    /api/v1/groups/:id/members/:uid/mute         禁言/解除
POST   /api/v1/groups/:id/leave                     退出群聊
PUT    /api/v1/groups/:id/transfer                  转让群主
PUT    /api/v1/groups/:id/members/me/nickname       修改群昵称
POST   /api/v1/groups/:id/join-requests             申请入群
GET    /api/v1/groups/:id/join-requests             入群申请列表
PUT    /api/v1/groups/:id/join-requests/:rid        审批入群申请
GET    /api/v1/groups/search                        搜索公开群

# 已读回执（✅ Phase 2c 完成）
GET    /api/v1/im/messages/:id/reads               消息已读详情
GET    /api/v1/im/messages/:id/read-count          消息已读计数

# 文件上传（✅ Phase 2c 完成）
POST   /api/v1/upload                               通用文件上传（MinIO）

# 会议模块
POST   /api/v1/meetings
POST   /api/v1/meetings/schedule
GET    /api/v1/meetings/:code
POST   /api/v1/meetings/:code/join
POST   /api/v1/meetings/:code/leave
GET    /api/v1/meetings/upcoming
GET    /api/v1/meetings/ongoing
GET    /api/v1/meetings/history

# 通知模块
GET    /api/v1/notifications
PUT    /api/v1/notifications/:id/read
PUT    /api/v1/notifications/read-all
```

### 5.4 后台管理 RESTful API

```
# 管理员认证（Phase 1 已实现）
POST   /api/v1/admin/auth/login           管理员登录

# 用户管理（Phase 1 已实现）
GET    /api/v1/admin/users                用户列表（分页）
GET    /api/v1/admin/users/:id            用户详情
PUT    /api/v1/admin/users/:id/status     启用/禁用用户（受 level 层级约束）
PUT    /api/v1/admin/users/:id/roles      分配角色（多选，受 level 约束）
POST   /api/v1/admin/users               创建用户
GET    /api/v1/admin/roles                角色列表（受 level 过滤，仅显示可管理角色）
GET    /api/v1/admin/users/:id/meetings   用户的会议记录

# 在线监控（Phase 2a 已实现）
GET    /api/v1/admin/online/users         在线用户列表
GET    /api/v1/admin/online/count         在线用户数

# 好友关系管理（Phase 2a 已实现）
GET    /api/v1/admin/contacts             所有好友关系（分页）
DELETE /api/v1/admin/contacts/:id         管理员解除好友关系

# 群聊管理（✅ Phase 2c 完成）
GET    /api/v1/admin/groups               群列表（分页 + 筛选）
GET    /api/v1/admin/groups/:id           群详情（含成员列表）
DELETE /api/v1/admin/groups/:id           管理员解散群
DELETE /api/v1/admin/groups/:id/members/:uid  管理员移除成员

# 会议管理（后续阶段）
GET    /api/v1/admin/meetings
GET    /api/v1/admin/meetings/:id
PUT    /api/v1/admin/meetings/:id/close
GET    /api/v1/admin/meetings/stats

# 系统管理（后续阶段）
GET    /api/v1/admin/dashboard
GET    /api/v1/admin/logs
GET    /api/v1/admin/system/config
PUT    /api/v1/admin/system/config
```

### 5.5 Go ↔ mediasoup Node 通信 API

```
POST   /media/router/create
POST   /media/transport/create
POST   /media/transport/connect
POST   /media/producer/create
POST   /media/producer/close
POST   /media/consumer/create
POST   /media/consumer/resume
GET    /media/router/:id/capabilities
DELETE /media/room/:id
```

### 5.6 统一响应格式与错误码

```json
{
    "code": 0,
    "message": "ok",
    "data": {},
    "time": "2026-02-27 18:00:00"
}
```

| 错误码 | 含义 |
|--------|------|
| 0 | 成功 |
| 1001 | 参数错误 |
| 1002 | 未认证 |
| 1003 | 权限不足 |
| 2001 | 用户相关错误 |
| 3001 | IM 相关错误 |
| 4001 | 会议相关错误 |
| 5001 | 系统内部错误 |

---

## 六、前端页面结构

### 6.1 前台用户端页面（uniapp）

```
pages/
├── auth/
│   ├── login.vue                # 登录页
│   ├── register.vue             # 注册页
│   └── forgot-password.vue      # 忘记密码
├── chat/
│   ├── index.vue                # 会话列表                          ✅ Phase 2b
│   ├── conversation.vue         # 聊天对话页                        ✅ Phase 2b
│   ├── settings.vue             # 聊天设置页                        ✅ Phase 2b
│   ├── search.vue               # 消息搜索页                        ✅ Phase 2b
│   └── group-create.vue         # 创建群聊（含全站用户搜索）         ✅ Phase 2c
├── contact/
│   ├── index.vue                # 联系人列表（含搜索/在线状态）     ✅ Phase 2a
│   ├── search.vue               # 搜索添加好友 + 好友推荐          ✅ Phase 2a
│   ├── request.vue              # 好友申请列表（接受/拒绝）         ✅ Phase 2a
│   ├── detail.vue               # 好友详情（备注/分组/拉黑/删除）   ✅ Phase 2a
│   ├── groups.vue               # 好友分组管理（CRUD）             ✅ Phase 2a
│   └── blacklist.vue            # 黑名单管理                       ✅ Phase 2a
├── meeting/
│   ├── index.vue                # 会议首页（即将开始 + 进行中 + 快速入口）
│   ├── create.vue               # 创建即时会议
│   ├── schedule.vue             # 预约会议
│   ├── room.vue                 # 会议房间（音视频画面）
│   ├── history.vue              # 历史会议
│   └── detail.vue               # 会议详情
├── notification/
│   └── index.vue                # 通知中心
├── profile/
│   ├── index.vue                # 个人中心
│   └── edit.vue                 # 编辑资料
└── index/
    └── index.vue                # 启动页/首页
```

TabBar 导航（底部4标签）：消息 | 联系人 | 会议 | 我的

### 6.2 后台管理端页面（Vue 3 + Element Plus）

```
views/
├── login.vue                     # 管理员登录                    ✅ Phase 1
├── dashboard/index.vue           # 数据看板                      📋 后续
├── user/
│   ├── list.vue                  # 用户列表                      ✅ Phase 1
│   └── detail.vue                # 用户详情（含会议记录Tab）       📋 后续
├── monitor/
│   └── online.vue                # 在线用户监控                   ✅ Phase 2a
├── contact/
│   └── list.vue                  # 好友关系管理                   ✅ Phase 2a
├── meeting/
│   ├── list.vue                  # 会议列表                      📋 后续
│   ├── detail.vue                # 会议详情                      📋 后续
│   └── monitor.vue               # 实时监控                      📋 后续
├── permission/
│   ├── role.vue                  # 角色管理                      📋 后续
│   └── assign.vue                # 权限分配                      📋 后续
├── system/
│   ├── config.vue                # 系统配置                      📋 后续
│   └── logs.vue                  # 操作日志                      📋 后续
└── layout/
    ├── index.vue                 # 布局框架（含侧边栏导航）       ✅ Phase 1
```

---

## 七、关键技术方案

### 7.1 WebSocket 连接管理

- 心跳保活：30秒一次 ping/pong
- 断线自动重连：指数退避（1s → 2s → 4s → 8s → 最大30s）
- 消息队列缓冲：断线期间消息先缓存，重连后重发
- 多页面共享：通过 Pinia store 管理连接状态

### 7.2 mediasoup-client 集成

```
services/
├── websocket.js           # WebSocket 连接管理（单例）
├── mediasoup-client.js    # mediasoup Device 管理
└── media-utils.js         # 音视频工具
```

会议加入完整信令流程：
1. HTTP: POST /api/v1/meetings/:code/join → 验证权限
2. WS: meeting.room.join → Go 通知 Node 创建 Router
3. WS: meeting.transport.create → Go 转发 Node 创建 Transport
4. mediasoup-client: device.load(routerRtpCapabilities)
5. sendTransport.produce(videoTrack/audioTrack) → WS 信令
6. 服务端通知其他成员 → 其他成员 consume
7. 音视频流直连 mediasoup Worker

### 7.3 状态管理（Pinia）

```
store/
├── user.js              # 用户信息、Token、登录状态
├── chat.js              # 会话列表、当前会话、消息缓存
├── contact.js           # 好友列表、好友请求
├── meeting.js           # 会议状态、参与者、本地媒体
├── notification.js      # 通知列表、未读数
└── websocket.js         # WebSocket 连接状态、消息分发
```

### 7.4 Docker Compose 编排

```yaml
services:
  go-service:        # Go 后端    → 8080
  media-server:      # mediasoup  → 3000 + 40000-40100/udp (RTP)
  postgres:          # PostgreSQL → 5432
  redis:             # Redis      → 6379
  nginx:             # 反向代理    → 80/443
```

---

## 八、开发分期规划

### 第一期（MVP）

#### Phase 1：基础设施与用户认证 ✅ 已完成
- 用户注册/登录（邮箱+密码、用户名+密码）
- 有状态 JWT Token 管理（Redis 存储 + client_type 隔离）
- RBAC 角色权限（level 等级体系）
- 后台管理端基础框架 + 用户管理
- Docker Compose 开发环境

#### Phase 2a：WebSocket 实时通讯与联系人管理 ✅ 已完成
- WebSocket 长连接（心跳、断线重连、Redis Pub/Sub 消息总线）
- 联系人完整功能（好友申请/接受/拒绝/删除/分组/黑名单/搜索/推荐）
- 在线状态管理（混合推拉方案）
- 管理端扩展（在线监控 + 好友关系管理）

#### Phase 2b：即时通讯消息系统 ✅ 已完成
- 单聊即时通讯（WebSocket 全双工消息收发、三态 ACK 确认）
- 会话管理（自动创建、置顶、软删除、清空聊天记录 — 个人视图）
- 消息撤回（2 分钟内）、正在输入提示
- 离线消息推送（WS 连接后服务端主动推送未读摘要）
- 未读消息管理（会话 badge + TabBar 总未读数，Lua 脚本原子操作）
- 全局消息搜索（PostgreSQL GIN 全文索引）
- WS 事件路由表机制（Hub.RegisterEvent/DispatchEvent）
- 前台 4 个页面 + chat Store + API 封装
- 设计文档：`docs/plans/2026-03-03-phase2b-design.md`

#### Phase 2c：群聊与已读回执 ✅ 全部完成
- 群聊管理（三级角色：建群/加入/退出/解散/角色管理/禁言/全体禁言/群公告/入群审批）
- 群消息收发（复用 im.message.* + @某人/@所有人 + 管理员撤回无时限 + 系统消息）
- 已读回执（单聊会话级 last_read_msg_id + 群聊消息级 im_message_reads + 实时推送）
- MinIO 文件存储服务（Docker + 通用上传 API）
- 管理端群聊管理（群列表/详情/解散/移除成员）
- 前端 9 个新页面 + 群聊 Store + 会话列表 Tab 改造
- 创建群聊/邀请入群支持全站搜索非好友用户
- 新增 3 张数据库表（im_groups / im_group_join_requests / im_message_reads）
- 设计文档：`docs/plans/2026-03-04-phase2c-design.md`
- 实施计划：`docs/plans/2026-03-04-phase2c-implementation.plan.md`（14 个 Task + 10 项测试修复）

#### Phase 2d：消息类型扩展 ✅ 已完成
- 消息类型扩展（图片/语音/文件消息）
- 管理端消息管理功能
- 详见 `docs/plans/2026-03-04-phase2d-design.md`

#### Phase 2e：会议与通知 🚧 拆分为三子阶段（详见 `docs/plans/2026-04-20-phase2e-design.md`）

- **Phase 2e-1：通知系统** ✅ 已完成
  - 统一通知中心（好友/群聊事件 + 系统广播 + 会议通知类型预留）
  - 双通道（持久化入库 + WS `notify.new` 实时推送 + mini-toast）+ TabBar「我的」聚合未读红点
  - 入口：「我的」Tab 顶部铃铛 + 数字徽标；通知中心顶部 5 分类 Tab
  - 11 种通知类型枚举（10 种本期落地 + `meeting_invite`/`meeting_reminder` 2 种预留）；30 天自动清理
  - **单端 WS 架构**（不做多端已读同步，多端改造推迟到 Phase 2f）
  - 跨模块 Pusher 接口（contact/group 模块解耦集成）；后期接入 meeting 模块（Phase 2e-2）
  - 专用设计：`docs/plans/2026-04-20-phase2e-1-design.md`；实施计划：`docs/plans/2026-04-20-phase2e-1-implementation.plan.md`

- **Phase 2e-2：会议 MVP** 📋 设计阶段完成（2026-04-21），代码开发待启动
  - mediasoup Node.js 独立 `media-server/` 子项目 + mediasoup-client 前端集成
  - 即时会议（≤ 8 人）、会议号 `XXX-XXX-XXX`、可选密码（**bcrypt**）、音视频开关、主持人四件套（静音/移除/转让/结束）
  - **入会前设备预览页**（设备选择 + 本地画面 + 音量检测）
  - **三合一加入方式**：手输会议号 + 邀请链接 + 通知中心 `meeting_invite` 卡片
  - WebSocket 信令复用现有 Hub（11 个事件，3 组分类）
  - **双态部署**：本机 Docker Compose + 公网 `announcedIp` + coturn（`--profile public`）
  - **响应式**：桌面 3×3 / 平板 2×2 / 手机单列+抽屉
  - **会议内聊天**（独立 `meeting_chats` 表，会议结束 24 小时后清理）
  - 主持人掉线 2 分钟宽限 + 自动转让（最早加入者）+ 空房 5 分钟 TTL
  - 不含：录制、屏幕共享、预约、等候室、锁定会议
  - 专用设计：`docs/plans/2026-04-21-phase2e-2-design.md`（16 章节）；实施计划：`docs/plans/2026-04-21-phase2e-2-implementation.plan.md`（17 个 Task ≈ 17 人日）
  - 11 项关键决策（D01-D11）已锁定，见设计文档 §三

- **Phase 2e-3：会议增强** 📋 待开发
  - 预约会议（`meeting_rooms.type=2`）+ 定时提醒（`meeting_reminder`）
  - 等候室（waiting room）+ 锁定会议（lock）
  - 设备预览高级参数（降噪 / 回声消除 / 虚拟背景）
  - 注：原计划在 2e-3 的「会议邀请」与「入会前设备预览」已上移到 **Phase 2e-2**

#### Phase 2f：管理端扩展（MVP 收尾）📋 待规划

*（从 Phase 2e 剥离出的管理端功能，保证前台用户体验先行）*

- 会议管理（列表/详情/强制关闭/统计仪表板）
- 通知广播发布 UI
- 管理端仪表板总览、操作日志页、系统配置管理
- 用户会议记录查询

### 第二期
- 屏幕共享
- 微信授权登录
- 互动直播（主播开播、观众观看、弹幕互动）
- 会议录制与回放

### 第三期
- 微服务拆分
- Kubernetes 部署编排
- 跨服务器会议（多 Worker 集群）
- AI 辅助功能（语音转文字、会议纪要等）
