使用 GetStream 实现通知系统
Notifications system implementation with GetStream
我正在尝试使用 GS
实现通知系统
基本上相关的机型有:
Organizations
(组织);
User
,组织成员。用户可以在 org 中拥有管理权限;
Entity
管理员用户可以创建,普通用户可以购买,实体也属于组织。
在 GS 中有以下提要:
notification:$userId-$orgId
---- 重新使用默认提要,特定组织中用户的个人通知提要
Org:$orgId
---- 所有组织成员的共享提要
AdminEntity:$entityId
---- 管理员用户对实体的用户操作通知
UserEntity:$entityId
---- 关于 admin/system 普通用户实体操作的通知
让我简要解释一下提要之间的基本关系:
1) Admin
创建 Entity
:
将 activity 推送到 Org:$orgId
关于此事件。 Activity 将 user:$userId
作为 Actor
,将 entity:$entityId
作为 Object
。
订阅他的notification:$userId-$orgId
到AdminEntity:$entityId
2) User
加入 Organization
:
将有关此事件的 activity 推送到 Org:$orgId
将他的 notification:$userId-$orgId
订阅到 Org:$orgId
。
3) User
购买 Entity
:
将activity推送到AdminEntity:$entityId
关于此事件。
订阅他的notification:$userId-$orgId
到UserEntity:$entityId
以此类推
所描述模型的问题在于,在第一种情况下,作为 Entity
的创建者的 User
也会通过 Org:$orgId
收到他创建了 Entity
的通知其他 Organization
名成员。与第二种情况相同。
这是通知系统不需要的行为。
理想情况下,我们不应该通知 User
他在 "shared" 提要中的活动,例如 Org:$orgId
。问题是——如何实现这一目标?也许 GS 提要应该以不同的方式组织,或者可以通过 Aggregation Format
语法来完成?
已编辑:如我所见,可能的解决方案是:
删除所有共享源,例如 Org
、AdminEntity
、UserEntity
;
通过 add_to_many
调用将有关用户在组织中的角色、与实体的关系等的活动直接推送到 notification:$userId-org$id
提要。
我不确定这是否是与 GS 一起使用的惯用解决方案。
我认为您的结构可能比您最初设计的更简单,并且更接近您在 post 底部提到的结构。我还建议您通读 http://blog.getstream.io/best-practices-for-instagram-style-feeds/,这听起来与您想在这里做的事情相似。
对于我们的 Feed 类型的简化分类:
- 通知供稿是为内容创作者准备的(“15 人喜欢你的 post”)。
- 汇总供稿供关注者使用(“添加了 12 post”)。
- 平面提要是添加核心活动的好地方("entities" 在你的情况下?),但你也可以使用 [=114= 将这些活动的额外副本保存到其他聚合和通知提要中] activity 模型上的字段,类似于抄送电子邮件
任何 Feed 类型都可以跟随 Flat Feed,但我们通常建议仅 Flat Feed 和聚合 Feed 跟随其他 Flat Feed。通知提要通常是独立的,我将在下面进行描述。
让我们做出以下假设:
- 有一个名为 "Silverthorne" 的管理员用户,其 ID 为
12
- 组织 ID
56
是 "Acme Inc"
- Ian 是 ID 为
74
的普通用户
- Silverthorne 准备了一些实体内容,当保存到您自己的数据库时,这些内容的 ID 为
63
让我们创建一个名为 org
的平面提要,其中将进行实体活动,并为聚合数据创建一个名为 org_aggregated
的聚合提要,一个名为 notifications
的通知提要,一个平面提要用于用户叫 timeline
。如果您的用户可以看到由另一个用户(而不是组织)创作的所有内容的提要,那么我会推荐另一个名为 usercontent
.
的平面提要
对于 Silverthorne 到 post this activity 到 GetStream,activity 参与者将是 "user:12"
,动词可以是 "post"
,宾语将是成为 "entity:63"
要对此进行伪编码,因为我不知道您使用的是哪个 SDK,将类似于:
# acme inc gets created as an organization, and so its aggregated feed
# should follow the org's flat feed
acme_org_feed = getstreamClient->feed('org', '56')
acme_org_agg_feed = getstreamClient->feed('org_aggregated', '56')
acme_org_agg_feed->follow(acme_org_feed)
此时,添加到 Acme 的 org
平面 Feed 中的每个实体也将被聚合。您可以在 GetStream 仪表板上设置这些信息的汇总方式。
现在,让 Ian 跟随 Acme:
# ian is a member of Acme Inc, so follow their entity feed
ian_feed = getstreamClient->feed('timeline', '74')
ian_feed->follow(acme_org_feed)
如果 Ian 是新注册的,您可以像这样向 Acme 的通知源发送 activity:
acme_notif_feed = getstreamClient->feed('org_notification', '56')
acme_notif_feed->addActivity({
'actor': 'user:74',
'verb': 'registration',
'object': 'user:74'
})
当您稍后检索此通知提要时,它会将所有动词组合在一起,然后分解通知活动,因此您的 UI 可以根据您的选择以不同方式报告这些内容。
现在让 Silverthorne 将该实体添加到 GetStream:
# Silverthorne 添加一个 activity 到 Acme Inc 的实体提要
# 我们将 "cc" activity 到聚合提要
acme_org_feed->添加活动({
'actor': 'user:12',
'verb': 'post',
'object': 'entity:63',
'to': ['usercontent:12'],
...您要跟踪的任何其他元数据
})
保存后,GetStream 将执行以下操作:
- 它将 activity 存储在 Acme (
org:56
) 的 org
flat feed 中
- 它将副本保存到 Ian 的时间轴中,因为
user:74
遵循 org:56
的固定提要
- 它还会从 'To' 字段保存一个副本到 Silverthorne 的提要 (
usercontent:12
);这完全是可选的
- 它将副本保存到 Acme (
org_aggregated:56
) 的聚合提要中,因为聚合提要遵循平面提要
当 Ian 登录时,您的应用程序现在可以显示多个可能的提要和选项,这完全取决于您的应用程序:
- 如果 Ian 应该看到他所遵循的一切的时间轴,您将获取
timeline:74
提要,其中将包含实体 #63 的副本
- 如果 Ian 应该看到他关注的所有组织的列表,您可以获取
ian_feed->followed()
以获取 Ian 关注的每个提要的列表
- 对于 Ian 关注的每个提要,您可以获取该组织的聚合提要供 Ian 查看摘要(即,获取
org_aggregated:56
并且 Ian 可以看到 one new entity added in the past day
如果这是您聚合您的方式的方式内容)
这里的关键是 Ian 没有 关注提要来查看内容,您的应用可以提取任何提要以显示给任何用户。
如果 Ian 'share' 或 'like' Silverthorne post 编辑的那个实体,您将向 'org_notification:56' 提要添加一个新的 activity 'user:74' 作为 Actor,'entity:63' 是宾语,verb 是你想要的任何字符串(最多 20 个字符)。如果您希望您的应用程序查看其他用户如何与组织的 post 交互,您可以获取 org_notification:56
并且所有用户都可以看到 "Ian liked entity 63",或者您的 UI 需要呈现那个。
希望这有助于澄清事情并为您提供一些额外的细节。如果您需要进一步的帮助或想法,请通过电子邮件联系我们的支持团队。
最后一点,我认为我不会 $userId-$orgId
...我们的一些 SDK(即 Rails 和 Django)会自动尝试 'enrich' 数据库中的那些值,因此您需要额外的逻辑来稍后尝试拆分这些值。我们建议使用 UUID 作为 collision-free 标识符,但这完全取决于您的控制。如果一个用户真的可以成为多个组织的一部分,您可以只让该用户 'follow' 组织的提要。
我正在尝试使用 GS
实现通知系统基本上相关的机型有:
Organizations
(组织);User
,组织成员。用户可以在 org 中拥有管理权限;Entity
管理员用户可以创建,普通用户可以购买,实体也属于组织。
在 GS 中有以下提要:
notification:$userId-$orgId
---- 重新使用默认提要,特定组织中用户的个人通知提要Org:$orgId
---- 所有组织成员的共享提要AdminEntity:$entityId
---- 管理员用户对实体的用户操作通知UserEntity:$entityId
---- 关于 admin/system 普通用户实体操作的通知
让我简要解释一下提要之间的基本关系:
1) Admin
创建 Entity
:
将 activity 推送到
Org:$orgId
关于此事件。 Activity 将user:$userId
作为Actor
,将entity:$entityId
作为Object
。订阅他的
notification:$userId-$orgId
到AdminEntity:$entityId
2) User
加入 Organization
:
将有关此事件的 activity 推送到
Org:$orgId
将他的
notification:$userId-$orgId
订阅到Org:$orgId
。
3) User
购买 Entity
:
将activity推送到
AdminEntity:$entityId
关于此事件。订阅他的
notification:$userId-$orgId
到UserEntity:$entityId
以此类推
所描述模型的问题在于,在第一种情况下,作为 Entity
的创建者的 User
也会通过 Org:$orgId
收到他创建了 Entity
的通知其他 Organization
名成员。与第二种情况相同。
这是通知系统不需要的行为。
理想情况下,我们不应该通知 User
他在 "shared" 提要中的活动,例如 Org:$orgId
。问题是——如何实现这一目标?也许 GS 提要应该以不同的方式组织,或者可以通过 Aggregation Format
语法来完成?
已编辑:如我所见,可能的解决方案是:
删除所有共享源,例如
Org
、AdminEntity
、UserEntity
;通过
add_to_many
调用将有关用户在组织中的角色、与实体的关系等的活动直接推送到notification:$userId-org$id
提要。
我不确定这是否是与 GS 一起使用的惯用解决方案。
我认为您的结构可能比您最初设计的更简单,并且更接近您在 post 底部提到的结构。我还建议您通读 http://blog.getstream.io/best-practices-for-instagram-style-feeds/,这听起来与您想在这里做的事情相似。
对于我们的 Feed 类型的简化分类:
- 通知供稿是为内容创作者准备的(“15 人喜欢你的 post”)。
- 汇总供稿供关注者使用(“添加了 12 post”)。
- 平面提要是添加核心活动的好地方("entities" 在你的情况下?),但你也可以使用 [=114= 将这些活动的额外副本保存到其他聚合和通知提要中] activity 模型上的字段,类似于抄送电子邮件
任何 Feed 类型都可以跟随 Flat Feed,但我们通常建议仅 Flat Feed 和聚合 Feed 跟随其他 Flat Feed。通知提要通常是独立的,我将在下面进行描述。
让我们做出以下假设:
- 有一个名为 "Silverthorne" 的管理员用户,其 ID 为
12
- 组织 ID
56
是 "Acme Inc" - Ian 是 ID 为
74
的普通用户
- Silverthorne 准备了一些实体内容,当保存到您自己的数据库时,这些内容的 ID 为
63
让我们创建一个名为 org
的平面提要,其中将进行实体活动,并为聚合数据创建一个名为 org_aggregated
的聚合提要,一个名为 notifications
的通知提要,一个平面提要用于用户叫 timeline
。如果您的用户可以看到由另一个用户(而不是组织)创作的所有内容的提要,那么我会推荐另一个名为 usercontent
.
对于 Silverthorne 到 post this activity 到 GetStream,activity 参与者将是 "user:12"
,动词可以是 "post"
,宾语将是成为 "entity:63"
要对此进行伪编码,因为我不知道您使用的是哪个 SDK,将类似于:
# acme inc gets created as an organization, and so its aggregated feed
# should follow the org's flat feed
acme_org_feed = getstreamClient->feed('org', '56')
acme_org_agg_feed = getstreamClient->feed('org_aggregated', '56')
acme_org_agg_feed->follow(acme_org_feed)
此时,添加到 Acme 的 org
平面 Feed 中的每个实体也将被聚合。您可以在 GetStream 仪表板上设置这些信息的汇总方式。
现在,让 Ian 跟随 Acme:
# ian is a member of Acme Inc, so follow their entity feed
ian_feed = getstreamClient->feed('timeline', '74')
ian_feed->follow(acme_org_feed)
如果 Ian 是新注册的,您可以像这样向 Acme 的通知源发送 activity:
acme_notif_feed = getstreamClient->feed('org_notification', '56')
acme_notif_feed->addActivity({
'actor': 'user:74',
'verb': 'registration',
'object': 'user:74'
})
当您稍后检索此通知提要时,它会将所有动词组合在一起,然后分解通知活动,因此您的 UI 可以根据您的选择以不同方式报告这些内容。
现在让 Silverthorne 将该实体添加到 GetStream: # Silverthorne 添加一个 activity 到 Acme Inc 的实体提要 # 我们将 "cc" activity 到聚合提要 acme_org_feed->添加活动({ 'actor': 'user:12', 'verb': 'post', 'object': 'entity:63', 'to': ['usercontent:12'], ...您要跟踪的任何其他元数据 })
保存后,GetStream 将执行以下操作:
- 它将 activity 存储在 Acme (
org:56
) 的 - 它将副本保存到 Ian 的时间轴中,因为
user:74
遵循org:56
的固定提要
- 它还会从 'To' 字段保存一个副本到 Silverthorne 的提要 (
usercontent:12
);这完全是可选的 - 它将副本保存到 Acme (
org_aggregated:56
) 的聚合提要中,因为聚合提要遵循平面提要
org
flat feed 中
当 Ian 登录时,您的应用程序现在可以显示多个可能的提要和选项,这完全取决于您的应用程序:
- 如果 Ian 应该看到他所遵循的一切的时间轴,您将获取
timeline:74
提要,其中将包含实体 #63 的副本
- 如果 Ian 应该看到他关注的所有组织的列表,您可以获取
ian_feed->followed()
以获取 Ian 关注的每个提要的列表 - 对于 Ian 关注的每个提要,您可以获取该组织的聚合提要供 Ian 查看摘要(即,获取
org_aggregated:56
并且 Ian 可以看到one new entity added in the past day
如果这是您聚合您的方式的方式内容)
这里的关键是 Ian 没有 关注提要来查看内容,您的应用可以提取任何提要以显示给任何用户。
如果 Ian 'share' 或 'like' Silverthorne post 编辑的那个实体,您将向 'org_notification:56' 提要添加一个新的 activity 'user:74' 作为 Actor,'entity:63' 是宾语,verb 是你想要的任何字符串(最多 20 个字符)。如果您希望您的应用程序查看其他用户如何与组织的 post 交互,您可以获取 org_notification:56
并且所有用户都可以看到 "Ian liked entity 63",或者您的 UI 需要呈现那个。
希望这有助于澄清事情并为您提供一些额外的细节。如果您需要进一步的帮助或想法,请通过电子邮件联系我们的支持团队。
最后一点,我认为我不会 $userId-$orgId
...我们的一些 SDK(即 Rails 和 Django)会自动尝试 'enrich' 数据库中的那些值,因此您需要额外的逻辑来稍后尝试拆分这些值。我们建议使用 UUID 作为 collision-free 标识符,但这完全取决于您的控制。如果一个用户真的可以成为多个组织的一部分,您可以只让该用户 'follow' 组织的提要。