526 lines
24 KiB
Markdown
526 lines
24 KiB
Markdown
# 数据库访问层审计报告 v1
|
||
|
||
> 创建日期:2026-07-07
|
||
> 审计范围:86 个 `data-access*.ts` + ~30 个 `actions.ts`
|
||
> 审计方案:纯深读(5 个并行 sub-agent 全量扫描)
|
||
> 框架依据:[data-access-audit-framework-v1.md](./data-access-audit-framework-v1.md)
|
||
> 原始输出:[g1-audit-output.json](./g1-audit-output.json) · [g2-data-access-audit.json](./g2-data-access-audit.json) · [g3-audit-output.json](./g3-audit-output.json) · [g4-audit-output.json](./g4-audit-output.json) · [g5-audit-output.json](./g5-audit-output.json)
|
||
|
||
---
|
||
|
||
## 一、执行摘要
|
||
|
||
| 指标 | 数值 |
|
||
|---|---|
|
||
| 审计文件总数 | 101(86 data-access + 15 actions 辅查) |
|
||
| 发现问题总数 | 230 |
|
||
| P0 Critical | 17(7.4%) |
|
||
| P1 High | 48(20.9%) |
|
||
| P2 Medium | 105(45.6%) |
|
||
| P3 Low | 60(26.1%) |
|
||
|
||
### 1.1 模块热度图(按 P0+P1 数量降序)
|
||
|
||
| 模块 | P0 | P1 | P0+P1 | 总计 | 风险等级 |
|
||
|---|---|---|---|---|---|
|
||
| messaging | 1 | 4 | 5 | 5 | 🔴 极高 |
|
||
| classes | 1 | 4 | 5 | 14 | 🔴 极高 |
|
||
| school | 0 | 5 | 5 | 8 | 🔴 极高 |
|
||
| lesson-preparation | 1 | 3 | 4 | 28 | 🟠 高 |
|
||
| scheduling | 1 | 2 | 3 | 6 | 🟠 高 |
|
||
| adaptive-practice | 1 | 2 | 3 | 4 | 🟠 高 |
|
||
| elective | 0 | 3 | 3 | 7 | 🟠 高 |
|
||
| textbooks | 2 | 1 | 3 | 11 | 🟠 高 |
|
||
| onboarding | 2 | 0 | 2 | 2 | 🟡 中 |
|
||
| grades | 0 | 2 | 2 | 4 | 🟡 中 |
|
||
| questions | 1 | 1 | 2 | 11 | 🟡 中 |
|
||
| audit | 1 | 1 | 2 | 3 | 🟡 中 |
|
||
| parent | 1 | 0 | 1 | 1 | 🟡 中 |
|
||
| attendance | 0 | 1 | 1 | 9 | 🟡 中 |
|
||
| files | 0 | 4 | 4 | 13 | 🟡 中 |
|
||
| exams | 1 | 0 | 1 | 1 | 🟢 低 |
|
||
| course-plans | 1 | 0 | 1 | 5 | 🟢 低 |
|
||
| homework | 0 | 1 | 1 | 2 | 🟢 低 |
|
||
| diagnostic | 0 | 1 | 1 | 2 | 🟢 低 |
|
||
| 其他 (auth/rbac/notifications/dashboard/search/ai/announcements/error-book/proctoring/settings) | 0 | 0 | 0 | 0-3 | 🟢 低 |
|
||
|
||
### 1.2 维度分布
|
||
|
||
| 维度 | 问题数 | 占比 | P0 | P1 |
|
||
|---|---|---|---|---|
|
||
| 架构违规(A-*) | 58 | 25.2% | 6 | 18 |
|
||
| 性能优化(F-*) | 71 | 30.9% | 7 | 14 |
|
||
| 结构可维护性(S-*) | 47 | 20.4% | 2 | 11 |
|
||
| 模式标准化(P-*) | 54 | 23.5% | 2 | 5 |
|
||
|
||
---
|
||
|
||
## 二、量化指标仪表盘
|
||
|
||
| 指标 | 数值 | 备注 |
|
||
|---|---|---|
|
||
| `import "server-only"` 缺失文件 | 2 | exams/data-access.ts、onboarding/data-access.ts |
|
||
| cacheFn 未覆盖读函数(估算) | 60+ | 集中在 classes(24+)、questions(5)、textbooks(3)、lesson-preparation(4) |
|
||
| 超长文件(>800 行) | 3 | messaging(1089,超硬限)、school(938)、grades-analytics(831) |
|
||
| 单文件导出函数 > 20 | 4 | messaging(42+)、classes/data-access.ts(25+)、school(30+)、questions(28)、textbooks(35) |
|
||
| `as` 断言(非豁免) | 2 | classes/data-access-admin.ts、classes/data-access-teacher.ts(DEFAULT_CLASS_SUBJECTS widening) |
|
||
| `any` 使用 | 0 | 全部合规 |
|
||
| `console.error` 调试代码 | 25+ | school(12)、files(12)、classes(3)、course-plans(2)、audit(9) |
|
||
| N+1 循环 SQL(F-01) | 11 | 跨 4 组 |
|
||
| `LIKE '%xxx%'` 全表扫描 | 7 | lesson-preparation(4)、questions(1)、textbooks(1)、classes(1)、messaging(1) |
|
||
| SELECT * 未指定列 | 35+ | 跨 G1(16)、G3(11)、G5(8) |
|
||
| 无 LIMIT 大表查询 | 18 | 集中在 lesson-preparation |
|
||
| 跨模块直查 schema 表(A-06) | 7 | textbooks-graph(2)、lesson-preparation(2)、questions(1)、scheduling(1)、announcements(1) |
|
||
| data-access 含业务逻辑(A-02) | 18 | 集中在 scheduling、messaging、elective、classes |
|
||
| 未包裹事务的多步写(F-09) | 4 | course-plans、classes、auth、school |
|
||
| actions 漏/错权限校验(A-08) | 4 | parent(缺失全部)、audit(purge 用读权限)、audit(retention 用读权限) |
|
||
| actions 直查 DB(A-09) | 1 | onboarding |
|
||
|
||
---
|
||
|
||
## 三、P0 Critical 问题清单(17 条,必须立即治理)
|
||
|
||
### 3.1 安全漏洞类(4 条)
|
||
|
||
| ID | 文件 | 问题 | 修复 |
|
||
|---|---|---|---|
|
||
| G4-003 | audit/actions.ts L192-225 | `purgeAuditLogsAction` 用 `AUDIT_LOG_READ`(读权限)执行物理删除,权限提权漏洞 | 新增 `AUDIT_LOG_PURGE` 权限点 |
|
||
| G4-002 | parent/ | 模块缺失 actions.ts,3 个 app 页面直接 import data-access,完全绕过 `requirePermission` | 新建 parent/actions.ts,3 个页面改调 Action |
|
||
| G2-001 | exams/data-access.ts L1 | 缺 `import "server-only"`,DB 逻辑可能泄露到客户端 bundle | 首行添加 `import "server-only"` |
|
||
| G5-001 | onboarding/data-access.ts L1 | 缺 `import "server-only"` | 首行添加 `import "server-only"` |
|
||
|
||
### 3.2 架构硬违规类(5 条)
|
||
|
||
| ID | 文件 | 问题 | 修复 |
|
||
|---|---|---|---|
|
||
| G1-001 | textbooks/data-access-graph.ts L7-121 | 直查 questions 模块 `questionsToKnowledgePoints` 表 + diagnostic 模块 `knowledgePointMastery` 表 | 改调对方 data-access 跨模块接口 |
|
||
| G3-002 | scheduling/data-access.ts L8-17 | 直查 classes/users/subjects 三模块的 schema 表 | 改调 `getClassNamesByIds`/`getUserNamesByIds` 等 |
|
||
| G3-003 | scheduling/data-access-class-schedule.ts | data-access 含时间校验、归属校验、状态机判断 | 校验逻辑移至 actions |
|
||
| G5-002 | onboarding/actions.ts L15-76 | actions.ts 直接 `import { db }` 并查 `users` 表,违反三层架构 | data-access 新增 `getUserOnboardedAt`,actions 改调 |
|
||
| G4-001 | messaging/data-access.ts L1-1089 | 单文件 1089 行超 1000 硬限,8 类职责混合 | 拆分为 7 个 data-access-*.ts |
|
||
|
||
### 3.3 必定性能问题类(8 条)
|
||
|
||
| ID | 文件 | 问题 | 修复 |
|
||
|---|---|---|---|
|
||
| G1-002 | questions/data-access.ts L294-315 | `deleteQuestionRecursive` 递归 N+1,每子题单独查询+删除 | 收集后代 ID + `inArray` 批量删除 |
|
||
| G1-003 | questions/data-access.ts L350-378 | `deleteQuestionsBatch` 循环调用 `deleteQuestionRecursive` 产生 N×深度 查询 | 一次性收集所有后代 + 单次 `inArray` 删除 |
|
||
| G1-004 | lesson-preparation/data-access-comments.ts L128-140 | `deleteComment` 递归 N+1 | 单次查询构建 parent→children Map + 批量删除 |
|
||
| G1-005 | textbooks/data-access.ts L426-458 | `reorderChapters` 循环内逐条 UPDATE | `CASE WHEN` 批量更新 |
|
||
| G3-001 | classes/data-access.ts L17-313 | 24+ 读函数全部未走 cacheFn,跨模块高频调用直连 DB | 补齐 Raw + Wrapper 配对 |
|
||
| G3-004 | classes/data-access-teacher.ts L92-116 | `getTeacherClassesRaw` 循环内对每班发起 2 次子查询(2N+1) | 新增批量接口 |
|
||
| G3-005 | course-plans/data-access.ts L324-331 | `reorderCoursePlanItems` 循环内 N 次 UPDATE 且未包裹事务 | 事务 + `CASE WHEN` 批量更新 |
|
||
| G2-003 | adaptive-practice/data-access-analytics.ts L311-384 | `getTeacherClassPracticeOverviewsRaw` 对每班发起 2 条 SQL(2N+1) | 批量查询 + groupBy |
|
||
|
||
---
|
||
|
||
## 四、P1 High 问题清单(48 条,Phase 1 治理)
|
||
|
||
### 4.1 性能类(14 条)
|
||
|
||
| ID | 文件 | 规则 | 概要 |
|
||
|---|---|---|---|
|
||
| G1-006~009 | lesson-preparation/questions/textbooks | F-02 | 4 处 `LIKE '%xxx%'` 全表扫描(课案标题、JSON content、题目 content、教材 4 字段) |
|
||
| G1-010 | lesson-preparation/data-access.ts L247-277 | F-04 | `getLessonPlansRaw` 5 表 LEFT JOIN |
|
||
| G1-011~013 | lesson-preparation (3 处) | F-05 | 列表查询无 LIMIT(getLessonPlansRaw、getPendingReviewPlansRaw、getCalendarEventsRaw) |
|
||
| G1-014~015 | lesson-preparation (2 处) | F-10 | 全表拉取后内存聚合统计 |
|
||
| G1-016 | lesson-preparation/data-access-analytics.ts L168-184 | F-06 | 5 次串行 COUNT 查询同表 |
|
||
| G1-017~018 | lesson-preparation (2 处) | F-01 | 拉全表后内存 filter |
|
||
| G1-019 | textbooks/actions.ts L396-398 | F-08 | 循环调用 `getGradeNameById`(N 次 DB) |
|
||
| G2-002 | grades/data-access-appeals.ts L122-151 | F-01 | `getPendingAppealsForReviewRaw` JS 层 filter 班级范围(潜在数据泄露) |
|
||
| G2-004 | adaptive-practice/data-access-analytics.ts L320-325 | F-08 | 循环内跨模块调用 `getActiveStudentIdsByClassId` |
|
||
| G3-006 | course-plans/data-access.ts L309-332 | F-09 | `reorderCoursePlanItems` 多次 UPDATE 未包裹事务 |
|
||
| G3-012 | school/data-access.ts L803-822 | F-09 | `promoteGrades` 循环 UPDATE 未包裹事务 |
|
||
| G3-017 | classes/data-access-students.ts L281-285 | F-02 | `LIKE '%xxx%'` 全表扫描 users.name/email |
|
||
| G3-022 | attendance/data-access-correlation.ts L46-193 | F-01/A-02 | 148 行业务编排逻辑(含跨模块调用) |
|
||
|
||
### 4.2 架构类(11 条)
|
||
|
||
| ID | 文件 | 规则 | 概要 |
|
||
|---|---|---|---|
|
||
| G4-004 | audit/actions.ts L163-190 | A-08 | `saveAuditRetentionConfigAction` 用读权限执行写操作 |
|
||
| G4-006~008 | messaging/data-access.ts (3 处) | A-02 | 状态机/防重复业务逻辑嵌入 data-access |
|
||
| G3-007~008 | school/data-access.ts | S-01/S-02 | 938 行 + 30+ 导出函数 |
|
||
| G3-010 | school/data-access.ts | A-10 | 12 处 `console.error` 吞异常 |
|
||
| G3-011 | school/data-access.ts L246-408 | A-02 | 角色判断业务逻辑嵌入 data-access |
|
||
| G3-024~025 | classes/data-access-teacher.ts L284-439 | A-02/F-09 | `enrollTeacherByInvitationCode` 155 行状态机 + 未包裹事务 |
|
||
| G5-003 | elective/data-access-operations.ts | A-02 | 业务逻辑混淆(抽签算法、冲突检测、i18n 通知) |
|
||
|
||
### 4.3 结构类(5 条)
|
||
|
||
| ID | 文件 | 规则 | 概要 |
|
||
|---|---|---|---|
|
||
| G2-005 | grades/data-access-analytics.ts | S-01 | 831 行超 800 警告线 |
|
||
| G5-004 | elective/data-access-operations.ts L222-304 | S-08 | DB 写入与抽签算法混淆 |
|
||
| G5-005 | files/data-access.ts | A-10 | 12 处 `console.error` |
|
||
| G5-006 | files/data-access.ts | P-05 | try-catch 吞错误返回 null/[]/false |
|
||
| G4-009 | auth/data-access.ts | F-09 | `createUser` 两次 INSERT 无事务包裹 |
|
||
|
||
(完整 P1 清单详见各 sub-agent JSON 输出)
|
||
|
||
---
|
||
|
||
## 五、按维度分组的问题清单
|
||
|
||
### 5.1 模式标准化(P-*,54 条)
|
||
|
||
#### P-01 `import "server-only"` 缺失(2 条 P0)
|
||
|
||
| ID | 文件 | 修复 |
|
||
|---|---|---|
|
||
| G2-001 | exams/data-access.ts L1 | 首行添加 `import "server-only"` |
|
||
| G5-001 | onboarding/data-access.ts L1 | 首行添加 `import "server-only"` |
|
||
|
||
#### P-03 cacheFn 未覆盖(30+ 条,P2)
|
||
|
||
集中模块:
|
||
- **classes/data-access.ts**(24+ 读函数,G3-001 P0)
|
||
- **lesson-preparation**(4 个,G1-021)
|
||
- **questions**(5 个,G1-023)
|
||
- **textbooks**(3 个,G1-024)
|
||
- **lesson-preparation-substitutes**(1 个,G1-022)
|
||
|
||
修复模式:
|
||
```ts
|
||
// Before
|
||
export const getClassNamesByIds = async (classIds: string[]) => { /* SQL */ }
|
||
|
||
// After
|
||
export const getClassNamesByIdsRaw = async (classIds: string[]) => { /* SQL */ }
|
||
export const getClassNamesByIds = cacheFn(getClassNamesByIdsRaw, {
|
||
tags: ["classes:names"],
|
||
ttl: 300,
|
||
keyParts: ["classes", "getClassNamesByIds"],
|
||
})
|
||
```
|
||
|
||
#### P-05 错误处理不一致(13 条,P1-P2)
|
||
|
||
集中模块:files(9 处 try-catch 吞错误)、school(12 处 console.error + 吞异常)
|
||
|
||
#### P-07 日期序列化 helper 重复(5 处,P2-P3)
|
||
|
||
- attendance/data-access.ts `serializeDate`
|
||
- attendance/data-access-stats.ts `serializeDate`
|
||
- scheduling/data-access.ts `serializeDate`
|
||
- school/data-access.ts `toIso`
|
||
- course-plans/data-access.ts `toIso`/`toIsoRequired`
|
||
|
||
修复:提取到 `src/shared/lib/date-utils.ts`
|
||
|
||
#### P-09 `as` 断言(2 条 P3,非豁免)
|
||
|
||
- classes/data-access-admin.ts L36 `DEFAULT_CLASS_SUBJECTS as readonly string[]`
|
||
- classes/data-access-teacher.ts L41 同上
|
||
|
||
#### P-10 `any` 使用
|
||
|
||
零违规,全部合规。
|
||
|
||
### 5.2 性能优化(F-*,71 条)
|
||
|
||
#### F-01 N+1 循环 SQL(11 条,跨 P0/P1/P2)
|
||
|
||
| ID | 文件 | 模式 |
|
||
|---|---|---|
|
||
| G1-002 | questions deleteQuestionRecursive | 递归内单独查询+删除 |
|
||
| G1-003 | questions deleteQuestionsBatch | 循环调用递归删除 |
|
||
| G1-004 | lesson-preparation deleteComment | 递归内单独查询+删除 |
|
||
| G1-005 | textbooks reorderChapters | 循环内逐条 UPDATE |
|
||
| G1-017 | lesson-preparation getSchedulesByDateRangeRaw | 拉全表后内存 filter |
|
||
| G1-018 | lesson-preparation getResponsesByStudentIdRaw | 拉全量后内存 filter |
|
||
| G2-002 | grades getPendingAppealsForReviewRaw | JS 层 filter 班级范围 |
|
||
| G2-003 | adaptive-practice getTeacherClassPracticeOverviewsRaw | Promise.all 内 2N+1 |
|
||
| G3-004 | classes getTeacherClassesRaw | 循环内 2 次子查询 |
|
||
| G3-005 | course-plans reorderCoursePlanItems | 循环内 N 次 UPDATE |
|
||
| G3-042 | classes generateUniqueInvitationCode | 循环内重试查询 |
|
||
|
||
#### F-02 `LIKE '%xxx%'` 全表扫描(7 条 P1)
|
||
|
||
| ID | 文件 | 字段 |
|
||
|---|---|---|
|
||
| G1-006 | lesson-preparation | lessonPlans.title |
|
||
| G1-007 | lesson-preparation-knowledge | lessonPlans.content (JSON) |
|
||
| G1-008 | questions | questions.content (JSON, +LOWER+CAST) |
|
||
| G1-009 | textbooks | title/subject/grade/publisher 4 字段 |
|
||
| G3-017 | classes-students | users.name/email |
|
||
| G4-010 | messaging | messages.subject/content |
|
||
|
||
修复策略:
|
||
- 短期:前缀匹配 `LIKE 'xxx%'`(可走索引)
|
||
- 中期:FULLTEXT 索引 + `MATCH AGAINST IN BOOLEAN MODE`(questions 表已实施,参见架构图 1.1.4)
|
||
- 长期:关联表存储提取后的关系(如 lesson_plan_knowledge_point_refs)
|
||
|
||
#### F-03 SELECT * 未指定列(35+ 条 P2-P3)
|
||
|
||
集中模块:lesson-preparation(16 处)、school(4 处)、scheduling(3 处)、attendance(2 处)、course-plans(7 处)、files(8 处)
|
||
|
||
#### F-05 无 LIMIT 大表查询(18 条 P1-P2)
|
||
|
||
集中模块:lesson-preparation(7 处)、textbooks(2 处)、classes(3 处)、proctoring(1 处)
|
||
|
||
#### F-09 事务范围问题(4 条 P1)
|
||
|
||
| ID | 文件 | 问题 |
|
||
|---|---|---|
|
||
| G3-006 | course-plans reorderCoursePlanItems | 多次 UPDATE 未包裹事务 |
|
||
| G3-012 | school promoteGrades | 循环 UPDATE 未包裹事务 |
|
||
| G3-025 | classes enrollTeacherByInvitationCode | 多次写操作未包裹事务 |
|
||
| G4-009 | auth createUser | 两次 INSERT 无事务包裹 |
|
||
|
||
#### F-10 全表 COUNT 无过滤(4 条 P2)
|
||
|
||
集中模块:textbooks、questions、lesson-preparation、classes
|
||
|
||
### 5.3 架构违规(A-*,58 条)
|
||
|
||
#### A-02 data-access 含业务逻辑(18 条 P0-P2)
|
||
|
||
| 模块 | 文件 | 业务逻辑类型 |
|
||
|---|---|---|
|
||
| scheduling | data-access-class-schedule.ts | 时间校验 + 归属校验 + 状态机 |
|
||
| scheduling | data-access.ts | — |
|
||
| messaging | data-access.ts | 撤回状态机 + 防重复 + 页面编排 |
|
||
| elective | data-access-operations.ts | 抽签算法 + 冲突检测 + i18n 通知 |
|
||
| classes | data-access-teacher.ts | 邀请码状态机 + 角色校验 |
|
||
| classes | data-access-invitations.ts | 懒清理状态迁移 |
|
||
| school | data-access.ts | 角色判断 + 权限感知查询 |
|
||
| attendance | data-access-correlation.ts | 跨模块编排 + 成绩归一化 |
|
||
| attendance | data-access-stats.ts | 纯计算函数导出 |
|
||
| lesson-preparation | data-access-review.ts | 状态机迁移 |
|
||
| lesson-preparation | data-access-ai-evaluation.ts | 评分算法纯函数 |
|
||
| textbooks | data-access.ts | 重排序算法 |
|
||
| diagnostic | data-access.ts | 掌握度累积计算 |
|
||
| homework | data-access.ts | computeOverdueCount 闭包 |
|
||
|
||
#### A-06 跨模块直查 schema 表(7 条 P0-P2)
|
||
|
||
| ID | 文件 | 被查模块 |
|
||
|---|---|---|
|
||
| G1-001 | textbooks/data-access-graph.ts | questions + diagnostic |
|
||
| G1-031 | lesson-preparation/data-access.ts | textbooks (textbooks/chapters) |
|
||
| G1-032 | lesson-preparation/data-access-schedules.ts | classes |
|
||
| G1-033 | questions/data-access.ts | textbooks (knowledgePoints) |
|
||
| G3-002 | scheduling/data-access.ts | classes + users + subjects |
|
||
|
||
#### A-08 actions 权限校验问题(4 条 P0-P1)
|
||
|
||
| ID | 文件 | 问题 |
|
||
|---|---|---|
|
||
| G4-002 | parent/ | 模块缺失 actions.ts,3 页面直访 data-access |
|
||
| G4-003 | audit/actions.ts | purge 用读权限 |
|
||
| G4-004 | audit/actions.ts | retention 配置用读权限 |
|
||
| G4-047 | rbac/data-access-assignments.ts | 内存 post-fetch 过滤导致 total 错误(伴随 A-02) |
|
||
|
||
#### A-09 actions 直查 DB(1 条 P0)
|
||
|
||
| ID | 文件 | 问题 |
|
||
|---|---|---|
|
||
| G5-002 | onboarding/actions.ts L15-76 | 直接 `import { db }` 并查 `users` 表 |
|
||
|
||
#### A-10 `console.error` 调试代码(25+ 条 P1-P2)
|
||
|
||
| 模块 | 文件 | 数量 |
|
||
|---|---|---|
|
||
| school | data-access.ts | 12 |
|
||
| files | data-access.ts | 12 |
|
||
| classes | data-access-teacher/students/admin | 3 |
|
||
| course-plans | data-access.ts | 2 |
|
||
| audit | data-access.ts | 9 |
|
||
|
||
### 5.4 结构与可维护性(S-*,47 条)
|
||
|
||
#### S-01 超长文件(3 条 P0-P1)
|
||
|
||
| ID | 文件 | 行数 | 状态 |
|
||
|---|---|---|---|
|
||
| G4-001 | messaging/data-access.ts | 1089 | 超 1000 硬限,必须拆分 |
|
||
| G3-007 | school/data-access.ts | 938 | 超 800 警告,接近硬限 |
|
||
| G2-005 | grades/data-access-analytics.ts | 831 | 超 800 警告 |
|
||
|
||
#### S-02 单文件导出函数过多(4 条 P2)
|
||
|
||
| 文件 | 导出数 |
|
||
|---|---|
|
||
| messaging/data-access.ts | 42+ |
|
||
| school/data-access.ts | 30+ |
|
||
| textbooks/data-access.ts | 35 |
|
||
| questions/data-access.ts | 28 |
|
||
| classes/data-access.ts | 25+ |
|
||
|
||
#### S-03 重复 helper(8 条 P2)
|
||
|
||
| helper | 出现模块 |
|
||
|---|---|
|
||
| serializeDate/toIso | attendance、scheduling、school、course-plans |
|
||
| toLessonPlanStatus | lesson-preparation(2 文件) |
|
||
| isStringArray | lesson-preparation(2 文件) |
|
||
| fetchClassesWithSubjects | classes(2 函数 145+124 行重复) |
|
||
| fetchGradesWithHeads | school(3 函数重复) |
|
||
|
||
#### S-06 缺 JSDoc(15+ 条 P2-P3)
|
||
|
||
集中模块:lesson-preparation(versions/templates)、questions、textbooks
|
||
|
||
---
|
||
|
||
## 六、P0-P3 优先级矩阵
|
||
|
||
```
|
||
高影响
|
||
│
|
||
│ P0 立即治理 P1 Phase 1
|
||
│ ───────────────── ─────────────────
|
||
│ • parent 权限漏洞 • N+1 循环 SQL(非热路径)
|
||
│ • audit 权限提权 • LIKE 全表扫描
|
||
│ • server-only 缺失 • 超长文件(school/grades)
|
||
│ • 跨模块 schema 直查 • 业务逻辑嵌入 data-access
|
||
│ • N+1 循环 SQL(热路径) • 事务未包裹
|
||
│ • messaging 超硬限 • console.error 吞异常
|
||
│
|
||
├──────────────────────────────────────────────
|
||
│
|
||
│ P2 Phase 2 P3 Phase 3
|
||
│ ───────────────── ─────────────────
|
||
│ • cacheFn 未覆盖 • as 断言(widening)
|
||
│ • SELECT * 未指定列 • 非空断言 !
|
||
│ • 无 LIMIT 大表查询 • JSDoc 补齐
|
||
│ • 重复 helper • 动态 import 注释
|
||
│ • 单文件导出过多 • export * 改显式
|
||
│
|
||
低影响
|
||
高紧迫 ─────────────────── 低紧迫
|
||
```
|
||
|
||
---
|
||
|
||
## 七、分阶段治理路线图
|
||
|
||
### Phase 0:紧急安全修复(XS-S,立即执行)
|
||
|
||
| 任务 | ID | 工作量 | 验证 |
|
||
|---|---|---|---|
|
||
| 添加 `import "server-only"` 到 exams/data-access.ts | G2-001 | XS | tsc + lint |
|
||
| 添加 `import "server-only"` 到 onboarding/data-access.ts | G5-001 | XS | tsc + lint |
|
||
| 新建 parent/actions.ts,3 页面改调 Action | G4-002 | M | 手动测试 3 页面 |
|
||
| audit purge 权限点新增 + 替换 | G4-003 | S | 权限矩阵测试 |
|
||
| audit retention 权限点替换 | G4-004 | S | 权限矩阵测试 |
|
||
| onboarding/actions.ts 移除直查 DB | G5-002 | S | tsc + lint |
|
||
|
||
**Phase 0 完成标准**:所有 P0 安全漏洞修复,`npm run lint` + `npx tsc --noEmit` 零错误。
|
||
|
||
### Phase 1:P0 架构与性能修复(M-L,1-2 周)
|
||
|
||
| 任务批次 | 涉及 ID | 工作量 | 依赖 |
|
||
|---|---|---|---|
|
||
| **1.1 跨模块 schema 直查治理** | G1-001, G3-002, G1-031~033 | L | 需在 questions/diagnostic/textbooks/classes 模块新增跨模块接口 |
|
||
| **1.2 messaging 拆分** | G4-001, G4-005~008 | L | 拆分为 7 个子文件 + 业务逻辑移至 actions |
|
||
| **1.3 N+1 热路径修复** | G1-002~005, G3-001, G3-004, G3-005, G2-003 | L | classes 补齐 cacheFn 是基础 |
|
||
| **1.4 scheduling 业务逻辑下移** | G3-003, G3-024, G3-025 | M | data-access-class-schedule.ts 重写 |
|
||
| **1.5 elective 业务逻辑拆分** | G5-003, G5-004 | L | 提取 lib/lottery.ts + lib/schedule-conflict.ts |
|
||
|
||
**Phase 1 完成标准**:所有 P0 修复,关键路径性能提升,架构分层清晰。
|
||
|
||
### Phase 2:P1 性能与结构优化(M-L,2-3 周)
|
||
|
||
| 任务批次 | 涉及 ID | 工作量 |
|
||
|---|---|---|
|
||
| **2.1 LIKE 全表扫描治理** | G1-006~009, G3-017, G4-010 | L(FULLTEXT 索引 + 查询重写) |
|
||
| **2.2 超长文件拆分** | G3-007, G2-005 | M(school 按职责拆 8 文件、grades-analytics 按维度拆) |
|
||
| **2.3 school 模块重构** | G3-007~011 | L(拆分 + 角色判断移至 actions + 删除 console.error) |
|
||
| **2.4 files 模块错误处理重构** | G5-005, G5-006, G5-007 | M(删除 try-catch + console.error) |
|
||
| **2.5 事务包裹修复** | G3-006, G3-012, G3-025, G4-009 | S |
|
||
| **2.6 无 LIMIT 查询保护** | G1-011~013, G1-050~053, G3-031~032, G3-041, G3-044 | M |
|
||
|
||
**Phase 2 完成标准**:所有 P1 修复,无超长文件,无 LIKE 全表扫描,无未包裹事务。
|
||
|
||
### Phase 3:P2 模式标准化(S-M,1-2 周)
|
||
|
||
| 任务批次 | 涉及 ID | 工作量 |
|
||
|---|---|---|
|
||
| **3.1 cacheFn 全量补齐** | G1-021~024, G3-001(剩余) | M |
|
||
| **3.2 SELECT * 改显式列** | G1-039~049, G3-009, G3-026~028, G5-007 | M(机械替换) |
|
||
| **3.3 日期 helper 提取** | G3-021, G3-047, G1-025 | S(提取 shared/lib/date-utils.ts) |
|
||
| **3.4 重复 helper 提取** | G1-026~027, G3-020, G3-036 | M |
|
||
| **3.5 JSDoc 补齐** | G1-034~036, G1-066~067, G3-046 | M |
|
||
|
||
**Phase 3 完成标准**:所有 P2 修复,模式统一,helper 集中到 shared/lib。
|
||
|
||
### Phase 4:P3 风格优化(XS,按需)
|
||
|
||
| 任务 | 涉及 ID | 工作量 |
|
||
|---|---|---|
|
||
| `as` widening 断言改类型标注 | G3-029~030 | XS |
|
||
| 非空断言 `!` 改类型守卫 | G1-060~065 | XS |
|
||
| `export *` 改显式 re-export | G3-048 | S |
|
||
| 死代码删除 | G3-018 | XS |
|
||
|
||
**Phase 4 完成标准**:零 `as`(非豁免)、零 `!`、零死代码。
|
||
|
||
---
|
||
|
||
## 八、跨模块治理建议
|
||
|
||
### 8.1 新增 shared/lib 公共 helper
|
||
|
||
| helper | 路径 | 用途 | 替代模块 |
|
||
|---|---|---|---|
|
||
| `toISODateString` | shared/lib/date-utils.ts | 日期序列化 | attendance/scheduling/school/course-plans |
|
||
| `buildScopeFilter` | shared/lib/scope-filter.ts | DataScope → SQL 过滤 | attendance/grades/homework 等重复实现 |
|
||
| `serializeDate` | (合并到 date-utils.ts) | 同 toISODateString | — |
|
||
|
||
### 8.2 新增跨模块批量接口
|
||
|
||
| 接口 | 模块 | 用途 | 调用方 |
|
||
|---|---|---|---|
|
||
| `getActiveStudentIdsByClassIds(classIds)` | classes | 批量获取多班学生 ID | adaptive-practice、attendance |
|
||
| `getGradeNamesByIds(gradeIds)` | school | 批量获取年级名称 | textbooks、lesson-preparation |
|
||
| `getQuestionCountByKpIds(kpIds)` | questions | 知识点关联题目数 | textbooks |
|
||
| `getKpMasteryByTextbookId(textbookId)` | diagnostic | 教材下知识点掌握度 | textbooks |
|
||
|
||
### 8.3 新增权限点
|
||
|
||
| 权限点 | 用途 | 角色映射 |
|
||
|---|---|---|
|
||
| `AUDIT_LOG_PURGE` | 审计日志物理删除 | admin 专属 |
|
||
| `AUDIT_RETENTION_MANAGE` | 审计保留策略配置 | admin 专属 |
|
||
|
||
---
|
||
|
||
## 九、附录
|
||
|
||
### 9.1 完整规则表
|
||
|
||
见 [data-access-audit-framework-v1.md](./data-access-audit-framework-v1.md) 第二节。
|
||
|
||
### 9.2 sub-agent 原始输出索引
|
||
|
||
| 组 | 文件 | 问题数 |
|
||
|---|---|---|
|
||
| G1 | [g1-audit-output.json](./g1-audit-output.json) | 67 |
|
||
| G2 | [g2-data-access-audit.json](./g2-data-access-audit.json) | 11 |
|
||
| G3 | [g3-audit-output.json](./g3-audit-output.json) | 50 |
|
||
| G4 | [g4-audit-output.json](./g4-audit-output.json) | 61 |
|
||
| G5 | [g5-audit-output.json](./g5-audit-output.json) | 41 |
|
||
|
||
### 9.3 架构图遗漏记录
|
||
|
||
审计过程中发现的架构图(004/005)需补记项(治理阶段统一补图):
|
||
|
||
1. **parent 模块缺失 actions.ts** —— 004 文档模块清单未标注此异常
|
||
2. **onboarding/actions.ts 直查 DB** —— 005 文档 dependencyMatrix 需修正
|
||
3. **messaging/data-access.ts 拆分后** —— 005 文档 modules.messaging.exports 需更新
|
||
4. **新增权限点 AUDIT_LOG_PURGE / AUDIT_RETENTION_MANAGE** —— 005 文档 permissions 节点需补记
|
||
5. **新增 shared/lib/date-utils.ts** —— 004/005 shared 模块清单需补记
|
||
|
||
### 9.4 治理验证检查清单
|
||
|
||
每个 Phase 完成后必须通过:
|
||
|
||
- [ ] `npm run lint` 零错误
|
||
- [ ] `npx tsc --noEmit` 零错误
|
||
- [ ] 架构文档 004/005 同步更新
|
||
- [ ] `docs/troubleshooting/known-issues.md` 追加新模式
|
||
- [ ] 受影响模块的功能测试通过
|
||
- [ ] P0/P1 问题在 issues JSON 中标记为 resolved
|