forked from erp-dev/erp
211 lines
5.1 KiB
Markdown
211 lines
5.1 KiB
Markdown
# Notifier 模块实现说明
|
||
|
||
本文档面向后端维护者,描述 `notifier` 模块当前的实现决策、已落地范围与后续扩展注意事项。
|
||
|
||
## 1. 当前阶段
|
||
|
||
`notifier` 现已从第一阶段的“`Notifier` 直接绑定 `event_key`”升级为第二阶段的“`Notifier + NotifierRoute`”结构。
|
||
|
||
这样做的原因是:
|
||
|
||
- 一个通知器可能需要绑定多个事件
|
||
- 同一事件需要支持按任务分类路由
|
||
- 同一事件可能同时发往多个不同通知器
|
||
- 后续还可能出现更多路由维度
|
||
|
||
因此当前模块职责被拆成两层:
|
||
|
||
- `Notifier`:通知渠道配置、模板配置、启停控制
|
||
- `NotifierRoute`:事件匹配与业务路由规则
|
||
|
||
## 2. 当前范围
|
||
|
||
本阶段已实现:
|
||
|
||
- 独立 app:`notifier`
|
||
- Celery task 化投递
|
||
- 企业微信 webhook 渠道
|
||
- 模板化内容渲染
|
||
- Admin 可配置
|
||
- `NotifierRoute` 事件路由
|
||
- `mission` 相关事件按任务分类路由
|
||
|
||
本阶段仍未做:
|
||
|
||
- 旧模块静态通知逻辑迁移
|
||
- 外部 API
|
||
- 通知投递明细表
|
||
- 数据库级别审计
|
||
|
||
## 3. 核心模型
|
||
|
||
### 3.1 Notifier
|
||
|
||
`Notifier` 负责“怎么发”:
|
||
|
||
- `merchant`
|
||
- `name`
|
||
- `channel`
|
||
- `template_key`
|
||
- `is_enabled`
|
||
- `config`
|
||
- `description`
|
||
|
||
当前 `Notifier` 已不再直接持有 `event_key`。
|
||
|
||
### 3.2 NotifierRoute
|
||
|
||
`NotifierRoute` 负责“何时发、发给谁”:
|
||
|
||
- `merchant`
|
||
- `notifier`
|
||
- `event_key`
|
||
- `mission_category`
|
||
- `is_enabled`
|
||
- `description`
|
||
|
||
其中:
|
||
|
||
- `mission_category = null` 表示该事件的通配路由
|
||
- `mission_category != null` 表示任务分类专用路由
|
||
|
||
### 3.3 约束设计
|
||
|
||
当前约束:
|
||
|
||
- `Notifier` 在同商户下 `name` 唯一
|
||
- `NotifierRoute` 在同一 `notifier + event_key + mission_category` 下唯一
|
||
- `NotifierRoute` 额外限制同一 `notifier + event_key` 只能有一条通配路由
|
||
|
||
## 4. 路由匹配规则
|
||
|
||
当前 `dispatch_notification_event(...)` 的匹配规则为:
|
||
|
||
1. 先按 `merchant + event_key + route.is_enabled=True + notifier.is_enabled=True` 查路由
|
||
2. 如果 payload 中带有 `category_id`
|
||
- 匹配该分类的专用路由
|
||
- 也允许匹配通配路由
|
||
3. 如果 payload 中没有 `category_id`
|
||
- 只匹配通配路由
|
||
4. 如果同一个 `Notifier` 同时命中专用路由和通配路由
|
||
- 只保留一条
|
||
- 优先保留专用路由
|
||
|
||
这样可以同时满足:
|
||
|
||
- 分类专用通知
|
||
- 默认兜底通知
|
||
- 多群并发通知
|
||
- 同一通知器不重复发送
|
||
|
||
## 5. 当前调用链
|
||
|
||
通知链路如下:
|
||
|
||
1. `mission.services` 在事务提交后发 signal
|
||
2. `mission.handlers` 构造 payload
|
||
3. payload 中已包含 `category_id`、`category_name`
|
||
4. handler 调用 `enqueue_notification_event(...)`
|
||
5. Celery task 调用 `dispatch_notification_event(...)`
|
||
6. notifier 根据 route 匹配命中的 `Notifier`
|
||
7. 渲染模板并调用 backend 发送
|
||
8. 记录路由日志和发送日志
|
||
|
||
## 6. 已接入的事件
|
||
|
||
当前 `mission` 已接入:
|
||
|
||
- `mission.created`
|
||
- `mission.replied`
|
||
- `mission.completed`
|
||
- `mission.reply_rejected`
|
||
- `mission.reopened`
|
||
- `mission.cancelled`
|
||
|
||
这些事件全部支持按任务分类路由。
|
||
|
||
## 7. 日志策略
|
||
|
||
当前日志覆盖以下节点:
|
||
|
||
- 事件入队
|
||
- 没有命中任何可用路由
|
||
- 路由命中成功
|
||
- backend 发送成功
|
||
- 单个通知发送失败
|
||
|
||
当前已增加 route 维度日志,重点字段包括:
|
||
|
||
- `event_key`
|
||
- `merchant_id`
|
||
- `route_id`
|
||
- `route_mission_category_id`
|
||
- `notifier_id`
|
||
|
||
## 8. Admin 现状
|
||
|
||
当前后台提供两个对象:
|
||
|
||
- `Notifier`
|
||
- `NotifierRoute`
|
||
|
||
并且:
|
||
|
||
- `Notifier` 页面支持 inline 维护其下路由
|
||
- `NotifierRoute` 也支持单独管理
|
||
|
||
在 `Notifier` inline 场景下,route 的 `merchant` 会自动同步为当前 notifier 的商户,避免管理人员重复录入。
|
||
|
||
## 9. 迁移策略
|
||
|
||
本次从旧结构迁到新结构时,做了自动回填:
|
||
|
||
- 对每条旧 `Notifier(event_key=...)`
|
||
- 自动创建一条 `NotifierRoute`
|
||
- `event_key` 原样继承
|
||
- `mission_category = null`
|
||
- `description` 标记为自动迁移生成
|
||
|
||
这样旧配置不会因为结构调整而丢失。
|
||
|
||
## 10. 测试覆盖重点
|
||
|
||
当前测试已覆盖:
|
||
|
||
- 模板渲染
|
||
- backend 发送
|
||
- 路由按事件匹配
|
||
- 分类专用路由优先于通配路由
|
||
- 无专用路由时回退到通配路由
|
||
- 任务事件 handler 正常入队
|
||
|
||
## 11. 当前风险与注意点
|
||
|
||
### 11.1 当前只对 mission 做了分类路由
|
||
|
||
当前 `mission_category` 是显式写进 `NotifierRoute` 的业务字段。
|
||
这适合当前目标,但如果未来要扩展到其它业务模型的复杂路由,可能需要更抽象的路由条件模型。
|
||
|
||
### 11.2 config 仍是自由 JSON
|
||
|
||
后台录入错误仍可能在发送时才暴露。
|
||
当前依赖日志排查,后续可增加更强校验。
|
||
|
||
### 11.3 模板校验仍在发送期暴露
|
||
|
||
`template_key` 如果写错,会在渲染阶段报错并记录日志。
|
||
后续可在 admin 或 model clean 中增强校验。
|
||
|
||
## 12. 当前结论
|
||
|
||
当前 `notifier` 已具备:
|
||
|
||
- 通知器配置
|
||
- 路由配置
|
||
- 分类分发
|
||
- 任务事件按分类路由
|
||
- 管理后台操作支持
|
||
- 日志与测试保障
|
||
|
||
这已经足以支撑你当前提出的“所有任务事件都支持按任务分类路由”的要求。
|