GET/POST admin/tutorials GET/PUT/DELETE admin/tutorials/:id PUT admin/tutorials/:id/visibility GET admin/tutorials/:id/exercises POST admin/exercises PUT/DELETE admin/exercises/:id GET admin/ai/reports (?pinnedOnly=true 不分页) GET admin/ai/reports/:id POST admin/ai/reports/:id/pin 几处判断: - 练习改成挂在教程下的嵌套路径,旧后端是 ?tutorial_id= 查询参数。本来就是一对多的 从属关系,嵌套更贴事实,也省掉「忘了传 tutorial_id」这类错误。 - 删教程必须先删练习。Django 的 on_delete=CASCADE 是应用层实现的,库里外键实际是 NO ACTION(核对过 pg_constraint.confdeltype='a'),直接删会撞外键变 500。 已实测:带 2 个练习的教程能正常删掉且练习一并清除。**后台每个 DELETE 都要照此 核一遍子表**,这是本阶段的通用陷阱。 - 改可见性不动 updatedAt —— 上下架不是内容修改,改了会打乱按更新时间排序的直觉。 - AI 报告的 data / systemPrompt / userPrompt 一律不下发,里面是喂给模型的原始 学情数据与提示词。列表只给 120 字摘要,与旧 AIAnalysisListSerializer 一致。 实测:匿名 401 / 学生 403 / 超管 200;教程练习增删改查、非法 type 400、 挂到不存在的教程 404、置顶互斥(同一学生至多一份)、再钉一次取消,全部符合预期。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
oj.xuyue.cc