Files
Edu/docs/troubleshooting/known-issues.md
SpecialX 2ba4250165
Some checks failed
CI Go / test (push) Has been cancelled
CI Proto / lint (push) Has been cancelled
CI Python / test (push) Has been cancelled
CI TypeScript / test (push) Has been cancelled
feat(p1): complete P1 foundation stage
- 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
2026-07-07 23:39:37 +08:00

296 lines
31 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 已知问题速查
> 索引式速查手册:场景 → 技术/规则映射。不写代码示例。
> 架构规则见 [../architecture/004_architecture_impact_map.md](../architecture/004_architecture_impact_map.md) 与 [../../project_rules.md](../../project_rules.md)
> 工作经验日志按时间倒序追加50 条上限AI 发现更好方案时可更新本节。
---
## 一、全局经验
### 1.1 多语言 monorepo 配置
| 场景 | 技术/规则 |
| ----------------------------- | ------------------------------------------------------------------------------------------ |
| 多语言 workspace | pnpm workspaceTS+ go.workGo+ pyproject.toml/uv workspacePython三套并存 |
| 根 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+prettiercommit-msg 校验 Conventional Commits |
| .editorconfig 多语言缩进 | Go 用 tabPython 用 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+RedisP3 加 Kafka+ZookeeperP4 加 Debezium+CH+Neo4jP5 加 ESP6 加 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` 统一生成 |
| 契约先行硬约束 | 任何服务间通信必须先改 protoCI 检测 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/ownedrepository 下推 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 改 RS256IAM 签发,公钥校验) |
| 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 初始化(不引入完整后端) |
| 三支柱 | Logspino/winston/zap+ Metricsprom-client+ TracesOTel SDK |
| traceId 注入 | Gateway 注入 → 服务读取 header → 日志/响应携带 |
| P6 完整后端 | Loki日志+ Grafana仪表盘+ Jaegertrace+ Prometheusmetrics |
| Prometheus 指标 | `http_request_duration_seconds`Histogram+ `http_requests_total`Counter |
| 采样策略 | P1 全量 traceP6 引入采样率降低开销 |
| 日志参数顺序 | `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 wrappertiptap / @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-gatewayGo
| 场景 | 技术/规则 |
| ------------------- | ------------------------------------------------------------------------------------------ |
| P1 鉴权 | Gateway 内置 HS256 JWT`jwt.ParseWithClaims` + `SigningMethodHMAC` 校验 |
| P2 鉴权升级 | 改 RS256IAM 私钥签发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 classesTS/NestJSP1 黄金模板)
| 场景 | 技术/规则 |
| ------------------------- | ----------------------------------------------------------------------------------------------- |
| 黄金模板定位 | 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+ E2EPlaywright |
| 覆盖率门槛 | 领域逻辑 ≥ 80%Handler ≥ 60%,整体 ≥ 60% |
| Drizzle schema | `mysqlTable` + `varchar`/`timestamp` + `index` |
| ID 生成 | `@paralleldrive/cuid2` 的 `createId()` |
| 响应转换 | repository 返回 Dateservice 转换为 `createdAt: number`(时间戳) |
| 阶段特有模式回写 | OutboxP3/ CDCP4/ 长连接P5实现后回写黄金模板 README |
### 2.3 iamTS/NestJSP2
| 场景 | 技术/规则 |
| --------------------- | ------------------------------------------------------------------------------------------ |
| 认证 | 登录/登出/JWT/2FARS256 非对称签名 |
| 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 7dayrefresh 用 Redis 黑名单失效 |
| 权限缓存 | `getEffectivePermissions` 结果 Redis 缓存 TTL 5 分钟,角色变更主动失效 |
| schema 表 | users / roles / permissions / role_permissions / role_viewports / parent_student_relations / class_subject_teachers |
### 2.4 core-eduTS/NestJSP3
| 场景 | 技术/规则 |
| --------------------- | ------------------------------------------------------------------------------------------ |
| 考试全生命周期 | 教师创建 → 发布 → 学生作答 → 教师批改 → 成绩统计 |
| 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 contentTS/NestJSP4
| 场景 | 技术/规则 |
| --------------------- | ------------------------------------------------------------------------------------------ |
| 知识图谱 | Neo4j 查询前置依赖图(秒级返回) |
| 题库 CRUD | P4 仅 CRUDP5 引入 ES 实现检索,避免 MySQL FULLTEXT → ES 迁移成本 |
| 双写避免 | Neo4j/ES 不直接双写,由消费 Kafka 事件同步,天然最终一致 |
| Neo4j 写入 | Content 服务写 MySQL 同时发事件,独立 worker 消费事件同步 Neo4j |
### 2.6 data-anaPython/FastAPIP4
| 场景 | 技术/规则 |
| --------------------- | ------------------------------------------------------------------------------------------ |
| 学情诊断 | ClickHouse 宽表查询5s 内返回 |
| CDC 链路 | Debezium 监听 MySQL binlog → Kafka`mysql.cdc.*`)→ DataAna 消费写 ClickHouse |
| CDC 延迟监控 | Debezium 暴露 lag metrics超阈值告警 |
| 双轨读策略 | 实时查 MySQL 主库(刚提交的成绩),聚合查 CH 宽表(延迟 1-5s 可接受) |
| 幂等消费 | 所有事件消费者必须幂等(基于 event_id 去重) |
### 2.7 messagingTS/NestJSP5
| 场景 | 技术/规则 |
| --------------------- | ------------------------------------------------------------------------------------------ |
| 消息 CRUD | 会话/消息 + 调 Push Gateway 推送 + 通知偏好 |
| 通知批量化 | `createNotifications(items)` 单次 INSERT沿用旧项目 dispatcher 模式 |
| 多渠道 | 站内/SMS/邮件/微信in_app 批量 + 其他渠道并行 |
| fan-out 分页 | `getAllUserIds(limit=1000, offset)` 分页遍历 |
| 撤回不乐观更新 | 需服务端返回判断 2 分钟窗口 |
### 2.8 push-gatewayGoP5
| 场景 | 技术/规则 |
| --------------------- | ------------------------------------------------------------------------------------------ |
| WebSocket 长连接 | 单节点支撑 10w+ 连接,业务服务只需发 Kafka 消息 |
| 跨实例同步 | Redis PubSub |
| 离线消息 | 仅推在线用户,离线消息存 MySQL上线时拉取 |
### 2.9 ai-gatewayPython/FastAPIP5
| 场景 | 技术/规则 |
| --------------------- | ------------------------------------------------------------------------------------------ |
| LLM Provider 适配 | OpenAI/Anthropiclangchain/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 用正则提取(函数/类型/importP2 起替换为 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 tokenP2 起由 IAM 签发 RS256 |
| P2 Module Federation | next.config.js 配置,按场景域分 4 个稳定 portal |
---
## 三、工作经验日志
> 按时间倒序50 条上限。AI 发现更好方案时可更新本节。
| 日期 | 时间 | 模块 | 做了什么 + 学到什么 |
| ---------- | ----- | ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 2026-07-07 | 全天 | 全局 | 文档体系初始化从旧项目e:\Desktop\CICDNext.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 硬化。 |