# 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 路由 - 后续可扩展到多渠道 同时复杂度仍控制在较低水平,适合作为第一阶段正式实现。