feat: 新增备课模块并修复全模块 P0/P1/P2 缺陷
Some checks failed
Security / deep-security-scan (push) Failing after 20m5s
DR Drill / dr-drill (push) Failing after 1m31s
CI / scheduled-backup (push) Failing after 1m31s
CI / backup-verify (push) Has been skipped
CI / weekly-dr-drill (push) Failing after 0s
CI / build-deploy (push) Has been cancelled
CI / security-scan (push) Has been cancelled
Some checks failed
Security / deep-security-scan (push) Failing after 20m5s
DR Drill / dr-drill (push) Failing after 1m31s
CI / scheduled-backup (push) Failing after 1m31s
CI / backup-verify (push) Has been skipped
CI / weekly-dr-drill (push) Failing after 0s
CI / build-deploy (push) Has been cancelled
CI / security-scan (push) Has been cancelled
主要变更: - 新增 lesson-preparation 模块: 备课编辑器、节点编辑、AI 建议、知识点选择、版本历史、作业发布 - 新增 shared 通用组件: charts/question-bank-filters/schedule-list/ui (chip-nav/filter-bar/page-header/stat-card/stat-item) - 新增 student/admin 端 loading.tsx 与 error.tsx, 优化加载与错误态体验 - 新增 teacher/lesson-plans 页面 (列表/新建/编辑) - 新增 drizzle 迁移 0002_tiny_lionheart 及 snapshot - 新增 textbooks/schema.ts 与 exams/utils/normalize-structure.ts - 修复 Tiptap v3 SSR hydration 崩溃 (rich-text-block immediatelyRender: false) - 重构多模块 data-access/actions/组件, 修复权限校验与类型规范 - 同步架构文档 004/005 反映新增模块、导出、依赖关系 - 归档 bugs/* 测试报告与 e2e 测试脚本 (admin/parent/student/teacher web_test)
This commit is contained in:
@@ -390,6 +390,27 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
- `validatePassword()` / `isAccountLocked()` / `rateLimit()` — 安全策略
|
||||
- `exportToExcel()` / `parseExcel()` / `generateTemplate()` — Excel 工具
|
||||
- `cn()` / `formatDate()` / `formatFileSize()` — 通用工具
|
||||
- `getInitials(name)` / `formatDateForFile(d?)` — 通用工具(P1-c / P1-a 重构新增:从 parent/lib/utils.ts、grades/export-button.tsx 等多处重复实现抽取)
|
||||
- `downloadBase64File(base64, filename, mimeType?)` / `downloadBlob(blob, filename)` — 客户端文件下载(P1-c 重构新增:从 grades/export-button、users/user-import-dialog、audit/audit-log-export-button 三处重复实现抽取,位于 `lib/download.ts`)
|
||||
|
||||
**共享组件导出**(P0-b / P1-a / P1-b / P1-c / P2-a / P2-b / P3-a / P3-b / P3-c / P3-d 重构新增,按类别组织):
|
||||
|
||||
| 类别 | 组件 | 文件 | 用途 | 消费方数量 |
|
||||
|------|------|------|------|-----------|
|
||||
| **UI 组件** | `StatCard` | `components/ui/stat-card.tsx` | 统计卡片(标题+数值+图标+描述+跳转+骨架屏) | 8 个(P1-a) |
|
||||
| **UI 组件** | `StatItem` | `components/ui/stat-item.tsx` | 紧凑统计项(label+icon+value+hint,用于统计面板网格) | 8 个(P1-a) |
|
||||
| **UI 组件** | `ChipNav` | `components/ui/chip-nav.tsx` | 芯片导航组(通过 URL search params 切换筛选维度,Link 跳转) | 3 个(P1-b) |
|
||||
| **UI 组件** | `PageHeader` | `components/ui/page-header.tsx` | 页面头部(标题+描述+icon+actions,响应式布局) | 2 个(P2-b: profile/page.tsx, settings/security/page.tsx) |
|
||||
| **UI 组件** | `FilterBar` / `FilterSearchInput` / `FilterResetButton` | `components/ui/filter-bar.tsx` | 筛选栏容器+搜索框+重置按钮(统一布局壳,URL 状态由各模块处理) | 5 个(P3-b: exam/textbook/question/audit-log/login-log filters) |
|
||||
| **图表组件** | `ChartCardShell` | `components/charts/chart-card-shell.tsx` | 图表卡片外壳(Card+Header+EmptyState+Content 统一结构) | 8 个(P3-c) |
|
||||
| **图表组件** | `TrendLineChart` | `components/charts/trend-line-chart.tsx` | 趋势折线图(LineChart 统一配置,支持单/多系列) | 8 个(P3-c: grade-trend-chart 等) |
|
||||
| **图表组件** | `SimpleBarChart` | `components/charts/simple-bar-chart.tsx` | 柱状图(BarChart 统一配置,支持单/多 Bar + Cell 分桶着色) | 8 个(P3-c: grade-distribution-chart 等) |
|
||||
| **图表组件** | `ComparisonRadarChart` | `components/charts/comparison-radar-chart.tsx` | 对比雷达图(RadarChart 统一配置,支持双 Radar 对比) | 8 个(P3-c: subject-comparison-chart, mastery-radar-chart 等) |
|
||||
| **课表组件** | `ScheduleList` / `ScheduleListItem` | `components/schedule/schedule-list.tsx` | 课表列表+列表项(课程+时间+地点+班级徽章,separator/card 两种变体) | 3 个(P3-a: student-today-schedule-card, child-schedule-card, student-schedule-view) |
|
||||
| **题库组件** | `QuestionBankFilters` | `components/question/question-bank-filters.tsx` | 题库筛选栏(搜索+题型+难度,default/compact 两种布局) | 2 个(P3-d: exam-assembly, question-bank-picker) |
|
||||
| **设置组件** | `SettingsView` | `modules/settings/components/settings-view.tsx` | 统一设置页布局(4 标签页:General/Notifications/Appearance/Security,角色差异通过 props 注入) | 3 个(P2-a: admin/teacher/student 设置页) |
|
||||
|
||||
> 注:`SettingsView` 位于 `modules/settings/components/`(非 shared 层),因仅被 settings 模块消费,未下沉到 shared。此处列出以完整反映本次重构的组件抽取范围。
|
||||
|
||||
**依赖关系**:
|
||||
- 被依赖方:**所有模块**依赖 shared
|
||||
@@ -431,9 +452,22 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
| `lib/file-storage.ts` | - | 文件存储抽象 |
|
||||
| `hooks/use-permission.ts` | - | 客户端权限 Hook |
|
||||
| `components/ui/*` | 34 文件 | shadcn/ui 标准组件 |
|
||||
| `components/ui/stat-card.tsx` | 95 | StatCard 统计卡片(P1-a 新增) |
|
||||
| `components/ui/stat-item.tsx` | 38 | StatItem 紧凑统计项(P1-a 新增) |
|
||||
| `components/ui/chip-nav.tsx` | 78 | ChipNav 芯片导航(P1-b 新增) |
|
||||
| `components/ui/page-header.tsx` | 44 | PageHeader 页面头部(P2-b 新增,含 icon 属性) |
|
||||
| `components/ui/filter-bar.tsx` | 124 | FilterBar + FilterSearchInput + FilterResetButton(P3-b 新增) |
|
||||
| `components/charts/chart-card-shell.tsx` | 90 | ChartCardShell 图表卡片外壳(P3-c 新增) |
|
||||
| `components/charts/trend-line-chart.tsx` | 153 | TrendLineChart 趋势折线图(P3-c 新增) |
|
||||
| `components/charts/simple-bar-chart.tsx` | 162 | SimpleBarChart 柱状图(P3-c 新增) |
|
||||
| `components/charts/comparison-radar-chart.tsx` | 143 | ComparisonRadarChart 对比雷达图(P3-c 新增) |
|
||||
| `components/schedule/schedule-list.tsx` | 112 | ScheduleList + ScheduleListItem 课表列表(P3-a 新增) |
|
||||
| `components/question/question-bank-filters.tsx` | 137 | QuestionBankFilters 题库筛选栏(P3-d 新增) |
|
||||
| `lib/download.ts` | 47 | downloadBase64File + downloadBlob 客户端下载工具(P1-c 新增) |
|
||||
| `lib/utils.ts` | - | 通用工具(P1-a/P1-c 新增 getInitials + formatDateForFile) |
|
||||
| `components/onboarding-gate.tsx` | 312 | 引导流程(业务泄漏) |
|
||||
| `components/global-search.tsx` | 221 | 全局搜索(业务泄漏) |
|
||||
| `types/permissions.ts` | 92 | 54 个权限点常量 |
|
||||
| `types/permissions.ts` | 157 | 61 个权限点常量 + Role/DataScope/AuthContext 类型 |
|
||||
|
||||
---
|
||||
|
||||
@@ -445,6 +479,7 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
- Actions:`createExamAction` / `createAiExamAction` / `previewAiExamAction` / `regenerateAiQuestionAction` / `updateExamAction` / `deleteExamAction` / `duplicateExamAction` / `getExamPreviewAction` / `getSubjectsAction` / `getGradesAction`(✅ P1-2 已修复:actions 层不再直接访问 DB,全部下沉到 data-access)
|
||||
- Data-access:`getExams` / `getExamById` / `persistExamDraft` / `persistAiGeneratedExamDraft` / `buildExamDescription` / `resolveSubjectGradeNames` / `getExamCreatorId` / `updateExamWithQuestions` / `deleteExamById` / `duplicateExam` / `getExamPreview` / `getExamSubjects` / `getExamGrades`(后 7 个为 P1-2 新增)
|
||||
- AI Pipeline:`generateAiCreateDraftFromSource` / `generateAiPreviewData` / `regenerateAiQuestionByInstruction`
|
||||
- Utils:`normalizeStructure`(v3 新增:将持久化的 `exam.structure` unknown JSON 运行时校验并归一化为类型安全的 `ExamNode[]`,类型守卫模式无 `as` 断言,从 `teacher/exams/[id]/build/page.tsx` 提取)
|
||||
|
||||
**依赖关系**:
|
||||
- 依赖:`shared/*`、`@/auth`、`questions`(✅ P0-1 已修复:通过 data-access.createQuestionWithRelations)、`classes`(✅ P0-2 已修复:通过 data-access.getClassGradeIdsByClassIds)、`school`(✅ P1-1 已修复:通过 school data-access.getSubjectOptions/getGradeOptions)
|
||||
@@ -466,6 +501,7 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
| `data-access.ts` | 473 | 考试 CRUD(含 P1-2 新增 7 个写/查询函数,P0-1/P0-2 已修复:通过 questions/classes data-access 跨模块通信) |
|
||||
| `types.ts` | 31 | 类型定义 |
|
||||
| `hooks/use-exam-preview.ts` | 295 | 预览 Hook |
|
||||
| `utils/normalize-structure.ts` | 57 | v3 新增:exam.structure 运行时校验与归一化(从 build/page.tsx 提取) |
|
||||
| `components/*` | 18 文件 | 考试表单/组卷/预览组件 |
|
||||
|
||||
---
|
||||
@@ -601,12 +637,12 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
|
||||
**导出函数**:
|
||||
- Actions:`createTeacherClassAction` / `updateTeacherClassAction` / `deleteTeacherClassAction` / `createAdminClassAction` / `updateAdminClassAction` / `deleteAdminClassAction` / `createGradeClassAction` / `updateGradeClassAction` / `deleteGradeClassAction`
|
||||
- Data-access:`getAdminClasses` / `getTeacherClasses` / `getGradeManagedClasses` / `getStudentClasses` / `getClassDetails` / `getClassStudents` / `getClassSchedule` / `getClassHomeworkInsights` / `getGradeHomeworkInsights` / `getStudentsSubjectScores` / `verifyTeacherOwnsClass`(✅ P0-5 已修复:classSchedule 写函数 createClassScheduleItem/updateClassScheduleItem/deleteClassScheduleItem 已迁移至 scheduling/data-access-class-schedule.ts,classes 模块仅保留 classSchedule 读函数;✅ P2 已修复:`getAccessibleClassIdsForTeacher` 使用 `Promise.all` 并行化 ownedIds 与 assignedIds 查询)
|
||||
- Data-access:`getAdminClasses` / `getTeacherClasses` / `getGradeManagedClasses` / `getStudentClasses` / `getClassDetails` / `getClassStudents` / `getClassSchedule` / `getClassHomeworkInsights` / `getGradeHomeworkInsights` / `getStudentsSubjectScores` / `verifyTeacherOwnsClass` / `getTeacherIdsByClassIds`(获取多个班级的所有教师 ID:班主任 + 任课教师,跨模块接口,供 messaging 模块调用)(✅ P0-5 已修复:classSchedule 写函数 createClassScheduleItem/updateClassScheduleItem/deleteClassScheduleItem 已迁移至 scheduling/data-access-class-schedule.ts,classes 模块仅保留 classSchedule 读函数;✅ P2 已修复:`getAccessibleClassIdsForTeacher` 使用 `Promise.all` 并行化 ownedIds 与 assignedIds 查询)
|
||||
- Schema:`CreateTeacherClassSchema` / `UpdateTeacherClassSchema` / `DeleteTeacherClassSchema` / `CreateAdminClassSchema` / `UpdateAdminClassSchema` / `DeleteAdminClassSchema` / `CreateGradeClassSchema` / `UpdateGradeClassSchema` / `DeleteGradeClassSchema` / `CreateClassScheduleItemSchema` / `UpdateClassScheduleItemSchema` / `DeleteClassScheduleItemSchema` / `EnrollStudentByEmailSchema`
|
||||
|
||||
**依赖关系**:
|
||||
- 依赖:`shared/*`、`@/auth`、`school`(✅ P1-1 已修复:通过 school data-access.isGradeHead/isGradeManager/findGradeIdByHeadAndName)、`homework`(✅ P0-7 已修复:通过 `homework/data-access-classes` 暴露的函数获取作业数据,不再直查 homework/exams 表)
|
||||
- 被依赖:`exams`/`homework`/`grades`/`attendance`/`scheduling`/`dashboard`(通过 data-access,P0-4 已修复)/`parent`/`course-plans`/`users`(✅ P1-1 已修复:8+ 处直查 classes 表改为通过 classes data-access)
|
||||
- 被依赖:`exams`/`homework`/`grades`/`attendance`/`scheduling`/`dashboard`(通过 data-access,P0-4 已修复)/`parent`/`course-plans`/`users`(✅ P1-1 已修复:8+ 处直查 classes 表改为通过 classes data-access)/`messaging`(通过 data-access.getTeacherIdsByClassIds/getStudentActiveClassId,支持学生/家长给班级教师发消息)
|
||||
|
||||
**已知问题**:
|
||||
- ✅ P0-1 已修复:`data-access.ts` 已拆分为 5 个文件(data-access/data-access-stats/data-access-schedule/data-access-students/data-access-admin),所有文件均 ≤800 行
|
||||
@@ -787,11 +823,11 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
|
||||
**导出函数**:
|
||||
- Actions:`sendMessageAction` / `getMessagesAction` / `getMessageAction` / `deleteMessageAction` / `getNotificationsAction` / `markNotificationReadAction` / `markAllNotificationsReadAction` / `getNotificationPreferencesAction` / `updateNotificationPreferencesAction`
|
||||
- Data-access:`getMessages` / `getMessageById` / `getMessageThread` / `createMessage` / `markMessageAsRead` / `deleteMessage` / `getUnreadMessageCount` / `getRecipients`(通知 CRUD 通过 re-export 从 notifications 模块重导出,保持向后兼容)
|
||||
- Notification-preferences:re-export shim(实际逻辑在 `notifications/preferences.ts`)
|
||||
- Data-access:`getMessages` / `getMessageById` / `getMessageThread` / `createMessage` / `markMessageAsRead` / `deleteMessage` / `getUnreadMessageCount` / `getRecipients`(按 DataScope 过滤可发送对象:class_taught 教师→学生、grade_managed 年级管理员→教师/学生、all 管理员、class_members 学生→自己班级的任课教师/班主任、children 家长→孩子的班主任/任课教师;通过 classes data-access.getTeacherIdsByClassIds/getStudentActiveClassId 获取班级教师 ID)(通知 CRUD 通过 re-export 从 notifications 模块重导出,保持向后兼容)
|
||||
- Notification-preferences:~~re-export shim(实际逻辑在 `notifications/preferences.ts`)~~ ✅ P0-b 已修复:`notification-preferences.ts` 文件已删除(通知模块去重),消费方改为直接从 `@/modules/notifications/preferences` 导入 `getNotificationPreferences` / `upsertNotificationPreferences`
|
||||
|
||||
**依赖关系**:
|
||||
- 依赖:`shared/*`、`@/auth`、`notifications`(✅ P0-4 / P1-5 已修复:通过 `sendNotification` dispatcher 发送通知,通知 CRUD 和偏好已迁移至 notifications 模块)
|
||||
- 依赖:`shared/*`、`@/auth`、`notifications`(✅ P0-4 / P1-5 已修复:通过 `sendNotification` dispatcher 发送通知,通知 CRUD 和偏好已迁移至 notifications 模块)、`classes`(通过 data-access.getTeacherIdsByClassIds/getStudentActiveClassId 获取班级教师 ID,支持学生 class_members 和家长 children 数据范围)、`users`(通过 data-access.getUserNamesByIds 获取用户显示名称)
|
||||
- 被依赖:`notifications`(✅ 已消除反向依赖)、`settings`(通知偏好表单)、`layout`(通知下拉)
|
||||
|
||||
**已知问题**:
|
||||
@@ -802,13 +838,14 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
- ✅ P1 已修复:~~`markMessageAsReadAction` / `deleteMessageAction` / `getMessageDetailAction` 缺少 Zod 校验~~ 已添加 `MessageIdSchema` 校验 messageId 参数
|
||||
- ✅ P1 已修复:~~`updateNotificationPreferencesAction` 缺少 Zod 校验~~ 已添加 `UpdateNotificationPreferencesSchema` 校验 8 个布尔字段
|
||||
- ✅ P2 已修复:`data-access.ts` 中 3 处 `or(...)!` 非空断言清理为安全守卫(条件 push)
|
||||
- ✅ P0-b 已修复:~~`notification-preferences.ts` re-export shim 文件~~ 已删除(通知模块去重),8 个消费方改为直接从 `@/modules/notifications/preferences` 导入 `getNotificationPreferences` / `upsertNotificationPreferences`,消除 messaging 模块对通知偏好的冗余 re-export 层
|
||||
|
||||
**文件清单**:
|
||||
| 文件 | 行数 | 职责 |
|
||||
|------|------|------|
|
||||
| `actions.ts` | 276 | 9 个 Server Action(通知相关 Action 委托 notifications 模块) |
|
||||
| `data-access.ts` | 199 | 私信 CRUD + re-export 通知 CRUD(向后兼容) |
|
||||
| `notification-preferences.ts` | 11 | re-export shim(实际逻辑在 notifications/preferences.ts) |
|
||||
| ~~`notification-preferences.ts`~~ | ~~11~~ | ~~re-export shim~~ ✅ P0-b 已删除(消费方改为直接从 notifications/preferences 导入) |
|
||||
| `schema.ts` | 41 | 私信发送校验 + messageId 校验 + 通知偏好更新校验 |
|
||||
| `types.ts` | 72 | 私信类型 + re-export 通知类型(向后兼容) |
|
||||
|
||||
@@ -918,6 +955,7 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
**已知问题**:
|
||||
- ✅ P2-13 已修复:~~所有函数 try-catch 吞错误返回空数组/null~~ 所有 catch 块已添加 `console.error` 输出错误上下文
|
||||
- ✅ P2 已修复:`getFileAttachmentsWithFilters` 中 `or(...)!` 非空断言清理为安全守卫
|
||||
- ✅ P2 已修复:~~`getFileAttachmentsWithFilters` 中 `conditions` 隐式 `any[]`~~ 改为显式 `SQL[]` 类型标注
|
||||
- ⚠️ P2:无 `actions.ts`,data-access 被路由直接调用
|
||||
- ✅ 职责单一,不跨模块查询
|
||||
|
||||
@@ -962,22 +1000,28 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
**职责**:家长视角的子女数据聚合与展示。
|
||||
|
||||
**导出函数**:
|
||||
- Data-access:`getChildren` / `getChildBasicInfo` / `getChildDashboardData`(✅ P2 已修复:`getChildBasicInfo` 使用 `Promise.all` 并行化 gradeOptions 与 classId 查询,并添加 `ChildBasicInfo` 显式返回类型;`getChildBasicInfo` 使用 `React.cache()` 包装实现请求级 memoization)
|
||||
- Data-access:`getChildren` / `getChildBasicInfo` / `getChildDashboardData` / `getParentDashboardData` / `verifyParentChildRelation`(✅ P2 已修复:`getChildBasicInfo` 使用 `Promise.all` 并行化 gradeName 与 activeClass 查询;新增 `verifyParentChildRelation` 同时按 parentId + studentId 过滤,防止跨家庭信息泄露;新增 `getStudentActiveClass` 一次 JOIN 返回 classId + className;新增 `getGradeNameById` 替代全量 `getGradeOptions`)
|
||||
|
||||
**依赖关系**:
|
||||
- 依赖:`shared/*`、`@/auth`、`classes`(合理)、`homework`(合理)、`grades`(合理)
|
||||
- 依赖:`shared/*`、`@/auth`、`classes`(合理)、`homework`(合理)、`grades`(合理)、`users`(合理)、`school`(合理)
|
||||
- 被依赖:无
|
||||
|
||||
**已知问题**:
|
||||
- ✅ P2 已修复:~~`getChildBasicInfo` 多次串行查询,可优化为 join~~ 改为使用 `Promise.all` 并行化 gradeOptions 与 classId 查询
|
||||
- ✅ P1 已修复:~~`app/(dashboard)/parent/children/[studentId]/page.tsx` 直接访问 DB(违反三层架构)~~ 改为调用 `verifyParentChildRelation` data-access 函数
|
||||
- ✅ P1 已修复:~~权限校验未加 parentId 条件,存在信息泄露风险~~ `verifyParentChildRelation` 同时按 parentId + studentId 过滤
|
||||
- ✅ P2 已修复:~~`getChildBasicInfo` 多次串行查询~~ 改为 `Promise.all` 并行化,并使用 `getStudentActiveClass` 一次 JOIN
|
||||
- ✅ P2 已修复:~~`getGradeOptions` 全量查询效率低~~ 改为 `getGradeNameById` 按 ID 查询
|
||||
- ✅ P2 已修复:~~`buildHomeworkSummary` 中 `[...assignments].sort()` 不必要拷贝~~ 改为 `toSorted()`
|
||||
- ✅ P2 已修复:~~`in7Days` 死代码~~ 已删除
|
||||
- ✅ 职责单一,正确复用其他模块 data-access
|
||||
|
||||
**文件清单**:
|
||||
| 文件 | 行数 | 职责 |
|
||||
|------|------|------|
|
||||
| `data-access.ts` | 234 | 子女关系 + 仪表盘数据聚合 |
|
||||
| `types.ts` | 57 | 类型定义 |
|
||||
| `components/*` | 7 文件 | 子女卡片/详情/仪表盘 |
|
||||
| `data-access.ts` | 227 | 子女关系 + 仪表盘数据聚合 + 关系校验 |
|
||||
| `types.ts` | 67 | 类型定义(含 JSDoc) |
|
||||
| `lib/utils.ts` | 7 | 模块共享工具函数(getInitials) |
|
||||
| `components/*` | 8 文件 | 子女卡片/详情/仪表盘/共享数据页 |
|
||||
|
||||
---
|
||||
|
||||
@@ -987,24 +1031,29 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
|
||||
**导出函数**:
|
||||
- Actions:`getElectiveCoursesAction` / `createElectiveCourseAction` / `updateElectiveCourseAction` / `deleteElectiveCourseAction` / `getStudentSelectionsAction` / `selectCourseAction` / `dropCourseAction` / `runLotteryAction` / `getAvailableCoursesForStudentAction`
|
||||
- Data-access:`getElectiveCourses` / `getElectiveCourseById` / `createElectiveCourse` / `updateElectiveCourse` / `deleteElectiveCourse` / `selectCourse` / `dropCourse` / `runLottery` / `getStudentSelections` / `getAvailableCoursesForStudent`
|
||||
- Data-access:`getElectiveCourses` / `getElectiveCourseById` / `createElectiveCourse` / `updateElectiveCourse` / `deleteElectiveCourse` / `openSelection` / `closeSelection` / `buildCourseSelect` / `mapCourseRow` / `resolveCourseDisplayNames` / `CourseCoreRow`(P3 新增导出,供 data-access-selections 复用)
|
||||
- Data-access-operations:`selectCourse` / `dropCourse` / `runLottery`
|
||||
- Data-access-selections:`getCourseSelections` / `getStudentSelections` / `getStudentGradeId` / `getAvailableCoursesForStudent`
|
||||
|
||||
**依赖关系**:
|
||||
- 依赖:`shared/*`、`@/auth`
|
||||
- 依赖:`shared/*`、`@/auth`、`school`(✅ P3 已修复:通过 school data-access.getSubjectOptions/getGradeOptions 获取科目/年级名称,不再直查 subjects/grades 表)、`users`(✅ P3 已修复:通过 users data-access.getUserNamesByIds 获取教师姓名,不再直查 users 表)、`classes`(通过 classes data-access.getStudentActiveGradeId 获取学生年级)
|
||||
- 被依赖:无
|
||||
|
||||
**已知问题**:
|
||||
- ⚠️ P1:`data-access.ts` 与 `data-access-selections.ts` 重复定义 `mapCourseRow`/`buildCourseSelect`(60 行重复)
|
||||
- ⚠️ P2:`runLottery` 使用 `Math.random()`,结果不可复现
|
||||
- ⚠️ P2:`selectCourse` FCFS 模式存在并发超卖风险
|
||||
- ✅ P1 已修复:~~`buildCourseSelect` 跨模块 join users/subjects/grades 表~~ 改为只查 electiveCourses 表,通过 `resolveCourseDisplayNames` 调用 school/users data-access 获取显示名称
|
||||
- ✅ P1 已修复:~~`getSubjectOptions` 本地直查 subjects 表且与 school 模块重复~~ 删除本地实现,改用 `school/data-access.getSubjectOptions`
|
||||
- ✅ P1 已修复:~~`selectCourse`/`dropCourse` 缺事务包裹~~ 改为 `db.transaction` 包裹,FCFS 模式下使用 `FOR UPDATE` 行锁防止并发超卖
|
||||
- ✅ P2 已修复:~~`mapCourseRow` 在 data-access.ts 与 data-access-selections.ts 重复定义~~ 抽取到 data-access.ts 统一导出,data-access-selections.ts 复用
|
||||
- ✅ P2 已修复:~~`runLottery` 使用 `sort(() => Math.random() - 0.5)` 有偏 shuffle~~ 改为 Fisher-Yates 无偏洗牌算法
|
||||
- ✅ P2 已修复:~~`selectCourse` FCFS 并发超卖风险~~ 使用 `db.transaction` + `.for("update")` 行锁
|
||||
- ✅ 权限校验完整(ELECTIVE_MANAGE/SELECT/READ)
|
||||
|
||||
**文件清单**:
|
||||
| 文件 | 行数 | 职责 |
|
||||
|------|------|------|
|
||||
| `actions.ts` | 304 | 11 个 Server Action |
|
||||
| `data-access.ts` | 242 | 课程 CRUD + scope 过滤 |
|
||||
| `data-access-operations.ts` | 217 | 选课操作(select/drop/lottery) |
|
||||
| `data-access.ts` | 250 | 课程 CRUD + scope 过滤 + 共享映射函数(P3 重构:移除跨模块 join,通过 school/users data-access 获取显示名称) |
|
||||
| `data-access-operations.ts` | 245 | 选课操作(select/drop/lottery,P3 重构:事务包裹 + FOR UPDATE 锁 + Fisher-Yates 洗牌) |
|
||||
| `data-access-selections.ts` | 189 | 选课记录查询 |
|
||||
| `schema.ts` | 132 | Zod 校验 |
|
||||
| `types.ts` | 108 | 类型定义 + 标签常量 |
|
||||
@@ -1028,6 +1077,7 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
- ✅ P0-6 已修复:~~事件上报存在 Server Action 与 REST API 双通道重复~~ 删除 `/api/proctoring/event` REST 路由(移至 deletes/),Server Action `recordProctoringEventAction` 为唯一规范路径
|
||||
- ✅ P1-1 已修复:~~跨模块直查 `exams`/`examSubmissions`/`users`~~ 改为通过 exams/users data-access 函数获取数据
|
||||
- ✅ P2 已修复:`actions.ts` 不再直接 import `db` 和 `examSubmissions`,submission 归属校验已下沉到 data-access;`recordProctoringEventAction` 改用 `requirePermission(EXAM_SUBMIT)` 并增加 `revalidatePath`
|
||||
- ✅ P2 已修复:~~`getStudentProctoringStatuses` 串行查询(getUserNamesByIds 后再查事件)~~ 改为 `Promise.all` 并行拉取学生姓名与事件记录
|
||||
|
||||
**文件清单**:
|
||||
| 文件 | 行数 | 职责 |
|
||||
@@ -1057,7 +1107,10 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
|
||||
**已知问题**:
|
||||
- ✅ P1-1 已修复:~~`updateMasteryFromSubmission` 跨模块直查 4 张表(与 exams/homework/questions 紧耦合)~~ 改为调用 `exams/data-access.getExamSubmissionWithAnswers` 和 `questions/data-access.getKnowledgePointsForQuestions`
|
||||
- ⚠️ P2:`data-access-reports.ts` 有未使用代码(`round2`)
|
||||
- ✅ P2 已修复:~~`data-access-reports.ts` 有未使用代码(`round2` + `void round2`)~~ 已删除死代码
|
||||
- ✅ P2 已修复:~~`updateMasteryFromSubmission` 循环内串行 await upsert~~ 改为 `Promise.all` 并行执行所有 upsert
|
||||
- ✅ P2 已修复:~~`getClassMasterySummary` 串行查询(className → studentIds → userMap → masteryRows)~~ 改为两组 `Promise.all` 并行(className+studentIds,userMap+masteryRows)
|
||||
- ✅ P2 已修复:~~`getDiagnosticReports` 中 `conditions` 隐式 `any[]`~~ 改为显式 `SQL[]` 类型标注
|
||||
- ⚠️ P2:班级报告将生成者 ID 存入 `studentId` 字段(schema 设计缺陷 workaround)
|
||||
- ✅ 与 grades 模块无职责重叠(grades 管分数,diagnostic 管知识点掌握度)
|
||||
|
||||
@@ -1081,6 +1134,7 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
- Actions:`getAiProvidersAction` / `createAiProviderAction` / `updateAiProviderAction` / `deleteAiProviderAction` / `testAiProviderAction`
|
||||
- Actions-password:`changePasswordAction`(✅ P1 已修复:使用 `requirePermission(USER_PROFILE_UPDATE)` + Zod 校验 + DB 操作下沉到 data-access)
|
||||
- Data-access:`getAiProviderSummaries` / `countDefaultAiProviders` / `getAiProviderForUpdate` / `updateAiProvider` / `createAiProvider` / `getUserPasswordHash` / `getPasswordSecurityByUserId` / `updateUserPassword` / `upsertPasswordSecurityOnPasswordChange`(P1 新增,从 actions 下沉)
|
||||
- Components:`SettingsView`(P2-a 新增:统一设置页布局,消除 admin/teacher/student 三个设置视图的重复布局;4 标签页 General/Notifications/Appearance/Security,角色差异通过 `description` / `backHref` / `generalExtra` 三个 props 注入;3 个消费方:admin/teacher/student 设置页)
|
||||
- Types:`AiProviderSummary` / `AiProviderName` / `AiProviderExisting`(P1 新增,从 actions.ts 迁出)
|
||||
|
||||
**依赖关系**:
|
||||
@@ -1092,6 +1146,7 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
- ✅ P1 已修复:~~无 `data-access.ts`,`actions.ts` 直接使用 `db`~~ 新建 `data-access.ts`,所有 DB 操作已下沉
|
||||
- ✅ P1 已修复:~~`changePasswordAction` 使用 `requireAuth()` 无 Zod 校验~~ 改为 `requirePermission(USER_PROFILE_UPDATE)` + `ChangePasswordSchema` Zod 校验 + 并行查询优化
|
||||
- ✅ P2 已修复:`actions-password.ts` 删除本地 `normalizeBcryptHash`,统一复用 `shared/lib/bcrypt-utils.normalizeBcryptHash`,消除重复代码
|
||||
- ✅ P2-a 已修复:~~admin/teacher/student 三个设置视图重复布局~~ 新增 `SettingsView` 统一设置页布局(4 标签页 + 角色差异通过 props 注入),3 个设置页改为消费 `SettingsView`
|
||||
- ⚠️ P2:`notification-preferences-form.tsx` 跨模块 UI 依赖
|
||||
- ✅ 密码修改有速率限制
|
||||
- ✅ AI Provider 操作有 `AI_CONFIGURE` 权限校验
|
||||
@@ -1103,6 +1158,7 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
| `actions-password.ts` | 107 | 修改密码(P1 已修复:requirePermission + Zod + data-access) |
|
||||
| `data-access.ts` | 175 | AI Provider CRUD + 密码修改 DB 操作(P1 新增) |
|
||||
| `types.ts` | 16 | 类型定义(P1 新增,AiProviderSummary 等) |
|
||||
| `components/settings-view.tsx` | 117 | SettingsView 统一设置页布局(P2-a 新增,4 标签页 + props 注入角色差异) |
|
||||
| `components/*` | 8 文件 | 通用设置 + AI 配置 + 密码 + 主题 + 通知偏好 |
|
||||
|
||||
---
|
||||
@@ -1167,22 +1223,47 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
**已知问题**:
|
||||
- ⚠️ P2:与 classes 模块的 `schedule-view.tsx`/`schedule-filters.tsx` 可能功能重叠
|
||||
- ✅ 纯 UI 模块,数据由页面通过 classes data-access 获取
|
||||
- ✅ 认证模式已统一:所有 student 页面使用 `getCurrentStudentUser()`(users 模块)或 `getAuthContext()`(shared 模块),不再直接调用 `auth()` 或 `getDemoStudentUser()`
|
||||
|
||||
**文件清单**:
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `components/student-courses-view.tsx` | 学生课程视图 |
|
||||
| `components/student-courses-view.tsx` | 学生课程视图(含 `ClassCard` memo 组件 + 加入班级表单,使用 `useTransition`) |
|
||||
| `components/student-schedule-filters.tsx` | 课表筛选器 |
|
||||
| `components/student-schedule-view.tsx` | 学生课表视图 |
|
||||
|
||||
**路由文件清单**(`app/(dashboard)/student/`):
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `dashboard/page.tsx` + `loading.tsx` | 学生仪表盘 + 骨架屏 |
|
||||
| `attendance/page.tsx` + `loading.tsx` | 学生考勤 + 骨架屏 |
|
||||
| `diagnostic/page.tsx` + `loading.tsx` | 学情诊断 + 骨架屏 |
|
||||
| `elective/page.tsx` + `loading.tsx` | 选课中心 + 骨架屏 |
|
||||
| `grades/page.tsx` + `loading.tsx` | 我的成绩 + 骨架屏 |
|
||||
| `learning/assignments/page.tsx` + `loading.tsx` | 作业列表(含 `AssignmentCard` 组件)+ 骨架屏 |
|
||||
| `learning/assignments/[assignmentId]/page.tsx` + `loading.tsx` | 作业作答/复习 + 骨架屏 |
|
||||
| `learning/courses/page.tsx` + `loading.tsx` | 课程列表 + 骨架屏 |
|
||||
| `learning/textbooks/page.tsx` + `loading.tsx` | 教材列表 + 骨架屏 |
|
||||
| `learning/textbooks/[id]/page.tsx` + `loading.tsx` | 教材阅读 + 骨架屏 |
|
||||
| `schedule/page.tsx` + `loading.tsx` | 课表 + 骨架屏 |
|
||||
| `error.tsx` | 路由组错误边界(提供"重试"按钮) |
|
||||
|
||||
---
|
||||
|
||||
## 2.27 lesson-preparation(备课模块)
|
||||
|
||||
**职责**:教师备课,基于教材章节创建课案(Block 编辑器),支持模板、版本管理、知识点标注、题目创建/拉取、作业发布。
|
||||
**职责**:教师备课,基于教材章节创建课案(**节点图编辑器 React Flow**),支持模板、版本管理、知识点标注、题目创建/拉取、作业发布。
|
||||
|
||||
> 架构变更(2026-06-21):编辑器从列表式(BlockRenderer + @dnd-kit)升级为节点图式(NodeEditor + @xyflow/react)。数据结构从 v1(blocks 数组)升级到 v2(nodes + edges 节点图),旧数据通过 `migrateV1ToV2()` 自动迁移。
|
||||
|
||||
**数据结构**:
|
||||
- v1(已废弃,仅向后兼容读取):`{ version: 1, blocks: Block[] }`
|
||||
- v2(当前):`{ version: 2, nodes: LessonPlanNode[]; edges: LessonPlanEdge[] }`
|
||||
- `LessonPlanNode`:`Block` + `position: { x, y }`(画布坐标)
|
||||
- `LessonPlanEdge`:`{ id, source, target, sourceHandle?, targetHandle? }`(节点间连线)
|
||||
|
||||
**导出函数**:
|
||||
- Data-access(`data-access.ts`):`getLessonPlans` / `getLessonPlanById` / `createLessonPlan` / `updateLessonPlanContent` / `softDeleteLessonPlan` / `duplicateLessonPlan` / `getTemplateById` / `buildInitialContent`
|
||||
- Data-access(`data-access.ts`):`getLessonPlans` / `getLessonPlanById` / `createLessonPlan` / `updateLessonPlanContent` / `softDeleteLessonPlan` / `duplicateLessonPlan` / `getTemplateById` / `buildInitialContent` / `migrateV1ToV2`(v1→v2 迁移:blocks 数组转换为 nodes + 线性 edges)/ `normalizeDocument`(规范化:确保 content 为 v2 格式,兼容旧数据)
|
||||
- Data-access-versions(`data-access-versions.ts`):`getLessonPlanVersions` / `createLessonPlanVersion` / `getVersionContent` / `revertToVersion` / `pruneAutoVersions`
|
||||
- Data-access-templates(`data-access-templates.ts`):`getLessonPlanTemplates` / `saveAsTemplate` / `deletePersonalTemplate`
|
||||
- Data-access-knowledge(`data-access-knowledge.ts`):`getLessonPlansByKnowledgePoint` / `getLessonPlansByQuestion`
|
||||
@@ -1191,21 +1272,23 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
- Actions:`getLessonPlansAction` / `getLessonPlanByIdAction` / `createLessonPlanAction` / `updateLessonPlanAction` / `saveLessonPlanVersionAction` / `getLessonPlanVersionsAction` / `revertLessonPlanVersionAction` / `deleteLessonPlanAction` / `duplicateLessonPlanAction` / `getLessonPlanTemplatesAction` / `saveAsTemplateAction` / `deleteTemplateAction` / `suggestKnowledgePointsAction` / `publishLessonPlanHomeworkAction` / `getKnowledgePointOptionsAction`
|
||||
|
||||
**依赖关系**:
|
||||
- 依赖:`shared/*`、`@/auth`、`shared/lib/ai`、`textbooks`(只读章节/知识点树)、`questions`(创建/查询题目)、`exams`(创建 exam 草稿)、`homework`(创建作业下发)、`classes`(查询教师班级)、`files`(附件)
|
||||
- 依赖:`shared/*`、`@/auth`、`shared/lib/ai`、`@xyflow/react`(节点图编辑器)、`textbooks`(只读章节/知识点树)、`questions`(创建/查询题目)、`exams`(创建 exam 草稿)、`homework`(创建作业下发)、`classes`(查询教师班级)、`files`(附件)
|
||||
- 被依赖:无
|
||||
|
||||
**已知问题**:
|
||||
- ✅ 通过对方 data-access 调用跨模块数据,无直查跨模块表
|
||||
- ✅ data-access 按职责拆分为 4 个文件(data-access/data-access-versions/data-access-templates/data-access-knowledge)
|
||||
- ✅ actions 按职责拆分为 4 个文件(actions/actions-publish/actions-ai/actions-kp)
|
||||
- ✅ 编辑器架构升级:NodeEditor(React Flow 画布)+ NodeEditPanel(侧边内容编辑面板)+ LessonNode(自定义节点组件),支持节点拖拽、连线、画布缩放
|
||||
- ⚠️ `block-renderer.tsx` 标记为 @deprecated(已被 NodeEditor 替代,保留用于向后兼容)
|
||||
|
||||
**文件清单**:
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `types.ts` | 类型定义 |
|
||||
| `types.ts` | 类型定义(含 v1/v2 文档类型、LessonPlanNode、LessonPlanEdge) |
|
||||
| `constants.ts` | 常量定义 |
|
||||
| `schema.ts` | Zod 验证 |
|
||||
| `data-access.ts` | 课案 CRUD + 模板查询 + 初始内容构建 |
|
||||
| `data-access.ts` | 课案 CRUD + 模板查询 + 初始内容构建 + v1→v2 迁移(migrateV1ToV2 / normalizeDocument) |
|
||||
| `data-access-versions.ts` | 版本管理(创建/查询/回滚/清理) |
|
||||
| `data-access-templates.ts` | 个人模板 CRUD |
|
||||
| `data-access-knowledge.ts` | 按知识点/题目反查课案 |
|
||||
@@ -1216,22 +1299,25 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
| `publish-service.ts` | 发布作业服务(编排 homework/exams/classes) |
|
||||
| `ai-suggest.ts` | AI 知识点建议服务 |
|
||||
| `seed-templates.ts` | 模板种子数据 |
|
||||
| `hooks/use-lesson-plan-editor.ts` | 课案编辑器 Hook |
|
||||
| `hooks/use-lesson-plan-editor.ts` | 课案编辑器 Hook(基于 zustand,支持 nodes/edges 操作:addNode/updateNode/updateNodePosition/removeNode/connect/disconnect/setEdges/selectNode) |
|
||||
| `components/lesson-plan-list.tsx` | 课案列表 |
|
||||
| `components/lesson-plan-card.tsx` | 课案卡片 |
|
||||
| `components/lesson-plan-filters.tsx` | 课案筛选器 |
|
||||
| `components/lesson-plan-editor.tsx` | 课案编辑器 |
|
||||
| `components/block-renderer.tsx` | Block 渲染器 |
|
||||
| `components/lesson-plan-editor.tsx` | 课案编辑器(编排 NodeEditor + NodeEditPanel) |
|
||||
| `components/node-editor.tsx` | **节点图画布**(React Flow,自定义 LessonNode,支持拖拽/连线/缩放) |
|
||||
| `components/node-edit-panel.tsx` | **侧边内容编辑面板**(选中节点后编辑标题/数据) |
|
||||
| `components/nodes/lesson-node.tsx` | **自定义节点组件**(按 BlockType 显示图标/颜色,含 Handle 连接点) |
|
||||
| `components/block-renderer.tsx` | ⚠️ @deprecated Block 渲染器(已被 NodeEditor 替代,保留向后兼容) |
|
||||
| `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` | 富文本 Block |
|
||||
| `components/blocks/text-study-block.tsx` | 课文研读 Block |
|
||||
| `components/blocks/exercise-block.tsx` | 练习 Block |
|
||||
| `components/blocks/reflection-block.tsx` | 反思 Block |
|
||||
| `components/blocks/rich-text-block.tsx` | 富文本 Block(被 NodeEditPanel 复用) |
|
||||
| `components/blocks/text-study-block.tsx` | 课文研读 Block(被 NodeEditPanel 复用) |
|
||||
| `components/blocks/exercise-block.tsx` | 练习 Block(被 NodeEditPanel 复用) |
|
||||
| `components/blocks/reflection-block.tsx` | 反思 Block(被 NodeEditPanel 复用) |
|
||||
|
||||
---
|
||||
|
||||
@@ -1417,9 +1503,9 @@ shared/lib/{audit-logger, change-logger, auth-guard} → @/auth → shared/lib/*
|
||||
| P2-10 | school 模块审计日志不一致(仅 school 实体记录) | school |
|
||||
| ~~P2-11~~ | ~~`announcements` 死代码 `void wasPublished`~~ ✅ 已修复(代码中已不存在) | announcements |
|
||||
| ~~P2-12~~ | ~~`announcements` 权限模式不一致(requireAuth vs requirePermission)~~ ✅ 已修复 | announcements |
|
||||
| P2-13 | `files` try-catch 吞错误 | files |
|
||||
| P2-14 | `elective` runLottery 使用 Math.random | elective |
|
||||
| P2-15 | `elective` selectCourse FCFS 并发超卖风险 | elective |
|
||||
| P2-13 | ~~`files` try-catch 吞错误~~ ✅ 已修复(所有 catch 块已添加 console.error;conditions 隐式 any[] 改为 SQL[]) | files |
|
||||
| ~~P2-14~~ | ~~`elective` runLottery 使用 Math.random~~ ✅ 已修复(改为 Fisher-Yates 无偏洗牌) | elective |
|
||||
| ~~P2-15~~ | ~~`elective` selectCourse FCFS 并发超卖风险~~ ✅ 已修复(db.transaction + FOR UPDATE 行锁) | elective |
|
||||
| P2-16 | `diagnostic` 班级报告 studentId 字段复用 | diagnostic |
|
||||
| ~~P2-17~~ | ~~`layout` 用权限反推角色~~ ✅ 已修复(`app-sidebar.tsx` 改用 `hasRole()` 判断角色) | layout |
|
||||
| ~~P2-18~~ | ~~`scheduling/actions.ts` 末尾 re-export data-access~~ ✅ 已修复(移除 re-export,4 个页面改为从 `data-access` 导入) | scheduling |
|
||||
@@ -1491,7 +1577,7 @@ shared/lib/{audit-logger, change-logger, auth-guard} → @/auth → shared/lib/*
|
||||
| **grades** | ✅ | ✅ | ✅外键 | ✅外键 | - | - | ✅data-access | ✅data-access | - | ✅data-access | - | - | - | - | - |
|
||||
| **dashboard** | ✅ | ✅ | ✅data-access | ✅data-access | ✅data-access | ✅data-access | ✅data-access | - | - | ✅data-access | - | - | - | - | - |
|
||||
| **users** | ✅ | ✅ | - | - | - | - | ✅data-access | - | - | - | - | - | - | - | - |
|
||||
| **messaging** | ✅ | ✅ | - | - | - | - | - | - | - | - | - | - | ✅dispatcher | - | - |
|
||||
| **messaging** | ✅ | ✅ | - | - | - | - | ✅data-access | - | - | - | - | - | ✅dispatcher | - | - |
|
||||
| **notifications** | ✅ | ✅ | - | - | - | - | ✅data-access | - | - | - | - | - | - | - | - |
|
||||
| **attendance** | ✅ | ✅ | - | - | - | - | ✅data-access | - | - | - | - | - | - | - | - |
|
||||
| **scheduling** | ✅ | ✅ | - | - | - | - | ✅data-access | - | - | ✅data-access | - | - | - | - | - |
|
||||
@@ -1538,7 +1624,7 @@ shared/lib/{audit-logger, change-logger, auth-guard} → @/auth → shared/lib/*
|
||||
6. 在 `auth-guard.ts` 中通过 `classSubjectTeachers` 查询教师关联的 classIds,构建 `DataScope.class_taught`
|
||||
|
||||
### `permission`
|
||||
1. 由 `shared/types/permissions.ts` 的 `Permissions` 常量定义(54 个权限点)
|
||||
1. 由 `shared/types/permissions.ts` 的 `Permissions` 常量定义(61 个权限点)
|
||||
2. 在 `shared/lib/permissions.ts` 中通过 `ROLE_PERMISSIONS` 映射角色到权限列表
|
||||
3. 在 `auth.ts` JWT callback 中通过 `resolvePermissions(roleNames)` 合并多角色权限,存入 JWT
|
||||
4. 在 `proxy.ts` middleware 中通过 `token.permissions` 检查路由访问权限
|
||||
@@ -1620,6 +1706,11 @@ formatFileSize(bytes: number): string
|
||||
// shared/lib/utils.ts
|
||||
cn(...inputs: ClassValue[]): string
|
||||
formatDate(date: string | Date, locale?: string): string
|
||||
getSearchParam(params: SearchParams, key: string): string | undefined
|
||||
formatNumber(v: number | null | undefined, digits?: number): string
|
||||
|
||||
// shared/lib/search-params.ts (re-export from utils.ts)
|
||||
getParam(params: SearchParams, key: string): string | undefined // = getSearchParam
|
||||
```
|
||||
|
||||
### 业务模块核心 Actions
|
||||
|
||||
Reference in New Issue
Block a user