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:
158
src/modules/lesson-preparation/actions-formative.ts
Normal file
158
src/modules/lesson-preparation/actions-formative.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* M5 形成性评价闭环 - Server Actions
|
||||
*/
|
||||
"use server";
|
||||
|
||||
import { z } from "zod";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import {
|
||||
getFormativeItemsByPlanId,
|
||||
createFormativeItem,
|
||||
updateFormativeItem,
|
||||
deleteFormativeItem,
|
||||
submitFormativeResponse,
|
||||
getResponsesByItemId,
|
||||
getFormativeItemStats,
|
||||
} from "./data-access-formative";
|
||||
import type { FormativeItem, FormativeResponse } from "./data-access-formative";
|
||||
import type { ActionState } from "@/shared/types/action-state";
|
||||
import { getAuthContext, requirePermission } from "@/shared/lib/auth-guard";
|
||||
import { handleActionError } from "@/shared/lib/action-utils";
|
||||
import { Permissions } from "@/shared/types/permissions";
|
||||
|
||||
const createFormativeItemSchema = z.object({
|
||||
planId: z.string().min(1),
|
||||
blockId: z.string().min(1),
|
||||
interactionType: z.enum(["poll", "quiz", "exit_ticket"]),
|
||||
payload: z.record(z.string(), z.unknown()),
|
||||
instantFeedback: z.boolean().optional(),
|
||||
orderIndex: z.number().int().min(0).optional(),
|
||||
});
|
||||
|
||||
const submitResponseSchema = z.object({
|
||||
itemId: z.string().min(1),
|
||||
classId: z.string().optional(),
|
||||
response: z.record(z.string(), z.unknown()),
|
||||
isCorrect: z.boolean().optional(),
|
||||
durationSec: z.number().int().min(0).optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
* 查询课案互动组件
|
||||
*/
|
||||
export async function getFormativeItemsAction(
|
||||
planId: string,
|
||||
): Promise<ActionState<{ items: FormativeItem[] }>> {
|
||||
try {
|
||||
await requirePermission(Permissions.LESSON_PLAN_READ);
|
||||
const items = await getFormativeItemsByPlanId(planId);
|
||||
return { success: true, data: { items } };
|
||||
} catch (e) {
|
||||
return handleActionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建互动组件
|
||||
*/
|
||||
export async function createFormativeItemAction(
|
||||
input: Record<string, unknown>,
|
||||
): Promise<ActionState<{ item: FormativeItem }>> {
|
||||
try {
|
||||
await requirePermission(Permissions.LESSON_PLAN_UPDATE);
|
||||
const parseResult = createFormativeItemSchema.safeParse(input);
|
||||
if (!parseResult.success) {
|
||||
return { success: false, message: "Invalid input" };
|
||||
}
|
||||
const item = await createFormativeItem(parseResult.data);
|
||||
revalidatePath(`/teacher/lesson-plans/${parseResult.data.planId}/edit`);
|
||||
return { success: true, data: { item } };
|
||||
} catch (e) {
|
||||
return handleActionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新互动组件
|
||||
*/
|
||||
export async function updateFormativeItemAction(
|
||||
id: string,
|
||||
patch: { payload?: unknown; instantFeedback?: boolean; orderIndex?: number },
|
||||
): Promise<ActionState<null>> {
|
||||
try {
|
||||
await requirePermission(Permissions.LESSON_PLAN_UPDATE);
|
||||
await updateFormativeItem(id, patch);
|
||||
return { success: true, data: null };
|
||||
} catch (e) {
|
||||
return handleActionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除互动组件
|
||||
*/
|
||||
export async function deleteFormativeItemAction(id: string): Promise<ActionState<null>> {
|
||||
try {
|
||||
await requirePermission(Permissions.LESSON_PLAN_UPDATE);
|
||||
await deleteFormativeItem(id);
|
||||
return { success: true, data: null };
|
||||
} catch (e) {
|
||||
return handleActionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 学生提交作答
|
||||
*/
|
||||
export async function submitFormativeResponseAction(
|
||||
input: Record<string, unknown>,
|
||||
): Promise<ActionState<{ response: FormativeResponse }>> {
|
||||
try {
|
||||
await requirePermission(Permissions.LESSON_PLAN_READ);
|
||||
const parseResult = submitResponseSchema.safeParse(input);
|
||||
if (!parseResult.success) {
|
||||
return { success: false, message: "Invalid input" };
|
||||
}
|
||||
const auth = await getAuthContext();
|
||||
if (!auth.userId) {
|
||||
return { success: false, message: "Unauthorized" };
|
||||
}
|
||||
const response = await submitFormativeResponse({
|
||||
...parseResult.data,
|
||||
studentId: auth.userId,
|
||||
});
|
||||
return { success: true, data: { response } };
|
||||
} catch (e) {
|
||||
return handleActionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询互动组件作答列表(教师查看)
|
||||
*/
|
||||
export async function getFormativeResponsesAction(
|
||||
itemId: string,
|
||||
): Promise<ActionState<{ responses: FormativeResponse[] }>> {
|
||||
try {
|
||||
await requirePermission(Permissions.LESSON_PLAN_READ);
|
||||
const responses = await getResponsesByItemId(itemId);
|
||||
return { success: true, data: { responses } };
|
||||
} catch (e) {
|
||||
return handleActionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询互动组件统计(教师实时反馈)
|
||||
*/
|
||||
export async function getFormativeItemStatsAction(
|
||||
itemId: string,
|
||||
): Promise<ActionState<{ stats: Awaited<ReturnType<typeof getFormativeItemStats>> }>> {
|
||||
try {
|
||||
await requirePermission(Permissions.LESSON_PLAN_READ);
|
||||
const stats = await getFormativeItemStats(itemId);
|
||||
return { success: true, data: { stats } };
|
||||
} catch (e) {
|
||||
return handleActionError(e);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user