- 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
146 lines
4.1 KiB
TypeScript
146 lines
4.1 KiB
TypeScript
/**
|
|
* M2 协同备课 - 评论 Server Actions
|
|
*/
|
|
"use server";
|
|
|
|
import { revalidatePath } from "next/cache";
|
|
import { z } from "zod";
|
|
import {
|
|
getCommentsByPlanId,
|
|
getCommentsByBlockId,
|
|
createComment,
|
|
updateCommentContent,
|
|
toggleCommentResolved,
|
|
deleteComment,
|
|
countUnresolvedComments,
|
|
} from "./data-access-comments";
|
|
import type { LessonPlanComment } from "./data-access-comments";
|
|
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 createCommentSchema = z.object({
|
|
planId: z.string().min(1, "error.planIdRequired"),
|
|
blockId: z.string().min(1, "error.blockIdRequired"),
|
|
content: z.string().min(1, "error.contentRequired").max(2000, "error.contentTooLong"),
|
|
parentCommentId: z.string().optional(),
|
|
});
|
|
|
|
const updateCommentSchema = z.object({
|
|
commentId: z.string().min(1, "error.commentIdRequired"),
|
|
content: z.string().min(1, "error.contentRequired").max(2000, "error.contentTooLong"),
|
|
});
|
|
|
|
/**
|
|
* 查询课案评论
|
|
*/
|
|
export async function getLessonPlanCommentsAction(
|
|
planId: string,
|
|
blockId?: string,
|
|
): Promise<ActionState<{ items: LessonPlanComment[] }>> {
|
|
try {
|
|
await requirePermission(Permissions.LESSON_PLAN_READ);
|
|
const items = blockId
|
|
? await getCommentsByBlockId(planId, blockId)
|
|
: await getCommentsByPlanId(planId);
|
|
return { success: true, data: { items } };
|
|
} catch (e) {
|
|
return handleActionError(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 创建评论
|
|
*/
|
|
export async function createLessonPlanCommentAction(
|
|
input: Record<string, unknown>,
|
|
): Promise<ActionState<{ comment: LessonPlanComment }>> {
|
|
try {
|
|
await requirePermission(Permissions.LESSON_PLAN_UPDATE);
|
|
const parseResult = createCommentSchema.safeParse(input);
|
|
if (!parseResult.success) {
|
|
return {
|
|
success: false,
|
|
message: "Invalid input",
|
|
errors: Object.fromEntries(
|
|
Object.entries(parseResult.error.flatten().fieldErrors),
|
|
),
|
|
};
|
|
}
|
|
const auth = await getAuthContext();
|
|
if (!auth.userId) {
|
|
return { success: false, message: "Unauthorized" };
|
|
}
|
|
const comment = await createComment(parseResult.data, auth.userId);
|
|
revalidatePath(`/teacher/lesson-plans/${parseResult.data.planId}/edit`);
|
|
return { success: true, data: { comment } };
|
|
} catch (e) {
|
|
return handleActionError(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 更新评论
|
|
*/
|
|
export async function updateLessonPlanCommentAction(
|
|
input: Record<string, unknown>,
|
|
): Promise<ActionState<null>> {
|
|
try {
|
|
await requirePermission(Permissions.LESSON_PLAN_UPDATE);
|
|
const parseResult = updateCommentSchema.safeParse(input);
|
|
if (!parseResult.success) {
|
|
return { success: false, message: "Invalid input" };
|
|
}
|
|
await updateCommentContent(parseResult.data.commentId, parseResult.data.content);
|
|
return { success: true, data: null };
|
|
} catch (e) {
|
|
return handleActionError(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 切换评论解决状态
|
|
*/
|
|
export async function toggleCommentResolvedAction(
|
|
commentId: string,
|
|
): Promise<ActionState<null>> {
|
|
try {
|
|
await requirePermission(Permissions.LESSON_PLAN_UPDATE);
|
|
await toggleCommentResolved(commentId);
|
|
return { success: true, data: null };
|
|
} catch (e) {
|
|
return handleActionError(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 删除评论
|
|
*/
|
|
export async function deleteLessonPlanCommentAction(
|
|
commentId: string,
|
|
): Promise<ActionState<null>> {
|
|
try {
|
|
await requirePermission(Permissions.LESSON_PLAN_UPDATE);
|
|
await deleteComment(commentId);
|
|
return { success: true, data: null };
|
|
} catch (e) {
|
|
return handleActionError(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 统计未解决评论数(用于列表卡片徽章)
|
|
*/
|
|
export async function getUnresolvedCommentCountAction(
|
|
planId: string,
|
|
): Promise<ActionState<{ count: number }>> {
|
|
try {
|
|
await requirePermission(Permissions.LESSON_PLAN_READ);
|
|
const count = await countUnresolvedComments(planId);
|
|
return { success: true, data: { count } };
|
|
} catch (e) {
|
|
return handleActionError(e);
|
|
}
|
|
}
|