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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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