Open edX 通知系统(Notifications)架构与实践指南 Open edX 通知系统Notifications架构与实践指南【免费下载链接】openedx-platformThe Open edX LMS Studio, powering education sites around the world!项目地址: https://gitcode.com/GitHub_Trending/ed/openedx-platform导读Open edX 的通知系统Notifications为 LMS 与 Studio 提供了面向学习者的站内通知能力用户在讨论区回复、被点赞、收到课程更新、获得 ORA 作业评分时系统会按通知类型与用户偏好通过 Web 托盘、Email、Push 三种渠道触达用户。本文以仓库openedx/core/djangoapps/notifications应用为主线从通知类型/应用配置、数据模型、事件驱动发送链路、REST API、邮件摘要调度、管理命令与运维配置七个层面展开帮助你理解系统全貌并能在此基础上自定义通知、接入新事件或完成生产环境部署调优。1. 系统概览与数据流通知系统以 Django app 的形式内置于 Open edX注册于 LMS 的INSTALLED_APPS见 lms/envs/common.py其核心职责是接收来自各业务模块讨论区、ORA、课程更新等的事件信号结合每个用户的通知偏好与受众过滤器生成通知记录再按渠道分发给用户。仓库中给出了官方的数据流架构图architecture.md完整描述了从事件触发到通知投递的三大阶段触发层Platform/app workflowORA 提交、课程更新、帖子/回复创建、回复被回复、评分分配等业务事件构建层Creating notification context事件处理器如 ORA Submission Handler、Course Update Handler将事件转换成「通知类型 内容模板上下文」分发层Dispatching notification系统按用户偏好、受众过滤器筛选目标用户生成Notification记录再交付到各渠道。2. 通知应用与通知类型系统的「配置中心」系统的行为几乎全部由两份声明式配置驱动分别定义在 base_notification.py 中2.1 通知应用Notification App_COURSE_NOTIFICATION_APPS定义了 3 个通知应用discussion、updates、grading每个应用是通知类型的逻辑分组并带有分组级别的渠道默认配置应用说明infowebemailpushemail_cadencediscussion关于帖子/回复/评论及关注内容、点赞的通知TrueTrueTrueDailyupdates课程团队的新公告与更新通知TrueTrueTrueDailygrading提交内容评分相关通知TrueTrueTrueDaily每个NotificationApp配置项base_notification.py包含enabled是否启用该应用及其关联的通知类型info展示给用户的应用描述用gettext_lazy包裹以支持翻译web/email/push分组通知在各渠道是否投递push注释明确说明「推送通道尚未完整实现」需由 waffle 开关开启email_cadence分组通知的邮件节奏Daily/Weekly/Immediately/Nevernon_editable禁止用户修改的渠道列表。2.2 通知类型Notification Type_COURSE_NOTIFICATION_TYPES定义了当前仓库内置的15 种通知类型每个类型通过notification_app关联到某个应用通知类型所属应用use_app_defaults典型触发场景new_comment_on_responsediscussionTrue有人评论了你对帖子的回复new_commentdiscussionTrue有人评论了你帖子的回复new_responsediscussionTrue有人回复了你的帖子new_discussion_postdiscussionFalse有人发布了新讨论帖new_question_postdiscussionFalse有人发布了新问题帖response_on_followed_postdiscussionTrue你关注的帖子有了新回复comment_on_followed_postdiscussionTrue你关注的帖子有了新评论content_reporteddiscussionFalse内容被举报仅管理员/版主/社区TA可见response_endorsed_on_threaddiscussionTrue你的帖子中的回复被点赞认可response_endorseddiscussionTrue你的回复被点赞认可course_updatesupdatesFalse课程团队发布了课程更新ora_staff_notificationsgradingFalseORA 有新的待评分提交仅课程员工/讲师可见ora_grade_assignedgradingFalseORA 评分已出ora_remindergradingFalseORA 有未完成的自评/互评步骤提醒new_instructor_all_learners_postdiscussionFalse讲师向全体学习者发布了帖子每个通知类型的字段base_notification.pynotification_app所属应用必须是应用配置的 keyname类型唯一标识use_app_defaults为True时继承所属应用的默认偏好覆盖类型自身设置的web/email/push/email_cadence/non_editable并在偏好 API 中聚合为「分组通知」content_template通知内容模板用gettext_lazy包裹支持翻译支持{p}、{strong}等结构标签与变量占位符如{replier_name}、{post_title}grouped_content_template分组展示时的模板如new_response的「{replier_name} and others have responded to your post」content_context模板可用变量的说明仅文档用途不参与渲染filters发送前应用的受众过滤器列表如filter_audit_expired_users_with_no_rolevisible_to可见性角色列表如content_reported仅对论坛管理员/版主/社区 TA 可见ora_staff_notifications仅对课程员工/讲师可见non_editable锁定用户不可修改的渠道。2.3 内容渲染与安全get_notification_contentbase_notification.py负责将模板与上下文渲染为最终内容。值得注意的细节渲染时会对除结构标签p、strong之外的所有上下文值执行 HTML 转义_STRUCTURAL_CONTEXT_KEYS防止用户输入的帖子标题、用户名注入 HTML因为通知内容在摘要邮件模板中以|safe渲染存在兼容逻辑course_update会被映射为course_updates若通知类型配置了上下文转换函数get_notification_type_context_function会先经其加工再渲染。3. 数据模型三个核心表模型定义在 models.py包含三张核心表migrations 见 migrations3.1Notification通知记录user外键级联删除、course_idCourseKeyField可空、app_name索引、notification_typecontent_contextJSONField保存模板渲染所需的上下文数据、content_urlweb/email/push三个布尔字段分别标记该通知在各渠道的投递开关默认webTrueemailFalsepushFalselast_read/last_seen已读/已见时间戳group_by_id分组 ID用于通知分组去重如讨论区同一帖子多条回复的聚合email_sent_on/email_scheduled邮件发送时间与「是否已排入摘要缓冲」标记二者连同user、course_id组成notif_email_buffer_idx复合索引服务于邮件缓冲查询content属性通过get_notification_content实时渲染内容。3.2NotificationPreference账号级用户偏好每个用户在每个user、app、type组合上唯一一条偏好记录unique_together渠道字段web/push/email与email_cadence可选Daily/Weekly/Immediatelyis_grouped属性当类型use_app_defaultsTrue时视为分组通知create_default_preferences_for_user批量补齐用户缺失的通知类型偏好bulk_create需注意新记录没有主键如需主键需重新查询is_enabled_for_any_channel/get_channels_for_notification_type发送决策时判断「任一渠道开启」并返回启用的渠道列表。3.3DigestSchedule摘要任务去重表记录user、cadence_type、delivery_time唯一的待执行摘要 Celery 任务是 Daily/Weekly 摘要调度的去重事实来源与Notification.email_scheduled职责分离后者面向「立即/缓冲」流程按通知行粒度工作前者按任务粒度工作仅用于每日/每周摘要。3.4 用户偏好自动初始化新用户注册时post_save信号处理器create_user_account_preferenceshandlers.py会在transaction.atomic中为全部通知类型批量创建默认偏好并通过ignore_conflictsTrue与IntegrityError捕获保证幂等若迁移未执行还会兜底捕获ProgrammingError。4. 事件驱动从业务事件到通知生成通知系统的入口是 Open edX Events 平台的两个信号handlers.py4.1 用户级通知USER_NOTIFICATION_REQUESTEDgenerate_user_notifications收到信号后将notification_data序列化course_key 转字符串直接调用send_notifications.delay(**notification_data)异步任务。4.2 课程级通知COURSE_NOTIFICATION_REQUESTEDgenerate_course_notifications的处理更复杂调用calculate_course_wide_notification_audience计算受众若无受众过滤器默认取该课程的全部有效选课用户CourseEnrollment.objects.filter(course_id..., is_activeTrue)若带受众过滤器则按过滤器类型分发到 5 种过滤器实现AUDIENCE_FILTER_CLASSES见 audience_filters.pydiscussion_roles→ForumRoleAudienceFilter论坛角色如管理员/版主/社区 TA/组长/学生course_roles→CourseRoleAudienceFilter课程员工/讲师角色enrollments→EnrollmentAudienceFilterteams→TeamAudienceFilter课程团队cohorts→CohortAudienceFilter学习小组从受众中剔除发送者本人sender_id组装notification_data含course_key、user_ids、context、app_name、notification_type、content_url调用send_notifications.delay。5. 发送核心send_notifications任务的完整链路send_notificationstasks.py是系统的中枢任务其执行流程如下总开关检查若 waffle 开关notifications.disable_notifications开启直接返回有效性校验is_notification_valid尝试渲染内容模板失败则抛出ValidationError批处理按NOTIFICATION_CREATION_BATCH_SIZE默认 76分批处理用户 ID避免大课程一次性载入过多用户受众过滤每批用户先经NotificationFilter().apply_filters如剔除已过期的审计用户等分组检查若存在group_by_id且该类型注册了分组器NotificationRegistry.get_grouper则查询用户已有通知调用group_user_notifications合并分组分组器注册见 grouping_notifications.py 的NotificationRegistry.register偏好决策读取用户的NotificationPreference缺少记录且类型默认开启 web 时自动补齐对每个偏好判断任一渠道启用 → 构造Notification对象并批量入库bulk_createemail开启且节奏为Immediately→ 进入立即邮件名单email开启且节奏为Daily/Weekly→ 进入摘要调度名单push 开启且课程级开关notifications.enable_push_notifications生效 → 进入推送名单渠道分发立即邮件调用send_immediate_cadence_email摘要邮件调用schedule_bulk_digest_emails批量调度延迟 Celery 任务推送调用send_ace_msg_to_push_channel走 ACE push 通道事件上报notification_generated_event发出通知生成事件供分析与埋点使用。6. REST API前端托盘的读写接口API 挂在 LMS 的/api/notifications/路径下见 lms/urls.py路由定义于 urls.py视图实现于 views.py方法与路径视图说明GET /api/notifications/NotificationListAPIView分页列出当前用户的通知支持app_name过滤带tray_opened参数时上报托盘打开事件只返回webTrue且创建时间在NOTIFICATIONS_EXPIRY之内的记录GET /api/notifications/count/NotificationCountView返回未读数count、按应用分组的count_by_app_name、show_notifications_tray由 waffle 开关决定与notification_expiry_daysPATCH /api/notifications/mark-seen/app_name/MarkNotificationsSeenAPIView将指定应用的所有未读通知标记为「已见」last_seenPATCH /api/notifications/read/NotificationReadAPIView按notification_id标记单条已读或按app_name批量标记已读last_read并上报读事件GET / PUT /api/notifications/v3/configurations/NotificationPreferencesViewV3读取/更新用户的全部偏好配置GET 返回按应用组织的结构化偏好含grouped_notification聚合项、enabled、non_editable并会通过create_account_notification_pref_if_not_exists自动补齐缺失类型PUT 支持更新单渠道web/push/email或email_cadence对grouped_notification会批量更新该应用下所有use_app_defaultsTrue的类型GET / POST /api/notifications/preferences/update/username/preference_update_from_encrypted_username_view基于加密用户名的一键退订入口受ONE_CLICK_UNSUBSCRIBE_RATE_LIMIT限流保护权限装饰器为allow_any_authenticated_user即所有已认证用户可用见 permissions.py。此外filter_out_visible_notifications与get_notification_types_with_visibility_settingsutils.py会在返回偏好前依据当前用户的论坛角色与课程角色剔除无权看到的类型如content_reported、ora_staff_notifications。7. 前端通知托盘Notification Tray启用托盘的方式记录在 notification_tray.md前端需要启用通知托盘以简化用户体验用户点击头部铃铛图标即可打开托盘查看上述应用产生的通知。托盘「是否展示」由后端show_notifications_tray标志驱动该标志来自get_show_notifications_trayutils.py本质是notifications.disable_notificationswaffle 开关的反向判断——只要总开关未关闭托盘即可展示。托盘通过上节 API 完成未读计数、标记已读/已见与偏好设置。8. 邮件通知即时邮件与每日/每周摘要邮件子系统位于 email/节奏定义在 email_notifications.py 的EmailCadenceDaily每日摘要默认每天 17:00 UTC 投递Weekly每周摘要默认每周一 17:00 UTC 投递Immediately即时邮件Never不发送。核心调度逻辑email/tasks.pyschedule_bulk_digest_emails批量调度摘要任务每个用户、节奏、投递时间只调度一次借助DigestSchedule去重并将窗口内的Notification.email_scheduled批量置位文档注释说明全程只需约 3 条查询/节奏类型规避 N1 问题send_immediate_cadence_email对Immediately节奏的用户立即发送单条通知邮件send_buffered_digest缓冲摘要发送任务兼容性保护is_digest_already_sent_in_window防止 cron 与延迟任务并存时重复发信。邮件模板位于 templates/notifications/edx_ace分为batched_email批量邮件、email_digest摘要邮件、immediate_email即时邮件、push四类均按 ACE 规范提供body.html、body.txt、subject.txt、head.html、from_name.txt。摘要邮件内容由 digest_content.html、digest_header.html、digest_footer.html 等组件拼接。注意管理命令send_email_digest已标记为DEPRECATEDsend_email_digest.py摘要邮件现已改为在通知创建时自动调度延迟 Celery 任务建议移除调用该命令的 cron 任务。9. 管理命令运维工具箱management/commands/提供了 5 个运维命令删除过期通知python manage.py lms delete_expired_notifications调用delete_expired_notifications任务按NOTIFICATIONS_EXPIRY天数批量删除过期记录每批EXPIRED_NOTIFICATIONS_DELETE_BATCH_SIZE条适合挂 cron 定期清理。条件删除通知python manage.py lms delete_notifications --app_name app --notification_type type --created date [--course_id key]按应用、类型与创建时间范围删除--created支持YYYY-MM-DD可用~指定起止区间最长 15 天例如python manage.py lms delete_notifications --app_name discussion --notification_type new_comment --created 2024-01-01~2024-01-15。批量修改用户偏好python manage.py update_notification_preference app type channel value [--user_ids ...] [--dry-run]例如python manage.py update_notification_preference discussion new_comment_on_response email false--dry-run可先预览影响范围。app/type/channel均受配置校验。修复脏数据python manage.py [lms] fix_mixed_email_cadence [--fix]默认 dry-run 统计email_cadenceMixed的非法记录迁移期遗留加--fix统一替换为Daily。发送摘要已弃用python manage.py lms send_email_digest Daily|Weekly仅保留向后兼容运行会输出弃用警告。10. Waffle 开关与 Django 设置10.1 Waffle 开关config/waffle.py开关类型默认作用notifications.disable_notificationsWaffleFlagFalse开启后整体禁用通知功能托盘隐藏、发送任务直接返回notifications.disable_email_notificationsWaffleFlagFalse开启后禁用邮件通知隐藏邮件偏好入口notifications.enable_push_notificationsCourseWaffleFlagFalse课程级开关开启后通知走 ACE push 通道临时性开关10.2 关键 Django 设置定义于 openedx/envs/common.py设置项默认值说明NOTIFICATIONS_EXPIRY60通知过期天数以 UTC 计算列表 API 与过期删除任务均以此为准EXPIRED_NOTIFICATIONS_DELETE_BATCH_SIZE10000过期通知每批删除条数NOTIFICATION_CREATION_BATCH_SIZE76生成通知时每批处理的用户数NOTIFICATIONS_DEFAULT_FROM_EMAILno-replyexample.com通知邮件默认发件人NOTIFICATION_DIGEST_LOGODEFAULT_EMAIL_LOGO_URL摘要邮件 LogoNOTIFICATION_IMMEDIATE_EMAIL_BUFFER_MINUTES15即时邮件缓冲窗口分钟NOTIFICATION_TYPE_ICONS/DEFAULT_NOTIFICATION_ICON_URL{}/各通知类型图标映射与默认图标NOTIFICATION_DAILY_DIGEST_DELIVERY_HOUR/MINUTE17/0每日摘要投递时间UTCNOTIFICATION_WEEKLY_DIGEST_DELIVERY_DAY/HOUR/MINUTE0周一/17/0每周摘要投递时间UTC0周一NOTIFICATION_APPS_OVERRIDE/NOTIFICATION_TYPES_OVERRIDE{}覆盖通知应用/类型的默认渠道配置见下文10.3 用 Settings 覆盖默认偏好NOTIFICATION_APPS_OVERRIDE与NOTIFICATION_TYPES_OVERRIDE是平台运营方定制通知行为的主要入口实现见 settings_override.py测试见 test_settings_override.py。两者在get_notification_types_config()/get_notification_apps_config()中以深拷贝 白名单键合并的方式应用于默认配置COURSE_NOTIFICATION_TYPES与COURSE_NOTIFICATION_APPS实际是覆盖后的结果。允许覆盖的键仅限web、email、push、non_editable、email_cadence。示例将course_updates类型的邮件节奏改为每周并锁定 web 渠道不可由用户修改NOTIFICATION_TYPES_OVERRIDE { course_updates: { email_cadence: Weekly, non_editable: [web], }, }示例关闭grading应用的推送渠道NOTIFICATION_APPS_OVERRIDE { grading: { push: False, }, }11. 测试覆盖与扩展方向仓库为通知系统提供了较完整的测试集tests/、email/tests、push/tests、management/teststest_base_notification.py通知类型/应用配置与内容渲染test_filters.py受众过滤与NotificationFiltertest_handlers.py事件信号处理链路test_notification_grouping.py分组逻辑test_settings_override.py配置覆盖机制test_tasks_with_account_level_pref.py账号级偏好下的任务批处理行为含分片数量断言test_views.pyAPI 行为与过期边界。如需新增一种通知可按如下路径扩展当前仓库源码即遵循该模式在_COURSE_NOTIFICATION_TYPES中注册类型与content_template→ 在_COURSE_NOTIFICATION_APPS中确认/新增所属应用 → 通过COURSE_NOTIFICATION_REQUESTED或USER_NOTIFICATION_REQUESTED事件信号触发 → 需要分组时用NotificationRegistry.register注册分组器 → 需要定向受众时复用AUDIENCE_FILTER_CLASSES中的过滤器。12. 小结Open edX 的通知系统是一个「配置驱动 事件驱动」的完整闭环声明式配置应用/类型/渠道/节奏决定行为边界Open edX Events 信号负责接入业务事件send_notifications任务完成偏好决策、受众过滤、分组与多渠道分发REST API 与邮件摘要/托盘机制承担用户交互与触达waffle 开关与NOTIFICATION_*_OVERRIDE设置则交给平台运营方做灰度与定制。理解这张配置表与发送链路是定制 Open edX 通知体验的最短路径。【免费下载链接】openedx-platformThe Open edX LMS Studio, powering education sites around the world!项目地址: https://gitcode.com/GitHub_Trending/ed/openedx-platform创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考