Files
NextEdu/docs/architecture/audit/data-access-audit-v1.md
SpecialX 5d9981fd7d
Some checks failed
CI / scheduled-backup (push) Has been skipped
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
docs(architecture): update impact map, data, audit reports, superpowers docs
- Update 004_architecture_impact_map.md and 005_architecture_data.json

- Add audit reports: data-access-audit-framework-v1, data-access-audit-v1-data.json,

  data-access-audit-v1, g1-g5 audit outputs

- Add superpowers plans and specs (logging-refactor, documentation-system-redesign)

- Update troubleshooting/known-issues.md
2026-07-07 16:23:35 +08:00

24 KiB
Raw Blame History

数据库访问层审计报告 v1

创建日期2026-07-07 审计范围86 个 data-access*.ts + ~30 个 actions.ts 审计方案纯深读5 个并行 sub-agent 全量扫描) 框架依据:data-access-audit-framework-v1.md 原始输出:g1-audit-output.json · g2-data-access-audit.json · g3-audit-output.json · g4-audit-output.json · g5-audit-output.json


一、执行摘要

指标 数值
审计文件总数 10186 data-access + 15 actions 辅查)
发现问题总数 230
P0 Critical 177.4%
P1 High 4820.9%
P2 Medium 10545.6%
P3 Low 6026.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+ 集中在 classes24+、questions5、textbooks3、lesson-preparation4
超长文件(>800 行) 3 messaging1089超硬限、school938、grades-analytics831
单文件导出函数 > 20 4 messaging42+、classes/data-access.ts25+、school30+、questions28、textbooks35
as 断言(非豁免) 2 classes/data-access-admin.ts、classes/data-access-teacher.tsDEFAULT_CLASS_SUBJECTS widening
any 使用 0 全部合规
console.error 调试代码 25+ school12、files12、classes3、course-plans2、audit9
N+1 循环 SQLF-01 11 跨 4 组
LIKE '%xxx%' 全表扫描 7 lesson-preparation(4)、questions(1)、textbooks(1)、classes(1)、messaging(1)
SELECT * 未指定列 35+ 跨 G116、G311、G58
无 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缺失全部、auditpurge 用读权限、auditretention 用读权限)
actions 直查 DBA-09 1 onboarding

三、P0 Critical 问题清单17 条,必须立即治理)

3.1 安全漏洞类4 条)

ID 文件 问题 修复
G4-003 audit/actions.ts L192-225 purgeAuditLogsActionAUDIT_LOG_READ(读权限)执行物理删除,权限提权漏洞 新增 AUDIT_LOG_PURGE 权限点
G4-002 parent/ 模块缺失 actions.ts3 个 app 页面直接 import data-access完全绕过 requirePermission 新建 parent/actions.ts3 个页面改调 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 新增 getUserOnboardedAtactions 改调
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 条 SQL2N+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 列表查询无 LIMITgetLessonPlansRaw、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 循环调用 getGradeNameByIdN 次 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.ts24+ 读函数G3-001 P0
  • lesson-preparation4 个G1-021
  • questions5 个G1-023
  • textbooks3 个G1-024
  • lesson-preparation-substitutes1 个G1-022

修复模式:

// 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

集中模块files9 处 try-catch 吞错误、school12 处 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 循环 SQL11 条,跨 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 MODEquestions 表已实施,参见架构图 1.1.4
  • 长期:关联表存储提取后的关系(如 lesson_plan_knowledge_point_refs

F-03 SELECT * 未指定列35+ 条 P2-P3

集中模块lesson-preparation16 处、school4 处、scheduling3 处、attendance2 处、course-plans7 处、files8 处)

F-05 无 LIMIT 大表查询18 条 P1-P2

集中模块lesson-preparation7 处、textbooks2 处、classes3 处、proctoring1 处)

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.ts3 页面直访 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 直查 DB1 条 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 重复 helper8 条 P2

helper 出现模块
serializeDate/toIso attendance、scheduling、school、course-plans
toLessonPlanStatus lesson-preparation2 文件)
isStringArray lesson-preparation2 文件)
fetchClassesWithSubjects classes2 函数 145+124 行重复)
fetchGradesWithHeads school3 函数重复)

S-06 缺 JSDoc15+ 条 P2-P3

集中模块lesson-preparationversions/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.ts3 页面改调 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 1P0 架构与性能修复M-L1-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 2P1 性能与结构优化M-L2-3 周)

任务批次 涉及 ID 工作量
2.1 LIKE 全表扫描治理 G1-006~009, G3-017, G4-010 LFULLTEXT 索引 + 查询重写)
2.2 超长文件拆分 G3-007, G2-005 Mschool 按职责拆 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-011013, G1-050053, G3-031~032, G3-041, G3-044 M

Phase 2 完成标准:所有 P1 修复,无超长文件,无 LIKE 全表扫描,无未包裹事务。

Phase 3P2 模式标准化S-M1-2 周)

任务批次 涉及 ID 工作量
3.1 cacheFn 全量补齐 G1-021~024, G3-001剩余 M
3.2 SELECT * 改显式列 G1-039049, G3-009, G3-026028, 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-034036, G1-066067, G3-046 M

Phase 3 完成标准:所有 P2 修复模式统一helper 集中到 shared/lib。

Phase 4P3 风格优化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 第二节。

9.2 sub-agent 原始输出索引

文件 问题数
G1 g1-audit-output.json 67
G2 g2-data-access-audit.json 11
G3 g3-audit-output.json 50
G4 g4-audit-output.json 61
G5 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