1. 程式人生 > >站內信設計

站內信設計

****
> 最近筆者瀏覽網頁的時候發現站內訊息提醒99+,一時不知所措。點完訊息後就在想訊息功能是怎麼實現的?
## 1. 站內信 站內信簡單點就是網站內的訊息通知,在網站內部實現,不用郵件,簡訊等服務。很多時候我們都在使用,比如系統推送的公告,使用者的私信,訂閱的更新等等很多
根據站內信的傳送範圍可將其分為: **一對一:**屬於私信,使用者與使用者之間互相傳送私信,或者是系統對某一特定使用者推送的內容 **一對多:**屬於群發,一使用者對多個使用者傳送訊息(垃圾廣告),或者系統對某特定的使用者群體推送內容 **一對全體:**屬於公告,是對全體使用者生效的,每個使用者都能收到這個公告訊息
根據站內信的內容可將其大致分為(參考Bilibili模型): **回覆我的** **@ 我的** **收到的贊** **系統通知** **我的訊息**
其他關注點: **訊息的設定:**是否開啟訊息提醒、免擾時間、訊息提醒的範圍 **訊息提醒的時限:**訊息也需要設定時限,不然幾年前發的公告,現在剛建立的使用者也會收到 **使用者群體:**對某些特定的群體傳送訊息,比如對常瀏覽科技區的使用者傳送科技短訊 **訊息訂閱:**對那些訂閱頻道的使用者推送更新提醒
說了那麼多,開始說重點了。筆者只實現最基本的一對一私信與一對全體的公告功能,以最簡潔的方式表達站內信的設計(主要是筆者沒有實現整體功能的實力)



## 2. 資料庫設計 將訊息內容與閱讀記錄分開,這樣做的目的是避免公告中每個使用者都需要一份訊息內容而形成冗餘。兩個表分別為`t_message_content`內容表,`t_message_record`記錄表
### t_message_content ```mysql 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為公告。傳送時間預設為當前時間
### t_message_record ```mysql 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刪除。為什麼需要刪除? 以公告為例:個人刪除公告的訊息可將閱讀記錄標記為刪除,這樣個人就不會顯示該公告了。但公告本身內容不能被個人刪除,刪除的話其餘的人就無法收到這條公告了。閱讀記錄預設為未讀。



## 3. 私信的操作步驟
### 3.1 使用者7 傳送私信給 使用者10 一、在內容表裡插入私信內容,並返回該內容的自增主鍵c_id = 5 ```mysql INSERT INTO t_message_content (`send_id`,`content`,`type`) VALUES (7,"這是7傳送私信給10",0) ```
二、往記錄表裡插入私信接收方未讀的記錄 ```mysql INSERT INTO t_message_record (`rec_id`,`c_id`) VALUES (10,5) ```
### 3.2 私信接收方接收訊息 一、使用者10 登入時獲取全部私信訊息 ```mysql 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 未刪除的私信
二、使用者10 點選閱讀時 ```mysql UPDATE t_message_record SET status = 1 WHERE c_id = 5 AND rec_id = 10 ``` 將閱讀記錄的狀態修改為已讀
三、當用戶10 點選刪除私信時 ```mysql UPDATE t_message_record SET status = 2 WHERE c_id = 5 AND rec_id = 10 ```



## 4. 公告的操作步驟
### 4.1 後臺使用者1 釋出公告 ```mysql 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萬人算,那資料庫不瞬間鎖表卡死,具體接收操作請看下面
### 4.2 使用者10 接收公告 一、使用者10 登陸時獲取全部公告訊息 ```mysql 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表示未讀,此時閱讀表裡是沒有這條記錄的
二、公告1設為已讀 ```mysql INSERT INTO t_message_record (`rec_id`,`c_id`,`status`) VALUES (10,1,1) ```
三、刪除公告1 ```mysql UPDATE t_message_record SET status = 2 WHERE rec_id = 10 AND c_id = 1 ```



#### 至此筆者理解的站內信就講完了

------ 參考:
<