首先我们来看一下市场上关于消息的实现是怎么样的。
简书的消息系统主要分了两种
简信
简信的性质其实跟私信是一样的,是用户发送给用户的一则消息,有具体的信息内容。

提醒
而提醒,则是系统发送的一则消息,其文案格式是固定的,并且对特殊对象一般拥有超链接。

知乎跟简书一样,主要分了两种:
立即学习“PHP免费学习笔记(深入)”;
私信
跟简书一样,使用户发送给用户的一则消息,也可以是管理员发送给用户的消息。

消息
知乎的消息比简书的提醒有过之而无不及,知乎会对多条相似的消息进行聚会,以达到减轻用户阅读压力的体验。

通过两种产品的简单分析,得出他们的消息有两种分类,在这基础上,我们再加上一种:公告。
公告的主要性质是系统发送一则含有具体内容的消息,站内所有用户都能读取到这条消息。
所以,消息有三种分类:
我们从简书取一组提醒样本:
分析句子结构,提醒的内容无非就是
<p>「谁对一样属于谁的事物做了什么操作」<br />「someone do something in someone's something」</p>
someone = 提醒的触发者,或者发送者,标记为sender
do something = 提醒的动作,评论、喜欢、关注都属于一个动作,标记为action
something = 提醒的动作作用对象,这就具体到是哪一篇文章,标记为target
someone's = 提醒的动作作用对象的所有者,标记为targetOwner
这就清楚了,sender和targetOwner就是网站的用户,而target是具体到哪一篇文章,如果提醒的对象不仅仅局限于文章,还有其他的话,就需要增加一项targetType,来标记目标是文章还是其他的什么。而action,则是固定的,整个网站会触发提醒的动作可能就只有那几样:评论、喜欢、关注.....(或者其他业务需要提醒的动作)
以知乎为例
推的比较常见,需要针对某一个问题维护着一张关注者的列表,每当触发这个问题推送的条件时(例如有人回答问题),就把这个通知发送给每个关注者。
拉的相对麻烦一点,就是推的反向,例如每个用户都有一张关注问题的列表,每当用户上线的时候,对每个问题进行轮询,当问题的事件列表出现了比我原本时间戳大的信息就进行拉取。
而我们则根据消息的不同分类采用不同的获取方式:
通告和提醒,适合使用拉取的方式,消息产生之后,会存在消息表中,用户在某一特定的时间根据自己关注问题的表进行消息的拉取,然后添加到自己的消息队列中,
信息,适合使用推的方式,在发送者建立一条信息之后,同时指定接收者,把消息添加到接收者的消息队列中。
根据提醒使用拉取的方式,需要维护一个关注某一事物的列表。
这种行为,我们称之为:「订阅」Subscribe
一则订阅有以下三个核心属性:
比如我发布了一篇文章,那么我会订阅文章《XXX》的评论动作,所以文章《XXX》每被人评论了,就需要发送一则提醒告知我。
订阅的规则还可以扩展
我喜欢了一篇文章,和我发布了一篇文章,订阅的动作可能不一样。
喜欢了一篇文章,我希望我订阅这篇文章更新、评论的动作。
而发布了一篇文章,我希望我只是订阅这篇文章的评论动作。
这时候就需要多一个参数:subscribReason
不同的subscribReason,对应着一个动作数组,
subscribReason = 喜欢,对应着 actions = [更新,评论]
subscribReason = 发布,对应着 actions = [评论]
订阅的规则还还可以扩展
用户可能会有一个自己的订阅设置,比如对于所有的喜欢的动作,我都不希望接收。
比如Knewone的提醒设置

所以我们需要再维护一个表:SubscriptionConfig,来存放用户的提醒设置。
并且,当用户没有提醒设置的时候,可以使用系统提供的一套默认设置:defaultSubscriptionConfig
如果我发布了一篇文章《XXX》,在我不在线的时候,被评论了10遍,当我一上线的时候,应该是收到十条信息类似于:「谁谁谁评论了你的文章《XXX》」?
还是应该收到一条信息:「甲、乙、丙、丁...评论了你的文章《XXX》」?
知乎在聚合上做的很优秀,要知道他们要实现这个还是挺有技术的:
知乎的消息机制,在技术上如何设计与规划?
网站的消息(通知)系统一般是如何实现的?
海洋影视管理系统(seacms,海洋cms)是一套专为不同需求的站长而设计的视频点播系统,灵活,方便,人性化设计简单易用是最大的特色,是快速架设视频网站首选,只需5分钟即可建立一个海量的视频讯息的行业网站。 海洋cms采用PHP+MYSQL架构,原生PHP代码带来卓越的访问速度和负载能力免去您的后顾之优。海洋cms支持一键转换原max的模板和数据,实现网站无缝迁移到新平台。众多人性化功能设计,超
116
关于这部分功能,我们还没有具体的实现方法,暂时也无法讲得更加详细。⊙﹏⊙
通过上面的分析,大概知道做这个消息系统,需要哪些实体类:
说了这么多,整理一下整个消息流程的一些行为:
<code class="javascript">id : {type: <span class="hljs-string">'integer', primaryKey: <span class="hljs-literal">true}, <span class="hljs-comment">// 主键
content : {type: <span class="hljs-string">'text'}, <span class="hljs-comment">// 消息的内容
type : {type: <span class="hljs-string">'integer', required: <span class="hljs-literal">true, enum: [<span class="hljs-number">1, <span class="hljs-number">2, <span class="hljs-number">3]}, <span class="hljs-comment">// 消息的类型,1: 公告 Announce,2: 提醒 Remind,3:信息 Message
target : {type: <span class="hljs-string">'integer'}, <span class="hljs-comment">// 目标的ID
targetType : {type: <span class="hljs-string">'string'}, <span class="hljs-comment">// 目标的类型
action : {type: <span class="hljs-string">'string'}, <span class="hljs-comment">// 提醒信息的动作类型
sender : {type: <span class="hljs-string">'integer'}, <span class="hljs-comment">// 发送者的ID
createdAt : {type: <span class="hljs-string">'datetime', required: <span class="hljs-literal">true}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code>Save Remind
消息表,我们需要target、targetType字段,来记录该条提醒所关联的对象。而action字段,则记录该条提醒所关联的动作。
比如消息:「小明喜欢了文章」
则:
<code class="javascript">target = <span class="hljs-number">123, <span class="hljs-comment">// 文章ID targetType = <span class="hljs-string">'post', <span class="hljs-comment">// 指明target所属类型是文章 sender = <span class="hljs-number">123456 <span class="hljs-comment">// 小明ID</span></span></span></span></span></span></code>
Save Announce and Message
当然,Notify还支持存储公告和信息。它们会用到content字段,而不会用到target、targetType、action字段。
<code class="javascript">id : {type: <span class="hljs-string">'integer', primaryKey: <span class="hljs-literal">true}, <span class="hljs-comment">// 主键
isRead : {type: <span class="hljs-string">'boolean', required: <span class="hljs-literal">true},
user : {type: <span class="hljs-string">'integer', required: <span class="hljs-literal">true}, <span class="hljs-comment">// 用户消息所属者
notify : {type: <span class="hljs-string">'integer', required: <span class="hljs-literal">true} <span class="hljs-comment">// 关联的Notify
createdAt : {type: <span class="hljs-string">'datetime', required: <span class="hljs-literal">true}</span></span></span></span></span></span></span></span></span></span></span></span></span></code>我们用UserNotify来存储用户的消息队列,它关联一则提醒(Notify)的具体内容。
UserNotify的创建,主要通过两个途径:
<code class="javascript">target : {type: <span class="hljs-string">'integer', required: <span class="hljs-literal">true}, <span class="hljs-comment">// 目标的ID
targetType : {type: <span class="hljs-string">'string', required: <span class="hljs-literal">true}, <span class="hljs-comment">// 目标的类型
action : {type: <span class="hljs-string">'string'}, <span class="hljs-comment">// 订阅动作,如: comment/like/post/update etc.
user : {type: <span class="hljs-string">'integer'},
createdAt : {type: <span class="hljs-string">'datetime', required: <span class="hljs-literal">true}</span></span></span></span></span></span></span></span></span></span></span></code>订阅,是从Notify表拉取消息到UserNotify的前提,用户首先订阅了某一个目标的某一个动作,在此之后产生这个目标的这个动作的消息,才会被通知到该用户。
如:「小明关注了产品A的评论」,数据表现为:
<code class="javascript">target: <span class="hljs-number">123, <span class="hljs-comment">// 产品A的ID targetType: <span class="hljs-string">'product', action: <span class="hljs-string">'comment', user: <span class="hljs-number">123 <span class="hljs-comment">// 小明的ID</span></span></span></span></span></span></code>
这样,产品A下产生的每一条评论,都会产生通知给小明了。
<code class="javascript">action: {type: <span class="hljs-string">'json', required: <span class="hljs-literal">true}, <span class="hljs-comment">// 用户的设置
user: {type: <span class="hljs-string">'integer'}</span></span></span></span></code>不同用户可能会有不一样的订阅习惯,在这个表中,用户可以统一针对某种动作进行是否订阅的设置。而默认是使用系统提供的默认配置:
<code class="javascript">defaultSubscriptionConfig: {
<span class="hljs-string">'comment' : <span class="hljs-literal">true, <span class="hljs-comment">// 评论
<span class="hljs-string">'like' : <span class="hljs-literal">true, <span class="hljs-comment">// 喜欢
}</span></span></span></span></span></span></code><p>在这套模型中,<code>targetType</code>、<code>action</code>是可以根据需求来扩展的,例如我们还可以增加多几个动作的提醒:<code>hate</code>被踩、<code>update</code>被更新....诸如此类。</p>
<code class="javascript"><span class="hljs-comment">// 提醒关联的目标类型
targetType: {
PRODUCT : <span class="hljs-string">'product', <span class="hljs-comment">// 产品
POST : <span class="hljs-string">'post' <span class="hljs-comment">// 文章
},
<span class="hljs-comment">// 提醒关联的动作
action: {
COMMENT : <span class="hljs-string">'comment', <span class="hljs-comment">// 评论
LIKE : <span class="hljs-string">'like', <span class="hljs-comment">// 喜欢
},
<span class="hljs-comment">// 订阅原因对应订阅事件
reasonAction: {
<span class="hljs-string">'create_product' : [<span class="hljs-string">'comment', <span class="hljs-string">'like']
<span class="hljs-string">'like_product' : [<span class="hljs-string">'comment'],
<span class="hljs-string">'like_post' : [<span class="hljs-string">'comment'],
},
<span class="hljs-comment">// 默认订阅配置
defaultSubscriptionConfig: {
<span class="hljs-string">'comment' : <span class="hljs-literal">true, <span class="hljs-comment">// 评论
<span class="hljs-string">'like' : <span class="hljs-literal">true, <span class="hljs-comment">// 喜欢
}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code>createAnnounce(content, sender)
createRemind(target, targetType, action, sender, content)
createMessage(content, sender, receiver)
pullAnnounce(user)
pullRemind(user)
subscribe(user, target, targetType, reason)
cancelSubscription(user, target ,targetType)
getSubscriptionConfig(userID)
updateSubscriptionConfig(userID)
getUserNotify(userID)
read(user, notifyIDs)

我们可以在产品创建之后,调用NotifyService.subscribe方法,
然后在产品被评论之后调用NotifyService.createRemind方法,
再就是用户登录系统或者其他的某一个时刻调用NotifyService.pullRemind方法,
最后在用户查询消息队列的时候调用NotifyService.getUserNotify方法。

在管理员发送了一则公告的时候,调用NotifyService.createAnnounce方法,
然后在用户登录系统或者其他的某一个时刻调用NotifyService.pullAnnounce方法,
最后在用户查询消息队列的时候调用NotifyService.getUserNotify方法。

信息的创建,只需要直接调用NotifyService.createMessage方法就可以了,
在下一次用户查询消息队列的时候,就会查询这条信息。
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号