- Add actions-appeal, actions-draft, actions-import, actions-lock for grade workflow - Add data-access-appeals, data-access-drafts, data-access-exam-entry for data layer - Add batch-grade-entry-dialog, batch-grade-entry-stats, batch-grade-entry-table - Add draft-lock-banner, excel-import-dialog for import and draft management - Add growth-archive-chart, knowledge-point-mastery-chart for analytics - Add report-card-view, report-card-print-action, report-card-print-button - Add import-export, lib/notify, lib/report-card, scope-check test, stats-service test - Add hooks directory
123 lines
3.2 KiB
TypeScript
123 lines
3.2 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 type { ActionState } from "@/shared/types/action-state"
|
|
import { handleActionError } from "@/shared/lib/action-utils"
|
|
|
|
import {
|
|
saveGradeDraft,
|
|
getGradeDraft,
|
|
deleteGradeDraft,
|
|
type GradeDraftData,
|
|
} from "./data-access-drafts"
|
|
import { assertClassInScope } from "./lib/scope-check"
|
|
|
|
/**
|
|
* P1-1 拆分:从 actions.ts 抽取的成绩录入草稿 Server Actions。
|
|
* 对应 data-access 的 saveGradeDraft / getGradeDraft / deleteGradeDraft。
|
|
*/
|
|
|
|
/**
|
|
* 保存成绩录入草稿到服务端(跨设备同步)。
|
|
*/
|
|
export async function saveGradeDraftAction(params: {
|
|
classId: string
|
|
subjectId: string
|
|
type: string
|
|
scores: Record<string, string>
|
|
}): Promise<ActionState<{ saved: true }>> {
|
|
try {
|
|
const ctx = await requirePermission(Permissions.GRADE_RECORD_MANAGE)
|
|
const t = await getTranslations("grades")
|
|
|
|
// 校验班级权限
|
|
const scopeError = assertClassInScope(ctx.dataScope, params.classId)
|
|
if (scopeError) {
|
|
return { success: false, message: t(`action.scopeError.${scopeError}`) }
|
|
}
|
|
|
|
const data: GradeDraftData = {
|
|
scores: params.scores,
|
|
timestamp: Date.now(),
|
|
}
|
|
|
|
await saveGradeDraft({
|
|
userId: ctx.userId,
|
|
classId: params.classId,
|
|
subjectId: params.subjectId,
|
|
type: params.type,
|
|
data,
|
|
})
|
|
|
|
return { success: true, data: { saved: true } }
|
|
} catch (e) {
|
|
return handleActionError(e)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取成绩录入草稿(跨设备恢复)。
|
|
*/
|
|
export async function getGradeDraftAction(params: {
|
|
classId: string
|
|
subjectId: string
|
|
type: string
|
|
}): Promise<ActionState<GradeDraftData | null>> {
|
|
try {
|
|
const ctx = await requirePermission(Permissions.GRADE_RECORD_READ)
|
|
const t = await getTranslations("grades")
|
|
|
|
// 校验班级权限
|
|
const scopeError = assertClassInScope(ctx.dataScope, params.classId)
|
|
if (scopeError) {
|
|
return { success: false, message: t(`action.scopeError.${scopeError}`) }
|
|
}
|
|
|
|
const draft = await getGradeDraft({
|
|
userId: ctx.userId,
|
|
classId: params.classId,
|
|
subjectId: params.subjectId,
|
|
type: params.type,
|
|
})
|
|
|
|
return { success: true, data: draft }
|
|
} catch (e) {
|
|
return handleActionError(e)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 删除成绩录入草稿(提交成功后调用)。
|
|
*/
|
|
export async function deleteGradeDraftAction(params: {
|
|
classId: string
|
|
subjectId: string
|
|
type: string
|
|
}): Promise<ActionState<{ deleted: true }>> {
|
|
try {
|
|
const ctx = await requirePermission(Permissions.GRADE_RECORD_MANAGE)
|
|
const t = await getTranslations("grades")
|
|
|
|
// 校验班级权限
|
|
const scopeError = assertClassInScope(ctx.dataScope, params.classId)
|
|
if (scopeError) {
|
|
return { success: false, message: t(`action.scopeError.${scopeError}`) }
|
|
}
|
|
|
|
await deleteGradeDraft({
|
|
userId: ctx.userId,
|
|
classId: params.classId,
|
|
subjectId: params.subjectId,
|
|
type: params.type,
|
|
})
|
|
|
|
revalidatePath("/teacher/grades")
|
|
return { success: true, data: { deleted: true } }
|
|
} catch (e) {
|
|
return handleActionError(e)
|
|
}
|
|
}
|