php 站内信,消息未读已读,数据表设计思路与方案值得揣摩

273 阅读6分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情

站内信,消息已读未读,这种功能我们经常见到,但是能认真思考一下这种功能是如何设计的应该不多吧。网上找了很多教程查看,觉得还是太过于老套了。
这里有一篇值得推荐的,设计思路与方案值得学习一下。主要是有思路,再到数据库的设计都有!这样写代码也是清晰明了了。

站内信通常需要解决两个需求:

1 用户对用户的站内信,管理员对用户的站内信:即一对一发送
2 管理员对多用户、用户组、全站的站内信:即一对多发送(还有一种是用户对产品的站内信,例如对某个模块的反馈、疑问之类的)

我们目前的需求是:

1 管理员对多用户发送站内信要求:对用户真实性不做校验,对标题长度、内容长度进行限制(分别是45个字节、150个字节,对应中文字符15个、50个),对收件人的拼音长度进行限制(最长50个字节)。
2 用户可以查看自己的站内信按“全部、已读、未读”过滤按消息来源分类:工单平台、资源管理、自动装机、漏洞平台、故障平台
3 用户可以删除、批量删除站内信
4 用户可以已阅、批量已阅、全部标记为已读 站内信
5 运维平台页面顶部的消息图标展示未读消息数,超过99显示 99+ ;鼠标放上去,会有下拉框,展示最近10条未读消息(展示“时间”,“消息来源”,“标题”);

下拉框的底部有两个按钮:“更多”,加载更多未读消息;“查看全部”,跳转到站内信列表页面(最好另开一个窗口); 点击下拉框里的未读消息,通过弹出框展示详情;然后在未读列表里删除该记录,在数据库里标记为已读,消息图标的未读消息数量减一。

系统设计与流程

  • 功能设计
  • 系统流程
  • 发送站内信
  • 读取POST请求的request body
  • 校验长度
  • 插入数据库
  • 返回

获取站内信列表调用子模块,插入发送给全站或我所属用户组的站内信;根据查询条件,返回数据库数据.
获取未读站内信数量调用子模块,插入发送给全站或我所属用户组的站内信; 返回数量 批量已阅检查messageId是不是属于当前用户;
inbox_message表里把 read 置为1,修改update_time 全部已阅

update inbox_message set “read”=1, 
“update_time”=now where 
“receiver_name”=currentUser() 
and “read” = 0

批量删除
检查messageId是不是属于当前用户inbox_message表里把 deleted 置为1,修改update_time

全部删除

update inbox_message set “deleted”=1, “update_time”=now where “receiver_name”=currentUser() and “deleted” = 0

数据库设计
**站内信内容表 **

CREATE TABLE `inbox_message_text` (
  `id` bigint(20NOT NULL AUTO_INCREMENT,
  `title` varchar(128NOT NULL DEFAULT '',
  `content` longtext NOT NULL,
  `create_time` datetime NOT NULL,
  `update_time` datetime NOT NULL,
  `send_type` tinyint(4NOT NULL DEFAULT '0',
  `creator_name` varchar(255NOT NULL DEFAULT '',
  `deleted` tinyint(4NOT NULL DEFAULT '0',
  `module_id` bigint(20NOT NULL,
  `link` varchar(255NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

站内信本身除了消息来源(module_name),还有一个纬度的描述,叫消息类型(message_type),例如安全消息、活动消息、服务消息等,每一大类里,又可以划分子类,例如活动消息-优惠活动。消息来源和消息类型可以是正交关系,即工单平台也可以有活动消息;消息来源也可以是消息类型的一种,称为“产品消息”。
站内信发送表

CREATE TABLE `inbox_message` (
  `id` bigint(20NOT NULL AUTO_INCREMENT,
  `message_text_id` bigint(20NOT NULL,
  `receiver_name` varchar(255NOT NULL DEFAULT '',
  `read` tinyint(4NOT NULL DEFAULT '0',
  `deleted` tinyint(4NOT NULL DEFAULT '0',
  `create_time` datetime NOT NULL,
  `update_time` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `inbox_message_receiver_name_deleted_read_id` (`receiver_name`,`deleted`,`read`,`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

消息来源表

CREATE TABLE `inbox_module` (
  `id` bigint(20NOT NULL AUTO_INCREMENT,
  `code` varchar(128NOT NULL DEFAULT '',
  `name` varchar(128NOT NULL DEFAULT '',
  `create_time` datetime NOT NULL,
  `update_time` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `code` (`code`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

**API设计 **
发送站内信:POST /v1/message

request body Content-Type: application/json
{
"title": "工单审批",
"content""XXX提交了变更申请,请审批",
"to""sunzhongyuan,shenli,wangya",
"module_name""工单平台",
"link""xxx"

}
response
{
"code": 200,
"data"32,
"msg""OK"
}

获取站内信列表:GET /v1/message User-Id: xxx

http://127.0.0.1:10085/v1/message?query=message_text_id.module_id.name:xxx&limit=1
{
"code": 200,
"data": {
"data": [
{
"id": 1,
"message_text": {
"id": 1,
"title": "title 2",
"content": "content 2",
"create_time": "2018-01-12 11:13:48",
"update_time": "2018-01-12 11:13:48",
"send_type": 1,
"creator_name": "sysadmin",
"deleted": 0,
"link": "xxx",
"Messages": null,
"module": {
"id": 4,
"code": "secure",
"name": "xxx",
"create_time": "2018-01-11 15:38:01",
"update_time": "2018-01-11 15:38:01",
"MessageTexts": null

}
},
"receiver_name": "xxx",
"read": 0,
"deleted": 0,
"create_time": "2018-01-12 11:13:48",
"update_time": "2018-01-12 11:13:48"

}
],
"total": 2

},
"msg": "OK"

}

注: 返回数据的个数是由 limit 限制,而 total 是符合query条件的总数(用于分页) 目前没有发送用户组、全站的行为,如果有的话,在获取列表接口里,增加一步“插入所有发送给我所在用户组,或发给全站的,且我自己的站内信列表里没有记录到的站内信”

获取未读站内信数量:GET /v1/message/unread_count response

{
"code": 200,
"data"29,
"msg""OK"

}

获取单个站内信内容:GET /v1/message/:id

{
"code": 200,
"data": {
"id": 2,
"message_text": {
"id": 2,
"title": "title 2",
"content": "content 3",
"create_time": "2018-01-12 11:37:54",
"update_time": "2018-01-12 11:37:54",
"send_type": 1,
"creator_name": "sysadmin",
"deleted": 0,
"link": "",
"Messages": null,
"module": {
"id": 4,
"code": "secure",
"name": "xxx",
"create_time": "2018-01-11 15:38:01",
"update_time": "2018-01-11 15:38:01",
"MessageTexts": null

}
},
"receiver_name": "xxx",
"read": 1,
"deleted": 0,
"create_time": "2018-01-12 11:37:54",
"update_time": "2018-01-22 17:33:20"

},
"msg": "OK"

}

已阅、批量已阅站内信:PUT /v1/read_messages/:messageIds response

{
"code": 200,
"data""OK",
"msg""OK"

}

全部已阅:PUT:/v1/read_all_messages response 同上
删除、批量删除站内信:PUT /v1/delete_messages/:messageIds
response 同上
全部删除站内信:PUT /v1/delete_all_messages response 同上
获取消息来源列表:GET /v1/module response

{
"code": 200,
"data": [
{
"id": 1,
"code": "worksheet",
"name": "工单平台",
"create_time": "2018-01-11 15:21:38",
"update_time": "2018-01-11 15:21:38",
"MessageTexts": null

},
{
"id": 2,
"code": "cmdb",
"name": "资源管理",
"create_time": "2018-01-11 15:22:28",
"update_time": "2018-01-11 15:22:28",
"MessageTexts": null

},
...
],
"msg": "OK"

}

测试注意点

1、发送站内信

  • 纯接口
  • 收件用户以逗号分割,真实性不做校验
  • 收件用户有长度校验,50个字节
  • title content 有长度校验,分别是45,150个字节
  • module_name 是一个列表,必须从这里选一个

2、其他接口都可以通过前端页面测试优化

  • 未读列表可以加上粗体显示,已读则是普通字体。
  • 对站内信进行分类,打上不同纬度的标签,方便过滤、搜索、屏蔽。
  • 用户可以设置允许接收的站内信的消息来源。管理员可以对全站消息、全站人员、全站的消息属性进行增删改查,比如撤销某个站内信,让所有人都看不见。
  • 管理员可以统计站内信的发送数量、各产品的使用情况、消息被读的比例、消息被读的时间、消息被读的方式(点开还是批量操作)等。