docs(architecture): add enterprise architecture normalization design spec
This commit is contained in:
@@ -0,0 +1,304 @@
|
||||
# 企业级架构规范化设计
|
||||
|
||||
> 日期: 2026-07-07
|
||||
> 状态: 已批准
|
||||
> 范围: P0 安全合规 + P1 大仓工程基建 + P2 配置升级
|
||||
> 实施策略: 方案 A(严格分阶段,每阶段独立提交)
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
### 1.1 现状诊断
|
||||
|
||||
通过 `arch:query violations` + 配置文件审查 + 文档检查,发现 14 个维度的企业级/大仓规范缺口,按严重级别分类:
|
||||
|
||||
**P0 严重(影响安全/规范底线)**
|
||||
- 32 个 Server Action 缺 `requirePermission()` 校验(集中在 ai/auth/parent/settings/rbac/onboarding)
|
||||
- 5 个文件超过 1000 行硬性上限(`schema.ts` 2245 行、`invalidation-map.ts` 1195 行、`messaging/actions.ts` 973 行、`textbooks/data-access.ts` 907 行、`questions/data-access.ts` 828 行)
|
||||
|
||||
**P1 工程基建缺口**
|
||||
- 缺大仓标配文档: LICENSE / CHANGELOG.md / CONTRIBUTING.md / SECURITY.md / .env.example
|
||||
- 缺提交期前钩子: 无 husky + lint-staged + commitlint,提交规范仅靠自觉
|
||||
- `docs/architecture/roadmap/tech-debt.md` 是空壳(11 行无内容)
|
||||
|
||||
**P2 配置不完整**
|
||||
- `tsconfig.json` 中 `noUncheckedIndexedAccess: false`
|
||||
- CI 不跑 unit test(`ci.yml` 跳过 `test:unit`)
|
||||
- `package.json` 无 `engines` / `packageManager` 字段
|
||||
- 无 bundle-analyzer / coverage 阈值 / `/api/health` 端点 / API 文档
|
||||
|
||||
### 1.2 目标
|
||||
|
||||
- P0: 消除所有架构违规(`arch:query violations` 输出仅剩已知豁免)
|
||||
- P1: 大仓文档齐全 + 提交规范工具链强制 + tech-debt 可追踪
|
||||
- P2: TypeScript 严格模式全量开启 + CI 覆盖 unit test + 可观测性补充
|
||||
|
||||
---
|
||||
|
||||
## 2. 设计决策
|
||||
|
||||
| 决策点 | 选择 | 理由 |
|
||||
|--------|------|------|
|
||||
| 实施范围 | 全部 P0+P1+P2 | 完整治理 |
|
||||
| 豁免机制 | JSDoc `@public` 标记 | 显式标记、可审计、与代码同源 |
|
||||
| noUncheckedIndexedAccess | 一次性开启+全量修复 | 一步到位,避免债务拖延 |
|
||||
| scripts 治理 | 本次不做,记入 tech-debt | 范围可控 |
|
||||
| 提交钩子 | husky + lint-staged + commitlint | 全套强制规范 |
|
||||
| LICENSE | 专有协议(内部项目) | 不计划开源 |
|
||||
| 实施策略 | 方案 A(严格分阶段) | 每阶段可独立验证+回滚 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 阶段 1: P0 安全合规
|
||||
|
||||
### 3.1 Server Action 权限治理
|
||||
|
||||
**3.1.1 arch:scan 豁免标记机制**
|
||||
|
||||
修改 `scripts/arch-scan/scanner.ts`:扫描函数时识别 JSDoc 中的 `@public` 标记,将该函数标记为 `is_public: true` 存入 `symbols` 表。
|
||||
|
||||
修改 `scripts/arch-scan/query.ts` 的 `violations` 命令:过滤掉 `is_public: true` 的 Server Action。
|
||||
|
||||
**3.1.2 合理豁免清单(加 `@public` 标记,共 ~12 个)**
|
||||
|
||||
| 文件 | 函数 | 豁免理由 |
|
||||
|------|------|---------|
|
||||
| `auth/actions.ts` | registerAction | 注册(登录前) |
|
||||
| `auth/actions.ts` | checkEmailAvailabilityAction | 邮箱可用性检查(注册时) |
|
||||
| `auth/actions.ts` | preflightTwoFactorAction | 2FA 预检(登录中) |
|
||||
| `invitation-codes/actions.ts` | validateInvitationCodeAction | 邀请码校验(注册时) |
|
||||
| `onboarding/actions.ts` | getOnboardingStatusAction | 引导状态查询(登录后但引导前) |
|
||||
| `onboarding/actions.ts` | completeOnboardingAction | 完成引导(引导阶段) |
|
||||
| `settings/actions-security.ts` | preflightTwoFactorAction | 2FA 预检(登录中) |
|
||||
| `settings/actions-security.ts` | verifyTwoFactorForLogin | 2FA 验证(登录中) |
|
||||
| `rbac/actions.ts` | isAdminRole | 内部辅助函数(非对外 Action) |
|
||||
| `shared/lib/audit-logger.ts` | logAudit | 基础设施(非 Action,被 Action 调用) |
|
||||
| `shared/lib/change-logger.ts` | logDataChange | 基础设施(同上) |
|
||||
| `shared/lib/login-logger.ts` | logLoginEvent | 基础设施(同上) |
|
||||
|
||||
**3.1.3 真违规修复清单(补 requirePermission,共 ~20 个)**
|
||||
|
||||
| 文件 | 函数 | 权限点 |
|
||||
|------|------|--------|
|
||||
| `ai/actions.ts` | suggestSimilarQuestionsAction | QUESTION_READ |
|
||||
| `ai/actions.ts` | suggestGradingAction | HOMEWORK_GRADE |
|
||||
| `ai/actions.ts` | generateLessonContentAction | LESSON_PLAN_CREATE |
|
||||
| `ai/actions.ts` | generateQuestionVariantAction | QUESTION_CREATE |
|
||||
| `ai/actions.ts` | analyzeWeaknessAction | DIAGNOSTIC_READ |
|
||||
| `ai/actions.ts` | explainErrorAction | ERROR_BOOK_READ |
|
||||
| `leave-requests/actions.ts` | listMyLeaveRequests | LEAVE_REQUEST_READ(自身) |
|
||||
| `lesson-preparation/actions.ts` | duplicateLessonPlanFormAction | LESSON_PLAN_CREATE |
|
||||
| `parent/actions.ts` | getChildrenAction | PARENT_DASHBOARD_VIEW |
|
||||
| `parent/actions.ts` | getChildBasicInfoAction | PARENT_DASHBOARD_VIEW |
|
||||
| `parent/actions.ts` | getChildDashboardDataAction | PARENT_DASHBOARD_VIEW |
|
||||
| `parent/actions.ts` | getParentDashboardDataAction | PARENT_DASHBOARD_VIEW |
|
||||
| `parent/actions.ts` | getChildNameListAction | PARENT_DASHBOARD_VIEW |
|
||||
| `parent/actions.ts` | verifyParentChildRelationAction | PARENT_DASHBOARD_VIEW |
|
||||
| `settings/actions-service.ts` | updateProfileAction | USER_PROFILE_UPDATE(自身) |
|
||||
| `settings/actions.ts` | getAiProviderSummaries | SYSTEM_SETTINGS_READ |
|
||||
| `settings/actions.ts` | upsertAiProviderAction | SYSTEM_SETTINGS_MANAGE |
|
||||
| `settings/actions.ts` | testAiProviderAction | SYSTEM_SETTINGS_MANAGE |
|
||||
| `settings/actions.ts` | deleteAiProviderAction | SYSTEM_SETTINGS_MANAGE |
|
||||
| `settings/actions.ts` | canConfigurePublicAiProvider | SYSTEM_SETTINGS_READ |
|
||||
|
||||
**注**: 若上述权限点不存在,需在 `shared/types/permissions.ts` 新增并注册到 `ROLE_PERMISSIONS_SEED` + `permission-bitmap.ts` + `permission-catalog.ts` + i18n `rbac.json` 五处。
|
||||
|
||||
### 3.2 超长文件拆分
|
||||
|
||||
**3.2.1 `shared/db/schema.ts` (2245 行) → 按域拆分**
|
||||
|
||||
```
|
||||
src/shared/db/
|
||||
├─ schema.ts # barrel: re-export 所有
|
||||
├─ schema/
|
||||
│ ├─ users.ts # users + userRoles + parentStudentRelations
|
||||
│ ├─ academic.ts # classes + subjects + grades + textbooks + chapters + knowledgePoints
|
||||
│ ├─ exams.ts # exams + examQuestions + homeworkAssignments + submissions + answers
|
||||
│ ├─ grades.ts # gradeRecords + gradeStats
|
||||
│ ├─ attendance.ts # attendanceRecords + attendanceRules
|
||||
│ ├─ messaging.ts # conversations + messages + messageReadStatus
|
||||
│ ├─ notifications.ts # notifications + notificationPreferences
|
||||
│ ├─ audit.ts # auditLogs + changeLogs
|
||||
│ ├─ rbac.ts # roles + permissions + rolePermissions
|
||||
│ ├─ files.ts # files + fileReferences
|
||||
│ ├─ scheduling.ts # classSchedule + timeSlots
|
||||
│ └─ misc.ts # 其余表
|
||||
```
|
||||
|
||||
**3.2.2 `shared/lib/cache/invalidation-map.ts` (1195 行) → 按模块拆分**
|
||||
|
||||
```
|
||||
src/shared/lib/cache/
|
||||
├─ invalidation-map.ts # barrel + registerInvalidationMaps
|
||||
├─ invalidation/
|
||||
│ ├─ exams.ts
|
||||
│ ├─ homework.ts
|
||||
│ ├─ grades.ts
|
||||
│ ├─ attendance.ts
|
||||
│ ├─ messaging.ts
|
||||
│ ├─ textbooks.ts
|
||||
│ └─ ...
|
||||
```
|
||||
|
||||
**3.2.3 `messaging/actions.ts` (973 行) → 按职责拆分**
|
||||
|
||||
```
|
||||
src/modules/messaging/
|
||||
├─ actions.ts # barrel
|
||||
├─ actions-conversations.ts # 会话 CRUD
|
||||
├─ actions-messages.ts # 消息发送/撤回
|
||||
├─ actions-starred.ts # 星标
|
||||
└─ actions-read.ts # 已读状态
|
||||
```
|
||||
|
||||
**3.2.4 `textbooks/data-access.ts` (907 行) → 按实体拆分**
|
||||
|
||||
```
|
||||
src/modules/textbooks/
|
||||
├─ data-access.ts # barrel + 核心
|
||||
├─ data-access-graph.ts # 已有
|
||||
├─ data-access-chapters.ts # 章节 CRUD
|
||||
└─ data-access-knowledge-points.ts # 知识点 CRUD
|
||||
```
|
||||
|
||||
**3.2.5 `questions/data-access.ts` (828 行) → 按职责拆分**
|
||||
|
||||
```
|
||||
src/modules/questions/
|
||||
├─ data-access.ts # barrel + 核心 CRUD
|
||||
└─ data-access-search.ts # FULLTEXT 检索
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 阶段 2: P1 大仓工程基建
|
||||
|
||||
### 4.1 大仓文档
|
||||
|
||||
| 文件 | 内容 |
|
||||
|------|------|
|
||||
| `LICENSE` | "Copyright (c) 2026 EazyGame. 保留所有权利。未经许可不得复制、修改、分发。" |
|
||||
| `CHANGELOG.md` | Keep-a-changelog 格式,初始 `## [1.0.0] - 2026-07-07` 条目(记录文档体系重设计完成) |
|
||||
| `CONTRIBUTING.md` | 分支策略(main 保护) + Conventional Commits + 提交前检查(lint+tsc) + 模块 README 维护规则 + arch:scan 同步 |
|
||||
| `SECURITY.md` | 漏洞报告流程 + 安全联系人 + 响应 SLA + 不公开披露策略 |
|
||||
| `.env.example` | 从 `src/env.mjs` 提取所有键(DATABASE_URL/NEXTAUTH_SECRET/NEXTAUTH_URL/AI keys/...)含注释说明 |
|
||||
|
||||
### 4.2 提交规范工具链
|
||||
|
||||
**4.2.1 依赖安装**
|
||||
|
||||
```bash
|
||||
npm install -D husky lint-staged @commitlint/cli @commitlint/config-conventional @next/bundle-analyzer
|
||||
```
|
||||
|
||||
**4.2.2 配置文件**
|
||||
|
||||
- `.husky/pre-commit`: `npx lint-staged`
|
||||
- `.husky/commit-msg`: `npx commitlint --edit $1`
|
||||
- `commitlint.config.js`: extends conventional + scope-enum(35 模块名)
|
||||
- `lint-staged.config.js`: `*.{ts,tsx}` → `eslint --fix` + `prettier --write`;`*.md` → `prettier --write`
|
||||
- `package.json` scripts: `"prepare": "husky"`
|
||||
|
||||
### 4.3 tech-debt.md 填充
|
||||
|
||||
整理待解决技术债(从 known-issues 工作经验日志 + 本次诊断补充):
|
||||
- scripts 目录混放治理(.sh/.ps1/.js/.mjs/.ts 统一)
|
||||
- API 文档自动化(OpenAPI 生成)
|
||||
- proctoring 模块的 exams 依赖未被 arch.db 捕获(scanner 改进)
|
||||
- FULLTEXT 索引迁移工具化
|
||||
- client-error 上报机制完善(Task 12-14)
|
||||
- React Flow 移除后的历史包袱清理
|
||||
|
||||
---
|
||||
|
||||
## 5. 阶段 3: P2 配置升级
|
||||
|
||||
### 5.1 TypeScript 严格化
|
||||
|
||||
- `tsconfig.json`: `"noUncheckedIndexedAccess": true`
|
||||
- 全量修复类型错误: 数组索引 `arr[0]` → `arr[0]!` 或 `arr.at(0) ?? defaultValue`;对象索引 `obj[key]` → 显式判空
|
||||
|
||||
### 5.2 CI 完善
|
||||
|
||||
**`ci.yml` 修改**:
|
||||
- Lint → **Unit test** → Typecheck → Architecture scan → Integration → E2E → Build
|
||||
|
||||
**`vitest.unit.config.ts` 加 coverage**:
|
||||
```ts
|
||||
coverage: {
|
||||
provider: "v8",
|
||||
lines: 60,
|
||||
functions: 60,
|
||||
branches: 60,
|
||||
statements: 60,
|
||||
}
|
||||
```
|
||||
|
||||
**`package.json` 补充字段**:
|
||||
```json
|
||||
{
|
||||
"engines": { "node": ">=22" },
|
||||
"packageManager": "npm@10.9.0"
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 可观测性补充
|
||||
|
||||
**`/api/health` 路由**:
|
||||
```ts
|
||||
// src/app/api/health/route.ts
|
||||
export async function GET() {
|
||||
return Response.json({
|
||||
status: "ok",
|
||||
uptime: process.uptime(),
|
||||
timestamp: new Date().toISOString(),
|
||||
version: process.env.npm_package_version,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Dockerfile 加 HEALTHCHECK**(审查现有 Dockerfile 后修改):
|
||||
```dockerfile
|
||||
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
|
||||
CMD wget -qO- http://localhost:3000/api/health || exit 1
|
||||
```
|
||||
|
||||
**bundle-analyzer**:
|
||||
- `package.json` scripts: `"analyze": "ANALYZE=true next build"`
|
||||
- `next.config.ts` 包装 `@next/bundle-analyzer`
|
||||
|
||||
### 5.4 API 文档
|
||||
|
||||
`docs/api/README.md`: 17 个路由清单 + 请求/响应 schema 示例(从 `withApiErrorHandler` 与 `ActionState` 推导)
|
||||
|
||||
---
|
||||
|
||||
## 6. 验收标准
|
||||
|
||||
| 阶段 | 验收项 |
|
||||
|------|--------|
|
||||
| P0 | `npm run arch:query -- violations` 仅剩 0 个真违规(豁免项不报);`npx tsc --noEmit` 0 错误;`npm run lint` 0 错误;5 个超长文件全部 < 1000 行 |
|
||||
| P1 | `LICENSE`/`CHANGELOG.md`/`CONTRIBUTING.md`/`SECURITY.md`/`.env.example` 存在;`npm run prepare` 安装 husky 钩子;commitlint 拒绝非规范提交;`tech-debt.md` 含至少 6 条待解决项 |
|
||||
| P2 | `noUncheckedIndexedAccess: true` 且 tsc 0 错误;CI 跑 unit test;`/api/health` 返回 200;`npm run analyze` 可用;`package.json` 含 engines+packageManager |
|
||||
|
||||
---
|
||||
|
||||
## 7. 实施约束
|
||||
|
||||
- 每阶段开始前 `git commit` 一次快照(用户要求"实施前提交备份")
|
||||
- 每阶段完成后 `git commit` + `git push`
|
||||
- 每阶段必须通过 `npx tsc --noEmit` + `npm run lint` + `npm run arch:scan`
|
||||
- 阶段 1 完成后更新 `docs/troubleshooting/known-issues.md` 工作经验日志
|
||||
- 阶段 3 完成后更新 `docs/architecture/004_architecture_impact_map.md`(若架构意图变化)
|
||||
- 全程不改 `project_rules.md` 除非有新的强制规则
|
||||
|
||||
---
|
||||
|
||||
## 8. 不在本次范围
|
||||
|
||||
- scripts 目录治理(记入 tech-debt)
|
||||
- OpenAPI 自动生成(记入 tech-debt,本次仅手写静态文档)
|
||||
- 分支保护策略(Gitea 配置,不在代码层)
|
||||
- 数据库迁移工具化(已有 drizzle-kit)
|
||||
- 客户端错误上报机制完善(Task 12-14,独立项目)
|
||||
Reference in New Issue
Block a user