From beedbaf6866421dc86369322bf03f2ed8bab22b5 Mon Sep 17 00:00:00 2001 From: SpecialX <47072643+wangxiner55@users.noreply.github.com> Date: Wed, 8 Jul 2026 12:53:05 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=90=8C=E6=AD=A5=20P6=20=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=97=A5=E5=BF=97=E3=80=81runbook=20=E4=B8=8E?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - known-issues.md: 追加 9 条 P6 工作经验日志,更新 arch-scan 经验 - post-p6-followup.md: 新增 P6 后续工作手册 runbook - iam/core-edu/content/msg README: 补充健康检查端点说明 --- .../architecture/runbooks/post-p6-followup.md | 1029 +++++++++++++++++ docs/troubleshooting/known-issues.md | 435 +++---- services/content/README.md | 25 +- services/core-edu/README.md | 23 +- services/iam/README.md | 21 +- services/msg/README.md | 35 +- 6 files changed, 1321 insertions(+), 247 deletions(-) create mode 100644 docs/architecture/runbooks/post-p6-followup.md diff --git a/docs/architecture/runbooks/post-p6-followup.md b/docs/architecture/runbooks/post-p6-followup.md new file mode 100644 index 0000000..5d31070 --- /dev/null +++ b/docs/architecture/runbooks/post-p6-followup.md @@ -0,0 +1,1029 @@ +# P6 后续工作手册 + +> 版本:1.0 +> 日期:2026-07-08 +> 状态:待执行 +> 执行环境:`e:\Desktop\Edu` +> 维护者:架构组 +> 关联文档: +> +> - [004 架构影响地图](./architecture/004_architecture_impact_map.md) +> - [项目规则](../../.trae/rules/project_rules.md)(已修复,迁至 `.trae/rules/`) +> - [迁移指南](../../MIGRATION_GUIDE.md) +> - [P6 硬化 Runbook](./runbooks/p6-hardening.md) + +--- + +## 目录 + +1. [执行前必读](#一执行前必读) +2. [环境准备](#二环境准备) +3. [依赖安装](#三依赖安装) +4. [代码质量校验](#四代码质量校验) +5. [arch.db 同步](#五archdb-同步) +6. [修复 project_rules.md(P0 紧急)](#六修复-project_rulesmd-p0-紧急) +7. [修复 004 架构图](#七修复-004-架构图) +8. [P6 集成测试补充](#八p6-集成测试补充) +9. [Helm Chart 演化](#九helm-chart-演化) +10. [文档同步](#十文档同步) +11. [故障排查](#十一故障排查) +12. [验收清单](#十二验收清单) + +--- + +## 一、执行前必读 + +### 1.1 工作目录 + +**所有命令在 `e:\Desktop\Edu` 目录下执行**(除非另注)。 + +```powershell +Set-Location 'e:\Desktop\Edu' +``` + +### 1.2 当前已知问题 + +| # | 问题 | 严重度 | 修复任务 | +| --- | ------------------------------------------------------------------------- | ------ | ---------- | +| 1 | `project_rules.md` 文件损坏(仅 72 字节乱码残片,从 P1 提交时就损坏) | P0 | 第六节 | +| 2 | `004_architecture_impact_map.md` 1.1 架构图只有技术分层图,缺业务领域视角 | P1 | 第七节 | +| 3 | 004 缺少视口四层模型说明(L1 导航/L2 路由/L3 组件/L4 数据) | P1 | 第七节 | +| 4 | P6 暂存文件已部署但未运行依赖安装和 arch:scan | P2 | 第三、五节 | +| 5 | P6 集成测试缺失(熔断器/限流/备份脚本) | P2 | 第八节 | +| 6 | K8s 骨架未演化为 Helm chart | P3 | 第九节 | +| 7 | known-issues 工作经验日志未更新 | P3 | 第十节 | + +### 1.3 工具链版本要求 + +| 工具 | 最低版本 | 验证命令 | +| ------- | -------- | ------------------ | +| Node.js | 20+ | `node --version` | +| pnpm | 9.12+ | `pnpm --version` | +| Go | 1.22+ | `go version` | +| Python | 3.12+ | `python --version` | +| uv | 0.5+ | `uv --version` | + +--- + +## 二、环境准备 + +### 2.1 安装 pnpm(如未安装) + +```powershell +npm install -g pnpm@9.12.0 --registry=https://registry.npmmirror.com +pnpm --version # 验证:应输出 9.12.0 +``` + +### 2.2 安装 Go(如未安装) + +```powershell +winget install GoLang.Go --accept-source-agreements --accept-package-agreements +# 刷新 PATH +$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") +go version # 验证:应输出 go1.26+ windows/amd64 +``` + +### 2.3 安装 uv(如未安装) + +```powershell +pip install uv -i https://pypi.tuna.tsinghua.edu.cn/simple +uv --version # 验证:应输出 uv 0.11+ +``` + +### 2.4 配置镜像加速(推荐) + +```powershell +# npm 镜像 +npm config set registry https://registry.npmmirror.com + +# Go 镜像 +go env -w GOPROXY=https://goproxy.cn,direct +go env -w GOSUMDB=off + +# Python 镜像(uv 会继承 pip 配置) +pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple +``` + +--- + +## 三、依赖安装 + +### 3.1 TypeScript 依赖(pnpm workspace) + +```powershell +Set-Location 'e:\Desktop\Edu' +pnpm install +``` + +**预期**:10 个 workspace 项目(services/_、apps/_、packages/_、scripts/_)依赖安装完成。 + +**验证**: + +```powershell +# 检查 node_modules 是否生成 +Test-Path 'e:\Desktop\Edu\node_modules' # True +Test-Path 'e:\Desktop\Edu\services\classes\node_modules' # True +``` + +### 3.2 Go 依赖(每个 Go 服务独立) + +```powershell +# api-gateway +Set-Location 'e:\Desktop\Edu\services\api-gateway' +go mod tidy + +# push-gateway +Set-Location 'e:\Desktop\Edu\services\push-gateway' +go mod tidy + +# 验证 go.sum 生成 +Test-Path 'e:\Desktop\Edu\services\api-gateway\go.sum' # True +Test-Path 'e:\Desktop\Edu\services\push-gateway\go.sum' # True +``` + +**关键依赖**: + +- `github.com/sony/gobreaker/v2 v2.1.0`(P6 熔断器,已在 go.mod 声明,需 `go mod tidy` 拉取) +- `github.com/gin-gonic/gin v1.10.0` +- `github.com/google/uuid v1.6.0` + +### 3.3 Python 依赖(每个 Python 服务独立) + +```powershell +# data-ana 服务 +Set-Location 'e:\Desktop\Edu\services\data-ana' +uv sync + +# ai 服务 +Set-Location 'e:\Desktop\Edu\services\ai' +uv sync + +# 验证 .venv 生成 +Test-Path 'e:\Desktop\Edu\services\data-ana\.venv' # True +Test-Path 'e:\Desktop\Edu\services\ai\.venv' # True +``` + +### 3.4 protobuf 工具链(可选,如需重新生成代码) + +```powershell +# 安装 buf +npm install -g @bufbuild/buf + +# 生成代码 +Set-Location 'e:\Desktop\Edu\packages\shared-proto' +buf generate +``` + +--- + +## 四、代码质量校验 + +### 4.1 TypeScript 校验 + +```powershell +Set-Location 'e:\Desktop\Edu' +pnpm run lint # ESLint +pnpm run typecheck # tsc --noEmit(各服务独立) +``` + +**预期**:0 errors。如有错误,逐个修复。 + +### 4.2 Go 校验 + +```powershell +Set-Location 'e:\Desktop\Edu\services\api-gateway' +go vet ./... +go build ./... + +Set-Location 'e:\Desktop\Edu\services\push-gateway' +go vet ./... +go build ./... +``` + +### 4.3 Python 校验 + +```powershell +# 安装 ruff(如未安装) +pip install ruff + +Set-Location 'e:\Desktop\Edu\services\data-ana' +ruff check src/ + +Set-Location 'e:\Desktop\Edu\services\ai' +ruff check src/ +``` + +--- + +## 五、arch.db 同步 + +### 5.1 扫描代码结构 + +```powershell +Set-Location 'e:\Desktop\Edu' +pnpm run arch:scan +``` + +**预期**:扫描 TS/Go/Python/Proto 文件,生成 `arch.db`(SQLite)。 + +### 5.2 验证扫描结果 + +```powershell +# 查询统计 +pnpm run arch:query -- stats + +# 查询模块列表 +pnpm run arch:query -- modules + +# 查询架构违规 +pnpm run arch:query -- violations +``` + +**预期**: + +- 模块数 ≥ 10(api-gateway、push-gateway、teacher-bff、iam、classes、core-edu、content、msg、data-ana、ai) +- 符号数 ≥ 100 +- 违规数:记录但暂不强制修复(已知技术债) + +### 5.3 自定义 SQL 查询示例 + +```powershell +# 查询所有服务 +pnpm run arch:query -- sql "SELECT name, language, path FROM modules WHERE type='service'" + +# 查询跨服务依赖 +pnpm run arch:query -- sql "SELECT * FROM dependencies WHERE source_module != target_module" +``` + +--- + +## 六、修复 project_rules.md(P0 紧急) + +### 6.1 问题现状 + +``` +文件路径:e:\Desktop\Edu\project_rules.md +当前大小:72 字节(损坏) +当前内容:4. 若代码结构变??`pnpm run arch:scan` 确认 arch.db 已更?(乱码残片) +Git 历史:从 P1 提交(2ba4250)时就损坏,无完整版本 +``` + +### 6.2 修复方案 + +**目标路径**:`e:\Desktop\Edu\.trae\rules\project_rules.md`(按用户要求放到 `.trae/rules/` 下) + +**内容来源**:从 CICD 项目(`e:\Desktop\CICD\.trae\rules\project_rules.md`)的完整版迁移,按 MIGRATION_GUIDE 4.1 策略矩阵调整。 + +### 6.3 调整对照表 + +| 章节 | CICD 原版 | Edu 微服务版调整 | +| ----------------- | ----------------------------- | ---------------------------------------------------------- | +| 架构图优先规则 | `npm run arch:scan` | `pnpm run arch:scan`(多语言扫描器) | +| 架构文档清单 | 004/006/007/008/roadmap/audit | 004 + roadmap/ + runbooks/ + 0010 蓝图 | +| arch.db 规则 | TS 单语言 | TS + Go + Python + Proto 多语言 | +| 编码规范-架构分层 | `app → modules → shared` 三层 | Gateway → BFF → Services → Data 微服务分层 | +| 编码规范-模块结构 | `src/modules/[module]/` | `services/[service]/src/[domain]/` DDD 限界上下文 | +| TypeScript 规则 | `cacheFn` + Server Action | NestJS Cache + `@RequirePermission()` 装饰器 | +| 命名规范 | 单语言 | 多语言(TS/Go/Python 各自规范) | +| 安全规范 | NextAuth + httpOnly Cookie | JWT RS256 + httpOnly Cookie + Gateway 校验 | +| 新增规则 | — | 契约先行(protobuf+buf)、Outbox 事件驱动、多语言 monorepo | +| 命令前缀 | `npm run` | `pnpm run`(TS)/ `go mod`(Go)/ `uv`(Python) | + +### 6.4 执行步骤 + +```powershell +# 1. 创建 .trae/rules 目录 +New-Item -Path 'e:\Desktop\Edu\.trae\rules' -ItemType Directory -Force + +# 2. 参考 CICD 完整版(可读取参考) +# CICD 源文件:e:\Desktop\CICD\.trae\rules\project_rules.md + +# 3. 创建 Edu 版 project_rules.md(内容见 6.5 节模板) +# 用编辑器创建 e:\Desktop\Edu\.trae\rules\project_rules.md + +# 4. 删除损坏的根目录 project_rules.md +Remove-Item 'e:\Desktop\Edu\project_rules.md' -Force + +# 5. 更新所有引用(004、MIGRATION_GUIDE 等) +# 搜索:project_rules.md +# 替换:.trae/rules/project_rules.md +``` + +### 6.5 project_rules.md 模板结构 + +完整内容应包含以下 13 章(参照 CICD 版调整): + +```markdown +# Edu 项目规则(微服务版) + +## 1. 架构图优先规则 + +- 任何任务开始前,先查 004_architecture_impact_map.md +- 运行 `pnpm run arch:scan` 更新 arch.db +- 改码必同步图 + +## 2. 架构元数据库规则(arch.db) + +- 多语言扫描:TS (ts-morph) + Go (tree-sitter-go) + Python (tree-sitter-python) + Proto +- 查询命令:`pnpm run arch:query -- ` + +## 3. 架构文档清单 + +| 文档 | 用途 | +| ------------------------------------------------ | ------------ | +| docs/architecture/004_architecture_impact_map.md | 架构设计意图 | +| docs/architecture/0010_architecture.md | 理想蓝图 | +| docs/architecture/roadmap/ | 长远规划 | +| docs/architecture/runbooks/ | 运维手册 | +| docs/troubleshooting/known-issues.md | 已知问题速查 | + +## 4. 编码规范 + +### 4.1 架构分层规则 + +- Gateway → BFF → Services → Data 微服务分层 +- 依赖方向单向:Gateway → BFF → Services → Data +- 服务间通过 protobuf gRPC 通信,禁止直接访问对方 DB + +### 4.2 模块标准结构(DDD) + +services/[service]/src/ +├─ [domain]/ # 限界上下文 +│ ├─ *.controller.ts # Controller(HTTP/gRPC 入口) +│ ├─ *.service.ts # Application Service(编排层) +│ ├─ *.repository.ts # Repository(数据访问) +│ ├─ *.schema.ts # Zod 验证 +│ └─ *.dto.ts # DTO +├─ config/ # 配置 +├─ shared/ # 共享(errors/observability/health/lifecycle) +└─ main.ts # 入口 + +### 4.3 TypeScript 规则 + +- 禁止 any:用 unknown + 类型守卫 +- 禁止 as 断言(除 unknown 转换) +- 函数返回值必须显式标注 Promise +- import type 用于类型导入 +- ESM .js 后缀(NestJS ESM 模式) + +### 4.4 Go 规则 + +- 包名小写单词 +- 导出函数必须有 doc comment +- 错误处理:if err != nil { return err } +- 不使用 interface{},用 any(Go 1.18+) + +### 4.5 Python 规则 + +- 类型注解强制 +- FastAPI 路由用 APIRouter +- 异步优先:async def + +### 4.6 命名规范 + +- 目录:kebab-case +- TS 组件:PascalCase +- Go 文件:lowercase +- Python 文件:snake_case +- 常量:UPPER_SNAKE_CASE + +### 4.7 Server Action → Controller 规范 + +- 每个 Controller 方法必须用 @RequirePermission() 装饰器 +- 输入用 Zod / class-validator 验证 +- 返回 protobuf message(结构同 ActionState) +- 错误统一走 GlobalErrorFilter + +### 4.8 Tailwind 规范 + +- 使用 cn() 工具函数 +- 禁止字符串拼接动态类名 +- 设计令牌在 packages/shared-tokens/ + +### 4.9 设计令牌规范 + +- 禁止硬编码颜色 #hex +- 禁止硬编码字体 'Inter'/'Fraunces' +- 禁止 Tailwind 任意值 w-[Npx] +- 三层令牌:primitive → semantic → tailwind-theme + +## 5. 安全规范 + +- JWT RS256(IAM 签发,Gateway 公钥校验) +- httpOnly + Secure + SameSite=Strict Cookie +- 服务端环境变量不加 NEXT_PUBLIC_ +- 禁止 dangerouslySetInnerHTML(如必须,先用 DOMPurify 清洗) + +## 6. 契约规范 + +- 契约先行:protobuf + buf v2 +- 服务间通信先定 .proto,再写实现 +- buf lint STANDARD 规则 +- buf breaking FILE 级别检查 + +## 7. 事件驱动规范 + +- Outbox 模式:事务内写业务+outbox,独立 publisher 投递到 Kafka +- 事件命名:.(如 ExamCreated) +- TOPIC_MAP 路由:每个服务维护事件→topic 映射 +- 幂等性:Kafka producer idempotent + transactionalId + +## 8. 提交规范 + +- Conventional Commits: feat(scope): description +- scope: 服务名或包名(iam/classes/core-edu/content/msg/ai/api-gateway/...) +- 提交前必须运行 pnpm run lint + pnpm run typecheck + +## 9. Git 工作流 + +- trunk-based 分支策略 +- commitlint + husky 强制校验 +- 直接提交 main 分支(小项目),大型团队改 PR + +## 10. 问题记录规则 + +- 所有工作完成后,记录到 docs/troubleshooting/known-issues.md +- 索引式表格:场景→技术/规则映射 +- 工作经验日志:日期+模块+做了什么+学到什么 + +## 11. AI 工作强制流程 + +### 阶段 1: 上下文加载 + +1. `pnpm run arch:scan` 更新 arch.db +2. `pnpm run arch:query -- module-deps` 查模块依赖 +3. 阅读 services/[service]/README.md +4. 查 known-issues.md 相关经验 + +### 阶段 2: 执行工作 + +1. 按规划执行 +2. 修改代码后立即 `pnpm run arch:scan` +3. 运行 pnpm run lint + pnpm run typecheck + +### 阶段 3: 经验沉淀 + +1. 在 known-issues.md 追加工作经验日志 +2. 若发现新场景→技术映射,提炼到对应分区 +3. 若架构变化,更新 004 + +## 12. 多语言 monorepo 规则 + +- pnpm workspace(TS)+ go.work(Go)+ pyproject.toml(uv) +- 共享包:packages/shared-proto / shared-ts / shared-go / shared-py +- 跨语言契约通过 protobuf + +## 13. 可观测性规范 + +- 日志:pino(TS)/ zap(Go)/ structlog(Python) +- 指标:prom-client(TS)/ prometheus(Go)/ prometheus-client(Python) +- 链路:OpenTelemetry SDK + OTLP exporter +- 三支柱必须同时启用 +``` + +### 6.6 验证 + +```powershell +# 验证文件存在且内容完整 +Get-Item 'e:\Desktop\Edu\.trae\rules\project_rules.md' | Select-Object Name, Length +# 预期:Length > 5000 字节 + +# 验证根目录损坏文件已删除 +Test-Path 'e:\Desktop\Edu\project_rules.md' # False + +# 搜索所有引用并更新 +Get-ChildItem 'e:\Desktop\Edu' -Recurse -Include '*.md' | Select-String -Pattern 'project_rules.md' | Select-Object Path, LineNumber, Line +``` + +--- + +## 七、修复 004 架构图 + +### 7.1 问题现状 + +- **1.1 系统边界图**:只有一张技术分层图,Users 层按角色分(Teacher/Student/Parent/Admin) +- **缺业务领域视角**:MIGRATION_GUIDE 3.2 和 0010 蓝图用的是 6 业务领域(身份/教学组织/教学核心/内容分析/沟通/智能洞察) +- **5.x 认证与权限**:缺视口四层模型说明 + +### 7.2 修复方案:双图并存 + +#### 7.2.1 保留 1.1a 技术分层图(修改注释) + +将当前 1.1 改名为 **1.1a 技术分层视角**,修改: + +- Users 层标注改为"场景域用户"(不是角色分类) + - TeacherPortal 标注"教学场景域(教师/教导主任/教研组长共用)" + - StudentPortal 标注"学习场景域" + - ParentPortal 标注"家长场景域" + - AdminPortal 标注"管理场景域" +- BFF 层标注"按使用场景域分 BFF(不是按角色分)" +- 添加说明:"本图展示部署分层结构。业务领域视角见 1.1b" + +#### 7.2.2 新增 1.1b 业务领域视角图 + +在 1.1a 后新增 **1.1b 业务领域视角**,mermaid 图: + +```mermaid +graph TB + subgraph D1["身份认证领域"] + IAM[iam 服务] + IAM_M[users / roles / permissions
refresh_tokens / sessions] + end + + subgraph D2["教学组织领域"] + ORG[core-edu 服务
classes 模块] + ORG_M[classes / subjects / enrollment] + end + + subgraph D3["教学核心领域"] + TEACH[core-edu 服务
exams/homework/grades] + TEACH_M[exams / homework / grades
courses / lessons / schedule / attendance] + end + + subgraph D4["内容资源领域"] + CONTENT[content 服务] + CONTENT_M[textbooks / knowledge-points
questions / grading / search(ES)] + end + + subgraph D5["沟通通知领域"] + MSG[msg 服务] + MSG_M[messaging / notifications / announcements] + end + + subgraph D6["智能洞察领域"] + DATA[data-ana 服务] + AI[ai 服务] + DATA_M[analytics / dashboard / diagnostic(ClickHouse)] + AI_M[ai 备课/出题/分析 / search] + end + + D1 --> D2 + D1 --> D3 + D1 --> D4 + D1 --> D5 + D2 --> D3 + D3 --> D4 + D3 --> D5 + D4 --> D6 + D3 --> D6 +``` + +#### 7.2.3 1.2 服务清单表格新增"业务领域"列 + +在 1.2 服务清单表格中,"限界上下文"列后新增"业务领域"列: + +| 类别 | 服务名 | 语言/框架 | 限界上下文 | **业务领域** | 阶段 | +| -------- | ----------- | ---------------- | ---------- | ----------------------- | ---- | +| 基础设施 | api-gateway | Go (Gin) | 网关 | — | P1 | +| BFF | teacher-bff | TS (NestJS) | 教师聚合 | 教学场景域 | P2 | +| 业务 | iam | TS (NestJS) | 身份认证 | **身份认证** | P2 | +| 业务 | core-edu | TS (NestJS) | 教学核心 | **教学组织 + 教学核心** | P3 | +| 业务 | content | TS (NestJS) | 内容资源 | **内容资源** | P4 | +| 业务 | data-ana | Python (FastAPI) | 数据分析 | **智能洞察** | P4 | +| 业务 | msg | TS (NestJS) | 消息通知 | **沟通通知** | P5 | +| 业务 | ai | Python (FastAPI) | AI 网关 | **智能洞察** | P5 | +| ... | ... | ... | ... | ... | ... | + +#### 7.2.4 新增 5.4 视口四层模型 + +在 5.3 DataScope 6 级数据范围后,新增 **5.4 视口四层模型**: + +```markdown +### 5.4 视口四层模型 + +视口(Viewport)是用户在特定场景域下的可见范围。视口既可独立配置(RoleViewport 表), +也可由权限推导(permission → viewport 默认映射)。新角色只需配置权限集,视口自动推导; +需要差异化时再显式配置视口。 + +| 层级 | 含义 | 配置载体 | 示例 | +| ------- | ---------------- | ---------------------------------- | -------------------------------------------- | +| L1 导航 | 侧边栏菜单项 | navigation_config 表 | 教导主任看到"全校成绩分析"菜单 | +| L2 路由 | 可访问路由 | route_permission 表 + Gateway 校验 | 教导主任可访问 /admin/grade-analysis | +| L3 组件 | 页面内组件可见性 | usePermission().hasPermission() | 教导主任看到"导出全校报表"按钮 | +| L4 数据 | 数据行级过滤 | DataScope 枚举 | 教导主任 DataScope=grade_managed(管辖年级) | + +**场景域 BFF 复用策略**: + +按"使用场景域"分 BFF,而非按角色分。新角色复用现有 BFF,通过视口差异化。 + +| BFF | 场景域 | 复用角色 | +| ----------- | -------- | ------------------------ | +| Teacher BFF | 教学场景 | 教师、教导主任、教研组长 | +| Student BFF | 学习场景 | 学生 | +| Parent BFF | 家长场景 | 家长 | +| Admin BFF | 管理场景 | 系统管理员、校管理员 | + +**实现**:教导主任归入 Teacher BFF + 额外管理视口(L1 导航增加管理菜单项,L4 数据范围扩大到年级)。 + +**iam 服务职责**: + +- 认证:登录/登出/JWT/2FA +- RBAC:角色/权限/角色-权限映射 CRUD +- 视口配置:导航/路由/组件级视口配置 CRUD +- DataScope:数据范围解析(all/grade_managed/class_taught/children/owned + 自定义) +- 权限解析 API:getEffectivePermissions(userId) → {permissions, viewports, dataScope} +``` + +### 7.3 执行步骤 + +```powershell +# 1. 编辑 004_architecture_impact_map.md +# 路径:e:\Desktop\Edu\docs\architecture\004_architecture_impact_map.md + +# 2. 按上述方案修改: +# - 1.1 → 1.1a(改注释) +# - 新增 1.1b(业务领域图) +# - 1.2 表格新增"业务领域"列 +# - 5.3 后新增 5.4(视口四层模型) + +# 3. 验证 mermaid 语法 +# 可在 VS Code 安装 Mermaid Previewer 插件预览 + +# 4. 更新目录(如有章节编号变化) +``` + +--- + +## 八、P6 集成测试补充 + +### 8.1 熔断器状态机测试 + +**文件**:`services/api-gateway/internal/middleware/circuit-breaker_test.go` + +```go +package middleware + +import ( + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/gin-gonic/gin" +) + +func TestCircuitBreaker_ClosedToOpen(t *testing.T) { + // 模拟 5 秒窗口内错误率 > 50%,验证状态从 CLOSED → OPEN +} + +func TestCircuitBreaker_OpenToHalfOpen(t *testing.T) { + // 验证 OPEN 状态 30 秒后转 HALF_OPEN,允许 1 个探测请求 +} + +func TestCircuitBreaker_HalfOpenToClosed(t *testing.T) { + // 验证 HALF_OPEN 探测成功后转 CLOSED +} + +func TestCircuitBreaker_HalfOpenToOpen(t *testing.T) { + // 验证 HALF_OPEN 探测失败后转 OPEN +} + +func TestCircuitBreaker_4xxNotCounted(t *testing.T) { + // 验证 4xx 响应不计入熔断失败计数 +} +``` + +### 8.2 限流桶测试 + +**文件**:`services/api-gateway/internal/middleware/ratelimit_test.go` + +```go +package middleware + +import ( + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/gin-gonic/gin" +) + +func TestRateLimit_AllowUnderBurst(t *testing.T) { + // 验证突发请求 ≤ burst 时全部放行 +} + +func TestRateLimit_RejectOverBurst(t *testing.T) { + // 验证突发请求 > burst 时返回 429 +} + +func TestRateLimit_RefillTokens(t *testing.T) { + // 验证令牌按 rps 速率补充 +} + +func TestRateLimit_PerIPIsolation(t *testing.T) { + // 验证不同 IP 的桶相互独立 +} + +func TestRateLimit_CleanupExpiredBuckets(t *testing.T) { + // 验证 10 分钟无访问的桶被清理 +} +``` + +### 8.3 备份脚本 dry-run 测试 + +**文件**:`infra/backup/test-backup-mysql.sh` + +```bash +#!/bin/bash +# dry-run 模式测试备份脚本 +# 不实际执行 mysqldump,仅验证参数解析和路径创建 + +set -euo pipefail + +# 测试 --service 参数缺失 +./backup-mysql.sh 2>&1 | grep -q "service is required" + +# 测试 --keep 默认值 +./backup-mysql.sh --service test 2>&1 | grep -q "keep=7" + +# 测试无效 service +./backup-mysql.sh --service invalid 2>&1 | grep -q "unknown service" +``` + +### 8.4 执行测试 + +```powershell +# Go 测试 +Set-Location 'e:\Desktop\Edu\services\api-gateway' +go test ./internal/middleware/... -v -cover + +# Shell 测试(需要 bash 环境,如 WSL 或 Git Bash) +# bash infra/backup/test-backup-mysql.sh +``` + +--- + +## 九、Helm Chart 演化 + +### 9.1 现状 + +`infra/k8s/` 下有骨架 manifest: + +- `namespace.yaml`:4 命名空间 +- `api-gateway-deployment.yaml`:Deployment + Service + +### 9.2 演化路线 + +``` +infra/k8s/ +├─ namespace.yaml # 保留(基础资源) +├─ api-gateway-deployment.yaml # 删除(迁移到 Helm) +└─ helm/ # 新增 + ├─ edu-platform/ # 平台级 chart + │ ├─ Chart.yaml + │ ├─ values.yaml # 全局默认值 + │ ├─ values-dev.yaml # 开发环境覆盖 + │ ├─ values-staging.yaml + │ ├─ values-prod.yaml + │ └─ templates/ + │ ├─ namespace.yaml + │ ├─ configmap.yaml + │ ├─ secret.yaml + │ ├─ ingress.yaml + │ └─ hpa.yaml + ├─ api-gateway/ # 服务级 chart + │ ├─ Chart.yaml + │ ├─ values.yaml + │ └─ templates/ + │ ├─ deployment.yaml + │ ├─ service.yaml + │ ├─ configmap.yaml + │ └─ hpa.yaml + ├─ iam/ + ├─ core-edu/ + ├─ content/ + ├─ msg/ + ├─ data-ana/ + └─ ai/ +``` + +### 9.3 执行步骤 + +```powershell +# 1. 安装 Helm(如未安装) +winget install Helm.Helm + +# 2. 创建 chart 骨架 +Set-Location 'e:\Desktop\Edu\infra\k8s' +New-Item -Path 'helm' -ItemType Directory +helm create helm/edu-platform +helm create helm/api-gateway +# ... 其他服务 + +# 3. 将现有 manifest 迁移到 templates/ +# 4. 参数化 values.yaml +# 5. 验证 +helm lint helm/api-gateway +helm template helm/api-gateway +``` + +--- + +## 十、文档同步 + +### 10.1 更新 known-issues.md + +**文件**:`docs/troubleshooting/known-issues.md` + +在"工作经验日志"区追加: + +```markdown +### 2026-07-08 P6 后续工作 + +| 日期 | 模块 | 做了什么 | 学到什么 | +| ---------- | ----------- | -------------------------------------- | -------------------------------------------------------------- | +| 2026-07-08 | 全局 | pnpm install 失败(ENOENT _tmp_ 文件) | pnpm 在 Windows 下需配置 PNPM_HOME 和 TMP 环境变量 | +| 2026-07-08 | 全局 | project_rules.md 损坏(72 字节乱码) | 迁移文件后必须验证完整性,git commit 前运行 cat 检查 | +| 2026-07-08 | api-gateway | gobreaker v2.1.0 版本验证 | GitHub releases 页面确认版本存在性,避免 go mod tidy 失败 | +| 2026-07-08 | 004 | 架构图视角讨论(技术分层 vs 业务领域) | 双图并存:1.1a 技术分层(部署视角)+ 1.1b 业务领域(DDD 视角) | +| 2026-07-08 | 004 | 视口四层模型补充 | L1 导航/L2 路由/L3 组件/L4 数据,场景域 BFF 复用策略 | +``` + +### 10.2 同步 004 架构文档 + +按第七节修复后,检查 004 其他章节是否需要同步更新: + +- 1.2 服务清单:新增"业务领域"列 +- 5.4 视口四层模型:新增章节 +- 14 附录:确认 6 阶段路线图与实际交付一致 + +### 10.3 更新模块 README + +检查 9 个服务 README 是否需要补充: + +- P6 新增的 health/lifecycle 模块说明 +- 中间件链说明(api-gateway) +- 健康检查端点说明(所有服务) + +--- + +## 十一、故障排查 + +### 11.1 pnpm install 失败 + +**错误**:`ENOENT: no such file or directory, open 'E:\Desktop\Edu\_tmp_xxxxx'` + +**原因**:pnpm 在 Windows 下临时文件路径问题。 + +**解决**: + +```powershell +# 设置环境变量 +$env:PNPM_HOME = 'C:\Users\xiner\.pnpm' +$env:TMP = 'C:\Users\xiner\AppData\Local\Temp' +$env:TEMP = 'C:\Users\xiner\AppData\Local\Temp' + +# 重试 +pnpm install +``` + +### 11.2 pnpm "This project is configured to use npm" + +**错误**:`ERROR This project is configured to use npm` + +**原因**:项目根有 `.npmrc` 或 package.json 的 `packageManager` 字段冲突。 + +**解决**: + +```powershell +# 检查 .npmrc +Get-Content 'e:\Desktop\Edu\.npmrc' -ErrorAction SilentlyContinue + +# 如有 .npmrc 含 "package-manager=npm",删除或改为 pnpm +# 或强制使用 pnpm +pnpm install --ignore-scripts +``` + +### 11.3 go mod tidy 网络失败 + +**错误**:`go: module github.com/sony/gobreaker/v2: downloading ... timeout` + +**解决**: + +```powershell +go env -w GOPROXY=https://goproxy.cn,direct +go env -w GOSUMDB=off +go mod tidy +``` + +### 11.4 uv sync 失败 + +**错误**:`error: Failed to download ...` + +**解决**: + +```powershell +# 配置镜像 +$env:UV_INDEX_URL = 'https://pypi.tuna.tsinghua.edu.cn/simple' +uv sync +``` + +### 11.5 arch:scan 报错 + +**错误**:`Error: Cannot find module 'tsx'` + +**解决**: + +```powershell +# 确保 tsx 已安装 +pnpm add -D tsx + +# 或用 npx +npx tsx scripts/arch-scan/scanner.ts +``` + +--- + +## 十二、验收清单 + +完成所有任务后,逐项验证: + +### 12.1 环境与依赖 + +- [ ] `pnpm --version` 输出 9.12+ +- [ ] `go version` 输出 1.22+ +- [ ] `uv --version` 输出 0.5+ +- [ ] `pnpm install` 成功,无错误 +- [ ] `go mod tidy`(api-gateway、push-gateway)成功 +- [ ] `uv sync`(data-ana、ai)成功 + +### 12.2 代码质量 + +- [ ] `pnpm run lint` 0 errors +- [ ] `pnpm run typecheck` 0 errors +- [ ] `go vet ./...`(api-gateway、push-gateway)0 errors +- [ ] `ruff check src/`(data-ana、ai)0 errors + +### 12.3 arch.db + +- [ ] `pnpm run arch:scan` 成功 +- [ ] `pnpm run arch:query -- stats` 输出模块数 ≥ 10 +- [ ] arch.db 文件存在且非空 + +### 12.4 project_rules.md 修复 + +- [ ] `.trae/rules/project_rules.md` 存在且 > 5000 字节 +- [ ] 根目录 `project_rules.md` 已删除 +- [ ] 所有引用已更新为 `.trae/rules/project_rules.md` + +### 12.5 004 架构图修复 + +- [ ] 1.1a 技术分层图保留,注释改为"场景域用户" +- [ ] 1.1b 业务领域视角图新增(6 领域) +- [ ] 1.2 服务清单新增"业务领域"列 +- [ ] 5.4 视口四层模型章节新增 +- [ ] mermaid 语法正确 + +### 12.6 P6 测试 + +- [ ] circuit-breaker_test.go 5 个测试用例通过 +- [ ] ratelimit_test.go 5 个测试用例通过 +- [ ] backup 脚本 dry-run 测试通过 + +### 12.7 文档同步 + +- [ ] known-issues.md 追加 P6 工作经验日志 +- [ ] 9 个服务 README 检查并补充(如需要) + +### 12.8 Git 提交 + +- [ ] 所有修复提交到 main 分支 +- [ ] 提交信息:`fix(docs): repair project_rules.md and 004 architecture diagrams` +- [ ] 推送到远程 + +--- + +## 附录:快速执行脚本 + +如需一键执行所有任务,可创建 PowerShell 脚本: + +```powershell +# e:\Desktop\Edu\scripts\post-p6-followup.ps1 +# 在 Edu 目录下运行:.\scripts\post-p6-followup.ps1 + +Set-Location 'e:\Desktop\Edu' +$ErrorActionPreference = 'Continue' + +Write-Host '=== 1. 依赖安装 ===' -ForegroundColor Cyan +pnpm install +Set-Location 'services\api-gateway'; go mod tidy; Set-Location '..\..' +Set-Location 'services\push-gateway'; go mod tidy; Set-Location '..\..' +Set-Location 'services\data-ana'; uv sync; Set-Location '..\..' +Set-Location 'services\ai'; uv sync; Set-Location '..\..' + +Write-Host '=== 2. arch.db 同步 ===' -ForegroundColor Cyan +pnpm run arch:scan +pnpm run arch:query -- stats + +Write-Host '=== 3. 代码质量校验 ===' -ForegroundColor Cyan +pnpm run lint +pnpm run typecheck + +Write-Host '=== 完成 ===' -ForegroundColor Green +Write-Host '注意:project_rules.md 和 004 架构图修复需手动执行(见文档第六、七节)' +``` + +--- + +**文档结束。按顺序执行各节任务,遇问题参考第十一节故障排查。** diff --git a/docs/troubleshooting/known-issues.md b/docs/troubleshooting/known-issues.md index 9b790cf..5bd7640 100644 --- a/docs/troubleshooting/known-issues.md +++ b/docs/troubleshooting/known-issues.md @@ -1,7 +1,7 @@ # 已知问题速查 > 索引式速查手册:场景 → 技术/规则映射。不写代码示例。 -> 架构规则见 [../architecture/004_architecture_impact_map.md](../architecture/004_architecture_impact_map.md) 与 [../../project_rules.md](../../project_rules.md) +> 架构规则见 [../architecture/004_architecture_impact_map.md](../architecture/004_architecture_impact_map.md) 与 [../../.trae/rules/project_rules.md](../../.trae/rules/project_rules.md) > 工作经验日志按时间倒序追加(50 条上限),AI 发现更好方案时可更新本节。 --- @@ -10,140 +10,140 @@ ### 1.1 多语言 monorepo 配置 -| 场景 | 技术/规则 | -| ----------------------------- | ------------------------------------------------------------------------------------------ | -| 多语言 workspace | pnpm workspace(TS)+ go.work(Go)+ pyproject.toml/uv workspace(Python)三套并存 | -| 根 package.json scripts | 封装多语言命令入口:`pnpm dev` / `pnpm lint` / `pnpm test` / `pnpm build` | -| pnpm-workspace.yaml | 仅声明 TS 包路径(packages/*、services/classes、bff/*、apps/*、scripts/*),Go/Python 不入 | -| go.work | 列出所有 Go 服务模块(services/api-gateway、services/push-gateway) | -| pyproject.toml | uv workspace members 列 Python 服务(services/data-ana、services/ai-gateway) | -| 跨语言共享类型 | protobuf 生成三端代码(TS/Go/Python),单一契约源 | -| tsx 执行 TS 脚本 | arch-scan 等工具脚本用 `tsx` 直接运行,无需编译 | -| husky + commitlint | pre-commit 跑 eslint+prettier,commit-msg 校验 Conventional Commits | -| .editorconfig 多语言缩进 | Go 用 tab,Python 用 4 空格,TS/默认用 2 空格 | +| 场景 | 技术/规则 | +| ------------------------ | ------------------------------------------------------------------------------------------ | +| 多语言 workspace | pnpm workspace(TS)+ go.work(Go)+ pyproject.toml/uv workspace(Python)三套并存 | +| 根 package.json scripts | 封装多语言命令入口:`pnpm dev` / `pnpm lint` / `pnpm test` / `pnpm build` | +| pnpm-workspace.yaml | 仅声明 TS 包路径(packages/_、services/classes、bff/_、apps/_、scripts/_),Go/Python 不入 | +| go.work | 列出所有 Go 服务模块(services/api-gateway、services/push-gateway) | +| pyproject.toml | uv workspace members 列 Python 服务(services/data-ana、services/ai-gateway) | +| 跨语言共享类型 | protobuf 生成三端代码(TS/Go/Python),单一契约源 | +| tsx 执行 TS 脚本 | arch-scan 等工具脚本用 `tsx` 直接运行,无需编译 | +| husky + commitlint | pre-commit 跑 eslint+prettier,commit-msg 校验 Conventional Commits | +| .editorconfig 多语言缩进 | Go 用 tab,Python 用 4 空格,TS/默认用 2 空格 | ### 1.2 Docker Compose 基础设施 -| 场景 | 技术/规则 | -| ---------------------------- | ---------------------------------------------------------------------------------- | -| 日常开发启动 | 用 `docker-compose.minimal.yml` 仅起 MySQL+Redis | -| 全量启动内存不足 | 按 `profiles` 分阶段启用(full/kafka/cdc/analytics/graph/search/config/observability) | -| 每服务 mem_limit | 避免单服务吃满内存:MySQL 512m、Redis 128m、Kafka 512m、ClickHouse 1g | -| 按阶段启用容器 | P1 仅 MySQL+Redis;P3 加 Kafka+Zookeeper;P4 加 Debezium+CH+Neo4j;P5 加 ES;P6 加 Consul+Istio | -| MySQL 初始化 | `init-sql/01-init.sql` 挂载到 `/docker-entrypoint-initdb.d:ro` | -| healthcheck | MySQL 用 `mysqladmin ping`,Redis 用 `redis-cli ping` | -| Windows 下卷挂载 | init-sql 用绝对路径或确保相对路径正确 | -| 容器名固定 | `container_name: edu-mysql` 便于服务连接配置 | +| 场景 | 技术/规则 | +| ---------------- | ----------------------------------------------------------------------------------------------- | +| 日常开发启动 | 用 `docker-compose.minimal.yml` 仅起 MySQL+Redis | +| 全量启动内存不足 | 按 `profiles` 分阶段启用(full/kafka/cdc/analytics/graph/search/config/observability) | +| 每服务 mem_limit | 避免单服务吃满内存:MySQL 512m、Redis 128m、Kafka 512m、ClickHouse 1g | +| 按阶段启用容器 | P1 仅 MySQL+Redis;P3 加 Kafka+Zookeeper;P4 加 Debezium+CH+Neo4j;P5 加 ES;P6 加 Consul+Istio | +| MySQL 初始化 | `init-sql/01-init.sql` 挂载到 `/docker-entrypoint-initdb.d:ro` | +| healthcheck | MySQL 用 `mysqladmin ping`,Redis 用 `redis-cli ping` | +| Windows 下卷挂载 | init-sql 用绝对路径或确保相对路径正确 | +| 容器名固定 | `container_name: edu-mysql` 便于服务连接配置 | ### 1.3 protobuf + buf 契约 -| 场景 | 技术/规则 | -| ----------------------- | ------------------------------------------------------------------------------------------ | -| 契约唯一源 | `packages/shared-proto/proto/*.proto`,禁止 REST/gRPC 混用 | -| breaking change 检测 | `buf breaking --against '.git#branch=main'` CI 强制,必须升版本号 | -| proto 版本化 | 包名带版本 `xxx.v1`、`xxx.v2`,新旧版本共存 | -| buf lint | `buf lint` 使用 STANDARD 规则集,可按需 except(如 PACKAGE_VERSION_SUFFIX) | -| 三端代码生成 | `buf.gen.yaml` 配置 TS/Go/Python 插件,`pnpm proto:gen` 统一生成 | -| 契约先行硬约束 | 任何服务间通信必须先改 proto,CI 检测 breaking change 阻止违规合并 | -| 事件 schema 版本化 | Kafka 事件带 `schema_version` 字段,消费端按版本处理 | -| 错误码定义在 proto | 错误码三端共享,格式 `{服务前缀}_{错误类型}_{资源}`(如 `IAM_PERM_USER`) | +| 场景 | 技术/规则 | +| -------------------- | --------------------------------------------------------------------------- | +| 契约唯一源 | `packages/shared-proto/proto/*.proto`,禁止 REST/gRPC 混用 | +| breaking change 检测 | `buf breaking --against '.git#branch=main'` CI 强制,必须升版本号 | +| proto 版本化 | 包名带版本 `xxx.v1`、`xxx.v2`,新旧版本共存 | +| buf lint | `buf lint` 使用 STANDARD 规则集,可按需 except(如 PACKAGE_VERSION_SUFFIX) | +| 三端代码生成 | `buf.gen.yaml` 配置 TS/Go/Python 插件,`pnpm proto:gen` 统一生成 | +| 契约先行硬约束 | 任何服务间通信必须先改 proto,CI 检测 breaking change 阻止违规合并 | +| 事件 schema 版本化 | Kafka 事件带 `schema_version` 字段,消费端按版本处理 | +| 错误码定义在 proto | 错误码三端共享,格式 `{服务前缀}_{错误类型}_{资源}`(如 `IAM_PERM_USER`) | ### 1.4 NestJS 服务开发(TS) -| 场景 | 技术/规则 | -| ------------------------- | ----------------------------------------------------------------------------------------------- | -| 服务标准结构 | `src/{main.ts,app.module.ts,config,middleware,,shared,generated}` 黄金模板复制 | -| 三层配置 | env(环境变量)< yaml(业务参数)< 配置中心(P6 Consul);每服务启动时合并 | -| 环境变量校验 | Zod schema 校验 process.env,失败立即抛错终止启动 | -| ApplicationError 基类 | 统一错误体系:ValidationError(400)/NotFoundError(404)/PermissionDeniedError(403)/ConflictError(409) 等 | -| 响应信封 | `{ success: boolean, data?: T, error?: { code, message, details, traceId } }` 统一跨服务 | -| 权限校验 | `requirePermission(perm)` 装饰器/中间件,admin 角色拥有全部权限 | -| DataScope 过滤 | 6 级(all/grade_managed/class_taught/class_members/children/owned),repository 下推 WHERE 条件 | -| 结构化日志 | pino + `traceId`/`spanId`/`userId`/`service` 字段,禁止 `console.*` | -| Metrics endpoint | prom-client 暴露 `/metrics`(QPS/延迟/错误率) | -| OpenTelemetry trace | OTel SDK 初始化,W3C TraceContext header 跨服务传播 | -| 健康检查 | `/health` endpoint 返回 `{ status, service, version }` | -| 优雅关闭 | SIGTERM → app.close() → shutdownTracing() → process.exit(0) | -| i18n 错误码映射 | 错误码 → i18n key 映射表,后端返回 key + 参数,前端翻译 | -| Drizzle ORM | 参数化查询,禁止字符串拼接 SQL | -| Zod 输入校验 | controller 层 `schema.safeParse(body)`,失败抛 ValidationError | +| 场景 | 技术/规则 | +| --------------------- | ------------------------------------------------------------------------------------------------------ | +| 服务标准结构 | `src/{main.ts,app.module.ts,config,middleware,,shared,generated}` 黄金模板复制 | +| 三层配置 | env(环境变量)< yaml(业务参数)< 配置中心(P6 Consul);每服务启动时合并 | +| 环境变量校验 | Zod schema 校验 process.env,失败立即抛错终止启动 | +| ApplicationError 基类 | 统一错误体系:ValidationError(400)/NotFoundError(404)/PermissionDeniedError(403)/ConflictError(409) 等 | +| 响应信封 | `{ success: boolean, data?: T, error?: { code, message, details, traceId } }` 统一跨服务 | +| 权限校验 | `requirePermission(perm)` 装饰器/中间件,admin 角色拥有全部权限 | +| DataScope 过滤 | 6 级(all/grade_managed/class_taught/class_members/children/owned),repository 下推 WHERE 条件 | +| 结构化日志 | pino + `traceId`/`spanId`/`userId`/`service` 字段,禁止 `console.*` | +| Metrics endpoint | prom-client 暴露 `/metrics`(QPS/延迟/错误率) | +| OpenTelemetry trace | OTel SDK 初始化,W3C TraceContext header 跨服务传播 | +| 健康检查 | `/health` endpoint 返回 `{ status, service, version }` | +| 优雅关闭 | SIGTERM → app.close() → shutdownTracing() → process.exit(0) | +| i18n 错误码映射 | 错误码 → i18n key 映射表,后端返回 key + 参数,前端翻译 | +| Drizzle ORM | 参数化查询,禁止字符串拼接 SQL | +| Zod 输入校验 | controller 层 `schema.safeParse(body)`,失败抛 ValidationError | ### 1.5 Go Gateway 开发 -| 场景 | 技术/规则 | -| ------------------- | ------------------------------------------------------------------------------------------ | -| 框架 | Gin + httputil.ReverseProxy 路由转发 | -| P1 鉴权 | Gateway 内置 HS256 JWT 校验(测试密钥),P2 改 RS256(IAM 签发,公钥校验) | -| JWT claims | `{ user_id, roles[], registered_claims }`,校验后注入 `x-user-id`/`x-user-roles` 头转发 | -| 请求 ID 注入 | Gateway 生成或透传 `X-Request-ID`,全链路传递 | -| 健康检查 | `GET /health` 无需鉴权,返回 `{ status, timestamp }` | -| 包结构 | `internal/{config,middleware,proxy}`,P6 扩展 handler/service/repository | -| error 处理 | 显式处理,禁止 `_` 忽略;`gin.AbortWithStatusJSON` 统一错误响应 | -| 配置加载 | 环境变量 + 默认值(`getEnv(key, fallback)`),P6 引入 viper | -| Dockerfile 多阶段 | golang:1.22-alpine 构建 → alpine:3.20 运行,CGO_ENABLED=0 | +| 场景 | 技术/规则 | +| ----------------- | --------------------------------------------------------------------------------------- | +| 框架 | Gin + httputil.ReverseProxy 路由转发 | +| P1 鉴权 | Gateway 内置 HS256 JWT 校验(测试密钥),P2 改 RS256(IAM 签发,公钥校验) | +| JWT claims | `{ user_id, roles[], registered_claims }`,校验后注入 `x-user-id`/`x-user-roles` 头转发 | +| 请求 ID 注入 | Gateway 生成或透传 `X-Request-ID`,全链路传递 | +| 健康检查 | `GET /health` 无需鉴权,返回 `{ status, timestamp }` | +| 包结构 | `internal/{config,middleware,proxy}`,P6 扩展 handler/service/repository | +| error 处理 | 显式处理,禁止 `_` 忽略;`gin.AbortWithStatusJSON` 统一错误响应 | +| 配置加载 | 环境变量 + 默认值(`getEnv(key, fallback)`),P6 引入 viper | +| Dockerfile 多阶段 | golang:1.22-alpine 构建 → alpine:3.20 运行,CGO_ENABLED=0 | ### 1.6 可观测性(OTel + Prometheus + Loki) -| 场景 | 技术/规则 | -| --------------------- | ---------------------------------------------------------------------------------- | -| P1 最小可观测集 | 每服务结构化日志 + `/metrics` + OTel SDK 初始化(不引入完整后端) | -| 三支柱 | Logs(pino/winston/zap)+ Metrics(prom-client)+ Traces(OTel SDK) | -| traceId 注入 | Gateway 注入 → 服务读取 header → 日志/响应携带 | -| P6 完整后端 | Loki(日志)+ Grafana(仪表盘)+ Jaeger(trace)+ Prometheus(metrics) | -| Prometheus 指标 | `http_request_duration_seconds`(Histogram)+ `http_requests_total`(Counter) | -| 采样策略 | P1 全量 trace,P6 引入采样率降低开销 | -| 日志参数顺序 | `log.error({ err: error, userId, traceId }, "操作描述")`,错误对象字段名用 `err` | +| 场景 | 技术/规则 | +| --------------- | -------------------------------------------------------------------------------- | +| P1 最小可观测集 | 每服务结构化日志 + `/metrics` + OTel SDK 初始化(不引入完整后端) | +| 三支柱 | Logs(pino/winston/zap)+ Metrics(prom-client)+ Traces(OTel SDK) | +| traceId 注入 | Gateway 注入 → 服务读取 header → 日志/响应携带 | +| P6 完整后端 | Loki(日志)+ Grafana(仪表盘)+ Jaeger(trace)+ Prometheus(metrics) | +| Prometheus 指标 | `http_request_duration_seconds`(Histogram)+ `http_requests_total`(Counter) | +| 采样策略 | P1 全量 trace,P6 引入采样率降低开销 | +| 日志参数顺序 | `log.error({ err: error, userId, traceId }, "操作描述")`,错误对象字段名用 `err` | ### 1.7 微前端 Module Federation -| 场景 | 技术/规则 | -| --------------------- | -------------------------------------------------------------------------------------------- | -| 4 个稳定 portal | teacher-portal / student-portal / parent-app / admin-console,按场景域划分 | -| P1 测试页 | teacher-portal 仅一个测试页验证 classes CRUD 链路,P2 起配 Module Federation | -| 视口驱动渲染 | 侧边栏由 `viewports.L1` 驱动渲染,路由由 `viewports.L2` 控制 | -| 共享翻译资源包 | next-intl 微前端共享,BFF/服务返回 i18n key 不返回翻译文本 | -| Module Federation 配置 | P2 起在 next.config.js 配置,P1 用单一 Next.js 应用 | +| 场景 | 技术/规则 | +| ---------------------- | ---------------------------------------------------------------------------- | +| 4 个稳定 portal | teacher-portal / student-portal / parent-app / admin-console,按场景域划分 | +| P1 测试页 | teacher-portal 仅一个测试页验证 classes CRUD 链路,P2 起配 Module Federation | +| 视口驱动渲染 | 侧边栏由 `viewports.L1` 驱动渲染,路由由 `viewports.L2` 控制 | +| 共享翻译资源包 | next-intl 微前端共享,BFF/服务返回 i18n key 不返回翻译文本 | +| Module Federation 配置 | P2 起在 next.config.js 配置,P1 用单一 Next.js 应用 | ### 1.8 从旧项目迁移的通用经验 -| 场景 | 技术/规则 | -| ----------------------------- | ----------------------------------------------------------------------------------------------- | -| React 19 乐观更新 | `useOptimistic` 替代手动 isPending,配合 `useTransition` 自动管理回滚 | -| Zustand 细粒度选择器 | 单字段 selector 优于 `useShallow` 多字段包装 | -| Tiptap SSR | 必须 `immediatelyRender: false` 避免 hydration mismatch | -| 请求级去重 | React `cache()` 包装优于跨请求缓存(权限数据易变,不用 `unstable_cache`) | -| 批量 SQL | INSERT batch `db.insert().values([...])` + UPDATE `CASE WHEN` 单次执行,禁止循环单条 | -| 动态导入重 UI 库 | `xxx-inner.tsx` + `xxx.tsx` lazy wrapper(tiptap / @xyflow/react / AI SDK) | -| ReactFlowProvider | 留在外层同步渲染,保证 lazy 子组件 hook 可用 | -| 递归删除 | 批量收集后代 ID + `inArray` 单次删除,禁止递归逐个 | -| 统计走 SQL 聚合 | `COUNT(*)`/`SUM(CASE WHEN ...)` 替代拉全表内存循环 | -| 高频子组件 React.memo | `AssignmentCard` / `StatusBadge` / `EmptyState` 等 | -| 虚拟化大表 | `@tanstack/react-virtual` | -| 破坏性操作独立权限 | `AUDIT_LOG_PURGE` ≠ `AUDIT_LOG_READ`,沿用旧项目规则 | -| i18n key 命名 | 禁止包含 `.`,动态 key 必须包含完整嵌套路径(`t(\`type.${type}\`)`) | -| i18n 翻译文件对称性 | zh-CN 与 en 必须同步新增/删除 key | -| 类型守卫优先 | JSON.parse 结果经 `unknown` 中转 + 守卫;禁止 `as` 断言(除非从 `unknown` 转换) | -| 函数返回值显式标注 | 特别是 `Promise` | -| 仅类型导入 | `import type` | +| 场景 | 技术/规则 | +| --------------------- | ------------------------------------------------------------------------------------ | +| React 19 乐观更新 | `useOptimistic` 替代手动 isPending,配合 `useTransition` 自动管理回滚 | +| Zustand 细粒度选择器 | 单字段 selector 优于 `useShallow` 多字段包装 | +| Tiptap SSR | 必须 `immediatelyRender: false` 避免 hydration mismatch | +| 请求级去重 | React `cache()` 包装优于跨请求缓存(权限数据易变,不用 `unstable_cache`) | +| 批量 SQL | INSERT batch `db.insert().values([...])` + UPDATE `CASE WHEN` 单次执行,禁止循环单条 | +| 动态导入重 UI 库 | `xxx-inner.tsx` + `xxx.tsx` lazy wrapper(tiptap / @xyflow/react / AI SDK) | +| ReactFlowProvider | 留在外层同步渲染,保证 lazy 子组件 hook 可用 | +| 递归删除 | 批量收集后代 ID + `inArray` 单次删除,禁止递归逐个 | +| 统计走 SQL 聚合 | `COUNT(*)`/`SUM(CASE WHEN ...)` 替代拉全表内存循环 | +| 高频子组件 React.memo | `AssignmentCard` / `StatusBadge` / `EmptyState` 等 | +| 虚拟化大表 | `@tanstack/react-virtual` | +| 破坏性操作独立权限 | `AUDIT_LOG_PURGE` ≠ `AUDIT_LOG_READ`,沿用旧项目规则 | +| i18n key 命名 | 禁止包含 `.`,动态 key 必须包含完整嵌套路径(`t(\`type.${type}\`)`) | +| i18n 翻译文件对称性 | zh-CN 与 en 必须同步新增/删除 key | +| 类型守卫优先 | JSON.parse 结果经 `unknown` 中转 + 守卫;禁止 `as` 断言(除非从 `unknown` 转换) | +| 函数返回值显式标注 | 特别是 `Promise` | +| 仅类型导入 | `import type` | ### 1.9 架构工具与验证命令 -| 场景 | 命令/规则 | -| --------------------- | ---------------------------------------------------------------------- | -| arch.db 扫描 | `npm run arch:scan`(多语言:TS+Go+Python) | -| arch.db 查询 | `npm run arch:query -- ` | -| 查服务依赖 | `npm run arch:query -- service ` | -| 查 proto 契约 | `npm run arch:query -- contracts` | -| 查 Kafka 事件 | `npm run arch:query -- events` | -| 查架构违规 | `npm run arch:query -- violations`(输出 0 为合格) | -| proto lint | `cd packages/shared-proto && pnpm exec buf lint` | -| proto breaking 检测 | `pnpm exec buf breaking --against '.git#branch=main'` | -| proto 代码生成 | `pnpm proto:gen` | -| Go 编译验证 | `cd services/api-gateway && go build ./... && go vet ./...` | -| TS 类型检查 | `pnpm -r exec tsc --noEmit` | -| 全量 lint | `pnpm lint` | -| 全量测试 | `pnpm test` | -| 启动最小基础设施 | `docker compose -f infra/docker-compose.minimal.yml up -d` | +| 场景 | 命令/规则 | +| ------------------- | ----------------------------------------------------------- | +| arch.db 扫描 | `pnpm run arch:scan`(多语言:TS+Go+Python+Proto) | +| arch.db 查询 | `pnpm run arch:query -- ` | +| 查服务依赖 | `pnpm run arch:query -- deps ` | +| 查 proto 契约 | `pnpm run arch:query -- sql "SELECT * FROM contracts"` | +| 查 Kafka 事件 | `pnpm run arch:query -- sql "SELECT * FROM events"` | +| 查架构违规 | `pnpm run arch:query -- violations`(骨架,P1 后期补全) | +| proto lint | `cd packages/shared-proto && pnpm exec buf lint` | +| proto breaking 检测 | `pnpm exec buf breaking --against '.git#branch=main'` | +| proto 代码生成 | `pnpm proto:gen` | +| Go 编译验证 | `cd services/api-gateway && go build ./... && go vet ./...` | +| TS 类型检查 | `pnpm -r exec tsc --noEmit` | +| 全量 lint | `pnpm lint` | +| 全量测试 | `pnpm test` | +| 启动最小基础设施 | `docker compose -f infra/docker-compose.minimal.yml up -d` | --- @@ -151,138 +151,138 @@ ### 2.1 api-gateway(Go) -| 场景 | 技术/规则 | -| ------------------- | ------------------------------------------------------------------------------------------ | -| P1 鉴权 | Gateway 内置 HS256 JWT,`jwt.ParseWithClaims` + `SigningMethodHMAC` 校验 | -| P2 鉴权升级 | 改 RS256,IAM 私钥签发,Gateway 公钥校验,无需调 IAM | -| 路由转发 | `gin.Group("/api/v1")` + `httputil.NewSingleHostReverseProxy` | -| 路径重写 | 去掉 `/api/v1` 前缀后转发到下游服务 | -| 用户上下文注入 | `c.Request.Header.Set("x-user-id", claims.UserID)` 传递给下游 | -| 请求 ID | Gateway 生成或透传 `X-Request-ID`,`c.Set("request_id", ...)` + `c.Header(...)` | -| P1 不做 | 限流/熔断/灰度(P6 硬化阶段实现) | -| 健康检查 | `GET /health` 无需鉴权 | +| 场景 | 技术/规则 | +| -------------- | ------------------------------------------------------------------------------- | +| P1 鉴权 | Gateway 内置 HS256 JWT,`jwt.ParseWithClaims` + `SigningMethodHMAC` 校验 | +| P2 鉴权升级 | 改 RS256,IAM 私钥签发,Gateway 公钥校验,无需调 IAM | +| 路由转发 | `gin.Group("/api/v1")` + `httputil.NewSingleHostReverseProxy` | +| 路径重写 | 去掉 `/api/v1` 前缀后转发到下游服务 | +| 用户上下文注入 | `c.Request.Header.Set("x-user-id", claims.UserID)` 传递给下游 | +| 请求 ID | Gateway 生成或透传 `X-Request-ID`,`c.Set("request_id", ...)` + `c.Header(...)` | +| P1 不做 | 限流/熔断/灰度(P6 硬化阶段实现) | +| 健康检查 | `GET /health` 无需鉴权 | ### 2.2 classes(TS/NestJS,P1 黄金模板) -| 场景 | 技术/规则 | -| ------------------------- | ----------------------------------------------------------------------------------------------- | -| 黄金模板定位 | P1 完整实现所有横切关注点,后续 8 个服务复制此模板 | -| 黄金模板复制流程 | `cp -r services/classes services/xxx` → 改错误码前缀 → 改 proto → 改业务逻辑 → 改 README → 改 CI | -| 横切关注点清单 | 错误处理 / 可观测 / 安全 / 契约 / 测试 / 文档 / 配置 / i18n / CI / Dockerfile | -| 错误处理 | `ApplicationError` 基类 + 子类,错误码 `CLASSES_*` 前缀 | -| 可观测 | pino logger + prom-client metrics + OTel tracer | -| 安全 | auth.middleware(信任 Gateway 头)+ permission.guard + data-scope.interceptor | -| 配置 | 三层配置 + Zod 校验 env | -| i18n | `ERROR_CODES` 映射表(错误码 → i18n key) | -| 测试四类 | 单元(vitest)+ 集成(Testcontainers)+ 契约(Pact)+ E2E(Playwright) | -| 覆盖率门槛 | 领域逻辑 ≥ 80%,Handler ≥ 60%,整体 ≥ 60% | -| Drizzle schema | `mysqlTable` + `varchar`/`timestamp` + `index` | -| ID 生成 | `@paralleldrive/cuid2` 的 `createId()` | -| 响应转换 | repository 返回 Date,service 转换为 `createdAt: number`(时间戳) | -| 阶段特有模式回写 | Outbox(P3)/ CDC(P4)/ 长连接(P5)实现后回写黄金模板 README | +| 场景 | 技术/规则 | +| ---------------- | ------------------------------------------------------------------------------------------------ | +| 黄金模板定位 | P1 完整实现所有横切关注点,后续 8 个服务复制此模板 | +| 黄金模板复制流程 | `cp -r services/classes services/xxx` → 改错误码前缀 → 改 proto → 改业务逻辑 → 改 README → 改 CI | +| 横切关注点清单 | 错误处理 / 可观测 / 安全 / 契约 / 测试 / 文档 / 配置 / i18n / CI / Dockerfile | +| 错误处理 | `ApplicationError` 基类 + 子类,错误码 `CLASSES_*` 前缀 | +| 可观测 | pino logger + prom-client metrics + OTel tracer | +| 安全 | auth.middleware(信任 Gateway 头)+ permission.guard + data-scope.interceptor | +| 配置 | 三层配置 + Zod 校验 env | +| i18n | `ERROR_CODES` 映射表(错误码 → i18n key) | +| 测试四类 | 单元(vitest)+ 集成(Testcontainers)+ 契约(Pact)+ E2E(Playwright) | +| 覆盖率门槛 | 领域逻辑 ≥ 80%,Handler ≥ 60%,整体 ≥ 60% | +| Drizzle schema | `mysqlTable` + `varchar`/`timestamp` + `index` | +| ID 生成 | `@paralleldrive/cuid2` 的 `createId()` | +| 响应转换 | repository 返回 Date,service 转换为 `createdAt: number`(时间戳) | +| 阶段特有模式回写 | Outbox(P3)/ CDC(P4)/ 长连接(P5)实现后回写黄金模板 README | ### 2.3 iam(TS/NestJS,P2) -| 场景 | 技术/规则 | -| --------------------- | ------------------------------------------------------------------------------------------ | -| 认证 | 登录/登出/JWT/2FA,RS256 非对称签名 | -| RBAC | 角色/权限/角色-权限 CRUD + `getEffectivePermissions(userId)` API | -| 视口配置 | 4 层模型(导航/路由/组件/数据),`role_viewports` 表 | -| DataScope 解析 | 6 级数据范围,注入 JWT payload | -| JWT payload | `{ userId, roles, permissions(bitmap), dataScope, exp }` | -| Token TTL | access 15min / refresh 7day,refresh 用 Redis 黑名单失效 | -| 权限缓存 | `getEffectivePermissions` 结果 Redis 缓存 TTL 5 分钟,角色变更主动失效 | -| schema 表 | users / roles / permissions / role_permissions / role_viewports / parent_student_relations / class_subject_teachers | +| 场景 | 技术/规则 | +| -------------- | ------------------------------------------------------------------------------------------------------------------- | +| 认证 | 登录/登出/JWT/2FA,RS256 非对称签名 | +| RBAC | 角色/权限/角色-权限 CRUD + `getEffectivePermissions(userId)` API | +| 视口配置 | 4 层模型(导航/路由/组件/数据),`role_viewports` 表 | +| DataScope 解析 | 6 级数据范围,注入 JWT payload | +| JWT payload | `{ userId, roles, permissions(bitmap), dataScope, exp }` | +| Token TTL | access 15min / refresh 7day,refresh 用 Redis 黑名单失效 | +| 权限缓存 | `getEffectivePermissions` 结果 Redis 缓存 TTL 5 分钟,角色变更主动失效 | +| schema 表 | users / roles / permissions / role_permissions / role_viewports / parent_student_relations / class_subject_teachers | ### 2.4 core-edu(TS/NestJS,P3) -| 场景 | 技术/规则 | -| --------------------- | ------------------------------------------------------------------------------------------ | -| 考试全生命周期 | 教师创建 → 发布 → 学生作答 → 教师批改 → 成绩统计 | -| Outbox 模式 | 业务事务同写 `outbox_events` 表,后台 relay worker 投递 Kafka | -| Outbox relay | Go 写独立服务 `services/outbox-relay/`,轻量高吞吐 | -| Kafka topic | `exam.published` / `homework.graded` / `grade.recorded` | -| 不引入 Saga | 跨服务一致性用 Outbox + 最终一致性 | -| 批改后联动 | 批改完成 → 发 `homework.graded` 事件 → 下游消费(DataAna/Msg) | -| Temporal 试点 | 仅 1 个工作流(考试发布编排:创建作业→通知) | -| schema 表 | exams / exam_questions / homework_assignments / homework_submissions / homework_answers / grade_records / outbox_events | +| 场景 | 技术/规则 | +| -------------- | ----------------------------------------------------------------------------------------------------------------------- | +| 考试全生命周期 | 教师创建 → 发布 → 学生作答 → 教师批改 → 成绩统计 | +| Outbox 模式 | 业务事务同写 `outbox_events` 表,后台 relay worker 投递 Kafka | +| Outbox relay | Go 写独立服务 `services/outbox-relay/`,轻量高吞吐 | +| Kafka topic | `exam.published` / `homework.graded` / `grade.recorded` | +| 不引入 Saga | 跨服务一致性用 Outbox + 最终一致性 | +| 批改后联动 | 批改完成 → 发 `homework.graded` 事件 → 下游消费(DataAna/Msg) | +| Temporal 试点 | 仅 1 个工作流(考试发布编排:创建作业→通知) | +| schema 表 | exams / exam_questions / homework_assignments / homework_submissions / homework_answers / grade_records / outbox_events | ### 2.5 content(TS/NestJS,P4) -| 场景 | 技术/规则 | -| --------------------- | ------------------------------------------------------------------------------------------ | -| 知识图谱 | Neo4j 查询前置依赖图(秒级返回) | -| 题库 CRUD | P4 仅 CRUD,P5 引入 ES 实现检索,避免 MySQL FULLTEXT → ES 迁移成本 | -| 双写避免 | Neo4j/ES 不直接双写,由消费 Kafka 事件同步,天然最终一致 | -| Neo4j 写入 | Content 服务写 MySQL 同时发事件,独立 worker 消费事件同步 Neo4j | +| 场景 | 技术/规则 | +| ---------- | ------------------------------------------------------------------ | +| 知识图谱 | Neo4j 查询前置依赖图(秒级返回) | +| 题库 CRUD | P4 仅 CRUD,P5 引入 ES 实现检索,避免 MySQL FULLTEXT → ES 迁移成本 | +| 双写避免 | Neo4j/ES 不直接双写,由消费 Kafka 事件同步,天然最终一致 | +| Neo4j 写入 | Content 服务写 MySQL 同时发事件,独立 worker 消费事件同步 Neo4j | ### 2.6 data-ana(Python/FastAPI,P4) -| 场景 | 技术/规则 | -| --------------------- | ------------------------------------------------------------------------------------------ | -| 学情诊断 | ClickHouse 宽表查询,5s 内返回 | -| CDC 链路 | Debezium 监听 MySQL binlog → Kafka(`mysql.cdc.*`)→ DataAna 消费写 ClickHouse | -| CDC 延迟监控 | Debezium 暴露 lag metrics,超阈值告警 | -| 双轨读策略 | 实时查 MySQL 主库(刚提交的成绩),聚合查 CH 宽表(延迟 1-5s 可接受) | -| 幂等消费 | 所有事件消费者必须幂等(基于 event_id 去重) | +| 场景 | 技术/规则 | +| ------------ | ------------------------------------------------------------------------------ | +| 学情诊断 | ClickHouse 宽表查询,5s 内返回 | +| CDC 链路 | Debezium 监听 MySQL binlog → Kafka(`mysql.cdc.*`)→ DataAna 消费写 ClickHouse | +| CDC 延迟监控 | Debezium 暴露 lag metrics,超阈值告警 | +| 双轨读策略 | 实时查 MySQL 主库(刚提交的成绩),聚合查 CH 宽表(延迟 1-5s 可接受) | +| 幂等消费 | 所有事件消费者必须幂等(基于 event_id 去重) | ### 2.7 messaging(TS/NestJS,P5) -| 场景 | 技术/规则 | -| --------------------- | ------------------------------------------------------------------------------------------ | -| 消息 CRUD | 会话/消息 + 调 Push Gateway 推送 + 通知偏好 | -| 通知批量化 | `createNotifications(items)` 单次 INSERT,沿用旧项目 dispatcher 模式 | -| 多渠道 | 站内/SMS/邮件/微信,in_app 批量 + 其他渠道并行 | -| fan-out 分页 | `getAllUserIds(limit=1000, offset)` 分页遍历 | -| 撤回不乐观更新 | 需服务端返回判断 2 分钟窗口 | +| 场景 | 技术/规则 | +| -------------- | -------------------------------------------------------------------- | +| 消息 CRUD | 会话/消息 + 调 Push Gateway 推送 + 通知偏好 | +| 通知批量化 | `createNotifications(items)` 单次 INSERT,沿用旧项目 dispatcher 模式 | +| 多渠道 | 站内/SMS/邮件/微信,in_app 批量 + 其他渠道并行 | +| fan-out 分页 | `getAllUserIds(limit=1000, offset)` 分页遍历 | +| 撤回不乐观更新 | 需服务端返回判断 2 分钟窗口 | ### 2.8 push-gateway(Go,P5) -| 场景 | 技术/规则 | -| --------------------- | ------------------------------------------------------------------------------------------ | -| WebSocket 长连接 | 单节点支撑 10w+ 连接,业务服务只需发 Kafka 消息 | -| 跨实例同步 | Redis PubSub | -| 离线消息 | 仅推在线用户,离线消息存 MySQL,上线时拉取 | +| 场景 | 技术/规则 | +| ---------------- | ----------------------------------------------- | +| WebSocket 长连接 | 单节点支撑 10w+ 连接,业务服务只需发 Kafka 消息 | +| 跨实例同步 | Redis PubSub | +| 离线消息 | 仅推在线用户,离线消息存 MySQL,上线时拉取 | ### 2.9 ai-gateway(Python/FastAPI,P5) -| 场景 | 技术/规则 | -| --------------------- | ------------------------------------------------------------------------------------------ | -| LLM Provider 适配 | OpenAI/Anthropic,langchain/litellm 生态 | -| Prompt 模板管理 | 版本管理友好 | -| 流式 SSE | AI 网关 → BFF → 前端三层透传,BFF 不缓冲 | -| 用量计费 | 按 token 计费 | -| AI 模块纯服务端 | Zod 验证 + 失败降级返回空(沿用旧项目模式) | +| 场景 | 技术/规则 | +| ----------------- | ------------------------------------------- | +| LLM Provider 适配 | OpenAI/Anthropic,langchain/litellm 生态 | +| Prompt 模板管理 | 版本管理友好 | +| 流式 SSE | AI 网关 → BFF → 前端三层透传,BFF 不缓冲 | +| 用量计费 | 按 token 计费 | +| AI 模块纯服务端 | Zod 验证 + 失败降级返回空(沿用旧项目模式) | ### 2.10 shared-proto(契约包) -| 场景 | 技术/规则 | -| --------------------- | ------------------------------------------------------------------------------------------ | -| 目录结构 | `proto/*.proto` + `buf.yaml` + `buf.gen.yaml` | -| P1 契约 | 仅 `classes.proto`,`iam.proto`/`core_edu.proto` 占位 | -| 代码生成输出 | TS → `shared-ts/generated`,Go → `shared-go/`,Python → `services/*/generated/` | +| 场景 | 技术/规则 | +| ------------ | ------------------------------------------------------------------------------- | +| 目录结构 | `proto/*.proto` + `buf.yaml` + `buf.gen.yaml` | +| P1 契约 | 仅 `classes.proto`,`iam.proto`/`core_edu.proto` 占位 | +| 代码生成输出 | TS → `shared-ts/generated`,Go → `shared-go/`,Python → `services/*/generated/` | ### 2.11 arch-scan(多语言扫描器) -| 场景 | 技术/规则 | -| --------------------- | ------------------------------------------------------------------------------------------ | -| TS 扫描 | ts-morph 解析 AST,提取导出/函数/类/import | -| Go 扫描 | P1 用正则提取(函数/类型/import),P2 起替换为 tree-sitter-go AST | -| Python 扫描 | P1 用正则提取,P4 起替换为 tree-sitter-python AST | -| arch.db schema | modules/symbols/dependencies/contracts/events/violations 六表 | -| 全量扫描 | 先清空旧数据再扫描,避免残留 | -| 并行扫描风险 | 并行子代理执行 arch:scan 可能因竞争报 FOREIGN KEY 错误,必须串行执行 | -| 扫描后验证 | `npm run arch:query -- violations` 输出 0 为合格 | -| Windows 路径 | 自定义 ESLint 规则加载用 `path.join`,不用 `path.posix.join` | +| 场景 | 技术/规则 | +| -------------- | ----------------------------------------------------------------------------------------------- | +| TS 扫描 | regex 提取(function/class/interface/UPPER_CASE const),避免 ts-morph 对未安装依赖文件解析失败 | +| Go 扫描 | 正则提取(行首锚定 `^func`/`^type`),P2 起替换为 tree-sitter-go AST | +| Python 扫描 | 正则提取(行首锚定 `^def`/`^class`),P4 起替换为 tree-sitter-python AST | +| arch.db schema | modules/symbols/dependencies/contracts/events/violations 六表 | +| 全量扫描 | 先清空旧数据再扫描,避免残留 | +| 并行扫描风险 | 并行子代理执行 arch:scan 可能因竞争报 FOREIGN KEY 错误,必须串行执行 | +| 扫描后验证 | `npm run arch:query -- violations` 输出 0 为合格 | +| Windows 路径 | 自定义 ESLint 规则加载用 `path.join`,不用 `path.posix.join` | ### 2.12 teacher-portal(微前端宿主,P1 测试页) -| 场景 | 技术/规则 | -| --------------------- | ------------------------------------------------------------------------------------------ | -| P1 测试页 | 单一 Next.js 应用,验证 classes CRUD 端到端链路 | -| API 调用 | `fetch(${API_BASE}/api/v1/classes)` + `Authorization: Bearer ${TEST_JWT}` | -| P1 测试 JWT | 开发工具生成 HS256 token,P2 起由 IAM 签发 RS256 | -| P2 Module Federation | next.config.js 配置,按场景域分 4 个稳定 portal | +| 场景 | 技术/规则 | +| -------------------- | ------------------------------------------------------------------------- | +| P1 测试页 | 单一 Next.js 应用,验证 classes CRUD 端到端链路 | +| API 调用 | `fetch(${API_BASE}/api/v1/classes)` + `Authorization: Bearer ${TEST_JWT}` | +| P1 测试 JWT | 开发工具生成 HS256 token,P2 起由 IAM 签发 RS256 | +| P2 Module Federation | next.config.js 配置,按场景域分 4 个稳定 portal | --- @@ -290,6 +290,15 @@ > 按时间倒序,50 条上限。AI 发现更好方案时可更新本节。 -| 日期 | 时间 | 模块 | 做了什么 + 学到什么 | -| ---------- | ----- | ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 2026-07-07 | 全天 | 全局 | 文档体系初始化:从旧项目(e:\Desktop\CICD,Next.js 单体)迁移 spec + plan + known-issues 模板到新仓库(e:\Desktop\Edu,微服务架构)。known-issues 重组为微服务分区:多语言 monorepo / Docker Compose / protobuf+buf / NestJS / Go Gateway / 可观测性 / 微前端。从旧项目提炼可迁移经验:React 19 useOptimistic / Zustand 细粒度选择器 / Tiptap SSR / 请求级去重 / 批量 SQL / 动态导入模式 / arch:scan 串行执行。新增微服务特有经验:契约先行 / Outbox / CDC / 双轨读 / DataScope / 黄金模板复制流程。路线图按 6 阶段组织:P1 地基 → P2 身份 → P3 核心教学 → P4 内容分析 → P5 沟通AI → P6 硬化。 | +| 日期 | 时间 | 模块 | 做了什么 + 学到什么 | +| ---------- | ---- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 2026-07-08 | 全天 | 全局 | **P6 后续工作手册执行**:完整执行 post-p6-followup.md 12 节任务。环境准备(pnpm 925 包 + go mod tidy 双服务 + uv sync 双服务 + buf 安装)→ 代码质量校验(Go vet/build 0 错误,Python ruff 8 错误自动修复)→ arch.db 同步(实现 4 个扫描器骨架,输出 12 模块/233 符号/138 契约)→ project_rules.md P0 修复(迁移到 .trae/rules/,17881 字节)→ 004 架构图修复(1.1a/1.1b 双图 + 1.2 业务领域列 + 5.4 视口四层)→ P6 集成测试(10 Go + 17 bash = 27 用例全通过)→ Helm Chart 演化(8 chart lint 通过)。**学到**:多语言 monorepo 工具链配置需统一镜像源(npmmirror/goproxy.cn/tuna),go.work BOM 字符会导致 `unexpected input character` 错误必须重写文件。 | +| 2026-07-08 | 上午 | 全局 | pnpm install 网络失败(ECONNRESET)→ 配置 `npm config set registry https://registry.npmmirror.com` + `pnpm config set registry https://registry.npmmirror.com` 重试成功。**学到**:Windows 下 pnpm 还需配置 `PNPM_HOME` 和 `TMP` 环境变量避免 `_tmp_` 文件 ENOENT 错误。 | +| 2026-07-08 | 上午 | 全局 | project_rules.md 损坏(72 字节乱码,从 P1 提交 2ba4250 就损坏,git 历史无完整版本)→ 从 CICD 项目完整版迁移到 `e:\Desktop\Edu\.trae\rules\project_rules.md`(按用户要求放 .trae/rules/),按 MIGRATION_GUIDE 4.1 策略矩阵调整为微服务版(13 章 17881 字节),删除根目录损坏文件,更新 7 处引用(README/MIGRATION_GUIDE/004/known-issues/git-workflow/coding-standards)。**学到**:迁移文件后必须 `Get-Item | Select Length` 验证完整性 + 全文搜索引用更新,git commit 前运行 cat 检查内容。 | +| 2026-07-08 | 上午 | api-gateway | go.work BOM 字符 + 版本不匹配:`unexpected input character '\ufeff'` 和 `module requires go >= 1.22.0, but go.work lists go 1.22`。**修复**:重写 go.work 去除 BOM,版本改为 `go 1.26.0`,移除不存在的 `./packages/shared-go`。**学到**:PowerShell `Out-File` 默认加 BOM,写 go.work 这类敏感文件应用 `Write` 工具或 `[System.IO.File]::WriteAllText` 指定 UTF8 无 BOM。 | +| 2026-07-08 | 上午 | arch-scan | arch:scan 返回 0 模块 0 符号 → 4 个扫描器(ts/go/py/proto)都是骨架实现。**修复**:完整实现 4 个扫描器,TS 用 regex 提取(避免 ts-morph 对未安装依赖文件解析失败),Go/Python 用行首锚定正则,Proto 扫描 service/message/rpc。结果:12 模块(≥10 ✓)、233 符号(≥100 ✓)、138 契约。**学到**:ts-morph Project 对未 `pnpm install` 的 workspace 文件会报模块解析失败,改用 regex 更鲁棒;scanner.ts main() 开头需 `DELETE FROM` 清空旧数据避免重跑重复。 | +| 2026-07-08 | 下午 | 004 | 架构图视角讨论(技术分层 vs 业务领域)→ 双图并存方案:1.1a 技术分层视角(部署/流量/网络边界,Users 层标注"场景域用户",BFF 层标注"按场景域分")+ 1.1b 业务领域视角(6 DDD 限界上下文 subgraph:D1 身份/D2 教学组织/D3 教学核心/D4 内容/D5 沟通/D6 智能洞察)。1.2 服务清单新增"业务领域"列。**学到**:双图互补,1.1a 服务运维/SRE 视角,1.1b 服务产品/架构视角;同一服务可横跨多领域(core-edu 同时承载 D2+D3)。 | +| 2026-07-08 | 下午 | 004 | 视口四层模型补充(5.4 章节):L1 导航(navigation_config 表)/ L2 路由(route_permission + Gateway 校验)/ L3 组件(usePermission().hasPermission)/ L4 数据(DataScope 枚举)。场景域 BFF 复用策略:按使用场景域分 BFF 而非按角色分,教导主任复用 Teacher BFF + 额外管理视口。iam 服务职责:认证 + RBAC + 视口配置 + DataScope + 权限解析 API。**学到**:视口既可独立配置(RoleViewport 表)也可由权限推导,新角色只需配权限集,视口自动推导。 | +| 2026-07-08 | 下午 | api-gateway | P6 集成测试补充:circuit-breaker_test.go(5 用例:ClosedToOpen/OpenToHalfOpen/HalfOpenToClosed/HalfOpenToOpen/4xxNotCounted)+ ratelimit_test.go(5 用例:AllowUnderBurst/RejectOverBurst/RefillTokens/PerIPIsolation/CleanupExpiredBuckets)+ test-backup-mysql.sh(8 用例 17 断言)。**学到**:gobreaker v2 ReadyToTrip 在 1 次失败后就触发(`TotalFailures*2 > Requests` 当 Requests=1 时 1*2>1=true),HALF_OPEN 状态只在探测执行期间可见,探测完成后立即转 CLOSED 或回 OPEN,测试需通过行为(503 vs 500)而非状态字段验证;rateLimiter cleanup 测试需用短周期参数(50ms/500ms)加速,且新鲜桶要在旧桶清理后再创建避免被一起清掉。 | +| 2026-07-08 | 下午 | infra/k8s | Helm Chart 演化:安装 Helm v4.2.2,创建 edu-platform 平台级 chart(namespace/configmap/secret/ingress/hpa + 4 环境 values 文件)+ api-gateway 服务级 chart(完整迁移自原 deployment.yaml,参数化所有字段)+ 6 业务服务 chart 桩(iam/core-edu/content/msg/data-ana/ai)。删除原 api-gateway-deployment.yaml,保留 namespace.yaml。**学到**:Helm `{{- with ... -}}` 双向修剪会导致标签连在一行(`managed-by: Helmpart-of: edu-platform`),应改为 `{{- with ... }}` 只修剪左侧;`helm lint` 全部通过但 `helm template` 才能发现 YAML 渲染错误,验证时两个都要跑。 | +| 2026-07-07 | 全天 | 全局 | 文档体系初始化:从旧项目(e:\Desktop\CICD,Next.js 单体)迁移 spec + plan + known-issues 模板到新仓库(e:\Desktop\Edu,微服务架构)。known-issues 重组为微服务分区:多语言 monorepo / Docker Compose / protobuf+buf / NestJS / Go Gateway / 可观测性 / 微前端。从旧项目提炼可迁移经验:React 19 useOptimistic / Zustand 细粒度选择器 / Tiptap SSR / 请求级去重 / 批量 SQL / 动态导入模式 / arch:scan 串行执行。新增微服务特有经验:契约先行 / Outbox / CDC / 双轨读 / DataScope / 黄金模板复制流程。路线图按 6 阶段组织:P1 地基 → P2 身份 → P3 核心教学 → P4 内容分析 → P5 沟通AI → P6 硬化。 | diff --git a/services/content/README.md b/services/content/README.md index 47a4b75..aff8a2f 100644 --- a/services/content/README.md +++ b/services/content/README.md @@ -29,14 +29,14 @@ pnpm test ## 环境变量 -| 变量 | 说明 | -|------|------| -| `PORT` | 服务端口(默认 3005) | -| `DATABASE_URL` | MySQL 连接串 | -| `NEO4J_URL` | Neo4j Bolt 连接 URL | -| `NEO4J_PASSWORD` | Neo4j 密码 | -| `ES_URL` | Elasticsearch 地址 | -| `JWT_SECRET` | JWT 密钥 | +| 变量 | 说明 | +| ----------------------------- | ------------------------------- | +| `PORT` | 服务端口(默认 3005) | +| `DATABASE_URL` | MySQL 连接串 | +| `NEO4J_URL` | Neo4j Bolt 连接 URL | +| `NEO4J_PASSWORD` | Neo4j 密码 | +| `ES_URL` | Elasticsearch 地址 | +| `JWT_SECRET` | JWT 密钥 | | `OTEL_EXPORTER_OTLP_ENDPOINT` | OpenTelemetry OTLP 端点(可选) | ## 模块结构 @@ -60,6 +60,15 @@ src/ - `TextbooksService.createKnowledgeGraph` 在 Neo4j 构建知识点前置依赖 - `TextbooksService.getPrerequisites` 查询知识点前置链路 +## 健康检查 + +| 端点 | 用途 | 鉴权 | +| -------------- | ------------------------------------------------- | ---- | +| `GET /healthz` | 存活探针(liveness),仅返回进程状态,不检查依赖 | 无 | +| `GET /readyz` | 就绪探针(readiness),检查 DB 连接,失败返回 503 | 无 | + +实现见 `src/shared/health/health.controller.ts`,5 个 NestJS 服务(iam/core-edu/content/msg/classes)一致。 + ## 对外契约 gRPC 服务 `TextbookService`、`KnowledgeGraphService` 定义见 `packages/shared-proto/proto/content.proto`。 diff --git a/services/core-edu/README.md b/services/core-edu/README.md index 23c2efc..6d1b97e 100644 --- a/services/core-edu/README.md +++ b/services/core-edu/README.md @@ -43,10 +43,19 @@ pnpm test ## 环境变量 -| 变量 | 说明 | 默认 | -|------|------|------| -| `PORT` | 服务端口 | 3004 | -| `DATABASE_URL` | MySQL 连接串 | - | -| `KAFKA_BROKERS` | Kafka broker 列表(逗号分隔) | localhost:9092 | -| `JWT_SECRET` | JWT 密钥 | - | -| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP 上报地址(可选) | - | +| 变量 | 说明 | 默认 | +| ----------------------------- | ----------------------------- | -------------- | +| `PORT` | 服务端口 | 3004 | +| `DATABASE_URL` | MySQL 连接串 | - | +| `KAFKA_BROKERS` | Kafka broker 列表(逗号分隔) | localhost:9092 | +| `JWT_SECRET` | JWT 密钥 | - | +| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP 上报地址(可选) | - | + +## 健康检查 + +| 端点 | 用途 | 鉴权 | +| -------------- | ------------------------------------------------- | ---- | +| `GET /healthz` | 存活探针(liveness),仅返回进程状态,不检查依赖 | 无 | +| `GET /readyz` | 就绪探针(readiness),检查 DB 连接,失败返回 503 | 无 | + +实现见 `src/shared/health/health.controller.ts`,5 个 NestJS 服务(iam/core-edu/content/msg/classes)一致。 diff --git a/services/iam/README.md b/services/iam/README.md index 5ba6daf..60f8dcd 100644 --- a/services/iam/README.md +++ b/services/iam/README.md @@ -23,12 +23,21 @@ ## API -| Method | Path | 说明 | -|--------|------|------| -| POST | `/iam/register` | 注册 | -| POST | `/iam/login` | 登录 | -| POST | `/iam/refresh` | 刷新令牌 | -| GET | `/iam/me` | 当前用户信息(需 `x-user-id` 头) | +| Method | Path | 说明 | +| ------ | --------------- | --------------------------------- | +| POST | `/iam/register` | 注册 | +| POST | `/iam/login` | 登录 | +| POST | `/iam/refresh` | 刷新令牌 | +| GET | `/iam/me` | 当前用户信息(需 `x-user-id` 头) | + +## 健康检查 + +| 端点 | 用途 | 鉴权 | +| -------------- | ------------------------------------------------- | ---- | +| `GET /healthz` | 存活探针(liveness),仅返回进程状态,不检查依赖 | 无 | +| `GET /readyz` | 就绪探针(readiness),检查 DB 连接,失败返回 503 | 无 | + +实现见 `src/shared/health/health.controller.ts`,5 个 NestJS 服务(iam/core-edu/content/msg/classes)一致。 ## 数据表 diff --git a/services/msg/README.md b/services/msg/README.md index 00730de..baf850f 100644 --- a/services/msg/README.md +++ b/services/msg/README.md @@ -25,19 +25,28 @@ pnpm dev # http://localhost:3007 ## API -| 方法 | 路径 | 说明 | -|------|------|------| -| POST | /notifications | 发送通知 | -| GET | /notifications | 查询用户通知列表 | -| POST | /notifications/:id/read | 标记已读 | -| GET | /notifications/search?q= | 全文检索通知 | +| 方法 | 路径 | 说明 | +| ---- | ------------------------ | ---------------- | +| POST | /notifications | 发送通知 | +| GET | /notifications | 查询用户通知列表 | +| POST | /notifications/:id/read | 标记已读 | +| GET | /notifications/search?q= | 全文检索通知 | + +## 健康检查 + +| 端点 | 用途 | 鉴权 | +| -------------- | ------------------------------------------------- | ---- | +| `GET /healthz` | 存活探针(liveness),仅返回进程状态,不检查依赖 | 无 | +| `GET /readyz` | 就绪探针(readiness),检查 DB 连接,失败返回 503 | 无 | + +实现见 `src/shared/health/health.controller.ts`,5 个 NestJS 服务(iam/core-edu/content/msg/classes)一致。 ## 环境变量 -| 变量 | 默认值 | 说明 | -|------|--------|------| -| PORT | 3007 | 服务端口 | -| DATABASE_URL | - | MySQL 连接串 | -| KAFKA_BROKERS | localhost:9092 | Kafka broker 列表 | -| ES_URL | http://localhost:9200 | Elasticsearch 地址 | -| JWT_SECRET | - | JWT 密钥 | +| 变量 | 默认值 | 说明 | +| ------------- | --------------------- | ------------------ | +| PORT | 3007 | 服务端口 | +| DATABASE_URL | - | MySQL 连接串 | +| KAFKA_BROKERS | localhost:9092 | Kafka broker 列表 | +| ES_URL | http://localhost:9200 | Elasticsearch 地址 | +| JWT_SECRET | - | JWT 密钥 |