- 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
155 lines
5.0 KiB
TypeScript
155 lines
5.0 KiB
TypeScript
"use server";
|
||
|
||
import { requirePermission } from "@/shared/lib/auth-guard";
|
||
import { handleActionError } from "@/shared/lib/action-utils";
|
||
import { Permissions } from "@/shared/types/permissions";
|
||
import { getKnowledgePointsByTextbookId } from "@/modules/textbooks/data-access";
|
||
import { suggestKnowledgePoints } from "./ai-suggest";
|
||
import { suggestKnowledgePointsSchema } from "./schema";
|
||
import { translateFieldErrors } from "./lib/i18n-errors";
|
||
import { generateLessonPlanFeedback, type AiFeedbackResult } from "./lib/ai-feedback";
|
||
import {
|
||
generateDifferentiationSuggestions,
|
||
checkCurriculumAlignment,
|
||
generateExplainableAssessment,
|
||
type DifferentiationSuggestion,
|
||
type CurriculumCheckItem,
|
||
type ExplainableAssessment,
|
||
} from "./lib/ai-differentiation";
|
||
import type { LessonPlanDocument } from "./types";
|
||
import type { ActionState } from "@/shared/types/action-state";
|
||
|
||
export async function suggestKnowledgePointsAction(input: {
|
||
doc: unknown;
|
||
textbookId?: string;
|
||
chapterId?: string;
|
||
}): Promise<
|
||
ActionState<{
|
||
suggestions: { id: string; name: string; reason: string }[];
|
||
}>
|
||
> {
|
||
try {
|
||
const parsed = suggestKnowledgePointsSchema.safeParse(input);
|
||
if (!parsed.success) {
|
||
const errors = await translateFieldErrors(parsed.error.flatten().fieldErrors);
|
||
return { success: false, errors };
|
||
}
|
||
|
||
// 并行校验两个权限点
|
||
await Promise.all([
|
||
requirePermission(Permissions.LESSON_PLAN_READ),
|
||
requirePermission(Permissions.AI_CHAT),
|
||
]);
|
||
|
||
// P1 修复:从 unknown 安全提取 nodes 数组,显式类型标注替代隐式 any
|
||
// Zod 已校验 doc 是 Record<string, unknown>,但 nodes 字段需运行时检查
|
||
const doc = parsed.data.doc;
|
||
const nodes: unknown[] = Array.isArray(doc.nodes) ? doc.nodes : [];
|
||
const suggestions = await suggestKnowledgePoints(
|
||
{ nodes },
|
||
parsed.data.textbookId,
|
||
parsed.data.chapterId,
|
||
);
|
||
return { success: true, data: { suggestions } };
|
||
} catch (e) {
|
||
// P1 修复:统一使用 handleActionError 处理错误,避免手动判断 PermissionDeniedError
|
||
return handleActionError(e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* V5-17 A1/A2:AI 反馈闭环。
|
||
* 调用 AI 对课案文档生成结构化反馈(含解释性展示)。
|
||
*/
|
||
export async function generateLessonPlanFeedbackAction(
|
||
doc: LessonPlanDocument,
|
||
): Promise<ActionState<AiFeedbackResult>> {
|
||
try {
|
||
await Promise.all([
|
||
requirePermission(Permissions.LESSON_PLAN_READ),
|
||
requirePermission(Permissions.AI_CHAT),
|
||
]);
|
||
const result = await generateLessonPlanFeedback(doc);
|
||
return { success: true, data: result };
|
||
} catch (e) {
|
||
return handleActionError(e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* V5-21 A3:AI 差异化教学建议生成。
|
||
* 根据课案内容生成基础/提高/拓展三个层次的教学建议。
|
||
*/
|
||
export async function generateDifferentiationSuggestionsAction(
|
||
doc: LessonPlanDocument,
|
||
): Promise<ActionState<{ items: DifferentiationSuggestion[] }>> {
|
||
try {
|
||
await Promise.all([
|
||
requirePermission(Permissions.LESSON_PLAN_READ),
|
||
requirePermission(Permissions.AI_CHAT),
|
||
]);
|
||
const items = await generateDifferentiationSuggestions(doc);
|
||
return { success: true, data: { items } };
|
||
} catch (e) {
|
||
return handleActionError(e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* V5-21 A4:AI 课标实时核对。
|
||
* 检查课案是否覆盖教材课标要求。
|
||
*/
|
||
export async function checkCurriculumAlignmentAction(
|
||
doc: LessonPlanDocument,
|
||
knowledgePoints: { id: string; name: string }[],
|
||
): Promise<ActionState<{ items: CurriculumCheckItem[] }>> {
|
||
try {
|
||
await Promise.all([
|
||
requirePermission(Permissions.LESSON_PLAN_READ),
|
||
requirePermission(Permissions.AI_CHAT),
|
||
]);
|
||
const items = await checkCurriculumAlignment(doc, knowledgePoints);
|
||
return { success: true, data: { items } };
|
||
} catch (e) {
|
||
return handleActionError(e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* V5-21 A5:AI 可解释评估。
|
||
* 对课案中的练习节点给出可解释的评分依据。
|
||
*/
|
||
export async function generateExplainableAssessmentAction(
|
||
doc: LessonPlanDocument,
|
||
): Promise<ActionState<{ items: ExplainableAssessment[] }>> {
|
||
try {
|
||
await Promise.all([
|
||
requirePermission(Permissions.LESSON_PLAN_READ),
|
||
requirePermission(Permissions.AI_CHAT),
|
||
]);
|
||
const items = await generateExplainableAssessment(doc);
|
||
return { success: true, data: { items } };
|
||
} catch (e) {
|
||
return handleActionError(e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* V5-21 A4 辅助:按教材 ID 获取知识点列表(仅 id + name),
|
||
* 供课标核对 Tab 按需加载,避免编辑页首屏负担。
|
||
*/
|
||
export async function getKnowledgePointsForAlignmentAction(
|
||
textbookId: string,
|
||
): Promise<ActionState<{ items: { id: string; name: string }[] }>> {
|
||
try {
|
||
await requirePermission(Permissions.LESSON_PLAN_READ);
|
||
const kps = await getKnowledgePointsByTextbookId(textbookId);
|
||
return {
|
||
success: true,
|
||
data: { items: kps.map((kp) => ({ id: kp.id, name: kp.name })) },
|
||
};
|
||
} catch (e) {
|
||
return handleActionError(e);
|
||
}
|
||
}
|