# 本地启动手册（Local Dev Runbook）

> 版本：1.0
> 日期：2026-07-08
> 适用范围：Edu 微服务（P1 阶段：api-gateway + classes + teacher-portal）
> 关联文档：[project_rules](../../.trae/rules/project_rules.md)、[004 架构影响地图](../architecture/004_architecture_impact_map.md)、[known-issues](../troubleshooting/known-issues.md)

---

## 1. 环境依赖

| 工具           | 版本要求 | 验证命令                 | 说明                       |
| -------------- | -------- | ------------------------ | -------------------------- |
| Node.js        | ≥ 20     | `node -v`                | LTS 版本                   |
| pnpm           | ≥ 9      | `pnpm -v`                | `corepack enable` 自动启用 |
| Go             | 1.22+    | `go version`             | api-gateway 编译           |
| Docker         | 24+      | `docker version`         | 基础设施容器化             |
| Docker Compose | v2+      | `docker compose version` | 编排基础设施               |
| Git            | 2.30+    | `git --version`          | husky hooks 需要           |

> Windows 用户：建议在 PowerShell 或 Git Bash 中执行。Go 工具链若不在 PATH，临时加入：`$env:Path = "C:\Program Files\Go\bin;" + $env:Path`

---

## 2. 服务端口分配

| 服务           | 端口 | 语言/框架    | 启动方式            | 健康检查          |
| -------------- | ---- | ------------ | ------------------- | ----------------- |
| MySQL          | 3306 | Docker       | `docker compose up` | `mysqladmin ping` |
| Redis          | 6379 | Docker       | `docker compose up` | `redis-cli ping`  |
| api-gateway    | 8080 | Go (Gin)     | `go run main.go`    | `GET /healthz`    |
| classes        | 3001 | NestJS (TS)  | `pnpm dev`          | `GET /healthz`    |
| teacher-portal | 3000 | Next.js (TS) | `pnpm dev`          | `GET /`           |
| iam            | 3002 | NestJS       | P2 阶段             | —                 |
| teacher-bff    | 3003 | NestJS       | P2 阶段             | —                 |

> 端口冲突排查：`netstat -ano \| findstr :8080`（Windows）

---

## 3. 本地开发模式（DEV_MODE）

### 3.1 首次准备

```bash
# 1. 克隆仓库
git clone <repo-url> Edu
cd Edu

# 2. 安装依赖
pnpm install

# 3. 配置环境变量
cp .env.example .env
# 编辑 .env，确认：
#   DEV_MODE=true           ← 本地联调用
#   JWT_SECRET=p1-dev-secret-change-in-production
#   DATABASE_URL=mysql://edu:changeme@localhost:3306/next_edu_cloud
#   REDIS_URL=redis://localhost:6379

# 4. 架构扫描（更新 arch.db）
pnpm run arch:scan
```

### 3.2 启动基础设施

```bash
# 启动 MySQL + Redis（最小基础设施）
docker compose -f infra/docker-compose.minimal.yml up -d

# 验证健康
docker compose -f infra/docker-compose.minimal.yml ps
# 状态应为 healthy

# （可选）国内镜像源加速：infra/docker-compose.minimal.override.yml 已配置 daocloud 镜像
```

### 3.3 启动应用服务（三个终端）

#### 终端 1：api-gateway（:8080）

```bash
cd services/api-gateway

# Windows：若 Go 不在 PATH，先设置
$env:Path = "C:\Program Files\Go\bin;" + $env:Path

# 设置开发模式环境变量
$env:DEV_MODE="true"
$env:JWT_SECRET="p1-dev-secret-change-in-production"

# 启动
go run main.go
```

验证：`curl http://localhost:8080/healthz` → 200 OK

#### 终端 2：classes 服务（:3001）

```bash
# 在项目根目录
pnpm --filter @edu/classes-service dev
```

验证：`curl http://localhost:3001/healthz` → 200 OK

> 注：classes 的 HealthModule 需注册到 AppModule，当前若返回 404 见 [known-issues]

#### 终端 3：teacher-portal（:3000）

```bash
# 在项目根目录
pnpm --filter @edu/teacher-portal dev
```

验证：浏览器访问 `http://localhost:3000` → 班级管理页面

### 3.4 一键启动（可选）

```bash
# 并行启动所有子包的 dev 脚本（含 api-gateway 需 go run，不会自动启动）
pnpm dev
```

> 注：`pnpm dev` 仅启动 pnpm workspace 内的 TS 服务。api-gateway 是 Go 服务，需单独 `go run`。

### 3.5 联调验证

```bash
# 1. 直接访问 api-gateway（带 dev-token）
curl -H "Authorization: Bearer dev-token" http://localhost:8080/api/v1/classes
# 预期：200 OK + 班级列表 JSON

# 2. 通过 teacher-portal 代理访问
curl -H "Authorization: Bearer dev-token" http://localhost:3000/api/v1/classes
# 预期：200 OK（经 Next.js rewrites → api-gateway → classes）

# 3. 浏览器访问
# http://localhost:3000 → 班级管理页面，自动加载班级列表
```

### 3.6 dev-token 说明

- `DEV_MODE=true` 时，api-gateway 接受 `Authorization: Bearer dev-token`
- 注入固定身份：`x-user-id: dev-user`，`x-user-roles: teacher,admin`
- **仅限本地联调，生产环境必须 `DEV_MODE=false`**

---

## 4. 生产模式（Docker Compose）

### 4.1 构建镜像

```bash
# 构建三个应用服务镜像
docker compose -f infra/docker-compose.prod.yml build
```

### 4.2 启动完整栈

```bash
# 1. 先启动基础设施（MySQL + Redis）
docker compose -f infra/docker-compose.minimal.yml up -d

# 2. 启动应用服务
docker compose -f infra/docker-compose.prod.yml up -d

# 3. 查看状态
docker compose -f infra/docker-compose.prod.yml ps
# 所有服务应为 healthy

# 4. 查看日志
docker compose -f infra/docker-compose.prod.yml logs -f api-gateway
```

### 4.3 生产环境配置要点

| 配置项         | 生产值           | 说明                                            |
| -------------- | ---------------- | ----------------------------------------------- |
| `DEV_MODE`     | `false`          | docker-compose.prod.yml 强制设为 false          |
| `JWT_SECRET`   | 强随机值         | 替换 `p1-dev-secret-change-in-production`       |
| `DATABASE_URL` | 生产数据库连接   | 容器内用 `host.docker.internal` 或 compose 网络 |
| `NODE_ENV`     | `production`     | teacher-portal 启用生产优化                     |
| `LOG_LEVEL`    | `info` 或 `warn` | 生产日志级别                                    |

### 4.4 生产验证

```bash
# 健康检查
curl http://localhost:8080/healthz   # api-gateway
curl http://localhost:3001/healthz   # classes
curl http://localhost:3000/          # teacher-portal

# 鉴权验证（生产模式 dev-token 应被拒绝）
curl -H "Authorization: Bearer dev-token" http://localhost:8080/api/v1/classes
# 预期：401 INVALID_TOKEN

# 真实 JWT 访问（需 IAM 签发）
curl -H "Authorization: Bearer <real-jwt>" http://localhost:8080/api/v1/classes
# 预期：200 OK
```

### 4.5 停止与清理

```bash
# 停止应用服务
docker compose -f infra/docker-compose.prod.yml down

# 停止基础设施
docker compose -f infra/docker-compose.minimal.yml down

# 清理数据卷（谨慎！会删除数据库数据）
docker compose -f infra/docker-compose.minimal.yml down -v
```

---

## 5. 单独构建与运行（裸机生产）

### 5.1 api-gateway

```bash
cd services/api-gateway

# 编译
go build -o bin/api-gateway ./main.go

# 运行（生产环境变量）
DEV_MODE=false JWT_SECRET=<your-secret> ./bin/api-gateway
```

### 5.2 classes 服务

```bash
# 构建
pnpm --filter @edu/classes-service build

# 运行（从 dist/ 启动）
NODE_ENV=production PORT=3001 \
  DATABASE_URL=mysql://edu:changeme@localhost:3306/next_edu_cloud \
  REDIS_URL=redis://localhost:6379 \
  node services/classes/dist/main.js
```

### 5.3 teacher-portal

```bash
# 构建
pnpm --filter @edu/teacher-portal build

# 运行
NODE_ENV=production PORT=3000 \
  API_GATEWAY_URL=http://localhost:8080 \
  node apps/teacher-portal/node_modules/.bin/next start -p 3000
```

---

## 6. 常见问题

### 6.1 ERR_TOO_MANY_REDIRECTS

**症状**：浏览器访问 `:3000/api/v1/classes` 无限重定向。

**根因**：Gin 默认 `RedirectTrailingSlash=true`，`/classes` → 301 → `/classes/`，Next.js rewrites 代理形成循环。

**修复**：`main.go` 已设 `r.RedirectTrailingSlash = false`，并同时注册无尾斜杠与通配符路由。

### 6.2 Docker Hub 拉取超时

**症状**：`docker compose up` 时 MySQL/Redis 镜像拉取超时。

**修复**：`infra/docker-compose.minimal.override.yml` 已配置 daocloud 镜像源，docker compose 会自动加载。

### 6.3 classes DI 注入失败

**症状**：`TypeError: Cannot read properties of undefined (reading 'create')`。

**根因**：NestJS ESM 模式下 `emitDecoratorMetadata` 不工作。

**修复**：`ClassesService` 构造函数已加 `@Inject(ClassesRepository)` 显式指定 token。

### 6.4 ESM import 缺 .js 后缀

**症状**：`error TS2307: Cannot find module './health.controller'`。

**修复**：ESM 模式下相对 import 必须带 `.js` 后缀（详见 project_rules §3.4）。

### 6.5 JWT 401 INVALID_TOKEN

**症状**：带 `dev-token` 仍返回 401。

**排查**：

1. 确认 `DEV_MODE=true` 环境变量已设置（api-gateway 进程）
2. 确认请求头格式：`Authorization: Bearer dev-token`（注意 Bearer 后空格）
3. 生产模式（`DEV_MODE=false`）下 dev-token 被拒绝是预期行为

### 6.6 Go 工具链不在 PATH

**症状**：Git Bash 或 PowerShell 中 `go: command not found`。

**修复**：

```powershell
$env:Path = "C:\Program Files\Go\bin;" + $env:Path
```

---

## 7. 数据库初始化

classes 服务使用 Drizzle ORM，首次运行需建表：

```bash
# 生成迁移
pnpm --filter @edu/classes-service drizzle-kit generate

# 执行迁移
pnpm --filter @edu/classes-service drizzle-kit migrate
```

> 若 `drizzle-kit` 未配置，可手动执行 `services/classes/src/db/schema.sql`（如存在）。

---

## 8. 相关文档

- [项目规则](../../.trae/rules/project_rules.md) — 强制约束
- [004 架构影响地图](../architecture/004_architecture_impact_map.md) — 服务清单与调用关系
- [known-issues](../troubleshooting/known-issues.md) — 已知问题速查
- [Git 工作流](./git-workflow.md) — 提交与 PR 规范
- [多 AI 协作指南](./multi-ai-collaboration.md) — 多 Agent 并行开发流程
