1
0
forked from erp-dev/erp
Files
erpnew/docs/notifier_module_design.md
2026-04-14 00:03:22 +08:00

211 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` 已具备:
- 通知器配置
- 路由配置
- 分类分发
- 任务事件按分类路由
- 管理后台操作支持
- 日志与测试保障
这已经足以支撑你当前提出的“所有任务事件都支持按任务分类路由”的要求。