"use server"; import { revalidatePath } from "next/cache"; import { getTranslations } from "next-intl/server"; import { requirePermission, PermissionDeniedError } from "@/shared/lib/auth-guard"; import { Permissions } from "@/shared/types/permissions"; import { getLessonPlans, getLessonPlanById, createLessonPlan, updateLessonPlanContent, softDeleteLessonPlan, duplicateLessonPlan, LessonPlanDataError, } from "./data-access"; import { getLessonPlanVersions, createLessonPlanVersion, revertToVersion, pruneAutoVersions, } from "./data-access-versions"; import { getLessonPlanTemplates, saveAsTemplate, deletePersonalTemplate, } from "./data-access-templates"; import { createLessonPlanSchema, updateLessonPlanContentSchema, saveVersionSchema, revertVersionSchema, saveAsTemplateSchema, } from "./schema"; import type { ActionState, LessonPlanDocument } from "./types"; // ---- 课案列表 ---- export async function getLessonPlansAction(params: { query?: string; textbookId?: string; chapterId?: string; subjectId?: string; status?: string; }): Promise< ActionState<{ items: Awaited>; }> > { const t = await getTranslations("lessonPreparation"); try { const ctx = await requirePermission(Permissions.LESSON_PLAN_READ); const items = await getLessonPlans(params, ctx.dataScope, ctx.userId); return { success: true, data: { items } }; } catch (e) { if (e instanceof PermissionDeniedError) return { success: false, message: e.message }; return { success: false, message: t("error.getList") }; } } // ---- 单课案 ---- export async function getLessonPlanByIdAction( planId: string, ): Promise< ActionState<{ plan: Awaited> }> > { const t = await getTranslations("lessonPreparation"); try { const ctx = await requirePermission(Permissions.LESSON_PLAN_READ); const plan = await getLessonPlanById(planId, ctx.userId); if (!plan) return { success: false, message: t("error.notFound") }; return { success: true, data: { plan } }; } catch (e) { if (e instanceof PermissionDeniedError) return { success: false, message: e.message }; return { success: false, message: t("error.getOne") }; } } // ---- 创建 ---- export async function createLessonPlanAction( prevState: ActionState | null, formData: FormData, ): Promise> { const t = await getTranslations("lessonPreparation"); try { const ctx = await requirePermission(Permissions.LESSON_PLAN_CREATE); const parsed = createLessonPlanSchema.safeParse({ title: formData.get("title"), textbookId: formData.get("textbookId") || undefined, chapterId: formData.get("chapterId") || undefined, subjectId: formData.get("subjectId") || undefined, gradeId: formData.get("gradeId") || undefined, templateId: formData.get("templateId"), }); if (!parsed.success) { return { success: false, errors: parsed.error.flatten().fieldErrors }; } const { planId } = await createLessonPlan({ ...parsed.data, creatorId: ctx.userId, // V2-2 修复:传入翻译函数,将 SYSTEM_TEMPLATES 中的 i18n 键翻译为实际文本 translateTitle: (key: string) => { // 支持 "blockType.objective" 和 "template.blocks.tpl_review.1" 两种键 if (key.startsWith("blockType.")) { const blockKey = key.replace("blockType.", ""); return t(`blockType.${blockKey}`); } if (key.startsWith("template.blocks.")) { const parts = key.split("."); const templateId = parts[2]; const blockIndex = parts[3]; return t(`template.blocks.${templateId}.${blockIndex}`); } return key; }, }); revalidatePath("/teacher/lesson-plans"); return { success: true, data: { planId } }; } catch (e) { if (e instanceof PermissionDeniedError) return { success: false, message: e.message }; if (e instanceof LessonPlanDataError && e.code === "TEMPLATE_NOT_FOUND") return { success: false, message: t("error.templateNotFound") }; return { success: false, message: t("error.create") }; } } // ---- 更新 content(自动保存)---- export async function updateLessonPlanAction(input: { planId: string; title?: string; content: LessonPlanDocument; }): Promise { const t = await getTranslations("lessonPreparation"); try { const ctx = await requirePermission(Permissions.LESSON_PLAN_UPDATE); const parsed = updateLessonPlanContentSchema.safeParse(input); if (!parsed.success) return { success: false, errors: parsed.error.flatten().fieldErrors }; await updateLessonPlanContent(parsed.data.planId, ctx.userId, { ...(parsed.data.title ? { title: parsed.data.title } : {}), content: parsed.data.content as LessonPlanDocument, }); return { success: true }; } catch (e) { if (e instanceof PermissionDeniedError) return { success: false, message: e.message }; return { success: false, message: t("error.save") }; } } // ---- 手动保存版本 ---- export async function saveLessonPlanVersionAction(input: { planId: string; content: LessonPlanDocument; label?: string; }): Promise> { const t = await getTranslations("lessonPreparation"); try { const ctx = await requirePermission(Permissions.LESSON_PLAN_UPDATE); const parsed = saveVersionSchema.safeParse(input); if (!parsed.success) return { success: false, errors: parsed.error.flatten().fieldErrors }; const { versionNo } = await createLessonPlanVersion({ planId: parsed.data.planId, content: input.content, userId: ctx.userId, isAuto: false, label: parsed.data.label, }); await pruneAutoVersions(parsed.data.planId); return { success: true, data: { versionNo } }; } catch (e) { if (e instanceof PermissionDeniedError) return { success: false, message: e.message }; return { success: false, message: t("error.saveVersion") }; } } // ---- 版本列表 ---- export async function getLessonPlanVersionsAction( planId: string, ): Promise< ActionState<{ versions: Awaited>; }> > { const t = await getTranslations("lessonPreparation"); try { const ctx = await requirePermission(Permissions.LESSON_PLAN_READ); const versions = await getLessonPlanVersions(planId, ctx.userId); return { success: true, data: { versions } }; } catch (e) { if (e instanceof PermissionDeniedError) return { success: false, message: e.message }; return { success: false, message: t("error.getVersions") }; } } // ---- 回退版本 ---- export async function revertLessonPlanVersionAction(input: { planId: string; versionNo: number; }): Promise> { const t = await getTranslations("lessonPreparation"); try { const ctx = await requirePermission(Permissions.LESSON_PLAN_UPDATE); const parsed = revertVersionSchema.safeParse(input); if (!parsed.success) return { success: false, errors: parsed.error.flatten().fieldErrors }; const result = await revertToVersion( parsed.data.planId, parsed.data.versionNo, ctx.userId, ); if (!result) return { success: false, message: t("error.versionNotFound") }; revalidatePath(`/teacher/lesson-plans/${parsed.data.planId}/edit`); return { success: true, data: { newVersionNo: result.newVersionNo } }; } catch (e) { if (e instanceof PermissionDeniedError) return { success: false, message: e.message }; return { success: false, message: t("error.revert") }; } } // ---- 删除(软删除)---- export async function deleteLessonPlanAction( planId: string, ): Promise { const t = await getTranslations("lessonPreparation"); try { const ctx = await requirePermission(Permissions.LESSON_PLAN_DELETE); await softDeleteLessonPlan(planId, ctx.userId); revalidatePath("/teacher/lesson-plans"); return { success: true }; } catch (e) { if (e instanceof PermissionDeniedError) return { success: false, message: e.message }; return { success: false, message: t("error.delete") }; } } // ---- 复制 ---- export async function duplicateLessonPlanAction( planId: string, ): Promise> { const t = await getTranslations("lessonPreparation"); try { const ctx = await requirePermission(Permissions.LESSON_PLAN_CREATE); const { newPlanId } = await duplicateLessonPlan(planId, ctx.userId); revalidatePath("/teacher/lesson-plans"); return { success: true, data: { newPlanId } }; } catch (e) { if (e instanceof PermissionDeniedError) return { success: false, message: e.message }; if (e instanceof LessonPlanDataError && e.code === "NOT_FOUND") return { success: false, message: t("error.notFound") }; return { success: false, message: t("error.duplicate") }; } } // ---- 模板列表 ---- export async function getLessonPlanTemplatesAction(): Promise< ActionState<{ templates: Awaited>; }> > { const t = await getTranslations("lessonPreparation"); try { const ctx = await requirePermission(Permissions.LESSON_PLAN_READ); const templates = await getLessonPlanTemplates(ctx.userId); return { success: true, data: { templates } }; } catch (e) { if (e instanceof PermissionDeniedError) return { success: false, message: e.message }; return { success: false, message: t("error.getTemplates") }; } } // ---- 另存为模板 ---- export async function saveAsTemplateAction(input: { sourcePlanId: string; name: string; }): Promise> { const t = await getTranslations("lessonPreparation"); try { const ctx = await requirePermission(Permissions.LESSON_PLAN_CREATE); const parsed = saveAsTemplateSchema.safeParse(input); if (!parsed.success) return { success: false, errors: parsed.error.flatten().fieldErrors }; const { templateId } = await saveAsTemplate({ ...parsed.data, userId: ctx.userId, }); return { success: true, data: { templateId } }; } catch (e) { if (e instanceof PermissionDeniedError) return { success: false, message: e.message }; if (e instanceof LessonPlanDataError && e.code === "NOT_FOUND") return { success: false, message: t("error.notFound") }; return { success: false, message: t("error.saveTemplate") }; } } // ---- 删除模板 ---- export async function deleteTemplateAction( templateId: string, ): Promise { const t = await getTranslations("lessonPreparation"); try { const ctx = await requirePermission(Permissions.LESSON_PLAN_DELETE); await deletePersonalTemplate(templateId, ctx.userId); return { success: true }; } catch (e) { if (e instanceof PermissionDeniedError) return { success: false, message: e.message }; return { success: false, message: t("error.deleteTemplate") }; } }