diff --git a/src/modules/adaptive-practice/actions.ts b/src/modules/adaptive-practice/actions.ts index 09b3c5a..9f6d789 100644 --- a/src/modules/adaptive-practice/actions.ts +++ b/src/modules/adaptive-practice/actions.ts @@ -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) { diff --git a/src/modules/attendance/actions.ts b/src/modules/attendance/actions.ts index fe59cef..cd77a04 100644 --- a/src/modules/attendance/actions.ts +++ b/src/modules/attendance/actions.ts @@ -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, diff --git a/src/modules/diagnostic/actions.ts b/src/modules/diagnostic/actions.ts index 225ef90..a8dc452 100644 --- a/src/modules/diagnostic/actions.ts +++ b/src/modules/diagnostic/actions.ts @@ -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) diff --git a/src/modules/error-book/actions.ts b/src/modules/error-book/actions.ts index d5a5802..c915212 100644 --- a/src/modules/error-book/actions.ts +++ b/src/modules/error-book/actions.ts @@ -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, diff --git a/src/modules/exams/actions-rich-editor.ts b/src/modules/exams/actions-rich-editor.ts index 53ba505..3296a72 100644 --- a/src/modules/exams/actions-rich-editor.ts +++ b/src/modules/exams/actions-rich-editor.ts @@ -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) { diff --git a/src/modules/exams/actions.ts b/src/modules/exams/actions.ts index fe311e6..07cf15c 100644 --- a/src/modules/exams/actions.ts +++ b/src/modules/exams/actions.ts @@ -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(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(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(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(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(t("dbDuplicateFailed")) } - revalidatePath("/teacher/exams/all") + await invalidateFor("exams.duplicate") // V3-4: 埋点监控 await trackExamEvent("exam.duplicated", { diff --git a/src/modules/grades/actions-appeal.ts b/src/modules/grades/actions-appeal.ts index 5b3cb69..91646a2 100644 --- a/src/modules/grades/actions-appeal.ts +++ b/src/modules/grades/actions-appeal.ts @@ -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" diff --git a/src/modules/grades/actions-draft.ts b/src/modules/grades/actions-draft.ts index ca061b7..88995df 100644 --- a/src/modules/grades/actions-draft.ts +++ b/src/modules/grades/actions-draft.ts @@ -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) diff --git a/src/modules/grades/actions-import.ts b/src/modules/grades/actions-import.ts index 78ca678..63586fa 100644 --- a/src/modules/grades/actions-import.ts +++ b/src/modules/grades/actions-import.ts @@ -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, diff --git a/src/modules/grades/actions.ts b/src/modules/grades/actions.ts index 2e638d2..f0f3457 100644 --- a/src/modules/grades/actions.ts +++ b/src/modules/grades/actions.ts @@ -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 }), diff --git a/src/modules/homework/actions.ts b/src/modules/homework/actions.ts index 033b001..c1b5541 100644 --- a/src/modules/homework/actions.ts +++ b/src/modules/homework/actions.ts @@ -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, diff --git a/src/modules/leave-requests/actions.ts b/src/modules/leave-requests/actions.ts index 4d462ae..9e29bdb 100644 --- a/src/modules/leave-requests/actions.ts +++ b/src/modules/leave-requests/actions.ts @@ -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,