refactor(classes): actions 迁移至 invalidateFor 集中编排
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
"use server"
|
"use server"
|
||||||
|
|
||||||
import { revalidatePath } from "next/cache"
|
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"
|
||||||
import { handleActionError } from "@/shared/lib/action-utils"
|
import { handleActionError } from "@/shared/lib/action-utils"
|
||||||
@@ -55,10 +55,7 @@ export async function createAdminClassAction(
|
|||||||
homeroom: homeroom ?? null,
|
homeroom: homeroom ?? null,
|
||||||
room: room ?? null,
|
room: room ?? null,
|
||||||
})
|
})
|
||||||
revalidatePath("/admin/school/classes")
|
await invalidateFor("classes.create")
|
||||||
revalidatePath("/teacher/classes/my")
|
|
||||||
revalidatePath("/teacher/classes/students")
|
|
||||||
revalidatePath("/teacher/classes/schedule")
|
|
||||||
return { success: true, message: t("actions.classCreated"), data: id }
|
return { success: true, message: t("actions.classCreated"), data: id }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleActionError(error)
|
return handleActionError(error)
|
||||||
@@ -114,10 +111,7 @@ export async function updateAdminClassAction(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
revalidatePath("/admin/school/classes")
|
await invalidateFor("classes.update", { id: validatedClassId })
|
||||||
revalidatePath("/teacher/classes/my")
|
|
||||||
revalidatePath("/teacher/classes/students")
|
|
||||||
revalidatePath("/teacher/classes/schedule")
|
|
||||||
return { success: true, message: t("actions.classUpdated") }
|
return { success: true, message: t("actions.classUpdated") }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleActionError(error)
|
return handleActionError(error)
|
||||||
@@ -139,10 +133,7 @@ export async function deleteAdminClassAction(classId: string): Promise<ActionSta
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await deleteAdminClass(parsed.data.classId)
|
await deleteAdminClass(parsed.data.classId)
|
||||||
revalidatePath("/admin/school/classes")
|
await invalidateFor("classes.delete", { id: parsed.data.classId })
|
||||||
revalidatePath("/teacher/classes/my")
|
|
||||||
revalidatePath("/teacher/classes/students")
|
|
||||||
revalidatePath("/teacher/classes/schedule")
|
|
||||||
return { success: true, message: t("actions.classDeleted") }
|
return { success: true, message: t("actions.classDeleted") }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleActionError(error)
|
return handleActionError(error)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use server"
|
"use server"
|
||||||
|
|
||||||
import { revalidatePath } from "next/cache"
|
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"
|
||||||
import { handleActionError } from "@/shared/lib/action-utils"
|
import { handleActionError } from "@/shared/lib/action-utils"
|
||||||
@@ -63,7 +63,7 @@ export async function createGradeClassAction(
|
|||||||
homeroom: homeroom ?? null,
|
homeroom: homeroom ?? null,
|
||||||
room: room ?? null,
|
room: room ?? null,
|
||||||
})
|
})
|
||||||
revalidatePath("/management/grade/classes")
|
await invalidateFor("classes.create")
|
||||||
return { success: true, message: t("actions.classCreated"), data: id }
|
return { success: true, message: t("actions.classCreated"), data: id }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleActionError(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") }
|
return { success: true, message: t("actions.classUpdated") }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleActionError(error)
|
return handleActionError(error)
|
||||||
@@ -173,7 +173,7 @@ export async function deleteGradeClassAction(classId: string): Promise<ActionSta
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await deleteAdminClass(validatedClassId)
|
await deleteAdminClass(validatedClassId)
|
||||||
revalidatePath("/management/grade/classes")
|
await invalidateFor("classes.delete", { id: validatedClassId })
|
||||||
return { success: true, message: t("actions.classDeleted") }
|
return { success: true, message: t("actions.classDeleted") }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleActionError(error)
|
return handleActionError(error)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use server"
|
"use server"
|
||||||
|
|
||||||
import { revalidatePath } from "next/cache"
|
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"
|
||||||
import { handleActionError } from "@/shared/lib/action-utils"
|
import { handleActionError } from "@/shared/lib/action-utils"
|
||||||
@@ -60,8 +60,7 @@ export async function enrollStudentByEmailAction(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await enrollStudentByEmail(parsed.data.classId, parsed.data.email)
|
await enrollStudentByEmail(parsed.data.classId, parsed.data.email)
|
||||||
revalidatePath("/teacher/classes/students")
|
await invalidateFor("classes.students.update", { classId: parsed.data.classId })
|
||||||
revalidatePath("/teacher/classes/my")
|
|
||||||
return { success: true, message: t("actions.studentAdded") }
|
return { success: true, message: t("actions.studentAdded") }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleActionError(error)
|
return handleActionError(error)
|
||||||
@@ -130,13 +129,7 @@ export async function joinClassByInvitationCodeAction(
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if (hasStudentScope(ctx)) {
|
await invalidateFor("classes.students.update", { classId })
|
||||||
revalidatePath("/student/learning/courses")
|
|
||||||
revalidatePath("/student/schedule")
|
|
||||||
} else {
|
|
||||||
revalidatePath("/teacher/classes/my")
|
|
||||||
}
|
|
||||||
revalidatePath("/profile")
|
|
||||||
return { success: true, message: t("actions.joinedClass"), data: { classId } }
|
return { success: true, message: t("actions.joinedClass"), data: { classId } }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 审计日志:加入失败
|
// 审计日志:加入失败
|
||||||
@@ -170,8 +163,7 @@ export async function ensureClassInvitationCodeAction(classId: string): Promise<
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const code = await ensureClassInvitationCode(classId)
|
const code = await ensureClassInvitationCode(classId)
|
||||||
revalidatePath("/teacher/classes/my")
|
await invalidateFor("classes.invitation.create", { classId })
|
||||||
revalidatePath(`/teacher/classes/my/${encodeURIComponent(classId)}`)
|
|
||||||
return { success: true, message: t("actions.invitationCodeReady"), data: { code } }
|
return { success: true, message: t("actions.invitationCodeReady"), data: { code } }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleActionError(error)
|
return handleActionError(error)
|
||||||
@@ -192,8 +184,7 @@ export async function regenerateClassInvitationCodeAction(classId: string): Prom
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const code = await regenerateClassInvitationCode(classId)
|
const code = await regenerateClassInvitationCode(classId)
|
||||||
revalidatePath("/teacher/classes/my")
|
await invalidateFor("classes.invitation.create", { classId })
|
||||||
revalidatePath(`/teacher/classes/my/${encodeURIComponent(classId)}`)
|
|
||||||
return { success: true, message: t("actions.invitationCodeUpdated"), data: { code } }
|
return { success: true, message: t("actions.invitationCodeUpdated"), data: { code } }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleActionError(error)
|
return handleActionError(error)
|
||||||
@@ -267,9 +258,7 @@ export async function createClassInvitationCodeAction(
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
revalidatePath("/teacher/classes/my")
|
await invalidateFor("classes.invitation.create", { classId })
|
||||||
revalidatePath(`/teacher/classes/my/${encodeURIComponent(classId)}`)
|
|
||||||
revalidatePath(`/admin/school/classes`)
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: t("actions.invitationCodeGenerated"),
|
message: t("actions.invitationCodeGenerated"),
|
||||||
@@ -300,7 +289,8 @@ export async function revokeClassInvitationCodeAction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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)
|
await revokeInvitationCode(codeId, ctx.userId)
|
||||||
|
|
||||||
const { logAudit } = await import("@/shared/lib/audit-logger")
|
const { logAudit } = await import("@/shared/lib/audit-logger")
|
||||||
@@ -312,8 +302,9 @@ export async function revokeClassInvitationCodeAction(
|
|||||||
detail: { revokedBy: ctx.userId },
|
detail: { revokedBy: ctx.userId },
|
||||||
})
|
})
|
||||||
|
|
||||||
revalidatePath("/teacher/classes/my")
|
if (classId) {
|
||||||
revalidatePath(`/admin/school/classes`)
|
await invalidateFor("classes.invitation.revoke", { classId })
|
||||||
|
}
|
||||||
return { success: true, message: t("actions.invitationCodeRevoked") }
|
return { success: true, message: t("actions.invitationCodeRevoked") }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleActionError(error)
|
return handleActionError(error)
|
||||||
@@ -383,8 +374,7 @@ export async function setStudentEnrollmentStatusAction(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await setStudentEnrollmentStatus(classId, studentId, status)
|
await setStudentEnrollmentStatus(classId, studentId, status)
|
||||||
revalidatePath("/teacher/classes/students")
|
await invalidateFor("classes.students.update", { classId })
|
||||||
revalidatePath("/teacher/classes/my")
|
|
||||||
return { success: true, message: t("actions.studentUpdated") }
|
return { success: true, message: t("actions.studentUpdated") }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleActionError(error)
|
return handleActionError(error)
|
||||||
@@ -448,8 +438,7 @@ export async function bulkEnrollStudentsAction(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
revalidatePath("/teacher/classes/students")
|
await invalidateFor("classes.students.update", { classId })
|
||||||
revalidatePath("/admin/school/classes")
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: t("actions.bulkImportResult", { imported, failed }),
|
message: t("actions.bulkImportResult", { imported, failed }),
|
||||||
@@ -510,7 +499,7 @@ export async function bulkAssignSubjectTeachersAction(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
revalidatePath("/admin/school/classes")
|
// 当前为 stub 实现(无 DB 写入),暂不触发缓存失效
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: t("actions.bulkAssignResult", { updated, failed }),
|
message: t("actions.bulkAssignResult", { updated, failed }),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use server"
|
"use server"
|
||||||
|
|
||||||
import { revalidatePath } from "next/cache"
|
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"
|
||||||
import { Permissions } from "@/shared/types/permissions"
|
import { Permissions } from "@/shared/types/permissions"
|
||||||
@@ -72,7 +72,7 @@ export async function createClassScheduleItemAction(
|
|||||||
course,
|
course,
|
||||||
location: location ?? null,
|
location: location ?? null,
|
||||||
})
|
})
|
||||||
revalidatePath("/teacher/classes/schedule")
|
await invalidateFor("classes.schedule.update", { classId })
|
||||||
return { success: true, message: t("actions.scheduleItemCreated"), data: id }
|
return { success: true, message: t("actions.scheduleItemCreated"), data: id }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleActionError(error)
|
return handleActionError(error)
|
||||||
@@ -126,7 +126,7 @@ export async function updateClassScheduleItemAction(
|
|||||||
course: course ?? undefined,
|
course: course ?? undefined,
|
||||||
location: location ?? undefined,
|
location: location ?? undefined,
|
||||||
})
|
})
|
||||||
revalidatePath("/teacher/classes/schedule")
|
await invalidateFor("classes.schedule.update", { classId: targetClassId })
|
||||||
return { success: true, message: t("actions.scheduleItemUpdated") }
|
return { success: true, message: t("actions.scheduleItemUpdated") }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleActionError(error)
|
return handleActionError(error)
|
||||||
@@ -156,7 +156,7 @@ export async function deleteClassScheduleItemAction(scheduleId: string): Promise
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await deleteClassScheduleItem(parsed.data.scheduleId)
|
await deleteClassScheduleItem(parsed.data.scheduleId)
|
||||||
revalidatePath("/teacher/classes/schedule")
|
await invalidateFor("classes.schedule.update", { classId: targetClassId })
|
||||||
return { success: true, message: t("actions.scheduleItemDeleted") }
|
return { success: true, message: t("actions.scheduleItemDeleted") }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleActionError(error)
|
return handleActionError(error)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use server"
|
"use server"
|
||||||
|
|
||||||
import { revalidatePath } from "next/cache"
|
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"
|
||||||
import { Permissions } from "@/shared/types/permissions"
|
import { Permissions } from "@/shared/types/permissions"
|
||||||
@@ -67,9 +67,7 @@ export async function createTeacherClassAction(
|
|||||||
homeroom: homeroom ?? null,
|
homeroom: homeroom ?? null,
|
||||||
room: room ?? null,
|
room: room ?? null,
|
||||||
})
|
})
|
||||||
revalidatePath("/teacher/classes/my")
|
await invalidateFor("classes.create")
|
||||||
revalidatePath("/teacher/classes/students")
|
|
||||||
revalidatePath("/teacher/classes/schedule")
|
|
||||||
return { success: true, message: t("actions.classCreated"), data: id }
|
return { success: true, message: t("actions.classCreated"), data: id }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleActionError(error)
|
return handleActionError(error)
|
||||||
@@ -122,9 +120,7 @@ export async function updateTeacherClassAction(
|
|||||||
homeroom: homeroom ?? undefined,
|
homeroom: homeroom ?? undefined,
|
||||||
room: room ?? undefined,
|
room: room ?? undefined,
|
||||||
})
|
})
|
||||||
revalidatePath("/teacher/classes/my")
|
await invalidateFor("classes.update", { id: validatedClassId })
|
||||||
revalidatePath("/teacher/classes/students")
|
|
||||||
revalidatePath("/teacher/classes/schedule")
|
|
||||||
return { success: true, message: t("actions.classUpdated") }
|
return { success: true, message: t("actions.classUpdated") }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleActionError(error)
|
return handleActionError(error)
|
||||||
@@ -154,9 +150,7 @@ export async function deleteTeacherClassAction(classId: string): Promise<ActionS
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await deleteTeacherClass(parsed.data.classId)
|
await deleteTeacherClass(parsed.data.classId)
|
||||||
revalidatePath("/teacher/classes/my")
|
await invalidateFor("classes.delete", { id: parsed.data.classId })
|
||||||
revalidatePath("/teacher/classes/students")
|
|
||||||
revalidatePath("/teacher/classes/schedule")
|
|
||||||
return { success: true, message: t("actions.classDeleted") }
|
return { success: true, message: t("actions.classDeleted") }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleActionError(error)
|
return handleActionError(error)
|
||||||
|
|||||||
@@ -307,6 +307,18 @@ export async function consumeInvitationCode(code: string): Promise<void> {
|
|||||||
.where(eq(classInvitationCodes.code, normalized))
|
.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
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 撤销邀请码(软删除)。
|
* 撤销邀请码(软删除)。
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user