/** * V5-7:课案-课时绑定数据访问层 * * 将课案绑定到具体班级的某个日期/节次,支持 calendar-view 安排课时。 */ import "server-only"; import { db } from "@/shared/db"; import { lessonPlanSchedules, classes } from "@/shared/db/schema"; import { and, eq, gte, lte, desc } from "drizzle-orm"; import { createId } from "@paralleldrive/cuid2"; /** 课时绑定记录 */ export interface LessonPlanScheduleRecord { id: string; planId: string; classId: string; className: string; scheduledDate: string; // YYYY-MM-DD period: number; classScheduleId?: string | null; durationMin: number; createdBy: string; createdAt: Date; updatedAt: Date; } /** 将 Date 转换为 YYYY-MM-DD 字符串(用于接口输出) */ function toDateStr(d: Date): string { const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, "0"); const day = String(d.getDate()).padStart(2, "0"); return `${y}-${m}-${day}`; } /** 查询课案的所有课时绑定 */ export async function getSchedulesByPlanId( planId: string, ): Promise { const rows = await db .select({ id: lessonPlanSchedules.id, planId: lessonPlanSchedules.planId, classId: lessonPlanSchedules.classId, className: classes.name, scheduledDate: lessonPlanSchedules.scheduledDate, period: lessonPlanSchedules.period, classScheduleId: lessonPlanSchedules.classScheduleId, durationMin: lessonPlanSchedules.durationMin, createdBy: lessonPlanSchedules.createdBy, createdAt: lessonPlanSchedules.createdAt, updatedAt: lessonPlanSchedules.updatedAt, }) .from(lessonPlanSchedules) .leftJoin(classes, eq(lessonPlanSchedules.classId, classes.id)) .where(eq(lessonPlanSchedules.planId, planId)) .orderBy(desc(lessonPlanSchedules.scheduledDate)); return rows.map((r) => ({ id: r.id, planId: r.planId, classId: r.classId, className: r.className ?? "", scheduledDate: toDateStr(r.scheduledDate), period: r.period, classScheduleId: r.classScheduleId, durationMin: r.durationMin, createdBy: r.createdBy, createdAt: r.createdAt, updatedAt: r.updatedAt, })); } /** 查询教师在某日期范围内的课时绑定 */ export async function getSchedulesByDateRange( teacherPlanIds: string[], startDate: string, endDate: string, ): Promise { if (teacherPlanIds.length === 0) return []; const rows = await db .select({ id: lessonPlanSchedules.id, planId: lessonPlanSchedules.planId, classId: lessonPlanSchedules.classId, className: classes.name, scheduledDate: lessonPlanSchedules.scheduledDate, period: lessonPlanSchedules.period, classScheduleId: lessonPlanSchedules.classScheduleId, durationMin: lessonPlanSchedules.durationMin, createdBy: lessonPlanSchedules.createdBy, createdAt: lessonPlanSchedules.createdAt, updatedAt: lessonPlanSchedules.updatedAt, }) .from(lessonPlanSchedules) .leftJoin(classes, eq(lessonPlanSchedules.classId, classes.id)) .where( and( // 简化:仅按日期范围过滤(planId 过滤由调用方处理或 join) gte(lessonPlanSchedules.scheduledDate, new Date(startDate)), lte(lessonPlanSchedules.scheduledDate, new Date(endDate)), ), ) .orderBy(desc(lessonPlanSchedules.scheduledDate)); return rows .filter((r) => teacherPlanIds.includes(r.planId)) .map((r) => ({ id: r.id, planId: r.planId, classId: r.classId, className: r.className ?? "", scheduledDate: toDateStr(r.scheduledDate), period: r.period, classScheduleId: r.classScheduleId, durationMin: r.durationMin, createdBy: r.createdBy, createdAt: r.createdAt, updatedAt: r.updatedAt, })); } /** 创建课时绑定 */ export async function createSchedule(input: { planId: string; classId: string; scheduledDate: string; period: number; classScheduleId?: string; durationMin?: number; createdBy: string; }): Promise { const newId = createId(); await db.insert(lessonPlanSchedules).values({ id: newId, planId: input.planId, classId: input.classId, scheduledDate: new Date(input.scheduledDate), period: input.period, classScheduleId: input.classScheduleId, durationMin: input.durationMin ?? 40, createdBy: input.createdBy, }); const created = await db .select({ id: lessonPlanSchedules.id, planId: lessonPlanSchedules.planId, classId: lessonPlanSchedules.classId, className: classes.name, scheduledDate: lessonPlanSchedules.scheduledDate, period: lessonPlanSchedules.period, classScheduleId: lessonPlanSchedules.classScheduleId, durationMin: lessonPlanSchedules.durationMin, createdBy: lessonPlanSchedules.createdBy, createdAt: lessonPlanSchedules.createdAt, updatedAt: lessonPlanSchedules.updatedAt, }) .from(lessonPlanSchedules) .leftJoin(classes, eq(lessonPlanSchedules.classId, classes.id)) .where(eq(lessonPlanSchedules.id, newId)); const r = created[0]!; return { id: r.id, planId: r.planId, classId: r.classId, className: r.className ?? "", scheduledDate: toDateStr(r.scheduledDate), period: r.period, classScheduleId: r.classScheduleId, durationMin: r.durationMin, createdBy: r.createdBy, createdAt: r.createdAt, updatedAt: r.updatedAt, }; } /** 删除课时绑定 */ export async function deleteSchedule(id: string): Promise { await db.delete(lessonPlanSchedules).where(eq(lessonPlanSchedules.id, id)); }