refactor: RBAC权限系统重构 + UI组件拆分 + 测试修复 + 架构文档
Some checks failed
CI / build-deploy (push) Has been cancelled

- RBAC: 新增30个权限点、DataScope行级权限、requirePermission守卫,所有57+ Server Action接入权限校验
- UI拆分: exam-form(1623行→11文件)、textbook-reader(744行→7文件),均降至300行以内
- 测试: 新增5个单元测试文件(19用例),修复4个集成测试文件(38用例全部通过)
- 架构文档: 新增架构影响地图(004/005)、标准功能清单(006)、差距审计报告(007)
- 项目规则: 架构图优先规则,改码必同步图
- 安全: rehype-sanitize净化、AES加密API Key、权限路由守卫
- 无障碍: skip-link、aria-label、prefers-reduced-motion
- 性能: next/font优化、next/image、代码分割
This commit is contained in:
SpecialX
2026-06-16 23:38:33 +08:00
parent 99f116cb64
commit 125f7ec54c
75 changed files with 9480 additions and 3289 deletions

View File

@@ -1,5 +1,7 @@
"use server";
import { requirePermission, PermissionDeniedError } from "@/shared/lib/auth-guard";
import { Permissions } from "@/shared/types/permissions";
import { revalidatePath } from "next/cache";
import {
createTextbook,
@@ -24,10 +26,14 @@ export async function reorderChaptersAction(
textbookId: string
): Promise<ActionState> {
try {
await requirePermission(Permissions.TEXTBOOK_UPDATE);
await reorderChapters(chapterId, newIndex, parentId);
revalidatePath(`/teacher/textbooks/${textbookId}`);
return { success: true, message: "Chapters reordered successfully" };
} catch {
} catch (e) {
if (e instanceof PermissionDeniedError) {
return { success: false, message: e.message };
}
return { success: false, message: "Failed to reorder chapters" };
}
}
@@ -60,13 +66,17 @@ export async function createTextbookAction(
}
try {
await requirePermission(Permissions.TEXTBOOK_CREATE);
await createTextbook(rawData);
revalidatePath("/teacher/textbooks");
return {
success: true,
message: "Textbook created successfully.",
};
} catch {
} catch (e) {
if (e instanceof PermissionDeniedError) {
return { success: false, message: e.message };
}
return {
success: false,
message: "Failed to create textbook.",
@@ -95,13 +105,17 @@ export async function updateTextbookAction(
}
try {
await requirePermission(Permissions.TEXTBOOK_UPDATE);
await updateTextbook(rawData);
revalidatePath(`/teacher/textbooks/${textbookId}`);
return {
success: true,
message: "Textbook updated successfully.",
};
} catch {
} catch (e) {
if (e instanceof PermissionDeniedError) {
return { success: false, message: e.message };
}
return {
success: false,
message: "Failed to update textbook.",
@@ -113,13 +127,17 @@ export async function deleteTextbookAction(
textbookId: string
): Promise<ActionState> {
try {
await requirePermission(Permissions.TEXTBOOK_DELETE);
await deleteTextbook(textbookId);
revalidatePath("/teacher/textbooks");
return {
success: true,
message: "Textbook deleted successfully.",
};
} catch {
} catch (e) {
if (e instanceof PermissionDeniedError) {
return { success: false, message: e.message };
}
return {
success: false,
message: "Failed to delete textbook.",
@@ -138,6 +156,7 @@ export async function createChapterAction(
if (!title) return { success: false, message: "Title is required" };
try {
await requirePermission(Permissions.TEXTBOOK_CREATE);
await createChapter({
textbookId,
title,
@@ -146,7 +165,10 @@ export async function createChapterAction(
});
revalidatePath(`/teacher/textbooks/${textbookId}`);
return { success: true, message: "Chapter created successfully" };
} catch {
} catch (e) {
if (e instanceof PermissionDeniedError) {
return { success: false, message: e.message };
}
return { success: false, message: "Failed to create chapter" };
}
}
@@ -157,10 +179,14 @@ export async function updateChapterContentAction(
textbookId: string
): Promise<ActionState> {
try {
await requirePermission(Permissions.TEXTBOOK_UPDATE);
await updateChapterContent({ chapterId, content });
revalidatePath(`/teacher/textbooks/${textbookId}`);
return { success: true, message: "Content updated successfully" };
} catch {
} catch (e) {
if (e instanceof PermissionDeniedError) {
return { success: false, message: e.message };
}
return { success: false, message: "Failed to update content" };
}
}
@@ -170,10 +196,14 @@ export async function deleteChapterAction(
textbookId: string
): Promise<ActionState> {
try {
await requirePermission(Permissions.TEXTBOOK_DELETE);
await deleteChapter(chapterId);
revalidatePath(`/teacher/textbooks/${textbookId}`);
return { success: true, message: "Chapter deleted successfully" };
} catch {
} catch (e) {
if (e instanceof PermissionDeniedError) {
return { success: false, message: e.message };
}
return { success: false, message: "Failed to delete chapter" };
}
}
@@ -191,10 +221,14 @@ export async function createKnowledgePointAction(
if (!name) return { success: false, message: "Name is required" };
try {
await requirePermission(Permissions.TEXTBOOK_CREATE);
await createKnowledgePoint({ name, description, anchorText, chapterId });
revalidatePath(`/teacher/textbooks/${textbookId}`);
return { success: true, message: "Knowledge point created successfully" };
} catch {
} catch (e) {
if (e instanceof PermissionDeniedError) {
return { success: false, message: e.message };
}
return { success: false, message: "Failed to create knowledge point" };
}
}
@@ -204,10 +238,14 @@ export async function deleteKnowledgePointAction(
textbookId: string
): Promise<ActionState> {
try {
await requirePermission(Permissions.TEXTBOOK_DELETE);
await deleteKnowledgePoint(kpId);
revalidatePath(`/teacher/textbooks/${textbookId}`);
return { success: true, message: "Knowledge point deleted successfully" };
} catch {
} catch (e) {
if (e instanceof PermissionDeniedError) {
return { success: false, message: e.message };
}
return { success: false, message: "Failed to delete knowledge point" };
}
}
@@ -225,10 +263,14 @@ export async function updateKnowledgePointAction(
if (!name) return { success: false, message: "Name is required" };
try {
await requirePermission(Permissions.TEXTBOOK_UPDATE);
await updateKnowledgePoint({ id: kpId, name, description, anchorText });
revalidatePath(`/teacher/textbooks/${textbookId}`);
return { success: true, message: "Knowledge point updated successfully" };
} catch {
} catch (e) {
if (e instanceof PermissionDeniedError) {
return { success: false, message: e.message };
}
return { success: false, message: "Failed to update knowledge point" };
}
}