Files
NextEdu/src/modules/classes/data-access-schedule.ts
SpecialX dfffb61e94 feat(homework,classes,course-plans): add scans, student data, take confirm, error boundaries, dialogs, hooks, calendar
homework:

- Add data-access-scans, data-access-student, data-access-utils, data-access-exam-cross

- Add excellent-submissions, homework-take-confirm-dialog, homework-take-sidebar components

classes:

- Add class-delete-dialog, class-error-boundary, class-form-dialog, class-form-utils

- Add class-list-table, class-list-toolbar, class-skeleton

- Add schedule-create-dialog, schedule-delete-dialog, schedule-edit-dialog, schedule-utils

- Add data-access-teacher and hooks directory

course-plans:

- Add course-plan-calendar, sortable-week-row, template-picker-dialog components

- Add lib directory
2026-07-03 10:25:35 +08:00

112 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import "server-only";
import { cache } from "react"
import { and, asc, eq, inArray, type SQL } from "drizzle-orm"
import { db } from "@/shared/db"
import {
classes,
classEnrollments,
classSchedule,
} from "@/shared/db/schema"
import type {
ClassScheduleItem,
StudentScheduleItem,
} from "./types"
import {
getAccessibleClassIdsForTeacher,
getSessionTeacherId,
} from "./data-access"
/**
* 根据课表项 ID 获取其所属班级 IDP0-3 审计修复:供 actions-schedule 越权校验使用)。
* classes 模块对 classSchedule 表有读权限usedBy 含 classes
*/
export async function getClassIdByScheduleId(scheduleId: string): Promise<string | null> {
const [row] = await db
.select({ classId: classSchedule.classId })
.from(classSchedule)
.where(eq(classSchedule.id, scheduleId))
.limit(1)
return row?.classId ?? null
}
const isWeekday = (n: unknown): n is 1 | 2 | 3 | 4 | 5 | 6 | 7 =>
typeof n === "number" && n >= 1 && n <= 7 && Number.isInteger(n)
const toWeekday = (n: number): 1 | 2 | 3 | 4 | 5 | 6 | 7 =>
isWeekday(n) ? n : 1
export const getStudentSchedule = cache(async (studentId: string): Promise<StudentScheduleItem[]> => {
const id = studentId.trim()
if (!id) return []
const rows = await db
.select({
id: classSchedule.id,
classId: classSchedule.classId,
className: classes.name,
weekday: classSchedule.weekday,
startTime: classSchedule.startTime,
endTime: classSchedule.endTime,
course: classSchedule.course,
location: classSchedule.location,
})
.from(classEnrollments)
.innerJoin(classes, eq(classes.id, classEnrollments.classId))
.innerJoin(classSchedule, eq(classSchedule.classId, classes.id))
.where(and(eq(classEnrollments.studentId, id), eq(classEnrollments.status, "active")))
.orderBy(asc(classSchedule.weekday), asc(classSchedule.startTime))
return rows.map((r) => ({
id: r.id,
classId: r.classId,
className: r.className,
weekday: toWeekday(r.weekday),
startTime: r.startTime,
endTime: r.endTime,
course: r.course,
location: r.location,
}))
})
export const getClassSchedule = cache(
async (params?: { classId?: string; teacherId?: string }): Promise<ClassScheduleItem[]> => {
const teacherId = params?.teacherId ?? (await getSessionTeacherId())
if (!teacherId) return []
const classId = params?.classId?.trim()
const accessibleIds = await getAccessibleClassIdsForTeacher(teacherId)
if (accessibleIds.length === 0) return []
const conditions: SQL[] = [inArray(classes.id, accessibleIds)]
if (classId) conditions.push(eq(classSchedule.classId, classId))
const rows = await db
.select({
id: classSchedule.id,
classId: classSchedule.classId,
weekday: classSchedule.weekday,
startTime: classSchedule.startTime,
endTime: classSchedule.endTime,
course: classSchedule.course,
location: classSchedule.location,
})
.from(classSchedule)
.innerJoin(classes, eq(classes.id, classSchedule.classId))
.where(and(...conditions))
.orderBy(asc(classSchedule.weekday), asc(classSchedule.startTime))
return rows.map((r) => ({
id: r.id,
classId: r.classId,
weekday: toWeekday(r.weekday),
startTime: r.startTime,
endTime: r.endTime,
course: r.course,
location: r.location,
}))
}
)