# 本地联调环境（docker-compose）

一行命令起一整套服务用于本地集成验证：PostgreSQL + Temporal Server + Temporal Web UI + NestJS Server + Temporal Worker + Web 前端。

> 生产部署走 K8s（参考 `docs/temporal-server-argocd.md`）。本环境**只**用于本地联调与端到端集成验证。

## 启动 / 停止

**推荐：用 `./compose-ctl.sh` 一键起，不用记 codegen 那一步。**

```bash
# 起所有服务（codegen 镜像不存在才构建，否则跳过）
./compose-ctl.sh

# 改了 config/ui/** 或 config/prisma/** 后，强制重建 codegen
./compose-ctl.sh --rebuild

# 完全重置：清数据卷 + 重建 codegen + 起服务（首启会重新初始化 schema/seed）
./compose-ctl.sh --reset

# 停服务（保留数据）
./compose-ctl.sh --down
```

如果想用原生 `docker compose` 命令（脚本就是这两步的封装）：

```bash
# 起所有服务（后台）
docker compose up -d

# 跟日志（按 Ctrl+C 退出，服务继续运行）
docker compose logs -f

# 停服务（保留数据）
docker compose down

# 完全重置（连数据卷一起删，下次首启会重新初始化 schema）
docker compose down -v

# 重建某个镜像（改了 Dockerfile / 源码后）
docker compose build server
docker compose up -d server

# 改了 config/ui/** 或 config/prisma/** 后，必须先 rebuild codegen 中间镜像
docker compose --profile codegen build codegen
docker compose build server web   # 让 server/web 拉到新的生成代码
docker compose up -d server web
```

## 访问入口

| 服务 | 地址 | 说明 |
|------|------|------|
| Web 前端 | http://localhost:3000 | 生产 nginx 镜像，**不支持热更新** |
| Server API | http://localhost:8000 | NestJS，健康检查 `/health/live` |
| Temporal Web UI | http://localhost:8080 | Workflow 可视化 |
| Temporal gRPC | `localhost:7233` | Worker / `@temporalio/client` 连这里 |
| PostgreSQL | `localhost:5432` | 用户 `kavenma` / 密码 `kavenma` |

库划分：

- `gadmin_demo` —— 业务库（server + worker 共用，Prisma 管理）
- `temporal` —— Temporal 元数据库（auto-setup 自动建表）
- `temporal_visibility` —— Temporal Visibility 库（PG-only，本地不开 ES）

## 与 `temporal/docker-compose.yml` 的区别

| 文件 | 内容 | 用途 |
|------|------|------|
| `docker-compose.yml`（本目录） | **完整**栈：PG + Temporal + Server + Worker + Web | 端到端集成验证 |
| `temporal/docker-compose.yml` | 只有 Temporal Server + UI（连 host PG） | 只想跑 Temporal、复用本机 PG 时使用 |

两个文件互不干扰，可单独使用。

## 前端开发态：不要用本 compose

本 compose 里的 `web` service 是 `nginx + 编译后静态文件`，改前端代码不会热更新。日常前端开发请：

```bash
cd web
yarn dev   # Vite dev server，端口 5173，自带 HMR
```

让 Vite dev server 反代到本 compose 起的 `server`（http://localhost:8000）。

后端开发同理：可以选择只起 `postgres` + `temporal-server` 这两个外部依赖：

```bash
docker compose up -d postgres temporal-server temporal-ui
cd server && yarn start:dev
```

## 启动顺序（depends_on + healthcheck）

```
postgres (healthy: pg_isready)
   ├─> server-init  (一次性容器：prisma db push + seed → exit 0)
   │       ├─> server  (启动 API)
   │       └─> temporal-worker  (并需 temporal-server healthy)
   └─> temporal-server (healthy: temporal operator cluster health)
           ├─> temporal-ui
           └─> temporal-worker
server (started)
   └─> web
```

首次启动 Temporal 需要跑 schema 初始化（约 30~60s），期间 `temporal-server` 会处于 `starting`，依赖它的 worker 会等待。

`server-init` 跑完 `prisma db push` + `seed` 就退出，`docker compose ps` 里它显示 `Exited (0)` 是正常的，不是故障 —— 见"常见问题"。

## 常见问题

### 端口冲突

```bash
# 看占用：
lsof -iTCP:3000 -sTCP:LISTEN
lsof -iTCP:5432 -sTCP:LISTEN
lsof -iTCP:7233 -sTCP:LISTEN
lsof -iTCP:8000 -sTCP:LISTEN
lsof -iTCP:8080 -sTCP:LISTEN
```

如本机已有 PG 在 5432，可临时把 `docker-compose.yml` 里 postgres 的 ports 改成 `"5433:5432"`，并把 server / worker 的 `DATABASE_URL` host 改回 `postgres`（容器内通信不变，宿主机端口不影响 docker 网络）。

### `server-init` 显示 `Exited (0)` 是 bug 吗？

**不是。** `server-init` 是一次性容器，启动后跑 `prisma db push` 同步业务表结构，再跑 `seed` 灌默认数据，跑完就退出 ——`docker compose ps` 里看到状态 `Exited (0)` 即代表初始化成功。`server` / `temporal-worker` 在 `depends_on` 里声明了 `condition: service_completed_successfully` 等它跑完。

> ⚠️ **podman-compose 1.x 注意**：实测对 `service_completed_successfully` 的支持不完整，`up -d` 时不会真的等 init 退出就启动 server/worker。NestJS 启动时不立即查表所以一般没问题，但严格的串行保证没了。真 docker-compose v2 没这问题。

要看它做了什么：

```bash
docker compose logs server-init
```

如果状态是 `Exited (1)` 或更高，说明 push:db 或 seed 失败了，server / worker 不会启动。看日志排错后用 `docker compose up -d server-init` 重跑（幂等）。

### Temporal schema 初始化失败

```bash
docker compose logs temporal-server | grep -i schema
```

最常见原因是 PG 没就绪 server 就启动了。`docker compose down -v` 重置后再 `up -d` 通常解决。

### Server 启动后访问 401 / Cookie 失效

server 镜像里 `TAIHU_*` 用的是 `server/.env` 默认值，对应太湖测试账号。如需走真实账号，先 `docker compose down`，把对应变量加到 shell 环境（或新建项目根 `.env` 文件，compose 会自动读取）后重启。

### 重置数据库（清空所有业务数据）

```bash
./compose-ctl.sh --reset
# 等 temporal-server 重新做 schema setup（看日志 `temporal-server` 里出现 "Temporal server started"）
# server-init 会自动重跑 push:db + seed，业务库回到种子数据状态
```

## 用到的镜像

| 镜像 | 版本 | 来源 |
|------|------|------|
| `postgres` | 15-alpine | docker hub 官方 |
| `temporalio/auto-setup` | 1.29.6.1 | Docker Hub 上 auto-setup 当前最新（生产 Server 是 1.31.0，auto-setup tag 通常滞后；schema 兼容） |
| `temporalio/ui` | latest | 仅 UI，跨版本兼容性好 |
| `gadmin2-codegen:local` | 本地 build | `Dockerfile.codegen`，仅做 `gadmin2 g prisma`，被 server/web 镜像通过 `FROM ${CODEGEN_IMAGE}` 引用；同时被 `server-init` 直接复用其全量依赖 |
| `gadmin-server` | 本地 build | `Dockerfile.server`，build context = 项目根，依赖 codegen 镜像 |
| `gadmin-temporal-worker` | 本地 build | `temporal/worker/Dockerfile` |
| `gadmin-web` | 本地 build | `Dockerfile.web`，nginx 静态镜像，依赖 codegen 镜像 |
