fix(课堂任务): 后台改一节不再把它排到同一分钟布置的另一节前面;学生管理员算进统计的课

- 后台「改」时刻只填到分,原来一保存就把布置时刻截成整分:先画流程图、紧接着再开一节写代码,
  只改了做什么,「今天最后一节」就换回了画流程图那节。没改时刻就留着原来的
- 数据统计「一行一节课」只数 Regular User,学生管理员的提交不算,和「做完 / 全班」的花名册对不上
- 学生「老师改了这节课」:画流程图改成写 C、先画再写改成写代码,原来写「语言 改成 C语言」,
  看不出不用画了;现在写「做法 改成 写代码 · C语言」
- 学生课堂标题按每道题合起来的要求写:先画流程图、再用这几道题写代码时,不再只写「用 C语言做」
- 先画再写的题,切到别的题再回来不再被拽回画布(原来只记最后一道题)
- 课堂任务顶上写代码的课也写「写代码」;讲错误的弹层开着时不再因为 15 秒刷新换了个人

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-11-03 09:26:39 +08:00
1 parent b09d479893
commit c3f1413545
7 files changed
+67 -25

No files matched your search

+5 -1
View File
@@ -161,10 +161,14 @@ adminLessonRoutes.put('/lessons/:id', requireTeacher, async (c) => {
// 班级能改:原来那个班和新的班学生看到的都可能变,两边各比一次
const classes = await affectedClasses(id, result.values.className)
const before = await Promise.all(classes.map((className) => todayLessonSnapshot(className)))
// 时刻只填到分:没改时刻就留着原来带秒的那个。不然只改了做什么,这一节也被挪到这一分钟的开头,
// 排到同一分钟里后布置的那节前面去 —— 「今天最后一节」就换了人(先画流程图、紧接着再开一节写代码,常在同一分钟里)
const [current] = await db.select({ createdAt: schema.classLesson.createdAt }).from(schema.classLesson).where(eq(schema.classLesson.id, id))
const sameMinute = current && Math.floor(Date.parse(current.createdAt) / 60_000) * 60_000 === Date.parse(result.values.createdAt)
// 布置的老师不换,和任务页上「改」一样认第一次
const [row] = await db
.update(schema.classLesson)
.set({ ...result.values, updatedAt: new Date().toISOString() })
.set({ ...result.values, createdAt: sameMinute ? current.createdAt : result.values.createdAt, updatedAt: new Date().toISOString() })
.where(eq(schema.classLesson.id, id))
.returning({ id: schema.classLesson.id })
if (!row) return failure(c, 404, 'lesson-not-found', '这一节已经被删掉了')
+9 -4
View File
@@ -527,6 +527,11 @@ const LESSON_LIVE_MINUTES = 15
/** 一次列多少节课。「一节课」「今天」一般一两行,「这学期」「全部」有几百节,先给最近的这些 */
const LESSONS_DEFAULT = 50
const LESSONS_MAX = 500
/** 课只算学生(STUDENT_ROLES,学生管理员也算),老师试题的提交不算 */
const studentRoles = sql.join(
STUDENT_ROLES.map((role) => sql`${role}`),
sql`, `,
)
type StatisticsRange = { start: string | null; end: string }
@@ -599,7 +604,7 @@ async function assignedLessons(plans: LessonPlan[], range: StatisticsRange) {
where s.contest_id is null and p.mode <> 'flowchart'
and s.create_time <= ${range.end}
${range.start ? sql`and s.create_time >= ${range.start}` : sql``}
and u.admin_type = 'Regular User' and u.is_disabled = false
and u.admin_type in (${studentRoles}) and u.is_disabled = false
union all
select p.k, f.user_id, f.problem_id, (f.status = 2 and coalesce(f.ai_grade in (${sql.join(
FLOWCHART_PASS_GRADES.map((grade) => sql`${grade}`),
@@ -612,7 +617,7 @@ async function assignedLessons(plans: LessonPlan[], range: StatisticsRange) {
where p.mode <> 'code'
and f.create_time <= ${range.end}
${range.start ? sql`and f.create_time >= ${range.start}` : sql``}
and u.admin_type = 'Regular User' and u.is_disabled = false
and u.admin_type in (${studentRoles}) and u.is_disabled = false
)
select k, problem_id,
count(distinct user_id)::int as users,
@@ -721,8 +726,8 @@ submissionStatisticsRoutes.get('/submissions/statistics/lessons', requireTeacher
where s.contest_id is null
and s.create_time <= ${range.end}
${range.start ? sql`and s.create_time >= ${range.start}` : sql``}
-- 只算普通学生:老师试题的提交不该把一段拼成「一节课」
and u.admin_type = 'Regular User' and u.is_disabled = false
-- 只算学生(学生管理员也是学生):老师试题的提交不该把一段拼成「一节课」
and u.admin_type in (${studentRoles}) and u.is_disabled = false
),
g as (
select *,