# 四套主题 Docker 生产部署任务

> **目标**：在同一台服务器、**同一套 Docker Compose 栈**内，同时运行 4 套玩家端主题（4 个 player 容器 + 1 套共用 api/admin/db）。  
> **适用分支**：`main`（暗金）、`theme-2`（Pinnacle 蓝白）、`theme-3`（统一移动端视觉）、`theme-4`（海军蓝暗色极简）。  
> **关联文档**：[Docker部署指南.md](./Docker部署指南.md)、[docker/镜像构建与导出.md](./docker/镜像构建与导出.md)、[AGENTS.md](../AGENTS.md)「主题分支」章节。

---

## 一、架构总览

```text
域名 A（主站）     → 127.0.0.1:8082 → thebet365-player      镜像 tag: main
域名 B（theme-2）  → 127.0.0.1:8083 → thebet365-player2     镜像 tag: theme-2
域名 C（theme-3）  → 127.0.0.1:8084 → thebet365-player3     镜像 tag: theme-3
域名 D（theme-4）  → 127.0.0.1:8085 → thebet365-player4     镜像 tag: theme-4

管理后台           → 127.0.0.1:8081 → thebet365-admin       镜像 tag: latest（或统一 IMAGE_TAG）
                              ↓
                    thebet365-api（Docker 内网，不映射宿主机端口）
                              ↓
                    thebet365-postgres + thebet365-redis
```

| 要点 | 说明 |
|------|------|
| 不是「一个容器四套皮肤」 | 是 **4 个 player 容器**，各跑各自构建好的静态资源 |
| 后端共用 | api / postgres / redis / uploads **只有一套**，账号与余额互通 |
| 本地开发 | 仍只需 `pnpm dev:player`（`:5173`），**切分支**预览不同主题；不必本地开 4 端口 |
| `deploy-update.sh` | 默认只维护 **一套** api/player/admin；多主题 player2~4 需 **单独 load + compose up** |
| 邀请注册链接 | 当前仅 `VITE_PLAYER_URL` 单域名；需 **阶段 F** 实现后台按主题选链接（见第十三节） |

---

## 二、任务清单（实施顺序）

按顺序勾选，下次上线可直接照着做。

### 阶段 A — 仓库改动（一次性）

- [ ] **A1** 新增 `docker-compose.themes.yml`（player2 / player3 / player4 服务定义）
- [ ] **A2** 更新 `.env.docker.example`（`PLAYER2_*` ~ `PLAYER4_*`、`CORS_ORIGINS` 示例）
- [ ] **A3** 更新 `docker-compose.prod.yml` 中 `player` 使用 `PLAYER_IMAGE_TAG`（与 `IMAGE_TAG` 解耦，见下文片段）
- [ ] **A4**（可选）在 `docs/Docker部署指南.md` 增加「多主题」小节链接到本文
- [ ] **A5**（可选）在 `AGENTS.md` 文档索引增加本文链接

### 阶段 B — 各主题分支同步 main 功能（发版前每个 theme 分支各做一遍）

- [ ] **B1** 在 `theme-2` / `theme-3` / `theme-4` 同步 API、Admin、shared、迁移（**禁止**整目录覆盖 player）
- [ ] **B2** Player 侧只做逻辑/i18n **增量合并**，保留各分支 `styles.css` 与主题资源
- [ ] **B3** 各分支本地 `pnpm build` 或 `pnpm dev:player` 冒烟
- [ ] **B4** `main` 分支照常维护；若 api/admin 有变更，四个 player 镜像可共用同一 api/admin 包

### 阶段 C — 本地打镜像（Windows 构建机）

- [ ] **C1** 四个分支分别打 **player** 镜像（tag 互不相同）
- [ ] **C2** 打 **api + admin** 一包（在 `main` 或任意已同步分支，tag 如 `latest`）
- [ ] **C3** 核对 `.manifest.txt` 中 `git_commit` 与分支一致
- [ ] **C4** 上传 tar 到服务器（勿覆盖 `.env.docker`）

### 阶段 D — 服务器首次启用多主题

- [ ] **D1** 备份：执行 `deploy-update` 前会自动备份；首次改 compose 前建议手动 `./scripts/backup-prod.sh`
- [ ] **D2** `docker load` 四个 player tar + api/admin tar
- [ ] **D3** 编辑 `.env.docker`（端口、镜像 tag、CORS、域名）
- [ ] **D4** `docker compose -f docker-compose.prod.yml -f docker-compose.themes.yml --env-file .env.docker up -d`
- [ ] **D5** 宝塔为 4 个玩家域名 + 1 个管理域名配置反代
- [ ] **D6** 执行下方「验收清单」

### 阶段 E — 日常更新（重复）

- [ ] 仅换某一主题 UI → 只 rebuild/load 对应 player tag → `up -d playerN`
- [ ] API/Admin/迁移变更 → `deploy-update.sh` 更新 api/admin；必要时四个 player 无需重建
- [ ] 发版后强刷浏览器 / 清 CDN

### 阶段 F — 邀请链接按主题选择（功能开发，可与 D 并行）

- [ ] **F1** API：`SystemConfig` 存四套玩家站域名配置（见第十三节）
- [ ] **F2** 管理端「全局设置」可编辑主题名称 + 公网 URL + 默认项
- [ ] **F3** 邀请面板 / 邀请历史：下拉选择主题后再复制注册链接
- [ ] **F4** 代理端邀请弹窗同样可读主题列表并选择（只读，不能改 URL）
- [ ] **F5** 部署四套主题后，在全局设置填齐 4 个 https 域名并验收邀请链接

---

## 三、仓库待实现：`docker-compose.themes.yml`

> **任务 A1**：在仓库根目录新建此文件，与 `docker-compose.prod.yml` 合并使用。

```yaml
# 四套主题扩展 — 与 docker-compose.prod.yml 一起使用：
# docker compose -f docker-compose.prod.yml -f docker-compose.themes.yml --env-file .env.docker up -d

services:
  player2:
    image: thebet365-player:${PLAYER2_IMAGE_TAG:-theme-2}
    container_name: thebet365-player2
    depends_on:
      api:
        condition: service_healthy
    ports:
      - '${BIND_ADDR:-127.0.0.1}:${PLAYER2_PORT:-8083}:80'
    healthcheck:
      test: ['CMD-SHELL', 'wget -q -O /dev/null http://127.0.0.1/ || exit 1']
      interval: 10s
      timeout: 5s
      retries: 6
      start_period: 10s
    restart: unless-stopped
    networks:
      - thebet365

  player3:
    image: thebet365-player:${PLAYER3_IMAGE_TAG:-theme-3}
    container_name: thebet365-player3
    depends_on:
      api:
        condition: service_healthy
    ports:
      - '${BIND_ADDR:-127.0.0.1}:${PLAYER3_PORT:-8084}:80'
    healthcheck:
      test: ['CMD-SHELL', 'wget -q -O /dev/null http://127.0.0.1/ || exit 1']
      interval: 10s
      timeout: 5s
      retries: 6
      start_period: 10s
    restart: unless-stopped
    networks:
      - thebet365

  player4:
    image: thebet365-player:${PLAYER4_IMAGE_TAG:-theme-4}
    container_name: thebet365-player4
    depends_on:
      api:
        condition: service_healthy
    ports:
      - '${BIND_ADDR:-127.0.0.1}:${PLAYER4_PORT:-8085}:80'
    healthcheck:
      test: ['CMD-SHELL', 'wget -q -O /dev/null http://127.0.0.1/ || exit 1']
      interval: 10s
      timeout: 5s
      retries: 6
      start_period: 10s
    restart: unless-stopped
    networks:
      - thebet365
```

### 任务 A3：调整 `docker-compose.prod.yml` 的 `player` 服务

将 `player.image` 从 `${IMAGE_TAG}` 改为独立变量（避免与 api/admin 的 `IMAGE_TAG` 绑死）：

```yaml
  player:
    image: thebet365-player:${PLAYER_IMAGE_TAG:-main}
    # build / ports / healthcheck 等其余保持不变
```

> `player2~4` **不要**写 `build:`，生产只 load 预构建镜像。

---

## 四、`.env.docker` 配置模板

> **任务 A2**：追加到 `.env.docker.example`；服务器 `.env.docker` 按真实域名填写。

```env
# ── 共用后端 ──
IMAGE_TAG=latest
ADMIN_PORT=8081
BIND_ADDR=127.0.0.1

# 管理端邀请链接默认指向的主玩家站（勿带末尾 /）
VITE_PLAYER_URL=https://www.example.com

# 四个玩家站域名都需列入（逗号分隔，无空格或统一 trim）
CORS_ORIGINS=https://www.example.com,https://theme2.example.com,https://theme3.example.com,https://theme4.example.com,https://admin.example.com

# ── 四套 player 镜像 tag 与宿主机端口 ──
PLAYER_IMAGE_TAG=main
PLAYER_PORT=8082

PLAYER2_IMAGE_TAG=theme-2
PLAYER2_PORT=8083

PLAYER3_IMAGE_TAG=theme-3
PLAYER3_PORT=8084

PLAYER4_IMAGE_TAG=theme-4
PLAYER4_PORT=8085
```

修改 `CORS_ORIGINS` 后必须重启 api：

```bash
docker compose -f docker-compose.prod.yml -f docker-compose.themes.yml --env-file .env.docker restart api
```

---

## 五、分支合并代码（main → theme-*）

> **发版前**在每个 `theme-*` 分支执行。目标：功能与 main 一致，**皮肤不变**。

### 5.1 禁止事项

| 禁止 | 原因 |
|------|------|
| `git merge main` 后直接发 player | 易把 main 暗金样式大量带入 |
| `git checkout main -- apps/player/` | 整目录覆盖会冲掉主题 CSS/组件 |
| `git checkout main -- apps/player/src/i18n/` | 会冲掉主题分支已有措辞 |

### 5.2 推荐：整目录同步（API / Admin / shared / 迁移）

在目标主题分支上（示例 `theme-2`）：

```bash
git checkout theme-2
git fetch origin

# 同步后端与共享包（可按需增减路径）
git checkout origin/main -- apps/api
git checkout origin/main -- apps/admin
git checkout origin/main -- packages/shared
git checkout origin/main -- pnpm-lock.yaml
git checkout origin/main -- pnpm-workspace.yaml

# 若有新迁移，务必带上
git checkout origin/main -- apps/api/prisma/migrations
git checkout origin/main -- apps/api/prisma/schema.prisma

git status   # 确认没有误改 apps/player 大段样式文件
git commit -m "sync: api/admin/shared/migrations from main"
```

### 5.3 Player：只做逻辑与文案增量

**方式 1 — 按文件 cherry-pick（有明确 commit 时）**

```bash
git log origin/main --oneline -- apps/player/src/composables apps/player/src/stores
git cherry-pick <commit-hash>   # 冲突时保留 theme 分支的 styles / 主题组件
```

**方式 2 — 按目录选择性 checkout（仅非 UI 目录）**

```bash
# 示例：只同步 stores、composables、api 封装（执行前确认路径在 main 有变更）
git checkout origin/main -- apps/player/src/stores
git checkout origin/main -- apps/player/src/composables
git checkout origin/main -- apps/player/src/api
```

**方式 3 — i18n 只合并新增 key**

- 对比 `apps/player/src/i18n/*.ts`，手工把 main **新增的 key** 补进三语文件  
- 不要用 main 文件整文件覆盖

### 5.4 合并后本地验证（每个 theme 分支）

```bash
pnpm install
pnpm db:generate          # 若 schema 有变
pnpm --filter @thebet365/shared build
pnpm --filter @thebet365/player build
pnpm dev:api              # 另开终端
pnpm dev:player           # http://localhost:5173 目视确认仍是本主题皮肤
```

### 5.5 main 分支

- 日常功能开发在 `main` 完成后再同步到各 theme 分支  
- `main` 自身 player 镜像 tag 建议使用 `main` 或 `latest`（与 `.env.docker` 中 `PLAYER_IMAGE_TAG` 一致即可）

---

## 六、构建与上传镜像

### 6.1 四个 player 镜像（Windows CMD）

```bat
cd C:\path\to\thebet365

git checkout main
docs\docker\build-and-export-player.bat --tag main

git checkout theme-2
docs\docker\build-and-export-player.bat --tag theme-2

git checkout theme-3
docs\docker\build-and-export-player.bat --tag theme-3

git checkout theme-4
docs\docker\build-and-export-player.bat --tag theme-4
```

产物（项目根目录，已在 `.gitignore`）：

| 文件 | 加载后镜像名 |
|------|----------------|
| `thebet365-player-main.tar` | `thebet365-player:main` |
| `thebet365-player-theme-2.tar` | `thebet365-player:theme-2` |
| `thebet365-player-theme-3.tar` | `thebet365-player:theme-3` |
| `thebet365-player-theme-4.tar` | `thebet365-player:theme-4` |

### 6.2 api + admin 一包

```bat
git checkout main
docs\docker\build-and-export-images.bat --tag latest
```

或 Linux：`./docs/docker/build-and-export-images.sh --tag latest`

### 6.3 上传至服务器

目录示例：`/www/wwwroot/thebet365`

| 必传 | 说明 |
|------|------|
| 四个 player tar | 四套皮肤 |
| `thebet365-images-latest.tar`（或带版本 tag） | api + admin |
| `docker-compose.prod.yml` | 若仓库有更新 |
| `docker-compose.themes.yml` | **首次多主题必传** |
| `scripts/` | 若部署脚本有更新 |

**勿覆盖**：`.env.docker`、Docker 数据卷、`backups/`

---

## 七、服务器部署命令

### 7.1 首次启用四套主题

```bash
cd /www/wwwroot/thebet365
chmod +x scripts/*.sh

# 导入全部镜像
docker load -i thebet365-images-latest.tar
docker load -i thebet365-player-main.tar
docker load -i thebet365-player-theme-2.tar
docker load -i thebet365-player-theme-3.tar
docker load -i thebet365-player-theme-4.tar

# 编辑 .env.docker（见第四节模板）后启动
docker compose -f docker-compose.prod.yml -f docker-compose.themes.yml --env-file .env.docker up -d
```

若当前环境已由 `deploy-first.sh` 跑通，只需 **load 新 player 镜像 + 合并 compose + up -d player2 player3 player4**，并调整 `.env.docker`。

### 7.2 更新共用 api/admin

仍用现有脚本（**不会**自动更新 player2~4）：

```bash
./scripts/deploy-update.sh --images thebet365-images-latest.tar --tag latest
```

之后确认多主题 compose 仍生效：

```bash
docker compose -f docker-compose.prod.yml -f docker-compose.themes.yml --env-file .env.docker up -d
```

### 7.3 只更新某一主题（例如 theme-4）

```bash
docker load -i thebet365-player-theme-4.tar
docker compose -f docker-compose.prod.yml -f docker-compose.themes.yml --env-file .env.docker up -d player4
```

其他主题容器不受影响。

### 7.4 回滚某一 player

```bash
# 加载旧 tag 的 tar 后
docker compose -f docker-compose.prod.yml -f docker-compose.themes.yml --env-file .env.docker up -d player3
```

api 回滚仍用 `./scripts/rollback.sh --to <tag>`（**不**回滚数据库）。

---

## 八、宝塔 / Nginx 反代

每个玩家站 **只反代到对应端口**，容器内已处理 `/api` 与 `/uploads`，宝塔无需再写 API 规则。

| 宝塔网站 | `proxy_pass` |
|----------|----------------|
| 主站（main） | `http://127.0.0.1:8082` |
| theme-2 站 | `http://127.0.0.1:8083` |
| theme-3 站 | `http://127.0.0.1:8084` |
| theme-4 站 | `http://127.0.0.1:8085` |
| 管理后台 | `http://127.0.0.1:8081` |

示例：

```nginx
location / {
    proxy_pass http://127.0.0.1:8082;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}
```

四套站点分别申请 SSL；`CORS_ORIGINS` 使用 **https** 域名。

---

## 九、验收清单

部署完成后逐项检查：

- [ ] `docker compose ... ps` — api、admin、player、player2、player3、player4 均为 **healthy**
- [ ] 四个玩家域名首页 UI 皮肤互不相同
- [ ] 各站登录同一账号，余额一致
- [ ] 各站下注、充值、站内信、公告正常
- [ ] 管理后台 `:8081` 正常；员工菜单与 Inbox 开关已配置（见 [Docker部署指南.md](./Docker部署指南.md) 第八节步骤 5）
- [ ] `npx prisma migrate status`（在 api 容器内）无 pending 迁移
- [ ] 浏览器 Network 无 CORS 报错（若有，检查 `CORS_ORIGINS` 并 restart api）
- [ ] theme-4 独有组件（如悬浮客服）仅在 theme-4 站出现
- [ ] **阶段 F 完成后**：邀请面板切换四套主题，复制链接分别打开对应域名 `/register?code=...`

---

## 十、常见问题

| 现象 | 处理 |
|------|------|
| 某站 502 | `docker compose ps` 看对应 player 是否 healthy；`docker logs thebet365-player3` |
| 接口 401/403 仅某一域名 | 检查 `CORS_ORIGINS` 是否包含该 https 域名 |
| 四套变成同一皮肤 | 检查是否四个 tag 打错或 `.env.docker` 的 `PLAYER*_IMAGE_TAG` 写错 |
| `deploy-update` 后 player2 消失 | 脚本未管理 themes compose；重新 `up -d` 并带 `-f docker-compose.themes.yml` |
| 邀请链接域名不对 | **未做阶段 F**：改 `VITE_PLAYER_URL` 重建 admin（仅一个默认站）；**已做阶段 F**：到「全局设置 → 玩家主题站点」核对 URL，邀请面板选对应主题 |
| 构建 ENOENT `public/球员` | 清理 `packages/shared/public` 下除 `flags`、`players` 外的中文目录后重试 |

---

## 十一、与本地开发的区别

| 场景 | 做法 |
|------|------|
| 日常改某一主题 UI | 切到对应分支 → `pnpm dev:player`（5173） |
| 本地同时对比 4 套 | 可选：多开终端 `--port 5173/5174/5175/5176`（非必须） |
| 生产 4 套并存 | 4 个 player 容器 + 4 个端口 + 4 个域名 |

---

## 十二、邀请链接按主题选择（阶段 F 详细设计）

> **现状**：`apps/admin/src/utils/invite-link.ts` 用构建时注入的 `VITE_PLAYER_URL` 拼链接；四套主题并存时后台无法选择注册落地页。  
> **目标**：管理员（及代理）生成/复制邀请链接时，**下拉选择**要落地的玩家主题站；配置存数据库，**改域名不必重建 admin 镜像**。

### 12.1 行为说明

| 角色 | 能力 |
|------|------|
| 平台管理员（`settings.manage`） | 在「全局设置」维护主题列表：显示名、公网 URL、是否启用、哪一项为默认 |
| 管理员 / 代理 | 在「邀请」弹窗选主题 → 复制链接 `{所选站}/register?code=xxx` |
| 玩家 | 无感知；任意主题站注册，同一邀请码、同一后端 |

邀请码本身与主题无关（仍走现有 `InvitesService`）；**仅注册链接里的域名**随所选主题变化。

### 12.2 数据模型（`SystemConfig`）

配置键：`player.theme_sites`  
值：JSON 字符串，结构示例：

```json
{
  "themes": [
    { "id": "main", "label": "暗金主站", "baseUrl": "https://www.example.com", "enabled": true, "isDefault": true },
    { "id": "theme-2", "label": "Pinnacle 蓝白", "baseUrl": "https://theme2.example.com", "enabled": true, "isDefault": false },
    { "id": "theme-3", "label": "统一移动端", "baseUrl": "https://theme3.example.com", "enabled": true, "isDefault": false },
    { "id": "theme-4", "label": "海军蓝极简", "baseUrl": "https://theme4.example.com", "enabled": true, "isDefault": false }
  ]
}
```

规则：

- `id` 与 Git 分支 / Docker 镜像 tag 对齐（`main`、`theme-2`、`theme-3`、`theme-4`）
- `baseUrl` **勿带末尾 `/`**；保存时 API 侧 trim + 校验 `https?://`
- 有且仅有一个 `isDefault: true`；未配置时 API 回退 `VITE_PLAYER_URL`（兼容旧环境）
- `enabled: false` 的主题不出现在邀请下拉里

### 12.3 API 任务（`apps/api`）

**F1 — `SystemConfigService` 新增：**

```typescript
// apps/api/src/shared/config/system-config.service.ts
getPlayerThemeSites(): Promise<PlayerThemeSitesSettings>
updatePlayerThemeSites(data): Promise<PlayerThemeSitesSettings>
listEnabledPlayerThemes(): Promise<PlayerThemePublicOption[]>  // 仅 id/label/baseUrl/isDefault
```

**F1 — Admin 控制器：**

| 方法 | 路径 | 权限 | 说明 |
|------|------|------|------|
| GET | `/admin/settings/player-themes` | `settings.manage` | 完整配置（含 disabled） |
| PUT | `/admin/settings/player-themes` | `settings.manage` | 保存；写审计 `UPDATE_PLAYER_THEME_SITES` |
| GET | `/admin/player-themes/options` | 登录员工/代理即可 | 仅 `enabled` 主题，供邀请 UI |

代理门户若走 `/manage/*`，在 `auth.controller` 或 manage 控制器增加同等 **GET options**（复用 service）。

**校验：**

- 至少保留 1 个 `enabled` 主题
- URL 格式合法；禁止重复 `baseUrl`
- 更新后无需重启 api（读 DB）

### 12.4 管理端任务（`apps/admin`）

**F2 — `GlobalSettingsView.vue` 增加卡片「玩家主题站点」：**

- 表格编辑：label、baseUrl、enabled、设为默认
- 保存调用 `PUT /admin/settings/player-themes`
- 与第四节四个域名保持一致（部署后首次必填）

**F3 — 邀请 UI：**

涉及文件：

- `apps/admin/src/utils/invite-link.ts` — 改为 `buildPlayerRegisterUrl(code, baseUrl?)`
- `apps/admin/src/components/InviteCodePanel.vue` — 加载 options，`el-select` 选主题，`registerUrl` 随选择变化
- `apps/admin/src/components/InviteHistoryPanel.vue` — 复制链接前同样选主题（或记住上次选择）

交互建议：

- 默认选中 API 返回的 `isDefault` 主题
- `localStorage` 键 `admin_invite_theme_id` 记住上次选择（可选）
- 无配置时回退 `VITE_PLAYER_URL`，与现网行为一致

**F4 — i18n（三语同步）：**

在 `admin-messages.ts` / `admin-pages*.ts` 增加 key，例如：

- `invite.theme_site` — 注册落地主题
- `invite.theme_site_hint` — 选择玩家看到的注册页面风格
- `settings.player_themes` — 玩家主题站点
- `settings.player_themes_hint` — 与四套 Docker 玩家域名对应

### 12.5 与 Docker 四套主题的对应关系

部署完四套 player 后，在 **全局设置** 填：

| id | 对应容器 | 示例 baseUrl |
|----|----------|--------------|
| `main` | `thebet365-player` :8082 | `https://www.example.com` |
| `theme-2` | `thebet365-player2` :8083 | `https://theme2.example.com` |
| `theme-3` | `thebet365-player3` :8084 | `https://theme3.example.com` |
| `theme-4` | `thebet365-player4` :8085 | `https://theme4.example.com` |

`VITE_PLAYER_URL` 仍可保留为 **构建默认值** 与 **未配置 DB 时的回退**；多主题上线后以 DB 配置为准。

### 12.6 阶段 F 验收

- [ ] 全局设置保存 4 个 URL 后刷新仍生效
- [ ] 邀请面板切换主题，链接域名随之变化，邀请码不变
- [ ] 四套链接均可打开注册页并带 `code` 参数
- [ ] 代理账号邀请弹窗可选主题（不能进全局设置改 URL）
- [ ] 禁用某主题后下拉里消失
- [ ] 未配置 DB 时行为与现网一致（`VITE_PLAYER_URL`）

### 12.7 实施顺序建议

```text
F1 API 配置读写 → F2 全局设置页 → F3 邀请面板 → F4 代理端 → 部署四套主题后填 URL → 12.6 验收
```

可与 **阶段 A~D（Docker 多 player）** 并行开发；功能不依赖 player2~4 已上线，但验收需要四个域名可访问。

---

## 十三、文档维护

| 变更类型 | 更新本文 |
|----------|----------|
| 新增 theme-5 | 复制 player4 模式，追加端口 8086 |
| 修改 deploy 脚本支持多 player | 更新第七节命令 |
| 分支合并策略变化 | 更新第五节 |

---

**下次实施入口**：

- **基础设施**：阶段 A → B → C → D → 第九节验收  
- **邀请多主题**：阶段 F（第十二节）可与 A~D 并行，四套域名就绪后做 12.6 验收
