Files
NextEdu/src/modules/lesson-preparation/data-access-schedules.ts
SpecialX 25dca843be feat(lesson-preparation): major update with AI features, schedules, and new components
- Add actions-schedules.ts and data-access-schedules.ts for schedule management

- Add AI differentiation, AI feedback, consistency check dialogs

- Add attachment-picker, curriculum-heatmap, print-view, version-diff-view

- Add lesson-plan-mobile-view and schedule-dialog components

- Add lib: ai-differentiation, ai-feedback, auto-layout, consistency-check,

  curriculum-coverage, export, version-diff

- Add history-slice hook for version history

- Update existing components, hooks, providers, services, types

- Add teacher lesson-plans heatmap and library pages
2026-07-04 10:22:10 +08:00

182 lines
5.6 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.
/**
* 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<LessonPlanScheduleRecord[]> {
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<LessonPlanScheduleRecord[]> {
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<LessonPlanScheduleRecord> {
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<void> {
await db.delete(lessonPlanSchedules).where(eq(lessonPlanSchedules.id, id));
}