Skip to content

受众领域模型(总纲) ​

状态:现行模型(2026-10-07 重写)。本文是 Herald 领域模型的唯一总纲:术语、边界、 受众层扩展全貌。关系的逐项详设(流程图 / 矩阵 / 参数表)在 受众订阅与投递中枢(下称「关系详设」); 概念分层与职责边界的裁决在概念边界与分层审计; Phase 0-9 管道改造的执行历史在现状审计(存档)。 铁律:方案变了先改本文——代码与本文不一致时,以本文为口径提批修正。

1. 定位 ​

Herald 是轻量的通知编排与投递基础设施。它决定的不只是「发出去」:谁、什么时候、通过什么渠道、以什么强度、是否聚合、是否过滤、是否升级、是否去重——这些编排决策由 Herald 收口,投递管道(Phase 0-9 已落地)是这一面的地基(见现状审计)。

在此之上,Herald 的定位是统一订阅与投递中枢:从「应用主动推送」补全为「被通知者做主」。应用(业务系统集成方)只持有受众 ID;谁在什么渠道、以什么频率收到什么品类,由受众自己的关系决定,而不是由调用方写死。

text
旧:应用 ──「发到 telegram:123456」──> Herald ──> 渠道
新:应用 ──「告警品类,通知 user:alice」──> Herald ──> 关系过滤 ──> 联系面 ──> 渠道
                                                        ▲
                              受众自己的订阅/偏好/退订决定这里放行什么

2. 术语契约(唯一口径) ​

文档与代码术语一一对应,一个词只有一个含义。全站文档(指南 / 架构 / API / 设计)一律按本表用词:

术语代码标识含义
受众audience(引用 group:<id> / user:<id>)身份主体,全系统唯一;应用只持受众 ID,不碰渠道凭据
接收人recipient受众成员的具名个人(recipients 静态配置实体)。注意与指派关系里的「接收者角色」区分——接收人是身份,接收者是关系角色
端点Endpoint接收人的静态投递地址(type + target)
联系面ContactSurface受众在运行时绑定的可达渠道凭据(TG chat_id、邮箱、公众号 openid、RSS 私密 token),带 pending/active/invalid 状态
订阅关系RelationSubscription / Subscribe()受众主动勾选的关系:品类×渠道×频率,完全自主可退订;关系中的受众角色叫订阅者(subscriber)
指派关系RelationEnrollment / Enroll()被动纳入的关系:管理员分组广播、运营触达、系统通知;关系中的受众角色叫接收者(recipient role)
策略位Policy(AllowUnsubscribe / MustDeliver)挂在关系上的权利位:退订权、必达标记
入口来源Source(bot / wechat_mp / preference_center / admin / app:<name>)关系与绑定经由哪个入口适配器进来,审计按此记
品类category业务通知的分类维度(告警 / 账单 / 域名 / 公告 / 系统 / 营销),关系的分类轴
渠道channel触达手段(telegram / email / 站内信 / RSS / 短信 / 电话),带侵扰度、实时性、投递方向(push/pull)三个属性。渠道≠provider:渠道经渠道块展开为 provider 实例,同一渠道可换实现(email→smtp 或 resend)
渠道块channels 配置块命名渠道 → provider 实例集合的静态展开(既有投递配置;与品类维度正交,见 §6)
偏好Preference订阅关系上的品类×渠道×频率三元组选择
聚合Digest按受众+品类+时间窗把多条事件收成一条摘要
紧急度Urgency(routine / normal / urgent / critical)消息的紧急级别,决定允许的渠道强度区间
侵扰度Intensity(L0–L5)渠道的打扰强度阶梯:拉式 < 邮件 < 站内信 < IM < 短信 < 电话
投递模式escalation / fixed / parallel升级链 / 固定单渠道 / 多渠道并行,三选一可配
频控once / throttle / always去重频控三档:仅一次 / 窗口节流 / 允许重复

受众是身份;订阅者/接收者是受众在某条关系里的角色。不存在平行的「订阅者」身份实体——应用侧只对接一套受众 ID。

3. 核心链路 ​

text
Event ──> Rule ──> Notification ──┬──> Audience(受众引用)
                                  └──> Template(渲染)
                                        │
                 受众层(§5):关系过滤(订阅/指派 × 渠道矩阵)
                              └──> 联系面(可达凭据,§5.2)
                                        │
                                        ▼
                    Routing(展开成投递任务)──> 去重/频控(§8)──> 投递模式(§7)
                                        │
                                        ▼
                    Delivery Task ──> Queue ──> Worker ──> Provider
                                        │
                    旁路:Digest 时间窗聚合(§10)/RSS 拉式出口(§10)

4. 概念边界 ​

text
Notification ≠ Delivery Task        一次通知 ≠ 一次渠道投递
Audience ≠ Channel ≠ Provider       通知谁 / 触达手段 / 怎么发送,三件事
接收人 ≠ 端点                        具名个人 ≠ 一条静态投递地址
端点 ≠ 联系面                        Endpoint 是静态配置地址;ContactSurface 是
                                    运行时绑定、带状态、可失效重绑的凭据
订阅 ≠ 指派                          主动勾选与被动纳入同表不同义:退订权、
                                    渠道矩阵、审计全部分开(关系详设 §4)
关系 ≠ 联系面                        意愿/义务(能不能收)≠ 可达性(收得到吗)
Routing ≠ Delivery                  决策(谁收到什么)≠ 执行(投出去、重试)
规则升级 ≠ 强度升级                  规则引擎升级回答「叫谁」;强度升级链回答
                                    「怎么叫得更响」,两者正交可叠加

5. 受众层 ​

5.1 身份:受众 → 接收人 → 端点 ​

受众有两级引用形态:group:<id>(群组,成员解析见通知群组设计)与 user:<id>(具名个人)。静态配置模型:

yaml
audiences:            # 受众 → 接收人
  ops:
    recipients: [alice, bob]
recipients:           # 接收人 → 端点
  alice:
    endpoints:
      - { type: telegram, target: "123456" }
      - { type: email, target: "alice@example.com" }

配置在启动时校验,非法即拒起(user: 引用绝不静默落空)。这张静态表是联系面的种子(见下节);无联系面扩展的既有用法行为完全不变。

5.2 联系面(ContactSurface) ​

渠道凭据的运行时挂点。应用侧只经手受众 ID 与一次性绑定 token,永不接触凭据明文;换绑需旧渠道确认,防止一条深链抢走别人的通知渠道。

状态含义投递行为
pending绑定发起未核销 / 换绑待旧渠道确认不投
active已核销可用按关系投递
invalid外部平台判定失效(对账 / chat 不可达 / 取关回流)不投,等待重新绑定

绑定走 bot deep-link 一次性 token(15 分钟过期、单次有效);每个受众随注册自动持有一份 RSS 私密 token(零绑定成本)。流程图与规则细节见关系详设 §3。已落地:core/audience.SurfaceRegistry(批次 2)。

5.3 关系(Relation) ​

投递的唯一合法依据:发送时校验「关系允许 × 联系面绑定」的交集,交集为空不投并审计。两种关系即使实现同表,语义、退订策略、审计也必须分开:

订阅 subscription指派 enrollment
发起方受众自己勾选管理员 / 运营 / 系统
退订权完全自主底线保留(见下)
接口Subscribe()Enroll()

指派关系的底线:营销/运营类必须可退订;系统必达类明示标记且仅限法定/合同/安全义务内容;渠道与频率上限用户始终可管。关系行带 type + source + policy,查询与审计按类型分,不做万能关系接口。已落地:core/audience.Registry(批次 1)。数据模型与流程图见关系详设 §4。

6. 渠道与渠道×关系矩阵 ​

渠道(触达手段)与 provider(发送实现)分开:渠道是受众视角的词,provider 是集成视角的词;既有 channels 配置块承担「命名渠道 → provider 集合」的静态展开,品类维度(告警/账单/…)随集成者 API 落地注册(关系详设 §13.2)。

可用渠道随关系类型走,不是一套。发送时校验三方交集:

text
投递目标 = 受众绑定的联系面 ∩ 关系类型允许的渠道 ∩ 紧急度允许的强度区间
渠道类订阅型指派·系统必达指派·营销/运营
即时类(IM/短信/推送)✅ 用户自选✅ 多渠道并行保必达❌ 禁用
邮件✅ 用户自选✅ 兜底并行✅ 默认渠道,低频可退订
应用侧(webhook→站内信)✅ 用户自选✅✅ 默认渠道,低频可退订
拉式(RSS)✅ 仅订阅型❌❌

每格默认策略、校验时机与违规报错见关系详设 §5。矩阵与过滤已落地(批次 4:矩阵入 Enroll 门 + Filter.Allow 发送前复核;expandRef 管道挂接随批次 11 触发面——现 /notify 无品类维度可过滤)。

7. 强度×紧急度与投递模式 ​

渠道不是平的。侵扰度 L0–L5 阶梯(RSS 拉取 → 邮件 → 站内信 → IM → 短信 → 电话)配消息紧急度四级(例行/一般/紧急/关键),紧急度决定允许的强度区间;匹配规则独立成策略件,不散落在 provider 里。

用户偏好可降不可升:可以在紧急度区间内往下关渠道,不能往上开;关键级有保底渠道(配置指定、偏好中心明示)。三种投递模式按品类/关系类型/单次触发可配,策略件同一入口不同执行器:

模式行为默认
升级链 escalation未应答逐级升强度,应答即停紧急/关键
固定单渠道 fixed只走指定的一种渠道例行/一般
多渠道并行 parallel一次全发保必达系统必达类强制

阶梯图、匹配矩阵、升级链参数与去重折叠的先后关系见关系详设 §6。已落地(批次 9,core/audience.DeliveryPolicy 策略件 + BuildPlan/RunPlan 执行器;触发面接线随批次 11)。

8. 去重与频控 ​

与聚合分工:Digest = 时间窗聚合不同消息;去重 = 相同消息折叠。去重发生在投递管道之前、升级链之前——10 条重复告警先折叠成 1 条再计应答与升级。

三层:事件幂等(event_id 重复触发只投一次)、内容折叠(同品类同内容窗口内折叠带计数「×10」,原始事件列表保留可审计)、状态机去重(状态型告警按翻转发,挂→恢复→再挂才再发)。频控三档可配(once / throttle / always,按品类/关系类型),用户偏好可收窄不可放宽。

既有能力:内容指纹去重与请求幂等键已上线;事件幂等/折叠/状态机/三档频控已落地(批次 10,core/dedup.Gate 闸序 + config.DedupConfig 品类档位与窗口覆盖表)。用户偏好收窄的 Narrow 格已备,端点随批次 11 偏好面接线。细节见关系详设 §11。

9. 偏好与入口 ​

订阅者在偏好中心自助勾选「什么品类 → 什么渠道 → 什么频率」,默认策略随品类给(系统必收、营销默认每周汇总、告警可降频不可静默)。偏好修改入口不唯一——bot 命令、公众号关注事件、应用内勾选、偏好中心都是来源适配器;Herald 是关系的权威登记处,所有入口收敛为注册表里的关系变更,取关必须回流(停止一切投递),外部平台状态定期对账。默认策略表与适配器规则见关系详设 §7-8。偏好模型已落地(批次 3,core/audience 的 Frequency/Preference/DefaultPolicy 与读写 API);来源适配器已落地(批次 8,SourceAdapter + bot/公众号/应用内三个入口端点 + 外部状态对账)。

10. 触达形态:Digest 与 RSS ​

Digest(批次 6):按受众×品类×时间窗(每日/每周)把零散事件收成一条摘要,走既有投递管道(摘要也享受重试/审计);实时类豁免直投,系统必达永不聚合。已落地(core/digest:窗口翻转严格晚到语义、Resolve 豁免裁决、digest:<品类> 模板换皮、FlipLoop 定时器 + 可选 redis 租约选主——无 redis_addr 单机直跑)。

RSS 拉式(批次 7):零绑定零推送的天然偏好通道。公共 feed 每品类一个(/feeds/<品类>.xml,只含无受众引用的公开内容);私密 feed 带受众的 rss_token(/feeds/private/<rss_token>.xml,批次 2 的重置即旧地址失效语义直接生效),账单等个人内容走这里;频率=阅读器轮询,Herald 不推,天然无骚扰;仅订阅型关系可用——指派内容不进 feed。已落地(core/feeds 拉式投影存储 + RSS 2.0 渲染,/feeds/** 端点,投递管道对 rss 类渠道就地投影不产生 provider 任务;可见性在读取时判定——token 归属 + 关系注册表×渠道矩阵复核,取关即从下一次拉取起消失;多地址容灾按边界审计 §4 归阅读器侧)。

11. 投递与审计 ​

投递侧已经落地的形状:任务状态枚举(delivered / failed / dead,重试中 retrying)、错误六类词汇(temporary / permanent / rate_limited / authentication / invalid_request / timeout)决定重试与终态、429 带 Retry-After 退避、可重试失败异步重新入队(next_retry_at 队列侧持留,worker 不睡退避)。审计侧已按关系详设 §12 补齐:关系类型与入口来源快照进通知→任务→投递日志全链、关系/联系面变更流水(core/audit,订阅/指派/退订与绑定/换绑/失效六类事件,按类型与受众分读)、去重折叠明细(delivery.deduped,带内容指纹)——审计回答「这条订阅从哪个入口来、这次投递依据哪条关系、为什么失败、谁在何时改了什么」。审计补齐已落地(批次 5);发送侧关系上下文随批次 8/11 触发面填充。

12. 集成者 API ​

全部模型能力配可编程配置 API:每个集成应用一个 app 命名空间(品类/模板/策略互相隔离),app token 按权限分级(config / trigger / query);配置、触发、查询三组端点与升级链、去重、渠道矩阵等策略件同口径;Go SDK 首发,webhook 回调带回投递结果与退订事件。契约草案见关系详设 §13,集成指南随批次 11 落地 docs/guide/integration.md。已落地(批次 11 增量一~八;2026-10-08 阶段③集成方对接验证收官——事件接入适配面与回调回带 event_id 已贯通)。

13. 落地状态(诚实口径) ​

能力状态证据
通知管道(路由/队列/worker/重试/错误分类/请求幂等)✅ 落地现状审计 Phase 0-9
关系模型与受众注册表✅ 落地批次 1(3fc1a82),core/audience.Registry
联系面与绑定(token/换绑/RSS token)✅ 落地批次 2(19cf16c),core/audience.SurfaceRegistry
偏好中心(品类×渠道×频率、默认策略表)✅ 落地批次 3(4df837a),core/audience.PreferenceRegistry
渠道×关系矩阵与投递过滤(四族分类、Enroll 门强制、发送前复核)✅ 落地批次 4(4b18b4a),core/audience.Filter;expandRef 挂接已随批次 11 贯通
投递审计补齐(变更流水、去重折叠明细、关系快照进任务与日志)✅ 落地批次 5(de05e94),core/audit + SetRecorder/SetFoldAudit
Digest 时间窗聚合(窗口翻转、豁免裁决、摘要模板、翻转循环与可选租约)✅ 落地批次 6(3204e90),core/digest + SetDigest/FlushDigest
RSS 拉式渠道(公共/私密 feed、读取时可见性、拉式投影)✅ 落地批次 7,core/feeds + /feeds/** 端点 + SetFeeds 投影;token/关系注册表随批次 8 已共享接线
来源适配器(bot /start /stop、公众号关注事件、应用内勾选、取关回流全停、外部状态对账)✅ 落地批次 8,core/audience.SourceAdapter/Reconciler + api 三个入口端点 + telegram/wechatmp probe
去重与频控(事件幂等、折叠账本、状态机、三档频控与品类默认/覆盖表)✅ 落地批次 10(3c6c395+ca6ee39),core/dedup.Gate + config.DedupConfig 覆盖表
强度与模式(强度阶梯、紧急度区间、电话双重同意门、三模式执行器)✅ 落地批次 9(0c20459),core/audience.DeliveryPolicy/BuildPlan/RunPlan;触发面接线已随批次 11 贯通
集成者 API 与 Go SDK✅ 落地批次 11 增量一~八,api/handler_apps.go(config/trigger/query 三面 + events 适配面 + callback)+ apps-sdk/go + docs/guide/integration.md;阶段③集成方对接验证(0096be4)闭环

在途能力的配置项与端点尚不存在,勿据本文档配置生产;落地一批,本文状态表与对应详设同步更新一批。(截至 2026-10-08,§15 批次 1-11 与三阶段验证已全部落地,本表无在途行。)

14. 沿革 ​

Phase 0-9 管道改造的原计划书(「Audience 领域模型:架构改进计划书」)已收官,执行记录与留批拍板在现状审计存档,计划原文由 git 历史保存。2026-10-05 立项受众层扩展(统一订阅与投递中枢),2026-10-07 本页由计划书重写为现行模型总纲;关系详设承接全部新增能力的逐项设计。