尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Open edX 证书颁发与吊销流程全解析:基于 edx-platform 信号、Celery 与 Credentials IDA 的架构图解

Open edX 证书颁发与吊销流程全解析:基于 edx-platform 信号、Celery 与 Credentials IDA 的架构图解 Open edX 证书颁发与吊销流程全解析基于 edx-platform 信号、Celery 与 Credentials IDA 的架构图解【免费下载链接】openedx-platformThe Open edX LMS Studio, powering education sites around the world!项目地址: https://gitcode.com/GitHub_Trending/ed/openedx-platform本文以 Open edX 核心仓库 openedx-platform 中 certificates 应用文档 为骨架结合其配套的 Structurizr DSL 架构图颁发流程 与 吊销流程以及真实源码实现完整讲解课程证书Course Certificate与项目证书Program Certificate从生成到吊销的端到端链路。读完本文你将掌握证书系统所依赖的信号清单、Celery 任务调用链、与 Credentials IDA 的同步机制并能根据这套流程在仓库中快速定位代码、排查问题。为什么需要这张证书流程图Open edX 的证书管理工作流并不集中在一个应用里而是横跨了 edx-platform 中的多个 Django app成绩Grades、身份验证Verify Student、学生与注册Student、证书本身Certificates、项目Programs以及 Credentials IDA。正如 docs/diagrams/README.rst 所指出Certificate management workflows are spread across a few different apps of edx-platform and can be hard to trace and understand——证书的颁发awarding与吊销revoking流程分散在多处维护者很难追踪全局。因此该文档文件夹提供了两幅高层级可视化图由 Structurizr DSL 编写、可渲染成系统全景图用于帮助维护者理解证书颁发与吊销流程究竟如何运转它们与 Open edX 生态中其他组件尤其是Credentials IDA与Event Bus之间如何交互。这套流程的核心设计思想是信号驱动 异步任务 事件总线LMS 内部通过 Django Signal 解耦各业务模块真正耗时的工作交给 Celery 异步执行而证书状态的变化则通过 Open edX Events 发布到事件总线供 Credentials IDA 等外部服务消费。架构总览LMS 域与 Credentials IDA 域两幅 DSL 图都把参与者划分成两个大组LMSOpen edX 平台本体包含Grades Django app成绩Verify Student app身份验证Student app学生Certificates app 内部的三个组件Certificates Signal Handlers信号处理、Certificates Generation Handler生成处理器、Certificate AllowList白名单Credentials Django app 与 Programs Django AppLMS 侧集成组件Celery异步任务队列与 Database数据库Credentials IDA独立部署的凭证服务包含Credentials Event Bus Consumer事件总线消费者监听证书事件Credentials API Django App凭证 APICredentials Django App凭证数据两者的衔接点正是Event BusLMS 把证书生命周期事件发布出去Credentials IDA 的消费者监听后驱动自己的数据模型从而在不直接依赖 LMS 内部实现的情况下完成凭证的颁发与吊销。从 DSL 源码看certificate_generation.dsl第 6-31 行模型还使用了structurizr.groupSeparator /属性以及softwareSystem元素来组织各系统并在views段定义了systemLandscape视图与autolayout lr从左到右自动布局数据库元素被显式指定为Cylinder形状。证书颁发流程Certificate Generation详解第一步五个入口信号汇聚到 Signal Handlers证书生成并非只有一个触发点。根据 certificate_generation.dsl 第 33-38 行以下组件都会向 Certificates Signal Handlers 发送信号发送方信号业务含义Grades appCOURSE_GRADE_NOW_PASSED学习者成绩变为通过最常见触发路径Verify Student appIDV_ATTEMPT_APPROVED身份验证IDV尝试获批Verify Student appLEARNER_SSO_VERIFIED学习者通过 SSO 验证Verify Student appPHOTO_VERIFICATION_APPROVED照片验证获批Student appENROLLMENT_TRACK_UPDATED选课模式enrollment mode变更Certificate AllowListAPPEND_CERTIFICATE_ALLOWLIST学习者被加入证书白名单这些信号在 signals.py 中均有对应的receiver实现。以最核心的COURSE_GRADE_NOW_PASSED为例signals.py 第 71-96 行处理函数listen_for_passing_grade先检查自动证书生成是否开启auto_certificate_generation_enabled()再确认该用户当前不存在已是 passing 状态的证书最后调用generate_certificate_task(user, course_id)如果被CertificateGenerationNotAllowed拦截则记录日志并返回。身份验证类信号LEARNER_SSO_VERIFIED、PHOTO_VERIFICATION_APPROVED、IDV_ATTEMPT_APPROVED见 signals.py 第 119-159 行统一走_handle_id_verification_approved遍历该用户的全部选课逐一尝试为其生成证书——这对应着用户之后才完成身份验证、从而补发证书的场景。白名单路径则由post_save信号监听CertificateAllowlistsignals.py 第 57-68 行用户一旦被加入白名单立即调用generate_allowlist_certificate_task尝试生成白名单证书。第二步Generation Handler 的资格检查与任务分发所有信号最终都会汇聚到 generation_handler.py 的入口generate_certificate_task(user, course_key)第 37-53 行。它先判断用户是否在白名单上据此二选一白名单路径generate_allowlist_certificate_task第 56-82 行普通路径_generate_regular_certificate_task第 85-101 行。无论哪条路径真正决定能否生成的是三层检查普通证书专属检查_can_generate_regular_certificate第 163-185 行CCX 课程不生成、beta 测试者不生成、非通过成绩course_grade.passed为假不生成公共检查_can_generate_certificate_common第 188-233 行用户不在CertificateInvalidation失效名单上、存在有效选课、选课模式可获证书modes_api.is_eligible_for_certificate如 audit 模式不可获证、必要时已完成 ID 验证honor / professional-no-id 模式豁免、课程存在且已启用 HTML 证书状态检查_can_generate_certificate_for_status第 355-372 行若已有downloadable状态证书且当前选课模式并未从不可获证变为可获证则视为终态不再重复生成。值得注意的扩展点是_generate_certificate_task第 104-139 行在真正入队前会执行CertificateCreationRequested.run_filter(...)filter 类型org.openedx.learning.certificate.creation.requested.v1。这是 Open edX Filters 机制提供的钩子第三方插件可以借此在证书创建前拦截或改写参数若 filter 抛出PreventCertificateCreation则抛出CertificateGenerationNotAllowed任务不会入队。第三步Celery 异步生成与延迟策略通过资格检查后任务以如下方式入队generation_handler.py 第 138 行generate_certificate.apply_async(countdowndelay_seconds, kwargskwargs)其中delay_seconds默认取 tasks.py 中定义的CERTIFICATE_DELAY_SECONDS 2第 23 行。源码注释解释了延迟的动机证书生成会被延迟以防调用方仍在完成自己的改动例如针对 post_save 而非 post_commit 信号的证书再生成场景。实际的 Celery 任务generate_certificate定义在 tasks.py 第 26-57 行它继承了LoggedPersistOnFailureTask并配置了default_retry_delay30、max_retries2的失败重试策略。任务接受的 kwargs 及其默认值如下参数说明默认值student用户 ID必填—course_key课程 run 的 CourseKey 字符串必填—status证书状态取值见CertificateStatusesdownloadableenrollment_mode选课模式如verified必填—course_grade成绩快照generation_mode事件来源标记self用户自助或batch其余场景batch任务内部将参数解包后调用generate_course_certificate(...)位于 generation.py完成实际的可下载证书渲染与落库。第四步数据库持久化与事件广播证书写入数据库后DSL 图标注了两条关键路径certificate_generation.dsl 第 43-46 行database - event_bus: CERTIFICATE_CREATED数据库层发出CERTIFICATE_CREATED事件。在 models.py 第 536-538 行 附近可以看到CERTIFICATE_CREATED.send_event(...)的实现其事件类型为org.opedx.learning.certificate.created.v1一类的 Open edX Event 定义事件数据结构基于CertificateData/UserData/UserPersonalData。对应的测试位于 test_events.py其中明确验证了证书生成完成后CERTIFICATE_CREATED被发送并被接收方收到。database - programs_app: COURSE_CERT_CHANGED证书记录保存时向 Programs app 发出COURSE_CERT_CHANGED信号若成绩为通过还会附带发出COURSE_CERT_AWARDED信号第 46 行。这三个信号COURSE_CERT_CHANGED、COURSE_CERT_AWARDED、COURSE_CERT_REVOKED统一定义在 openedx/core/djangoapps/signals/signals.py 第 16-18 行。第五步Programs 与 Credentials IDA 的联动COURSE_CERT_AWARDED与COURSE_CERT_CHANGED在 LMS 侧的 Programs app 中由 programs/signals.py 消费handle_course_cert_awarded第 23-46 行在 Credentials 服务启用is_credentials_enabled()的前提下award_program_certificates.delay(user.username)异步检查该学习者是否还满足某个项目Program的全部课程要求若满足则为其颁发项目证书handle_course_cert_changed第 49-90 行使用transaction.on_commit确保事务提交后再调度award_course_certificate.delay(user.username, str(course_key))把课程证书的创建或更新同步到 Credentials IDA。源码注释特别强调了一个容易混淆的命名虽然任务名叫award_course_certificate它实际同时负责 Credentials 中课程证书的颁发与吊销两种操作见第 55-58 行的Important注释。DSL 图第 47-53 行随后展示了 Credentials 侧的完整链路programs_app --enqueue-- celery --process-- credentials_app --POST-- credentials_api_app即 Celery 执行award_course_certificate/award_program_certificate任务LMS 内的 credentials app 以POST 请求调用 Credentials IDA 的credentials_api_app颁发课程凭证或项目凭证与此同时Credentials IDA 的Event Bus Consumercredentials_ida_consumer - event_bus Listening for Certificate events第 45 行监听事件总线上的证书事件并在自己的credentials_credentials_app中落一条Award Certificate record。事件总线与 API 调用双通道并行保证了 LMS 状态与 Credentials IDA 状态的最终一致。证书吊销流程Certificate Revocation详解吊销流程图certificate_revocation.dsl结构上更精简去掉了验证、白名单等组件聚焦成绩失效 - 状态更新 - 事件广播 - 凭证吊销这条主线第一步成绩失效信号触发状态更新唯一的外部入口是grades_app - signal_handlers: Emits COURSE_GRADE_NOW_FAILEDDSL 第 29 行。对应实现是 signals.py 第 99-116 行 的_listen_for_failing_grade先检查用户是否在课程白名单上——如果在失败成绩不影响其证书直接返回否则获取该用户的GeneratedCertificate若证书处于 passing 状态则调用cert.mark_notpassing(mode..., grade..., sourcenotpassing_signal)把证书状态更新为NOT_PASSING。第二步状态落库与双重信号发出mark_notpassing实现在 models.py 第 380 行起清空error_reason、download_uuid、download_url等字段写入新的 grade 与 status 后save()随后做两件事发送 Django 信号COURSE_CERT_REVOKED.send_robust(...)第 409-415 行携带user、course_key、mode、status发送 Open edX 事件CERTIFICATE_REVOKED.send_event(...)第 419 行起事件类型org.openedx.learning.certificate.revoked.v1同样携带CertificateData结构含用户名、邮箱、姓名等 PII 字段。代码注释还提到一个细节grade 若为 float 需转成字符串以兼容CERTIFICATE_REVOKED事件定义。这两步对应 DSL 图第 30-31 行signal_handlers - database Update certificates status to NOT_PASSING、database - event_bus Emits a CERTIFICATE_REVOKED event。第三步Credentials IDA 事件消费与 API 吊销事件发出后credentials_ida_consumer监听到CERTIFICATE_REVOKED事件在credentials_credentials_app中执行Revoke Certificate recordDSL 第 32-33 行即把 Credentials IDA 侧的用户凭证一并作废。第四步Programs 侧的级联吊销在 LMS 内GeneratedCertificate的save()会向 Programs app 发出COURSE_CERT_CHANGED与COURSE_CERT_REVOKED两个信号DSL 第 34-35 行。programs/signals.py 中的handle_course_cert_revoked第 93-116 行随即调度revoke_program_certificates.delay(user.username, str(course_key))——因为该学习者某门课程的证书被吊销可能导致其不再满足某个项目证书的条件需要级联检查。DSL 图第 36-43 行给出了一条非常值得注意的注释原样保留在源码中无论颁发还是吊销课程证书都会入队同一个award_course_certificate任务该任务同时处理两种操作。随后programs_app - celery分别入队award_course_certificate与revoke_program_certificate任务由 Celery 交给 LMS 内的 credentials app 处理最终以POST 请求调用 Credentials IDA 的credentials_api_app吊销课程凭证或项目凭证。两条流程的对照与关键要点维度颁发Generation吊销Revocation主触发信号COURSE_GRADE_NOW_PASSED等 6 类信号COURSE_GRADE_NOW_FAILED核心处理Signal Handlers → Generation Handler 资格检查 → Celery 生成任务Signal Handlers →mark_notpassing状态更新数据库动作UPSERT 证书记录状态更新为NOT_PASSING事件总线事件CERTIFICATE_CREATEDCERTIFICATE_REVOKED对 Programs 的信号COURSE_CERT_CHANGED/COURSE_CERT_AWARDEDCOURSE_CERT_CHANGED/COURSE_CERT_REVOKEDCelery 任务generate_certificate生成、award_course_certificate/award_program_certificate同步 Credentialsaward_course_certificate吊销课程凭证、revoke_program_certificate/revoke_program_certificates吊销项目凭证Credentials IDA 联动Event Bus 消费 POST 颁发 APIEvent Bus 消费 POST 吊销 API排查证书问题时可以沿此对照表快速定位证书没生成优先看COURSE_GRADE_NOW_PASSED是否发出、generate_certificate_task的资格检查在哪一层被拦截generation_handler.py 中每层检查都有对应日志证书生成了但 Credentials 没同步检查is_credentials_enabled()、is_learner_records_enabled_for_org()与transaction.on_commit之后的任务是否执行programs/signals.py证书该吊销未吊销检查COURSE_GRADE_NOW_FAILED处理时用户是否在白名单上白名单豁免吊销、mark_notpassing是否执行、CERTIFICATE_REVOKED事件是否被 Event Bus 消费者接收。如何查看与重新渲染这些架构图图示文件由Structurizr DSLhttps://structurizr.org/编写存放在 lms/djangoapps/certificates/docs/diagrams/ 目录下源码certificate_generation.dsl、certificate_revocation.dsl已渲染图rendered/certificate_generation.png、rendered/certificate_revocation.png如需重新渲染或按需调整视图可使用 Structurizr CLIstructurizr-cli export -workspace certificate_generation.dsl -format png或 Structurizr 的在线/本地工作区导入 DSL 文件两份 DSL 均定义了systemLandscape视图与autolayout lr布局无需额外修改即可生成与仓库内置 PNG 相同风格的全景图。当前仓库中的两张渲染图即由该 DSL 直接导出与源码保持同步。总结通过 docs/diagrams/README.rst 及其配套 DSL 图我们可以把 Open edX 证书系统抽象成一条清晰的链路LMS 内部多个应用通过 Django 信号触发证书状态变更 → Certificates Generation Handler 做多层资格校验 → Celery 异步完成渲染与落库 → 数据库层以 Open edX Events 广播证书生命周期事件 → Programs app 与 Credentials IDA 通过 Celery 任务 POST API 事件总线消费三路协同完成课程证书与项目证书的颁发与吊销。这套信号驱动 异步任务 事件总线的设计既避免了各业务模块间的强耦合也让证书状态在 LMS 与独立部署的 Credentials IDA 之间保持最终一致。对维护者而言读懂这两张图就等于拿到了排查证书相关问题的全局地图——从触发信号、资格检查、Celery 任务到事件消费与 IDA 同步每一步都能在本文列出的源码文件中找到对应的实现与日志。【免费下载链接】openedx-platformThe Open edX LMS Studio, powering education sites around the world!项目地址: https://gitcode.com/GitHub_Trending/ed/openedx-platform创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表