站内信,消息未读已读,数据表设计思路与方案,用golang如何实现?

1,298 阅读3分钟

在设计站内信系统时,需要考虑消息的存储、用户状态的管理(未读、已读)、消息推送、定时清理等。以下是站内信消息未读/已读数据表设计的思路与方案,以及如何用Golang实现的简要介绍。

数据表设计思路

  1. 消息表(messages): 存储所有站内信的内容。一个消息可能会发送给多个用户,因此我们把站内信的内容与接收者的状态分开管理。
  • id: 消息的唯一标识(主键)
  • sender_id: 发送者用户ID
  • title: 消息标题
  • content: 消息内容
  • create_time: 消息发送时间
  • type: 消息类型(系统通知、用户消息等)
CREATE TABLE messages (
   id BIGINT AUTO_INCREMENT PRIMARY KEY,
   sender_id BIGINT NOT NULL,
   title VARCHAR(255),
   content TEXT,
   create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   type INT
);
  1. 用户-消息关联表(user_message_status): 用于存储每个用户对消息的状态(未读、已读、删除等)。
  • id: 唯一标识(主键)
  • user_id: 接收消息的用户ID
  • message_id: 消息ID(关联messages表)
  • status: 消息状态(0:未读,1:已读,2:删除等)
  • read_time: 阅读时间(如果已读则记录阅读时间)
CREATE TABLE user_message_status (
   id BIGINT AUTO_INCREMENT PRIMARY KEY,
   user_id BIGINT NOT NULL,
   message_id BIGINT NOT NULL,
   status TINYINT DEFAULT 0, -- 0未读,1已读,2删除
   read_time TIMESTAMP NULL,
   CONSTRAINT fk_message FOREIGN KEY (message_id) REFERENCES messages(id),
   CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id)
);

站内信基本流程

  • 发送消息:将消息插入messages表,并根据接收用户列表在user_message_status中为每个用户创建一条记录,默认状态为未读。
  • 读取消息:用户读取消息时,更新user_message_status中的status为已读,并记录read_time
  • 查询未读消息:根据用户ID查询user_message_status表中status为未读的消息。
  • 删除消息:可将status更新为删除,或者直接从数据库中移除该条数据。

用Golang实现

使用Golang实现该方案可以结合数据库驱动(如GORMdatabase/sql等)和常用的Web框架(如GinEcho)。

1. 初始化数据库模型(使用GORM为例)

import (
    "time"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

// 消息结构体
type Message struct {
    ID        uint `gorm:"primaryKey"`
    SenderID  uint
    Title     string
    Content   string
    CreateTime time.Time `gorm:"autoCreateTime"`
    Type      int
}

// 用户-消息状态结构体
type UserMessageStatus struct {
    ID         uint `gorm:"primaryKey"`
    UserID     uint
    MessageID  uint
    Status     int       // 0未读,1已读,2删除
    ReadTime   time.Time
}

// 初始化数据库连接
func InitDB() (*gorm.DB, error) {
    dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        return nil, err
    }
    db.AutoMigrate(&Message{}, &UserMessageStatus{})
    return db, nil
}

2. 发送消息

func SendMessage(db *gorm.DB, senderID uint, title, content string, userIDs []uint) error {
    // 插入消息
    message := Message{
        SenderID: senderID,
        Title:    title,
        Content:  content,
        Type:     1, // 假设1是用户消息类型
    }
    if err := db.Create(&message).Error; err != nil {
        return err
    }
    
    // 为每个接收者创建未读记录
    for _, userID := range userIDs {
        status := UserMessageStatus{
            UserID:    userID,
            MessageID: message.ID,
            Status:    0, // 未读
        }
        db.Create(&status)
    }
    return nil
}

3. 阅读消息

func MarkMessageAsRead(db *gorm.DB, userID, messageID uint) error {
    // 更新为已读
    return db.Model(&UserMessageStatus{}).Where("user_id = ? AND message_id = ?", userID, messageID).
        Updates(map[string]interface{}{
            "status":   1, // 已读
            "read_time": time.Now(),
        }).Error
}

4. 获取未读消息

func GetUnreadMessages(db *gorm.DB, userID uint) ([]Message, error) {
    var messages []Message
    db.Joins("JOIN user_message_status ON messages.id = user_message_status.message_id").
        Where("user_message_status.user_id = ? AND user_message_status.status = 0", userID).
        Find(&messages)
    return messages, nil
}

总结

  • 通过数据库表的设计,消息和用户状态分离,保证了扩展性和可维护性。
  • 使用GORM等ORM工具可以方便地操作数据库,配合Golang中的业务逻辑实现站内信的功能,包括发送、读取、未读查询等。