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)
37 KiB
37 KiB
后端模块规范核查报告 v2
核查日期:2026-06-18 核查范围:
src/modules/下所有后端.ts文件 核查依据:
.trae/rules/project_rules.md项目规则docs/standards/coding-standards.md编码规范docs/architecture/004_architecture_impact_map.md架构影响地图- Vercel React Best Practices 性能优化规则
- v1 报告
bugs/back_bug.md(对照修复状态)本报告相比 v1 的核心变化:
- 对每个问题标注 修复状态(已修复/未修复/部分修复/新问题)
- 汇总 v1→v2 的修复进度
- 列出 v2 新发现的问题
目录
一、v1→v2 修复进度总览
1.1 整体修复率
| 指标 | v1 问题数 | 已修复 | 部分修复 | 未修复 | 修复率 |
|---|---|---|---|---|---|
| 数量 | 129 | 90 | 12 | 27 | 70% 已修复 + 9% 部分修复 |
| P0 | 14 | 14 | 0 | 0 | 100% |
| P1 | 60 | 39 | 7 | 14 | 65% + 12% |
| P2 | 55 | 37 | 5 | 13 | 67% + 9% |
1.2 按模块修复率
| 模块 | v1 问题数 | 已修复 | 部分修复 | 未修复 | 修复率 |
|---|---|---|---|---|---|
| homework | 6 | 6 | 0 | 0 | 100% |
| parent | 3 | 3 | 0 | 0 | 100% |
| proctoring | 9 | 9 | 0 | 0 | 100% |
| settings | 9 | 9 | 0 | 0 | 100% |
| dashboard | 0 | - | - | - | 标杆模块 |
| grades | 8 | 7 | 1 | 0 | 88% |
| questions | 5 | 4 | 0 | 1 | 80% |
| users | 7 | 6 | 0 | 1 | 86% |
| exams | 7 | 5 | 1 | 1 | 71% |
| messaging | 7 | 5 | 0 | 2 | 71% |
| notifications | 7 | 5 | 0 | 2 | 71% |
| audit | 5 | 2 | 0 | 3 | 40% |
| textbooks | 5 | 2 | 1 | 2 | 40% |
| classes | 10 | 7 | 2 | 1 | 70% |
| announcements | 6 | 5 | 1 | 0 | 83% |
| school | 3 | 2 | 0 | 1 | 67% |
| scheduling | 6 | 5 | 1 | 0 | 83% |
| attendance | 1 | 1 | 0 | 0 | 100% |
| course-plans | 3 | 1 | 1 | 1 | 33% |
| elective | 7 | 6 | 1 | 0 | 86% |
| diagnostic | 8 | 7 | 0 | 1 | 88% |
| files | 5 | 3 | 1 | 1 | 60% |
| layout | 2 | 0 | 0 | 2 | 0% |
1.3 P0 问题修复情况(全部已修复)
| 编号 | v1 P0 问题 | 修复状态 |
|---|---|---|
| P0-1 | exams/data-access.ts persistAiGeneratedExamDraft 直写 questions 表 | ✅ 已修复:改用 createQuestionWithRelations |
| P0-2 | exams/data-access.ts getExams 等直查 classes 表 | ✅ 已修复:改用 getClassGradeIdsByClassIds |
| P0-3 | questions/schema.ts z.any() | ✅ 已修复:改为 z.unknown() |
| P0-4 | questions/actions.ts 未返回 ActionState | ✅ 已修复:包装为 ActionState |
| P0-5 | textbooks 无 Zod 验证 + 14 处 as 断言 | ⚠️ 部分修复:as 断言已清理,但 Zod 验证仍未添加 |
| P0-6 | grades N+1 查询 | ✅ 已修复:改为 inArray 批量查询 + Map 分组 |
| P0-7 | classes 跨模块直查 homework/exams 表 | ✅ 已修复:改用 homework/data-access-classes |
| P0-8 | school actions 层直接 DB 操作 | ✅ 已修复:DB 操作下沉到 data-access |
| P0-9 | proctoring actions 层直接 DB 操作 | ✅ 已修复:下沉到 data-access |
| P0-10 | messaging↔notifications 循环依赖 | ✅ 已修复:表所有权移交 notifications |
| P0-11 | notifications/in-app-channel.ts 非法 as 断言 | ✅ 已修复:新增 mapPayloadTypeToNotificationType |
| P0-12 | users 硬编码弱密码 "123456" | ✅ 已修复:改用 randomBytes 生成 |
| P0-13 | users/updateUserProfile 绕过权限 | ✅ 已修复:改用 requirePermission + Zod + ActionState |
| P0-14 | scheduling 4 个函数缺返回类型 | ✅ 已修复:已添加返回类型标注 |
二、v2 当前问题汇总
2.1 按严重程度统计(v2 当前状态)
| 严重程度 | 未修复 v1 问题 | 部分修复 v1 问题 | v2 新发现问题 | 合计 |
|---|---|---|---|---|
| P0 | 0 | 1(textbooks Zod) | 0 | 1 |
| P1 | 14 | 7 | 16 | 37 |
| P2 | 13 | 5 | 25 | 43 |
| 合计 | 27 | 12 | 41 | 80 |
2.2 按问题类别统计(v2 当前状态)
| 问题类别 | 数量 | 主要分布 |
|---|---|---|
| 架构违规 | 8 | 跨模块直查 DB(exams→school、questions→textbooks、classes→scheduling、messaging→classes、elective→school/users) |
| TS 规范 | 35 | as 断言、缺返回类型标注、隐式 any[]、非空断言 |
| Server Action 规范 | 6 | textbooks 无 Zod、notifications 无 Zod、school 用 parse 非 safeParse、course-plans 缺 revalidatePath |
| 性能 | 15 | 串行查询未并行化、循环内串行 await、未用 React.cache()、隐式 any[] |
| 代码质量 | 12 | try-catch 吞错误、重复 try/catch、死代码、重复代码 |
| 命名规范 | 3 | 布尔变量前缀、函数命名不一致 |
| 数据一致性 | 4 | elective selectCourse/dropCourse 缺事务 |
| 文件行数 | 2 | exams/ai-pipeline.ts 916 行、classes/data-access.ts 866 行 |
三、仍需优先修复的问题
3.1 P0 级别(立即修复)
P0-1:textbooks 模块仍无 Zod 验证(v1 未修复)
- 文件:
src/modules/textbooks/actions.ts - 行号:54-281(所有 Action)
- 问题:v1 报告的 P0 问题"actions.ts 完全无 Zod 验证"未修复。所有 Action 仍使用手动
if校验,textbooks 模块甚至没有 schema.ts 文件 - 修复建议:新建
textbooks/schema.ts,定义CreateTextbookSchema、UpdateTextbookSchema、CreateChapterSchema、CreateKnowledgePointSchema等 Zod schema,所有 Action 改用schema.safeParse()
3.2 P1 级别(尽快修复)
P1-1:exams/data-access.ts 直接查询 school 模块表(v1 未修复)
- 文件:
src/modules/exams/data-access.ts - 行号:2, 208-228, 519-524, 529-534
- 问题:
resolveSubjectGradeNames、getExamSubjects、getExamGrades仍直接查询subjects/grades表(school 模块) - 修复建议:在 school 模块暴露
getGradeOptions()、getSubjectNameById(id)、getGradeNameById(id)接口
P1-2:questions/data-access.ts 直接查询 textbooks 模块表(v1 未修复)
- 文件:
src/modules/questions/data-access.ts - 行号:4, 266-299
- 问题:
getKnowledgePointOptions仍直接 LEFT JOIN 查询knowledgePoints、chapters、textbooks三张表 - 修复建议:在 textbooks 模块暴露
getKnowledgePointOptionsForQuestions()接口
P1-3:classes/data-access-schedule.ts 直接查询 classSchedule 表(v1 未修复)
- 文件:
src/modules/classes/data-access-schedule.ts - 行号:7-11, 31-46, 73-86
- 问题:仍直接导入并查询
classSchedule表(scheduling 模块的表) - 修复建议:在 scheduling 模块暴露只读查询函数
getClassScheduleByClassIds
P1-4:messaging/data-access.ts getRecipients 直接 JOIN 跨模块表(v1 未修复)
- 文件:
src/modules/messaging/data-access.ts - 行号:26-27, 173-188
- 问题:
getRecipients直接 import 并 JOINclassEnrollments、classes表 - 修复建议:通过 classes 模块暴露
getStudentIdsByClassIds、getStudentIdsByGradeIds接口
P1-5:notifications/actions.ts 参数未用 Zod 验证(v1 未修复)
- 文件:
src/modules/notifications/actions.ts - 行号:28-50, 60-110
- 问题:
sendNotificationAction和sendClassNotificationAction仅使用 TypeScript 类型标注和手动 if 检查 - 修复建议:新增
NotificationPayloadSchema和ClassNotificationSchema
P1-6:textbooks/actions.ts 本地定义 ActionState 类型(v1 未修复)
- 文件:
src/modules/textbooks/actions.ts - 行号:46-50
- 问题:仍在本地定义
ActionState类型,而非从@/shared/types/action-state导入 - 修复建议:删除本地定义,改为
import type { ActionState } from "@/shared/types/action-state"
P1-7:elective/data-access.ts 跨模块直查(v2 新发现)
- 文件:
src/modules/elective/data-access.ts - 行号:77-106(
buildCourseSelect)、231-242(getSubjectOptions) - 问题:
buildCourseSelect直接leftJoin(users/subjects/grades);本地getSubjectOptions直查subjects表且与 school 模块重复 - 修复建议:移除 join,改为先查主表再用
getUserNamesByIds/getSubjectOptions/getGradeOptions批量解析;删除本地getSubjectOptions改用 school 模块
P1-8:elective selectCourse/dropCourse 缺事务(v2 新发现)
- 文件:
src/modules/elective/data-access-operations.ts - 行号:97-172(selectCourse)、174-241(dropCourse)
- 问题:FCFS 模式下 update + insert 两步无事务包裹;dropCourse 最多 5 个连续写操作无事务
- 修复建议:用
db.transaction包裹所有写操作
P1-9:classes/actions.ts as 断言(v2 新发现)
- 文件:
src/modules/classes/actions.ts - 行号:47, 521, 565
- 问题:
v as ClassSubject、weekday as 1|2|3|4|5|6|7使用 as 断言 - 修复建议:在 schema.ts 中使用 Zod transform 使解析后直接产出目标类型
P1-10:school/actions.ts 使用 .parse() 而非 .safeParse()(v2 新发现)
- 文件:
src/modules/school/actions.ts - 行号:33, 60, 98, 129, 171, 202, 256, 289
- 问题:所有 action 使用
Schema.parse()而非safeParse(),验证失败抛 ZodError,无法返回结构化 fieldErrors - 修复建议:改用
safeParse(),失败时返回{ success: false, errors: parsed.error.flatten().fieldErrors }
P1-11:多个模块函数缺返回类型标注(v2 新发现 + v1 部分未修复)
| 模块 | 文件 | 函数 |
|---|---|---|
| classes | data-access.ts:53,60,93,95,100,107 | isDuplicateInvitationCodeError, generateInvitationCode, normalizeSortText, parseFirstInt, compareGradeLabel, compareClassLike |
| school | data-access.ts:24 | toIso |
| attendance | data-access.ts:70 | resolveRecorderNames |
| exams | ai-pipeline.ts:68,177,309,453,480,499,571,712 | sanitizeJsonCandidate, normalizeScores, buildAiMessages, splitStructureItems, mapWithConcurrency, parseQuestionDetail, buildQuestionContent, previewToDraft |
| exams | data-access.ts:269 | buildOrderedQuestionsFromStructure |
| grades | data-access.ts:57, data-access-analytics.ts:34 | buildScopeClassFilter |
| textbooks | data-access.ts:19,25 | normalizeOptional, sortChapters |
P1-12:files/data-access.ts conditions 隐式 any[](v1 未修复)
- 文件:
src/modules/files/data-access.ts - 行号:201
- 问题:
const conditions = []无类型注解,推断为any[] - 修复建议:改为
const conditions: SQL[] = []
P1-13:course-plans updateCoursePlanItemAction 缺 revalidatePath(v1 部分修复)
- 文件:
src/modules/course-plans/actions.ts - 行号:197-234
- 问题:v1 指出的 deleteCoursePlanItemAction 和 toggleCoursePlanItemCompletedAction 已修复,但
updateCoursePlanItemAction仍缺revalidatePath - 修复建议:在
await updateCoursePlanItem(id, parsed.data)后添加revalidatePlanPaths()调用
3.3 P2 级别(迭代优化)
性能类
| 模块 | 文件 | 问题 |
|---|---|---|
| grades | data-access.ts:273,377,397 | 3 个查询函数未用 React.cache() |
| elective | data-access.ts:231 | getSubjectOptions 未用 cache() |
| course-plans | data-access.ts:302-307 | reorderCoursePlanItems 循环内串行 await |
| diagnostic | data-access.ts:119-140 | updateMasteryFromSubmission 循环内串行 await |
| proctoring | data-access.ts:271-287 | getStudentProctoringStatuses 串行查询未并行化 |
| diagnostic | data-access.ts:147-159 | getClassMasterySummary 串行查询未并行化 |
| grades | export.ts:129 | 循环内 find O(n) 查找,应改用 Map |
| school | data-access.ts:406-469 | isGradeHead/isGradeManager/findGradeIdByHeadAndName 未用 cache() |
代码质量类
| 模块 | 文件 | 问题 |
|---|---|---|
| school | data-access.ts:26-206 | 8 个函数 try-catch 吞错误返回空数组 |
| files | data-access.ts | 10 处静默 catch 吞错误 |
| classes | data-access.ts:371-373, data-access-admin.ts:88-89,244-247, data-access-students.ts:159-160 | 多处 try-catch 吞错误 |
| course-plans | data-access.ts:143-162,166-189,312-323 | 3 处 try-catch 吞错误 |
| announcements | data-access.ts:88-91,119-122 | catch 已加 console.error 但仍吞错误 |
| announcements | actions.ts:26-230 | 6 处重复 try/catch 块未抽取公共 helper |
| diagnostic | data-access-reports.ts:22,207-208 | void round2 死代码 |
| scheduling | data-access.ts:113-114 | select 中 requesterName 字段冗余 |
| elective | data-access.ts:46-75, data-access-selections.ts:46-74 | mapCourseRow 重复定义 |
TS 规范类
| 模块 | 文件 | 问题 |
|---|---|---|
| users | import-export.ts:14 | as 断言未加注释 |
| users | import-export.ts:98 | conditions 隐式 any[] |
| messaging | data-access.ts:84 | conds 隐式 any[] |
| audit | data-access.ts:40,96,161 | conditions 隐式 any[] |
| classes | data-access.ts:675 | 非空断言 ! |
| textbooks | data-access.ts:314 | 非空断言 stack.pop()! |
| notifications | external-sdk.d.ts | 多处 any(有 eslint-disable 注释) |
| notifications | wechat-channel.ts:106 | as 断言未加注释 |
命名规范类
| 模块 | 文件 | 问题 |
|---|---|---|
| questions | actions.ts:26 | createNestedQuestion 命名不一致 |
| settings | actions.ts:60-63 | getAiProviderSummaries 返回非 ActionState |
架构/类型位置类
| 模块 | 文件 | 问题 |
|---|---|---|
| layout | navigation.ts:30-31 | permission 字段为 string 而非 Permission 类型 |
| layout | navigation.ts:34 | Role 类型应迁移至 shared/types |
| audit | actions.ts:63-205 | Excel 导出逻辑内联在 actions 层 |
文件行数类
| 模块 | 文件 | 行数 | 建议 |
|---|---|---|---|
| exams | ai-pipeline.ts | 916 行 | 拆分为 prompts/json-parser/schemas/index |
| classes | data-access.ts | 866 行 | 拆分 enrollment 相关函数到 data-access-enrollment.ts |
数据一致性/业务逻辑类
| 模块 | 文件 | 问题 |
|---|---|---|
| elective | data-access-operations.ts:139-148 | FCFS 并发超卖风险(架构图 P2-15) |
| elective | data-access-operations.ts:49 | runLottery 使用 Math.random 不可复现(架构图 P2-14) |
| diagnostic | data-access-reports.ts:113 | 班级报告 studentId 字段复用(架构图 P2-16) |
四、按模块详细核查
4.1 exams 模块
v1 修复情况
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P0: persistAiGeneratedExamDraft 直写 questions 表 | ✅ 已修复 | 改用 createQuestionWithRelations |
| P0: getExams 等直查 classes 表 | ✅ 已修复 | 改用 getClassGradeIdsByClassIds |
| P1: 直接查询 subjects/grades 表 | ❌ 未修复 | 仍直查 school 模块表 |
| P1: actions.ts as 断言 | ✅ 已修复 | 改用 as unknown + safeParse |
| P2: import { ActionState } 未用 import type | ✅ 已修复 | 已改为 import type |
| P2: ai-pipeline.ts 912 行超长 | ❌ 未修复 | 当前 916 行 |
| P2: data-access.ts as string[] 断言 | ✅ 已修复 | 改用 getStringArray 类型守卫 |
v2 新发现问题
| 严重程度 | 文件 | 问题 |
|---|---|---|
| P2 | ai-pipeline.ts:68,177,309,453,480,499,571,712 | 8 个函数缺少显式返回类型标注 |
| P2 | data-access.ts:269 | buildOrderedQuestionsFromStructure 缺返回类型 |
4.2 homework 模块
v1 修复情况(100% 修复)
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P1: data-access.ts 直查 exams/classEnrollments/subjects/users 表 | ✅ 已修复 | 改用跨模块 data-access 接口 |
| P1: data-access-write.ts 直查 classes/classEnrollments/classSubjectTeachers/exams 表 | ✅ 已修复 | 改用跨模块接口 |
| P1: stats-service.ts 直查 classEnrollments/classes/exams/users 表 | ✅ 已修复 | 改用跨模块接口 |
| P1: data-access.ts:39 as 断言 | ✅ 已修复 | 改用 isHomeworkQuestionContent 类型守卫 |
| P2: data-access-write.ts 循环内串行 await 未用事务 | ✅ 已修复 | 已用 db.transaction 包裹 |
| P2: data-access.ts 使用 auth() 而非 getAuthContext() | ✅ 已修复 | 不再使用 auth() |
v2 无新发现问题,模块状态良好。
4.3 questions 模块
v1 修复情况
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P0: schema.ts z.any() | ✅ 已修复 | 改为 z.unknown() |
| P0: actions.ts 未返回 ActionState | ✅ 已修复 | 包装为 ActionState |
| P1: data-access.ts 直查 textbooks 模块表 | ❌ 未修复 | 仍直查 knowledgePoints/chapters/textbooks |
| P1: actions.ts import type | ✅ 已修复 | 已改为 import type |
| P2: createNestedQuestion 命名不一致 | ❌ 未修复 | 仍为 createNestedQuestion |
v2 无新发现问题。
4.4 grades 模块
v1 修复情况
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P0: N+1 查询 | ✅ 已修复 | 改为 inArray 批量查询 + Map 分组 |
| P1: 跨模块直查 | ✅ 已修复 | 改用跨模块 data-access 接口 |
| P1: 动态 import | ✅ 已修复 | 改为静态 import |
| P1: 除零 bug | ✅ 已修复 | 添加 if (fullScores[i] <= 0) continue |
| P2: 未用 React.cache() | ⚠️ 部分修复 | 4 个函数已用 cache(),3 个仍未用 |
| P2: includes O(n) 查找 | ✅ 已修复 | 改用 Set.has() |
| P2: 重复 filter | ✅ 已修复 | 改用单次 reduce |
v2 新发现问题
| 严重程度 | 文件 | 问题 |
|---|---|---|
| P2 | data-access.ts:57, data-access-analytics.ts:34 | buildScopeClassFilter 缺返回类型 |
| P2 | export.ts:129 | 循环内 find O(n) 查找,应改用 Map |
4.5 textbooks 模块
v1 修复情况
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P0: 无 Zod 验证 | ❌ 未修复 | 仍无 schema.ts,所有 Action 手动校验 |
| P0: 14 处 as 断言 | ✅ 已修复 | 已清理所有 as 断言 |
| P1: 本地定义 ActionState | ❌ 未修复 | 仍在本地定义 |
| P1: import type | ✅ 已修复 | 已改为 import type |
| P1: data-access.ts as 断言 | ✅ 已修复 | 改用 isChapterNode 类型守卫 |
| P2: 非空断言 | ⚠️ 部分修复 | 原位置已修复,但 314 行仍有 stack.pop()! |
v2 新发现问题
| 严重程度 | 文件 | 问题 |
|---|---|---|
| P2 | data-access.ts:19,25 | normalizeOptional、sortChapters 缺返回类型 |
4.6 classes 模块
v1 修复情况
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P0: actions.ts 直接 DB 操作 | ✅ 已修复 | 已下沉到 data-access |
| P0: getTeacherClasses 混入 homework/scheduling | ⚠️ 部分修复 | 架构合规,但职责仍混合 |
| P0: data-access-stats.ts 直查 homework/exams | ✅ 已修复 | 改用 homework/data-access-classes |
| P0: data-access-students.ts 直查 homework/exams | ✅ 已修复 | 同上 |
| P1: 无 schema.ts | ✅ 已修复 | 已创建 schema.ts |
| P1: as 断言 | ✅ 已修复 | 原位置已清理 |
| P1: 箭头函数缺返回类型 | ⚠️ 部分修复 | 6 个同步箭头函数仍缺 |
| P1: data-access-schedule.ts 直查 classSchedule | ❌ 未修复 | 仍直查 scheduling 模块表 |
| P2: 不可达代码 | ✅ 已修复 | 已删除 |
| P2: 串行查询未并行化 | ✅ 已修复 | 已用 Promise.all |
v2 新发现问题
| 严重程度 | 文件 | 问题 |
|---|---|---|
| P1 | actions.ts:47,521,565 | as 断言(v as ClassSubject、weekday as 1|2|...|7) |
| P2 | data-access.ts:675 | 非空断言 ! |
| P2 | data-access.ts:371-373 等 | 多处 try-catch 吞错误 |
| P2 | data-access.ts | 文件 866 行超 800 行建议上限 |
4.7 school 模块
v1 修复情况
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P0: actions 层直接 DB 操作 | ✅ 已修复 | DB 操作下沉到 data-access |
| P2: try-catch 吞错误 | ❌ 未修复 | 8 个函数仍吞错误 |
| P2: logAudit 阻塞响应 | ✅ 已修复 | 已用 after() 异步执行 |
v2 新发现问题
| 严重程度 | 文件 | 问题 |
|---|---|---|
| P1 | actions.ts:33,60,98,129,171,202,256,289 | 使用 .parse() 而非 .safeParse() |
| P1 | data-access.ts:24 | toIso 缺返回类型 |
| P2 | data-access.ts:406-469 | 3 个跨模块查询函数未用 cache() |
4.8 scheduling 模块
v1 修复情况
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P0: actions.ts 直查 users 表 | ✅ 已修复 | 改用 getUserNamesByIds |
| P0: 4 个函数缺返回类型 | ✅ 已修复 | 已添加返回类型 |
| P1: 非空断言(3 处) | ✅ 已修复 | 改用显式判空 |
| P2: auto-scheduler.ts 310 行 | ⚠️ 部分修复 | 311 行,多个函数超 40 行 |
v2 新发现问题
| 严重程度 | 文件 | 问题 |
|---|---|---|
| P2 | data-access.ts:113-114 | select 中 requesterName 字段冗余 |
| P2 | data-access.ts:135-145 | 用户查询应使用 inArray 替代 or(...map(eq)) |
4.9 attendance 模块
v1 修复情况
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P1: Record<string, unknown> 丢失类型安全 | ✅ 已修复 | 改用 Partial<typeof attendanceRecords.$inferSelect> |
v2 新发现问题
| 严重程度 | 文件 | 问题 |
|---|---|---|
| P1 | data-access.ts:70 | resolveRecorderNames 缺返回类型 |
4.10 course-plans 模块
v1 修复情况
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P1: 缺 revalidatePath | ⚠️ 部分修复 | delete/toggle 已修复,update 仍缺 |
| P1: as 断言 | ✅ 已修复 | 已清理 |
| P2: 循环内串行 await | ❌ 未修复 | 仍串行 |
v2 新发现问题
| 严重程度 | 文件 | 问题 |
|---|---|---|
| P2 | data-access.ts:143-162,166-189,312-323 | 3 处 try-catch 吞错误 |
4.11 users 模块
v1 修复情况
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P0: updateUserProfile 绕过权限 | ✅ 已修复 | 改用 requirePermission + Zod + ActionState |
| P0: 硬编码弱密码 | ✅ 已修复 | 改用 randomBytes 生成 |
| P1: actions 层直接 DB 操作 | ✅ 已修复 | 下沉到 data-access |
| P1: batchImportUsers 无事务 | ✅ 已修复 | 每个用户创建包裹在 db.transaction |
| P2: rolePriority 命名 | ✅ 已修复 | 已移除,改用 resolvePrimaryRole |
| P2: normalizeRoleName 重复 | ✅ 已修复 | 改用 shared |
| P2: conditions 隐式 any[] | ❌ 未修复 | 仍为 const conditions = [] |
v2 新发现问题
| 严重程度 | 文件 | 问题 |
|---|---|---|
| P2 | import-export.ts:14 | as 断言未加注释 |
4.12 messaging 模块
v1 修复情况
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P0: 循环依赖 | ✅ 已修复 | 表所有权移交 notifications |
| P1: 5 个 Action 用 requireAuth | ✅ 已修复 | 改用 requirePermission |
| P1: getRecipients 直查跨模块表 | ❌ 未修复 | 仍 JOIN classEnrollments/classes |
| P1: 无 Zod 验证 | ✅ 已修复 | 已用 UpdateNotificationPreferencesSchema |
| P2: 非空断言 | ✅ 已修复 | 改用 ?? null |
| P2: 缺返回类型 | ✅ 已修复 | 已添加 |
v2 新发现问题
| 严重程度 | 文件 | 问题 |
|---|---|---|
| P2 | data-access.ts:84 | conds 隐式 any[] |
4.13 notifications 模块
v1 修复情况
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P0: 反向依赖 messaging | ✅ 已修复 | 表所有权归 notifications |
| P0: in-app-channel 动态 import messaging | ✅ 已修复 | 改为静态 import |
| P0: 非法 as 断言 | ✅ 已修复 | 新增 mapPayloadTypeToNotificationType |
| P1: actions.ts 直查 classes 表 | ✅ 已修复 | 改用 classes data-access 函数 |
| P1: 参数无 Zod 验证 | ❌ 未修复 | 仍用手动 if 检查 |
| P2: 缺返回类型 | ✅ 已修复 | 已添加 |
| P2: external-sdk.d.ts any | ❌ 未修复 | 有 eslint-disable 注释 |
v2 新发现问题
| 严重程度 | 文件 | 问题 |
|---|---|---|
| P2 | wechat-channel.ts:106 | as 断言未加注释 |
4.14 parent 模块
v1 修复情况(100% 修复)
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P1: getChildBasicInfo 直查跨模块表 | ✅ 已修复 | 改用各模块 data-access 函数 |
| P2: as 断言 | ✅ 已修复 | 改用 isWeekday 类型守卫 |
| P2: 串行查询 | ✅ 已修复 | 改用 Promise.all |
v2 无新发现问题,模块状态良好,是跨模块通信的标杆实现。
4.15 audit 模块
v1 修复情况
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P1: 导出函数数据截断 | ✅ 已修复 | 改用分页循环拉取全部数据 |
| P2: as 断言 | ✅ 已修复 | 已清理 |
| P2: Excel 导出逻辑内联 | ❌ 未修复 | 仍内联在 actions |
| P2: conditions 隐式 any[] | ❌ 未修复 | 3 处仍为 const conditions = [] |
4.16 elective 模块
v1 修复情况
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P1: data-access-selections.ts 直查 classes 表 | ✅ 已修复 | 改用跨模块接口 |
| P1: runLottery 无事务 | ✅ 已修复 | 已用 db.transaction |
| P1: 循环内逐条 await | ✅ 已修复 | 改用 inArray 批量更新 |
| P1: as 断言 | ✅ 已修复 | 已清理 |
| P2: 串行查询未并行化 | ✅ 已修复 | 改用 Promise.all |
| P2: 未用 React.cache() | ⚠️ 部分修复 | 大部分已用,getSubjectOptions 仍未用 |
v2 新发现问题
| 严重程度 | 文件 | 问题 |
|---|---|---|
| P1 | data-access.ts:77-106 | buildCourseSelect 跨模块 join users/subjects/grades |
| P1 | data-access.ts:231-242 | 本地 getSubjectOptions 直查 subjects 表且与 school 重复 |
| P1 | data-access-operations.ts:97-172 | selectCourse 缺事务包裹 |
| P1 | data-access-operations.ts:174-241 | dropCourse 缺事务包裹 |
| P2 | data-access-operations.ts:139-148 | FCFS 并发超卖风险 |
| P2 | data-access-operations.ts:49 | runLottery 使用 Math.random 不可复现 |
| P2 | data-access.ts:46-75, data-access-selections.ts:46-74 | mapCourseRow 重复定义 |
4.17 proctoring 模块
v1 修复情况(100% 修复)
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P0: actions.ts 直接 DB 操作 | ✅ 已修复 | 下沉到 data-access |
| P1: import type | ✅ 已修复 | 已改为 import type |
| P1: as 断言 | ✅ 已修复 | 改用类型守卫 |
| P1: requireAuth | ✅ 已修复 | 改用 requirePermission |
| P1: 直查 exams/examSubmissions 表 | ✅ 已修复 | 改用跨模块函数 |
| P1: 多处 as 断言 | ✅ 已修复 | 改用 toExamMode/isSubmissionStatus |
| P2: 未调用 revalidatePath | ✅ 已修复 | 已添加 |
| P2: 串行查询 | ✅ 已修复 | 改用 Promise.all |
| P2: 重复 filter | ✅ 已修复 | 改用单次循环 |
v2 新发现问题
| 严重程度 | 文件 | 问题 |
|---|---|---|
| P2 | data-access.ts:271-287 | getStudentProctoringStatuses 串行查询未并行化 |
4.18 diagnostic 模块
v1 修复情况
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P1: 4 个 Action 无 Zod | ✅ 已修复 | 新增 schema.ts,6 个 Action 全用 Zod |
| P1: 直查跨模块表 | ✅ 已修复 | 改用跨模块 data-access |
| P1: as 断言 | ✅ 已修复 | 改用 isStringArray 类型守卫 |
| P2: 循环内 find | ✅ 已修复 | 改用 Map |
| P2: 循环内串行 await | ❌ 未修复 | updateMasteryFromSubmission 仍串行 |
| P2: 重复 filter | ✅ 已修复 | 改用单次循环 |
| P2: 动态 import | ✅ 已修复 | 改为静态 import |
| P2: void round2 死代码 | ❌ 未修复 | 仍存在 |
| P2: 未用 React.cache() | ✅ 已修复 | 全部用 cache() 包装 |
v2 新发现问题
| 严重程度 | 文件 | 问题 |
|---|---|---|
| P2 | data-access.ts:147-159 | getClassMasterySummary 串行查询未并行化 |
4.19 dashboard 模块
v1 无违规问题,v2 仍为标杆模块。 正确使用 Promise.all 并行获取多模块数据,正确使用 cache(),正确通过各模块 data-access 通信。
4.20 files 模块
v1 修复情况
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P1: conditions 隐式 any[] | ❌ 未修复 | 仍为 const conditions = [] |
| P1: or(...)! 非空断言 | ✅ 已修复 | 改用显式判断 |
| P2: 循环内串行 await | ⚠️ 部分修复 | 主路径已批量删除,catch 回退仍串行 |
| P2: 9 处静默 catch | ❌ 未修复 | 实际 10 处 |
| P2: 未用 React.cache() | ✅ 已修复 | 全部用 cache() 包装 |
4.21 announcements 模块
v1 修复情况
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P1: as string 断言 | ✅ 已修复 | 新增 toIso/toIsoRequired 工具函数 |
| P2: 冗余 as 断言 | ✅ 已修复 | 已清理 |
| P2: catch 吞错误 | ⚠️ 部分修复 | 已加 console.error 但仍吞错误 |
| P2: 类型重复定义 | ✅ 已修复 | 已修复 |
| P2: requireAuth | ✅ 已修复 | 改用 requirePermission |
| P2: 重复 try/catch | ❌ 未修复 | 6 处仍重复 |
4.22 settings 模块
v1 修复情况(100% 修复)
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P1: 无 data-access.ts | ✅ 已修复 | 新建 data-access.ts |
| P1: actions-password.ts 无 data-access | ✅ 已修复 | DB 操作下沉 |
| P1: 无 Zod 验证 | ✅ 已修复 | 新增 ChangePasswordSchema |
| P2: 类型定义位置 | ✅ 已修复 | 新建 types.ts |
| P2: 缺返回类型 | ✅ 已修复 | 已添加 |
| P2: 串行查询 | ✅ 已修复 | 改用 Promise.all |
| P2: 布尔命名 | ✅ 已修复 | 改为 hasDefault/isNextDefault/shouldMakeDefault |
| P2: requireAuth | ✅ 已修复 | 改用 requirePermission |
| P2: 串行查询 | ✅ 已修复 | 改用 Promise.all |
v2 新发现问题
| 严重程度 | 文件 | 问题 |
|---|---|---|
| P2 | actions.ts:60-63 | getAiProviderSummaries 返回非 ActionState |
4.23 layout 模块
v1 修复情况(0% 修复)
| v1 问题 | 修复状态 | 说明 |
|---|---|---|
| P2: permission 字段为 string | ❌ 未修复 | 仍为 string |
| P2: Role 类型位置 | ❌ 未修复 | 仍在 navigation.ts |
五、v2 新发现问题清单
5.1 P1 级别新问题(16 个)
| 编号 | 模块 | 文件 | 问题 |
|---|---|---|---|
| N1 | elective | data-access.ts:77-106 | buildCourseSelect 跨模块 join users/subjects/grades |
| N2 | elective | data-access.ts:231-242 | 本地 getSubjectOptions 直查 subjects 表且与 school 重复 |
| N3 | elective | data-access-operations.ts:97-172 | selectCourse 缺事务包裹 |
| N4 | elective | data-access-operations.ts:174-241 | dropCourse 缺事务包裹 |
| N5 | classes | actions.ts:47,521,565 | as 断言(v as ClassSubject、weekday as 1|2|...|7) |
| N6 | school | actions.ts:33 等 | 使用 .parse() 而非 .safeParse() |
| N7 | school | data-access.ts:24 | toIso 缺返回类型 |
| N8 | attendance | data-access.ts:70 | resolveRecorderNames 缺返回类型 |
| N9 | exams | ai-pipeline.ts | 8 个函数缺返回类型 |
| N10 | exams | data-access.ts:269 | buildOrderedQuestionsFromStructure 缺返回类型 |
| N11 | grades | data-access.ts:57, data-access-analytics.ts:34 | buildScopeClassFilter 缺返回类型 |
| N12 | textbooks | data-access.ts:19,25 | normalizeOptional、sortChapters 缺返回类型 |
| N13 | settings | actions.ts:60-63 | getAiProviderSummaries 返回非 ActionState |
| N14 | messaging | data-access.ts:84 | conds 隐式 any[] |
| N15 | users | import-export.ts:14 | as 断言未加注释 |
| N16 | notifications | wechat-channel.ts:106 | as 断言未加注释 |
5.2 P2 级别新问题(25 个)
| 编号 | 模块 | 文件 | 问题 |
|---|---|---|---|
| N17 | classes | data-access.ts:675 | 非空断言 ! |
| N18 | classes | data-access.ts:371-373 等 | 多处 try-catch 吞错误 |
| N19 | classes | data-access.ts | 文件 866 行超 800 行建议上限 |
| N20 | school | data-access.ts:406-469 | 3 个跨模块查询函数未用 cache() |
| N21 | scheduling | data-access.ts:113-114 | select 中 requesterName 字段冗余 |
| N22 | scheduling | data-access.ts:135-145 | 用户查询应使用 inArray |
| N23 | course-plans | data-access.ts:143-162 等 | 3 处 try-catch 吞错误 |
| N24 | grades | export.ts:129 | 循环内 find O(n) 查找 |
| N25 | proctoring | data-access.ts:271-287 | getStudentProctoringStatuses 串行查询 |
| N26 | diagnostic | data-access.ts:147-159 | getClassMasterySummary 串行查询 |
| N27 | elective | data-access-operations.ts:139-148 | FCFS 并发超卖风险 |
| N28 | elective | data-access-operations.ts:49 | runLottery 使用 Math.random |
| N29 | elective | data-access.ts:46-75 等 | mapCourseRow 重复定义 |
| N30 | users | import-export.ts:98 | conditions 隐式 any[] |
| N31 | audit | data-access.ts:40,96,161 | conditions 隐式 any[] |
六、架构文档同步提醒
根据项目规则"改码必同步图",以下架构图信息需更新:
6.1 需更新的架构文档
| 文档 | 需更新内容 |
|---|---|
docs/architecture/004_architecture_impact_map.md |
1. exams/actions.ts 行数(v1 记录 691,实际 771) 2. exams/ai-pipeline.ts 行数(v1 记录 857,实际 916) 3. settings 导出函数列表(v1 记录 getAiProvidersAction 等,实际为 getAiProviderSummaries/upsertAiProviderAction/testAiProviderAction) 4. P2-11 死代码 void wasPublished 状态(已修复) 5. announcements 依赖 school 模块(仅 components,非后端) 6. elective 依赖关系需补充 classes/school/users 7. messaging↔notifications 循环依赖已解决 8. classes 跨模块直查 homework/exams 已解决 |
6.2 需同步的代码变更
本轮修复涉及大量模块结构调整,必须同步更新 004 和 005 架构文档:
- 新增模块文件:classes/schema.ts、settings/data-access.ts、settings/types.ts、diagnostic/schema.ts
- 新增跨模块接口:classes 暴露 getClassGradeIdsByClassIds、getStudentIdsByClassId 等;exams 暴露 getExamIdsByGradeIds、getExamWithQuestionsForHomework 等;users 暴露 getUserNamesByIds、getUserBasicInfo 等;school 暴露 getSubjectOptions、getGradeOptions 等
- 表所有权迁移:messageNotifications、notificationPreferences 表所有权从 messaging 移交至 notifications
- 权限点新增:USER_PROFILE_UPDATE、PASSWORD_SELF_CHANGE 等
七、总体评价与建议
7.1 修复成效
本次 v2 核查显示,项目在 v1 报告后进行了大规模且有成效的修复:
- 所有 P0 问题已全部修复(14/14):包括跨模块直写 DB、循环依赖、硬编码弱密码、N+1 查询等高危问题
- P1 问题修复率 65%:剩余 14 个未修复 + 7 个部分修复
- 4 个模块达到 100% 修复率:homework、parent、proctoring、settings
- 架构层面显著改善:
- messaging↔notifications 循环依赖彻底消除
- 跨模块直查 DB 大幅减少(exams、homework、grades、classes、proctoring、diagnostic 等模块已改用 data-access 接口)
- parent 模块成为跨模块通信的标杆实现
7.2 仍需改进的领域
- textbooks 模块:P0 问题(无 Zod 验证)仍未修复,是所有模块中唯一未实现输入验证的 Server Action 文件
- 跨模块直查残留:exams→school、questions→textbooks、classes→scheduling、messaging→classes 仍存在直查
- 函数返回类型标注:多个模块仍存在箭头函数缺返回类型的问题(classes、school、attendance、exams、grades、textbooks)
- 隐式 any[]:
const conditions = []在 users、messaging、audit、files 等模块普遍存在 - 错误处理:try-catch 吞错误在 school、files、classes、course-plans 等模块仍普遍存在
- elective 模块:v2 新发现 selectCourse/dropCourse 缺事务、data-access.ts 跨模块直查等问题
7.3 下一阶段优先修复建议
第一优先级(P0/P1 核心问题):
- textbooks 模块新建 schema.ts,所有 Action 改用 Zod safeParse
- textbooks/actions.ts 改用共享 ActionState 类型
- exams、questions、classes、messaging 模块消除剩余跨模块直查
- elective selectCourse/dropCourse 加事务包裹
- school/actions.ts 改用 safeParse
- 补齐所有函数返回类型标注
第二优先级(P2 系统性优化):
- 全项目统一修复
const conditions = []隐式 any[](改为SQL[]) - 清理 try-catch 吞错误(至少加 console.error 或向上抛出)
- 补齐 React.cache() 包装
- 串行查询改用 Promise.all
- 同步架构文档
第三优先级(代码质量):
- 抽取重复代码(mapCourseRow、handleActionError、buildExcelSheet 等)
- 清理死代码(void round2 等)
- 拆分超长文件(ai-pipeline.ts、classes/data-access.ts)
- layout 模块类型规范修复