- Add actions-ai-evaluation, actions-analytics, actions-attachments, actions-calendar, actions-comments, actions-formative, actions-questions, actions-review, actions-substitutes - Add corresponding data-access layers for each new action module - Add calendar-view, curriculum-map-view, version-diff-viewer components - Add editor-slice, selection-slice, version-slice hooks for state management - Add document-diff and scope-check lib utilities - Add default-question-service and external-questions-bridge services
90 lines
3.3 KiB
TypeScript
90 lines
3.3 KiB
TypeScript
"use server";
|
||
|
||
import { revalidatePath } from "next/cache";
|
||
import { getTranslations } from "next-intl/server";
|
||
import {
|
||
requirePermission,
|
||
} from "@/shared/lib/auth-guard";
|
||
import { Permissions } from "@/shared/types/permissions";
|
||
import { handleActionError, safeParseDate } from "@/shared/lib/action-utils";
|
||
import { getLessonPlanById } from "./data-access";
|
||
import { publishLessonPlanHomework, PublishServiceError } from "./publish-service";
|
||
import { publishLessonPlanHomeworkSchema } from "./schema";
|
||
import { translateFieldErrors } from "./lib/i18n-errors";
|
||
import type { ActionState } from "./types";
|
||
|
||
export async function publishLessonPlanHomeworkAction(input: {
|
||
planId: string;
|
||
blockId: string;
|
||
classIds: string[];
|
||
availableAt?: string;
|
||
dueAt?: string;
|
||
}): Promise<ActionState<{ examId: string; assignmentId: string }>> {
|
||
const t = await getTranslations("lessonPreparation");
|
||
try {
|
||
const parsed = publishLessonPlanHomeworkSchema.safeParse(input);
|
||
if (!parsed.success) {
|
||
const errors = await translateFieldErrors(parsed.error.flatten().fieldErrors);
|
||
return { success: false, errors };
|
||
}
|
||
|
||
const ctx = await requirePermission(
|
||
Permissions.LESSON_PLAN_PUBLISH,
|
||
);
|
||
await requirePermission(Permissions.HOMEWORK_CREATE);
|
||
|
||
// P0-6 修复:先查询课案标题,用于作业标题的 i18n 翻译
|
||
// (原代码错误地传入 planId 作为 title 参数)
|
||
const plan = await getLessonPlanById(parsed.data.planId, ctx.userId);
|
||
if (!plan) {
|
||
return { success: false, message: t("error.notFound") };
|
||
}
|
||
const homeworkTitle = t("publish.homeworkTitle", { title: plan.title });
|
||
const homeworkDescription = t("publish.homeworkDescription");
|
||
const availableAtLabel = t("publish.availableAtLabel");
|
||
const dueAtLabel = t("publish.dueAtLabel");
|
||
const result = await publishLessonPlanHomework({
|
||
planId: parsed.data.planId,
|
||
blockId: parsed.data.blockId,
|
||
userId: ctx.userId,
|
||
classIds: parsed.data.classIds,
|
||
availableAt: parsed.data.availableAt
|
||
? safeParseDate(parsed.data.availableAt, availableAtLabel)
|
||
: undefined,
|
||
dueAt: parsed.data.dueAt
|
||
? safeParseDate(parsed.data.dueAt, dueAtLabel)
|
||
: undefined,
|
||
homeworkTitle,
|
||
homeworkDescription,
|
||
});
|
||
revalidatePath("/teacher/lesson-plans");
|
||
revalidatePath("/teacher/homework");
|
||
return {
|
||
success: true,
|
||
data: {
|
||
examId: result.examId,
|
||
assignmentId: result.assignmentId,
|
||
},
|
||
};
|
||
} catch (e) {
|
||
// publish-service 抛出 PublishServiceError(含 code),翻译为 i18n 消息
|
||
if (e instanceof PublishServiceError) {
|
||
const messageKey = PUBLISH_ERROR_KEY_MAP[e.code] ?? "error.publish";
|
||
return { success: false, message: t(messageKey) };
|
||
}
|
||
return handleActionError(e);
|
||
}
|
||
}
|
||
|
||
// publish-service 错误码 → i18n 键映射
|
||
const PUBLISH_ERROR_KEY_MAP: Record<string, string> = {
|
||
PLAN_NOT_FOUND: "publish.planNotFound",
|
||
NO_PERMISSION: "publish.noPermission",
|
||
NO_EXERCISE_BLOCK: "publish.noExerciseBlock",
|
||
NO_QUESTIONS: "publish.noQuestions",
|
||
ALREADY_PUBLISHED: "publish.alreadyPublished",
|
||
NO_SUBJECT_OR_GRADE: "publish.noSubjectOrGrade",
|
||
NO_STUDENTS: "publish.noStudents",
|
||
INVALID_QUESTION_TYPE: "error.invalidQuestionType",
|
||
};
|