chore(deps): 统一依赖管理 - pnpm 11 + node:22 + golang:1.25 + python:3.12 + shared-* 集中化
Some checks failed
CI / quality-ts (push) Failing after 6s
CI / quality-go (push) Failing after 25s
CI / quality-proto (push) Failing after 6s
CI / deploy (push) Has been skipped

- Node.js 统一到 node:22-alpine,Go 统一到 golang:1.25-alpine,Python 统一到 python:3.12-slim

- pnpm 升级到 11.13.0(corepack),新增 allowBuilds 白名单解决 ERR_PNPM_IGNORED_BUILDS

- 新增 packages/shared-py 集中 Python 共享依赖,shared-ts 补充 graphql-yoga/prom-client

- api-gateway 修复 go.mod 的 shared-go 依赖 + Dockerfile 改用 repo 根作 context

- Python 服务(data-ana/ai)Dockerfile 改用 repo 根作 context + 声明 uv workspace sources

- 16 个服务的 Dockerfile + CI + docker-compose.tools.yml 全部对齐版本矩阵

- known-issues.md 沉淀 9 条 pnpm 11 / uv workspace / Go shared-go 迁移经验

- 验证:4 服务完全成功(api-gateway /healthz 200),其余 install 成功(build 失败为预存 TS 错误)
This commit is contained in:
SpecialX
2026-07-14 12:04:49 +08:00
parent 74077c7083
commit 81a539b9ab
39 changed files with 31999 additions and 3244 deletions

View File

@@ -1,6 +1,7 @@
# 多阶段构建G1 多阶段强制规则)
# Stage 1: builder - 安装依赖 + 生成 proto 代码
# Stage 2: runtime - 精简运行时镜像
# 用法docker build -t edu/ai:latest -f services/ai/Dockerfile .
# === Stage 1: builder ===
FROM python:3.12-slim AS builder
@@ -10,14 +11,19 @@ WORKDIR /app
# 安装 uv快速依赖管理
RUN pip install --no-cache-dir uv
# 复制依赖文件,利用 Docker layer cache
COPY pyproject.toml ./
# 复制 workspace 根配置 + shared-py 包uv workspace 依赖)
COPY pyproject.toml uv.lock* ./
COPY packages/shared-py ./packages/shared-py
# 复制 ai 服务配置
COPY services/ai/pyproject.toml ./services/ai/pyproject.toml
# 安装生产依赖(含 dev 依赖用于生成 proto
WORKDIR /app/services/ai
RUN uv sync --all-extras
# 复制源码
COPY src ./src
COPY services/ai/src ./src
# 生成 gRPC 代码(如果 proto_gen 不存在或需更新)
RUN uv run python -m grpc_tools.protoc \
@@ -33,14 +39,14 @@ WORKDIR /app
# 安装运行时系统依赖curl 用于健康检查)
RUN apt-get update && apt-get install -y --no-install-recommends \
curl=7.88.* \
curl \
&& rm -rf /var/lib/apt/lists/*
# 从 builder 复制虚拟环境
# 从 builder 复制虚拟环境uv workspace 在根目录创建 .venv
COPY --from=builder /app/.venv /app/.venv
# 从 builder 复制源码(含生成的 proto 代码)
COPY --from=builder /app/src /app/src
COPY --from=builder /app/services/ai/src /app/src
# 环境变量
ENV PATH="/app/.venv/bin:$PATH" \

View File

@@ -4,10 +4,9 @@ version = "0.1.0"
description = "AI 网关服务 - LLM 集成 + 出题 + 备课工作流D6 智能洞察领域 · 生成子域)"
requires-python = ">=3.12"
dependencies = [
"fastapi>=0.115.0",
"uvicorn[standard]>=0.30.0",
"pydantic>=2.9.0",
"pydantic-settings>=2.5.0",
# 通过 workspace 继承 shared-py 集中声明的共享库版本
"edu-shared-py",
# 服务特有依赖(非共享库)
"httpx>=0.27.0",
# gRPC
"grpcio>=1.66.0",
@@ -21,16 +20,11 @@ dependencies = [
"redis>=5.1.0",
# 重试机制LLM 调用 + 下游 gRPC
"tenacity>=9.0.0",
# 可观测性三支柱
"opentelemetry-api>=1.27.0",
"opentelemetry-sdk>=1.27.0",
"opentelemetry-exporter-otlp>=1.27.0",
"opentelemetry-instrumentation-fastapi>=0.48b0",
"opentelemetry-instrumentation-grpc>=0.48b0",
"prometheus-client>=0.20.0",
"structlog>=24.4.0",
]
[tool.uv.sources]
edu-shared-py = { workspace = true }
[project.optional-dependencies]
dev = [
"grpcio-tools>=1.66.0",

View File

@@ -2,20 +2,25 @@
# 用法docker build -t edu/api-gateway:latest -f services/api-gateway/Dockerfile .
# ============ Builder ============
FROM golang:1.22-alpine AS builder
FROM golang:1.25-alpine AS builder
WORKDIR /app
# git 与 ca-certificates 为 go mod 下载所需
RUN apk add --no-cache git ca-certificates
# 先拷依赖清单利用缓存
COPY go.mod go.sum ./
RUN go mod download
# 先拷依赖清单利用缓存shared-go + api-gateway
COPY packages/shared-go/go.mod ./packages/shared-go/
COPY services/api-gateway/go.mod services/api-gateway/go.sum ./services/api-gateway/
ENV GOPROXY=https://goproxy.cn,direct
RUN cd services/api-gateway && go mod download
# 拷源码并构建
COPY . .
# 静态编译CGO_DISABLED 便于 alpine 运行
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/bin/api-gateway ./main.go
COPY services/api-gateway ./services/api-gateway
COPY packages/shared-go ./packages/shared-go
RUN cd services/api-gateway && CGO_ENABLED=0 GOOS=linux go build \
-ldflags="-s -w" \
-o /app/bin/api-gateway \
./main.go
# ============ Runtime ============
FROM alpine:3.20 AS runner

View File

@@ -1,8 +1,9 @@
module github.com/edu-cloud/api-gateway
go 1.22
go 1.25.0
require (
github.com/edu-cloud/shared-go v0.0.0
github.com/gin-gonic/gin v1.12.0
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/uuid v1.6.0
@@ -14,6 +15,8 @@ require (
go.opentelemetry.io/otel/sdk v1.44.0
)
replace github.com/edu-cloud/shared-go => ../../packages/shared-go
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bytedance/gopkg v0.1.4 // indirect

View File

@@ -1,19 +1,22 @@
# Build stage
FROM node:20-alpine AS builder
FROM node:22-alpine AS builder
WORKDIR /app
RUN npm install -g pnpm
RUN corepack enable && corepack prepare pnpm@11.13.0 --activate
COPY package.json pnpm-lock.yaml* ./
RUN pnpm install --frozen-lockfile
# pnpm 11 默认拒绝 build scripts独立安装时无法读取 workspace 的 allowBuilds 配置
# 使用 --ignore-scripts 跳过(@nestjs/core/protobufjs 的 postinstall 非功能必需)
# --no-frozen-lockfileservices/classes/ 无独立 pnpm-lock.yaml
RUN pnpm install --no-frozen-lockfile --ignore-scripts
COPY tsconfig.json nest-cli.json ./
COPY src ./src
RUN pnpm build
# Runtime stage
FROM node:20-alpine
FROM node:22-alpine
WORKDIR /app
RUN npm install -g pnpm
RUN corepack enable && corepack prepare pnpm@11.13.0 --activate
COPY package.json pnpm-lock.yaml* ./
RUN pnpm install --prod --frozen-lockfile
RUN pnpm install --prod --no-frozen-lockfile --ignore-scripts
COPY --from=builder /app/dist ./dist
EXPOSE 3001
CMD ["node", "dist/main.js"]

View File

@@ -23,7 +23,7 @@
"@opentelemetry/sdk-node": "^0.53.0",
"drizzle-orm": "^0.31.0",
"ioredis": "^5.11.1",
"kafkajs": "^2.2.0",
"kafkajs": "^2.2.4",
"mysql2": "^3.11.0",
"pino": "^9.4.0",
"pino-http": "^10.0.0",

View File

@@ -1,7 +1,7 @@
# Build stage
FROM node:20-alpine AS builder
FROM node:22-alpine AS builder
WORKDIR /app
RUN npm install -g pnpm
RUN corepack enable && corepack prepare pnpm@11.13.0 --activate
# 复制 workspace 根配置
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml tsconfig.base.json ./
@@ -21,9 +21,9 @@ RUN pnpm install --frozen-lockfile --ignore-scripts
RUN cd services/content && pnpm build
# Runtime stage
FROM node:20-alpine
FROM node:22-alpine
WORKDIR /app
RUN npm install -g pnpm
RUN corepack enable && corepack prepare pnpm@11.13.0 --activate
# 复制 workspace 配置
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml tsconfig.base.json ./

View File

@@ -1,19 +1,27 @@
# Build stage
FROM node:20-alpine AS builder
# 单阶段构建(测试用)- 使用 repo 根作为 build context
# 构建命令: docker build -t edu/core-edu:test -f services/core-edu/Dockerfile .
FROM node:22-alpine
WORKDIR /app
RUN npm install -g pnpm
COPY package.json pnpm-lock.yaml* ./
RUN pnpm install --frozen-lockfile
COPY tsconfig.json nest-cli.json ./
COPY src ./src
RUN pnpm build
# Runtime stage
FROM node:20-alpine
WORKDIR /app
RUN npm install -g pnpm
COPY package.json pnpm-lock.yaml* ./
RUN pnpm install --prod --frozen-lockfile
COPY --from=builder /app/dist ./dist
EXPOSE 3004
# 安装 pnpm
RUN corepack enable && corepack prepare pnpm@11.13.0 --activate
# 复制 workspace 配置和共享包
COPY package.json pnpm-lock.yaml* pnpm-workspace.yaml tsconfig.base.json ./
COPY packages/shared-proto ./packages/shared-proto
# 复制 core-edu 服务文件
COPY services/core-edu/package.json services/core-edu/tsconfig.json services/core-edu/nest-cli.json ./services/core-edu/
COPY services/core-edu/src ./services/core-edu/src
# 安装依赖并构建
WORKDIR /app/services/core-edu
RUN pnpm install --frozen-lockfile
RUN npx tsc
# 复制 proto 文件到 /app/protogrpc.server.ts 的 fallback 路径)
RUN cp -r /app/packages/shared-proto/proto /app/proto
EXPOSE 3004 50053
WORKDIR /app/services/core-edu
CMD ["node", "dist/main.js"]

View File

@@ -17,7 +17,9 @@
"@nestjs/platform-express": "^10.4.0",
"drizzle-orm": "^0.31.0",
"mysql2": "^3.11.0",
"kafkajs": "^2.2.0",
"kafkajs": "^2.2.4",
"@grpc/grpc-js": "^1.12.0",
"@grpc/proto-loader": "^0.7.13",
"pino": "^9.4.0",
"prom-client": "^15.1.0",
"@opentelemetry/api": "^1.9.0",

View File

@@ -1,5 +1,5 @@
# 多阶段构建builder 缓存依赖runtime 精简镜像
# 对齐 02-architecture-design.md §16 部署规范
# 用法docker build -t edu/data-ana:latest -f services/data-ana/Dockerfile .
# ===== Stage 1: builder =====
FROM python:3.12-slim AS builder
@@ -9,10 +9,15 @@ WORKDIR /app
# 安装 uv快速依赖管理
RUN pip install uv
# 复制 pyproject.toml利用 Docker 层缓存
COPY pyproject.toml .
# 复制 workspace 根配置 + shared-py 包uv workspace 依赖)
COPY pyproject.toml uv.lock* ./
COPY packages/shared-py ./packages/shared-py
# 复制 data-ana 服务配置
COPY services/data-ana/pyproject.toml ./services/data-ana/pyproject.toml
# 同步依赖到虚拟环境(--no-dev 不装开发依赖)
WORKDIR /app/services/data-ana
RUN uv sync --no-dev
# ===== Stage 2: runtime =====
@@ -20,7 +25,7 @@ FROM python:3.12-slim AS runtime
WORKDIR /app
# 从 builder 复制虚拟环境
# 从 builder 复制虚拟环境uv workspace 在根目录创建 .venv
COPY --from=builder /app/.venv /app/.venv
# 确保虚拟环境在 PATH 中
@@ -30,7 +35,7 @@ ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
# 复制源码
COPY src ./src
COPY services/data-ana/src ./src
# 暴露端口HTTP 3006Gateway 直连降级)+ gRPC 50055AnalyticsService 12 RPC
EXPOSE 3006 50055

View File

@@ -4,28 +4,21 @@ version = "1.0.0"
description = "数据分析服务 - ClickHouse 宽表 + CDC + 掌握度算法 + 预警"
requires-python = ">=3.12"
dependencies = [
"fastapi>=0.115.0",
"uvicorn[standard]>=0.30.0",
# 通过 workspace 继承 shared-py 集中声明的共享库版本
"edu-shared-py",
# 服务特有依赖(非共享库)
"clickhouse-connect>=0.7.0",
"pydantic>=2.9.0",
"pydantic-settings>=2.5.0",
"opentelemetry-api>=1.27.0",
"opentelemetry-sdk>=1.27.0",
"opentelemetry-exporter-otlp>=1.27.0",
"opentelemetry-instrumentation-fastapi>=0.48b0",
"prometheus-client>=0.20.0",
"structlog>=24.4.0",
# CDC 链路:消费 Debezium 写入 Kafka 的 MySQL binlog 变更事件
"aiokafka>=0.11.0",
# RedisDataScope 缓存 + 事件去重 + 预警去重位图
"redis>=5.0.0",
# gRPCAnalyticsService 12 RPC + HealthService端口 50055
"grpcio>=1.66.0",
"grpcio-health-checking>=1.66.0",
# protobuf 运行时buf generate 生成的 stub 依赖)
"protobuf>=5.28.0",
]
[tool.uv.sources]
edu-shared-py = { workspace = true }
[tool.ruff]
line-length = 100
target-version = "py312"

View File

@@ -1,19 +1,64 @@
# Build stage
FROM node:20-alpine AS builder
FROM node:22-alpine AS builder
WORKDIR /app
RUN npm install -g pnpm
COPY package.json pnpm-lock.yaml* ./
RUN pnpm install --frozen-lockfile
COPY tsconfig.json nest-cli.json ./
COPY src ./src
RUN corepack enable && corepack prepare pnpm@11.13.0 --activate
# 复制 workspace 配置 + tsconfig 基线
COPY package.json pnpm-lock.yaml* pnpm-workspace.yaml tsconfig.base.json ./
# 移除 prepare 脚本husky 在 Docker 中不可用)
RUN node -e "const p=require('./package.json');delete p.scripts.prepare;require('fs').writeFileSync('./package.json',JSON.stringify(p,null,2))"
# 复制 shared-protogRPC proto-loader 运行时加载)
COPY packages/shared-proto ./packages/shared-proto
# 复制 shared-tsworkspace 依赖)
COPY packages/shared-ts ./packages/shared-ts
# 复制 iam 源码
COPY services/iam ./services/iam
# 安装依赖(仅 iam + shared-ts
RUN cd services/iam && pnpm install --frozen-lockfile
# 先构建 shared-tsiam 依赖 @edu/shared-ts/outbox
WORKDIR /app/packages/shared-ts
RUN pnpm build
# 构建 iam
WORKDIR /app/services/iam
RUN pnpm build
# 复制 proto 文件到 dist 旁(生产环境 ./proto/iam.proto
RUN mkdir -p /app/proto && cp /app/packages/shared-proto/proto/iam.proto /app/proto/iam.proto
# Runtime stage
FROM node:20-alpine
FROM node:22-alpine
WORKDIR /app
RUN npm install -g pnpm
COPY package.json pnpm-lock.yaml* ./
RUN corepack enable && corepack prepare pnpm@11.13.0 --activate
# 复制 workspace 配置 + tsconfig 基线
COPY package.json pnpm-lock.yaml* pnpm-workspace.yaml tsconfig.base.json ./
# 移除 prepare 脚本husky 在 Docker 中不可用)
RUN node -e "const p=require('./package.json');delete p.scripts.prepare;require('fs').writeFileSync('./package.json',JSON.stringify(p,null,2))"
# 复制 shared-proto + shared-ts运行时需要
COPY packages/shared-proto ./packages/shared-proto
COPY packages/shared-ts ./packages/shared-ts
# 复制 iam
COPY services/iam ./services/iam
WORKDIR /app/services/iam
RUN pnpm install --prod --frozen-lockfile
COPY --from=builder /app/dist ./dist
EXPOSE 3002
# 复制构建产物
COPY --from=builder /app/services/iam/dist ./dist
COPY --from=builder /app/packages/shared-ts/dist /app/packages/shared-ts/dist
# 复制 proto 文件(生产环境从 ./proto/ 加载)
COPY --from=builder /app/proto ./proto
EXPOSE 3002 50052
CMD ["node", "dist/main.js"]

View File

@@ -14,7 +14,7 @@
"dependencies": {
"@edu/shared-ts": "workspace:*",
"@grpc/grpc-js": "^1.12.0",
"@grpc/proto-loader": "^0.7.0",
"@grpc/proto-loader": "^0.7.13",
"@nestjs/common": "^10.4.0",
"@nestjs/core": "^10.4.0",
"@nestjs/microservices": "^10.4.0",
@@ -27,7 +27,7 @@
"drizzle-orm": "^0.31.0",
"ioredis": "^5.4.0",
"jsonwebtoken": "^9.0.0",
"kafkajs": "^2.2.0",
"kafkajs": "^2.2.4",
"mysql2": "^3.11.0",
"pino": "^9.4.0",
"prom-client": "^15.1.0",

View File

@@ -1,19 +1,57 @@
# Build stage
FROM node:20-alpine AS builder
FROM node:22-alpine AS builder
WORKDIR /app
RUN npm install -g pnpm
COPY package.json pnpm-lock.yaml* ./
RUN pnpm install --frozen-lockfile
COPY tsconfig.json nest-cli.json ./
COPY src ./src
RUN corepack enable && corepack prepare pnpm@11.13.0 --activate
# 复制 workspace 配置 + tsconfig 基线
COPY package.json pnpm-lock.yaml* pnpm-workspace.yaml tsconfig.base.json ./
# 复制 shared-protogRPC proto-loader 运行时加载)
COPY packages/shared-proto ./packages/shared-proto
# 复制 shared-tsworkspace 依赖)
COPY packages/shared-ts ./packages/shared-ts
# 复制 msg 源码
COPY services/msg ./services/msg
# HUSKY=0 跳过根 package.json 的 prepare:husky 脚本
ENV HUSKY=0
# 从根目录安装全部 workspace 依赖
RUN pnpm install --no-frozen-lockfile --ignore-scripts
# 构建 shared-ts若 msg 依赖 @edu/shared-ts dist 产物)
RUN cd packages/shared-ts && pnpm build
# 构建 msg
WORKDIR /app/services/msg
RUN pnpm build
# 剪枝 devDependencies
RUN cd /app/services/msg && pnpm prune --prod
# Runtime stage
FROM node:20-alpine
FROM node:22-alpine
WORKDIR /app
RUN npm install -g pnpm
COPY package.json pnpm-lock.yaml* ./
RUN pnpm install --prod --frozen-lockfile
COPY --from=builder /app/dist ./dist
EXPOSE 3007
CMD ["node", "dist/main.js"]
# 复制 workspace 配置 + tsconfig 基线
COPY package.json pnpm-lock.yaml* pnpm-workspace.yaml tsconfig.base.json ./
# 复制 shared-proto + shared-ts运行时需要proto 文件 + shared-ts dist
COPY packages/shared-proto ./packages/shared-proto
COPY packages/shared-ts ./packages/shared-ts
# 复制 msg 源码
COPY services/msg ./services/msg
# 从 builder 复制已编译的 prod node_modules + msg dist + shared-ts dist
COPY --from=builder /app/services/msg/node_modules ./services/msg/node_modules
COPY --from=builder /app/packages/shared-ts/node_modules ./packages/shared-ts/node_modules
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/services/msg/dist ./services/msg/dist
COPY --from=builder /app/packages/shared-ts/dist ./packages/shared-ts/dist
WORKDIR /app/services/msg
EXPOSE 3007 50056
CMD ["node", "dist/main.js"]

View File

@@ -22,14 +22,14 @@
"drizzle-orm": "^0.31.0",
"grpc-reflection-js": "^0.3.0",
"ioredis": "^5.4.0",
"kafkajs": "^2.2.0",
"kafkajs": "^2.2.4",
"mysql2": "^3.11.0",
"prom-client": "^15.1.0",
"pino": "^9.4.0",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.0",
"zod": "^3.23.0",
"@grpc/grpc-js": "^1.11.0",
"@grpc/grpc-js": "^1.12.0",
"@grpc/proto-loader": "^0.7.13",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/sdk-node": "^0.55.0",

View File

@@ -1,19 +1,21 @@
# Build stage
FROM node:20-alpine AS builder
FROM node:22-alpine AS builder
WORKDIR /app
RUN npm install -g pnpm
RUN corepack enable && corepack prepare pnpm@11.13.0 --activate
COPY package.json pnpm-lock.yaml* ./
RUN pnpm install --frozen-lockfile
# pnpm 11 默认拒绝 build scripts独立安装时无法读取 workspace 的 allowBuilds 配置
# 使用 --ignore-scripts 跳过(@nestjs/core/protobufjs 的 postinstall 非功能必需)
RUN pnpm install --frozen-lockfile --ignore-scripts
COPY tsconfig.json nest-cli.json ./
COPY src ./src
RUN pnpm build
# Runtime stage
FROM node:20-alpine
FROM node:22-alpine
WORKDIR /app
RUN npm install -g pnpm
RUN corepack enable && corepack prepare pnpm@11.13.0 --activate
COPY package.json pnpm-lock.yaml* ./
RUN pnpm install --prod --frozen-lockfile
RUN pnpm install --prod --frozen-lockfile --ignore-scripts
COPY --from=builder /app/dist ./dist
EXPOSE 3010
CMD ["node", "dist/main.js"]

7182
services/parent-bff/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
# Build stage (coord-final-decisions G1: 多阶段构建首次实现即用)
FROM node:22-alpine AS builder
WORKDIR /app
RUN npm install -g pnpm
RUN corepack enable && corepack prepare pnpm@11.13.0 --activate
COPY package.json pnpm-lock.yaml* ./
# 复制 workspace 配置以解析 @edu/shared-ts 依赖
COPY pnpm-workspace.yaml ./
@@ -15,7 +15,7 @@ RUN pnpm run build
# Runtime stage
FROM node:22-alpine
WORKDIR /app
RUN npm install -g pnpm
RUN corepack enable && corepack prepare pnpm@11.13.0 --activate
COPY package.json pnpm-lock.yaml* ./
COPY pnpm-workspace.yaml ./
COPY packages/shared-ts ./packages/shared-ts

View File

@@ -23,7 +23,7 @@
"graphql": "^16.9.0",
"graphql-yoga": "^5.7.0",
"ioredis": "^5.4.1",
"kafkajs": "^2.2.0",
"kafkajs": "^2.2.4",
"opossum": "^8.4.0",
"pino": "^9.4.0",
"prom-client": "^15.1.0",

View File

@@ -1,7 +1,7 @@
# Build stage
FROM node:20-alpine AS builder
FROM node:22-alpine AS builder
WORKDIR /app
RUN npm install -g pnpm
RUN corepack enable && corepack prepare pnpm@11.13.0 --activate
# 复制 workspace 配置
COPY package.json pnpm-lock.yaml* pnpm-workspace.yaml ./
@@ -23,9 +23,9 @@ WORKDIR /app/services/teacher-bff
RUN pnpm build
# Runtime stage
FROM node:20-alpine
FROM node:22-alpine
WORKDIR /app
RUN npm install -g pnpm
RUN corepack enable && corepack prepare pnpm@11.13.0 --activate
# 复制 workspace 配置
COPY package.json pnpm-lock.yaml* pnpm-workspace.yaml ./