Files
NextEdu/docs/troubleshooting/known-issues.md
SpecialX e3d132dc1b
Some checks failed
CI / scheduled-backup (push) Failing after 1m32s
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
Lighthouse CI / lighthouse (push) Has been cancelled
docs(known-issues): log enterprise architecture normalization work experience
2026-07-07 20:25:33 +08:00

38 KiB
Raw Blame History

已知问题速查

索引式速查手册:场景 → 技术/规则映射 架构规则见 ../architecture/004_architecture_impact_map.md.trae/rules/project_rules.md 原始版本归档于 ../architecture/audit/archive/known-issues_v1.md


一、全局经验

1.1 i18n

场景 规则
key 命名 禁止包含 .,不同字段用驼峰,同字段多角色变体用嵌套对象
动态 key 调用 t() 参数为动态变量时必须包含完整嵌套路径(t(\type.${type}`)而非t(type)`
ICU 参数 单花括号 {level},多参数直接并列 {count} 题 · {subject}
Namespace 与文件名一致性 namespace 必须与文件名严格一致
翻译文件对称性 zh-CN 与 en 必须同步新增/删除 key
硬编码中文 所有用户可见文本必须走 i18nAI Prompt 中文常量允许)
i18n dead 节清理 删除组件时同步清理对应的 i18n key
动态键翻译守卫 缺失 key 时降级返回 key 本身而非抛错

1.2 Next.js 配置与运行时

场景 技术/规则
Node.js 服务端驱动 serverExternalPackages: ["mysql2"] 标记为外部包
输出模式 output: "standalone"
Tailwind v4 排除非源码 globals.css@source not "../../docs"; 防止 docs 字符串被扫描成类
可选依赖 动态 import + /* webpackIgnore: true */(如 @upstash/redis
环境变量驱动加载 if (env.DRIVER === "redis") { const { Redis } = await import(...) }
next-auth useSession SSR 客户端用 useSession() 时需 SessionProvider + 等待 status === "authenticated"
useSearchParams Hydration 包裹 <Suspense> 或在 useEffect 中读取
Edge Runtime 限制 proxy.ts 不能导入 Node.js 模块,用 Web Crypto API
React StrictMode 与 Server Action StrictMode 双调用不影响 Server Actionidempotent 设计)

1.3 Server/Client Component 边界

场景 规则
"use server" 文件 只能导出 async function;常量放 types.ts/constants.ts;同步函数放 lib/;禁止值 re-export类型 re-export 允许
Client Component 依赖隔离 不得 import server-only 文件;import { db } 的文件不得被客户端 import
Server Component 传递函数 禁止向 Client Component 传递函数,改传纯数据 + 在客户端调用 Server Action
Server Component 传递纯数据 序列化日期用 ISO string不用 Date 对象
useState SSR/CSR 一致性 初始值必须可序列化且在服务端和客户端一致

1.4 数据库与 Drizzle

场景 规则
schema.ts 修改后 必须 npm run db:pushnpm run db:migrate 同步数据库
Unknown column 报错 SHOW COLUMNS FROM 表名 对比实际结构与 schema.ts
迁移文件管理 必须录入 _journal.json;编号唯一不可复用;__drizzle_migrations 表必须记录 hash
手动补迁移流程 创建 .sql → 更新 journal → 计算 sha256 插入 migrations 表 → 验证 No new migrations to apply
字段映射 TS 属性名与 DB 列名不同时,验证 SQL 用 DB 列名ORM 查询用 TS 属性名
子查询字段 alias Drizzle 子查询字段必须显式 alias否则类型推断失败
FULLTEXT 索引声明 drizzle-kit 无法生成,迁移文件手动追加 CREATE FULLTEXT INDEX + 同步 snapshot
MATCH AGAINST BOOLEAN MODE 转义特殊字符 + - < > ( ) ~ * " ',转 +word1* +word2* 格式

1.5 状态管理

场景 技术
5 层状态模型 L1 URL(nuqs) · L2 Server(TanStack Query) · L3 Client(Zustand slice) · L4 Global UI(Zustand ui-store + ModalRoot) · L5 Form(react-hook-form + zodResolver)
Zustand selector 单字段 selector 优于 useShallow 多字段包装
useOptimistic 乐观更新(消息星标、成绩编辑、考勤提交),addOptimistic 必须在 transition 或 action 内调用
useOptimistic 不适用场景 需要服务端返回判断成功的操作(如撤回校验时间窗口)
React 19 useTransition 配合 useOptimistic 自动管理乐观状态与回滚

1.6 性能优化

场景 技术
批量 INSERT db.insert().values([...]) 单次写入
批量 UPDATE sql.join + CASE WHEN 单次执行
递归删除 批量收集后代 ID + inArray 单次删除,禁止递归逐个
动态导入重 UI 库 xxx-inner.tsx + xxx.tsx lazy wrappertiptap / @xyflow/react / AI SDK
ReactFlowProvider 留在外层同步渲染,保证 lazy 子组件 hook 可用
请求级去重 React cache() 包装auth 相关 4 函数)
跨请求缓存 不用 unstable_cache(权限数据易变)
高 LIMIT 调低 非分页场景默认 LIMIT ≤ 100
内存 filter 改 SQL inArray(field, ids) 下推
统计走 SQL 聚合 COUNT(*)/SUM(CASE WHEN ...) 替代拉全表内存循环
chart props 模块级常量 12 个 recharts 图表提取 CHART_MARGIN
高频子组件 React.memo AssignmentCard / StatusBadge / EmptyState
虚拟化大表 @tanstack/react-virtual

1.7 设计令牌

场景 规则
禁止硬编码颜色 TSX/TS/CSS 中不得出现 #hex,用 hsl(var(--*)) 或 Tailwind 类
禁止硬编码字体 不得出现 'Inter'/'Fraunces'/'JetBrains Mono' 字面量
禁止硬编码字号 不得出现 font-size: Npx,用 var(--font-size-1~9)
禁止 Tailwind 任意值 不得使用 w-[Npx]/h-[Npx],映射到 --space-*
豁免场景 PWA manifest、邮件 HTML 内联样式、图表 SVG 固定画布、loading.tsx 骨架
豁免注释格式 // eslint-disable-next-line no-restricted-syntax -- <reason>
白名单文件 primitive.cssemail-channel.tsmanifest.ts

1.8 TypeScript

场景 规则
禁止 any 未知类型用 unknown + 类型守卫
禁止 as 断言 除非从 unknown 转换或测试中(需注释原因)
类型守卫优先 JSON.parse 结果经 unknown 中转 + 守卫localStorage/URL param/HTTP 响应需守卫
模块私有类型守卫 lib/type-guards.ts,导出 isX / toX
函数返回值 必须显式标注,特别是 Promise<T>
仅类型导入 import type
可选链后禁止 ! a?.b! 禁止
非空断言 ! 数组取值后判空再访问;split 结果用 ?? 兜底
空对象赋值 显式标注 const empty: Record<string, string> = {}

1.9 API 路由

场景 规则
响应信封 { success, message?, errorCode?, data? }ActionState<T> 对齐
错误→状态码 PermissionDeniedError→403 / NotFoundError→404 / ValidationError→400 / BusinessError→400 / 其他→500
统一异常处理 withApiErrorHandler(handler) HOF 消除重复 try/catch
Action 转 API apiFromAction(result, options?) 通过 errorCode 驱动状态码
SSE 头 Content-Type: text/event-stream + Cache-Control: no-cache, no-transform + Connection: keep-alive
SSE 错误格式 { type: "error", message }
SSE 结束信号 [DONE]
JSDoc 注释 禁止 **/ 序列(破坏注释解析)
Permissions 类型 Permissions 是常量对象,Permission 是联合类型,不可混用

1.10 日志系统

场景 规则
服务端日志 createModuleLogger("module-name") + pino禁止 console.*
日志参数顺序 log.error({ err: error, userId }, "操作描述")
错误对象字段名 err(不是 error
模块级 logger 命名 kebab-caseaudit / exams / client-error
ESLint no-console 服务端强制,客户端 track-event 改为 no-op
客户端错误上报 通过 track-event 上报,不导入服务端 logger
Request ID proxy.ts 注入 + withRequestContext 包装 Server Action + getRequestContext().requestId 读取

1.11 测试

场景 规则
Vitest + Next.js 16 缓存测试 server-only alias 到空 stubcache() 包装函数测试需 mock
Playwright 检测 检查 HTTP 状态码 + pageerror循环外注册一次+ 可见错误元素,不检查 HTML content
testTimeout ts-morph 解析慢arch-scan 测试设置 60000ms

1.12 ESLint 与验证命令

场景 命令/规则
类型检查 npx tsc --noEmit
Lint npm run lint
开发服务器 npm run dev
生产构建 npm run build
同步 schema npm run db:push
执行迁移 npm run db:migrate
验证 schema 一致性 node scripts/verify-modules-schema.mjs
检查 i18n key 含 . Grep ^\s*"[a-zA-Z_]+\.[a-zA-Z_\.]+"\s*: glob **/messages/**/*.json
arch.db 扫描 npm run arch:scan
arch.db 查询 npm run arch:query -- <command>
ESLint 零警告 所有修改文件必须 0 errors 0 warnings
自定义规则加载 Windows 路径用 path.join,不用 path.posix.join

二、模块经验

2.1 lesson-preparation备课

场景 规则
Zustand store 拆 slice mutation 方法包装 historypushHistory + setupdateNodePosition 不推 historyhydrate/replaceDoc 清空历史history 栈上限 MAX_HISTORY
撤销/重做快捷键 metaKey || ctrlKey 区分 MacShift+Z 或 Y 重做;preventDefault
自动保存失败 UI toast.error + 断网时不触发 + 监听 online/offline + saveError 显示重试按钮
发布前预览 3 步 select → preview → confirm 状态机;总分 useMemo确认页警告文案
Tiptap Image 扩展 inline: false, allowBase64: falseeditor.commands.setImage;不自建上传组件
Tiptap SSR immediatelyRender: false 避免 hydration mismatch
附件表 Date 转换 data-access 返回 Dateservice 转换为 ISO string
打印视图 print:hidden / print:static / break-inside-avoid / print:fixed
素材库 picker 复用 use-file-upload + 通过 service 调用 + FileTargetType 扩展
Server Action form action 适配 duplicateLessonPlanFormAction(formData) 内部调用 + redirect()
知识点按需加载 对话框 Tab 内 useEffect 调用,不首屏 Promise.all
AI 模块纯服务端 import "server-only" + Zod 验证 + 失败降级返回空
版本对比/一致性校验 纯函数 diffDocuments / checkConsistency / computeCurriculumCoverage
移动端切换 useMediaQuery 在 readonly-view避免服务端 useState 默认值不一致
类型扩展 新增可选字段 stage? / differentiation?,不破坏旧数据
chapterId null kp.chapterId ?? nullstring | undefinedstring | null
React Flow 移除 改为三列布局sidebar + paper area + detail panel纸感界面
师生互动节点 渲染为对话 + '师'/'生' 角色标签 + 虚线分隔
节点展开/收起 左侧栏、右侧面板、右键菜单三处入口一致
右键菜单 节点操作(展开/收起、移动、复制、删除)+ AI 辅助(生成内容、优化表达、差异化建议、分层提问)
排版差异 Fraunces 16px 主文(近黑)+ Inter 13.5px 节点(暖灰)+ 左 2px 细线

2.2 exams / homework / grades

场景 规则
AI 出题 exams/ai-pipeline.ts + createAiChatCompletion + JSON 解析 + Zod 校验
作业化考试 sourceExamId 外键关联;homeworkSubmissions + homeworkAnswers
批改事务 db.transaction 包裹多步写
成绩批量更新 useOptimistic<GradeRecordListItem[], GradeRecordListItem>
FULLTEXT 检索 questions 表 content_text STORED 生成列 + MATCH AGAINST BOOLEAN MODE

2.3 messaging / notifications

场景 规则
消息星标乐观更新 useOptimistic<boolean> + useTransition + addOptimisticStarred
消息列表星标 Map useOptimistic<Map<string, boolean>>key=messageId
撤回不乐观更新 需服务端返回判断 2 分钟窗口
通知批量化 createNotifications(items) 单次 INSERTin_app 批量 + 其他渠道并行
通知偏好 已从 messaging 迁移至 notifications
fan-out 分页 getAllUserIds(limit=1000, offset) 分页遍历

2.4 attendance

场景 规则
考勤提交乐观更新 useOptimistic<boolean> 保留 form action 模式(useFormStatus 依赖 form action promise
不用 useTransition 包裹 会导致 form action 立即返回、useFormStatus 不可靠

2.5 school / classes / scheduling

场景 规则
school data-access 拆分 data-access-{departments,grades,subjects,classrooms,semesters,schools}.ts + barrel
角色判断迁出 data-access lib/school-permissions.ts 导出 getSchoolsForUser(userId)
promoteGrades 事务 db.transaction 包裹 select + update 循环
classSchedule 写入口 统一到 scheduling 模块
跨模块批量接口 getGradeNamesByIds(gradeIds): Promise<Map<string,string>>
A-06 JOIN 改批量查询 移除 .leftJoin(textbooks, ...),改 getTextbookTitlesByIds(ids) + Map 合并
单条查询用单 ID 接口 getClassNameById(classId) 替代 db.select().from(classes)

2.6 audit / rbac

场景 规则
破坏性操作用专属权限 AUDIT_LOG_PURGEAUDIT_LOG_READ
权限点注册全链路 permissions.tsROLE_PERMISSIONS_SEEDpermission-bitmap.tspermission-catalog.ts → i18n rbac.json 五处同步
审计日志保留管理 AUDIT_RETENTION_MANAGE 权限点

2.7 parent

场景 规则
parent actions.ts app/ 必须调用 @/modules/parent/actions,不直接 import data-access
路由权限校验 同时校验 parentId + studentId 防止信息泄露
dashboard 数据获取 Promise.all / Promise.allSettled 并行
dashboard 组件 用 Server Component除非需要 useMemo

2.8 shared

场景 规则
session.ts 单一入口 shared/lib/* 不再静态 import @/auth
dynamic import 打破循环 session.ts 内部 dynamic import("@/auth")
重复 helper 提取 serializeDate / downloadBase64File / getInitials / isRecord / extractQuestionPreview
ROLE_NAMES 常量 替代 DB 查询中硬编码角色名
api-response 工具 apiSuccess / apiError / apiFromAction / withApiErrorHandler
sse 工具 formatSseEvent / createSseResponse / createSseError

三、工作经验日志

按时间倒序50 条上限。AI 发现更好方案时可更新本节。

2026-07-07

时间 模块 经验
22:30 全局 企业级架构规范化 P2 完成CI 新增 Unit test 阶段;创建 /api/health 端点 + Dockerfile HEALTHCHECK安装 @next/bundle-analyzernpm run analyze 触发noUncheckedIndexedAccess 评估 419 错误,推迟到独立迭代(记入 tech-debt TD-P1-001
22:10 全局 企业级架构规范化 P1 完成:创建 LICENSE专有协议/CHANGELOG/CONTRIBUTING/SECURITY 大仓文档;安装 husky + lint-staged + commitlintpre-commit 运行 eslint+prettiercommit-msg 校验 conventional commits 格式;填充 tech-debt.mdP0/P1/P2 分级 + 已解决项)
21:50 全局 企业级架构规范化 P0 完成arch:scan @public JSDoc 标记豁免机制12 个登录前/公开/内部工具 Action递归 CTE 识别间接调用链requireAiPermission 等辅助函数);修复 9 个真违规parent 5 个 requireAuth→requirePermission + settings 1 个显式校验 + 3 个 @public 豁免);拆分 5 个超长文件schema.ts 2245→29+27子文件、invalidation-map.ts 1195→50+6子文件、messaging/actions.ts 973→47+5子文件、textbooks/data-access.ts 907→15+6子文件、questions/data-access.ts 828→48+4子文件违规数 32→0
21:30 全局 004 架构文档 V3 重构:从"影响地图"升级为"架构设计文档"14 章节 912 行。核心 mermaid 图:系统边界图 + 三层架构图 + 模块依赖全景图35 模块分层)+ 认证 sequenceDiagram + 权限系统架构图 + 数据访问分层图 + 5 层状态模型图 + 跨模块协作 sequenceDiagram + 考试全生命周期 sequenceDiagram跨 4 模块)+ 令牌分层架构图 + API 路由分类图 + 错误→状态码映射图 + userId/permission 影响链。模块细节移至各模块 README004 聚焦架构层面:技术栈选型/分层规则/依赖深度统计/循环依赖处理/权限 3 道防线/缓存策略/状态层选用规则/聚合层模式/事件驱动闭环/硬性约束/性能原则/安全原则/架构决策记录
20:56 全模块 35 个模块 README 重写为完整架构文档:每个含 mermaid 架构图graph TB 分层)+ 2-3 个 流程图sequenceDiagram/flowchart+ 技术栈表格 + 文件结构(行数来自 arch.db+ 依赖关系双向表 + 关键约束 + 架构决策表。标杆 textbooks/README.md 251 行。arch:query 提供真实数据(模块统计/文件清单/依赖关系/技术标签)。并行子代理执行时 arch:scan 可能因竞争报 FOREIGN KEY 错误,串行执行正常
12:56 onboarding/parent/messaging data-access 审计 Phase 0 P0 修复完成6 项onboarding 加 server-only + DB 操作下沉parent 新建 actions.ts6 个 Server Actionmessaging data-access.ts1089 行)拆分为 5 子文件 + barrel
12:27 全局 data-access 审计 v1 完成86 文件 ~30 actions5 维度 4 严重级别,生成 230 issues17 P0/48 P1/105 P2/60 P35 子代理并行审查
10:45 全局 cacheFn 迁移审查完成302 cacheFn 块全部正确放置Raw + Wrapper 配对9 处 POS-ERR 为 JSDoc/}; 结尾的误报
09:49 全局 V5 状态管理重构 Phase 4-5 完成5 层状态模型URL/Server/Client/UI/Form+ notify 替换规则 + ModalRoot 命令式 registry
08:13 lesson-preparation/messaging as 断言违规修复11 个 isXxxBlockData 类型守卫 + BLOCK_DATA_GUARDS registry替换 14 处 as 断言

2026-07-06

时间 模块 经验
23:50 全局 文档体系重设计计划制定arch.db 扫描器 + 004 瘦身 + 005 废弃 + known-issues 精简 + 35 模块 README

2026-07-05

时间 模块 经验
全天 全局 V5 状态管理重构 Phase 1-3 完成notify 替换为 zustand + ModalRootuseDialogState 评估结论(不采用)
全天 全局 性能预算重构Phase 1 配置基线 + Phase 2 Bundle 预算 + Phase 3 数据层优化 + Phase 4 组件渲染优化
全天 questions/search FULLTEXT 索引 + MATCH AGAINST BOOLEAN MODE 替代 LIKE '%xxx%'
全天 notifications fan-out 批量化:getAllUserIds 分页 + createNotifications 批量 INSERT
全天 全局 高 LIMIT 调低attendance 5000→100、adaptive-practice 1000→100、questions 1000→100
全天 messaging/grades/attendance useOptimistic + useTransition 乐观更新改造

2026-07-04

时间 模块 经验
全天 全局 API 规范化重构:响应信封 + 错误→状态码映射 + SSE 工具 + withApiErrorHandler HOF
全天 全局 设计令牌专项重构双层令牌架构Primitive + Semantic+ ESLint 强制约束
全天 全局 PWA 支持manifest.ts + viewport + 触控友好panOnDrag/zoomOnPinch/zoomOnDoubleClick=false

2026-07-02

时间 模块 经验
全天 lesson-preparation V5 备课模块场景缺口修复Zustand slice + history + 自动保存 + Tiptap Image + 打印视图

2026-06-26

时间 模块 经验
全天 student 学生端路由权限校验修复:homework:submitCLASS_READ;新增 loading.tsx + error.tsx
全天 classes teacher/classes/* 路由权限校验修复:统一 CLASS_READ

2026-06-24

时间 模块 经验
全天 grades 成绩录入支持按试卷录入:getExamsForGradeEntry / getExamForGradeEntry
全天 shared ROLE_NAMES 常量替代 DB 硬编码角色名;extractQuestionPreview 共享工具

2026-06-23

时间 模块 经验
全天 shared Action 错误处理统一:handleActionError / safeActionCall / BusinessError / NotFoundError / ValidationError

2026-06-22

时间 模块 经验
全天 classes teacher/classes 路由权限审计修复

2026-06-17

时间 模块 经验
全天 全局 004 架构影响地图首次编写4227 行,后于 2026-07-07 瘦身至 458 行)