refactor(actions): 组 B 模块迁移至 invalidateFor(exams/grades/homework/attendance/leave-requests/diagnostic/error-book/adaptive-practice)

This commit is contained in:
SpecialX
2026-07-05 22:44:38 +08:00
parent 05e68a3dad
commit 7cfd85d0f5
12 changed files with 60 additions and 78 deletions

View File

@@ -1,8 +1,7 @@
"use server"
import { revalidatePath } from "next/cache"
import { requirePermission, PermissionDeniedError } from "@/shared/lib/auth-guard"
import { invalidateFor } from "@/shared/lib/cache"
import { Permissions, type AuthContext } from "@/shared/types/permissions"
import type { ActionState } from "@/shared/types/action-state"
import { handleActionError } from "@/shared/lib/action-utils"
@@ -221,7 +220,7 @@ export async function createPracticeSessionAction(
questionCount: parsed.data.questionCount,
})
revalidatePath("/student/practice")
await invalidateFor("adaptive-practice.create")
return {
success: true,
@@ -269,7 +268,7 @@ export async function submitPracticeAnswerAction(
skip ?? false,
)
revalidatePath(`/student/practice/${sessionId}`)
await invalidateFor("adaptive-practice.submit")
return {
success: true,
@@ -305,8 +304,7 @@ export async function completePracticeSessionAction(
await completePracticeSession(parsed.data.sessionId, targetStudentId)
revalidatePath("/student/practice")
revalidatePath(`/student/practice/${parsed.data.sessionId}`)
await invalidateFor("adaptive-practice.complete")
return { success: true }
} catch (e) {
@@ -339,7 +337,7 @@ export async function abandonPracticeSessionAction(
await abandonPracticeSession(parsed.data.sessionId, targetStudentId)
revalidatePath("/student/practice")
await invalidateFor("adaptive-practice.abandon")
return { success: true }
} catch (e) {

View File

@@ -1,7 +1,7 @@
"use server"
import { revalidatePath } from "next/cache"
import { getTranslations } from "next-intl/server"
import { invalidateFor } from "@/shared/lib/cache"
import { requirePermission } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
import type { ActionState } from "@/shared/types/action-state"
@@ -95,7 +95,7 @@ export async function recordAttendanceAction(
}
const id = await createAttendanceRecord(parsed.data, ctx.userId)
revalidatePath("/teacher/attendance")
await invalidateFor("attendance.record")
await trackEvent({
event: "attendance.recorded",
userId: ctx.userId,
@@ -135,7 +135,7 @@ export async function batchRecordAttendanceAction(
}
const count = await batchCreateAttendanceRecords(parsed.data, ctx.userId)
revalidatePath("/teacher/attendance")
await invalidateFor("attendance.batchRecord")
await trackEvent({
event: "attendance.batch_recorded",
userId: ctx.userId,
@@ -196,7 +196,7 @@ export async function updateAttendanceAction(
}
await updateAttendanceRecord(id, parsed.data)
revalidatePath("/teacher/attendance")
await invalidateFor("attendance.update")
await trackEvent({
event: "attendance.updated",
userId: ctx.userId,
@@ -223,7 +223,7 @@ export async function deleteAttendanceAction(
}
await deleteAttendanceRecord(id)
revalidatePath("/teacher/attendance")
await invalidateFor("attendance.delete")
await trackEvent({
event: "attendance.deleted",
userId: ctx.userId,
@@ -265,7 +265,7 @@ export async function saveAttendanceRulesAction(
}
const id = await upsertAttendanceRules(parsed.data)
revalidatePath("/teacher/attendance")
await invalidateFor("attendance.saveRules")
await trackEvent({
event: "attendance.rules_saved",
userId: ctx.userId,

View File

@@ -1,7 +1,7 @@
"use server"
import { revalidatePath } from "next/cache"
import { requirePermission } from "@/shared/lib/auth-guard"
import { invalidateFor } from "@/shared/lib/cache"
import { Permissions } from "@/shared/types/permissions"
import type { ActionState } from "@/shared/types/action-state"
import { handleActionError } from "@/shared/lib/action-utils"
@@ -48,8 +48,7 @@ export async function generateStudentReportAction(
const { studentId, period } = parsed.data
const id = await generateDiagnosticReport(studentId, period, ctx.userId)
revalidatePath("/teacher/diagnostic")
revalidatePath(`/teacher/diagnostic/student/${studentId}`)
await invalidateFor("diagnostic.generateStudentReport")
return { success: true, message: "Diagnostic report generated", data: id }
} catch (e) {
return handleActionError(e)
@@ -74,8 +73,7 @@ export async function generateClassReportAction(
const { classId, period } = parsed.data
const id = await generateClassDiagnosticReport(classId, period, ctx.userId)
revalidatePath("/teacher/diagnostic")
revalidatePath(`/teacher/diagnostic/class/${classId}`)
await invalidateFor("diagnostic.generateClassReport")
return { success: true, message: "Class diagnostic report generated", data: id }
} catch (e) {
return handleActionError(e)
@@ -100,8 +98,7 @@ export async function generateGradeReportAction(
const { gradeId, period } = parsed.data
const id = await generateGradeDiagnosticReport(gradeId, period, ctx.userId)
revalidatePath("/teacher/diagnostic")
revalidatePath("/admin/diagnostic")
await invalidateFor("diagnostic.generateGradeReport")
return { success: true, message: "Grade diagnostic report generated", data: id }
} catch (e) {
return handleActionError(e)
@@ -130,7 +127,7 @@ export async function publishReportAction(
// - 班级报告:通知全班学生 + 全班学生家长
const report = await getDiagnosticReportById(parsed.data.id)
if (!report) {
revalidatePath("/teacher/diagnostic")
await invalidateFor("diagnostic.publish")
return { success: true, message: "Report published" }
}
@@ -186,7 +183,7 @@ export async function publishReportAction(
}
}
revalidatePath("/teacher/diagnostic")
await invalidateFor("diagnostic.publish")
return { success: true, message: "Report published" }
} catch (e) {
return handleActionError(e)
@@ -209,7 +206,7 @@ export async function deleteReportAction(
}
await deleteDiagnosticReport(parsed.data.id)
revalidatePath("/teacher/diagnostic")
await invalidateFor("diagnostic.delete")
return { success: true, message: "Report deleted" }
} catch (e) {
return handleActionError(e)

View File

@@ -1,9 +1,9 @@
"use server"
import { revalidatePath } from "next/cache"
import { z } from "zod"
import { requirePermission, PermissionDeniedError } from "@/shared/lib/auth-guard"
import { invalidateFor } from "@/shared/lib/cache"
import { Permissions } from "@/shared/types/permissions"
import type { ActionState } from "@/shared/types/action-state"
@@ -162,7 +162,7 @@ export async function createErrorBookItemAction(
}
const id = await createErrorBookItem(ctx.userId, parsed.data)
revalidatePath("/student/error-book")
await invalidateFor("error-book.create")
return { success: true, message: "错题已添加", data: id }
} catch (e) {
@@ -198,7 +198,7 @@ export async function updateErrorBookNoteAction(
const { itemId, ...input } = parsed.data
await updateErrorBookNote(itemId, ctx.userId, input)
revalidatePath("/student/error-book")
await invalidateFor("error-book.update")
return { success: true, message: "笔记已更新" }
} catch (e) {
@@ -233,7 +233,7 @@ export async function reviewErrorBookItemAction(
await recordReview(parsed.data.itemId, ctx.userId, parsed.data.result)
revalidatePath("/student/error-book")
await invalidateFor("error-book.review")
return { success: true, message: "复习结果已记录" }
} catch (e) {
@@ -259,7 +259,7 @@ export async function deleteErrorBookItemAction(
await deleteErrorBookItem(itemId, ctx.userId)
revalidatePath("/student/error-book")
await invalidateFor("error-book.delete")
return { success: true, message: "错题已删除" }
} catch (e) {
@@ -285,7 +285,7 @@ export async function archiveErrorBookItemAction(
await archiveErrorBookItem(itemId, ctx.userId)
revalidatePath("/student/error-book")
await invalidateFor("error-book.archive")
return { success: true, message: "错题已归档" }
} catch (e) {
@@ -322,7 +322,7 @@ export async function collectFromSubmissionAction(
? await collectFromExamSubmission(parsed.data.submissionId, ctx.userId)
: await collectFromHomeworkSubmission(parsed.data.submissionId, ctx.userId)
revalidatePath("/student/error-book")
await invalidateFor("error-book.collect")
return {
success: true,

View File

@@ -9,8 +9,8 @@
* - 共享辅助函数convertStructureNode递归转换 EditorStructureNode → AiGeneratedStructureNode
*/
import { revalidatePath } from "next/cache"
import { getTranslations } from "next-intl/server"
import { invalidateFor } from "@/shared/lib/cache"
import type { ActionState } from "@/shared/types/action-state"
import { requirePermission, PermissionDeniedError } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
@@ -169,7 +169,7 @@ export async function createExamFromRichEditorAction(
examModeConfig: parseExamModeConfig(formData),
})
revalidatePath("/teacher/exams/all")
await invalidateFor("exams.create")
return successState(context.examId, t("exam.actionMessages.draftCreated"))
} catch (error) {
if (error instanceof PermissionDeniedError) {
@@ -279,8 +279,7 @@ export async function updateExamFromRichEditorAction(
orderedQuestions,
})
revalidatePath("/teacher/exams/all")
revalidatePath(`/teacher/exams/${input.examId}/build`)
await invalidateFor("exams.update")
return successState(input.examId, t("exam.actionMessages.examUpdated"))
} catch (error) {
if (error instanceof PermissionDeniedError) {

View File

@@ -1,7 +1,7 @@
"use server"
import { revalidatePath } from "next/cache"
import { getTranslations } from "next-intl/server"
import { invalidateFor } from "@/shared/lib/cache"
import type { ActionState } from "@/shared/types/action-state"
import { requirePermission, PermissionDeniedError } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
@@ -193,7 +193,7 @@ export async function createExamAction(
return failState<string>(t("dbCreateFailed"))
}
revalidatePath("/teacher/exams/all")
await invalidateFor("exams.create")
return successState(context.examId, t("createdSuccess"))
} catch (error) {
@@ -314,7 +314,7 @@ export async function createAiExamAction(
return failState<string>(t("dbCreateFailed"))
}
revalidatePath("/teacher/exams/all")
await invalidateFor("exams.createAi")
// V3-4: 埋点监控AI 生成考试)
await trackExamEvent("exam.ai_generated", {
@@ -496,7 +496,7 @@ export async function updateExamAction(
return failState<string>(t("dbUpdateFailed"))
}
revalidatePath("/teacher/exams/all")
await invalidateFor("exams.update")
// V3-4: 埋点监控
await trackExamEvent("exam.updated", {
@@ -554,7 +554,7 @@ export async function deleteExamAction(
return failState<string>(t("dbDeleteFailed"))
}
revalidatePath("/teacher/exams/all")
await invalidateFor("exams.delete")
// V3-4: 埋点监控
await trackExamEvent("exam.deleted", {
@@ -608,7 +608,7 @@ export async function duplicateExamAction(
return failState<string>(t("dbDuplicateFailed"))
}
revalidatePath("/teacher/exams/all")
await invalidateFor("exams.duplicate")
// V3-4: 埋点监控
await trackExamEvent("exam.duplicated", {

View File

@@ -1,7 +1,7 @@
"use server"
import { revalidatePath } from "next/cache"
import { getTranslations } from "next-intl/server"
import { invalidateFor } from "@/shared/lib/cache"
import { requirePermission } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
import type { ActionState } from "@/shared/types/action-state"
@@ -83,7 +83,7 @@ export async function createGradeAppealAction(
expectedScore: parsed.data.expectedScore,
})
revalidatePath("/student/grades")
await invalidateFor("grades.appeal.create")
return { success: true, message: t("appeal.created"), data: id }
} catch (e) {
return handleActionError(e)
@@ -101,7 +101,7 @@ export async function withdrawGradeAppealAction(
await withdrawGradeAppeal({ appealId, studentId: ctx.userId })
revalidatePath("/student/grades")
await invalidateFor("grades.appeal.withdraw")
return { success: true, data: { withdrawn: true } }
} catch (e) {
return handleActionError(e)
@@ -176,8 +176,7 @@ export async function reviewGradeAppealAction(
adjustedScore: parsed.data.adjustedScore,
})
revalidatePath("/teacher/grades")
revalidatePath("/student/grades")
await invalidateFor("grades.appeal.review")
return {
success: true,
message: parsed.data.decision === "approved"

View File

@@ -1,7 +1,7 @@
"use server"
import { revalidatePath } from "next/cache"
import { getTranslations } from "next-intl/server"
import { invalidateFor } from "@/shared/lib/cache"
import { requirePermission } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
import type { ActionState } from "@/shared/types/action-state"
@@ -114,7 +114,7 @@ export async function deleteGradeDraftAction(params: {
type: params.type,
})
revalidatePath("/teacher/grades")
await invalidateFor("grades.draft.delete")
return { success: true, data: { deleted: true } }
} catch (e) {
return handleActionError(e)

View File

@@ -1,7 +1,7 @@
"use server"
import { revalidatePath } from "next/cache"
import { getTranslations } from "next-intl/server"
import { invalidateFor } from "@/shared/lib/cache"
import { requirePermission } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
@@ -207,9 +207,7 @@ export async function importGradesFromExcelAction(
}
// 刷新缓存
revalidatePath("/teacher/grades")
revalidatePath("/teacher/grades/entry")
revalidatePath(`/teacher/grades?classId=${parsed.data.classId}`)
await invalidateFor("grades.import")
const result: GradeImportResult = {
successCount: createdIds.length,

View File

@@ -1,7 +1,7 @@
"use server"
import { revalidatePath } from "next/cache"
import { getTranslations } from "next-intl/server"
import { invalidateFor } from "@/shared/lib/cache"
import { requirePermission } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
import type { ActionState } from "@/shared/types/action-state"
@@ -119,7 +119,7 @@ export async function createGradeRecordAction(
} catch {
// 通知失败不阻断成绩录入
}
revalidatePath("/teacher/grades")
await invalidateFor("grades.create")
return { success: true, message: t("action.recordCreated"), data: id }
} catch (e) {
return handleActionError(e)
@@ -204,7 +204,7 @@ export async function batchCreateGradeRecordsAction(
// 异常检测失败不阻断成绩录入
}
revalidatePath("/teacher/grades")
await invalidateFor("grades.batchCreate")
return {
success: true,
message: t("action.recordsCreated", { count: ids.length }),
@@ -338,7 +338,7 @@ export async function batchCreateGradeRecordsByExamAction(
// 异常检测失败不阻断成绩录入
}
revalidatePath("/teacher/grades")
await invalidateFor("grades.batchCreateByExam")
return {
success: true,
message: t("action.recordsCreated", { count: ids.length }),
@@ -370,7 +370,7 @@ export async function undoBatchCreateGradeRecordsAction(
}
const deletedCount = await undoBatchCreateGradeRecords(ids, ctx.userId)
revalidatePath("/teacher/grades")
await invalidateFor("grades.undoBatchCreate")
return {
success: true,
message: t("action.recordsUndoneCount", { count: deletedCount }),
@@ -409,7 +409,7 @@ export async function updateGradeRecordAction(
}
await updateGradeRecord(id, parsed.data)
revalidatePath("/teacher/grades")
await invalidateFor("grades.update")
return { success: true, message: t("action.recordUpdated") }
} catch (e) {
return handleActionError(e)
@@ -433,7 +433,7 @@ export async function deleteGradeRecordAction(
}
await deleteGradeRecord(parsed.data.id)
revalidatePath("/teacher/grades")
await invalidateFor("grades.delete")
return { success: true, message: t("action.recordDeleted") }
} catch (e) {
return handleActionError(e)
@@ -461,7 +461,7 @@ export async function bulkDeleteGradeRecordsAction(
}
const deletedCount = await bulkDeleteGradeRecords(ids)
revalidatePath("/teacher/grades")
await invalidateFor("grades.bulkDelete")
return {
success: true,
message: t("action.recordsDeletedCount", { count: deletedCount }),

View File

@@ -1,7 +1,7 @@
"use server"
import { revalidatePath } from "next/cache"
import { createId } from "@paralleldrive/cuid2"
import { invalidateFor } from "@/shared/lib/cache"
import { getTranslations } from "next-intl/server"
import { requirePermission } from "@/shared/lib/auth-guard"
@@ -206,8 +206,7 @@ export async function createHomeworkAssignmentAction(
targetStudentIds,
})
revalidatePath("/teacher/homework/assignments")
revalidatePath("/teacher/homework/submissions")
await invalidateFor("homework.create")
// V3-4: 埋点监控
await trackExamEvent("homework.created", {
@@ -241,7 +240,7 @@ export async function startHomeworkSubmissionAction(
return { success: false, message: result.error }
}
revalidatePath("/student/learning/assignments")
await invalidateFor("homework.startSubmission")
await trackExamEvent("homework.started", {
userId: ctx.userId,
@@ -327,8 +326,7 @@ export async function submitHomeworkAction(
await runPostGradingHooks(submissionId, ctx.userId)
}
revalidatePath("/teacher/homework/submissions")
revalidatePath("/student/learning/assignments")
await invalidateFor("homework.submit")
// V3-4: 埋点监控
await trackExamEvent("homework.submitted", {
@@ -397,7 +395,7 @@ export async function gradeHomeworkSubmissionAction(
// 批改完成后自动采集错题 + 更新掌握度
await runPostGradingHooks(submissionId, submissionForGrading.studentId)
revalidatePath("/teacher/homework/submissions")
await invalidateFor("homework.grade")
// V3-4: 埋点监控
await trackExamEvent("homework.graded", {
@@ -466,8 +464,7 @@ export async function batchAutoGradeSubmissionsAction(
.map((r) => runPostGradingHooks(r.submissionId, r.studentId)),
)
revalidatePath("/teacher/homework/submissions")
revalidatePath("/teacher/homework/assignments")
await invalidateFor("homework.batchAutoGrade")
await trackExamEvent("homework.graded", {
userId: ctx.userId,

View File

@@ -1,7 +1,7 @@
"use server"
import { revalidatePath } from "next/cache"
import { getTranslations } from "next-intl/server"
import { invalidateFor } from "@/shared/lib/cache"
import { requirePermission } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
import type { ActionState } from "@/shared/types/action-state"
@@ -89,9 +89,7 @@ export async function createLeaveRequestAction(
}
const id = await createLeaveRequest(parsed.data, ctx.userId)
revalidatePath("/parent/leave")
revalidatePath("/student/leave")
revalidatePath("/teacher/leave")
await invalidateFor("leave-requests.create")
await trackEvent({
event: "leave_request.created",
userId: ctx.userId,
@@ -175,9 +173,7 @@ export async function reviewLeaveRequestAction(
}
}
revalidatePath("/teacher/leave")
revalidatePath("/parent/leave")
revalidatePath(`/teacher/leave/${id}`)
await invalidateFor("leave-requests.review")
await trackEvent({
event: "leave_request.reviewed",
userId: ctx.userId,
@@ -221,9 +217,7 @@ export async function cancelLeaveRequestAction(
return { success: false, message: t("errors.cannotCancel") }
}
revalidatePath("/parent/leave")
revalidatePath("/student/leave")
revalidatePath("/teacher/leave")
await invalidateFor("leave-requests.cancel")
await trackEvent({
event: "leave_request.cancelled",
userId: ctx.userId,