1
0
forked from erp-dev/erp
Files
erpnew/docs/notifier_module_design.md
2026-04-13 12:30:33 +08:00

230 lines
6.4 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. 背景与目标
项目原有的通知能力主要以企业微信机器人为主,并且配置集中在 `settings.py` 中,属于静态配置方案。
新的 `mission` 模块希望从一开始就采用:
- 独立 Django app
- task 化投递
- 后台可配置
- signal 与 notifier 动态绑定
- 为后续增加其它通知渠道预留统一接口
因此本次新增独立模块 `notifier`,并先将 `mission` 的新信号通知接入该模块。
## 2. 当前范围
本次实现属于 Phase 1范围有意收敛
- 已新增独立 app`notifier`
- 已支持后台配置 `Notifier`
- 已支持按 `event_key` + `merchant` 动态匹配通知器
- 已支持 Celery task 化派发
- 已支持模板化内容渲染
- 已支持第一种渠道:企业微信机器人 webhook
- 已接入 `mission` 的 6 个业务信号
本次**没有**做的内容:
- 没有改造旧模块的静态通知逻辑
- 没有引入通知订阅/端点拆表
- 没有引入通知投递明细表(如 `NotificationDelivery`
- 没有做数据库级别审计
- 没有提供对外 API
## 3. 核心模型
当前模型只有一个主模型:`notifier.Notifier`
字段职责如下:
- `merchant`: 多商户隔离
- `name`: 通知器名称,仅要求在同商户内唯一
- `event_key`: 事件标识,用于和业务 signal 对接
- `channel`: 通知渠道,当前仅实现 `wecom_webhook`
- `template_key`: 模板标识,对应固定目录中的模板文件
- `is_enabled`: 启用/停用
- `config`: 渠道配置,当前主要存放企业微信 webhook key、msgtype、timeout 等
- `description`: 备注
当前将 `event_key` 直接放在 `Notifier` 上,而没有拆成“事件订阅 + 通知端点”两层,原因是现阶段追求低复杂度、可快速上线。
后续如果一个通知端点需要订阅多个事件,或者一个事件需要更复杂的启停/优先级/路由策略,再考虑拆模。
## 4. 目录结构
关键文件如下:
- `notifier/models.py`
- `notifier/admin.py`
- `notifier/services.py`
- `notifier/tasks.py`
- `notifier/backends.py`
- `notifier/registry.py`
- `notifier/templates/notifier/events/`
模板固定目录为:
`notifier/templates/notifier/events/`
当前已提供的模板:
- `mission_created.md`
- `mission_replied.md`
- `mission_completed.md`
- `mission_reply_rejected.md`
- `mission_reopened.md`
- `mission_cancelled.md`
`template_key` 与模板文件名一一对应,例如:
- `template_key="mission_created"`
- 模板路径 `notifier/events/mission_created.md`
## 5. 调用链路
当前通知链路为:
1. `mission.services` 在事务提交后发送业务 signal
2. `mission.handlers` 监听 signal
3. handler 将业务对象整理为纯字典 payload
4. handler 调用 `notifier.services.enqueue_notification_event(...)`
5. notifier 通过 Celery task 异步执行投递
6. task 内部调用 `dispatch_notification_event(...)`
7.`merchant_id + event_key + is_enabled=True` 查询匹配的 `Notifier`
8. 逐个渲染模板并调用对应 backend 的 `notify(...)`
9. 写详细日志
这里有两个关键约束:
- handler 只做 payload 组装和入队,不做实际发送
- task 层才做真正的通知投递
这样可以保持业务事务与外部通知解耦。
## 6. 渠道抽象
当前 backend 接口约定为:
- `BaseNotifierBackend.notify(notifier, content, context) -> dict`
当前已实现:
- `WeComWebhookNotifierBackend`
其复用了现有工具:
- `api_v1.utils.wecom_webhook.send_wecom_webhook_message`
这样做的原因:
- 避免重复实现 webhook 发送逻辑
- 保持旧工具可复用
- 新模块只负责“编排”和“动态配置”
## 7. Mission 已接入事件
当前 `mission` 已接入以下事件:
- `mission.created`
- `mission.replied`
- `mission.completed`
- `mission.reply_rejected`
- `mission.reopened`
- `mission.cancelled`
对应 handler 在:
- `mission/handlers.py`
当前 handler 不再只是打日志,而是会构造 payload 并投递到 notifier task。
## 8. Admin 配置方式
`Notifier` 已接入 Django Admin可进行
- 添加
- 编辑
- 删除
- 启用/停用
当前推荐的使用方式:
1. 在 admin 中新建 `Notifier`
2. 选择所属商户
3. 选择 `event_key`
4. 选择 `channel=wecom_webhook`
5. 填写 `template_key`
6.`config` 中填写 webhook key 等参数
7. 启用 `is_enabled`
当前 `config` 示例:
```json
{
"key": "企业微信机器人key",
"msgtype": "markdown",
"timeout_seconds": 10
}
```
## 9. 日志策略
本阶段没有引入数据库投递明细表,因此发送明细主要依赖日志。
当前日志覆盖以下节点:
- 任务入队
- backend 发送成功
- 单个 notifier 发送成功
- 单个 notifier 发送失败
- 某事件无匹配 notifier
这满足当前“先可用、后增强”的目标,也符合“暂不做数据库级审计”的约束。
## 10. 当前风险与注意点
### 10.1 配置合法性主要依赖管理规范
当前 `config` 是自由 JSON没有做更强的结构化校验。
优点是灵活,缺点是后台录入错误会在发送时才暴露。
### 10.2 模板标识依赖文件存在
`template_key` 对应的模板文件如果不存在,会在发送阶段报错并记录日志。
这在当前阶段是可接受的,但后续可以考虑在 admin 或 model clean 中增加校验。
### 10.3 目前仍是“单对象订阅”模型
一个 `Notifier` 对应一个 `event_key`
如果后续出现“一个群同时订阅多个事件”的强需求,可以考虑抽象出 Subscription 层。
### 10.4 旧通知逻辑尚未迁移
当前仅 `mission` 新通知走 `notifier`
`printing``shipment` 等旧逻辑仍保留原来的静态方式,不应在本次改动中混改。
## 11. 后续建议
按优先级建议如下:
1. 在 admin 使用中观察 `config``template_key` 是否已足够稳定
2. 若 notifier 数量增多,再决定是否拆分“通知端点”与“事件订阅”
3. 若需要追踪投递历史,再增加 `NotificationDelivery`
4. 当 mission 通知稳定后,再考虑逐步迁移新业务节点到 notifier
## 12. 当前结论
当前方案已经满足:
- 独立模块
- admin 配置
- task 化通知
- 模板化内容
- 动态 signal -> notifier 路由
- 后续可扩展到多渠道
同时复杂度仍控制在较低水平,适合作为第一阶段正式实现。