feat(布置作业): 能按知识点点选题目;一个班一天能布置几节课;数据统计认老师布置的课;后台能管布置过的课
Deploy / deploy (push) Canceled after 0s

- 课堂看板「选题」弹框:左边知识点(加必会题),右边搜题号/题名、点选,按点的先后排;
  只列这次作业的语言能交的题(/problems、/problem-tags 加 ?language=)
- 一天多节课:0040 拿掉 class_lesson (class_name, day) 唯一约束,每次布置一行;
  看板「改」只改当前一节,加「新的一节」;这一节布置超过 100 分钟时先问这节课做哪几道题。
  不带 lessonId/newLesson 的老请求照旧改今天最后一节
- 数据统计「一行一节课」:布置过的课以布置为准(题目和顺序照布置、做完按全班算、标「布置」),
  这些提交不再参与推断切段,也不算零散;详情页「做完」注明是这一天/这节课/这段时间
- 后台「课堂作业」:一节一行,改(班级/日子/时刻/语言/题目)、看统计、删除、补一节;
  看板加「布置过的课」入口。题号校验和「做完」口径抽到 services/lesson.ts,三处共用

⚠️ 机房站点要和服务器一起升级:旧代码布置时 ON CONFLICT (class_name, day) 找不到约束会报错

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-11-02 20:41:25 +08:00
1 parent 0916015405
commit b344bd1dd6
26 files changed
+5685 -99

No files matched your search

+21 -1
View File
@@ -1,6 +1,7 @@
import {
AST_SUPPORTED_LANGUAGES,
isSqlProblem,
lessonLanguageSchema,
parseLanguage,
problemListSortSchema,
problemTypeFilterSchema,
@@ -71,6 +72,15 @@ function typeFilter(type: ProblemTypeFilter | undefined) {
return undefined
}
/**
* 课堂看板「选题」弹框只列能用这次作业的语言交的题(`?language=C`),左栏知识点的题数也按它数。
* 学生的题目列表不带这个参数
*/
function lessonLanguageFilter(raw: string | undefined) {
const language = lessonLanguageSchema.safeParse(raw).data
return language ? sql`${schema.problem.languages} @> ${JSON.stringify([language])}::jsonb` : undefined
}
type ProblemStates = Awaited<ReturnType<typeof problemStates>> | null
type UserCounts = Awaited<ReturnType<typeof problemUserCounts>>
@@ -137,6 +147,8 @@ problemRoutes.get('/problems', optionalAuth, async (c) => {
}
const byType = typeFilter(type)
if (byType) filters.push(byType)
const byLanguage = lessonLanguageFilter(c.req.query('language'))
if (byLanguage) filters.push(byLanguage)
const states = user ? await problemStates(user.id) : null
if (undone && states && states.solved.size > 0) filters.push(notInArray(schema.problem.id, [...states.solved]))
@@ -207,7 +219,15 @@ problemRoutes.get('/problem-tags', async (c) => {
})
.from(schema.problemTag)
.innerJoin(schema.problemTags, eq(schema.problemTags.problemtagId, schema.problemTag.id))
.innerJoin(schema.problem, and(eq(schema.problem.id, schema.problemTags.problemId), eq(schema.problem.visible, true), isNull(schema.problem.contestId)))
.innerJoin(
schema.problem,
and(
eq(schema.problem.id, schema.problemTags.problemId),
eq(schema.problem.visible, true),
isNull(schema.problem.contestId),
lessonLanguageFilter(c.req.query('language')),
),
)
.where(keyword ? ilike(schema.problemTag.name, `%${keyword}%`) : undefined)
.groupBy(schema.problemTag.id, schema.problemTag.name, schema.problemTag.category)
.having(sql`count(${schema.problemTags.problemId}) > 0`)