聊天消息持久化实现方案

  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 字段添加数据库索引
  - 考虑消息归档机制，定期清理旧消息

  这个方案能够实现聊天消息的持久化存储，并支持用户进入课堂时查看历史消息。