Files
NextEdu/docs/superpowers/plans/2026-07-07-enterprise-architecture-normalization.md

1004 lines
27 KiB
Markdown

# 企业级架构规范化实施计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 消除所有架构违规 + 补齐大仓工程基建 + 升级 TypeScript/CI 配置,使项目达到企业级规范标准
**Architecture:** 严格分阶段(P0→P1→P2),每阶段独立 git 提交,失败可回滚。P0 修复 32 个无权限 Action + 5 个超长文件;P1 补齐文档 + 提交钩子 + tech-debt;P2 开启 noUncheckedIndexedAccess + CI 加 unit test + 可观测性补充
**Tech Stack:** TypeScript 5 / Next.js 16 / Drizzle / ESLint 9 / husky / commitlint / vitest / @next/bundle-analyzer
**Spec:** [docs/superpowers/specs/2026-07-07-enterprise-architecture-normalization-design.md](../specs/2026-07-07-enterprise-architecture-normalization-design.md)
---
## 阶段 1: P0 安全合规
### Task 1: arch:scan 添加 @public 豁免标记机制
**Files:**
- Modify: `scripts/arch-scan/schema.ts` (symbols 表加 is_public 列)
- Modify: `scripts/arch-scan/scanner.ts` (识别 JSDoc @public)
- Modify: `scripts/arch-scan/query.ts` (violations 过滤 is_public)
- Test: `scripts/arch-scan/scanner.test.ts`
- [ ] **Step 1: 给 symbols 表加 is_public 列**
修改 `scripts/arch-scan/schema.ts`,在 symbols 表定义中加:
```ts
is_public: integer("is_public", { mode: "boolean" }).notNull().default(false),
```
删除 `scripts/arch-scan/arch.db` 让 schema 重建。
- [ ] **Step 2: scanner.ts 识别 JSDoc @public 标记**
修改 `scripts/arch-scan/scanner.ts` 的符号扫描逻辑。对每个 async function 导出,检查其上方的 JSDoc 注释是否包含 `@public` 标记。若是,设 `is_public: true`
参考实现(ts-morph API):
```ts
const jsDocs = symbolNode.getJsDocs();
const isPublic = jsDocs.some(doc => doc.getTags().some(tag => tag.getTagName() === "public"));
```
- [ ] **Step 3: query.ts violations 过滤 is_public**
修改 `scripts/arch-scan/query.ts``printViolations` 函数,Server Actions without requirePermission 查询加 `AND is_public = 0`
- [ ] **Step 4: 运行测试验证**
```bash
npm run test:arch-scan
```
Expected: 所有测试 PASS(25/25)。若 schema 变更导致测试失败,更新测试 fixture。
- [ ] **Step 5: 重新扫描验证**
```bash
npm run arch:scan
npm run arch:query -- violations
```
Expected: violations 输出仍是 32 个(因为还没给 Action 加 @public)。
- [ ] **Step 6: Commit**
```bash
git add scripts/arch-scan/
git commit -m "feat(arch-scan): add @public tag exemption mechanism for server actions"
```
---
### Task 2: 给 12 个豁免 Action 加 @public 标记
**Files:**
- Modify: `src/modules/auth/actions.ts`
- Modify: `src/modules/invitation-codes/actions.ts`
- Modify: `src/modules/onboarding/actions.ts`
- Modify: `src/modules/settings/actions-security.ts`
- Modify: `src/modules/rbac/actions.ts`
- Modify: `src/shared/lib/audit-logger.ts`
- Modify: `src/shared/lib/change-logger.ts`
- Modify: `src/shared/lib/login-logger.ts`
- [ ] **Step 1: auth/actions.ts 加 @public**
`registerAction``checkEmailAvailabilityAction``preflightTwoFactorAction` 三个函数,在 JSDoc 中加 `@public` 标记。示例:
```ts
/**
* 注册新用户(登录前,无需权限校验)
* @public
*/
export async function registerAction(...): Promise<ActionState<...>> { ... }
```
- [ ] **Step 2: invitation-codes/actions.ts 加 @public**
`validateInvitationCodeAction``@public`(注册时校验邀请码)。
- [ ] **Step 3: onboarding/actions.ts 加 @public**
`getOnboardingStatusAction``completeOnboardingAction``@public`(引导阶段,已登录但未完成引导)。
- [ ] **Step 4: settings/actions-security.ts 加 @public**
`preflightTwoFactorAction``verifyTwoFactorForLogin``@public`(登录中 2FA 流程)。
- [ ] **Step 5: rbac/actions.ts 加 @public**
`isAdminRole``@public`(内部辅助函数,非对外 Action)。
- [ ] **Step 6: shared/lib 三个 logger 加 @public**
`audit-logger.ts``logAudit``change-logger.ts``logDataChange``login-logger.ts``logLoginEvent``@public`(基础设施,被 Action 内部调用,非对外 Action)。
- [ ] **Step 7: 重新扫描验证豁免生效**
```bash
npm run arch:scan
npm run arch:query -- violations
```
Expected: Server Actions without requirePermission 从 32 降至 20(豁免 12 个)。
- [ ] **Step 8: 验证 tsc + lint**
```bash
npx tsc --noEmit
npm run lint
```
Expected: 0 errors。
- [ ] **Step 9: Commit**
```bash
git add src/modules/ src/shared/lib/
git commit -m "fix(permissions): mark 12 public/infrastructure actions with @public tag"
```
---
### Task 3: 修复 ai 模块 6 个 Action 权限
**Files:**
- Modify: `src/modules/ai/actions.ts`
- Possibly modify: `src/shared/types/permissions.ts` (若需新增权限点)
- [ ] **Step 1: 读取 ai/actions.ts 理解 6 个 Action 签名**
```bash
# 用 Read 工具读取 src/modules/ai/actions.ts
```
确认每个 Action 的参数和现有权限校验方式。
- [ ] **Step 2: 确认所需权限点存在**
检查 `src/shared/types/permissions.ts` 是否有:QUESTION_READ、HOMEWORK_GRADE、LESSON_PLAN_CREATE、QUESTION_CREATE、DIAGNOSTIC_READ、ERROR_BOOK_READ。若缺失,新增并注册到 5 处(permissions.ts → ROLE_PERMISSIONS_SEED → permission-bitmap.ts → permission-catalog.ts → i18n/rbac.json)。
- [ ] **Step 3: 给 6 个 Action 加 requirePermission**
```ts
import { requirePermission } from "@/shared/lib/auth-guard";
import { Permissions } from "@/shared/types/permissions";
export async function suggestSimilarQuestionsAction(...) {
const ctx = await requirePermission(Permissions.QUESTION_READ);
// ... 原 logic 使用 ctx.userId
}
```
6 个映射:suggestSimilarQuestionsAction→QUESTION_READ、suggestGradingAction→HOMEWORK_GRADE、generateLessonContentAction→LESSON_PLAN_CREATE、generateQuestionVariantAction→QUESTION_CREATE、analyzeWeaknessAction→DIAGNOSTIC_READ、explainErrorAction→ERROR_BOOK_READ。
- [ ] **Step 4: 验证 tsc + lint**
```bash
npx tsc --noEmit
npm run lint
```
Expected: 0 errors。
- [ ] **Step 5: Commit**
```bash
git add src/modules/ai/ src/shared/types/
git commit -m "fix(ai): add requirePermission to 6 ai server actions"
```
---
### Task 4: 修复 parent 模块 6 个 Action 权限
**Files:**
- Modify: `src/modules/parent/actions.ts`
- Possibly modify: `src/shared/types/permissions.ts`
- [ ] **Step 1: 读取 parent/actions.ts**
确认 6 个 Action 的现有实现。parent 模块的特殊性:需要同时校验 parentId + studentId 防跨家庭信息泄露。
- [ ] **Step 2: 确认 PARENT_DASHBOARD_VIEW 权限点存在**
若不存在,新增 `PARENT_DASHBOARD_VIEW` 到 permissions.ts + 5 处注册。
- [ ] **Step 3: 给 6 个 Action 加 requirePermission + parentId/studentId 校验**
```ts
export async function getChildrenAction() {
const ctx = await requirePermission(Permissions.PARENT_DASHBOARD_VIEW);
// ctx.userId 即 parentId
return getChildren(ctx.userId);
}
export async function getChildDashboardDataAction(studentId: string) {
const ctx = await requirePermission(Permissions.PARENT_DASHBOARD_VIEW);
await verifyParentChildRelationAction(ctx.userId, studentId); // 防越权
return getChildDashboardData(studentId);
}
```
6 个:getChildrenAction、getChildBasicInfoAction、getChildDashboardDataAction、getParentDashboardDataAction、getChildNameListAction、verifyParentChildRelationAction。
- [ ] **Step 4: 验证 tsc + lint + arch:scan**
```bash
npx tsc --noEmit
npm run lint
npm run arch:scan
npm run arch:query -- violations
```
Expected: tsc/lint 0 errors;violations 中 parent 模块的 6 个消失。
- [ ] **Step 5: Commit**
```bash
git add src/modules/parent/ src/shared/types/
git commit -m "fix(parent): add requirePermission + parent-child relation check to 6 actions"
```
---
### Task 5: 修复 settings 模块 5 个 Action 权限
**Files:**
- Modify: `src/modules/settings/actions.ts` (4 个)
- Modify: `src/modules/settings/actions-service.ts` (1 个: updateProfileAction)
- [ ] **Step 1: 确认权限点**
确认 SYSTEM_SETTINGS_READ、SYSTEM_SETTINGS_MANAGE、USER_PROFILE_UPDATE 存在。USER_PROFILE_UPDATE 若不存在,用 `getAuthContext()` 替代(自身资料更新,任何登录用户均可)。
- [ ] **Step 2: 给 5 个 Action 加权限校验**
actions.ts 中 4 个:getAiProviderSummaries→SYSTEM_SETTINGS_READ、upsertAiProviderAction→SYSTEM_SETTINGS_MANAGE、testAiProviderAction→SYSTEM_SETTINGS_MANAGE、deleteAiProviderAction→SYSTEM_SETTINGS_MANAGE、canConfigurePublicAiProvider→SYSTEM_SETTINGS_READ。
actions-service.ts 中 updateProfileAction:用 `getAuthContext()`(任何登录用户可改自己资料)。
- [ ] **Step 3: 验证**
```bash
npx tsc --noEmit && npm run lint
```
- [ ] **Step 4: Commit**
```bash
git add src/modules/settings/
git commit -m "fix(settings): add requirePermission to 5 settings server actions"
```
---
### Task 6: 修复剩余 3 个 Action 权限
**Files:**
- Modify: `src/modules/leave-requests/actions.ts` (listMyLeaveRequests)
- Modify: `src/modules/lesson-preparation/actions.ts` (duplicateLessonPlanFormAction)
- [ ] **Step 1: leave-requests listMyLeaveRequests**
`getAuthContext()`(查自己的请假记录,任何登录用户可查自己)。
- [ ] **Step 2: lesson-preparation duplicateLessonPlanFormAction**
`requirePermission(Permissions.LESSON_PLAN_CREATE)`
- [ ] **Step 3: 验证全部 P0 权限修复完成**
```bash
npm run arch:scan
npm run arch:query -- violations
```
Expected: Server Actions without requirePermission = 0。
- [ ] **Step 4: tsc + lint**
```bash
npx tsc --noEmit && npm run lint
```
- [ ] **Step 5: Commit P0 权限修复完成**
```bash
git add src/modules/
git commit -m "fix(permissions): complete requirePermission for remaining 3 actions"
```
---
### Task 7: 拆分 shared/db/schema.ts (2245 行 → 按域拆分)
**Files:**
- Create: `src/shared/db/schema/users.ts`
- Create: `src/shared/db/schema/academic.ts`
- Create: `src/shared/db/schema/exams.ts`
- Create: `src/shared/db/schema/grades.ts`
- Create: `src/shared/db/schema/attendance.ts`
- Create: `src/shared/db/schema/messaging.ts`
- Create: `src/shared/db/schema/notifications.ts`
- Create: `src/shared/db/schema/audit.ts`
- Create: `src/shared/db/schema/rbac.ts`
- Create: `src/shared/db/schema/files.ts`
- Create: `src/shared/db/schema/scheduling.ts`
- Create: `src/shared/db/schema/misc.ts`
- Modify: `src/shared/db/schema.ts` (改为 barrel re-export)
- [ ] **Step 1: 读取 schema.ts 按表分组**
读取 `src/shared/db/schema.ts`,按域将 54 张表分组到上述 12 个文件。
- [ ] **Step 2: 创建 schema/ 目录,按域拆分**
每个 `schema/<domain>.ts` 文件:
```ts
import { mysqlTable, varchar, ... } from "drizzle-orm/mysql-core";
// 该域的表定义
export const users = mysqlTable("users", { ... });
```
- [ ] **Step 3: schema.ts 改为 barrel**
```ts
// src/shared/db/schema.ts
export * from "./schema/users";
export * from "./schema/academic";
export * from "./schema/exams";
// ... 12 个 re-export
```
- [ ] **Step 4: 验证所有 import 仍正常**
```bash
npx tsc --noEmit
```
Expected: 0 errors(因为 barrel re-export 保持 import 路径不变)。
- [ ] **Step 5: 验证文件行数**
```powershell
Get-Content src/shared/db/schema.ts | Measure-Object -Line
# Expected: < 50 行(barrel)
Get-ChildItem src/shared/db/schema/*.ts | ForEach-Object { "$($_.Name): $((Get-Content $_.FullName | Measure-Object -Line).Lines)" }
# Expected: 每个文件 < 500 行
```
- [ ] **Step 6: Commit**
```bash
git add src/shared/db/
git commit -m "refactor(db): split schema.ts (2245 lines) into 12 domain files"
```
---
### Task 8: 拆分 shared/lib/cache/invalidation-map.ts (1195 行)
**Files:**
- Create: `src/shared/lib/cache/invalidation/<module>.ts` (按模块拆分)
- Modify: `src/shared/lib/cache/invalidation-map.ts` (改为 barrel + registerInvalidationMaps)
- [ ] **Step 1: 读取 invalidation-map.ts 按模块分组**
- [ ] **Step 2: 按模块拆分到 invalidation/ 目录**
每个模块的 invalidation map 移到独立文件,导出该模块的 invalidation 配置对象。
- [ ] **Step 3: invalidation-map.ts 改为 barrel + 聚合注册**
```ts
import { examsInvalidation } from "./invalidation/exams";
// ...
export function registerInvalidationMaps() {
register(examsInvalidation);
// ...
}
```
- [ ] **Step 4: 验证**
```bash
npx tsc --noEmit && npm run lint
```
- [ ] **Step 5: Commit**
```bash
git add src/shared/lib/cache/
git commit -m "refactor(cache): split invalidation-map.ts (1195 lines) by module"
```
---
### Task 9: 拆分 messaging/actions.ts (973 行)
**Files:**
- Create: `src/modules/messaging/actions-conversations.ts`
- Create: `src/modules/messaging/actions-messages.ts`
- Create: `src/modules/messaging/actions-starred.ts`
- Create: `src/modules/messaging/actions-read.ts`
- Modify: `src/modules/messaging/actions.ts` (改为 barrel)
- [ ] **Step 1: 读取 actions.ts 按职责分组**
- [ ] **Step 2: 按职责拆分到 4 个文件**
每个文件顶部 `"use server"`,按职责归类 Action。
- [ ] **Step 3: actions.ts 改为 barrel**
```ts
export * from "./actions-conversations";
export * from "./actions-messages";
export * from "./actions-starred";
export * from "./actions-read";
```
- [ ] **Step 4: 验证**
```bash
npx tsc --noEmit && npm run lint && npm run arch:scan
npm run arch:query -- violations
```
Expected: messaging/actions.ts 不再在超长文件列表。
- [ ] **Step 5: Commit**
```bash
git add src/modules/messaging/
git commit -m "refactor(messaging): split actions.ts (973 lines) by responsibility"
```
---
### Task 10: 拆分 textbooks/data-access.ts (907 行)
**Files:**
- Create: `src/modules/textbooks/data-access-chapters.ts`
- Create: `src/modules/textbooks/data-access-knowledge-points.ts`
- Modify: `src/modules/textbooks/data-access.ts` (改为 barrel + 核心)
- [ ] **Step 1: 拆分章节 CRUD 到 data-access-chapters.ts**
- [ ] **Step 2: 拆分知识点 CRUD 到 data-access-knowledge-points.ts**
- [ ] **Step 3: data-access.ts 保留核心 + barrel re-export**
- [ ] **Step 4: 验证**
```bash
npx tsc --noEmit && npm run lint && npm run arch:scan
npm run arch:query -- violations
```
Expected: textbooks/data-access.ts 不再在超长文件列表。
- [ ] **Step 5: Commit**
```bash
git add src/modules/textbooks/
git commit -m "refactor(textbooks): split data-access.ts (907 lines) by entity"
```
---
### Task 11: 拆分 questions/data-access.ts (828 行)
**Files:**
- Create: `src/modules/questions/data-access-search.ts`
- Modify: `src/modules/questions/data-access.ts` (保留核心 CRUD + barrel)
- [ ] **Step 1: 拆分 FULLTEXT 检索到 data-access-search.ts**
- [ ] **Step 2: data-access.ts 保留核心 CRUD + re-export**
- [ ] **Step 3: 验证 P0 全部完成**
```bash
npm run arch:scan
npm run arch:query -- violations
```
Expected: Long files = 0;Server Actions without requirePermission = 0。
```bash
npx tsc --noEmit && npm run lint
```
Expected: 0 errors。
- [ ] **Step 4: 更新 known-issues.md 工作经验日志**
`docs/troubleshooting/known-issues.md` 工作经验日志区追加 P0 完成记录。
- [ ] **Step 5: Commit P0 阶段完成**
```bash
git add src/modules/questions/ docs/troubleshooting/
git commit -m "refactor(questions): split data-access.ts and complete P0 security phase"
git push
```
---
## 阶段 2: P1 大仓工程基建
### Task 12: 创建大仓文档
**Files:**
- Create: `LICENSE`
- Create: `CHANGELOG.md`
- Create: `CONTRIBUTING.md`
- Create: `SECURITY.md`
- Create: `.env.example`
- [ ] **Step 1: 创建 LICENSE(专有协议)**
```
Copyright (c) 2026 EazyGame. 保留所有权利。
未经版权所有者书面许可,不得以任何形式复制、修改、合并、发布、分发、再许可或销售本软件的任何部分。
```
- [ ] **Step 2: 创建 CHANGELOG.md(Keep-a-changelog 格式)**
```markdown
# 更新日志
本项目遵循 [Keep a Changelog](https://keepachangelog.com/) 格式,版本号遵循 [Semantic Versioning](https://semver.org/)。
## [1.0.0] - 2026-07-07
### Added
- arch.db 架构元数据库 + 扫描器(ts-morph + SQLite)
- 35 个模块 README.md(含 mermaid 架构图与流程图)
- 004 架构设计文档 V3(14 章节 + 13 mermaid 图)
- known-issues.md 索引式速查手册
### Changed
- 004 从"影响地图"重构为"架构设计文档"(458→912 行)
- project_rules.md 引入 arch.db + AI 工作强制流程
- coding-standards.md 更新令牌分层 + cacheFn + 5 层状态模型
```
- [ ] **Step 3: 创建 CONTRIBUTING.md**
包含:分支策略(main 保护)、Conventional Commits 规范、提交前检查(lint+tsc+arch:scan)、模块 README 维护规则、代码审查清单。
- [ ] **Step 4: 创建 SECURITY.md**
包含:漏洞报告流程、安全联系人、响应 SLA、不公开披露策略。
- [ ] **Step 5: 创建 .env.example**
读取 `src/env.mjs` 提取所有环境变量键,加注释:
```
# 数据库
DATABASE_URL="mysql://user:pass@localhost:3306/next_edu"
# NextAuth
NEXTAUTH_SECRET="your-secret-here"
NEXTAUTH_URL="http://localhost:3000"
# AI Provider(可选)
OPENAI_API_KEY=""
ANTHROPIC_API_KEY=""
# Redis(可选)
REDIS_URL=""
# 文件存储
UPLOAD_DIR="./uploads"
# ... 其余从 env.mjs 提取
```
- [ ] **Step 6: Commit**
```bash
git add LICENSE CHANGELOG.md CONTRIBUTING.md SECURITY.md .env.example
git commit -m "docs: add enterprise baseline docs (LICENSE/CHANGELOG/CONTRIBUTING/SECURITY/env.example)"
```
---
### Task 13: 安装 husky + lint-staged + commitlint
**Files:**
- Modify: `package.json` (dependencies + scripts)
- Create: `.husky/pre-commit`
- Create: `.husky/commit-msg`
- Create: `commitlint.config.js`
- Create: `lint-staged.config.js`
- [ ] **Step 1: 安装依赖**
```bash
npm install -D husky lint-staged @commitlint/cli @commitlint/config-conventional
```
- [ ] **Step 2: 初始化 husky**
```bash
npx husky init
```
这会创建 `.husky/` 目录并生成 `.husky/pre-commit` 示例。
- [ ] **Step 3: 配置 pre-commit 钩子**
`.husky/pre-commit`:
```bash
npx lint-staged
```
- [ ] **Step 4: 配置 commit-msg 钩子**
`.husky/commit-msg`:
```bash
npx commitlint --edit "$1"
```
- [ ] **Step 5: 创建 commitlint.config.js**
```js
module.exports = {
extends: ["@commitlint/config-conventional"],
rules: {
"scope-enum": [2, "always", [
// 35 模块名
"exams", "homework", "questions", "textbooks", "grades",
"lesson-preparation", "classes", "school", "scheduling",
"attendance", "course-plans", "users", "messaging",
"notifications", "parent", "audit", "rbac", "auth",
"onboarding", "ai", "dashboard", "diagnostic",
"adaptive-practice", "error-book", "elective",
"proctoring", "leave-requests", "invitation-codes",
"files", "settings", "layout", "student", "search",
"announcements", "standards",
// 架构层
"arch-scan", "db", "cache", "ui", "deps", "config",
// 文档
"docs", "architecture", "troubleshooting",
]],
},
};
```
- [ ] **Step 6: 创建 lint-staged.config.js**
```js
module.exports = {
"*.{ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{js,mjs,cjs}": ["prettier --write"],
"*.md": ["prettier --write"],
"*.{json,yml,yaml}": ["prettier --write"],
};
```
- [ ] **Step 7: package.json 加 prepare 脚本**
```json
"scripts": {
"prepare": "husky"
}
```
- [ ] **Step 8: 验证钩子工作**
```bash
# 测试 commitlint 拒绝非规范提交
git commit -m "bad commit message" --allow-empty
# Expected: FAIL (commitlint 拒绝)
# 测试规范提交
git commit -m "test(husky): verify commitlint works" --allow-empty
# Expected: PASS
```
- [ ] **Step 9: Commit**
```bash
git add .husky/ commitlint.config.js lint-staged.config.js package.json package-lock.json
git commit -m "chore(deps): add husky + lint-staged + commitlint for commit convention enforcement"
```
---
### Task 14: 填充 tech-debt.md
**Files:**
- Modify: `docs/architecture/roadmap/tech-debt.md`
- [ ] **Step 1: 整理待解决技术债**
从 known-issues 工作经验日志 + 本次诊断补充,整理至少 6 条待解决技术债:
1. scripts 目录混放治理(.sh/.ps1/.js/.mjs/.ts 统一)
2. API 文档自动化(OpenAPI 生成)
3. proctoring 模块的 exams 依赖未被 arch.db 捕获(scanner 改进)
4. FULLTEXT 索引迁移工具化
5. client-error 上报机制完善(Task 12-14)
6. React Flow 移除后的历史包袱清理
7. noUncheckedIndexedAccess 历史债务(本次 P2 解决)
8. coverage 阈值渐进提升(本次设 60% 起步)
- [ ] **Step 2: 写入 tech-debt.md**
每条含:描述、影响、建议方案、优先级(P0/P1/P2)。
- [ ] **Step 3: Commit P1 阶段完成**
```bash
git add docs/architecture/roadmap/tech-debt.md
git commit -m "docs(roadmap): populate tech-debt.md with 8 pending items"
git push
```
---
## 阶段 3: P2 配置升级
### Task 15: 开启 noUncheckedIndexedAccess + 全量修复
**Files:**
- Modify: `tsconfig.json`
- Modify: 多个源文件(修复类型错误)
- [ ] **Step 1: tsconfig.json 开启 noUncheckedIndexedAccess**
```json
"noUncheckedIndexedAccess": true,
```
- [ ] **Step 2: 运行 tsc 收集所有错误**
```bash
npx tsc --noEmit 2>&1 | Select-String "error TS"
```
记录所有错误位置。
- [ ] **Step 3: 批量修复类型错误**
按模块批量修复:
- 数组索引 `arr[0]``arr[0]!`(确认存在)或 `arr.at(0) ?? defaultValue`(可能不存在)
- 对象索引 `obj[key]` → 显式判空 `if (key in obj) { ... }`
- `split`/`match` 结果 → `?? []` 兜底
- `Record<string, T>` 访问 → `T | undefined` 处理
- [ ] **Step 4: 验证 tsc 0 错误**
```bash
npx tsc --noEmit
```
Expected: 0 errors。
- [ ] **Step 5: 验证 lint + arch:scan**
```bash
npm run lint && npm run arch:scan
```
- [ ] **Step 6: Commit**
```bash
git add tsconfig.json src/
git commit -m "refactor(ts): enable noUncheckedIndexedAccess and fix all type errors"
```
---
### Task 16: CI 加 unit test + coverage
**Files:**
- Modify: `.gitea/workflows/ci.yml`
- Modify: `vitest.unit.config.ts`
- Modify: `package.json` (加 engines + packageManager)
- [ ] **Step 1: ci.yml 在 Lint 后、Typecheck 前加 Unit test**
```yaml
- name: Lint
run: npm run lint
- name: Unit tests
run: npm run test:unit
- name: Typecheck
run: npm run typecheck
```
- [ ] **Step 2: vitest.unit.config.ts 加 coverage 配置**
```ts
test: {
name: "unit",
environment: "jsdom",
include: ["src/**/*.test.{ts,tsx}"],
clearMocks: true,
restoreMocks: true,
mockReset: true,
coverage: {
provider: "v8",
reporter: ["text", "json-summary"],
lines: 60,
functions: 60,
branches: 60,
statements: 60,
},
},
```
- [ ] **Step 3: package.json 加 engines + packageManager**
```json
"engines": {
"node": ">=22"
},
"packageManager": "npm@10.9.0",
```
- [ ] **Step 4: 验证 unit test 通过**
```bash
npm run test:unit
```
Expected: 全部 PASS。
- [ ] **Step 5: Commit**
```bash
git add .gitea/workflows/ci.yml vitest.unit.config.ts package.json
git commit -m "ci: add unit test step and coverage threshold to CI"
```
---
### Task 17: /api/health 端点 + Dockerfile HEALTHCHECK
**Files:**
- Create: `src/app/api/health/route.ts`
- Modify: Dockerfile(在 .next/standalone/Dockerfile)
- [ ] **Step 1: 创建 /api/health 路由**
```ts
// src/app/api/health/route.ts
import { NextResponse } from "next/server";
export async function GET(): Promise<NextResponse> {
return NextResponse.json({
status: "ok",
uptime: process.uptime(),
timestamp: new Date().toISOString(),
version: process.env.npm_package_version ?? "unknown",
});
}
```
- [ ] **Step 2: 读取并修改 Dockerfile**
读取 `.next/standalone/Dockerfile`(若不存在,读取项目根 Dockerfile)。加:
```dockerfile
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget -qO- http://localhost:3000/api/health || exit 1
```
- [ ] **Step 3: 验证**
```bash
npx tsc --noEmit && npm run lint
```
- [ ] **Step 4: Commit**
```bash
git add src/app/api/health/ Dockerfile
git commit -m "feat(observability): add /api/health endpoint and Docker HEALTHCHECK"
```
---
### Task 18: bundle-analyzer
**Files:**
- Modify: `package.json` (devDeps + scripts)
- Modify: `next.config.ts`(若存在)或 `next.config.mjs`
- [ ] **Step 1: 安装 @next/bundle-analyzer**
```bash
npm install -D @next/bundle-analyzer
```
- [ ] **Step 2: 配置 next.config**
读取现有 next.config 文件,用 `@next/bundle-analyzer` 包装:
```ts
import bundleAnalyzer from "@next/bundle-analyzer";
const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === "true",
});
export default withBundleAnalyzer(nextConfig);
```
- [ ] **Step 3: package.json 加 analyze 脚本**
```json
"analyze": "ANALYZE=true next build"
```
- [ ] **Step 4: 验证**
```bash
npm run lint
```
- [ ] **Step 5: Commit**
```bash
git add next.config.* package.json package-lock.json
git commit -m "feat(perf): add @next/bundle-analyzer for bundle visualization"
```
---
### Task 19: API 文档
**Files:**
- Create: `docs/api/README.md`
- [ ] **Step 1: 创建 docs/api/README.md**
列出 17 个 API 路由,每个含:方法、路径、用途、请求 schema、响应 schema、权限要求。从 `withApiErrorHandler``ActionState` 推导统一格式。
- [ ] **Step 2: Commit P2 阶段完成**
```bash
git add docs/api/
git commit -m "docs(api): add API documentation for 17 routes"
git push
```
---
### Task 20: 最终验收 + 经验沉淀
- [ ] **Step 1: 全量验证**
```bash
npm run arch:scan
npm run arch:query -- violations
npx tsc --noEmit
npm run lint
npm run test:unit
```
Expected:
- violations: Long files = 0, Server Actions without requirePermission = 0
- tsc: 0 errors
- lint: 0 errors
- test:unit: 全部 PASS
- [ ] **Step 2: 更新 known-issues.md 工作经验日志**
追加本次规范化治理的经验记录。
- [ ] **Step 3: 更新 004(若架构意图变化)**
若 P0 阶段的文件拆分改变了架构描述,更新 `docs/architecture/004_architecture_impact_map.md`
- [ ] **Step 4: 最终 commit + push**
```bash
git add docs/
git commit -m "docs: final validation and experience logging for architecture normalization"
git push
```
---
## 验收标准
| 阶段 | 验收项 | 验证命令 |
|------|--------|---------|
| P0 | 0 个超长文件 + 0 个无权限 Action | `npm run arch:query -- violations` |
| P0 | tsc + lint 0 错误 | `npx tsc --noEmit && npm run lint` |
| P1 | 5 个文档存在 | `ls LICENSE CHANGELOG.md CONTRIBUTING.md SECURITY.md .env.example` |
| P1 | husky 钩子工作 | `git commit -m "bad"` 被拒绝 |
| P1 | tech-debt.md 含 6+ 条 | `Get-Content docs/architecture/roadmap/tech-debt.md \| Measure-Object -Line` |
| P2 | noUncheckedIndexedAccess=true | `cat tsconfig.json \| Select-String noUncheckedIndexedAccess` |
| P2 | CI 跑 unit test | `Select-String "Unit tests" .gitea/workflows/ci.yml` |
| P2 | /api/health 可用 | `curl http://localhost:3000/api/health` |
| P2 | bundle-analyzer 可用 | `npm run analyze` |