Flarum
通知系统详解
Flarum
中的通知系统是内置的,安装的时候就随身携带
我不打算去深挖每一个源码文件,而是基于自己修复 https://talk.twle.cn/ 的心得去探索 Flarum
中的通知系统
表 ( tables )
Flarum
中的表涉及到两个表 notifications
和 users
notifications_read_time
我们先看看 users
表,跟通知相关的就一个字段 notifications_read_time
,这个字段的意思就如它的名字一样,用来标识 通知 的 已读时间 。 但是它的真正作用却不是这样的,我也是调试了好久,还不确定它的作用到底是不是这样
用于标识上一次拉取通知列表的时间
如果不这样理解,那么会犯疑的,如果这个时间前的 通知 没有标识为已读,岂不是拉取的时候就不会显示了
notifications
notifications
用于存放所有的通知消息,这个表结构也比较简单
+--------------+------------------+------+-----+ | Field | Type | Null | Key | +--------------+------------------+------+-----+ | id | int(10) unsigned | NO | PRI | | user_id | int(10) unsigned | NO | | | sender_id | int(10) unsigned | YES | | | type | varchar(100) | NO | | | subject_type | varchar(200) | YES | | | subject_id | int(10) unsigned | YES | | | data | text | YES | | | time | datetime | NO | | | is_read | tinyint(1) | NO | | | is_deleted | tinyint(1) | NO | | +--------------+------------------+------+-----+
其中有 5 个字段需要注意
-
is_read
这个字段只有在点击 √ 时才设置,因为这是用户主动触发的 已读,这个字段和
users
表中的notifications
字段没有半毛钱关系 -
subject_id
触发通知的主体
id
, 目前就两大类,一个是 discussions ,另一个是 posts- posts : 点赞 ( postLiked ) 、 提及用户 ( userMentioned )
- discussions: 好多,比如发布回复,锁定帖子,置顶帖子,有新回复...
-
subject_type
这个字段看似好用,其实是一个废品,为什么呢? 因为官方根本就没用到,大家好像也很遵守约定不会去用
没有作用的原因是返回内容的时候根本无法根据这个内容去确定是返回
discussion
或者posts
因为返回的通知内容要包含一系列的各种关系,这种关系只能在源码里指定,如果是这样,那还要这个字段做什么呢
-
type
这个可谓是最关键的字段了,用于标识 通知类型,目前已知的通知类型有
通知类型 说明 userMentioned 提到用户 newPost 新回复 postLiked 帖子锁定 discussionLocked 帖子被锁定 discussionStickied 帖子被指定 话题被删除 和 回复被删除 这两个还没看到
-
data
扩展数据
这个好像 ,只有在类型为
newPost
才用到,数据形如'{"postNumber":5'}
API
Flarum 通知系统提供了 3 个 API
请求方法 | URI | 说明 |
---|---|---|
PATCH POST | /api/notifications/read | 标识全部通知为已读 |
PATCH POST | /api/notifications/<id> | 标识某个通知为已读 |
GET | /api/notifications/ | 拉取全部未读消息列表 |
既然知道每个 API 的作用,那么实现功能也就很简单了,需要注意的是第三个 GET /api/notifications
这个 API 的原本用意是拉取上一次拉取以来的新的数据,但是,我们要怎么区分上一次的的数据要不要重新来取呢?
除非,我们改动源码,把拉取的时间也传上去,这个其实就是 users
表中 notifications_read_time
字段的意思,但是,这个字段显然不是这么用的,这个字段需要客户端传上来才是最佳选择
显然,官方没有这么做,所以很坑,导致 notifications_read_time
像一个废品
序列化 ( API 返回内容 )
看到那么多 API 序列化返回的内容,我觉得只有 notifications
做到了简单极致
看一个范例
{ "data": [ { "type": "notifications", "id": "9", "attributes": { "id": 9, "contentType": "newPost", "content": { "postNumber": 5 }, "time": "2018-05-09T01:28:07+00:00", "isRead": false }, "relationships": { "sender": { "data": { "type": "users", "id": "1" } }, "subject": { "data": { "type": "discussions", "id": "3" } } } } ], "included": [ { "type": "users", "id": "1", "attributes": { "username": "yufei", "avatarUrl": "http://w.d.cn/assets/avatars/bntvmp7b6qwpbfrq.png" } }, { "type": "discussions", "id": "3", "attributes": { "title": "携一丝暗", "slug": "-", "isApproved": true } }, { "type": "posts", "id": "6", "attributes": { "id": 6, "number": 4, "time": "2018-05-09T01:27:46+00:00", "contentType": "comment", "contentHtml": "<p><a href=\"http://w.d.cn/u/yufei\" class=\"UserMention\">@yufei</a> 你说呢</p>", "isApproved": true }, "relationships": { "discussion": { "data": { "type": "discussions", "id": "3" } } } } ] }
简直就是业界良心啊,每个字段都是那么的重要
序列化的那个 subject
的 relationships
要根据类型来返回是 discussions
或者 posts
我直接贴上自己写的还没经过优化的代码吧
content = {"postNumber":0} if o['data']: content = json.loads(o['data']) row['attributes']['content'] = content if o['type'] == 'postLiked': row["relationships"]["subject"] = {"data": {"type": "posts","id": str(o['subject_id'])}} rs['extra']['wei_post'].append(o['subject_id']) elif o['type'] == 'newPost': rs['extra']['wei_discussion'].append(o['subject_id']) row["relationships"]["subject"] = {"data": {"type":"discussions","id":str(o['subject_id'])}} elif o['type'] == 'userMentioned': rs['extra']['wei_post'].append(o['subject_id']) row["relationships"]["subject"] = {"data": {"type":"posts","id":str(o['subject_id'])}} else: rs['extra']['wei_discussion'].append(o['subject_id']) row["relationships"]["subject"] = {"data": {"type":"discussions","id":str(o['subject_id'])}}
rs['extra']['wei_post']
这行代码大家请忽略,因为这个和 includes
有关系