- monorepo: pnpm workspace + go.work + pyproject.toml + commitlint/husky - infra: docker-compose (minimal + full profiles) + init-sql + prometheus - arch-scan: multi-language scanner skeleton (TS/Go/Python/Proto) - shared-proto: buf v2 + classes.proto (ClassService CRUD contract) - api-gateway: Go/Gin + JWT HS256 auth + reverse proxy + request ID - classes: NestJS golden template (error system + observability + middleware + CRUD + tests) - teacher-portal: Next.js + paper-feel UI design system - CI/CD: 4 workflows (go/ts/py/proto) - docs: migration guide + project_rules + coding-standards + git-workflow + ui-design-system + 004 + 9 module READMEs + known-issues + spec/plan migration + roadmap
31 KiB
31 KiB
已知问题速查
索引式速查手册:场景 → 技术/规则映射。不写代码示例。 架构规则见 ../architecture/004_architecture_impact_map.md 与 ../../project_rules.md 工作经验日志按时间倒序追加(50 条上限),AI 发现更好方案时可更新本节。
一、全局经验
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 空格 |
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 便于服务连接配置 |
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) |
1.4 NestJS 服务开发(TS)
| 场景 | 技术/规则 |
|---|---|
| 服务标准结构 | src/{main.ts,app.module.ts,config,middleware,<domain>,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 |
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 |
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 应用 |
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<T> |
| 仅类型导入 | import type |
1.9 架构工具与验证命令
| 场景 | 命令/规则 |
|---|---|
| arch.db 扫描 | npm run arch:scan(多语言:TS+Go+Python) |
| arch.db 查询 | npm run arch:query -- <command> |
| 查服务依赖 | npm run arch:query -- service <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 |
二、模块经验
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 无需鉴权 |
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 |
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 |
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 |
2.5 content(TS/NestJS,P4)
| 场景 | 技术/规则 |
|---|---|
| 知识图谱 | 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 去重) |
2.7 messaging(TS/NestJS,P5)
| 场景 | 技术/规则 |
|---|---|
| 消息 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,上线时拉取 |
2.9 ai-gateway(Python/FastAPI,P5)
| 场景 | 技术/规则 |
|---|---|
| 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/ |
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 |
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 |
三、工作经验日志
按时间倒序,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 硬化。 |