Files
NextEdu/docs/architecture/005_architecture_data.json
SpecialX 49291fcc31 refactor: fix all P0/P1/P2 bugs and architecture issues
Bug fixes (from bugs/ directory):

- Fix cross-module DB queries in 9 modules (homework, grades, parent, diagnostic, elective, proctoring, notifications, scheduling, classes) by routing through data-access functions

- Fix shared/lib <-> auth circular dependency via new session.ts module

- Fix divide-by-zero guard in grades data-access

- Fix audit export data truncation (paginated fetch for full datasets)

- Fix missing transactions in homework grading and elective lottery

- Fix missing revalidatePath in course-plans actions

- Fix frontend permission checks using requirePermission instead of requireAuth

- Fix dashboard role routing using session.user.roles

- Fix student auth pattern (migrate getDemoStudentUser to users module)

- Fix ActionState return type handling in components

Code quality fixes:

- Remove 60+ as type assertions (replace with type guards)

- Remove non-null assertions (use optional chaining or explicit checks)

- Convert dynamic imports to static imports (grades, diagnostic)

- Add React.cache() wrapping for read functions

- Parallelize independent queries with Promise.all

- Add explicit return types to 30+ arrow functions

- Replace any with unknown + type guards

- Fix import type for type-only imports

- Add Zod validation schemas for classes and diagnostic modules

- Extract duplicate code (normalizeRoleName, normalizeBcryptHash, logger IP extraction)

- Add console.error to silent catch blocks

- Fix permission naming consistency (exam:proctor_read -> exam:proctor:read)

Architecture doc sync:

- Update 004_architecture_impact_map.md and 005_architecture_data.json

- Update management-modules-audit.md for P0-7 cross-module fix

Moved deleted proctoring event route to deletes/ folder.
2026-06-19 05:13:34 +08:00

14310 lines
488 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"_meta": {
"project": "Next_Edu",
"description": "K12 智慧教务管理系统",
"generatedAt": "2026-06-17",
"formatVersion": "1.1",
"rule": "每次文件修改后须同步更新本文件",
"lastUpdate": "P1-1 已修复:所有跨模块直查已改为通过对方 data-access 接口homework/grades/parent/diagnostic/elective/proctoring/notifications/scheduling/classes 模块exams 模块 getSubjectsAction/getGradesAction 改为调用 school data-accessquestions 模块 getKnowledgePointOptionsAction 改为调用 textbooks data-accessgrades 模块多处直查 classes/classEnrollments/subjects/users 改为调用对应模块 data-accessclasses 模块 actions 直查 grades 表改为调用 school data-accessgetSessionTeacherId 改为通过 auth-guard.getAuthContextattendance 模块 getClassStudentsForAttendance 改为通过 classes data-accessscheduling 模块 autoScheduleAction 直查 users 改为通过 users data-accessnotifications 模块 sendClassNotificationAction 直查 classes/classEnrollments 改为通过 classes data-accessproctoring 模块跨模块直查 exams/examSubmissions/users 改为通过 exams/users data-accessdiagnostic 模块 updateMasteryFromSubmission 跨模块直查 4 张表改为通过 exams/questions data-accessdashboard 教师仪表盘直查 users 改为通过 users data-accessusers 模块 updateUserProfile 绕过 data-access 已下沉P2-20 已修复getDemoStudentUser 从 homework 模块迁移至 users 模块 getCurrentStudentUserhomework 保留 re-export 向后兼容6 个 student 页面dashboard/assignments/assignments-[assignmentId]/courses/textbooks/textbooks-[id]/schedule改用 users 模块,消除对 homework 的虚假依赖student/elective 改用 getAuthContext()shared/types/action-state.ts 移除分号修复 Prettier 违规shared/types/permissions.ts EXAM_PROCTOR_READ 字符串从 exam:proctor_read 改为 exam:proctor:read 统一命名;为 ActionState/DataScope/AuthContext 添加 JSDoc 注释P0-2 已修复shared/lib ↔ auth 循环依赖已解决,新增 shared/lib/session.ts 单一入口封装 getSession()dynamic import 打破静态循环audit-logger/change-logger/auth-guard 改为通过 session.ts 获取 sessionP1-6 已修复http-utils.ts 新增 getUserAgent()audit-logger/change-logger/login-logger 删除本地重复 IP/UA 提取逻辑,统一复用 resolveClientIp/getUserAgentP0-1 已修复exams/data-access.persistAiGeneratedExamDraft 改为调用 questions/data-access.createQuestionWithRelations不再直查 questions 表P0-2 已修复exams/data-access.getExams/getExamById/getExamsDashboardStats 改为调用 classes/data-access.getClassGradeIdsByClassIds不再直查 classes 表P1-2 已修复exams/homework/questions/announcements actions 层 DB 操作下沉到 data-accessP2-2 已修复ai.ts 拆分为 ai/ 目录P0-8 已修复school actions 层 DB 操作下沉到 data-accesslogAudit 改为 after() 异步非阻塞P0-7 已修复classes/data-access-stats.ts 和 data-access-students.ts 不再直查 homework/exams 表,改为调用新增的 homework/data-access-classes.ts 暴露的 7 个函数;新增 diagnostic/schema.ts6 个 Zod schema和 classes/schema.ts13 个 Zod schemaactions.ts 中手动 typeof 校验全部替换为 Zod safeParse"
},
"architectureOverview": {
"layers": [
{
"name": "app",
"description": "路由层,Next.js App Router,按角色分组(admin/teacher/student/parent/management)",
"path": "src/app/"
},
{
"name": "modules",
"description": "业务模块层,按业务领域划分(25 个模块)",
"path": "src/modules/"
},
{
"name": "shared",
"description": "基础设施层,提供共享能力(db/lib/hooks/components/types)",
"path": "src/shared/"
}
],
"dependencyRule": "app → modules → shared (单向依赖,上层可依赖下层,下层不可依赖上层)",
"violations": [
"✅ shared/lib ↔ auth 循环依赖已修复(audit-logger/change-logger/auth-guard 改为通过 shared/lib/session.ts 单一入口获取 sessionsession.ts 内部 dynamic import @/auth 打破静态循环)",
"✅ dashboard 跨模块直查 11 张表已修复(改为并行调用各模块 dashboard stats 函数: getUsersDashboardStats/getClassesDashboardStats/getTextbooksDashboardStats/getQuestionsDashboardStats/getExamsDashboardStats/getHomeworkDashboardStats)",
"✅ messaging 绕过 notifications dispatcher 已修复(通知 CRUD 和偏好迁移至 notifications 模块messaging 通过 dispatcher 发送通知)",
"✅ classes/data-access.ts 混入 homework/scheduling/grades 跨模块逻辑已修复(拆分为 5 个文件classes 通过 homework/data-access-classes 获取作业数据)",
"✅ classSchedule 表三处写入口已统一(写函数从 classes 迁移至 scheduling/data-access-class-schedule.tsclasses 仅保留读函数)",
"✅ P1-1 跨模块直查已修复(homework/grades/parent/diagnostic/elective/proctoring/notifications/scheduling/classes 等模块的直查改为通过对方 data-access 接口)"
]
},
"techStack": {
"framework": "Next.js 16 (App Router)",
"language": "TypeScript (strict)",
"ui": "React 19 + Tailwind CSS v4 + shadcn/ui",
"state": [
"Zustand",
"nuqs",
"React Hook Form"
],
"database": "MySQL + Drizzle ORM 0.45",
"auth": "NextAuth v5 (JWT strategy)",
"validation": "Zod 4",
"ai": "OpenAI SDK (multi-provider)",
"testing": [
"Vitest",
"Playwright"
]
},
"roles": [
"admin",
"teacher",
"student",
"parent",
"grade_head",
"teaching_head"
],
"permissions": {
"EXAM_CREATE": "exam:create",
"EXAM_READ": "exam:read",
"EXAM_UPDATE": "exam:update",
"EXAM_DELETE": "exam:delete",
"EXAM_DUPLICATE": "exam:duplicate",
"EXAM_PUBLISH": "exam:publish",
"EXAM_AI_GENERATE": "exam:ai_generate",
"EXAM_SUBMIT": "exam:submit",
"HOMEWORK_CREATE": "homework:create",
"HOMEWORK_GRADE": "homework:grade",
"HOMEWORK_SUBMIT": "homework:submit",
"QUESTION_CREATE": "question:create",
"QUESTION_READ": "question:read",
"QUESTION_UPDATE": "question:update",
"QUESTION_DELETE": "question:delete",
"TEXTBOOK_CREATE": "textbook:create",
"TEXTBOOK_READ": "textbook:read",
"TEXTBOOK_UPDATE": "textbook:update",
"TEXTBOOK_DELETE": "textbook:delete",
"CLASS_CREATE": "class:create",
"CLASS_READ": "class:read",
"CLASS_UPDATE": "class:update",
"CLASS_DELETE": "class:delete",
"CLASS_ENROLL": "class:enroll",
"CLASS_SCHEDULE": "class:schedule",
"SCHOOL_MANAGE": "school:manage",
"GRADE_MANAGE": "grade:manage",
"USER_MANAGE": "user:manage",
"AI_CHAT": "ai:chat",
"AI_CONFIGURE": "ai:configure",
"SETTINGS_ADMIN": "settings:admin",
"AUDIT_LOG_READ": "audit_log:read",
"ANNOUNCEMENT_MANAGE": "announcement:manage",
"ANNOUNCEMENT_READ": "announcement:read",
"GRADE_RECORD_MANAGE": "grade_record:manage",
"GRADE_RECORD_READ": "grade_record:read",
"FILE_UPLOAD": "file:upload",
"FILE_READ": "file:read",
"FILE_DELETE": "file:delete",
"COURSE_PLAN_MANAGE": "course_plan:manage",
"COURSE_PLAN_READ": "course_plan:read",
"ATTENDANCE_MANAGE": "attendance:manage",
"ATTENDANCE_READ": "attendance:read",
"MESSAGE_SEND": "message:send",
"MESSAGE_READ": "message:read",
"MESSAGE_DELETE": "message:delete",
"SCHEDULE_AUTO": "schedule:auto",
"SCHEDULE_ADJUST": "schedule:adjust",
"DIAGNOSTIC_MANAGE": "diagnostic:manage",
"DIAGNOSTIC_READ": "diagnostic:read",
"ELECTIVE_MANAGE": "elective:manage",
"ELECTIVE_READ": "elective:read",
"ELECTIVE_SELECT": "elective:select",
"EXAM_PROCTOR": "exam:proctor",
"EXAM_PROCTOR_READ": "exam:proctor:read",
"LESSON_PLAN_CREATE": "lesson_plan:create",
"LESSON_PLAN_READ": "lesson_plan:read",
"LESSON_PLAN_UPDATE": "lesson_plan:update",
"LESSON_PLAN_DELETE": "lesson_plan:delete",
"LESSON_PLAN_PUBLISH": "lesson_plan:publish"
},
"rolePermissions": {
"admin": [
"EXAM_CREATE",
"EXAM_READ",
"EXAM_UPDATE",
"EXAM_DELETE",
"EXAM_DUPLICATE",
"EXAM_PUBLISH",
"EXAM_AI_GENERATE",
"HOMEWORK_CREATE",
"HOMEWORK_GRADE",
"QUESTION_CREATE",
"QUESTION_READ",
"QUESTION_UPDATE",
"QUESTION_DELETE",
"TEXTBOOK_CREATE",
"TEXTBOOK_READ",
"TEXTBOOK_UPDATE",
"TEXTBOOK_DELETE",
"CLASS_CREATE",
"CLASS_READ",
"CLASS_UPDATE",
"CLASS_DELETE",
"CLASS_ENROLL",
"CLASS_SCHEDULE",
"SCHOOL_MANAGE",
"GRADE_MANAGE",
"USER_MANAGE",
"AI_CHAT",
"AI_CONFIGURE",
"SETTINGS_ADMIN",
"AUDIT_LOG_READ",
"ANNOUNCEMENT_MANAGE",
"ANNOUNCEMENT_READ",
"GRADE_RECORD_MANAGE",
"GRADE_RECORD_READ",
"FILE_UPLOAD",
"FILE_READ",
"FILE_DELETE",
"COURSE_PLAN_MANAGE",
"COURSE_PLAN_READ",
"ATTENDANCE_MANAGE",
"ATTENDANCE_READ",
"MESSAGE_SEND",
"MESSAGE_READ",
"MESSAGE_DELETE",
"SCHEDULE_AUTO",
"SCHEDULE_ADJUST",
"DIAGNOSTIC_MANAGE",
"DIAGNOSTIC_READ",
"ELECTIVE_MANAGE",
"ELECTIVE_READ",
"EXAM_PROCTOR",
"EXAM_PROCTOR_READ"
],
"teacher": [
"EXAM_CREATE",
"EXAM_READ",
"EXAM_UPDATE",
"EXAM_DELETE",
"EXAM_DUPLICATE",
"EXAM_PUBLISH",
"EXAM_AI_GENERATE",
"HOMEWORK_CREATE",
"HOMEWORK_GRADE",
"QUESTION_CREATE",
"QUESTION_READ",
"QUESTION_UPDATE",
"QUESTION_DELETE",
"TEXTBOOK_CREATE",
"TEXTBOOK_READ",
"TEXTBOOK_UPDATE",
"CLASS_READ",
"CLASS_ENROLL",
"CLASS_SCHEDULE",
"AI_CHAT",
"ANNOUNCEMENT_READ",
"GRADE_RECORD_MANAGE",
"GRADE_RECORD_READ",
"FILE_UPLOAD",
"FILE_READ",
"FILE_DELETE",
"COURSE_PLAN_READ",
"ATTENDANCE_MANAGE",
"ATTENDANCE_READ",
"MESSAGE_SEND",
"MESSAGE_READ",
"MESSAGE_DELETE",
"DIAGNOSTIC_MANAGE",
"DIAGNOSTIC_READ",
"ELECTIVE_MANAGE",
"ELECTIVE_READ",
"EXAM_PROCTOR",
"EXAM_PROCTOR_READ"
],
"student": [
"EXAM_READ",
"EXAM_SUBMIT",
"HOMEWORK_SUBMIT",
"QUESTION_READ",
"TEXTBOOK_READ",
"CLASS_READ",
"AI_CHAT",
"ANNOUNCEMENT_READ",
"GRADE_RECORD_READ",
"FILE_READ",
"COURSE_PLAN_READ",
"ATTENDANCE_READ",
"MESSAGE_READ",
"MESSAGE_DELETE",
"DIAGNOSTIC_READ",
"ELECTIVE_SELECT",
"ELECTIVE_READ"
],
"parent": [
"EXAM_READ",
"TEXTBOOK_READ",
"CLASS_READ",
"ANNOUNCEMENT_READ",
"GRADE_RECORD_READ",
"FILE_READ",
"ATTENDANCE_READ",
"MESSAGE_SEND",
"MESSAGE_READ",
"MESSAGE_DELETE"
],
"grade_head": [
"EXAM_CREATE",
"EXAM_READ",
"EXAM_UPDATE",
"EXAM_DELETE",
"EXAM_DUPLICATE",
"EXAM_PUBLISH",
"EXAM_AI_GENERATE",
"HOMEWORK_CREATE",
"HOMEWORK_GRADE",
"QUESTION_CREATE",
"QUESTION_READ",
"QUESTION_UPDATE",
"QUESTION_DELETE",
"TEXTBOOK_CREATE",
"TEXTBOOK_READ",
"TEXTBOOK_UPDATE",
"CLASS_CREATE",
"CLASS_READ",
"CLASS_UPDATE",
"CLASS_ENROLL",
"CLASS_SCHEDULE",
"GRADE_MANAGE",
"AI_CHAT",
"ANNOUNCEMENT_READ",
"GRADE_RECORD_READ",
"COURSE_PLAN_READ",
"ATTENDANCE_READ",
"MESSAGE_SEND",
"MESSAGE_READ",
"MESSAGE_DELETE",
"DIAGNOSTIC_MANAGE",
"DIAGNOSTIC_READ",
"ELECTIVE_READ",
"EXAM_PROCTOR_READ"
],
"teaching_head": [
"EXAM_CREATE",
"EXAM_READ",
"EXAM_UPDATE",
"EXAM_DELETE",
"EXAM_DUPLICATE",
"EXAM_PUBLISH",
"EXAM_AI_GENERATE",
"HOMEWORK_CREATE",
"HOMEWORK_GRADE",
"QUESTION_CREATE",
"QUESTION_READ",
"QUESTION_UPDATE",
"QUESTION_DELETE",
"TEXTBOOK_CREATE",
"TEXTBOOK_READ",
"TEXTBOOK_UPDATE",
"CLASS_READ",
"GRADE_MANAGE",
"AI_CHAT",
"ANNOUNCEMENT_READ",
"GRADE_RECORD_READ",
"COURSE_PLAN_READ",
"ATTENDANCE_READ",
"MESSAGE_SEND",
"MESSAGE_READ",
"MESSAGE_DELETE",
"DIAGNOSTIC_READ",
"ELECTIVE_READ"
]
},
"dataScopeTypes": {
"all": "管理员:无过滤",
"owned": "仅自己创建的资源,含 userId 字段",
"class_taught": "教师:所教班级,含 classIds[] 和可选 subjectIds[]",
"grade_managed": "年级主任:所管年级,含 gradeIds[]",
"class_members": "学生:所在班级的成员数据",
"children": "家长:子女数据,含 childrenIds[]"
},
"modules": {
"shared": {
"path": "src/shared",
"description": "全项目共享基础设施数据库、工具函数、权限系统、UI组件、Hooks",
"exports": {
"functions": [
{
"name": "cn",
"file": "lib/utils.ts",
"signature": "cn(...inputs: ClassValue[]): string",
"purpose": "合并CSS类名并解决Tailwind冲突",
"deps": [
"clsx",
"tailwind-merge"
],
"usedBy": [
"*所有模块组件"
]
},
{
"name": "formatDate",
"file": "lib/utils.ts",
"signature": "formatDate(date: string | Date, locale?: string): string",
"params": {
"date": "日期值",
"locale": "Intl locale默认zh-CN"
},
"purpose": "国际化日期格式化",
"deps": [],
"usedBy": [
"exams",
"homework",
"dashboard",
"textbooks"
]
},
{
"name": "parseAiChatPayload",
"file": "lib/ai/payload-parser.ts",
"signature": "parseAiChatPayload(body: unknown): AiChatRequest",
"purpose": "解析并校验AI聊天请求负载",
"deps": [
"zod"
],
"usedBy": [
"app/api/ai/chat/route.ts"
]
},
{
"name": "encryptAiApiKey",
"file": "lib/ai/api-key-crypto.ts",
"signature": "encryptAiApiKey(value: string): string",
"purpose": "AES加密AI Provider API Key",
"deps": [
"crypto"
],
"usedBy": [
"settings/actions.ts"
]
},
{
"name": "decryptAiApiKey",
"file": "lib/ai/api-key-crypto.ts",
"signature": "decryptAiApiKey(value: string): string",
"purpose": "AES解密AI Provider API Key",
"deps": [
"crypto"
],
"usedBy": [
"settings/actions.ts",
"ai/api-key-crypto.ts内部"
]
},
{
"name": "testAiProviderConfig",
"file": "lib/ai/client.ts",
"signature": "testAiProviderConfig(input: { apiKey: string; baseUrl?: string; model: string }): Promise<boolean>",
"purpose": "测试AI Provider连通性(直接配置)",
"deps": [
"createAiChatCompletion"
],
"usedBy": [
"settings/actions.ts"
]
},
{
"name": "testAiProviderById",
"file": "lib/ai/client.ts",
"signature": "testAiProviderById(providerId: string, overrides?: { baseUrl?: string; model?: string }): Promise<boolean>",
"purpose": "测试AI Provider连通性(按ID)",
"deps": [
"shared/db",
"createAiChatCompletion"
],
"usedBy": [
"settings/actions.ts"
]
},
{
"name": "createAiChatCompletion",
"file": "lib/ai/client.ts",
"signature": "createAiChatCompletion(input: AiChatRequest): Promise<{ content: string; usage: unknown }>",
"purpose": "调用AI模型生成聊天回复",
"deps": [
"openai",
"shared/db",
"ai/provider-config.ts"
],
"usedBy": [
"exams/ai-pipeline.ts",
"app/api/ai/chat/route.ts"
]
},
{
"name": "getAiErrorMessage",
"file": "lib/ai/errors.ts",
"signature": "getAiErrorMessage(v: unknown): string",
"purpose": "从AI错误中提取可读消息",
"deps": [],
"usedBy": [
"exams/ai-pipeline.ts"
]
},
{
"name": "getAuthContext",
"file": "lib/auth-guard.ts",
"signature": "getAuthContext(): Promise<AuthContext>",
"returns": "AuthContext { userId, roles, permissions, dataScope }",
"purpose": "获取当前用户完整认证上下文",
"deps": [
"shared/lib/session.getSession",
"shared/db"
],
"usedBy": [
"所有业务模块的Server Actions"
]
},
{
"name": "requirePermission",
"file": "lib/auth-guard.ts",
"signature": "requirePermission(permission: Permission): Promise<AuthContext>",
"throws": "PermissionDeniedError",
"purpose": "断言当前用户拥有指定权限",
"deps": [
"getAuthContext"
],
"usedBy": [
"所有业务模块的Server Actions"
]
},
{
"name": "checkPermission",
"file": "lib/auth-guard.ts",
"signature": "checkPermission(permission: Permission): Promise<{ allowed: boolean; ctx: AuthContext }>",
"purpose": "非抛出版权限检查",
"deps": [
"getAuthContext"
],
"usedBy": []
},
{
"name": "requireAuth",
"file": "lib/auth-guard.ts",
"signature": "requireAuth(): Promise<AuthContext>",
"purpose": "仅断言用户已登录",
"deps": [
"getAuthContext"
],
"usedBy": []
},
{
"name": "resolvePermissions",
"file": "lib/permissions.ts",
"signature": "resolvePermissions(roleNames: string[]): Permission[]",
"purpose": "合并多角色的权限列表(去重)",
"deps": [
"ROLE_PERMISSIONS"
],
"usedBy": [
"auth.ts (JWT callback)"
]
},
{
"name": "logAudit",
"file": "lib/audit-logger.ts",
"signature": "logAudit(params: LogAuditParams): Promise<void>",
"purpose": "记录操作日志(静默失败)",
"deps": [
"shared/lib/session.getSession",
"shared/lib/http-utils.resolveClientIp",
"shared/lib/http-utils.getUserAgent",
"shared.db",
"shared.db.schema.auditLogs",
"@paralleldrive/cuid2"
],
"usedBy": [
"school/actions.ts",
"其他Server Actions"
]
},
{
"name": "logLoginEvent",
"file": "lib/login-logger.ts",
"signature": "logLoginEvent(params: LogLoginEventParams): Promise<void>",
"purpose": "记录登录日志signin/signout/signup静默失败",
"deps": [
"shared/lib/http-utils.resolveClientIp",
"shared/lib/http-utils.getUserAgent",
"shared.db",
"shared.db.schema.loginLogs",
"@paralleldrive/cuid2"
],
"usedBy": [
"auth.ts (events.signIn, events.signOut)"
]
},
{
"name": "normalizeRole",
"file": "lib/role-utils.ts",
"signature": "normalizeRole(value: unknown): NormalizedRole",
"purpose": "将角色值规范化为 admin/teacher/student/parent 之一纯函数legacy 别名 grade_head/teaching_head→teacher",
"deps": [],
"usedBy": [
"auth.ts (jwt/session callbacks)",
"lib/role-utils.resolvePrimaryRole"
]
},
{
"name": "resolvePrimaryRole",
"file": "lib/role-utils.ts",
"signature": "resolvePrimaryRole(roleNames: string[]): NormalizedRole",
"purpose": "从多角色列表解析主角色(优先级 admin>teacher>parent>student纯函数",
"deps": [
"lib/role-utils.normalizeRole"
],
"usedBy": [
"auth.ts (authorize, jwt callback)"
]
},
{
"name": "normalizeBcryptHash",
"file": "lib/bcrypt-utils.ts",
"signature": "normalizeBcryptHash(value: string): string",
"purpose": "将存储的 bcrypt 哈希规范化为 $2b$ 前缀形式(纯函数,兼容 legacy 无前缀存储)",
"deps": [],
"usedBy": [
"auth.ts (authorize)"
]
},
{
"name": "resolveClientIp",
"file": "lib/http-utils.ts",
"signature": "resolveClientIp(): Promise<string>",
"purpose": "从请求头解析客户端 IPx-forwarded-for 第一段/x-real-ipbest-effort失败返回 unknown",
"deps": [
"next/headers"
],
"usedBy": [
"auth.ts (authorize 速率限制键)",
"lib/audit-logger.logAudit",
"lib/change-logger.logDataChange",
"lib/login-logger.logLoginEvent"
]
},
{
"name": "getUserAgent",
"file": "lib/http-utils.ts",
"signature": "getUserAgent(): Promise<string>",
"purpose": "从请求头解析 User-Agentbest-effort失败返回 unknown",
"deps": [
"next/headers"
],
"usedBy": [
"lib/audit-logger.logAudit",
"lib/login-logger.logLoginEvent"
]
},
{
"name": "getSession",
"file": "lib/session.ts",
"signature": "getSession(): Promise<AppSession>",
"purpose": "session 获取单一入口server-only内部 dynamic import @/auth 打破 shared/lib ↔ auth 静态循环依赖)",
"deps": [
"@/auth (dynamic import)"
],
"usedBy": [
"lib/auth-guard.getAuthContext",
"lib/audit-logger.logAudit",
"lib/change-logger.logDataChange"
]
},
{
"name": "getOrCreatePasswordSecurity",
"file": "lib/password-security-service.ts",
"signature": "getOrCreatePasswordSecurity(db, passwordSecurity, userId: string): Promise<PasswordSecurityRow>",
"purpose": "获取或创建用户的 password_security 行server-only",
"deps": [
"drizzle-orm.eq",
"@paralleldrive/cuid2",
"shared.db",
"shared.db.schema.passwordSecurity"
],
"usedBy": [
"auth.ts (authorize)",
"lib/password-security-service.recordFailedLogin",
"lib/password-security-service.resetFailedLogin"
]
},
{
"name": "recordFailedLogin",
"file": "lib/password-security-service.ts",
"signature": "recordFailedLogin(db, passwordSecurity, userId: string): Promise<{ locked: boolean; lockedUntil: Date | null }>",
"purpose": "递增失败登录计数达到阈值则锁定账户server-only",
"deps": [
"lib/password-security-service.getOrCreatePasswordSecurity",
"lib/password-policy.PASSWORD_RULES",
"shared.db",
"shared.db.schema.passwordSecurity"
],
"usedBy": [
"auth.ts (authorize 密码校验失败分支)"
]
},
{
"name": "resetFailedLogin",
"file": "lib/password-security-service.ts",
"signature": "resetFailedLogin(db, passwordSecurity, userId: string): Promise<void>",
"purpose": "登录成功后重置失败计数与锁定状态server-only",
"deps": [
"lib/password-security-service.getOrCreatePasswordSecurity",
"shared.db",
"shared.db.schema.passwordSecurity"
],
"usedBy": [
"auth.ts (authorize 登录成功分支)"
]
},
{
"name": "isAllowedMimeType",
"file": "lib/file-storage.ts",
"signature": "isAllowedMimeType(mimeType: string): boolean",
"purpose": "判断MIME类型是否在允许上传的白名单中",
"deps": [
"ALLOWED_MIME_TYPES 常量"
],
"usedBy": [
"app/api/upload/route.ts",
"files/components/file-upload.tsx"
]
},
{
"name": "generateStoragePath",
"file": "lib/file-storage.ts",
"signature": "generateStoragePath(originalName: string): string",
"purpose": "根据原始文件名生成存储路径 uploads/YYYY-MM/cuid.ext相对于public/",
"deps": [
"@paralleldrive/cuid2"
],
"usedBy": [
"app/api/upload/route.ts"
]
},
{
"name": "getFileExtension",
"file": "lib/file-storage.ts",
"signature": "getFileExtension(filename: string): string",
"purpose": "从文件名中提取小写扩展名(不含点)",
"deps": [],
"usedBy": [
"shared/lib/file-storage 内部"
]
},
{
"name": "formatFileSize",
"file": "lib/file-storage.ts",
"signature": "formatFileSize(bytes: number): string",
"purpose": "将字节数格式化为人类可读字符串(如 1.5 MB、800 KB",
"deps": [],
"usedBy": [
"files/components/file-upload.tsx",
"files/components/file-list.tsx",
"files/components/file-preview.tsx"
]
},
{
"name": "exportToExcel",
"file": "lib/excel.ts",
"signature": "exportToExcel(params: { sheets: ExcelSheet[] }): Promise<Buffer>",
"params": {
"sheets": "ExcelSheet[],每个含 name/columns/rows"
},
"purpose": "将多 sheet 数据导出为 Excel Buffer表头加粗+冻结首行+自动筛选)",
"deps": [
"exceljs"
],
"usedBy": [
"users/import-export.exportUsersToExcel",
"grades/export.exportGradeRecordsToExcel",
"grades/export.exportClassGradeReportToExcel"
]
},
{
"name": "parseExcel",
"file": "lib/excel.ts",
"signature": "parseExcel(buffer: Buffer): Promise<ParsedSheet[]>",
"returns": "ParsedSheet[],每个含 sheetName/rows",
"purpose": "从 Buffer 解析 Excel 文件,首行作为表头,返回每 sheet 的行记录数组",
"deps": [
"exceljs"
],
"usedBy": [
"users/actions.importUsersAction",
"app/api/import/route.ts"
]
},
{
"name": "generateTemplate",
"file": "lib/excel.ts",
"signature": "generateTemplate(params: { sheets: TemplateSheet[] }): Promise<Buffer>",
"params": {
"sheets": "TemplateSheet[],每个含 name/columns(含 note)/sampleRows?"
},
"purpose": "生成导入模板 Buffer表头加粗+第二行填写说明+示例行)",
"deps": [
"exceljs"
],
"usedBy": [
"users/import-export.generateUserImportTemplate"
]
},
{
"name": "useA11yId",
"file": "lib/a11y.ts",
"signature": "useA11yId(prefix: string): string",
"purpose": "基于React.useId生成SSR安全的唯一ID用于aria-describedby、aria-labelledby等",
"deps": [
"react"
],
"usedBy": [
"待扩展表单组件、a11y组件"
]
},
{
"name": "mergeA11yProps",
"file": "lib/a11y.ts",
"signature": "mergeA11yProps<T extends Record<string, unknown>>(...props: (T | undefined | null | false)[]): T",
"purpose": "合并多组aria/data属性普通属性后者覆盖前者aria-*/data-*字符串属性以空格拼接",
"deps": [],
"usedBy": [
"待扩展"
]
},
{
"name": "describeInput",
"file": "lib/a11y.ts",
"signature": "describeInput(describedBy?: string, error?: string, hint?: string): { ariaDescribedBy?: string; ariaInvalid?: boolean }",
"purpose": "计算输入框的aria-describedby合并多个ID与aria-invaliderror存在则为true",
"deps": [],
"usedBy": [
"待扩展(表单组件)"
]
},
{
"name": "loadingAria",
"file": "lib/a11y.ts",
"signature": "loadingAria(isLoading: boolean): { ariaBusy: boolean; ariaLive: 'polite' | 'assertive' }",
"purpose": "提供加载状态的aria-busy与aria-live=polite属性",
"deps": [],
"usedBy": [
"待扩展"
]
}
],
"hooks": [
{
"name": "useActionWithToast",
"file": "hooks/use-action-with-toast.ts",
"signature": "useActionWithToast<T>(): { isPending: boolean; execute: (action: () => Promise<ActionState<T>>) => void }",
"purpose": "包装Server Action + toast反馈"
},
{
"name": "useDebounce",
"file": "hooks/use-debounce.ts",
"signature": "useDebounce<T>(value: T, delay?: number): T",
"purpose": "防抖Hook"
},
{
"name": "useMediaQuery",
"file": "hooks/use-media-query.ts",
"signature": "useMediaQuery(query: string): boolean",
"purpose": "响应式媒体查询Hook"
},
{
"name": "useLocalStorage",
"file": "hooks/use-local-storage.ts",
"signature": "useLocalStorage<T>(key: string, initialValue: T): [T, (value: T | ((prev: T) => T)) => void]",
"purpose": "localStorage持久化Hook"
},
{
"name": "usePermission",
"file": "hooks/use-permission.ts",
"signature": "usePermission(): { permissions: Permission[]; roles: string[]; hasPermission: (p: Permission) => boolean; hasAnyPermission: (...p: Permission[]) => boolean; hasAllPermissions: (...p: Permission[]) => boolean; hasRole: (r: string) => boolean }",
"purpose": "客户端权限检查Hook",
"usedBy": [
"layout/app-sidebar.tsx",
"exams/components",
"homework/components"
]
},
{
"name": "logDataChange",
"file": "lib/change-logger.ts",
"signature": "logDataChange(params: LogDataChangeParams): Promise<void>",
"purpose": "记录数据变更日志(写入 dataChangeLogs 表,自动获取 changedBy/changedByName/ipAddress静默失败",
"deps": [
"shared/lib/session.getSession",
"shared/lib/http-utils.resolveClientIp",
"shared/db (dataChangeLogs)",
"@paralleldrive/cuid2"
],
"usedBy": [
"待扩展(数据变更场景)"
]
},
{
"name": "StorageProvider",
"file": "lib/storage-provider.ts",
"signature": "interface StorageProvider { save(file: Buffer, storagePath: string): Promise<string>; read(storagePath: string): Promise<Buffer>; delete(storagePath: string): Promise<void>; exists(storagePath: string): Promise<boolean>; getUrl(storagePath: string): string }",
"purpose": "文件存储抽象接口,便于未来切换到 OSS/S3",
"usedBy": [
"app/api/files/batch-delete/route.ts"
]
},
{
"name": "LocalStorageProvider",
"file": "lib/storage-provider.ts",
"signature": "class LocalStorageProvider implements StorageProvider",
"purpose": "本地磁盘存储实现,文件持久化到 public/uploads/...URL 为 /uploads/...",
"deps": [
"fs/promises",
"path"
],
"usedBy": [
"通过 storageProvider 实例使用"
]
},
{
"name": "storageProvider",
"file": "lib/storage-provider.ts",
"signature": "const storageProvider: StorageProvider",
"purpose": "默认存储 Provider 单例LocalStorageProvider 实例),替换此实例可迁移到 OSS/S3",
"usedBy": [
"app/api/files/batch-delete/route.ts"
]
}
],
"components": [
{
"name": "AuthSessionProvider",
"file": "components/auth-session-provider.tsx",
"purpose": "NextAuth SessionProvider包装",
"usedBy": [
"app/layout.tsx"
]
},
{
"name": "OnboardingGate",
"file": "components/onboarding-gate.tsx",
"purpose": "新用户引导流程",
"usedBy": [
"app/layout.tsx"
]
},
{
"name": "ThemeProvider",
"file": "components/theme-provider.tsx",
"purpose": "next-themes主题切换",
"usedBy": [
"app/layout.tsx"
]
},
{
"name": "EmptyState",
"file": "components/ui/empty-state.tsx",
"purpose": "列表空状态展示",
"usedBy": [
"exams",
"homework",
"questions",
"textbooks"
]
},
{
"name": "GlobalSearch",
"file": "components/global-search.tsx",
"props": "{ className?, placeholder? }",
"purpose": "全局搜索组件防抖300ms调用 GET /api/searchCmd/Ctrl+K快捷键聚焦Escape关闭↑/↓键盘导航Enter跳转下拉展示 question/textbook/exam/announcement 四类结果)",
"internalDeps": [
"useDebounce",
"Input",
"Link",
"useRouter"
],
"usedBy": [
"layout/components/site-header.tsx"
]
},
{
"name": "Switch",
"file": "components/ui/switch.tsx",
"basedOn": "@radix-ui/react-switch",
"props": "Radix Switch Root props (checked, onCheckedChange, disabled, id, aria-label)",
"purpose": "开关切换UI组件shadcn风格checked/unchecked两态",
"usedBy": [
"settings/components/notification-preferences-form.tsx"
]
}
],
"constants": [
{
"name": "ROLE_PERMISSIONS",
"file": "lib/permissions.ts",
"type": "const",
"description": "角色-权限映射表",
"usedBy": [
"resolvePermissions",
"auth.ts JWT callback"
]
},
{
"name": "Permissions",
"file": "types/permissions.ts",
"type": "const",
"description": "38个权限点常量对象含 FILE_UPLOAD/FILE_READ/FILE_DELETE",
"usedBy": [
"auth-guard",
"use-permission",
"所有actions"
]
},
{
"name": "db",
"file": "db/index.ts",
"type": "const",
"description": "Drizzle ORM 实例",
"usedBy": [
"所有业务模块"
]
},
{
"name": "questionTypeEnum",
"file": "db/schema.ts",
"type": "const",
"description": "题目类型枚举",
"usedBy": [
"questions",
"exams"
]
},
{
"name": "classEnrollmentStatusEnum",
"file": "db/schema.ts",
"type": "const",
"description": "选课状态枚举",
"usedBy": [
"classes"
]
}
],
"files": [
{
"path": "db/relations.ts",
"description": "20+ 个 Drizzle relations 定义",
"usedBy": [
"所有业务模块的关联查询"
]
},
{
"path": "next-auth.d.ts",
"description": "NextAuth Session/JWT 类型扩展声明",
"usedBy": [
"auth.ts",
"useSession"
]
}
],
"types": [
{
"name": "ActionState",
"file": "types/action-state.ts",
"definition": "ActionState<T = void> = { success: boolean; message?: string; errors?: Record<string, string[]>; data?: T }",
"usedBy": [
"所有模块Server Action"
]
},
{
"name": "Permission",
"file": "types/permissions.ts",
"definition": "Permission = (typeof Permissions)[keyof typeof Permissions]",
"usedBy": [
"auth-guard",
"use-permission",
"所有actions"
]
},
{
"name": "DataScope",
"file": "types/permissions.ts",
"definition": "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[] }",
"usedBy": [
"auth-guard",
"exams/data-access",
"homework/data-access",
"dashboard/data-access",
"grades/data-access",
"parent/children/[studentId]/page.tsx"
]
},
{
"name": "AuthContext",
"file": "types/permissions.ts",
"definition": "AuthContext = { userId: string; roles: string[]; permissions: Permission[]; dataScope: DataScope }",
"usedBy": [
"auth-guard",
"所有调用requirePermission的Server Action"
]
},
{
"name": "PermissionDeniedError",
"file": "lib/auth-guard.ts",
"definition": "class PermissionDeniedError extends Error { constructor(permission: string) }",
"usedBy": [
"所有Server Action的try/catch"
]
}
]
},
"dbTables": {
"users": {
"fields": [
"id",
"name",
"email",
"emailVerified",
"image",
"password",
"phone",
"address",
"gender",
"age",
"birthDate",
"guardianName",
"guardianPhone",
"guardianRelation",
"consentAcceptedAt",
"gradeId",
"departmentId",
"onboardedAt",
"createdAt",
"updatedAt"
],
"usedBy": [
"auth",
"users",
"dashboard",
"classes"
]
},
"accounts": {
"fields": [
"userId",
"type",
"provider",
"providerAccountId",
"refresh_token",
"access_token",
"expires_at",
"token_type",
"scope",
"id_token",
"session_state"
],
"usedBy": [
"auth"
]
},
"sessions": {
"fields": [
"sessionToken",
"userId",
"expires"
],
"usedBy": [
"auth"
]
},
"verificationTokens": {
"fields": [
"identifier",
"token",
"expires"
],
"usedBy": [
"auth"
]
},
"roles": {
"fields": [
"id",
"name",
"description",
"createdAt",
"updatedAt"
],
"usedBy": [
"auth",
"auth-guard"
]
},
"usersToRoles": {
"fields": [
"userId",
"roleId"
],
"usedBy": [
"auth",
"auth-guard"
]
},
"rolePermissions": {
"fields": [
"roleId",
"permission"
],
"usedBy": [
"auth (seed)"
]
},
"knowledgePoints": {
"fields": [
"id",
"name",
"description",
"anchorText",
"parentId",
"chapterId",
"level",
"order",
"createdAt",
"updatedAt"
],
"usedBy": [
"textbooks",
"questions"
]
},
"questions": {
"fields": [
"id",
"content",
"type",
"difficulty",
"authorId",
"parentId",
"createdAt",
"updatedAt"
],
"usedBy": [
"questions",
"exams",
"homework"
]
},
"questionsToKnowledgePoints": {
"fields": [
"questionId",
"knowledgePointId"
],
"usedBy": [
"questions"
]
},
"subjects": {
"fields": [
"id",
"name",
"code",
"order",
"createdAt",
"updatedAt"
],
"usedBy": [
"exams",
"textbooks"
]
},
"textbooks": {
"fields": [
"id",
"title",
"subject",
"grade",
"publisher",
"createdAt",
"updatedAt"
],
"usedBy": [
"textbooks"
]
},
"chapters": {
"fields": [
"id",
"textbookId",
"title",
"order",
"parentId",
"content",
"createdAt",
"updatedAt"
],
"usedBy": [
"textbooks"
]
},
"departments": {
"fields": [
"id",
"name",
"description",
"createdAt",
"updatedAt"
],
"usedBy": [
"school"
]
},
"classrooms": {
"fields": [
"id",
"name",
"building",
"floor",
"capacity",
"createdAt",
"updatedAt"
],
"usedBy": [
"school"
]
},
"academicYears": {
"fields": [
"id",
"name",
"startDate",
"endDate",
"isActive",
"createdAt",
"updatedAt"
],
"usedBy": [
"school"
]
},
"schools": {
"fields": [
"id",
"name",
"code",
"createdAt",
"updatedAt"
],
"usedBy": [
"school",
"classes"
]
},
"grades": {
"fields": [
"id",
"schoolId",
"name",
"order",
"gradeHeadId",
"teachingHeadId",
"createdAt",
"updatedAt"
],
"usedBy": [
"school",
"classes",
"exams",
"auth-guard"
]
},
"classes": {
"fields": [
"id",
"schoolId",
"gradeId",
"teacherId",
"name",
"homeroom",
"room",
"invitationCode",
"schoolName",
"grade",
"createdAt",
"updatedAt"
],
"usedBy": [
"classes",
"homework",
"auth-guard"
]
},
"classSubjectTeachers": {
"fields": [
"classId",
"teacherId",
"subjectId",
"createdAt",
"updatedAt"
],
"usedBy": [
"classes",
"auth-guard"
]
},
"classEnrollments": {
"fields": [
"classId",
"studentId",
"status",
"createdAt"
],
"usedBy": [
"classes",
"homework"
]
},
"classSchedule": {
"fields": [
"id",
"classId",
"weekday",
"startTime",
"endTime",
"course",
"location",
"createdAt",
"updatedAt"
],
"usedBy": [
"classes"
]
},
"exams": {
"fields": [
"id",
"creatorId",
"title",
"description",
"subjectId",
"gradeId",
"status",
"structure",
"startTime",
"endTime",
"createdAt",
"updatedAt"
],
"usedBy": [
"exams",
"homework"
]
},
"examQuestions": {
"fields": [
"examId",
"questionId",
"score",
"order"
],
"usedBy": [
"exams"
]
},
"examSubmissions": {
"fields": [
"id",
"examId",
"studentId",
"score",
"submittedAt",
"status",
"createdAt",
"updatedAt"
],
"usedBy": [
"exams"
]
},
"submissionAnswers": {
"fields": [
"id",
"submissionId",
"questionId",
"answerContent",
"score",
"feedback",
"createdAt",
"updatedAt"
],
"usedBy": [
"exams"
]
},
"homeworkAssignments": {
"fields": [
"id",
"creatorId",
"sourceExamId",
"title",
"description",
"status",
"availableAt",
"dueAt",
"allowLate",
"lateDueAt",
"maxAttempts",
"structure",
"createdAt",
"updatedAt"
],
"usedBy": [
"homework"
]
},
"homeworkAssignmentQuestions": {
"fields": [
"assignmentId",
"questionId",
"score",
"order"
],
"usedBy": [
"homework"
]
},
"homeworkAssignmentTargets": {
"fields": [
"assignmentId",
"studentId",
"createdAt"
],
"usedBy": [
"homework"
]
},
"homeworkSubmissions": {
"fields": [
"id",
"assignmentId",
"studentId",
"status",
"attemptNo",
"score",
"submittedAt",
"startedAt",
"isLate",
"createdAt",
"updatedAt"
],
"usedBy": [
"homework"
]
},
"homeworkAnswers": {
"fields": [
"id",
"submissionId",
"questionId",
"answerContent",
"score",
"feedback",
"createdAt",
"updatedAt"
],
"usedBy": [
"homework"
]
},
"aiProviders": {
"fields": [
"id",
"provider",
"baseUrl",
"model",
"apiKeyEncrypted",
"apiKeyLast4",
"isDefault",
"createdBy",
"updatedBy",
"createdAt",
"updatedAt"
],
"usedBy": [
"settings",
"ai"
]
},
"auditLogs": {
"fields": [
"id",
"userId",
"userName",
"action",
"module",
"targetId",
"targetType",
"detail",
"ipAddress",
"userAgent",
"status",
"createdAt"
],
"usedBy": [
"audit",
"shared/lib/audit-logger"
]
},
"loginLogs": {
"fields": [
"id",
"userId",
"userEmail",
"action",
"status",
"ipAddress",
"userAgent",
"errorMessage",
"createdAt"
],
"usedBy": [
"audit",
"shared/lib/login-logger",
"auth"
]
},
"dataChangeLogs": {
"fields": [
"id",
"tableName",
"recordId",
"action",
"oldValue",
"newValue",
"changedBy",
"changedByName",
"ipAddress",
"createdAt"
],
"usedBy": [
"audit",
"shared/lib/change-logger"
]
},
"announcements": {
"fields": [
"id",
"title",
"content",
"type",
"status",
"targetGradeId",
"targetClassId",
"authorId",
"publishedAt",
"createdAt",
"updatedAt"
],
"usedBy": [
"announcements"
]
},
"fileAttachments": {
"fields": [
"id",
"filename",
"originalName",
"mimeType",
"size",
"storagePath",
"url",
"uploaderId",
"targetType",
"targetId",
"createdAt"
],
"usedBy": [
"files"
]
},
"gradeRecords": {
"fields": [
"id",
"studentId",
"classId",
"subjectId",
"examId",
"academicYearId",
"title",
"score",
"fullScore",
"type",
"semester",
"recordedBy",
"remark",
"createdAt",
"updatedAt"
],
"usedBy": [
"grades"
]
},
"coursePlans": {
"fields": [
"id",
"classId",
"subjectId",
"teacherId",
"academicYearId",
"semester",
"totalHours",
"completedHours",
"weeklyHours",
"startDate",
"endDate",
"syllabus",
"objectives",
"status",
"createdBy",
"createdAt",
"updatedAt"
],
"usedBy": [
"course-plans"
]
},
"coursePlanItems": {
"fields": [
"id",
"planId",
"week",
"topic",
"content",
"hours",
"textbookChapter",
"notes",
"isCompleted",
"completedAt",
"createdAt",
"updatedAt"
],
"usedBy": [
"course-plans"
]
},
"parentStudentRelations": {
"fields": [
"id",
"parentId",
"studentId",
"relation",
"createdAt"
],
"usedBy": [
"parent",
"auth-guard"
]
},
"messages": {
"fields": [
"id",
"senderId",
"receiverId",
"subject",
"content",
"isRead",
"readAt",
"parentMessageId",
"createdAt"
],
"usedBy": [
"messaging"
]
},
"messageNotifications": {
"fields": [
"id",
"userId",
"type",
"title",
"content",
"link",
"isRead",
"createdAt"
],
"usedBy": [
"notifications",
"messaging (via re-export)"
]
},
"notificationPreferences": {
"fields": [
"id",
"userId",
"emailEnabled",
"smsEnabled",
"pushEnabled",
"homeworkNotifications",
"gradeNotifications",
"announcementNotifications",
"messageNotifications",
"attendanceNotifications",
"createdAt",
"updatedAt"
],
"usedBy": [
"notifications",
"messaging (via re-export)",
"settings"
]
},
"attendanceRecords": {
"fields": [
"id",
"studentId",
"classId",
"scheduleId",
"date",
"status",
"remark",
"recordedBy",
"createdAt",
"updatedAt"
],
"usedBy": [
"attendance"
]
},
"attendanceRules": {
"fields": [
"id",
"classId",
"lateThresholdMinutes",
"earlyLeaveThresholdMinutes",
"enableAutoMark",
"createdAt",
"updatedAt"
],
"usedBy": [
"attendance"
]
},
"schedulingRules": {
"fields": [
"id",
"classId",
"maxDailyHours",
"maxContinuousHours",
"lunchBreakStart",
"lunchBreakEnd",
"morningStart",
"afternoonEnd",
"avoidBackToBack",
"balancedSubjects",
"createdAt",
"updatedAt"
],
"usedBy": [
"scheduling"
]
},
"scheduleChanges": {
"fields": [
"id",
"originalScheduleId",
"classId",
"originalTeacherId",
"substituteTeacherId",
"originalDate",
"newDate",
"newStartTime",
"newEndTime",
"reason",
"status",
"requestedBy",
"approvedBy",
"createdAt",
"updatedAt"
],
"usedBy": [
"scheduling"
]
},
"passwordSecurity": {
"fields": [
"id",
"userId",
"failedLoginAttempts",
"lockedUntil",
"passwordChangedAt",
"mustChangePassword",
"lastPasswordChange",
"createdAt",
"updatedAt"
],
"usedBy": [
"auth",
"settings"
]
},
"knowledgePointMastery": {
"fields": [
"id",
"studentId",
"knowledgePointId",
"masteryLevel",
"totalQuestions",
"correctQuestions",
"lastAssessedAt",
"createdAt",
"updatedAt"
],
"usedBy": [
"diagnostic"
],
"description": "知识点掌握度记录(复合主键 studentId+knowledgePointIdonDuplicateKeyUpdate upsert"
},
"learningDiagnosticReports": {
"fields": [
"id",
"studentId",
"generatedBy",
"reportType",
"period",
"summary",
"strengths",
"weaknesses",
"recommendations",
"overallScore",
"status",
"createdAt",
"updatedAt"
],
"usedBy": [
"diagnostic"
],
"description": "学情诊断报告reportType: individual/class/gradestatus: draft/published/archived"
},
"electiveCourses": {
"fields": [
"id",
"name",
"subjectId",
"teacherId",
"gradeId",
"description",
"capacity",
"enrolledCount",
"classroom",
"schedule",
"startDate",
"endDate",
"selectionStartAt",
"selectionEndAt",
"status",
"selectionMode",
"credit",
"createdAt",
"updatedAt"
],
"usedBy": [
"elective"
],
"description": "选修课程status: draft/open/closed/cancelledselectionMode: fcfs/lottery"
},
"courseSelections": {
"fields": [
"id",
"courseId",
"studentId",
"status",
"priority",
"selectedAt",
"enrolledAt",
"droppedAt",
"lotteryRank",
"createdAt",
"updatedAt"
],
"usedBy": [
"elective"
],
"description": "选课记录(复合主键 courseId+studentIdstatus: selected/enrolled/waitlist/dropped/rejected"
}
}
},
"auth": {
"path": "src/auth.ts",
"description": "用户认证NextAuth配置handlers/auth/signIn/signOut、JWT/Session callbacks、events回调(登录日志)。集成密码安全策略账户锁定、失败登录追踪和登录速率限制。P1-3 拆分后,辅助函数已迁移至 shared/lib/{role-utils,bcrypt-utils,http-utils,password-security-service}",
"imports": [
"shared/lib/permissions",
"shared/lib/login-logger",
"shared/lib/password-policy",
"shared/lib/rate-limit",
"shared/lib/role-utils",
"shared/lib/bcrypt-utils",
"shared/lib/http-utils",
"shared/lib/password-security-service",
"shared/db",
"shared/db/schema"
],
"exports": {
"functions": [
{
"name": "auth",
"signature": "auth(): Promise<Session | null>",
"purpose": "获取当前用户Session",
"usedBy": [
"shared/lib/session.ts (dynamic import再被 auth-guard/audit-logger/change-logger 间接使用)",
"所有Server Component页面",
"app/api 路由"
]
},
{
"name": "handlers",
"signature": "{ GET, POST }",
"purpose": "NextAuth Route Handler",
"usedBy": [
"app/api/auth/[...nextauth]/route.ts"
]
},
{
"name": "signIn",
"signature": "signIn(provider, options?)",
"purpose": "登录",
"usedBy": [
"login-form.tsx"
]
},
{
"name": "signOut",
"signature": "signOut(options?)",
"purpose": "登出",
"usedBy": [
"site-header.tsx"
]
}
],
"events": [
{
"name": "signIn",
"signature": "async signIn({ user }) => void",
"purpose": "用户登录成功后记录登录日志",
"deps": [
"shared/lib/login-logger.logLoginEvent"
],
"params": "{ userId: user.id, userEmail: user.email, action: 'signin', status: 'success' }"
},
{
"name": "signOut",
"signature": "async signOut(message) => void",
"purpose": "用户登出后记录登录日志(处理NextAuth v5不同message形状)",
"deps": [
"shared/lib/login-logger.logLoginEvent"
],
"params": "{ userId?, userEmail, action: 'signout', status: 'success' }"
}
]
},
"middleware": {
"file": "src/proxy.ts",
"signature": "middleware(request: NextRequest): Promise<NextResponse>",
"purpose": "基于权限点的路由守卫",
"routePermissions": {
"/admin": "school:manage",
"/teacher": "exam:read",
"/student": "homework:submit",
"/parent": "exam:read",
"/management": "grade:manage"
},
"apiPermissions": {
"/api/ai/chat": "ai:chat"
}
}
},
"exams": {
"path": "src/modules/exams",
"description": "考试全生命周期:创建(手动/AI)、编辑、预览、发布、删除、复制",
"exports": {
"actions": [
{
"name": "createExamAction",
"permission": "EXAM_CREATE",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"purpose": "手动模式创建考试草稿",
"deps": [
"requirePermission",
"shared/db",
"data-access.persistExamDraft"
],
"usedBy": [
"exam-form.tsx"
]
},
{
"name": "createAiExamAction",
"permission": "EXAM_AI_GENERATE",
"signature": "同上",
"purpose": "AI模式创建考试",
"deps": [
"requirePermission",
"ai-pipeline.generateAiCreateDraftFromSource",
"data-access.persistAiGeneratedExamDraft"
],
"usedBy": [
"exam-form.tsx"
]
},
{
"name": "previewAiExamAction",
"permission": "EXAM_AI_GENERATE",
"signature": "(prevState: ActionState<AiPreviewData> | null, formData: FormData) => Promise<ActionState<AiPreviewData>>",
"purpose": "AI预览试卷(不持久化)",
"deps": [
"requirePermission",
"ai-pipeline.generateAiPreviewData"
],
"usedBy": [
"exam-ai-generator.tsx"
]
},
{
"name": "regenerateAiQuestionAction",
"permission": "EXAM_AI_GENERATE",
"signature": "(prevState: ActionState<AiRewriteQuestionData> | null, formData: FormData) => Promise<ActionState<AiRewriteQuestionData>>",
"purpose": "AI重写单个题目",
"deps": [
"requirePermission",
"ai-pipeline.regenerateAiQuestionByInstruction"
],
"usedBy": [
"exam-preview-question-editor.tsx"
]
},
{
"name": "updateExamAction",
"permission": "EXAM_UPDATE",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"purpose": "更新考试(含资源归属校验)",
"deps": [
"requirePermission",
"data-access.getExamCreatorId",
"data-access.updateExamWithQuestions"
],
"usedBy": [
"exam-form.tsx"
]
},
{
"name": "deleteExamAction",
"permission": "EXAM_DELETE",
"signature": "同上",
"purpose": "删除考试(含资源归属校验)",
"deps": [
"requirePermission",
"data-access.getExamCreatorId",
"data-access.deleteExamById"
],
"usedBy": [
"exam-actions.tsx"
]
},
{
"name": "duplicateExamAction",
"permission": "EXAM_DUPLICATE",
"signature": "同上",
"purpose": "复制考试",
"deps": [
"requirePermission",
"data-access.getExamCreatorId",
"data-access.duplicateExam"
],
"usedBy": [
"exam-actions.tsx"
]
},
{
"name": "getExamPreviewAction",
"permission": "EXAM_READ",
"signature": "(examId: string) => Promise<ActionState<{ structure: unknown; questions: Array<{ id: string }> }>>",
"purpose": "获取考试预览数据",
"deps": [
"requirePermission",
"data-access.getExamPreview"
],
"usedBy": [
"exam-viewer.tsx"
]
},
{
"name": "getSubjectsAction",
"permission": "EXAM_READ",
"signature": "() => Promise<ActionState<{id:string;name:string}[]>>",
"purpose": "获取科目列表",
"deps": [
"requirePermission",
"data-access.getExamSubjects"
],
"usedBy": [
"exam-form.tsx"
]
},
{
"name": "getGradesAction",
"permission": "EXAM_READ",
"signature": "同上",
"purpose": "获取年级列表",
"deps": [
"requirePermission",
"data-access.getExamGrades"
],
"usedBy": [
"exam-form.tsx"
]
}
],
"dataAccess": [
{
"name": "getExams",
"signature": "getExams(params: GetExamsParams & { scope: DataScope }): Promise<Exam[]>",
"purpose": "查询考试列表(含数据权限过滤)",
"usedBy": [
"teacher/exams/all/page.tsx",
"homework创建页面"
]
},
{
"name": "getExamById",
"signature": "getExamById(id: string, scope?: DataScope): Promise<Exam | null>",
"purpose": "按ID获取考试详情",
"usedBy": [
"exam详情/编辑页面"
]
},
{
"name": "persistExamDraft",
"signature": "persistExamDraft(input: { examId, title, creatorId, subjectId, gradeId, scheduledAt?, description }): Promise<void>",
"purpose": "持久化手动考试草稿",
"usedBy": [
"createExamAction"
]
},
{
"name": "persistAiGeneratedExamDraft",
"signature": "persistAiGeneratedExamDraft(input: { examId, title, creatorId, subjectId, gradeId, scheduledAt?, description, structure, generated }): Promise<void>",
"purpose": "持久化AI生成考试草稿P0-1 已修复:通过 questions/data-access.createQuestionWithRelations 创建题目,不再直查 questions 表)",
"usedBy": [
"createAiExamAction"
]
},
{
"name": "getExamsDashboardStats",
"signature": "(scope?: DataScope) => Promise<ExamsDashboardStats>",
"purpose": "获取考试仪表盘统计数据(考试总数,支持数据范围过滤)",
"usedBy": [
"dashboard/data-access.getAdminDashboardData"
]
},
{
"name": "omitScheduledAtFromDescription",
"signature": "(description: string | null) => string",
"purpose": "从描述中移除scheduledAt信息",
"usedBy": [
"exams/data-access内部"
]
},
{
"name": "resolveSubjectGradeNames",
"signature": "(input: { subjectId?, gradeId? }) => Promise<{ subjectName?, gradeName? }>",
"purpose": "解析科目与年级名称",
"usedBy": [
"exams/data-access内部"
]
},
{
"name": "buildExamDescription",
"signature": "(input: { subject, grade, difficulty, totalScore, durationMin, scheduledAt?, questionCount? }) => string",
"purpose": "构建考试描述文本",
"usedBy": [
"exams/data-access内部"
]
},
{
"name": "getExamCreatorId",
"signature": "(examId: string) => Promise<string | null>",
"purpose": "获取考试创建者ID用于权限校验",
"usedBy": [
"updateExamAction",
"deleteExamAction",
"duplicateExamAction"
]
},
{
"name": "updateExamWithQuestions",
"signature": "(input: { examId, title, subjectId, gradeId, difficulty, totalScore, durationMin, scheduledAt?, description?, structure }) => Promise<void>",
"purpose": "更新考试及题目关联P1-2 新增,从 actions 下沉)",
"usedBy": [
"updateExamAction"
]
},
{
"name": "deleteExamById",
"signature": "(examId: string) => Promise<void>",
"purpose": "按ID删除考试P1-2 新增,从 actions 下沉)",
"usedBy": [
"deleteExamAction"
]
},
{
"name": "duplicateExam",
"signature": "(examId: string, creatorId: string) => Promise<{ newExamId: string }>",
"purpose": "复制考试P1-2 新增,从 actions 下沉)",
"usedBy": [
"duplicateExamAction"
]
},
{
"name": "getExamPreview",
"signature": "(examId: string) => Promise<{ structure: unknown; questions: Array<{ id: string }> } | null>",
"purpose": "获取考试预览数据P1-2 新增,从 actions 下沉)",
"usedBy": [
"getExamPreviewAction"
]
},
{
"name": "getExamSubjects",
"signature": "() => Promise<Array<{ id: string; name: string }>>",
"purpose": "获取科目列表P1-2 新增,从 actions 下沉)",
"usedBy": [
"getSubjectsAction"
]
},
{
"name": "getExamGrades",
"signature": "() => Promise<Array<{ id: string; name: string }>>",
"purpose": "获取年级列表P1-2 新增,从 actions 下沉)",
"usedBy": [
"getGradesAction"
]
},
{
"name": "GetExamsParams",
"type": "type",
"definition": "{ q?, status?, difficulty?, page?, pageSize? }",
"usedBy": [
"getExams",
"getExamsAction"
]
}
],
"aiPipeline": [
{
"name": "generateAiPreviewData",
"signature": "(input: { title, subject?, grade?, difficulty, totalScore, durationMin, questionCount?, sourceText, aiProviderId? }) => Promise<{ ok, data?, rawOutput?, message? }>",
"purpose": "AI预览生成",
"deps": [
"shared/lib/ai.createAiChatCompletion"
],
"usedBy": [
"previewAiExamAction"
]
},
{
"name": "generateAiCreateDraftFromSource",
"signature": "同上",
"purpose": "AI从源文本生成完整考试",
"deps": [
"shared/lib/ai.createAiChatCompletion"
],
"usedBy": [
"createAiExamAction"
]
},
{
"name": "regenerateAiQuestionByInstruction",
"signature": "(input: { instruction, originalQuestion, sourceText?, aiProviderId? }) => Promise<{ ok, data?, message? }>",
"purpose": "AI按指令重写题目",
"deps": [
"shared/lib/ai.createAiChatCompletion"
],
"usedBy": [
"regenerateAiQuestionAction"
]
},
{
"name": "AiQuestionSchema",
"type": "const",
"description": "zod schema 校验AI生成题目",
"usedBy": [
"ai-pipeline内部"
]
},
{
"name": "AiInsertQuestionSchema",
"type": "const",
"description": "zod schema 校验AI插入题目",
"usedBy": [
"ai-pipeline内部"
]
},
{
"name": "AiGeneratedQuestion",
"type": "type",
"definition": "{ id, type, difficulty, score, content }",
"usedBy": [
"ai-pipeline内部",
"exams/components"
]
},
{
"name": "AiGeneratedStructureNode",
"type": "type",
"definition": "{ id, type: \"group\"|\"question\", title?, questionId?, score?, children? }",
"usedBy": [
"ai-pipeline内部",
"exams/components"
]
},
{
"name": "AiGeneratedStructureNodeSchema",
"type": "const",
"description": "zod schema 校验AI生成结构节点(递归)",
"usedBy": [
"ai-pipeline内部"
]
},
{
"name": "AiGeneratedStructureSchema",
"type": "const",
"description": "zod schema 校验AI生成结构",
"usedBy": [
"ai-pipeline内部"
]
},
{
"name": "generateAiExamDraft",
"type": "function",
"signature": "(input) => Promise<AiDraftResult>",
"purpose": "生成AI考试草稿",
"deps": [
"shared/lib/ai.createAiChatCompletion"
],
"usedBy": [
"ai-pipeline内部"
]
}
],
"types": [
{
"name": "Exam",
"definition": "{ id, title, subject, grade, status, difficulty, totalScore, durationMin, questionCount, scheduledAt?, createdAt, updatedAt?, tags? }",
"usedBy": [
"exams/components",
"homework/types",
"dashboard/types"
]
},
{
"name": "AiPreviewData",
"definition": "{ title, rawOutput?, sections?, questions? }",
"usedBy": [
"exams/actions",
"exams/components"
]
},
{
"name": "AiRewriteQuestionData",
"definition": "{ type, difficulty, score, content }",
"usedBy": [
"exams/actions",
"exams/components"
]
},
{
"name": "ExamStatus",
"type": "type",
"definition": "\"draft\" | \"published\" | \"archived\"",
"usedBy": [
"exams/components",
"exams/data-access"
]
},
{
"name": "ExamDifficulty",
"type": "type",
"definition": "1 | 2 | 3 | 4 | 5",
"usedBy": [
"exams/components",
"exams/data-access"
]
},
{
"name": "SubmissionStatus",
"type": "type",
"definition": "\"pending\" | \"graded\"",
"usedBy": [
"exams/components",
"exams/data-access"
]
},
{
"name": "ExamSubmission",
"type": "interface",
"definition": "{ id, examId, examTitle, studentName, submittedAt, score?, status }",
"usedBy": [
"exams/components"
]
}
],
"components": [
{
"name": "ExamPaperPreview",
"file": "assembly/exam-paper-preview.tsx",
"purpose": "试卷预览"
},
{
"name": "QuestionBankList",
"file": "assembly/question-bank-list.tsx",
"purpose": "题库列表(组卷选择)"
},
{
"name": "SelectedQuestionList",
"file": "assembly/selected-question-list.tsx",
"purpose": "已选题目列表",
"types": [
"ExamNode"
]
},
{
"name": "StructureEditor",
"file": "assembly/structure-editor.tsx",
"purpose": "试卷结构编辑器"
},
{
"name": "ExamActions",
"file": "exam-actions.tsx",
"purpose": "考试操作按钮(删除/复制等)"
},
{
"name": "ExamAiGenerator",
"file": "exam-ai-generator.tsx",
"purpose": "AI生成考试面板"
},
{
"name": "ExamAssembly",
"file": "exam-assembly.tsx",
"purpose": "组卷主界面"
},
{
"name": "ExamBasicInfoForm",
"file": "exam-basic-info-form.tsx",
"purpose": "考试基本信息表单"
},
{
"name": "ExamCard",
"file": "exam-card.tsx",
"purpose": "考试卡片"
},
{
"name": "examColumns",
"file": "exam-columns.tsx",
"type": "ColumnDef[]",
"purpose": "考试表格列定义"
},
{
"name": "ExamDataTable",
"file": "exam-data-table.tsx",
"purpose": "考试数据表格"
},
{
"name": "ExamFilters",
"file": "exam-filters.tsx",
"purpose": "考试筛选器"
},
{
"name": "formSchema",
"file": "exam-form-types.ts",
"type": "const",
"purpose": "表单zod schema"
},
{
"name": "ExamFormValues",
"file": "exam-form-types.ts",
"type": "type",
"purpose": "表单值类型"
},
{
"name": "PreviewQuestion",
"file": "exam-form-types.ts",
"type": "type",
"purpose": "预览题目类型"
},
{
"name": "EditableQuestionContent",
"file": "exam-form-types.ts",
"type": "type",
"purpose": "可编辑题目内容"
},
{
"name": "PreviewSnapshotMeta",
"file": "exam-form-types.ts",
"type": "type",
"purpose": "预览快照元数据"
},
{
"name": "PreviewBackgroundTask",
"file": "exam-form-types.ts",
"type": "type",
"purpose": "预览后台任务"
},
{
"name": "aiProviderLabels",
"file": "exam-form-types.ts",
"type": "const",
"purpose": "AI Provider标签映射"
},
{
"name": "defaultValues",
"file": "exam-form-types.ts",
"type": "const",
"purpose": "表单默认值"
},
{
"name": "previewTaskStorageKey",
"file": "exam-form-types.ts",
"type": "const",
"purpose": "预览任务localStorage key"
},
{
"name": "ExamForm",
"file": "exam-form.tsx",
"purpose": "考试表单(创建/编辑)"
},
{
"name": "ExamGrid",
"file": "exam-grid.tsx",
"purpose": "考试网格视图"
},
{
"name": "ExamModeSelector",
"file": "exam-mode-selector.tsx",
"purpose": "考试模式选择(手动/AI)"
},
{
"name": "ExamPreviewDialog",
"file": "exam-preview-dialog.tsx",
"purpose": "考试预览对话框"
},
{
"name": "ExamPreviewQuestionEditor",
"file": "exam-preview-question-editor.tsx",
"purpose": "预览题目编辑器"
},
{
"name": "buildPreviewNodes",
"file": "exam-preview-utils.ts",
"type": "function",
"purpose": "构建预览节点"
},
{
"name": "parseEditableContent",
"file": "exam-preview-utils.ts",
"type": "function",
"purpose": "解析可编辑内容"
},
{
"name": "flattenPreviewQuestions",
"file": "exam-preview-utils.ts",
"type": "function",
"purpose": "扁平化预览题目"
},
{
"name": "findPreviewQuestionNode",
"file": "exam-preview-utils.ts",
"type": "function",
"purpose": "查找预览题目节点"
},
{
"name": "updatePreviewQuestionNodeInList",
"file": "exam-preview-utils.ts",
"type": "function",
"purpose": "更新预览题目节点"
},
{
"name": "buildPreviewSignature",
"file": "exam-preview-utils.ts",
"type": "function",
"purpose": "构建预览签名"
},
{
"name": "buildPreviewPayload",
"file": "exam-preview-utils.ts",
"type": "function",
"purpose": "构建预览payload"
},
{
"name": "buildPreviewRequestData",
"file": "exam-preview-utils.ts",
"type": "function",
"purpose": "构建预览请求数据"
},
{
"name": "ExamViewer",
"file": "exam-viewer.tsx",
"purpose": "考试查看器"
},
{
"name": "QuestionOptionsEditor",
"file": "question-options-editor.tsx",
"purpose": "题目选项编辑器"
},
{
"name": "QuestionSubQuestionsEditor",
"file": "question-sub-questions-editor.tsx",
"purpose": "子题目编辑器"
}
],
"hooks": [
{
"name": "useExamPreview",
"file": "use-exam-preview.ts",
"signature": "(form: UseFormReturn<ExamFormValues>) => { previewOpen, setPreviewOpen, previewLoading, previewNodes, handlePreview, handleBackgroundPreview, handleOpenPreviewTask, handleRewriteSelectedQuestion }",
"purpose": "考试预览Hook",
"usedBy": [
"exam-form.tsx"
]
}
]
}
},
"homework": {
"path": "src/modules/homework",
"description": "作业全生命周期:创建(源自考试)、发布、学生作答、教师批改、数据分析",
"exports": {
"actions": [
{
"name": "createHomeworkAssignmentAction",
"permission": "HOMEWORK_CREATE",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"purpose": "从已有考试创建作业",
"deps": [
"requirePermission",
"data-access-write.createHomeworkAssignment",
"exams/data-access.getExams"
],
"usedBy": [
"homework-assignment-form.tsx"
]
},
{
"name": "startHomeworkSubmissionAction",
"permission": "HOMEWORK_SUBMIT",
"signature": "同上",
"purpose": "学生开始作答",
"deps": [
"requirePermission",
"data-access-write.startHomeworkSubmission"
],
"usedBy": [
"homework-take-view.tsx"
]
},
{
"name": "saveHomeworkAnswerAction",
"permission": "HOMEWORK_SUBMIT",
"signature": "同上",
"purpose": "保存单题答案",
"deps": [
"requirePermission",
"data-access-write.saveHomeworkAnswer"
],
"usedBy": [
"homework-take-view.tsx"
]
},
{
"name": "submitHomeworkAction",
"permission": "HOMEWORK_SUBMIT",
"signature": "同上",
"purpose": "提交作业",
"deps": [
"requirePermission",
"data-access-write.submitHomework"
],
"usedBy": [
"homework-take-view.tsx"
]
},
{
"name": "gradeHomeworkSubmissionAction",
"permission": "HOMEWORK_GRADE",
"signature": "同上",
"purpose": "教师批改作业",
"deps": [
"requirePermission",
"data-access-write.gradeHomeworkSubmission"
],
"usedBy": [
"homework-grading-view.tsx"
]
}
],
"dataAccess": [
{
"name": "getHomeworkAssignments",
"signature": "(params?: { creatorId?, ids?, classId?, scope? }) => Promise<HomeworkAssignmentListItem[]>",
"usedBy": [
"teacher作业列表页",
"homework-assignment-form.tsx"
]
},
{
"name": "getHomeworkAssignmentReviewList",
"signature": "(params: { creatorId: string; scope? }) => Promise<HomeworkAssignmentReviewListItem[]>",
"usedBy": [
"teacher批改列表"
]
},
{
"name": "getHomeworkSubmissions",
"signature": "(params?: { assignmentId?, classId?, creatorId?, scope? }) => Promise<HomeworkSubmissionListItem[]>",
"usedBy": [
"teacher提交列表"
]
},
{
"name": "getStudentHomeworkAssignments",
"signature": "(studentId: string) => Promise<StudentHomeworkAssignmentListItem[]>",
"usedBy": [
"student/dashboard"
]
},
{
"name": "getHomeworkAssignmentById",
"signature": "(id: string, scope?: DataScope) => Promise<...>",
"purpose": "按ID获取作业详情",
"usedBy": [
"homework详情页"
]
},
{
"name": "getHomeworkSubmissionDetails",
"signature": "(submissionId: string) => Promise<HomeworkSubmissionDetails | null>",
"purpose": "获取提交详情(含答案)",
"usedBy": [
"homework-grading-view.tsx"
]
},
{
"name": "getDemoStudentUser",
"signature": "() => Promise<{ id: string; name: string } | null>",
"purpose": "获取演示学生用户(已迁移至 users 模块 getCurrentStudentUser此处为 re-export 向后兼容)",
"usedBy": [
"homework内部"
]
},
{
"name": "getStudentHomeworkTakeData",
"signature": "(assignmentId: string, studentId: string) => Promise<StudentHomeworkTakeData | null>",
"purpose": "获取学生作答数据",
"usedBy": [
"homework-take-view.tsx"
]
}
],
"dataAccessClasses": [
{
"name": "getAssignmentIdsForStudents",
"file": "data-access-classes.ts",
"signature": "(studentIds: string[]) => Promise<string[]>",
"purpose": "返回目标学生涉及的作业ID列表P0-7 新增,供 classes 模块跨模块调用)",
"usedBy": [
"classes/data-access-stats.getClassHomeworkInsights",
"classes/data-access-stats.getGradeHomeworkInsights",
"classes/data-access-students.getStudentsSubjectScores"
]
},
{
"name": "getHomeworkAssignmentsWithSubject",
"file": "data-access-classes.ts",
"signature": "(params: { assignmentIds, subjectIdFilter?, limit? }) => Promise<HomeworkAssignmentWithSubject[]>",
"purpose": "返回作业含科目信息via source exam可选按科目过滤P0-7 新增)",
"usedBy": [
"classes/data-access-stats.getClassHomeworkInsights"
]
},
{
"name": "getHomeworkAssignmentsByIds",
"file": "data-access-classes.ts",
"signature": "(params: { assignmentIds, limit? }) => Promise<HomeworkAssignmentBrief[]>",
"purpose": "按 ID 返回作业简要信息不含科目P0-7 新增)",
"usedBy": [
"classes/data-access-stats.getGradeHomeworkInsights"
]
},
{
"name": "getAssignmentTargetCounts",
"file": "data-access-classes.ts",
"signature": "(params: { assignmentIds, studentIds }) => Promise<Map<string, number>>",
"purpose": "返回每个作业在指定学生中的目标计数P0-7 新增)",
"usedBy": [
"classes/data-access-stats.getClassHomeworkInsights",
"classes/data-access-stats.getGradeHomeworkInsights"
]
},
{
"name": "getHomeworkSubmissionsForStudents",
"file": "data-access-classes.ts",
"signature": "(params: { assignmentIds, studentIds }) => Promise<HomeworkSubmissionRecord[]>",
"purpose": "返回指定作业和学生的提交记录(按 createdAt descP0-7 新增)",
"usedBy": [
"classes/data-access-stats.getClassHomeworkInsights",
"classes/data-access-stats.getGradeHomeworkInsights"
]
},
{
"name": "getPublishedHomeworkAssignmentsWithSubject",
"file": "data-access-classes.ts",
"signature": "(params: { assignmentIds }) => Promise<HomeworkAssignmentSubjectRow[]>",
"purpose": "返回已发布作业含科目信息via source examP0-7 新增)",
"usedBy": [
"classes/data-access-students.getStudentsSubjectScores"
]
},
{
"name": "getHomeworkSubmissionsForAssignments",
"file": "data-access-classes.ts",
"signature": "(assignmentIds: string[]) => Promise<HomeworkSubmissionScoreRecord[]>",
"purpose": "返回指定作业的提交记录(按 createdAt descP0-7 新增)",
"usedBy": [
"classes/data-access-students.getStudentsSubjectScores"
]
}
],
"dataAccessWrite": [
{
"name": "createHomeworkAssignment",
"file": "data-access-write.ts",
"signature": "(input: { examId, classId, title, description, dueAt, allowLate?, lateDueAt?, maxAttempts? }) => Promise<{ assignmentId: string }>",
"purpose": "创建作业P1-2 新增,从 actions 下沉)",
"usedBy": [
"createHomeworkAssignmentAction"
]
},
{
"name": "startHomeworkSubmission",
"file": "data-access-write.ts",
"signature": "(assignmentId: string, studentId: string) => Promise<{ submissionId: string }>",
"purpose": "学生开始作答P1-2 新增,从 actions 下沉)",
"usedBy": [
"startHomeworkSubmissionAction"
]
},
{
"name": "saveHomeworkAnswer",
"file": "data-access-write.ts",
"signature": "(submissionId: string, questionId: string, answer: string) => Promise<void>",
"purpose": "保存单题答案P1-2 新增,从 actions 下沉)",
"usedBy": [
"saveHomeworkAnswerAction"
]
},
{
"name": "submitHomework",
"file": "data-access-write.ts",
"signature": "(submissionId: string, studentId: string) => Promise<void>",
"purpose": "提交作业P1-2 新增,从 actions 下沉)",
"usedBy": [
"submitHomeworkAction"
]
},
{
"name": "gradeHomeworkSubmission",
"file": "data-access-write.ts",
"signature": "(submissionId: string, grades: Array<{ answerId: string; isCorrect: boolean; score: number; feedback?: string }>) => Promise<void>",
"purpose": "教师批改作业P1-2 新增,从 actions 下沉)",
"usedBy": [
"gradeHomeworkSubmissionAction"
]
}
],
"statsService": [
{
"name": "getTeacherGradeTrends",
"file": "stats-service.ts",
"signature": "(teacherId: string, limit?: number) => Promise<TeacherGradeTrendItem[]>",
"purpose": "教师仪表盘年级趋势数据",
"deps": [
"data-access.getAssignmentMaxScoreById"
],
"usedBy": [
"dashboard (教师仪表盘)"
],
"reExportedFrom": "data-access.ts (向后兼容)"
},
{
"name": "getHomeworkAssignmentAnalytics",
"file": "stats-service.ts",
"signature": "(assignmentId: string) => Promise<HomeworkAssignmentAnalytics | null>",
"purpose": "作业整体分析(含题目错误率/错答样本)",
"deps": [
"data-access.isRecord",
"data-access.toQuestionContent"
],
"usedBy": [
"homework错误分析组件"
],
"reExportedFrom": "data-access.ts (向后兼容)"
},
{
"name": "getStudentDashboardGrades",
"file": "stats-service.ts",
"signature": "(studentId: string) => Promise<StudentDashboardGradeProps>",
"purpose": "学生仪表盘成绩(趋势/近期/班级排名)",
"deps": [
"data-access.getAssignmentMaxScoreById"
],
"usedBy": [
"dashboard/data-access.ts"
],
"reExportedFrom": "data-access.ts (向后兼容)"
},
{
"name": "getHomeworkDashboardStats",
"file": "stats-service.ts",
"signature": "(scope?: DataScope) => Promise<HomeworkDashboardStats>",
"purpose": "获取作业仪表盘统计数据(作业数/已发布数/提交数/待批改数,支持数据范围过滤)",
"usedBy": [
"dashboard/data-access.getAdminDashboardData"
],
"reExportedFrom": "data-access.ts (向后兼容)"
}
],
"schema": [
{
"name": "CreateHomeworkAssignmentSchema",
"type": "const",
"description": "zod schema 创建作业",
"usedBy": [
"createHomeworkAssignmentAction",
"homework-assignment-form.tsx"
]
},
{
"name": "CreateHomeworkAssignmentInput",
"type": "type",
"definition": "z.infer<typeof CreateHomeworkAssignmentSchema>",
"usedBy": [
"createHomeworkAssignmentAction"
]
},
{
"name": "GradeHomeworkSchema",
"type": "const",
"description": "zod schema 批改作业",
"usedBy": [
"gradeHomeworkSubmissionAction",
"homework-grading-view.tsx"
]
}
],
"types": [
{
"name": "StudentDashboardGradeProps",
"definition": "{ trend, recent, ranking }",
"usedBy": [
"dashboard/types.ts"
]
},
{
"name": "HomeworkAssignmentListItem",
"usedBy": [
"homework列表页"
]
},
{
"name": "StudentHomeworkTakeData",
"usedBy": [
"homework-take-view.tsx"
]
},
{
"name": "HomeworkAssignmentStatus",
"type": "type",
"definition": "作业状态枚举",
"usedBy": [
"homework/components",
"homework/data-access"
]
},
{
"name": "HomeworkSubmissionStatus",
"type": "type",
"definition": "提交状态枚举",
"usedBy": [
"homework/components",
"homework/data-access"
]
},
{
"name": "TeacherGradeTrendItem",
"type": "type",
"definition": "教师年级趋势项",
"usedBy": [
"dashboard (教师仪表盘)"
]
},
{
"name": "HomeworkAssignmentReviewListItem",
"type": "type",
"definition": "批改列表项",
"usedBy": [
"teacher批改列表"
]
},
{
"name": "HomeworkSubmissionListItem",
"type": "type",
"definition": "提交列表项",
"usedBy": [
"teacher提交列表"
]
},
{
"name": "HomeworkQuestionContent",
"type": "type",
"definition": "作业题目内容",
"usedBy": [
"homework/components"
]
},
{
"name": "HomeworkSubmissionAnswerDetails",
"type": "type",
"definition": "提交答案详情",
"usedBy": [
"homework-grading-view.tsx"
]
},
{
"name": "HomeworkSubmissionDetails",
"type": "type",
"definition": "提交详情(含答案列表)",
"usedBy": [
"homework-grading-view.tsx"
]
},
{
"name": "StudentHomeworkProgressStatus",
"type": "type",
"definition": "学生作业进度状态",
"usedBy": [
"student/dashboard"
]
},
{
"name": "StudentHomeworkAssignmentListItem",
"type": "type",
"definition": "学生作业列表项",
"usedBy": [
"student/dashboard"
]
},
{
"name": "StudentHomeworkPerformanceItem",
"type": "type",
"definition": "学生表现项",
"usedBy": [
"student/dashboard"
]
},
{
"name": "StudentHomeworkPerformanceSummary",
"type": "type",
"definition": "学生表现汇总",
"usedBy": [
"student/dashboard"
]
},
{
"name": "StudentHomeworkTakeQuestion",
"type": "type",
"definition": "学生作答题目",
"usedBy": [
"homework-take-view.tsx"
]
},
{
"name": "HomeworkAssignmentQuestionAnalytics",
"type": "type",
"definition": "作业题目分析",
"usedBy": [
"homework错误分析组件"
]
},
{
"name": "HomeworkAssignmentAnalytics",
"type": "type",
"definition": "作业整体分析",
"usedBy": [
"homework错误分析组件"
]
},
{
"name": "StudentHomeworkScoreAnalytics",
"type": "type",
"definition": "学生成绩分析",
"usedBy": [
"student/dashboard"
]
},
{
"name": "StudentRanking",
"type": "type",
"definition": "学生排名",
"usedBy": [
"student/dashboard"
]
}
],
"components": [
{
"name": "HomeworkAssignmentExamContentCard",
"file": "homework-assignment-exam-content-card.tsx",
"purpose": "作业考试内容卡片"
},
{
"name": "HomeworkAssignmentExamErrorExplorerLazy",
"file": "homework-assignment-exam-error-explorer-lazy.tsx",
"purpose": "作业错误分析(懒加载)"
},
{
"name": "HomeworkAssignmentExamErrorExplorer",
"file": "homework-assignment-exam-error-explorer.tsx",
"purpose": "作业错误分析探索器"
},
{
"name": "HomeworkAssignmentExamPreviewPane",
"file": "homework-assignment-exam-preview-pane.tsx",
"purpose": "作业考试预览面板"
},
{
"name": "HomeworkAssignmentForm",
"file": "homework-assignment-form.tsx",
"purpose": "作业创建表单"
},
{
"name": "HomeworkAssignmentQuestionErrorDetailPanel",
"file": "homework-assignment-question-error-detail-panel.tsx",
"purpose": "题目错误详情面板"
},
{
"name": "HomeworkAssignmentQuestionErrorOverviewCard",
"file": "homework-assignment-question-error-overview-card.tsx",
"purpose": "题目错误概览卡片"
},
{
"name": "HomeworkGradingView",
"file": "homework-grading-view.tsx",
"purpose": "作业批改视图"
},
{
"name": "HomeworkTakeView",
"file": "homework-take-view.tsx",
"purpose": "学生作答视图"
},
{
"name": "HomeworkReviewView",
"file": "student-homework-review-view.tsx",
"purpose": "学生作业复习视图"
}
]
}
},
"questions": {
"path": "src/modules/questions",
"description": "题库管理题目CRUD、知识点关联、多题型支持",
"exports": {
"actions": [
{
"name": "createNestedQuestion",
"permission": "QUESTION_CREATE",
"signature": "(prevState: ActionState<string> | undefined, formData: FormData | CreateQuestionInput) => Promise<ActionState<string>>",
"purpose": "创建题目(含嵌套)",
"deps": [
"requirePermission",
"data-access.createQuestionWithRelations"
],
"usedBy": [
"create-question-dialog.tsx"
]
},
{
"name": "updateQuestionAction",
"permission": "QUESTION_UPDATE",
"signature": "同上",
"purpose": "更新题目",
"deps": [
"requirePermission",
"data-access.updateQuestionById"
],
"usedBy": [
"question-actions.tsx"
]
},
{
"name": "deleteQuestionAction",
"permission": "QUESTION_DELETE",
"signature": "同上",
"purpose": "删除题目",
"deps": [
"requirePermission",
"data-access.deleteQuestionByIdRecursive"
],
"usedBy": [
"question-actions.tsx"
]
},
{
"name": "getQuestionsAction",
"permission": "QUESTION_READ",
"signature": "(params: GetQuestionsParams) => Promise<...>",
"purpose": "查询题目列表",
"deps": [
"requirePermission",
"data-access.getQuestions"
],
"usedBy": [
"teacher/questions/page.tsx"
]
},
{
"name": "getKnowledgePointOptionsAction",
"permission": "QUESTION_READ",
"signature": "() => Promise<KnowledgePointOption[]>",
"purpose": "获取知识点选项",
"deps": [
"requirePermission",
"data-access.getKnowledgePointOptions"
],
"usedBy": [
"create-question-dialog.tsx"
]
}
],
"dataAccess": [
{
"name": "getQuestions",
"signature": "(params?: GetQuestionsParams) => Promise<{ data: Question[], meta: { page, pageSize, total, totalPages } }>",
"type": "cache function",
"purpose": "查询题目列表(缓存)",
"usedBy": [
"getQuestionsAction",
"teacher/questions/page.tsx"
]
},
{
"name": "getQuestionsDashboardStats",
"signature": "() => Promise<QuestionsDashboardStats>",
"purpose": "获取题目仪表盘统计数据(题目总数)",
"usedBy": [
"dashboard/data-access.getAdminDashboardData"
]
},
{
"name": "createQuestionWithRelations",
"signature": "(input: CreateQuestionInput) => Promise<{ questionId: string }>",
"purpose": "创建题目含嵌套子题目、知识点关联P1-2 新增,从 actions 下沉)",
"usedBy": [
"createNestedQuestion"
]
},
{
"name": "updateQuestionById",
"signature": "(questionId: string, input: Partial<CreateQuestionInput>) => Promise<void>",
"purpose": "按ID更新题目P1-2 新增,从 actions 下沉)",
"usedBy": [
"updateQuestionAction"
]
},
{
"name": "deleteQuestionByIdRecursive",
"signature": "(questionId: string) => Promise<void>",
"purpose": "递归删除题目含子题目、知识点关联P1-2 新增,从 actions 下沉)",
"usedBy": [
"deleteQuestionAction"
]
},
{
"name": "getKnowledgePointOptions",
"signature": "() => Promise<KnowledgePointOption[]>",
"purpose": "获取知识点选项(含章节/教材信息P1-2 新增,从 actions 下沉)",
"usedBy": [
"getKnowledgePointOptionsAction"
]
},
{
"name": "GetQuestionsParams",
"type": "type",
"definition": "{ q?, page?, pageSize?, ids?, knowledgePointId?, type?, difficulty? }",
"usedBy": [
"getQuestions",
"getQuestionsAction"
]
}
],
"schema": [
{
"name": "QuestionTypeEnum",
"type": "const",
"description": "zod enum: z.enum([\"single_choice\", \"multiple_choice\", \"text\", \"judgment\", \"composite\"])",
"usedBy": [
"CreateQuestionSchema",
"questions/components"
]
},
{
"name": "BaseQuestionSchema",
"type": "const",
"description": "zod schema 基础题目校验",
"usedBy": [
"CreateQuestionSchema"
]
},
{
"name": "CreateQuestionInput",
"type": "type",
"definition": "z.infer<typeof CreateQuestionSchema>",
"usedBy": [
"createNestedQuestion",
"create-question-dialog.tsx"
]
},
{
"name": "CreateQuestionSchema",
"type": "const",
"description": "zod schema 创建题目(递归支持嵌套)",
"usedBy": [
"createNestedQuestion",
"create-question-dialog.tsx"
]
}
],
"types": [
{
"name": "Question",
"definition": "{ id, content, type, difficulty, createdAt, updatedAt, author, knowledgePoints, childrenCount? }",
"usedBy": [
"exams (题目选择)",
"homework (作业题目)"
]
},
{
"name": "KnowledgePointOption",
"definition": "{ id, name, chapterId, chapterTitle, textbookId, textbookTitle, subject, grade }",
"usedBy": [
"create-question-dialog.tsx"
]
},
{
"name": "QuestionType",
"type": "type",
"definition": "z.infer<typeof QuestionTypeEnum>",
"usedBy": [
"questions/components",
"exams/components"
]
}
],
"components": [
{
"name": "CreateQuestionButton",
"file": "create-question-button.tsx",
"purpose": "创建题目按钮"
},
{
"name": "CreateQuestionDialog",
"file": "create-question-dialog.tsx",
"purpose": "创建题目对话框"
},
{
"name": "QuestionActions",
"file": "question-actions.tsx",
"purpose": "题目操作按钮"
},
{
"name": "columns",
"file": "question-columns.tsx",
"type": "ColumnDef[]",
"purpose": "题目表格列定义"
},
{
"name": "QuestionDataTable",
"file": "question-data-table.tsx",
"purpose": "题目数据表格"
},
{
"name": "QuestionFilters",
"file": "question-filters.tsx",
"purpose": "题目筛选器"
}
]
}
},
"textbooks": {
"path": "src/modules/textbooks",
"description": "教材与知识体系:教材/章节树形结构、知识点CRUD、Markdown内容编辑、知识图谱",
"exports": {
"actions": [
{
"name": "createTextbookAction",
"permission": "TEXTBOOK_CREATE",
"signature": "(prevState, formData) => Promise<ActionState>",
"purpose": "创建教材"
},
{
"name": "updateTextbookAction",
"permission": "TEXTBOOK_UPDATE",
"signature": "(textbookId, prevState, formData) => Promise<ActionState>",
"purpose": "更新教材元信息"
},
{
"name": "deleteTextbookAction",
"permission": "TEXTBOOK_DELETE",
"signature": "(textbookId) => Promise<ActionState>",
"purpose": "删除教材"
},
{
"name": "createChapterAction",
"permission": "TEXTBOOK_CREATE",
"signature": "(textbookId, parentId?, prevState, formData) => Promise<ActionState>",
"purpose": "创建章节"
},
{
"name": "updateChapterContentAction",
"permission": "TEXTBOOK_UPDATE",
"signature": "(chapterId, content, textbookId) => Promise<ActionState>",
"purpose": "更新章节内容(Markdown)"
},
{
"name": "deleteChapterAction",
"permission": "TEXTBOOK_DELETE",
"signature": "(chapterId, textbookId) => Promise<ActionState>",
"purpose": "删除章节"
},
{
"name": "createKnowledgePointAction",
"permission": "TEXTBOOK_CREATE",
"signature": "(chapterId, textbookId, prevState, formData) => Promise<ActionState>",
"purpose": "创建知识点"
},
{
"name": "updateKnowledgePointAction",
"permission": "TEXTBOOK_UPDATE",
"signature": "(kpId, textbookId, prevState, formData) => Promise<ActionState>",
"purpose": "更新知识点"
},
{
"name": "deleteKnowledgePointAction",
"permission": "TEXTBOOK_DELETE",
"signature": "(kpId, textbookId) => Promise<ActionState>",
"purpose": "删除知识点"
},
{
"name": "reorderChaptersAction",
"permission": "TEXTBOOK_UPDATE",
"signature": "(chapterId, newIndex, parentId, textbookId) => Promise<ActionState>",
"purpose": "章节排序"
}
],
"dataAccess": [
{
"name": "getTextbooks",
"signature": "(query?, subject?, grade?) => Promise<Textbook[]>",
"usedBy": [
"teacher/textbooks/page.tsx"
]
},
{
"name": "getTextbookById",
"signature": "(id) => Promise<Textbook | undefined>",
"usedBy": [
"teacher/textbooks/[id]/page.tsx"
]
},
{
"name": "getChaptersByTextbookId",
"signature": "(textbookId) => Promise<Chapter[]>",
"usedBy": [
"textbook-reader.tsx"
]
},
{
"name": "getKnowledgePointsByChapterId",
"signature": "(chapterId) => Promise<KnowledgePoint[]>",
"usedBy": [
"textbook-reader.tsx"
]
},
{
"name": "getKnowledgePointsByTextbookId",
"signature": "(textbookId) => Promise<KnowledgePoint[]>",
"usedBy": [
"textbook-reader.tsx"
]
},
{
"name": "createTextbook",
"signature": "(input: CreateTextbookInput) => Promise<Textbook>",
"purpose": "创建教材",
"usedBy": [
"createTextbookAction"
]
},
{
"name": "updateTextbook",
"signature": "(id: string, input: UpdateTextbookInput) => Promise<void>",
"purpose": "更新教材",
"usedBy": [
"updateTextbookAction"
]
},
{
"name": "deleteTextbook",
"signature": "(id: string) => Promise<void>",
"purpose": "删除教材",
"usedBy": [
"deleteTextbookAction"
]
},
{
"name": "createChapter",
"signature": "(input: CreateChapterInput) => Promise<Chapter>",
"purpose": "创建章节",
"usedBy": [
"createChapterAction"
]
},
{
"name": "updateChapterContent",
"signature": "(input: UpdateChapterContentInput) => Promise<void>",
"purpose": "更新章节内容",
"usedBy": [
"updateChapterContentAction"
]
},
{
"name": "deleteChapter",
"signature": "(chapterId: string, textbookId: string) => Promise<void>",
"purpose": "删除章节",
"usedBy": [
"deleteChapterAction"
]
},
{
"name": "createKnowledgePoint",
"signature": "(input: CreateKnowledgePointInput) => Promise<KnowledgePoint>",
"purpose": "创建知识点",
"usedBy": [
"createKnowledgePointAction"
]
},
{
"name": "updateKnowledgePoint",
"signature": "(input: UpdateKnowledgePointInput) => Promise<void>",
"purpose": "更新知识点",
"usedBy": [
"updateKnowledgePointAction"
]
},
{
"name": "deleteKnowledgePoint",
"signature": "(kpId: string, textbookId: string) => Promise<void>",
"purpose": "删除知识点",
"usedBy": [
"deleteKnowledgePointAction"
]
},
{
"name": "reorderChapters",
"signature": "(chapterId: string, newIndex: number, parentId: string | null, textbookId: string) => Promise<void>",
"purpose": "章节排序",
"usedBy": [
"reorderChaptersAction"
]
},
{
"name": "getTextbooksDashboardStats",
"signature": "() => Promise<TextbooksDashboardStats>",
"purpose": "获取教材仪表盘统计数据(教材总数、章节总数)",
"usedBy": [
"dashboard/data-access.getAdminDashboardData"
]
}
],
"hooks": [
{
"name": "useTextSelection",
"file": "hooks/use-text-selection.ts",
"signature": "() => { selectedText, setSelectedText, selectionRef, contentRef, setCreateDialogOpen, setIsCreating, createDialogOpen, isCreating, handleContentPointerDown, handleContextMenuChange }",
"purpose": "文本选区Hook(无参数)",
"usedBy": [
"textbook-content-panel.tsx"
]
},
{
"name": "useKnowledgePointActions",
"file": "hooks/use-knowledge-point-actions.ts",
"signature": "(textbookId, selectedChapterId, selectedChapterTextbookId, highlightedKpId, setHighlightedKpId, onKpCreated?) => { editingKp, setEditingKp, editKpDialogOpen, setEditKpDialogOpen, isUpdatingKp, questionDialogOpen, setQuestionDialogOpen, targetKpForQuestion, setTargetKpForQuestion, deleteConfirmOpen, setDeleteConfirmOpen, handleCreateKnowledgePoint, requestDeleteKP, confirmDeleteKP, handleUpdateKP }",
"purpose": "知识点操作Hook(6参数)",
"usedBy": [
"textbook-reader.tsx"
]
}
],
"types": [
{
"name": "Chapter",
"definition": "{ id, textbookId, title, order, parentId, content?, children? }",
"usedBy": [
"textbooks/components",
"questions (知识点关联)"
]
},
{
"name": "KnowledgePoint",
"definition": "{ id, name, description?, anchorText?, parentId?, chapterId?, level, order }",
"usedBy": [
"textbooks/components",
"questions/types"
]
},
{
"name": "Textbook",
"type": "type",
"definition": "{ id, title, subject, grade, publisher, createdAt, updatedAt }",
"usedBy": [
"textbooks/components",
"textbooks/data-access"
]
},
{
"name": "CreateTextbookInput",
"type": "type",
"definition": "创建教材输入",
"usedBy": [
"createTextbook",
"createTextbookAction"
]
},
{
"name": "UpdateTextbookInput",
"type": "type",
"definition": "更新教材输入",
"usedBy": [
"updateTextbook",
"updateTextbookAction"
]
},
{
"name": "CreateChapterInput",
"type": "type",
"definition": "创建章节输入",
"usedBy": [
"createChapter",
"createChapterAction"
]
},
{
"name": "UpdateChapterContentInput",
"type": "type",
"definition": "更新章节内容输入",
"usedBy": [
"updateChapterContent",
"updateChapterContentAction"
]
},
{
"name": "CreateKnowledgePointInput",
"type": "type",
"definition": "创建知识点输入",
"usedBy": [
"createKnowledgePoint",
"createKnowledgePointAction"
]
},
{
"name": "UpdateKnowledgePointInput",
"type": "type",
"definition": "更新知识点输入",
"usedBy": [
"updateKnowledgePoint",
"updateKnowledgePointAction"
]
}
],
"components": [
{
"name": "ChapterSidebarList",
"purpose": "章节侧边栏列表"
},
{
"name": "CreateChapterDialog",
"purpose": "创建章节对话框"
},
{
"name": "CreateKnowledgePointDialog",
"purpose": "创建知识点对话框"
},
{
"name": "KnowledgeGraph",
"purpose": "知识图谱可视化"
},
{
"name": "KnowledgePointDialogs",
"purpose": "知识点对话框集合"
},
{
"name": "KnowledgePointList",
"purpose": "知识点列表"
},
{
"name": "KnowledgePointPanel",
"purpose": "知识点面板"
},
{
"name": "TextbookCard",
"purpose": "教材卡片"
},
{
"name": "TextbookContentPanel",
"purpose": "教材内容面板"
},
{
"name": "TextbookFilters",
"purpose": "教材筛选器"
},
{
"name": "TextbookFormDialog",
"purpose": "教材表单对话框"
},
{
"name": "TextbookReader",
"purpose": "教材阅读器"
},
{
"name": "TextbookSettingsDialog",
"purpose": "教材设置对话框"
}
]
}
},
"classes": {
"path": "src/modules/classes",
"description": "班级管理班级CRUD、学生注册/退班、邀请码、课表、学科教师分配",
"exports": {
"actions": [
{
"name": "createTeacherClassAction",
"permission": "CLASS_CREATE",
"signature": "(prevState, formData) => Promise<ActionState<string>>",
"purpose": "教师创建班级"
},
{
"name": "updateTeacherClassAction",
"permission": "CLASS_UPDATE",
"signature": "(classId, prevState, formData) => Promise<ActionState>",
"purpose": "教师更新班级"
},
{
"name": "deleteTeacherClassAction",
"permission": "CLASS_DELETE",
"signature": "(classId) => Promise<ActionState>",
"purpose": "教师删除班级"
},
{
"name": "createGradeClassAction",
"permission": "CLASS_CREATE",
"signature": "(prevState, formData) => Promise<ActionState<string>>",
"purpose": "年级主任创建班级"
},
{
"name": "updateGradeClassAction",
"permission": "CLASS_UPDATE",
"signature": "(classId, prevState, formData) => Promise<ActionState>",
"purpose": "年级主任更新班级"
},
{
"name": "deleteGradeClassAction",
"permission": "CLASS_DELETE",
"signature": "(classId) => Promise<ActionState>",
"purpose": "年级主任删除班级"
},
{
"name": "enrollStudentByEmailAction",
"permission": "CLASS_ENROLL",
"signature": "(classId, prevState, formData) => Promise<ActionState>",
"purpose": "通过邮箱注册学生"
},
{
"name": "joinClassByInvitationCodeAction",
"permission": "CLASS_ENROLL",
"signature": "(prevState, formData) => Promise<ActionState<{classId:string}>>",
"purpose": "通过邀请码加入"
},
{
"name": "ensureClassInvitationCodeAction",
"permission": "CLASS_ENROLL",
"signature": "(classId) => Promise<ActionState<{code:string}>>",
"purpose": "确保邀请码存在"
},
{
"name": "regenerateClassInvitationCodeAction",
"permission": "CLASS_ENROLL",
"signature": "(classId) => Promise<ActionState<{code:string}>>",
"purpose": "重新生成邀请码"
},
{
"name": "setStudentEnrollmentStatusAction",
"permission": "CLASS_ENROLL",
"signature": "(classId, studentId, status) => Promise<ActionState>",
"purpose": "设置学生状态"
},
{
"name": "createClassScheduleItemAction",
"permission": "CLASS_SCHEDULE",
"signature": "(prevState, formData) => Promise<ActionState<string>>",
"purpose": "创建课表项"
},
{
"name": "updateClassScheduleItemAction",
"permission": "CLASS_SCHEDULE",
"signature": "(scheduleId, prevState, formData) => Promise<ActionState>",
"purpose": "更新课表项"
},
{
"name": "deleteClassScheduleItemAction",
"permission": "CLASS_SCHEDULE",
"signature": "(scheduleId) => Promise<ActionState>",
"purpose": "删除课表项"
},
{
"name": "createAdminClassAction",
"permission": "CLASS_CREATE",
"signature": "(prevState, formData) => Promise<ActionState<string>>",
"purpose": "管理员创建班级"
},
{
"name": "updateAdminClassAction",
"permission": "CLASS_UPDATE",
"signature": "(classId, prevState, formData) => Promise<ActionState>",
"purpose": "管理员更新班级"
},
{
"name": "deleteAdminClassAction",
"permission": "CLASS_DELETE",
"signature": "(classId) => Promise<ActionState>",
"purpose": "管理员删除班级"
}
],
"dataAccess": [
{
"name": "getTeacherClasses",
"signature": "(params?: { teacherId?: string }) => Promise<TeacherClass[]>",
"usedBy": [
"teacher/classes/my",
"dashboard"
]
},
{
"name": "getAdminClasses",
"signature": "() => Promise<AdminClassListItem[]>",
"usedBy": [
"admin班级管理"
]
},
{
"name": "getGradeManagedClasses",
"signature": "(userId) => Promise<AdminClassListItem[]>",
"usedBy": [
"grade_head班级管理"
]
},
{
"name": "getStudentClasses",
"signature": "(studentId) => Promise<StudentEnrolledClass[]>",
"usedBy": [
"student/dashboard"
]
},
{
"name": "getStudentSchedule",
"signature": "(studentId) => Promise<StudentScheduleItem[]>",
"usedBy": [
"student课表"
]
},
{
"name": "getClassStudents",
"signature": "(params?: { classId?, q?, status?, teacherId? }) => Promise<ClassStudent[]>",
"usedBy": [
"teacher/classes/students"
]
},
{
"name": "getClassSchedule",
"signature": "(params?: { classId?, teacherId? }) => Promise<ClassScheduleItem[]>",
"usedBy": [
"teacher/classes/schedule"
]
},
{
"name": "getClassHomeworkInsights",
"signature": "(params: { classId, teacherId?, limit? }) => Promise<ClassHomeworkInsights | null>",
"usedBy": [
"classes作业洞察"
]
},
{
"name": "getGradeHomeworkInsights",
"signature": "(params: { gradeId, limit? }) => Promise<GradeHomeworkInsights | null>",
"usedBy": [
"年级作业洞察"
]
},
{
"name": "getTeacherIdForMutations",
"signature": "() => Promise<string>",
"purpose": "获取当前教师ID(用于写操作)",
"usedBy": [
"classes写操作内部"
]
},
{
"name": "getClassSubjects",
"signature": "(classId: string) => Promise<ClassSubject[]>",
"purpose": "获取班级学科列表",
"usedBy": [
"class-detail组件"
]
},
{
"name": "getTeacherOptions",
"signature": "() => Promise<TeacherOption[]>",
"purpose": "获取教师选项列表",
"usedBy": [
"class-detail组件"
]
},
{
"name": "getTeacherTeachingSubjects",
"signature": "(teacherId: string) => Promise<...>",
"purpose": "获取教师所教学科",
"usedBy": [
"classes内部"
]
},
{
"name": "getManagedGrades",
"signature": "(userId: string) => Promise<...>",
"purpose": "获取所管年级",
"usedBy": [
"grade_head视图"
]
},
{
"name": "getStudentsSubjectScores",
"signature": "(...) => Promise<...>",
"purpose": "获取学生学科成绩",
"usedBy": [
"classes内部"
]
},
{
"name": "getClassStudentSubjectScoresV2",
"signature": "(...) => Promise<...>",
"purpose": "获取班级学生学科成绩V2",
"usedBy": [
"classes内部"
]
},
{
"name": "getClassesDashboardStats",
"signature": "() => Promise<ClassesDashboardStats>",
"purpose": "获取班级仪表盘统计数据(班级总数)",
"usedBy": [
"dashboard/data-access.getAdminDashboardData"
]
},
{
"name": "createTeacherClass",
"signature": "(input) => Promise<string>",
"purpose": "教师创建班级",
"usedBy": [
"createTeacherClassAction"
]
},
{
"name": "createAdminClass",
"signature": "(input) => Promise<string>",
"purpose": "管理员创建班级",
"usedBy": [
"createAdminClassAction"
]
},
{
"name": "ensureClassInvitationCode",
"signature": "(classId: string) => Promise<{ code: string }>",
"purpose": "确保邀请码存在",
"usedBy": [
"ensureClassInvitationCodeAction"
]
},
{
"name": "regenerateClassInvitationCode",
"signature": "(classId: string) => Promise<{ code: string }>",
"purpose": "重新生成邀请码",
"usedBy": [
"regenerateClassInvitationCodeAction"
]
},
{
"name": "enrollStudentByInvitationCode",
"signature": "(code: string, studentId: string) => Promise<{ classId: string }>",
"purpose": "通过邀请码注册学生",
"usedBy": [
"joinClassByInvitationCodeAction"
]
},
{
"name": "enrollTeacherByInvitationCode",
"signature": "(code: string, teacherId: string) => Promise<...>",
"purpose": "通过邀请码注册教师",
"usedBy": [
"classes内部"
]
},
{
"name": "updateTeacherClass",
"signature": "(classId: string, input) => Promise<void>",
"purpose": "教师更新班级",
"usedBy": [
"updateTeacherClassAction"
]
},
{
"name": "updateAdminClass",
"signature": "(classId: string, input) => Promise<void>",
"purpose": "管理员更新班级",
"usedBy": [
"updateAdminClassAction"
]
},
{
"name": "setClassSubjectTeachers",
"signature": "(classId: string, assignments: ClassSubjectTeacherAssignment[]) => Promise<void>",
"purpose": "设置班级学科教师",
"usedBy": [
"classes内部"
]
},
{
"name": "deleteTeacherClass",
"signature": "(classId: string) => Promise<void>",
"purpose": "教师删除班级",
"usedBy": [
"deleteTeacherClassAction"
]
},
{
"name": "deleteAdminClass",
"signature": "(classId: string) => Promise<void>",
"purpose": "管理员删除班级",
"usedBy": [
"deleteAdminClassAction"
]
},
{
"name": "enrollStudentByEmail",
"signature": "(classId: string, email: string) => Promise<void>",
"purpose": "通过邮箱注册学生",
"usedBy": [
"enrollStudentByEmailAction"
]
},
{
"name": "setStudentEnrollmentStatus",
"signature": "(classId: string, studentId: string, status: string) => Promise<void>",
"purpose": "设置学生状态",
"usedBy": [
"setStudentEnrollmentStatusAction"
]
},
{
"name": "verifyTeacherOwnsClass",
"signature": "(classId: string, teacherId: string) => Promise<boolean>",
"purpose": "校验教师是否拥有班级P0-5 新增,供 scheduling 模块跨模块校验 classSchedule 写权限)",
"usedBy": [
"scheduling/data-access-class-schedule.createClassScheduleItem",
"scheduling/data-access-class-schedule.updateClassScheduleItem",
"scheduling/data-access-class-schedule.deleteClassScheduleItem"
]
},
{
"name": "getStudentIdsByClassId",
"signature": "(classId: string) => Promise<string[]>",
"purpose": "获取班级所有学生 ID跨模块接口",
"usedBy": [
"notifications/actions.sendClassNotificationAction"
]
},
{
"name": "getStudentIdsByClassIds",
"signature": "(classIds: string[]) => Promise<string[]>",
"purpose": "获取多个班级的所有学生 ID跨模块接口",
"usedBy": [
"homework/data-access",
"homework/stats-service"
]
},
{
"name": "getActiveStudentIdsByClassId",
"signature": "(classId: string) => Promise<string[]>",
"purpose": "获取班级所有活跃学生 ID跨模块接口",
"usedBy": [
"homework/data-access",
"homework/stats-service",
"grades/data-access",
"diagnostic/data-access"
]
},
{
"name": "getTeacherSubjectIdsByClass",
"signature": "(classId: string, teacherId: string) => Promise<string[]>",
"purpose": "获取教师在班级所教科目标识(跨模块接口)",
"usedBy": [
"homework/data-access-write"
]
},
{
"name": "getStudentActiveClassId",
"signature": "(studentId: string) => Promise<string | null>",
"purpose": "获取学生当前活跃班级 ID跨模块接口",
"usedBy": [
"homework/stats-service",
"grades/data-access-ranking",
"parent/data-access"
]
},
{
"name": "getStudentActiveGradeId",
"signature": "(studentId: string) => Promise<string | null>",
"purpose": "获取学生当前活跃班级对应的年级 ID跨模块接口",
"usedBy": [
"elective/data-access-selections.getStudentGradeId"
]
},
{
"name": "getClassExists",
"signature": "(classId: string) => Promise<boolean>",
"purpose": "校验班级是否存在(跨模块接口)",
"usedBy": [
"notifications/actions.sendClassNotificationAction",
"grades/data-access",
"diagnostic/data-access"
]
},
{
"name": "getClassNameById",
"signature": "(classId: string) => Promise<string | null>",
"purpose": "获取班级名称(跨模块接口)",
"usedBy": [
"grades/data-access",
"grades/data-access-analytics",
"grades/export",
"parent/data-access",
"diagnostic/data-access"
]
},
{
"name": "getClassGradeId",
"signature": "(classId: string) => Promise<string | null>",
"purpose": "获取班级关联的年级 ID跨模块接口",
"usedBy": [
"classes/actions.updateGradeClassAction",
"classes/actions.deleteGradeClassAction"
]
},
{
"name": "getGradeIdsByClassIds",
"signature": "(classIds: string[]) => Promise<string[]>",
"purpose": "获取多个班级关联的年级 ID 列表(跨模块接口)",
"usedBy": [
"homework/stats-service"
]
},
{
"name": "getClassNamesByIds",
"signature": "(classIds: string[]) => Promise<Map<string, string>>",
"purpose": "批量获取班级名称(跨模块接口)",
"usedBy": [
"grades/data-access"
]
},
{
"name": "getClassesByGradeId",
"signature": "(gradeId: string) => Promise<Array<{ id: string; name: string }>>",
"purpose": "获取指定年级下的所有班级(跨模块接口)",
"usedBy": [
"grades/data-access-analytics"
]
}
],
"schema": [
{
"name": "CreateTeacherClassSchema",
"type": "const",
"file": "schema.ts",
"description": "zod schema 教师创建班级name, grade 必填schoolName/schoolId/gradeId/homeroom/room 可选)",
"usedBy": [
"actions.createTeacherClassAction"
]
},
{
"name": "UpdateTeacherClassSchema",
"type": "const",
"file": "schema.ts",
"description": "zod schema 教师更新班级classId 必填,其余可选)",
"usedBy": [
"actions.updateTeacherClassAction"
]
},
{
"name": "DeleteTeacherClassSchema",
"type": "const",
"file": "schema.ts",
"description": "zod schema 教师删除班级classId",
"usedBy": [
"actions.deleteTeacherClassAction"
]
},
{
"name": "CreateAdminClassSchema",
"type": "const",
"file": "schema.ts",
"description": "zod schema 管理员创建班级name, grade, teacherId 必填,其余可选)",
"usedBy": [
"actions.createAdminClassAction"
]
},
{
"name": "UpdateAdminClassSchema",
"type": "const",
"file": "schema.ts",
"description": "zod schema 管理员更新班级classId 必填,其余可选)",
"usedBy": [
"actions.updateAdminClassAction"
]
},
{
"name": "DeleteAdminClassSchema",
"type": "const",
"file": "schema.ts",
"description": "zod schema 管理员删除班级classId",
"usedBy": [
"actions.deleteAdminClassAction"
]
},
{
"name": "CreateGradeClassSchema",
"type": "const",
"file": "schema.ts",
"description": "zod schema 年级主任创建班级name, gradeId, teacherId 必填,其余可选)",
"usedBy": [
"actions.createGradeClassAction"
]
},
{
"name": "UpdateGradeClassSchema",
"type": "const",
"file": "schema.ts",
"description": "zod schema 年级主任更新班级classId 必填,其余可选)",
"usedBy": [
"actions.updateGradeClassAction"
]
},
{
"name": "DeleteGradeClassSchema",
"type": "const",
"file": "schema.ts",
"description": "zod schema 年级主任删除班级classId",
"usedBy": [
"actions.deleteGradeClassAction"
]
},
{
"name": "CreateClassScheduleItemSchema",
"type": "const",
"file": "schema.ts",
"description": "zod schema 创建课表项classId, weekday 1-7, course, startTime, endTime 必填location 可选)",
"usedBy": [
"actions.createClassScheduleItemAction"
]
},
{
"name": "UpdateClassScheduleItemSchema",
"type": "const",
"file": "schema.ts",
"description": "zod schema 更新课表项scheduleId 必填,其余可选)",
"usedBy": [
"actions.updateClassScheduleItemAction"
]
},
{
"name": "DeleteClassScheduleItemSchema",
"type": "const",
"file": "schema.ts",
"description": "zod schema 删除课表项scheduleId",
"usedBy": [
"actions.deleteClassScheduleItemAction"
]
},
{
"name": "EnrollStudentByEmailSchema",
"type": "const",
"file": "schema.ts",
"description": "zod schema 通过邮箱注册学生classId, email",
"usedBy": [
"actions.enrollStudentByEmailAction"
]
}
],
"types": [
{
"name": "TeacherClass",
"type": "type",
"definition": "教师班级对象",
"usedBy": [
"getTeacherClasses",
"dashboard"
]
},
{
"name": "AssignmentSummary",
"type": "type",
"definition": "作业摘要",
"usedBy": [
"classes/components"
]
},
{
"name": "TeacherOption",
"type": "type",
"definition": "教师选项",
"usedBy": [
"getTeacherOptions",
"class-detail组件"
]
},
{
"name": "DEFAULT_CLASS_SUBJECTS",
"type": "const",
"definition": "默认班级学科列表",
"usedBy": [
"classes内部"
]
},
{
"name": "ClassSubject",
"type": "type",
"definition": "班级学科",
"usedBy": [
"getClassSubjects",
"classes/components"
]
},
{
"name": "ClassSubjectTeacherAssignment",
"type": "type",
"definition": "班级学科教师分配",
"usedBy": [
"setClassSubjectTeachers"
]
},
{
"name": "AdminClassListItem",
"type": "type",
"definition": "管理员班级列表项",
"usedBy": [
"getAdminClasses",
"getGradeManagedClasses"
]
},
{
"name": "CreateTeacherClassInput",
"type": "type",
"definition": "教师创建班级输入",
"usedBy": [
"createTeacherClass"
]
},
{
"name": "UpdateTeacherClassInput",
"type": "type",
"definition": "教师更新班级输入",
"usedBy": [
"updateTeacherClass"
]
},
{
"name": "ClassStudent",
"type": "type",
"definition": "班级学生",
"usedBy": [
"getClassStudents",
"classes/components"
]
},
{
"name": "ClassScheduleItem",
"type": "type",
"definition": "班级课表项",
"usedBy": [
"getClassSchedule",
"classes/components"
]
},
{
"name": "StudentEnrolledClass",
"type": "type",
"definition": "学生已注册班级",
"usedBy": [
"getStudentClasses",
"dashboard"
]
},
{
"name": "StudentScheduleItem",
"type": "type",
"definition": "学生课表项",
"usedBy": [
"getStudentSchedule",
"dashboard"
]
},
{
"name": "ScoreStats",
"type": "type",
"definition": "成绩统计",
"usedBy": [
"classes/components"
]
},
{
"name": "ClassHomeworkAssignmentStats",
"type": "type",
"definition": "班级作业统计",
"usedBy": [
"classes/components"
]
},
{
"name": "ClassHomeworkInsights",
"type": "type",
"definition": "班级作业洞察",
"usedBy": [
"getClassHomeworkInsights",
"classes/components"
]
},
{
"name": "GradeHomeworkClassSummary",
"type": "type",
"definition": "年级作业班级汇总",
"usedBy": [
"classes/components"
]
},
{
"name": "GradeHomeworkInsights",
"type": "type",
"definition": "年级作业洞察",
"usedBy": [
"getGradeHomeworkInsights",
"classes/components"
]
}
],
"components": [
{
"name": "StudentsTable",
"purpose": "学生表格"
},
{
"name": "StudentsFilters",
"purpose": "学生筛选器"
},
{
"name": "ScheduleView",
"purpose": "课表视图"
},
{
"name": "MyClassesGrid",
"purpose": "我的班级网格"
},
{
"name": "AdminClassesClient",
"purpose": "管理员班级客户端"
},
{
"name": "ScheduleFilters",
"purpose": "课表筛选器"
},
{
"name": "GradeClassesClient",
"purpose": "年级班级客户端"
},
{
"name": "transformAssignmentsToChartData",
"file": "class-detail/transformAssignmentsToChartData",
"type": "function",
"purpose": "转换作业为图表数据"
},
{
"name": "ClassSubmissionTrendChart",
"file": "class-detail/ClassSubmissionTrendChart",
"purpose": "班级提交趋势图表"
},
{
"name": "ClassTrendsWidget",
"file": "class-detail/ClassTrendsWidget",
"purpose": "班级趋势组件"
},
{
"name": "ClassStudentsWidget",
"file": "class-detail/ClassStudentsWidget",
"purpose": "班级学生组件"
},
{
"name": "ClassScheduleGrid",
"file": "class-detail/ClassScheduleGrid",
"purpose": "班级课表网格"
},
{
"name": "ClassScheduleWidget",
"file": "class-detail/ClassScheduleWidget",
"purpose": "班级课表组件"
},
{
"name": "ClassQuickActions",
"file": "class-detail/ClassQuickActions",
"purpose": "班级快捷操作"
},
{
"name": "EditClassDialog",
"file": "class-detail/EditClassDialog",
"purpose": "编辑班级对话框"
},
{
"name": "ClassOverviewStats",
"file": "class-detail/ClassOverviewStats",
"purpose": "班级概览统计"
},
{
"name": "ClassHeader",
"file": "class-detail/ClassHeader",
"purpose": "班级头部"
},
{
"name": "ClassAssignmentsWidget",
"file": "class-detail/ClassAssignmentsWidget",
"purpose": "班级作业组件"
}
]
},
"files": [
{
"path": "data-access.ts",
"lines": 548,
"description": "核心班级 CRUD + 邀请码 + 教师班级管理(含 re-export 向后兼容)",
"exports": [
"getTeacherClasses",
"getTeacherOptions",
"getTeacherTeachingSubjects",
"createTeacherClass",
"ensureClassInvitationCode",
"regenerateClassInvitationCode",
"enrollStudentByInvitationCode",
"enrollTeacherByInvitationCode",
"updateTeacherClass",
"setClassSubjectTeachers",
"deleteTeacherClass",
"enrollStudentByEmail",
"setStudentEnrollmentStatus",
"getSessionTeacherId",
"getTeacherIdForMutations",
"getAccessibleClassIdsForTeacher",
"getClassGradeIdsByClassIds",
"getTeacherSubjectIdsForClass",
"getClassSubjects",
"compareClassLike",
"isDuplicateInvitationCodeError",
"generateUniqueInvitationCode"
]
},
{
"path": "data-access-stats.ts",
"lines": 513,
"description": "作业统计查询(班级/年级作业洞察,通过 homework/data-access-classes 获取数据)",
"exports": [
"getClassHomeworkInsights",
"getGradeHomeworkInsights",
"getClassesDashboardStats"
]
},
{
"path": "data-access-schedule.ts",
"lines": 93,
"description": "课表查询(学生/班级课表只读P0-5 已修复:写函数已迁移至 scheduling/data-access-class-schedule.ts",
"exports": [
"getStudentSchedule",
"getClassSchedule"
]
},
{
"path": "data-access-students.ts",
"lines": 253,
"description": "学生相关查询(科目成绩、学生名单、学生班级,通过 homework/data-access-classes 获取数据)",
"exports": [
"getStudentsSubjectScores",
"getClassStudentSubjectScoresV2",
"getStudentClasses",
"getClassStudents"
]
},
{
"path": "data-access-admin.ts",
"lines": 406,
"description": "管理员班级管理(管理员班级 CRUD、年级管理班级查询",
"exports": [
"getAdminClasses",
"getGradeManagedClasses",
"getManagedGrades",
"createAdminClass",
"updateAdminClass",
"deleteAdminClass"
]
}
]
},
"school": {
"path": "src/modules/school",
"description": "学校基础数据管理学校、年级、部门、学年的CRUD。actions 层仅做编排(权限校验+Zod+revalidatePath+after(logAudit)),所有 DB 操作下沉至 data-access。学校CRUD actions 在写操作成功后通过 Next.js after() 异步调用 logAudit() 记录操作日志",
"exports": {
"actions": [
{
"name": "createSchoolAction",
"permission": "SCHOOL_MANAGE",
"signature": "(prevState, formData) => Promise<ActionState<string>>",
"purpose": "创建学校",
"auditLog": "school.create"
},
{
"name": "updateSchoolAction",
"permission": "SCHOOL_MANAGE",
"signature": "(schoolId, prevState, formData) => Promise<ActionState<string>>",
"purpose": "更新学校",
"auditLog": "school.update"
},
{
"name": "deleteSchoolAction",
"permission": "SCHOOL_MANAGE",
"signature": "(schoolId) => Promise<ActionState<string>>",
"purpose": "删除学校",
"auditLog": "school.delete"
},
{
"name": "createGradeAction",
"permission": "GRADE_MANAGE",
"signature": "(prevState, formData) => Promise<ActionState<string>>",
"purpose": "创建年级"
},
{
"name": "updateGradeAction",
"permission": "GRADE_MANAGE",
"signature": "(gradeId, prevState, formData) => Promise<ActionState<string>>",
"purpose": "更新年级"
},
{
"name": "deleteGradeAction",
"permission": "GRADE_MANAGE",
"signature": "(gradeId) => Promise<ActionState<string>>",
"purpose": "删除年级"
},
{
"name": "createDepartmentAction",
"permission": "SCHOOL_MANAGE",
"signature": "(prevState, formData) => Promise<ActionState<string>>",
"purpose": "创建部门"
},
{
"name": "updateDepartmentAction",
"permission": "SCHOOL_MANAGE",
"signature": "(departmentId, prevState, formData) => Promise<ActionState<string>>",
"purpose": "更新部门"
},
{
"name": "deleteDepartmentAction",
"permission": "SCHOOL_MANAGE",
"signature": "(departmentId) => Promise<ActionState<string>>",
"purpose": "删除部门"
},
{
"name": "createAcademicYearAction",
"permission": "SCHOOL_MANAGE",
"signature": "(prevState, formData) => Promise<ActionState<string>>",
"purpose": "创建学年"
},
{
"name": "updateAcademicYearAction",
"permission": "SCHOOL_MANAGE",
"signature": "(academicYearId, prevState, formData) => Promise<ActionState<string>>",
"purpose": "更新学年"
},
{
"name": "deleteAcademicYearAction",
"permission": "SCHOOL_MANAGE",
"signature": "(academicYearId) => Promise<ActionState<string>>",
"purpose": "删除学年"
}
],
"dataAccess": [
{
"name": "getSchools",
"signature": "() => Promise<SchoolListItem[]>",
"usedBy": [
"admin学校管理",
"onboarding"
]
},
{
"name": "getGrades",
"signature": "() => Promise<GradeListItem[]>",
"usedBy": [
"admin年级管理",
"exams",
"onboarding"
]
},
{
"name": "getDepartments",
"signature": "() => Promise<DepartmentListItem[]>",
"usedBy": [
"admin部门管理"
]
},
{
"name": "getAcademicYears",
"signature": "() => Promise<AcademicYearListItem[]>",
"usedBy": [
"admin学年管理"
]
},
{
"name": "getStaffOptions",
"signature": "() => Promise<StaffOption[]>",
"usedBy": [
"school组件"
]
},
{
"name": "getGradesForStaff",
"signature": "(staffId) => Promise<GradeListItem[]>",
"usedBy": [
"grade_head视图"
]
},
{
"name": "createDepartment",
"signature": "(data: DepartmentInsertData) => Promise<void>",
"usedBy": [
"createDepartmentAction"
]
},
{
"name": "updateDepartment",
"signature": "(id: string, data: DepartmentUpdateData) => Promise<void>",
"usedBy": [
"updateDepartmentAction"
]
},
{
"name": "deleteDepartment",
"signature": "(id: string) => Promise<void>",
"usedBy": [
"deleteDepartmentAction"
]
},
{
"name": "createSchool",
"signature": "(data: SchoolInsertData) => Promise<void>",
"usedBy": [
"createSchoolAction"
]
},
{
"name": "updateSchool",
"signature": "(id: string, data: SchoolUpdateData) => Promise<void>",
"usedBy": [
"updateSchoolAction"
]
},
{
"name": "deleteSchool",
"signature": "(id: string) => Promise<void>",
"usedBy": [
"deleteSchoolAction"
]
},
{
"name": "createGrade",
"signature": "(data: GradeInsertData) => Promise<void>",
"usedBy": [
"createGradeAction"
]
},
{
"name": "updateGrade",
"signature": "(id: string, data: GradeUpdateData) => Promise<void>",
"usedBy": [
"updateGradeAction"
]
},
{
"name": "deleteGrade",
"signature": "(id: string) => Promise<void>",
"usedBy": [
"deleteGradeAction"
]
},
{
"name": "createAcademicYear",
"signature": "(data: AcademicYearInsertData) => Promise<void>",
"usedBy": [
"createAcademicYearAction"
]
},
{
"name": "updateAcademicYear",
"signature": "(id: string, data: AcademicYearUpdateData) => Promise<void>",
"usedBy": [
"updateAcademicYearAction"
]
},
{
"name": "deleteAcademicYear",
"signature": "(id: string) => Promise<void>",
"usedBy": [
"deleteAcademicYearAction"
]
},
{
"name": "getSubjectOptions",
"signature": "() => Promise<SubjectOption[]>",
"purpose": "获取科目选项列表(跨模块接口)",
"usedBy": [
"homework/data-access",
"grades/data-access",
"grades/data-access-analytics",
"grades/export",
"elective/data-access-selections"
]
},
{
"name": "getGradeOptions",
"signature": "() => Promise<GradeOption[]>",
"purpose": "获取年级选项列表(跨模块接口)",
"usedBy": [
"grades/data-access",
"grades/export",
"parent/data-access",
"elective/data-access-selections"
]
},
{
"name": "isGradeHead",
"signature": "(gradeId: string, userId: string) => Promise<boolean>",
"purpose": "校验用户是否为指定年级的年级主任(跨模块接口)",
"usedBy": [
"classes/actions.createTeacherClassAction"
]
},
{
"name": "isGradeManager",
"signature": "(gradeId: string, userId: string) => Promise<boolean>",
"purpose": "校验用户是否为指定年级的年级主任或教学主任(跨模块接口)",
"usedBy": [
"classes/actions.createGradeClassAction",
"classes/actions.updateGradeClassAction",
"classes/actions.deleteGradeClassAction"
]
},
{
"name": "findGradeIdByHeadAndName",
"signature": "(userId: string, gradeName: string) => Promise<string | null>",
"purpose": "根据年级名称查找用户担任年级主任的年级 ID跨模块接口",
"usedBy": [
"classes/actions.createTeacherClassAction"
]
}
],
"schema": [
{
"name": "UpsertDepartmentSchema",
"type": "const",
"description": "zod schema 部门upsert",
"usedBy": [
"createDepartmentAction",
"updateDepartmentAction"
]
},
{
"name": "UpsertAcademicYearSchema",
"type": "const",
"description": "zod schema 学年upsert",
"usedBy": [
"createAcademicYearAction",
"updateAcademicYearAction"
]
},
{
"name": "UpsertSchoolSchema",
"type": "const",
"description": "zod schema 学校upsert",
"usedBy": [
"createSchoolAction",
"updateSchoolAction"
]
},
{
"name": "UpsertGradeSchema",
"type": "const",
"description": "zod schema 年级upsert",
"usedBy": [
"createGradeAction",
"updateGradeAction"
]
}
],
"types": [
{
"name": "DepartmentListItem",
"type": "type",
"definition": "部门列表项",
"usedBy": [
"getDepartments",
"school/components"
]
},
{
"name": "AcademicYearListItem",
"type": "type",
"definition": "学年列表项",
"usedBy": [
"getAcademicYears",
"school/components"
]
},
{
"name": "SchoolListItem",
"type": "type",
"definition": "学校列表项",
"usedBy": [
"getSchools",
"school/components"
]
},
{
"name": "StaffOption",
"type": "type",
"definition": "员工选项",
"usedBy": [
"getStaffOptions",
"school/components"
]
},
{
"name": "GradeListItem",
"type": "type",
"definition": "年级列表项",
"usedBy": [
"getGrades",
"school/components",
"exams"
]
},
{
"name": "DepartmentInsertData",
"type": "type",
"definition": "部门创建入参",
"usedBy": [
"createDepartment"
]
},
{
"name": "DepartmentUpdateData",
"type": "type",
"definition": "部门更新入参",
"usedBy": [
"updateDepartment"
]
},
{
"name": "SchoolInsertData",
"type": "type",
"definition": "学校创建入参",
"usedBy": [
"createSchool"
]
},
{
"name": "SchoolUpdateData",
"type": "type",
"definition": "学校更新入参",
"usedBy": [
"updateSchool"
]
},
{
"name": "GradeInsertData",
"type": "type",
"definition": "年级创建入参",
"usedBy": [
"createGrade"
]
},
{
"name": "GradeUpdateData",
"type": "type",
"definition": "年级更新入参",
"usedBy": [
"updateGrade"
]
},
{
"name": "AcademicYearInsertData",
"type": "type",
"definition": "学年创建入参",
"usedBy": [
"createAcademicYear"
]
},
{
"name": "AcademicYearUpdateData",
"type": "type",
"definition": "学年更新入参",
"usedBy": [
"updateAcademicYear"
]
}
],
"components": [
{
"name": "SchoolsClient",
"purpose": "学校管理客户端"
},
{
"name": "GradesClient",
"purpose": "年级管理客户端"
},
{
"name": "DepartmentsClient",
"purpose": "部门管理客户端"
},
{
"name": "AcademicYearClient",
"purpose": "学年管理客户端"
}
]
}
},
"dashboard": {
"path": "src/modules/dashboard",
"description": "各角色仪表盘数据聚合与展示",
"exports": {
"dataAccess": [
{
"name": "getAdminDashboardData",
"signature": "(scope?: DataScope) => Promise<AdminDashboardData>",
"deps": [
"users/data-access.getUsersDashboardStats",
"classes/data-access.getClassesDashboardStats",
"textbooks/data-access.getTextbooksDashboardStats",
"questions/data-access.getQuestionsDashboardStats",
"exams/data-access.getExamsDashboardStats",
"homework/stats-service.getHomeworkDashboardStats",
"DataScope"
],
"usedBy": [
"admin/dashboard/page.tsx"
]
}
],
"types": [
{
"name": "StudentDashboardProps",
"definition": "{ studentName, enrolledClassCount, dueSoonCount, overdueCount, gradedCount, todayScheduleItems, upcomingAssignments, grades }",
"deps": [
"homework/types.StudentDashboardGradeProps"
],
"usedBy": [
"student-dashboard-view.tsx"
]
},
{
"name": "TeacherDashboardData",
"definition": "{ classes, schedule, assignments, submissions, teacherName, gradeTrends }",
"deps": [
"homework/data-access.getTeacherGradeTrends",
"classes/data-access.getTeacherClasses"
],
"usedBy": [
"teacher-dashboard-view.tsx"
]
},
{
"name": "AdminDashboardData",
"definition": "{ activeSessionsCount, userCount, userRoleCounts, classCount, ... }",
"usedBy": [
"admin/dashboard/page.tsx"
]
},
{
"name": "AdminDashboardUserRoleCount",
"type": "type",
"definition": "管理员仪表盘用户角色计数",
"usedBy": [
"admin-dashboard/AdminDashboardView"
]
},
{
"name": "AdminDashboardRecentUser",
"type": "type",
"definition": "管理员仪表盘最近用户",
"usedBy": [
"admin-dashboard/AdminDashboardView"
]
},
{
"name": "StudentTodayScheduleItem",
"type": "type",
"definition": "学生今日课表项",
"usedBy": [
"student-dashboard/StudentTodayScheduleCard"
]
},
{
"name": "TeacherTodayScheduleItem",
"type": "type",
"definition": "教师今日课表项",
"usedBy": [
"teacher-dashboard/TeacherSchedule"
]
}
],
"components": [
{
"name": "AdminDashboardView",
"file": "admin-dashboard/AdminDashboardView",
"purpose": "管理员仪表盘视图"
},
{
"name": "StudentDashboard",
"file": "student-dashboard/StudentDashboard",
"purpose": "学生仪表盘(注意:非StudentDashboardView)"
},
{
"name": "StudentDashboardHeader",
"file": "student-dashboard/StudentDashboardHeader",
"purpose": "学生仪表盘头部"
},
{
"name": "StudentGradesCard",
"file": "student-dashboard/StudentGradesCard",
"purpose": "学生成绩卡片"
},
{
"name": "StudentStatsGrid",
"file": "student-dashboard/StudentStatsGrid",
"purpose": "学生统计网格"
},
{
"name": "StudentTodayScheduleCard",
"file": "student-dashboard/StudentTodayScheduleCard",
"purpose": "学生今日课表卡片"
},
{
"name": "StudentUpcomingAssignmentsCard",
"file": "student-dashboard/StudentUpcomingAssignmentsCard",
"purpose": "学生即将到来作业卡片"
},
{
"name": "TeacherDashboardView",
"file": "teacher-dashboard/TeacherDashboardView",
"purpose": "教师仪表盘视图"
},
{
"name": "TeacherClassesCard",
"file": "teacher-dashboard/TeacherClassesCard",
"purpose": "教师班级卡片"
},
{
"name": "TeacherDashboardHeader",
"file": "teacher-dashboard/TeacherDashboardHeader",
"purpose": "教师仪表盘头部"
},
{
"name": "TeacherGradeTrends",
"file": "teacher-dashboard/TeacherGradeTrends",
"purpose": "教师年级趋势"
},
{
"name": "TeacherHomeworkCard",
"file": "teacher-dashboard/TeacherHomeworkCard",
"purpose": "教师作业卡片"
},
{
"name": "TeacherQuickActions",
"file": "teacher-dashboard/TeacherQuickActions",
"purpose": "教师快捷操作"
},
{
"name": "TeacherSchedule",
"file": "teacher-dashboard/TeacherSchedule",
"purpose": "教师课表"
},
{
"name": "TeacherStats",
"file": "teacher-dashboard/TeacherStats",
"purpose": "教师统计"
},
{
"name": "RecentSubmissions",
"file": "teacher-dashboard/RecentSubmissions",
"purpose": "最近提交"
}
]
}
},
"layout": {
"path": "src/modules/layout",
"description": "应用布局框架:侧边栏、顶栏、导航配置",
"exports": {
"components": [
{
"name": "AppSidebar",
"purpose": "根据权限渲染侧边栏导航",
"internalDeps": [
"usePermission",
"NAV_CONFIG"
]
},
{
"name": "SiteHeader",
"purpose": "顶部导航栏(集成 GlobalSearch 全局搜索Cmd/Ctrl+K 唤起、300ms 防抖、↑/↓ 导航、Enter 跳转NotificationDropdown 通知下拉)",
"internalDeps": [
"useSession",
"signOut",
"shared/components/global-search.GlobalSearch",
"messaging/components/notification-dropdown.NotificationDropdown"
]
},
{
"name": "SidebarProvider",
"props": "{ children, sidebar }",
"purpose": "侧边栏上下文Provider"
},
{
"name": "useSidebar",
"type": "hook",
"purpose": "侧边栏状态Hook"
}
],
"types": [
{
"name": "Role",
"type": "type",
"definition": "\"admin\" | \"teacher\" | \"student\" | \"parent\"",
"usedBy": [
"NAV_CONFIG",
"usePermission"
]
},
{
"name": "NavItem",
"type": "type",
"definition": "{ title, href, icon?, permission? }",
"usedBy": [
"NAV_CONFIG",
"AppSidebar"
]
}
],
"config": [
{
"name": "NAV_CONFIG",
"type": "Record<Role, NavItem[]>",
"note": "每个NavItem含permission字段用于权限过滤。admin角色菜单包含Audit Logs项(icon: ScrollText, href: /admin/audit-logs, permission: AUDIT_LOG_READ)含子项Operation Logs与Login Logs。admin角色菜单的School Management子菜单包含Import Users项(href: /admin/users/import, permission: USER_MANAGE)。admin角色菜单包含Scheduling项(icon: CalendarClock, href: /admin/scheduling/rules, permission: 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: GRADE_RECORD_READ)含子项All Grades(/teacher/grades)、Batch Entry(/teacher/grades/entry, permission: GRADE_RECORD_MANAGE)、Statistics(/teacher/grades/stats)。teacher角色菜单包含Schedule Changes项(icon: CalendarClock, href: /teacher/schedule-changes, permission: SCHEDULE_ADJUST)。teacher角色菜单包含Diagnostic项(icon: Stethoscope, href: /teacher/diagnostic, permission: DIAGNOSTIC_READ)。student角色菜单包含My Grades项(icon: GraduationCap, href: /student/grades, permission: GRADE_RECORD_READ)。student角色菜单包含Diagnostic项(icon: Stethoscope, href: /student/diagnostic, permission: DIAGNOSTIC_READ)。parent角色菜单包含Dashboard项(icon: LayoutDashboard, href: /parent/dashboard无permission字段仅需登录)、Grades项(icon: GraduationCap, href: /parent/grades, permission: GRADE_RECORD_READ)、Announcements项(icon: Megaphone, href: /announcements, permission: ANNOUNCEMENT_READ)"
}
]
}
},
"settings": {
"path": "src/modules/settings",
"description": "系统设置AI Provider配置、用户偏好、密码安全修改密码、强度校验",
"exports": {
"actions": [
{
"name": "getAiProviderSummaries",
"permission": "AI_CONFIGURE",
"signature": "() => Promise<AiProviderSummary[]>",
"purpose": "获取AI Provider列表P1 已修复DB 操作下沉到 data-access.getAiProviderSummaries",
"deps": [
"data-access.getAiProviderSummaries"
]
},
{
"name": "upsertAiProviderAction",
"permission": "AI_CONFIGURE",
"signature": "(data) => Promise<ActionState<string>>",
"purpose": "创建/更新AI ProviderP1 已修复DB 操作下沉到 data-access并行查询优化",
"deps": [
"shared/lib/ai (encrypt/decrypt)",
"data-access.countDefaultAiProviders",
"data-access.getAiProviderForUpdate",
"data-access.updateAiProvider",
"data-access.createAiProvider"
]
},
{
"name": "testAiProviderAction",
"permission": "AI_CONFIGURE",
"signature": "(data) => Promise<ActionState<null>>",
"purpose": "测试AI Provider连通性",
"deps": [
"shared/lib/ai.testAiProviderConfig"
]
},
{
"name": "changePasswordAction",
"file": "actions-password.ts",
"permission": "USER_PROFILE_UPDATE",
"signature": "(prevState: ActionState<null>, formData: FormData) => Promise<ActionState<null>>",
"purpose": "修改当前用户密码Zod 校验 + 校验当前密码 + 新密码策略 + 速率限制 PASSWORD_CHANGE: 5次/分钟 + 并行查询优化)",
"deps": [
"requirePermission",
"validatePassword",
"rateLimit",
"bcryptjs (hash/compare)",
"data-access.getUserPasswordHash",
"data-access.getPasswordSecurityByUserId",
"data-access.updateUserPassword",
"data-access.upsertPasswordSecurityOnPasswordChange"
],
"usedBy": [
"components/password-change-form.tsx"
]
}
],
"dataAccess": [
{
"name": "getAiProviderSummaries",
"signature": "() => Promise<AiProviderSummary[]>",
"file": "data-access.ts",
"purpose": "获取AI Provider列表P1 新增,从 actions 下沉)",
"deps": [
"shared.db",
"shared.db.schema.aiProviders"
]
},
{
"name": "countDefaultAiProviders",
"signature": "() => Promise<number>",
"file": "data-access.ts",
"purpose": "统计默认AI Provider数量P1 新增)",
"deps": [
"shared.db",
"shared.db.schema.aiProviders"
]
},
{
"name": "getAiProviderForUpdate",
"signature": "(id: string) => Promise<AiProviderExisting | null>",
"file": "data-access.ts",
"purpose": "获取AI Provider更新所需字段P1 新增)",
"deps": [
"shared.db",
"shared.db.schema.aiProviders"
]
},
{
"name": "updateAiProvider",
"signature": "(id: string, data: UpdateAiProviderInput, resetOtherDefaults: boolean) => Promise<void>",
"file": "data-access.ts",
"purpose": "更新AI Provider事务P1 新增)",
"deps": [
"shared.db",
"shared.db.schema.aiProviders"
]
},
{
"name": "createAiProvider",
"signature": "(data: CreateAiProviderInput, resetOtherDefaults: boolean) => Promise<void>",
"file": "data-access.ts",
"purpose": "创建AI Provider事务P1 新增)",
"deps": [
"shared.db",
"shared.db.schema.aiProviders"
]
},
{
"name": "getUserPasswordHash",
"signature": "(userId: string) => Promise<{ password: string | null } | null>",
"file": "data-access.ts",
"purpose": "获取用户密码哈希P1 新增,从 actions-password 下沉)",
"deps": [
"shared.db",
"shared.db.schema.users"
]
},
{
"name": "getPasswordSecurityByUserId",
"signature": "(userId: string) => Promise<{ id: string } | null>",
"file": "data-access.ts",
"purpose": "获取用户密码安全记录P1 新增,从 actions-password 下沉)",
"deps": [
"shared.db",
"shared.db.schema.passwordSecurity"
]
},
{
"name": "updateUserPassword",
"signature": "(userId: string, newHash: string, now: Date) => Promise<void>",
"file": "data-access.ts",
"purpose": "更新用户密码P1 新增,从 actions-password 下沉)",
"deps": [
"shared.db",
"shared.db.schema.users"
]
},
{
"name": "upsertPasswordSecurityOnPasswordChange",
"signature": "(userId: string, now: Date, existing: { id: string } | null) => Promise<void>",
"file": "data-access.ts",
"purpose": "密码修改后更新或创建密码安全记录P1 新增,从 actions-password 下沉)",
"deps": [
"shared.db",
"shared.db.schema.passwordSecurity"
]
}
],
"types": [
{
"name": "AiProviderSummary",
"file": "types.ts",
"type": "interface",
"definition": "AI Provider摘要P1 从 actions.ts 迁至 types.ts",
"usedBy": [
"getAiProviderSummaries",
"settings/components",
"exams/components"
]
},
{
"name": "AiProviderName",
"file": "types.ts",
"type": "type",
"definition": "AI Provider名称联合类型P1 新增)",
"usedBy": [
"data-access",
"types.AiProviderSummary"
]
},
{
"name": "AiProviderExisting",
"file": "types.ts",
"type": "interface",
"definition": "AI Provider更新所需字段P1 新增)",
"usedBy": [
"data-access.getAiProviderForUpdate"
]
}
],
"components": [
{
"name": "AiProviderSettingsCard",
"purpose": "AI Provider设置卡片"
},
{
"name": "AdminSettingsView",
"purpose": "管理员设置视图General/Appearance/Security/Notifications tabSecurity 含 PasswordChangeFormNotifications 含 NotificationPreferencesForm"
},
{
"name": "ProfileSettingsForm",
"purpose": "个人资料设置表单"
},
{
"name": "ThemePreferencesCard",
"purpose": "主题偏好卡片"
},
{
"name": "StudentSettingsView",
"purpose": "学生设置视图(含 Notifications tab"
},
{
"name": "TeacherSettingsView",
"purpose": "教师设置视图(含 Notifications tab"
},
{
"name": "PasswordChangeForm",
"purpose": "密码修改表单(当前密码/新密码/确认密码 + 强度指示器 + 需求提示)",
"deps": [
"changePasswordAction",
"getPasswordStrength",
"PASSWORD_REQUIREMENT_HINTS"
]
},
{
"name": "NotificationPreferencesForm",
"file": "components/notification-preferences-form.tsx",
"purpose": "通知偏好设置表单Switch 切换 email/sms/push 通道 + 5 个分类开关:作业/成绩/公告/消息/考勤;隐藏 checkbox 与 Switch 同步useActionState 调用 updateNotificationPreferencesAction",
"deps": [
"updateNotificationPreferencesAction",
"shared/components/ui/switch",
"shared/components/ui/card",
"react.useActionState"
],
"usedBy": [
"AdminSettingsView",
"TeacherSettingsView",
"StudentSettingsView"
]
}
]
}
},
"users": {
"path": "src/modules/users",
"description": "用户个人资料管理 + 用户批量导入/导出Excel",
"exports": {
"actions": [
{
"name": "updateUserProfile",
"signature": "(data: UpdateUserProfileInput) => Promise<void>",
"file": "actions.ts",
"permission": "requireAuth()",
"deps": [
"shared.db",
"shared.db.schema.users",
"shared.lib.auth-guard.requireAuth"
]
},
{
"name": "UpdateUserProfileInput",
"type": "type",
"file": "actions.ts",
"definition": "{ name?, phone?, address?, gender?, age? }"
},
{
"name": "downloadUserTemplateAction",
"signature": "() => Promise<ActionState<string>>",
"file": "actions.ts",
"permission": "USER_MANAGE",
"purpose": "生成用户导入模板(返回 base64 编码的 Excel",
"deps": [
"requirePermission",
"import-export.generateUserImportTemplate"
],
"usedBy": [
"components/user-import-dialog.tsx"
]
},
{
"name": "importUsersAction",
"signature": "(prevState: ActionState<UserImportResult> | null, formData: FormData) => Promise<ActionState<UserImportResult>>",
"file": "actions.ts",
"permission": "USER_MANAGE",
"purpose": "导入用户:接收文件,解析+验证+批量创建(默认密码 123456",
"deps": [
"requirePermission",
"shared.lib.excel.parseExcel",
"import-export.parseUserImportData",
"import-export.batchImportUsers"
],
"usedBy": [
"components/user-import-dialog.tsx"
]
},
{
"name": "exportUsersAction",
"signature": "(role?: string) => Promise<ActionState<{ buffer: string; filename: string }>>",
"file": "actions.ts",
"permission": "USER_MANAGE",
"purpose": "导出用户列表(返回 base64 编码的 Excel",
"deps": [
"requirePermission",
"import-export.exportUsersToExcel"
],
"usedBy": [
"待扩展"
]
}
],
"dataAccess": [
{
"name": "getUserProfile",
"signature": "(userId: string) => Promise<UserProfile | null>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.users"
]
},
{
"name": "getUsersDashboardStats",
"signature": "() => Promise<UsersDashboardStats>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.users",
"shared.db.schema.sessions",
"shared.db.schema.usersToRoles",
"shared.db.schema.roles"
],
"usedBy": [
"dashboard/data-access.getAdminDashboardData"
]
},
{
"name": "getCurrentStudentUser",
"signature": "() => Promise<{ id: string; name: string } | null>",
"file": "data-access.ts",
"purpose": "获取当前已认证的学生用户id + name通过 session + JOIN users/usersToRoles/roles 校验 student 角色",
"deps": [
"auth",
"data-access.getUserWithRole"
],
"usedBy": [
"student/dashboard",
"student/learning/assignments",
"student/learning/assignments/[assignmentId]",
"student/learning/courses",
"student/learning/textbooks",
"student/learning/textbooks/[id]",
"student/schedule"
]
},
{
"name": "UserProfile",
"type": "type",
"file": "data-access.ts",
"definition": "{ id, name, email, image, role, phone, address, gender, age, onboardedAt, createdAt, updatedAt }"
},
{
"name": "UsersDashboardStats",
"type": "type",
"file": "data-access.ts",
"definition": "{ userCount, activeSessionsCount, userRoleCounts, recentUsers }"
}
],
"importExport": [
{
"name": "generateUserImportTemplate",
"signature": "() => Promise<Buffer>",
"file": "import-export.ts",
"purpose": "生成用户导入模板(列:姓名/邮箱/角色/手机/班级邀请码,含示例行)",
"deps": [
"shared.lib.excel.generateTemplate"
],
"usedBy": [
"actions.downloadUserTemplateAction"
]
},
{
"name": "parseUserImportData",
"signature": "(rows: Record<string, unknown>[]) => UserImportValidation",
"file": "import-export.ts",
"purpose": "解析并验证导入行(校验姓名/邮箱格式/角色枚举/邀请码仅 student",
"deps": [],
"usedBy": [
"actions.importUsersAction"
]
},
{
"name": "exportUsersToExcel",
"signature": "(params: { scope: DataScope; role?: string }) => Promise<Buffer>",
"file": "import-export.ts",
"purpose": "导出用户列表到 Excel含姓名/邮箱/手机/性别/年龄/角色/创建时间)",
"deps": [
"shared.db",
"shared.db.schema.users",
"shared.db.schema.roles",
"shared.db.schema.usersToRoles",
"shared.lib.excel.exportToExcel"
],
"usedBy": [
"actions.exportUsersAction",
"app/api/export/route.ts"
]
}
],
"userService": [
{
"name": "batchImportUsers",
"signature": "(records: UserImportRecord[]) => Promise<UserImportResult>",
"file": "user-service.ts",
"purpose": "批量创建用户(默认密码 123456 bcrypt 哈希,自动创建 usersToRolesstudent 通过邀请码自动加入班级——委托 class-registration",
"deps": [
"shared.db",
"shared.db.schema.users",
"shared.db.schema.roles",
"shared.db.schema.usersToRoles",
"bcryptjs",
"@paralleldrive/cuid2",
"class-registration.registerStudentByInvitationCode"
],
"usedBy": [
"actions.importUsersAction",
"import-export.ts (re-export 向后兼容)"
]
}
],
"classRegistration": [
{
"name": "registerStudentByInvitationCode",
"signature": "(studentId: string, invitationCode: string) => Promise<ClassRegistrationResult>",
"file": "class-registration.ts",
"purpose": "通过邀请码将学生注册到班级,委托 classes/data-access.enrollStudentByInvitationCode返回结构化结果不抛异常",
"deps": [
"classes/data-access.enrollStudentByInvitationCode"
],
"usedBy": [
"user-service.batchImportUsers"
]
},
{
"name": "ClassRegistrationResult",
"type": "type",
"file": "class-registration.ts",
"definition": "{ success: boolean; error?: string }"
}
],
"types": [
{
"name": "UserImportRecord",
"type": "type",
"file": "import-export.ts",
"definition": "{ name, email, role, phone?, invitationCode? }",
"usedBy": [
"parseUserImportData",
"batchImportUsers"
]
},
{
"name": "UserImportValidation",
"type": "type",
"file": "import-export.ts",
"definition": "{ valid: UserImportRecord[], invalid: Array<{ row, record, errors }> }",
"usedBy": [
"parseUserImportData"
]
},
{
"name": "UserImportResult",
"type": "type",
"file": "user-service.ts",
"definition": "{ successCount, failedCount, errors: Array<{ row, email, error }> }",
"usedBy": [
"batchImportUsers",
"importUsersAction",
"import-export.ts (re-export 向后兼容)"
]
}
],
"components": [
{
"name": "UserImportDialog",
"file": "components/user-import-dialog.tsx",
"purpose": "用户批量导入对话框4 状态idle/preview/importing/done下载模板→上传预览→确认导入→结果展示",
"usedBy": [
"app/(dashboard)/admin/users/import/page.tsx"
]
}
]
}
},
"audit": {
"path": "src/modules/audit",
"description": "操作日志、登录日志与数据变更日志查询,支持 Excel 导出",
"exports": {
"dataAccess": [
{
"name": "getAuditLogs",
"signature": "(params?: AuditLogQueryParams) => Promise<PaginatedResult<AuditLog>>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.auditLogs"
],
"usedBy": [
"app/(dashboard)/admin/audit-logs/page.tsx"
]
},
{
"name": "getLoginLogs",
"signature": "(params?: LoginLogQueryParams) => Promise<PaginatedResult<LoginLog>>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.loginLogs"
],
"usedBy": [
"app/(dashboard)/admin/audit-logs/login-logs/page.tsx"
]
},
{
"name": "getAuditModuleOptions",
"signature": "() => Promise<string[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.auditLogs"
],
"usedBy": [
"app/(dashboard)/admin/audit-logs/page.tsx"
]
},
{
"name": "getDataChangeLogs",
"signature": "(params?: DataChangeLogQueryParams) => Promise<PaginatedResult<DataChangeLog>>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.dataChangeLogs"
],
"usedBy": [
"getDataChangeLogsAction"
]
},
{
"name": "getDataChangeStats",
"signature": "() => Promise<DataChangeStat[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.dataChangeLogs"
],
"usedBy": [
"getDataChangeLogsAction"
]
},
{
"name": "getDataChangeTableOptions",
"signature": "() => Promise<string[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.dataChangeLogs"
],
"usedBy": [
"getDataChangeLogsAction"
]
},
{
"name": "getDataChangeLogsForExport",
"signature": "(params?: DataChangeLogQueryParams) => Promise<DataChangeLog[]>",
"file": "data-access.ts",
"deps": [
"getDataChangeLogs"
],
"usedBy": [
"exportDataChangeLogsAction"
]
},
{
"name": "getAuditLogsForExport",
"signature": "(params?: AuditLogQueryParams) => Promise<AuditLog[]>",
"file": "data-access.ts",
"deps": [
"getAuditLogs"
],
"usedBy": [
"exportAuditLogsAction"
]
},
{
"name": "getLoginLogsForExport",
"signature": "(params?: LoginLogQueryParams) => Promise<LoginLog[]>",
"file": "data-access.ts",
"deps": [
"getLoginLogs"
],
"usedBy": [
"exportLoginLogsAction"
]
}
],
"actions": [
{
"name": "getDataChangeLogsAction",
"permission": "AUDIT_LOG_READ",
"signature": "(params?: DataChangeLogQueryParams) => Promise<ActionState<{ items, total, page, pageSize, totalPages, tableOptions, stats }>>",
"file": "actions.ts",
"purpose": "获取数据变更日志(分页结果 + tableOptions + stats 三者并行加载)",
"deps": [
"requirePermission",
"data-access.getDataChangeLogs",
"data-access.getDataChangeTableOptions",
"data-access.getDataChangeStats"
],
"usedBy": [
"待扩展"
]
},
{
"name": "exportAuditLogsAction",
"permission": "AUDIT_LOG_READ",
"signature": "(params?: AuditLogQueryParams) => Promise<ActionState<{ buffer: Buffer; filename: string }>>",
"file": "actions.ts",
"purpose": "导出操作日志为 Excel",
"deps": [
"requirePermission",
"data-access.getAuditLogsForExport",
"shared.lib.excel.exportToExcel"
],
"usedBy": [
"待扩展"
]
},
{
"name": "exportLoginLogsAction",
"permission": "AUDIT_LOG_READ",
"signature": "(params?: LoginLogQueryParams) => Promise<ActionState<{ buffer: Buffer; filename: string }>>",
"file": "actions.ts",
"purpose": "导出登录日志为 Excel",
"deps": [
"requirePermission",
"data-access.getLoginLogsForExport",
"shared.lib.excel.exportToExcel"
],
"usedBy": [
"待扩展"
]
},
{
"name": "exportDataChangeLogsAction",
"permission": "AUDIT_LOG_READ",
"signature": "(params?: DataChangeLogQueryParams) => Promise<ActionState<{ buffer: Buffer; filename: string }>>",
"file": "actions.ts",
"purpose": "导出数据变更日志为 Excel",
"deps": [
"requirePermission",
"data-access.getDataChangeLogsForExport",
"shared.lib.excel.exportToExcel"
],
"usedBy": [
"待扩展"
]
}
],
"types": [
{
"name": "AuditLog",
"type": "interface",
"file": "types.ts",
"definition": "{ id, userId, userName, action, module, targetId, targetType, detail, ipAddress, userAgent, status, createdAt }"
},
{
"name": "LoginLog",
"type": "interface",
"file": "types.ts",
"definition": "{ id, userId, userEmail, action, status, ipAddress, userAgent, errorMessage, createdAt }"
},
{
"name": "AuditLogQueryParams",
"type": "type",
"file": "types.ts",
"definition": "{ userId?, module?, action?, status?, page?, pageSize?, startDate?, endDate? }"
},
{
"name": "LoginLogQueryParams",
"type": "type",
"file": "types.ts",
"definition": "{ userId?, action?, status?, page?, pageSize?, startDate?, endDate? }"
},
{
"name": "PaginatedResult",
"type": "interface",
"file": "types.ts",
"definition": "{ items: T[], total, page, pageSize, totalPages }"
},
{
"name": "DataChangeAction",
"type": "type",
"file": "types.ts",
"definition": "'create' | 'update' | 'delete'",
"usedBy": [
"data-access",
"shared/lib/change-logger",
"DataChangeLog"
]
},
{
"name": "DataChangeLog",
"type": "interface",
"file": "types.ts",
"definition": "{ id, tableName, recordId, action, oldValue, newValue, changedBy, changedByName, ipAddress, createdAt }",
"usedBy": [
"audit/data-access",
"audit/actions"
]
},
{
"name": "DataChangeStat",
"type": "interface",
"file": "types.ts",
"definition": "{ tableName: string, count: number }",
"usedBy": [
"getDataChangeStats",
"getDataChangeLogsAction"
]
},
{
"name": "DataChangeLogQueryParams",
"type": "type",
"file": "types.ts",
"definition": "{ tableName?, recordId?, action?, userId?, page?, pageSize?, startDate?, endDate? }",
"usedBy": [
"getDataChangeLogs",
"getDataChangeLogsForExport",
"getDataChangeLogsAction",
"exportDataChangeLogsAction"
]
}
],
"components": [
{
"name": "AuditLogTable",
"file": "components/audit-log-table.tsx",
"purpose": "操作日志表格(分页)"
},
{
"name": "AuditLogFilters",
"file": "components/audit-log-filters.tsx",
"purpose": "操作日志筛选器(模块/操作/状态/日期)"
},
{
"name": "AuditLogView",
"file": "components/audit-log-view.tsx",
"purpose": "操作日志视图(筛选+表格+分页)"
},
{
"name": "LoginLogTable",
"file": "components/login-log-table.tsx",
"purpose": "登录日志表格(分页)"
},
{
"name": "LoginLogFilters",
"file": "components/login-log-filters.tsx",
"purpose": "登录日志筛选器(操作/状态/日期)"
},
{
"name": "LoginLogView",
"file": "components/login-log-view.tsx",
"purpose": "登录日志视图(筛选+表格+分页)"
}
]
}
},
"announcements": {
"path": "src/modules/announcements",
"description": "通知公告系统:创建、编辑、发布、归档、删除公告,所有登录用户可查看已发布公告",
"exports": {
"actions": [
{
"name": "createAnnouncementAction",
"permission": "ANNOUNCEMENT_MANAGE",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"purpose": "创建公告(草稿/已发布)",
"deps": [
"requirePermission",
"data-access.insertAnnouncement"
],
"usedBy": [
"announcement-form.tsx"
]
},
{
"name": "updateAnnouncementAction",
"permission": "ANNOUNCEMENT_MANAGE",
"signature": "(id: string, prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"purpose": "更新公告",
"deps": [
"requirePermission",
"data-access.updateAnnouncementById"
],
"usedBy": [
"announcement-form.tsx"
]
},
{
"name": "deleteAnnouncementAction",
"permission": "ANNOUNCEMENT_MANAGE",
"signature": "(id: string) => Promise<ActionState<string>>",
"purpose": "删除公告",
"deps": [
"requirePermission",
"data-access.deleteAnnouncementById"
],
"usedBy": [
"announcement-detail.tsx"
]
},
{
"name": "publishAnnouncementAction",
"permission": "ANNOUNCEMENT_MANAGE",
"signature": "(id: string) => Promise<ActionState<string>>",
"purpose": "发布公告",
"deps": [
"requirePermission",
"data-access.publishAnnouncementById"
],
"usedBy": [
"announcement-detail.tsx"
]
},
{
"name": "archiveAnnouncementAction",
"permission": "ANNOUNCEMENT_MANAGE",
"signature": "(id: string) => Promise<ActionState<string>>",
"purpose": "归档公告",
"deps": [
"requirePermission",
"data-access.archiveAnnouncementById"
],
"usedBy": [
"announcement-detail.tsx"
]
},
{
"name": "getAnnouncementsAction",
"permission": "ANNOUNCEMENT_READ",
"signature": "(params?: GetAnnouncementsParams) => Promise<ActionState<Announcement[]>>",
"purpose": "获取公告列表(所有登录用户可读)",
"deps": [
"requirePermission",
"data-access.getAnnouncements"
],
"usedBy": [
"待扩展"
]
}
],
"dataAccess": [
{
"name": "getAnnouncements",
"signature": "(params?: { status?, type?, page?, pageSize? }) => Promise<Announcement[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.announcements"
],
"usedBy": [
"admin/announcements/page.tsx",
"announcements/page.tsx"
]
},
{
"name": "getAnnouncementById",
"signature": "(id: string) => Promise<Announcement | null>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.announcements"
],
"usedBy": [
"admin/announcements/[id]/page.tsx"
]
},
{
"name": "insertAnnouncement",
"signature": "(input: { title, content, type?, status?, targetGradeId?, targetClassId?, publishedAt?, authorId }) => Promise<{ announcementId: string }>",
"file": "data-access.ts",
"purpose": "插入公告P1-2 新增,从 actions 下沉)",
"deps": [
"shared.db",
"shared.db.schema.announcements"
],
"usedBy": [
"createAnnouncementAction"
]
},
{
"name": "updateAnnouncementById",
"signature": "(id: string, input: Partial<{ title, content, type?, status?, targetGradeId?, targetClassId?, publishedAt? }>) => Promise<void>",
"file": "data-access.ts",
"purpose": "按ID更新公告P1-2 新增,从 actions 下沉)",
"deps": [
"shared.db",
"shared.db.schema.announcements"
],
"usedBy": [
"updateAnnouncementAction"
]
},
{
"name": "deleteAnnouncementById",
"signature": "(id: string) => Promise<void>",
"file": "data-access.ts",
"purpose": "按ID删除公告P1-2 新增,从 actions 下沉)",
"deps": [
"shared.db",
"shared.db.schema.announcements"
],
"usedBy": [
"deleteAnnouncementAction"
]
},
{
"name": "publishAnnouncementById",
"signature": "(id: string) => Promise<void>",
"file": "data-access.ts",
"purpose": "发布公告P1-2 新增,从 actions 下沉)",
"deps": [
"shared.db",
"shared.db.schema.announcements"
],
"usedBy": [
"publishAnnouncementAction"
]
},
{
"name": "archiveAnnouncementById",
"signature": "(id: string) => Promise<void>",
"file": "data-access.ts",
"purpose": "归档公告P1-2 新增,从 actions 下沉)",
"deps": [
"shared.db",
"shared.db.schema.announcements"
],
"usedBy": [
"archiveAnnouncementAction"
]
}
],
"schemas": [
{
"name": "CreateAnnouncementSchema",
"type": "zod",
"file": "schema.ts",
"definition": "{ title, content, type?, status?, targetGradeId?, targetClassId?, publishedAt? }",
"usedBy": [
"createAnnouncementAction"
]
},
{
"name": "UpdateAnnouncementSchema",
"type": "zod",
"file": "schema.ts",
"definition": "{ title, content, type?, status?, targetGradeId?, targetClassId?, publishedAt? }",
"usedBy": [
"updateAnnouncementAction"
]
}
],
"types": [
{
"name": "Announcement",
"type": "interface",
"file": "types.ts",
"definition": "{ id, title, content, type, status, targetGradeId, targetClassId, authorId, authorName, publishedAt, createdAt, updatedAt }",
"usedBy": [
"announcements/components",
"页面"
]
},
{
"name": "AnnouncementListItem",
"type": "type",
"file": "types.ts",
"definition": "= Announcement",
"usedBy": [
"列表页"
]
},
{
"name": "AnnouncementStatus",
"type": "type",
"file": "types.ts",
"definition": "\"draft\" | \"published\" | \"archived\"",
"usedBy": [
"data-access",
"components"
]
},
{
"name": "AnnouncementType",
"type": "type",
"file": "types.ts",
"definition": "\"school\" | \"grade\" | \"class\"",
"usedBy": [
"data-access",
"components"
]
},
{
"name": "GetAnnouncementsParams",
"type": "interface",
"file": "types.ts",
"definition": "{ status?, type?, page?, pageSize? }",
"usedBy": [
"getAnnouncements",
"getAnnouncementsAction"
]
}
],
"components": [
{
"name": "AnnouncementList",
"file": "components/announcement-list.tsx",
"purpose": "公告列表(支持状态筛选)"
},
{
"name": "AnnouncementCard",
"file": "components/announcement-card.tsx",
"purpose": "单条公告卡片"
},
{
"name": "AnnouncementForm",
"file": "components/announcement-form.tsx",
"purpose": "创建/编辑表单"
},
{
"name": "AnnouncementDetail",
"file": "components/announcement-detail.tsx",
"purpose": "详情查看(含发布/归档/删除操作)"
},
{
"name": "AdminAnnouncementsView",
"file": "components/admin-announcements-view.tsx",
"purpose": "管理端公告视图(列表+创建对话框)"
}
]
}
},
"files": {
"path": "src/modules/files",
"description": "文件上传与管理:通过 API 路由处理文件上传(保存到 public/uploads/YYYY-MM/),记录文件元数据到 DB支持按关联资源exam/textbook/question/announcement多态查询、下载与删除",
"exports": {
"dataAccess": [
{
"name": "createFileAttachment",
"signature": "(data: CreateFileAttachmentInput) => Promise<FileAttachment | null>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.fileAttachments"
],
"usedBy": [
"app/api/upload/route.ts"
]
},
{
"name": "getFileAttachment",
"signature": "(id: string) => Promise<FileAttachment | null>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.fileAttachments"
],
"usedBy": [
"app/api/files/[id]/route.ts"
]
},
{
"name": "getFileAttachmentsByTarget",
"signature": "(targetType: string, targetId: string) => Promise<FileAttachment[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.fileAttachments"
],
"usedBy": [
"按关联资源查询文件列表"
]
},
{
"name": "getFileAttachmentsByUploader",
"signature": "(uploaderId: string) => Promise<FileAttachment[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.fileAttachments"
],
"usedBy": [
"按上传者查询文件列表"
]
},
{
"name": "getAllFileAttachments",
"signature": "(limit?: number) => Promise<FileAttachment[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.fileAttachments"
],
"usedBy": [
"app/(dashboard)/admin/files/page.tsx"
]
},
{
"name": "deleteFileAttachment",
"signature": "(id: string) => Promise<boolean>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.fileAttachments"
],
"usedBy": [
"app/api/files/[id]/route.ts"
]
},
{
"name": "deleteFileAttachments",
"signature": "(ids: string[]) => Promise<BatchDeleteResult>",
"file": "data-access.ts",
"purpose": "批量删除文件附件记录(仅删 DB 行,磁盘文件由调用方处理;失败时回退到逐条删除)",
"deps": [
"shared.db",
"shared.db.schema.fileAttachments",
"drizzle-orm.inArray"
],
"usedBy": [
"app/api/files/batch-delete/route.ts"
]
},
{
"name": "getFileAttachmentsWithFilters",
"signature": "(params: FileAttachmentQueryParams) => Promise<FileAttachment[]>",
"file": "data-access.ts",
"purpose": "按 mimeType精确或前缀匹配与 searchoriginalName/filename 模糊匹配)筛选文件列表,支持 limit/offset 分页",
"deps": [
"shared.db",
"shared.db.schema.fileAttachments",
"drizzle-orm.like",
"drizzle-orm.or",
"drizzle-orm.and"
],
"usedBy": [
"app/(dashboard)/admin/files/page.tsx"
]
},
{
"name": "getFileStats",
"signature": "() => Promise<FileStats>",
"file": "data-access.ts",
"purpose": "获取文件统计(总数、总大小、按 mimeType 分组的 count/size",
"deps": [
"shared.db",
"shared.db.schema.fileAttachments",
"drizzle-orm.count",
"drizzle-orm.sql"
],
"usedBy": [
"app/(dashboard)/admin/files/page.tsx"
]
},
{
"name": "getFileAttachmentsByIds",
"signature": "(ids: string[]) => Promise<FileAttachment[]>",
"file": "data-access.ts",
"purpose": "按 ID 列表批量查询文件(用于批量删除前获取磁盘路径)",
"deps": [
"shared.db",
"shared.db.schema.fileAttachments",
"drizzle-orm.inArray"
],
"usedBy": [
"app/api/files/batch-delete/route.ts"
]
}
],
"types": [
{
"name": "FileAttachment",
"type": "interface",
"file": "types.ts",
"definition": "{ id, filename, originalName, mimeType, size, storagePath, url, uploaderId, targetType, targetId, createdAt }",
"usedBy": [
"files/components",
"data-access",
"API 路由"
]
},
{
"name": "FileUploadResult",
"type": "interface",
"file": "types.ts",
"definition": "{ id, url, filename, originalName, size, mimeType }",
"usedBy": [
"app/api/upload/route.ts 响应",
"file-upload.tsx 回调"
]
},
{
"name": "FileTargetType",
"type": "type",
"file": "types.ts",
"definition": "\"exam\" | \"textbook\" | \"question\" | \"announcement\"",
"usedBy": [
"types.FileAttachment.targetType",
"file-upload.tsx"
]
},
{
"name": "CreateFileAttachmentInput",
"type": "interface",
"file": "types.ts",
"definition": "{ id, filename, originalName, mimeType, size, storagePath, url, uploaderId, targetType?, targetId? }",
"usedBy": [
"createFileAttachment"
]
},
{
"name": "FileAttachmentQueryParams",
"type": "interface",
"file": "types.ts",
"definition": "{ mimeType?, search?, limit?, offset? }",
"usedBy": [
"getFileAttachmentsWithFilters"
]
},
{
"name": "FileStats",
"type": "interface",
"file": "types.ts",
"definition": "{ totalCount, totalSize, byType: Array<{ mimeType, count, size }> }",
"usedBy": [
"getFileStats"
]
},
{
"name": "BatchDeleteResult",
"type": "interface",
"file": "types.ts",
"definition": "{ success, deletedCount, failedIds: string[] }",
"usedBy": [
"deleteFileAttachments",
"app/api/files/batch-delete/route.ts"
]
}
],
"components": [
{
"name": "FileUpload",
"file": "components/file-upload.tsx",
"purpose": "文件上传组件(拖拽+点击上传,进度条,文件类型校验,调用 /api/upload"
},
{
"name": "FileList",
"file": "components/file-list.tsx",
"purpose": "文件列表展示(图标、文件名、大小、下载链接、删除按钮)"
},
{
"name": "FilePreview",
"file": "components/file-preview.tsx",
"purpose": "文件预览图片直接预览PDF iframe其他下载"
},
{
"name": "FileIcon",
"file": "components/file-icon.tsx",
"purpose": "根据 MIME 类型显示不同图标与颜色"
},
{
"name": "AdminFilesView",
"file": "components/admin-files-view.tsx",
"purpose": "管理端文件视图(上传+列表+删除)"
}
]
}
},
"grades": {
"path": "src/modules/grades",
"description": "成绩分析模块:成绩录入(单条+批量)、查询(按班级/科目/考试/学期过滤、统计报表均分、中位数、标准差、及格率、优秀率、排名、Excel 导出(成绩明细+统计汇总/班级多科目横向对比)、趋势对比分析(成绩趋势、班级对比、科目对比、分数分布、排名趋势)",
"exports": {
"dataAccess": [
{
"name": "getGradeRecords",
"signature": "(params: GradeQueryParams & { scope: DataScope; currentUserId?: string }) => Promise<GradeRecordListItem[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.gradeRecords",
"shared.db.schema.classes",
"shared.db.schema.classEnrollments",
"shared.db.schema.subjects",
"shared.db.schema.users"
],
"usedBy": [
"grades/actions.getGradeRecordsAction"
]
},
{
"name": "getGradeRecordById",
"signature": "(id: string) => Promise<GradeRecord | null>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.gradeRecords"
],
"usedBy": [
"grades/actions.getGradeRecordByIdAction"
]
},
{
"name": "createGradeRecord",
"signature": "(data: CreateGradeRecordInput, recordedBy: string) => Promise<string>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.gradeRecords"
],
"usedBy": [
"grades/actions.createGradeRecordAction"
]
},
{
"name": "batchCreateGradeRecords",
"signature": "(data: BatchCreateGradeRecordInput, recordedBy: string) => Promise<number>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.gradeRecords"
],
"usedBy": [
"grades/actions.batchCreateGradeRecordsAction"
]
},
{
"name": "updateGradeRecord",
"signature": "(id: string, data: UpdateGradeRecordInput) => Promise<boolean>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.gradeRecords"
],
"usedBy": [
"grades/actions.updateGradeRecordAction"
]
},
{
"name": "deleteGradeRecord",
"signature": "(id: string) => Promise<boolean>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.gradeRecords"
],
"usedBy": [
"grades/actions.deleteGradeRecordAction"
]
},
{
"name": "getClassGradeStats",
"signature": "(classId: string, subjectId?: string, examId?: string) => Promise<GradeStats>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.gradeRecords"
],
"usedBy": [
"grades/data-access.getClassGradeStatsWithMeta"
]
},
{
"name": "getClassGradeStatsWithMeta",
"signature": "(classId: string, subjectId?: string, examId?: string) => Promise<ClassGradeStats>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.gradeRecords",
"shared.db.schema.classes",
"shared.db.schema.subjects"
],
"usedBy": [
"grades/actions.getClassGradeStatsAction"
]
},
{
"name": "getStudentGradeSummary",
"signature": "(studentId: string) => Promise<StudentGradeSummary>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.gradeRecords",
"shared.db.schema.subjects"
],
"usedBy": [
"grades/actions.getStudentGradeSummaryAction"
]
},
{
"name": "getClassRanking",
"signature": "(classId: string, subjectId?: string, examId?: string) => Promise<ClassRankingItem[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.gradeRecords",
"shared.db.schema.users"
],
"usedBy": [
"grades/actions.getClassRankingAction"
]
},
{
"name": "getClassStudentsForEntry",
"signature": "(classId: string) => Promise<{ id: string; name: string }[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.classEnrollments",
"shared.db.schema.users"
],
"usedBy": [
"grades/components/batch-grade-entry"
]
},
{
"name": "getGradeTrend",
"signature": "(params: { studentId; subjectId?; semester?; scope: DataScope }) => Promise<GradeTrendResult>",
"file": "data-access-analytics.ts",
"deps": [
"shared.db",
"shared.db.schema.gradeRecords",
"shared.db.schema.classEnrollments"
],
"usedBy": [
"grades/actions-analytics.getGradeTrendAction",
"teacher/grades/analytics"
]
},
{
"name": "getClassComparison",
"signature": "(params: { gradeId; subjectId; examId?; scope: DataScope }) => Promise<ClassComparisonItem[]>",
"file": "data-access-analytics.ts",
"deps": [
"shared.db",
"shared.db.schema.gradeRecords",
"shared.db.schema.classes"
],
"usedBy": [
"grades/actions-analytics.getClassComparisonAction",
"teacher/grades/analytics"
]
},
{
"name": "getSubjectComparison",
"signature": "(params: { classId; examId?; semester?; scope: DataScope }) => Promise<SubjectComparisonItem[]>",
"file": "data-access-analytics.ts",
"deps": [
"shared.db",
"shared.db.schema.gradeRecords",
"shared.db.schema.subjects"
],
"usedBy": [
"grades/actions-analytics.getSubjectComparisonAction",
"teacher/grades/analytics"
]
},
{
"name": "getGradeDistribution",
"signature": "(params: { classId; subjectId?; examId?; scope: DataScope }) => Promise<GradeDistributionResult>",
"file": "data-access-analytics.ts",
"deps": [
"shared.db",
"shared.db.schema.gradeRecords"
],
"usedBy": [
"grades/actions-analytics.getGradeDistributionAction",
"teacher/grades/analytics"
]
},
{
"name": "getRankingTrend",
"signature": "(studentId: string, subjectId?, semester?) => Promise<RankingTrendResult | null>",
"file": "data-access-ranking.ts",
"deps": [
"shared.db",
"shared.db.schema.gradeRecords",
"shared.db.schema.classEnrollments"
],
"usedBy": [
"grades/actions-analytics.getRankingTrendAction"
]
}
],
"actions": [
{
"name": "createGradeRecordAction",
"signature": "(prevState, formData) => Promise<ActionState<string>>",
"file": "actions.ts",
"permission": "GRADE_RECORD_MANAGE",
"usedBy": [
"grades/components/grade-record-form"
]
},
{
"name": "batchCreateGradeRecordsAction",
"signature": "(prevState, formData) => Promise<ActionState<number>>",
"file": "actions.ts",
"permission": "GRADE_RECORD_MANAGE",
"usedBy": [
"grades/components/batch-grade-entry"
]
},
{
"name": "updateGradeRecordAction",
"signature": "(prevState, formData) => Promise<ActionState<string>>",
"file": "actions.ts",
"permission": "GRADE_RECORD_MANAGE",
"usedBy": [
"grades/components/grade-record-list"
]
},
{
"name": "deleteGradeRecordAction",
"signature": "(prevState, formData) => Promise<ActionState<string>>",
"file": "actions.ts",
"permission": "GRADE_RECORD_MANAGE",
"usedBy": [
"grades/components/grade-record-list"
]
},
{
"name": "getGradeRecordsAction",
"signature": "(params) => Promise<GradeRecordListItem[]>",
"file": "actions.ts",
"permission": "GRADE_RECORD_READ",
"usedBy": [
"teacher/grades/page"
]
},
{
"name": "getClassGradeStatsAction",
"signature": "(classId, subjectId?, examId?) => Promise<ClassGradeStats>",
"file": "actions.ts",
"permission": "GRADE_RECORD_READ",
"usedBy": [
"teacher/grades/stats/page"
]
},
{
"name": "getStudentGradeSummaryAction",
"signature": "(studentId?) => Promise<StudentGradeSummary>",
"file": "actions.ts",
"permission": "GRADE_RECORD_READ",
"usedBy": [
"student/grades/page",
"parent/grades/page"
]
},
{
"name": "getClassRankingAction",
"signature": "(classId, subjectId?, examId?) => Promise<ClassRankingItem[]>",
"file": "actions.ts",
"permission": "GRADE_RECORD_READ",
"usedBy": [
"teacher/grades/stats/page"
]
},
{
"name": "getGradeRecordByIdAction",
"signature": "(id) => Promise<GradeRecord | null>",
"file": "actions.ts",
"permission": "GRADE_RECORD_READ",
"usedBy": [
"grades/components/grade-record-list"
]
},
{
"name": "exportGradesAction",
"signature": "(params: { classId: string; subjectId?: string; examId?: string; reportType?: \"detail\" | \"class\" }) => Promise<ActionState<{ buffer: string; filename: string }>>",
"file": "actions.ts",
"permission": "GRADE_RECORD_READ",
"purpose": "导出成绩到 Exceldetail=成绩明细+统计汇总class=班级多科目横向对比总表),返回 base64 buffer",
"deps": [
"requirePermission",
"export.exportGradeRecordsToExcel",
"export.exportClassGradeReportToExcel",
"export.formatDateForFile"
],
"usedBy": [
"grades/components/export-button.tsx"
]
},
{
"name": "getGradeTrendAction",
"signature": "(params) => Promise<GradeTrendResult>",
"file": "actions-analytics.ts",
"permission": "GRADE_RECORD_READ",
"purpose": "获取成绩趋势(按学生/科目/学期,返回归一化分数趋势点)",
"usedBy": [
"teacher/grades/analytics"
]
},
{
"name": "getClassComparisonAction",
"signature": "(params) => Promise<ClassComparisonItem[]>",
"file": "actions-analytics.ts",
"permission": "GRADE_RECORD_READ",
"purpose": "获取班级对比(同年级各班的均分/及格率/优秀率)",
"usedBy": [
"teacher/grades/analytics"
]
},
{
"name": "getSubjectComparisonAction",
"signature": "(params) => Promise<SubjectComparisonItem[]>",
"file": "actions-analytics.ts",
"permission": "GRADE_RECORD_READ",
"purpose": "获取科目对比(同班级各科目雷达图数据)",
"usedBy": [
"teacher/grades/analytics"
]
},
{
"name": "getGradeDistributionAction",
"signature": "(params) => Promise<GradeDistributionResult>",
"file": "actions-analytics.ts",
"permission": "GRADE_RECORD_READ",
"purpose": "获取分数分布90-100/80-89/70-79/60-69/<60 各区间人数)",
"usedBy": [
"teacher/grades/analytics"
]
},
{
"name": "getRankingTrendAction",
"signature": "(studentId, subjectId?, semester?) => Promise<RankingTrendResult | null>",
"file": "actions-analytics.ts",
"permission": "GRADE_RECORD_READ",
"purpose": "获取排名趋势(学生历次考试排名变化,含 DataScope 二次校验)",
"usedBy": [
"待扩展"
]
}
],
"schemas": [
{
"name": "CreateGradeRecordSchema",
"type": "ZodSchema",
"file": "schema.ts",
"usedBy": [
"createGradeRecordAction"
]
},
{
"name": "BatchCreateGradeRecordSchema",
"type": "ZodSchema",
"file": "schema.ts",
"usedBy": [
"batchCreateGradeRecordsAction"
]
},
{
"name": "UpdateGradeRecordSchema",
"type": "ZodSchema",
"file": "schema.ts",
"usedBy": [
"updateGradeRecordAction"
]
}
],
"types": [
{
"name": "GradeRecord",
"type": "interface",
"file": "types.ts",
"usedBy": [
"data-access",
"actions"
]
},
{
"name": "GradeRecordListItem",
"type": "interface",
"file": "types.ts",
"usedBy": [
"data-access",
"actions",
"components/grade-record-list"
]
},
{
"name": "GradeStats",
"type": "interface",
"file": "types.ts",
"definition": "{ count, average, median, stdDev, passRate, excellentRate, maxScore, minScore }",
"usedBy": [
"data-access",
"components/grade-stats-card"
]
},
{
"name": "ClassGradeStats",
"type": "interface",
"file": "types.ts",
"usedBy": [
"data-access",
"actions",
"components/class-grade-report"
]
},
{
"name": "StudentGradeSummary",
"type": "interface",
"file": "types.ts",
"usedBy": [
"data-access",
"actions",
"components/student-grade-summary"
]
},
{
"name": "ClassRankingItem",
"type": "interface",
"file": "types.ts",
"usedBy": [
"data-access",
"actions",
"components/class-grade-report"
]
},
{
"name": "GradeRecordType",
"type": "type",
"file": "types.ts",
"definition": "\"exam\" | \"quiz\" | \"assignment\" | \"monthly\" | \"midterm\" | \"final\"",
"usedBy": [
"types.GradeRecord.type"
]
},
{
"name": "GradeRecordSemester",
"type": "type",
"file": "types.ts",
"definition": "\"1\" | \"2\"",
"usedBy": [
"types.GradeRecord.semester"
]
},
{
"name": "GradeQueryParams",
"type": "interface",
"file": "types.ts",
"usedBy": [
"data-access.getGradeRecords"
]
},
{
"name": "GradeTrendPoint",
"type": "interface",
"file": "types.ts",
"definition": "{ date, title, score, fullScore, normalizedScore, type }",
"usedBy": [
"data-access-analytics.getGradeTrend",
"grade-trend-chart"
]
},
{
"name": "GradeTrendResult",
"type": "interface",
"file": "types.ts",
"definition": "{ label, points: GradeTrendPoint[], averageScore }",
"usedBy": [
"data-access-analytics.getGradeTrend",
"grade-trend-chart"
]
},
{
"name": "ClassComparisonItem",
"type": "interface",
"file": "types.ts",
"definition": "{ classId, className, averageScore, passRate, excellentRate, studentCount }",
"usedBy": [
"data-access-analytics.getClassComparison",
"class-comparison-chart"
]
},
{
"name": "SubjectComparisonItem",
"type": "interface",
"file": "types.ts",
"definition": "{ subjectId, subjectName, averageScore, passRate, excellentRate }",
"usedBy": [
"data-access-analytics.getSubjectComparison",
"subject-comparison-chart"
]
},
{
"name": "GradeDistributionBucket",
"type": "interface",
"file": "types.ts",
"definition": "{ label, min, max, count, percentage }",
"usedBy": [
"data-access-analytics.getGradeDistribution",
"grade-distribution-chart"
]
},
{
"name": "GradeDistributionResult",
"type": "interface",
"file": "types.ts",
"definition": "{ buckets: GradeDistributionBucket[], totalCount }",
"usedBy": [
"data-access-analytics.getGradeDistribution",
"grade-distribution-chart"
]
},
{
"name": "RankingTrendPoint",
"type": "interface",
"file": "types.ts",
"definition": "{ title, date, rank, totalStudents, score }",
"usedBy": [
"data-access-ranking.getRankingTrend"
]
},
{
"name": "RankingTrendResult",
"type": "interface",
"file": "types.ts",
"definition": "{ studentName, points: RankingTrendPoint[] }",
"usedBy": [
"data-access-ranking.getRankingTrend"
]
}
],
"importExport": [
{
"name": "exportGradeRecordsToExcel",
"signature": "(params: { classId: string; subjectId?: string; examId?: string; scope: DataScope }) => Promise<Buffer>",
"file": "export.ts",
"purpose": "导出成绩单Sheet1 成绩明细Sheet2 统计汇总:均分/中位数/最高分/最低分/标准差/及格率/优秀率/参考人数)",
"deps": [
"shared.lib.excel.exportToExcel",
"data-access.getGradeRecords",
"data-access.getClassGradeStats"
],
"usedBy": [
"actions.exportGradesAction",
"app/api/export/route.ts"
]
},
{
"name": "exportClassGradeReportToExcel",
"signature": "(params: { classId: string; scope: DataScope }) => Promise<Buffer>",
"file": "export.ts",
"purpose": "导出班级成绩总表(多科目横向对比,含总分/平均分/排名列)",
"deps": [
"shared.db",
"shared.db.schema.classes",
"shared.db.schema.subjects",
"shared.db.schema.gradeRecords",
"shared.db.schema.users",
"shared.lib.excel.exportToExcel",
"data-access.getGradeRecords"
],
"usedBy": [
"actions.exportGradesAction"
]
},
{
"name": "formatDateForFile",
"signature": "(d?: Date) => string",
"file": "export.ts",
"purpose": "格式化日期为 YYYY-MM-DD 用于文件名",
"deps": [],
"usedBy": [
"actions.exportGradesAction"
]
}
],
"components": [
{
"name": "GradeRecordForm",
"file": "components/grade-record-form.tsx",
"purpose": "单条成绩录入表单(选择学生、班级、科目、考试,输入分数、满分、类型、学期、备注)"
},
{
"name": "BatchGradeEntry",
"file": "components/batch-grade-entry.tsx",
"purpose": "批量录入界面(选择班级+科目+考试,表格形式录入每个学生分数)"
},
{
"name": "GradeRecordList",
"file": "components/grade-record-list.tsx",
"purpose": "成绩列表(含查询筛选、删除对话框)"
},
{
"name": "GradeStatsCard",
"file": "components/grade-stats-card.tsx",
"purpose": "统计卡片(均分、中位数、标准差、及格率、优秀率、最高分、最低分)"
},
{
"name": "GradeQueryFilters",
"file": "components/grade-query-filters.tsx",
"purpose": "查询筛选器(班级、科目、考试类型、学期)"
},
{
"name": "StudentGradeSummary",
"file": "components/student-grade-summary.tsx",
"purpose": "学生成绩汇总视图(按科目分组展示成绩趋势)"
},
{
"name": "ClassGradeReport",
"file": "components/class-grade-report.tsx",
"purpose": "班级成绩报表(含统计+排名)"
},
{
"name": "ExportButton",
"file": "components/export-button.tsx",
"purpose": "成绩导出按钮DropdownMenu 选择 detail/class 报表类型,调用 exportGradesAction 并触发浏览器下载)",
"usedBy": [
"teacher/grades/page.tsx",
"teacher/grades/stats/page.tsx"
]
},
{
"name": "GradeTrendChart",
"file": "components/grade-trend-chart.tsx",
"purpose": "成绩趋势折线图recharts LineChart归一化分数 0-100",
"deps": [
"recharts",
"shared/components/ui/chart"
]
},
{
"name": "ClassComparisonChart",
"file": "components/class-comparison-chart.tsx",
"purpose": "班级对比柱状图recharts BarChart均分/及格率/优秀率)",
"deps": [
"recharts",
"shared/components/ui/chart"
]
},
{
"name": "SubjectComparisonChart",
"file": "components/subject-comparison-chart.tsx",
"purpose": "科目对比雷达图recharts RadarChart",
"deps": [
"recharts",
"shared/components/ui/chart"
]
},
{
"name": "GradeDistributionChart",
"file": "components/grade-distribution-chart.tsx",
"purpose": "分数分布柱状图recharts BarChart彩色区间 90-100/80-89/70-79/60-69/<60",
"deps": [
"recharts",
"shared/components/ui/chart"
]
}
]
}
},
"course-plans": {
"path": "src/modules/course-plans",
"description": "课程计划管理:创建、编辑、删除课程计划(含周计划条目),管理员可管理全部,教师/学生/年级主任/教务主任可查看",
"exports": {
"actions": [
{
"name": "createCoursePlanAction",
"permission": "COURSE_PLAN_MANAGE",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"purpose": "创建课程计划",
"deps": [
"requirePermission",
"shared/db",
"data-access.createCoursePlan"
],
"usedBy": [
"course-plan-form.tsx"
]
},
{
"name": "updateCoursePlanAction",
"permission": "COURSE_PLAN_MANAGE",
"signature": "(id: string, prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"purpose": "更新课程计划",
"deps": [
"requirePermission",
"shared/db",
"data-access.updateCoursePlan"
],
"usedBy": [
"course-plan-form.tsx"
]
},
{
"name": "deleteCoursePlanAction",
"permission": "COURSE_PLAN_MANAGE",
"signature": "(id: string) => Promise<ActionState<string>>",
"purpose": "删除课程计划",
"deps": [
"requirePermission",
"shared/db",
"data-access.deleteCoursePlan"
],
"usedBy": [
"course-plan-detail.tsx"
]
},
{
"name": "getCoursePlansAction",
"permission": "COURSE_PLAN_READ",
"signature": "(params?: GetCoursePlansParams) => Promise<ActionState<CoursePlanListItem[]>>",
"purpose": "获取课程计划列表",
"deps": [
"requirePermission",
"data-access.getCoursePlans"
],
"usedBy": [
"待扩展"
]
},
{
"name": "getCoursePlanAction",
"permission": "COURSE_PLAN_READ",
"signature": "(id: string) => Promise<ActionState<CoursePlanWithItems>>",
"purpose": "获取课程计划详情(含周计划条目)",
"deps": [
"requirePermission",
"data-access.getCoursePlanById"
],
"usedBy": [
"待扩展"
]
},
{
"name": "createCoursePlanItemAction",
"permission": "COURSE_PLAN_MANAGE",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"purpose": "创建周计划条目",
"deps": [
"requirePermission",
"shared/db",
"data-access.createCoursePlanItem"
],
"usedBy": [
"course-plan-item-editor.tsx"
]
},
{
"name": "updateCoursePlanItemAction",
"permission": "COURSE_PLAN_MANAGE",
"signature": "(id: string, prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"purpose": "更新周计划条目",
"deps": [
"requirePermission",
"shared/db",
"data-access.updateCoursePlanItem"
],
"usedBy": [
"course-plan-item-editor.tsx"
]
},
{
"name": "deleteCoursePlanItemAction",
"permission": "COURSE_PLAN_MANAGE",
"signature": "(id: string) => Promise<ActionState<string>>",
"purpose": "删除周计划条目",
"deps": [
"requirePermission",
"shared/db",
"data-access.deleteCoursePlanItem"
],
"usedBy": [
"course-plan-item-editor.tsx"
]
},
{
"name": "toggleCoursePlanItemCompletedAction",
"permission": "COURSE_PLAN_MANAGE",
"signature": "(id: string, completed: boolean) => Promise<ActionState<string>>",
"purpose": "切换周计划条目完成状态",
"deps": [
"requirePermission",
"shared/db",
"data-access.updateCoursePlanItem"
],
"usedBy": [
"course-plan-detail.tsx"
]
}
],
"dataAccess": [
{
"name": "getCoursePlans",
"signature": "(params?: GetCoursePlansParams) => Promise<CoursePlanListItem[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.coursePlans",
"shared.db.schema.classes",
"shared.db.schema.subjects",
"shared.db.schema.users"
],
"usedBy": [
"admin/course-plans/page.tsx",
"teacher/course-plans/page.tsx"
]
},
{
"name": "getCoursePlanById",
"signature": "(id: string) => Promise<CoursePlanWithItems | null>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.coursePlans",
"shared.db.schema.coursePlanItems",
"shared.db.schema.classes",
"shared.db.schema.subjects",
"shared.db.schema.users"
],
"usedBy": [
"admin/course-plans/[id]/page.tsx",
"teacher/course-plans/[id]/page.tsx"
]
},
{
"name": "createCoursePlan",
"signature": "(data: CreateCoursePlanInput, createdBy: string) => Promise<string>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.coursePlans",
"@paralleldrive/cuid2"
],
"usedBy": [
"createCoursePlanAction"
]
},
{
"name": "updateCoursePlan",
"signature": "(id: string, data: Partial<UpdateCoursePlanInput>) => Promise<void>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.coursePlans"
],
"usedBy": [
"updateCoursePlanAction"
]
},
{
"name": "deleteCoursePlan",
"signature": "(id: string) => Promise<void>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.coursePlans"
],
"usedBy": [
"deleteCoursePlanAction"
]
},
{
"name": "createCoursePlanItem",
"signature": "(data: CreateCoursePlanItemInput) => Promise<string>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.coursePlanItems",
"@paralleldrive/cuid2"
],
"usedBy": [
"createCoursePlanItemAction"
]
},
{
"name": "updateCoursePlanItem",
"signature": "(id: string, data: Partial<UpdateCoursePlanItemInput>) => Promise<void>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.coursePlanItems"
],
"usedBy": [
"updateCoursePlanItemAction",
"toggleCoursePlanItemCompletedAction"
]
},
{
"name": "deleteCoursePlanItem",
"signature": "(id: string) => Promise<void>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.coursePlanItems"
],
"usedBy": [
"deleteCoursePlanItemAction"
]
},
{
"name": "reorderCoursePlanItems",
"signature": "(planId: string, items: ReorderCoursePlanItemInput[]) => Promise<void>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.coursePlanItems"
],
"usedBy": [
"待扩展"
]
},
{
"name": "getSubjectOptions",
"signature": "() => Promise<{id:string;name:string}[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.subjects"
],
"usedBy": [
"admin/course-plans/create/page.tsx",
"admin/course-plans/[id]/edit/page.tsx"
]
}
],
"schemas": [
{
"name": "CreateCoursePlanSchema",
"type": "zod",
"file": "schema.ts",
"definition": "{ classId, subjectId, teacherId, academicYearId?, semester?, totalHours?, weeklyHours?, startDate?, endDate?, syllabus?, objectives?, status? }",
"usedBy": [
"createCoursePlanAction"
]
},
{
"name": "UpdateCoursePlanSchema",
"type": "zod",
"file": "schema.ts",
"definition": "{ classId?, subjectId?, teacherId?, academicYearId?, semester?, totalHours?, completedHours?, weeklyHours?, startDate?, endDate?, syllabus?, objectives?, status? }",
"usedBy": [
"updateCoursePlanAction"
]
},
{
"name": "CreateCoursePlanItemSchema",
"type": "zod",
"file": "schema.ts",
"definition": "{ planId, week, topic, content?, hours?, textbookChapter?, notes? }",
"usedBy": [
"createCoursePlanItemAction"
]
},
{
"name": "UpdateCoursePlanItemSchema",
"type": "zod",
"file": "schema.ts",
"definition": "{ week?, topic?, content?, hours?, textbookChapter?, notes?, isCompleted?, completedAt? }",
"usedBy": [
"updateCoursePlanItemAction"
]
}
],
"types": [
{
"name": "CoursePlan",
"type": "interface",
"file": "types.ts",
"definition": "{ id, classId, subjectId, teacherId, academicYearId, semester, totalHours, completedHours, weeklyHours, startDate, endDate, syllabus, objectives, status, createdBy, createdAt, updatedAt }",
"usedBy": [
"course-plans/components",
"data-access"
]
},
{
"name": "CoursePlanItem",
"type": "interface",
"file": "types.ts",
"definition": "{ id, planId, week, topic, content, hours, textbookChapter, notes, isCompleted, completedAt, createdAt, updatedAt }",
"usedBy": [
"course-plans/components",
"data-access"
]
},
{
"name": "CoursePlanListItem",
"type": "interface",
"file": "types.ts",
"definition": "= CoursePlan & { className, subjectName, teacherName }",
"usedBy": [
"列表页",
"data-access"
]
},
{
"name": "CoursePlanWithItems",
"type": "interface",
"file": "types.ts",
"definition": "= CoursePlanListItem & { items: CoursePlanItem[] }",
"usedBy": [
"详情页",
"data-access"
]
},
{
"name": "CoursePlanStatus",
"type": "type",
"file": "types.ts",
"definition": "\"planning\" | \"active\" | \"completed\" | \"paused\"",
"usedBy": [
"data-access",
"components"
]
},
{
"name": "CoursePlanSemester",
"type": "type",
"file": "types.ts",
"definition": "\"1\" | \"2\"",
"usedBy": [
"data-access",
"components"
]
},
{
"name": "GetCoursePlansParams",
"type": "interface",
"file": "types.ts",
"definition": "{ classId?, teacherId?, subjectId?, status? }",
"usedBy": [
"getCoursePlans",
"getCoursePlansAction"
]
},
{
"name": "ReorderCoursePlanItemInput",
"type": "interface",
"file": "types.ts",
"definition": "{ id, week }",
"usedBy": [
"reorderCoursePlanItems"
]
}
],
"components": [
{
"name": "CoursePlanProgress",
"file": "components/course-plan-progress.tsx",
"purpose": "进度条组件completedHours/totalHours 百分比)"
},
{
"name": "CoursePlanList",
"file": "components/course-plan-list.tsx",
"purpose": "课程计划列表支持状态筛选URL同步"
},
{
"name": "CoursePlanForm",
"file": "components/course-plan-form.tsx",
"purpose": "创建/编辑表单(班级、科目、教师、学年、学期、状态、课时、日期、大纲、目标)"
},
{
"name": "CoursePlanItemEditor",
"file": "components/course-plan-item-editor.tsx",
"purpose": "周计划条目编辑器Dialog支持创建/编辑/删除/切换完成)"
},
{
"name": "CoursePlanDetail",
"file": "components/course-plan-detail.tsx",
"purpose": "详情视图(计划信息、进度、大纲、目标、周计划表格、删除确认)"
}
]
}
},
"parent": {
"path": "src/modules/parent",
"description": "家长端仪表盘:聚合家长关联子女的学习数据(课表、作业、成绩、班级),支持多子女切换查看。家长通过 parentStudentRelations 表关联子女DataScope 解析为 children 类型",
"exports": {
"dataAccess": [
{
"name": "getChildren",
"signature": "(parentId: string) => Promise<ParentChildRelation[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.parentStudentRelations",
"react.cache"
],
"usedBy": [
"getParentDashboardData (内部)",
"parent/children/[studentId]/page.tsx"
]
},
{
"name": "getChildBasicInfo",
"signature": "(studentId: string, relation?: string | null) => Promise<ChildBasicInfo | null>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.users",
"shared.db.schema.grades",
"shared.db.schema.classEnrollments",
"shared.db.schema.classes",
"react.cache"
],
"usedBy": [
"getChildDashboardData (内部)"
]
},
{
"name": "getChildDashboardData",
"signature": "(studentId: string, relation?: string | null) => Promise<ChildDashboardData | null>",
"file": "data-access.ts",
"deps": [
"getChildBasicInfo",
"classes/data-access.getStudentClasses",
"classes/data-access.getStudentSchedule",
"homework/data-access.getStudentHomeworkAssignments",
"homework/data-access.getStudentDashboardGrades",
"grades/data-access.getStudentGradeSummary",
"react.cache"
],
"usedBy": [
"parent/children/[studentId]/page.tsx",
"getParentDashboardData (内部)"
]
},
{
"name": "getParentDashboardData",
"signature": "(parentId: string) => Promise<ParentDashboardData>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.users",
"getChildren",
"getChildDashboardData",
"react.cache"
],
"usedBy": [
"parent/dashboard/page.tsx"
]
}
],
"types": [
{
"name": "ParentChildRelation",
"type": "type",
"file": "types.ts",
"definition": "{ id, parentId, studentId, relation: string | null, createdAt: string }",
"usedBy": [
"getChildren",
"getParentDashboardData"
]
},
{
"name": "ChildBasicInfo",
"type": "type",
"file": "types.ts",
"definition": "{ id, name: string | null, email, image: string | null, gradeName: string | null, className: string | null, classId: string | null, relation: string | null }",
"usedBy": [
"getChildBasicInfo",
"ChildDashboardData.basicInfo"
]
},
{
"name": "ChildScheduleItem",
"type": "type",
"file": "types.ts",
"definition": "{ id, classId, className, course, startTime, endTime, location: string | null }",
"usedBy": [
"ChildDashboardData.todaySchedule",
"child-schedule-card.tsx"
]
},
{
"name": "ChildHomeworkSummary",
"type": "type",
"file": "types.ts",
"definition": "{ pendingCount, submittedCount, gradedCount, overdueCount, recentAssignments: StudentHomeworkAssignmentListItem[] }",
"usedBy": [
"ChildDashboardData.homeworkSummary",
"child-homework-summary.tsx"
]
},
{
"name": "ChildDashboardData",
"type": "type",
"file": "types.ts",
"definition": "{ basicInfo: ChildBasicInfo, enrolledClasses: StudentEnrolledClass[], todaySchedule: ChildScheduleItem[], homeworkSummary: ChildHomeworkSummary, gradeTrend: StudentDashboardGradeProps, gradeSummary: StudentGradeSummary | null }",
"usedBy": [
"getChildDashboardData",
"ParentDashboardData.children",
"所有 child-* 组件"
]
},
{
"name": "ParentDashboardData",
"type": "type",
"file": "types.ts",
"definition": "{ parentName: string | null, children: ChildDashboardData[] }",
"usedBy": [
"getParentDashboardData",
"parent-dashboard.tsx"
]
}
],
"components": [
{
"name": "ParentDashboard",
"file": "components/parent-dashboard.tsx",
"purpose": "主容器组件(问候语、子女卡片网格、空状态)"
},
{
"name": "ChildCard",
"file": "components/child-card.tsx",
"purpose": "子女卡片(头像、姓名、班级、待完成/逾期/平均分统计,点击跳转详情)"
},
{
"name": "ChildDetailHeader",
"file": "components/child-detail-header.tsx",
"purpose": "子女详情页头部(返回按钮、头像、姓名、班级、年级、关系)"
},
{
"name": "ChildDetailPanel",
"file": "components/child-detail-panel.tsx",
"purpose": "子女详情面板容器(组合 homework/grade/schedule 三个子组件)"
},
{
"name": "ChildHomeworkSummary",
"file": "components/child-homework-summary.tsx",
"purpose": "子女作业概览pending/submitted/graded/overdue 统计 + 最近作业列表)"
},
{
"name": "ChildGradeSummary",
"file": "components/child-grade-summary.tsx",
"purpose": "子女成绩概览Recharts 折线图趋势 + 最新分数 + 班级排名 + 最近成绩列表use client"
},
{
"name": "ChildScheduleCard",
"file": "components/child-schedule-card.tsx",
"purpose": "子女今日课表卡片(课程、时间、地点、班级)"
}
]
}
},
"messaging": {
"path": "src/modules/messaging",
"description": "站内消息系统用户间私信收发支持回复链、站内通知多态类型message/announcement/homework/gradeSiteHeader 通知下拉菜单展示未读数",
"exports": {
"actions": [
{
"name": "sendMessageAction",
"permission": "MESSAGE_SEND",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"purpose": "发送消息(同时通过 notifications dispatcher 为收件人创建多渠道通知;支持 parentMessageId 回复)",
"deps": [
"requirePermission",
"shared/db",
"data-access.createMessage",
"notifications.dispatcher.sendNotification",
"revalidatePath"
],
"usedBy": [
"message-compose.tsx"
]
},
{
"name": "markMessageAsReadAction",
"permission": "MESSAGE_READ",
"signature": "(id: string) => Promise<ActionState<void>>",
"purpose": "标记消息已读(设置 readAt",
"deps": [
"requirePermission",
"schema.MessageIdSchema",
"data-access.markMessageAsRead",
"revalidatePath"
],
"usedBy": [
"message-detail.tsx",
"messages/[id]/page.tsx"
]
},
{
"name": "deleteMessageAction",
"permission": "MESSAGE_DELETE",
"signature": "(id: string) => Promise<ActionState<void>>",
"purpose": "删除消息(仅发送者或接收者可删)",
"deps": [
"requirePermission",
"schema.MessageIdSchema",
"data-access.deleteMessage",
"revalidatePath"
],
"usedBy": [
"message-detail.tsx"
]
},
{
"name": "getMessagesAction",
"permission": "MESSAGE_READ",
"signature": "(params?: { type?, page?, pageSize? }) => Promise<ActionState<PaginatedResult<MessageListItem>>>",
"purpose": "获取消息列表(收件箱/已发送,分页)",
"deps": [
"requirePermission",
"data-access.getMessages"
],
"usedBy": [
"message-list.tsx"
]
},
{
"name": "getMessageDetailAction",
"permission": "MESSAGE_READ",
"signature": "(id: string) => Promise<ActionState<Message>>",
"purpose": "获取消息详情(含回复线程)",
"deps": [
"requirePermission",
"schema.MessageIdSchema",
"data-access.getMessageById",
"data-access.getMessageThread"
],
"usedBy": [
"message-detail.tsx"
]
},
{
"name": "getRecipientsAction",
"permission": "MESSAGE_SEND",
"signature": "() => Promise<ActionState<RecipientOption[]>>",
"purpose": "获取可发送对象列表(按 DataScope 过滤)",
"deps": [
"requirePermission",
"data-access.getRecipients"
],
"usedBy": [
"messages/compose/page.tsx"
]
},
{
"name": "getNotificationsAction",
"permission": "MESSAGE_READ",
"signature": "(params?: { page?, pageSize? }) => Promise<ActionState<PaginatedResult<NotificationListItem>>>",
"purpose": "获取当前用户通知列表(分页)",
"deps": [
"requirePermission",
"data-access.getNotifications"
],
"usedBy": [
"notification-dropdown.tsx",
"notification-list.tsx"
]
},
{
"name": "markNotificationAsReadAction",
"permission": "MESSAGE_READ",
"signature": "(id: string) => Promise<ActionState<void>>",
"purpose": "标记单条通知已读",
"deps": [
"requirePermission",
"data-access.markNotificationAsRead",
"revalidatePath"
],
"usedBy": [
"notification-dropdown.tsx",
"notification-list.tsx"
]
},
{
"name": "markAllNotificationsAsReadAction",
"permission": "MESSAGE_READ",
"signature": "() => Promise<ActionState<void>>",
"purpose": "标记所有通知已读",
"deps": [
"requirePermission",
"data-access.markAllNotificationsAsRead",
"revalidatePath"
],
"usedBy": [
"notification-dropdown.tsx",
"notification-list.tsx"
]
},
{
"name": "getNotificationPreferencesAction",
"permission": "MESSAGE_READ",
"signature": "() => Promise<ActionState<NotificationPreferences>>",
"purpose": "获取当前用户的通知偏好设置(首次访问自动创建默认记录)",
"deps": [
"requirePermission",
"notification-preferences.getNotificationPreferences"
],
"usedBy": [
"settings/page.tsx",
"settings/components/notification-preferences-form.tsx"
]
},
{
"name": "updateNotificationPreferencesAction",
"permission": "MESSAGE_READ",
"signature": "(prevState: ActionState<NotificationPreferences> | null, formData: FormData) => Promise<ActionState<NotificationPreferences>>",
"purpose": "更新当前用户的通知偏好设置upsert 语义,未提供字段保留原值)",
"deps": [
"requirePermission",
"schema.UpdateNotificationPreferencesSchema",
"notification-preferences.upsertNotificationPreferences",
"revalidatePath"
],
"usedBy": [
"settings/components/notification-preferences-form.tsx"
]
}
],
"dataAccess": [
{
"name": "getMessages",
"signature": "(userId: string, params?: { type?, page?, pageSize? }) => Promise<PaginatedResult<MessageListItem>>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.messages",
"shared.db.schema.users"
],
"usedBy": [
"getMessagesAction"
]
},
{
"name": "getMessageById",
"signature": "(id: string, userId: string) => Promise<Message | null>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.messages",
"shared.db.schema.users"
],
"usedBy": [
"getMessageDetailAction",
"messages/[id]/page.tsx"
]
},
{
"name": "getMessageThread",
"signature": "(rootId: string, userId: string) => Promise<Message[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.messages",
"shared.db.schema.users"
],
"usedBy": [
"getMessageDetailAction"
]
},
{
"name": "createMessage",
"signature": "(input: CreateMessageInput) => Promise<Message>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.messages",
"@paralleldrive/cuid2"
],
"usedBy": [
"sendMessageAction"
]
},
{
"name": "markMessageAsRead",
"signature": "(id: string, userId: string) => Promise<void>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.messages"
],
"usedBy": [
"markMessageAsReadAction",
"messages/[id]/page.tsx"
]
},
{
"name": "deleteMessage",
"signature": "(id: string, userId: string) => Promise<void>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.messages"
],
"usedBy": [
"deleteMessageAction"
]
},
{
"name": "getUnreadMessageCount",
"signature": "(userId: string) => Promise<number>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.messages"
],
"usedBy": [
"待扩展"
]
},
{
"name": "getNotifications",
"signature": "(userId: string, params?: { page?, pageSize? }) => Promise<PaginatedResult<NotificationListItem>>",
"file": "data-access.ts",
"purpose": "re-export shim实际逻辑在 notifications/data-access.tsP0-4 / P1-5 修复后迁移)",
"deps": [
"notifications.data-access.getNotifications"
],
"usedBy": [
"getNotificationsAction",
"messages/page.tsx"
]
},
{
"name": "createNotification",
"signature": "(input: CreateNotificationInput) => Promise<string>",
"file": "data-access.ts",
"purpose": "re-export shim实际逻辑在 notifications/data-access.tsP0-4 / P1-5 修复后迁移)",
"deps": [
"notifications.data-access.createNotification"
],
"usedBy": [
"notifications.dispatcher (via in-app-channel)"
]
},
{
"name": "markNotificationAsRead",
"signature": "(id: string, userId: string) => Promise<void>",
"file": "data-access.ts",
"purpose": "re-export shim实际逻辑在 notifications/data-access.tsP0-4 / P1-5 修复后迁移)",
"deps": [
"notifications.data-access.markNotificationAsRead"
],
"usedBy": [
"markNotificationAsReadAction"
]
},
{
"name": "markAllNotificationsAsRead",
"signature": "(userId: string) => Promise<void>",
"file": "data-access.ts",
"purpose": "re-export shim实际逻辑在 notifications/data-access.tsP0-4 / P1-5 修复后迁移)",
"deps": [
"notifications.data-access.markAllNotificationsAsRead"
],
"usedBy": [
"markAllNotificationsAsReadAction"
]
},
{
"name": "getUnreadNotificationCount",
"signature": "(userId: string) => Promise<number>",
"file": "data-access.ts",
"purpose": "re-export shim实际逻辑在 notifications/data-access.tsP0-4 / P1-5 修复后迁移)",
"deps": [
"notifications.data-access.getUnreadNotificationCount"
],
"usedBy": [
"待扩展"
]
},
{
"name": "getRecipients",
"signature": "(ctx: AuthContext) => Promise<RecipientOption[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.users",
"shared.db.schema.classEnrollments",
"shared.db.schema.classes",
"shared.db.schema.grades"
],
"usedBy": [
"getRecipientsAction",
"messages/compose/page.tsx"
]
}
],
"notificationPreferences": [
{
"name": "getNotificationPreferences",
"signature": "(userId: string) => Promise<NotificationPreferences>",
"file": "notification-preferences.ts",
"purpose": "re-export shim实际逻辑在 notifications/preferences.tsP0-4 / P1-5 修复后迁移React cache 包装;若不存在则自动创建默认记录,并发冲突回退到查询)",
"deps": [
"notifications.preferences.getNotificationPreferences"
],
"usedBy": [
"getNotificationPreferencesAction",
"settings/page.tsx"
]
},
{
"name": "upsertNotificationPreferences",
"signature": "(userId: string, input: UpdateNotificationPreferencesInput) => Promise<NotificationPreferences | null>",
"file": "notification-preferences.ts",
"purpose": "re-export shim实际逻辑在 notifications/preferences.tsP0-4 / P1-5 修复后迁移;存在则部分更新,不存在则插入;未提供字段保留原值)",
"deps": [
"notifications.preferences.upsertNotificationPreferences"
],
"usedBy": [
"updateNotificationPreferencesAction"
]
}
],
"schemas": [
{
"name": "SendMessageSchema",
"type": "zod",
"file": "schema.ts",
"definition": "{ receiverId: string, subject?: string, content: string, parentMessageId?: string }",
"usedBy": [
"sendMessageAction"
]
}
],
"types": [
{
"name": "Message",
"type": "type",
"file": "types.ts",
"definition": "{ id, senderId, receiverId, subject: string | null, content, isRead, readAt: string | null, parentMessageId: string | null, createdAt, senderName, receiverName }",
"usedBy": [
"messaging/components",
"页面"
]
},
{
"name": "MessageListItem",
"type": "type",
"file": "types.ts",
"definition": "消息列表项类型(同 Message 精简版)",
"usedBy": [
"列表页"
]
},
{
"name": "MessageThread",
"type": "type",
"file": "types.ts",
"definition": "{ root: Message, replies: Message[] }",
"usedBy": [
"详情页"
]
},
{
"name": "Notification",
"type": "type",
"file": "types.ts",
"definition": "{ id, userId, type: NotificationType, title, content: string | null, link: string | null, isRead, createdAt }",
"usedBy": [
"notification-dropdown",
"notification-list"
]
},
{
"name": "NotificationListItem",
"type": "type",
"file": "types.ts",
"definition": "通知列表项类型(同 Notification",
"usedBy": [
"列表页"
]
},
{
"name": "NotificationType",
"type": "type",
"file": "types.ts",
"definition": "'message' | 'announcement' | 'homework' | 'grade'",
"usedBy": [
"data-access",
"components"
]
},
{
"name": "MessageType",
"type": "type",
"file": "types.ts",
"definition": "'inbox' | 'sent'",
"usedBy": [
"getMessages 参数"
]
},
{
"name": "CreateMessageInput",
"type": "type",
"file": "types.ts",
"definition": "{ senderId, receiverId, subject?, content, parentMessageId? }",
"usedBy": [
"createMessage"
]
},
{
"name": "CreateNotificationInput",
"type": "type",
"file": "types.ts",
"definition": "{ userId, type: NotificationType, title, content?, link? }",
"usedBy": [
"createNotification"
]
},
{
"name": "RecipientOption",
"type": "type",
"file": "types.ts",
"definition": "{ id: string, name: string }",
"usedBy": [
"compose 页面下拉选项"
]
},
{
"name": "PaginatedResult",
"type": "type",
"file": "types.ts",
"definition": "{ items: T[], total: number, page: number, pageSize: number, totalPages: number }",
"usedBy": [
"getMessages",
"getNotifications"
]
},
{
"name": "NotificationPreferences",
"type": "interface",
"file": "types.ts",
"definition": "{ id, userId, emailEnabled, smsEnabled, pushEnabled, homeworkNotifications, gradeNotifications, announcementNotifications, messageNotifications, attendanceNotifications, createdAt, updatedAt }",
"usedBy": [
"notification-preferences",
"getNotificationPreferencesAction",
"updateNotificationPreferencesAction",
"settings/components/notification-preferences-form"
]
},
{
"name": "UpdateNotificationPreferencesInput",
"type": "interface",
"file": "types.ts",
"definition": "{ emailEnabled?, smsEnabled?, pushEnabled?, homeworkNotifications?, gradeNotifications?, announcementNotifications?, messageNotifications?, attendanceNotifications? }",
"usedBy": [
"upsertNotificationPreferences",
"updateNotificationPreferencesAction"
]
}
],
"components": [
{
"name": "MessageList",
"file": "components/message-list.tsx",
"purpose": "消息列表(收件箱/已发送 Tab 切换,已读/未读标记usePermission 控制写消息按钮)"
},
{
"name": "MessageDetail",
"file": "components/message-detail.tsx",
"purpose": "消息详情(含回复线程、回复/删除操作AlertDialog 删除确认usePermission 控制按钮可见性)"
},
{
"name": "MessageCompose",
"file": "components/message-compose.tsx",
"purpose": "写消息表单(收件人 Select、主题 Input、内容 Textarea支持回复模式"
},
{
"name": "NotificationDropdown",
"file": "components/notification-dropdown.tsx",
"purpose": "SiteHeader 通知下拉菜单Bell 图标 + 未读数 Badge滚动列表标记已读查看全部链接"
},
{
"name": "NotificationList",
"file": "components/notification-list.tsx",
"purpose": "通知完整列表(全部标记已读、单条标记已读、查看链接)"
}
]
}
},
"notifications": {
"path": "src/modules/notifications",
"description": "通知渠道集成层基于用户通知偏好notification_preferences将通知分发到站内消息/SMS/微信公众号/邮件多渠道。所有渠道实现统一 NotificationChannelSender 接口dispatcher 按偏好并行发送。支持 Mock 模式(开发环境无需外部服务)。",
"exports": {
"actions": [
{
"name": "sendNotificationAction",
"permission": "MESSAGE_SEND",
"signature": "(payload: NotificationPayload) => Promise<ActionState<ChannelSendResult[]>>",
"purpose": "发送通知给指定用户(按偏好多渠道分发)",
"deps": [
"requirePermission",
"dispatcher.sendNotification"
],
"usedBy": [
"待扩展"
]
},
{
"name": "sendClassNotificationAction",
"permission": "MESSAGE_SEND",
"signature": "(classId: string, payload: Omit<NotificationPayload, 'userId'>) => Promise<ActionState<ChannelSendResult[][]>>",
"purpose": "发送班级通知(批量发送给班级所有学生;教师只能给自己所教班级发送)",
"deps": [
"requirePermission",
"db.schema.classEnrollments",
"db.schema.classes",
"dispatcher.sendBatchNotifications"
],
"usedBy": [
"待扩展"
]
}
],
"dispatcher": [
{
"name": "sendNotification",
"signature": "(payload: NotificationPayload) => Promise<ChannelSendResult[]>",
"file": "dispatcher.ts",
"purpose": "发送单条通知:读取用户偏好+联系方式,按偏好选择渠道并行发送,记录日志",
"deps": [
"preferences.getNotificationPreferences",
"data-access.getUserContactInfo",
"data-access.logNotificationSendBatch",
"channels.sms-channel.createSmsSender",
"channels.wechat-channel.createWechatSender",
"channels.email-channel.createEmailSender",
"channels.in-app-channel.createInAppSender"
],
"usedBy": [
"sendNotificationAction",
"sendClassNotificationAction",
"messaging.sendMessageAction"
]
},
{
"name": "sendBatchNotifications",
"signature": "(payloads: NotificationPayload[]) => Promise<ChannelSendResult[][]>",
"file": "dispatcher.ts",
"purpose": "批量发送通知(每个用户独立选择渠道,并行发送)",
"deps": [
"sendNotification"
],
"usedBy": [
"sendClassNotificationAction"
]
}
],
"dataAccess": [
{
"name": "getNotifications",
"signature": "(userId: string, params?: { page?, pageSize?, unreadOnly? }) => Promise<PaginatedResult<Notification>>",
"file": "data-access.ts",
"purpose": "获取用户站内通知列表(分页,支持 unreadOnly 过滤P0-4 / P1-5 修复后从 messaging 迁移)",
"deps": [
"shared.db",
"shared.db.schema.messageNotifications",
"react.cache"
],
"usedBy": [
"messaging.getNotificationsAction (via re-export)",
"messages/page.tsx (via re-export)"
]
},
{
"name": "createNotification",
"signature": "(input: CreateNotificationInput) => Promise<string>",
"file": "data-access.ts",
"purpose": "创建站内通知记录(写入 message_notifications 表P0-4 / P1-5 修复后从 messaging 迁移)",
"deps": [
"shared.db",
"shared.db.schema.messageNotifications",
"@paralleldrive/cuid2"
],
"usedBy": [
"channels.in-app-channel.InAppChannelSender.send"
]
},
{
"name": "markNotificationAsRead",
"signature": "(id: string, userId: string) => Promise<void>",
"file": "data-access.ts",
"purpose": "标记单条通知已读P0-4 / P1-5 修复后从 messaging 迁移)",
"deps": [
"shared.db",
"shared.db.schema.messageNotifications"
],
"usedBy": [
"messaging.markNotificationAsReadAction (via re-export)"
]
},
{
"name": "markAllNotificationsAsRead",
"signature": "(userId: string) => Promise<void>",
"file": "data-access.ts",
"purpose": "标记用户所有通知已读P0-4 / P1-5 修复后从 messaging 迁移)",
"deps": [
"shared.db",
"shared.db.schema.messageNotifications"
],
"usedBy": [
"messaging.markAllNotificationsAsReadAction (via re-export)"
]
},
{
"name": "getUnreadNotificationCount",
"signature": "(userId: string) => Promise<number>",
"file": "data-access.ts",
"purpose": "获取用户未读通知数P0-4 / P1-5 修复后从 messaging 迁移)",
"deps": [
"shared.db",
"shared.db.schema.messageNotifications",
"react.cache"
],
"usedBy": [
"待扩展"
]
},
{
"name": "getUserContactInfo",
"signature": "(userId: string) => Promise<ChannelRecipient>",
"file": "data-access.ts",
"purpose": "获取用户联系方式phone/emailwechatOpenId 暂不支持users 表无此字段)",
"deps": [
"shared.db",
"shared.db.schema.users",
"react.cache"
],
"usedBy": [
"dispatcher.sendNotification"
]
},
{
"name": "logNotificationSend",
"signature": "(result: ChannelSendResult) => void",
"file": "data-access.ts",
"purpose": "记录单条发送日志(当前使用 console.info未来可扩展 notification_logs 表)",
"deps": [],
"usedBy": [
"logNotificationSendBatch"
]
},
{
"name": "logNotificationSendBatch",
"signature": "(results: ChannelSendResult[]) => void",
"file": "data-access.ts",
"purpose": "批量记录发送日志",
"deps": [
"logNotificationSend"
],
"usedBy": [
"dispatcher.sendNotification",
"dispatcher.sendBatchNotifications"
]
}
],
"preferences": [
{
"name": "getNotificationPreferences",
"signature": "(userId: string) => Promise<NotificationPreferences>",
"file": "preferences.ts",
"purpose": "获取用户通知偏好React cache 包装若不存在则自动创建默认记录并发冲突回退到查询P0-4 / P1-5 修复后从 messaging 迁移)",
"deps": [
"shared.db",
"shared.db.schema.notificationPreferences",
"react.cache",
"@paralleldrive/cuid2"
],
"usedBy": [
"dispatcher.sendNotification",
"messaging.getNotificationPreferencesAction (via re-export)",
"settings/page.tsx (via re-export)"
]
},
{
"name": "upsertNotificationPreferences",
"signature": "(userId: string, input: UpdateNotificationPreferencesInput) => Promise<NotificationPreferences | null>",
"file": "preferences.ts",
"purpose": "更新或插入用户通知偏好存在则部分更新不存在则插入未提供字段保留原值P0-4 / P1-5 修复后从 messaging 迁移)",
"deps": [
"shared.db",
"shared.db.schema.notificationPreferences",
"@paralleldrive/cuid2"
],
"usedBy": [
"messaging.updateNotificationPreferencesAction (via re-export)"
]
}
],
"channels": [
{
"name": "createSmsSender",
"file": "channels/sms-channel.ts",
"purpose": "创建 SMS 渠道发送器aliyun/tencent/mock根据 SMS_PROVIDER 环境变量选择SDK 动态 import",
"deps": [
"环境变量: SMS_PROVIDER, SMS_ACCESS_KEY_ID, SMS_ACCESS_KEY_SECRET, SMS_SIGN_NAME, SMS_TEMPLATE_CODE"
]
},
{
"name": "createWechatSender",
"file": "channels/wechat-channel.ts",
"purpose": "创建微信渠道发送器(配置完整用真实发送器,否则 Mockaccess_token 带缓存)",
"deps": [
"环境变量: WECHAT_APP_ID, WECHAT_APP_SECRET, WECHAT_TEMPLATE_ID"
]
},
{
"name": "createEmailSender",
"file": "channels/email-channel.ts",
"purpose": "创建邮件渠道发送器(配置 EMAIL_HOST 用 Nodemailer SMTP否则 MockHTML 模板按 type 着色)",
"deps": [
"环境变量: EMAIL_HOST, EMAIL_PORT, EMAIL_USER, EMAIL_PASS, EMAIL_FROM"
]
},
{
"name": "createInAppSender",
"file": "channels/in-app-channel.ts",
"purpose": "创建站内消息渠道发送器(调用 notifications.data-access.createNotification 写入 message_notifications 表总是启用P0-4 / P1-5 修复后改为静态导入本地 createNotification不再动态 import messaging",
"deps": [
"data-access.createNotification"
]
}
],
"types": [
{
"name": "NotificationChannel",
"type": "type",
"file": "types.ts",
"definition": "'in_app' | 'email' | 'sms' | 'wechat'",
"usedBy": [
"所有渠道文件",
"dispatcher"
]
},
{
"name": "NotificationPayload",
"type": "interface",
"file": "types.ts",
"definition": "{ userId, title, content, type: 'info'|'warning'|'error'|'success', metadata?, actionUrl? }",
"usedBy": [
"dispatcher",
"actions",
"所有渠道"
]
},
{
"name": "ChannelSendResult",
"type": "interface",
"file": "types.ts",
"definition": "{ channel, success, messageId?, error?, sentAt }",
"usedBy": [
"dispatcher",
"actions",
"所有渠道"
]
},
{
"name": "NotificationChannelConfig",
"type": "interface",
"file": "types.ts",
"definition": "{ enabled, sms?, wechat?, email? }",
"usedBy": [
"类型定义"
]
},
{
"name": "NotificationChannelSender",
"type": "interface",
"file": "channels/types.ts",
"definition": "{ channel: NotificationChannel, send(payload, recipient), sendBatch(items) }",
"usedBy": [
"所有渠道实现",
"dispatcher"
]
},
{
"name": "ChannelRecipient",
"type": "interface",
"file": "channels/types.ts",
"definition": "{ userId, phone?, email?, wechatOpenId? }",
"usedBy": [
"所有渠道",
"data-access.getUserContactInfo"
]
},
{
"name": "NotificationType",
"type": "type",
"file": "types.ts",
"definition": "'message' | 'announcement' | 'homework' | 'grade'",
"usedBy": [
"data-access",
"channels.in-app-channel",
"messaging (via re-export)"
]
},
{
"name": "Notification",
"type": "interface",
"file": "types.ts",
"definition": "{ id, userId, type: NotificationType, title, content: string | null, link: string | null, isRead, createdAt }",
"usedBy": [
"data-access.getNotifications",
"messaging (via re-export)"
]
},
{
"name": "NotificationListItem",
"type": "type",
"file": "types.ts",
"definition": "通知列表项类型(同 Notification",
"usedBy": [
"messaging (via re-export)"
]
},
{
"name": "PaginatedResult",
"type": "interface",
"file": "types.ts",
"definition": "{ items: T[], total: number, page: number, pageSize: number, totalPages: number }",
"usedBy": [
"data-access.getNotifications",
"messaging (via re-export)"
]
},
{
"name": "GetNotificationsParams",
"type": "interface",
"file": "types.ts",
"definition": "{ page?, pageSize?, unreadOnly? }",
"usedBy": [
"data-access.getNotifications",
"messaging (via re-export)"
]
},
{
"name": "CreateNotificationInput",
"type": "interface",
"file": "types.ts",
"definition": "{ userId, type: NotificationType, title, content?, link? }",
"usedBy": [
"data-access.createNotification",
"channels.in-app-channel",
"messaging (via re-export)"
]
},
{
"name": "NotificationPreferences",
"type": "interface",
"file": "types.ts",
"definition": "{ id, userId, emailEnabled, smsEnabled, pushEnabled, homeworkNotifications, gradeNotifications, announcementNotifications, messageNotifications, attendanceNotifications, createdAt, updatedAt }",
"usedBy": [
"preferences.getNotificationPreferences",
"preferences.upsertNotificationPreferences",
"dispatcher.sendNotification",
"messaging (via re-export)"
]
},
{
"name": "UpdateNotificationPreferencesInput",
"type": "interface",
"file": "types.ts",
"definition": "{ emailEnabled?, smsEnabled?, pushEnabled?, homeworkNotifications?, gradeNotifications?, announcementNotifications?, messageNotifications?, attendanceNotifications? }",
"usedBy": [
"preferences.upsertNotificationPreferences",
"messaging (via re-export)"
]
}
]
}
},
"attendance": {
"path": "src/modules/attendance",
"description": "学生考勤管理:教师按班级/日期点名(单条/批量)、查询考勤记录、统计出勤率/迟到率,学生/家长查看本人/子女考勤汇总,管理员查看全校考勤记录。支持班级考勤规则配置。",
"exports": {
"actions": [
{
"name": "recordAttendanceAction",
"permission": "ATTENDANCE_MANAGE",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"purpose": "创建单条考勤记录",
"deps": [
"requirePermission",
"data-access.createAttendanceRecord",
"revalidatePath"
],
"usedBy": [
"attendance-record-list.tsx"
]
},
{
"name": "batchRecordAttendanceAction",
"permission": "ATTENDANCE_MANAGE",
"signature": "(prevState: ActionState<number> | null, formData: FormData) => Promise<ActionState<number>>",
"purpose": "批量点名(班级+日期,表格形式录入每个学生状态)",
"deps": [
"requirePermission",
"data-access.batchCreateAttendanceRecords",
"revalidatePath"
],
"usedBy": [
"attendance-sheet.tsx"
]
},
{
"name": "updateAttendanceAction",
"permission": "ATTENDANCE_MANAGE",
"signature": "(id: string, prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"purpose": "更新考勤记录(状态、备注)",
"deps": [
"requirePermission",
"data-access.updateAttendanceRecord",
"revalidatePath"
],
"usedBy": [
"attendance-record-list.tsx"
]
},
{
"name": "deleteAttendanceAction",
"permission": "ATTENDANCE_MANAGE",
"signature": "(id: string) => Promise<ActionState<void>>",
"purpose": "删除考勤记录",
"deps": [
"requirePermission",
"data-access.deleteAttendanceRecord",
"revalidatePath"
],
"usedBy": [
"attendance-record-list.tsx"
]
},
{
"name": "getAttendanceAction",
"permission": "ATTENDANCE_READ",
"signature": "(params?: AttendanceQueryParams) => Promise<ActionState<PaginatedAttendanceResult>>",
"purpose": "分页查询考勤记录(按 scope 过滤)",
"deps": [
"requirePermission",
"data-access.getAttendanceRecords"
],
"usedBy": [
"teacher/attendance/page.tsx",
"admin/attendance/page.tsx"
]
},
{
"name": "getStudentAttendanceAction",
"permission": "ATTENDANCE_READ",
"signature": "(studentId: string) => Promise<ActionState<StudentAttendanceSummary>>",
"purpose": "获取学生考勤汇总(含 DataScope 二次校验class_members 仅查自己children 仅查子女)",
"deps": [
"requirePermission",
"data-access-stats.getStudentAttendanceSummary"
],
"usedBy": [
"student/attendance/page.tsx",
"parent/attendance/page.tsx"
]
},
{
"name": "getClassAttendanceStatsAction",
"permission": "ATTENDANCE_READ",
"signature": "(classId: string, startDate?: string, endDate?: string) => Promise<ActionState<ClassAttendanceSummary>>",
"purpose": "获取班级考勤统计",
"deps": [
"requirePermission",
"data-access-stats.getClassAttendanceStats"
],
"usedBy": [
"teacher/attendance/stats/page.tsx"
]
},
{
"name": "getClassAttendanceForDateAction",
"permission": "ATTENDANCE_READ",
"signature": "(classId: string, date: string) => Promise<ActionState<AttendanceListItem[]>>",
"purpose": "获取班级指定日期考勤(用于点名页加载已有记录)",
"deps": [
"requirePermission",
"data-access.getClassAttendanceForDate"
],
"usedBy": [
"attendance-sheet.tsx"
]
},
{
"name": "saveAttendanceRulesAction",
"permission": "ATTENDANCE_MANAGE",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"purpose": "保存班级考勤规则upsert",
"deps": [
"requirePermission",
"data-access.upsertAttendanceRules",
"revalidatePath"
],
"usedBy": [
"attendance-rules-form.tsx"
]
},
{
"name": "getAttendanceRulesAction",
"permission": "ATTENDANCE_READ",
"signature": "(classId?: string) => Promise<ActionState<AttendanceRule[]>>",
"purpose": "获取班级考勤规则",
"deps": [
"requirePermission",
"data-access.getAttendanceRules"
],
"usedBy": [
"attendance-rules-form.tsx"
]
}
],
"dataAccess": [
{
"name": "getAttendanceRecords",
"signature": "(params: AttendanceQueryParams & { scope: DataScope; currentUserId?: string }) => Promise<PaginatedAttendanceResult>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.attendanceRecords",
"shared.db.schema.users",
"shared.db.schema.classes",
"types.DataScope"
],
"usedBy": [
"getAttendanceAction"
]
},
{
"name": "getClassAttendanceForDate",
"signature": "(classId: string, date: string) => Promise<AttendanceListItem[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.attendanceRecords",
"shared.db.schema.users",
"shared.db.schema.classes"
],
"usedBy": [
"getClassAttendanceForDateAction"
]
},
{
"name": "createAttendanceRecord",
"signature": "(data: RecordAttendanceInput, recordedBy: string) => Promise<string>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.attendanceRecords",
"@paralleldrive/cuid2"
],
"usedBy": [
"recordAttendanceAction"
]
},
{
"name": "batchCreateAttendanceRecords",
"signature": "(data: BatchRecordAttendanceInput, recordedBy: string) => Promise<number>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.attendanceRecords",
"@paralleldrive/cuid2"
],
"usedBy": [
"batchRecordAttendanceAction"
]
},
{
"name": "updateAttendanceRecord",
"signature": "(id: string, data: UpdateAttendanceInput) => Promise<void>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.attendanceRecords"
],
"usedBy": [
"updateAttendanceAction"
]
},
{
"name": "deleteAttendanceRecord",
"signature": "(id: string) => Promise<void>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.attendanceRecords"
],
"usedBy": [
"deleteAttendanceAction"
]
},
{
"name": "getClassStudentsForAttendance",
"signature": "(classId: string) => Promise<Array<{ id, name, email }>>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.classEnrollments",
"shared.db.schema.users"
],
"usedBy": [
"attendance-sheet.tsx"
]
},
{
"name": "getAttendanceRules",
"signature": "(classId?: string) => Promise<AttendanceRule[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.attendanceRules"
],
"usedBy": [
"getAttendanceRulesAction"
]
},
{
"name": "upsertAttendanceRules",
"signature": "(data: AttendanceRuleInput) => Promise<string>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.attendanceRules",
"@paralleldrive/cuid2"
],
"usedBy": [
"saveAttendanceRulesAction"
]
},
{
"name": "getStudentAttendanceSummary",
"signature": "(studentId: string, startDate?: string, endDate?: string) => Promise<StudentAttendanceSummary | null>",
"file": "data-access-stats.ts",
"deps": [
"shared.db",
"shared.db.schema.attendanceRecords",
"shared.db.schema.classes",
"shared.db.schema.users"
],
"usedBy": [
"getStudentAttendanceAction",
"student/attendance/page.tsx",
"parent/attendance/page.tsx"
]
},
{
"name": "getClassAttendanceStats",
"signature": "(classId: string, startDate?: string, endDate?: string) => Promise<ClassAttendanceSummary | null>",
"file": "data-access-stats.ts",
"deps": [
"shared.db",
"shared.db.schema.attendanceRecords",
"shared.db.schema.classes",
"shared.db.schema.users"
],
"usedBy": [
"getClassAttendanceStatsAction",
"teacher/attendance/stats/page.tsx"
]
}
],
"schemas": [
{
"name": "AttendanceStatusEnum",
"type": "zod",
"file": "schema.ts",
"definition": "enum('present','absent','late','early_leave','excused')",
"usedBy": [
"RecordAttendanceSchema",
"BatchRecordAttendanceSchema",
"UpdateAttendanceSchema"
]
},
{
"name": "RecordAttendanceSchema",
"type": "zod",
"file": "schema.ts",
"definition": "{ studentId, classId, scheduleId?, date, status, remark? }",
"usedBy": [
"recordAttendanceAction"
]
},
{
"name": "BatchRecordAttendanceSchema",
"type": "zod",
"file": "schema.ts",
"definition": "{ records: [{ studentId, classId, scheduleId?, date, status, remark? }] }",
"usedBy": [
"batchRecordAttendanceAction"
]
},
{
"name": "UpdateAttendanceSchema",
"type": "zod",
"file": "schema.ts",
"definition": "{ status?, remark?, scheduleId? }",
"usedBy": [
"updateAttendanceAction"
]
},
{
"name": "AttendanceRuleSchema",
"type": "zod",
"file": "schema.ts",
"definition": "{ classId, lateThresholdMinutes?, earlyLeaveThresholdMinutes?, enableAutoMark? }",
"usedBy": [
"saveAttendanceRulesAction"
]
}
],
"types": [
{
"name": "AttendanceStatus",
"type": "type",
"file": "types.ts",
"definition": "'present' | 'absent' | 'late' | 'early_leave' | 'excused'",
"usedBy": [
"attendance/data-access",
"attendance/components"
]
},
{
"name": "AttendanceRecord",
"type": "type",
"file": "types.ts",
"definition": "考勤记录完整类型",
"usedBy": [
"attendance/data-access"
]
},
{
"name": "AttendanceListItem",
"type": "type",
"file": "types.ts",
"definition": "{ id, studentId, studentName, classId, className, scheduleId, date, status, remark, recordedBy, recorderName, createdAt }",
"usedBy": [
"attendance/components",
"页面"
]
},
{
"name": "AttendanceStats",
"type": "type",
"file": "types.ts",
"definition": "{ total, present, absent, late, earlyLeave, excused, presentRate, lateRate }",
"usedBy": [
"attendance-stats-card.tsx"
]
},
{
"name": "StudentAttendanceSummary",
"type": "type",
"file": "types.ts",
"definition": "{ studentId, studentName, stats: AttendanceStats, recentRecords: AttendanceListItem[] }",
"usedBy": [
"student-attendance-view.tsx"
]
},
{
"name": "ClassAttendanceSummary",
"type": "type",
"file": "types.ts",
"definition": "{ classId, className, date, stats: AttendanceStats, studentRecords: AttendanceListItem[] }",
"usedBy": [
"teacher/attendance/stats"
]
},
{
"name": "AttendanceRule",
"type": "type",
"file": "types.ts",
"definition": "{ id, classId, lateThresholdMinutes, earlyLeaveThresholdMinutes, enableAutoMark, createdAt, updatedAt }",
"usedBy": [
"attendance-rules-form.tsx"
]
},
{
"name": "AttendanceQueryParams",
"type": "type",
"file": "types.ts",
"definition": "{ classId?, studentId?, date?, startDate?, endDate?, status?, page?, pageSize? }",
"usedBy": [
"getAttendanceRecords",
"getAttendanceAction"
]
},
{
"name": "PaginatedAttendanceResult",
"type": "type",
"file": "types.ts",
"definition": "{ items: AttendanceListItem[], total, page, pageSize, totalPages }",
"usedBy": [
"getAttendanceRecords",
"getAttendanceAction"
]
},
{
"name": "ATTENDANCE_STATUS_LABELS",
"type": "const",
"file": "types.ts",
"definition": "状态中文标签常量",
"usedBy": [
"attendance/components"
]
},
{
"name": "ATTENDANCE_STATUS_COLORS",
"type": "const",
"file": "types.ts",
"definition": "状态颜色常量(用于 Badge",
"usedBy": [
"attendance/components"
]
}
],
"components": [
{
"name": "AttendanceSheet",
"file": "components/attendance-sheet.tsx",
"purpose": "批量点名表单(班级/日期选择器 + 学生表格 + 每行状态 Select + 全部标记到场按钮)"
},
{
"name": "AttendanceRecordList",
"file": "components/attendance-record-list.tsx",
"purpose": "考勤记录列表表格(含删除确认对话框)"
},
{
"name": "AttendanceStatsCard",
"file": "components/attendance-stats-card.tsx",
"purpose": "统计卡片(总数、到场、缺勤、迟到、早退、请假、出勤率、迟到率)"
},
{
"name": "AttendanceFilters",
"file": "components/attendance-filters.tsx",
"purpose": "URL 同步筛选器(班级、状态、日期)"
},
{
"name": "StudentAttendanceView",
"file": "components/student-attendance-view.tsx",
"purpose": "学生/家长视图(统计卡片 + 最近记录表格)"
},
{
"name": "AttendanceRulesForm",
"file": "components/attendance-rules-form.tsx",
"purpose": "考勤规则配置表单(班级选择器、迟到/早退阈值、自动标记勾选)"
}
]
}
},
"scheduling": {
"path": "src/modules/scheduling",
"description": "排课与调课:管理员配置班级排课规则(每日课时、连续课时、午休、上下学时间、避免背靠背、科目均衡),自动排课引擎按规则生成周课表,调课/代课申请与审批流程,课表冲突检测。",
"exports": {
"actions": [
{
"name": "saveSchedulingRulesAction",
"permission": "SCHEDULE_ADJUST",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"purpose": "保存班级排课规则upsertclassId 为空时为全局规则)",
"deps": [
"requirePermission",
"data-access.upsertSchedulingRules",
"revalidatePath"
],
"usedBy": [
"scheduling-rules-form.tsx"
]
},
{
"name": "autoScheduleAction",
"permission": "SCHEDULE_AUTO",
"signature": "(prevState: ActionState<AutoScheduleResult> | null, formData: FormData) => Promise<ActionState<AutoScheduleResult>>",
"purpose": "根据规则与科目分配生成预览课表(不落库)",
"deps": [
"requirePermission",
"data-access.getSchedulingRules",
"data-access.getClassSubjectsForScheduling",
"data-access.getClassroomsForScheduling",
"auto-scheduler.autoSchedule",
"auto-scheduler.buildDefaultTimeSlots",
"revalidatePath"
],
"usedBy": [
"auto-schedule-panel.tsx"
]
},
{
"name": "applyAutoScheduleAction",
"permission": "SCHEDULE_AUTO",
"signature": "(classId: string, schedules: Array<{ weekday, startTime, endTime, course, location }>) => Promise<ActionState<number>>",
"purpose": "将生成的课表写入 classSchedule 表(先删除该班旧课表再插入新课表,事务)",
"deps": [
"requirePermission",
"shared.db",
"shared.db.schema.classSchedule",
"@paralleldrive/cuid2",
"revalidatePath"
],
"usedBy": [
"auto-schedule-panel.tsx"
]
},
{
"name": "requestScheduleChangeAction",
"permission": "SCHEDULE_ADJUST",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"purpose": "提交调课/代课申请status=pending",
"deps": [
"requirePermission",
"data-access.createScheduleChange",
"revalidatePath"
],
"usedBy": [
"schedule-change-form.tsx"
]
},
{
"name": "approveScheduleChangeAction",
"permission": "SCHEDULE_AUTO",
"signature": "(changeId: string) => Promise<ActionState>",
"purpose": "审批通过调课申请status=approved",
"deps": [
"requirePermission",
"data-access.updateScheduleChangeStatus",
"revalidatePath"
],
"usedBy": [
"schedule-change-list.tsx"
]
},
{
"name": "rejectScheduleChangeAction",
"permission": "SCHEDULE_AUTO",
"signature": "(changeId: string, reason?: string) => Promise<ActionState>",
"purpose": "驳回调课申请status=rejected",
"deps": [
"requirePermission",
"data-access.updateScheduleChangeStatus",
"revalidatePath"
],
"usedBy": [
"schedule-change-list.tsx"
]
},
{
"name": "getScheduleChangesAction",
"permission": "SCHEDULE_ADJUST",
"signature": "(params: ScheduleChangeQueryParams) => Promise<ActionState<ScheduleChangeListItem[]>>",
"purpose": "查询调课申请列表(可按 classId/status/requesterId 过滤)",
"deps": [
"requirePermission",
"data-access.getScheduleChanges"
],
"usedBy": [
"admin/scheduling/changes/page.tsx",
"teacher/schedule-changes/page.tsx"
]
},
{
"name": "getClassConflictsAction",
"permission": "SCHEDULE_ADJUST",
"signature": "(classId: string) => Promise<ActionState<ScheduleConflict[]>>",
"purpose": "检测班级课表时间重叠冲突",
"deps": [
"requirePermission",
"data-access.getClassConflicts"
],
"usedBy": [
"schedule-conflicts-view.tsx"
]
}
],
"dataAccess": [
{
"name": "getSchedulingRules",
"signature": "(classId?: string) => Promise<SchedulingRule[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.schedulingRules"
],
"usedBy": [
"saveSchedulingRulesAction",
"autoScheduleAction",
"admin/scheduling/rules/page.tsx"
]
},
{
"name": "upsertSchedulingRules",
"signature": "(data: SchedulingRuleInput) => Promise<string>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.schedulingRules",
"@paralleldrive/cuid2"
],
"usedBy": [
"saveSchedulingRulesAction"
]
},
{
"name": "getScheduleChanges",
"signature": "(params: ScheduleChangeQueryParams) => Promise<ScheduleChangeListItem[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.scheduleChanges",
"shared.db.schema.classes",
"shared.db.schema.users"
],
"usedBy": [
"getScheduleChangesAction",
"admin/scheduling/changes/page.tsx",
"teacher/schedule-changes/page.tsx"
]
},
{
"name": "createScheduleChange",
"signature": "(data: ScheduleChangeInput, requestedBy: string) => Promise<string>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.scheduleChanges",
"@paralleldrive/cuid2"
],
"usedBy": [
"requestScheduleChangeAction"
]
},
{
"name": "updateScheduleChangeStatus",
"signature": "(id: string, status: 'approved'|'rejected'|'completed', approverId: string) => Promise<void>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.scheduleChanges"
],
"usedBy": [
"approveScheduleChangeAction",
"rejectScheduleChangeAction"
]
},
{
"name": "getClassConflicts",
"signature": "(classId: string) => Promise<ScheduleConflict[]>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.classSchedule"
],
"usedBy": [
"getClassConflictsAction"
]
},
{
"name": "getAdminClassesForScheduling",
"signature": "() => Promise<Array<{ id, name, grade }>>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.classes"
],
"usedBy": [
"admin/scheduling/rules/page.tsx",
"admin/scheduling/auto/page.tsx",
"admin/scheduling/changes/page.tsx",
"teacher/schedule-changes/page.tsx"
]
},
{
"name": "getTeachersForScheduling",
"signature": "() => Promise<Array<{ id, name, email }>>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.users",
"shared.db.schema.classSubjectTeachers"
],
"usedBy": [
"teacher/schedule-changes/page.tsx"
]
},
{
"name": "getClassroomsForScheduling",
"signature": "() => Promise<Array<{ id, name, building }>>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.classrooms"
],
"usedBy": [
"autoScheduleAction"
]
},
{
"name": "getClassSubjectsForScheduling",
"signature": "(classId: string) => Promise<Array<{ subjectId, subjectName, teacherId }>>",
"file": "data-access.ts",
"deps": [
"shared.db",
"shared.db.schema.classSubjectTeachers",
"shared.db.schema.subjects"
],
"usedBy": [
"autoScheduleAction"
]
},
{
"name": "createClassScheduleItem",
"signature": "(data: CreateClassScheduleItemInput) => Promise<string>",
"file": "data-access-class-schedule.ts",
"purpose": "创建课表项P0-5 从 classes 模块迁移,含教师班级归属校验)",
"deps": [
"classes/data-access.getTeacherIdForMutations",
"classes/data-access.verifyTeacherOwnsClass",
"data-access.insertClassScheduleItem"
],
"usedBy": [
"classes/actions.createClassScheduleItemAction"
]
},
{
"name": "updateClassScheduleItem",
"signature": "(scheduleId: string, data: UpdateClassScheduleItemInput) => Promise<void>",
"file": "data-access-class-schedule.ts",
"purpose": "更新课表项P0-5 从 classes 模块迁移,含教师班级归属校验)",
"deps": [
"classes/data-access.getTeacherIdForMutations",
"classes/data-access.verifyTeacherOwnsClass",
"data-access.updateClassScheduleItemById"
],
"usedBy": [
"classes/actions.updateClassScheduleItemAction"
]
},
{
"name": "deleteClassScheduleItem",
"signature": "(scheduleId: string) => Promise<void>",
"file": "data-access-class-schedule.ts",
"purpose": "删除课表项P0-5 从 classes 模块迁移,含教师班级归属校验)",
"deps": [
"classes/data-access.getTeacherIdForMutations",
"classes/data-access.verifyTeacherOwnsClass",
"data-access.deleteClassScheduleItemById"
],
"usedBy": [
"classes/actions.deleteClassScheduleItemAction"
]
}
],
"autoScheduler": [
{
"name": "autoSchedule",
"signature": "(params: AutoScheduleParams) => AutoScheduleResult",
"file": "auto-scheduler.ts",
"purpose": "贪心+冲突检测排课算法:按科目每周课时降序,为每节课选择第一个满足约束的时段(午休、每日窗口、班级/教师/教室冲突、每日最大课时、避免背靠背)",
"deps": [
"findOptimalSlot",
"validateSchedule"
],
"usedBy": [
"autoScheduleAction"
]
},
{
"name": "findOptimalSlot",
"signature": "(args) => TimeSlot | null",
"file": "auto-scheduler.ts",
"purpose": "在候选时段中找到第一个满足所有约束的时段",
"usedBy": [
"autoSchedule"
]
},
{
"name": "validateSchedule",
"signature": "(schedules: GeneratedSchedule[], rules: SchedulingRule) => ScheduleConflict[]",
"file": "auto-scheduler.ts",
"purpose": "校验生成的课表是否违反规则,返回冲突列表",
"usedBy": [
"autoSchedule"
]
},
{
"name": "buildDefaultTimeSlots",
"signature": "(morningStart, afternoonEnd, lunchBreakStart, lunchBreakEnd) => TimeSlot[]",
"file": "auto-scheduler.ts",
"purpose": "根据上下学时间和午休时间构建默认时段周一至周五上午4节+下午4节",
"usedBy": [
"autoScheduleAction"
]
}
],
"schemas": [
{
"name": "SchedulingRuleSchema",
"type": "zod",
"file": "schema.ts",
"definition": "{ classId, maxDailyHours?, maxContinuousHours?, lunchBreakStart?, lunchBreakEnd?, morningStart?, afternoonEnd?, avoidBackToBack?, balancedSubjects? }",
"usedBy": [
"saveSchedulingRulesAction"
]
},
{
"name": "ScheduleChangeSchema",
"type": "zod",
"file": "schema.ts",
"definition": "{ classId, originalScheduleId?, originalTeacherId?, substituteTeacherId?, originalDate?, newDate?, newStartTime?, newEndTime?, reason }",
"usedBy": [
"requestScheduleChangeAction"
]
},
{
"name": "AutoScheduleParamsSchema",
"type": "zod",
"file": "schema.ts",
"definition": "{ classId, rules: SchedulingRuleSchema, subjects: [{ subjectId, subjectName, weeklyHours, teacherId? }], teachers: [{ id, name }], classrooms: [{ id, name }], timeSlots: [{ weekday, startTime, endTime }] }",
"usedBy": [
"autoScheduleAction"
]
},
{
"name": "ScheduleChangeStatusEnum",
"type": "zod",
"file": "schema.ts",
"definition": "enum('pending','approved','rejected','completed')",
"usedBy": [
"ScheduleChangeSchema"
]
},
{
"name": "ApproveScheduleChangeSchema",
"type": "zod",
"file": "schema.ts",
"definition": "{ changeId, reason? }",
"usedBy": [
"approveScheduleChangeAction"
]
}
],
"types": [
{
"name": "ScheduleChangeStatus",
"type": "type",
"file": "types.ts",
"definition": "'pending' | 'approved' | 'rejected' | 'completed'",
"usedBy": [
"scheduling/data-access",
"scheduling/components"
]
},
{
"name": "Weekday",
"type": "type",
"file": "types.ts",
"definition": "1 | 2 | 3 | 4 | 5 | 6 | 7",
"usedBy": [
"CreateClassScheduleItemInput",
"UpdateClassScheduleItemInput"
]
},
{
"name": "CreateClassScheduleItemInput",
"type": "type",
"file": "types.ts",
"definition": "{ classId, weekday: Weekday, startTime, endTime, course, location? }P0-5 从 classes/types.ts 迁移)",
"usedBy": [
"scheduling/data-access-class-schedule.createClassScheduleItem"
]
},
{
"name": "UpdateClassScheduleItemInput",
"type": "type",
"file": "types.ts",
"definition": "{ classId?, weekday?: Weekday, startTime?, endTime?, course?, location? }P0-5 从 classes/types.ts 迁移)",
"usedBy": [
"scheduling/data-access-class-schedule.updateClassScheduleItem"
]
},
{
"name": "SchedulingRule",
"type": "type",
"file": "types.ts",
"definition": "{ id, classId, maxDailyHours, maxContinuousHours, lunchBreakStart, lunchBreakEnd, morningStart, afternoonEnd, avoidBackToBack, balancedSubjects, createdAt, updatedAt }",
"usedBy": [
"scheduling-rules-form.tsx",
"auto-scheduler.ts"
]
},
{
"name": "ScheduleChange",
"type": "type",
"file": "types.ts",
"definition": "调课申请完整类型",
"usedBy": [
"scheduling/data-access"
]
},
{
"name": "ScheduleChangeListItem",
"type": "type",
"file": "types.ts",
"definition": "{ ...ScheduleChange, className, originalTeacherName, substituteTeacherName, requesterName, approverName }",
"usedBy": [
"schedule-change-list.tsx",
"页面"
]
},
{
"name": "TimeSlot",
"type": "type",
"file": "types.ts",
"definition": "{ weekday, startTime, endTime }",
"usedBy": [
"auto-scheduler.ts"
]
},
{
"name": "ScheduleConflict",
"type": "type",
"file": "types.ts",
"definition": "{ type: 'teacher_overlap'|'classroom_overlap'|'class_overlap'|'rule_violation', description, scheduleIds }",
"usedBy": [
"auto-schedule-result.tsx",
"schedule-conflicts-view.tsx"
]
},
{
"name": "AutoScheduleResult",
"type": "type",
"file": "types.ts",
"definition": "{ success, scheduledCount, conflictCount, conflicts, schedules: GeneratedSchedule[] }",
"usedBy": [
"auto-schedule-panel.tsx",
"auto-schedule-result.tsx"
]
},
{
"name": "GeneratedSchedule",
"type": "type",
"file": "types.ts",
"definition": "{ classId, weekday, startTime, endTime, course, location, teacherId, subjectId }",
"usedBy": [
"auto-scheduler.ts",
"auto-schedule-result.tsx"
]
},
{
"name": "AutoScheduleParams",
"type": "type",
"file": "types.ts",
"definition": "{ classId, rules, subjects, teachers, classrooms, timeSlots }",
"usedBy": [
"auto-scheduler.ts",
"autoScheduleAction"
]
},
{
"name": "ScheduleChangeQueryParams",
"type": "type",
"file": "types.ts",
"definition": "{ classId?, status?, requesterId? }",
"usedBy": [
"getScheduleChanges",
"getScheduleChangesAction"
]
},
{
"name": "SCHEDULE_CHANGE_STATUS_LABELS",
"type": "const",
"file": "types.ts",
"definition": "状态英文标签常量",
"usedBy": [
"schedule-change-list.tsx"
]
},
{
"name": "SCHEDULE_CHANGE_STATUS_COLORS",
"type": "const",
"file": "types.ts",
"definition": "状态颜色常量(用于 Badge",
"usedBy": [
"schedule-change-list.tsx"
]
}
],
"components": [
{
"name": "SchedulingRulesForm",
"file": "components/scheduling-rules-form.tsx",
"purpose": "排课规则配置表单(班级选择器、每日最大课时、连续课时、午休时间、上下学时间、避免背靠背、科目均衡)"
},
{
"name": "AutoSchedulePanel",
"file": "components/auto-schedule-panel.tsx",
"purpose": "自动排课面板(班级选择→预览→应用流程,调用 autoScheduleAction 和 applyAutoScheduleAction"
},
{
"name": "AutoScheduleResultView",
"file": "components/auto-schedule-result.tsx",
"purpose": "排课结果预览(课表表格 + 冲突/警告列表)"
},
{
"name": "ScheduleChangeForm",
"file": "components/schedule-change-form.tsx",
"purpose": "调课/代课申请表单(班级、原任课教师、代课教师、原日期、新日期、新时间、原因)"
},
{
"name": "ScheduleChangeList",
"file": "components/schedule-change-list.tsx",
"purpose": "调课申请列表表格(含审批/驳回对话框canApprove 控制是否显示审批按钮)"
},
{
"name": "ScheduleConflictsView",
"file": "components/schedule-conflicts-view.tsx",
"purpose": "冲突检测视图(班级选择器 + 检测按钮 + 冲突结果列表)"
}
]
}
},
"proctoring": {
"path": "src/modules/proctoring",
"description": "考试监考模块:监考模式考试实时监控、防作弊事件采集、教师监考面板、学生端防作弊监控、考试模式配置",
"exports": {
"actions": [
{
"name": "recordProctoringEventAction",
"permission": "EXAM_SUBMIT",
"signature": "(prevState: ActionState<{id:string}> | null, formData: FormData) => Promise<ActionState<{id:string}>>",
"purpose": "学生端上报监考事件(含 submission 归属校验,刷新监考面板缓存)",
"deps": [
"requirePermission(EXAM_SUBMIT)",
"data-access.getExamSubmissionForProctoring",
"data-access.recordProctoringEvent",
"revalidatePath"
],
"usedBy": [
"anti-cheat-monitor.tsx"
]
},
{
"name": "getProctoringDashboardAction",
"permission": "EXAM_PROCTOR",
"signature": "(examId: string) => Promise<ActionState<ProctoringDashboardData>>",
"purpose": "获取监考面板数据(摘要+学生状态+最近事件)",
"deps": [
"requirePermission(EXAM_PROCTOR)",
"data-access.getExamForProctoring,getExamProctoringSummary,getStudentProctoringStatuses,getRecentProctoringEvents"
],
"usedBy": [
"proctoring-dashboard.tsx"
]
}
],
"dataAccess": [
{
"name": "getExamSubmissionForProctoring",
"signature": "(submissionId: string, studentId: string) => Promise<{id,examId,studentId} | null>",
"purpose": "校验提交记录归属(监考事件上报前的安全校验)",
"usedBy": [
"actions.recordProctoringEventAction"
]
},
{
"name": "recordProctoringEvent",
"signature": "(input: RecordProctoringEventInput) => Promise<ProctoringEvent>",
"purpose": "记录一条监考事件",
"usedBy": [
"actions.recordProctoringEventAction",
"api/proctoring/event/route.ts"
]
},
{
"name": "getProctoringEvents",
"signature": "(examId: string, filters?: GetProctoringEventsFilters) => Promise<ProctoringEventWithDetails[]>",
"purpose": "查询考试监考事件(含学生姓名、考试标题)",
"usedBy": [
"待扩展"
]
},
{
"name": "getProctoringEventsBySubmission",
"signature": "(submissionId: string) => Promise<ProctoringEvent[]>",
"purpose": "查询提交的监考事件",
"usedBy": [
"待扩展"
]
},
{
"name": "getExamProctoringSummary",
"signature": "(examId: string) => Promise<ExamProctoringSummary>",
"purpose": "获取考试监考摘要",
"usedBy": [
"actions.getProctoringDashboardAction",
"teacher/exams/[id]/proctoring/page.tsx"
]
},
{
"name": "getStudentProctoringStatuses",
"signature": "(examId: string) => Promise<StudentProctoringStatus[]>",
"purpose": "获取所有学生监考状态",
"usedBy": [
"actions.getProctoringDashboardAction",
"teacher/exams/[id]/proctoring/page.tsx"
]
},
{
"name": "getExamForProctoring",
"signature": "(examId: string) => Promise<{id,title,examMode,config} | null>",
"purpose": "获取考试信息(含 examMode 设置)",
"usedBy": [
"actions.getProctoringDashboardAction",
"teacher/exams/[id]/proctoring/page.tsx"
]
},
{
"name": "getRecentProctoringEvents",
"signature": "(examId: string, limit?: number) => Promise<ProctoringEventWithDetails[]>",
"purpose": "获取最近 N 条监考事件",
"usedBy": [
"actions.getProctoringDashboardAction",
"teacher/exams/[id]/proctoring/page.tsx"
]
}
],
"types": [
{
"name": "ProctoringEventType",
"type": "type",
"definition": "\"tab_switch\" | \"window_blur\" | \"copy_attempt\" | \"paste_attempt\" | \"right_click\" | \"devtools_open\" | \"fullscreen_exit\" | \"idle_timeout\""
},
{
"name": "ExamMode",
"type": "type",
"definition": "\"homework\" | \"timed\" | \"proctored\""
},
{
"name": "ProctoringEvent",
"type": "interface",
"definition": "{ id, submissionId, studentId, examId, eventType, eventDetail?, occurredAt, createdAt }"
},
{
"name": "ProctoringEventWithDetails",
"type": "interface",
"definition": "ProctoringEvent & { studentName, examTitle }"
},
{
"name": "ExamProctoringSummary",
"type": "interface",
"definition": "{ examId, examTitle, examMode, totalStudents, startedStudents, submittedStudents, totalEvents, abnormalStudents, eventsByType }"
},
{
"name": "StudentProctoringStatus",
"type": "interface",
"definition": "{ studentId, studentName, submissionId, submissionStatus, eventCount, lastEventAt, isAbnormal, eventsByType }"
},
{
"name": "ProctoringDashboardData",
"type": "interface",
"definition": "{ summary, students, recentEvents }"
},
{
"name": "ExamModeConfig",
"type": "interface",
"definition": "{ examMode, durationMinutes, shuffleQuestions, allowLateStart, lateStartGraceMinutes, antiCheatEnabled }"
},
{
"name": "PROCTORING_EVENT_LABELS",
"type": "const",
"description": "事件类型中文标签常量"
},
{
"name": "EXAM_MODE_LABELS",
"type": "const",
"description": "考试模式中文标签常量"
},
{
"name": "ABNORMAL_EVENT_THRESHOLD",
"type": "const",
"description": "异常学生事件数阈值3"
}
],
"components": [
{
"name": "ProctoringDashboard",
"file": "components/proctoring-dashboard.tsx",
"purpose": "教师监考面板实时学生状态、异常事件统计、异常学生高亮、10 秒轮询、usePermission 权限控制)"
},
{
"name": "AntiCheatMonitor",
"file": "components/anti-cheat-monitor.tsx",
"purpose": "学生端防作弊监控visibilitychange/blur/copy/paste/contextmenu/keydown/fullscreenchange 监听、空闲超时检测、强制全屏、警告提示、事件上报)"
},
{
"name": "ExamModeConfig",
"file": "components/exam-mode-config.tsx",
"purpose": "考试模式配置react-hook-form Controller作业/限时/监考模式选择,限时设置时长,监考设置防作弊选项)"
}
]
}
},
"diagnostic": {
"path": "src/modules/diagnostic",
"description": "学情诊断报告模块基于知识点掌握度knowledgePointMastery生成个人/班级诊断报告,掌握度雷达图(学生 vs 班级平均),强项/弱项分析,知识点掌握度热力图,需重点关注学生列表,报告发布/删除管理",
"exports": {
"dataAccess": [
{
"name": "getStudentMastery",
"signature": "(studentId: string) => Promise<MasteryWithKnowledgePoint[]>",
"file": "data-access.ts",
"purpose": "获取学生在所有知识点的掌握度(含知识点名称,按掌握度降序)",
"deps": [
"shared.db",
"shared.db.schema.knowledgePointMastery",
"shared.db.schema.knowledgePoints"
],
"usedBy": [
"data-access.getStudentMasterySummary",
"teacher/diagnostic/student/[studentId]/page.tsx"
]
},
{
"name": "getStudentMasterySummary",
"signature": "(studentId: string) => Promise<StudentMasterySummary | null>",
"file": "data-access.ts",
"purpose": "获取学生掌握度摘要平均掌握度、强项≥80%、弱项<60%",
"deps": [
"shared.db",
"shared.db.schema.users",
"data-access.getStudentMastery"
],
"usedBy": [
"data-access-reports.generateDiagnosticReport",
"teacher/diagnostic/student/[studentId]/page.tsx",
"student/diagnostic/page.tsx"
]
},
{
"name": "updateMasteryFromSubmission",
"signature": "(submissionId: string) => Promise<void>",
"file": "data-access.ts",
"purpose": "从提交答案更新掌握度按知识点聚合正确率onDuplicateKeyUpdate upsert",
"deps": [
"shared.db",
"shared.db.schema.examSubmissions",
"shared.db.schema.submissionAnswers",
"shared.db.schema.questionsToKnowledgePoints",
"shared.db.schema.knowledgePointMastery"
],
"usedBy": [
"待扩展(作业/考试提交后触发)"
]
},
{
"name": "getClassMasterySummary",
"signature": "(classId: string) => Promise<ClassMasterySummary | null>",
"file": "data-access.ts",
"purpose": "获取班级掌握度摘要(学生数、平均掌握度、知识点统计、需重点关注学生)",
"deps": [
"shared.db",
"shared.db.schema.classes",
"shared.db.schema.classEnrollments",
"shared.db.schema.users",
"shared.db.schema.knowledgePointMastery",
"shared.db.schema.knowledgePoints"
],
"usedBy": [
"data-access-reports.generateClassDiagnosticReport",
"teacher/diagnostic/class/[classId]/page.tsx"
]
},
{
"name": "getKnowledgePointStats",
"signature": "(classId?: string, gradeId?: string) => Promise<KnowledgePointStat[]>",
"file": "data-access.ts",
"purpose": "获取知识点统计(按班级或年级聚合平均掌握度、掌握人数、未掌握人数)",
"deps": [
"shared.db",
"shared.db.schema.classEnrollments",
"shared.db.schema.users",
"shared.db.schema.knowledgePointMastery",
"shared.db.schema.knowledgePoints"
],
"usedBy": [
"teacher/diagnostic/student/[studentId]/page.tsx (班级平均对比)"
]
},
{
"name": "generateDiagnosticReport",
"signature": "(studentId: string, period: string, generatedBy: string) => Promise<string>",
"file": "data-access-reports.ts",
"purpose": "生成个人诊断报告(计算 overallScore、强项/弱项列表、复习建议status=draft",
"deps": [
"shared.db",
"shared.db.schema.learningDiagnosticReports",
"data-access.getStudentMasterySummary",
"@paralleldrive/cuid2"
],
"usedBy": [
"actions.generateStudentReportAction"
]
},
{
"name": "generateClassDiagnosticReport",
"signature": "(classId: string, period: string, generatedBy: string) => Promise<string>",
"file": "data-access-reports.ts",
"purpose": "生成班级诊断报告聚合班级掌握度识别薄弱知识点status=draftstudentId 存生成者 ID",
"deps": [
"shared.db",
"shared.db.schema.learningDiagnosticReports",
"data-access.getClassMasterySummary",
"@paralleldrive/cuid2"
],
"usedBy": [
"actions.generateClassReportAction"
]
},
{
"name": "getDiagnosticReports",
"signature": "(filters: DiagnosticReportQueryParams) => Promise<DiagnosticReportWithDetails[]>",
"file": "data-access-reports.ts",
"purpose": "查询诊断报告列表(可按 studentId/reportType/status/period 过滤,含学生名和生成者名)",
"deps": [
"shared.db",
"shared.db.schema.learningDiagnosticReports",
"shared.db.schema.users"
],
"usedBy": [
"actions.getDiagnosticReportsAction",
"teacher/diagnostic/page.tsx",
"teacher/diagnostic/student/[studentId]/page.tsx",
"student/diagnostic/page.tsx"
]
},
{
"name": "getDiagnosticReportById",
"signature": "(id: string) => Promise<DiagnosticReportWithDetails | null>",
"file": "data-access-reports.ts",
"purpose": "获取报告详情(含学生名和生成者名)",
"deps": [
"shared.db",
"shared.db.schema.learningDiagnosticReports",
"shared.db.schema.users"
],
"usedBy": [
"actions.getDiagnosticReportByIdAction"
]
},
{
"name": "publishDiagnosticReport",
"signature": "(id: string) => Promise<void>",
"file": "data-access-reports.ts",
"purpose": "发布诊断报告status=published",
"deps": [
"shared.db",
"shared.db.schema.learningDiagnosticReports"
],
"usedBy": [
"actions.publishReportAction"
]
},
{
"name": "deleteDiagnosticReport",
"signature": "(id: string) => Promise<void>",
"file": "data-access-reports.ts",
"purpose": "删除诊断报告",
"deps": [
"shared.db",
"shared.db.schema.learningDiagnosticReports"
],
"usedBy": [
"actions.deleteReportAction"
]
}
],
"actions": [
{
"name": "generateStudentReportAction",
"permission": "DIAGNOSTIC_MANAGE",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"file": "actions.ts",
"purpose": "生成学生个人诊断报告formData: studentId, period",
"deps": [
"requirePermission",
"data-access-reports.generateDiagnosticReport",
"revalidatePath"
],
"usedBy": [
"components/student-diagnostic-view.tsx"
]
},
{
"name": "generateClassReportAction",
"permission": "DIAGNOSTIC_MANAGE",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"file": "actions.ts",
"purpose": "生成班级诊断报告formData: classId, period",
"deps": [
"requirePermission",
"data-access-reports.generateClassDiagnosticReport",
"revalidatePath"
],
"usedBy": [
"components/class-diagnostic-view.tsx"
]
},
{
"name": "publishReportAction",
"permission": "DIAGNOSTIC_MANAGE",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"file": "actions.ts",
"purpose": "发布诊断报告formData: id",
"deps": [
"requirePermission",
"data-access-reports.publishDiagnosticReport",
"revalidatePath"
],
"usedBy": [
"components/report-list.tsx"
]
},
{
"name": "deleteReportAction",
"permission": "DIAGNOSTIC_MANAGE",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"file": "actions.ts",
"purpose": "删除诊断报告formData: id",
"deps": [
"requirePermission",
"data-access-reports.deleteDiagnosticReport",
"revalidatePath"
],
"usedBy": [
"components/report-list.tsx"
]
},
{
"name": "getDiagnosticReportsAction",
"permission": "DIAGNOSTIC_READ",
"signature": "(params: DiagnosticReportQueryParams) => Promise<ActionState<DiagnosticReportWithDetails[]>>",
"file": "actions.ts",
"purpose": "查询诊断报告列表(读权限)",
"deps": [
"requirePermission",
"data-access-reports.getDiagnosticReports"
],
"usedBy": [
"待扩展"
]
},
{
"name": "getDiagnosticReportByIdAction",
"permission": "DIAGNOSTIC_READ",
"signature": "(id: string) => Promise<ActionState<DiagnosticReportWithDetails | null>>",
"file": "actions.ts",
"purpose": "获取诊断报告详情(读权限)",
"deps": [
"requirePermission",
"data-access-reports.getDiagnosticReportById"
],
"usedBy": [
"待扩展"
]
}
],
"schema": [
{
"name": "GenerateStudentReportSchema",
"type": "const",
"file": "schema.ts",
"description": "zod schema 生成学生个人诊断报告studentId, period",
"usedBy": [
"actions.generateStudentReportAction"
]
},
{
"name": "GenerateClassReportSchema",
"type": "const",
"file": "schema.ts",
"description": "zod schema 生成班级诊断报告classId, period",
"usedBy": [
"actions.generateClassReportAction"
]
},
{
"name": "PublishReportSchema",
"type": "const",
"file": "schema.ts",
"description": "zod schema 发布诊断报告id",
"usedBy": [
"actions.publishReportAction"
]
},
{
"name": "DeleteReportSchema",
"type": "const",
"file": "schema.ts",
"description": "zod schema 删除诊断报告id",
"usedBy": [
"actions.deleteReportAction"
]
},
{
"name": "GetDiagnosticReportsSchema",
"type": "const",
"file": "schema.ts",
"description": "zod schema 查询诊断报告列表studentId?, reportType?, status?, period?",
"usedBy": [
"actions.getDiagnosticReportsAction"
]
},
{
"name": "GetDiagnosticReportByIdSchema",
"type": "const",
"file": "schema.ts",
"description": "zod schema 获取诊断报告详情id",
"usedBy": [
"actions.getDiagnosticReportByIdAction"
]
}
],
"types": [
{
"name": "DiagnosticReportType",
"type": "type",
"file": "types.ts",
"definition": "\"individual\" | \"class\" | \"grade\"",
"usedBy": [
"types.DiagnosticReport.reportType",
"actions",
"components/report-list.tsx"
]
},
{
"name": "DiagnosticReportStatus",
"type": "type",
"file": "types.ts",
"definition": "\"draft\" | \"published\" | \"archived\"",
"usedBy": [
"types.DiagnosticReport.status",
"actions",
"components/report-list.tsx"
]
},
{
"name": "KnowledgePointMastery",
"type": "interface",
"file": "types.ts",
"definition": "{ id, studentId, knowledgePointId, masteryLevel(0-100), totalQuestions, correctQuestions, lastAssessedAt, createdAt, updatedAt }",
"usedBy": [
"data-access",
"types.MasteryWithKnowledgePoint"
]
},
{
"name": "MasteryWithKnowledgePoint",
"type": "interface",
"file": "types.ts",
"definition": "KnowledgePointMastery & { knowledgePointName, knowledgePointDescription }",
"usedBy": [
"data-access.getStudentMastery",
"types.StudentMasterySummary"
]
},
{
"name": "StudentMasterySummary",
"type": "interface",
"file": "types.ts",
"definition": "{ studentId, studentName, averageMastery, totalKnowledgePoints, strengths(≥80), weaknesses(<60), allMastery }",
"usedBy": [
"data-access.getStudentMasterySummary",
"data-access-reports.generateDiagnosticReport",
"components/student-diagnostic-view.tsx"
]
},
{
"name": "DiagnosticReport",
"type": "interface",
"file": "types.ts",
"definition": "{ id, studentId, generatedBy, reportType, period, summary, strengths[], weaknesses[], recommendations[], overallScore, status, createdAt, updatedAt }",
"usedBy": [
"data-access-reports",
"types.DiagnosticReportWithDetails"
]
},
{
"name": "DiagnosticReportWithDetails",
"type": "interface",
"file": "types.ts",
"definition": "DiagnosticReport & { studentName, generatedByName }",
"usedBy": [
"data-access-reports.getDiagnosticReports",
"actions",
"components/report-list.tsx",
"components/student-diagnostic-view.tsx"
]
},
{
"name": "ClassMasterySummary",
"type": "interface",
"file": "types.ts",
"definition": "{ classId, className, studentCount, averageMastery, knowledgePointStats[], studentsNeedingAttention[] }",
"usedBy": [
"data-access.getClassMasterySummary",
"data-access-reports.generateClassDiagnosticReport",
"components/class-diagnostic-view.tsx"
]
},
{
"name": "KnowledgePointStat",
"type": "interface",
"file": "types.ts",
"definition": "{ knowledgePointId, knowledgePointName, averageMastery, masteredCount(≥80), notMasteredCount(<60), totalStudents }",
"usedBy": [
"data-access.getKnowledgePointStats",
"types.ClassMasterySummary",
"components/class-diagnostic-view.tsx"
]
},
{
"name": "DiagnosticReportQueryParams",
"type": "interface",
"file": "types.ts",
"definition": "{ studentId?, reportType?, status?, period? }",
"usedBy": [
"data-access-reports.getDiagnosticReports",
"actions.getDiagnosticReportsAction"
]
},
{
"name": "MasteryRadarPoint",
"type": "interface",
"file": "types.ts",
"definition": "{ knowledgePoint, student(0-100), classAverage?(0-100) }",
"usedBy": [
"components/mastery-radar-chart.tsx",
"components/student-diagnostic-view.tsx",
"teacher/diagnostic/student/[studentId]/page.tsx"
]
}
],
"components": [
{
"name": "MasteryRadarChart",
"file": "components/mastery-radar-chart.tsx",
"purpose": "知识点掌握度雷达图recharts RadarChart学生 vs 班级平均对比,无数据时显示 EmptyState",
"deps": [
"recharts",
"shared/components/ui/card",
"shared/components/ui/chart",
"shared/components/ui/empty-state"
]
},
{
"name": "StudentDiagnosticView",
"file": "components/student-diagnostic-view.tsx",
"purpose": "学生诊断视图(概览卡片、雷达图、强项/弱项列表、生成报告表单[DIAGNOSTIC_MANAGE]、最新报告与建议展示)",
"deps": [
"usePermission",
"actions.generateStudentReportAction",
"components/mastery-radar-chart",
"shared/components/ui/*"
]
},
{
"name": "ClassDiagnosticView",
"file": "components/class-diagnostic-view.tsx",
"purpose": "班级诊断视图(概览卡片、知识点掌握度热力图[绿/黄/橙/红]、知识点排名表、需重点关注学生表[链接到学生视图]、生成班级报告表单[DIAGNOSTIC_MANAGE]",
"deps": [
"usePermission",
"actions.generateClassReportAction",
"shared/components/ui/*"
]
},
{
"name": "ReportList",
"file": "components/report-list.tsx",
"purpose": "诊断报告列表reportType/status 过滤器[URL searchParams]、报告表格、发布/删除操作[DIAGNOSTIC_MANAGE]、确认对话框)",
"deps": [
"usePermission",
"actions.publishReportAction",
"actions.deleteReportAction",
"shared/components/ui/*"
]
}
]
}
},
"elective": {
"path": "src/modules/elective",
"description": "选课管理模块:选修课程 CRUD、选课开放/关闭、学生选课/退课、抽签模式批量录取、FCFS 即时录取、DataScope 行级过滤admin 全部、teacher 所教、grade_head 所管年级、student 可选课程)",
"exports": {
"actions": [
{
"name": "createElectiveCourseAction",
"permission": "ELECTIVE_MANAGE",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"file": "actions.ts",
"purpose": "创建选修课程formData: name, subjectId?, teacherId, gradeId?, description?, capacity?, classroom?, schedule?, startDate?, endDate?, selectionStartAt?, selectionEndAt?, selectionMode?, credit?",
"deps": [
"requirePermission(ELECTIVE_MANAGE)",
"data-access.createElectiveCourse",
"revalidatePath"
],
"usedBy": [
"admin/elective/create/page.tsx"
]
},
{
"name": "updateElectiveCourseAction",
"permission": "ELECTIVE_MANAGE",
"signature": "(id: string, prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"file": "actions.ts",
"purpose": "更新选修课程",
"deps": [
"requirePermission(ELECTIVE_MANAGE)",
"data-access.getElectiveCourseById",
"data-access.updateElectiveCourse",
"revalidatePath"
],
"usedBy": [
"admin/elective/[id]/edit/page.tsx"
]
},
{
"name": "deleteElectiveCourseAction",
"permission": "ELECTIVE_MANAGE",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"file": "actions.ts",
"purpose": "删除选修课程formData: courseId",
"deps": [
"requirePermission(ELECTIVE_MANAGE)",
"data-access.deleteElectiveCourse",
"revalidatePath"
],
"usedBy": [
"components/elective-course-list.tsx"
]
},
{
"name": "openSelectionAction",
"permission": "ELECTIVE_MANAGE",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"file": "actions.ts",
"purpose": "开放选课formData: courseId",
"deps": [
"requirePermission(ELECTIVE_MANAGE)",
"data-access.openSelection",
"revalidatePath"
],
"usedBy": [
"components/elective-course-list.tsx"
]
},
{
"name": "closeSelectionAction",
"permission": "ELECTIVE_MANAGE",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"file": "actions.ts",
"purpose": "关闭选课formData: courseId",
"deps": [
"requirePermission(ELECTIVE_MANAGE)",
"data-access.closeSelection",
"revalidatePath"
],
"usedBy": [
"components/elective-course-list.tsx"
]
},
{
"name": "runLotteryAction",
"permission": "ELECTIVE_MANAGE",
"signature": "(prevState: ActionState<{enrolled:number,waitlist:number}> | null, formData: FormData) => Promise<ActionState<{enrolled:number,waitlist:number}>>",
"file": "actions.ts",
"purpose": "执行抽签录取formData: courseId",
"deps": [
"requirePermission(ELECTIVE_MANAGE)",
"data-access-operations.runLottery",
"revalidatePath"
],
"usedBy": [
"components/elective-course-list.tsx"
]
},
{
"name": "selectCourseAction",
"permission": "ELECTIVE_SELECT",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"file": "actions.ts",
"purpose": "学生选课formData: courseId, priority?",
"deps": [
"requirePermission(ELECTIVE_SELECT)",
"data-access-operations.selectCourse",
"revalidatePath"
],
"usedBy": [
"components/student-selection-view.tsx"
]
},
{
"name": "dropCourseAction",
"permission": "ELECTIVE_SELECT",
"signature": "(prevState: ActionState<string> | null, formData: FormData) => Promise<ActionState<string>>",
"file": "actions.ts",
"purpose": "学生退课formData: courseId",
"deps": [
"requirePermission(ELECTIVE_SELECT)",
"data-access-operations.dropCourse",
"revalidatePath"
],
"usedBy": [
"components/student-selection-view.tsx"
]
},
{
"name": "getElectiveCoursesAction",
"permission": "ELECTIVE_READ",
"signature": "(params?: GetElectiveCoursesParams) => Promise<ActionState<ElectiveCourseWithDetails[]>>",
"file": "actions.ts",
"purpose": "查询选修课程列表(按 DataScope 过滤)",
"deps": [
"requirePermission(ELECTIVE_READ)",
"data-access.getElectiveCourses (scope, currentUserId)"
],
"usedBy": [
"admin/elective/page.tsx",
"teacher/elective/page.tsx"
]
},
{
"name": "getStudentSelectionsAction",
"permission": "ELECTIVE_READ",
"signature": "(studentId: string) => Promise<ActionState<CourseSelectionWithDetails[]>>",
"file": "actions.ts",
"purpose": "查询学生选课记录(含 DataScope 二次校验class_members 仅自己children 仅子女)",
"deps": [
"requirePermission(ELECTIVE_READ)",
"data-access-selections.getStudentSelections"
],
"usedBy": [
"待扩展"
]
},
{
"name": "getAvailableCoursesAction",
"permission": "ELECTIVE_SELECT",
"signature": "() => Promise<ActionState<ElectiveCourseWithDetails[]>>",
"file": "actions.ts",
"purpose": "获取学生可选课程status=open 且匹配年级)",
"deps": [
"requirePermission(ELECTIVE_SELECT)",
"data-access-selections.getAvailableCoursesForStudent"
],
"usedBy": [
"待扩展"
]
}
],
"dataAccess": [
{
"name": "getElectiveCourses",
"file": "data-access.ts",
"signature": "(params?: GetElectiveCoursesParams & { scope?: DataScope; currentUserId?: string }) => Promise<ElectiveCourseWithDetails[]>",
"purpose": "查询选修课程列表(按 scope 行级过滤owned/class_taught 按 teacherIdgrade_managed 按 gradeIds",
"usedBy": [
"actions.getElectiveCoursesAction",
"admin/elective/page.tsx",
"teacher/elective/page.tsx"
]
},
{
"name": "getElectiveCourseById",
"file": "data-access.ts",
"signature": "(id: string) => Promise<ElectiveCourseWithDetails | null>",
"purpose": "获取课程详情",
"usedBy": [
"actions.updateElectiveCourseAction",
"admin/elective/[id]/edit/page.tsx"
]
},
{
"name": "createElectiveCourse",
"file": "data-access.ts",
"signature": "(data: CreateElectiveCourseInput, teacherId: string) => Promise<string>",
"purpose": "创建选修课程status=draft, enrolledCount=0",
"usedBy": [
"actions.createElectiveCourseAction"
]
},
{
"name": "updateElectiveCourse",
"file": "data-access.ts",
"signature": "(id: string, data: Partial<UpdateElectiveCourseInput>) => Promise<void>",
"purpose": "更新选修课程字段",
"usedBy": [
"actions.updateElectiveCourseAction"
]
},
{
"name": "deleteElectiveCourse",
"file": "data-access.ts",
"signature": "(id: string) => Promise<void>",
"purpose": "删除选修课程",
"usedBy": [
"actions.deleteElectiveCourseAction"
]
},
{
"name": "openSelection",
"file": "data-access.ts",
"signature": "(courseId: string) => Promise<void>",
"purpose": "开放选课status=open",
"usedBy": [
"actions.openSelectionAction"
]
},
{
"name": "closeSelection",
"file": "data-access.ts",
"signature": "(courseId: string) => Promise<void>",
"purpose": "关闭选课status=closed",
"usedBy": [
"actions.closeSelectionAction"
]
},
{
"name": "getSubjectOptions",
"file": "data-access.ts",
"signature": "() => Promise<{id, name}[]>",
"purpose": "获取学科选项(按 order, name 排序)",
"usedBy": [
"admin/elective/create/page.tsx",
"admin/elective/[id]/edit/page.tsx"
]
},
{
"name": "getCourseSelections",
"file": "data-access-selections.ts",
"signature": "(courseId: string) => Promise<CourseSelectionWithDetails[]>",
"purpose": "查询课程所有选课记录(按 priority, selectedAt 排序)",
"usedBy": [
"待扩展"
]
},
{
"name": "getStudentSelections",
"file": "data-access-selections.ts",
"signature": "(studentId: string) => Promise<CourseSelectionWithDetails[]>",
"purpose": "查询学生选课记录(按 selectedAt 降序)",
"usedBy": [
"actions.getStudentSelectionsAction",
"student/elective/page.tsx"
]
},
{
"name": "getStudentGradeId",
"file": "data-access-selections.ts",
"signature": "(studentId: string) => Promise<string | null>",
"purpose": "获取学生所在年级 ID通过 classEnrollments active 记录)",
"usedBy": [
"data-access-selections.getAvailableCoursesForStudent"
]
},
{
"name": "getAvailableCoursesForStudent",
"file": "data-access-selections.ts",
"signature": "(studentId: string, gradeId?: string | null) => Promise<ElectiveCourseWithDetails[]>",
"purpose": "获取学生可选课程status=open 且 gradeId 匹配或为空)",
"usedBy": [
"actions.getAvailableCoursesAction",
"student/elective/page.tsx"
]
},
{
"name": "runLottery",
"file": "data-access-operations.ts",
"signature": "(courseId: string) => Promise<{enrolled: number, waitlist: number}>",
"purpose": "抽签录取(随机打乱 selected 记录,前 capacity 名 enrolled其余 waitlist课程 status=closed",
"usedBy": [
"actions.runLotteryAction"
]
},
{
"name": "selectCourse",
"file": "data-access-operations.ts",
"signature": "(courseId: string, studentId: string, priority?: number) => Promise<{status: CourseSelectionStatus, message: string}>",
"purpose": "学生选课(校验课程状态/时间窗口/重复选课FCFS 模式即时 enrolled/waitlistlottery 模式 selected",
"usedBy": [
"actions.selectCourseAction"
]
},
{
"name": "dropCourse",
"file": "data-access-operations.ts",
"signature": "(courseId: string, studentId: string) => Promise<void>",
"purpose": "学生退课status=droppedFCFS 模式自动递补 waitlist 首位)",
"usedBy": [
"actions.dropCourseAction"
]
}
],
"types": [
{
"name": "ElectiveCourseStatus",
"type": "type",
"file": "types.ts",
"definition": "\"draft\" | \"open\" | \"closed\" | \"cancelled\""
},
{
"name": "ElectiveSelectionMode",
"type": "type",
"file": "types.ts",
"definition": "\"fcfs\" | \"lottery\""
},
{
"name": "CourseSelectionStatus",
"type": "type",
"file": "types.ts",
"definition": "\"selected\" | \"enrolled\" | \"waitlist\" | \"dropped\" | \"rejected\""
},
{
"name": "ElectiveCourse",
"type": "interface",
"file": "types.ts",
"definition": "{ id, name, subjectId?, teacherId, gradeId?, description?, capacity, enrolledCount, classroom?, schedule?, startDate?, endDate?, selectionStartAt?, selectionEndAt?, status, selectionMode, credit, createdAt, updatedAt }"
},
{
"name": "ElectiveCourseWithDetails",
"type": "interface",
"file": "types.ts",
"definition": "ElectiveCourse & { teacherName?, subjectName?, gradeName? }"
},
{
"name": "CourseSelection",
"type": "interface",
"file": "types.ts",
"definition": "{ id, courseId, studentId, status, priority?, selectedAt, enrolledAt?, droppedAt?, lotteryRank?, createdAt, updatedAt }"
},
{
"name": "CourseSelectionWithDetails",
"type": "interface",
"file": "types.ts",
"definition": "CourseSelection & { courseName?, studentName?, courseCapacity?, courseEnrolledCount?, courseStatus? }"
},
{
"name": "GetElectiveCoursesParams",
"type": "interface",
"file": "types.ts",
"definition": "{ status?, gradeId?, subjectId?, teacherId? }"
},
{
"name": "ELECTIVE_STATUS_LABELS",
"type": "const",
"file": "types.ts",
"description": "课程状态标签常量"
},
{
"name": "ELECTIVE_STATUS_COLORS",
"type": "const",
"file": "types.ts",
"description": "课程状态颜色常量Badge variant"
},
{
"name": "SELECTION_MODE_LABELS",
"type": "const",
"file": "types.ts",
"description": "选课模式标签常量"
},
{
"name": "COURSE_SELECTION_STATUS_LABELS",
"type": "const",
"file": "types.ts",
"description": "选课状态标签常量"
},
{
"name": "COURSE_SELECTION_STATUS_COLORS",
"type": "const",
"file": "types.ts",
"description": "选课状态颜色常量Badge variant"
}
],
"schemas": [
{
"name": "ElectiveCourseStatusEnum",
"file": "schema.ts",
"definition": "z.enum([\"draft\",\"open\",\"closed\",\"cancelled\"])"
},
{
"name": "ElectiveSelectionModeEnum",
"file": "schema.ts",
"definition": "z.enum([\"fcfs\",\"lottery\"])"
},
{
"name": "CourseSelectionStatusEnum",
"file": "schema.ts",
"definition": "z.enum([\"selected\",\"enrolled\",\"waitlist\",\"dropped\",\"rejected\"])"
},
{
"name": "CreateElectiveCourseSchema",
"file": "schema.ts",
"purpose": "创建课程校验name 必填teacherId 必填capacity 1-500 默认 30selectionMode 默认 fcfscredit 默认 1.0"
},
{
"name": "UpdateElectiveCourseSchema",
"file": "schema.ts",
"purpose": "更新课程校验(所有字段可选,含 status"
},
{
"name": "SelectCourseSchema",
"file": "schema.ts",
"purpose": "选课校验courseId 必填priority 1-10 可选)"
},
{
"name": "DropCourseSchema",
"file": "schema.ts",
"purpose": "退课校验courseId 必填)"
},
{
"name": "RunLotterySchema",
"file": "schema.ts",
"purpose": "抽签校验courseId 必填)"
}
],
"components": [
{
"name": "ElectiveCourseList",
"file": "components/elective-course-list.tsx",
"purpose": "课程卡片列表(管理员/教师视图,含编辑/开放/关闭/抽签/删除操作按钮usePermission 控制权限)",
"deps": [
"usePermission",
"actions.deleteElectiveCourseAction",
"actions.openSelectionAction",
"actions.closeSelectionAction",
"actions.runLotteryAction",
"shared/components/ui/*"
]
},
{
"name": "ElectiveCourseForm",
"file": "components/elective-course-form.tsx",
"purpose": "课程创建/编辑表单name, subjectId, teacherId, gradeId, description, capacity, classroom, schedule, dates, selectionMode, credit",
"deps": [
"react-hook-form",
"actions.createElectiveCourseAction",
"actions.updateElectiveCourseAction",
"shared/components/ui/*"
]
},
{
"name": "StudentSelectionView",
"file": "components/student-selection-view.tsx",
"purpose": "学生选课视图(可选课程列表 + 我的选课记录,含选课/退课按钮)",
"deps": [
"usePermission",
"actions.selectCourseAction",
"actions.dropCourseAction",
"shared/components/ui/*"
]
}
]
}
},
"lesson_preparation": {
"path": "src/modules/lesson-preparation",
"description": "教师备课模块基于教材章节创建课案Block 编辑器),支持模板、版本管理、知识点标注、题目创建/拉取、作业发布",
"exports": {
"dataAccess": [
{
"name": "getLessonPlans",
"file": "data-access.ts",
"purpose": "查询课案列表(按教师/教材/章节过滤)"
},
{
"name": "getLessonPlanById",
"file": "data-access.ts",
"purpose": "按 ID 获取课案详情"
},
{
"name": "createLessonPlan",
"file": "data-access.ts",
"purpose": "创建课案"
},
{
"name": "updateLessonPlanContent",
"file": "data-access.ts",
"purpose": "更新课案内容Block JSON"
},
{
"name": "softDeleteLessonPlan",
"file": "data-access.ts",
"purpose": "软删除课案"
},
{
"name": "duplicateLessonPlan",
"file": "data-access.ts",
"purpose": "复制课案"
},
{
"name": "getTemplateById",
"file": "data-access.ts",
"purpose": "按 ID 获取模板"
},
{
"name": "buildInitialContent",
"file": "data-access.ts",
"purpose": "基于模板构建初始课案内容"
},
{
"name": "getLessonPlanVersions",
"file": "data-access-versions.ts",
"purpose": "查询课案版本列表"
},
{
"name": "createLessonPlanVersion",
"file": "data-access-versions.ts",
"purpose": "创建课案版本(手动/自动)"
},
{
"name": "getVersionContent",
"file": "data-access-versions.ts",
"purpose": "获取指定版本内容"
},
{
"name": "revertToVersion",
"file": "data-access-versions.ts",
"purpose": "回滚到指定版本"
},
{
"name": "pruneAutoVersions",
"file": "data-access-versions.ts",
"purpose": "清理自动版本(保留最近 N 个)"
},
{
"name": "getLessonPlanTemplates",
"file": "data-access-templates.ts",
"purpose": "查询模板列表(系统/个人)"
},
{
"name": "saveAsTemplate",
"file": "data-access-templates.ts",
"purpose": "将课案保存为个人模板"
},
{
"name": "deletePersonalTemplate",
"file": "data-access-templates.ts",
"purpose": "删除个人模板"
},
{
"name": "getLessonPlansByKnowledgePoint",
"file": "data-access-knowledge.ts",
"purpose": "按知识点反查课案"
},
{
"name": "getLessonPlansByQuestion",
"file": "data-access-knowledge.ts",
"purpose": "按题目反查课案"
},
{
"name": "publishLessonPlanHomework",
"file": "publish-service.ts",
"purpose": "发布课案为作业(编排 homework/exams/classes"
},
{
"name": "suggestKnowledgePoints",
"file": "ai-suggest.ts",
"purpose": "AI 建议知识点(基于课案内容)"
}
],
"actions": [
{
"name": "getLessonPlansAction",
"permission": "LESSON_PLAN_READ",
"file": "actions.ts",
"purpose": "查询课案列表"
},
{
"name": "getLessonPlanByIdAction",
"permission": "LESSON_PLAN_READ",
"file": "actions.ts",
"purpose": "获取课案详情"
},
{
"name": "createLessonPlanAction",
"permission": "LESSON_PLAN_CREATE",
"file": "actions.ts",
"purpose": "创建课案"
},
{
"name": "updateLessonPlanAction",
"permission": "LESSON_PLAN_UPDATE",
"file": "actions.ts",
"purpose": "更新课案内容"
},
{
"name": "saveLessonPlanVersionAction",
"permission": "LESSON_PLAN_UPDATE",
"file": "actions.ts",
"purpose": "保存课案版本"
},
{
"name": "getLessonPlanVersionsAction",
"permission": "LESSON_PLAN_READ",
"file": "actions.ts",
"purpose": "查询课案版本列表"
},
{
"name": "revertLessonPlanVersionAction",
"permission": "LESSON_PLAN_UPDATE",
"file": "actions.ts",
"purpose": "回滚到指定版本"
},
{
"name": "deleteLessonPlanAction",
"permission": "LESSON_PLAN_DELETE",
"file": "actions.ts",
"purpose": "删除课案"
},
{
"name": "duplicateLessonPlanAction",
"permission": "LESSON_PLAN_CREATE",
"file": "actions.ts",
"purpose": "复制课案"
},
{
"name": "getLessonPlanTemplatesAction",
"permission": "LESSON_PLAN_READ",
"file": "actions.ts",
"purpose": "查询模板列表"
},
{
"name": "saveAsTemplateAction",
"permission": "LESSON_PLAN_UPDATE",
"file": "actions.ts",
"purpose": "保存为个人模板"
},
{
"name": "deleteTemplateAction",
"permission": "LESSON_PLAN_DELETE",
"file": "actions.ts",
"purpose": "删除个人模板"
},
{
"name": "suggestKnowledgePointsAction",
"permission": "LESSON_PLAN_UPDATE",
"file": "actions-ai.ts",
"purpose": "AI 建议知识点"
},
{
"name": "publishLessonPlanHomeworkAction",
"permission": "LESSON_PLAN_PUBLISH",
"file": "actions-publish.ts",
"purpose": "发布课案为作业"
},
{
"name": "getKnowledgePointOptionsAction",
"permission": "LESSON_PLAN_READ",
"file": "actions-kp.ts",
"purpose": "获取知识点选项(委托 textbooks data-access"
}
]
},
"dependencies": [
"textbooks",
"questions",
"exams",
"homework",
"classes",
"files",
"shared/lib/ai"
],
"files": [
"types.ts",
"constants.ts",
"schema.ts",
"data-access.ts",
"data-access-versions.ts",
"data-access-templates.ts",
"data-access-knowledge.ts",
"actions.ts",
"actions-publish.ts",
"actions-ai.ts",
"actions-kp.ts",
"publish-service.ts",
"ai-suggest.ts",
"seed-templates.ts",
"hooks/use-lesson-plan-editor.ts",
"components/lesson-plan-list.tsx",
"components/lesson-plan-card.tsx",
"components/lesson-plan-filters.tsx",
"components/lesson-plan-editor.tsx",
"components/block-renderer.tsx",
"components/template-picker.tsx",
"components/version-history-drawer.tsx",
"components/knowledge-point-picker.tsx",
"components/question-bank-picker.tsx",
"components/inline-question-editor.tsx",
"components/publish-homework-dialog.tsx",
"components/blocks/rich-text-block.tsx",
"components/blocks/text-study-block.tsx",
"components/blocks/exercise-block.tsx",
"components/blocks/reflection-block.tsx"
]
}
},
"dbTables": {
"_meta": {
"total": 54,
"orm": "Drizzle ORM 0.45",
"database": "MySQL",
"idStrategy": "CUID2 (varchar length 128)",
"source": "src/shared/db/schema.ts"
},
"usersAndAuth": {
"description": "用户与认证 (Auth.js v5 + RBAC)",
"tables": {
"users": {
"owner": "users",
"description": "用户主表(含未成年人信息保护字段)"
},
"accounts": {
"owner": "auth",
"description": "OAuth 账户"
},
"sessions": {
"owner": "auth",
"description": "Auth.js 会话"
},
"verificationTokens": {
"owner": "auth",
"description": "验证令牌"
},
"roles": {
"owner": "auth",
"description": "角色(admin/teacher/student/parent/grade_head/teaching_head)"
},
"usersToRoles": {
"owner": "auth",
"description": "用户-角色多对多"
},
"rolePermissions": {
"owner": "auth",
"description": "角色-权限(细粒度 RBAC)"
},
"passwordSecurity": {
"owner": "auth",
"description": "密码安全策略(失败次数/锁定/必须改密)"
}
}
},
"knowledgeStructure": {
"description": "知识点树结构",
"tables": {
"knowledgePoints": {
"owner": "textbooks",
"description": "知识点(树结构,parentId 自引用)"
}
}
},
"questionBank": {
"description": "题库核心",
"tables": {
"questions": {
"owner": "questions",
"description": "题目(content JSON,支持复合题 parentId 自引用)"
},
"questionsToKnowledgePoints": {
"owner": "questions",
"description": "题目-知识点多对多"
}
}
},
"academicStructure": {
"description": "教学结构",
"tables": {
"subjects": {
"owner": "school",
"description": "科目"
},
"textbooks": {
"owner": "textbooks",
"description": "教材"
},
"chapters": {
"owner": "textbooks",
"description": "章节(支持嵌套 parentId)"
}
}
},
"schoolManagement": {
"description": "学校管理",
"tables": {
"departments": {
"owner": "school",
"description": "部门"
},
"classrooms": {
"owner": "school",
"description": "教室"
},
"academicYears": {
"owner": "school",
"description": "学年"
},
"schools": {
"owner": "school",
"description": "学校"
},
"grades": {
"owner": "school",
"description": "年级(含 gradeHeadId/teachingHeadId)"
}
}
},
"classes": {
"description": "班级/选课/课表",
"tables": {
"classes": {
"owner": "classes",
"description": "班级"
},
"classSubjectTeachers": {
"owner": "classes",
"description": "班级-科目-教师"
},
"classEnrollments": {
"owner": "classes",
"description": "班级选课(active/inactive)"
},
"classSchedule": {
"owner": "scheduling",
"description": "课表(注意:三处写入口,见 knownIssues P0-6)"
}
}
},
"exams": {
"description": "考试与提交",
"tables": {
"exams": {
"owner": "exams",
"description": "考试(含 examMode: homework/timed/proctored)"
},
"examQuestions": {
"owner": "exams",
"description": "考试-题目多对多"
},
"examSubmissions": {
"owner": "exams",
"description": "考试提交(started/submitted/graded)"
},
"submissionAnswers": {
"owner": "exams",
"description": "提交答案"
}
}
},
"homework": {
"description": "作业",
"tables": {
"homeworkAssignments": {
"owner": "homework",
"description": "作业(关联 sourceExamId)"
},
"homeworkAssignmentQuestions": {
"owner": "homework",
"description": "作业-题目"
},
"homeworkAssignmentTargets": {
"owner": "homework",
"description": "作业目标学生"
},
"homeworkSubmissions": {
"owner": "homework",
"description": "作业提交(含 attemptNo/isLate)"
},
"homeworkAnswers": {
"owner": "homework",
"description": "作业答案"
}
}
},
"ai": {
"description": "AI 配置",
"tables": {
"aiProviders": {
"owner": "settings",
"description": "AI Provider(zhipu/openai/gemini/custom,apiKey 加密)"
}
}
},
"announcements": {
"description": "公告",
"tables": {
"announcements": {
"owner": "announcements",
"description": "公告(school/grade/class,draft/published/archived)"
}
}
},
"auditLogs": {
"description": "审计与登录日志",
"tables": {
"auditLogs": {
"owner": "audit",
"description": "审计日志(success/failure)"
},
"loginLogs": {
"owner": "audit",
"description": "登录日志(signin/signout/signup)"
},
"dataChangeLogs": {
"owner": "audit",
"description": "数据变更日志(create/update/delete)"
}
}
},
"gradeRecords": {
"description": "成绩录入",
"tables": {
"gradeRecords": {
"owner": "grades",
"description": "成绩记录(exam/quiz/homework/other)"
}
}
},
"files": {
"description": "文件附件",
"tables": {
"fileAttachments": {
"owner": "files",
"description": "文件附件(含 storagePath/url)"
}
}
},
"coursePlans": {
"description": "课程计划",
"tables": {
"coursePlans": {
"owner": "course-plans",
"description": "课程计划(planning/active/completed/paused)"
},
"coursePlanItems": {
"owner": "course-plans",
"description": "课程计划项(按周)"
}
}
},
"messaging": {
"description": "消息与通知",
"tables": {
"messages": {
"owner": "messaging",
"description": "站内消息(含回复链 parentMessageId)"
},
"messageNotifications": {
"owner": "notifications",
"description": "消息通知"
},
"notificationPreferences": {
"owner": "notifications",
"description": "通知偏好(email/sms/push + 分类开关)"
}
}
},
"parentStudent": {
"description": "家长-子女关联",
"tables": {
"parentStudentRelations": {
"owner": "parent",
"description": "家长-子女关系"
}
}
},
"attendance": {
"description": "考勤",
"tables": {
"attendanceRecords": {
"owner": "attendance",
"description": "考勤记录(present/absent/late/early_leave/excused)"
},
"attendanceRules": {
"owner": "attendance",
"description": "考勤规则(迟到/早退阈值)"
}
}
},
"scheduling": {
"description": "排课",
"tables": {
"schedulingRules": {
"owner": "scheduling",
"description": "排课规则(每日最大课时/连续课时/午休等)"
},
"scheduleChanges": {
"owner": "scheduling",
"description": "调课/代课申请(pending/approved/rejected/completed)"
}
}
},
"elective": {
"description": "选课管理 (P2)",
"tables": {
"electiveCourses": {
"owner": "elective",
"description": "选修课程(draft/open/closed/cancelled,fcfs/lottery)"
},
"courseSelections": {
"owner": "elective",
"description": "选课记录(selected/enrolled/waitlist/dropped/rejected)"
}
}
},
"proctoring": {
"description": "考试监考 (P2)",
"tables": {
"examProctoringEvents": {
"owner": "proctoring",
"description": "监考事件(tab_switch/window_blur/copy_attempt 等)"
}
}
},
"diagnostic": {
"description": "学情诊断 (P2)",
"tables": {
"knowledgePointMastery": {
"owner": "diagnostic",
"description": "知识点掌握度(学生-知识点)"
},
"learningDiagnosticReports": {
"owner": "diagnostic",
"description": "学情诊断报告(individual/class/grade)"
}
}
},
"lessonPreparation": {
"description": "教师备课",
"tables": {
"lessonPlans": {
"owner": "lesson-preparation",
"description": "课案主表(Block JSON 内容,15 列/4 索引/5 外键)",
"columns": 15,
"indexes": 4,
"foreignKeys": 5
},
"lessonPlanVersions": {
"owner": "lesson-preparation",
"description": "课案版本(手动/自动快照,8 列/2 索引/2 外键)",
"columns": 8,
"indexes": 2,
"foreignKeys": 2
},
"lessonPlanTemplates": {
"owner": "lesson-preparation",
"description": "课案模板(系统/个人,8 列/1 索引/1 外键)",
"columns": 8,
"indexes": 1,
"foreignKeys": 1
}
}
}
},
"dependencyMatrix": {
"shared": {
"dependsOn": []
},
"auth": {
"dependsOn": [
"shared"
],
"uses": {
"shared": [
"db",
"schema",
"permissions"
]
}
},
"exams": {
"dependsOn": [
"shared",
"auth",
"questions",
"classes"
],
"uses": {
"shared": [
"db",
"auth-guard",
"types",
"ai"
],
"auth": [
"auth"
],
"questions": [
"data-access.createQuestionWithRelations"
],
"classes": [
"data-access.getClassGradeIdsByClassIds"
]
}
},
"homework": {
"dependsOn": [
"shared",
"auth",
"exams",
"classes",
"school",
"users"
],
"uses": {
"shared": [
"db",
"auth-guard",
"types"
],
"auth": [
"auth"
],
"exams": [
"data-access.getExamIdsByGradeIds",
"data-access.getExamSubjectIdMap",
"data-access.getExamWithQuestionsForHomework"
],
"classes": [
"data-access.getStudentIdsByClassId",
"data-access.getStudentIdsByClassIds",
"data-access.getActiveStudentIdsByClassId",
"data-access.getTeacherSubjectIdsByClass",
"data-access.getStudentActiveClassId",
"data-access.getGradeIdsByClassIds",
"data-access.getClassTeacherById"
],
"school": [
"data-access.getSubjectOptions"
],
"users": [
"data-access.getUserWithRole",
"data-access.getUserNamesByIds"
]
}
},
"questions": {
"dependsOn": [
"shared",
"auth"
],
"uses": {
"shared": [
"db",
"auth-guard",
"types"
],
"auth": [
"auth"
]
}
},
"textbooks": {
"dependsOn": [
"shared",
"auth"
],
"uses": {
"shared": [
"db",
"auth-guard",
"types"
],
"auth": [
"auth"
]
}
},
"classes": {
"dependsOn": [
"shared",
"auth",
"homework",
"scheduling",
"school"
],
"uses": {
"shared": [
"db",
"auth-guard",
"types"
],
"auth": [
"auth"
],
"homework": [
"data-access-classes.getAssignmentIdsForStudents",
"data-access-classes.getHomeworkAssignmentsWithSubject",
"data-access-classes.getHomeworkAssignmentsByIds",
"data-access-classes.getAssignmentMaxScoreById",
"data-access-classes.getAssignmentTargetCounts",
"data-access-classes.getHomeworkSubmissionsForStudents",
"data-access-classes.getPublishedHomeworkAssignmentsWithSubject",
"data-access-classes.getHomeworkSubmissionsForAssignments"
],
"scheduling": [
"data-access-class-schedule.createClassScheduleItem",
"data-access-class-schedule.updateClassScheduleItem",
"data-access-class-schedule.deleteClassScheduleItem"
],
"school": [
"data-access.isGradeHead",
"data-access.isGradeManager",
"data-access.findGradeIdByHeadAndName"
]
}
},
"school": {
"dependsOn": [
"shared",
"auth"
],
"uses": {
"shared": [
"db",
"auth-guard",
"types"
],
"auth": [
"auth"
]
}
},
"dashboard": {
"dependsOn": [
"shared",
"auth",
"homework",
"classes"
],
"uses": {
"shared": [
"db",
"types"
],
"auth": [
"auth"
],
"homework": [
"data-access.getTeacherGradeTrends",
"data-access.getStudentDashboardGrades"
],
"classes": [
"data-access.getTeacherClasses",
"data-access.getStudentClasses",
"data-access.getStudentSchedule"
]
}
},
"layout": {
"dependsOn": [
"shared",
"auth",
"messaging"
],
"uses": {
"shared": [
"hooks.usePermission",
"components.global-search.GlobalSearch"
],
"auth": [
"useSession"
],
"messaging": [
"components.notification-dropdown"
]
}
},
"settings": {
"dependsOn": [
"shared",
"auth",
"messaging"
],
"uses": {
"shared": [
"db",
"auth-guard",
"ai",
"types",
"components.ui.switch"
],
"auth": [
"auth"
],
"messaging": [
"notification-preferences.getNotificationPreferences",
"actions.getNotificationPreferencesAction",
"actions.updateNotificationPreferencesAction"
]
}
},
"users": {
"dependsOn": [
"shared",
"auth",
"classes"
],
"uses": {
"shared": [
"db",
"auth-guard.requireAuth",
"auth-guard.requirePermission",
"db.schema.users",
"db.schema.roles",
"db.schema.usersToRoles",
"types.permissions",
"types.action-state",
"lib.excel"
],
"auth": [
"auth"
],
"classes": [
"data-access.enrollStudentByInvitationCode"
]
}
},
"audit": {
"dependsOn": [
"shared",
"auth"
],
"uses": {
"shared": [
"db",
"auth-guard.requirePermission",
"db.schema.auditLogs",
"db.schema.loginLogs",
"db.schema.dataChangeLogs",
"types.permissions",
"lib.excel"
],
"auth": [
"auth"
]
}
},
"announcements": {
"dependsOn": [
"shared",
"auth",
"school"
],
"uses": {
"shared": [
"db",
"auth-guard.requirePermission",
"auth-guard.requireAuth",
"db.schema.announcements",
"types.permissions"
],
"auth": [
"auth"
],
"school": [
"data-access.getGrades"
]
}
},
"files": {
"dependsOn": [
"shared",
"auth"
],
"uses": {
"shared": [
"db",
"auth-guard.requireAuth",
"auth-guard.requirePermission",
"types.permissions",
"lib.file-storage",
"lib.storage-provider"
],
"auth": [
"auth"
]
}
},
"course-plans": {
"dependsOn": [
"shared",
"auth",
"school",
"classes"
],
"uses": {
"shared": [
"db",
"auth-guard.requirePermission",
"db.schema.coursePlans",
"db.schema.coursePlanItems",
"db.schema.classes",
"db.schema.subjects",
"db.schema.users",
"types.permissions",
"types.action-state"
],
"auth": [
"auth"
],
"school": [
"data-access.getAcademicYears"
],
"classes": [
"data-access.getAdminClasses",
"data-access.getStaffOptions"
]
}
},
"grades": {
"dependsOn": [
"shared",
"auth",
"classes",
"school",
"users"
],
"uses": {
"shared": [
"db",
"auth-guard.requirePermission",
"types.permissions",
"types.action-state",
"db.schema.gradeRecords",
"lib.excel"
],
"auth": [
"auth"
],
"classes": [
"data-access.getClassExists",
"data-access.getClassNameById",
"data-access.getClassNamesByIds",
"data-access.getActiveStudentIdsByClassId",
"data-access.getStudentActiveClassId",
"data-access.getClassesByGradeId"
],
"school": [
"data-access.getSubjectOptions",
"data-access.getGradeOptions"
],
"users": [
"data-access.getUserNamesByIds"
]
}
},
"parent": {
"dependsOn": [
"shared",
"auth",
"homework",
"classes",
"grades",
"school",
"users"
],
"uses": {
"shared": [
"db",
"auth-guard.requireAuth",
"db.schema.parentStudentRelations",
"types"
],
"auth": [
"auth"
],
"homework": [
"data-access.getStudentHomeworkAssignments",
"data-access.getStudentDashboardGrades"
],
"classes": [
"data-access.getStudentClasses",
"data-access.getStudentSchedule",
"data-access.getClassNameById",
"data-access.getStudentActiveClassId"
],
"grades": [
"data-access.getStudentGradeSummary"
],
"school": [
"data-access.getGradeOptions"
],
"users": [
"data-access.getUserBasicInfo",
"data-access.getUserNamesByIds"
]
}
},
"messaging": {
"dependsOn": [
"shared",
"auth",
"notifications"
],
"uses": {
"shared": [
"db",
"auth-guard.requirePermission",
"auth-guard.requireAuth",
"db.schema.messages",
"db.schema.users",
"db.schema.classEnrollments",
"db.schema.classes",
"db.schema.grades",
"types.permissions",
"types.action-state"
],
"auth": [
"auth"
],
"notifications": [
"dispatcher.sendNotification",
"data-access.createNotification (via re-export)",
"data-access.getNotifications (via re-export)",
"data-access.markNotificationAsRead (via re-export)",
"data-access.markAllNotificationsAsRead (via re-export)",
"data-access.getUnreadNotificationCount (via re-export)",
"preferences.getNotificationPreferences (via re-export)",
"preferences.upsertNotificationPreferences (via re-export)"
]
}
},
"notifications": {
"dependsOn": [
"shared",
"auth",
"classes"
],
"uses": {
"shared": [
"db",
"auth-guard.requirePermission",
"db.schema.messageNotifications",
"db.schema.notificationPreferences",
"types.permissions",
"types.action-state"
],
"auth": [
"auth"
],
"classes": [
"data-access.getClassExists",
"data-access.getStudentIdsByClassId"
]
}
},
"attendance": {
"dependsOn": [
"shared",
"auth",
"classes"
],
"uses": {
"shared": [
"db",
"auth-guard.requirePermission",
"db.schema.attendanceRecords",
"db.schema.attendanceRules",
"db.schema.classEnrollments",
"db.schema.users",
"db.schema.classes",
"types.permissions",
"types.action-state",
"types.DataScope"
],
"auth": [
"auth"
],
"classes": [
"data-access.getTeacherClasses",
"data-access.getAdminClasses"
]
}
},
"scheduling": {
"dependsOn": [
"shared",
"auth",
"classes",
"users"
],
"uses": {
"shared": [
"db",
"auth-guard.requirePermission",
"auth-guard.getAuthContext",
"db.schema.schedulingRules",
"db.schema.scheduleChanges",
"db.schema.classSchedule",
"db.schema.classSubjectTeachers",
"db.schema.subjects",
"db.schema.classrooms",
"types.permissions",
"types.action-state"
],
"auth": [
"auth"
],
"classes": [
"data-access.verifyTeacherOwnsClass",
"data-access.getTeacherIdForMutations"
],
"users": [
"data-access.getUserNamesByIds"
]
}
},
"diagnostic": {
"dependsOn": [
"shared",
"auth",
"classes",
"exams",
"questions",
"users"
],
"uses": {
"shared": [
"db",
"auth-guard.requirePermission",
"auth-guard.getAuthContext",
"db.schema.knowledgePointMastery",
"db.schema.learningDiagnosticReports",
"db.schema.knowledgePoints",
"types.permissions",
"types.action-state",
"hooks.usePermission",
"components.ui.*"
],
"auth": [
"auth"
],
"classes": [
"data-access.getClassExists",
"data-access.getClassNameById",
"data-access.getActiveStudentIdsByClassId"
],
"exams": [
"data-access.getExamSubmissionWithAnswers"
],
"questions": [
"data-access.getKnowledgePointsForQuestions"
],
"users": [
"data-access.getUserNamesByIds",
"data-access.getUserIdsByGradeId"
]
}
},
"elective": {
"dependsOn": [
"shared",
"auth",
"classes",
"school",
"users"
],
"uses": {
"shared": [
"db",
"auth-guard.requirePermission",
"db.schema.electiveCourses",
"db.schema.courseSelections",
"types.permissions",
"types.action-state",
"types.DataScope",
"hooks.usePermission",
"components.ui.*"
],
"auth": [
"auth"
],
"classes": [
"data-access.getStudentActiveGradeId"
],
"school": [
"data-access.getSubjectOptions",
"data-access.getGradeOptions"
],
"users": [
"data-access.getUserNamesByIds"
]
}
},
"proctoring": {
"dependsOn": [
"shared",
"auth",
"exams",
"users"
],
"uses": {
"shared": [
"db",
"auth-guard.requirePermission",
"db.schema.examProctoringEvents",
"types.permissions",
"types.action-state",
"hooks.usePermission",
"components.ui.*",
"next/cache.revalidatePath"
],
"auth": [
"auth"
],
"exams": [
"data-access.getExamForProctoringCrossModule",
"data-access.getExamSubmissionForProctoringCrossModule",
"data-access.getExamSubmissionsForExam",
"data-access.getExamTitleById"
],
"users": [
"data-access.getUserNamesByIds"
]
}
},
"lesson_preparation": {
"dependsOn": [
"shared",
"auth",
"textbooks",
"questions",
"exams",
"homework",
"classes",
"files"
],
"uses": {
"shared": [
"db",
"auth-guard.requirePermission",
"db.schema.lessonPlans",
"db.schema.lessonPlanVersions",
"db.schema.lessonPlanTemplates",
"lib.ai.createAiChatCompletion",
"types.permissions",
"types.action-state",
"hooks.usePermission",
"components.ui.*",
"next/cache.revalidatePath"
],
"auth": [
"auth"
],
"textbooks": [
"data-access.getChapters",
"data-access.getKnowledgePoints"
],
"questions": [
"data-access.getQuestions",
"data-access.createQuestionWithRelations"
],
"exams": [
"data-access.persistExamDraft"
],
"homework": [
"data-access-write.createHomeworkAssignment"
],
"classes": [
"data-access.getTeacherClasses"
],
"files": [
"data-access.createFileAttachment",
"data-access.getFileAttachmentsByTarget"
]
}
}
},
"moduleDependencyGraph": {
"nodes": [
"shared",
"auth",
"exams",
"homework",
"questions",
"textbooks",
"classes",
"school",
"dashboard",
"layout",
"settings",
"users",
"audit",
"announcements",
"files",
"course-plans",
"grades",
"parent",
"messaging",
"notifications",
"attendance",
"scheduling",
"proctoring",
"diagnostic",
"elective"
],
"edges": [
{
"from": "exams",
"to": "questions",
"type": "data-access",
"description": "引用题目(examQuestions 关联)"
},
{
"from": "exams",
"to": "shared",
"type": "normal",
"description": "使用 db/auth-guard/ai"
},
{
"from": "homework",
"to": "exams",
"type": "data-access",
"description": "引用试卷结构(sourceExamId)"
},
{
"from": "homework",
"to": "questions",
"type": "data-access",
"description": "引用题目"
},
{
"from": "grades",
"to": "classes",
"type": "data-access",
"description": "查询班级"
},
{
"from": "grades",
"to": "exams",
"type": "data-access",
"description": "关联考试(examId)"
},
{
"from": "grades",
"to": "subjects",
"type": "data-access",
"description": "查询科目"
},
{
"from": "dashboard",
"to": "exams",
"type": "data-access",
"description": "调用 getExamsDashboardStats 获取考试统计(P0-4 已修复)"
},
{
"from": "dashboard",
"to": "homework",
"type": "data-access",
"description": "调用 getHomeworkDashboardStats 获取作业统计(P0-4 已修复)"
},
{
"from": "dashboard",
"to": "classes",
"type": "data-access",
"description": "调用 getClassesDashboardStats 获取班级统计(P0-4 已修复)"
},
{
"from": "dashboard",
"to": "users",
"type": "data-access",
"description": "调用 getUsersDashboardStats 获取用户/会话/角色统计(P0-4 已修复)"
},
{
"from": "dashboard",
"to": "textbooks",
"type": "data-access",
"description": "调用 getTextbooksDashboardStats 获取教材/章节统计(P0-4 已修复)"
},
{
"from": "dashboard",
"to": "questions",
"type": "data-access",
"description": "调用 getQuestionsDashboardStats 获取题目统计(P0-4 已修复)"
},
{
"from": "messaging",
"to": "notifications",
"type": "data-access",
"description": "✅ P0-4 / P1-5 已修复messaging 通过 sendNotification dispatcher 发送通知,通知 CRUD 和偏好通过 re-export 保持向后兼容"
},
{
"from": "classes",
"to": "homework",
"type": "data-access",
"description": "通过 homework/data-access-classes 获取作业数据(P0-7 已修复,原为 violation)"
},
{
"from": "classes",
"to": "scheduling",
"type": "resolved",
"description": "✅ P0-5 已修复classSchedule 写函数从 classes 迁移至 scheduling/data-access-class-schedule.tsclasses 仅保留读函数"
},
{
"from": "classes",
"to": "grades",
"type": "resolved",
"description": "✅ P0-1 已修复classes/data-access.ts 已拆分为 5 个文件,不再混入 grades 查询逻辑"
},
{
"from": "proctoring",
"to": "exams",
"type": "data-access",
"description": "关联考试与提交(examSubmissions)"
},
{
"from": "diagnostic",
"to": "questions",
"type": "data-access",
"description": "关联题目知识点(questionsToKnowledgePoints)"
},
{
"from": "diagnostic",
"to": "exams",
"type": "data-access",
"description": "关联考试提交(examSubmissions/submissionAnswers)"
},
{
"from": "elective",
"to": "school",
"type": "data-access",
"description": "关联年级(grades)/科目(subjects)"
},
{
"from": "attendance",
"to": "classes",
"type": "data-access",
"description": "查询班级与课表"
},
{
"from": "parent",
"to": "grades",
"type": "data-access",
"description": "查询子女成绩"
},
{
"from": "parent",
"to": "attendance",
"type": "data-access",
"description": "查询子女考勤"
},
{
"from": "parent",
"to": "homework",
"type": "data-access",
"description": "查询子女作业"
},
{
"from": "scheduling",
"to": "classes",
"type": "data-access",
"description": "排课关联班级"
},
{
"from": "course-plans",
"to": "classes",
"type": "data-access",
"description": "课程计划关联班级"
},
{
"from": "course-plans",
"to": "school",
"type": "data-access",
"description": "关联科目"
},
{
"from": "announcements",
"to": "school",
"type": "data-access",
"description": "关联年级/班级"
},
{
"from": "shared",
"to": "auth",
"type": "resolved",
"description": "✅ 已修复shared/lib 通过 session.ts 单一入口获取 sessiondynamic import 打破静态循环)"
},
{
"from": "auth",
"to": "shared",
"type": "normal",
"description": "auth.ts 依赖 shared/lib合理单向"
},
{
"from": "layout",
"to": "shared",
"type": "normal",
"description": "使用 shared 组件与配置"
},
{
"from": "settings",
"to": "shared",
"type": "normal",
"description": "使用 shared/lib/ai"
},
{
"from": "settings",
"to": "messaging",
"type": "normal",
"description": "通知偏好设置"
},
{
"from": "audit",
"to": "shared",
"type": "normal",
"description": "使用 shared/lib/audit-logger"
},
{
"from": "files",
"to": "shared",
"type": "normal",
"description": "使用 shared/db"
},
{
"from": "users",
"to": "classes",
"type": "data-access",
"description": "✅ P1-4 已修复:通过 class-registration.ts 调用 classes/data-access.enrollStudentByInvitationCode不再直写 classEnrollments"
}
]
},
"knownIssues": [
{
"id": "P0-1",
"severity": "P0",
"title": "classes/data-access.ts 超过 1000 行",
"file": "src/modules/classes/data-access.ts",
"lines": 2104,
"problem": "混入 homework/scheduling/grades 逻辑,严重违反模块职责单一原则",
"suggestion": "按职责拆分为 class-query/schedule/homework-insights/grade-query",
"status": "resolved",
"resolvedAt": "2026-06-17",
"resolution": "拆分为 5 个文件data-access.ts(548行,核心CRUD+邀请码+教师班级管理) + data-access-stats.ts(531行,作业统计) + data-access-schedule.ts(194行,课表) + data-access-students.ts(244行,学生查询) + data-access-admin.ts(406行,管理员班级管理),所有文件均 ≤800 行data-access.ts 通过 re-export 保持向后兼容"
},
{
"id": "P0-2",
"severity": "P0",
"title": "homework/data-access.ts 超过 1000 行",
"file": "src/modules/homework/data-access.ts",
"lines": 1038,
"problem": "混入排名计算业务逻辑",
"suggestion": "分离排名逻辑到独立文件(如 data-access-ranking.ts)",
"status": "resolved",
"resolvedAt": "2026-06-17",
"resolution": "拆分为 data-access.ts(598行) + stats-service.ts(425行),统计函数(getTeacherGradeTrends/getHomeworkAssignmentAnalytics/getStudentDashboardGrades)迁移至 stats-service.tsdata-access.ts 通过 re-export 保持向后兼容"
},
{
"id": "P0-3",
"severity": "P0",
"title": "shared/lib ↔ auth 循环依赖",
"file": "src/shared/lib/{audit-logger,change-logger,auth-guard}.ts ↔ src/auth.ts",
"problem": "shared/lib 依赖 @/auth(src/auth.ts),auth.ts 又依赖 shared/lib,形成循环依赖",
"suggestion": "拆分 auth.ts,将 shared 依赖部分抽出为独立模块",
"status": "resolved",
"resolvedAt": "2026-06-18",
"resolution": "新增 shared/lib/session.ts 单一入口封装 getSession()server-only内部 dynamic import @/auth 打破模块级静态循环。audit-logger.ts/change-logger.ts/auth-guard.ts 改为 import { getSession } from '@/shared/lib/session',不再直接依赖 @/auth。运行时调用链保持不变模块加载图无环。"
},
{
"id": "P0-4",
"severity": "P0",
"title": "dashboard 跨模块直接查询 11 张表",
"file": "src/modules/dashboard/data-access.ts",
"problem": "getAdminDashboardData 直查 sessions/users/classes/textbooks/chapters/questions/exams/homeworkAssignments/homeworkSubmissions/usersToRoles/roles,严重违反模块封装",
"suggestion": "改为通过各模块 data-access 获取数据",
"status": "fixed",
"fixedBy": "新增 getUsersDashboardStats/getClassesDashboardStats/getTextbooksDashboardStats/getQuestionsDashboardStats/getExamsDashboardStats/getHomeworkDashboardStats,dashboard 改为并行调用各模块 stats 函数"
},
{
"id": "P0-5",
"severity": "P0",
"title": "messaging 绕过 notifications 直接写通知",
"file": "src/modules/messaging/actions.ts",
"lines": "66-72",
"problem": "直接调用 createNotification,导致用户通知偏好失效、多渠道通知无效",
"suggestion": "改为通过 notifications dispatcher 统一分发",
"status": "resolved",
"resolvedAt": "2026-06-17",
"resolution": "将 messageNotifications 和 notificationPreferences 表的 CRUD 函数从 messaging 迁移到 notifications 模块notifications/channels/in-app-channel.ts 改为静态导入本地 createNotification不再动态 import messagingnotifications/dispatcher.ts 改为从本地 preferences.ts 导入偏好函数messaging 模块通过 re-export 保持向后兼容依赖方向变为单向messaging → notificationsmessaging 调用 sendNotification dispatcher 发送通知)"
},
{
"id": "P0-6",
"severity": "P0",
"title": "classSchedule 表三处写入口",
"file": "src/modules/classes/data-access.ts, src/modules/scheduling/actions.ts, src/modules/scheduling/data-access.ts",
"problem": "三处独立写入 classSchedule 表,数据完整性高风险",
"suggestion": "统一写入口到 scheduling 模块",
"status": "fixed",
"fixedBy": "将 classSchedule 写函数(createClassScheduleItem/updateClassScheduleItem/deleteClassScheduleItem)从 classes/data-access-schedule.ts 迁移到新文件 scheduling/data-access-class-schedule.ts;classes 模块仅保留 READ 函数(getStudentSchedule/getClassSchedule);新增 classes/data-access.verifyTeacherOwnsClass 供 scheduling 模块跨模块校验教师班级归属;classes/actions.ts 改为从 @/modules/scheduling/data-access-class-schedule 导入写函数;类型 CreateClassScheduleItemInput/UpdateClassScheduleItemInput 从 classes/types.ts 迁移到 scheduling/types.ts;所有 classSchedule DB 写入统一由 scheduling 模块管理"
},
{
"id": "P0-7",
"severity": "P0",
"title": "classes 模块直查 homework/exams 表违反三层架构",
"file": "src/modules/classes/data-access-stats.ts, src/modules/classes/data-access-students.ts",
"problem": "data-access-stats.ts 和 data-access-students.ts 直接导入并查询 homeworkAssignmentQuestions/homeworkAssignmentTargets/homeworkAssignments/homeworkSubmissions/exams 表,违反 modules/ 不直接查询其他模块 DB 表的规则",
"suggestion": "在 homework 模块新增 data-access-classes.ts 暴露所需数据,classes 模块改为调用这些函数",
"status": "resolved",
"resolvedAt": "2026-06-18",
"resolution": "新增 src/modules/homework/data-access-classes.ts(232行,7个函数:getAssignmentIdsForStudents/getHomeworkAssignmentsWithSubject/getHomeworkAssignmentsByIds/getAssignmentTargetCounts/getHomeworkSubmissionsForStudents/getPublishedHomeworkAssignmentsWithSubject/getHomeworkSubmissionsForAssignments,re-export getAssignmentMaxScoreById);classes/data-access-stats.ts 和 data-access-students.ts 改为调用这些函数,移除对 homework/exams 表的直接导入;同时修复 P1 问题:移除 4 处 `as string` 断言(data-access-stats.ts),将 `studentScores.get(s.studentId)!` 非空断言替换为显式 null 检查(data-access-students.ts)"
},
{
"id": "P1-1",
"severity": "P1",
"title": "跨模块直接 DB 查询普遍存在",
"problem": "classes(8+)/classEnrollments(6+)/users(6+)/subjects(6+)/exams(5+) 表被多个模块直接查询",
"suggestion": "建立模块间数据访问规范,通过对方 data-access 或导出查询函数",
"status": "resolved",
"resolvedAt": "2026-06-18",
"resolution": "所有跨模块直查已改为通过对方 data-access 接口exams 通过 school data-access.getSubjectOptions/getGradeOptionsquestions 通过 textbooks data-access.getKnowledgePointOptionshomework 通过 exams/classes/school/users data-accessgrades 通过 classes/school/users data-accessclasses 通过 school/homework data-accessattendance 通过 classes data-accessscheduling 通过 users data-accessnotifications 通过 classes data-accessproctoring 通过 exams/users data-accessdiagnostic 通过 exams/questions/classes/users data-accessdashboard 通过各模块 data-accessusers updateUserProfile 已下沉到 data-access。各模块 data-access 暴露的查询接口classesgetClassExists/getClassNameById/getClassNamesByIds/getActiveStudentIdsByClassId/getStudentActiveClassId/getClassesByGradeId/verifyTeacherOwnsClass/getTeacherIdForMutations 等、examsgetExamIdsByGradeIds/getExamSubjectIdMap/getExamWithQuestionsForHomework/getExamSubmissionWithAnswers/getExamForProctoringCrossModule 等、schoolgetSubjectOptions/getGradeOptions、usersgetUserNamesByIds/getUserWithRole/getUserBasicInfo/getUserIdsByGradeId/getCurrentStudentUser、textbooksgetKnowledgePointOptions、questionscreateQuestionWithRelations/getKnowledgePointsForQuestions、homework/data-access-classes7 个函数供 classes 模块跨模块调用)"
},
{
"id": "P1-2",
"severity": "P1",
"title": "actions 层混入数据访问逻辑",
"file": "src/modules/{exams,homework,questions,announcements}/actions.ts",
"problem": "actions.ts 中存在直接 db.insert/update/delete,应通过 data-access 层",
"suggestion": "将 DB 操作下沉到 data-access 层",
"status": "resolved",
"resolvedAt": "2026-06-17",
"resolution": "4 个模块的 actions 层 DB 操作全部下沉到 data-accessexams 新增 7 个 data-access 函数actions.ts 831→691 行data-access.ts 374→471 行homework 新建 data-access-write.ts285 行10 个写函数actions.ts 387→239 行questions 新增 4 个 data-access 函数actions.ts 294→149 行data-access.ts 138→260 行announcements 新增 5 个 data-access 函数actions.ts 242→197 行data-access.ts 120→171 行。users/scheduling 待后续处理"
},
{
"id": "P1-3",
"severity": "P1",
"title": "auth.ts 混合 5 类职责",
"file": "src/auth.ts",
"problem": "NextAuth 配置 + 密码安全 DB 操作 + 角色规范化 + IP 解析 + 回调函数混合",
"suggestion": "拆分为 auth-config/password-security/role-normalizer/ip-utils 等多文件",
"status": "resolved",
"resolution": "已拆分密码安全DB操作→shared/lib/password-security-service.ts角色规范化→shared/lib/role-utils.tsbcrypt哈希规范化→shared/lib/bcrypt-utils.tsIP解析→shared/lib/http-utils.ts。auth.ts 现 193 行仅保留 NextAuth 配置"
},
{
"id": "P1-4",
"severity": "P1",
"title": "users/import-export.ts 四重职责",
"file": "src/modules/users/import-export.ts",
"problem": "导入解析 + 导出 + 用户创建(含密码哈希) + 班级注册(跨模块写 classEnrollments)",
"suggestion": "按职责拆分为 import-parser/exporter/user-creator/enrollment",
"status": "resolved",
"resolvedAt": "2026-06-17",
"resolution": "已拆分import-export.ts(157行,仅保留文件解析/生成) + user-service.ts(82行,batchImportUsers 用户创建+密码哈希+角色分配) + class-registration.ts(21行,registerStudentByInvitationCode 委托 classes/data-access.enrollStudentByInvitationCode)。batchImportUsers 不再直写 classEnrollments改为调用 classes/data-access.enrollStudentByInvitationCode。import-export.ts 通过 re-export batchImportUsers/UserImportResult 保持向后兼容"
},
{
"id": "P1-5",
"severity": "P1",
"title": "proctoring 死代码",
"file": "src/modules/proctoring/components/exam-mode-config.tsx",
"problem": "组件已创建但未集成到考试表单,DB schema 有 examMode 字段但表单不收集;事件上报存在 Server Action 与 REST API 双通道重复",
"suggestion": "集成 proctoring/exam-mode-config 到考试表单;删除重复的 REST API 路由",
"status": "partially_fixed",
"fixedBy": "删除 /api/proctoring/event REST 路由(移至 deletes/),Server Action recordProctoringEventAction 为唯一规范路径;exam-mode-config.tsx 集成属于功能新增,暂保留原位"
},
{
"id": "P2-1",
"severity": "P2",
"title": "schema.ts 54 张表混合(1111 行)",
"file": "src/shared/db/schema.ts",
"lines": 1111,
"problem": "所有表定义混合在单文件,虽可接受但需分节",
"suggestion": "按业务域分节(加注释分隔)或拆分为多文件"
},
{
"id": "P2-2",
"severity": "P2",
"title": "ai.ts 混合 5 类职责(218 行)",
"file": "src/shared/lib/ai.ts",
"lines": 218,
"problem": "AI 调用 + Provider 配置 + API Key 加密 + 错误格式化 + 负载解析混合在单文件",
"suggestion": "按职责拆分为 ai/ 目录多文件",
"status": "resolved",
"resolvedAt": "2026-06-17",
"resolution": "已拆分为 src/shared/lib/ai/ 目录payload-parser.ts(78 行,请求负载解析) + api-key-crypto.ts(28 行,API Key 加密/解密) + provider-config.ts(61 行,Provider 配置查询) + client.ts(58 行,AI 客户端创建与调用) + errors.ts(8 行,错误格式化) + index.ts(5 行,聚合导出)。原 ai.ts 保留为向后兼容的重导出文件(9 行)"
},
{
"id": "P1-6",
"severity": "P1",
"title": "三个 logger 重复实现 IP/Header 提取",
"file": "src/shared/lib/{audit-logger,change-logger,login-logger}.ts, src/auth.ts",
"problem": "audit-logger.ts / change-logger.ts / login-logger.ts / auth.ts 四处重复实现 IP/User-Agent 提取逻辑,且实现略有差异",
"suggestion": "提取 shared/lib/http-utils.ts 统一 IP/UA 提取",
"status": "resolved",
"resolvedAt": "2026-06-18",
"resolution": "shared/lib/http-utils.ts 新增 getUserAgent() 函数(与已有 resolveClientIp() 配套audit-logger.ts / change-logger.ts / login-logger.ts 改为从 @/shared/lib/http-utils 导入 resolveClientIp 和 getUserAgent删除本地重复实现auth.ts 已在 P1-3 中改用 resolveClientIp。四处实现统一消除不一致风险resolveClientIp 取 x-forwarded-for 第一段,更准确)"
},
{
"id": "P2-20",
"severity": "P2",
"title": "homework/data-access.getDemoStudentUser 使用 auth() 而非 auth-guard",
"file": "src/modules/homework/data-access.ts",
"problem": "getDemoStudentUser 直接调用 auth() 获取学生身份,绕过 shared/lib/auth-guard 的统一认证上下文,且导致 student 页面虚假依赖 homework 模块",
"suggestion": "迁移到 users 模块 getCurrentStudentUser,使用 auth-guard.getAuthContext",
"status": "resolved",
"resolvedAt": "2026-06-18",
"resolution": "getDemoStudentUser 从 homework 模块迁移至 users 模块 getCurrentStudentUser通过 session + JOIN users/usersToRoles/roles 校验 student 角色6 个 student 页面dashboard/assignments/assignments-[assignmentId]/courses/textbooks/textbooks-[id]/schedule改用 users 模块,消除对 homework 的虚假依赖student/elective 改用 getAuthContext()homework 保留 re-export 向后兼容"
}
],
"routes": {
"auth": {
"/login": {
"component": "LoginForm",
"type": "client",
"module": "auth"
},
"/register": {
"component": "RegisterForm + registerAction",
"type": "server",
"module": "auth",
"description": "注册页面(含未成年人信息保护、隐私政策/用户协议同意勾选)"
},
"/privacy": {
"component": "PrivacyPage",
"type": "server",
"module": "auth",
"description": "隐私政策页面(信息收集/使用/保护、用户权利、Cookie、未成年人保护条款、联系方式"
},
"/terms": {
"component": "TermsPage",
"type": "server",
"module": "auth",
"description": "用户协议页面(服务说明、注册、行为规范、知识产权、免责、变更终止、法律适用)"
}
},
"admin": {
"/admin/dashboard": {
"component": "AdminDashboardView",
"type": "server",
"dataAccess": [
"dashboard/data-access.getAdminDashboardData"
],
"permission": "school:manage"
},
"/admin/school": {
"component": "重定向",
"type": "server",
"redirect": "/admin/school/classes",
"permission": "school:manage"
},
"/admin/school/schools": {
"component": "SchoolsClient",
"type": "client",
"module": "school",
"permission": "school:manage"
},
"/admin/school/grades": {
"component": "GradesClient",
"type": "client",
"module": "school",
"permission": "grade:manage"
},
"/admin/school/grades/insights": {
"component": "年级作业洞察",
"type": "server",
"dataAccess": [
"classes/data-access.getGradeHomeworkInsights"
],
"permission": "grade:manage"
},
"/admin/school/departments": {
"component": "DepartmentsClient",
"type": "client",
"module": "school",
"permission": "school:manage"
},
"/admin/school/classes": {
"component": "AdminClassesClient",
"type": "client",
"module": "classes",
"permission": "school:manage"
},
"/admin/school/academic-year": {
"component": "AcademicYearClient",
"type": "client",
"module": "school",
"permission": "school:manage"
},
"/admin/audit-logs": {
"component": "AuditLogView",
"type": "server",
"module": "audit",
"dataAccess": [
"audit/data-access.getAuditLogs",
"audit/data-access.getAuditModuleOptions"
],
"permission": "audit_log:read"
},
"/admin/audit-logs/login-logs": {
"component": "LoginLogView",
"type": "server",
"module": "audit",
"dataAccess": [
"audit/data-access.getLoginLogs"
],
"permission": "audit_log:read"
},
"/admin/announcements": {
"component": "AdminAnnouncementsView",
"type": "server",
"module": "announcements",
"dataAccess": [
"announcements/data-access.getAnnouncements",
"school/data-access.getGrades"
],
"actions": [
"createAnnouncementAction"
],
"permission": "announcement:manage"
},
"/admin/announcements/[id]": {
"component": "AnnouncementForm (edit)",
"type": "server",
"module": "announcements",
"dataAccess": [
"announcements/data-access.getAnnouncementById",
"school/data-access.getGrades"
],
"actions": [
"updateAnnouncementAction"
],
"permission": "announcement:manage"
},
"/admin/files": {
"component": "AdminFilesView",
"type": "server",
"module": "files",
"dataAccess": [
"files/data-access.getAllFileAttachments"
],
"permission": "file:read"
},
"/admin/course-plans": {
"component": "CoursePlanList",
"type": "client",
"module": "course-plans",
"dataAccess": [
"course-plans/data-access.getCoursePlans"
],
"permission": "course_plan:manage"
},
"/admin/course-plans/create": {
"component": "CoursePlanForm (create)",
"type": "client",
"module": "course-plans",
"actions": [
"createCoursePlanAction"
],
"dataAccess": [
"classes/data-access.getAdminClasses",
"course-plans/data-access.getSubjectOptions",
"classes/data-access.getStaffOptions",
"school/data-access.getAcademicYears"
],
"permission": "course_plan:manage"
},
"/admin/course-plans/[id]": {
"component": "CoursePlanDetail",
"type": "client",
"module": "course-plans",
"dataAccess": [
"course-plans/data-access.getCoursePlanById"
],
"actions": [
"deleteCoursePlanAction",
"createCoursePlanItemAction",
"updateCoursePlanItemAction",
"deleteCoursePlanItemAction",
"toggleCoursePlanItemCompletedAction"
],
"permission": "course_plan:manage"
},
"/admin/course-plans/[id]/edit": {
"component": "CoursePlanForm (edit)",
"type": "client",
"module": "course-plans",
"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"
],
"permission": "course_plan:manage"
},
"/admin/attendance": {
"component": "AttendanceRecordList",
"type": "server",
"module": "attendance",
"dataAccess": [
"attendance/data-access.getAttendanceRecords (scope=all)",
"classes/data-access.getAdminClasses"
],
"permission": "attendance:manage",
"description": "管理员考勤总览权限requirePermission(ATTENDANCE_MANAGE)"
},
"/admin/users/import": {
"component": "UserImportPage (含 UserImportDialog)",
"type": "server",
"module": "users",
"actions": [
"users/actions.downloadUserTemplateAction",
"users/actions.importUsersAction"
],
"permission": "user:manage",
"description": "用户批量导入页面(说明卡片+字段文档表+导入对话框权限requirePermission(USER_MANAGE)"
},
"/admin/scheduling/rules": {
"component": "SchedulingRulesForm",
"type": "server",
"module": "scheduling",
"dataAccess": [
"scheduling/actions.getAdminClassesForScheduling",
"scheduling/actions.getSchedulingRules"
],
"actions": [
"saveSchedulingRulesAction"
],
"permission": "schedule:adjust",
"description": "排课规则配置页面权限requirePermission(SCHEDULE_ADJUST)"
},
"/admin/scheduling/auto": {
"component": "AutoSchedulePanel + AutoScheduleResultView",
"type": "server",
"module": "scheduling",
"dataAccess": [
"scheduling/actions.getAdminClassesForScheduling"
],
"actions": [
"autoScheduleAction",
"applyAutoScheduleAction"
],
"permission": "schedule:auto",
"description": "自动排课页面(预览+应用权限requirePermission(SCHEDULE_AUTO)"
},
"/admin/scheduling/changes": {
"component": "ScheduleChangeList + ScheduleConflictsView",
"type": "server",
"module": "scheduling",
"dataAccess": [
"scheduling/actions.getAdminClassesForScheduling",
"scheduling/actions.getScheduleChanges"
],
"actions": [
"approveScheduleChangeAction",
"rejectScheduleChangeAction",
"getClassConflictsAction"
],
"permission": "schedule:adjust",
"description": "调课申请审批+冲突检测页面权限requirePermission(SCHEDULE_ADJUST);审批操作需 SCHEDULE_AUTO"
},
"/admin/elective": {
"component": "ElectiveCourseList",
"type": "server",
"module": "elective",
"dataAccess": [
"elective/data-access.getElectiveCourses (scope=all)"
],
"actions": [
"deleteElectiveCourseAction",
"openSelectionAction",
"closeSelectionAction",
"runLotteryAction"
],
"permission": "elective:manage",
"description": "管理员选修课程列表权限requirePermission(ELECTIVE_MANAGE)"
},
"/admin/elective/create": {
"component": "ElectiveCourseForm",
"type": "client",
"module": "elective",
"actions": [
"createElectiveCourseAction"
],
"dataAccess": [
"elective/data-access.getSubjectOptions"
],
"permission": "elective:manage",
"description": "创建选修课程权限requirePermission(ELECTIVE_MANAGE)"
},
"/admin/elective/[id]/edit": {
"component": "ElectiveCourseForm (edit)",
"type": "client",
"module": "elective",
"actions": [
"updateElectiveCourseAction"
],
"dataAccess": [
"elective/data-access.getElectiveCourseById",
"elective/data-access.getSubjectOptions"
],
"permission": "elective:manage",
"description": "编辑选修课程权限requirePermission(ELECTIVE_MANAGE)"
}
},
"teacher": {
"/teacher/dashboard": {
"component": "TeacherDashboardView",
"type": "server",
"dataAccess": [
"dashboard/data-access (teacher)",
"homework/data-access.getTeacherGradeTrends",
"classes/data-access.getTeacherClasses"
],
"permission": "exam:read"
},
"/teacher/exams/all": {
"component": "ExamDataTable",
"type": "server",
"dataAccess": [
"exams/data-access.getExams"
],
"permission": "exam:read"
},
"/teacher/exams/create": {
"component": "ExamForm",
"type": "client",
"actions": [
"createExamAction",
"createAiExamAction",
"previewAiExamAction"
],
"permission": "exam:create"
},
"/teacher/questions": {
"component": "QuestionDataTable",
"type": "server",
"dataAccess": [
"questions/data-access.getQuestions"
],
"permission": "question:read"
},
"/teacher/textbooks": {
"component": "TextbookList",
"type": "server",
"dataAccess": [
"textbooks/data-access.getTextbooks"
],
"permission": "textbook:read"
},
"/teacher/textbooks/[id]": {
"component": "TextbookReader",
"type": "client",
"dataAccess": [
"textbooks/data-access.getTextbookById",
"getChaptersByTextbookId",
"getKnowledgePointsByTextbookId"
],
"permission": "textbook:read"
},
"/teacher/classes/my": {
"component": "ClassList",
"type": "server",
"dataAccess": [
"classes/data-access.getTeacherClasses"
],
"permission": "class:read"
},
"/teacher/classes/schedule": {
"component": "ClassSchedule",
"type": "server",
"dataAccess": [
"classes/data-access.getClassSchedule"
],
"permission": "class:read"
},
"/teacher/classes/students": {
"component": "ClassStudents",
"type": "server",
"dataAccess": [
"classes/data-access.getClassStudents"
],
"permission": "class:read"
},
"/teacher/classes": {
"component": "重定向",
"type": "server",
"redirect": "/teacher/classes/my",
"permission": "class:read"
},
"/teacher/classes/my/[id]": {
"component": "班级详情",
"type": "client",
"module": "classes",
"dataAccess": [
"classes/data-access.getClassStudents",
"classes/data-access.getClassSchedule",
"classes/data-access.getClassHomeworkInsights"
],
"permission": "class:read"
},
"/teacher/homework": {
"component": "重定向",
"type": "server",
"redirect": "/teacher/homework/assignments",
"permission": "homework:create"
},
"/teacher/homework/assignments": {
"component": "作业列表",
"type": "server",
"module": "homework",
"dataAccess": [
"homework/data-access.getHomeworkAssignments"
],
"permission": "homework:create"
},
"/teacher/homework/assignments/create": {
"component": "HomeworkAssignmentForm",
"type": "client",
"module": "homework",
"actions": [
"createHomeworkAssignmentAction"
],
"permission": "homework:create"
},
"/teacher/homework/assignments/[id]": {
"component": "作业详情+错误分析",
"type": "client",
"module": "homework",
"dataAccess": [
"homework/data-access.getHomeworkAssignmentById",
"homework/data-access.getHomeworkAssignmentAnalytics"
],
"permission": "homework:create"
},
"/teacher/homework/assignments/[id]/submissions": {
"component": "作业提交列表",
"type": "server",
"module": "homework",
"dataAccess": [
"homework/data-access.getHomeworkSubmissions"
],
"permission": "homework:create"
},
"/teacher/homework/submissions": {
"component": "批改列表",
"type": "server",
"module": "homework",
"dataAccess": [
"homework/data-access.getHomeworkAssignmentReviewList"
],
"permission": "homework:grade"
},
"/teacher/homework/submissions/[submissionId]": {
"component": "HomeworkGradingView",
"type": "client",
"module": "homework",
"actions": [
"gradeHomeworkSubmissionAction"
],
"dataAccess": [
"homework/data-access.getHomeworkSubmissionDetails"
],
"permission": "homework:grade"
},
"/teacher/exams": {
"component": "重定向",
"type": "server",
"redirect": "/teacher/exams/all",
"permission": "exam:read"
},
"/teacher/exams/[id]/build": {
"component": "ExamAssembly",
"type": "client",
"module": "exams",
"permission": "exam:update"
},
"/teacher/exams/grading": {
"component": "重定向",
"type": "server",
"redirect": "/teacher/homework/submissions",
"permission": "homework:grade"
},
"/teacher/exams/grading/[submissionId]": {
"component": "重定向",
"type": "server",
"redirect": "/teacher/homework/submissions",
"permission": "homework:grade"
},
"/teacher/grades": {
"component": "成绩管理首页",
"type": "server",
"module": "grades",
"dataAccess": [
"grades/actions.getGradeRecordsAction"
],
"permission": "grade_record:read"
},
"/teacher/grades/entry": {
"component": "批量成绩录入",
"type": "server",
"module": "grades",
"actions": [
"grades/actions.batchCreateGradeRecordsAction",
"grades/actions.createGradeRecordAction"
],
"permission": "grade_record:manage"
},
"/teacher/grades/stats": {
"component": "成绩统计报表",
"type": "server",
"module": "grades",
"dataAccess": [
"grades/actions.getClassGradeStatsAction",
"grades/actions.getClassRankingAction"
],
"permission": "grade_record:read"
},
"/teacher/course-plans": {
"component": "CoursePlanList (teacher)",
"type": "client",
"module": "course-plans",
"dataAccess": [
"course-plans/data-access.getCoursePlans (filtered by teacherId)"
],
"permission": "course_plan:read"
},
"/teacher/course-plans/[id]": {
"component": "CoursePlanDetail (teacher, read-only)",
"type": "client",
"module": "course-plans",
"dataAccess": [
"course-plans/data-access.getCoursePlanById"
],
"permission": "course_plan:read"
},
"/teacher/attendance": {
"component": "AttendanceRecordList + AttendanceFilters",
"type": "server",
"module": "attendance",
"dataAccess": [
"attendance/data-access.getAttendanceRecords",
"classes/data-access.getTeacherClasses"
],
"permission": "attendance:manage",
"description": "教师考勤记录列表权限requirePermission(ATTENDANCE_MANAGE)"
},
"/teacher/attendance/sheet": {
"component": "AttendanceSheet",
"type": "client",
"module": "attendance",
"actions": [
"batchRecordAttendanceAction",
"getClassAttendanceForDateAction"
],
"dataAccess": [
"attendance/data-access.getClassStudentsForAttendance"
],
"permission": "attendance:manage",
"description": "批量点名页面权限requirePermission(ATTENDANCE_MANAGE)"
},
"/teacher/attendance/stats": {
"component": "AttendanceStatsCard",
"type": "server",
"module": "attendance",
"dataAccess": [
"attendance/data-access-stats.getClassAttendanceStats",
"classes/data-access.getTeacherClasses"
],
"permission": "attendance:read",
"description": "班级考勤统计权限requirePermission(ATTENDANCE_READ)"
},
"/teacher/schedule-changes": {
"component": "ScheduleChangeForm + ScheduleChangeList",
"type": "server",
"module": "scheduling",
"dataAccess": [
"scheduling/actions.getAdminClassesForScheduling",
"scheduling/actions.getTeachersForScheduling",
"scheduling/actions.getScheduleChanges (requesterId=ctx.userId)"
],
"actions": [
"requestScheduleChangeAction"
],
"permission": "schedule:adjust",
"description": "教师调课/代课申请页面(提交申请+查看本人申请列表权限requirePermission(SCHEDULE_ADJUST)admin 角色查看全部申请)"
},
"/teacher/diagnostic": {
"component": "ReportList",
"type": "client",
"module": "diagnostic",
"dataAccess": [
"diagnostic/data-access-reports.getDiagnosticReports"
],
"actions": [
"publishReportAction",
"deleteReportAction"
],
"permission": "diagnostic:read",
"description": "学情诊断报告列表reportType/status 过滤器权限requirePermission(DIAGNOSTIC_READ)DataScope.class_members 仅查看自己报告;发布/删除操作需 DIAGNOSTIC_MANAGE"
},
"/teacher/diagnostic/student/[studentId]": {
"component": "StudentDiagnosticView",
"type": "client",
"module": "diagnostic",
"dataAccess": [
"diagnostic/data-access.getStudentMasterySummary",
"diagnostic/data-access.getKnowledgePointStats (班级平均对比)",
"diagnostic/data-access-reports.getDiagnosticReports"
],
"actions": [
"generateStudentReportAction"
],
"permission": "diagnostic:read",
"description": "学生学情诊断视图(概览卡片+雷达图+强项/弱项+生成报告[DIAGNOSTIC_MANAGE]+最新报告权限getAuthContext + DataScope 二次校验class_members 仅自己children 仅子女)"
},
"/teacher/diagnostic/class/[classId]": {
"component": "ClassDiagnosticView",
"type": "client",
"module": "diagnostic",
"dataAccess": [
"diagnostic/data-access.getClassMasterySummary"
],
"actions": [
"generateClassReportAction"
],
"permission": "diagnostic:read",
"description": "班级学情诊断视图(概览+知识点热力图+排名表+需重点关注学生+生成班级报告[DIAGNOSTIC_MANAGE]权限getAuthContext + DataScope 校验class_taught 必须包含 classIdclass_members/children notFound"
},
"/teacher/elective": {
"component": "ElectiveCourseList (teacher)",
"type": "server",
"module": "elective",
"dataAccess": [
"elective/data-access.getElectiveCourses (scope=class_taught/owned, currentUserId)"
],
"actions": [
"deleteElectiveCourseAction",
"openSelectionAction",
"closeSelectionAction",
"runLotteryAction"
],
"permission": "elective:manage",
"description": "教师选修课程列表权限requirePermission(ELECTIVE_MANAGE)DataScope.class_taught/owned 按 teacherId 过滤)"
},
"/teacher/lesson-plans": {
"component": "LessonPlanList",
"type": "server",
"module": "lesson-preparation",
"method": "GET",
"dataAccess": [
"lesson-preparation/data-access.getLessonPlans"
],
"permission": "lesson_plan:read",
"description": "教师课案列表权限requirePermission(LESSON_PLAN_READ)"
},
"/teacher/lesson-plans/new": {
"component": "LessonPlanEditor",
"type": "client",
"module": "lesson-preparation",
"method": "GET",
"actions": [
"createLessonPlanAction",
"getLessonPlanTemplatesAction",
"getKnowledgePointOptionsAction"
],
"permission": "lesson_plan:create",
"description": "新建课案页面权限requirePermission(LESSON_PLAN_CREATE)"
},
"/teacher/lesson-plans/[planId]/edit": {
"component": "LessonPlanEditor",
"type": "client",
"module": "lesson-preparation",
"method": "GET",
"dataAccess": [
"lesson-preparation/data-access.getLessonPlanById",
"lesson-preparation/data-access-versions.getLessonPlanVersions"
],
"actions": [
"updateLessonPlanAction",
"saveLessonPlanVersionAction",
"revertLessonPlanVersionAction",
"deleteLessonPlanAction",
"duplicateLessonPlanAction",
"saveAsTemplateAction",
"suggestKnowledgePointsAction",
"publishLessonPlanHomeworkAction"
],
"permission": "lesson_plan:update",
"permissionRead": "lesson_plan:read",
"description": "编辑课案页面权限requirePermission(LESSON_PLAN_UPDATE);只读访问需 LESSON_PLAN_READ"
}
},
"student": {
"/student/dashboard": {
"component": "StudentDashboardView",
"type": "server",
"dataAccess": [
"dashboard/data-access (student)",
"homework/data-access.getStudentDashboardGrades",
"classes/data-access.getStudentClasses"
],
"permission": "homework:submit"
},
"/student/learning/assignments": {
"component": "学生作业列表",
"type": "server",
"module": "homework",
"dataAccess": [
"homework/data-access.getStudentHomeworkAssignments"
],
"permission": "homework:submit"
},
"/student/learning/assignments/[assignmentId]": {
"component": "学生作答/复习",
"type": "client",
"module": "homework",
"actions": [
"startHomeworkSubmissionAction",
"saveHomeworkAnswerAction",
"submitHomeworkAction"
],
"dataAccess": [
"homework/data-access.getStudentHomeworkTakeData"
],
"permission": "homework:submit"
},
"/student/learning/courses": {
"component": "StudentCoursesView",
"type": "server",
"permission": "homework:submit"
},
"/student/learning/textbooks": {
"component": "学生教材列表(只读)",
"type": "server",
"module": "textbooks",
"dataAccess": [
"textbooks/data-access.getTextbooks"
],
"permission": "textbook:read"
},
"/student/learning/textbooks/[id]": {
"component": "学生教材阅读(只读)",
"type": "client",
"module": "textbooks",
"dataAccess": [
"textbooks/data-access.getTextbookById",
"getChaptersByTextbookId",
"getKnowledgePointsByTextbookId"
],
"permission": "textbook:read"
},
"/student/schedule": {
"component": "学生课表",
"type": "server",
"module": "classes",
"dataAccess": [
"classes/data-access.getStudentSchedule"
],
"permission": "homework:submit"
},
"/student/grades": {
"component": "我的成绩",
"type": "server",
"module": "grades",
"dataAccess": [
"grades/actions.getStudentGradeSummaryAction"
],
"permission": "grade_record:read"
},
"/student/attendance": {
"component": "StudentAttendanceView",
"type": "server",
"module": "attendance",
"dataAccess": [
"attendance/data-access-stats.getStudentAttendanceSummary"
],
"permission": "attendance:read",
"description": "学生考勤视图(统计卡片 + 最近记录权限requirePermission(ATTENDANCE_READ)DataScope.class_members 仅查自己)"
},
"/student/diagnostic": {
"component": "StudentDiagnosticView",
"type": "client",
"module": "diagnostic",
"dataAccess": [
"diagnostic/data-access.getStudentMasterySummary (ctx.userId)",
"diagnostic/data-access-reports.getDiagnosticReports (studentId=ctx.userId)"
],
"permission": "diagnostic:read",
"description": "学生本人学情诊断视图(概览+雷达图+强项/弱项+最新报告权限requirePermission(DIAGNOSTIC_READ)DataScope.class_members 仅查自己)"
},
"/student/elective": {
"component": "StudentSelectionView",
"type": "server",
"module": "elective",
"dataAccess": [
"elective/data-access-selections.getAvailableCoursesForStudent",
"elective/data-access-selections.getStudentSelections"
],
"actions": [
"selectCourseAction",
"dropCourseAction"
],
"permission": "elective:select",
"description": "学生选课页面(可选课程列表 + 我的选课记录权限requirePermission(ELECTIVE_SELECT)"
}
},
"management": {
"/management/grade/classes": {
"component": "GradeClassesClient",
"type": "client",
"module": "classes",
"permission": "grade:manage"
},
"/management/grade/insights": {
"component": "年级作业洞察",
"type": "server",
"dataAccess": [
"classes/data-access.getGradeHomeworkInsights"
],
"permission": "grade:manage"
}
},
"parent": {
"/parent/dashboard": {
"component": "ParentDashboard",
"type": "server",
"module": "parent",
"dataAccess": [
"parent/data-access.getParentDashboardData"
],
"permission": "auth_required",
"description": "家长仪表盘首页(问候语 + 子女卡片网格权限requireAuth()"
},
"/parent/children/[studentId]": {
"component": "ChildDetailHeader + ChildDetailPanel",
"type": "server",
"module": "parent",
"dataAccess": [
"parent/data-access.getChildDashboardData"
],
"permission": "auth_required",
"description": "子女详情页(头部 + 作业/成绩/课表面板权限requireAuth() + 二次校验 ctx.dataScope.childrenIds 包含 studentId"
},
"/parent/grades": {
"component": "子女成绩",
"type": "server",
"module": "grades",
"dataAccess": [
"grades/data-access.getStudentGradeSummary"
],
"permission": "grade_record:read",
"description": "家长成绩视图(按 DataScope.children 过滤)"
},
"/parent/attendance": {
"component": "StudentAttendanceView (per child)",
"type": "server",
"module": "attendance",
"dataAccess": [
"parent/data-access.getChildren",
"attendance/data-access-stats.getStudentAttendanceSummary"
],
"permission": "attendance:read",
"description": "家长考勤视图(遍历子女,每个子女展示 StudentAttendanceView权限requirePermission(ATTENDANCE_READ)DataScope.children 仅查子女)"
}
},
"root": {
"/": {
"component": "重定向",
"type": "server",
"redirect": "/dashboard"
}
},
"shared": {
"/dashboard": {
"component": "角色路由分发",
"type": "server",
"redirect": "按permissions判断→/admin|/teacher|/student|/parent"
},
"/profile": {
"component": "ProfilePage",
"type": "server",
"permission": "auth_required"
},
"/settings": {
"component": "SettingsPage",
"type": "server",
"permission": "auth_required",
"dataAccess": [
"messaging/notification-preferences.getNotificationPreferences (re-export from notifications/preferences.ts)"
],
"description": "设置页面(按角色分发 AdminSettingsView/TeacherSettingsView/StudentSettingsView含 General/Appearance/Security/Notifications tabNotifications 渲染 NotificationPreferencesForm"
},
"/announcements": {
"component": "AnnouncementList (published only)",
"type": "server",
"module": "announcements",
"dataAccess": [
"announcements/data-access.getAnnouncements (status=published)"
],
"permission": "announcement:read"
}
},
"messages": {
"/messages": {
"component": "MessageList + NotificationList",
"type": "server",
"module": "messaging",
"dataAccess": [
"messaging/data-access.getMessages",
"messaging/data-access.getNotifications"
],
"permission": "message:read",
"description": "消息首页(收件箱/已发送列表 + 通知列表权限requirePermission(MESSAGE_READ)"
},
"/messages/[id]": {
"component": "MessageDetail",
"type": "server",
"module": "messaging",
"dataAccess": [
"messaging/data-access.getMessageById",
"messaging/data-access.getMessageThread"
],
"actions": [
"markMessageAsReadAction (自动已读)"
],
"permission": "message:read",
"description": "消息详情含回复线程权限requirePermission(MESSAGE_READ)"
},
"/messages/compose": {
"component": "MessageCompose",
"type": "server",
"module": "messaging",
"dataAccess": [
"messaging/data-access.getRecipients"
],
"permission": "message:send",
"description": "写消息页面(支持 reply 模式 via searchParams: receiverId, subject, parentMessageId权限requirePermission(MESSAGE_SEND)"
}
}
},
"apiRoutes": {
"/api/auth/[...nextauth]": {
"methods": [
"GET",
"POST"
],
"handler": "auth.handlers",
"auth": "public"
},
"/api/ai/chat": {
"methods": [
"POST"
],
"handler": "createAiChatCompletion",
"auth": "AI_CHAT",
"validation": "parseAiChatPayload (Zod)"
},
"/api/onboarding/complete": {
"methods": [
"POST"
],
"handler": "onboarding complete",
"auth": "required",
"validation": "Zod schema"
},
"/api/onboarding/status": {
"methods": [
"GET"
],
"handler": "onboarding status",
"auth": "required"
},
"/api/upload": {
"methods": [
"POST"
],
"handler": "文件上传 (multipart/form-data)",
"auth": "requireAuth",
"module": "files",
"validation": "isAllowedMimeType + MAX_FILE_SIZE (10MB)",
"description": "保存文件到 public/uploads/YYYY-MM/cuid.ext写入 fileAttachments 表,返回 FileUploadResult"
},
"/api/files/[id]": {
"methods": [
"GET",
"DELETE"
],
"handler": "文件元数据查询/删除",
"auth": "GET: requireAuth, DELETE: requirePermission(FILE_DELETE)",
"module": "files",
"description": "GET 返回文件元数据DELETE 删除 DB 记录并 unlink 磁盘文件(静默失败)"
},
"/api/files/batch-delete": {
"methods": [
"POST"
],
"handler": "批量删除文件",
"auth": "requirePermission(FILE_DELETE)",
"module": "files",
"validation": "JSON body { ids: string[] },空数组返回 400",
"description": "先通过 getFileAttachmentsByIds 查出文件记录,并行调用 storageProvider.delete 删除磁盘文件(静默失败),再调用 deleteFileAttachments 删除 DB 记录(失败时回退到逐条删除);响应 { success, message, deletedCount, failedIds }"
},
"/api/search": {
"methods": [
"GET"
],
"handler": "全局全文检索",
"auth": "requireAuth",
"module": "shared.db (questions/textbooks/exams/announcements)",
"validation": "query params: q (关键词), type=all|question|textbook|exam|announcement, page=1, pageSize=10 (上限 50)",
"description": "并行查询 questions/textbooks/exams/announcements公告仅 status=published按 createdAt 降序排序后分页question content 字段 CAST AS CHAR 模糊匹配;返回 { success, query, type, results: [{ id, title, snippet, type, href, createdAt }], total, page, pageSize }"
},
"/api/export": {
"methods": [
"POST"
],
"handler": "Excel 导出grades/users/attendance",
"auth": "requireAuth",
"module": "shared.lib.excel + users/grades",
"validation": "JSON body { type, params }",
"description": "按 type 分发到 exportGradeRecordsToExcel/exportUsersToExcel返回 application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 二进制流"
},
"/api/import": {
"methods": [
"POST"
],
"handler": "Excel 解析预览(不写 DB",
"auth": "requirePermission(USER_MANAGE)",
"module": "shared.lib.excel",
"validation": "multipart/form-data file限 .xlsx/.xls10MB 上限",
"description": "接收 Excel 文件,调用 parseExcel 返回 sheets 预览数据(实际导入由 users/actions.importUsersAction 完成)"
}
},
"devops": {
"ci": {
"configFile": ".gitea/workflows/ci.yml",
"triggers": [
"push to main",
"pull_request to main",
"schedule cron 0 2 * * *"
],
"jobs": {
"build-deploy": {
"runsOn": "CDCD",
"container": "dockerreg.eazygame.cn/node-with-docker:22",
"trigger": "push/PR to main",
"steps": [
"checkout",
"cache npm",
"configure npm proxy",
"npm ci",
"lint",
"typecheck",
"install playwright chromium",
"integration tests",
"e2e tests",
"cache next.js build",
"build",
"prepare standalone",
"deploy to docker"
]
},
"security-scan": {
"runsOn": "ubuntu-latest",
"trigger": "push/PR to main",
"needs": "build-deploy",
"continueOnError": true,
"steps": [
"checkout",
"setup node 20",
"npm ci",
"npm audit --audit-level=moderate + 生成 audit-report.json (continue-on-error)",
"Snyk scan --severity-threshold=high --sarif-file-output=snyk.sarif (env SNYK_TOKEN, continue-on-error)",
"Trivy fs scan json+table (continue-on-error)",
"OWASP ZAP baseline scan target=NEXTAUTH_URL||localhost:8015 cmd_options='-a -j' (continue-on-error)",
"upload security-reports artifact (audit-report.json, trivy-fs-report.json, snyk.sarif)"
]
},
"scheduled-backup": {
"runsOn": "ubuntu-latest",
"trigger": "schedule cron 0 2 * * *",
"condition": "github.event_name == 'schedule'",
"steps": [
"checkout",
"run scripts/backup-db.sh (env DATABASE_URL, BACKUP_DIR)",
"run scripts/backup-verify.sh (校验备份完整性)",
"run scripts/backup-offsite-sync.sh (异地同步, env BACKUP_OFFSITE_*, 失败不阻塞)",
"upload backups/ artifact (retention 30 days)"
]
},
"backup-verify": {
"runsOn": "ubuntu-latest",
"trigger": "schedule",
"condition": "github.event_name == 'schedule'",
"needs": "scheduled-backup",
"steps": [
"checkout",
"download db-backup artifact",
"run scripts/backup-verify.sh (独立校验)",
"run scripts/health-check.sh > health-report.json",
"upload backup-verify-report artifact (backups/, health-report.json, retention 7 days)"
]
},
"weekly-dr-drill": {
"runsOn": "ubuntu-latest",
"trigger": "schedule (每周触发, github.run_attempt % 7 == 0)",
"condition": "github.event_name == 'schedule' && github.run_attempt % 7 == 0",
"needs": "backup-verify",
"steps": [
"checkout",
"run scripts/dr-drill.sh (env DATABASE_URL, DR_DRILL_TEST_DB=next_edu_dr_drill)",
"upload dr-drill-report artifact (docs/dr/reports/, retention 90 days)"
]
}
}
},
"drDrillWorkflow": {
"configFile": ".gitea/workflows/dr-drill.yml",
"triggers": [
"schedule cron 0 4 * * 1 (每周一凌晨 4 点)",
"workflow_dispatch (inputs: backup_file, no_cleanup)"
],
"job": "dr-drill",
"runsOn": "ubuntu-latest",
"timeoutMinutes": 30,
"steps": [
"checkout",
"install mysql-client",
"prepare backup directory (mkdir backups docs/dr/reports)",
"download db-backup artifact (continue-on-error) 或现场执行 backup-db.sh",
"run scripts/dr-drill.sh (支持 --backup/--no-cleanup 参数)",
"upload dr-drill-report-${{ github.run_id }} artifact (docs/dr/reports/, retention 90 days)",
"on failure: webhook 通知运维团队 (DR_NOTIFICATION_WEBHOOK)"
]
},
"securityWorkflow": {
"configFile": ".gitea/workflows/security.yml",
"triggers": [
"schedule cron 0 3 * * 1 (每周一凌晨 3 点)",
"workflow_dispatch (inputs: target_url, skip_dast)"
],
"job": "deep-security-scan",
"runsOn": "ubuntu-latest",
"continueOnError": true,
"steps": [
"checkout",
"setup node 20",
"npm ci",
"npm audit + 生成 audit-report.json (依赖扫描)",
"Snyk scan --severity-threshold=medium --sarif-file-output=snyk.sarif (env SNYK_TOKEN, 深度依赖+静态分析)",
"Trivy fs scan json+table (文件系统扫描, trivy-fs-report.json)",
"Build Next.js standalone + docker build nextjs-app:scan + Trivy image scan (容器镜像扫描, trivy-image-report.json)",
"OWASP ZAP baseline scan (DAST, target=inputs.target_url||NEXTAUTH_URL||localhost:8015, 可通过 skip_dast 跳过)",
"Generate security-summary.md (jq 汇总各报告漏洞计数)",
"upload security-reports-full artifact (audit-report.json, trivy-fs-report.json, trivy-image-report.json, snyk.sarif, security-summary.md)"
],
"configFiles": {
"suppressions": ".gitea/suppressions.json (Snyk 漏洞抑制, 每条含 id/package/severity/reason/expires/owner)",
"trivyignore": ".trivyignore (Trivy CVE 忽略列表, 每行一个 CVE 带注释)"
}
},
"scripts": {
"scripts/audit.sh": {
"type": "bash",
"purpose": "依赖安全审计,运行 npm audit --audit-level=moderate失败时生成 audit-report.json",
"platform": "Linux/macOS"
},
"scripts/audit.ps1": {
"type": "powershell",
"purpose": "依赖安全审计Windows 版本)",
"platform": "Windows"
},
"scripts/backup-db.sh": {
"type": "bash",
"purpose": "MySQL 数据库备份,从 DATABASE_URL 解析连接信息gzip 压缩,保留 RETENTION_DAYS 天(默认 30",
"env": [
"DATABASE_URL",
"BACKUP_DIR",
"RETENTION_DAYS"
],
"output": "${BACKUP_DIR}/db_backup_${TIMESTAMP}.sql.gz"
},
"scripts/restore-db.sh": {
"type": "bash",
"purpose": "MySQL 数据库恢复,从指定备份文件恢复",
"env": [
"DATABASE_URL"
],
"usage": "./restore-db.sh <backup_file>"
},
"scripts/test-backup.sh": {
"type": "bash",
"purpose": "备份流程测试,执行一次备份并验证最新备份文件"
},
"scripts/backup-verify.sh": {
"type": "bash",
"purpose": "备份完整性校验:检查文件存在/大小/gzip 完整性/SQL 内容结构/SQL 语法(可选,需 DATABASE_URL",
"env": [
"BACKUP_DIR",
"DATABASE_URL",
"BACKUP_VERIFY_MIN_SIZE"
],
"exitCodes": {
"0": "校验通过",
"1": "校验失败"
},
"options": [
"--min-size BYTES",
"--no-sql-check",
"--help"
]
},
"scripts/backup-offsite-sync.sh": {
"type": "bash",
"purpose": "异地备份同步:支持 S3/OSS/NFS 后端,同步后校验文件数量,清理远程过期备份(保留 90 天)",
"env": [
"BACKUP_DIR",
"BACKUP_OFFSITE_BACKEND",
"BACKUP_OFFSITE_REMOTE",
"BACKUP_OFFSITE_BUCKET",
"BACKUP_OFFSITE_ACCESS_KEY",
"BACKUP_OFFSITE_SECRET_KEY",
"BACKUP_OFFSITE_REGION",
"BACKUP_OFFSITE_RETENTION_DAYS"
],
"tools": [
"aws-cli (s3)",
"rclone (s3/oss)",
"ossutil (oss)",
"rsync (nfs)"
],
"exitCodes": {
"0": "同步成功",
"1": "同步失败"
},
"options": [
"--backend TYPE",
"--no-cleanup",
"--no-verify",
"--help"
]
},
"scripts/dr-drill.sh": {
"type": "bash",
"purpose": "灾备演练:创建测试库→从备份恢复→数据完整性检查→冒烟测试→清理→生成报告到 docs/dr/reports/",
"env": [
"DATABASE_URL",
"BACKUP_DIR",
"DR_DRILL_TEST_DB",
"DR_DRILL_REPORT_DIR"
],
"exitCodes": {
"0": "演练成功",
"1": "演练失败"
},
"options": [
"--backup FILE",
"--test-db NAME",
"--no-cleanup",
"--report-dir DIR",
"--help"
]
},
"scripts/dr-drill.ps1": {
"type": "powershell",
"purpose": "灾备演练Windows PowerShell 5.1+ 版本),功能同 Bash 版本",
"env": [
"DATABASE_URL",
"BACKUP_DIR",
"DR_DRILL_TEST_DB",
"DR_DRILL_REPORT_DIR"
],
"platform": "Windows",
"options": [
"-BackupFile FILE",
"-TestDb NAME",
"-NoCleanup",
"-ReportDir DIR",
"-Help"
]
},
"scripts/failover.sh": {
"type": "bash",
"purpose": "故障切换:检测主库健康→提升备库→更新应用配置→重启应用→验证切换",
"env": [
"DATABASE_URL",
"DATABASE_URL_STANDBY",
"FAILOVER_APP_URL",
"FAILOVER_APP_NAME",
"FAILOVER_CONFIG_FILE",
"FAILOVER_LOG_FILE"
],
"exitCodes": {
"0": "切换成功",
"1": "切换失败"
},
"options": [
"--auto",
"--primary URL",
"--standby URL",
"--app-url URL",
"--no-restart",
"--dry-run",
"--help"
]
},
"scripts/health-check.sh": {
"type": "bash",
"purpose": "健康检查:检查应用 HTTP/数据库连接/磁盘空间/备份新鲜度,输出 JSON 报告",
"env": [
"DATABASE_URL",
"HEALTH_CHECK_URL",
"BACKUP_DIR",
"HEALTH_CHECK_DISK_THRESHOLD",
"HEALTH_CHECK_BACKUP_MAX_AGE"
],
"exitCodes": {
"0": "健康",
"1": "异常"
},
"options": [
"--app-url URL",
"--no-app",
"--no-db",
"--no-disk",
"--no-backup",
"--disk-threshold PCT",
"--backup-max-age HRS",
"--help"
]
}
},
"packageJsonScripts": {
"audit": "npm audit --audit-level=moderate",
"audit:report": "npm audit --json > audit-report.json",
"security:audit": "npm audit --audit-level=moderate",
"security:scan": "bash scripts/security-scan.sh",
"backup": "bash scripts/backup-db.sh",
"restore": "bash scripts/restore-db.sh",
"dr:backup-verify": "bash scripts/backup-verify.sh",
"dr:offsite-sync": "bash scripts/backup-offsite-sync.sh",
"dr:drill": "bash scripts/dr-drill.sh",
"dr:drill:ps1": "powershell -ExecutionPolicy Bypass -File scripts/dr-drill.ps1",
"dr:health-check": "bash scripts/health-check.sh",
"dr:failover": "bash scripts/failover.sh"
},
"drDocs": {
"docs/dr/dr-plan.md": "灾备计划文档RTO/RPO 定义4h/24h、备份策略、故障切换流程、联系人列表、恢复步骤",
"docs/dr/dr-runbook.md": "灾备操作手册:数据库故障/应用故障/备份失败/异地同步失败/演练失败/磁盘不足场景的诊断与处理",
"docs/dr/reports/": "灾备演练报告存档目录Markdown 格式,由 dr-drill.sh 生成)",
"docs/dr/logs/": "故障切换日志目录(由 failover.sh 生成)"
},
"drEnvVars": {
"BACKUP_OFFSITE_BACKEND": "异地备份后端类型: s3|oss|nfs|none",
"BACKUP_OFFSITE_REMOTE": "远程存储路径",
"BACKUP_OFFSITE_BUCKET": "存储桶名称(仅 s3/oss",
"BACKUP_OFFSITE_ACCESS_KEY": "访问密钥",
"BACKUP_OFFSITE_SECRET_KEY": "秘密密钥",
"BACKUP_OFFSITE_REGION": "区域(默认 us-east-1",
"BACKUP_OFFSITE_RETENTION_DAYS": "远程备份保留天数(默认 90",
"DR_DRILL_TEST_DB": "演练测试数据库名(默认 next_edu_dr_drill",
"HEALTH_CHECK_URL": "应用健康检查 URL默认 http://localhost:8015",
"DATABASE_URL_STANDBY": "备库连接 URL故障切换时使用",
"FAILOVER_APP_NAME": "应用容器名(默认 nextjs-app"
},
"gitignore": {
"added": [
"/backups/",
"/audit-report.json",
"/trivy-fs-report.json",
"/trivy-image-report.json",
"/snyk.sarif",
"/security-summary.md",
"/playwright-report/",
"/test-results/",
"/tests/visual/.auth/"
],
"exceptions": [
".env.example (灾备环境变量示例,允许提交)"
]
}
},
"testing": {
"e2e": {
"configFile": "playwright.config.ts",
"testDir": "./tests",
"baseURL": "http://127.0.0.1:3000",
"webServer": {
"command": "npm run dev",
"port": 3000,
"timeout": 180000,
"reuseExistingServer": "!process.env.CI",
"env": {
"SKIP_ENV_VALIDATION": "1",
"NEXTAUTH_SECRET": "test-nextauth-secret",
"NEXTAUTH_URL": "http://127.0.0.1:3000",
"DATABASE_URL": "mysql://test:test@127.0.0.1:3306/test_db"
}
},
"projects": [
{
"name": "chromium",
"testDir": "./tests/e2e",
"channel": "CI: undefined, local: chrome"
},
{
"name": "visual-chromium",
"testDir": "./tests/visual",
"channel": "CI: undefined, local: chrome"
}
],
"snapshotPathTemplate": "{testDir}/__screenshots__/{testFilePath}/{arg}{ext}",
"expect": {
"toHaveScreenshot": {
"maxDiffPixelRatio": 0.01,
"animations": "disabled",
"caret": "hide"
},
"toMatchSnapshot": {
"maxDiffPixelRatio": 0.01
}
},
"retries": "CI: 2, local: 0",
"workers": "CI: 2, local: default",
"testFiles": {
"smoke-auth.spec.ts": {
"coverage": "登录/注册页面控件渲染冒烟测试",
"requiresDb": false
},
"auth-business-flow.spec.ts": {
"coverage": "注册→登录→访问受保护区域完整流程",
"requiresDb": true
},
"full-route-regression.spec.ts": {
"coverage": "全路由清单完整性 + 公开/受保护路由守卫",
"requiresDb": false
},
"auth.spec.ts": {
"coverage": "认证页面(登录/注册/隐私/协议)渲染 + 未认证重定向",
"requiresDb": false
},
"navigation.spec.ts": {
"coverage": "admin/teacher/student 导航链接无 404",
"requiresDb": true,
"envVars": [
"E2E_ADMIN_EMAIL",
"E2E_TEACHER_EMAIL",
"E2E_STUDENT_EMAIL"
]
},
"announcements.spec.ts": {
"coverage": "公告页面未认证重定向 + 登录后渲染",
"requiresDb": "partial"
},
"grades.spec.ts": {
"coverage": "成绩页面未认证重定向 + 登录后渲染",
"requiresDb": "partial"
}
}
},
"visual": {
"configFile": "tests/visual/visual.config.ts",
"snapshotDir": "tests/visual/__screenshots__",
"storageStateDir": "tests/visual/.auth/",
"viewports": {
"desktop": {
"width": 1920,
"height": 1080
},
"tablet": {
"width": 768,
"height": 1024
},
"mobile": {
"width": 375,
"height": 812
}
},
"themes": [
"light",
"dark"
],
"defaultMaxDiffPixelRatio": 0.01,
"testAccounts": {
"admin": {
"email": "admin@xiaoxue.edu.cn",
"envVars": [
"VISUAL_ADMIN_EMAIL",
"VISUAL_ADMIN_PASSWORD"
]
},
"teacher": {
"email": "admin@xiaoxue.edu.cn",
"envVars": [
"VISUAL_TEACHER_EMAIL",
"VISUAL_TEACHER_PASSWORD"
]
},
"student": {
"email": "admin@xiaoxue.edu.cn",
"envVars": [
"VISUAL_STUDENT_EMAIL",
"VISUAL_STUDENT_PASSWORD"
]
}
},
"testFiles": {
"homepage.spec.ts": {
"coverage": "登录页在 desktop/tablet/mobile × light/dark 下的快照",
"requiresDb": false
},
"admin-dashboard.spec.ts": {
"coverage": "管理员仪表盘整页 + 侧边栏/主内容区组件快照",
"requiresDb": true,
"role": "admin"
},
"teacher-dashboard.spec.ts": {
"coverage": "教师仪表盘整页 + 侧边栏/主内容区组件快照",
"requiresDb": true,
"role": "teacher"
},
"student-dashboard.spec.ts": {
"coverage": "学生仪表盘整页 + 侧边栏/主内容区组件快照",
"requiresDb": true,
"role": "student"
}
},
"helpers": {
"auth.ts": [
"setupAuthState(role)",
"loginByUI(page, role)",
"storageStatePath(role)"
],
"visual-helpers.ts": [
"setViewport(page, size)",
"setTheme(page, theme)",
"waitForPageReady(page)",
"maskDynamicElements(page, selectors)",
"buildMaskOption(masks)"
]
},
"scripts": {
"test:visual": "playwright test --project=visual-chromium",
"test:visual:update": "playwright test --project=visual-chromium --update-snapshots"
}
}
}
}