feat(lesson-preparation): add AI evaluation, analytics, attachments, calendar, comments, review, substitutes, formative, and version diff

- 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
This commit is contained in:
SpecialX
2026-07-03 10:25:21 +08:00
parent a16f09d3c3
commit 20023e13fd
75 changed files with 5131 additions and 1186 deletions

View File

@@ -1,33 +1,38 @@
"use server";
import { getTranslations } from "next-intl/server";
import { requirePermission, PermissionDeniedError } from "@/shared/lib/auth-guard";
import { requirePermission } from "@/shared/lib/auth-guard";
import { handleActionError } from "@/shared/lib/action-utils";
import { Permissions } from "@/shared/types/permissions";
import {
getKnowledgePointsByTextbookId,
getKnowledgePointsByChapterId,
} from "@/modules/textbooks/data-access";
import { getKnowledgePointOptionsSchema } from "./schema";
import { translateFieldErrors } from "./lib/i18n-errors";
import type { ActionState } from "./types";
// 知识点选项类型P1 修复:显式类型标注替代隐式 any
type KnowledgePointOption = { id: string; name: string };
// 加载知识点选项(供客户端知识点选择器使用)
export async function getKnowledgePointOptionsAction(input: {
textbookId?: string;
chapterId?: string;
}): Promise<
ActionState<{ options: { id: string; name: string }[] }>
ActionState<{ options: KnowledgePointOption[] }>
> {
const t = await getTranslations("lessonPreparation");
try {
const parsed = getKnowledgePointOptionsSchema.safeParse(input);
if (!parsed.success) {
return { success: false, errors: parsed.error.flatten().fieldErrors };
const errors = await translateFieldErrors(parsed.error.flatten().fieldErrors);
return { success: false, errors };
}
await requirePermission(Permissions.LESSON_PLAN_READ);
if (!parsed.data.textbookId) return { success: true, data: { options: [] } };
let kps;
// P1 修复:显式类型标注替代 `let kps;` 隐式 any
let kps: KnowledgePointOption[];
if (parsed.data.chapterId) {
kps = await getKnowledgePointsByChapterId(parsed.data.chapterId);
} else {
@@ -40,8 +45,7 @@ export async function getKnowledgePointOptionsAction(input: {
},
};
} catch (e) {
if (e instanceof PermissionDeniedError)
return { success: false, message: e.message };
return { success: false, message: t("error.loadKnowledgePoints") };
// P1 修复:统一使用 handleActionError 处理错误
return handleActionError(e);
}
}