Open edX 课程证书模块(Certificates App)全解析:从数据模型、生成流程到证书管理实战
2026/9/17 8:27:27 网站建设 项目流程

Open edX 课程证书模块(Certificates App)全解析:从数据模型、生成流程到证书管理实战

【免费下载链接】openedx-platformThe Open edX LMS & Studio, powering education sites around the world!项目地址: https://gitcode.com/GitHub_Trending/ed/openedx-platform

导读

Open edX 平台的课程证书功能并非只存在于单一目录,而是横跨 LMS、Studio(CMS)与 Credentials 等多个子系统。本文以lms/djangoapps/certificates为核心,系统讲解该应用的职责边界、核心数据模型、证书状态机、证书生成与撤销的完整调用链,并结合cms/djangoapps/contentstore/views/certificates.pyopenedx/core/djangoapps/credentials等关联代码,帮助你理解"证书配置 → 证书生成 → 证书展示 → 证书撤销"的端到端实现,掌握通过源码定位证书问题、通过管理命令批量发证的能力。

仓库定位:本应用当前状态为Maintenance(维护中),即功能已基本稳定,新功能开发优先考虑 Credentials 等外部服务,本模块以维护为主。

Open edX 课程证书生成流程架构图

一、应用职责:证书的"创建与管理中枢"

根据 lms/djangoapps/certificates/README.rst,Certificates 应用负责创建和管理课程证书(course certificates),其职责覆盖三个层面:

  1. 证书设置(certificate settings)——课程的证书启用开关、显示行为、生效日期等配置;
  2. 课程证书模板(course certificate templates)——Web 端自定义证书的 Django 模板及其配套资源;
  3. 已生成的学习者课程证书(generated learner course certificates)——每位学习者在某次课程运行(course run)下实际获得的证书记录。

同时,该应用提供了两类关键数据模型能力:证书无效化(invalidating certificates)白名单管理(managing the allowlist)

1.1 功能散布在多个子系统

证书相关功能分散在多处,理解"哪里改什么"是排查问题的第一步:

位置职责
lms/djangoapps/certificates核心:证书数据模型、生成逻辑、任务、信号、API 与 Web 展示
openedx/core/djangoapps/credentials学习者的项目证书(Program Certificates)记录系统(system of record),与课程证书联动
cms/djangoapps/contentstore/views/certificates.pyStudio 侧证书配置的 CRUD API(创建/编辑/删除证书及签名人)
各前端静态模板证书列表页(Backbone 应用)、证书 Web 视图等 UI

值得特别说明的是Credentials 服务:它是学习者项目证书的权威记录(system of record)系统。在本仓库中对应openedx/core/djangoapps/credentials;课程证书在生成或状态变化时会通过COURSE_CERT_CHANGEDCOURSE_CERT_AWARDED等信号通知 Credentials,由其判断是否可以授予项目级证书。

二、核心数据模型(models.py)

证书模块的数据模型全部定义在lms/djangoapps/certificates/models.py,下面按"学习者证书 → 例外与撤销 → 配置 → 模板"四类介绍。

2.1 GeneratedCertificate:学习者证书主模型

这是全模块最核心的模型,一条记录代表"一个学生在一次课程运行中获得的证书"。关键字段:

字段说明
user关联用户
course_id课程运行 key(CourseKeyField
verify_uuid证书唯一标识(32 位 UUID),用于证书校验 URL
grade生成证书时的成绩快照(注意:该成绩不会随课程成绩变化更新,权威成绩应使用 PersistentCourseGrade)
key证书标识符(PDF 证书时代使用)
distinction是否"荣誉"通过(当前未使用)
status证书状态,见下文状态机
mode课程运行模式(verified、honor、audit、masters 等)
name证书上显示的用户姓名(PII 字段)
download_uuid/download_url/error_reason遗留的 PDF 证书字段,已不再使用(见 ADR 008-certificate-model-remnants.rst)

模型内置了三个 Manager:

  • objects:默认管理器,包含不合格证书(如 audit 模式的新证书);
  • eligible_certificates:自动排除audit_passingaudit_notpassing状态,防止意外给未注册证书模式的学生发证;
  • eligible_available_certificates:在 eligible 基础上,仅返回存在对应CourseOverview的证书。
# 使用示例:查询某学生在某课程的证书 cert = GeneratedCertificate.certificate_for_student(student, course_id)

2.2 例外与撤销类模型

  • CertificateAllowlist(证书白名单):记录某个课程运行下被"特批"发证的学生,allowlist=True时生效;get_certificate_allowlist(course_id, student)会返回包含id/user_id/user_name/user_email/course_id/created/notes/certificate_generated的字典列表。
  • CertificateInvalidation(证书无效化):记录对某张证书的无效化操作,active=True表示当前生效;提供get_certificate_invalidations()has_certificate_invalidation(student, course_key)查询接口。
  • CertificateDateOverride(证书日期覆盖):手动将某张证书的展示日期覆盖为指定日期(date字段,时间建议设为 00:00:00),并记录reasonoverridden_by;保存/删除时会通过COURSE_CERT_CHANGED信号通知 Credentials 刷新。

2.3 配置类模型

  • CertificateGenerationConfiguration:全局开关,控制"自助生成证书"功能(关闭时进度页隐藏"生成证书"按钮);
  • CertificateGenerationCourseSetting:按课程配置,包括self_generation_enabled(允许学生自助生成)、language_specific_templates_enabled(多语言模板)、include_hours_of_effort(展示学时投入,需 Discovery 提供weeks_to_completemax_effort);
  • CertificateHtmlViewConfiguration:HTML 证书视图的静态上下文参数(JSON),支持default与按 mode 覆盖;
  • CertificateGenerationCommandConfiguration / ModifiedCertificateTemplateCommandConfiguration / PurgeReferencestoPDFCertificatesCommandConfiguration:分别对应三条管理命令的参数存储(可配合--args-from-database使用);
  • ExampleCertificateSet / ExampleCertificate:示例证书,用于在启用自助发证前验证模板与生成链路是否正常(示例证书不关联真实用户,姓名固定为John Doë)。

2.4 模板类模型

  • CertificateTemplate:自定义 Web 证书模板,字段包括template(Django 模板 HTML)、organization_idcourse_keymodelanguageis_active。唯一性约束为(organization_id, course_key, mode, language)
  • CertificateTemplateAsset:模板配套资源(图片、CSS 等),上传至MEDIA_ROOT/certificate_template_assets/<id>/,通过asset_slug在模板中引用。

三、证书状态机(data.py)

所有状态常量定义在CertificateStatuses类中。当前代码实际会写入的状态有四种:

状态含义设置入口
downloadable已授予且可下载generation.py中的生成流程
notpassing未达到通过成绩GeneratedCertificate.mark_notpassing()
unavailable证书已被无效化GeneratedCertificate.invalidate()
unverified身份验证未通过GeneratedCertificate.mark_unverified()

其余状态(generatingerrorrequestingdeletedaudit_passing等)为 PDF 证书时代的遗留值,仅用于兼容历史数据。

两个派生常量值得注意:

  • PASSED_STATUSES = (downloadable, generating)——判断学生是否通过课程;
  • NON_REFUNDABLE_STATUSES = (downloadable, generating, unavailable)——用于退款资格判断(is_refundable_status)。

GeneratedCertificate.invalidate()/mark_notpassing()/mark_unverified()最终都汇聚到私有方法_revoke_certificate(),它会清空download_uuiddownload_url、更新状态,随后依次:

  1. 发送COURSE_CERT_REVOKED信号(用于联动撤销项目证书);
  2. 发送 Open edX 事件org.openedx.learning.certificate.revoked.v1CERTIFICATE_REVOKED);
  3. 若撤销前状态为downloadable,额外发出edx.certificate.revoked埋点事件。

四、证书生成流程:从信号到 Celery 的完整链路

证书生成采用"信号触发 → 资格校验 → 异步任务"的架构,相关代码集中在 generation_handler.py 与 tasks.py。

4.1 触发信号(signals.py)

证书生成的触发源包括:

  • COURSE_GRADE_NOW_PASSED:学生成绩变为通过(自动发证模式下);
  • COURSE_GRADE_NOW_FAILED:成绩变为不通过(标记notpassing,白名单学生除外);
  • LEARNER_SSO_VERIFIED/PHOTO_VERIFICATION_APPROVED/IDV_ATTEMPT_APPROVED:身份验证通过后补发证书;
  • ENROLLMENT_TRACK_UPDATED:选课模式变为可发证模式(如升级到 verified);
  • post_save(CertificateAllowlist):学生被加入白名单后自动尝试发证。

4.2 资格校验(generation_handler.py)

入口函数generate_certificate_task(user, course_key, generation_mode, delay_seconds)会先判断学生是否在白名单上,分流到两条路径:

  • 白名单路径generate_allowlist_certificate_task():不要求通过成绩,但仍需满足公共校验;
  • 常规路径_generate_regular_certificate_task():额外要求非 CCX 课程、非 Beta 测试者、成绩通过。

两条路径共享公共校验_can_generate_certificate_common(),其检查项依次为:

  1. 是否在证书无效化列表(CertificateInvalidation.has_certificate_invalidation)——是则拒绝;
  2. 是否存在选课记录(enrollment_mode 为空则拒绝);
  3. 选课模式是否可发证(modes_api.is_eligible_for_certificate);
  4. 若课程强制身份验证(ENABLE_CERTIFICATES_IDV_REQUIREMENT)且用户未验证,且模式不属于NON_VERIFIED_MODES(honor、no-id-professional 等)——则拒绝;
  5. 已有证书状态是否允许生成(downloadable且当前模式未升级为可发证模式则拒绝);
  6. 课程是否存在CourseOverview
  7. 是否已启用 HTML 证书(has_html_certificates_enabled)。

校验通过后调用_generate_certificate_task(),其中会触发 Open edX 过滤器CertificateCreationRequested(类型org.openedx.learning.certificate.creation.requested.v1),允许插件在生成前拦截(抛出PreventCertificateCreation则中止)。

4.3 异步任务执行(tasks.py / generation.py)

# tasks.py 中的任务定义(节选) @shared_task(base=LoggedPersistOnFailureTask, bind=True, default_retry_delay=30, max_retries=2) def generate_certificate(self, **kwargs): student = User.objects.get(id=kwargs.pop("student")) course_key = CourseKey.from_string(kwargs.pop("course_key")) status = kwargs.pop("status", CertificateStatuses.downloadable) enrollment_mode = kwargs.pop("enrollment_mode") course_grade = kwargs.pop("course_grade", "") generation_mode = kwargs.pop("generation_mode", "batch") generate_course_certificate(user=student, course_key=course_key, status=status, enrollment_mode=enrollment_mode, course_grade=course_grade, generation_mode=generation_mode)

任务参数说明:

参数必填默认值说明
student学生用户 ID
course_key课程运行 key
statusdownloadable目标证书状态
enrollment_mode选课模式
course_grade''课程成绩
generation_modebatch事件模式:self(用户自助)/batch(批量等)

任务默认延迟CERTIFICATE_DELAY_SECONDS = 2秒执行(防止调用方在 post-save 阶段仍有未提交改动),失败时最多重试 2 次、间隔 30 秒。

真正落库的逻辑在 generation.py 的generate_course_certificate()

  1. 若已存在证书则复用其verify_uuid(保证学习者证书 URL 不变),否则新生成 UUID;
  2. 通过GeneratedCertificate.objects.update_or_create(user, course_id, ...)创建或更新记录;
  3. 若状态为通过类状态(downloadable/generating),发出edx.certificate.created埋点事件;
  4. 若状态为unverified,调用mark_unverified()落库。

同时,GeneratedCertificate.save()的重写会在保存后发出COURSE_CERT_CHANGEDCERTIFICATE_CHANGED事件,以及(通过状态时)COURSE_CERT_AWARDEDCERTIFICATE_CREATED事件,供 Credentials 与事件总线消费——这正是架构图中"信号 → Celery → 数据库 → Credentials/Event Bus"的实现对应。

五、Studio 侧的证书配置 API

证书的配置(而非具体某张证书)存储在课程 Modulestore 的course.certificates字段中,由 Studio 管理,对应 cms/djangoapps/contentstore/views/certificates.py。其数据契约如下:

course.certificates: { 'certificates': [ { 'version': 1, // 数据契约版本 'id': 12345, // 自动生成的标识符 'name': 'Certificate 1', 'description': 'Certificate 1 Description', 'course_title': 'course title', 'signatories': [ { 'id': 24680, // 自动生成的标识符 'name': 'Dr. Bob Smith', 'title': 'Dean of the College', 'organization': 'Awesome College' } ] } ] }

提供的接口能力:

  • 证书列表/创建certificates_list_handler,GET/POST):返回证书列表页(Backbone 应用)或 JSON 列表;POST 时通过CertificateManager.deserialize_certificate()校验并追加新证书;
  • 证书详情CertificateDetailAPIView,POST/PUT/DELETE):创建、更新、删除单条证书配置;删除处于激活状态的证书需要 GlobalStaff 权限
  • 签名人管理signatory_detail_handler):删除证书签名人;
  • 证书激活/停用CertificateActivationAPIView,POST{ "is_active": true/false }):切换证书配置的激活状态(假设每个课程只有一条激活证书配置)。

所有接口都先通过has_studio_write_access(user, course_key)做权限校验,因此仅具备 Studio 写权限的用户可管理证书配置。

六、HTML 证书的展示(views/webview.py)

现代 Open edX 使用Web 证书(HTML 证书)替代 PDF 证书。渲染入口为render_html_view(),其判断链为:

  1. 全局开关settings.CERTIFICATES_HTML_VIEW关闭 → 返回 "invalid" 页;
  2. 课程cert_html_view_enabled为假 → 返回 "invalid" 页;
  3. 学生无downloadable状态证书 → 返回 "invalid" 页;
  4. 课程无激活证书配置(get_active_web_certificate)→ 返回 "invalid" 页。

渲染时支持两种模板路径:

  • 标准模板certificates/valid.html,上下文由CertificateHtmlViewConfiguration.get_config()提供默认值并按user_certificate.mode覆盖;
  • 自定义模板:启用CUSTOM_CERTIFICATE_TEMPLATES_ENABLED后,按"组织+课程+模式+语言"四级匹配CertificateTemplateget_certificate_template()),并使用模板自带语言渲染。

此外,渲染前会触发过滤器CertificateRenderStartedorg.openedx.learning.certificate.render.started.v1),插件可借此替换为自定义响应;视图还支持?preview=verified等参数实现 CMS 侧的证书预览。

证书的显示日期由display_date_for_certificate()(api.py)决定:优先使用CertificateDateOverride覆盖日期;否则按课程的certificates_display_behaviorEND_WITH_DATE/END/EARLY_NO_INFO)选择"证书可用日期 / 课程结束日期 / 证书修改日期"。

七、证书的撤销与无效化

证书撤销的核心入口:

  • 单张无效化GeneratedCertificate.invalidate(mode, source),将状态置为unavailable
  • API 级无效化api.invalidate_certificate(user_id, course_key, source),白名单学生豁免;
  • 事件驱动撤销EXAM_ATTEMPT_REJECTED事件(考试作弊被拒)会触发invalidate_certificate(source='exam_event')(见 signals.py);
  • 白名单移除联动remove_allowlist_entry()在把学生移出白名单前会先无效化其证书(source='allowlist_removal')。

Open edX 课程证书撤销流程架构图

八、管理命令:批量发证与模板维护

lms/djangoapps/certificates/management/commands/下提供多条运维命令:

8.1 cert_generation:为指定用户批量生成证书

./manage.py lms cert_generation -u 123 456 -c course-v1:edX+DemoX+Demo_Course

参数说明:

参数说明
-u, --user用户 ID(可传多个,空格分隔)
-c, --course-key课程运行 key
--args-from-databaseCertificateGenerationCommandConfiguration配置读取参数(如-u <user_id> -c <course_run_key>

命令内部对每个用户调用generate_certificate_task(),不存在的用户或不允许生成的情况仅记录日志。

8.2 其余命令

  • modify_cert_template:批量修改CertificateTemplate中的文本(--old-text/--new-text/--template_ids/--dry-run),支持ModifiedCertificateTemplateCommandConfiguration存储参数;
  • purge_references_to_pdf_certificates:清理 PDF 证书遗留引用(--certificate_ids);
  • purge_pii_from_generatedcertificates:清理已生成证书中的个人隐私信息(PII)。

九、测试与设计决策参考

  • 单元测试位于 lms/djangoapps/certificates/tests/,覆盖生成(test_generation.py)、处理器(test_generation_handler.py)、信号(test_signals.py)、任务(test_tasks.py)、模型(test_models.py)、Web 视图(test_webview_views.py)等;
  • 管理命令测试位于 management/commands/tests/;
  • 设计决策(ADR)位于 docs/decisions/,推荐重点阅读:003-web-certs.rst(Web 证书)、004-cert-status.rst(状态机)、006-cert-date-override.rst(日期覆盖)、008-certificate-model-remnants.rst(PDF 遗留字段去留);
  • 流程图源码(PlantUML DSL)位于 docs/diagrams/,可在仓库内直接查看生成与撤销两条流程的完整组件交互。

十、排查指引小结

症状排查入口
学生未自动获证检查CertificateGenerationConfiguration全局开关、课程self_generation_enabledCourseOverviewcert_html_view_enabledENABLE_CERTIFICATES_IDV_REQUIREMENT及信号日志
证书显示 "invalid"依次核对CERTIFICATES_HTML_VIEWcert_html_view_enabled、证书状态是否downloadableget_active_web_certificate是否有激活配置
需要给未通过学生发证使用CertificateAllowlist白名单(API:create_or_update_certificate_allowlist_entry),加入后自动触发发证
需批量补发证书./manage.py lms cert_generation -u ... -c <course_key>
某学生证书需作废invalidate_certificate()/CertificateInvalidation记录 +GeneratedCertificate.invalidate()
证书日期需修正Django Admin 中维护CertificateDateOverride,保存后自动广播COURSE_CERT_CHANGED

综上,Open edX 的证书体系以GeneratedCertificate为核心、以信号和 Celery 为纽带、以 Credentials 为下游权威记录,将"配置(Studio)→ 生成(LMS)→ 展示(Web 证书)→ 撤销"串联为完整闭环。理解这条链路,即可从容应对证书相关的绝大多数开发与运维场景。

【免费下载链接】openedx-platformThe Open edX LMS & Studio, powering education sites around the world!项目地址: https://gitcode.com/GitHub_Trending/ed/openedx-platform

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询