feat(school,classes): 实现 P2 长期问题全量改进项
P2-2: 新增 OrgTreeNav 组件(学校→年级→班级三级树形导航,支持搜索过滤/选中高亮/展开折叠) P2-3: 新增 promoteGradesAction 年级升级功能(中文数字/阿拉伯数字识别,按 order 降序避免冲突) P2-4: 新增 bulkEnrollStudentsAction(CSV 批量导入学生)+ bulkAssignSubjectTeachersAction(CSV 批量分配教师) P2-5: 为 department/academicYear/grade 的 9 个 CRUD Action 补充 logAudit 审计日志 同步更新架构图文档 004/005
This commit is contained in:
@@ -654,7 +654,7 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
|
||||
**导出函数**:
|
||||
- Actions(14 个,均为写操作;读操作由 RSC 页面直接调用 data-access):`createTextbookAction` / `updateTextbookAction` / `deleteTextbookAction` / `createChapterAction` / `updateChapterContentAction` / `deleteChapterAction` / `reorderChaptersAction` / `createKnowledgePointAction` / `updateKnowledgePointAction` / `deleteKnowledgePointAction` / `getKnowledgePointsByChapterAction` / `getKnowledgeGraphDataAction`(✅ Task 7 新增:知识图谱数据查询,支持 structure/student-mastery/class-mastery 三种视图)/ `createPrerequisiteAction`(✅ Task 7 新增:声明前置依赖,含循环检测)/ `deletePrerequisiteAction`(✅ Task 7 新增:删除前置依赖)
|
||||
- Data-access:`getTextbooks` / `getTextbookById` / `getChaptersByTextbookId` / `getKnowledgePointsByChapterId` / `getKnowledgePointsByTextbookId` / `createTextbook` / `updateTextbook` / `deleteTextbook` / `createChapter` / `updateChapterContent` / `deleteChapter` / `createKnowledgePoint` / `updateKnowledgePoint` / `deleteKnowledgePoint` / `reorderChapters` / `getTextbooksDashboardStats` / `getKnowledgePointOptions`(跨模块接口,供 questions 调用)/ `getTextbooksWithScope`(P1-1 新增:按数据范围获取教材列表,学生端强制按年级过滤)/ `verifyChapterBelongsToTextbook`(P0-4 新增:资源归属校验)/ `verifyKnowledgePointBelongsToTextbook`(P0-4 新增:资源归属校验)/ `createPrerequisite`(✅ Task 7 新增:创建前置依赖)/ `deletePrerequisite`(✅ Task 7 新增:删除前置依赖)/ `getPrerequisiteEdgesForTextbook`(✅ Task 7 新增:获取教材下所有前置依赖边,用于循环检测)/ `getSubjectLabelKey` / `getGradeLabelKey`(i18n 标签键)
|
||||
- Data-access:`getTextbooks` / `getTextbookById` / `getChaptersByTextbookId` / `getKnowledgePointsByChapterId` / `getKnowledgePointsByTextbookId` / `createTextbook` / `updateTextbook` / `deleteTextbook` / `createChapter` / `updateChapterContent` / `deleteChapter` / `createKnowledgePoint` / `updateKnowledgePoint` / `deleteKnowledgePoint` / `reorderChapters` / `getTextbooksDashboardStats` / `getKnowledgePointOptions`(跨模块接口,供 questions 调用)/ `getTextbooksWithScope`(P1-1 新增:按数据范围获取教材列表,学生端强制按年级过滤)/ `verifyChapterBelongsToTextbook`(P0-4 新增:资源归属校验)/ `verifyKnowledgePointBelongsToTextbook`(P0-4 新增:资源归属校验)/ `verifyKnowledgePointBelongsToChapter`(P0-4 新增:校验知识点是否属于指定章节)/ `createPrerequisite`(✅ Task 7 新增:创建前置依赖)/ `deletePrerequisite`(✅ Task 7 新增:删除前置依赖)/ `getPrerequisiteEdgesForTextbook`(✅ Task 7 新增:获取教材下所有前置依赖边,用于循环检测)/ `getSubjectLabelKey` / `getGradeLabelKey`(i18n 标签键)
|
||||
- Data-access-graph(✅ Task 5 新增,图谱只读查询):`getKnowledgePointsWithRelations`(知识点+依赖+题目数聚合查询)/ `getStudentKpMastery`(学生掌握度)/ `getClassKpMastery`(班级平均掌握度)/ `getPrerequisitesForKp` / `getSuccessorsForKp`
|
||||
- Constants(✅ 新增):`SUBJECTS` / `GRADES` / `SUBJECT_COLORS` / `getSubjectColor` / `getSubjectLabelKey` / `getGradeLabelKey`
|
||||
- ✅ v1 测试修复:`SUBJECTS` 新增 `Chinese` 学科、`GRADES` 新增 `Grade 1`/`Grade 2`,与 seed 数据对齐
|
||||
@@ -693,25 +693,28 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
**文件清单**:
|
||||
| 文件 | 行数 | 职责 |
|
||||
|------|------|------|
|
||||
| `actions.ts` | 502 | 14 个 Server Action(写操作,含 Zod 校验 + 资源归属校验 + 知识图谱查询/前置依赖管理) |
|
||||
| `data-access.ts` | 586 | 教材/章节/知识点 CRUD + 跨模块查询接口 + 资源归属校验 + 数据范围过滤 + 前置依赖 CRUD |
|
||||
| `data-access-graph.ts` | 184 | 知识图谱只读查询(✅ Task 5 新增:知识点关联聚合、学生/班级掌握度、前置后置知识点,标记 `server-only`) |
|
||||
| `types.ts` | 94 | 类型定义(含知识图谱类型:GraphViewMode/MasteryInfo/KpWithRelations/GraphNodeData/GraphEdgeData/KnowledgeGraphData/MasteryLevel) |
|
||||
| `schema.ts` | 62 | Zod 校验(含 CreatePrerequisiteSchema/DeletePrerequisiteSchema) |
|
||||
| `constants.ts` | 99 | 学科/年级常量与颜色映射(✅ 新增;v1 测试修复:新增 Chinese/Grade 1/Grade 2) |
|
||||
| `utils.ts` | 203 | 章节树构建/排序/查找等纯函数 + 循环检测(✅ 新增,含单测) |
|
||||
| `graph-layout.ts` | 105 | 知识图谱布局计算纯函数(✅ Task 8 重写为 dagre 集成,输出 React Flow 格式,含单测) |
|
||||
| `actions.ts` | 515 | 14 个 Server Action(写操作,含 Zod 校验 + 资源归属校验 + 知识图谱查询/前置依赖管理 + class-mastery 视图) |
|
||||
| `data-access.ts` | 662 | 教材/章节/知识点 CRUD + 跨模块查询接口 + 资源归属校验 + 数据范围过滤 + 前置依赖 CRUD |
|
||||
| `data-access-graph.ts` | 207 | 知识图谱只读查询(✅ Task 5 新增:知识点关联聚合、学生/班级掌握度、前置后置知识点,标记 `server-only`;v2 修复:前置依赖双向 IN 过滤防跨教材污染) |
|
||||
| `types.ts` | 106 | 类型定义(含知识图谱类型:GraphViewMode/MasteryInfo/KpWithRelations/GraphNodeData/GraphEdgeData/KnowledgeGraphData/MasteryLevel) |
|
||||
| `schema.ts` | 81 | Zod 校验(含 CreatePrerequisiteSchema/DeletePrerequisiteSchema;v2 修复:refine 消息改为英文) |
|
||||
| `constants.ts` | 96 | 学科/年级常量与颜色映射(✅ 新增;v1 测试修复:新增 Chinese/Grade 1/Grade 2) |
|
||||
| `utils.ts` | 225 | 章节树构建/排序/查找等纯函数 + 循环检测(✅ 新增,含单测) |
|
||||
| `graph-layout.ts` | 121 | 知识图谱布局计算纯函数(✅ Task 8 重写为 dagre 集成,输出 React Flow 格式,含单测) |
|
||||
| `analytics.tsx` | 43 | 教材分析 Context/Provider/Hook(✅ 新增) |
|
||||
| `hooks/use-knowledge-point-actions.ts` | 121 | 知识点操作 Hook |
|
||||
| `hooks/use-knowledge-point-actions.ts` | 49 | 知识点操作门面 Hook(v2 拆分:组合 useKpDialogState + useKpCrud,对外 API 不变) |
|
||||
| `hooks/use-kp-dialog-state.ts` | 38 | 知识点对话框状态管理 Hook(v2 新增:从 use-knowledge-point-actions 拆分) |
|
||||
| `hooks/use-kp-crud.ts` | 122 | 知识点 CRUD 操作 Hook(v2 新增:从 use-knowledge-point-actions 拆分) |
|
||||
| `hooks/use-text-selection.ts` | 57 | 文本选区捕获 Hook |
|
||||
| `hooks/use-graph-data.ts` | 58 | 知识图谱数据加载 Hook(✅ Task 11 新增:派生值模式避免 effect 中 setState) |
|
||||
| `components/teacher-textbook-reader.tsx` | 41 | 教师端 TextbookReader 客户端包装(✅ v1 测试修复:解决 Server→Client 函数 prop 序列化问题) |
|
||||
| `components/knowledge-graph.tsx` | 249 | React Flow 知识图谱主组件(✅ Task 10/15 重写:全书视图 + 搜索高亮 + 关联节点高亮 + 章节着色) |
|
||||
| `components/graph-kp-node.tsx` | 80 | React Flow 自定义节点(✅ Task 9 新增:知识点名称+题目数徽章+掌握度进度条) |
|
||||
| `components/graph-prerequisite-edge.tsx` | 40 | React Flow 自定义边(✅ Task 9 新增:虚线+箭头表示前置依赖) |
|
||||
| `components/graph-toolbar.tsx` | 77 | 图谱工具栏(✅ Task 9 新增:视图模式切换 + 搜索 + 重置视图) |
|
||||
| `components/graph-node-detail-panel.tsx` | 171 | 节点详情侧边面板(✅ Task 13 新增:描述/掌握度/关联题目/前置后置列表) |
|
||||
| `components/*` | 17 文件 | 教材编辑/知识图谱组件(新增 `section-error-boundary.tsx`、`teacher-textbook-reader.tsx`、5 个 graph-* 组件) |
|
||||
| `hooks/use-graph-data.ts` | 76 | 知识图谱数据加载 Hook(✅ Task 11 新增:派生值模式避免 effect 中 setState;v2 修复:区分 isLoading/isRefreshing 避免 UI 闪烁) |
|
||||
| `components/knowledge-graph.tsx` | 376 | React Flow 知识图谱主组件(✅ Task 10/15 重写:全书视图 + 搜索高亮 + 关联节点高亮 + 章节着色;v2 新增:添加/删除前置依赖 Dialog) |
|
||||
| `components/graph-kp-node.tsx` | 92 | React Flow 自定义节点(✅ Task 9 新增:知识点名称+题目数徽章+掌握度进度条;v2 修复:节点宽度常量化 NODE_WIDTH) |
|
||||
| `components/graph-prerequisite-edge.tsx` | 48 | React Flow 自定义边(✅ Task 9 新增:虚线+箭头表示前置依赖;v2 修复:GraphEdgeData 类型安全转换) |
|
||||
| `components/graph-toolbar.tsx` | 93 | 图谱工具栏(✅ Task 9 新增:视图模式切换 + 搜索 + 重置视图;v2 新增:isRefreshing 轻量指示器) |
|
||||
| `components/graph-node-detail-panel.tsx` | 181 | 节点详情侧边面板(✅ Task 13 新增:描述/掌握度/关联题目/前置后置列表;v2 修复:移除未使用 textbookId prop) |
|
||||
| `components/chapter-sidebar-list.tsx` | 342 | 章节侧边栏列表(v2 修复:canEdit 默认 false + orderUpdateFailed i18n key) |
|
||||
| `components/section-error-boundary.tsx` | 71 | 章节内容错误边界(v2 修复:默认值改为空字符串 + 条件渲染) |
|
||||
| `components/*` | 16 文件 | 教材编辑/知识图谱组件(新增 `section-error-boundary.tsx`、5 个 graph-* 组件;`teacher-textbook-reader.tsx` 已移至 app 层) |
|
||||
|
||||
---
|
||||
|
||||
@@ -723,7 +726,7 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
- Actions:`getGradeRecordsAction` / `createGradeRecordAction` / `updateGradeRecordAction` / `deleteGradeRecordAction` / `exportGradesAction` / `getGradeTrendAction` / `getClassComparisonAction` / `getSubjectComparisonAction` / `getGradeDistributionAction` / `getClassRankingAction` / `getRankingTrendAction` / `getGradeRecordByIdAction` / `getClassGradeStatsAction` / `getStudentGradeSummaryAction` / `batchCreateGradeRecordsAction` / `assertClassInScope`(✅ P3 新增导出:班级 scope 校验工具,供 actions-analytics 复用)/ `saveGradeDraftAction` / `getGradeDraftAction` / `deleteGradeDraftAction`(✅ v3-P2 新增:成绩录入草稿 Server Actions,分别使用 GRADE_RECORD_MANAGE/GRADE_RECORD_READ/GRADE_RECORD_MANAGE 权限)
|
||||
- Data-access:`getGradeRecords` / `getStudentGradeSummary` / `getClassRanking` / `getClassStudentsForEntry` / `getClassGradeStats` / `getClassGradeStatsWithMeta` / `getGradeTrend` / `getClassComparison` / `getSubjectComparison` / `getGradeDistribution` / `getRankingTrend` / `PaginatedGradeRecords`(✅ P3 新增:分页结果接口 `{ records, total }`)/ `saveGradeDraft` / `getGradeDraft` / `deleteGradeDraft`(✅ v3-P2 新增:成绩录入草稿 CRUD,upsert + 24 小时过期)/ `getExamOptionsForGrades` / `getSchoolWideGradeSummary`(✅ v3-P2 新增:考试选项查询 + 全校各年级成绩汇总,管理员视图按年级聚合平均分/及格率/优秀率/学生数/班级数,加权平均计算全校汇总)
|
||||
- Types(✅ v3-P2 新增):`SchoolWideGradeSummaryItem`(全校汇总按年级聚合项:gradeId/gradeName/schoolName/classCount/studentCount/averageScore/passRate/excellentRate/recordCount)/ `SchoolWideGradeSummary`(全校汇总:grades 数组 + totals 汇总对象)/ `GradeDraftData`(草稿数据接口:{ scores: Record<string, string>, timestamp: number },位于 data-access.ts)
|
||||
- Lib(✅ P1-2 新增,✅ P3 更新签名):`toNumber` / `normalize` / `buildScopeClassFilter(scope, currentUserId?)`(P3 修复:`class_members` scope 内置 studentId 过滤,需传入 currentUserId 参数)
|
||||
- Lib(✅ P1-2 新增,✅ P3 更新签名,✅ P3-26 拆分):`toNumber` / `normalize`(位于 `lib/grade-utils.ts`);`buildScopeClassFilter(scope, currentUserId?)`(P3-26 从 grade-utils.ts 迁移至 `lib/scope-filter.ts`,P3 修复:`class_members` scope 内置 studentId 过滤,需传入 currentUserId 参数)
|
||||
- Stats-service(✅ P1-1 新增):`computeGradeStats` / `computeAverageScore` / `buildGradeTrendPoints` / `computeTrendAverage` / `computeClassComparisonStats` / `computeSubjectComparisonStats` / `computeGradeDistribution` / `buildRankingTrendPoints`(从 3 个 data-access 文件抽取的纯函数,使数据层专注 DB I/O,统计逻辑可独立测试)
|
||||
- Components(✅ P1-5 新增):`WidgetBoundary`(Error Boundary + Suspense + Skeleton 组合,含 a11y 属性)/ `SchoolWideSummaryCard`(✅ v3-P2 新增:管理员全校成绩汇总卡片,4 个统计卡片 + 各年级对比表格)
|
||||
|
||||
@@ -763,6 +766,11 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
- ✅ v3-P2 改进(2026-06-23):`getGradeTrend`/`getGradeDistribution`/`getSubjectComparison`/`getClassComparison` 均新增 `semester` 和 `examId` 可选参数,支持按学期和考试筛选分析
|
||||
- ✅ v3-P2 改进(2026-06-23):新增 `getSchoolWideGradeSummary` data-access 函数 + `SchoolWideSummaryCard` 组件 + `SchoolWideGradeSummary`/`SchoolWideGradeSummaryItem` 类型,管理员全校成绩汇总视图(按年级聚合平均分/及格率/优秀率/学生数/班级数,加权平均计算全校汇总);admin/school/grades/insights/page.tsx 顶部新增 SchoolWideSummaryCard
|
||||
- ✅ v3-P2 改进(2026-06-23):parent/grades/page.tsx 为每个子女并行查询 `getClassAverageTrend`,渲染 GradeTrendCard
|
||||
- ✅ P3 修复(2026-06-23):~~`lib/grade-utils.ts` 72 行超 40 行工具函数上限~~ P3-26 将 `buildScopeClassFilter` 迁移至 `lib/scope-filter.ts`,grade-utils.ts 仅保留 toNumber/normalize(20 行)
|
||||
- ✅ P3 修复(2026-06-23):~~`stats-service.ts` createDefaultBuckets 不必要导出~~ P3-10 移除 export 关键字,改为内部函数
|
||||
- ✅ P3 修复(2026-06-23):~~`stats-service.ts` buildGradeTrendPoints 使用 as 断言~~ P3-24 新增 isGradeTrendType 类型守卫函数替代 as 断言
|
||||
- ✅ P3 修复(2026-06-23):~~`export.ts` 局部 avg 函数与 stats-service.computeAverageScore 重复~~ P3-6 删除局部 avg,复用 computeAverageScore
|
||||
- ✅ P3 修复(2026-06-23):~~`export.ts` TYPE_LABELS 硬编码中文~~ P3-7 改用 next-intl getTranslations,新增 export.sheets/columns/metrics i18n 键(zh-CN/en 同步)
|
||||
|
||||
**文件清单**:
|
||||
| 文件 | 行数 | 职责 |
|
||||
@@ -772,10 +780,11 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
| `data-access.ts` | 428+ | 成绩 CRUD + 统计 + 草稿(含 v2-P2-9 修复:recorderName 批量查询;P3 修复:PaginatedGradeRecords 接口 + DB 层分页 + 事务 + 存在性检查 + scope 过滤 + 并列排名;v3-P2 新增:saveGradeDraft/getGradeDraft/deleteGradeDraft + GradeDraftData 接口) |
|
||||
| `data-access-analytics.ts` | 200+ | 趋势/对比分析(P3 修复:getClassComparison 应用 buildScopeClassFilter;v3-P2 新增:getExamOptionsForGrades/getSchoolWideGradeSummary;getGradeTrend/getClassComparison/getSubjectComparison/getGradeDistribution 新增 semester/examId 可选参数) |
|
||||
| `data-access-ranking.ts` | 83 | 排名查询(P3 修复:getRankingTrend 接受 scope 参数 + class_taught 校验) |
|
||||
| `stats-service.ts` | 279 | 统计计算纯函数(P1-1 新增:8 个纯函数 + 2 个常量 + 2 个接口) |
|
||||
| `export.ts` | 189 | Excel 导出(v2-P1-5 修复:传递 currentUserId 到 data-access;P3 修复:适配 PaginatedGradeRecords 结构 + 传递 scope) |
|
||||
| `stats-service.ts` | 285 | 统计计算纯函数(P1-1 新增:8 个纯函数 + 2 个常量 + 2 个接口;P3-10:createDefaultBuckets 改为内部函数;P3-24:buildGradeTrendPoints 使用 isGradeTrendType 类型守卫替代 as 断言) |
|
||||
| `export.ts` | 209 | Excel 导出(v2-P1-5 修复:传递 currentUserId 到 data-access;P3 修复:适配 PaginatedGradeRecords 结构 + 传递 scope;P3-6:复用 stats-service.computeAverageScore 替代局部 avg;P3-7:硬编码中文改用 next-intl getTranslations) |
|
||||
| `schema.ts` | 113+ | Zod 校验(含 12 个查询 schema;P3 修复:score .max(1000) + records .max(500) + 补全查询字段;v3-P2 新增:grade_drafts 表定义第 1444-1469 行) |
|
||||
| `lib/grade-utils.ts` | 66 | 公共工具函数(toNumber/normalize/buildScopeClassFilter,v2-P2-2 修复:改用 classes data-access 子查询;P3 修复:buildScopeClassFilter 新增 currentUserId 参数) |
|
||||
| `lib/grade-utils.ts` | 20 | 公共工具函数(toNumber/normalize;P3-26:buildScopeClassFilter 迁移至 scope-filter.ts) |
|
||||
| `lib/scope-filter.ts` | 56 | DB 行级权限过滤(buildScopeClassFilter;P3-26 从 grade-utils.ts 迁移;v2-P2-2 修复:改用 classes data-access 子查询;P3 修复:新增 currentUserId 参数) |
|
||||
| `types.ts` | 168+ | 类型定义(v3-P2 新增:SchoolWideGradeSummaryItem/SchoolWideGradeSummary) |
|
||||
| `components/widget-boundary.tsx` | 136 | Widget 边界组件(P1-5 新增,v2-P1-1 已在 3 个页面应用) |
|
||||
| `components/school-wide-summary-card.tsx` | - | v3-P2 新增:管理员全校成绩汇总卡片(4 个统计卡片 + 各年级对比表格) |
|
||||
@@ -834,7 +843,7 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
| `actions-teacher.ts` | 100 | 教师班级 CRUD(3 个 Action) |
|
||||
| `actions-admin.ts` | 120 | 管理员班级 CRUD(3 个 Action) |
|
||||
| `actions-grade.ts` | 110 | 年级组长班级 CRUD(3 个 Action) |
|
||||
| `actions-invitations.ts` | 280 | 邀请码与注册(8 个 Action) |
|
||||
| `actions-invitations.ts` | 502 | 邀请码与注册 + 批量操作(10 个 Action,P2-4 新增批量导入学生/批量分配教师) |
|
||||
| `actions-schedule.ts` | 90 | 班级课表 CRUD(3 个 Action) |
|
||||
| `actions-shared.ts` | 60 | 共享工具(hasAdminScope/hasTeacherScope/hasStudentScope/parseSubjectTeachers/toWeekday) |
|
||||
| `schema.ts` | 152 | Zod 校验(13 个 schema:教师/管理员/年级班级 CRUD + 课表 CRUD + 邮箱注册) |
|
||||
@@ -847,8 +856,8 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
**职责**:学校/学年/部门/年级的 CRUD。
|
||||
|
||||
**导出函数**:
|
||||
- Actions:`createSchoolAction` / `updateSchoolAction` / `deleteSchoolAction` / `createAcademicYearAction` / `updateAcademicYearAction` / `deleteAcademicYearAction` / `createDepartmentAction` / `updateDepartmentAction` / `deleteDepartmentAction` / `createGradeAction` / `updateGradeAction` / `deleteGradeAction`(编排层:权限校验 + Zod 校验 + 调用 data-access + revalidatePath + after(logAudit))
|
||||
- Data-access:只读查询(`getSchools` / `getGrades` / `getDepartments` / `getAcademicYears` / `getStaffOptions` / `getGradesForStaff`)+ 写操作(`create/update/delete` × `Department/School/Grade/AcademicYear`)
|
||||
- Actions:`createSchoolAction` / `updateSchoolAction` / `deleteSchoolAction` / `createAcademicYearAction` / `updateAcademicYearAction` / `deleteAcademicYearAction` / `createDepartmentAction` / `updateDepartmentAction` / `deleteDepartmentAction` / `createGradeAction` / `updateGradeAction` / `deleteGradeAction` / `promoteGradesAction`(编排层:权限校验 + Zod 校验 + 调用 data-access + revalidatePath + after(logAudit);`promoteGradesAction` 年级升级,审计日志 `grade.promote`)
|
||||
- Data-access:只读查询(`getSchools` / `getGrades` / `getDepartments` / `getAcademicYears` / `getStaffOptions` / `getGradesForStaff` / `getOrgTree`)+ 写操作(`create/update/delete` × `Department/School/Grade/AcademicYear`)+ `promoteGrades(schoolId)` 年级升级(order +1 + 名称升级,辅助函数 `promoteGradeName`)
|
||||
|
||||
**依赖关系**:
|
||||
- 依赖:`shared/*`、`@/auth`、`users`(⚠️ `getStaffOptions` 直查 users/roles,可接受)
|
||||
@@ -866,20 +875,25 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
- ✅ P1-5 修复(2026-06-22):~~`schools-view.tsx` 硬编码 Table+Dialog+AlertDialog~~ 拆分为组合模式:SchoolListToolbar + SchoolFormDialog + SchoolDeleteDialog + useSchoolData hook
|
||||
- ✅ P1-6 修复(2026-06-22):新增 `getSchoolsForUser(userId)` / `getGradesForUser(userId)` 权限感知查询函数,根据用户角色返回可见数据范围
|
||||
- ✅ P2-1 修复(2026-06-22):抽取 `use-school-data` hook,将对话框状态管理逻辑与 UI 分离
|
||||
- ✅ P2-2 修复(2026-06-23):新增 `getOrgTree()` data-access 函数(学校→年级→班级三级树,通过 `import("@/modules/classes/data-access")` 动态导入获取班级数据避免循环依赖)+ `OrgTreeNode` 类型 + `OrgTreeNav` 组件(三级树形导航,支持搜索过滤/选中高亮/展开折叠/不同节点类型图标);school.json 补充 `orgTree.*` 翻译键
|
||||
- ✅ P2-3 修复(2026-06-23):新增 `promoteGradesAction` + `promoteGrades(schoolId)` + `promoteGradeName(name)` 辅助函数(中文数字 一→二…十二、阿拉伯数字 1→2…12 识别),按 order 降序逐条 +1 避免唯一约束冲突;含 `after(() => logAudit({ action: "grade.promote" }))` 审计日志
|
||||
- ✅ P2-4 修复(2026-06-23):新增 `bulkEnrollStudentsAction`(CSV 批量导入学生,复用 enrollStudentByEmail)+ `bulkAssignSubjectTeachersAction`(CSV 批量分配教师,简化实现含 TODO 待完善查找逻辑);classes/actions.ts barrel 导出
|
||||
- ✅ P2-5 修复(2026-06-23):为 department/academicYear/grade 的 9 个 CRUD Action 补充 `after(() => logAudit(...))` 审计日志(action: department.create/update/delete、academicYear.create/update/delete、grade.create/update/delete),与 school 实体审计日志策略一致
|
||||
|
||||
**文件清单**:
|
||||
| 文件 | 行数 | 职责 |
|
||||
|------|------|------|
|
||||
| `actions.ts` | 349 | 12 个 Server Action(编排层,无 DB 直访) |
|
||||
| `data-access.ts` | 504+ | 只读查询 + 12 个写操作 + 跨模块查询接口 + 权限感知函数(getSchoolsForUser/getGradesForUser) |
|
||||
| `schema.ts` | 51 | Zod 校验 |
|
||||
| `types.ts` | 96 | 类型定义(含 Insert/Update 入参类型) |
|
||||
| `actions.ts` | 457 | 13 个 Server Action(编排层,无 DB 直访;含 `promoteGradesAction` 年级升级) |
|
||||
| `data-access.ts` | 782 | 只读查询 + 12 个写操作 + `promoteGrades` 年级升级 + `getOrgTree` 组织架构树 + 跨模块查询接口 + 权限感知函数(getSchoolsForUser/getGradesForUser) |
|
||||
| `schema.ts` | 56 | Zod 校验(含 `PromoteGradesSchema`) |
|
||||
| `types.ts` | 103 | 类型定义(含 Insert/Update 入参类型 + `OrgTreeNode`) |
|
||||
| components/schools-view.tsx | 132 | 学校列表容器(组合模式,P1-5 修复) |
|
||||
| components/school-form-dialog.tsx | 80 | 学校创建/编辑对话框(P1-5 修复) |
|
||||
| components/school-delete-dialog.tsx | 50 | 学校删除确认对话框(P1-5 修复) |
|
||||
| components/school-list-toolbar.tsx | 30 | 学校列表工具栏(P1-5 修复) |
|
||||
| components/school-error-boundary.tsx | 72 | 共享 Error Boundary(P1-3 修复) |
|
||||
| components/school-skeleton.tsx | 69 | 共享骨架屏(P1-3 修复) |
|
||||
| components/org-tree-nav.tsx | 134 | 学校→年级→班级三级树形导航(P2-2 修复:搜索过滤 + 选中高亮 + 展开折叠 + 节点类型图标) |
|
||||
| hooks/use-school-data.ts | 40 | 学校数据管理 hook(P2-1 修复) |
|
||||
|
||||
---
|
||||
@@ -1153,7 +1167,7 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
|
||||
| 文件 | 行数 | 职责 |
|
||||
|------|------|------|
|
||||
| `dispatcher.ts` | 152 | 渠道选择 + 并行分发 |
|
||||
| `data-access.ts` | 177 | 站内通知 CRUD + 用户联系方式 + 日志(P0-4 / P1-5 修复后新增通知 CRUD) |
|
||||
| `data-access.ts` | 212 | 站内通知 CRUD + 用户联系方式 + 发送日志持久化(notification_logs 表;P0-4 / P1-5 修复后新增通知 CRUD) |
|
||||
| `preferences.ts` | 166 | 通知偏好 CRUD(P0-4 / P1-5 修复后从 messaging 迁移) |
|
||||
| `actions.ts` | ~260 | 6 个 Server Action(✅ P1-4:新增 4 个通知 CRUD Action) |
|
||||
| `types.ts` | 120 | 通知负载 + 渠道配置 + 通知记录 + 偏好类型(P0-4 / P1-5 修复后扩充) |
|
||||
@@ -2329,7 +2343,7 @@ shared/lib/{audit-logger, change-logger, auth-guard} → @/auth → shared/lib/*
|
||||
| `getRankingTrend` | `data-access-ranking.ts` | `(studentId, subjectId?, semester?) => Promise<RankingTrendResult \| null>` | `(studentId, subjectId?, semester?, scope?: DataScope) => Promise<RankingTrendResult \| null>`(class_taught scope 校验) |
|
||||
|
||||
**lib 层签名变更**:
|
||||
- `buildScopeClassFilter(scope: DataScope, currentUserId?: string): SQL | null`(新增 `currentUserId` 参数,`class_members` scope 内置 `eq(gradeRecords.studentId, currentUserId)` 过滤)
|
||||
- `buildScopeClassFilter(scope: DataScope, currentUserId?: string): SQL | null`(新增 `currentUserId` 参数,`class_members` scope 内置 `eq(gradeRecords.studentId, currentUserId)` 过滤;P3-26:从 `lib/grade-utils.ts` 迁移至 `lib/scope-filter.ts`)
|
||||
|
||||
**新增导出**:
|
||||
- `assertClassInScope(scope: DataScope, classId: string): string | null`(`actions.ts`,校验 classId 是否在 scope 允许范围内,供 actions.ts 与 actions-analytics.ts 复用)
|
||||
|
||||
@@ -4625,11 +4625,29 @@
|
||||
"textbook-content-panel.tsx"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "useKpDialogState",
|
||||
"file": "hooks/use-kp-dialog-state.ts",
|
||||
"signature": "() => { editingKp, setEditingKp, editKpDialogOpen, setEditKpDialogOpen, isUpdatingKp, setIsUpdatingKp, questionDialogOpen, setQuestionDialogOpen, targetKpForQuestion, setTargetKpForQuestion, deleteConfirmOpen, setDeleteConfirmOpen, pendingDeleteKpId, setPendingDeleteKpId }",
|
||||
"purpose": "知识点对话框状态管理 Hook(编辑/题目/删除确认),从 use-knowledge-point-actions 拆分",
|
||||
"usedBy": [
|
||||
"hooks/use-knowledge-point-actions.ts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "useKpCrud",
|
||||
"file": "hooks/use-kp-crud.ts",
|
||||
"signature": "(args: UseKpCrudArgs) => { handleCreateKnowledgePoint, requestDeleteKnowledgePoint, confirmDeleteKnowledgePoint, handleUpdateKnowledgePoint }",
|
||||
"purpose": "知识点 CRUD 操作 Hook,依赖 useKpDialogState 提供的状态,从 use-knowledge-point-actions 拆分",
|
||||
"usedBy": [
|
||||
"hooks/use-knowledge-point-actions.ts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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, requestDeleteKnowledgePoint, confirmDeleteKnowledgePoint, handleUpdateKnowledgePoint }",
|
||||
"purpose": "知识点操作Hook(6参数)",
|
||||
"purpose": "知识点操作门面 Hook(组合 useKpDialogState + useKpCrud,对外保持原有 API 不变)",
|
||||
"usedBy": [
|
||||
"textbook-reader.tsx"
|
||||
]
|
||||
@@ -4637,8 +4655,8 @@
|
||||
{
|
||||
"name": "useGraphData",
|
||||
"file": "hooks/use-graph-data.ts",
|
||||
"signature": "(textbookId: string, viewMode: GraphViewMode) => { data: KnowledgeGraphData | null, isLoading: boolean, error: string | null, reload: () => void }",
|
||||
"purpose": "Task 11 新增:知识图谱数据加载 Hook,按 textbookId + viewMode 加载,使用派生值模式避免 effect 中 setState",
|
||||
"signature": "(textbookId: string, viewMode: GraphViewMode) => { data: KnowledgeGraphData | null, isLoading: boolean, isRefreshing: boolean, error: string | null, reload: () => void }",
|
||||
"purpose": "Task 11 新增:知识图谱数据加载 Hook,按 textbookId + viewMode 加载,使用派生值模式避免 effect 中 setState。区分 isLoading(首次加载)和 isRefreshing(切换模式刷新,保留旧数据避免 UI 闪烁)",
|
||||
"usedBy": [
|
||||
"components/knowledge-graph.tsx"
|
||||
]
|
||||
@@ -4975,42 +4993,51 @@
|
||||
"name": "TextbookReader",
|
||||
"purpose": "教材阅读器"
|
||||
},
|
||||
{
|
||||
"name": "TeacherTextbookReader",
|
||||
"purpose": "教师端 TextbookReader 客户端包装(v1 测试修复:解决 Server→Client 函数 prop 序列化问题)"
|
||||
},
|
||||
{
|
||||
"name": "TextbookSettingsDialog",
|
||||
"purpose": "教材设置对话框"
|
||||
}
|
||||
],
|
||||
"uiDeps": [],
|
||||
"uiDepsNote": "已通过 render prop 解耦,不再直接 import questions 模块组件",
|
||||
"uiDepsNote": "已通过 render prop 解耦,TeacherTextbookReader 已移至 app 层(src/app/(dashboard)/teacher/textbooks/[id]/_components/),textbooks 模块不再直接 import questions 模块组件",
|
||||
"knownIssues": [
|
||||
"i18n 覆盖率约 95%(chapter-sidebar-list 已接入,actions.ts 已接入,section-error-boundary 默认值已改英文)",
|
||||
"i18n 覆盖率约 98%(chapter-sidebar-list/actions.ts/section-error-boundary/8 处硬编码英文 toast 已全部接入,Zod refine 消息改为英文,错误分支不再复用成功消息)",
|
||||
"类型断言残留 3 处 as string",
|
||||
"P2 图谱方向键导航未实现",
|
||||
"v1 测试已修复:textbook-reader.tsx SheetTrigger 越界、Server→Client 函数 prop 序列化、seed 数据 i18n key 不匹配"
|
||||
"v1 测试已修复:textbook-reader.tsx SheetTrigger 越界、Server→Client 函数 prop 序列化、seed 数据 i18n key 不匹配",
|
||||
"v2 核查修复:class-mastery 视图模式已实现、跨教材前置依赖双向 IN 过滤、ChapterSidebarList canEdit 默认 false、isTeacher 冗余变量移除、graph-kp-node 节点宽度常量化、GraphNodeDetailPanel textbookId 未使用 prop 移除、use-knowledge-point-actions 拆分为门面+状态+CRUD 三 Hook、use-graph-data 区分 isLoading/isRefreshing 避免 UI 闪烁、TeacherTextbookReader 移至 app 层解耦跨模块依赖"
|
||||
],
|
||||
"files": {
|
||||
"actions.ts": 502,
|
||||
"data-access.ts": 586,
|
||||
"data-access-graph.ts": 184,
|
||||
"types.ts": 94,
|
||||
"schema.ts": 62,
|
||||
"constants.ts": 99,
|
||||
"utils.ts": 203,
|
||||
"graph-layout.ts": 105,
|
||||
"actions.ts": 515,
|
||||
"data-access.ts": 662,
|
||||
"data-access-graph.ts": 207,
|
||||
"types.ts": 106,
|
||||
"schema.ts": 81,
|
||||
"constants.ts": 96,
|
||||
"utils.ts": 225,
|
||||
"graph-layout.ts": 121,
|
||||
"analytics.tsx": 43,
|
||||
"hooks/use-knowledge-point-actions.ts": 121,
|
||||
"hooks/use-knowledge-point-actions.ts": 49,
|
||||
"hooks/use-kp-dialog-state.ts": 38,
|
||||
"hooks/use-kp-crud.ts": 122,
|
||||
"hooks/use-text-selection.ts": 57,
|
||||
"hooks/use-graph-data.ts": 58,
|
||||
"components/teacher-textbook-reader.tsx": 41,
|
||||
"components/knowledge-graph.tsx": 249,
|
||||
"components/graph-kp-node.tsx": 80,
|
||||
"components/graph-prerequisite-edge.tsx": 40,
|
||||
"components/graph-toolbar.tsx": 77,
|
||||
"components/graph-node-detail-panel.tsx": 171
|
||||
"hooks/use-graph-data.ts": 76,
|
||||
"components/chapter-sidebar-list.tsx": 342,
|
||||
"components/create-chapter-dialog.tsx": 111,
|
||||
"components/graph-kp-node.tsx": 92,
|
||||
"components/graph-node-detail-panel.tsx": 181,
|
||||
"components/graph-prerequisite-edge.tsx": 48,
|
||||
"components/graph-toolbar.tsx": 93,
|
||||
"components/knowledge-graph.tsx": 376,
|
||||
"components/knowledge-point-dialogs.tsx": 175,
|
||||
"components/knowledge-point-list.tsx": 122,
|
||||
"components/section-error-boundary.tsx": 71,
|
||||
"components/textbook-card.tsx": 181,
|
||||
"components/textbook-content-panel.tsx": 189,
|
||||
"components/textbook-filters.tsx": 71,
|
||||
"components/textbook-form-dialog.tsx": 139,
|
||||
"components/textbook-reader.tsx": 446,
|
||||
"components/textbook-settings-dialog.tsx": 203
|
||||
},
|
||||
"auditReport": "audit/textbooks-audit-report.md"
|
||||
}
|
||||
@@ -5963,8 +5990,8 @@
|
||||
},
|
||||
{
|
||||
"path": "actions-invitations.ts",
|
||||
"lines": 280,
|
||||
"description": "邀请码与注册(8 个 Action,P0-3 修复)"
|
||||
"lines": 502,
|
||||
"description": "邀请码与注册 + 批量操作(10 个 Action,P0-3 修复;P2-4 新增批量导入学生/批量分配教师)"
|
||||
},
|
||||
{
|
||||
"path": "actions-schedule.ts",
|
||||
@@ -6022,6 +6049,13 @@
|
||||
"signature": "(gradeId) => Promise<ActionState<string>>",
|
||||
"purpose": "删除年级"
|
||||
},
|
||||
{
|
||||
"name": "promoteGradesAction",
|
||||
"permission": "GRADE_MANAGE",
|
||||
"signature": "(prevState, formData) => Promise<ActionState<string>>",
|
||||
"purpose": "年级升级(order +1 + 名称升级)",
|
||||
"auditLog": "grade.promote"
|
||||
},
|
||||
{
|
||||
"name": "createDepartmentAction",
|
||||
"permission": "SCHOOL_MANAGE",
|
||||
@@ -6401,6 +6435,15 @@
|
||||
"usedBy": [
|
||||
"updateAcademicYear"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OrgTreeNode",
|
||||
"type": "type",
|
||||
"definition": "组织架构树节点(学校/年级/班级三级,P2-2 修复)",
|
||||
"usedBy": [
|
||||
"getOrgTree",
|
||||
"school/components/org-tree-nav.tsx"
|
||||
]
|
||||
}
|
||||
],
|
||||
"components": [
|
||||
@@ -6455,6 +6498,12 @@
|
||||
"file": "components/school-skeleton.tsx",
|
||||
"purpose": "卡片加载骨架屏(animate-pulse)",
|
||||
"props": ""
|
||||
},
|
||||
{
|
||||
"name": "OrgTreeNav",
|
||||
"file": "components/org-tree-nav.tsx",
|
||||
"purpose": "学校→年级→班级三级树形导航(P2-2 修复):搜索过滤 + 选中高亮 + 展开/折叠 + 不同节点类型图标(School/GraduationCap/Users)+ 默认展开第一级",
|
||||
"props": "nodes: OrgTreeNode[], onSelect?: (node: OrgTreeNode) => void, selectedId?: string"
|
||||
}
|
||||
],
|
||||
"hooks": [
|
||||
@@ -8622,7 +8671,7 @@
|
||||
"deps": [
|
||||
"shared.db",
|
||||
"shared.db.schema.gradeRecords",
|
||||
"grades/lib/grade-utils.buildScopeClassFilter"
|
||||
"grades/lib/scope-filter.buildScopeClassFilter"
|
||||
],
|
||||
"usedBy": [
|
||||
"grades/data-access.getClassGradeStatsWithMeta"
|
||||
@@ -8664,7 +8713,7 @@
|
||||
"shared.db",
|
||||
"shared.db.schema.gradeRecords",
|
||||
"shared.db.schema.users",
|
||||
"grades/lib/grade-utils.buildScopeClassFilter",
|
||||
"grades/lib/scope-filter.buildScopeClassFilter",
|
||||
"users/data-access.getUserNamesByIds"
|
||||
],
|
||||
"usedBy": [
|
||||
@@ -9368,11 +9417,12 @@
|
||||
"name": "exportGradeRecordsToExcel",
|
||||
"signature": "(params: { classId: string; subjectId?: string; examId?: string; scope: DataScope; currentUserId?: string }) => Promise<Buffer>",
|
||||
"file": "export.ts",
|
||||
"purpose": "导出成绩单(Sheet1 成绩明细,Sheet2 统计汇总:均分/中位数/最高分/最低分/标准差/及格率/优秀率/参考人数)(P3 更新:传递 scope/currentUserId 到 data-access)",
|
||||
"purpose": "导出成绩单(Sheet1 成绩明细,Sheet2 统计汇总:均分/中位数/最高分/最低分/标准差/及格率/优秀率/参考人数)(P3 更新:传递 scope/currentUserId 到 data-access;P3-7:硬编码中文改用 next-intl getTranslations)",
|
||||
"deps": [
|
||||
"shared.lib.excel.exportToExcel",
|
||||
"data-access.getGradeRecords",
|
||||
"data-access.getClassGradeStats"
|
||||
"data-access.getClassGradeStats",
|
||||
"next-intl/server.getTranslations"
|
||||
],
|
||||
"usedBy": [
|
||||
"actions.exportGradesAction",
|
||||
@@ -9383,7 +9433,7 @@
|
||||
"name": "exportClassGradeReportToExcel",
|
||||
"signature": "(params: { classId: string; scope: DataScope; currentUserId?: string }) => Promise<Buffer>",
|
||||
"file": "export.ts",
|
||||
"purpose": "导出班级成绩总表(多科目横向对比,含总分/平均分/排名列)(P3 更新:适配 PaginatedGradeRecords 结构 + 传递 scope/currentUserId)",
|
||||
"purpose": "导出班级成绩总表(多科目横向对比,含总分/平均分/排名列)(P3 更新:适配 PaginatedGradeRecords 结构 + 传递 scope/currentUserId;P3-6:复用 stats-service.computeAverageScore 替代局部 avg;P3-7:硬编码中文改用 next-intl getTranslations)",
|
||||
"deps": [
|
||||
"shared.db",
|
||||
"shared.db.schema.classes",
|
||||
@@ -9391,7 +9441,9 @@
|
||||
"shared.db.schema.gradeRecords",
|
||||
"shared.db.schema.users",
|
||||
"shared.lib.excel.exportToExcel",
|
||||
"data-access.getGradeRecords"
|
||||
"data-access.getGradeRecords",
|
||||
"stats-service.computeAverageScore",
|
||||
"next-intl/server.getTranslations"
|
||||
],
|
||||
"usedBy": [
|
||||
"actions.exportGradesAction"
|
||||
@@ -9440,8 +9492,8 @@
|
||||
{
|
||||
"name": "buildScopeClassFilter",
|
||||
"signature": "(scope: DataScope, currentUserId?: string) => SQL | null",
|
||||
"file": "lib/grade-utils.ts",
|
||||
"purpose": "根据 DataScope 构建 gradeRecords 表的行级权限过滤条件(P1-2 新增:从 data-access/data-access-analytics 抽取;P3 更新:class_members scope 内置 studentId 过滤,需传入 currentUserId 参数)",
|
||||
"file": "lib/scope-filter.ts",
|
||||
"purpose": "根据 DataScope 构建 gradeRecords 表的行级权限过滤条件(P1-2 新增:从 data-access/data-access-analytics 抽取;P3 更新:class_members scope 内置 studentId 过滤,需传入 currentUserId 参数;P3-26:从 grade-utils.ts 迁移至 scope-filter.ts)",
|
||||
"usedBy": [
|
||||
"data-access.getGradeRecords",
|
||||
"data-access.getClassGradeStats",
|
||||
@@ -9469,16 +9521,17 @@
|
||||
"name": "computeAverageScore",
|
||||
"signature": "(scores: number[]) => number",
|
||||
"file": "stats-service.ts",
|
||||
"purpose": "计算分数平均值(空数组返回 0)(P1-1 新增:从 data-access.getStudentGradeSummary 抽取为纯函数)",
|
||||
"purpose": "计算分数平均值(空数组返回 0)(P1-1 新增:从 data-access.getStudentGradeSummary 抽取为纯函数;P3-6:export.ts 复用此函数替代局部 avg)",
|
||||
"usedBy": [
|
||||
"data-access.getStudentGradeSummary"
|
||||
"data-access.getStudentGradeSummary",
|
||||
"export.exportClassGradeReportToExcel"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "buildGradeTrendPoints",
|
||||
"signature": "(rows: RawScoreRow[]) => GradeTrendPoint[]",
|
||||
"file": "stats-service.ts",
|
||||
"purpose": "构建成绩趋势数据点(按考试标题分组,归一化分数 0-100)(P1-1 新增:从 data-access-analytics.getGradeTrend 抽取为纯函数)",
|
||||
"purpose": "构建成绩趋势数据点(按考试标题分组,归一化分数 0-100)(P1-1 新增:从 data-access-analytics.getGradeTrend 抽取为纯函数;P3-24:使用 isGradeTrendType 类型守卫替代 as 断言)",
|
||||
"usedBy": [
|
||||
"data-access-analytics.getGradeTrend"
|
||||
]
|
||||
@@ -11040,19 +11093,23 @@
|
||||
},
|
||||
{
|
||||
"name": "logNotificationSend",
|
||||
"signature": "(result: ChannelSendResult) => void",
|
||||
"signature": "(result: ChannelSendResult, payload?: { userId: string; title: string }) => Promise<void>",
|
||||
"file": "data-access.ts",
|
||||
"purpose": "记录单条发送日志(当前使用 console.info;未来可扩展 notification_logs 表)",
|
||||
"deps": [],
|
||||
"purpose": "记录单条发送日志到 notification_logs 表(DB 写入失败时降级 console,不阻塞通知流程)",
|
||||
"deps": [
|
||||
"shared.db",
|
||||
"shared.db.schema.notificationLogs",
|
||||
"@paralleldrive/cuid2"
|
||||
],
|
||||
"usedBy": [
|
||||
"logNotificationSendBatch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "logNotificationSendBatch",
|
||||
"signature": "(results: ChannelSendResult[]) => void",
|
||||
"signature": "(results: ChannelSendResult[], payload?: { userId: string; title: string }) => Promise<void>",
|
||||
"file": "data-access.ts",
|
||||
"purpose": "批量记录发送日志",
|
||||
"purpose": "批量记录发送日志(并行调用 logNotificationSend)",
|
||||
"deps": [
|
||||
"logNotificationSend"
|
||||
],
|
||||
@@ -11221,6 +11278,13 @@
|
||||
"messaging (via re-export)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "NotificationLog",
|
||||
"type": "interface",
|
||||
"file": "types.ts",
|
||||
"definition": "{ id, userId, title, channel: NotificationChannel, status: 'success' | 'failure', messageId: string | null, error: string | null, sentAt }",
|
||||
"usedBy": []
|
||||
},
|
||||
{
|
||||
"name": "PaginatedResult",
|
||||
"type": "interface",
|
||||
@@ -14339,7 +14403,7 @@
|
||||
},
|
||||
"dbTables": {
|
||||
"_meta": {
|
||||
"total": 58,
|
||||
"total": 59,
|
||||
"orm": "Drizzle ORM 0.45",
|
||||
"database": "MySQL",
|
||||
"idStrategy": "CUID2 (varchar length 128)",
|
||||
@@ -14598,6 +14662,10 @@
|
||||
"owner": "notifications",
|
||||
"description": "消息通知"
|
||||
},
|
||||
"notificationLogs": {
|
||||
"owner": "notifications",
|
||||
"description": "通知发送日志(channel/status/messageId/error/sentAt)"
|
||||
},
|
||||
"notificationPreferences": {
|
||||
"owner": "notifications",
|
||||
"description": "通知偏好(email/sms/push + 分类开关)"
|
||||
@@ -14980,7 +15048,9 @@
|
||||
"textbooks": {
|
||||
"dependsOn": [
|
||||
"shared",
|
||||
"auth"
|
||||
"auth",
|
||||
"users",
|
||||
"classes"
|
||||
],
|
||||
"uses": {
|
||||
"shared": [
|
||||
@@ -14990,6 +15060,12 @@
|
||||
],
|
||||
"auth": [
|
||||
"auth"
|
||||
],
|
||||
"users": [
|
||||
"data-access.getCurrentStudentUser"
|
||||
],
|
||||
"classes": [
|
||||
"data-access-students.getClassStudents"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user