openlearn-next 0.4.2 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/README.md +26 -21
  2. package/cli-data.mjs +535 -40
  3. package/cli-doctor.mjs +456 -17
  4. package/cli.mjs +214 -12
  5. package/dist/assets/AcademicYearFilter-DUUGpDKt.js +1 -0
  6. package/dist/assets/ActivityWorkspaceWidget-5PzyxF6a.js +1 -0
  7. package/dist/assets/AdminDirectoryView-BCtmQTEK.js +5 -0
  8. package/dist/assets/AssessmentCenter-B43ojHG-.js +1 -0
  9. package/dist/assets/BatchPickerModal-CbmjInp7.js +1 -0
  10. package/dist/assets/ClassesView-DEnMaDOt.js +32 -0
  11. package/dist/assets/ClassroomCountdownWidget-CHq4983w.js +2 -0
  12. package/dist/assets/CloudDriveModal-D2t3Uyzr.js +1 -0
  13. package/dist/assets/ComputerLabView-YtnoT5Rb.js +1 -0
  14. package/dist/assets/CourseManagement-DOXyJ_j-.js +1 -0
  15. package/dist/assets/CourseWizardModal-Dbi6Caw2.js +3 -0
  16. package/dist/assets/CoursewareHubPanel-CyxDIJAF.js +1 -0
  17. package/dist/assets/Dashboard-2rLu_ZBy.js +8 -0
  18. package/dist/assets/ExportWeightModal-BrmAh6VX.js +1 -0
  19. package/dist/assets/GLTFLoader-CZJbo_y1.js +1 -0
  20. package/dist/assets/HelpTour-D_q1z_MI.js +1 -0
  21. package/dist/assets/HelpView-CVBrKDrd.js +204 -0
  22. package/dist/assets/ImportLessonsModal-PhHtX31R.js +1 -0
  23. package/dist/assets/ImportModal-DqjqNeH4.js +2 -0
  24. package/dist/assets/InteractiveCoursewareViewer-Vi6lLz7a.js +1 -0
  25. package/dist/assets/InteractiveWhiteboard-lTzeayX5.js +65 -0
  26. package/dist/assets/LazyCourseware-KIt0rGtW.js +2 -0
  27. package/dist/assets/LazyWhiteboard-CCN3pYcW.js +2 -0
  28. package/dist/assets/LessonEditorView-D_GVhHuM.js +1 -0
  29. package/dist/assets/LiveClassroomView-BRsZ6QEq.js +61 -0
  30. package/dist/assets/NotificationDetailModal-tY5rxYCN.js +1 -0
  31. package/dist/assets/OrbitControls-CQjN_uwS.js +1 -0
  32. package/dist/assets/PluginView-CedpAW6d.js +77 -0
  33. package/dist/assets/ProcessLogsModal-Cfr-T9Gd.js +1 -0
  34. package/dist/assets/QuizGeneratorModal-bUA002kP.js +1 -0
  35. package/dist/assets/StudentAssignmentView-BhAz6Hfk.js +1 -0
  36. package/dist/assets/StudentLessonView-DC1yX4kG.js +1 -0
  37. package/dist/assets/StudentView-PHKxIV9T.js +2 -0
  38. package/dist/assets/SystemResourceLibraryModal-DgXYe6_x.js +1 -0
  39. package/dist/assets/TeacherView-DscJtOJ9.js +2 -0
  40. package/dist/assets/TimetableView-D2ozoj_D.js +6 -0
  41. package/dist/assets/UTIF-FfCsrHhe.js +1 -0
  42. package/dist/assets/__vite-optional-peer-dep_ai_ooxml-ui-BIHI7g3E.js +1 -0
  43. package/dist/assets/browser-DEI0QxZC.js +1 -0
  44. package/dist/assets/html2canvas-pro.esm-Bo50djkB.js +12 -0
  45. package/dist/assets/index-2676X8YB.js +131 -0
  46. package/dist/assets/index-C6hlh8pw.css +1 -0
  47. package/dist/assets/{index.es-C5eWEO4o.js → index.es-DyEF0CZ9.js} +1 -1
  48. package/dist/assets/pluginParsers-e4PSm2Iy.js +1 -0
  49. package/dist/assets/react_pptx-react-viewer-BIHI7g3E.js +1 -0
  50. package/dist/assets/teaching-modes-client-BeVaDdJ-.js +1 -0
  51. package/dist/assets/three.module-iOYB3rD4.js +4228 -0
  52. package/dist/assets/timeline-duration-BgdJCSrW.js +1 -0
  53. package/dist/assets/{vendor-charts-qLbgF1eL.js → vendor-charts-SrpBsvM3.js} +2 -2
  54. package/dist/assets/{vendor-icons-BRmhY6fg.js → vendor-icons-CXrZ3R2V.js} +2 -2
  55. package/dist/assets/{vendor-konva-CEOq4dcl.js → vendor-konva-Dne0Z-WC.js} +1 -1
  56. package/dist/assets/{vendor-pdf-D7rGevI1.js → vendor-pdf-C1Vh2Aqq.js} +56 -56
  57. package/dist/assets/vendor-pptx-Bzn6whfK.js +11311 -0
  58. package/dist/assets/vendor-pptx-DIWdXF1L.css +1 -0
  59. package/dist/assets/{vendor-react-B0t7H3hh.js → vendor-react-Kv_JATwr.js} +1 -1
  60. package/dist/assets/vendor-utils-aM7aYvPP.js +1 -0
  61. package/dist/cli-migrator.cjs +310 -0
  62. package/dist/env-validation.cjs +351 -0
  63. package/dist/index.html +13 -10
  64. package/dist/plugin-net-preload.cjs +263 -0
  65. package/dist/server.cjs +51140 -27229
  66. package/migrations/000_initial_schema.sql +410 -0
  67. package/migrations/001_add_execution_mode.sql +6 -0
  68. package/migrations/002_add_client_session_expiry.sql +6 -0
  69. package/migrations/003_classroom_runtime.sql +33 -0
  70. package/migrations/004_courseware_score_config.sql +26 -0
  71. package/migrations/005_assignment_hub.sql +155 -0
  72. package/migrations/006_classroom_event_bus.sql +28 -0
  73. package/migrations/007_interactive_classroom.sql +119 -0
  74. package/migrations/008_teaching_modes.sql +36 -0
  75. package/migrations/009_classroom_peer_review.sql +159 -0
  76. package/migrations/010_class_groups.sql +33 -0
  77. package/migrations/011_classroom_feed.sql +34 -0
  78. package/migrations/012_auto_record_score.sql +48 -0
  79. package/migrations/013_score_source_strategy.sql +81 -0
  80. package/migrations/014_performance_indexes.sql +21 -0
  81. package/migrations/015_demo_data_registry.sql +26 -0
  82. package/migrations/016_preset_polls_and_passcode_expiry.sql +33 -0
  83. package/migrations/017_pacing_signals_segment.sql +6 -0
  84. package/migrations/018_processes_plugin_owner.sql +26 -0
  85. package/migrations/019_plugins_version.sql +61 -0
  86. package/migrations/020_upload_max_file_mb.sql +14 -0
  87. package/migrations/021_class_owner_and_password_gate.sql +11 -0
  88. package/migrations/022_plugin_public_routes.sql +14 -0
  89. package/migrations/023_encrypt_class_passcodes.sql +18 -0
  90. package/migrations/024_segment_gates.sql +37 -0
  91. package/migrations/025_assignment_lookup_indexes.sql +11 -0
  92. package/migrations/026_plugin_grants.sql +21 -0
  93. package/migrations/027_academic_year.sql +26 -0
  94. package/migrations/028_classroom_indexes.sql +14 -0
  95. package/migrations/029_clean_lesson_think_artifacts.sql +13 -0
  96. package/migrations/030_backfill_academic_year.sql +30 -0
  97. package/migrations/031_assessment_core.sql +158 -0
  98. package/migrations/032_progress_verification.sql +17 -0
  99. package/migrations/033_student_lock_mode.sql +9 -0
  100. package/migrations/034_plugin_orphan_data.sql +27 -0
  101. package/migrations/035_icebreaker_checkins.sql +26 -0
  102. package/migrations/036_student_read_notifications_created_at.sql +19 -0
  103. package/migrations/037_plugin_config_scopes.sql +40 -0
  104. package/migrations/038_assessment_ai_grading_suggestion.sql +10 -0
  105. package/migrations/039_question_bank_sharing_and_papers.sql +14 -0
  106. package/migrations/040_assignment_grade_history.sql +37 -0
  107. package/migrations/041_course_package_idempotency.sql +33 -0
  108. package/migrations/042_assignment_grade_resubmit_audit.sql +12 -0
  109. package/migrations/043_mark_sha256_passwords.sql +31 -0
  110. package/migrations/README.md +61 -0
  111. package/migrations/schema-golden.json +640 -0
  112. package/package.json +54 -28
  113. package/dist/assets/AdminDirectoryView-DY--WM5Z.js +0 -1
  114. package/dist/assets/BatchPickerModal-BpWu0Lil.js +0 -1
  115. package/dist/assets/ClassesView-KbHEqJto.js +0 -22
  116. package/dist/assets/ClassroomCountdownWidget-96WFRO9A.js +0 -2
  117. package/dist/assets/CloudDriveModal-DEQ1nkNk.js +0 -1
  118. package/dist/assets/ComputerLabView-1qWDNRQ2.js +0 -1
  119. package/dist/assets/CourseManagement-D3ZpxY0l.js +0 -1
  120. package/dist/assets/CourseWizardModal-Ck_-M8pk.js +0 -3
  121. package/dist/assets/CoursewareHubPanel-CaEizhxl.js +0 -1
  122. package/dist/assets/Dashboard-CYbpbofV.js +0 -8
  123. package/dist/assets/ExportWeightModal-DW2bu9Zz.js +0 -1
  124. package/dist/assets/HelpTour-CMJbbGne.js +0 -1
  125. package/dist/assets/HelpView-CtzdqhBq.js +0 -204
  126. package/dist/assets/ImportLessonsModal-jBuIK453.js +0 -1
  127. package/dist/assets/ImportModal-D7L-KNhZ.js +0 -1
  128. package/dist/assets/InteractiveCoursewareViewer-Bj7ahXMs.js +0 -1
  129. package/dist/assets/InteractiveWhiteboard-B7JcJvDu.js +0 -77
  130. package/dist/assets/LazyCourseware-CuiPxhDg.js +0 -2
  131. package/dist/assets/LazyWhiteboard-BJwCEZAU.js +0 -2
  132. package/dist/assets/LessonEditorView-DFrOp2qp.js +0 -1
  133. package/dist/assets/LiveClassroomView-BZ5nGee3.js +0 -59
  134. package/dist/assets/NotificationDetailModal-Baqnc5pU.js +0 -1
  135. package/dist/assets/PluginView-CCbIuZ24.js +0 -66
  136. package/dist/assets/ProcessLogsModal-DmFXg9mG.js +0 -1
  137. package/dist/assets/QuizGeneratorModal-BBEDssp7.js +0 -1
  138. package/dist/assets/StudentAssignmentView-DgIjUsXz.js +0 -1
  139. package/dist/assets/StudentLessonView-CBJbI5x6.js +0 -1
  140. package/dist/assets/StudentPreviewModal-Bh8OZs0t.js +0 -1
  141. package/dist/assets/StudentView--uGxc5TJ.js +0 -2
  142. package/dist/assets/SystemResourceLibraryModal-BVCclcqA.js +0 -1
  143. package/dist/assets/TeacherView-D4Zsc0eZ.js +0 -2
  144. package/dist/assets/TimetableView-BQG-rEb5.js +0 -6
  145. package/dist/assets/index-BKBCBKvy.js +0 -217
  146. package/dist/assets/index-CvGu7LSj.css +0 -1
  147. package/dist/assets/purify.es-CYR4BTuT.js +0 -3
  148. package/dist/assets/vendor-pptx-C5-U7ieS.js +0 -39
  149. package/dist/assets/vendor-utils-m0Urvtws.js +0 -1
@@ -0,0 +1,33 @@
1
+ -- UP
2
+ -- 课程包导入幂等(G-06,docs/audit/course-import-audit.md):
3
+ --
4
+ -- 课程包格式本身没有天然主键(不像数据库行有 id),去重键只能派生:
5
+ -- * 包声明了 meta.id → key = "id:<meta.id>"(作者给的稳定标识,抗内容漂移)
6
+ -- * 包没声明 meta.id → key = "sha256:<内容哈希>"(规范化内容,见
7
+ -- server/services/course-package-identity.ts)
8
+ --
9
+ -- 三列各司其职:
10
+ -- source_package_key 去重键本体。查重就查它 —— 命中即视为「同一个包」。
11
+ -- source_package_hash 内容哈希。即便包声明了 id 也要留着:换了内容但沿用
12
+ -- 同一个 id 时,能看出「内容确实变过」。
13
+ -- source_package_id 包声明的 meta.id(可空)。仅为可读性/排查而存在。
14
+ --
15
+ -- 作用域是**按创建者**:内置课程模板要能被每一位教师各导入一份,
16
+ -- 所以去重只在同一 creator_id 内生效,不做全局去重。
17
+ --
18
+ -- 键随课程行一起存在 → 教师删课即释放该键,无需额外的清理链路。
19
+
20
+ ALTER TABLE lessons ADD COLUMN source_package_key TEXT;
21
+ ALTER TABLE lessons ADD COLUMN source_package_hash TEXT;
22
+ ALTER TABLE lessons ADD COLUMN source_package_id TEXT;
23
+
24
+ -- 查重路径:creator_id + source_package_key 精确命中。
25
+ CREATE INDEX IF NOT EXISTS idx_lessons_source_package_key
26
+ ON lessons(creator_id, source_package_key)
27
+ WHERE source_package_key IS NOT NULL;
28
+
29
+ -- DOWN
30
+ DROP INDEX IF EXISTS idx_lessons_source_package_key;
31
+ ALTER TABLE lessons DROP COLUMN source_package_id;
32
+ ALTER TABLE lessons DROP COLUMN source_package_hash;
33
+ ALTER TABLE lessons DROP COLUMN source_package_key;
@@ -0,0 +1,12 @@
1
+ -- UP
2
+ -- Issue #248:学生重交会替换已批改行的作答内容,但 `assignment_grade_history`
3
+ -- 只记分数 / 状态 / 来源 —— 查不到「这份分数是针对哪份内容给的」。
4
+ -- 于是出现「内容换了、分数没动、历史里查不出来」的脱钩。
5
+ --
6
+ -- 补一列 `old_content`:只存**被替换掉的那份**内容(带分数的那份)。
7
+ -- 新内容就在 `assignment_submissions` 行里,不必再存一份 ——
8
+ -- 避免审计表被大段文本撑爆。
9
+ ALTER TABLE assignment_grade_history ADD COLUMN old_content TEXT;
10
+
11
+ -- DOWN
12
+ ALTER TABLE assignment_grade_history DROP COLUMN old_content;
@@ -0,0 +1,31 @@
1
+ -- UP
2
+ -- Issue #227:users 表里仍可能存在**无盐 SHA-256** 的口令摘要 ——
3
+ -- 由 builtin.user 插件命令的 user.create / user.update 写入(现已改用 bcrypt)。
4
+ --
5
+ -- SHA-256 是快速哈希,没有盐也没有工作因子:库一旦泄露(备份文件 / SQLite 文件
6
+ -- 被拷走),这些口令的爆破成本几乎为零。与 bcrypt 账号混在同一张表里,
7
+ -- 排查时也容易误以为全库都是 bcrypt。
8
+ --
9
+ -- 存量行无法反推原口令(无盐哈希不可逆),只能重置:
10
+ -- 把它们的 must_change_password 置 1 —— 复用平台既有的强制改密机制
11
+ -- (enforcePasswordChanged 中间件 + 前端改密页)。
12
+ -- 顺带一提:staff 登录时 verifyPassword 命中旧 SHA-256 会**自动升级**为 bcrypt,
13
+ -- 因此这批账号在首次登录后即变成 bcrypt 摘要。
14
+ --
15
+ -- 判定条件:64 位纯小写十六进制(bcrypt 摘要以 $2 开头,天然不匹配)。
16
+ UPDATE users
17
+ SET must_change_password = 1
18
+ WHERE password_hash IS NOT NULL
19
+ AND length(password_hash) = 64
20
+ AND password_hash NOT GLOB '*[^0-9a-f]*';
21
+
22
+ -- DOWN
23
+ -- 回滚只能把「看起来像 SHA-256 的行」的强制改密标记清掉 ——
24
+ -- 但其中一部分在迁移前就已经是 must_change_password = 1(种子账号默认口令等),
25
+ -- 因此本 DOWN 是**有损的**:无法区分「本次标记的」与「本来就有的」。
26
+ -- 真正的回退口径应是重新设置这些账号的口令,而不是恢复标记位。
27
+ UPDATE users
28
+ SET must_change_password = 0
29
+ WHERE password_hash IS NOT NULL
30
+ AND length(password_hash) = 64
31
+ AND password_hash NOT GLOB '*[^0-9a-f]*';
@@ -0,0 +1,61 @@
1
+ # Database Migrations
2
+
3
+ 本目录包含 OpenLearnV2 的版本化数据库迁移文件。
4
+
5
+ ## 约定
6
+
7
+ - 文件名格式:`NNN_description.sql`(NNN 为三位序号)
8
+ - 每个文件包含 `-- UP`(应用迁移)和 `-- DOWN`(回滚迁移)两部分
9
+ - 迁移由 `server/utils/migrate.ts` 在服务启动时自动执行
10
+ - 已应用的迁移记录在 `_migrations` 表中
11
+ - 已应用的迁移**不可改名/修改**(checksum 台账会检出漂移);台账条目在目录中不存在时启动直接 FATAL(代码降级或文件被删,可用 `MIGRATION_ALLOW_UNKNOWN_LEDGER=true` 显式放行降级运维)
12
+ - `schema-golden.json`:结构金样(空库跑完全部迁移后的表→列快照,自动生成勿手改)。启动终检实际库必须覆盖金样全部结构,缺表/缺列即拒绝服务——改迁移后必须跑 `pnpm db:gen-golden` 再生成,`server/__tests__/schema-golden.test.ts` 会对账
13
+
14
+ ## 当前迁移列表
15
+
16
+ | 序号 | 文件 | 描述 |
17
+ | ---- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
18
+ | 000 | 000_initial_schema.sql | 初始完整 Schema(30+ 表与核心索引) |
19
+ | 001 | 001_add_execution_mode.sql | plugins 表添加 execution_mode |
20
+ | 002 | 002_add_client_session_expiry.sql | client_sessions 添加 expires_at |
21
+ | 003 | 003_classroom_runtime.sql | 课堂工具与 AI 对话记忆表(student_rollcalls, site_settings, agent_conversations) |
22
+ | 004 | 004_courseware_score_config.sql | 互动课件成绩归集策略表(courseware_score_config,宿主侧按策略从样本历史算最终分) |
23
+ | 005 | 005_assignment_hub.sql | 作业中心(plugin_assignments / plugin_submission_versions / plugin_assignment_files / plugin_peer_review_tasks,并重建 plugin_submissions 支持按作业唯一) |
24
+ | 006 | 006_classroom_event_bus.sql | `events` 审计表补课堂维度:`lesson_id` 列与 (type,timestamp) / correlationId / (lesson_id,timestamp) 索引,让课堂事件可按时间窗口、业务流串联与课节重放查询 |
25
+ | 007 | 007_interactive_classroom.sql | 互动课堂与课节生命周期(lesson_quiz_submissions / classroom_sessions / classroom_quick_polls / classroom_poll_votes / classroom_buzzers / classroom_exit_tickets / classroom_pacing_signals) |
26
+ | 008 | 008_teaching_modes.sql | 教学模式表 teaching_modes 与 classroom_sessions.teaching_mode_id(课堂启动门户的模式选择器数据源) |
27
+ | 009 | 009_classroom_peer_review.sql | 课中互评(classroom_peer_review_groups / classroom_peer_review_submissions) |
28
+ | 010 | 010_class_groups.sql | 小组(class_groups) |
29
+ | 011 | 011_classroom_feed.sql | 课堂动态流(classroom_feed) |
30
+ | 012 | 012_auto_record_score.sql | `courseware_score_config` 补自动录入规则列(auto_record_enabled / auto_record_min_completion),把「学生提交数据」页的逐条手动「录入成绩」升级为按规则自动录入 |
31
+ | 013 | 013_score_source_strategy.sql | `assignment_submissions` 补 source 列(manual/auto 来源分流,自动路径遇 manual/缺考行受保护)+ `courseware_score_config` 补 auto_record_strategy(latest/highest 更新策略) |
32
+ | 014 | 014_performance_indexes.sql | 五个高频查询索引(vfs_nodes.parent_id、plugin_submissions.lesson_id、student_point_logs.student_id、student_rollcalls.class_id/student_id) |
33
+ | 015 | 015_demo_data_registry.sql | `demo_data_registry`:演示数据精确登记,清理时只删登记行 |
34
+ | 016 | 016_preset_polls_and_passcode_expiry.sql | 课程预设投票 `lesson_preset_polls`、班级口令有效期与点名奖赏字段 |
35
+ | 017 | 017_pacing_signals_segment.sql | `classroom_pacing_signals.segment_id` 及会话分段索引 |
36
+ | 018 | 018_processes_plugin_owner.sql | `processes.plugin_id`,后台任务可判定归属 |
37
+ | 019 | 019_plugins_version.sql | `plugins.version`,版本可在 SQL 层查询 |
38
+ | 020 | 020_upload_max_file_mb.sql | `site_settings.upload_max_file_mb`,课件上传上限改为管理员可配置 |
39
+ | 021 | 021_class_owner_and_password_gate.sql | `classes.owner_user_id`(教师仅能管理自己的班;空值仅管理员可见)与 `users.must_change_password`。空归属用 `node scripts/backfill-class-owners.mjs --assign <教师用户名>`,或 `UPDATE classes SET owner_user_id = '<教师用户 id>' WHERE owner_user_id IS NULL` |
40
+ | 022 | 022_plugin_public_routes.sql | `plugin_public_routes`:管理员显式批准的插件公开路由。`auth: false` 本身不再匿名放行。升级后需重新批准 |
41
+ | 023 | 023_encrypt_class_passcodes.sql | 标记班级临时口令改为 AES-256-GCM 密文。启动时 `encryptStoredClassPasscodes()` 把仍是明文的行加密,已带 `olenc1:` 的行跳过 |
42
+ | 024 | 024_segment_gates.sql | 教学环节门槛:`segment_visits`(停留)、`segment_gate_overrides`(教师放行)、`lesson_checkins`(无排课时的签到) |
43
+ | 025 | 025_assignment_lookup_indexes.sql | `assignment_submissions(student_id)` 与 `assignments(lesson_id)` 索引,供课节进度聚合作业最高分和学生提交查询。可重复执行 |
44
+ | 026 | 026_plugin_grants.sql | 第三方插件能力授权与出站网络白名单表(`plugin_capability_grants` / `plugin_network_grants`) |
45
+ | 027 | 027_academic_year.sql | 学年维度:为 schedules/assignments/attendance/exams/semester reports 等表增加 `academic_year` 字段与索引;site_settings 增加当前学年配置 |
46
+ | 028 | 028_classroom_indexes.sql | 补齐课堂互动与会话高频查询/级联删除索引(`classroom_buzzers`、`classroom_sessions`) |
47
+ | 029 | 029_clean_lesson_think_artifacts.sql | 课程大纲模型思维链残留 (<think>...</think>) 清洗标记与启动期幂等清理 |
48
+ | 030 | 030_backfill_academic_year.sql | 学年历史数据回填:为老数据库中 NULL 学年的排课/作业/考勤等按日期与父级关联平滑补齐 |
49
+ | 031 | 031_assessment_core.sql | 测评子系统核心数据结构(试卷、考试、题库、题目等) |
50
+ | 032 | 032_progress_verification.sql | 学习进度区分「学生自报」与「已验证」:新增 `verified` / `verified_progress_percent`,成绩只计入已验证进度;历史进度回填为已验证以保持既有成绩口径 |
51
+ | 033 | 033_student_lock_mode.sql | 学生端锁定模式 (lock_mode) 与锁定目标 (lock_target) |
52
+ | 034 | 034_plugin_orphan_data.sql | 插件卸载数据语义:孤立插件数据登记表(`plugin_orphan_data`,keep 保留登记 / purge 归档后可清除) |
53
+ | 035 | 035_icebreaker_checkins.sql | 课前破冰心情打卡持久化与去重(`icebreaker_checkins` 表与按学生去重唯一索引) |
54
+ | 036 | 036_student_read_notifications_created_at.sql | 保留策略:为 `student_read_notifications` 补齐 `created_at` 以便按 30 天窗口清理 |
55
+ | 037 | 037_plugin_config_scopes.sql | 插件配置分层:`plugin_config` 表(平台/学校/班级),旧 `plugin_storage` 的 `config:*` 值迁移到平台层 |
56
+ | 038 | 038_assessment_ai_grading_suggestion.sql | AI 辅助批改建议:`assessment_answers.ai_suggestion_json` 存「建议分 + 理由 + 置信度」,与正式分数分开 |
57
+ | 039 | 039_question_bank_sharing_and_papers.sql | 题库共享与按规则随机抽题(每人一卷):`question_bank_items.visibility` 区分 private / school |
58
+ | 040 | 040_assignment_grade_history.sql | 成绩审计与手动分保护:`assignment_grade_history` 记录每次成绩变更的旧/新分数、状态、来源与操作者 |
59
+ | 041 | 041_course_package_idempotency.sql | 课程包导入幂等(G-06):同一包重复导入不再产生重复课程 |
60
+ | 042 | 042_assignment_grade_resubmit_audit.sql | 学生重交审计:`assignment_grade_history` 补 `old_content`,可追溯「这份分数是针对哪份内容给的」 |
61
+ | 043 | 043_mark_sha256_passwords.sql | Issue #227:把仍是无盐 SHA-256 摘要的存量账号标记为 `must_change_password = 1`,强制走既有改密流程(SHA-256 不可逆,无法反推原口令) |