|
@@ -0,0 +1,116 @@
|
|
|
|
|
+聊天消息持久化实现方案
|
|
|
|
|
+
|
|
|
|
|
+ 1. 数据库实体设计
|
|
|
|
|
+
|
|
|
|
|
+ 需要创建聊天消息实体,包含以下字段:
|
|
|
|
|
+ - id: 主键ID
|
|
|
|
|
+ - classId: 课堂ID(群组ID)
|
|
|
|
|
+ - type: 消息类型(text/image/system)
|
|
|
|
|
+ - content: 消息内容
|
|
|
|
|
+ - senderId: 发送者ID
|
|
|
|
|
+ - senderName: 发送者名称
|
|
|
|
|
+ - timestamp: 消息时间戳
|
|
|
|
|
+ - 操作人跟踪字段(createdBy/updatedBy)
|
|
|
|
|
+ - 时间戳字段(createdAt/updatedAt)
|
|
|
|
|
+
|
|
|
|
|
+ 2. 技术实现步骤
|
|
|
|
|
+
|
|
|
|
|
+ 2.1 创建消息实体和Schema
|
|
|
|
|
+
|
|
|
|
|
+ // src/server/modules/chat/chat-message.entity.ts
|
|
|
|
|
+ @Entity('chat_messages')
|
|
|
|
|
+ export class ChatMessage {
|
|
|
|
|
+ @PrimaryGeneratedColumn({ unsigned: true })
|
|
|
|
|
+ id!: number;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'class_id', type: 'varchar', length: 255, comment: '课堂ID' })
|
|
|
|
|
+ classId!: string;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'type', type: 'enum', enum: ['text', 'image', 'system'], comment: '消息类型' })
|
|
|
|
|
+ type!: 'text' | 'image' | 'system';
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'content', type: 'text', comment: '消息内容' })
|
|
|
|
|
+ content!: string;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'sender_id', type: 'varchar', length: 255, nullable: true, comment: '发送者ID' })
|
|
|
|
|
+ senderId!: string | null;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'sender_name', type: 'varchar', length: 255, nullable: true, comment: '发送者名称' })
|
|
|
|
|
+ senderName!: string | null;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'timestamp', type: 'bigint', comment: '消息时间戳' })
|
|
|
|
|
+ timestamp!: number;
|
|
|
|
|
+
|
|
|
|
|
+ // 操作人跟踪字段
|
|
|
|
|
+ @Column({ name: 'created_by', type: 'int', nullable: true, comment: '创建用户ID' })
|
|
|
|
|
+ createdBy!: number | null;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'updated_by', type: 'int', nullable: true, comment: '更新用户ID' })
|
|
|
|
|
+ updatedBy!: number | null;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'created_at', type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
|
|
|
|
|
+ createdAt!: Date;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'updated_at', type: 'timestamp', default: () => 'CURRENT_TIMESTAMP', onUpdate: 'CURRENT_TIMESTAMP' })
|
|
|
|
|
+ updatedAt!: Date;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 2.2 创建消息服务
|
|
|
|
|
+
|
|
|
|
|
+ // src/server/modules/chat/chat-message.service.ts
|
|
|
|
|
+ export class ChatMessageService extends GenericCrudService<ChatMessage> {
|
|
|
|
|
+ constructor(dataSource: DataSource) {
|
|
|
|
|
+ super(dataSource, ChatMessage);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async findByClassId(classId: string, page: number = 1, pageSize: number = 50): Promise<[ChatMessage[], number]> {
|
|
|
|
|
+ return this.getList(page, pageSize, undefined, undefined, { classId } as any, [], { timestamp: 'DESC' });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 2.3 创建API路由
|
|
|
|
|
+
|
|
|
|
|
+ 使用通用CRUD路由快速生成消息管理接口,并添加历史消息查询接口。
|
|
|
|
|
+
|
|
|
|
|
+ 2.4 修改消息接收逻辑
|
|
|
|
|
+
|
|
|
|
|
+ 在 useClassroom.ts 的 showMessage、showImageMessage、showSystemMessage 方法中添加数据库存储逻辑。
|
|
|
|
|
+
|
|
|
|
|
+ 2.5 添加历史消息加载
|
|
|
|
|
+
|
|
|
|
|
+ 在用户加入课堂时,调用API加载该课堂的历史消息。
|
|
|
|
|
+
|
|
|
|
|
+ 3. 关键修改点
|
|
|
|
|
+
|
|
|
|
|
+ 3.1 消息存储中间件
|
|
|
|
|
+
|
|
|
|
|
+ 在收到IM消息时,除了显示到界面,还要保存到数据库:
|
|
|
|
|
+ // 在 recvgroupmessage 事件处理中添加
|
|
|
|
|
+ const savedMessage = await chatMessageService.create({
|
|
|
|
|
+ classId: groupId,
|
|
|
|
|
+ type: 'text',
|
|
|
|
|
+ content: msg.data,
|
|
|
|
|
+ senderId: sender?.userId,
|
|
|
|
|
+ senderName: senderName,
|
|
|
|
|
+ timestamp: msg.timestamp || Date.now()
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ 3.2 历史消息加载
|
|
|
|
|
+
|
|
|
|
|
+ 在用户加入课堂成功后,加载历史消息:
|
|
|
|
|
+ // 在 joinClass 方法中添加
|
|
|
|
|
+ const historyResponse = await chatClient.history.$get({
|
|
|
|
|
+ query: { classId, page: 1, pageSize: 50 }
|
|
|
|
|
+ });
|
|
|
|
|
+ if (historyResponse.status === 200) {
|
|
|
|
|
+ const historyData = await historyResponse.json();
|
|
|
|
|
+ setMessageList(historyData.data.reverse()); // 按时间顺序显示
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 4. 性能考虑
|
|
|
|
|
+
|
|
|
|
|
+ - 使用分页加载历史消息,避免一次性加载过多
|
|
|
|
|
+ - 为 classId 和 timestamp 字段添加数据库索引
|
|
|
|
|
+ - 考虑消息归档机制,定期清理旧消息
|
|
|
|
|
+
|
|
|
|
|
+ 这个方案能够实现聊天消息的持久化存储,并支持用户进入课堂时查看历史消息。
|