站內信消息未讀 - 已讀數據表設計 如何用 Golang 實現
今天我們來聊一個經常在後臺開發中遇到的話題——站內信消息的未讀 / 已讀狀態數據表設計,以及如何用 Golang 來實現這個功能。
對於很多做過後臺開發的程序員來說,站內信系統幾乎是每個系統都會涉及到的功能之一。無論是用戶通知、系統提醒,還是訂單更新,站內信的使用都非常廣泛。
不過,在實際的開發過程中,關於站內信消息的存儲、管理以及查詢等,往往會讓我們花上一些時間去設計和優化。特別是當我們需要處理未讀和已讀這兩個狀態時,設計一張合理的數據庫表結構和高效的業務邏輯就顯得尤爲重要了。
今天就來跟大家分享一下,如何通過 Golang 來實現一個高效的站內信消息管理系統,確保每個用戶都能方便地查看和管理自己的消息。
一、站內信系統設計思路
首先,我們需要明確站內信系統的核心需求:每個用戶都能接收不同發送者的消息,消息有未讀和已讀的狀態,甚至可能有刪除的需求。那麼我們要如何設計數據表,才能滿足這些需求呢?
1.1 消息表(messages)
消息表用來存儲每一條站內信的基本內容。一般來說,消息表會包含以下幾個重要字段:
-
id:消息 ID(主鍵)。 -
sender_id:發送者的用戶 ID。 -
receiver_id:接收者的用戶 ID(有些系統可能支持多人接收)。 -
title:消息標題。 -
content:消息內容。 -
type:消息類型(如通知、提醒等)。 -
created_at:消息的創建時間。
消息表的設計可以簡單明瞭地存儲每一條消息的核心內容,這樣我們就能對消息的內容進行管理,比如刪除、修改等操作。
1.2 用戶 - 消息關聯表(user_message_status)
爲了追蹤每個用戶對每條消息的閱讀狀態,我們需要設計一個關聯表,存儲每個用戶與消息的關係。常見的字段包括:
-
id:記錄 ID(主鍵)。 -
user_id:用戶 ID,表示這條記錄屬於哪個用戶。 -
message_id:消息 ID,表示用戶與哪條消息有關聯。 -
status:消息的狀態(未讀、已讀、刪除等)。 -
read_at:如果是已讀狀態,記錄用戶讀取消息的時間。 -
deleted_at:如果消息已刪除,記錄刪除時間。
通過這個關聯表,我們可以清晰地知道每個用戶對每條消息的處理狀態。對於查詢未讀消息和已讀消息的操作非常高效。
二、站內信基本流程
接下來,我們來看看站內信系統的基本流程是怎樣的:
2.1 發送消息
當一個用戶發送消息時,我們會向消息表中插入一條新的記錄,表示這條消息已經被創建了。同時,我們還需要在用戶 - 消息關聯表中爲每個接收者創建記錄,默認狀態爲未讀。代碼實現大概如下:
// 消息結構體
type Message struct {
ID uint `gorm:"primaryKey"`
SenderID uint `gorm:"not null"`
ReceiverID uint `gorm:"not null"`
Title string `gorm:"not null"`
Content string `gorm:"not null"`
Type string
CreatedAt time.Time
}
// 用戶-消息關聯表結構體
type UserMessageStatus struct {
ID uint `gorm:"primaryKey"`
UserID uint `gorm:"not null"`
MessageID uint `gorm:"not null"`
Status string `gorm:"default:'unread'"`
ReadAt *time.Time
DeletedAt *time.Time
}
func sendMessage(db *gorm.DB, senderID, receiverID uint, title, content string) error {
message := Message{
SenderID: senderID,
ReceiverID: receiverID,
Title: title,
Content: content,
Type: "notification", // 假設這是一個通知類型的消息
CreatedAt: time.Now(),
}
if err := db.Create(&message).Error; err != nil {
return err
}
// 爲每個接收者創建用戶消息關聯記錄,默認未讀
userMessageStatus := UserMessageStatus{
UserID: receiverID,
MessageID: message.ID,
Status: "unread",
}
if err := db.Create(&userMessageStatus).Error; err != nil {
return err
}
return nil
}
2.2 讀取消息
當用戶查看一條消息時,我們需要更新用戶 - 消息關聯表中的狀態爲已讀,並記錄閱讀時間。代碼大致如下:
func markMessageAsRead(db *gorm.DB, userID, messageID uint) error {
var userMessageStatus UserMessageStatus
if err := db.Where("user_id = ? AND message_id = ?", userID, messageID).First(&userMessageStatus).Error; err != nil {
return err
}
userMessageStatus.Status = "read"
userMessageStatus.ReadAt = time.Now()
if err := db.Save(&userMessageStatus).Error; err != nil {
return err
}
return nil
}
2.3 查詢未讀消息
我們常常需要查詢用戶的未讀消息。這個操作通過查詢用戶 - 消息關聯表中狀態爲 “未讀” 的記錄來實現:
func getUnreadMessages(db *gorm.DB, userID uint) ([]Message, error) {
var messages []Message
err := db.Joins("JOIN user_message_status ums ON ums.message_id = messages.id").
Where("ums.user_id = ? AND ums.status = ?", userID, "unread").
Find(&messages).Error
return messages, err
}
2.4 刪除消息
刪除消息其實可以有兩種處理方式:
-
物理刪除:直接從表中刪除消息記錄。
-
邏輯刪除:更新記錄狀態爲 “已刪除”,這樣用戶還是能查看到自己刪除的消息,只是狀態變成“已刪除” 而已。
如果是邏輯刪除,代碼可能是這樣:
func deleteMessage(db *gorm.DB, userID, messageID uint) error {
var userMessageStatus UserMessageStatus
if err := db.Where("user_id = ? AND message_id = ?", userID, messageID).First(&userMessageStatus).Error; err != nil {
return err
}
userMessageStatus.Status = "deleted"
userMessageStatus.DeletedAt = time.Now()
if err := db.Save(&userMessageStatus).Error; err != nil {
return err
}
return nil
}
通過設計合理的數據庫結構,我們能清晰地分離消息內容和用戶狀態,提高系統的可維護性和擴展性。藉助 Golang 強大的併發能力和 ORM 框架 GORM 的支持,我們可以輕鬆實現消息發送、閱讀、查詢等業務邏輯。
這套方案適用於大多數站內信系統,如果需要支持更多複雜功能(例如消息分組、提醒通知、消息推送等),我們也可以在此基礎上進行擴展。
每當用戶發送一條消息,系統會自動爲每個接收者生成記錄,確保消息能夠順利送達並被處理。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/IH46SmxSUGKObuel5CJU1g