# zhctapp v2.0 Docker 镜像说明

本文档记录 v2.0 镜像的目录结构、容器路径、访问方式和打包方式。

## 1. 版本

```text
基础镜像：zhctapp:v2.0
当前前端开发镜像：zhctapp:v2.0-node14-current-20260507_114848
架构：linux/arm64
基础环境：openEuler 24.03 LTS
PHP：7.3、7.4
Web：Nginx
Node：14.21.3
npm：6.14.18
Python：2.7.18
Node20：通过 nvm 安装，用于 store20/master20
```

## 2. 宿主机目录

`zhct` 是每个人电脑上自行选择的整体目录。

```text
zhct
├── zhctprompt
│   ├── work
│   ├── work_store
│   ├── work_ai_api
│   ├── work_ai_store
│   ├── work_ai_app
│   └── docker
├── zhctproject
│   ├── store
│   ├── store20
│   ├── ai_api
│   ├── ai_store
│   └── ai_app
└── zhctlogs
    └── wwwlogs
```

## 3. 容器路径

```text
/workspace/zhct/zhctproject/store     # 智慧营养健康餐厅
/workspace/zhct/zhctproject/store20   # 智慧营养健康餐厅 Node20 前端，分支 master20
/workspace/zhct/zhctproject/ai_api    # AI运动营养师 API
/workspace/zhct/zhctproject/ai_store  # AI运动营养师 PC 端
/workspace/zhct/zhctproject/ai_app    # AI运动营养师 uniapp 小程序
/workspace/zhct/zhctlogs/log          # 应用日志
/workspace/wwwlogs                    # Nginx / PHP-FPM 日志
```

## 4. 启动前本地配置

本镜像只提供 Nginx、PHP 和容器路径，不内置业务项目的本地配置。

仓库会拉到的入口文件：

- `zhctproject/store/env.php`
- `zhctproject/ai_api/env.php`

以下三份是 git 忽略的本地配置文件，拉业务代码不会自动出现，首次启动前必须从 `zhctprompt/docker/local-config-templates/` 复制真实可用配置。

`store`：

- `zhctproject/store/application/config/local/config.php`
- `zhctproject/store/application/config/local/database.php`

`ai_api`：

- `zhctproject/ai_api/.env.local`

复制真实配置：

```bash
ZHCT_ROOT=/path/to/zhct
CONFIG_SOURCE="$ZHCT_ROOT/zhctprompt/docker/local-config-templates"

mkdir -p "$ZHCT_ROOT/zhctproject/store/application/config/local"
cp "$CONFIG_SOURCE/store/application/config/local/config.php" \
  "$ZHCT_ROOT/zhctproject/store/application/config/local/config.php"
cp "$CONFIG_SOURCE/store/application/config/local/database.php" \
  "$ZHCT_ROOT/zhctproject/store/application/config/local/database.php"
cp "$CONFIG_SOURCE/ai_api/.env.local" \
  "$ZHCT_ROOT/zhctproject/ai_api/.env.local"

rg -n "__[A-Z0-9_]+__" \
  "$ZHCT_ROOT/zhctproject/store/application/config/local" \
  "$ZHCT_ROOT/zhctproject/ai_api/.env.local"
```

这些文件由业务项目在运行时读取，包含数据库、Redis、日志路径、接口域名和第三方服务等配置。`local-config-templates` 是企业内部仓库里的真实配置源，复制后通常不需要再补占位符；检查命令有输出时，说明配置文件里仍有未替换项。上述三份文件是目标业务仓库里的 git 忽略文件，不要提交到 `store` 或 `ai_api` 业务仓库。

日志路径统一使用：

```text
zhct/zhctlogs/log/zhct/
zhct/zhctlogs/log/aizhct/
```

容器入口脚本会修正已知旧日志路径，但不会凭空生成业务配置文件。

## 5. 启动容器

推荐使用：

```bash
cd zhct/zhctprompt/docker
./start_zhct.sh
```

当前机器常用启动命令：

```bash
cd /Users/lqt/work/zhct/zhctprompt/docker
HOST_PORT=80 PC_PORT=8080 MOBILE_PORT=8081 SCREEN_PORT=8082 ./start_zhct.sh
```

赖清涛本机快速启动/验证记录（2026-05-27）：

```bash
# 1. 宿主机 Homebrew nginx 会占用 IPv4 的 80 和 8080，先停掉
brew services stop nginx

# 2. 使用本机已验证镜像启动。注意这里从 zhctprompt 根目录执行
cd /Users/laiqingtao/work/zhct/zhctprompt
IMAGE_NAME=zhctapp:v2.0-node14-local-amd64-20260522 ./docker/start_zhct.sh
```

如果 PC 端首次编译较慢，脚本可能在默认等待时间内提示超时，但容器内 webpack 仍会继续编译。2026-05-27 这次 PC 首次编译约 `474098ms`，最终正常输出 `DONE  Compiled successfully`。下次可直接加长等待时间：

```bash
cd /Users/laiqingtao/work/zhct/zhctprompt
FRONTEND_READY_TIMEOUT=600 IMAGE_NAME=zhctapp:v2.0-node14-local-amd64-20260522 ./docker/start_zhct.sh
```

启动后快速验证：

```bash
# 容器和端口
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'
lsof -nP -iTCP:80 -iTCP:8080 -iTCP:8081 -iTCP:8082 -sTCP:LISTEN

# 三端前台日志
docker exec zhctapp_v2_0 bash -lc "grep -aE 'Compiled successfully|DONE|ERROR|Failed to compile' /tmp/store-*.log"

# 后端、PC、移动端、大屏端、AI 页面
curl -sS 'http://127.0.0.1/index.php?s=/p/Restaurant/index'
curl -I 'http://127.0.0.1:8080/'
curl -sS 'http://127.0.0.1:8081/' | grep -o '<title>[^<]*</title>'
curl -sS 'http://127.0.0.1:8082/' | grep -o '<title>[^<]*</title>'
curl -sS 'http://127.0.0.1/aizhct/' | grep -o '<title>[^<]*</title>'
```

验证结果参考：

```text
zhctapp_v2_0 运行中，映射 0.0.0.0:80->80/tcp、0.0.0.0:8080-8082->8080-8082/tcp
80、8080、8081、8082 只应看到 Docker/com.docker 监听，不应再看到宿主机 nginx
后端接口返回 {"code":100,"message":"您尚未登录"} 是正常未登录鉴权结果
PC 端：http://127.0.0.1:8080/#/login
移动端：http://127.0.0.1:8081/
大屏端：http://127.0.0.1:8082/
AI 页面：http://127.0.0.1/aizhct/
```

启动脚本会自动拉起 `store` 项目的三端 npm dev server：

| 端 | 容器目录 | 命令 | 端口 | 日志 | PID |
| --- | --- | --- | --- | --- | --- |
| PC 端 | `/workspace/zhct/zhctproject/store/public/static` | `npm run dev` | `8080` | `/tmp/store-static.log` | `/tmp/store-static.pid` |
| 移动端 | `/workspace/zhct/zhctproject/store/public/mobile` | `npm run serve` | `8081` | `/tmp/store-mobile.log` | `/tmp/store-mobile.pid` |
| 大屏端 | `/workspace/zhct/zhctproject/store/public/screen` | `npm run dev` | `8082` | `/tmp/store-screen.log` | `/tmp/store-screen.pid` |

脚本默认会检查 `node_modules` 是否存在以及依赖是否完整；首次启动或依赖缺失时会自动执行 `npm install --no-save --package-lock=false`，只补本地 `node_modules`，不改业务仓库的 `package-lock.json`。可用环境变量控制：

```bash
AUTO_START_FRONTENDS=0 ./start_zhct.sh     # 只启动容器和后端，不自动启动三端前台
AUTO_NPM_INSTALL=0 ./start_zhct.sh         # 依赖缺失时直接失败，不自动安装
FORCE_RESTART_FRONTENDS=1 ./start_zhct.sh  # 强制重启三端前台 dev server
FRONTEND_READY_TIMEOUT=300 ./start_zhct.sh # 调整前台编译等待时间，单位秒
```

查看三端前端编译日志有两种方式。

先进入容器，再查看日志：

```bash
docker exec -it zhctapp_v2_0 bash
tail -n 200 /tmp/store-static.log
tail -n 200 /tmp/store-mobile.log
tail -n 200 /tmp/store-screen.log
tail -n 200 /tmp/store-static.log.install
tail -n 200 /tmp/store-mobile.log.install
tail -n 200 /tmp/store-screen.log.install
tail -f /tmp/store-static.log
tail -f /tmp/store-mobile.log
tail -f /tmp/store-screen.log
exit
```

不进入容器，直接在宿主机查看日志：

```bash
docker exec zhctapp_v2_0 bash -lc 'tail -n 200 /tmp/store-static.log'
docker exec zhctapp_v2_0 bash -lc 'tail -n 200 /tmp/store-mobile.log'
docker exec zhctapp_v2_0 bash -lc 'tail -n 200 /tmp/store-screen.log'
docker exec zhctapp_v2_0 bash -lc 'tail -n 200 /tmp/store-static.log.install'
docker exec zhctapp_v2_0 bash -lc 'tail -n 200 /tmp/store-mobile.log.install'
docker exec zhctapp_v2_0 bash -lc 'tail -n 200 /tmp/store-screen.log.install'
docker exec zhctapp_v2_0 bash -lc 'tail -f /tmp/store-static.log'
docker exec zhctapp_v2_0 bash -lc 'tail -f /tmp/store-mobile.log'
docker exec zhctapp_v2_0 bash -lc 'tail -f /tmp/store-screen.log'
docker exec zhctapp_v2_0 bash -lc "grep -aE 'Failed to compile|ERROR|Module not found|This dependency was not found' /tmp/store-*.log"
```

已有同名容器时，启动脚本会校验镜像、挂载目录和端口映射；不一致时会停止并提示处理方式。如需保留旧容器并换端口并行启动，同时指定新的 `CONTAINER_NAME`、`HOST_PORT`、`PC_PORT`、`MOBILE_PORT` 和 `SCREEN_PORT`。

手动启动：

```bash
cd zhct
mkdir -p zhctlogs/wwwlogs

docker run -d \
  --name zhctapp_v2_0 \
  -p 80:80 \
  -p 8080:8080 \
  -p 8081:8081 \
  -p 8082:8082 \
  -v "$PWD:[REDACTED_SECRET]] \
  -v "$PWD/zhctlogs/wwwlogs:/workspace/wwwlogs" \
  zhctapp:v2.0-node14-current-20260507_114848
```

## 6. 访问方式

v2.0 不需要配置本地域名或 `hosts`。

```text
http://127.0.0.1
http://127.0.0.1/aizhct
http://127.0.0.1:8080/#/login
http://127.0.0.1:8080/#/restaurantManagement/restaurantManagement
http://127.0.0.1:8081
http://127.0.0.1:8082
```

PC 端是 Vue Router 默认 hash 路由，页面路径必须走 `/#/...`。例如餐厅管理页面是：

```text
http://127.0.0.1:8080/#/restaurantManagement/restaurantManagement
```

`/p/*` 是后端接口前缀，不是 PC 页面路由。例如：

```text
http://127.0.0.1:8080/p/Restaurant/index
```

该地址会被 PC 端开发服务代理到后端接口；未登录时返回 `{"code":100,"message":"您尚未登录"}` 是正常鉴权结果。不要把这个接口地址当页面入口验收，否则浏览器会看到 JSON 或业务合约错误。

局域网访问时，将 `127.0.0.1` 替换为本机 IP。

## 7. 前台 PC 端开发环境

前台 PC 端位于：

```text
宿主机：zhct/zhctproject/store/public/static
容器内：/workspace/zhct/zhctproject/store/public/static
```

该项目是 Vue 2 + webpack 3 项目，本地开发使用 Node 14 和 npm，不安装 yarn。

当前已验证的容器环境：

```text
镜像：zhctapp:v2.0-node14-current-20260507_114848
容器：zhctapp_v2_0
端口：80:80、8080:8080、8081:8081、8082:8082
Node：v14.21.3
npm：6.14.18
Python：2.7.18
Sass：node-sass 4.14.1 / libsass 3.5.5
```

### 7.1 重建带三端前端端口的容器

`store20` 是第五个业务仓库，拉取方式：

```bash
cd zhct/zhctproject
git clone g***@codeup.aliyun.com:60069db88deaa14d9e02b875/zhct/store.git store20
cd store20
git checkout master20
```

默认三端启动 `store`，使用 Node 14；需要启动 `store20` 时，先停掉 `store` 的三个前端进程，再通过 `nvm use 20` 启动 `store20` 的 PC、移动端、大屏端，继续复用 8080、8081、8082 端口。

```bash
cd zhct/zhctprompt/docker
./scripts/start_store_frontends.sh
./scripts/start_store20_frontends.sh
```

```bash
cd zhct

docker rm -f zhctapp_v2_0

docker run -d \
  --name zhctapp_v2_0 \
  -p 80:80 \
  -p 8080:8080 \
  -p 8081:8081 \
  -p 8082:8082 \
  -v "$PWD:[REDACTED_SECRET]] \
  -v "$PWD/zhctlogs/wwwlogs:/workspace/wwwlogs" \
  zhctapp:v2.0-node14-current-20260507_114848
```

### 7.2 Node 14 安装方式

如需从 `zhctapp:v2.0` 原始镜像补 Node 环境，在容器内使用 `nvm`：

```bash
export NVM_DIR=/usr/local/nvm
mkdir -p "$NVM_DIR"
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
. "$NVM_DIR/nvm.sh"
nvm install 14.21.3
nvm alias default 14.21.3

ln -sf "$NVM_DIR/versions/node/v14.21.3/bin/node" /usr/local/bin/node
ln -sf "$NVM_DIR/versions/node/v14.21.3/bin/npm" /usr/local/bin/npm
ln -sf "$NVM_DIR/versions/node/v14.21.3/bin/npx" /usr/local/bin/npx

npm uninstall -g yarn || true
rm -f /usr/local/bin/yarn /usr/local/bin/yarnpkg
```

不安装 yarn，后续依赖安装和启动统一使用 npm。

### 7.3 node-sass 处理

`store/public/static` 依赖 `node-sass@4.14.1`。arm64 容器通常没有可用的预编译二进制包，需要 Python 2 编译。处理原则是保留原 `node-sass`，不大改 Sass 配置或替换构建链。

容器内检查：

```bash
python --version
npm config get python
```

预期：

```text
Python 2.7.18
/usr/local/bin/python2
```

如果缺少 Python 2，安装后配置：

```bash
npm config set python /usr/local/bin/python2
```

### 7.4 安装依赖并启动

```bash
docker exec -it zhctapp_v2_0 bash

cd /workspace/zhct/zhctproject/store/public/static
npm install

./node_modules/.bin/node-sass --version
```

后台启动：

```bash
cd /workspace/zhct/zhctproject/store/public/static
nohup env HOST=0.0.0.0 PORT=8080 NODE_OPTIONS=--max_old_space_size=4096 \
  npm run dev > /tmp/store-static.log 2>&1 &
echo $! > /tmp/store-static.pid
```

验证：

```bash
grep -E "Compiled successfully|Your application" /tmp/store-static.log
curl -i http://127.0.0.1:8080/
curl -i http://127.0.0.1:8080/app.js
curl -i http://127.0.0.1:8080/p/Restaurant/index
```

停止：

```bash
kill "$(cat /tmp/store-static.pid)"
```

### 7.5 本地后台代理

前台开发环境应指向本机后台：

```text
http://127.0.0.1/
```

确认 `store/public/static/src/util/globalAPI.js`：

```js
const devHttp = 'http://127.0.0.1'
```

确认 `store/public/static/config/index.js` 的 `proxyTable` 使用 `globalAPI.dev`。PC 端开发服务会把后端接口路径转发给后台；页面路径必须走 Vue hash 路由。

验证时：

- `GET http://127.0.0.1:8080/` 应返回前台页面。
- `GET http://127.0.0.1:8080/app.js` 应返回前台脚本。
- 浏览器访问 `http://127.0.0.1:8080/#/restaurantManagement/restaurantManagement` 是 PC 餐厅管理页面路由。
- `GET http://127.0.0.1:8080/p/Restaurant/index` 应走后端接口；未登录时返回 JSON `code=100`。

注意：`/#/...` 是浏览器端 hash 路由，`curl` 不会把 `#` 后面的内容发送给服务器，页面路由需要在浏览器中验证。

如需保存当前容器环境为镜像：

```bash
docker commit zhctapp_v2_0 zhctapp:v2.0-node14-current-20260507_114848
```

## 8. 本次镜像生成过程

镜像包通过本地 `docker/` 目录或资源服务器分发，不提交到 Git 仓库：

```text
文件名：zhctapp_v2.0-node14-current-20260507_114848_arm64.tar.gz
本地包路径：zhct/zhctprompt/docker/zhctapp_v2.0-node14-current-20260507_114848_arm64.tar.gz
资源服务器分发地址（上传后使用）：https://img.kxunpt.cn/zhct/resource/zhctapp_v2.0-node14-current-20260507_114848_arm64.tar.gz
SHA256：a84aa9aa4518000dfb39f2b60f18dda1a434935545248ee9ad0007937e757211
```

旧容器保留运行，不删除、不停止。

```bash
docker commit zhctapp zhctapp:v2.0-base
```

基于 `zhctapp:v2.0-base` 创建独立调试容器：

```bash
docker run -d \
  --name zhctapp_v2_0_build \
  -p 80:80 \
  -v /path/to/zhct:/workspace/zhct \
  -v /path/to/zhct/zhctlogs/wwwlogs:/workspace/wwwlogs \
  zhctapp:v2.0-base
```

在调试容器内更新：

- Nginx root 路径改为 `/workspace/zhct/zhctproject/store/public`
- AI API root 路径改为 `/workspace/zhct/zhctproject/ai_api/public`
- 应用日志路径改为 `/workspace/zhct/zhctlogs/log`
- 访问入口改为本机 IP 或 `127.0.0.1`

验证：

```bash
docker exec zhctapp_v2_0_build nginx -t
docker exec zhctapp_v2_0_build /usr/local/php7.3/bin/php -v
docker exec zhctapp_v2_0_build /usr/local/php7.4/bin/php -v
curl -i http://127.0.0.1/
curl -i http://127.0.0.1/aizhct/
curl -i 'http://127.0.0.1/aizhct/index.php?s=api/dishes.dishes/cate'
```

`aizhct` 菜品分类接口预期返回 `{"status":200,"message":"success"}`。

提交最终镜像：

```bash
docker commit zhctapp_v2_0_build zhctapp:v2.0
docker tag zhctapp:v2.0 zhctapp:latest
```

导出镜像：

```bash
docker save zhctapp:v2.0-node14-current-20260507_114848 | gzip > zhctapp_v2.0-node14-current-20260507_114848_arm64.tar.gz
```

导出后如需分发，上传到：

```text
https://img.kxunpt.cn/zhct/resource/zhctapp_v2.0-node14-current-20260507_114848_arm64.tar.gz
```

2026-05-07 本地修复版导出验证：

```bash
gzip -t zhctapp_v2.0-node14-current-20260507_114848_arm64.tar.gz
shasum -a 256 zhctapp_v2.0-node14-current-20260507_114848_arm64.tar.gz
```

## 9. 说明

- `zhctapp:v2.0-base` 是从当前运行容器提交出来的基线镜像。
- `zhctapp:v2.0` 是调整完新目录结构后的最终镜像。
- `zhctapp:v2.0-node14-current-20260507_114848` 是在 `v2.0` 基础上补充 Node 14、npm、Python 2、前台 PC/移动端/大屏端编译环境，并内置 `node-sass` arm64 binding 缓存后的本地开发镜像。
- `store20` 依赖 Node 20，可与默认 `store` 在同一容器内通过 `nvm` 共存；同一时刻同一组端口只运行一套前端。
- `zhctapp_*.tar` 和 `zhctapp_*.tar.gz` 已加入 `.gitignore`，镜像包不进 Git。
- 如果代码目录为空，访问时可能返回 `File not found`，这表示 Nginx 和 PHP 已响应，但业务代码尚未拉取。
