Flarum 通知系统详解

yufei       6 年, 4 月 前       1150

Flarum 通知系统详解

Flarum 中的通知系统是内置的,安装的时候就随身携带

我不打算去深挖每一个源码文件,而是基于自己修复 https://talk.twle.cn/ 的心得去探索 Flarum 中的通知系统

表 ( tables )

Flarum 中的表涉及到两个表 notificationsusers

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 个字段需要注意

  1. is_read

    这个字段只有在点击 时才设置,因为这是用户主动触发的 已读,这个字段和 users 表中的 notifications 字段没有半毛钱关系

  2. subject_id

    触发通知的主体 id, 目前就两大类,一个是 discussions ,另一个是 posts

    1. posts : 点赞 ( postLiked ) 、 提及用户 ( userMentioned )
    2. discussions: 好多,比如发布回复,锁定帖子,置顶帖子,有新回复...
  3. subject_type

    这个字段看似好用,其实是一个废品,为什么呢? 因为官方根本就没用到,大家好像也很遵守约定不会去用

    没有作用的原因是返回内容的时候根本无法根据这个内容去确定是返回 discussion 或者 posts

    因为返回的通知内容要包含一系列的各种关系,这种关系只能在源码里指定,如果是这样,那还要这个字段做什么呢

  4. type

    这个可谓是最关键的字段了,用于标识 通知类型,目前已知的通知类型有

    通知类型 说明
    userMentioned 提到用户
    newPost 新回复
    postLiked 帖子锁定
    discussionLocked 帖子被锁定
    discussionStickied 帖子被指定

    话题被删除回复被删除 这两个还没看到

  5. 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"
          }
        }
      }
    }
  ]
}

简直就是业界良心啊,每个字段都是那么的重要

序列化的那个 subjectrelationships 要根据类型来返回是 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 有关系

目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.