refactor(classes): actions 迁移至 invalidateFor 集中编排

This commit is contained in:
SpecialX
2026-07-05 18:38:58 +08:00
parent 220d702b44
commit 80dd67780c
6 changed files with 42 additions and 56 deletions

View File

@@ -1,6 +1,6 @@
"use server"
import { revalidatePath } from "next/cache"
import { invalidateFor } from "@/shared/lib/cache"
import { getTranslations } from "next-intl/server"
import { requirePermission } from "@/shared/lib/auth-guard"
import { handleActionError } from "@/shared/lib/action-utils"
@@ -55,10 +55,7 @@ export async function createAdminClassAction(
homeroom: homeroom ?? null,
room: room ?? null,
})
revalidatePath("/admin/school/classes")
revalidatePath("/teacher/classes/my")
revalidatePath("/teacher/classes/students")
revalidatePath("/teacher/classes/schedule")
await invalidateFor("classes.create")
return { success: true, message: t("actions.classCreated"), data: id }
} catch (error) {
return handleActionError(error)
@@ -114,10 +111,7 @@ export async function updateAdminClassAction(
})
}
revalidatePath("/admin/school/classes")
revalidatePath("/teacher/classes/my")
revalidatePath("/teacher/classes/students")
revalidatePath("/teacher/classes/schedule")
await invalidateFor("classes.update", { id: validatedClassId })
return { success: true, message: t("actions.classUpdated") }
} catch (error) {
return handleActionError(error)
@@ -139,10 +133,7 @@ export async function deleteAdminClassAction(classId: string): Promise<ActionSta
try {
await deleteAdminClass(parsed.data.classId)
revalidatePath("/admin/school/classes")
revalidatePath("/teacher/classes/my")
revalidatePath("/teacher/classes/students")
revalidatePath("/teacher/classes/schedule")
await invalidateFor("classes.delete", { id: parsed.data.classId })
return { success: true, message: t("actions.classDeleted") }
} catch (error) {
return handleActionError(error)

View File

@@ -1,6 +1,6 @@
"use server"
import { revalidatePath } from "next/cache"
import { invalidateFor } from "@/shared/lib/cache"
import { getTranslations } from "next-intl/server"
import { requirePermission } from "@/shared/lib/auth-guard"
import { handleActionError } from "@/shared/lib/action-utils"
@@ -63,7 +63,7 @@ export async function createGradeClassAction(
homeroom: homeroom ?? null,
room: room ?? null,
})
revalidatePath("/management/grade/classes")
await invalidateFor("classes.create")
return { success: true, message: t("actions.classCreated"), data: id }
} catch (error) {
return handleActionError(error)
@@ -138,7 +138,7 @@ export async function updateGradeClassAction(
})
}
revalidatePath("/management/grade/classes")
await invalidateFor("classes.update", { id: validatedClassId })
return { success: true, message: t("actions.classUpdated") }
} catch (error) {
return handleActionError(error)
@@ -173,7 +173,7 @@ export async function deleteGradeClassAction(classId: string): Promise<ActionSta
try {
await deleteAdminClass(validatedClassId)
revalidatePath("/management/grade/classes")
await invalidateFor("classes.delete", { id: validatedClassId })
return { success: true, message: t("actions.classDeleted") }
} catch (error) {
return handleActionError(error)

View File

@@ -1,6 +1,6 @@
"use server"
import { revalidatePath } from "next/cache"
import { invalidateFor } from "@/shared/lib/cache"
import { getTranslations } from "next-intl/server"
import { requirePermission } from "@/shared/lib/auth-guard"
import { handleActionError } from "@/shared/lib/action-utils"
@@ -60,8 +60,7 @@ export async function enrollStudentByEmailAction(
try {
await enrollStudentByEmail(parsed.data.classId, parsed.data.email)
revalidatePath("/teacher/classes/students")
revalidatePath("/teacher/classes/my")
await invalidateFor("classes.students.update", { classId: parsed.data.classId })
return { success: true, message: t("actions.studentAdded") }
} catch (error) {
return handleActionError(error)
@@ -130,13 +129,7 @@ export async function joinClassByInvitationCodeAction(
},
})
if (hasStudentScope(ctx)) {
revalidatePath("/student/learning/courses")
revalidatePath("/student/schedule")
} else {
revalidatePath("/teacher/classes/my")
}
revalidatePath("/profile")
await invalidateFor("classes.students.update", { classId })
return { success: true, message: t("actions.joinedClass"), data: { classId } }
} catch (error) {
// 审计日志:加入失败
@@ -170,8 +163,7 @@ export async function ensureClassInvitationCodeAction(classId: string): Promise<
try {
const code = await ensureClassInvitationCode(classId)
revalidatePath("/teacher/classes/my")
revalidatePath(`/teacher/classes/my/${encodeURIComponent(classId)}`)
await invalidateFor("classes.invitation.create", { classId })
return { success: true, message: t("actions.invitationCodeReady"), data: { code } }
} catch (error) {
return handleActionError(error)
@@ -192,8 +184,7 @@ export async function regenerateClassInvitationCodeAction(classId: string): Prom
try {
const code = await regenerateClassInvitationCode(classId)
revalidatePath("/teacher/classes/my")
revalidatePath(`/teacher/classes/my/${encodeURIComponent(classId)}`)
await invalidateFor("classes.invitation.create", { classId })
return { success: true, message: t("actions.invitationCodeUpdated"), data: { code } }
} catch (error) {
return handleActionError(error)
@@ -267,9 +258,7 @@ export async function createClassInvitationCodeAction(
},
})
revalidatePath("/teacher/classes/my")
revalidatePath(`/teacher/classes/my/${encodeURIComponent(classId)}`)
revalidatePath(`/admin/school/classes`)
await invalidateFor("classes.invitation.create", { classId })
return {
success: true,
message: t("actions.invitationCodeGenerated"),
@@ -300,7 +289,8 @@ export async function revokeClassInvitationCodeAction(
}
try {
const { revokeInvitationCode } = await import("./data-access-invitations")
const { revokeInvitationCode, getClassIdByCodeId } = await import("./data-access-invitations")
const classId = await getClassIdByCodeId(codeId)
await revokeInvitationCode(codeId, ctx.userId)
const { logAudit } = await import("@/shared/lib/audit-logger")
@@ -312,8 +302,9 @@ export async function revokeClassInvitationCodeAction(
detail: { revokedBy: ctx.userId },
})
revalidatePath("/teacher/classes/my")
revalidatePath(`/admin/school/classes`)
if (classId) {
await invalidateFor("classes.invitation.revoke", { classId })
}
return { success: true, message: t("actions.invitationCodeRevoked") }
} catch (error) {
return handleActionError(error)
@@ -383,8 +374,7 @@ export async function setStudentEnrollmentStatusAction(
try {
await setStudentEnrollmentStatus(classId, studentId, status)
revalidatePath("/teacher/classes/students")
revalidatePath("/teacher/classes/my")
await invalidateFor("classes.students.update", { classId })
return { success: true, message: t("actions.studentUpdated") }
} catch (error) {
return handleActionError(error)
@@ -448,8 +438,7 @@ export async function bulkEnrollStudentsAction(
}
}
revalidatePath("/teacher/classes/students")
revalidatePath("/admin/school/classes")
await invalidateFor("classes.students.update", { classId })
return {
success: true,
message: t("actions.bulkImportResult", { imported, failed }),
@@ -510,7 +499,7 @@ export async function bulkAssignSubjectTeachersAction(
}
}
revalidatePath("/admin/school/classes")
// 当前为 stub 实现(无 DB 写入),暂不触发缓存失效
return {
success: true,
message: t("actions.bulkAssignResult", { updated, failed }),

View File

@@ -1,6 +1,6 @@
"use server"
import { revalidatePath } from "next/cache"
import { invalidateFor } from "@/shared/lib/cache"
import { getTranslations } from "next-intl/server"
import { requirePermission } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
@@ -72,7 +72,7 @@ export async function createClassScheduleItemAction(
course,
location: location ?? null,
})
revalidatePath("/teacher/classes/schedule")
await invalidateFor("classes.schedule.update", { classId })
return { success: true, message: t("actions.scheduleItemCreated"), data: id }
} catch (error) {
return handleActionError(error)
@@ -126,7 +126,7 @@ export async function updateClassScheduleItemAction(
course: course ?? undefined,
location: location ?? undefined,
})
revalidatePath("/teacher/classes/schedule")
await invalidateFor("classes.schedule.update", { classId: targetClassId })
return { success: true, message: t("actions.scheduleItemUpdated") }
} catch (error) {
return handleActionError(error)
@@ -156,7 +156,7 @@ export async function deleteClassScheduleItemAction(scheduleId: string): Promise
try {
await deleteClassScheduleItem(parsed.data.scheduleId)
revalidatePath("/teacher/classes/schedule")
await invalidateFor("classes.schedule.update", { classId: targetClassId })
return { success: true, message: t("actions.scheduleItemDeleted") }
} catch (error) {
return handleActionError(error)

View File

@@ -1,6 +1,6 @@
"use server"
import { revalidatePath } from "next/cache"
import { invalidateFor } from "@/shared/lib/cache"
import { getTranslations } from "next-intl/server"
import { requirePermission } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
@@ -67,9 +67,7 @@ export async function createTeacherClassAction(
homeroom: homeroom ?? null,
room: room ?? null,
})
revalidatePath("/teacher/classes/my")
revalidatePath("/teacher/classes/students")
revalidatePath("/teacher/classes/schedule")
await invalidateFor("classes.create")
return { success: true, message: t("actions.classCreated"), data: id }
} catch (error) {
return handleActionError(error)
@@ -122,9 +120,7 @@ export async function updateTeacherClassAction(
homeroom: homeroom ?? undefined,
room: room ?? undefined,
})
revalidatePath("/teacher/classes/my")
revalidatePath("/teacher/classes/students")
revalidatePath("/teacher/classes/schedule")
await invalidateFor("classes.update", { id: validatedClassId })
return { success: true, message: t("actions.classUpdated") }
} catch (error) {
return handleActionError(error)
@@ -154,9 +150,7 @@ export async function deleteTeacherClassAction(classId: string): Promise<ActionS
try {
await deleteTeacherClass(parsed.data.classId)
revalidatePath("/teacher/classes/my")
revalidatePath("/teacher/classes/students")
revalidatePath("/teacher/classes/schedule")
await invalidateFor("classes.delete", { id: parsed.data.classId })
return { success: true, message: t("actions.classDeleted") }
} catch (error) {
return handleActionError(error)

View File

@@ -307,6 +307,18 @@ export async function consumeInvitationCode(code: string): Promise<void> {
.where(eq(classInvitationCodes.code, normalized))
}
/**
* 根据邀请码 ID 查询所属班级 ID用于缓存失效参数填充
*/
export async function getClassIdByCodeId(codeId: string): Promise<string | null> {
const [record] = await db
.select({ classId: classInvitationCodes.classId })
.from(classInvitationCodes)
.where(eq(classInvitationCodes.id, codeId))
.limit(1)
return record?.classId ?? null
}
/**
* 撤销邀请码(软删除)。
*/