feat(phase2c): 群聊与已读回执功能完整实现
Phase 2c 全部 14 个 Task 完成,包含: 后端(Go): - MinIO 文件存储服务集成(Docker + Go SDK + 通用上传 API) - Group 模块完整实现(DAO + Service + Controller + Router + Wire) - 18 个群管理 REST API + 11 个 WS 群事件推送 - 群创建/解散/邀请/踢人/退出/转让群主/设管理员/禁言/全体禁言/群公告/群昵称/免打扰/搜索 - IM Service 扩展(群消息发送/撤回 + @提醒 + 管理员无时限撤回) - 已读回执后端(单聊会话级 last_read_msg_id + 群聊消息级 im_message_reads) - 管理端群组管理(列表/详情/解散) - 数据库迁移(3 张新表 + 2 张表字段扩展) 前端(uni-app): - 群聊 Store + API 封装 + 11 个 WS 事件监听 - 已读回执 UI(单聊已读/未读标记 + 群聊 X人已读 + 已读详情页) - 7 个群聊页面(对话/创建/设置/成员/邀请/审批/搜索) - 会话列表改造(全部/单聊/群聊 Tab + @标记 + 免打扰标识) 管理端(Vue 3 + Element Plus): - 群组列表页(搜索/分页/详情弹窗/解散群聊) - 侧边栏群组管理入口 文档同步:进度/架构/设计/API/规范文档全部更新 Made-with: Cursor
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
-- ============================================================
|
||||
-- EchoChat 数据库初始化脚本
|
||||
-- 包含模块:auth(用户认证)、contact(联系人)、im(即时通讯)
|
||||
-- 包含模块:auth(用户认证)、contact(联系人)、im(即时通讯)、group(群聊)
|
||||
-- ============================================================
|
||||
|
||||
-- ============================================================
|
||||
@@ -188,6 +188,12 @@ CREATE TABLE im_conversation_members (
|
||||
unread_count INT DEFAULT 0,
|
||||
last_read_msg_id BIGINT DEFAULT 0,
|
||||
clear_before_msg_id BIGINT DEFAULT 0,
|
||||
role SMALLINT NOT NULL DEFAULT 0,
|
||||
nickname VARCHAR(50) DEFAULT '',
|
||||
is_muted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
is_do_not_disturb BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
joined_at TIMESTAMP(0) DEFAULT NULL,
|
||||
at_me_count INT DEFAULT 0,
|
||||
created_at TIMESTAMP(0) NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP(0) NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (conversation_id, user_id)
|
||||
@@ -202,6 +208,12 @@ 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(个人视图,不影响对方)';
|
||||
COMMENT ON COLUMN im_conversation_members.role IS '成员角色:0=普通成员,1=管理员,2=群主';
|
||||
COMMENT ON COLUMN im_conversation_members.nickname IS '群内昵称(仅群聊有效)';
|
||||
COMMENT ON COLUMN im_conversation_members.is_muted IS '是否被禁言';
|
||||
COMMENT ON COLUMN im_conversation_members.is_do_not_disturb IS '是否消息免打扰(仍计未读但灰色显示,不推送通知)';
|
||||
COMMENT ON COLUMN im_conversation_members.joined_at IS '加入群聊时间';
|
||||
COMMENT ON COLUMN im_conversation_members.at_me_count IS '被@提醒未读计数,打开会话后清零';
|
||||
COMMENT ON COLUMN im_conversation_members.created_at IS '加入会话时间';
|
||||
COMMENT ON COLUMN im_conversation_members.updated_at IS '最后更新时间';
|
||||
|
||||
@@ -222,6 +234,7 @@ CREATE TABLE im_messages (
|
||||
extra JSONB DEFAULT NULL,
|
||||
status SMALLINT NOT NULL DEFAULT 1,
|
||||
client_msg_id VARCHAR(64) DEFAULT '',
|
||||
at_user_ids BIGINT[] DEFAULT NULL,
|
||||
created_at TIMESTAMP(0) NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
@@ -234,8 +247,87 @@ 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.at_user_ids IS '@提醒用户 ID 列表,NULL=无@,包含 0 表示 @所有人';
|
||||
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));
|
||||
|
||||
-- ============================================================
|
||||
-- im_groups: 群聊信息表
|
||||
-- 与 im_conversations (type=2) 一对一关联,存储群聊独有属性
|
||||
-- ============================================================
|
||||
CREATE TABLE im_groups (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
conversation_id BIGINT NOT NULL UNIQUE REFERENCES im_conversations(id),
|
||||
name VARCHAR(100) NOT NULL DEFAULT '',
|
||||
avatar VARCHAR(500) DEFAULT '',
|
||||
owner_id BIGINT NOT NULL,
|
||||
notice TEXT DEFAULT '',
|
||||
max_members INT NOT NULL DEFAULT 200,
|
||||
is_searchable BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
is_all_muted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
status SMALLINT NOT NULL DEFAULT 1,
|
||||
created_at TIMESTAMP(0) NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP(0) NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
COMMENT ON TABLE im_groups IS '群聊信息表';
|
||||
COMMENT ON COLUMN im_groups.id IS '群唯一标识';
|
||||
COMMENT ON COLUMN im_groups.conversation_id IS '关联 im_conversations.id';
|
||||
COMMENT ON COLUMN im_groups.name IS '群名称';
|
||||
COMMENT ON COLUMN im_groups.avatar IS '群头像 URL(MinIO)';
|
||||
COMMENT ON COLUMN im_groups.owner_id IS '群主用户 ID';
|
||||
COMMENT ON COLUMN im_groups.notice IS '群公告内容';
|
||||
COMMENT ON COLUMN im_groups.max_members IS '最大成员数,默认 200';
|
||||
COMMENT ON COLUMN im_groups.is_searchable IS '是否可被搜索发现';
|
||||
COMMENT ON COLUMN im_groups.is_all_muted IS '是否全体禁言';
|
||||
COMMENT ON COLUMN im_groups.status IS '群状态:1=正常,2=已解散';
|
||||
|
||||
CREATE INDEX idx_im_groups_owner ON im_groups(owner_id);
|
||||
CREATE INDEX idx_im_groups_name ON im_groups USING gin(to_tsvector('simple', name));
|
||||
|
||||
-- ============================================================
|
||||
-- im_group_join_requests: 入群申请表
|
||||
-- 记录用户主动申请加入群聊的审批流程
|
||||
-- ============================================================
|
||||
CREATE TABLE im_group_join_requests (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
group_id BIGINT NOT NULL REFERENCES im_groups(id),
|
||||
user_id BIGINT NOT NULL,
|
||||
message TEXT DEFAULT '',
|
||||
reviewer_id BIGINT DEFAULT NULL,
|
||||
status SMALLINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP(0) NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP(0) NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
COMMENT ON TABLE im_group_join_requests IS '入群申请表';
|
||||
COMMENT ON COLUMN im_group_join_requests.id IS '申请唯一标识';
|
||||
COMMENT ON COLUMN im_group_join_requests.group_id IS '目标群 ID';
|
||||
COMMENT ON COLUMN im_group_join_requests.user_id IS '申请人用户 ID';
|
||||
COMMENT ON COLUMN im_group_join_requests.message IS '申请附言';
|
||||
COMMENT ON COLUMN im_group_join_requests.reviewer_id IS '审批人用户 ID';
|
||||
COMMENT ON COLUMN im_group_join_requests.status IS '状态:0=待审批,1=通过,2=拒绝';
|
||||
|
||||
CREATE INDEX idx_group_join_req_group ON im_group_join_requests(group_id, status);
|
||||
CREATE INDEX idx_group_join_req_user ON im_group_join_requests(user_id);
|
||||
|
||||
-- ============================================================
|
||||
-- im_message_reads: 群聊消息已读记录表(消息级别)
|
||||
-- 复合主键 (message_id, user_id),存储每条群消息的已读用户
|
||||
-- ============================================================
|
||||
CREATE TABLE im_message_reads (
|
||||
message_id BIGINT NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
read_at TIMESTAMP(0) NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (message_id, user_id)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE im_message_reads IS '群聊消息已读记录表(消息级别)';
|
||||
COMMENT ON COLUMN im_message_reads.message_id IS '消息 ID';
|
||||
COMMENT ON COLUMN im_message_reads.user_id IS '已读用户 ID';
|
||||
COMMENT ON COLUMN im_message_reads.read_at IS '已读时间';
|
||||
|
||||
CREATE INDEX idx_msg_reads_user ON im_message_reads(user_id, read_at);
|
||||
|
||||
109
deploy/docker/postgres/phase2c_migration.sql
Normal file
109
deploy/docker/postgres/phase2c_migration.sql
Normal file
@@ -0,0 +1,109 @@
|
||||
-- ============================================================
|
||||
-- Phase 2c 数据库迁移:群聊 + 已读回执
|
||||
-- 执行环境:在已有 Phase 2b 数据库结构基础上增量升级
|
||||
-- ============================================================
|
||||
|
||||
-- ============================================================
|
||||
-- im_groups: 群聊信息表
|
||||
-- 与 im_conversations (type=2) 一对一关联,存储群聊独有属性
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS im_groups (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
conversation_id BIGINT NOT NULL UNIQUE REFERENCES im_conversations(id),
|
||||
name VARCHAR(100) NOT NULL DEFAULT '',
|
||||
avatar VARCHAR(500) DEFAULT '',
|
||||
owner_id BIGINT NOT NULL,
|
||||
notice TEXT DEFAULT '',
|
||||
max_members INT NOT NULL DEFAULT 200,
|
||||
is_searchable BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
is_all_muted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
status SMALLINT NOT NULL DEFAULT 1,
|
||||
created_at TIMESTAMP(0) NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP(0) NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
COMMENT ON TABLE im_groups IS '群聊信息表';
|
||||
COMMENT ON COLUMN im_groups.id IS '群唯一标识';
|
||||
COMMENT ON COLUMN im_groups.conversation_id IS '关联 im_conversations.id';
|
||||
COMMENT ON COLUMN im_groups.name IS '群名称';
|
||||
COMMENT ON COLUMN im_groups.avatar IS '群头像 URL(MinIO)';
|
||||
COMMENT ON COLUMN im_groups.owner_id IS '群主用户 ID';
|
||||
COMMENT ON COLUMN im_groups.notice IS '群公告内容';
|
||||
COMMENT ON COLUMN im_groups.max_members IS '最大成员数,默认 200';
|
||||
COMMENT ON COLUMN im_groups.is_searchable IS '是否可被搜索发现';
|
||||
COMMENT ON COLUMN im_groups.is_all_muted IS '是否全体禁言';
|
||||
COMMENT ON COLUMN im_groups.status IS '群状态:1=正常,2=已解散';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_im_groups_owner ON im_groups(owner_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_im_groups_name ON im_groups USING gin(to_tsvector('simple', name));
|
||||
|
||||
-- ============================================================
|
||||
-- im_group_join_requests: 入群申请表
|
||||
-- 记录用户主动申请加入群聊的审批流程
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS im_group_join_requests (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
group_id BIGINT NOT NULL REFERENCES im_groups(id),
|
||||
user_id BIGINT NOT NULL,
|
||||
message TEXT DEFAULT '',
|
||||
reviewer_id BIGINT DEFAULT NULL,
|
||||
status SMALLINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP(0) NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP(0) NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
COMMENT ON TABLE im_group_join_requests IS '入群申请表';
|
||||
COMMENT ON COLUMN im_group_join_requests.id IS '申请唯一标识';
|
||||
COMMENT ON COLUMN im_group_join_requests.group_id IS '目标群 ID';
|
||||
COMMENT ON COLUMN im_group_join_requests.user_id IS '申请人用户 ID';
|
||||
COMMENT ON COLUMN im_group_join_requests.message IS '申请附言';
|
||||
COMMENT ON COLUMN im_group_join_requests.reviewer_id IS '审批人用户 ID';
|
||||
COMMENT ON COLUMN im_group_join_requests.status IS '状态:0=待审批,1=通过,2=拒绝';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_group_join_req_group ON im_group_join_requests(group_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_group_join_req_user ON im_group_join_requests(user_id);
|
||||
|
||||
-- ============================================================
|
||||
-- im_message_reads: 群聊消息已读记录表(消息级别)
|
||||
-- 复合主键 (message_id, user_id),存储每条群消息的已读用户
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS im_message_reads (
|
||||
message_id BIGINT NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
read_at TIMESTAMP(0) NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (message_id, user_id)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE im_message_reads IS '群聊消息已读记录表(消息级别)';
|
||||
COMMENT ON COLUMN im_message_reads.message_id IS '消息 ID';
|
||||
COMMENT ON COLUMN im_message_reads.user_id IS '已读用户 ID';
|
||||
COMMENT ON COLUMN im_message_reads.read_at IS '已读时间';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_msg_reads_user ON im_message_reads(user_id, read_at);
|
||||
|
||||
-- ============================================================
|
||||
-- im_conversation_members: 新增群聊相关字段
|
||||
-- role/nickname/is_muted/is_do_not_disturb/joined_at/at_me_count
|
||||
-- ============================================================
|
||||
ALTER TABLE im_conversation_members
|
||||
ADD COLUMN IF NOT EXISTS role SMALLINT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS nickname VARCHAR(50) DEFAULT '',
|
||||
ADD COLUMN IF NOT EXISTS is_muted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
ADD COLUMN IF NOT EXISTS is_do_not_disturb BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
ADD COLUMN IF NOT EXISTS joined_at TIMESTAMP(0) DEFAULT NULL,
|
||||
ADD COLUMN IF NOT EXISTS at_me_count INT DEFAULT 0;
|
||||
|
||||
COMMENT ON COLUMN im_conversation_members.role IS '成员角色:0=普通成员,1=管理员,2=群主';
|
||||
COMMENT ON COLUMN im_conversation_members.nickname IS '群内昵称(仅群聊有效)';
|
||||
COMMENT ON COLUMN im_conversation_members.is_muted IS '是否被禁言';
|
||||
COMMENT ON COLUMN im_conversation_members.is_do_not_disturb IS '是否消息免打扰(仍计未读但灰色显示,不推送通知)';
|
||||
COMMENT ON COLUMN im_conversation_members.joined_at IS '加入群聊时间';
|
||||
COMMENT ON COLUMN im_conversation_members.at_me_count IS '被@提醒未读计数,打开会话后清零';
|
||||
|
||||
-- ============================================================
|
||||
-- im_messages: 新增 @提醒用户列表字段
|
||||
-- ============================================================
|
||||
ALTER TABLE im_messages
|
||||
ADD COLUMN IF NOT EXISTS at_user_ids BIGINT[] DEFAULT NULL;
|
||||
|
||||
COMMENT ON COLUMN im_messages.at_user_ids IS '@提醒用户 ID 列表,NULL=无@,包含 0 表示 @所有人';
|
||||
Reference in New Issue
Block a user