refactor(actions): 组 A 模块迁移至 invalidateFor(users/school/textbooks/questions/course-plans/standards/scheduling/audit/onboarding/invitation-codes/proctoring/i18n)

This commit is contained in:
SpecialX
2026-07-05 22:43:08 +08:00
parent 0f33484bd2
commit 05e68a3dad
12 changed files with 73 additions and 100 deletions

View File

@@ -1,7 +1,6 @@
"use server"
import { cookies } from "next/headers"
import { revalidatePath } from "next/cache"
import {
LOCALE_COOKIE,
@@ -9,6 +8,7 @@ import {
type Locale,
isLocale,
} from "@/shared/i18n/locale"
import { invalidateFor } from "@/shared/lib/cache"
/**
* 切换语言:写入 cookie + 触发页面刷新。
@@ -16,7 +16,7 @@ import {
* 设计说明:
* - 不使用 URL 路由段,避免破坏现有路由组结构
* - cookie 持久化用户偏好SSR 时由 i18n/request.ts 读取
* - revalidatePath 确保所有页面用新 locale 重新渲染
* - invalidateFor 确保所有页面用新 locale 重新渲染
*/
export async function setLocaleAction(locale: string): Promise<{ success: boolean }> {
if (!isLocale(locale)) {
@@ -27,6 +27,6 @@ export async function setLocaleAction(locale: string): Promise<{ success: boolea
cookieStore.set(LOCALE_COOKIE, locale as Locale, LOCALE_COOKIE_OPTIONS)
// 刷新所有页面,让 SSR 用新 locale 重新渲染
revalidatePath("/", "layout")
await invalidateFor("i18n.setLocale")
return { success: true }
}

View File

@@ -1,9 +1,9 @@
"use server"
import { revalidatePath } from "next/cache"
import { PermissionDeniedError, requirePermission } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
import type { ActionState } from "@/shared/types/action-state"
import { invalidateFor } from "@/shared/lib/cache"
import { trackEvent } from "@/shared/lib/track-event"
import { getSession } from "@/shared/lib/session"
@@ -180,7 +180,7 @@ export async function saveAuditRetentionConfigAction(
},
})
revalidatePath("/admin/audit-logs/overview")
await invalidateFor("audit.saveRetentionConfig")
return { success: true, data: config }
} catch (e) {
if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
@@ -215,8 +215,7 @@ export async function purgeAuditLogsAction(
},
})
revalidatePath("/admin/audit-logs/overview")
revalidatePath("/admin/audit-logs")
await invalidateFor("audit.purge")
return { success: true, data: result }
} catch (e) {
if (e instanceof PermissionDeniedError) return { success: false, message: e.message }

View File

@@ -1,9 +1,9 @@
"use server"
import { revalidatePath } from "next/cache"
import { requirePermission } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
import type { ActionState } from "@/shared/types/action-state"
import { invalidateFor } from "@/shared/lib/cache"
import { handleActionError } from "@/shared/lib/action-utils"
import type { DataScope } from "@/shared/types/permissions"
@@ -90,19 +90,6 @@ function resolveScope(
return { userId: ctx.userId, isAdmin: false }
}
const revalidatePlanPaths = (id?: string): void => {
revalidatePath("/admin/course-plans")
revalidatePath("/teacher/course-plans")
revalidatePath("/parent/course-plans")
revalidatePath("/student/course-plans")
if (id) {
revalidatePath(`/admin/course-plans/${id}`)
revalidatePath(`/teacher/course-plans/${id}`)
revalidatePath(`/parent/course-plans/${id}`)
revalidatePath(`/student/course-plans/${id}`)
}
}
export async function createCoursePlanAction(
prevState: ActionState<string> | null,
formData: FormData
@@ -134,7 +121,7 @@ export async function createCoursePlanAction(
}
const id = await createCoursePlan(parsed.data, ctx.userId)
revalidatePlanPaths(id)
await invalidateFor("course-plans.create", { id })
return { success: true, message: "Course plan created", data: id }
} catch (e) {
return handleActionError(e)
@@ -179,7 +166,7 @@ export async function updateCoursePlanAction(
}
await updateCoursePlan(id, parsed.data)
revalidatePlanPaths(id)
await invalidateFor("course-plans.update", { id })
return { success: true, message: "Course plan updated", data: id }
} catch (e) {
return handleActionError(e)
@@ -198,7 +185,7 @@ export async function deleteCoursePlanAction(
if (!existing) return { success: false, message: "Course plan not found" }
await deleteCoursePlan(id)
revalidatePlanPaths()
await invalidateFor("course-plans.delete")
return { success: true, message: "Course plan deleted" }
} catch (e) {
return handleActionError(e)
@@ -279,7 +266,7 @@ export async function createCoursePlanItemAction(
if (!existing) return { success: false, message: "Course plan not found" }
const itemId = await createCoursePlanItem(parsed.data)
revalidatePlanPaths(parsed.data.planId)
await invalidateFor("course-plans.item.create", { id: parsed.data.planId })
return { success: true, message: "Week plan added", data: itemId }
} catch (e) {
return handleActionError(e)
@@ -319,7 +306,7 @@ export async function updateCoursePlanItemAction(
}
await updateCoursePlanItem(id, parsed.data)
revalidatePlanPaths()
await invalidateFor("course-plans.item.update")
return { success: true, message: "Week plan updated", data: id }
} catch (e) {
return handleActionError(e)
@@ -332,7 +319,7 @@ export async function deleteCoursePlanItemAction(
try {
await requirePermission(Permissions.COURSE_PLAN_MANAGE)
await deleteCoursePlanItem(id)
revalidatePlanPaths()
await invalidateFor("course-plans.item.delete")
return { success: true, message: "Week plan deleted" }
} catch (e) {
return handleActionError(e)
@@ -349,7 +336,7 @@ export async function toggleCoursePlanItemCompletedAction(
isCompleted: completed,
completedAt: completed ? new Date().toISOString().slice(0, 10) : null,
})
revalidatePlanPaths()
await invalidateFor("course-plans.item.toggleCompleted")
return {
success: true,
message: completed ? "Marked as completed" : "Marked as incomplete",
@@ -379,7 +366,7 @@ export async function reorderCoursePlanItemsAction(
if (!existing) return { success: false, message: "Course plan not found" }
await reorderCoursePlanItems(parsed.data.planId, parsed.data.items)
revalidatePlanPaths(parsed.data.planId)
await invalidateFor("course-plans.item.reorder", { id: parsed.data.planId })
return { success: true, message: "Order saved", data: parsed.data.planId }
} catch (e) {
return handleActionError(e)
@@ -402,7 +389,7 @@ export async function bulkToggleItemsAction(
}
const count = await bulkUpdateItemCompleted(parsed.data.itemIds, parsed.data.completed)
revalidatePlanPaths()
await invalidateFor("course-plans.item.bulkToggle")
return { success: true, message: "Bulk updated", data: count }
} catch (e) {
return handleActionError(e)
@@ -430,7 +417,7 @@ export async function copyCoursePlanAction(
if (!existing) return { success: false, message: "Course plan not found" }
const ids = await copyCoursePlanToClasses(parsed.data.sourcePlanId, parsed.data.targetClassIds)
revalidatePlanPaths()
await invalidateFor("course-plans.copy")
return { success: true, message: "Copied", data: ids }
} catch (e) {
return handleActionError(e)

View File

@@ -1,10 +1,9 @@
"use server"
import { revalidatePath } from "next/cache"
import type { ActionState } from "@/shared/types/action-state"
import { requirePermission } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
import { invalidateFor } from "@/shared/lib/cache"
import { trackEvent } from "@/shared/lib/track-event"
import {
@@ -63,7 +62,7 @@ export async function generateInvitationCodesAction(
},
})
revalidatePath("/admin/invitation-codes")
await invalidateFor("invitation-codes.generate")
return { success: true, data: result }
} catch (error) {
@@ -121,7 +120,7 @@ export async function deleteInvitationCodeAction(
targetId: codeId,
})
revalidatePath("/admin/invitation-codes")
await invalidateFor("invitation-codes.delete")
return { success: true, data: null }
} catch (error) {
const message =

View File

@@ -1,10 +1,10 @@
"use server"
import { revalidatePath } from "next/cache"
import { eq } from "drizzle-orm"
import type { ActionState } from "@/shared/types/action-state"
import { requireAuth } from "@/shared/lib/auth-guard"
import { invalidateFor } from "@/shared/lib/cache"
import { logAudit } from "@/shared/lib/audit-logger"
import { resolveDefaultPath } from "@/shared/lib/route-resolver"
import {
@@ -243,7 +243,7 @@ export async function completeOnboardingAction(
status: failures.length === 0 ? "success" : "failure",
})
revalidatePath("/onboarding")
await invalidateFor("onboarding.complete")
// 若有局部失败返回成功但附带失败列表P1-2
if (failures.length > 0) {

View File

@@ -1,8 +1,8 @@
"use server"
import { revalidatePath } from "next/cache"
import type { ActionState } from "@/shared/types/action-state"
import { requirePermission } from "@/shared/lib/auth-guard"
import { invalidateFor } from "@/shared/lib/cache"
import { handleActionError } from "@/shared/lib/action-utils"
import { Permissions } from "@/shared/types/permissions"
import { z } from "zod"
@@ -85,7 +85,7 @@ export async function recordProctoringEventAction(
eventDetail: parsed.data.eventDetail,
})
revalidatePath(`/teacher/exams/${parsed.data.examId}/proctoring`)
await invalidateFor("proctoring.recordEvent", { examId: parsed.data.examId })
return successState({ id: event.id }, "Event recorded")
} catch (error) {

View File

@@ -5,7 +5,7 @@ import { Permissions } from "@/shared/types/permissions"
import { CreateQuestionSchema } from "./schema"
import type { CreateQuestionInput } from "./schema"
import type { ActionState } from "@/shared/types/action-state"
import { revalidatePath } from "next/cache"
import { invalidateFor } from "@/shared/lib/cache"
import { z } from "zod"
import {
createQuestionWithRelations,
@@ -68,8 +68,7 @@ export async function createQuestionAction(
trackQuestionCreated(questionId, input.type, input.difficulty)
revalidatePath("/teacher/questions")
revalidatePath("/admin/questions")
await invalidateFor("questions.create")
return {
success: true,
@@ -117,8 +116,7 @@ export async function updateQuestionAction(
trackQuestionUpdated(id, updateData.type, updateData.difficulty)
revalidatePath("/teacher/questions")
revalidatePath("/admin/questions")
await invalidateFor("questions.update")
return { success: true, message: "Question updated successfully", data: id }
} catch (e) {
@@ -143,8 +141,7 @@ export async function deleteQuestionAction(
trackQuestionDeleted(questionId)
revalidatePath("/teacher/questions")
revalidatePath("/admin/questions")
await invalidateFor("questions.delete")
return { success: true, message: "Question deleted successfully", data: questionId }
} catch (e) {
@@ -190,8 +187,7 @@ export async function deleteQuestionsBatchAction(
trackQuestionDeleted(id)
}
revalidatePath("/teacher/questions")
revalidatePath("/admin/questions")
await invalidateFor("questions.deleteBatch")
return { success: true, message: "Batch delete completed", data: { deleted: deletedCount } }
} catch (e) {
@@ -369,8 +365,7 @@ export async function importQuestionsAction(
trackQuestionCreated(createdIds[i], item.type, item.difficulty)
}
revalidatePath("/teacher/questions")
revalidatePath("/admin/questions")
await invalidateFor("questions.import")
return { success: true, message: "Import completed", data: { imported: createdIds.length } }
} catch (e) {

View File

@@ -1,10 +1,9 @@
"use server"
import { revalidatePath } from "next/cache"
import { requirePermission, PermissionDeniedError } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
import type { ActionState } from "@/shared/types/action-state"
import { invalidateFor } from "@/shared/lib/cache"
import { getUserNamesByIds } from "@/modules/users/data-access"
@@ -61,7 +60,7 @@ export async function saveSchedulingRulesAction(
}
const id = await upsertSchedulingRules(parsed.data)
revalidatePath("/admin/scheduling/rules")
await invalidateFor("scheduling.saveRules")
return { success: true, message: "Scheduling rules saved", data: id }
} catch (e) {
if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
@@ -167,8 +166,7 @@ export async function applyAutoScheduleAction(
}))
)
revalidatePath("/admin/scheduling/auto")
revalidatePath("/teacher/classes/schedule")
await invalidateFor("scheduling.autoSchedule")
return { success: true, message: `Applied ${schedules.length} schedule items`, data: schedules.length }
} catch (e) {
if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
@@ -205,8 +203,7 @@ export async function requestScheduleChangeAction(
}
const id = await createScheduleChange(parsed.data, ctx.userId)
revalidatePath("/teacher/schedule-changes")
revalidatePath("/admin/scheduling/changes")
await invalidateFor("scheduling.requestChange")
return { success: true, message: "Schedule change request submitted", data: id }
} catch (e) {
if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
@@ -221,8 +218,7 @@ export async function approveScheduleChangeAction(changeId: string): Promise<Act
if (!changeId) return { success: false, message: "Missing change id" }
await updateScheduleChangeStatus(changeId, "approved", ctx.userId)
revalidatePath("/admin/scheduling/changes")
revalidatePath("/teacher/schedule-changes")
await invalidateFor("scheduling.approveChange")
return { success: true, message: "Schedule change approved" }
} catch (e) {
if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
@@ -240,8 +236,7 @@ export async function rejectScheduleChangeAction(
if (!changeId) return { success: false, message: "Missing change id" }
await updateScheduleChangeStatus(changeId, "rejected", ctx.userId)
revalidatePath("/admin/scheduling/changes")
revalidatePath("/teacher/schedule-changes")
await invalidateFor("scheduling.rejectChange")
return { success: true, message: reason ? `Rejected: ${reason}` : "Schedule change rejected" }
} catch (e) {
if (e instanceof PermissionDeniedError) return { success: false, message: e.message }

View File

@@ -1,10 +1,10 @@
"use server"
import { revalidatePath } from "next/cache"
import { after } from "next/server"
import { createId } from "@paralleldrive/cuid2"
import type { ActionState } from "@/shared/types/action-state"
import { invalidateFor } from "@/shared/lib/cache"
import { requirePermission, PermissionDeniedError } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
import { logAudit } from "@/shared/lib/audit-logger"
@@ -54,7 +54,7 @@ export async function createDepartmentAction(
})
)
revalidatePath("/admin/school/departments")
await invalidateFor("school.department.create")
return { success: true, message: "Department created" }
} catch (error) {
if (error instanceof PermissionDeniedError) return { success: false, message: error.message }
@@ -93,7 +93,7 @@ export async function updateDepartmentAction(
})
)
revalidatePath("/admin/school/departments")
await invalidateFor("school.department.update")
return { success: true, message: "Department updated" }
} catch (error) {
if (error instanceof PermissionDeniedError) return { success: false, message: error.message }
@@ -114,7 +114,7 @@ export async function deleteDepartmentAction(departmentId: string): Promise<Acti
targetType: "department",
})
)
revalidatePath("/admin/school/departments")
await invalidateFor("school.department.delete")
return { success: true, message: "Department deleted" }
} catch (error) {
if (error instanceof PermissionDeniedError) return { success: false, message: error.message }
@@ -156,7 +156,7 @@ export async function createAcademicYearAction(
})
)
revalidatePath("/admin/school/academic-year")
await invalidateFor("school.academicYear.create")
return { success: true, message: "Academic year created" }
} catch (error) {
if (error instanceof PermissionDeniedError) return { success: false, message: error.message }
@@ -199,7 +199,7 @@ export async function updateAcademicYearAction(
})
)
revalidatePath("/admin/school/academic-year")
await invalidateFor("school.academicYear.update")
return { success: true, message: "Academic year updated" }
} catch (error) {
if (error instanceof PermissionDeniedError) return { success: false, message: error.message }
@@ -212,7 +212,7 @@ export async function deleteAcademicYearAction(academicYearId: string): Promise<
try {
await requirePermission(Permissions.SCHOOL_MANAGE)
await deleteAcademicYear(academicYearId)
revalidatePath("/admin/school/academic-year")
await invalidateFor("school.academicYear.delete")
return { success: true, message: "Academic year deleted" }
} catch (error) {
if (error instanceof PermissionDeniedError) return { success: false, message: error.message }
@@ -245,7 +245,7 @@ export async function createSchoolAction(
logAudit({ action: "school.create", module: "school", targetType: "school", detail: { name: parsed.data.name } })
)
revalidatePath("/admin/school/schools")
await invalidateFor("school.school.create")
return { success: true, message: "School created" }
} catch (error) {
if (error instanceof PermissionDeniedError) return { success: false, message: error.message }
@@ -284,7 +284,7 @@ export async function updateSchoolAction(
})
)
revalidatePath("/admin/school/schools")
await invalidateFor("school.school.update")
return { success: true, message: "School updated" }
} catch (error) {
if (error instanceof PermissionDeniedError) return { success: false, message: error.message }
@@ -302,8 +302,7 @@ export async function deleteSchoolAction(schoolId: string): Promise<ActionState<
logAudit({ action: "school.delete", module: "school", targetId: schoolId, targetType: "school" })
)
revalidatePath("/admin/school/schools")
revalidatePath("/admin/school/grades")
await invalidateFor("school.school.delete")
return { success: true, message: "School deleted" }
} catch (error) {
if (error instanceof PermissionDeniedError) return { success: false, message: error.message }
@@ -347,7 +346,7 @@ export async function createGradeAction(
})
)
revalidatePath("/admin/school/grades")
await invalidateFor("school.grade.create")
return { success: true, message: "Grade created" }
} catch (error) {
if (error instanceof PermissionDeniedError) return { success: false, message: error.message }
@@ -392,7 +391,7 @@ export async function updateGradeAction(
})
)
revalidatePath("/admin/school/grades")
await invalidateFor("school.grade.update")
return { success: true, message: "Grade updated" }
} catch (error) {
if (error instanceof PermissionDeniedError) return { success: false, message: error.message }
@@ -413,7 +412,7 @@ export async function deleteGradeAction(gradeId: string): Promise<ActionState<st
targetType: "grade",
})
)
revalidatePath("/admin/school/grades")
await invalidateFor("school.grade.delete")
return { success: true, message: "Grade deleted" }
} catch (error) {
if (error instanceof PermissionDeniedError) return { success: false, message: error.message }
@@ -447,7 +446,7 @@ export async function promoteGradesAction(
})
)
revalidatePath("/admin/school/grades")
await invalidateFor("school.grade.promote")
return { success: true, message: `Promoted ${result.promoted} grades` }
} catch (error) {
if (error instanceof PermissionDeniedError) return { success: false, message: error.message }

View File

@@ -7,7 +7,7 @@
"use server";
import { getTranslations } from "next-intl/server";
import { revalidatePath } from "next/cache";
import { invalidateFor } from "@/shared/lib/cache";
import {
createStandardSchema,
updateStandardSchema,
@@ -99,7 +99,7 @@ export async function createStandardAction(
return { success: false, message: t("error.unauthorized") };
}
const standard = await createStandard(parseResult.data, auth.userId);
revalidatePath("/admin/standards");
await invalidateFor("standards.create");
return { success: true, data: { standard } };
} catch (e) {
return handleActionError(e);
@@ -123,7 +123,7 @@ export async function updateStandardAction(
if (!updated) {
return { success: false, message: t("error.notFound") };
}
revalidatePath("/admin/standards");
await invalidateFor("standards.update");
return { success: true, data: { standard: updated } };
} catch (e) {
return handleActionError(e);
@@ -137,7 +137,7 @@ export async function deactivateStandardAction(id: string): Promise<ActionState<
try {
await requirePermission(Permissions.STANDARD_MANAGE);
await deactivateStandard(id);
revalidatePath("/admin/standards");
await invalidateFor("standards.deactivate");
return { success: true, data: null };
} catch (e) {
return handleActionError(e);
@@ -197,7 +197,7 @@ export async function linkPlanToStandardAction(
parseResult.data.relationType,
auth.userId,
);
revalidatePath(`/teacher/lesson-plans/${parseResult.data.planId}/edit`);
await invalidateFor("standards.linkPlan", { planId: parseResult.data.planId });
return { success: true, data: { link } };
} catch (e) {
return handleActionError(e);
@@ -214,7 +214,7 @@ export async function unlinkPlanFromStandardAction(
try {
await requirePermission(Permissions.STANDARD_LINK);
await unlinkPlanFromStandard(planId, standardId);
revalidatePath(`/teacher/lesson-plans/${planId}/edit`);
await invalidateFor("standards.unlinkPlan", { planId });
return { success: true, data: null };
} catch (e) {
return handleActionError(e);

View File

@@ -3,7 +3,7 @@
import { requirePermission } from "@/shared/lib/auth-guard";
import { Permissions } from "@/shared/types/permissions";
import type { ActionState } from "@/shared/types/action-state";
import { revalidatePath } from "next/cache";
import { invalidateFor } from "@/shared/lib/cache";
import { getTranslations } from "next-intl/server";
import { getCurrentStudentUser } from "@/modules/users/data-access";
import { getGradeNameById } from "@/modules/school/data-access";
@@ -66,7 +66,7 @@ export async function reorderChaptersAction(
return { success: false, message: t("chapterNotBelong") };
}
await reorderChapters(chapterId, newIndex, parentId);
revalidatePath(`/teacher/textbooks/${textbookId}`);
await invalidateFor("textbooks.chapter.reorder", { textbookId });
return { success: true, message: t("chaptersReordered") };
} catch (e) {
return handleActionError(e)
@@ -96,7 +96,7 @@ export async function createTextbookAction(
try {
await requirePermission(Permissions.TEXTBOOK_CREATE);
await createTextbook(parsed.data);
revalidatePath("/teacher/textbooks");
await invalidateFor("textbooks.create");
return {
success: true,
message: t("createSuccess"),
@@ -131,7 +131,7 @@ export async function updateTextbookAction(
try {
await requirePermission(Permissions.TEXTBOOK_UPDATE);
await updateTextbook(parsed.data);
revalidatePath(`/teacher/textbooks/${textbookId}`);
await invalidateFor("textbooks.update", { id: textbookId });
return {
success: true,
message: t("updateSuccess"),
@@ -148,7 +148,7 @@ export async function deleteTextbookAction(
const t = await getTranslations("textbooks.action");
await requirePermission(Permissions.TEXTBOOK_DELETE);
await deleteTextbook(textbookId);
revalidatePath("/teacher/textbooks");
await invalidateFor("textbooks.delete");
return {
success: true,
message: t("deleteSuccess"),
@@ -183,7 +183,7 @@ export async function createChapterAction(
try {
await requirePermission(Permissions.TEXTBOOK_CREATE);
await createChapter(parsed.data);
revalidatePath(`/teacher/textbooks/${textbookId}`);
await invalidateFor("textbooks.chapter.create", { textbookId });
return { success: true, message: t("chapterCreateSuccess") };
} catch (e) {
return handleActionError(e)
@@ -217,7 +217,7 @@ export async function updateChapterContentAction(
return { success: false, message: t("chapterNotBelong") };
}
await updateChapterContent(parsed.data);
revalidatePath(`/teacher/textbooks/${textbookId}`);
await invalidateFor("textbooks.chapter.update", { textbookId });
return { success: true, message: t("contentUpdateSuccess") };
} catch (e) {
return handleActionError(e)
@@ -237,7 +237,7 @@ export async function deleteChapterAction(
return { success: false, message: t("chapterNotBelong") };
}
await deleteChapter(chapterId);
revalidatePath(`/teacher/textbooks/${textbookId}`);
await invalidateFor("textbooks.chapter.delete", { textbookId });
return { success: true, message: t("chapterDeleteSuccess") };
} catch (e) {
return handleActionError(e)
@@ -274,7 +274,7 @@ export async function createKnowledgePointAction(
return { success: false, message: t("chapterNotBelong") };
}
await createKnowledgePoint(parsed.data);
revalidatePath(`/teacher/textbooks/${textbookId}`);
await invalidateFor("textbooks.knowledgePoint.create", { textbookId });
return { success: true, message: t("kpCreateSuccess") };
} catch (e) {
return handleActionError(e)
@@ -294,7 +294,7 @@ export async function deleteKnowledgePointAction(
return { success: false, message: t("kpNotBelong") };
}
await deleteKnowledgePoint(kpId);
revalidatePath(`/teacher/textbooks/${textbookId}`);
await invalidateFor("textbooks.knowledgePoint.delete", { textbookId });
return { success: true, message: t("kpDeleteSuccess") };
} catch (e) {
return handleActionError(e)
@@ -331,7 +331,7 @@ export async function updateKnowledgePointAction(
return { success: false, message: t("kpNotBelong") };
}
await updateKnowledgePoint(parsed.data);
revalidatePath(`/teacher/textbooks/${textbookId}`);
await invalidateFor("textbooks.knowledgePoint.update", { textbookId });
return { success: true, message: t("kpUpdateSuccess") };
} catch (e) {
return handleActionError(e)
@@ -490,7 +490,7 @@ export async function createPrerequisiteAction(
}
await createPrerequisite(parsed.data);
revalidatePath(`/teacher/textbooks/${textbookId}`);
await invalidateFor("textbooks.prerequisite.create", { textbookId });
return { success: true, message: t("prerequisiteCreated") };
} catch (e) {
return handleActionError(e)
@@ -536,7 +536,7 @@ export async function deletePrerequisiteAction(
}
await deletePrerequisite(parsed.data);
revalidatePath(`/teacher/textbooks/${textbookId}`);
await invalidateFor("textbooks.prerequisite.delete", { textbookId });
return { success: true, message: t("prerequisiteDeleted") };
} catch (e) {
return handleActionError(e)

View File

@@ -1,8 +1,8 @@
"use server"
import { revalidatePath } from "next/cache"
import { z } from "zod"
import { invalidateFor } from "@/shared/lib/cache"
import { requirePermission, PermissionDeniedError } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
import type { ActionState } from "@/shared/types/action-state"
@@ -54,8 +54,7 @@ export async function updateUserProfile(
await updateUserProfileById(ctx.userId, parsed.data)
revalidatePath("/profile")
revalidatePath("/settings")
await invalidateFor("users.update")
return { success: true, message: "Profile updated successfully" }
} catch (e) {
@@ -127,7 +126,7 @@ export async function importUsersAction(
}
const result = await batchImportUsers(validation.valid)
revalidatePath("/admin/users/import")
await invalidateFor("users.import")
return {
success: true,
@@ -180,7 +179,7 @@ export async function deleteUserAction(
try {
await requirePermission(Permissions.USER_MANAGE)
await deleteUserById(userId)
revalidatePath("/admin/users")
await invalidateFor("users.delete")
return { success: true, message: "用户已删除" }
} catch (e) {
if (e instanceof PermissionDeniedError) {