最近筆者瀏覽網頁的時候發現站內消息提醒99+,一時不知所措。點完消息後就在想消息功能是怎麼實現的?html
站內信簡單點就是網站內的消息通知,在網站內部實現,不用郵件,短信等服務。不少時候咱們都在使用,好比系統推送的公告,用戶的私信,訂閱的更新等等不少mysql
根據站內信的發送範圍可將其分爲:sql
一對一:屬於私信,用戶與用戶之間互相發送私信,或者是系統對某一特定用戶推送的內容數據庫
一對多:屬於羣發,一用戶對多個用戶發送消息(垃圾廣告),或者系統對某特定的用戶羣體推送內容segmentfault
一對全體:屬於公告,是對全體用戶生效的,每一個用戶都能收到這個公告消息數據庫設計
根據站內信的內容可將其大體分爲(參考Bilibili模型):網站
回覆個人ui
@ 個人.net
收到的贊設計
系統通知
個人消息
其餘關注點:
消息的設置:是否開啓消息提醒、免擾時間、消息提醒的範圍
消息提醒的時限:消息也須要設置時限,否則幾年前發的公告,如今剛建立的用戶也會收到
用戶羣體:對某些特定的羣體發送消息,好比對常瀏覽科技區的用戶發送科技短訊
消息訂閱:對那些訂閱頻道的用戶推送更新提醒
說了那麼多,開始說重點了。筆者只實現最基本的一對一私信與一對全體的公告功能,以最簡潔的方式表達站內信的設計(主要是筆者沒有實現總體功能的實力)
將消息內容與閱讀記錄分開,這樣作的目的是避免公告中每一個用戶都須要一份消息內容而造成冗餘。兩個表分別爲t_message_content
內容表,t_message_record
記錄表
CREATE TABLE `t_message_content` ( `c_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '消息的id', `send_id` int(11) DEFAULT NULL COMMENT '消息發送者的id', `content` varchar(255) DEFAULT NULL COMMENT '消息的內容', `type` int(11) DEFAULT NULL COMMENT '消息的類型', `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '消息發送的時間', PRIMARY KEY (`c_id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
其中type消息類型分爲私信和公告,0是私信、1爲公告。發送時間默認爲當前時間
CREATE TABLE `t_message_record` ( `r_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '閱讀記錄的id', `rec_id` int(11) DEFAULT NULL COMMENT '消息接收者的id', `c_id` int(11) DEFAULT NULL COMMENT '對應消息的id', `status` int(11) DEFAULT '0' COMMENT '閱讀記錄的狀態', PRIMARY KEY (`r_id`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
status表示閱讀記錄的狀態,0表示未讀,1已讀,2刪除。爲何須要刪除? 以公告爲例:我的刪除公告的消息可將閱讀記錄標記爲刪除,這樣我的就不會顯示該公告了。但公告自己內容不能被我的刪除,刪除的話其他的人就沒法收到這條公告了。閱讀記錄默認爲未讀。
1、在內容表裏插入私信內容,並返回該內容的自增主鍵c_id = 5
INSERT INTO t_message_content (`send_id`,`content`,`type`) VALUES (7,"這是7發送私信給10",0)
2、往記錄表裏插入私信接收方未讀的記錄
INSERT INTO t_message_record (`rec_id`,`c_id`) VALUES (10,5)
1、用戶10 登陸時獲取所有私信消息
SELECT c.*,r.status FROM t_message_content c LEFT JOIN t_message_record r ON c.c_id = r.c_id WHERE r.rec_id = 10 AND c.type = 0 AND r.`status` != 2
c_id | send_id | content | type | create_time | status |
---|---|---|---|---|---|
5 | 7 | 7發送私信給10 | 0 | 2020-03-09 13:23:15 | 0 |
內容表左聯記錄表,外聯+where都是操做在臨時表上的,篩選出用戶10 未刪除的私信
2、用戶10 點擊閱讀時
UPDATE t_message_record SET status = 1 WHERE c_id = 5 AND rec_id = 10
將閱讀記錄的狀態修改成已讀
3、當用戶10 點擊刪除私信時
UPDATE t_message_record SET status = 2 WHERE c_id = 5 AND rec_id = 10
INSERT INTO `t_message_content` (`send_id`,`content`,`type`) VALUES (1,"這是公告1的內容",1) INSERT INTO `t_message_content` (`send_id`,`content`,`type`) VALUES (1,"這是公告2的內容",1)
沒錯就一步,不須要往記錄表插入記錄,由於公告是面對全體的。若插入記錄以用戶基數10萬人算,那數據庫不瞬間鎖表卡死,具體接收操做請看下面
1、用戶10 登錄時獲取所有公告消息
SELECT c.*,IFNULL(r.status,0) AS status FROM t_message_content c LEFT JOIN t_message_record r ON (c.c_id = r.c_id AND r.rec_id = 10) WHERE c.type = 1
這裏須要思考了:先查出所有公告,而後左聯記錄表,得出臨時表(記錄了所有公告和能匹配的閱讀記錄),沒有匹配則是null,而後用IFNULL設爲0表示未讀,此時閱讀表裏是沒有這條記錄的
2、公告1設爲已讀
INSERT INTO t_message_record (`rec_id`,`c_id`,`status`) VALUES (10,1,1)
3、刪除公告1
UPDATE t_message_record SET status = 2 WHERE rec_id = 10 AND c_id = 1
至此筆者理解的站內信就講完了
直接搬運,地址在文末給出
id: {type: 'integer', primaryKey: true, autoIncrement:true} //編號; dialogueID: {type: 'string', required: true} //對話編號; senderID: {type: 'string', required: true} //發送者編號; recipientID: {type: 'string', required: true} //接收者編號; messageID: {type: 'integer', required: true} //私信內容ID; createdAt:{type: 'timestamp', required: true} //發送時間; state: {type: 'integer', required: true} //狀態,已讀|未讀; readAt:{type: 'timestamp', required: true} //閱讀時間;
1、私信的通知
select * from notify_inbox where recipientID="uid" order by createdAt desc
2、私信對話框
select * from notify_inbox where dialogueID=「XXXX」 and (recipientID=「uid」 or senderID="uid") order by createdAt asc
私信回覆時,回覆的是dialogueID
1、私信的發送
select * from notify_inbox where senderID="uid" order by createdAt desc
2、私信對話框
select * from notify_inbox where dialogueID=「XXXX」 and (senderID=「uid」 or recipientID="uid") order by createdAt asc
id: {type: 'integer', primaryKey: true, autoIncrement:true} //編號; senderID: {type: 'string', required: true} //發送者編號; content: {type: 'string', required: true} //私信內容; createdAt:{type: 'timestamp', required: true}
參考:
https://www.cnblogs.com/grenet/archive/2010/03/08/1680655.html