feat(grades): add appeals, drafts, import, report card, and growth archive
- 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
This commit is contained in:
122
src/modules/grades/actions-draft.ts
Normal file
122
src/modules/grades/actions-draft.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
"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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user