## P1 功能(20 项) - 站内消息系统、家长仪表盘、学生考勤管理 - Excel 导入导出、用户批量导入、成绩导出 - 排课规则+自动排课+课表调整 - 成绩趋势+对比分析、密码安全策略、速率限制 - 数据变更日志、文件预览+存储策略、全文检索 - 依赖审计集成 CI、数据库定时备份、E2E 测试完善 - 通知偏好管理 ## 基础设施修复 - src/proxy.ts: 将 middleware 导出重命名为 proxy(Next.js 16 要求) - .env: MySQL 端口从 13002 切换至 14013 - scripts/create-db.ts: 新增数据库初始化脚本 ## 架构文档同步 - 004_architecture_impact_map.md 和 005_architecture_data.json 完整记录所有新增表、模块、路由、权限、依赖关系
2894 lines
154 KiB
Markdown
2894 lines
154 KiB
Markdown
# 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`
|
||
|
||
### 导出常量与实例
|
||
|
||
#### `Permissions` (常量对象)
|
||
- 文件:`types/permissions.ts`
|
||
- 定义:47 个权限常量(`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` 等)
|
||
- 被使用: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 无排课权限
|
||
- 被使用:`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`
|
||
|
||
### 导出 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 |
|
||
|
||
---
|
||
|
||
## 模块: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)
|
||
|
||
> 登录流程集成密码安全策略和速率限制:
|
||
|
||
1. **速率限制**:按 `IP:email` 维度限流(RATE_LIMIT_RULES.LOGIN: 5次/15分钟),超限返回 null
|
||
2. **账户锁定检查**:通过 `isAccountLocked(failedLoginAttempts, lastFailedAt)` 判断,锁定则返回 null
|
||
3. **密码验证失败**:调用 `recordFailedLogin` 递增失败次数,达阈值自动锁定
|
||
4. **登录成功**:调用 `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)
|
||
- student 角色菜单包含 "My Grades" 项(icon: GraduationCap, href: /student/grades, permission: Permissions.GRADE_RECORD_READ)
|
||
- student 角色菜单包含 "Attendance" 项(icon: CalendarCheck, href: /student/attendance, permission: Permissions.ATTENDANCE_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"`,使用 React `cache` 包装读取函数。
|
||
|
||
#### `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` | 通知完整列表(全部标记已读、单条标记已读、查看链接) |
|
||
|
||
---
|
||
|
||
## 模块: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` | 冲突检测视图(班级选择器 + 检测按钮 + 冲突结果列表) |
|
||
|
||
---
|
||
|
||
## 模块间依赖矩阵
|
||
|
||
| ↓ 使用 → | shared | auth | exams | homework | questions | textbooks | classes | school | dashboard | layout | settings | users | audit | announcements | files | grades | course-plans | parent | messaging | attendance | scheduling |
|
||
|----------|--------|------|-------|----------|-----------|-----------|---------|--------|-----------|--------|----------|-------|-------|---------------|-------|-------|-------------|--------|-----------|------------|-----------|
|
||
| **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 | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - |
|
||
| **attendance** | db,auth-guard.requirePermission,types | auth | - | - | - | - | - | data-access.getTeacherClasses,getAdminClasses | - | - | - | - | - | - | - | - | - | - | - | - | - |
|
||
| **scheduling** | db,auth-guard(requirePermission,getAuthContext),types | auth | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - | - |
|
||
|
||
---
|
||
|
||
## 关键参数影响链
|
||
|
||
### `userId`
|
||
1. 由 `auth.ts` JWT callback 从 `users` 表查询产生,存入 JWT
|
||
2. 通过 `session.user.id` 传递到所有 Server Components 和 Client Components
|
||
3. 通过 `getAuthContext().userId` 传递到所有 Server Actions
|
||
4. 在 `auth-guard.ts` 中用于查询 `usersToRoles`(获取角色)和 `classSubjectTeachers`/`grades`(获取 DataScope)
|
||
5. 在 exams/actions.ts 中作为 `creatorId` 写入 `exams` 表
|
||
6. 在 homework/actions.ts 中作为 `creatorId` 写入 `homeworkAssignments` 表
|
||
7. 在 classes/data-access.ts 中查询 `getTeacherClasses(teacherId)` 和 `getGradeManagedClasses(userId)`
|
||
|
||
### `examId`
|
||
1. 由 `exams/actions.ts` 的 `createExamAction` 产生,通过 CUID2 生成,写入 `exams` 表
|
||
2. 被 `exams/data-access.getExamById(id)` 读取
|
||
3. 被 `exams/actions.ts` 的 `updateExamAction`/`deleteExamAction`/`duplicateExamAction` 用于定位考试
|
||
4. 传入 `homework/actions.ts` 的 `createHomeworkAssignmentAction` 的 `sourceExamId` 参数
|
||
5. 在 `homeworkAssignments` 表中作为外键关联到源考试
|
||
6. 被 `homework/data-access.getHomeworkAssignmentAnalytics` 用于追溯作业来源
|
||
|
||
### `classId`
|
||
1. 由 `classes/actions.ts` 的 `createTeacherClassAction`/`createAdminClassAction` 产生
|
||
2. 被 `classes/data-access.getClassStudents(classId)` 读取学生列表
|
||
3. 被 `classes/data-access.getClassSchedule(classId)` 读取课表
|
||
4. 被 `classes/data-access.getClassHomeworkInsights(classId)` 读取作业洞察
|
||
5. 被 `homework/data-access.getHomeworkAssignments({ classId })` 过滤作业列表
|
||
6. 在 `auth-guard.ts` 中通过 `classSubjectTeachers` 查询教师关联的 classIds,构建 `DataScope.class_taught`
|
||
|
||
### `permission`
|
||
1. 由 `shared/types/permissions.ts` 的 `Permissions` 常量定义(47 个权限点,含 `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` 等)
|
||
2. 在 `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 无排课权限)
|
||
3. 在 `auth.ts` JWT callback 中通过 `resolvePermissions(roleNames)` 合并多角色权限,存入 JWT
|
||
4. 在 `proxy.ts` middleware 中通过 `token.permissions` 检查路由访问权限
|
||
5. 在 `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)`)
|
||
6. 在 `shared/hooks/use-permission.ts` 中通过 `hasPermission(permission)` 在客户端组件中条件渲染
|
||
7. 在 `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`)
|
||
|
||
### `DataScope`
|
||
1. 由 `auth-guard.ts` 的 `resolveDataScope(userId, roles)` 根据用户角色和 DB 关系动态计算
|
||
2. 传递到 `exams/data-access.getExams({ scope })` 进行行级过滤
|
||
3. 传递到 `homework/data-access.getHomeworkAssignments({ scope })` 进行行级过滤
|
||
4. 传递到 `dashboard/data-access.getAdminDashboardData(scope)` 进行统计过滤
|
||
5. 在 exams/actions.ts 的 `updateExamAction`/`deleteExamAction` 中用于判断是否需要资源归属校验(`scope.type !== "all"`)
|
||
6. 对 parent 角色,`resolveDataScope` 查询 `parentStudentRelations` 表构建 `{ type: "children", childrenIds: string[] }`,传递到 `grades/data-access.getStudentGradeSummary` 等函数进行行级过滤
|
||
7. 在 `parent/children/[studentId]/page.tsx` 中通过 `ctx.dataScope.type === "children" && !ctx.dataScope.childrenIds.includes(studentId)` 二次校验家长拥有该子女
|
||
8. 传递到 `attendance/data-access.getAttendanceRecords({ scope })` 进行行级过滤(class_taught 按教师班级过滤,children 按子女过滤,class_members 仅查自己,all 查全部)
|
||
9. 在 `attendance/actions.ts` 的 `getStudentAttendanceAction` 中对 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/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()) |
|
||
|
||
### 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-audit` | push/PR to main | 依赖安全审计:`npm audit` moderate/critical 检查,上传 audit-report.json artifact |
|
||
| `scheduled-backup` | schedule cron `0 2 * * *` | 每天凌晨 2 点执行数据库备份,上传 backups/ artifact(保留 30 天) |
|
||
|
||
### 运维脚本 (`scripts/`)
|
||
|
||
| 脚本 | 用途 |
|
||
|------|------|
|
||
| `scripts/audit.sh` | Bash 依赖审计脚本,运行 `npm audit --audit-level=moderate`,失败时生成 audit-report.json |
|
||
| `scripts/audit.ps1` | PowerShell 版本依赖审计脚本(Windows 环境) |
|
||
| `scripts/backup-db.sh` | MySQL 数据库备份脚本,从 DATABASE_URL 解析连接信息,gzip 压缩备份,保留 30 天 |
|
||
| `scripts/restore-db.sh` | MySQL 数据库恢复脚本,从指定备份文件恢复 |
|
||
| `scripts/test-backup.sh` | 备份流程测试脚本,执行一次备份并验证 |
|
||
|
||
### package.json 脚本
|
||
|
||
| 脚本 | 命令 | 说明 |
|
||
|------|------|------|
|
||
| `audit` | `npm audit --audit-level=moderate` | 依赖安全审计 |
|
||
| `audit:report` | `npm audit --json > audit-report.json` | 生成 JSON 审计报告 |
|
||
| `backup` | `bash scripts/backup-db.sh` | 执行数据库备份 |
|
||
| `restore` | `bash scripts/restore-db.sh` | 执行数据库恢复 |
|
||
|
||
---
|
||
|
||
## 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/e2e`
|
||
- `baseURL`: `http://127.0.0.1:3000`
|
||
- `webServer`: 自动启动 `npm run dev`,端口 3000,超时 180s
|
||
- `webServer.env`: 注入 `SKIP_ENV_VALIDATION=1`、`NEXTAUTH_SECRET`、`NEXTAUTH_URL`、`DATABASE_URL`(测试库)
|
||
- `projects`: chromium(CI 通道为 undefined,本地为 chrome)
|
||
- `retries`: CI 2 次,本地 0 次
|
||
- `workers`: CI 2 个,本地默认
|