feat(自学): 教程有了「所属项目」和课末的相关题,正文里的代码能直接运行

- 0031 迁移:tutorial 加 project 列(从正文「所属项目:」回填),新表 tutorial_problem
  挂每课的相关题(两头 CASCADE)
- 学完一课 = 练一练全对(契约 isLessonDone),不再按停留满 3 分钟算
- /tutorials/:id 带相关题和做对没有,/tutorials/:id/exercises 带每道做对没有
- /trial-runs 收 tutorialId:教程里的代码块走同一台判题机跑一组
- 后台:教程列表带练一练 / 相关题数 / 打开过的人,拖动排序、项目改名、挑题候选(必会题在前)
- 自学情况换成学生 × 课的方块格(/learn-analytics/board),默认班和排名页同一套猜法

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-10-12 09:40:34 +08:00
1 parent 6d1bfc522d
commit 11194543a3
14 files changed
+4621 -268

No files matched your search

+3 -1
View File
@@ -558,7 +558,8 @@ export const TRIAL_ADMIN_MAX_CASES = 50
* 试运行:「运行例子」「自己输入」、后台「生成测试点」。走本站判题机,不落库、不算提交。
*
* - 带 `problemId`:按这道题的时间 / 内存限制和代码模板跑,和提交一个口径;
* - 不带:只有管理员能这么调(后台生成测试点时题目可能还没存)。
* - 带 `tutorialId`:自学教程正文里的代码块「运行」,只能跑一组、限制用默认的;
* - 都不带:只有管理员能这么调(后台生成测试点时题目可能还没存)。
*
* `output` 是这组的正确输出,给了就由判题机比对(和提交一样去掉末尾空白再比),
* 结果是通过 / 答案错误;不给(「自己输入」、生成测试点)就只看有没有跑出错。
@@ -566,6 +567,7 @@ export const TRIAL_ADMIN_MAX_CASES = 50
export const trialRunRequestSchema = z.object({
problemId: z.number().int().positive().optional(),
contestId: z.number().int().positive().optional(),
tutorialId: z.number().int().positive().optional(),
language: runnableLanguageSchema,
code: z
.string()