Files
Edu/docs/troubleshooting/known-issues.md
SpecialX 0a71b02e04
Some checks failed
CI / quality-ts (push) Failing after 48s
CI / quality-go (push) Failing after 4s
CI / quality-proto (push) Failing after 2s
CI / deploy (push) Has been skipped
fix: code compliance audit and fix across all services
NestJS (6 services): implement @RequirePermission decorator with
SetMetadata+Reflector, register APP_GUARD globally, fix as assertions
to type guards, add explicit return types, fix import type for express,
fix /metrics implicit any, replace native Error with ApplicationError,
remove typeorm remnants, register LifecycleService.

teacher-bff: add logger, ApplicationError, GlobalErrorFilter, forward
real userId to downstream, log downstream failures, migrate health
controller to shared/health.

Go (2 services): interface to any, doc comments, CORS dev whitelist,
JWT secret fail-fast, push-gateway internal API auth, metrics and
readyz endpoints, remove dead code.

Python (2 services): lifespan return type, dev_mode to bool, data-ana
APIRouter, ai POST body model, ClickHouse async wrapping.
2026-07-09 17:28:27 +08:00

128 KiB
Raw Blame History

已知问题速查

索引式速查手册:场景 → 技术/规则映射。不写代码示例。 架构规则见 ../architecture/004_architecture_impact_map.md../../.trae/rules/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 空格
ESLint 9 flat config P6 硬化:创建 eslint.config.jsflat configlint 脚本去掉 --ext .tslint-staged 恢复 eslint --fix
next lint 交互式初始化 teacher-portal 无 .eslintrc.jsonnext lint 触发 Strict/Base 选择提示CI 中需预置配置或改 eslint src

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 pingRedis 用 redis-cli ping
Windows 下卷挂载 init-sql 用绝对路径或确保相对路径正确
容器名固定 container_name: edu-mysql 便于服务连接配置
Kafka 双 listener INSIDE (kafka:29092) 容器间互访 + OUTSIDE (localhost:9092) 主机访问,避免 Debezium 拿到 localhost metadata 后切回连不上
ClickHouse 远程访问 默认 default-user.xml 限制 127.0.0.1/::1 无密码,挂载 clickhouse/users.d/custom-users.xml 覆盖密码+任意 IP
Debezium Connect 镜像源 daocloud 禁用 debezium/*,用 quay.io/debezium/connect:2.7 替代
Debezium 跨网络访问 MySQL MySQL 容器在 edu-minimal_default 时,docker network connect edu-full_default edu-mysql 让 Debezium 同时可达
CDC 注册 connector POST :8083/connectors,配置 topic.prefix/database.include.list/schema.history.internal.kafka.topic

1.3 protobuf + buf 契约

场景 技术/规则
契约唯一源 packages/shared-proto/proto/*.proto,禁止 REST/gRPC 混用
breaking change 检测 buf breaking --against '.git#branch=main' CI 强制,必须升版本号
proto 版本化 包名带版本 xxx.v1xxx.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 暴露 /metricsQPS/延迟/错误率)
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_secondsHistogram+ http_requests_totalCounter
采样策略 P1 全量 traceP6 引入采样率降低开销
日志参数顺序 log.error({ err: error, userId, traceId }, "操作描述"),错误对象字段名用 err
P6 /metrics 端点 NestJS 用 app.getHttpAdapter().get('/metrics', handler),不能用 app.get()(会被解析为 DI 容器 get
P6 prometheus.yml 8 个应用服务 + MySQL/Redis + node-exporter + prometheus 自身rule_files 引用 rules.ymlalerting 关联 alertmanager
P6 Grafana 数据源 provisioning/datasources 同时声明 Prometheus默认和 Lokiuid=loki
P6 Promtail 采集 docker_sd_configs + relabel_configs 仅采集 edu-* 前缀容器日志,避免无关日志
P6 service label static_configs.labels.service 给所有抓取目标打服务标签,告警规则按 service 聚合
P6 collectDefaultMetrics prom-client 的 collectDefaultMetrics({ register }) 自动收集进程级指标CPU/内存/事件循环/GC无需业务埋点即可让 /metrics 有数据
P6 Counter/Histogram 埋点缺失 metrics.ts 定义了 Counter/Histogram 但 service/controller 未调用 .inc()/.observe(),需后续补 HTTP 中间件自动埋点或业务埋点
P6 OTel instrumentations 缺失 tracer.ts 只配置 traceExporter 未注册 auto-instrumentationsHttpInstrumentation/ExpressInstrumentation导致 Jaeger 收不到业务 trace需后续补 @opentelemetry/auto-instrumentations已补全NestJS 6 服务用 getNodeAutoInstrumentations()Python 2 服务用 FastAPIInstrumentor.instrument_app(app)Go 2 服务用 otelgin.Middleware()
P6 镜像源配置 国内 docker.io 被墙compose image 必须加 docker.m.daocloud.io/ 前缀Elastic 官方镜像在 docker.elastic.co 不被墙
P6 Grafana 端口冲突 Grafana 默认 3000 与 teacher-portal Next.js dev 冲突,改映射为 3030:3000
P6 compose --no-deps mysql/redis 已在另一 compose 项目运行时,启动新服务用 --no-deps + 显式指定服务名,避免重建依赖容器

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_PURGEAUDIT_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 扫描 pnpm run arch:scan多语言TS+Go+Python+Proto
arch.db 查询 pnpm run arch:query -- <command>
查服务依赖 pnpm run arch:query -- deps <module>
查 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

二、模块经验

2.1 api-gatewayGo

场景 技术/规则
P1 鉴权 Gateway 内置 HS256 JWTjwt.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-IDc.Set("request_id", ...) + c.Header(...)
P1 不做 限流/熔断/灰度P6 硬化阶段实现)
健康检查 GET /health 无需鉴权
尾斜杠重定向循环 r.RedirectTrailingSlash=false + 同时注册 Any("/classes")Any("/classes/*path")
开发模式鉴权旁路 DEV_MODE=true 时接受 Bearer dev-token,注入固定身份;生产必须 false
路由注册位置 真实路由在 main.goapi.Group 内注册,internal/routing/routing.go 若未被 main 引用即为死代码
多服务路由扩展 新增服务代理时在 main.go 注册两组路由:Any("/x") + Any("/x/*path"),与 classes 一致
DEV_MODE 环境变量 Go 不自动加载 .envDEV_MODE 必须在启动前 export 或写入系统环境变量,否则 DevMode=false 导致 dev-token 被拒

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/cuid2createId()
响应转换 repository 返回 Dateservice 转换为 createdAt: number(时间戳)
阶段特有模式回写 OutboxP3/ CDCP4/ 长连接P5实现后回写黄金模板 README
健康检查依赖 readyz 用 Drizzle getDb().execute(sql\SELECT 1`)` 校验,不要依赖 typeorm DataSource DI
AppModule 注册 HealthModule 必须在 app.module.ts imports 数组显式声明,否则 NestFactory 不扫描 HealthController
增量编译陷阱 tsconfig.json 显式 "incremental": false 覆盖 base避免 .tsbuildinfo 导致 nest watch 不 emit

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
ESM 模式 DI providers: [IamService, IamRepository] + 构造器 @Inject(IamRepository) 显式注入,避免 undefined 运行时错误
Drizzle ORM API inArray(col, vals) 替代不存在的 .in()select 返回字段名按 schema 定义(如 r.iam_roles 而非 r.roles
健康检查依赖 readyzdb.execute(sql\SELECT 1`)` 校验连接,不要依赖 typeorm DataSourceIAM 用 Drizzle无 typeorm
Gateway 身份传递 Controller 直接读 req.headers['x-user-id'] / x-user-roles,不要依赖未注册的 AuthMiddleware 的 AuthenticatedRequest
DEV_MODE 登录 DEV_MODE=true 时 Gateway 接受 Bearer dev-token 注入固定身份IAM 仍支持真实 JWTHS256P2 应改 RS256
P2 公开路径白名单 Gateway publicPaths map 含 /iam/register//iam/login//iam/refreshAuthMiddleware 跳过鉴权避免死锁
P2 视口过滤 getUserViewportsrequiredPermission 过滤 + sortOrder 字典序排序,无权限要求的视口全员可见
P2 JWT payload HS256 签名含 sub/email/roles/dataScope/typeregister 自动分配 teacher 角色TEACHER_ROLE_ID 固定 UUID

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
datetime 列 Drizzle datetime 列需 Date 对象HTTP 请求体里是 ISO 字符串service 层必须 new Date(input) 转换否则报 toISOString is not a function
Kafka 非阻塞 开发环境 Kafka 未启动时 connectKafka() 必须 try/catch 且 main 中用 void 调用,否则阻塞服务启动
相对 import src/<domain>/ 下文件回溯一级用 ../,不要用 ../../NestJS ESM 模块)
身份头读取 Controller 用 @Req() reqreq.headers['x-user-id'] 读 createdBy/gradedBy不依赖未注册的 AuthMiddleware
健康检查 health.controller 用 Drizzle db.execute(sql\SELECT 1`)`,不用 typeorm DataSource
Outbox 验证 业务事务同写 core_edu_outboxKafka 未启动时事件 status=failed启动后会重试

2.5 contentTS/NestJSP4

场景 技术/规则
知识图谱 Neo4j 查询前置依赖图(秒级返回)
题库 CRUD P4 仅 CRUDP5 引入 ES 实现检索,避免 MySQL FULLTEXT → ES 迁移成本
双写避免 Neo4j/ES 不直接双写,由消费 Kafka 事件同步,天然最终一致
Neo4j 写入 Content 服务写 MySQL 同时发事件,独立 worker 消费事件同步 Neo4j
API 字段名 请求体用 TS schema 字段名(如 order),非 DB 列名(如 order_num
Neo4j 不可用 未设置 NEO4J_URL 时 driver=nullgetNeo4jSession 返回 null业务正常落库 MySQL
Neo4j 连接超时 driver 配置 connectionTimeout:3000避免 Neo4j 不可用时拖慢 HTTP 响应
Drizzle int drizzle-orm/mysql-core 导出 int() 不是 integer()
db 常量导出 database.ts 导出 db 常量替代 getDb() 函数,与 core-edu/classes 黄金模板对齐

2.6 data-anaPython/FastAPIP4

场景 技术/规则
学情诊断 ClickHouse 宽表查询5s 内返回
CDC 链路 Debezium 监听 MySQL binlog → Kafkaedu-cdc.next_edu_cloud.<table>)→ DataAna 消费写 ClickHouse
CDC 延迟监控 Debezium 暴露 lag metrics超阈值告警
双轨读策略 实时查 MySQL 主库(刚提交的成绩),聚合查 CH 宽表(延迟 1-5s 可接受)
幂等消费 所有事件消费者必须幂等(基于 event_id 去重)
CDC 消费者实现 cdc_consumer.py 用 aiokafka AIOKafkaConsumerlifespan 启动 asyncio.create_task 后台运行
ClickHouse 写入 clickhouse_client.upsert_student_dashboard() 用 client.insert() 写宽表client 为 None 时降级返回 False
Debezium 事件解析 before/after/source/op/ts_ms 五字段op=r(快照)/c(新增)/u(更新)/d(删除)
多表关联缓存 内存 ExamCache 缓存 exam_id→class_id 映射(来自 core_edu_exams CDC 事件grades 事件触发时查缓存填充宽表 class_id
Consumer offset 重置 kafka-consumer-groups --reset-offsets --to-earliest --execute 需先停消费者让 group 处于 Empty 状态
structlog API 24.x 用 make_filtering_bound_logger(level),旧版 make_filtering_logger 已废弃

2.7 messagingTS/NestJSP5

场景 技术/规则
消息 CRUD 会话/消息 + 调 Push Gateway 推送 + 通知偏好
通知批量化 createBatch(items) 单次 INSERT沿用旧项目 dispatcher 模式
多渠道 站内/SMS/邮件/微信in_app 批量 + 其他渠道并行
fan-out 分页 listByUserWithPagination(userId, page, pageSize) 分页查询
ES 降级 ES_URL 未设置时 esClient=nullsafeIndex/safeSearch 跳过返回空结果
Push 推送降级 PUSH_GATEWAY_URL 未设置或连接失败时 try/catch 跳过,不影响 DB 写入
db 常量导出 database.ts 导出 db 常量替代 getDb() 函数

2.8 push-gatewayGoP5

场景 技术/规则
WebSocket 长连接 单节点支撑 10w+ 连接,业务服务只需调 /internal/push
跨实例同步 Redis PubSubRedisURL 配置,预留 P6 实现)
离线消息 仅推在线用户,离线消息存 MySQL上线时拉取
并发写修复 send chan + 单写协程模式,避免 gorilla/websocket 并发写竞争
DEV_MODE 鉴权 DEV_MODE=true 时接受 dev-token生产环境必须 JWT 校验
广播端点 POST /internal/broadcastbody {event, data},调用 hub.Broadcast

2.9 ai-gatewayPython/FastAPIP5

场景 技术/规则
LLM Provider 适配 OpenAI 兼容 REST APIhttpx 异步),不引入 openai SDK
降级模式 API key 为空或调用失败时返回骨架响应,标记 degraded: true
流式 SSE AI 网关 → BFF → 前端三层透传BFF 不缓冲
路由前缀 业务路由加 /ai 前缀APIRouter prefix="/ai"Gateway 代理 /ai
dev_mode tracer dev_mode=true 时跳过 OTel exporter 初始化

2.10 shared-proto契约包

场景 技术/规则
目录结构 proto/*.proto + buf.yaml + buf.gen.yaml
P1 契约 classes.protoiam.proto/core_edu.proto 占位
代码生成输出 TS → shared-ts/generatedGo → shared-go/Python → services/*/generated/

2.11 arch-scan多语言扫描器

场景 技术/规则
TS 扫描 regex 提取function/class/interface/UPPER_CASE const避免 ts-morph 对未安装依赖文件解析失败
Go 扫描 正则提取(行首锚定 ^func/^typeP2 起替换为 tree-sitter-go AST
Python 扫描 正则提取(行首锚定 ^def/^classP4 起替换为 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
P2 路由组 + AppShell app/(app)/layout.tsx 用 AppShell 包裹受保护页;/login/ 不套壳
P2 真实 JWT 登录后 token 存 localStorageauthHeaders()Bearer ${getToken()}
P2 视口驱动侧边栏 AppShell fetch /teacher/viewports 渲染左侧导航active 路由高亮
P2 根路径重定向 app/page.tsx 客户端组件 router.replace(isAuthenticated() ? '/dashboard' : '/login')
fetch headers 类型 authHeaders(): Record<string, string> 显式标注,避免 {}HeadersInit 不兼容
P2 前端权限校验等价物 usePermission().hasPermission() Hook + <RequirePermission> 组件,镜像后端 @RequirePermission() 装饰器,禁止 role === "xxx" 硬编码§3.1
P2 MF Shell+Remote 架构 teacher-portal:3000 为 Shellstudent:3001/parent:3002/admin:3003 为 Remote共享登录态/布局/组件库/权限体系
P2 统一 API 请求层 ApiClient 封装 401 自动刷新 token 轮转 + ActionState {success,data,error} 解析 + 错误码前缀路由 i18n替代页面级 authHeaders()+fetch 重复
P2 设计令牌三层模型 primitive原始色板→ semantic-light/dark语义→ tailwind-theme@theme inline 暴露 bg-*ESLint no-restricted-syntax#hex + design-tokens/no-hardcoded-fonts 禁字面量字体§3.10
P2 5 层状态管理 nuqsURL 同步)/ TanStack Query服务端缓存/ Zustand客户端业务状态/ Zustand UIUI 临时态)/ react-hook-form表单
P2 4 端错误码前缀对齐 TP_=teacher-bff / SP_=student-bff / PP_=parent-bff / AP_=admin-bff前端按前缀路由 i18n key
P2 ErrorBoundary 路由级 <ErrorBoundary> 包裹避免白屏,搭配 <Suspense> 流式加载

三、工作经验日志

按时间倒序50 条上限。AI 发现更好方案时可更新本节。

日期 时间 模块 做了什么 + 学到什么
2026-07-09 夜间 data-ana/aiai06 ai06 阶段 1+2 交付Python 双服务)(1) 按 ai-allocation.md §4 必读清单读完 7 份全局文档README/MIGRATION_GUIDE/004/pending-features/project_rules/coding-standards/multi-ai-collaboration+ classes 黄金模板源码controller/service/repository/schema/dto+ shared-proto 8 份 protoiam/classes/core_edu/events/analytics/ai/msg/push+ ai-allocation.md §5 ai06 分工data-ana P4 + ai P5+ data-ana/ai 现有全部源码main.py/clickhouse_client.py/cdc_consumer.py/llm_client.py/config.py。(2) 阶段 1 产出 docs/architecture/ai06-phase1-understanding.mddata-ana 模块理解确认书(架构定位 L5 业务服务/限界上下文 D6 智能洞察/契约 AnalyticsService 3RPC + 消费 6 CDC topic/技术栈 Python3.12+FastAPI+ClickHouse+aiokafka/P4 阶段归属)+ ai 模块理解确认书(架构定位 L5/限界上下文 D6/契约 AiService 4RPC 含 StreamChat/技术栈 Python+FastAPI+LLM REST/P5 阶段归属)+ §10 服务审计表(两服务均缺 gRPC server 实现/缺权限校验 Depends/data-ana 缺 mastery.updated 事件发布/ai 缺 Redis 限流)+ 8 项跨模块契约待 coord 仲裁。(3) 阶段 2 产出 docs/architecture/ai06-phase2-design.mddata-ana 设计含 Mermaid 内部分层图Entry/Middleware/Service/Repo/Consumer/Storage 6 层)+ ClickHouse 4 张宽表 DDLstudent_dashboard_view ReplacingMergeTree(last_updated)/student_errors/mastery_snapshot/ai_usage_log+ HTTP+gRPC 双入口 API 设计 + 6 个 CDC topic 消费路由 + 发布 edu.insight.mastery.updated 事件 + DATA_ANA_* 错误码清单 + 10 项 metrics + 优雅关闭序列ai 设计含 Mermaid 分层图LLM Provider 适配器模式)+ 无 DB 状态less + 4 LLM ProviderOpenAI/Anthropic/Baichuan/Ollama+ Prompt 模板注册表 + 出题 4 步编排 + AI_* 错误码 + Redis 令牌桶限流 + edu.insight.ai.usage 事件发布 + 9 项待 coord 仲裁决策。(4) 学到Python 服务权限校验等价 NestJS @RequirePermission 的是 FastAPI Depends 依赖注入 + DataScope 6 级过滤SELF/CLASS/GRADE/SCHOOL/DISTRICT/ALLClickHouse ReplacingMergeTree 查询必须加 FINAL 关键字否则读到重复版本数据Python 服务无 MySQL 写事务故不能用 Outbox 模式派生数据事件mastery.updated/ai.usage建议直接 Kafka producer已提请 coord 仲裁是否豁免 §12.2 Outbox 强制约束ai 服务无 DB 设计stateless与 data-ana 有 ClickHouseOLAP形成对比需在审计表区分标注gRPC streamingStreamChat在 Python 用 grpc.aio + AsyncGenerator与 TS @grpc/grpc-js 双向流实现差异需在阶段 3 实施时对照ai-allocation.md §9.3 提交规范为 docs(<service>): 模块架构设计文档,三阶段强制经验沉淀到 known-issues.md 工作经验日志。
2026-07-09 夜间 teacher-portal/前端4端ai07 ai07 阶段 1+2 交付(前端 4 端)(1) 按 ai-allocation.md §4 必读清单读完 7 份全局文档README/MIGRATION_GUIDE/004/pending-features/project_rules/coding-standards/multi-ai-collaboration+ classes 黄金模板源码controller/service/repository/schema/dto+ shared-proto 8 份 proto + ai-allocation.md §5 ai07 分工。(2) 运行 pnpm run arch:scan 更新 arch.db10 TS/264 符号、2 Go/33 符号、2 Py/45 符号、138 proto 契约)。(3) 阶段 1 产出 4 端模块理解确认书(架构定位/限界上下文/契约依赖/技术栈/阶段归属/黄金模板对齐审计),合并到 apps/teacher-portal/README.md。(4) teacher-portal 现状审计 19 维度,发现 7 项高优违规AppShell.tsx L143 硬编码 user.roles.join(",")(违反 §3.1 前端禁 role 硬编码)/ globals.css+tailwind.config.js 硬编码 hsl() 字面量与 #hex(违反 §3.10 设计令牌)/ layout.tsx 直接 import 'Inter'/'Fraunces'/'JetBrains Mono' 字面量(违反 §3.10 禁硬编码字体)/ 缺统一 ApiClient 层4 页面重复 local authHeaders()+fetch/ 缺 usePermission Hook前端无 @RequirePermission 等价物)/ 缺 ErrorBoundary白屏风险/ 缺 5 层状态管理nuqs/TanStack Query/Zustand/Zustand-UI/react-hook-form 全缺)/ next.config.js 无 Module Federation 配置4 端无法 Shell+Remote 组合)。(5) 阶段 2 产出模块架构设计文档MF 2.0 Shell+Remote 架构teacher-portal:3000 为 Shellstudent:3001/parent:3002/admin:3003 为 Remote含 mermaid 架构图 + MF config 代码示例 + 领域模型Session/Viewport/Permission TS 接口)+ 数据模型缓存策略表5 层 DataScope × 5 类缓存键)+ API 请求层设计ApiClient 401 自动刷新 + ActionState 解析 + 错误码前缀路由 i18n+ WebSocket/SSE 事件设计 + 横切关注点对齐清单(权限表 4 端前缀/错误码前缀 TP_/SP_/PP_/AP_/logger/metrics/tracer/health。(6) 4 端差异化对照表 5 张(总体/L1 导航/L2 路由/L3 组件/L4 数据)。(7) 交互点契约清单 12 项 + 风险 7 项 + 假设 5 项 + 4 项待 coord 仲裁packages 归属/GraphQL vs REST/i18n key 命名/MF 暴露粒度)。(8) coord 交叉审查信息:端口矩阵 3000-3003、5 个 shared 包待建shared-ts/shared-tokens/shared-ui/shared-mf/shared-perm、11 个后端契约依赖、错误码前缀对齐、无 Kafka 事件消费。学到:前端权限校验等价物是 usePermission().hasPermission() Hook + <RequirePermission> 组件(镜像后端 @RequirePermission() 装饰器MF Shell+Remote 架构选择优于 4 独立 Shell共享登录态/布局/组件库/权限体系)和单 Next.js 应用4 端独立部署/独立 CI/独立回滚teacher-portal 现状有 7 项高优违规需 P2 闭环前修复4 端 API 错误码前缀需与后端服务对齐TP_=teacher-bff、SP_=student-bff、PP_=parent-bff、AP_=admin-bff设计令牌三层模型primitive/semantic-light+dark/tailwind-theme必须 ESLint 强制约束no-restricted-syntax 禁 #hex + design-tokens/no-hardcoded-fonts 禁字面量字体)。
2026-07-09 夜间 api-gateway/push-gatewayai01 ai01 阶段 1+2 交付Go 网关层)(1) 按 ai-allocation.md §4 必读清单读完 7 份全局文档README/MIGRATION_GUIDE/004/pending-features/project_rules/coding-standards/multi-ai-collaboration+ classes 黄金模板全部源码 + shared-proto 8 份 protoiam/msg/events+ api-gateway/push-gateway 全部 Go 源码。(2) 运行 pnpm run arch:scan 更新 arch.db10 TS/264 符号、2 Go/33 符号、2 Py/45 符号、138 proto 契约)。(3) 阶段 1 产出 2 份模块理解确认书services/{api-gateway,push-gateway}/docs/01-understanding.md+ 审计表api-gateway 审计出 13 项差距3 高优先:缺 /metrics 端点、/readyz 是 stub 返回 ok 不检查依赖、auth.go L124-139 死代码 RequestIDMiddleware+generateUUID 与 requestid.go 重复4 中logger 用 fmt 非 log/slog、go.mod go 1.25.0 与 Dockerfile golang:1.22-alpine 版本不匹配、HS256 待 P2 升 RS256、DevMode 生产环境风险6 低push-gateway 审计出 16 项差距6 高:无 Redis Pub/Sub 横向扩展、CheckOrigin 直接 return true 安全风险、用文本 "ping"/"pong" 心跳非 RFC 6455 控制帧、无单用户连接数上限、/internal/* 无鉴权、Dockerfile 单阶段且 root 用户无 healthcheck。(4) 阶段 2 产出 2 份模块架构设计文档02-architecture-design.mdapi-gateway 覆盖 9 节(内部分层图、路由表矩阵 9 下游含端口+鉴权规则、限流策略表按路由差异化 RPS/burst、熔断阈值表按服务、JWT RS256 流程含 JWKS 缓存、CORS 白名单、请求 ID 注入、metrics 7 项指标清单、P0-P3 实施优先级push-gateway 覆盖 13 节内部分层图、Connection/Hub 领域模型、Redis 4 个 key pattern、WebSocket 端点+子协议 JSON 格式、内部推送 API+X-Internal-Token 鉴权、双通道协议 HTTP 同步+Kafka 异步、WebSocket 生命周期状态图、心跳协议 RFC 6455 控制帧 30s 间隔 60s 超时、单用户最大 5 连接、重连协议 P6 预留、多实例架构图、Redis Pub/Sub 跨实例流程、容量目标 10w+ 连接 <50ms 本地推送 <200ms 跨实例)。学到gobreaker v2 ReadyToTrip 在 Requests=1 时 1*2>1=true 即 1 次失败就触发 OPEN与 P6 集成测试观察一致gorilla/websocket 不支持并发写同一连接Hub.Send 必须用 send chan + 单写协程串行化(已在 P5 修复但设计文档需明确标注此约束push-gateway 骨架用文本 "ping"/"pong" 违反 RFC 6455应用 SetPongHandler 处理控制帧004 §7.2 事件 topic 用 edu.teaching.exam.published 前缀但代码 TOPIC_MAP 用 edu.exam.eventsai03 已提请 coord 仲裁,本 AI 在 push-gateway 设计中消费 edu.notification.events 待 coord 统一命名后同步api-gateway 与 push-gateway 重复 tracer.go/logger.go/jwks.go/env.go建议提取到 packages/shared-go/(需 coord 创建包后多 AI 协同迁移Go 服务 .env 不会自动加载DevMode 必须在启动前 export DEV_MODE=true 或集成 godotenv。
2026-07-09 夜间 teacher-bff/core-eduai03 ai03 阶段 1 全局理解交付(1) 按 ai-allocation.md §4 必读清单读完 7 份全局文档 + classes 黄金模板全部源码 + shared-proto 8 份 proto + teacher-bff/core-edu 现有实现。(2) 运行 arch:scan 更新 arch.db10 TS/264、2 Go/33、2 Py/45、138 proto 契约arch:query deps/stats 发现 arch.db 仅记录模块/符号统计不记录跨模块调用边。(3) 按 §6 模板产出两份模块理解确认书 + §10 审计表,交付 docs/architecture/ai03-phase1-understanding.md。(4) 审计发现 teacher-bff 7 项差距REST 非 gRPC/无 GraphQL/无 DataLoader/无 Redis 缓存/无 readyz/无 Zod/无测试、core-edu 12 项差距(考试状态机缺失/作业状态机不完整/成绩无校验/无并发锁/homework.graded 与 grade.updated 事件未触发/未消费 IAM user.created/Drizzle db 导出 vs classes getDb() 不一致/kafka.ts 用 console 非 logger/classes 模块仅占位待合并/REST 未转 gRPC/无 Zod/无测试)。(5) 提请 coord 交叉审查 6 项跨模块契约对齐iam getEffectivePermissions 聚合 API proto / iam user.created topic / 端口 3004 / Kafka topic 命名 004 文档与代码不一致 / data-ana 消费契约 / msg 消费契约)。学到004 §7.2 事件 topic 用 edu.teaching.exam.published 前缀,但 core-edu outbox.publisher.ts TOPIC_MAP 用 edu.exam.events,文档与代码不一致需 coord 仲裁统一teacher-bff 当前用 REST fetch 但 P2 退出标准要求 GraphQL Yoga + DataLoader阶段 2 设计需补通信方式迁移core-edu 与 classes 黄金模板的 Drizzle 访问方式不一致core-edu 直接 export const dbclasses 用 getDb() 函数),建议统一为 getDb() 函数式以匹配 HealthController 已有约定。
2026-07-09 傍晚 全局 全服务代码合规性审查 + 批量修复:对 10 个服务6 NestJS + 2 Go + 2 Python执行严格代码审查发现 7 critical + 42 major + 35 minor 问题,批量修复如下。(1) @RequirePermission() 装饰器实现6 个 NestJS 服务全部实现 SetMetadata + Reflector 标准模式PermissionGuard 改用 getAllAndOverride 读取元数据,注册为 APP_GUARD 全局 GuardDEV_MODE 旁路。content/msg 新建 permission.guard.ts + auth.middleware.ts。(2) as 断言消除:所有 req.headers['x-user-id'] as string 改为 typeof 类型守卫,涉及 auth.middleware.ts/global-error.filter.ts/controller.ts。(3) 返回类型补充getDb() 标注 MySql2Database<typeof schema>Controller 方法补充 Promise<{ success: true; data: ... }>。(4) import type 修复express 的 Request/Response/NextFunction 改为 import type。(5) main.ts /metrics 隐式 any 修复:参数标注 Request/Response 类型。(6) 原生 Error → ApplicationErrorrepository.ts 的 throw new Error 改为 DatabaseError。(7) typeorm 残留清理classes lifecycle.service.ts 移除 typeorm DataSource 依赖改用 Drizzle closeDb()。(8) LifecycleService 注册5 个 NestJS 服务的 AppModule 注册 LifecycleService。(9) teacher-bff 补全:新建 logger.ts + ApplicationError 体系 + GlobalErrorFilter下游调用转发真实 userId替换硬编码 "bff");失败时 logger.warn + BadGatewayError不再静默吞错health.controller 迁移到 shared/health/ 标准结构。(10) Go 安全修复CORS 默认 * 改为开发白名单 + warningJWT 密钥非 DevMode fail-fastpush-gateway /internal/* 添加 X-Internal-Key 鉴权;interface{}any;删除死代码 RequestIDMiddleware/generateUUID/getEnvInt;补充 doc commentapi-gateway + push-gateway 添加 /metrics 端点push-gateway 添加 /readyz。(11) Python 修复lifespan 返回类型标注 AsyncGenerator[None, None]dev_modestr 改为 booldata-ana 业务路由改用 APIRouterai POST 端点从 query 参数改为 Pydantic 请求体模型data-ana ClickHouse 同步调用包装在 asyncio.to_thread() 中。(12) core-edu GlobalErrorFilter 统一:响应契约改为 { success: false, error: { code, message, details, traceId } } 与 content/msg 一致。(13) msg 修复notifications.dto.ts 新建 Zod 验证 schemauuid v4 替换为 node:crypto.randomUUIDES 模块 console.error 改为结构化 logger。学到NestJS SetMetadata@nestjs/common 导入(非 @nestjs/coreAPP_GUARD 注册全局 Guard 是标准模式;Reflector.getAllAndOverride 支持 handler+class 两级元数据查找PermissionGuard 必须在 DEV_MODE 下旁路否则开发环境无法测试Go 的 interface{} 在 1.18+ 应统一用 anyPython asyncio.to_thread() 是包装同步 IO 为异步的标准方式FastAPI lifespan 返回类型是 AsyncGenerator[None, None]
2026-07-09 下午 classes/全局 一键启动脚本 NestJS dist/ 不生成根因定位 + classes 健康检查修复(1) 根因定位:tsconfig.base.jsonincremental: true + nest-cli.jsondeleteOutDir: true 冲突。nest start --watch 启动时先删除 dist/tsc 读残留 .tsbuildinfo 认为无变化跳过 emitdist/ 不生成 → Cannot find module dist/main。(2) 修复6 个 NestJS 服务classes/iam/teacher-bff/core-edu/content/msgtsconfig.json 显式加 "incremental": false 覆盖 base 配置,删除所有残留 .tsbuildinfo 文件。(3) classes AppModule 缺 HealthModule 导入导致 /healthz 404iam 同样问题,修复 app.module.ts 加 imports: [..., HealthModule]。(4) classes HealthController 误用 TypeORM DataSource DI与 iam 不一致),运行时报 Nest can't resolve dependencies of the HealthController (DataSource)。修复:改为 Drizzle getDb() 函数式调用,与 iam 一致。(5) 一键启动验证11/11 应用 + 11/11 基础设施 + 5/5 可观测性端点全绿。学到NestJS + TypeScript incremental 编译是陷阱组合——nest-cli deleteOutDir 删 dist 但 tsc 读 tsbuildinfo 认为无变化,必须在服务级 tsconfig 显式 incremental: falseHealthModule 必须在 AppModule imports 中显式声明才能被 NestFactory 扫描到5 个 NestJS 服务的 HealthController 应统一用 Drizzle getDb() 函数式调用而非 TypeORM DataSource DI项目已弃 TypeORM 改 Drizzle
2026-07-09 下午 全局 OTel auto-instrumentations 全服务补全(1) NestJS 6 服务iam/classes/core-edu/content/msg/teacher-bfftracer.ts 补 getNodeAutoInstrumentations()NodeSDK 传 instrumentations 参数自动埋点 HTTP/Express/DB。(2) Python 2 服务data-ana/aimain.py 补 FastAPIInstrumentor.instrument_app(app)ai 补缺失的 opentelemetry-exporter-otlp 依赖。(3) teacher-bff 从零补完整 OTelenv.ts 加 OTEL_EXPORTER_OTLP_ENDPOINT 字段 + 新建 shared/observability/tracer.ts + main.ts 调用 initTracer/shutdownTracer + package.json 加 sdk-node/exporter/auto-instrumentations 依赖。(4) Go 2 服务api-gateway/push-gateway新建 internal/observability/tracer.goOTLP HTTP exporter + resource + TracerProvider + W3C propagator+ main.go 调用 InitTracer + otelgin.Middleware 注册 Gin 中间件push-gateway config.go 补 OTLPEndpoint 字段。(5) 质量校验全通过TS typecheck 9 服务 + ESLint 6 服务 + ruff 2 服务 + go vet/build 2 服务零错误。学到getNodeAutoInstrumentations() 一次注册所有 Node.js 自动埋点http/express/dns/fs/net/grpc 等),比手动逐个注册 HttpInstrumentation 更简洁Go OTel 用 otlptracehttp.WithEndpoint(host) + WithInsecure() 需从 "http://host:port" URL 解析出 hostotelgin.Middleware 必须在 Recovery 之后其他中间件之前注册,确保所有后续 handler 都被 tracePython FastAPIInstrumentor.instrument_app(app) 在 app 创建后立即调用lifespan 不受影响。
2026-07-09 下午 全局 P6 硬化:可观测性 + 部署 + CI 硬化(1) 可观测性栈完善5 个 NestJS 服务 main.ts 添加 /metrics Prometheus 端点(用 app.getHttpAdapter().get('/metrics', ...) 绕过 DI 容器 get 方法prometheus.yml 从 2 个目标扩展到 8 个应用服务 + MySQL/Redis + node-exporter + prometheus 自身 + rule_files + alertmanager 关联monitoring compose 用 Loki + Promtail 替换未配置的 blackbox-exporterGrafana datasource 新增 Loki新建 promtail/config.yml 用 docker_sd_configs 仅采集 edu-* 容器日志。(2) 部署 compose 扩展docker-compose.deploy.yml 从 3 服务扩展到 11 服务(+ iam/teacher-bff/core-edu/content/msg/ai/data-ana/push-gateway每个服务带 healthcheck + depends_on 条件 + edu-net/edu-shared 双网络deploy.env.example 补全 Neo4j/ES/ClickHouse/LLM/Kafka 可选依赖配置。(3) teacher-bff 补 health.controller.ts原缺失 /healthz 导致 deploy depends_on service_healthy 失败)。(4) CI 硬化:移除 lint 步骤的 continue-on-errorESLint 9 flat config 已配置完成test 保留 continue-on-error部分服务无 test 脚本)。学到NestJS app.get('/metrics') 会被解析为 DI 容器 get(typeOrToken),必须用 app.getHttpAdapter().get() 才能注册 Express 路由Promtail docker_sd_configs 通过 relabel_configs 的 regex: '/(edu-.*).*' 过滤容器名前缀docker-compose.depends_on.condition: service_healthy 要求被依赖服务必须有 healthcheck 配置,否则启动失败。
2026-07-09 下午 data-ana/infra CDC 完整链路实现MySQL binlog → Debezium Connect → Kafka → data-ana 消费者 → ClickHouse 宽表。(1) MySQL binlog 配置log_bin=ON, binlog_format=ROW, binlog_row_image=FULL, server_id=1用 root 创建 debezium 用户授予 REPLICATION SLAVE + REPLICATION CLIENT。(2) Debezium Connect 容器daocloud 禁用 debezium 镜像改用 quay.io/debezium/connect:2.7MySQL 容器在 edu-minimal_default 网络,需 docker network connect edu-full_default edu-mysql 让 Debezium 同时可达Kafka 必须配置双 listenerINSIDE:kafka:29092 + OUTSIDE:localhost:9092否则 Debezium 拿到 advertised.listeners 中的 localhost metadata 后切换失败Debezium 2.x 容器环境变量名用 BOOTSTRAP_SERVERS不带 KAFKA_ 前缀),通过 envsubst 替换到 connect-distributed.properties。(3) 注册 connectorPOST :8083/connectors配置 topic.prefix=edu-cdc, database.include.list=next_edu_cloud, snapshot.mode=initial4 张表core_edu_grades/exams/classes/iam_users成功产生快照事件。(4) data-ana 消费者实现:新建 cdc_consumer.py 用 aiokafka AIOKafkaConsumerlifespan 中 asyncio.create_task 后台运行;按 source.table 路由exams→内存缓存 exam_id→class_id 映射grades→查缓存填 class_id 后 upsert ClickHousereadyz 端点附加 cdc_consumer 状态。(5) ClickHouse 远程访问:默认 default-user.xml 限制 127.0.0.1/::1 无密码,挂载 clickhouse/users.d/custom-users.xml 覆盖密码+任意 IP。(6) structlog 24.x APImake_filtering_bound_logger(level) 替代废弃的 make_filtering_logger。(7) E2E 验证MySQL INSERT 成绩 → Debezium op=c 事件 → Kafka → 消费者写 ClickHouse 宽表class_id 通过 exam 缓存正确填充)→ /readyz cdc_consumer=running → /analytics/student/student-002/weakness 返回实时 92 分数据。学到Debezium 2.x 容器 bootstrap.servers 默认值是 0.0.0.0:9092 必须显式覆盖Kafka 单 listener 配置 localhost 会让容器间通信的客户端拿到 metadata 后切换失败,必须用双 listenerClickHouse users_xml 存储是 readonly 不能用 ALTER USER 修改密码,必须挂载 users.d 配置文件覆盖;消费者 offset 重置必须先停消费者让 group 处于 Empty 状态才能执行 --reset-offsets。
2026-07-09 下午 全局 P6 硬化ESLint 9 flat config 配置(1) 根目录创建 eslint.config.jsESLint 9 flat config 格式):用 typescript-eslint recommended 规则集 + @eslint/js recommended + eslint-config-prettier 禁用冲突规则;自定义规则:no-explicit-any warn + no-unused-vars 允许下划线前缀 + 测试文件放宽。(2) 6 个 TS 服务 package.json lint 脚本从 eslint src --ext .ts 改为 eslint srcflat config 不需要 --ext。(3) lint-staged.config.js 恢复 eslint --fix。(4) 验证classes/content/msg/core-edu 四服务 lint 全部零错误零警告通过。学到ESLint 9 flat config 用 tseslint.config() 工厂函数组装配置数组;--ext 参数在 flat config 模式下被移除ESLint 自动根据 eslint.config.js 中的 files 匹配;@typescript-eslint/consistent-type-assertions 规则选项格式在 v8 中变化(objectLiteralTypeobjectLiteralTypeAssertions),配置时需查最新文档。
2026-07-09 中午 msg/push-gateway/ai/api-gateway P5 沟通与 AI 阶段三服务完善(1) msg 服务修复database.ts 导出 db 常量env.ts JWT_SECRET/ES_URL 改 optional 加 DEV_MODE/PUSH_GATEWAY_URLelasticsearch.ts ES 降级esClient=null 时 safeIndex/safeSearch 跳过notifications.service.ts 加 createBatch + listByUserWithPagination + Push Gateway 推送调用try/catch 降级);新建 msg-init.sql 2 张表。(2) push-gateway 完善hub.go 重写用 send chan + 单写协程模式修复 gorilla/websocket 并发写竞争handler.go 加 DEV_MODE dev-token 支持 + broadcast 端点config.go 加 DevMode/RedisURL。(3) ai 服务完善config.py 加 openai_api_key/base_url/dev_mode新建 llm_client.pyhttpx 异步调 OpenAI REST APImain.py 加 /ai 前缀 + 降级模式(无 key 返回骨架 + degraded: true+ /readyz 端点。(4) Gateway 路由扩展:/notifications → msg/ai → ai 服务。学到gorilla/websocket 不支持并发写,必须用 send chan 串行化所有写入FastAPI APIRouter prefix 与 Gateway 代理路径要协调ai 服务加 /ai 前缀Gateway 代理 /ai/*pathLLM 降级策略统一返回 degraded 标记,调用方据此判断是否路由流量。
2026-07-09 上午 content/api-gateway P4 内容分析服务端到端打通(1) content 服务系统性修复database.ts 导出 db 常量env.ts JWT_SECRET/ES_URL/NEO4J_URL/NEO4J_PASSWORD 改 optional 加 DEV_MODEneo4j.ts driver 惰性创建+try/catch+connectionTimeout:3000health/lifecycle 改用 Drizzleglobal-error.filter 移除 @types/express 依赖textbooks.schema 修复 integer→int + 导出 NewTextbook/NewChapter 类型textbooks.controller 移除 body as any + 加 PUT/DELETE。(2) 新建 3 模块chaptersCRUD + 按 textbook 查询、knowledge-pointsCRUD + Neo4j 前置依赖图非阻塞查询、questionsCRUD + 4 种题型校验)。(3) Gateway 路由扩展textbooks/chapters/knowledge-points/questions 四组路由。(4) 数据库content-init.sql 4 张表。(5) E2E 验证POST /textbooks 201 → POST /chapters 201字段用 order 非 orderNum→ POST /knowledge-points 201Neo4j 不可用 MySQL 正常写入)→ POST /questions 201 → GET 各列表 200。学到Drizzle schema TS 字段名与 DB 列名解耦order→order_numAPI 请求体用 TS 字段名Neo4j 不可用时必须 driver=null不设 NEO4J_URL否则每次请求尝试连接拖慢响应neo4j-driver safeCreateNode 用 try/catch 非阻塞MySQL 数据始终先落库。
2026-07-09 凌晨 core-edu/api-gateway P3 核心教学服务端到端打通(1) core-edu 服务系统性修复 13 项database.ts 导出 db 常量替代 getDb()env.ts JWT_SECRET 改 optional 加 DEV_MODEkafka.ts connectKafka 加 try/catch 不阻塞启动main.ts 去全局 /api 前缀 + connectKafka 改 void 非阻塞app.module 移除未用 AuthMiddleware/ClassesesModule 加 HealthModule3 个 controller 路由去前缀去 UseGuards 从 x-user-id 读身份exams/homework service datetime 列 ISO 字符串转 Date 修复 drizzle toISOString 错误;修正 10 处相对 import 路径health/lifecycle 改用 Drizzle 原生查询;新增 core-edu-init.sql 4 张表。(2) Gateway 路由扩展:发现 internal/routing/routing.go 是死代码(未被 main 引用),真正路由在 main.go在 main.go 添加 exams/homework/grades 三组路由(无尾斜杠+通配符);删除 routing.goconfig.go 加 CoreEduServiceURL。(3) DEV_MODE 环境变量问题Go 不自动加载 .env必须在启动前 export DEV_MODE=true 否则 dev-token 被拒 401。(4) E2E 验证POST /exams 201 → GET /exams/:id 200 → GET /exams/class/:id 200 → POST /homework 201 → POST /grades 201 → Outbox 3 条事件正确写入exam.failed 因 Kafka 未启动homework/grade pending学到drizzle datetime 列需 Date 对象不是 ISO 字符串mapToDriverValue 调 toISOStringGo 项目 .env 不会自动加载需显式 export 或 godotenv 库NestJS controller 路由前缀与 Gateway 代理路径要协调Gateway 去掉 /api/v1 后转发controller 用裸路径如 'exams'Outbox 模式业务事务同写验证通过Kafka 未启动时事件 status=failed 但业务数据已落库。
2026-07-09 上午 iam/teacher-bff/teacher-portal P2 身份阶段完整实现(1) Gateway 公开路径白名单register/login/refresh解决无 token 死锁。(2) IAM schema 扩展users 加 dataScope新增 role_viewports 表。(3) RBAC 端点 4 个 GET。(4) 视口按 requiredPermission 过滤 + sortOrder 排序getEffectivePermissions 用 Set 去重。(5) JWT payload 含 dataScoperegister 自动分配 teacher 角色。(6) 种子数据 7 权限+12 映射+7 视口。(7) Teacher BFF 视口聚合。(8) 前端lib/auth.ts + login + AppShell + (app) 路由组 + dashboard + classes真实 JWT+ 根重定向。(9) E2E 全链路通过。学到Next.js 路由组 (app) 不影响 URL/login 与 /dashboard 共存只后者套壳fetch headers 函数返回 Record<string,string> 避免 TS2769ESLint 9 需 flat config 留 P6AppShell aside 用 flex flex-col + mt-auto 比 absolute 稳健。
2026-07-08 晚上 iam/classes/api-gateway P1 端到端链路验证 + IAM 服务修复:验证 register → JWT → Gateway /iam/me → Gateway /classes CRUD → teacher-portal 前端渲染全链路打通。(1) IAM 服务 14 个 TS 编译错误修复:移除 typeorm/ioredis/kafkajs 依赖IAM 用 Drizzlehealth.controller.ts 改用 db.execute(sql\SELECT 1`)lifecycle.service.ts 简化为只关闭 Drizzle 连接池Drizzle API 修正(r.rolesr.iam_roles.in()inArray())。(2) NestJS ESM DI 修复iam.module.ts 简化 providers 为 [IamService, IamRepository]iam.service.ts 构造器加 @Inject(IamRepository)(参考 classes 黄金模板),修复运行时 Cannot read properties of undefined (reading 'findUserByEmail')。(3) Gateway /iam/me 404 修复iam.controller.ts 直接读 req.headers['x-user-id']替代未注册的AuthenticatedRequest。(4) 创建 scripts/iam-init.sql建 6 张 IAM 表 + 种子数据。(5) E2E 验证iam:3002 注册/登录 → Gateway /iam/me 200 → Gateway GET /classes 200 → Gateway POST /classes合法 UUID gradeId201 → teacher-portal:3000 首页渲染 200 + 含"班级管理" → Next.js rewrites 透传 dev-token 到 Gateway 全链路通。**学到**NestJS ESM 模式下 DI 无法通过类型推断解析 token必须显式@Inject(Token)Drizzle select 返回字段名按 schema 定义而非表名classes.dto.ts 的 gradeId 要求 UUID 格式,测试数据不能用 "grade-12" 这类字符串PowerShell 控制台中文显示为 ?是编码问题数据库实际存储正确DEV_MODE 下前端用Bearer dev-token` 即可走通链路,无需真实 JWT。
2026-07-08 下午 全局 CI/CD 完整配置 + 多AI协作规范入规则(1) project_rules.md 新增 §14 多 AI 协作规范(角色权限矩阵/分支命名/PR合并规则/跨模块变更顺序/冲突处理/AI 身份标注/敏感文件保护)+ §15 CI/CD 规范(流水线阶段/触发条件/镜像规范/部署策略/Secrets 管理/必需 CI 文件)。(2) 优化现有 4 个 ci-*.ymlci-ts.yml 加 arch-scan + docker-build jobci-go.yml 去掉 golangci-lintlint-staged 预存问题),加 docker-buildci-proto.yml 修复 buf breaking URL从 github.com 改为 .git 本地比较)。(3) 新增 docker.ymlmain/tag 触发,构建推送 3 服务镜像到 Gitea Container Registrygit.eazygame.cn/xiner/edu/:latest + sha tag + version tag用 GITHUB_TOKEN 自动认证。(4) 新增 deploy.ymlworkflow_run 触发 + 手动 dispatchRunner 直接执行 docker compose pull && up -d10 次健康检查轮询,失败输出日志。(5) 新增 infra/docker-compose.deploy.yml(部署用,镜像来自 Gitea registry连接服务器已有 MySQL/Redis 通过 edu-shared 外部网络)+ infra/deploy.env.example(部署环境变量模板)。(6) 编写 docs/standards/cicd-runbook.mdCI/CD 使用手册,含架构总览/一次性配置/日常使用/镜像管理/部署验证/回滚/常见问题/排查命令/安全注意事项)。学到Docker Compose 不支持 restart_policy(是 swarm 字段),用 restart: unless-stopped 替代Gitea Actions 兼容 GitHub Actions 语法但 workflow_run 触发可能不完整,备选手动 dispatch应用容器访问宿主机已有 MySQL/Redis 需通过共享外部网络(docker network create edu-shared + docker network connect)而非 host.docker.internal
2026-07-08 下午 api-gateway 重定向循环修复 + 生产模式部署准备 + 多AI协作文档(1) 修复 ERR_TOO_MANY_REDIRECTSGin 默认 RedirectTrailingSlash=true 导致 /api/v1/classes → 301 → /classes/Next.js rewrites 代理时形成循环。修复r.RedirectTrailingSlash=false + 同时注册无尾斜杠路由(/classes)与通配符路由(/classes/*path)。(2) 新增 DEV_MODE 旁路:config.goDevMode 字段,auth.goDEV_MODE=true 时接受 dev-token 注入固定身份(生产必须 false。(3) 生产 Docker 化:新建 apps/teacher-portal/Dockerfile(多阶段 Next.js build+ services/api-gateway/Dockerfile(多阶段 Go 静态编译)+ infra/docker-compose.prod.yml(三服务编排,强制 DEV_MODE=false。(4) 编写 docs/standards/local-dev-runbook.md(本地启动手册,含端口表/开发模式/生产模式/常见问题)+ docs/standards/multi-ai-collaboration.md多AI协作文档含模块分工矩阵/分支命名/PR流程/合并策略/冲突处理/权限矩阵)。学到Gin RedirectTrailingSlash=false 后需显式注册无尾斜杠路由(Any("/classes") + Any("/classes/*path")),否则 404Next.js rewrites 代理会透传 301 给浏览器形成循环,开发模式旁路应通过环境变量控制而非硬编码。
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/tunago.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_HOMETMP 环境变量避免 _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
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 限界上下文 subgraphD1 身份/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.go5 用例ClosedToOpen/OpenToHalfOpen/HalfOpenToClosed/HalfOpenToOpen/4xxNotCounted+ ratelimit_test.go5 用例AllowUnderBurst/RejectOverBurst/RefillTokens/PerIPIsolation/CleanupExpiredBuckets+ test-backup-mysql.sh8 用例 17 断言)。学到gobreaker v2 ReadyToTrip 在 1 次失败后就触发(TotalFailures*2 > Requests 当 Requests=1 时 1*2>1=trueHALF_OPEN 状态只在探测执行期间可见,探测完成后立即转 CLOSED 或回 OPEN测试需通过行为503 vs 500而非状态字段验证rateLimiter cleanup 测试需用短周期参数50ms/500ms加速且新鲜桶要在旧桶清理后再创建避免被一起清掉。
2026-07-08 下午 infra/k8s Helm Chart 演化:安装 Helm v4.2.2,创建 edu-platform 平台级 chartnamespace/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\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 硬化。