docs(known-issues): log enterprise architecture normalization work experience
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
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
This commit is contained in:
@@ -10,158 +10,158 @@
|
||||
|
||||
### 1.1 i18n
|
||||
|
||||
| 场景 | 规则 |
|
||||
|------|------|
|
||||
| key 命名 | 禁止包含 `.`,不同字段用驼峰,同字段多角色变体用嵌套对象 |
|
||||
| 动态 key 调用 | `t()` 参数为动态变量时必须包含完整嵌套路径(`t(\`type.${type}\`)` 而非 `t(type)`) |
|
||||
| ICU 参数 | 单花括号 `{level}`,多参数直接并列 `{count} 题 · {subject}` |
|
||||
| Namespace 与文件名一致性 | namespace 必须与文件名严格一致 |
|
||||
| 翻译文件对称性 | zh-CN 与 en 必须同步新增/删除 key |
|
||||
| 硬编码中文 | 所有用户可见文本必须走 i18n(AI Prompt 中文常量允许) |
|
||||
| i18n dead 节清理 | 删除组件时同步清理对应的 i18n key |
|
||||
| 动态键翻译守卫 | 缺失 key 时降级返回 key 本身而非抛错 |
|
||||
| 场景 | 规则 |
|
||||
| ------------------------ | -------------------------------------------------------------------------------- |
|
||||
| key 命名 | 禁止包含 `.`,不同字段用驼峰,同字段多角色变体用嵌套对象 |
|
||||
| 动态 key 调用 | `t()` 参数为动态变量时必须包含完整嵌套路径(`t(\`type.${type}\`)`而非`t(type)`) |
|
||||
| ICU 参数 | 单花括号 `{level}`,多参数直接并列 `{count} 题 · {subject}` |
|
||||
| Namespace 与文件名一致性 | namespace 必须与文件名严格一致 |
|
||||
| 翻译文件对称性 | zh-CN 与 en 必须同步新增/删除 key |
|
||||
| 硬编码中文 | 所有用户可见文本必须走 i18n(AI 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 Action(idempotent 设计) |
|
||||
| 场景 | 技术/规则 |
|
||||
| --------------------------------- | ---------------------------------------------------------------------------------- |
|
||||
| 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 Action(idempotent 设计) |
|
||||
|
||||
### 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 一致性 | 初始值必须可序列化且在服务端和客户端一致 |
|
||||
| 场景 | 规则 |
|
||||
| --------------------------- | --------------------------------------------------------------------------------------------------------------------- |
|
||||
| "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:push` 或 `npm 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*` 格式 |
|
||||
| 场景 | 规则 |
|
||||
| -------------------------- | ----------------------------------------------------------------------------------------------- |
|
||||
| schema.ts 修改后 | 必须 `npm run db:push` 或 `npm 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 自动管理乐观状态与回滚 |
|
||||
| 场景 | 技术 |
|
||||
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 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 wrapper(tiptap / @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` |
|
||||
| 场景 | 技术 |
|
||||
| ---------------------- | --------------------------------------------------------------------------- |
|
||||
| 批量 INSERT | `db.insert().values([...])` 单次写入 |
|
||||
| 批量 UPDATE | `sql.join` + `CASE WHEN` 单次执行 |
|
||||
| 递归删除 | 批量收集后代 ID + `inArray` 单次删除,禁止递归逐个 |
|
||||
| 动态导入重 UI 库 | `xxx-inner.tsx` + `xxx.tsx` lazy wrapper(tiptap / @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.css`、`email-channel.ts`、`manifest.ts` |
|
||||
| 场景 | 规则 |
|
||||
| -------------------- | --------------------------------------------------------------------- |
|
||||
| 禁止硬编码颜色 | 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.css`、`email-channel.ts`、`manifest.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> = {}` |
|
||||
| 场景 | 规则 |
|
||||
| ---------------- | ------------------------------------------------------------------------------- |
|
||||
| 禁止 `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` 是联合类型,不可混用 |
|
||||
| 场景 | 规则 |
|
||||
| ---------------- | ------------------------------------------------------------------------------------------------------ |
|
||||
| 响应信封 | `{ 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-case(`audit` / `exams` / `client-error`) |
|
||||
| ESLint no-console | 服务端强制,客户端 track-event 改为 no-op |
|
||||
| 客户端错误上报 | 通过 track-event 上报,不导入服务端 logger |
|
||||
| Request ID | proxy.ts 注入 + `withRequestContext` 包装 Server Action + `getRequestContext().requestId` 读取 |
|
||||
| 场景 | 规则 |
|
||||
| ------------------ | ---------------------------------------------------------------------------------------------- |
|
||||
| 服务端日志 | `createModuleLogger("module-name")` + pino,禁止 `console.*` |
|
||||
| 日志参数顺序 | `log.error({ err: error, userId }, "操作描述")` |
|
||||
| 错误对象字段名 | `err`(不是 `error`) |
|
||||
| 模块级 logger 命名 | kebab-case(`audit` / `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 到空 stub;`cache()` 包装函数测试需 mock |
|
||||
| Playwright 检测 | 检查 HTTP 状态码 + pageerror(循环外注册一次)+ 可见错误元素,不检查 HTML content |
|
||||
| testTimeout | ts-morph 解析慢,arch-scan 测试设置 60000ms |
|
||||
| 场景 | 规则 |
|
||||
| ---------------------------- | --------------------------------------------------------------------------------- |
|
||||
| Vitest + Next.js 16 缓存测试 | server-only alias 到空 stub;`cache()` 包装函数测试需 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` |
|
||||
| 场景 | 命令/规则 |
|
||||
| -------------------- | ---------------------------------------------------------------------- |
|
||||
| 类型检查 | `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` |
|
||||
| arch.db 扫描 | `npm run arch:scan` |
|
||||
| arch.db 查询 | `npm run arch:query -- <command>` |
|
||||
| ESLint 零警告 | 所有修改文件必须 0 errors 0 warnings |
|
||||
| 自定义规则加载 | Windows 路径用 `path.join`,不用 `path.posix.join` |
|
||||
|
||||
---
|
||||
|
||||
@@ -169,97 +169,97 @@
|
||||
|
||||
### 2.1 lesson-preparation(备课)
|
||||
|
||||
| 场景 | 规则 |
|
||||
|------|------|
|
||||
| Zustand store 拆 slice | mutation 方法包装 history(`pushHistory` + `set`);`updateNodePosition` 不推 history;hydrate/replaceDoc 清空历史;history 栈上限 `MAX_HISTORY` |
|
||||
| 撤销/重做快捷键 | `metaKey \|\| ctrlKey` 区分 Mac;Shift+Z 或 Y 重做;`preventDefault` |
|
||||
| 自动保存失败 UI | toast.error + 断网时不触发 + 监听 online/offline + saveError 显示重试按钮 |
|
||||
| 发布前预览 3 步 | `select → preview → confirm` 状态机;总分 useMemo;确认页警告文案 |
|
||||
| Tiptap Image 扩展 | `inline: false, allowBase64: false`;`editor.commands.setImage`;不自建上传组件 |
|
||||
| Tiptap SSR | `immediatelyRender: false` 避免 hydration mismatch |
|
||||
| 附件表 Date 转换 | data-access 返回 Date,service 转换为 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 ?? null`(`string \| undefined` → `string \| null`) |
|
||||
| React Flow 移除 | 改为三列布局(sidebar + paper area + detail panel),纸感界面 |
|
||||
| 师生互动节点 | 渲染为对话 + '师'/'生' 角色标签 + 虚线分隔 |
|
||||
| 节点展开/收起 | 左侧栏、右侧面板、右键菜单三处入口一致 |
|
||||
| 右键菜单 | 节点操作(展开/收起、移动、复制、删除)+ AI 辅助(生成内容、优化表达、差异化建议、分层提问) |
|
||||
| 排版差异 | Fraunces 16px 主文(近黑)+ Inter 13.5px 节点(暖灰)+ 左 2px 细线 |
|
||||
| 场景 | 规则 |
|
||||
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Zustand store 拆 slice | mutation 方法包装 history(`pushHistory` + `set`);`updateNodePosition` 不推 history;hydrate/replaceDoc 清空历史;history 栈上限 `MAX_HISTORY` |
|
||||
| 撤销/重做快捷键 | `metaKey \|\| ctrlKey` 区分 Mac;Shift+Z 或 Y 重做;`preventDefault` |
|
||||
| 自动保存失败 UI | toast.error + 断网时不触发 + 监听 online/offline + saveError 显示重试按钮 |
|
||||
| 发布前预览 3 步 | `select → preview → confirm` 状态机;总分 useMemo;确认页警告文案 |
|
||||
| Tiptap Image 扩展 | `inline: false, allowBase64: false`;`editor.commands.setImage`;不自建上传组件 |
|
||||
| Tiptap SSR | `immediatelyRender: false` 避免 hydration mismatch |
|
||||
| 附件表 Date 转换 | data-access 返回 Date,service 转换为 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 ?? null`(`string \| undefined` → `string \| 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>` |
|
||||
| 场景 | 规则 |
|
||||
| ------------- | ------------------------------------------------------------------------ |
|
||||
| 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)` 单次 INSERT;in_app 批量 + 其他渠道并行 |
|
||||
| 通知偏好 | 已从 messaging 迁移至 notifications |
|
||||
| fan-out 分页 | `getAllUserIds(limit=1000, offset)` 分页遍历 |
|
||||
| 场景 | 规则 |
|
||||
| ---------------- | -------------------------------------------------------------------- |
|
||||
| 消息星标乐观更新 | `useOptimistic<boolean>` + `useTransition` + `addOptimisticStarred` |
|
||||
| 消息列表星标 Map | `useOptimistic<Map<string, boolean>>`(key=messageId) |
|
||||
| 撤回不乐观更新 | 需服务端返回判断 2 分钟窗口 |
|
||||
| 通知批量化 | `createNotifications(items)` 单次 INSERT;in_app 批量 + 其他渠道并行 |
|
||||
| 通知偏好 | 已从 messaging 迁移至 notifications |
|
||||
| fan-out 分页 | `getAllUserIds(limit=1000, offset)` 分页遍历 |
|
||||
|
||||
### 2.4 attendance
|
||||
|
||||
| 场景 | 规则 |
|
||||
|------|------|
|
||||
| 考勤提交乐观更新 | `useOptimistic<boolean>` 保留 form action 模式(`useFormStatus` 依赖 form action promise) |
|
||||
| 不用 useTransition 包裹 | 会导致 form action 立即返回、`useFormStatus` 不可靠 |
|
||||
| 场景 | 规则 |
|
||||
| ----------------------- | ------------------------------------------------------------------------------------------ |
|
||||
| 考勤提交乐观更新 | `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)` |
|
||||
| 场景 | 规则 |
|
||||
| ------------------------ | ------------------------------------------------------------------------------------ |
|
||||
| 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_PURGE` ≠ `AUDIT_LOG_READ` |
|
||||
| 权限点注册全链路 | `permissions.ts` → `ROLE_PERMISSIONS_SEED` → `permission-bitmap.ts` → `permission-catalog.ts` → i18n `rbac.json` 五处同步 |
|
||||
| 审计日志保留管理 | `AUDIT_RETENTION_MANAGE` 权限点 |
|
||||
| 场景 | 规则 |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 破坏性操作用专属权限 | `AUDIT_LOG_PURGE` ≠ `AUDIT_LOG_READ` |
|
||||
| 权限点注册全链路 | `permissions.ts` → `ROLE_PERMISSIONS_SEED` → `permission-bitmap.ts` → `permission-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` |
|
||||
| 场景 | 规则 |
|
||||
| ------------------ | ------------------------------------------------------------------- |
|
||||
| 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` |
|
||||
| 场景 | 规则 |
|
||||
| ----------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| 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` |
|
||||
|
||||
---
|
||||
|
||||
@@ -269,75 +269,78 @@
|
||||
|
||||
### 2026-07-07
|
||||
|
||||
| 时间 | 模块 | 经验 |
|
||||
|------|------|------|
|
||||
| 21:30 | 全局 | 004 架构文档 V3 重构:从"影响地图"升级为"架构设计文档",14 章节 912 行。核心 mermaid 图:系统边界图 + 三层架构图 + 模块依赖全景图(35 模块分层)+ 认证 sequenceDiagram + 权限系统架构图 + 数据访问分层图 + 5 层状态模型图 + 跨模块协作 sequenceDiagram + 考试全生命周期 sequenceDiagram(跨 4 模块)+ 令牌分层架构图 + API 路由分类图 + 错误→状态码映射图 + userId/permission 影响链。模块细节移至各模块 README,004 聚焦架构层面:技术栈选型/分层规则/依赖深度统计/循环依赖处理/权限 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.ts(6 个 Server Action);messaging data-access.ts(1089 行)拆分为 5 子文件 + barrel |
|
||||
| 12:27 | 全局 | data-access 审计 v1 完成:86 文件 ~30 actions,5 维度 4 严重级别,生成 230 issues(17 P0/48 P1/105 P2/60 P3),5 子代理并行审查 |
|
||||
| 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` 断言 |
|
||||
| 时间 | 模块 | 经验 |
|
||||
| ----- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 22:30 | 全局 | 企业级架构规范化 P2 完成:CI 新增 Unit test 阶段;创建 /api/health 端点 + Dockerfile HEALTHCHECK;安装 @next/bundle-analyzer(`npm run analyze` 触发);noUncheckedIndexedAccess 评估 419 错误,推迟到独立迭代(记入 tech-debt TD-P1-001) |
|
||||
| 22:10 | 全局 | 企业级架构规范化 P1 完成:创建 LICENSE(专有协议)/CHANGELOG/CONTRIBUTING/SECURITY 大仓文档;安装 husky + lint-staged + commitlint,pre-commit 运行 eslint+prettier,commit-msg 校验 conventional commits 格式;填充 tech-debt.md(P0/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 影响链。模块细节移至各模块 README,004 聚焦架构层面:技术栈选型/分层规则/依赖深度统计/循环依赖处理/权限 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.ts(6 个 Server Action);messaging data-access.ts(1089 行)拆分为 5 子文件 + barrel |
|
||||
| 12:27 | 全局 | data-access 审计 v1 完成:86 文件 ~30 actions,5 维度 4 严重级别,生成 230 issues(17 P0/48 P1/105 P2/60 P3),5 子代理并行审查 |
|
||||
| 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 + ModalRoot;useDialogState 评估结论(不采用) |
|
||||
| 全天 | 全局 | 性能预算重构: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 乐观更新改造 |
|
||||
| 时间 | 模块 | 经验 |
|
||||
| ---- | --------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||
| 全天 | 全局 | V5 状态管理重构 Phase 1-3 完成:notify 替换为 zustand + ModalRoot;useDialogState 评估结论(不采用) |
|
||||
| 全天 | 全局 | 性能预算重构: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 强制约束 |
|
||||
| 时间 | 模块 | 经验 |
|
||||
| ---- | ---- | -------------------------------------------------------------------------------------------- |
|
||||
| 全天 | 全局 | 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:submit` → `CLASS_READ`;新增 `loading.tsx` + `error.tsx` |
|
||||
| 全天 | classes | teacher/classes/* 路由权限校验修复:统一 `CLASS_READ` |
|
||||
| 全天 | 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 行) |
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user