---
title: 部署
description: PAC 的服务架构、首次部署与常用运维命令。
icon: Rocket
---

一套 PAC 由四个服务组成 —— **Postgres + Redis + pac-service + pac-web**,容器化用 `docker compose` 一键拉起。数据摄入见 [数据摄入](/docs/ingestion),监控见 [监控](/docs/monitoring),故障处理见 [故障排查与灾备](/docs/troubleshooting)。

---

## 一、架构与组件

```
                浏览器 / iframe
                      │
                      ▼
┌──────────── Server(2 核 8G 起步,建议 4 核 16G + SSD)────────────┐
│                                                                   │
│  ┌──────────┐    HTTP API    ┌──────────────────────────────┐    │
│  │ pac-web  │ ─────────────▶ │ pac-service            :3101  │    │
│  │  :3100   │                │ NestJS · API + Cron + Worker  │    │
│  │ Next.js  │                └──────┬──────────────┬────────┘    │
│  └──────────┘                       │              │             │
│                                     ▼              ▼             │
│                              ┌────────────┐ ┌────────────┐       │
│                              │ Postgres16 │ │  Redis 7   │       │
│                              │  :5532     │ │  :6479     │       │
│                              │  pac DB    │ │ 队列 / 会话│       │
│                              └────────────┘ └────────────┘       │
└─────────────────────────────────────┬─────────────────────────────┘
                                       │ pac-service 只读拉取
                                       ▼
                                   外部数据源
```

| 组件 | 端口 | 用途 |
|---|---|---|
| pac-web | 3100 | 前端(Next.js) |
| pac-service | 3101 | API + Cron + BullMQ Worker |
| Postgres 16 | 5532 | PAC 主数据(patients / facts / personas / plans / sync_logs) |
| Redis 7 | 6479 | BullMQ 队列 + 会话 |

---

## 二、首次部署

### 2.1 环境准备

```bash
docker --version           # >= 24
docker compose version
# 若不用容器、裸机跑 pac-service:
node --version             # >= 20
pnpm --version             # >= 9
```

### 2.2 拉代码与配置 env

```bash
# 安装目录任选,下文命令统一用 $PAC_HOME 指代(示例 /opt/pac)
export PAC_HOME=/opt/pac
git clone <repo> $PAC_HOME && cd $PAC_HOME
cp apps/pac-service/.env.example apps/pac-service/.env
```

必填项(`apps/pac-service/.env`):

```ini
# ─── 基础 ───
NODE_ENV=production
PORT=3101
LOG_LEVEL=info
DATABASE_URL=postgresql://pac:<pwd>@localhost:5532/pac?schema=public
REDIS_URL=redis://localhost:6479
JWT_SECRET=<openssl rand -hex 32>
JWT_REFRESH_SECRET=<openssl rand -hex 32>
CORS_ORIGINS=https://<pac-web-domain>

# ─── 数据源连接(只读;按 host 的 manifest 配置,env 非空即覆盖 manifest)───
DW_CLICKHOUSE_URL=<数据源地址>
DW_CLICKHOUSE_DATABASE=<库名>
DW_CLICKHOUSE_USERNAME=<只读账号>
DW_CLICKHOUSE_PASSWORD=<密码>                  # env 名须等于 manifest 的 password_env

# ─── AI(DeepSeek)───
DEEPSEEK_API_KEY=<key>
DEEPSEEK_BASE_URL=https://api.deepseek.com
AI_DEFAULT_MODEL=deepseek-v4-flash

# ─── 定时任务 ───
# 注:增量 / 滞后监控 / 兜底扫描的代码默认是"永不"哨兵,必须显式设 env 才会调度
PAC_INCREMENTAL_CRON=15 8 * * *               # 须设在数据源当日刷新之后(见 数据摄入);太早跑会拉到旧数据
PAC_INCREMENTAL_HOSTS=<host>                   # 多个用逗号分隔
PAC_INCREMENTAL_DATA_DIR=<安装目录>/apps/pac-service/data   # 配置文件里写绝对路径,即 $PAC_HOME/apps/pac-service/data
PAC_INCREMENTAL_LOOKBACK_HOURS=48             # 游标下界回看重叠窗(默认 48h),靠 source_event_id 去重;0=严格 > cursor
PAC_STALE_SCAN_CRON=0 2 * * *                 # 兜底:每日扫"画像落后"患者补入重算队列

# ─── 资源调优(可选,不设走默认)───
PAC_COHORT_BATCH_SIZE=5000                    # 按患者分批(控内存);0 = 不分批
PAC_COHORT_CONCURRENCY=1                      # cohort 并发;1=串行(稳),3-4=提高吞吐(连接池自动放大)
PAC_WRITE_BATCH_SIZE=1000                     # 每批写入行数;0 = 逐行

# ─── 监控 / 告警 ───
PAC_LAG_MONITOR_CRON=0 * * * *                # 数据源滞后监控(每小时)
PAC_LAG_WARN_HOURS=24                         # 超 24h → warning
PAC_LAG_ERROR_HOURS=48                        # 超 48h → critical
PAC_HEALTH_REPORT_CRON=0 9 * * *              # 每日健康日报(代码默认即 09:00)
ALERT_WEBHOOK_URL=<企微/钉钉/slack 机器人 webhook>   # 告警与日报的推送通道;留空则仅落 log
```

> **Docker Compose 部署务必带 `--env-file apps/pac-service/.env`**。
> compose 对 `${POSTGRES_PASSWORD}` 等变量的插值只从 *项目根 .env / shell* 读,**不读** service 的 `env_file:`;漏带会让密码插值成默认值导致连库认证失败。

### 2.3 启动

**A. Docker Compose(推荐,生产实际用法)**

```bash
cd $PAC_HOME
# 代码更新后,pac-migrate 与 pac-service 是两个独立镜像,都要 rebuild
docker compose --env-file apps/pac-service/.env -f docker-compose.prod.yml build pac-migrate pac-service
docker compose --env-file apps/pac-service/.env -f docker-compose.prod.yml up -d
# pac-migrate(restart:no)自动跑 prisma migrate deploy 后退出,pac-service 依赖它成功
# 验证迁移:docker compose ... logs pac-migrate | grep "migrations have been applied"
```

**B. 裸机 / systemd**

```bash
cd $PAC_HOME
pnpm install --frozen-lockfile
pnpm --filter @pac/types build
pnpm --filter @pac/service prisma:deploy      # = prisma migrate deploy
pnpm --filter @pac/service prisma:seed        # 写入 demo / 示例 host 行
pnpm --filter @pac/service build              # 编译 NestJS
pnpm --filter @pac/web build                  # Next.js prod build

# 起 postgres + redis(若未用托管实例)
docker run -d --name pac-postgres -p 5532:5432 \
  -e POSTGRES_USER=pac -e POSTGRES_PASSWORD=<pwd> -e POSTGRES_DB=pac \
  -v pac-pg-data:/var/lib/postgresql/data postgres:16-alpine
docker run -d --name pac-redis -p 6479:6379 \
  -v pac-redis-data:/data redis:7-alpine

# 起服务(交给 systemd / pm2 托管)
cd apps/pac-service && node dist/main.js &    # = package.json 的 start 脚本
cd apps/pac-web && pnpm start &
```

### 2.4 部署后验证

启动后用健康检查端点确认存活:

```bash
curl -s http://localhost:3101/health     # → {"status":"ok","timestamp":"..."}
```

`GET /health` 免鉴权、且**不走** `pac/v1` 全局前缀,适合做负载均衡探针 / 容器 healthcheck / 外部拨测,返回 `{ "status": "ok", "timestamp": "<ISO>" }`。(同样豁免前缀的还有 Swagger `/api/docs`、Bull Board `/admin/queues`。)

随后即可首次导入数据 —— 见 [数据摄入](/docs/ingestion)。

---

## 三、常用运维命令

| 场景 | 命令 |
|---|---|
| 重启 pac-service | `systemctl restart pac-service`(或 `pm2 reload pac-service`) |
| 手动补跑增量 | `pnpm sync-incremental -- --dir=./data/<host>` |
| 某患者召回不对,临时单刷 | `pnpm recompute-plans -- --host=<host>`(全 host),或详情页"刷新"按钮(单患者) |
| 改 scenario SQL 后重算 plan | `pnpm recompute-plans -- --host=<host>`(facts 不变,无需 reparse) |
| 改 yaml 字典后补存量 fact | `pnpm reparse -- --host=<host> --subject-type=<canonical>`(见 [数据摄入 · 配置变更](/docs/ingestion)) |
