## 新增功能 ### 1. 屏幕阅读器兼容性增强(a11y) - 无障碍工具库:src/shared/lib/a11y.ts - aria-live Hook:src/shared/hooks/use-aria-live.ts - a11y 组件:skip-link/visually-hidden/focus-trap/aria-status - 增强 UI:table.tsx 系统性 ARIA role,dialog.tsx aria-modal - 审计文档:docs/accessibility/a11y-audit.md(WCAG 2.1 AA 清单) ### 2. 视觉回归测试 - 测试套件:tests/visual/(homepage + 3 个 dashboard) - 3 视口(desktop/tablet/mobile)× 2 主题(light/dark) - 动态元素遮罩,避免误报 - playwright.config.ts 新增 visual-chromium 项目 - 文档:docs/testing/visual-regression.md ### 3. 短信/微信推送渠道集成 - 新模块:src/modules/notifications/ - 4 个渠道:SMS(阿里云/腾讯云)、WeChat(公众号)、Email(SMTP)、In-App - 分发器按用户偏好并行多渠道发送 - 外部 SDK 动态 import,Mock 模式开发可用 - 文档:docs/notifications/channels.md ### 4. 漏洞扫描 CI 集成 - CI security-scan job:npm audit + Snyk + Trivy FS + OWASP ZAP - 独立工作流 security.yml:每周一深度扫描 + 容器镜像扫描 - 配置:suppressions.json + .trivyignore - 本地脚本:security-scan.sh/ps1 - 文档:docs/security/scanning.md(SLA 分级) ### 5. 灾备方案 - 脚本:backup-verify/backup-offsite-sync/dr-drill/failover/health-check - CI 增强:备份后校验+异地同步,每周灾备演练 - 独立工作流 dr-drill.yml:每周一凌晨 4 点自动演练 - 文档:docs/dr/dr-plan.md(RTO 4h/RPO 24h)+ dr-runbook.md(6 故障场景) ## 验证 - npx tsc --noEmit:0 错误 - npm run lint:0 错误 0 警告
194 KiB
Next_Edu 架构影响地图
全模块·全函数·全参数级别 生成日期:2026-06-16 规则:每次文件修改后须同步更新本文档
模块:shared
模块职责
提供全项目共享的基础设施:数据库连接、Schema、工具函数、权限系统、UI 基础组件、通用 Hooks。
导出函数
cn
- 签名:
cn(...inputs: ClassValue[]): string - 参数说明:
inputs: ClassValue[],来自clsx+tailwind-merge,CSS 类名列表
- 功能:合并 CSS 类名并解决 Tailwind 冲突
- 依赖:
clsx,tailwind-merge - 被以下模块使用:所有模块的组件(50+ 文件)
formatDate
- 签名:
formatDate(date: string | Date, locale?: string): string - 参数说明:
date: string | Date,日期值locale: string,默认"zh-CN",Intl.DateTimeFormat的 locale
- 功能:国际化日期格式化
- 依赖:无
- 被以下模块使用:exams, homework, dashboard, textbooks
parseAiChatPayload
- 签名:
parseAiChatPayload(body: unknown): AiChatRequest - 参数说明:
body: unknown,HTTP 请求体 - 功能:解析并校验 AI 聊天请求负载
- 依赖:Zod schema
- 被以下模块使用:
app/api/ai/chat/route.ts
encryptAiApiKey / decryptAiApiKey
- 签名:
(value: string) => string - 功能:AES 加密/解密 AI Provider API Key
- 依赖:
crypto(Node.js 内置) - 被以下模块使用:settings (存储/读取 API Key)
testAiProviderConfig / testAiProviderById
- 签名:
(input: { apiKey, baseUrl?, model }) => Promise<boolean>/(providerId, overrides?) => Promise<boolean> - 功能:测试 AI Provider 连通性
- 依赖:
createAiChatCompletion,shared/db - 被以下模块使用:settings/actions.ts
createAiChatCompletion
- 签名:
(input: AiChatRequest) => Promise<{ content, usage }> - 功能:调用 AI 模型生成聊天回复
- 依赖:OpenAI SDK,
shared/db(读取 provider 配置) - 被以下模块使用:exams/ai-pipeline.ts,
app/api/ai/chat/route.ts
getAuthContext
- 签名:
getAuthContext(): Promise<AuthContext> - 功能:获取当前用户的完整认证上下文(userId, roles, permissions, dataScope)
- 依赖:
auth(NextAuth),shared/db(查询角色/班级/年级关系) - 被以下模块使用:所有业务模块的 Server Actions
requirePermission
- 签名:
requirePermission(permission: Permission): Promise<AuthContext> - 参数说明:
permission: Permission,来自shared/types/permissions的权限常量 - 功能:断言当前用户拥有指定权限,否则抛出 PermissionDeniedError
- 依赖:
getAuthContext - 被以下模块使用:所有业务模块的 Server Actions(exams, homework, questions, textbooks, classes, school, settings, audit, announcements, files, grades, attendance)
checkPermission
- 签名:
checkPermission(permission: Permission): Promise<{ allowed: boolean; ctx: AuthContext }> - 功能:非抛出版权限检查
- 依赖:
getAuthContext - 被以下模块使用:待扩展
requireAuth
- 签名:
requireAuth(): Promise<AuthContext> - 功能:仅断言用户已登录
- 依赖:
getAuthContext - 被以下模块使用:待扩展
resolvePermissions
- 签名:
resolvePermissions(roleNames: string[]): Permission[] - 参数说明:
roleNames: string[],用户的角色名称列表 - 功能:合并多角色的权限列表(去重)
- 依赖:
ROLE_PERMISSIONS常量 - 被以下模块使用:auth.ts (JWT callback)
logAudit
- 签名:
logAudit(params: LogAuditParams): Promise<void> - 参数说明:
params: LogAuditParams,含 userId, userName, action, module, targetId?, targetType?, detail?, status? - 功能:记录操作日志(静默失败,不影响主流程)
- 依赖:
auth(NextAuth),shared/db(auditLogs 表),next/headers - 被以下模块使用:school/actions.ts,其他 Server Actions
logLoginEvent
- 签名:
logLoginEvent(params: LogLoginEventParams): Promise<void> - 参数说明:
params: LogLoginEventParams,含 userId?, userEmail, action (signin/signout/signup), status, errorMessage? - 功能:记录登录日志(不依赖 auth 上下文,可在 NextAuth events 中调用,静默失败)
- 依赖:
shared/db(loginLogs 表),next/headers - 被以下模块使用:auth.ts (events.signIn, events.signOut)
isAllowedMimeType
- 签名:
isAllowedMimeType(mimeType: string): boolean - 功能:判断 MIME 类型是否在允许上传的白名单中
- 依赖:
ALLOWED_MIME_TYPES常量 - 被以下模块使用:
app/api/upload/route.ts, files/components/file-upload.tsx
generateStoragePath
- 签名:
generateStoragePath(originalName: string): string - 功能:根据原始文件名生成存储路径
uploads/YYYY-MM/cuid.ext(相对于 public/) - 依赖:
@paralleldrive/cuid2 - 被以下模块使用:
app/api/upload/route.ts
getFileExtension
- 签名:
getFileExtension(filename: string): string - 功能:从文件名中提取小写扩展名(不含点)
- 被以下模块使用:
shared/lib/file-storage内部
formatFileSize
- 签名:
formatFileSize(bytes: number): string - 功能:将字节数格式化为人类可读字符串(如
1.5 MB、800 KB) - 被以下模块使用:files/components/file-upload.tsx, file-list.tsx, file-preview.tsx
exportToExcel
- 签名:
exportToExcel(params: { sheets: ExcelSheet[] }): Promise<Buffer> - 参数说明:
sheets: ExcelSheet[],每个含name/columns/rows - 功能:将多 sheet 数据导出为 Excel Buffer(表头加粗+冻结首行+自动筛选)
- 依赖:
exceljs - 被以下模块使用:users/import-export.exportUsersToExcel, grades/export.exportGradeRecordsToExcel, grades/export.exportClassGradeReportToExcel
parseExcel
- 签名:
parseExcel(buffer: Buffer): Promise<ParsedSheet[]> - 返回:ParsedSheet[],每个含
sheetName/rows - 功能:从 Buffer 解析 Excel 文件,首行作为表头,返回每 sheet 的行记录数组
- 依赖:
exceljs - 被以下模块使用:users/actions.importUsersAction,
app/api/import/route.ts
generateTemplate
- 签名:
(params: { sheets: TemplateSheet[] }): Promise<Buffer> - 参数说明:
sheets: TemplateSheet[],每个含name/columns(含note)/sampleRows? - 功能:生成导入模板 Buffer(表头加粗+第二行填写说明+示例行)
- 依赖:
exceljs - 被以下模块使用:users/import-export.generateUserImportTemplate
validatePassword
- 签名:
(password: string) => { valid: boolean; errors: string[] } - 功能:校验密码是否符合策略(最小长度、大小写、数字等)
- 依赖:
PASSWORD_RULES常量 - 被以下模块使用:settings/actions-password.changePasswordAction, auth.ts
getPasswordStrength
- 签名:
(password: string) => "weak" | "medium" | "strong" - 功能:基于长度和字符多样性计算密码强度等级(纯函数,可在客户端使用)
- 依赖:无
- 被以下模块使用:settings/components/password-change-form.tsx
isAccountLocked
- 签名:
(failedAttempts: number, lastFailedAt: Date | null) => boolean - 功能:判断账户是否应被锁定(失败次数达阈值且锁定时间未过期)
- 依赖:
PASSWORD_RULES.maxLoginAttempts,PASSWORD_RULES.lockoutDurationMinutes - 被以下模块使用:auth.ts (authorize callback)
getRemainingLockoutMs
- 签名:
(failedAttempts: number, lastFailedAt: Date | null) => number - 功能:计算剩余锁定时间(毫秒,0 表示已解锁)
- 依赖:
isAccountLocked - 被以下模块使用:待扩展
rateLimit
- 签名:
(params: { key: string; limit: number; windowMs: number }) => { success: boolean; remaining: number; resetTime: number; retryAfterMs: number } - 功能:内存滑动窗口限流检查(单实例,多实例需替换为 @upstash/ratelimit)
- 依赖:无(Map 存储)
- 被以下模块使用:auth.ts (LOGIN), settings/actions-password (PASSWORD_CHANGE), app/api/ai/chat (AI_CHAT), app/api/upload (UPLOAD), app/api/rate-limit-test
resetRateLimit
- 签名:
(key: string) => void - 功能:重置指定 key 的限流计数(如登录成功后清除失败计数)
- 被以下模块使用:auth.ts (成功登录后)
rateLimitKey
- 签名:
(prefix: string, identifier: string) => string - 功能:构建限流 key(如
login:ip:email) - 被以下模块使用:auth.ts, settings/actions-password, app/api/ai/chat, app/api/upload
rateLimitHeaders
- 签名:
(result: RateLimitResult) => Record<string, string> - 功能:将限流结果转为 HTTP 响应头(X-RateLimit-Limit/Remaining/Reset, Retry-After)
- 被以下模块使用:app/api/ai/chat, app/api/upload, app/api/rate-limit-test
logDataChange
- 签名:
logDataChange(params: LogDataChangeParams): Promise<void> - 参数说明:
params: LogDataChangeParams,含 tableName, recordId, action ("create" | "update" | "delete"), oldValue?, newValue? - 功能:记录数据变更日志(写入 dataChangeLogs 表,自动从 NextAuth session 获取 changedBy/changedByName,从 headers 获取 ipAddress;静默失败,不阻塞主流程)
- 依赖:
auth(NextAuth),shared/db(dataChangeLogs 表),next/headers,@paralleldrive/cuid2 - 被以下模块使用:待扩展(数据变更场景)
StorageProvider (接口)
- 文件:
lib/storage-provider.ts - 定义:文件存储抽象接口,方法
save(file, storagePath) => Promise<string>、read(storagePath) => Promise<Buffer>、delete(storagePath) => Promise<void>、exists(storagePath) => Promise<boolean>、getUrl(storagePath) => string - 功能:抽象文件持久化层,便于未来切换到 OSS/S3 而不修改调用方
- 被以下模块使用:
app/api/files/batch-delete/route.ts
LocalStorageProvider (类)
- 文件:
lib/storage-provider.ts - 实现:
StorageProvider接口,文件持久化到public/uploads/...,URL 为/uploads/... - 依赖:
fs/promises(mkdir/readFile/writeFile/unlink/access),path - 被以下模块使用:通过
storageProvider实例使用
storageProvider (实例)
- 文件:
lib/storage-provider.ts - 类型:
StorageProvider(默认为LocalStorageProvider实例) - 功能:默认存储 Provider 单例,替换此实例可迁移到 OSS/S3
- 被以下模块使用:
app/api/files/batch-delete/route.ts
useA11yId
- 签名:
useA11yId(prefix: string): string - 功能:基于
React.useId生成 SSR 安全的唯一 ID,用于aria-describedby、aria-labelledby等 - 依赖:
react - 被以下模块使用:待扩展(表单组件、a11y 组件)
mergeA11yProps
- 签名:
mergeA11yProps<T extends Record<string, unknown>>(...props: (T | undefined | null | false)[]): T - 功能:合并多组 aria/data 属性,普通属性后者覆盖前者,
aria-*/data-*字符串属性以空格拼接 - 依赖:无
- 被以下模块使用:待扩展
describeInput
- 签名:
describeInput(describedBy?: string, error?: string, hint?: string): { ariaDescribedBy?: string; ariaInvalid?: boolean } - 功能:计算输入框的
aria-describedby(合并多个 ID)与aria-invalid(error 存在则为 true) - 依赖:无
- 被以下模块使用:待扩展(表单组件)
loadingAria
- 签名:
loadingAria(isLoading: boolean): { ariaBusy: boolean; ariaLive: "polite" | "assertive" } - 功能:提供加载状态的
aria-busy与aria-live=polite属性 - 依赖:无
- 被以下模块使用:待扩展
导出常量与实例
Permissions (常量对象)
- 文件:
types/permissions.ts - 定义:54 个权限常量(
exam:create,homework:grade,audit_log:read,announcement:manage,file:upload,file:read,file:delete,grade_record:manage,grade_record:read,course_plan:manage,course_plan:read,attendance:manage,attendance:read,message:send,message:read,message:delete,schedule:auto,schedule:adjust,elective:manage,elective:read,elective:select,exam:proctor,exam:proctor_read,diagnostic:manage,diagnostic:read等) - 被使用:auth-guard.ts, 所有模块的 actions.ts, 前端组件
ROLE_PERMISSIONS (常量对象)
- 文件:
lib/permissions.ts - 定义:角色到权限列表的映射(admin/teacher/student/parent/grade_head/teaching_head)
- 课程计划权限:admin 含
COURSE_PLAN_MANAGE+COURSE_PLAN_READ;teacher/student/grade_head/teaching_head 含COURSE_PLAN_READ - 考勤权限:admin/teacher 含
ATTENDANCE_MANAGE+ATTENDANCE_READ;student/parent/grade_head/teaching_head 含ATTENDANCE_READ - 消息权限:admin/teacher/grade_head/teaching_head 含
MESSAGE_SEND+MESSAGE_READ+MESSAGE_DELETE;student/parent 含MESSAGE_SEND+MESSAGE_READ - 排课权限:admin 含
SCHEDULE_AUTO+SCHEDULE_ADJUST;teacher/student/parent/grade_head/teaching_head 无排课权限 - 学情诊断权限:admin/teacher/grade_head 含
DIAGNOSTIC_MANAGE+DIAGNOSTIC_READ;teaching_head/student 含DIAGNOSTIC_READ - 被使用:
resolvePermissions, auth.ts (JWT callback)
db (Drizzle 实例)
- 文件:
db/index.ts - 定义:Drizzle ORM 客户端实例(MySQL)
- 被使用:所有业务模块的 data-access.ts 和 actions.ts
questionTypeEnum
- 文件:
db/schema.ts(或 schema 枚举导出) - 定义:题目类型枚举(选择/填空/判断/复合等)
- 被使用:questions, exams, homework
classEnrollmentStatusEnum
- 文件:
db/schema.ts(或 schema 枚举导出) - 定义:班级注册状态枚举(active/inactive 等)
- 被使用:classes, homework
PASSWORD_RULES (常量对象)
- 文件:
lib/password-policy.ts - 定义:密码策略配置(minLength: 8, requireUppercase/Lowercase/Number: true, maxLoginAttempts: 5, lockoutDurationMinutes: 30)
- 被使用:auth.ts, settings/actions-password.ts, password-change-form.tsx (via PASSWORD_REQUIREMENT_HINTS)
RATE_LIMIT_RULES (常量对象)
- 文件:
lib/rate-limit.ts - 定义:预定义限流规则(LOGIN: 5/15min, API: 100/min, UPLOAD: 10/min, AI_CHAT: 20/min, PASSWORD_CHANGE: 5/min)
- 被使用:auth.ts (LOGIN), settings/actions-password (PASSWORD_CHANGE), app/api/ai/chat (AI_CHAT), app/api/upload (UPLOAD)
文件记录
db/relations.ts
- 内容:24+ Drizzle relations(表间关系定义,含 coursePlansRelations、coursePlanItemsRelations、attendanceRecordsRelations、attendanceRulesRelations)
- 被使用:Drizzle ORM 关联查询(所有 data-access.ts)
next-auth.d.ts
- 内容:NextAuth 类型扩展(Session.user 增加 id/roles/permissions 等字段)
- 被使用:auth.ts, 所有使用
useSession/auth()的代码
导出组件
AuthSessionProvider
- Props:
{ children: React.ReactNode } - 内部使用:
next-auth/react的SessionProvider - 被使用:
app/layout.tsx
OnboardingGate
- Props: 无(内部使用
useSession) - 内部使用:
useSession,Permissions, 多个 Server Action - 功能:新用户引导流程(角色选择、学校/班级配置)
- 被使用:
app/layout.tsx
ThemeProvider
- Props:
next-themes的ThemeProviderProps - 被使用:
app/layout.tsx
EmptyState
- 文件:
components/ui/empty-state.tsx - Props:
{ icon?, title, description, action? } - 被使用:exams, homework, questions, textbooks 等模块的列表空状态
GlobalSearch
- 文件:
components/global-search.tsx - Props:
{ className?, placeholder? } - 功能:全局搜索组件(防抖 300ms 调用
GET /api/search,Cmd/Ctrl+K 快捷键聚焦,Escape 关闭,↑/↓ 键盘导航,Enter 跳转;下拉展示 question/textbook/exam/announcement 四类结果,点击外部自动关闭) - 内部使用:
useDebounce,Input,Link,useRouter - 被使用:
layout/components/site-header.tsx
Switch
- 文件:
components/ui/switch.tsx - 基于:
@radix-ui/react-switch - Props: Radix Switch Root props(含
checked,onCheckedChange,disabled,id,aria-label等) - 功能:开关切换 UI 组件(shadcn 风格,checked/unchecked 两态)
- 被使用:settings/components/notification-preferences-form.tsx
SkipLink
- 文件:
components/a11y/skip-link.tsx - Props:
{ href?, children?, ...AnchorHTMLAttributes },默认 href=#main-content,默认文字"跳转到主内容" - 功能:跳转链接组件,视觉隐藏,获得焦点时高对比度显示,供键盘用户跳过导航直达主内容
- 被使用:待替换
app/(dashboard)/layout.tsx中的内联 skip-link
VisuallyHidden
- 文件:
components/a11y/visually-hidden.tsx - Props:
{ children?, ...HTMLAttributes } - 功能:视觉隐藏但屏幕阅读器可读,用于图标按钮文字描述、表单辅助说明
- 被使用:待扩展
FocusTrap
- 文件:
components/a11y/focus-trap.tsx - Props:
{ children, active?, initialFocusRef?, restoreFocus?, className? } - 功能:焦点陷阱组件,捕获 Tab/Shift+Tab 在容器内循环,支持初始焦点与焦点恢复,用于模态框/对话框
- 被使用:待扩展(Dialog/Sheet 自定义场景)
AriaStatus
- 文件:
components/a11y/aria-status.tsx - Props:
{ children?, politeness?, atomic?, ...HTMLAttributes },默认 politeness=polite,atomic=true - 功能:ARIA 状态通知区域,渲染
aria-live区域(role=status),用于页面级状态通知(如"加载中"、"已保存") - 被使用:待扩展
导出 Hooks
useActionWithToast
- 签名:
useActionWithToast<T>(): { isPending, execute } - 功能:包装 Server Action + toast 反馈
- 被使用:待扩展
useDebounce
- 签名:
useDebounce<T>(value: T, delay?: number): T - 被使用:搜索输入框等
useMediaQuery
- 签名:
useMediaQuery(query: string): boolean - 被使用:响应式布局判断
useLocalStorage
- 签名:
useLocalStorage<T>(key: string, initialValue: T): [T, setter] - 被使用:exam-form (previewTaskStorageKey)
usePermission
- 签名:
usePermission(): { permissions, roles, hasPermission, hasAnyPermission, hasAllPermissions, hasRole } - 功能:客户端权限检查 Hook
- 被使用:layout/app-sidebar.tsx, exams/components, homework/components
类型/接口
ActionState<T>
- 定义:
{ success: boolean; message?: string; errors?: Record<string, string[]>; data?: T } - 被使用:所有模块的 Server Action 返回类型
Permission (类型)
- 定义:
Permissions值的联合类型 - 被使用:auth-guard.ts, use-permission.ts
DataScope (联合类型)
- 定义:
{ type: "all" } | { type: "owned"; userId: string } | { type: "class_taught"; classIds: string[]; subjectIds?: string[] } | { type: "grade_managed"; gradeIds: string[] } | { type: "class_members" } | { type: "children"; childrenIds: string[] } - 被使用:auth-guard.ts, exams/data-access.ts, homework/data-access.ts, dashboard/data-access.ts
AuthContext (接口)
- 定义:
{ userId: string; roles: string[]; permissions: Permission[]; dataScope: DataScope } - 被使用:auth-guard.ts, 所有调用
requirePermission的 Server Action
PermissionDeniedError (类)
- 被使用:所有 Server Action 的 try/catch
数据库表 (shared/db/schema.ts)
| 表名 | 核心字段 | 被哪些模块使用 |
|---|---|---|
users |
id, name, email, emailVerified, image, password, phone, address, gender, age, birthDate, guardianName, guardianPhone, guardianRelation, consentAcceptedAt, gradeId, departmentId, onboardedAt, createdAt, updatedAt | auth, users, dashboard, classes |
accounts |
userId, type, provider, providerAccountId, refresh_token, access_token, expires_at, token_type, scope, id_token, session_state | auth |
sessions |
sessionToken, userId, expires | auth |
verificationTokens |
identifier, token, expires | auth |
roles |
id, name, description, createdAt, updatedAt | auth, auth-guard |
usersToRoles |
userId, roleId | auth, auth-guard |
rolePermissions |
roleId, permission | auth (seed) |
knowledgePoints |
id, name, description, anchorText, parentId, chapterId, level, order, createdAt, updatedAt | textbooks, questions |
questions |
id, content, type, difficulty, authorId, parentId, createdAt, updatedAt | questions, exams, homework |
questionsToKnowledgePoints |
questionId, knowledgePointId | questions |
subjects |
id, name, order, code, createdAt, updatedAt | exams, textbooks |
textbooks |
id, title, subject, grade, publisher, createdAt, updatedAt | textbooks |
chapters |
id, textbookId, title, order, parentId, content, createdAt, updatedAt | textbooks |
departments |
id, name, description, createdAt, updatedAt | school |
classrooms |
id, name, building, floor, capacity, createdAt, updatedAt | school |
academicYears |
id, name, startDate, endDate, isActive, createdAt, updatedAt | school |
schools |
id, name, code, createdAt, updatedAt | school, classes |
grades |
id, schoolId, name, order, gradeHeadId, teachingHeadId, createdAt, updatedAt | school, classes, exams, auth-guard |
classes |
id, schoolId, gradeId, teacherId, name, homeroom, room, invitationCode, schoolName, grade, createdAt, updatedAt | classes, homework, auth-guard |
classSubjectTeachers |
classId, teacherId, subjectId, createdAt, updatedAt | classes, auth-guard |
classEnrollments |
classId, studentId, status, createdAt | classes, homework |
classSchedule |
id, classId, weekday, startTime, endTime, course, location, createdAt, updatedAt | classes |
exams |
id, creatorId, title, description, subjectId, gradeId, status, structure, startTime, endTime, createdAt, updatedAt | exams, homework |
examQuestions |
examId, questionId, score, order | exams |
examSubmissions |
id, examId, studentId, score, status, submittedAt, createdAt, updatedAt | exams |
submissionAnswers |
id, submissionId, questionId, answerContent, score, feedback, createdAt, updatedAt | exams |
homeworkAssignments |
id, creatorId, sourceExamId, title, description, status, structure, availableAt, dueAt, allowLate, lateDueAt, maxAttempts, createdAt, updatedAt | homework |
homeworkAssignmentQuestions |
assignmentId, questionId, score, order | homework |
homeworkAssignmentTargets |
assignmentId, studentId, createdAt | homework |
homeworkSubmissions |
id, assignmentId, studentId, status, attemptNo, score, submittedAt, startedAt, isLate, createdAt, updatedAt | homework |
homeworkAnswers |
id, submissionId, questionId, answerContent, score, feedback, createdAt, updatedAt | homework |
aiProviders |
id, provider, baseUrl, model, apiKeyEncrypted, apiKeyLast4, isDefault, createdBy, updatedBy, createdAt, updatedAt | settings, ai |
announcements |
id, title, content, type, status, targetGradeId, targetClassId, authorId, publishedAt, createdAt, updatedAt | announcements |
auditLogs |
id, userId, userName, action, module, targetId, targetType, detail, ipAddress, userAgent, status, createdAt | audit, shared/lib/audit-logger |
loginLogs |
id, userId, userEmail, action, status, ipAddress, userAgent, errorMessage, createdAt | audit, shared/lib/login-logger, auth |
dataChangeLogs |
id, tableName, recordId, action (create/update/delete), oldValue, newValue, changedBy, changedByName, ipAddress, createdAt | audit, shared/lib/change-logger |
fileAttachments |
id, filename, originalName, mimeType, size, storagePath, url, uploaderId, targetType, targetId, createdAt | files |
gradeRecords |
id, studentId, classId, subjectId, examId, academicYearId, title, score, fullScore, type, semester, recordedBy, remark, createdAt, updatedAt | grades |
coursePlans |
id, classId, subjectId, teacherId, academicYearId, semester, totalHours, completedHours, weeklyHours, startDate, endDate, syllabus, objectives, status, createdBy, createdAt, updatedAt | course-plans |
coursePlanItems |
id, planId, week, topic, content, hours, textbookChapter, notes, isCompleted, completedAt, createdAt, updatedAt | course-plans |
parentStudentRelations |
id, parentId, studentId, relation, createdAt | parent, auth-guard |
messages |
id, senderId, receiverId, subject, content, isRead, readAt, parentMessageId, createdAt | messaging |
messageNotifications |
id, userId, type, title, content, link, isRead, createdAt | messaging |
notificationPreferences |
id, userId (unique FK→users), emailEnabled, smsEnabled, pushEnabled, homeworkNotifications, gradeNotifications, announcementNotifications, messageNotifications, attendanceNotifications, createdAt, updatedAt | messaging, settings |
attendanceRecords |
id, studentId, classId, scheduleId, date, status, remark, recordedBy, createdAt, updatedAt | attendance |
attendanceRules |
id, classId, lateThresholdMinutes, earlyLeaveThresholdMinutes, enableAutoMark, createdAt, updatedAt | attendance |
schedulingRules |
id, classId, maxDailyHours, maxContinuousHours, lunchBreakStart, lunchBreakEnd, morningStart, afternoonEnd, avoidBackToBack, balancedSubjects, createdAt, updatedAt | scheduling |
scheduleChanges |
id, originalScheduleId, classId, originalTeacherId, substituteTeacherId, originalDate, newDate, newStartTime, newEndTime, reason, status, requestedBy, approvedBy, createdAt, updatedAt | scheduling |
passwordSecurity |
id, userId, failedLoginAttempts, lockedUntil, passwordChangedAt, mustChangePassword, lastPasswordChange, createdAt, updatedAt | auth, settings |
knowledgePointMastery |
id, studentId, knowledgePointId, masteryLevel, totalQuestions, correctQuestions, lastAssessedAt, createdAt, updatedAt | diagnostic |
learningDiagnosticReports |
id, studentId, generatedBy, reportType, period, summary, strengths, weaknesses, recommendations, overallScore, status, createdAt, updatedAt | diagnostic |
模块:auth
模块职责
处理用户认证(登录/注册/JWT/Session),提供 NextAuth 实例和中间件。通过 events 回调记录登录日志。 集成密码安全策略(账户锁定、失败登录追踪)和登录速率限制。
导出函数
auth
- 签名:
auth(): Promise<Session | null>(NextAuth 导出) - 功能:获取当前用户 Session
- 被使用:auth-guard.ts, 所有 Server Component 页面, audit-logger.ts
handlers
- 签名:
{ GET, POST }(NextAuth Route Handler) - 被使用:
app/api/auth/[...nextauth]/route.ts
signIn / signOut
- 被使用:login-form.tsx, site-header.tsx
authorize 回调(Credentials Provider)
登录流程集成密码安全策略和速率限制:
- 速率限制:按
IP:email维度限流(RATE_LIMIT_RULES.LOGIN: 5次/15分钟),超限返回 null - 账户锁定检查:通过
isAccountLocked(failedLoginAttempts, lastFailedAt)判断,锁定则返回 null - 密码验证失败:调用
recordFailedLogin递增失败次数,达阈值自动锁定 - 登录成功:调用
resetFailedLogin清零失败次数,resetRateLimit清除限流计数
Events 回调
events.signIn
- 签名:
async signIn({ user }) => void - 功能:用户登录成功后记录登录日志
- 依赖:
shared/lib/login-logger.logLoginEvent - 调用参数:
{ userId: user.id, userEmail: user.email, action: "signin", status: "success" }
events.signOut
- 签名:
async signOut(message) => void - 功能:用户登出后记录登录日志(处理 NextAuth v5 不同 message 形状)
- 依赖:
shared/lib/login-logger.logLoginEvent - 调用参数:
{ userId?, userEmail, action: "signout", status: "success" }
middleware (proxy.ts)
- 签名:
middleware(request: NextRequest) => Promise<NextResponse> - 功能:基于权限点的路由守卫,未登录重定向 /login,无权限重定向角色首页
- 依赖:
getToken(next-auth/jwt),Permissions - 被使用:Next.js middleware 层
模块:exams
模块职责
考试全生命周期管理:创建(手动/AI)、编辑、预览、发布、删除、复制。
导出函数 (actions.ts)
createExamAction
- 签名:
(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>> - 参数说明:
formData包含 mode, title, subject, grade, difficulty, totalScore, durationMin, scheduledAt - 功能:手动模式创建考试草稿
- 依赖:
requirePermission(EXAM_CREATE),shared/db,data-access.persistExamDraft - 被使用:exam-form.tsx
createAiExamAction
- 签名:同上
- 功能:AI 模式创建考试(调用 AI pipeline 生成题目)
- 依赖:
requirePermission(EXAM_AI_GENERATE),ai-pipeline.generateAiCreateDraftFromSource,data-access.persistAiGeneratedExamDraft - 被使用:exam-form.tsx
previewAiExamAction
- 签名:
(prevState: ActionState<AiPreviewData> | null, formData: FormData) => Promise<ActionState<AiPreviewData>> - 功能:AI 预览试卷(不持久化)
- 依赖:
requirePermission(EXAM_AI_GENERATE),ai-pipeline.generateAiPreviewData - 被使用:exam-ai-generator.tsx (via useExamPreview)
regenerateAiQuestionAction
- 签名:
(prevState: ActionState<AiRewriteQuestionData> | null, formData: FormData) => Promise<ActionState<AiRewriteQuestionData>> - 功能:AI 重写单个题目
- 依赖:
requirePermission(EXAM_AI_GENERATE),ai-pipeline.regenerateAiQuestionByInstruction - 被使用:exam-preview-question-editor.tsx
updateExamAction
- 签名:
(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>> - 功能:更新考试信息,含资源归属校验(非 admin 只能改自己的)
- 依赖:
requirePermission(EXAM_UPDATE),shared/db - 被使用:exam-form.tsx
deleteExamAction
- 签名:同上
- 功能:删除考试,含资源归属校验
- 依赖:
requirePermission(EXAM_DELETE),shared/db - 被使用:exam-actions.tsx
duplicateExamAction
- 签名:同上
- 功能:复制考试
- 依赖:
requirePermission(EXAM_DUPLICATE),shared/db - 被使用:exam-actions.tsx
getExamPreviewAction
- 签名:
(examId: string) => Promise<ActionState<...>> - 功能:获取考试预览数据
- 依赖:
requirePermission(EXAM_READ),shared/db - 被使用:exam-viewer.tsx
getSubjectsAction
- 签名:
() => Promise<ActionState<{ id: string; name: string }[]>> - 依赖:
requirePermission(EXAM_READ),shared/db - 被使用:exam-form.tsx
getGradesAction
- 签名:同上
- 依赖:
requirePermission(EXAM_READ),shared/db - 被使用:exam-form.tsx
导出函数 (data-access.ts)
getExams
- 签名:
getExams(params: GetExamsParams & { scope: DataScope }): Promise<Exam[]> - 参数说明:
scope来自auth-guard.getAuthContext().dataScope - 功能:查询考试列表,含数据权限过滤
- 依赖:
shared/db,DataScope - 被使用:teacher/exams/all/page.tsx, homework 创建页面
getExamById
- 签名:
getExamById(id: string, scope?: DataScope): Promise<Exam | null> - 被使用:exam 详情/编辑页面
persistExamDraft / persistAiGeneratedExamDraft
- 被使用:createExamAction, createAiExamAction
omitScheduledAtFromDescription
- 功能:从考试描述中移除 scheduledAt 字段(用于编辑场景)
- 被使用:exams/data-access.ts 内部
resolveSubjectGradeNames
- 功能:将 subjectId/gradeId 解析为可读名称
- 被使用:exams/data-access.ts 内部, buildExamDescription
buildExamDescription
- 功能:构建考试描述文本(含科目、年级、时间等)
- 依赖:
resolveSubjectGradeNames,omitScheduledAtFromDescription - 被使用:createExamAction, createAiExamAction
GetExamsParams (类型)
- 定义:查询参数类型(含 subjectId?, gradeId?, status?, keyword? 等过滤条件)
- 被使用:
getExams,getQuestionsAction
导出函数 (ai-pipeline.ts)
generateAiPreviewData
- 签名:
(input: { title, subject?, grade?, difficulty, totalScore, durationMin, questionCount?, sourceText, aiProviderId? }) => Promise<{ ok, data?, rawOutput?, message? }> - 依赖:
shared/lib/ai.createAiChatCompletion - 被使用:previewAiExamAction
generateAiCreateDraftFromSource
- 被使用:createAiExamAction
regenerateAiQuestionByInstruction
- 被使用:regenerateAiQuestionAction
generateAiExamDraft
- 功能:生成 AI 考试草稿(含结构与题目)
- 依赖:
shared/lib/ai.createAiChatCompletion,AiQuestionSchema,AiGeneratedStructureSchema - 被使用:createAiExamAction
AI Schema 与类型 (ai-pipeline.ts)
AiQuestionSchema
- 类型:Zod schema
- 定义:AI 生成题目的校验 schema(type, content, difficulty, score, options? 等)
- 被使用:
generateAiExamDraft,AiGeneratedQuestion
AiInsertQuestionSchema
- 类型:Zod schema
- 定义:插入题目到 DB 的校验 schema(含 authorId, parentId 等 DB 字段)
- 被使用:
persistAiGeneratedExamDraft
AiGeneratedQuestion
- 类型:TypeScript 类型(基于
AiQuestionSchema推断) - 被使用:
AiPreviewData, exams/components
AiGeneratedStructureNode
- 类型:TypeScript 类型
- 定义:AI 生成的试卷结构节点(section 标题、题目列表)
- 被使用:
AiGeneratedStructureSchema
AiGeneratedStructureNodeSchema
- 类型:Zod schema
- 定义:
AiGeneratedStructureNode的校验 schema - 被使用:
AiGeneratedStructureSchema
AiGeneratedStructureSchema
- 类型:Zod schema
- 定义:AI 生成的完整试卷结构校验 schema(含
AiGeneratedStructureNode[]) - 被使用:
generateAiExamDraft
类型/接口
Exam
- 被使用:exams/components, homework/types (sourceExam), dashboard/types
AiPreviewData / AiRewriteQuestionData
- 被使用:exams/actions.ts, exams/components
ExamStatus
- 定义:考试状态枚举类型(draft/published 等)
- 被使用:exams/data-access.ts, exams/components
ExamDifficulty
- 定义:考试难度枚举类型
- 被使用:exams/components, ai-pipeline.ts
SubmissionStatus
- 定义:提交状态枚举类型(in_progress/submitted/graded 等)
- 被使用:examSubmissions 相关逻辑
ExamSubmission
- 定义:考试提交记录类型(含 studentId, score, status 等)
- 被使用:exams/data-access.ts, exams/components
导出 Hooks
useExamPreview
- 签名:
useExamPreview(): { isPending, execute, previewData, error } - 功能:包装
previewAiExamAction,管理 AI 预览状态 - 被使用:exam-ai-generator.tsx
导出组件 (components/)
| 组件文件 | 功能 |
|---|---|
exam-form.tsx |
考试创建/编辑表单(手动 + AI 模式) |
exam-ai-generator.tsx |
AI 生成考试配置面板 |
exam-viewer.tsx |
考试预览展示 |
exam-actions.tsx |
考试操作菜单(复制/删除) |
exam-data-table.tsx |
考试列表数据表格 |
exam-preview-question-editor.tsx |
AI 预览题目编辑器 |
exam-status-badge.tsx |
考试状态徽章 |
exam-card.tsx |
考试卡片 |
exam-list.tsx |
考试列表 |
exam-detail.tsx |
考试详情 |
exam-header.tsx |
考试头部信息 |
exam-info.tsx |
考试基本信息展示 |
exam-question-list.tsx |
考试题目列表 |
exam-question-item.tsx |
考试题目项 |
exam-question-picker.tsx |
题目选择器 |
exam-score-summary.tsx |
分值汇总 |
exam-schedule-picker.tsx |
考试时间选择器 |
exam-subject-grade-select.tsx |
科目年级选择器 |
exam-empty.tsx |
考试空状态 |
assembly/exam-assembly-panel.tsx |
组卷面板 |
assembly/question-pool.tsx |
题库选择池 |
assembly/assembly-cart.tsx |
已选题目购物车 |
assembly/assembly-summary.tsx |
组卷汇总 |
模块:homework
模块职责
作业全生命周期:创建(源自考试)、发布、学生作答、教师批改、数据分析。
导出函数 (actions.ts)
createHomeworkAssignmentAction
- 签名:
(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>> - 功能:从已有考试创建作业
- 依赖:
requirePermission(HOMEWORK_CREATE),shared/db,exams/data-access.getExams - 被使用:homework-assignment-form.tsx
startHomeworkSubmissionAction
- 签名:同上
- 功能:学生开始作答
- 依赖:
requirePermission(HOMEWORK_SUBMIT),shared/db - 被使用:homework-take-view.tsx
saveHomeworkAnswerAction
- 签名:同上
- 功能:保存单题答案
- 依赖:
requirePermission(HOMEWORK_SUBMIT),shared/db - 被使用:homework-take-view.tsx
submitHomeworkAction
- 签名:同上
- 功能:提交作业
- 依赖:
requirePermission(HOMEWORK_SUBMIT),shared/db - 被使用:homework-take-view.tsx
gradeHomeworkSubmissionAction
- 签名:同上
- 功能:教师批改作业
- 依赖:
requirePermission(HOMEWORK_GRADE),shared/db - 被使用:homework-grading-view.tsx
导出函数 (data-access.ts)
getHomeworkAssignments
- 签名:
(params?: { creatorId?, ids?, classId?, scope? }) => Promise<HomeworkAssignmentListItem[]> - 依赖:
shared/db,DataScope - 被使用:teacher 作业列表页, homework-assignment-form.tsx
getStudentHomeworkAssignments
- 签名:
(studentId: string) => Promise<StudentHomeworkAssignmentListItem[]> - 被使用:student/dashboard
getStudentDashboardGrades
- 签名:
(studentId: string) => Promise<StudentDashboardGradeProps> - 被使用:dashboard/data-access.ts (学生仪表盘)
getHomeworkAssignmentAnalytics
- 签名:
(assignmentId: string) => Promise<HomeworkAssignmentAnalytics | null> - 被使用:homework 错误分析组件
getHomeworkAssignmentById
- 签名:
(assignmentId: string, scope?: DataScope) => Promise<HomeworkAssignment | null> - 功能:按 ID 获取作业详情(含数据权限过滤)
- 被使用:homework 详情/编辑页面, homework-take-view.tsx
getHomeworkSubmissionDetails
- 签名:
(submissionId: string, scope?: DataScope) => Promise<HomeworkSubmissionDetails | null> - 功能:获取作业提交详情(含答案列表、学生信息)
- 被使用:homework-grading-view.tsx
getDemoStudentUser
- 签名:
() => Promise<{ id: string; name: string } | null> - 功能:获取演示学生用户(用于 demo/预览场景)
- 被使用:homework demo 相关页面
getStudentHomeworkTakeData
- 签名:
(studentId: string, assignmentId: string) => Promise<StudentHomeworkTakeData | null> - 功能:获取学生作答作业所需完整数据(作业、题目、已答内容、提交状态)
- 被使用:homework-take-view.tsx
Schema (schema.ts)
CreateHomeworkAssignmentSchema
- 类型:Zod schema
- 定义:创建作业的校验 schema(sourceExamId, title, classIds, dueAt, allowLate 等)
- 被使用:
createHomeworkAssignmentAction
CreateHomeworkAssignmentInput
- 类型:TypeScript 类型(基于
CreateHomeworkAssignmentSchema推断) - 被使用:
createHomeworkAssignmentAction, homework-assignment-form.tsx
GradeHomeworkSchema
- 类型:Zod schema
- 定义:批改作业的校验 schema(submissionId, answers[{ questionId, score, feedback }])
- 被使用:
gradeHomeworkSubmissionAction
类型/接口
StudentDashboardGradeProps
- 被使用:dashboard/types.ts (StudentDashboardProps.grades)
HomeworkAssignmentListItem
- 被使用:homework 列表页, homework-assignment-form.tsx
HomeworkAssignment
- 定义:作业完整类型(含 sourceExam, targets, questions 等关联)
- 被使用:homework 详情/编辑页面
HomeworkAssignmentReviewListItem
- 被使用:teacher 批改列表
HomeworkSubmissionListItem
- 被使用:teacher 提交列表
HomeworkSubmission
- 定义:作业提交记录类型
- 被使用:homework-grading-view.tsx
HomeworkSubmissionDetails
- 定义:提交详情类型(含学生、答案列表)
- 被使用:homework-grading-view.tsx
HomeworkAnswer
- 定义:作业答案类型
- 被使用:homework-take-view.tsx, homework-grading-view.tsx
HomeworkAssignmentAnalytics
- 定义:作业分析数据类型(含错误率、平均分等)
- 被使用:homework 错误分析组件
StudentHomeworkAssignmentListItem
- 被使用:student/dashboard
StudentHomeworkTakeData
- 定义:学生作答数据类型(含 assignment, questions, currentAnswers, submission)
- 被使用:homework-take-view.tsx
TeacherGradeTrendItem
- 被使用:dashboard (教师仪表盘)
HomeworkAssignmentStatus
- 定义:作业状态枚举类型(draft/published/closed 等)
- 被使用:homework/data-access.ts, homework/components
HomeworkSubmissionStatus
- 定义:提交状态枚举类型(in_progress/submitted/graded 等)
- 被使用:homework/data-access.ts, homework/components
HomeworkAssignmentTarget
- 定义:作业目标学生类型
- 被使用:homework/data-access.ts
HomeworkQuestion
- 定义:作业题目类型(含 score, order)
- 被使用:homework-take-view.tsx
HomeworkGradingInput
- 定义:批改输入类型
- 被使用:homework-grading-view.tsx
导出组件 (components/)
| 组件文件 | 功能 |
|---|---|
homework-assignment-form.tsx |
作业创建表单(源自考试) |
homework-take-view.tsx |
学生作答视图 |
homework-grading-view.tsx |
教师批改视图 |
homework-assignment-list.tsx |
作业列表 |
homework-assignment-card.tsx |
作业卡片 |
homework-assignment-detail.tsx |
作业详情 |
homework-submission-list.tsx |
提交列表 |
homework-submission-card.tsx |
提交卡片 |
homework-analytics.tsx |
作业分析(错误率/平均分) |
homework-status-badge.tsx |
作业状态徽章 |
模块:questions
模块职责
题库管理:题目 CRUD、知识点关联、题型支持(选择/填空/判断/复合)。
导出函数 (actions.ts)
createNestedQuestion
- 签名:
(prevState: ActionState<string> | undefined, formData: FormData | CreateQuestionInput) => Promise<ActionState<string>> - 依赖:
requirePermission(QUESTION_CREATE),shared/db - 被使用:create-question-dialog.tsx
updateQuestionAction
- 签名:同上
- 依赖:
requirePermission(QUESTION_UPDATE),shared/db - 被使用:question-actions.tsx
deleteQuestionAction
- 签名:同上
- 依赖:
requirePermission(QUESTION_DELETE),shared/db - 被使用:question-actions.tsx
getQuestionsAction
- 签名:
(params: GetQuestionsParams) => Promise<...> - 依赖:
requirePermission(QUESTION_READ),data-access.getQuestions - 被使用:teacher/questions/page.tsx
getKnowledgePointOptionsAction
- 签名:
() => Promise<KnowledgePointOption[]> - 依赖:
requirePermission(QUESTION_READ),shared/db - 被使用:create-question-dialog.tsx
导出函数 (data-access.ts)
getQuestions
- 签名:
(params: GetQuestionsParams & { scope?: DataScope }) => Promise<Question[]> - 功能:查询题目列表(含知识点关联、数据权限过滤)
- 依赖:
shared/db,DataScope - 被使用:
getQuestionsAction, teacher/questions/page.tsx
GetQuestionsParams (类型)
- 定义:查询参数类型(含 keyword?, type?, difficulty?, knowledgePointId?, parentId? 等过滤条件)
- 被使用:
getQuestions,getQuestionsAction
Schema (schema.ts)
QuestionTypeEnum
- 类型:Zod enum
- 定义:题目类型枚举(choice/fill/judge/composite 等)
- 被使用:
BaseQuestionSchema,CreateQuestionSchema
BaseQuestionSchema
- 类型:Zod schema
- 定义:题目基础校验 schema(type, content, difficulty, knowledgePointIds? 等)
- 被使用:
CreateQuestionSchema
CreateQuestionInput
- 类型:TypeScript 类型(基于
CreateQuestionSchema推断) - 被使用:
createNestedQuestion, create-question-dialog.tsx
CreateQuestionSchema
- 类型:Zod schema
- 定义:创建题目的完整校验 schema(含
BaseQuestionSchema+ 选项/答案/子题目) - 被使用:
createNestedQuestion
类型/接口
Question
- 被使用:exams (题目选择), homework (作业题目)
KnowledgePointOption
- 被使用:create-question-dialog.tsx
QuestionType
- 定义:题目类型联合类型(基于
QuestionTypeEnum) - 被使用:questions/components, exams/components, homework/components
导出组件 (components/)
| 组件文件 | 功能 |
|---|---|
create-question-dialog.tsx |
创建题目对话框(含嵌套题目) |
question-actions.tsx |
题目操作菜单(编辑/删除) |
question-data-table.tsx |
题目列表数据表格 |
question-card.tsx |
题目卡片 |
question-detail.tsx |
题目详情展示 |
question-type-badge.tsx |
题目类型徽章 |
模块:textbooks
模块职责
教材与知识体系管理:教材/章节树形结构、知识点 CRUD、Markdown 内容编辑、知识图谱。
导出函数 (actions.ts)
| 函数 | 权限 | 核心功能 |
|---|---|---|
createTextbookAction |
TEXTBOOK_CREATE | 创建教材 |
updateTextbookAction |
TEXTBOOK_UPDATE | 更新教材元信息 |
deleteTextbookAction |
TEXTBOOK_DELETE | 删除教材 |
createChapterAction |
TEXTBOOK_CREATE | 创建章节 |
updateChapterContentAction |
TEXTBOOK_UPDATE | 更新章节内容(Markdown) |
deleteChapterAction |
TEXTBOOK_DELETE | 删除章节 |
createKnowledgePointAction |
TEXTBOOK_CREATE | 创建知识点 |
updateKnowledgePointAction |
TEXTBOOK_UPDATE | 更新知识点 |
deleteKnowledgePointAction |
TEXTBOOK_DELETE | 删除知识点 |
reorderChaptersAction |
TEXTBOOK_UPDATE | 章节排序 |
导出函数 (data-access.ts)
| 函数 | 签名 | 被使用 |
|---|---|---|
getTextbooks |
(query?, subject?, grade?) => Promise<Textbook[]> |
teacher/textbooks/page.tsx |
getTextbookById |
(id: string) => Promise<Textbook | undefined> |
teacher/textbooks/[id]/page.tsx |
getChaptersByTextbookId |
(textbookId: string) => Promise<Chapter[]> |
textbook-reader.tsx |
getKnowledgePointsByChapterId |
(chapterId: string) => Promise<KnowledgePoint[]> |
textbook-reader.tsx |
getKnowledgePointsByTextbookId |
(textbookId: string) => Promise<KnowledgePoint[]> |
textbook-reader.tsx |
写操作函数 (data-access.ts)
| 函数 | 签名 | 被使用 |
|---|---|---|
persistTextbook |
(input: { textbookId, title, subject, grade, publisher, creatorId }) => Promise<void> |
createTextbookAction |
updateTextbookMeta |
(textbookId, input: { title?, subject?, grade?, publisher? }) => Promise<void> |
updateTextbookAction |
deleteTextbookRecord |
(textbookId) => Promise<void> |
deleteTextbookAction |
persistChapter |
(input: { chapterId, textbookId, parentId?, title, order }) => Promise<void> |
createChapterAction |
updateChapterContent |
(chapterId, content, textbookId) => Promise<void> |
updateChapterContentAction |
deleteChapterRecord |
(chapterId, textbookId) => Promise<void> |
deleteChapterAction |
reorderChapters |
(chapterId, newIndex, parentId, textbookId) => Promise<void> |
reorderChaptersAction |
persistKnowledgePoint |
(input: { kpId, chapterId, textbookId, name, description?, anchorText? }) => Promise<void> |
createKnowledgePointAction |
updateKnowledgePointRecord |
(kpId, textbookId, input: { name?, description?, anchorText? }) => Promise<void> |
updateKnowledgePointAction |
deleteKnowledgePointRecord |
(kpId, textbookId) => Promise<void> |
deleteKnowledgePointAction |
导出 Hooks
useTextSelection
- 签名:
useTextSelection() => { selectedText, createDialogOpen, isCreating, handleContentPointerDown, handleContextMenuChange } - 功能:管理教材内容文本选择与知识点创建对话框状态(无参数,内部使用 ref 与状态)
- 被使用:textbook-content-panel.tsx
useKnowledgePointActions
- 签名:
useKnowledgePointActions(textbookId, selectedChapterId, highlightedKpId, setHighlightedKpId, onCreateKP, onEditKP) => { editingKp, editKpDialogOpen, ..., requestDeleteKnowledgePoint, confirmDeleteKnowledgePoint, handleUpdateKnowledgePoint } - 功能:知识点 CRUD 操作集合(6 参数:textbookId, selectedChapterId, highlightedKpId, setHighlightedKpId, onCreateKP, onEditKP)
- 被使用:textbook-reader.tsx
类型/接口
Chapter
- 被使用:textbooks/components, questions (知识点关联)
KnowledgePoint
- 被使用:textbooks/components, questions/types (KnowledgePointOption)
Textbook
- 定义:教材类型(含 id, title, subject, grade, publisher 等)
- 被使用:textbooks/components, teacher/textbooks/page.tsx
TextbookListItem
- 定义:教材列表项类型
- 被使用:teacher/textbooks/page.tsx
ChapterTreeNode
- 定义:章节树节点类型(含 children[])
- 被使用:textbook-reader.tsx, chapter-tree.tsx
KnowledgePointInput
- 定义:知识点创建/更新输入类型
- 被使用:useKnowledgePointActions, createKnowledgePointAction
ChapterInput
- 定义:章节创建输入类型
- 被使用:createChapterAction
ReorderChaptersInput
- 定义:章节排序输入类型
- 被使用:reorderChaptersAction
导出组件 (components/)
| 组件文件 | 功能 |
|---|---|
textbook-reader.tsx |
教材阅读器(章节树 + 内容面板 + 知识点) |
textbook-list.tsx |
教材列表 |
textbook-card.tsx |
教材卡片 |
textbook-form.tsx |
教材创建/编辑表单 |
textbook-content-panel.tsx |
教材内容面板(Markdown 渲染 + 文本选择) |
chapter-tree.tsx |
章节树(可拖拽排序) |
chapter-node.tsx |
章节树节点 |
chapter-form.tsx |
章节创建/编辑表单 |
chapter-content-editor.tsx |
章节 Markdown 内容编辑器 |
knowledge-point-list.tsx |
知识点列表 |
knowledge-point-item.tsx |
知识点项 |
knowledge-point-form.tsx |
知识点创建/编辑表单 |
knowledge-graph.tsx |
知识图谱可视化 |
模块:classes
模块职责
班级管理:班级 CRUD、学生注册/退班、邀请码、课表、学科教师分配。
导出函数 (actions.ts)
| 函数 | 权限 | 核心功能 |
|---|---|---|
createTeacherClassAction |
CLASS_CREATE | 教师创建班级 |
updateTeacherClassAction |
CLASS_UPDATE | 教师更新班级 |
deleteTeacherClassAction |
CLASS_DELETE | 教师删除班级 |
createGradeClassAction |
CLASS_CREATE | 年级主任创建班级 |
updateGradeClassAction |
CLASS_UPDATE | 年级主任更新班级 |
deleteGradeClassAction |
CLASS_DELETE | 年级主任删除班级 |
enrollStudentByEmailAction |
CLASS_ENROLL | 通过邮箱注册学生 |
joinClassByInvitationCodeAction |
CLASS_ENROLL | 通过邀请码加入 |
ensureClassInvitationCodeAction |
CLASS_ENROLL | 确保邀请码存在 |
regenerateClassInvitationCodeAction |
CLASS_ENROLL | 重新生成邀请码 |
setStudentEnrollmentStatusAction |
CLASS_ENROLL | 设置学生状态 |
createClassScheduleItemAction |
CLASS_SCHEDULE | 创建课表项 |
updateClassScheduleItemAction |
CLASS_SCHEDULE | 更新课表项 |
deleteClassScheduleItemAction |
CLASS_SCHEDULE | 删除课表项 |
createAdminClassAction |
CLASS_CREATE | 管理员创建班级 |
updateAdminClassAction |
CLASS_UPDATE | 管理员更新班级 |
deleteAdminClassAction |
CLASS_DELETE | 管理员删除班级 |
导出函数 (data-access.ts)
读操作函数
| 函数 | 签名 | 被使用 |
|---|---|---|
getTeacherClasses |
(params?: { teacherId? }) => Promise<TeacherClass[]> |
teacher/classes/my, dashboard |
getAdminClasses |
() => Promise<AdminClassListItem[]> |
admin 班级管理 |
getGradeManagedClasses |
(userId) => Promise<AdminClassListItem[]> |
grade_head 班级管理 |
getStudentClasses |
(studentId) => Promise<StudentEnrolledClass[]> |
student/dashboard |
getStudentSchedule |
(studentId) => Promise<StudentScheduleItem[]> |
student 课表 |
getClassStudents |
(classId, scope?) => Promise<ClassStudent[]> |
teacher/classes/students |
getClassSchedule |
(classId) => Promise<ClassScheduleItem[]> |
teacher/classes/schedule |
getClassHomeworkInsights |
(classId) => Promise<ClassHomeworkInsights | null> |
classes 作业洞察 |
getGradeHomeworkInsights |
(gradeId) => Promise<GradeHomeworkInsights | null> |
年级作业洞察 |
getClassById |
(classId, scope?) => Promise<Class | null> |
班级详情页 |
getClassDetails |
(classId, scope?) => Promise<ClassDetails | null> |
班级详情(含学生数、教师数) |
getClassSubjectTeachers |
(classId) => Promise<ClassSubjectTeacher[]> |
班级学科教师分配 |
getStudentEnrollmentStatus |
(classId, studentId) => Promise<EnrollmentStatus | null> |
注册状态查询 |
validateInvitationCode |
(code) => Promise<{ valid: boolean; classId?: string }> |
邀请码校验 |
getClassInsights |
(classId) => Promise<ClassInsights | null> |
班级洞察数据 |
写操作函数
| 函数 | 签名 | 被使用 |
|---|---|---|
persistTeacherClass |
(input: { classId, teacherId, name, gradeId, ... }) => Promise<void> |
createTeacherClassAction |
updateTeacherClassRecord |
(classId, input) => Promise<void> |
updateTeacherClassAction |
deleteTeacherClassRecord |
(classId) => Promise<void> |
deleteTeacherClassAction |
persistGradeClass |
(input: { classId, gradeId, ... }) => Promise<void> |
createGradeClassAction |
updateGradeClassRecord |
(classId, input) => Promise<void> |
updateGradeClassAction |
deleteGradeClassRecord |
(classId) => Promise<void> |
deleteGradeClassAction |
enrollStudentByEmail |
(classId, email) => Promise<{ studentId }> |
enrollStudentByEmailAction |
joinClassByInvitationCode |
(code, studentId) => Promise<{ classId }> |
joinClassByInvitationCodeAction |
ensureInvitationCode |
(classId) => Promise<{ code }> |
ensureClassInvitationCodeAction |
regenerateInvitationCode |
(classId) => Promise<{ code }> |
regenerateClassInvitationCodeAction |
setStudentEnrollmentStatus |
(classId, studentId, status) => Promise<void> |
setStudentEnrollmentStatusAction |
persistClassScheduleItem |
(input: { scheduleId, classId, ... }) => Promise<void> |
createClassScheduleItemAction |
updateClassScheduleItem |
(scheduleId, input) => Promise<void> |
updateClassScheduleItemAction |
deleteClassScheduleItem |
(scheduleId) => Promise<void> |
deleteClassScheduleItemAction |
persistAdminClass |
(input: { classId, ... }) => Promise<void> |
createAdminClassAction |
updateAdminClassRecord |
(classId, input) => Promise<void> |
updateAdminClassAction |
deleteAdminClassRecord |
(classId) => Promise<void> |
deleteAdminClassAction |
assignSubjectTeacher |
(classId, teacherId, subjectId) => Promise<void> |
学科教师分配 |
removeClassSubjectTeacher |
(classId, teacherId, subjectId) => Promise<void> |
移除学科教师 |
bulkEnrollStudents |
(classId, studentIds[]) => Promise<void> |
批量注册学生 |
transferStudent |
(fromClassId, toClassId, studentId) => Promise<void> |
学生转班 |
getClassInvitationInfo |
(classId) => Promise<{ code, expiresAt? }> |
邀请信息查询 |
countClassStudents |
(classId) => Promise<number> |
学生计数 |
类型/接口
TeacherClass
- 定义:教师班级类型(含 grade, studentCount 等)
- 被使用:teacher/classes/my, dashboard
AdminClassListItem
- 定义:管理员班级列表项类型
- 被使用:admin 班级管理
StudentEnrolledClass
- 定义:学生已加入班级类型
- 被使用:student/dashboard
StudentScheduleItem
- 定义:学生课表项类型
- 被使用:student 课表
ClassStudent
- 定义:班级学生类型(含 enrollmentStatus)
- 被使用:teacher/classes/students
ClassScheduleItem
- 定义:班级课表项类型
- 被使用:teacher/classes/schedule
ClassHomeworkInsights
- 定义:班级作业洞察类型
- 被使用:classes 作业洞察
GradeHomeworkInsights
- 定义:年级作业洞察类型
- 被使用:年级作业洞察
Class
- 定义:班级基础类型
- 被使用:classes/components
ClassDetails
- 定义:班级详情类型(含学生数、教师数、课表等)
- 被使用:班级详情页
ClassSubjectTeacher
- 定义:班级学科教师类型
- 被使用:班级学科教师分配
EnrollmentStatus
- 定义:注册状态枚举类型(active/inactive/pending)
- 被使用:classes/data-access.ts
ClassInsights
- 定义:班级洞察数据类型
- 被使用:班级洞察页面
CreateClassInput
- 定义:创建班级输入类型
- 被使用:createTeacherClassAction, createAdminClassAction
UpdateClassInput
- 定义:更新班级输入类型
- 被使用:updateTeacherClassAction, updateAdminClassAction
CreateScheduleItemInput
- 定义:创建课表项输入类型
- 被使用:createClassScheduleItemAction
EnrollmentInput
- 定义:注册输入类型
- 被使用:enrollStudentByEmailAction
InvitationCodeResult
- 定义:邀请码结果类型
- 被使用:ensureClassInvitationCodeAction
ClassListItem
- 定义:班级列表项类型
- 被使用:班级列表页
ClassFormValues
- 定义:班级表单值类型
- 被使用:class-form.tsx
导出组件 (components/)
| 组件文件 | 功能 |
|---|---|
class-list.tsx |
班级列表 |
class-card.tsx |
班级卡片 |
class-form.tsx |
班级创建/编辑表单 |
class-detail.tsx |
班级详情 |
class-actions.tsx |
班级操作菜单 |
class-students.tsx |
班级学生列表 |
class-student-card.tsx |
班级学生卡片 |
class-schedule.tsx |
班级课表 |
class-schedule-form.tsx |
课表项创建/编辑表单 |
class-schedule-item.tsx |
课表项展示 |
class-invitation.tsx |
邀请码组件 |
class-invitation-dialog.tsx |
邀请码对话框 |
class-enroll-dialog.tsx |
学生注册对话框 |
class-subject-teachers.tsx |
学科教师分配 |
class-subject-teacher-form.tsx |
学科教师表单 |
class-insights.tsx |
班级洞察 |
class-homework-insights.tsx |
班级作业洞察 |
class-status-badge.tsx |
班级状态徽章 |
模块:school
模块职责
学校基础数据管理:学校、年级、部门、学年的 CRUD。
导出函数 (actions.ts)
所有 12 个 actions 均使用
requirePermission()进行权限校验。 学校 CRUD actions(createSchoolAction/updateSchoolAction/deleteSchoolAction)在写操作成功后调用logAudit()记录操作日志。
| 函数 | 权限 | 核心功能 |
|---|---|---|
createSchoolAction |
SCHOOL_MANAGE | 创建学校(成功后记录 audit log: school.create) |
updateSchoolAction |
SCHOOL_MANAGE | 更新学校(成功后记录 audit log: school.update) |
deleteSchoolAction |
SCHOOL_MANAGE | 删除学校(成功后记录 audit log: school.delete) |
createGradeAction |
GRADE_MANAGE | 创建年级 |
updateGradeAction |
GRADE_MANAGE | 更新年级 |
deleteGradeAction |
GRADE_MANAGE | 删除年级 |
createDepartmentAction |
SCHOOL_MANAGE | 创建部门 |
updateDepartmentAction |
SCHOOL_MANAGE | 更新部门 |
deleteDepartmentAction |
SCHOOL_MANAGE | 删除部门 |
createAcademicYearAction |
SCHOOL_MANAGE | 创建学年 |
updateAcademicYearAction |
SCHOOL_MANAGE | 更新学年 |
deleteAcademicYearAction |
SCHOOL_MANAGE | 删除学年 |
导出函数 (data-access.ts)
| 函数 | 被使用 |
|---|---|
getSchools() |
admin 学校管理, onboarding |
getGrades() |
admin 年级管理, exams, onboarding |
getDepartments() |
admin 部门管理 |
getAcademicYears() |
admin 学年管理 |
getStaffOptions() |
school 组件 (年级主任选择) |
getGradesForStaff(staffId) |
grade_head 视图 |
Schema (schema.ts)
CreateSchoolSchema
- 类型:Zod schema
- 定义:创建学校的校验 schema(name, code)
- 被使用:
createSchoolAction
CreateGradeSchema
- 类型:Zod schema
- 定义:创建年级的校验 schema(schoolId, name, order, gradeHeadId?, teachingHeadId?)
- 被使用:
createGradeAction
CreateDepartmentSchema
- 类型:Zod schema
- 定义:创建部门的校验 schema(name, description?)
- 被使用:
createDepartmentAction
CreateAcademicYearSchema
- 类型:Zod schema
- 定义:创建学年的校验 schema(name, startDate, endDate, isActive?)
- 被使用:
createAcademicYearAction
类型/接口
SchoolListItem
- 定义:学校列表项类型(含 id, name, code)
- 被使用:admin 学校管理
GradeListItem
- 定义:年级列表项类型(含 schoolId, name, gradeHeadId?)
- 被使用:admin 年级管理, exams
DepartmentListItem
- 定义:部门列表项类型
- 被使用:admin 部门管理
AcademicYearListItem
- 定义:学年列表项类型
- 被使用:admin 学年管理
StaffOption
- 定义:员工选项类型(含 id, name)
- 被使用:school 组件 (年级主任选择)
导出组件 (components/)
| 组件文件 | 功能 |
|---|---|
school-form.tsx |
学校创建/编辑表单 |
grade-form.tsx |
年级创建/编辑表单 |
department-form.tsx |
部门创建/编辑表单 |
academic-year-form.tsx |
学年创建/编辑表单 |
模块:dashboard
模块职责
各角色仪表盘数据聚合与展示。
导出函数 (data-access.ts)
getAdminDashboardData
- 签名:
getAdminDashboardData(scope?: DataScope): Promise<AdminDashboardData> - 依赖:
shared/db,DataScope - 被使用:admin/dashboard/page.tsx
类型/接口
StudentDashboardProps
- 被使用:student-dashboard.tsx
- 依赖:
homework/types.StudentDashboardGradeProps
StudentDashboard
- 定义:学生仪表盘组件(原
StudentDashboardView,已重命名) - 被使用:student/dashboard/page.tsx
TeacherDashboardData
- 被使用:teacher-dashboard-view.tsx
- 依赖:
homework/data-access.getTeacherGradeTrends,classes/data-access.getTeacherClasses
TeacherDashboardProps
- 定义:教师仪表盘组件 Props 类型
- 被使用:teacher-dashboard-view.tsx
AdminDashboardData
- 定义:管理员仪表盘数据类型(含 activeSessionsCount, userCount, userRoleCounts, classCount 等)
- 被使用:admin/dashboard/page.tsx
DashboardWidget
- 定义:仪表盘通用小组件类型(含 title, value, trend? 等)
- 被使用:dashboard/components
导出组件 (components/)
| 组件文件 | 功能 |
|---|---|
admin-dashboard-view.tsx |
管理员仪表盘视图 |
teacher-dashboard-view.tsx |
教师仪表盘视图 |
student-dashboard.tsx |
学生仪表盘视图(原 StudentDashboardView) |
parent-dashboard-view.tsx |
家长仪表盘视图 |
dashboard-card.tsx |
仪表盘卡片 |
dashboard-widget.tsx |
仪表盘小组件 |
dashboard-stat.tsx |
统计数据展示 |
dashboard-chart.tsx |
图表组件 |
dashboard-schedule.tsx |
课表展示 |
dashboard-assignment-list.tsx |
作业列表 |
dashboard-assignment-item.tsx |
作业项 |
dashboard-grade-trend.tsx |
成绩趋势 |
dashboard-class-list.tsx |
班级列表 |
dashboard-submission-list.tsx |
提交列表 |
dashboard-empty.tsx |
仪表盘空状态 |
dashboard-header.tsx |
仪表盘头部 |
模块:layout
模块职责
应用布局框架:侧边栏、顶栏、导航配置。
导出组件
AppSidebar
- 内部使用:
usePermission,NAV_CONFIG - 功能:根据权限渲染侧边栏导航
SiteHeader
- 内部使用:
useSession,signOut,NotificationDropdown(来自 messaging 模块),GlobalSearch(来自 shared/components),Breadcrumb - 功能:顶部导航栏(含全局搜索 GlobalSearch、通知下拉菜单展示未读通知数、面包屑导航、用户下拉菜单)
SidebarProvider
- Props:
{ children: React.ReactNode, defaultOpen?: boolean } - 功能:侧边栏状态上下文 Provider(管理展开/折叠状态)
- 被使用:app/layout.tsx
导出 Hooks
useSidebar
- 签名:
useSidebar(): { isOpen, toggle, setOpen, isMobile, openMobile, setOpenMobile } - 功能:访问侧边栏状态(需在
SidebarProvider内使用) - 被使用:app-sidebar.tsx, site-header.tsx
类型/接口
Role
- 定义:
type Role = "admin" | "teacher" | "student" | "parent" | "grade_head" | "teaching_head" - 被使用:NAV_CONFIG, usePermission
NavItem
- 定义:
{ title: string; href: string; icon?: string; permission?: Permission; children?: NavItem[] } - 被使用:NAV_CONFIG, app-sidebar.tsx
导出配置
NAV_CONFIG
- 类型:
Record<Role, NavItem[]> - 每个NavItem含
permission?: Permission字段 - admin 角色菜单包含 "Audit Logs" 项(icon: ScrollText, href: /admin/audit-logs, permission: Permissions.AUDIT_LOG_READ),含子项 Operation Logs 与 Login Logs
- admin 角色菜单的 "School Management" 子菜单包含 "Course Plans" 项(href: /admin/course-plans, permission: Permissions.COURSE_PLAN_MANAGE)
- admin 角色菜单的 "School Management" 子菜单包含 "Import Users" 项(href: /admin/users/import, permission: Permissions.USER_MANAGE)
- admin 角色菜单包含 "Scheduling" 项(icon: CalendarClock, href: /admin/scheduling/rules, permission: Permissions.SCHEDULE_ADJUST),含子项 Rules (/admin/scheduling/rules, permission: SCHEDULE_ADJUST)、Auto Schedule (/admin/scheduling/auto, permission: SCHEDULE_AUTO)、Change Requests (/admin/scheduling/changes, permission: SCHEDULE_ADJUST)
- teacher 角色菜单包含 "Grades" 项(icon: GraduationCap, permission: Permissions.GRADE_RECORD_READ),含子项 All Grades (/teacher/grades)、Batch Entry (/teacher/grades/entry, permission: GRADE_RECORD_MANAGE)、Statistics (/teacher/grades/stats)、Analytics (/teacher/grades/analytics, permission: GRADE_RECORD_READ)
- teacher 角色菜单包含 "Course Plans" 项(icon: CalendarRange, href: /teacher/course-plans, permission: Permissions.COURSE_PLAN_READ)
- teacher 角色菜单包含 "Attendance" 项(icon: CalendarCheck, href: /teacher/attendance, permission: Permissions.ATTENDANCE_MANAGE),含子项 Records (/teacher/attendance)、Take Attendance (/teacher/attendance/sheet, permission: ATTENDANCE_MANAGE)、Statistics (/teacher/attendance/stats, permission: ATTENDANCE_READ)
- teacher 角色菜单包含 "Schedule Changes" 项(icon: CalendarClock, href: /teacher/schedule-changes, permission: Permissions.SCHEDULE_ADJUST)
- teacher 角色菜单包含 "Diagnostic" 项(icon: Stethoscope, href: /teacher/diagnostic, permission: Permissions.DIAGNOSTIC_READ)
- student 角色菜单包含 "My Grades" 项(icon: GraduationCap, href: /student/grades, permission: Permissions.GRADE_RECORD_READ)
- student 角色菜单包含 "Attendance" 项(icon: CalendarCheck, href: /student/attendance, permission: Permissions.ATTENDANCE_READ)
- student 角色菜单包含 "Diagnostic" 项(icon: Stethoscope, href: /student/diagnostic, permission: Permissions.DIAGNOSTIC_READ)
- parent 角色菜单包含 "Dashboard" 项(icon: LayoutDashboard, href: /parent/dashboard,无 permission 字段,仅需登录)
- parent 角色菜单包含 "Grades" 项(icon: GraduationCap, href: /parent/grades, permission: Permissions.GRADE_RECORD_READ)
- parent 角色菜单包含 "Attendance" 项(icon: CalendarCheck, href: /parent/attendance, permission: Permissions.ATTENDANCE_READ)
- parent 角色菜单包含 "Announcements" 项(icon: Megaphone, href: /announcements, permission: Permissions.ANNOUNCEMENT_READ)
- 所有角色(admin/teacher/student/parent)菜单均包含 "Messages" 项(icon: Mail, href: /messages, permission: Permissions.MESSAGE_READ)
- 被使用:app-sidebar.tsx
模块:settings
模块职责
系统设置:AI Provider 配置、用户偏好、密码安全(修改密码、强度校验)。
导出函数 (actions.ts)
所有 3 个 actions 均使用
requirePermission(AI_CONFIGURE)进行权限校验。
| 函数 | 权限 | 核心功能 |
|---|---|---|
getAiProviderSummaries() |
AI_CONFIGURE | 获取 AI Provider 列表 |
upsertAiProviderAction(data) |
AI_CONFIGURE | 创建/更新 AI Provider |
testAiProviderAction(data) |
AI_CONFIGURE | 测试 AI Provider 连通性 |
导出函数 (actions-password.ts)
changePasswordAction
- 签名:
(prevState: ActionState<null>, formData: FormData) => Promise<ActionState<null>> - 权限:
requireAuth()(任何登录用户可修改自己的密码) - 功能:修改当前用户密码(校验当前密码、新密码策略、速率限制 PASSWORD_CHANGE: 5次/分钟)
- 依赖:
requireAuth,validatePassword,rateLimit, bcryptjs (hash/compare),shared/db(users, passwordSecurity) - 被使用:settings/components/password-change-form.tsx
类型/接口
AiProviderSummary
- 定义:AI Provider 摘要类型(含 id, provider, baseUrl, model, apiKeyLast4, isDefault 等,不含完整 apiKey)
- 被使用:getAiProviderSummaries, settings/components
导出组件 (components/)
| 组件文件 | 功能 |
|---|---|
ai-provider-list.tsx |
AI Provider 列表 |
ai-provider-form.tsx |
AI Provider 创建/编辑表单 |
ai-provider-card.tsx |
AI Provider 卡片 |
ai-provider-test-dialog.tsx |
AI Provider 测试对话框 |
ai-provider-actions.tsx |
AI Provider 操作菜单 |
settings-layout.tsx |
设置页面布局 |
password-change-form.tsx |
密码修改表单(当前密码/新密码/确认密码 + 强度指示器 + 需求提示) |
notification-preferences-form.tsx |
通知偏好表单(Delivery Channels: push/email/sms + Notification Categories: messages/announcements/homework/grades/attendance;Switch 切换 + 隐藏 checkbox 提交;useActionState 调用 updateNotificationPreferencesAction;toast 反馈) |
admin-settings-view.tsx |
管理员设置视图(General/Appearance/Security/Notifications tab,Security 含 PasswordChangeForm,Notifications 含 NotificationPreferencesForm;接收 notificationPreferences prop) |
teacher-settings-view.tsx |
教师设置视图(同上,含 Notifications tab) |
student-settings-view.tsx |
学生设置视图(同上,含 Notifications tab) |
模块:users
模块职责
用户个人资料管理:当前用户查看与更新自己的资料;用户批量导入/导出(Excel)。
模块路径
src/modules/users
导出函数 (actions.ts)
updateUserProfile
- 签名:
(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>> - 功能:更新当前用户个人资料(name, phone, address, gender, age 等)
- 依赖:
requireAuth(),shared/db - 被使用:profile-form.tsx
注:本模块仅使用
requireAuth()校验登录状态,不涉及权限点(用户只能修改自己的资料)。
downloadUserTemplateAction
- 签名:
() => Promise<ActionState<string>> - 权限:
requirePermission(USER_MANAGE) - 功能:生成用户导入模板(返回 base64 编码的 Excel)
- 依赖:
requirePermission,import-export.generateUserImportTemplate - 被使用:components/user-import-dialog.tsx
importUsersAction
- 签名:
(prevState: ActionState<UserImportResult> | null, formData: FormData) => Promise<ActionState<UserImportResult>> - 权限:
requirePermission(USER_MANAGE) - 功能:导入用户:接收文件,解析+验证+批量创建(默认密码 123456)
- 依赖:
requirePermission,shared/lib/excel.parseExcel,import-export.parseUserImportData,import-export.batchImportUsers - 被使用:components/user-import-dialog.tsx
exportUsersAction
- 签名:
(role?: string) => Promise<ActionState<{ buffer: string; filename: string }>> - 权限:
requirePermission(USER_MANAGE) - 功能:导出用户列表(返回 base64 编码的 Excel)
- 依赖:
requirePermission,import-export.exportUsersToExcel - 被使用:待扩展
导出函数 (data-access.ts)
getUserProfile
- 签名:
(userId: string) => Promise<UserProfile | null> - 功能:获取用户个人资料(含 name, email, phone, address, gender, age, grade, department 等)
- 依赖:
shared/db - 被使用:profile/page.tsx, profile-form.tsx
导出函数 (import-export.ts)
generateUserImportTemplate
- 签名:
() => Promise<Buffer> - 功能:生成用户导入模板(列:姓名/邮箱/角色/手机/班级邀请码,含示例行)
- 依赖:
shared/lib/excel.generateTemplate - 被使用:
downloadUserTemplateAction
parseUserImportData
- 签名:
(rows: Record<string, unknown>[]) => UserImportValidation - 功能:解析并验证导入行(校验姓名/邮箱格式/角色枚举/邀请码仅 student)
- 依赖:无
- 被使用:
importUsersAction
batchImportUsers
- 签名:
(records: UserImportRecord[]) => Promise<UserImportResult> - 功能:批量创建用户(默认密码 123456 bcrypt 哈希,自动创建 usersToRoles,student 通过邀请码自动加入班级)
- 依赖:
shared/db,bcryptjs,@paralleldrive/cuid2 - 被使用:
importUsersAction
exportUsersToExcel
- 签名:
(params: { scope: DataScope; role?: string }) => Promise<Buffer> - 功能:导出用户列表到 Excel(含姓名/邮箱/手机/性别/年龄/角色/创建时间)
- 依赖:
shared/db,shared/lib/excel.exportToExcel - 被使用:
exportUsersAction,app/api/export/route.ts
类型/接口
UserProfile
- 定义:用户资料类型(含 id, name, email, phone, address, gender, age, gradeId?, departmentId?, onboardedAt? 等)
- 被使用:profile/page.tsx, profile-form.tsx
UpdateUserProfileInput
- 定义:更新用户资料输入类型(name?, phone?, address?, gender?, age? 等可选字段)
- 被使用:
updateUserProfile, profile-form.tsx
UserImportRecord
- 定义:
{ name, email, role, phone?, invitationCode? } - 被使用:
parseUserImportData,batchImportUsers
UserImportValidation
- 定义:
{ valid: UserImportRecord[], invalid: Array<{ row, record, errors }> } - 被使用:
parseUserImportData
UserImportResult
- 定义:
{ successCount, failedCount, errors: Array<{ row, email, error }> } - 被使用:
batchImportUsers,importUsersAction
导出组件 (components/)
| 组件文件 | 功能 |
|---|---|
user-import-dialog.tsx |
用户批量导入对话框(4 状态:idle/preview/importing/done;下载模板→上传预览→确认导入→结果展示) |
模块:audit
模块职责
操作日志与登录日志查询:提供审计日志的列表展示、筛选与分页能力,支撑管理员审计与合规需求。
模块路径
src/modules/audit
导出函数 (data-access.ts)
getAuditLogs
- 签名:
getAuditLogs(params?: AuditLogQueryParams): Promise<PaginatedResult<AuditLog>> - 参数说明:
params: AuditLogQueryParams,含 userId?, module?, action?, status?, page?, pageSize?, startDate?, endDate? - 功能:分页查询操作日志(支持按模块/操作/状态/日期范围过滤)
- 依赖:
shared/db(auditLogs 表) - 被使用:app/(dashboard)/admin/audit-logs/page.tsx
getLoginLogs
- 签名:
getLoginLogs(params?: LoginLogQueryParams): Promise<PaginatedResult<LoginLog>> - 参数说明:
params: LoginLogQueryParams,含 userId?, action?, status?, page?, pageSize?, startDate?, endDate? - 功能:分页查询登录日志(支持按操作/状态/日期范围过滤)
- 依赖:
shared/db(loginLogs 表) - 被使用:app/(dashboard)/admin/audit-logs/login-logs/page.tsx
getAuditModuleOptions
- 签名:
getAuditModuleOptions(): Promise<string[]> - 功能:获取操作日志中所有不同的 module 值(用于筛选下拉框)
- 依赖:
shared/db(auditLogs 表) - 被使用:app/(dashboard)/admin/audit-logs/page.tsx
getDataChangeLogs
- 签名:
getDataChangeLogs(params?: DataChangeLogQueryParams): Promise<PaginatedResult<DataChangeLog>> - 参数说明:
params: DataChangeLogQueryParams,含 tableName?, recordId?, action?, userId?, page?, pageSize?, startDate?, endDate? - 功能:分页查询数据变更日志(支持按表名/记录ID/操作/用户/日期范围过滤)
- 依赖:
shared/db(dataChangeLogs 表) - 被使用:
getDataChangeLogsAction
getDataChangeStats
- 签名:
getDataChangeStats(): Promise<DataChangeStat[]> - 功能:按 tableName 分组统计数据变更日志数量(用于统计卡片)
- 依赖:
shared/db(dataChangeLogs 表) - 被使用:
getDataChangeLogsAction
getDataChangeTableOptions
- 签名:
getDataChangeTableOptions(): Promise<string[]> - 功能:获取数据变更日志中所有不同的 tableName 值(用于筛选下拉框)
- 依赖:
shared/db(dataChangeLogs 表) - 被使用:
getDataChangeLogsAction
getDataChangeLogsForExport
- 签名:
getDataChangeLogsForExport(params?: DataChangeLogQueryParams): Promise<DataChangeLog[]> - 功能:导出用:按条件查询全部数据变更日志(取前 100 条,无分页上限封顶)
- 依赖:
getDataChangeLogs - 被使用:
exportDataChangeLogsAction
getAuditLogsForExport
- 签名:
getAuditLogsForExport(params?: AuditLogQueryParams): Promise<AuditLog[]> - 功能:导出用:按条件查询全部操作日志(取前 100 条)
- 依赖:
getAuditLogs - 被使用:
exportAuditLogsAction
getLoginLogsForExport
- 签名:
getLoginLogsForExport(params?: LoginLogQueryParams): Promise<LoginLog[]> - 功能:导出用:按条件查询全部登录日志(取前 100 条)
- 依赖:
getLoginLogs - 被使用:
exportLoginLogsAction
导出函数 (actions.ts)
所有 actions 均使用
requirePermission(AUDIT_LOG_READ)进行权限校验(数据变更日志复用 AUDIT_LOG_READ 权限)。
| 函数 | 权限 | 核心功能 |
|---|---|---|
getDataChangeLogsAction |
AUDIT_LOG_READ | 获取数据变更日志(分页结果 + tableOptions + stats 三者并行加载) |
exportAuditLogsAction |
AUDIT_LOG_READ | 导出操作日志为 Excel(返回 { buffer, filename }) |
exportLoginLogsAction |
AUDIT_LOG_READ | 导出登录日志为 Excel(返回 { buffer, filename }) |
exportDataChangeLogsAction |
AUDIT_LOG_READ | 导出数据变更日志为 Excel(返回 { buffer, filename }) |
类型/接口 (types.ts)
AuditLog
- 定义:操作日志类型(含 id, userId, userName, action, module, targetId, targetType, detail, ipAddress, userAgent, status, createdAt)
- 被使用:audit/components, audit/data-access
LoginLog
- 定义:登录日志类型(含 id, userId, userEmail, action, status, ipAddress, userAgent, errorMessage, createdAt)
- 被使用:audit/components, audit/data-access
AuditLogQueryParams
- 定义:操作日志查询参数类型(含 userId?, module?, action?, status?, page?, pageSize?, startDate?, endDate?)
- 被使用:
getAuditLogs, audit-logs/page.tsx
LoginLogQueryParams
- 定义:登录日志查询参数类型(含 userId?, action?, status?, page?, pageSize?, startDate?, endDate?)
- 被使用:
getLoginLogs, login-logs/page.tsx
PaginatedResult<T>
- 定义:分页结果接口(含 items: T[], total, page, pageSize, totalPages)
- 被使用:
getAuditLogs,getLoginLogs,getDataChangeLogs, audit/components
DataChangeAction
- 定义:
"create" | "update" | "delete" - 被使用:data-access, change-logger, types.DataChangeLog
DataChangeLog
- 定义:数据变更日志类型(含 id, tableName, recordId, action, oldValue, newValue, changedBy, changedByName, ipAddress, createdAt)
- 被使用:audit/data-access, audit/actions
DataChangeStat
- 定义:
{ tableName: string; count: number }(按表名分组的变更统计) - 被使用:
getDataChangeStats,getDataChangeLogsAction
DataChangeLogQueryParams
- 定义:数据变更日志查询参数类型(含 tableName?, recordId?, action?, userId?, page?, pageSize?, startDate?, endDate?)
- 被使用:
getDataChangeLogs,getDataChangeLogsForExport,getDataChangeLogsAction,exportDataChangeLogsAction
导出组件 (components/)
| 组件文件 | 功能 |
|---|---|
audit-log-table.tsx |
操作日志表格(含分页控件) |
audit-log-filters.tsx |
操作日志筛选器(模块/操作/状态/日期,基于 nuqs) |
audit-log-view.tsx |
操作日志视图(筛选+表格+URL 分页) |
login-log-table.tsx |
登录日志表格(含分页控件) |
login-log-filters.tsx |
登录日志筛选器(操作/状态/日期,基于 nuqs) |
login-log-view.tsx |
登录日志视图(筛选+表格+URL 分页) |
模块:announcements
模块职责
通知公告系统:创建、编辑、发布、归档、删除公告,所有登录用户可查看已发布公告。
模块路径
src/modules/announcements
导出函数 (actions.ts)
所有 manage actions 均使用
requirePermission(ANNOUNCEMENT_MANAGE)进行权限校验。
| 函数 | 权限 | 核心功能 |
|---|---|---|
createAnnouncementAction |
ANNOUNCEMENT_MANAGE | 创建公告(草稿/已发布) |
updateAnnouncementAction |
ANNOUNCEMENT_MANAGE | 更新公告 |
deleteAnnouncementAction |
ANNOUNCEMENT_MANAGE | 删除公告 |
publishAnnouncementAction |
ANNOUNCEMENT_MANAGE | 发布公告(设置 published 状态) |
archiveAnnouncementAction |
ANNOUNCEMENT_MANAGE | 归档公告 |
getAnnouncementsAction |
requireAuth | 获取公告列表(所有登录用户可读) |
导出函数 (data-access.ts)
| 函数 | 签名 | 被使用 |
|---|---|---|
getAnnouncements |
(params?: { status?, type?, page?, pageSize? }) => Promise<Announcement[]> |
admin/announcements, announcements 页面 |
getAnnouncementById |
(id: string) => Promise<Announcement | null> |
编辑页面 |
Schema (schema.ts)
CreateAnnouncementSchema
- 类型:Zod schema
- 定义:创建公告的校验 schema(title, content, type, status, targetGradeId?, targetClassId?, publishedAt?)
- 被使用:
createAnnouncementAction
UpdateAnnouncementSchema
- 类型:Zod schema
- 定义:更新公告的校验 schema(同上)
- 被使用:
updateAnnouncementAction
类型/接口
Announcement
- 定义:公告完整类型(含 id, title, content, type, status, targetGradeId, targetClassId, authorId, authorName, publishedAt, createdAt, updatedAt)
- 被使用:announcements/components, 页面
AnnouncementListItem
- 定义:公告列表项类型(同 Announcement)
- 被使用:列表页
AnnouncementStatus
- 定义:
"draft" | "published" | "archived" - 被使用:data-access, components
AnnouncementType
- 定义:
"school" | "grade" | "class" - 被使用:data-access, components
GetAnnouncementsParams
- 定义:查询参数类型(status?, type?, page?, pageSize?)
- 被使用:
getAnnouncements,getAnnouncementsAction
导出组件 (components/)
| 组件文件 | 功能 |
|---|---|
announcement-list.tsx |
公告列表(支持状态筛选) |
announcement-card.tsx |
单条公告卡片 |
announcement-form.tsx |
创建/编辑表单 |
announcement-detail.tsx |
详情查看(含发布/归档/删除操作) |
admin-announcements-view.tsx |
管理端公告视图(列表+创建对话框) |
模块:files
模块职责
文件上传与管理:通过 API 路由处理文件上传(保存到 public/uploads/YYYY-MM/),记录文件元数据到 DB,支持按关联资源(exam/textbook/question/announcement)多态查询、下载与删除。
模块路径
src/modules/files
导出函数 (data-access.ts)
| 函数 | 签名 | 被使用 |
|---|---|---|
createFileAttachment |
(data: CreateFileAttachmentInput) => Promise<FileAttachment | null> |
app/api/upload/route.ts |
getFileAttachment |
(id: string) => Promise<FileAttachment | null> |
app/api/files/[id]/route.ts |
getFileAttachmentsByTarget |
(targetType: string, targetId: string) => Promise<FileAttachment[]> |
按关联资源查询文件列表 |
getFileAttachmentsByUploader |
(uploaderId: string) => Promise<FileAttachment[]> |
按上传者查询文件列表 |
getAllFileAttachments |
(limit?: number) => Promise<FileAttachment[]> |
app/(dashboard)/admin/files/page.tsx |
deleteFileAttachment |
(id: string) => Promise<boolean> |
app/api/files/[id]/route.ts |
deleteFileAttachments |
(ids: string[]) => Promise<BatchDeleteResult> |
app/api/files/batch-delete/route.ts(批量删除 DB 记录,失败回退逐条删除) |
getFileAttachmentsWithFilters |
(params: FileAttachmentQueryParams) => Promise<FileAttachment[]> |
管理员文件筛选(mimeType 精确/前缀匹配 + originalName/filename 模糊搜索 + limit/offset 分页) |
getFileStats |
() => Promise<FileStats> |
文件统计(总数、总大小、按 mimeType 分组) |
getFileAttachmentsByIds |
(ids: string[]) => Promise<FileAttachment[]> |
app/api/files/batch-delete/route.ts(批量删除前获取磁盘路径) |
类型/接口 (types.ts)
FileAttachment
- 定义:文件附件完整类型(含 id, filename, originalName, mimeType, size, storagePath, url, uploaderId, targetType, targetId, createdAt)
- 被使用:files/components, data-access, API 路由
FileUploadResult
- 定义:上传成功返回类型
{ id, url, filename, originalName, size, mimeType } - 被使用:
app/api/upload/route.ts响应, file-upload.tsx 回调
FileTargetType
- 定义:
"exam" | "textbook" | "question" | "announcement" - 被使用:types.FileAttachment.targetType, file-upload.tsx
CreateFileAttachmentInput
- 定义:创建文件附件记录的输入类型
- 被使用:
createFileAttachment
导出组件 (components/)
| 组件文件 | 功能 |
|---|---|
file-upload.tsx |
文件上传组件(拖拽+点击上传,进度条,文件类型校验,调用 /api/upload) |
file-list.tsx |
文件列表展示(图标、文件名、大小、下载链接、删除按钮) |
file-preview.tsx |
文件预览(图片直接预览,PDF iframe,其他下载) |
file-icon.tsx |
根据 MIME 类型显示不同图标与颜色 |
admin-files-view.tsx |
管理端文件视图(上传+列表+删除) |
API 路由
POST /api/upload
- 文件:
app/api/upload/route.ts - 功能:接收
multipart/form-data,保存文件到public/uploads/YYYY-MM/cuid.ext,写入 DB,返回FileUploadResult - 权限:
requireAuth()(需登录) - 限制:10MB、白名单 MIME 类型
GET /api/files/[id]
- 文件:
app/api/files/[id]/route.ts - 功能:获取文件元数据
- 权限:
requireAuth()
DELETE /api/files/[id]
- 文件:
app/api/files/[id]/route.ts - 功能:删除文件(DB 记录 + 磁盘文件)
- 权限:
requirePermission(FILE_DELETE)
POST /api/files/batch-delete
- 文件:
app/api/files/batch-delete/route.ts - 功能:批量删除文件(先查文件记录,通过
storageProvider.delete删除磁盘文件静默失败,再调用deleteFileAttachments删除 DB 记录) - 权限:
requirePermission(FILE_DELETE) - 请求体:JSON
{ ids: string[] } - 响应:
{ success, message, deletedCount, failedIds }
POST /api/export
- 文件:
app/api/export/route.ts - 功能:Excel 导出(grades/users/attendance),按
type分发到exportGradeRecordsToExcel/exportUsersToExcel,返回application/vnd.openxmlformats-officedocument.spreadsheetml.sheet二进制流 - 权限:
requireAuth()(需登录) - 请求体:JSON
{ type: "grades" | "users" | "attendance", params?: Record<string, unknown> }
POST /api/import
- 文件:
app/api/import/route.ts - 功能:Excel 解析预览(不写 DB),接收 Excel 文件,调用
parseExcel返回 sheets 预览数据(实际导入由users/actions.importUsersAction完成) - 权限:
requirePermission(USER_MANAGE) - 请求体:
multipart/form-data,字段file - 限制:仅
.xlsx/.xls,10MB 上限
模块:grades
模块职责
成绩分析模块:成绩录入(单条/批量)、查询、统计报表(均分、中位数、标准差、及格率、优秀率、班级排名)、Excel 导出(成绩明细+统计汇总/班级多科目横向对比)、趋势对比分析(成绩趋势、班级对比、科目对比、分数分布、排名趋势)。
模块路径
src/modules/grades
导出函数 (actions.ts)
所有 manage actions 均使用
requirePermission(GRADE_RECORD_MANAGE)进行权限校验,read actions 使用requirePermission(GRADE_RECORD_READ)。
| 函数 | 权限 | 核心功能 |
|---|---|---|
createGradeRecordAction |
GRADE_RECORD_MANAGE | 创建单条成绩记录 |
batchCreateGradeRecordsAction |
GRADE_RECORD_MANAGE | 批量录入成绩(班级+科目+考试,表格形式) |
updateGradeRecordAction |
GRADE_RECORD_MANAGE | 更新成绩记录 |
deleteGradeRecordAction |
GRADE_RECORD_MANAGE | 删除成绩记录 |
getGradeRecordsAction |
GRADE_RECORD_READ | 查询成绩列表(按 scope 过滤) |
getClassGradeStatsAction |
GRADE_RECORD_READ | 获取班级成绩统计 |
getStudentGradeSummaryAction |
GRADE_RECORD_READ | 获取学生成绩汇总(学生/家长只能查自己/子女) |
getClassRankingAction |
GRADE_RECORD_READ | 获取班级排名 |
getGradeRecordByIdAction |
GRADE_RECORD_READ | 获取单条成绩记录 |
exportGradesAction |
GRADE_RECORD_READ | 导出成绩到 Excel(detail=成绩明细+统计汇总,class=班级多科目横向对比总表),返回 base64 buffer |
导出函数 (actions-analytics.ts)
所有 analytics actions 均使用
requirePermission(GRADE_RECORD_READ)进行权限校验。
| 函数 | 权限 | 核心功能 |
|---|---|---|
getGradeTrendAction |
GRADE_RECORD_READ | 获取成绩趋势(按学生/科目/学期,返回归一化分数趋势点) |
getClassComparisonAction |
GRADE_RECORD_READ | 获取班级对比(同年级各班的均分/及格率/优秀率) |
getSubjectComparisonAction |
GRADE_RECORD_READ | 获取科目对比(同班级各科目雷达图数据) |
getGradeDistributionAction |
GRADE_RECORD_READ | 获取分数分布(90-100/80-89/70-79/60-69/<60 各区间人数) |
getRankingTrendAction |
GRADE_RECORD_READ | 获取排名趋势(学生历次考试排名变化,含 DataScope 二次校验) |
导出函数 (data-access.ts)
| 函数 | 签名 | 被使用 |
|---|---|---|
getGradeRecords |
(params: GradeQueryParams & { scope: DataScope; currentUserId?: string }) => Promise<GradeRecordListItem[]> |
teacher/grades/page.tsx, getGradeRecordsAction |
getGradeRecordById |
(id: string) => Promise<GradeRecord | null> |
getGradeRecordByIdAction |
createGradeRecord |
(data: CreateGradeRecordInput, recordedBy: string) => Promise<string> |
createGradeRecordAction |
batchCreateGradeRecords |
(data: BatchCreateGradeRecordInput, recordedBy: string) => Promise<number> |
batchCreateGradeRecordsAction |
updateGradeRecord |
(id: string, data: UpdateGradeRecordInput) => Promise<void> |
updateGradeRecordAction |
deleteGradeRecord |
(id: string) => Promise<void> |
deleteGradeRecordAction |
getClassGradeStats |
(classId, subjectId?, examId?) => Promise<GradeStats | null> |
getClassGradeStatsAction |
getStudentGradeSummary |
(studentId: string) => Promise<StudentGradeSummary | null> |
getStudentGradeSummaryAction, student/grades, parent/grades |
getClassRanking |
(classId, subjectId?, examId?) => Promise<ClassRankingItem[]> |
getClassRankingAction, teacher/grades/stats |
getClassStudentsForEntry |
(classId: string) => Promise<Array<{ id, name, email }>> |
teacher/grades/entry |
getClassGradeStatsWithMeta |
(classId, subjectId?, examId?) => Promise<ClassGradeStats | null> |
teacher/grades/stats |
导出函数 (data-access-analytics.ts)
| 函数 | 签名 | 被使用 |
|---|---|---|
getGradeTrend |
(params: { studentId; subjectId?; semester?; scope: DataScope }) => Promise<GradeTrendResult> |
getGradeTrendAction, teacher/grades/analytics |
getClassComparison |
(params: { gradeId; subjectId; examId?; scope: DataScope }) => Promise<ClassComparisonItem[]> |
getClassComparisonAction, teacher/grades/analytics |
getSubjectComparison |
(params: { classId; examId?; semester?; scope: DataScope }) => Promise<SubjectComparisonItem[]> |
getSubjectComparisonAction, teacher/grades/analytics |
getGradeDistribution |
(params: { classId; subjectId?; examId?; scope: DataScope }) => Promise<GradeDistributionResult> |
getGradeDistributionAction, teacher/grades/analytics |
导出函数 (data-access-ranking.ts)
| 函数 | 签名 | 被使用 |
|---|---|---|
getRankingTrend |
(studentId: string, subjectId?, semester?) => Promise<RankingTrendResult | null> |
getRankingTrendAction |
导出函数 (export.ts)
exportGradeRecordsToExcel
- 签名:
(params: { classId: string; subjectId?: string; examId?: string; scope: DataScope }) => Promise<Buffer> - 功能:导出成绩单(Sheet1 成绩明细,Sheet2 统计汇总:均分/中位数/最高分/最低分/标准差/及格率/优秀率/参考人数)
- 依赖:
shared/lib/excel.exportToExcel,data-access.getGradeRecords,data-access.getClassGradeStats - 被使用:
exportGradesAction,app/api/export/route.ts
exportClassGradeReportToExcel
- 签名:
(params: { classId: string; scope: DataScope }) => Promise<Buffer> - 功能:导出班级成绩总表(多科目横向对比,含总分/平均分/排名列)
- 依赖:
shared/db,shared/lib/excel.exportToExcel,data-access.getGradeRecords - 被使用:
exportGradesAction
formatDateForFile
- 签名:
(d?: Date) => string - 功能:格式化日期为
YYYY-MM-DD用于文件名 - 依赖:无
- 被使用:
exportGradesAction
Schema (schema.ts)
CreateGradeRecordSchema
- 类型:Zod schema
- 定义:创建成绩记录的校验 schema(studentId, classId, subjectId, examId?, title, score, fullScore?, type?, semester?, remark?)
- 被使用:
createGradeRecordAction
BatchCreateGradeRecordSchema
- 类型:Zod schema
- 定义:批量录入成绩的校验 schema(classId, subjectId, examId?, title, fullScore?, type?, semester?, records[{ studentId, score, remark? }])
- 被使用:
batchCreateGradeRecordsAction
UpdateGradeRecordSchema
- 类型:Zod schema
- 定义:更新成绩记录的校验 schema(title?, score?, fullScore?, type?, semester?, remark?, examId?)
- 被使用:
updateGradeRecordAction
类型/接口 (types.ts)
GradeRecord
- 定义:成绩记录完整类型(含 id, studentId, classId, subjectId, examId, title, score, fullScore, type, semester, recordedBy, remark, createdAt, updatedAt)
- 被使用:grades/data-access, grades/components
GradeRecordListItem
- 定义:成绩列表项类型(含 studentName, className, subjectName, recorderName 等关联名称)
- 被使用:grades/components, teacher/grades
GradeStats
- 定义:成绩统计类型(average, median, max, min, stdDev, passRate, excellentRate, count)
- 被使用:grades/components, teacher/grades/stats
ClassGradeStats
- 定义:班级成绩统计类型(classId, className, stats: GradeStats, studentCount)
- 被使用:grades/components, teacher/grades/stats
StudentGradeSummary
- 定义:学生成绩汇总类型(studentId, studentName, records[], averageScore, rank)
- 被使用:grades/components, student/grades, parent/grades
ClassRankingItem
- 定义:班级排名项类型(studentId, studentName, averageScore, rank, recordCount)
- 被使用:grades/components, teacher/grades/stats
GradeRecordType
- 定义:
"exam" | "quiz" | "homework" | "other" - 被使用:grades/data-access, grades/components
GradeQueryParams
- 定义:查询参数类型(classId?, subjectId?, studentId?, type?, semester?, examId?)
- 被使用:
getGradeRecords,getGradeRecordsAction
GradeTrendPoint / GradeTrendResult
- 定义:成绩趋势类型(Point: date, title, score, fullScore, normalizedScore, type;Result: label, points[], averageScore)
- 被使用:getGradeTrend, getGradeTrendAction, grade-trend-chart
ClassComparisonItem
- 定义:班级对比项类型(classId, className, averageScore, passRate, excellentRate, studentCount)
- 被使用:getClassComparison, getClassComparisonAction, class-comparison-chart
SubjectComparisonItem
- 定义:科目对比项类型(subjectId, subjectName, averageScore, passRate, excellentRate)
- 被使用:getSubjectComparison, getSubjectComparisonAction, subject-comparison-chart
GradeDistributionBucket / GradeDistributionResult
- 定义:分数分布类型(Bucket: label, min, max, count, percentage;Result: buckets[], totalCount)
- 被使用:getGradeDistribution, getGradeDistributionAction, grade-distribution-chart
RankingTrendPoint / RankingTrendResult
- 定义:排名趋势类型(Point: title, date, rank, totalStudents, score;Result: studentName, points[])
- 被使用:getRankingTrend, getRankingTrendAction
导出组件 (components/)
| 组件文件 | 功能 |
|---|---|
grade-record-form.tsx |
单条成绩录入表单 |
batch-grade-entry.tsx |
批量录入界面(选择班级+科目+考试,表格形式录入每个学生分数) |
grade-record-list.tsx |
成绩列表(含删除功能) |
grade-stats-card.tsx |
统计卡片(均分、中位数、及格率、优秀率等) |
grade-query-filters.tsx |
查询筛选器(班级、科目、考试类型、学期) |
student-grade-summary.tsx |
学生成绩汇总视图 |
class-grade-report.tsx |
班级成绩报表(含统计+排名) |
export-button.tsx |
成绩导出按钮(DropdownMenu 选择 detail/class 报表类型,调用 exportGradesAction 并触发浏览器下载) |
grade-trend-chart.tsx |
成绩趋势折线图(recharts LineChart,归一化分数 0-100) |
class-comparison-chart.tsx |
班级对比柱状图(recharts BarChart,均分/及格率/优秀率) |
subject-comparison-chart.tsx |
科目对比雷达图(recharts RadarChart) |
grade-distribution-chart.tsx |
分数分布柱状图(recharts BarChart,彩色区间 90-100/80-89/70-79/60-69/<60) |
模块:course-plans
模块职责
课程计划管理:创建、编辑、删除课程计划(含周计划条目),管理员可管理全部,教师/学生/年级主任/教务主任可查看。
导出函数 (actions.ts)
createCoursePlanAction
- 签名:
(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>> - 权限:
requirePermission(COURSE_PLAN_MANAGE) - 功能:创建课程计划
- 依赖:
requirePermission,shared/db,data-access.createCoursePlan - 被以下模块使用:course-plan-form.tsx
updateCoursePlanAction
- 签名:
(id: string, prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>> - 权限:
requirePermission(COURSE_PLAN_MANAGE) - 功能:更新课程计划
- 依赖:
requirePermission,shared/db,data-access.updateCoursePlan - 被以下模块使用:course-plan-form.tsx
deleteCoursePlanAction
- 签名:
(id: string) => Promise<ActionState<string>> - 权限:
requirePermission(COURSE_PLAN_MANAGE) - 功能:删除课程计划
- 依赖:
requirePermission,shared/db,data-access.deleteCoursePlan - 被以下模块使用:course-plan-detail.tsx
getCoursePlansAction
- 签名:
(params?: GetCoursePlansParams) => Promise<ActionState<CoursePlanListItem[]>> - 权限:
requirePermission(COURSE_PLAN_READ) - 功能:获取课程计划列表
- 依赖:
requirePermission,data-access.getCoursePlans
getCoursePlanAction
- 签名:
(id: string) => Promise<ActionState<CoursePlanWithItems>> - 权限:
requirePermission(COURSE_PLAN_READ) - 功能:获取课程计划详情(含周计划条目)
- 依赖:
requirePermission,data-access.getCoursePlanById
createCoursePlanItemAction
- 签名:
(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>> - 权限:
requirePermission(COURSE_PLAN_MANAGE) - 功能:创建周计划条目
- 依赖:
requirePermission,shared/db,data-access.createCoursePlanItem - 被以下模块使用:course-plan-item-editor.tsx
updateCoursePlanItemAction
- 签名:
(id: string, prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>> - 权限:
requirePermission(COURSE_PLAN_MANAGE) - 功能:更新周计划条目
- 依赖:
requirePermission,shared/db,data-access.updateCoursePlanItem - 被以下模块使用:course-plan-item-editor.tsx
deleteCoursePlanItemAction
- 签名:
(id: string) => Promise<ActionState<string>> - 权限:
requirePermission(COURSE_PLAN_MANAGE) - 功能:删除周计划条目
- 依赖:
requirePermission,shared/db,data-access.deleteCoursePlanItem - 被以下模块使用:course-plan-item-editor.tsx
toggleCoursePlanItemCompletedAction
- 签名:
(id: string, completed: boolean) => Promise<ActionState<string>> - 权限:
requirePermission(COURSE_PLAN_MANAGE) - 功能:切换周计划条目完成状态
- 依赖:
requirePermission,shared/db,data-access.updateCoursePlanItem - 被以下模块使用:course-plan-detail.tsx
导出函数 (data-access.ts)
getCoursePlans
- 签名:
(params?: GetCoursePlansParams) => Promise<CoursePlanListItem[]> - 功能:查询课程计划列表(含班级/科目/教师名称,支持按 classId/teacherId/subjectId/status 过滤)
- 依赖:
shared.db,shared.db.schema.coursePlans/classes/subjects/users - 被以下模块使用:admin/course-plans/page.tsx, teacher/course-plans/page.tsx
getCoursePlanById
- 签名:
(id: string) => Promise<CoursePlanWithItems | null> - 功能:按 ID 获取课程计划详情(含周计划条目列表)
- 依赖:
shared.db,shared.db.schema.coursePlans/coursePlanItems/classes/subjects/users - 被以下模块使用:admin/course-plans/[id]/page.tsx, teacher/course-plans/[id]/page.tsx
createCoursePlan
- 签名:
(data: CreateCoursePlanInput, createdBy: string) => Promise<string> - 功能:创建课程计划记录
- 依赖:
shared.db,shared.db.schema.coursePlans,@paralleldrive/cuid2 - 被以下模块使用:createCoursePlanAction
updateCoursePlan
- 签名:
(id: string, data: Partial<UpdateCoursePlanInput>) => Promise<void> - 功能:更新课程计划(部分字段)
- 依赖:
shared.db,shared.db.schema.coursePlans - 被以下模块使用:updateCoursePlanAction
deleteCoursePlan
- 签名:
(id: string) => Promise<void> - 功能:删除课程计划(级联删除周计划条目)
- 依赖:
shared.db,shared.db.schema.coursePlans - 被以下模块使用:deleteCoursePlanAction
createCoursePlanItem / updateCoursePlanItem / deleteCoursePlanItem
- 签名:
(data: CreateCoursePlanItemInput) => Promise<string>/(id: string, data: Partial<UpdateCoursePlanItemInput>) => Promise<void>/(id: string) => Promise<void> - 功能:周计划条目 CRUD
- 依赖:
shared.db,shared.db.schema.coursePlanItems,@paralleldrive/cuid2 - 被以下模块使用:createCoursePlanItemAction, updateCoursePlanItemAction, deleteCoursePlanItemAction, toggleCoursePlanItemCompletedAction
reorderCoursePlanItems
- 签名:
(planId: string, items: ReorderCoursePlanItemInput[]) => Promise<void> - 功能:重排周计划条目顺序
- 依赖:
shared.db,shared.db.schema.coursePlanItems
getSubjectOptions
- 签名:
() => Promise<{id:string;name:string}[]> - 功能:获取科目选项列表
- 依赖:
shared.db,shared.db.schema.subjects - 被以下模块使用:admin/course-plans/create/page.tsx, admin/course-plans/[id]/edit/page.tsx
Zod Schema (schema.ts)
| Schema | 用途 |
|---|---|
CreateCoursePlanSchema |
创建课程计划校验(classId, subjectId, teacherId, academicYearId?, semester?, totalHours?, weeklyHours?, startDate?, endDate?, syllabus?, objectives?, status?) |
UpdateCoursePlanSchema |
更新课程计划校验(所有字段可选,含 completedHours?) |
CreateCoursePlanItemSchema |
创建周计划条目校验(planId, week, topic, content?, hours?, textbookChapter?, notes?) |
UpdateCoursePlanItemSchema |
更新周计划条目校验(所有字段可选,含 isCompleted?, completedAt?) |
类型/接口 (types.ts)
| 类型 | 定义 |
|---|---|
CoursePlan |
课程计划基础接口 |
CoursePlanItem |
周计划条目接口 |
CoursePlanListItem |
= CoursePlan & { className, subjectName, teacherName } |
CoursePlanWithItems |
= CoursePlanListItem & { items: CoursePlanItem[] } |
CoursePlanStatus |
"planning" | "active" | "completed" | "paused" |
CoursePlanSemester |
"1" | "2" |
GetCoursePlansParams |
{ classId?, teacherId?, subjectId?, status? } |
ReorderCoursePlanItemInput |
{ id, week } |
导出组件 (components/)
| 组件文件 | 功能 |
|---|---|
course-plan-progress.tsx |
进度条组件(completedHours/totalHours 百分比) |
course-plan-list.tsx |
课程计划列表(支持状态筛选,URL 同步) |
course-plan-form.tsx |
创建/编辑表单(班级、科目、教师、学年、学期、状态、课时、日期、大纲、目标) |
course-plan-item-editor.tsx |
周计划条目编辑器(Dialog,支持创建/编辑/删除/切换完成) |
course-plan-detail.tsx |
详情视图(计划信息、进度、大纲、目标、周计划表格、删除确认) |
模块:parent
模块职责
家长端仪表盘:聚合家长关联子女的学习数据(课表、作业、成绩、班级),支持多子女切换查看。家长通过 parentStudentRelations 表关联子女,DataScope 解析为 children 类型。
模块路径
src/modules/parent
导出函数 (data-access.ts)
所有函数使用
cache()包装以实现请求级缓存。本模块仅用于读操作聚合,无 Server Action,权限校验在页面层通过requireAuth()完成。
| 函数 | 签名 | 被使用 |
|---|---|---|
getChildren |
(parentId: string) => Promise<ParentChildRelation[]> |
getParentDashboardData 内部, parent/children/[studentId] 页面 |
getChildBasicInfo |
(studentId: string, relation?: string | null) => Promise<ChildBasicInfo | null> |
getChildDashboardData 内部 |
getChildDashboardData |
(studentId: string, relation?: string | null) => Promise<ChildDashboardData | null> |
parent/children/[studentId]/page.tsx, getParentDashboardData 内部 |
getParentDashboardData |
(parentId: string) => Promise<ParentDashboardData> |
parent/dashboard/page.tsx |
getChildren
- 依赖:
shared/db(parentStudentRelations 表) - 功能:查询指定家长的所有子女关联记录,按 createdAt 升序
getChildBasicInfo
- 依赖:
shared/db(users, grades, classEnrollments, classes 表) - 功能:聚合子女基础信息(姓名、邮箱、头像、年级名称、班级名称、与家长关系)
getChildDashboardData
- 依赖:
getChildBasicInfo,classes/data-access.getStudentClasses,classes/data-access.getStudentSchedule,homework/data-access.getStudentHomeworkAssignments,homework/data-access.getStudentDashboardGrades,grades/data-access.getStudentGradeSummary - 功能:并行聚合单个子女的完整仪表盘数据(基础信息、已加入班级、今日课表、作业概览、成绩趋势、成绩汇总)
getParentDashboardData
- 依赖:
shared/db(users 表),getChildren,getChildDashboardData - 功能:聚合家长名称与所有子女的仪表盘数据
类型/接口 (types.ts)
ParentChildRelation
- 定义:家长-子女关联记录类型(id, parentId, studentId, relation, createdAt)
- 被使用:
getChildren,getParentDashboardData
ChildBasicInfo
- 定义:子女基础信息类型(id, name, email, image, gradeName, className, classId, relation)
- 被使用:
getChildBasicInfo,ChildDashboardData.basicInfo
ChildScheduleItem
- 定义:子女今日课表项类型(id, classId, className, course, startTime, endTime, location)
- 被使用:
ChildDashboardData.todaySchedule, child-schedule-card.tsx
ChildHomeworkSummary
- 定义:子女作业概览类型(pendingCount, submittedCount, gradedCount, overdueCount, recentAssignments)
- 被使用:
ChildDashboardData.homeworkSummary, child-homework-summary.tsx
ChildDashboardData
- 定义:单个子女仪表盘数据类型(basicInfo, enrolledClasses, todaySchedule, homeworkSummary, gradeTrend, gradeSummary)
- 依赖:
homework/types.StudentDashboardGradeProps,homework/types.StudentHomeworkAssignmentListItem,classes/types.StudentEnrolledClass,grades/types.StudentGradeSummary - 被使用:
getChildDashboardData,ParentDashboardData.children, 所有 child-* 组件
ParentDashboardData
- 定义:家长仪表盘数据类型(parentName, children: ChildDashboardData[])
- 被使用:
getParentDashboardData, parent-dashboard.tsx
导出组件 (components/)
| 组件文件 | 功能 |
|---|---|
parent-dashboard.tsx |
主容器组件(问候语、子女卡片网格、空状态) |
child-card.tsx |
子女卡片(头像、姓名、班级、待完成/逾期/平均分统计,点击跳转详情) |
child-detail-header.tsx |
子女详情页头部(返回按钮、头像、姓名、班级、年级、关系) |
child-detail-panel.tsx |
子女详情面板容器(组合 homework/grade/schedule 三个子组件) |
child-homework-summary.tsx |
子女作业概览(pending/submitted/graded/overdue 统计 + 最近作业列表) |
child-grade-summary.tsx |
子女成绩概览(Recharts 折线图趋势 + 最新分数 + 班级排名 + 最近成绩列表,"use client") |
child-schedule-card.tsx |
子女今日课表卡片(课程、时间、地点、班级) |
模块:messaging
模块职责
站内消息系统:用户间私信收发(支持回复链)、站内通知(多态类型:message/announcement/homework/grade),SiteHeader 通知下拉菜单展示未读数。
模块路径
src/modules/messaging
导出函数 (actions.ts)
send actions 使用
requirePermission(MESSAGE_SEND),read actions 使用requirePermission(MESSAGE_READ),delete actions 使用requirePermission(MESSAGE_DELETE),通知读取使用requireAuth()。
| 函数 | 权限 | 核心功能 |
|---|---|---|
sendMessageAction |
MESSAGE_SEND | 发送消息(同时为收件人创建通知;支持 parentMessageId 回复) |
markMessageAsReadAction |
MESSAGE_READ | 标记消息已读(设置 readAt) |
deleteMessageAction |
MESSAGE_DELETE | 删除消息(仅发送者或接收者可删) |
getMessagesAction |
MESSAGE_READ | 获取消息列表(收件箱/已发送,分页) |
getMessageDetailAction |
MESSAGE_READ | 获取消息详情(含回复线程) |
getRecipientsAction |
MESSAGE_SEND | 获取可发送对象列表(按 DataScope 过滤) |
getNotificationsAction |
requireAuth | 获取当前用户通知列表(分页) |
markNotificationAsReadAction |
requireAuth | 标记单条通知已读 |
markAllNotificationsAsReadAction |
requireAuth | 标记所有通知已读 |
getNotificationPreferencesAction |
requireAuth | 获取当前用户通知偏好(无记录时自动创建默认记录) |
updateNotificationPreferencesAction |
requireAuth | 更新(upsert)当前用户通知偏好(从 FormData 解析 checkbox "on" 为布尔值) |
导出函数 (data-access.ts)
| 函数 | 签名 | 被使用 |
|---|---|---|
getMessages |
(userId: string, params?: { type?, page?, pageSize? }) => Promise<PaginatedResult<MessageListItem>> |
messages 页面 |
getMessageById |
(id: string, userId: string) => Promise<Message | null> |
消息详情页 |
getMessageThread |
(rootId: string, userId: string) => Promise<Message[]> |
消息详情页(回复链) |
createMessage |
(input: CreateMessageInput) => Promise<Message> |
sendMessageAction |
markMessageAsRead |
(id: string, userId: string) => Promise<void> |
markMessageAsReadAction, 详情页自动已读 |
deleteMessage |
(id: string, userId: string) => Promise<void> |
deleteMessageAction |
getUnreadMessageCount |
(userId: string) => Promise<number> |
待扩展 |
getNotifications |
(userId: string, params?: { page?, pageSize? }) => Promise<PaginatedResult<NotificationListItem>> |
通知列表/下拉菜单 |
createNotification |
(input: CreateNotificationInput) => Promise<void> |
sendMessageAction(内部调用) |
markNotificationAsRead |
(id: string, userId: string) => Promise<void> |
markNotificationAsReadAction |
markAllNotificationsAsRead |
(userId: string) => Promise<void> |
markAllNotificationsAsReadAction |
getUnreadNotificationCount |
(userId: string) => Promise<number> |
待扩展 |
getRecipients |
(ctx: AuthContext) => Promise<RecipientOption[]> |
compose 页面(按 DataScope 过滤) |
导出函数 (notification-preferences.ts)
文件标记
"server-only",使用 Reactcache包装读取函数。
getNotificationPreferences
- 签名:
getNotificationPreferences(userId: string): Promise<NotificationPreferences>(cache 包装) - 功能:获取用户通知偏好;若用户无记录则自动创建一条默认记录(pushEnabled/homeworkNotifications/gradeNotifications/announcementNotifications/messageNotifications/attendanceNotifications 默认 true,emailEnabled/smsEnabled 默认 false),并发冲突时回退到查询
- 依赖:
shared/db(notificationPreferences 表),@paralleldrive/cuid2,react(cache) - 被使用:
getNotificationPreferencesAction,app/(dashboard)/settings/page.tsx
upsertNotificationPreferences
- 签名:
upsertNotificationPreferences(userId: string, input: UpdateNotificationPreferencesInput): Promise<NotificationPreferences | null> - 功能:upsert 语义更新通知偏好(存在则部分字段更新,不存在则插入;未提供的字段保留原值或使用默认值)
- 依赖:
shared/db(notificationPreferences 表),@paralleldrive/cuid2 - 被使用:
updateNotificationPreferencesAction
Schema (schema.ts)
SendMessageSchema
- 类型:Zod schema
- 定义:发送消息校验 schema(receiverId, subject?, content, parentMessageId?)
- 被使用:
sendMessageAction
类型/接口
Message
- 定义:消息完整类型(含 id, senderId, receiverId, subject, content, isRead, readAt, parentMessageId, createdAt, senderName, receiverName)
- 被使用:messaging/components, 页面
MessageListItem
- 定义:消息列表项类型(同 Message 精简版)
- 被使用:列表页
MessageThread
- 定义:消息线程类型(root + replies)
- 被使用:详情页
Notification
- 定义:通知完整类型(含 id, userId, type, title, content, link, isRead, createdAt)
- 被使用:notification-dropdown, notification-list
NotificationListItem
- 定义:通知列表项类型(同 Notification)
- 被使用:列表页
NotificationType
- 定义:
"message" | "announcement" | "homework" | "grade" - 被使用:data-access, components
MessageType
- 定义:
"inbox" | "sent" - 被使用:getMessages 参数
CreateMessageInput / CreateNotificationInput
- 定义:创建消息/通知的输入类型
- 被使用:data-access.createMessage, createNotification
RecipientOption
- 定义:
{ id: string; name: string } - 被使用:compose 页面下拉选项
NotificationPreferences
- 定义:通知偏好完整类型(含 id, userId, emailEnabled, smsEnabled, pushEnabled, homeworkNotifications, gradeNotifications, announcementNotifications, messageNotifications, attendanceNotifications, createdAt, updatedAt)
- 被使用:
getNotificationPreferences,upsertNotificationPreferences,getNotificationPreferencesAction,updateNotificationPreferencesAction, settings/components/notification-preferences-form.tsx
UpdateNotificationPreferencesInput
- 定义:更新通知偏好的输入类型(所有字段可选:emailEnabled?, smsEnabled?, pushEnabled?, homeworkNotifications?, gradeNotifications?, announcementNotifications?, messageNotifications?, attendanceNotifications?;未提供则保留原值)
- 被使用:
upsertNotificationPreferences,updateNotificationPreferencesAction
导出组件 (components/)
| 组件文件 | 功能 |
|---|---|
message-list.tsx |
消息列表(收件箱/已发送 Tab 切换,已读/未读标记,usePermission 控制"写消息"按钮) |
message-detail.tsx |
消息详情(含回复线程、回复/删除操作,AlertDialog 删除确认,usePermission 控制按钮可见性) |
message-compose.tsx |
写消息表单(收件人 Select、主题 Input、内容 Textarea,支持回复模式) |
notification-dropdown.tsx |
SiteHeader 通知下拉菜单(Bell 图标 + 未读数 Badge,滚动列表,标记已读,查看全部链接) |
notification-list.tsx |
通知完整列表(全部标记已读、单条标记已读、查看链接) |
模块:notifications
模块职责
通知渠道集成层:基于用户通知偏好(notification_preferences 表)将通知分发到站内消息 / SMS / 微信公众号 / 邮件多渠道。所有渠道实现统一 NotificationChannelSender 接口,dispatcher 按偏好并行发送。支持 Mock 模式(开发环境无需外部服务即可运行)。
模块路径
src/modules/notifications
依赖关系
- 依赖
shared(db, auth-guard, types) - 依赖
messaging(复用notification-preferences.getNotificationPreferences和data-access.createNotification) - 所有渠道文件首行
import "server-only",外部 SDK 使用动态 import
导出函数 (actions.ts)
使用
requirePermission(MESSAGE_SEND)校验权限(项目无独立 NOTIFICATION_SEND 权限点,复用 MESSAGE_SEND)。
| 函数 | 权限 | 核心功能 |
|---|---|---|
sendNotificationAction |
MESSAGE_SEND | 发送通知给指定用户(按偏好多渠道分发) |
sendClassNotificationAction |
MESSAGE_SEND | 发送班级通知(批量发送给班级所有学生;教师只能给自己所教班级发送,通过 dataScope 校验) |
导出函数 (dispatcher.ts)
文件标记
"server-only"。
sendNotification
- 签名:
sendNotification(payload: NotificationPayload): Promise<ChannelSendResult[]> - 功能:读取用户通知偏好 + 联系方式,按偏好选择渠道(in_app 总是启用;sms 需 smsEnabled+phone;email 需 emailEnabled+email;wechat 需 pushEnabled+openId),并行发送,记录日志
- 依赖:
data-access.getUserNotificationPreferences,data-access.getUserContactInfo,data-access.logNotificationSendBatch, 各渠道createXxxSender - 被使用:
sendNotificationAction,sendClassNotificationAction
sendBatchNotifications
- 签名:
sendBatchNotifications(payloads: NotificationPayload[]): Promise<ChannelSendResult[][]> - 功能:批量发送通知(每个用户独立选择渠道,并行发送)
- 依赖:
sendNotification - 被使用:
sendClassNotificationAction
导出函数 (data-access.ts)
文件标记
"server-only"。
| 函数 | 签名 | 核心功能 |
|---|---|---|
getUserNotificationPreferences |
(userId: string) => Promise<NotificationPreferences> |
获取用户通知偏好(复用 messaging.notification-preferences) |
getUserContactInfo |
(userId: string) => Promise<ChannelRecipient> |
获取用户联系方式(phone/email;wechatOpenId 暂不支持,users 表无此字段;React cache 包装) |
logNotificationSend |
(result: ChannelSendResult) => void |
记录单条发送日志(当前 console.info;未来可扩展 notification_logs 表) |
logNotificationSendBatch |
(results: ChannelSendResult[]) => void |
批量记录发送日志 |
渠道实现 (channels/)
所有渠道文件首行
import "server-only",外部 SDK 使用动态 import 避免增加构建体积。
| 文件 | 渠道 | 工厂函数 | 说明 |
|---|---|---|---|
sms-channel.ts |
sms | createSmsSender() |
支持 aliyun/tencent/mock(根据 SMS_PROVIDER 环境变量选择);模板变量替换 title/content |
wechat-channel.ts |
createWechatSender() |
微信公众号模板消息;access_token 带缓存(提前 5 分钟刷新);配置完整用真实发送器,否则 Mock | |
email-channel.ts |
createEmailSender() |
Nodemailer SMTP;HTML 模板按 type 着色;配置 EMAIL_HOST 启用,否则 Mock | |
in-app-channel.ts |
in_app | createInAppSender() |
复用 messaging.data-access.createNotification 写入 message_notifications 表;总是启用 |
types.ts |
- | - | 渠道接口定义(NotificationChannelSender, ChannelRecipient) |
环境变量
| 变量 | 默认值 | 说明 |
|---|---|---|
SMS_PROVIDER |
mock |
SMS 渠道 provider:aliyun/tencent/mock |
SMS_ACCESS_KEY_ID |
- | SMS AccessKey ID |
SMS_ACCESS_KEY_SECRET |
- | SMS AccessKey Secret |
SMS_SIGN_NAME |
- | SMS 签名 |
SMS_TEMPLATE_CODE |
- | SMS 模板 ID |
WECHAT_APP_ID |
- | 微信公众号 AppID |
WECHAT_APP_SECRET |
- | 微信公众号 AppSecret |
WECHAT_TEMPLATE_ID |
- | 微信模板消息 ID |
EMAIL_HOST |
- | SMTP 主机(配置后启用真实发送) |
EMAIL_PORT |
587 |
SMTP 端口 |
EMAIL_USER |
- | SMTP 用户名 |
EMAIL_PASS |
- | SMTP 密码 |
EMAIL_FROM |
noreply@example.com |
发件人地址 |
类型/接口
NotificationChannel
- 定义:
"in_app" | "email" | "sms" | "wechat" - 被使用:所有渠道文件, dispatcher
NotificationPayload
- 定义:
{ userId, title, content, type: "info"|"warning"|"error"|"success", metadata?, actionUrl? } - 被使用:dispatcher, actions, 所有渠道
ChannelSendResult
- 定义:
{ channel, success, messageId?, error?, sentAt } - 被使用:dispatcher, actions, 所有渠道
NotificationChannelSender(接口)
- 定义:
{ channel: NotificationChannel, send(payload, recipient): Promise<ChannelSendResult>, sendBatch(items): Promise<ChannelSendResult[]> } - 被使用:所有渠道实现, dispatcher
ChannelRecipient(接口)
- 定义:
{ userId, phone?, email?, wechatOpenId? } - 被使用:所有渠道, data-access.getUserContactInfo
文档
docs/notifications/channels.md:通知渠道配置说明、Mock 模式、生产环境配置、扩展新渠道指南
模块:attendance
模块职责
学生考勤管理:教师按班级/日期点名(单条/批量)、查询考勤记录、统计出勤率/迟到率,学生/家长查看本人/子女考勤汇总,管理员查看全校考勤记录。支持班级考勤规则配置(迟到阈值、早退阈值、自动标记)。
模块路径
src/modules/attendance
导出函数 (actions.ts)
所有 manage actions 均使用
requirePermission(ATTENDANCE_MANAGE)进行权限校验,read actions 使用requirePermission(ATTENDANCE_READ)。学生/家长在getStudentAttendanceAction中进行 DataScope 二次校验(class_members 仅查自己,children 仅查子女)。
| 函数 | 权限 | 核心功能 |
|---|---|---|
recordAttendanceAction |
ATTENDANCE_MANAGE | 创建单条考勤记录 |
batchRecordAttendanceAction |
ATTENDANCE_MANAGE | 批量点名(班级+日期,表格形式录入每个学生状态) |
updateAttendanceAction |
ATTENDANCE_MANAGE | 更新考勤记录(状态、备注) |
deleteAttendanceAction |
ATTENDANCE_MANAGE | 删除考勤记录 |
getAttendanceAction |
ATTENDANCE_READ | 分页查询考勤记录(按 scope 过滤) |
getStudentAttendanceAction |
ATTENDANCE_READ | 获取学生考勤汇总(含 DataScope 二次校验) |
getClassAttendanceStatsAction |
ATTENDANCE_READ | 获取班级考勤统计 |
getClassAttendanceForDateAction |
ATTENDANCE_READ | 获取班级指定日期考勤(用于点名页加载已有记录) |
saveAttendanceRulesAction |
ATTENDANCE_MANAGE | 保存班级考勤规则(upsert) |
getAttendanceRulesAction |
ATTENDANCE_READ | 获取班级考勤规则 |
导出函数 (data-access.ts)
| 函数 | 签名 | 被使用 |
|---|---|---|
getAttendanceRecords |
(params: AttendanceQueryParams & { scope: DataScope; currentUserId?: string }) => Promise<PaginatedAttendanceResult> |
getAttendanceAction |
getClassAttendanceForDate |
(classId: string, date: string) => Promise<AttendanceListItem[]> |
getClassAttendanceForDateAction |
createAttendanceRecord |
(data: RecordAttendanceInput, recordedBy: string) => Promise<string> |
recordAttendanceAction |
batchCreateAttendanceRecords |
(data: BatchRecordAttendanceInput, recordedBy: string) => Promise<number> |
batchRecordAttendanceAction |
updateAttendanceRecord |
(id: string, data: UpdateAttendanceInput) => Promise<void> |
updateAttendanceAction |
deleteAttendanceRecord |
(id: string) => Promise<void> |
deleteAttendanceAction |
getClassStudentsForAttendance |
(classId: string) => Promise<Array<{ id, name, email }>> |
点名页学生列表 |
getAttendanceRules |
(classId?: string) => Promise<AttendanceRule[]> |
getAttendanceRulesAction |
upsertAttendanceRules |
(data: AttendanceRuleInput) => Promise<string> |
saveAttendanceRulesAction |
导出函数 (data-access-stats.ts)
从 data-access.ts 拆分以遵守单文件 ≤300 行规则。
| 函数 | 签名 | 被使用 |
|---|---|---|
getStudentAttendanceSummary |
(studentId: string, startDate?: string, endDate?: string) => Promise<StudentAttendanceSummary | null> |
getStudentAttendanceAction, student/attendance, parent/attendance |
getClassAttendanceStats |
(classId: string, startDate?: string, endDate?: string) => Promise<ClassAttendanceSummary | null> |
getClassAttendanceStatsAction, teacher/attendance/stats |
Schema (schema.ts)
| Schema | 用途 |
|---|---|
AttendanceStatusEnum |
考勤状态枚举(present/absent/late/early_leave/excused) |
RecordAttendanceSchema |
单条考勤记录校验(studentId, classId, scheduleId?, date, status, remark?) |
BatchRecordAttendanceSchema |
批量考勤校验(records[{ studentId, classId, scheduleId?, date, status, remark? }]) |
UpdateAttendanceSchema |
更新考勤校验(status?, remark?, scheduleId?) |
AttendanceRuleSchema |
考勤规则校验(classId, lateThresholdMinutes?, earlyLeaveThresholdMinutes?, enableAutoMark?) |
类型/接口 (types.ts)
| 类型 | 定义 |
|---|---|
AttendanceStatus |
"present" | "absent" | "late" | "early_leave" | "excused" |
AttendanceRecord |
考勤记录完整类型 |
AttendanceListItem |
列表项类型(含 studentName, className, recorderName) |
AttendanceStats |
统计类型(total, present, absent, late, earlyLeave, excused, presentRate, lateRate) |
StudentAttendanceSummary |
学生考勤汇总(studentId, studentName, stats, recentRecords) |
ClassAttendanceSummary |
班级考勤汇总(classId, className, date, stats, studentRecords) |
AttendanceRule |
考勤规则类型(classId, lateThresholdMinutes, earlyLeaveThresholdMinutes, enableAutoMark) |
AttendanceQueryParams |
查询参数(classId?, studentId?, date?, startDate?, endDate?, status?, page?, pageSize?) |
PaginatedAttendanceResult |
分页结果(items, total, page, pageSize, totalPages) |
ATTENDANCE_STATUS_LABELS |
状态中文标签常量 |
ATTENDANCE_STATUS_COLORS |
状态颜色常量(用于 Badge) |
导出组件 (components/)
| 组件文件 | 功能 |
|---|---|
attendance-sheet.tsx |
批量点名表单(班级/日期选择器 + 学生表格 + 每行状态 Select + "全部标记到场"按钮) |
attendance-record-list.tsx |
考勤记录列表表格(含删除确认对话框) |
attendance-stats-card.tsx |
统计卡片(总数、到场、缺勤、迟到、早退、请假、出勤率、迟到率) |
attendance-filters.tsx |
URL 同步筛选器(班级、状态、日期) |
student-attendance-view.tsx |
学生/家长视图(统计卡片 + 最近记录表格) |
attendance-rules-form.tsx |
考勤规则配置表单(班级选择器、迟到/早退阈值、自动标记勾选) |
模块:scheduling
src/modules/scheduling
排课与调课模块:管理员配置班级排课规则(每日课时、连续课时、午休、上下学时间、避免背靠背、科目均衡),自动排课引擎按规则生成周课表,调课/代课申请与审批流程,课表冲突检测。
所有 actions 均使用
requirePermission()进行权限校验:规则配置/调课申请/冲突检测/查询使用requirePermission(SCHEDULE_ADJUST),自动排课/应用课表/审批调课使用requirePermission(SCHEDULE_AUTO)。admin 角色拥有 SCHEDULE_AUTO+SCHEDULE_ADJUST,teacher 角色无排课权限。
Server Actions (actions.ts)
| Action | 权限 | 用途 |
|---|---|---|
saveSchedulingRulesAction |
SCHEDULE_ADJUST | 保存班级排课规则(upsert,classId 为空时为全局规则) |
autoScheduleAction |
SCHEDULE_AUTO | 根据规则与科目分配生成预览课表(不落库) |
applyAutoScheduleAction |
SCHEDULE_AUTO | 将生成的课表写入 classSchedule 表(事务:先删后插) |
requestScheduleChangeAction |
SCHEDULE_ADJUST | 提交调课/代课申请(status=pending) |
approveScheduleChangeAction |
SCHEDULE_AUTO | 审批通过调课申请(status=approved) |
rejectScheduleChangeAction |
SCHEDULE_AUTO | 驳回调课申请(status=rejected) |
getScheduleChangesAction |
SCHEDULE_ADJUST | 查询调课申请列表(可按 classId/status/requesterId 过滤) |
getClassConflictsAction |
SCHEDULE_ADJUST | 检测班级课表时间重叠冲突 |
Data Access (data-access.ts)
| 函数 | 签名 | 被使用 |
|---|---|---|
getSchedulingRules |
(classId?: string) => Promise<SchedulingRule[]> |
saveSchedulingRulesAction, autoScheduleAction, admin/scheduling/rules |
upsertSchedulingRules |
(data: SchedulingRuleInput) => Promise<string> |
saveSchedulingRulesAction |
getScheduleChanges |
(params: ScheduleChangeQueryParams) => Promise<ScheduleChangeListItem[]> |
getScheduleChangesAction, admin/scheduling/changes, teacher/schedule-changes |
createScheduleChange |
(data: ScheduleChangeInput, requestedBy: string) => Promise<string> |
requestScheduleChangeAction |
updateScheduleChangeStatus |
(id, status, approverId) => Promise<void> |
approveScheduleChangeAction, rejectScheduleChangeAction |
getClassConflicts |
(classId: string) => Promise<ScheduleConflict[]> |
getClassConflictsAction |
getAdminClassesForScheduling |
() => Promise<Array<{ id, name, grade }>> |
所有 scheduling 页面 |
getTeachersForScheduling |
() => Promise<Array<{ id, name, email }>> |
teacher/schedule-changes |
getClassroomsForScheduling |
() => Promise<Array<{ id, name, building }>> |
autoScheduleAction |
getClassSubjectsForScheduling |
(classId) => Promise<Array<{ subjectId, subjectName, teacherId }>> |
autoScheduleAction |
Auto Scheduler (auto-scheduler.ts)
| 函数 | 用途 |
|---|---|
autoSchedule |
贪心+冲突检测排课算法:按科目每周课时降序,为每节课选择第一个满足约束的时段(午休、每日窗口、班级/教师/教室冲突、每日最大课时、避免背靠背) |
findOptimalSlot |
在候选时段中找到第一个满足所有约束的时段 |
validateSchedule |
校验生成的课表是否违反规则,返回冲突列表 |
buildDefaultTimeSlots |
根据上下学时间和午休时间构建默认时段(周一至周五,上午4节+下午4节) |
Schemas (schema.ts)
| Schema | 用途 |
|---|---|
SchedulingRuleSchema |
排课规则校验(classId, maxDailyHours?, maxContinuousHours?, lunchBreakStart?, lunchBreakEnd?, morningStart?, afternoonEnd?, avoidBackToBack?, balancedSubjects?) |
ScheduleChangeSchema |
调课申请校验(classId, originalScheduleId?, originalTeacherId?, substituteTeacherId?, originalDate?, newDate?, newStartTime?, newEndTime?, reason) |
AutoScheduleParamsSchema |
自动排课参数校验(classId, rules, subjects[], teachers[], classrooms[], timeSlots[]) |
ScheduleChangeStatusEnum |
调课状态枚举(pending/approved/rejected/completed) |
ApproveScheduleChangeSchema |
审批校验(changeId, reason?) |
Types (types.ts)
| Type | 定义 |
|---|---|
ScheduleChangeStatus |
"pending" | "approved" | "rejected" | "completed" |
SchedulingRule |
排课规则完整类型 |
ScheduleChange |
调课申请完整类型 |
ScheduleChangeListItem |
列表项类型(含 className, originalTeacherName, substituteTeacherName, requesterName, approverName) |
TimeSlot |
{ weekday, startTime, endTime } |
ScheduleConflict |
{ type, description, scheduleIds }(type: teacher_overlap/classroom_overlap/class_overlap/rule_violation) |
AutoScheduleResult |
{ success, scheduledCount, conflictCount, conflicts, schedules } |
GeneratedSchedule |
{ classId, weekday, startTime, endTime, course, location, teacherId, subjectId } |
AutoScheduleParams |
{ classId, rules, subjects, teachers, classrooms, timeSlots } |
ScheduleChangeQueryParams |
{ classId?, status?, requesterId? } |
SCHEDULE_CHANGE_STATUS_LABELS |
状态英文标签常量 |
SCHEDULE_CHANGE_STATUS_COLORS |
状态颜色常量(用于 Badge) |
Components (components/)
| 组件 | 用途 |
|---|---|
scheduling-rules-form.tsx |
排课规则配置表单(班级选择器、每日最大课时、连续课时、午休时间、上下学时间、避免背靠背、科目均衡) |
auto-schedule-panel.tsx |
自动排课面板(班级选择→预览→应用流程) |
auto-schedule-result.tsx |
排课结果预览(课表表格 + 冲突/警告列表) |
schedule-change-form.tsx |
调课/代课申请表单(班级、原任课教师、代课教师、原日期、新日期、新时间、原因) |
schedule-change-list.tsx |
调课申请列表表格(含审批/驳回对话框,canApprove 控制审批按钮可见性) |
schedule-conflicts-view.tsx |
冲突检测视图(班级选择器 + 检测按钮 + 冲突结果列表) |
模块:proctoring
src/modules/proctoring
考试监考模块:监考模式考试实时监控、防作弊事件采集、教师监考面板、学生端防作弊监控、考试模式配置。
权限:教师监考面板使用
requirePermission(EXAM_PROCTOR);学生端上报事件使用requireAuth()(学生上报自己的事件,不需要管理权限)。前端组件使用usePermission().hasPermission(EXAM_PROCTOR)控制权限。
Server Actions (actions.ts)
| Action | 权限 | 用途 |
|---|---|---|
recordProctoringEventAction |
requireAuth() | 学生端上报监考事件(含 submission 归属校验) |
getProctoringDashboardAction |
EXAM_PROCTOR | 获取监考面板数据(摘要+学生状态+最近事件) |
Data Access (data-access.ts)
import "server-only"
| 函数 | 签名 | 被使用 |
|---|---|---|
recordProctoringEvent |
(input: RecordProctoringEventInput) => Promise<ProctoringEvent> |
actions.recordProctoringEventAction, API /api/proctoring/event |
getProctoringEvents |
(examId, filters?) => Promise<ProctoringEventWithDetails[]> |
待扩展 |
getProctoringEventsBySubmission |
(submissionId) => Promise<ProctoringEvent[]> |
待扩展 |
getExamProctoringSummary |
(examId) => Promise<ExamProctoringSummary> |
actions.getProctoringDashboardAction, teacher/exams/[id]/proctoring/page.tsx |
getStudentProctoringStatuses |
(examId) => Promise<StudentProctoringStatus[]> |
actions.getProctoringDashboardAction, teacher/exams/[id]/proctoring/page.tsx |
getExamForProctoring |
`(examId) => Promise<{id,title,examMode,config} | null>` |
getRecentProctoringEvents |
(examId, limit?) => Promise<ProctoringEventWithDetails[]> |
actions.getProctoringDashboardAction, teacher/exams/[id]/proctoring/page.tsx |
Types (types.ts)
| 类型 | 定义 |
|---|---|
ProctoringEventType |
"tab_switch" | "window_blur" | "copy_attempt" | "paste_attempt" | "right_click" | "devtools_open" | "fullscreen_exit" | "idle_timeout" |
ExamMode |
"homework" | "timed" | "proctored" |
ProctoringEvent |
{ id, submissionId, studentId, examId, eventType, eventDetail?, occurredAt, createdAt } |
ProctoringEventWithDetails |
ProctoringEvent & { studentName, examTitle } |
ExamProctoringSummary |
{ examId, examTitle, examMode, totalStudents, startedStudents, submittedStudents, totalEvents, abnormalStudents, eventsByType } |
StudentProctoringStatus |
{ studentId, studentName, submissionId, submissionStatus, eventCount, lastEventAt, isAbnormal, eventsByType } |
ProctoringDashboardData |
{ summary, students, recentEvents } |
ExamModeConfig |
{ examMode, durationMinutes, shuffleQuestions, allowLateStart, lateStartGraceMinutes, antiCheatEnabled } |
PROCTORING_EVENT_LABELS |
事件类型中文标签常量 |
EXAM_MODE_LABELS |
考试模式中文标签常量 |
ABNORMAL_EVENT_THRESHOLD |
异常学生事件数阈值(3) |
Components (components/)
| 组件 | 用途 |
|---|---|
proctoring-dashboard.tsx |
教师监考面板(实时学生状态、异常事件统计、异常学生高亮、10 秒轮询、usePermission 权限控制) |
anti-cheat-monitor.tsx |
学生端防作弊监控(visibilitychange/blur/copy/paste/contextmenu/keydown/fullscreenchange 监听、空闲超时检测、强制全屏、警告提示、事件上报) |
exam-mode-config.tsx |
考试模式配置(react-hook-form Controller,作业/限时/监考模式选择,限时设置时长,监考设置防作弊选项) |
API Routes
| 路由 | 方法 | 权限 | 用途 |
|---|---|---|---|
/api/proctoring/event |
POST | requireAuth() | 接收学生端上报的监考事件(含 submission 归属校验) |
模块:diagnostic
src/modules/diagnostic
学情诊断报告模块:基于知识点掌握度(knowledgePointMastery 表)生成个人/班级诊断报告,掌握度雷达图(学生 vs 班级平均),强项/弱项分析,知识点掌握度热力图,需重点关注学生列表,报告发布/删除管理。
权限:所有 Server Actions 使用
requirePermission()校验。生成/发布/删除报告使用requirePermission(DIAGNOSTIC_MANAGE),查询/详情使用requirePermission(DIAGNOSTIC_READ)。admin/teacher/grade_head 角色拥有 DIAGNOSTIC_MANAGE+DIAGNOSTIC_READ,teaching_head/student 角色仅有 DIAGNOSTIC_READ。前端组件使用usePermission().hasPermission(DIAGNOSTIC_MANAGE)控制生成/发布/删除按钮可见性(无role === "xxx"硬编码)。页面路由通过getAuthContext()进行 DataScope 二次校验:class_members仅查自己,children仅查子女,class_taught必须包含 classId。
Server Actions (actions.ts)
"use server"
| Action | 权限 | 用途 |
|---|---|---|
generateStudentReportAction |
DIAGNOSTIC_MANAGE | 生成学生个人诊断报告(formData: studentId, period) |
generateClassReportAction |
DIAGNOSTIC_MANAGE | 生成班级诊断报告(formData: classId, period) |
publishReportAction |
DIAGNOSTIC_MANAGE | 发布诊断报告(formData: id,status → published) |
deleteReportAction |
DIAGNOSTIC_MANAGE | 删除诊断报告(formData: id) |
getDiagnosticReportsAction |
DIAGNOSTIC_READ | 查询诊断报告列表(params: DiagnosticReportQueryParams) |
getDiagnosticReportByIdAction |
DIAGNOSTIC_READ | 获取诊断报告详情(id) |
Data Access (data-access.ts + data-access-reports.ts)
import "server-only"(两个文件均以此开头)
data-access.ts(掌握度相关)
| 函数 | 签名 | 被使用 |
|---|---|---|
getStudentMastery |
(studentId: string) => Promise<MasteryWithKnowledgePoint[]> |
getStudentMasterySummary, teacher/diagnostic/student/[studentId] |
getStudentMasterySummary |
(studentId: string) => Promise<StudentMasterySummary | null> |
generateDiagnosticReport, teacher/diagnostic/student/[studentId], student/diagnostic |
updateMasteryFromSubmission |
(submissionId: string) => Promise<void> |
待扩展(作业/考试提交后触发,onDuplicateKeyUpdate upsert) |
getClassMasterySummary |
(classId: string) => Promise<ClassMasterySummary | null> |
generateClassDiagnosticReport, teacher/diagnostic/class/[classId] |
getKnowledgePointStats |
(classId?: string, gradeId?: string) => Promise<KnowledgePointStat[]> |
teacher/diagnostic/student/[studentId](班级平均对比) |
data-access-reports.ts(报告相关)
| 函数 | 签名 | 被使用 |
|---|---|---|
generateDiagnosticReport |
(studentId, period, generatedBy) => Promise<string> |
generateStudentReportAction |
generateClassDiagnosticReport |
(classId, period, generatedBy) => Promise<string> |
generateClassReportAction |
getDiagnosticReports |
(filters: DiagnosticReportQueryParams) => Promise<DiagnosticReportWithDetails[]> |
getDiagnosticReportsAction, teacher/diagnostic, teacher/diagnostic/student/[studentId], student/diagnostic |
getDiagnosticReportById |
(id: string) => Promise<DiagnosticReportWithDetails | null> |
getDiagnosticReportByIdAction |
publishDiagnosticReport |
(id: string) => Promise<void> |
publishReportAction |
deleteDiagnosticReport |
(id: string) => Promise<void> |
deleteReportAction |
Types (types.ts)
| Type | 定义 |
|---|---|
DiagnosticReportType |
"individual" | "class" | "grade" |
DiagnosticReportStatus |
"draft" | "published" | "archived" |
KnowledgePointMastery |
{ id, studentId, knowledgePointId, masteryLevel(0-100), totalQuestions, correctQuestions, lastAssessedAt, createdAt, updatedAt } |
MasteryWithKnowledgePoint |
KnowledgePointMastery & { knowledgePointName, knowledgePointDescription } |
StudentMasterySummary |
{ studentId, studentName, averageMastery, totalKnowledgePoints, strengths(≥80), weaknesses(<60), allMastery } |
DiagnosticReport |
{ id, studentId, generatedBy, reportType, period, summary, strengths[], weaknesses[], recommendations[], overallScore, status, createdAt, updatedAt } |
DiagnosticReportWithDetails |
DiagnosticReport & { studentName, generatedByName } |
ClassMasterySummary |
{ classId, className, studentCount, averageMastery, knowledgePointStats[], studentsNeedingAttention[] } |
KnowledgePointStat |
{ knowledgePointId, knowledgePointName, averageMastery, masteredCount(≥80), notMasteredCount(<60), totalStudents } |
DiagnosticReportQueryParams |
{ studentId?, reportType?, status?, period? } |
MasteryRadarPoint |
{ knowledgePoint, student(0-100), classAverage?(0-100) } |
Components (components/)
所有组件以 "use client" 开头。
| 组件 | 用途 |
|---|---|
mastery-radar-chart.tsx |
知识点掌握度雷达图(recharts RadarChart,学生 vs 班级平均对比,无数据时显示 EmptyState) |
student-diagnostic-view.tsx |
学生诊断视图(概览卡片、雷达图、强项/弱项列表、生成报告表单[DIAGNOSTIC_MANAGE]、最新报告与建议展示) |
class-diagnostic-view.tsx |
班级诊断视图(概览卡片、知识点掌握度热力图[绿≥80/黄60-79/橙40-59/红<40]、知识点排名表、需重点关注学生表[链接到学生视图]、生成班级报告表单[DIAGNOSTIC_MANAGE]) |
report-list.tsx |
诊断报告列表(reportType/status 过滤器[URL searchParams]、报告表格、发布/删除操作[DIAGNOSTIC_MANAGE]、确认对话框) |
数据库表
| 表 | 用途 |
|---|---|
knowledgePointMastery |
知识点掌握度记录(复合主键 studentId+knowledgePointId,onDuplicateKeyUpdate upsert) |
learningDiagnosticReports |
学情诊断报告(reportType: individual/class/grade;status: draft/published/archived) |
页面路由
| 路由 | 组件 | 权限 | DataScope 校验 |
|---|---|---|---|
/teacher/diagnostic |
ReportList | diagnostic:read | class_members 仅查看自己报告 |
/teacher/diagnostic/student/[studentId] |
StudentDiagnosticView | diagnostic:read | class_members 仅自己,children 仅子女 |
/teacher/diagnostic/class/[classId] |
ClassDiagnosticView | diagnostic:read | class_taught 必须包含 classId,class_members/children → notFound |
/student/diagnostic |
StudentDiagnosticView | diagnostic:read | class_members 仅查自己(ctx.userId) |
模块:elective
src/modules/elective
选课管理模块:选修课程 CRUD、选课开放/关闭、学生选课/退课、抽签模式批量录取(runLottery)、FCFS 即时录取、DataScope 行级过滤(admin 全部、teacher 所教、grade_head 所管年级、student 可选课程)。
权限:管理操作使用
requirePermission(ELECTIVE_MANAGE);读取使用requirePermission(ELECTIVE_READ);学生选课/退课使用requirePermission(ELECTIVE_SELECT)。前端组件使用usePermission().hasPermission()控制权限。getStudentSelectionsAction对 class_members/children 进行 DataScope 二次校验。
Server Actions (actions.ts)
| Action | 权限 | 用途 |
|---|---|---|
createElectiveCourseAction |
ELECTIVE_MANAGE | 创建选修课程(formData: name, subjectId?, teacherId, gradeId?, description?, capacity?, classroom?, schedule?, startDate?, endDate?, selectionStartAt?, selectionEndAt?, selectionMode?, credit?) |
updateElectiveCourseAction |
ELECTIVE_MANAGE | 更新选修课程(id + formData) |
deleteElectiveCourseAction |
ELECTIVE_MANAGE | 删除选修课程(formData: courseId) |
openSelectionAction |
ELECTIVE_MANAGE | 开放选课(formData: courseId) |
closeSelectionAction |
ELECTIVE_MANAGE | 关闭选课(formData: courseId) |
runLotteryAction |
ELECTIVE_MANAGE | 执行抽签录取(formData: courseId),返回 {enrolled, waitlist} |
selectCourseAction |
ELECTIVE_SELECT | 学生选课(formData: courseId, priority?) |
dropCourseAction |
ELECTIVE_SELECT | 学生退课(formData: courseId) |
getElectiveCoursesAction |
ELECTIVE_READ | 查询选修课程列表(按 DataScope 过滤,传 currentUserId) |
getStudentSelectionsAction |
ELECTIVE_READ | 查询学生选课记录(DataScope 二次校验:class_members 仅自己,children 仅子女) |
getAvailableCoursesAction |
ELECTIVE_SELECT | 获取学生可选课程(status=open 且匹配年级) |
Data Access
data-access.ts (import "server-only")
| 函数 | 签名 | 被使用 |
|---|---|---|
getElectiveCourses |
(params?: GetElectiveCoursesParams & { scope?: DataScope; currentUserId?: string }) => Promise<ElectiveCourseWithDetails[]> |
getElectiveCoursesAction, admin/elective, teacher/elective |
getElectiveCourseById |
(id: string) => Promise<ElectiveCourseWithDetails | null> |
updateElectiveCourseAction, admin/elective/[id]/edit |
createElectiveCourse |
(data: CreateElectiveCourseInput, teacherId: string) => Promise<string> |
createElectiveCourseAction |
updateElectiveCourse |
(id: string, data: Partial<UpdateElectiveCourseInput>) => Promise<void> |
updateElectiveCourseAction |
deleteElectiveCourse |
(id: string) => Promise<void> |
deleteElectiveCourseAction |
openSelection |
(courseId: string) => Promise<void> |
openSelectionAction |
closeSelection |
(courseId: string) => Promise<void> |
closeSelectionAction |
getSubjectOptions |
() => Promise<{id, name}[]> |
admin/elective/create, admin/elective/[id]/edit |
buildScopeFilter(scope, userId)内部函数:owned/class_taught 按teacherId过滤,grade_managed 按gradeIds过滤,class_members/children 返回 null(学生通过getAvailableCoursesForStudent获取可选课程)。
data-access-selections.ts (import "server-only")
从 data-access.ts 拆分以遵守单文件 ≤300 行规则。
| 函数 | 签名 | 被使用 |
|---|---|---|
getCourseSelections |
(courseId: string) => Promise<CourseSelectionWithDetails[]> |
待扩展 |
getStudentSelections |
(studentId: string) => Promise<CourseSelectionWithDetails[]> |
getStudentSelectionsAction, student/elective |
getStudentGradeId |
(studentId: string) => Promise<string | null> |
getAvailableCoursesForStudent |
getAvailableCoursesForStudent |
(studentId: string, gradeId?: string | null) => Promise<ElectiveCourseWithDetails[]> |
getAvailableCoursesAction, student/elective |
data-access-operations.ts (import "server-only")
从 data-access.ts 拆分以遵守单文件 ≤300 行规则。包含选课/退课/抽签的写操作。
| 函数 | 签名 | 被使用 |
|---|---|---|
runLottery |
(courseId: string) => Promise<{enrolled: number, waitlist: number}> |
runLotteryAction |
selectCourse |
(courseId: string, studentId: string, priority?: number) => Promise<{status: CourseSelectionStatus, message: string}> |
selectCourseAction |
dropCourse |
(courseId: string, studentId: string) => Promise<void> |
dropCourseAction |
Schema (schema.ts)
| Schema | 用途 |
|---|---|
ElectiveCourseStatusEnum |
课程状态枚举(draft/open/closed/cancelled) |
ElectiveSelectionModeEnum |
选课模式枚举(fcfs/lottery) |
CourseSelectionStatusEnum |
选课状态枚举(selected/enrolled/waitlist/dropped/rejected) |
CreateElectiveCourseSchema |
创建课程校验(name 必填,teacherId 必填,capacity 1-500 默认 30,selectionMode 默认 fcfs,credit 默认 1.0) |
UpdateElectiveCourseSchema |
更新课程校验(所有字段可选,含 status) |
SelectCourseSchema |
选课校验(courseId 必填,priority 1-10 可选) |
DropCourseSchema |
退课校验(courseId 必填) |
RunLotterySchema |
抽签校验(courseId 必填) |
类型/接口 (types.ts)
| 类型 | 定义 |
|---|---|
ElectiveCourseStatus |
"draft" | "open" | "closed" | "cancelled" |
ElectiveSelectionMode |
"fcfs" | "lottery" |
CourseSelectionStatus |
"selected" | "enrolled" | "waitlist" | "dropped" | "rejected" |
ElectiveCourse |
课程完整类型(id, name, subjectId?, teacherId, gradeId?, description?, capacity, enrolledCount, classroom?, schedule?, startDate?, endDate?, selectionStartAt?, selectionEndAt?, status, selectionMode, credit, createdAt, updatedAt) |
ElectiveCourseWithDetails |
ElectiveCourse & { teacherName?, subjectName?, gradeName? } |
CourseSelection |
选课记录类型(id, courseId, studentId, status, priority?, selectedAt, enrolledAt?, droppedAt?, lotteryRank?, createdAt, updatedAt) |
CourseSelectionWithDetails |
CourseSelection & { courseName?, studentName?, courseCapacity?, courseEnrolledCount?, courseStatus? } |
GetElectiveCoursesParams |
查询参数(status?, gradeId?, subjectId?, teacherId?) |
ELECTIVE_STATUS_LABELS |
课程状态标签常量 |
ELECTIVE_STATUS_COLORS |
课程状态颜色常量(Badge variant) |
SELECTION_MODE_LABELS |
选课模式标签常量 |
COURSE_SELECTION_STATUS_LABELS |
选课状态标签常量 |
COURSE_SELECTION_STATUS_COLORS |
选课状态颜色常量(Badge variant) |
导出组件 (components/)
| 组件文件 | 功能 |
|---|---|
elective-course-list.tsx |
课程卡片列表(管理员/教师视图,含编辑/开放/关闭/抽签/删除操作按钮,usePermission 权限控制) |
elective-course-form.tsx |
课程创建/编辑表单(name, subjectId, teacherId, gradeId, description, capacity, classroom, schedule, dates, selectionMode, credit) |
student-selection-view.tsx |
学生选课视图(可选课程列表 + 我的选课记录,含选课/退课按钮) |
路由页面
| 路由 | 组件 | 权限 | 说明 |
|---|---|---|---|
/admin/elective |
ElectiveCourseList | elective:manage | 管理员选修课程列表(scope=all) |
/admin/elective/create |
ElectiveCourseForm | elective:manage | 创建选修课程 |
/admin/elective/[id]/edit |
ElectiveCourseForm (edit) | elective:manage | 编辑选修课程 |
/teacher/elective |
ElectiveCourseList (teacher) | elective:manage | 教师选修课程列表(scope=class_taught/owned,按 teacherId 过滤) |
/student/elective |
StudentSelectionView | elective:select | 学生选课页面(可选课程 + 我的选课) |
模块间依赖矩阵
| ↓ 使用 → | shared | auth | exams | homework | questions | textbooks | classes | school | dashboard | layout | settings | users | audit | announcements | files | grades | course-plans | parent | messaging | attendance | scheduling | proctoring | diagnostic | elective |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| shared | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | |
| auth | db,schema,permissions,login-logger | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | |
| exams | db,auth-guard,types,ai | auth | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | |
| homework | db,auth-guard,types | auth | data-access.getExams | - | - | - | schema | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | |
| questions | db,auth-guard,types | auth | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | |
| textbooks | db,auth-guard,types | auth | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | |
| classes | db,auth-guard,types | auth | - | homework-insights | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | |
| school | db,auth-guard,types,audit-logger | auth | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | |
| dashboard | db,types | auth | - | data-access.getTeacherGradeTrends,getStudentDashboardGrades | - | - | data-access.getTeacherClasses,getStudentClasses,getStudentSchedule | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | |
| layout | hooks.usePermission | auth(useSession) | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | notification-dropdown | - | - | - | - | |
| settings | db,auth-guard,ai,types | auth | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | |
| users | db,auth-guard(requireAuth,requirePermission),types,lib.excel | auth | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | |
| audit | db,auth-guard.requirePermission,types.permissions | auth | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | |
| announcements | db,auth-guard,types | auth | - | - | - | - | - | - | data-access.getGrades | - | - | - | - | - | - | - | - | - | - | - | - | - | - | |
| files | db,auth-guard(requireAuth,requirePermission),types,lib/file-storage | auth | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | |
| grades | db,auth-guard,types,lib.excel | auth | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | |
| course-plans | db,auth-guard.requirePermission,types | auth | - | - | - | - | - | data-access.getAdminClasses,getStaffOptions | data-access.getAcademicYears | - | - | - | - | - | - | - | - | - | - | - | - | - | - | |
| parent | db,auth-guard(requireAuth),types | auth | - | data-access.getStudentHomeworkAssignments,getStudentDashboardGrades | - | - | data-access.getStudentClasses,getStudentSchedule | - | - | - | - | - | - | - | - | data-access.getStudentGradeSummary | - | - | - | - | - | - | ||
| messaging | db,auth-guard(requirePermission,requireAuth),types | auth | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | |
| notifications | db,auth-guard.requirePermission,types | auth | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | notification-preferences,data-access.createNotification | - | - | - | - | - |
| attendance | db,auth-guard.requirePermission,types | auth | - | - | - | - | - | data-access.getTeacherClasses,getAdminClasses | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | |
| scheduling | db,auth-guard(requirePermission,getAuthContext),types | auth | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | |
| proctoring | db,auth-guard(requirePermission,requireAuth),types,components.ui,hooks.usePermission | auth | schema.exams,examSubmissions,examProctoringEvents | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - |
| diagnostic | db,auth-guard(requirePermission,getAuthContext),types,hooks.usePermission,components.ui | auth | schema.examSubmissions,submissionAnswers,questionsToKnowledgePoints | - | - | - | schema.classes,classEnrollments | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - |
| elective | db,auth-guard.requirePermission,types,types.DataScope,hooks.usePermission,components.ui | auth | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - |
关键参数影响链
userId
- 由
auth.tsJWT callback 从users表查询产生,存入 JWT - 通过
session.user.id传递到所有 Server Components 和 Client Components - 通过
getAuthContext().userId传递到所有 Server Actions - 在
auth-guard.ts中用于查询usersToRoles(获取角色)和classSubjectTeachers/grades(获取 DataScope) - 在 exams/actions.ts 中作为
creatorId写入exams表 - 在 homework/actions.ts 中作为
creatorId写入homeworkAssignments表 - 在 classes/data-access.ts 中查询
getTeacherClasses(teacherId)和getGradeManagedClasses(userId) - 在 elective/actions.ts 中作为
teacherId默认值写入electiveCourses表(createElectiveCourseAction),作为studentId查询学生选课(selectCourseAction/dropCourseAction/getStudentSelectionsAction) - 在 elective/data-access.ts 中
getElectiveCourses({ scope, currentUserId })按teacherId过滤(class_taught/owned scope)
examId
- 由
exams/actions.ts的createExamAction产生,通过 CUID2 生成,写入exams表 - 被
exams/data-access.getExamById(id)读取 - 被
exams/actions.ts的updateExamAction/deleteExamAction/duplicateExamAction用于定位考试 - 传入
homework/actions.ts的createHomeworkAssignmentAction的sourceExamId参数 - 在
homeworkAssignments表中作为外键关联到源考试 - 被
homework/data-access.getHomeworkAssignmentAnalytics用于追溯作业来源
classId
- 由
classes/actions.ts的createTeacherClassAction/createAdminClassAction产生 - 被
classes/data-access.getClassStudents(classId)读取学生列表 - 被
classes/data-access.getClassSchedule(classId)读取课表 - 被
classes/data-access.getClassHomeworkInsights(classId)读取作业洞察 - 被
homework/data-access.getHomeworkAssignments({ classId })过滤作业列表 - 在
auth-guard.ts中通过classSubjectTeachers查询教师关联的 classIds,构建DataScope.class_taught
permission
- 由
shared/types/permissions.ts的Permissions常量定义(57 个权限点,含AUDIT_LOG_READ、ANNOUNCEMENT_MANAGE、FILE_UPLOAD、FILE_READ、FILE_DELETE、GRADE_RECORD_MANAGE、GRADE_RECORD_READ、COURSE_PLAN_MANAGE、COURSE_PLAN_READ、ATTENDANCE_MANAGE、ATTENDANCE_READ、MESSAGE_SEND、MESSAGE_READ、MESSAGE_DELETE、SCHEDULE_AUTO、SCHEDULE_ADJUST、DIAGNOSTIC_MANAGE、DIAGNOSTIC_READ、ELECTIVE_MANAGE、ELECTIVE_READ、ELECTIVE_SELECT等) - 在
shared/lib/permissions.ts中通过ROLE_PERMISSIONS映射角色到权限列表(admin 角色包含AUDIT_LOG_READ、COURSE_PLAN_MANAGE+COURSE_PLAN_READ;admin/teacher 含FILE_UPLOAD/READ/DELETE及GRADE_RECORD_MANAGE/READ;teacher/student/grade_head/teaching_head 含COURSE_PLAN_READ;student/parent 含FILE_READ及GRADE_RECORD_READ;admin/teacher 含ATTENDANCE_MANAGE+ATTENDANCE_READ,student/parent/grade_head/teaching_head 含ATTENDANCE_READ;admin/teacher/parent/grade_head/teaching_head 含MESSAGE_SEND/READ/DELETE;student 含MESSAGE_READ/DELETE但无MESSAGE_SEND;admin 含SCHEDULE_AUTO+SCHEDULE_ADJUST,teacher/student/parent/grade_head/teaching_head 无排课权限;admin/teacher/grade_head 含DIAGNOSTIC_MANAGE+DIAGNOSTIC_READ,teaching_head/student 含DIAGNOSTIC_READ;admin/teacher 含ELECTIVE_MANAGE+ELECTIVE_READ,student 含ELECTIVE_SELECT+ELECTIVE_READ,grade_head/teaching_head 含ELECTIVE_READ) - 在
auth.tsJWT callback 中通过resolvePermissions(roleNames)合并多角色权限,存入 JWT - 在
proxy.tsmiddleware 中通过token.permissions检查路由访问权限 - 在
shared/lib/auth-guard.ts中通过requirePermission(permission)在 Server Action 层断言权限(如 audit-logs 页面使用requirePermission(AUDIT_LOG_READ);DELETE /api/files/[id]使用requirePermission(FILE_DELETE);messaging/actions.ts 使用requirePermission(MESSAGE_SEND/READ/DELETE);attendance/actions.ts 使用requirePermission(ATTENDANCE_MANAGE/READ);users/actions.ts 的importUsersAction/exportUsersAction/downloadUserTemplateAction使用requirePermission(USER_MANAGE);grades/actions.ts 的exportGradesAction使用requirePermission(GRADE_RECORD_READ);POST /api/import使用requirePermission(USER_MANAGE);scheduling/actions.ts 使用requirePermission(SCHEDULE_AUTO/SCHEDULE_ADJUST);diagnostic/actions.ts 使用requirePermission(DIAGNOSTIC_MANAGE/READ);elective/actions.ts 使用requirePermission(ELECTIVE_MANAGE/READ/SELECT)) - 在
shared/hooks/use-permission.ts中通过hasPermission(permission)在客户端组件中条件渲染 - 在
layout/config/navigation.ts中作为NavItem.permission字段过滤侧边栏菜单(Audit Logs 菜单项使用Permissions.AUDIT_LOG_READ;Messages 菜单项使用Permissions.MESSAGE_READ;Attendance 菜单项 teacher 使用Permissions.ATTENDANCE_MANAGE,student/parent 使用Permissions.ATTENDANCE_READ;Import Users 菜单项使用Permissions.USER_MANAGE;Scheduling 菜单项 admin 使用Permissions.SCHEDULE_ADJUST/SCHEDULE_AUTO;teacher Schedule Changes 菜单项使用Permissions.SCHEDULE_ADJUST;teacher/student Diagnostic 菜单项使用Permissions.DIAGNOSTIC_READ;Electives 菜单项 admin/teacher 使用Permissions.ELECTIVE_MANAGE,student 使用Permissions.ELECTIVE_SELECT)
DataScope
- 由
auth-guard.ts的resolveDataScope(userId, roles)根据用户角色和 DB 关系动态计算 - 传递到
exams/data-access.getExams({ scope })进行行级过滤 - 传递到
homework/data-access.getHomeworkAssignments({ scope })进行行级过滤 - 传递到
dashboard/data-access.getAdminDashboardData(scope)进行统计过滤 - 在 exams/actions.ts 的
updateExamAction/deleteExamAction中用于判断是否需要资源归属校验(scope.type !== "all") - 对 parent 角色,
resolveDataScope查询parentStudentRelations表构建{ type: "children", childrenIds: string[] },传递到grades/data-access.getStudentGradeSummary等函数进行行级过滤 - 在
parent/children/[studentId]/page.tsx中通过ctx.dataScope.type === "children" && !ctx.dataScope.childrenIds.includes(studentId)二次校验家长拥有该子女 - 传递到
attendance/data-access.getAttendanceRecords({ scope })进行行级过滤(class_taught 按教师班级过滤,children 按子女过滤,class_members 仅查自己,all 查全部) - 在
attendance/actions.ts的getStudentAttendanceAction中对 class_members/children 进行 DataScope 二次校验 - 传递到
elective/data-access.getElectiveCourses({ scope, currentUserId })进行行级过滤(owned/class_taught 按teacherId过滤,grade_managed 按gradeIds过滤,class_members/children 返回 null 由getAvailableCoursesForStudent处理) - 在
elective/actions.ts的getStudentSelectionsAction中对 class_members/children 进行 DataScope 二次校验
路由表
根路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/ |
角色路由分发 | server | auth_required | 重定向到 /dashboard |
auth/* 子路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/login |
LoginForm | client | public | 登录页面 |
/register |
RegisterForm + registerAction | server | public | 注册页面(含未成年人信息保护、隐私政策/用户协议同意勾选) |
/privacy |
PrivacyPage | server | public | 隐私政策页面(信息收集/使用/保护、用户权利、Cookie、未成年人保护条款、联系方式) |
/terms |
TermsPage | server | public | 用户协议页面(服务说明、注册、行为规范、知识产权、免责、变更终止、法律适用) |
admin/school/* 子路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/admin/school |
SchoolManagementHome | server | school:manage | 学校管理首页(聚合入口) |
/admin/school/schools |
SchoolList | server | school:manage | 学校列表(dataAccess: school/data-access.getSchools) |
/admin/school/grades |
GradeList | server | grade:manage | 年级列表(dataAccess: school/data-access.getGrades) |
/admin/school/grades/insights |
GradeInsights | server | grade:manage | 年级洞察(dataAccess: classes/data-access.getGradeHomeworkInsights) |
/admin/school/departments |
DepartmentList | server | school:manage | 部门列表(dataAccess: school/data-access.getDepartments) |
/admin/school/classes |
AdminClassList | server | school:manage | 班级列表(dataAccess: classes/data-access.getAdminClasses) |
/admin/school/academic-year |
AcademicYearList | server | school:manage | 学年列表(dataAccess: school/data-access.getAcademicYears) |
admin/audit-logs/* 子路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/admin/audit-logs |
AuditLogView | server | audit_log:read | 操作日志列表(dataAccess: audit/data-access.getAuditLogs, getAuditModuleOptions;权限:requirePermission(AUDIT_LOG_READ)) |
/admin/audit-logs/login-logs |
LoginLogView | server | audit_log:read | 登录日志列表(dataAccess: audit/data-access.getLoginLogs;权限:requirePermission(AUDIT_LOG_READ)) |
admin/announcements/* 子路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/admin/announcements |
AdminAnnouncementsView | client | announcement:manage | 公告管理首页(列表+创建对话框;dataAccess: announcements/data-access.getAnnouncements, school/data-access.getGrades) |
/admin/announcements/[id] |
AnnouncementForm | client | announcement:manage | 编辑公告(dataAccess: announcements/data-access.getAnnouncementById, school/data-access.getGrades;actions: updateAnnouncementAction) |
admin/files/* 路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/admin/files |
AdminFilesView | client | file:read | 管理员文件管理页面(上传+列表+删除;dataAccess: files/data-access.getAllFileAttachments;API: POST /api/upload, DELETE /api/files/[id] 需 file:delete) |
admin/course-plans/* 路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/admin/course-plans |
CoursePlanList | client | course_plan:manage | 管理员课程计划列表(支持状态筛选;dataAccess: course-plans/data-access.getCoursePlans) |
/admin/course-plans/create |
CoursePlanForm (create) | client | course_plan:manage | 创建课程计划(actions: createCoursePlanAction;dataAccess: classes/data-access.getAdminClasses, course-plans/data-access.getSubjectOptions, classes/data-access.getStaffOptions, school/data-access.getAcademicYears) |
/admin/course-plans/[id] |
CoursePlanDetail | client | course_plan:manage | 课程计划详情(含周计划表格;actions: deleteCoursePlanAction, createCoursePlanItemAction, updateCoursePlanItemAction, deleteCoursePlanItemAction, toggleCoursePlanItemCompletedAction;dataAccess: course-plans/data-access.getCoursePlanById) |
/admin/course-plans/[id]/edit |
CoursePlanForm (edit) | client | course_plan:manage | 编辑课程计划(actions: updateCoursePlanAction;dataAccess: course-plans/data-access.getCoursePlanById, classes/data-access.getAdminClasses, course-plans/data-access.getSubjectOptions, classes/data-access.getStaffOptions, school/data-access.getAcademicYears) |
admin/users/* 路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/admin/users/import |
UserImportPage (含 UserImportDialog) | server | user:manage | 用户批量导入页面(说明卡片+字段文档表+导入对话框;actions: users/actions.downloadUserTemplateAction, users/actions.importUsersAction;权限:requirePermission(USER_MANAGE)) |
announcements/* 路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/announcements |
AnnouncementList | client | auth_required | 所有登录用户可查看的公告列表(仅 published;dataAccess: announcements/data-access.getAnnouncements) |
management/* 路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/management/grade/classes |
GradeManagedClasses | server | grade:manage | 年级主任管理的班级(dataAccess: classes/data-access.getGradeManagedClasses) |
/management/grade/insights |
GradeInsightsView | server | grade:manage | 年级洞察视图(dataAccess: classes/data-access.getGradeHomeworkInsights) |
student/learning/* 路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/student/learning/assignments |
StudentAssignmentList | server | homework:submit | 学生作业列表(dataAccess: homework/data-access.getStudentHomeworkAssignments) |
/student/learning/assignments/[assignmentId] |
HomeworkTakeView | client | homework:submit | 学生作答页面(actions: startHomeworkSubmissionAction, saveHomeworkAnswerAction, submitHomeworkAction) |
/student/learning/courses |
StudentCourseList | server | class:read | 学生课程列表(dataAccess: classes/data-access.getStudentClasses) |
/student/learning/textbooks |
StudentTextbookList | server | textbook:read | 学生教材列表(dataAccess: textbooks/data-access.getTextbooks) |
/student/learning/textbooks/[id] |
TextbookReader | client | textbook:read | 学生教材阅读器(dataAccess: textbooks/data-access.getTextbookById, getChaptersByTextbookId) |
/student/schedule |
StudentSchedule | server | class:read | 学生课表(dataAccess: classes/data-access.getStudentSchedule) |
teacher/homework/* 路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/teacher/homework |
HomeworkHome | server | homework:create | 作业管理首页(聚合入口) |
/teacher/homework/assignments |
HomeworkAssignmentList | server | homework:create | 作业列表(dataAccess: homework/data-access.getHomeworkAssignments) |
/teacher/homework/assignments/create |
HomeworkAssignmentForm | client | homework:create | 创建作业(actions: createHomeworkAssignmentAction) |
/teacher/homework/assignments/[id] |
HomeworkAssignmentDetail | server | homework:create | 作业详情(dataAccess: homework/data-access.getHomeworkAssignmentById) |
/teacher/homework/assignments/[id]/submissions |
HomeworkSubmissionList | server | homework:grade | 作业提交列表(dataAccess: homework/data-access.getHomeworkSubmissions) |
/teacher/homework/submissions |
HomeworkReviewList | server | homework:grade | 批改列表(dataAccess: homework/data-access.getHomeworkAssignmentReviewList) |
/teacher/homework/submissions/[submissionId] |
HomeworkGradingView | client | homework:grade | 批改页面(actions: gradeHomeworkSubmissionAction, dataAccess: getHomeworkSubmissionDetails) |
teacher 其他路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/teacher/exams |
ExamDataTable | server | exam:read | 考试列表(dataAccess: exams/data-access.getExams) |
/teacher/exams/[id]/build |
ExamAssemblyPanel | client | exam:update | 组卷页面(components: assembly/*, actions: updateExamAction) |
/teacher/exams/[id]/proctoring |
ProctoringDashboard | server+client | exam:proctor | 教师监考面板(dataAccess: proctoring/data-access.getExamForProctoring,getExamProctoringSummary,getStudentProctoringStatuses,getRecentProctoringEvents;权限:requirePermission(EXAM_PROCTOR);组件:proctoring/components/proctoring-dashboard.tsx,10 秒轮询刷新) |
/teacher/exams/grading |
ExamGradingList | server | exam:read | 考试批改列表 |
/teacher/exams/grading/[submissionId] |
ExamGradingView | client | exam:read | 考试批改页面 |
/teacher/classes/my/[id] |
ClassDetail | server | class:read | 班级详情(dataAccess: classes/data-access.getClassDetails) |
/teacher/classes |
TeacherClassList | server | class:read | 教师班级列表(dataAccess: classes/data-access.getTeacherClasses) |
/teacher/course-plans |
CoursePlanList (teacher) | client | course_plan:read | 教师课程计划列表(按当前用户 teacherId 过滤;dataAccess: course-plans/data-access.getCoursePlans) |
/teacher/course-plans/[id] |
CoursePlanDetail (teacher) | client | course_plan:read | 教师课程计划详情(只读,无编辑按钮;dataAccess: course-plans/data-access.getCoursePlanById) |
parent/* 路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/parent/dashboard |
ParentDashboard | server | auth_required | 家长仪表盘首页(问候语 + 子女卡片网格;dataAccess: parent/data-access.getParentDashboardData;权限:requireAuth()) |
/parent/children/[studentId] |
ChildDetailHeader + ChildDetailPanel | server | auth_required | 子女详情页(头部 + 作业/成绩/课表面板;dataAccess: parent/data-access.getChildDashboardData;权限:requireAuth() + 二次校验 ctx.dataScope.childrenIds 包含 studentId) |
/parent/grades |
ParentGradesView | server | grade_record:read | 家长成绩视图(dataAccess: grades/data-access.getStudentGradeSummary,按 DataScope.children 过滤) |
messages/* 路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/messages |
MessageList + NotificationList | server | message:read | 消息首页(收件箱/已发送列表 + 通知列表;dataAccess: messaging/data-access.getMessages, getNotifications;权限:requirePermission(MESSAGE_READ)) |
/messages/[id] |
MessageDetail | server | message:read | 消息详情(含回复线程;dataAccess: messaging/data-access.getMessageById, getMessageThread;actions: markMessageAsReadAction 自动已读;权限:requirePermission(MESSAGE_READ)) |
/messages/compose |
MessageCompose | server | message:send | 写消息页面(支持 reply 模式 via searchParams: receiverId, subject, parentMessageId;dataAccess: messaging/data-access.getRecipients;权限:requirePermission(MESSAGE_SEND)) |
attendance/* 路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/teacher/attendance |
AttendanceRecordList + AttendanceFilters | server | attendance:manage | 教师考勤记录列表(dataAccess: attendance/data-access.getAttendanceRecords, classes/data-access.getTeacherClasses;权限:requirePermission(ATTENDANCE_MANAGE)) |
/teacher/attendance/sheet |
AttendanceSheet | client | attendance:manage | 批量点名页面(班级/日期选择 + 学生表格;actions: batchRecordAttendanceAction, getClassAttendanceForDateAction, getClassStudentsForAttendance;权限:requirePermission(ATTENDANCE_MANAGE)) |
/teacher/attendance/stats |
AttendanceStatsCard | server | attendance:read | 班级考勤统计(dataAccess: attendance/data-access-stats.getClassAttendanceStats, classes/data-access.getTeacherClasses;权限:requirePermission(ATTENDANCE_READ)) |
/student/attendance |
StudentAttendanceView | server | attendance:read | 学生考勤视图(统计卡片 + 最近记录;dataAccess: attendance/data-access-stats.getStudentAttendanceSummary;权限:requirePermission(ATTENDANCE_READ),DataScope.class_members 仅查自己) |
/parent/attendance |
StudentAttendanceView (per child) | server | attendance:read | 家长考勤视图(遍历子女,每个子女展示 StudentAttendanceView;dataAccess: parent/data-access.getChildren, attendance/data-access-stats.getStudentAttendanceSummary;权限:requirePermission(ATTENDANCE_READ),DataScope.children 仅查子女) |
/admin/attendance |
AttendanceRecordList | server | attendance:manage | 管理员考勤总览(dataAccess: attendance/data-access.getAttendanceRecords(scope=all), classes/data-access.getAdminClasses;权限:requirePermission(ATTENDANCE_MANAGE)) |
scheduling/* 路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/admin/scheduling/rules |
SchedulingRulesForm | server | schedule:adjust | 排课规则配置页面(dataAccess: scheduling/actions.getAdminClassesForScheduling, getSchedulingRules;actions: saveSchedulingRulesAction;权限:requirePermission(SCHEDULE_ADJUST)) |
/admin/scheduling/auto |
AutoSchedulePanel + AutoScheduleResultView | server | schedule:auto | 自动排课页面(dataAccess: scheduling/actions.getAdminClassesForScheduling;actions: autoScheduleAction, applyAutoScheduleAction;权限:requirePermission(SCHEDULE_AUTO)) |
/admin/scheduling/changes |
ScheduleChangeList + ScheduleConflictsView | server | schedule:adjust | 调课申请审批+冲突检测页面(dataAccess: scheduling/actions.getAdminClassesForScheduling, getScheduleChanges;actions: approveScheduleChangeAction, rejectScheduleChangeAction, getClassConflictsAction;权限:requirePermission(SCHEDULE_ADJUST);审批操作需 SCHEDULE_AUTO) |
/teacher/schedule-changes |
ScheduleChangeForm + ScheduleChangeList | server | schedule:adjust | 教师调课/代课申请页面(dataAccess: scheduling/actions.getAdminClassesForScheduling, getTeachersForScheduling, getScheduleChanges(requesterId=ctx.userId);actions: requestScheduleChangeAction;权限:requirePermission(SCHEDULE_ADJUST);admin 角色查看全部申请) |
grades/* 路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/teacher/grades |
成绩管理首页 | server | grade_record:read | 成绩列表(dataAccess: grades/actions.getGradeRecordsAction) |
/teacher/grades/entry |
批量成绩录入 | server | grade_record:manage | 批量录入(actions: batchCreateGradeRecordsAction, createGradeRecordAction) |
/teacher/grades/stats |
成绩统计报表 | server | grade_record:read | 班级统计+排名(dataAccess: getClassGradeStatsAction, getClassRankingAction) |
/teacher/grades/analytics |
成绩趋势对比分析 | server | grade_record:read | 分析仪表盘(4 个分析图表并行加载;dataAccess: getGradeTrend, getGradeDistribution, getSubjectComparison, getClassComparison;权限:requirePermission(GRADE_RECORD_READ)) |
/student/grades |
StudentGradesView | server | grade_record:read | 学生成绩视图(dataAccess: getStudentGradeSummary,DataScope.class_members 仅查自己) |
/parent/grades |
ParentGradesView | server | grade_record:read | 家长成绩视图(dataAccess: getStudentGradeSummary,按 DataScope.children 过滤) |
settings/* 路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/settings |
角色分发设置页 | server | auth_required | 根据权限渲染 Admin/Teacher/Student 设置视图(含 General/Appearance/Security/Notifications tab,Security tab 含 PasswordChangeForm,Notifications tab 含 NotificationPreferencesForm;dataAccess: messaging/notification-preferences.getNotificationPreferences) |
/settings/security |
SecuritySettingsPage | server | auth_required | 安全设置独立页面(PasswordChangeForm + 安全提示;权限:requireAuth()) |
diagnostic/* 路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/teacher/diagnostic |
ReportList | client | diagnostic:read | 学情诊断报告列表(reportType/status 过滤器[URL searchParams];dataAccess: diagnostic/data-access-reports.getDiagnosticReports;actions: publishReportAction, deleteReportAction[DIAGNOSTIC_MANAGE];权限:requirePermission(DIAGNOSTIC_READ);DataScope.class_members 仅查看自己报告) |
/teacher/diagnostic/student/[studentId] |
StudentDiagnosticView | client | diagnostic:read | 学生学情诊断视图(概览卡片+雷达图+强项/弱项+生成报告[DIAGNOSTIC_MANAGE]+最新报告;dataAccess: getStudentMasterySummary, getKnowledgePointStats[班级平均对比], getDiagnosticReports;actions: generateStudentReportAction;权限:getAuthContext + DataScope 二次校验,class_members 仅自己,children 仅子女) |
/teacher/diagnostic/class/[classId] |
ClassDiagnosticView | client | diagnostic:read | 班级学情诊断视图(概览+知识点热力图+排名表+需重点关注学生+生成班级报告[DIAGNOSTIC_MANAGE];dataAccess: getClassMasterySummary;actions: generateClassReportAction;权限:getAuthContext + DataScope 校验,class_taught 必须包含 classId,class_members/children → notFound) |
/student/diagnostic |
StudentDiagnosticView | client | diagnostic:read | 学生本人学情诊断视图(概览+雷达图+强项/弱项+最新报告;dataAccess: getStudentMasterySummary(ctx.userId), getDiagnosticReports(studentId=ctx.userId);权限:requirePermission(DIAGNOSTIC_READ),DataScope.class_members 仅查自己) |
elective/* 路由
| 路由 | 组件 | 类型 | 权限 | 说明 |
|---|---|---|---|---|
/admin/elective |
ElectiveCourseList | server | elective:manage | 管理员选修课程列表(dataAccess: getElectiveCourses(scope=all);actions: deleteElectiveCourseAction, openSelectionAction, closeSelectionAction, runLotteryAction;权限:requirePermission(ELECTIVE_MANAGE)) |
/admin/elective/create |
ElectiveCourseForm | client | elective:manage | 创建选修课程(actions: createElectiveCourseAction;dataAccess: getSubjectOptions;权限:requirePermission(ELECTIVE_MANAGE)) |
/admin/elective/[id]/edit |
ElectiveCourseForm (edit) | client | elective:manage | 编辑选修课程(actions: updateElectiveCourseAction;dataAccess: getElectiveCourseById, getSubjectOptions;权限:requirePermission(ELECTIVE_MANAGE)) |
/teacher/elective |
ElectiveCourseList (teacher) | server | elective:manage | 教师选修课程列表(dataAccess: getElectiveCourses(scope=class_taught/owned, currentUserId);actions: deleteElectiveCourseAction, openSelectionAction, closeSelectionAction, runLotteryAction;权限:requirePermission(ELECTIVE_MANAGE);按 teacherId 过滤) |
/student/elective |
StudentSelectionView | server | elective:select | 学生选课页面(dataAccess: getAvailableCoursesForStudent, getStudentSelections;actions: selectCourseAction, dropCourseAction;权限:requirePermission(ELECTIVE_SELECT)) |
API 路由(含速率限制)
| 路由 | 方法 | 限流规则 | 说明 |
|---|---|---|---|
/api/auth/[...nextauth] |
GET, POST | — | NextAuth 认证(登录流程内置 LOGIN 限流: 5次/15分钟) |
/api/ai/chat |
POST | AI_CHAT: 20次/分钟 | AI 聊天(按 userId 限流,超限返回 429 + Retry-After 头) |
/api/upload |
POST | UPLOAD: 10次/分钟 | 文件上传(按 userId 限流,超限返回 429 + Retry-After 头) |
/api/rate-limit-test |
GET | PASSWORD_CHANGE: 5次/分钟 | 限流测试端点(按 userId 限流,用于手动验证 429 响应) |
/api/export |
POST | — | Excel 导出 |
/api/import |
POST | — | Excel 解析预览 |
/api/files/[id] |
GET, DELETE | — | 文件元数据/删除 |
/api/files/batch-delete |
POST | — | 批量删除文件(需 FILE_DELETE 权限,先删磁盘文件再删 DB 记录) |
/api/search |
GET | — | 全文检索(questions/textbooks/exams/announcements,需登录;参数 q/type/page/pageSize) |
/api/onboarding/* |
GET, POST | — | 用户引导 |
DevOps 与脚本
CI 配置 (.gitea/workflows/ci.yml)
| Job | 触发条件 | 说明 |
|---|---|---|
build-deploy |
push/PR to main | 构建、测试、部署到 Docker(自托管 runner CDCD) |
security-scan |
push/PR to main(needs: build-deploy) | 完整安全扫描:npm audit + Snyk + Trivy FS + OWASP ZAP 基线扫描,所有步骤 continue-on-error,上传 security-reports artifact(audit-report.json/trivy-fs-report.json/snyk.sarif) |
scheduled-backup |
schedule cron 0 2 * * * |
每天凌晨 2 点执行数据库备份→校验完整性→异地同步,上传 backups/ artifact(保留 30 天) |
backup-verify |
schedule(needs: scheduled-backup) | 下载备份 artifact,独立校验备份完整性,运行健康检查,上传 backup-verify-report artifact(保留 7 天) |
weekly-dr-drill |
schedule(needs: backup-verify,每周触发) | 灾备演练:从备份恢复到测试数据库,验证数据完整性,上传 dr-drill-report artifact(保留 90 天) |
灾备演练工作流 (.gitea/workflows/dr-drill.yml)
独立灾备演练工作流,触发方式:定时 cron 0 4 * * 1(每周一凌晨 4 点)/ 手动 workflow_dispatch(可指定 backup_file、no_cleanup)。
| 步骤 | 说明 |
|---|---|
| 安装 MySQL 客户端 | apt-get install mysql-client |
| 准备备份 | 下载 db-backup artifact 或现场执行 backup-db.sh |
| 执行演练 | 运行 scripts/dr-drill.sh(创建测试库→恢复→完整性检查→冒烟测试→清理→报告) |
| 上传报告 | dr-drill-report artifact(保留 90 天) |
| 失败通知 | webhook 通知运维团队(DR_NOTIFICATION_WEBHOOK) |
安全扫描工作流 (.gitea/workflows/security.yml)
独立深度安全扫描工作流,触发方式:定时 cron 0 3 * * 1(每周一凌晨 3 点)/ 手动 workflow_dispatch(可指定 target_url、skip_dast)。
| 步骤 | 工具 | 类型 | 输出 |
|---|---|---|---|
| 依赖扫描 | npm audit | 依赖 | audit-report.json |
| 深度依赖 + 静态分析 | Snyk(severity-threshold=medium) | 依赖 + 代码 | snyk.sarif |
| 文件系统扫描 | Trivy fs | 代码 + 依赖 | trivy-fs-report.json |
| 容器镜像扫描 | Trivy image(构建 nextjs-app:scan 镜像) | 容器 | trivy-image-report.json |
| DAST | OWASP ZAP baseline | 动态 | 控制台报告 |
| 汇总报告 | shell + jq | 汇总 | security-summary.md |
所有报告上传为 artifact security-reports-full。安全扫描配置文件:.gitea/suppressions.json(Snyk 漏洞抑制)、.trivyignore(Trivy CVE 忽略列表)。
运维脚本 (scripts/)
| 脚本 | 用途 |
|---|---|
scripts/audit.sh |
Bash 依赖审计脚本,运行 npm audit --audit-level=moderate,失败时生成 audit-report.json |
scripts/audit.ps1 |
PowerShell 版本依赖审计脚本(Windows 环境) |
scripts/security-scan.sh |
Bash 本地安全扫描脚本:npm audit + Trivy fs,彩色报告,退出码 0=无高危/1=有高危 |
scripts/security-scan.ps1 |
PowerShell 版本本地安全扫描脚本(Windows 环境) |
scripts/backup-db.sh |
MySQL 数据库备份脚本,从 DATABASE_URL 解析连接信息,gzip 压缩备份,保留 30 天 |
scripts/restore-db.sh |
MySQL 数据库恢复脚本,从指定备份文件恢复 |
scripts/test-backup.sh |
备份流程测试脚本,执行一次备份并验证 |
scripts/backup-verify.sh |
备份完整性校验脚本:检查文件存在/大小/gzip 完整性/SQL 内容结构/SQL 语法(可选,需 DATABASE_URL),退出码 0=通过/1=失败 |
scripts/backup-offsite-sync.sh |
异地备份同步脚本:支持 S3/OSS/NFS 后端,同步后校验文件数量,清理远程过期备份(保留 90 天),使用 aws-cli/rclone/ossutil/rsync |
scripts/dr-drill.sh |
灾备演练脚本(Bash):创建测试库→从备份恢复→数据完整性检查→冒烟测试→清理→生成报告到 docs/dr/reports/,退出码 0=成功/1=失败 |
scripts/dr-drill.ps1 |
灾备演练脚本(Windows PowerShell 5.1+):功能同 Bash 版本 |
scripts/failover.sh |
故障切换脚本:检测主库健康→提升备库→更新应用配置→重启应用→验证切换,支持手动/半自动/演练模式 |
scripts/health-check.sh |
健康检查脚本:检查应用 HTTP/数据库连接/磁盘空间/备份新鲜度,输出 JSON 报告,退出码 0=健康/1=异常 |
package.json 脚本
| 脚本 | 命令 | 说明 |
|---|---|---|
audit |
npm audit --audit-level=moderate |
依赖安全审计 |
audit:report |
npm audit --json > audit-report.json |
生成 JSON 审计报告 |
security:audit |
npm audit --audit-level=moderate |
依赖安全审计(security 别名) |
security:scan |
bash scripts/security-scan.sh |
本地完整安全扫描(npm audit + Trivy fs) |
backup |
bash scripts/backup-db.sh |
执行数据库备份 |
restore |
bash scripts/restore-db.sh |
执行数据库恢复 |
dr:backup-verify |
bash scripts/backup-verify.sh |
校验备份完整性 |
dr:offsite-sync |
bash scripts/backup-offsite-sync.sh |
异地备份同步 |
dr:drill |
bash scripts/dr-drill.sh |
灾备演练(Bash) |
dr:drill:ps1 |
powershell -ExecutionPolicy Bypass -File scripts/dr-drill.ps1 |
灾备演练(PowerShell) |
dr:health-check |
bash scripts/health-check.sh |
健康检查(JSON 报告) |
dr:failover |
bash scripts/failover.sh |
故障切换 |
灾备文档 (docs/dr/)
| 文档 | 用途 |
|---|---|
docs/dr/dr-plan.md |
灾备计划文档:RTO/RPO 定义(4h/24h)、备份策略、故障切换流程、联系人列表、恢复步骤 |
docs/dr/dr-runbook.md |
灾备操作手册:数据库故障/应用故障/备份失败/异地同步失败/演练失败/磁盘不足场景的诊断与处理 |
docs/dr/reports/ |
灾备演练报告存档目录(Markdown 格式,由 dr-drill.sh 生成) |
docs/dr/logs/ |
故障切换日志目录(由 failover.sh 生成) |
灾备环境变量 (.env.example)
| 变量 | 用途 |
|---|---|
BACKUP_OFFSITE_BACKEND |
异地备份后端类型: s3/oss/nfs/none |
BACKUP_OFFSITE_REMOTE |
远程存储路径 |
BACKUP_OFFSITE_BUCKET |
存储桶名称(仅 s3/oss) |
BACKUP_OFFSITE_ACCESS_KEY |
访问密钥 |
BACKUP_OFFSITE_SECRET_KEY |
秘密密钥 |
BACKUP_OFFSITE_REGION |
区域(默认 us-east-1) |
BACKUP_OFFSITE_RETENTION_DAYS |
远程备份保留天数(默认 90) |
DR_DRILL_TEST_DB |
演练测试数据库名(默认 next_edu_dr_drill) |
HEALTH_CHECK_URL |
应用健康检查 URL(默认 http://localhost:8015) |
DATABASE_URL_STANDBY |
备库连接 URL(故障切换时使用) |
FAILOVER_APP_NAME |
应用容器名(默认 nextjs-app) |
E2E 测试 (tests/e2e/)
| 测试文件 | 覆盖范围 | 依赖 |
|---|---|---|
smoke-auth.spec.ts |
登录/注册页面控件渲染冒烟测试 | 无需 DB |
auth-business-flow.spec.ts |
注册→登录→访问受保护区域完整流程 | DATABASE_URL |
full-route-regression.spec.ts |
全路由清单完整性 + 公开/受保护路由守卫 | 无需 DB(守卫测试) |
auth.spec.ts |
认证页面(登录/注册/隐私/协议)渲染 + 未认证重定向 | 无需 DB |
navigation.spec.ts |
admin/teacher/student 导航链接无 404 | DATABASE_URL + 测试账号 |
announcements.spec.ts |
公告页面未认证重定向 + 登录后渲染 | 部分需 DATABASE_URL |
grades.spec.ts |
成绩页面未认证重定向 + 登录后渲染 | 部分需 DATABASE_URL |
Playwright 配置 (playwright.config.ts)
testDir:./tests(顶层,由各 project 通过testDir限定范围)baseURL:http://127.0.0.1:3000webServer: 自动启动npm run dev,端口 3000,超时 180swebServer.env: 注入SKIP_ENV_VALIDATION=1、NEXTAUTH_SECRET、NEXTAUTH_URL、DATABASE_URL(测试库)projects:chromium(E2E 测试,testDir: ./tests/e2e,CI 通道为 undefined,本地为 chrome)visual-chromium(视觉回归测试,testDir: ./tests/visual,CI 通道为 undefined,本地为 chrome)
snapshotPathTemplate:{testDir}/__screenshots__/{testFilePath}/{arg}{ext}expect.toHaveScreenshot:maxDiffPixelRatio: 0.01、animations: "disabled"、caret: "hide"retries: CI 2 次,本地 0 次workers: CI 2 个,本地默认
视觉回归测试 (tests/visual/)
| 测试文件 | 覆盖范围 | 依赖 |
|---|---|---|
homepage.spec.ts |
登录页在 desktop/tablet/mobile × light/dark 下的快照 | 无需 DB |
admin-dashboard.spec.ts |
管理员仪表盘整页 + 侧边栏/主内容区组件快照 | DATABASE_URL + admin 账号 |
teacher-dashboard.spec.ts |
教师仪表盘整页 + 侧边栏/主内容区组件快照 | DATABASE_URL + teacher 账号 |
student-dashboard.spec.ts |
学生仪表盘整页 + 侧边栏/主内容区组件快照 | DATABASE_URL + student 账号 |
视觉测试辅助 (tests/visual/helpers/)
| 文件 | 用途 |
|---|---|
auth.ts |
登录辅助 setupAuthState(role)、loginByUI(page, role),测试账号默认 admin@xiaoxue.edu.cn / 123456 |
visual-helpers.ts |
setViewport、setTheme、waitForPageReady、maskDynamicElements、buildMaskOption |
视觉测试配置 (tests/visual/visual.config.ts)
- 视口: desktop 1920×1080、tablet 768×1024、mobile 375×812
- 主题: light、dark
- 快照目录:
tests/visual/__screenshots__/ - storageState 目录:
tests/visual/.auth/(已加入 .gitignore) - 默认容差:
maxDiffPixelRatio: 0.01