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.
14310 lines
488 KiB
JSON
14310 lines
488 KiB
JSON
{
|
||
"_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-access;questions 模块 getKnowledgePointOptionsAction 改为调用 textbooks data-access;grades 模块多处直查 classes/classEnrollments/subjects/users 改为调用对应模块 data-access;classes 模块 actions 直查 grades 表改为调用 school data-access,getSessionTeacherId 改为通过 auth-guard.getAuthContext;attendance 模块 getClassStudentsForAttendance 改为通过 classes data-access;scheduling 模块 autoScheduleAction 直查 users 改为通过 users data-access;notifications 模块 sendClassNotificationAction 直查 classes/classEnrollments 改为通过 classes data-access;proctoring 模块跨模块直查 exams/examSubmissions/users 改为通过 exams/users data-access;diagnostic 模块 updateMasteryFromSubmission 跨模块直查 4 张表改为通过 exams/questions data-access;dashboard 教师仪表盘直查 users 改为通过 users data-access;users 模块 updateUserProfile 绕过 data-access 已下沉;P2-20 已修复:getDemoStudentUser 从 homework 模块迁移至 users 模块 getCurrentStudentUser(homework 保留 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 获取 session;P1-6 已修复:http-utils.ts 新增 getUserAgent(),audit-logger/change-logger/login-logger 删除本地重复 IP/UA 提取逻辑,统一复用 resolveClientIp/getUserAgent;P0-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-access;P2-2 已修复:ai.ts 拆分为 ai/ 目录;P0-8 已修复:school actions 层 DB 操作下沉到 data-access,logAudit 改为 after() 异步非阻塞;P0-7 已修复:classes/data-access-stats.ts 和 data-access-students.ts 不再直查 homework/exams 表,改为调用新增的 homework/data-access-classes.ts 暴露的 7 个函数;新增 diagnostic/schema.ts(6 个 Zod schema)和 classes/schema.ts(13 个 Zod schema),actions.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 单一入口获取 session,session.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.ts,classes 仅保留读函数)",
|
||
"✅ 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": "从请求头解析客户端 IP(x-forwarded-for 第一段/x-real-ip,best-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-Agent(best-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-invalid(error存在则为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/search,Cmd/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+knowledgePointId,onDuplicateKeyUpdate upsert)"
|
||
},
|
||
"learningDiagnosticReports": {
|
||
"fields": [
|
||
"id",
|
||
"studentId",
|
||
"generatedBy",
|
||
"reportType",
|
||
"period",
|
||
"summary",
|
||
"strengths",
|
||
"weaknesses",
|
||
"recommendations",
|
||
"overallScore",
|
||
"status",
|
||
"createdAt",
|
||
"updatedAt"
|
||
],
|
||
"usedBy": [
|
||
"diagnostic"
|
||
],
|
||
"description": "学情诊断报告(reportType: individual/class/grade;status: 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/cancelled;selectionMode: fcfs/lottery)"
|
||
},
|
||
"courseSelections": {
|
||
"fields": [
|
||
"id",
|
||
"courseId",
|
||
"studentId",
|
||
"status",
|
||
"priority",
|
||
"selectedAt",
|
||
"enrolledAt",
|
||
"droppedAt",
|
||
"lotteryRank",
|
||
"createdAt",
|
||
"updatedAt"
|
||
],
|
||
"usedBy": [
|
||
"elective"
|
||
],
|
||
"description": "选课记录(复合主键 courseId+studentId;status: 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 desc,P0-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 exam,P0-7 新增)",
|
||
"usedBy": [
|
||
"classes/data-access-students.getStudentsSubjectScores"
|
||
]
|
||
},
|
||
{
|
||
"name": "getHomeworkSubmissionsForAssignments",
|
||
"file": "data-access-classes.ts",
|
||
"signature": "(assignmentIds: string[]) => Promise<HomeworkSubmissionScoreRecord[]>",
|
||
"purpose": "返回指定作业的提交记录(按 createdAt desc,P0-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 Provider(P1 已修复: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 tab,Security 含 PasswordChangeForm,Notifications 含 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 哈希,自动创建 usersToRoles,student 通过邀请码自动加入班级——委托 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(精确或前缀匹配)与 search(originalName/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": "导出成绩到 Excel(detail=成绩明细+统计汇总,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/grade),SiteHeader 通知下拉菜单展示未读数",
|
||
"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.ts,P0-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.ts,P0-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.ts,P0-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.ts,P0-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.ts,P0-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.ts,P0-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.ts,P0-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/email;wechatOpenId 暂不支持,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": "创建微信渠道发送器(配置完整用真实发送器,否则 Mock;access_token 带缓存)",
|
||
"deps": [
|
||
"环境变量: WECHAT_APP_ID, WECHAT_APP_SECRET, WECHAT_TEMPLATE_ID"
|
||
]
|
||
},
|
||
{
|
||
"name": "createEmailSender",
|
||
"file": "channels/email-channel.ts",
|
||
"purpose": "创建邮件渠道发送器(配置 EMAIL_HOST 用 Nodemailer SMTP,否则 Mock;HTML 模板按 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": "保存班级排课规则(upsert,classId 为空时为全局规则)",
|
||
"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=draft,studentId 存生成者 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 按 teacherId,grade_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/waitlist,lottery 模式 selected)",
|
||
"usedBy": [
|
||
"actions.selectCourseAction"
|
||
]
|
||
},
|
||
{
|
||
"name": "dropCourse",
|
||
"file": "data-access-operations.ts",
|
||
"signature": "(courseId: string, studentId: string) => Promise<void>",
|
||
"purpose": "学生退课(status=dropped;FCFS 模式自动递补 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 默认 30,selectionMode 默认 fcfs,credit 默认 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.ts,classes 仅保留读函数"
|
||
},
|
||
{
|
||
"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 单一入口获取 session(dynamic 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.ts,data-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 messaging);notifications/dispatcher.ts 改为从本地 preferences.ts 导入偏好函数;messaging 模块通过 re-export 保持向后兼容;依赖方向变为单向:messaging → notifications(messaging 调用 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/getGradeOptions;questions 通过 textbooks data-access.getKnowledgePointOptions;homework 通过 exams/classes/school/users data-access;grades 通过 classes/school/users data-access;classes 通过 school/homework data-access;attendance 通过 classes data-access;scheduling 通过 users data-access;notifications 通过 classes data-access;proctoring 通过 exams/users data-access;diagnostic 通过 exams/questions/classes/users data-access;dashboard 通过各模块 data-access;users updateUserProfile 已下沉到 data-access。各模块 data-access 暴露的查询接口:classes(getClassExists/getClassNameById/getClassNamesByIds/getActiveStudentIdsByClassId/getStudentActiveClassId/getClassesByGradeId/verifyTeacherOwnsClass/getTeacherIdForMutations 等)、exams(getExamIdsByGradeIds/getExamSubjectIdMap/getExamWithQuestionsForHomework/getExamSubmissionWithAnswers/getExamForProctoringCrossModule 等)、school(getSubjectOptions/getGradeOptions)、users(getUserNamesByIds/getUserWithRole/getUserBasicInfo/getUserIdsByGradeId/getCurrentStudentUser)、textbooks(getKnowledgePointOptions)、questions(createQuestionWithRelations/getKnowledgePointsForQuestions)、homework/data-access-classes(7 个函数供 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-access:exams 新增 7 个 data-access 函数(actions.ts 831→691 行,data-access.ts 374→471 行);homework 新建 data-access-write.ts(285 行,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.ts,bcrypt哈希规范化→shared/lib/bcrypt-utils.ts,IP解析→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 必须包含 classId,class_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 tab,Notifications 渲染 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/.xls,10MB 上限",
|
||
"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"
|
||
}
|
||
}
|
||
}
|
||
}
|