diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index e07007ed..1852330d 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -3,24 +3,23 @@ # 用于统一管理所有服务的启动 # ================================================ # 使用方法: -# 启动所有服务: docker compose up -d +# 启动所有服务: docker compose --profile all up -d # 启动基础设施: docker compose --profile infra up -d -# 启动后端服务: docker compose --profile serv up -d -# 启动前端服务: docker compose --profile web up -d # 停止所有服务: docker compose down # 查看日志: docker compose logs -f [service_name] +# +# All-in-One 模式: +# - urban-lifeline-serv: 所有后端服务 (单容器) +# - urban-lifeline-web: 所有前端应用 (单容器) +# - nginx: 反向代理 # ================================================ name: urban-lifeline # 引入子目录的 compose 文件 include: - # Level 1: 基础设施 + # 基础设施 + 业务服务 (All-in-One) - path: ./infra/docker-compose.yml - # Level 2: 后端服务 - - path: ./urbanLifeline/serv/docker-compose.yml - # Level 3: 前端服务 - - path: ./urbanLifeline/web/docker-compose.yml # 共享网络定义 networks: diff --git a/docker/infra/docker-compose.yml b/docker/infra/docker-compose.yml index 10b94e39..3ff07132 100644 --- a/docker/infra/docker-compose.yml +++ b/docker/infra/docker-compose.yml @@ -6,7 +6,7 @@ services: # ====================== Nginx 反向代理 ====================== nginx: - image: urban-lifeline-web:${IMAGE_VERSION:-latest} + image: nginx:alpine container_name: urban-lifeline-nginx restart: unless-stopped profiles: ["infra", "all"] @@ -23,6 +23,9 @@ services: - ./nginx/conf.d:/etc/nginx/conf.d:ro # SSL 证书(可选) # - ./nginx/ssl:/etc/nginx/ssl:ro + depends_on: + - urban-lifeline-serv + - urban-lifeline-web healthcheck: test: ["CMD", "curl", "-f", "http://localhost/health"] interval: 30s @@ -30,6 +33,73 @@ services: retries: 3 start_period: 30s + # ====================== 后端服务 All-in-One ====================== + urban-lifeline-serv: + image: urban-lifeline-serv:${IMAGE_VERSION:-latest} + container_name: urban-lifeline-serv + restart: unless-stopped + profiles: ["infra", "serv", "all"] + networks: + - urban-lifeline + expose: + - "8080" + - "8081" + - "8082" + - "8083" + - "8084" + - "8085" + - "8086" + - "8087" + - "8088" + - "8089" + - "8090" + environment: + TZ: Asia/Shanghai + SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod} + NACOS_SERVER_ADDR: nacos:8848 + NACOS_NAMESPACE: ${NACOS_NAMESPACE:-} + volumes: + - ${DATA_ROOT:-../volumes}/logs/serv:/app/logs + depends_on: + nacos: + condition: service_healthy + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 180s + + # ====================== 前端服务 All-in-One ====================== + urban-lifeline-web: + image: urban-lifeline-web:${IMAGE_VERSION:-latest} + container_name: urban-lifeline-web + restart: unless-stopped + profiles: ["infra", "web", "all"] + networks: + - urban-lifeline + expose: + - "8000" + - "8001" + - "8002" + - "8003" + - "8004" + environment: + TZ: Asia/Shanghai + SHARED_PORT: 8000 + PLATFORM_PORT: 8001 + WORKCASE_PORT: 8002 + BIDDING_PORT: 8003 + WORKCASE_WECHAT_PORT: 8004 + volumes: + - ${DATA_ROOT:-../volumes}/logs/web:/app/logs + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + # ====================== Nacos 注册中心 ====================== nacos: image: nacos/nacos-server:v3.1.0 diff --git a/docker/infra/nginx/conf.d/default.conf b/docker/infra/nginx/conf.d/default.conf index 8081022d..28ad83e9 100644 --- a/docker/infra/nginx/conf.d/default.conf +++ b/docker/infra/nginx/conf.d/default.conf @@ -1,19 +1,32 @@ # ================================================ -# Urban Lifeline - 站点配置 +# Urban Lifeline - 站点配置 (All-in-One 模式) # ================================================ -# 上游服务定义 +# 上游服务定义 - 后端 All-in-One 容器 upstream gateway { - server gateway:8080; + server urban-lifeline-serv:8080; keepalive 32; } +# 上游服务定义 - 前端 All-in-One 容器 +upstream shared { + server urban-lifeline-web:8000; +} + upstream platform { - server platform:80; + server urban-lifeline-web:8001; } upstream workcase-web { - server workcase-web:80; + server urban-lifeline-web:8002; +} + +upstream bidding-web { + server urban-lifeline-web:8003; +} + +upstream workcase-wechat { + server urban-lifeline-web:8004; } server { @@ -29,6 +42,21 @@ server { # ====================== 前端应用代理 ====================== + # Shared 公共模块 (Module Federation 远程模块) + location /shared/ { + proxy_pass http://shared/; + proxy_http_version 1.1; + 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; + + # 允许跨域 (Module Federation 需要) + add_header Access-Control-Allow-Origin *; + add_header Access-Control-Allow-Methods "GET, OPTIONS"; + add_header Access-Control-Allow-Headers "Origin, Content-Type, Accept"; + } + # Platform 管理平台 location /platform/ { proxy_pass http://platform/; @@ -39,7 +67,7 @@ server { proxy_set_header X-Forwarded-Proto $scheme; } - # Workcase 工单系统 + # Workcase 工单系统 PC端 location /workcase/ { proxy_pass http://workcase-web/; proxy_http_version 1.1; @@ -49,6 +77,26 @@ server { proxy_set_header X-Forwarded-Proto $scheme; } + # Bidding 招标系统 + location /bidding/ { + proxy_pass http://bidding-web/; + proxy_http_version 1.1; + 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; + } + + # Workcase 工单系统微信端 + location /workcase-wechat/ { + proxy_pass http://workcase-wechat/; + proxy_http_version 1.1; + 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; + } + # 默认首页(重定向到 platform) location = / { return 302 /platform/; diff --git a/docker/urbanLifeline/.env.example b/docker/urbanLifeline/.env.example new file mode 100644 index 00000000..e294c73f --- /dev/null +++ b/docker/urbanLifeline/.env.example @@ -0,0 +1,33 @@ +# ================================================ +# Urban Lifeline - Docker 环境变量配置 +# 复制此文件为 .env 并修改配置 +# ================================================ + +# 镜像版本 +IMAGE_VERSION=latest + +# Spring 运行环境 +SPRING_PROFILES_ACTIVE=prod + +# Nacos 配置 +NACOS_SERVER_ADDR=nacos:8848 +NACOS_NAMESPACE= + +# MinIO 配置 +MINIO_ENDPOINT=http://minio:9000 + +# Dify AI 配置 +DIFY_API_URL=http://dify-api:5001 + +# 日志目录 +LOG_ROOT=../../volumes/logs + +# 前端 API 地址 +API_BASE_URL=http://gateway:8080 + +# 端口映射 +GATEWAY_PORT=8080 +PLATFORM_PORT=8001 +WORKCASE_PORT=8002 +BIDDING_WEB_PORT=8003 +WORKCASE_WECHAT_PORT=8004 diff --git a/docker/urbanLifeline/Makefile b/docker/urbanLifeline/Makefile new file mode 100644 index 00000000..9c545bd3 --- /dev/null +++ b/docker/urbanLifeline/Makefile @@ -0,0 +1,79 @@ +# ================================================ +# Urban Lifeline - Docker 构建与部署 (All-in-One) +# ================================================ + +.PHONY: help build build-serv build-web up down logs ps status restart + +# 默认目标 +help: + @echo "Urban Lifeline Docker 命令 (All-in-One 模式)" + @echo "" + @echo "构建命令:" + @echo " make build - 构建所有镜像" + @echo " make build-serv - 构建后端镜像" + @echo " make build-web - 构建前端镜像" + @echo "" + @echo "启动/停止:" + @echo " make up - 启动所有服务" + @echo " make down - 停止所有服务" + @echo "" + @echo "管理命令:" + @echo " make ps - 查看容器状态" + @echo " make status - 查看服务状态" + @echo " make logs - 查看日志" + @echo " make restart-serv SERVICE=gateway - 重启后端服务" + @echo " make restart-web SITE=platform - 重启前端站点" + +# 构建 +build: build-serv build-web + +build-serv: + bash serv/build.sh + +build-web: + bash web/build.sh + +# 启动/停止 (通过主 docker-compose) +up: + cd .. && docker compose --profile all up -d + +down: + cd .. && docker compose --profile all down + +# 状态 +ps: + docker ps --filter "name=urban-lifeline" + +status: + @echo "=== 后端服务状态 ===" + @docker exec urban-lifeline-serv /app/service-manager.sh status 2>/dev/null || echo "后端容器未运行" + @echo "" + @echo "=== 前端服务状态 ===" + @docker exec urban-lifeline-web /app/web-manager.sh status 2>/dev/null || echo "前端容器未运行" + +# 日志 +logs: + cd .. && docker compose logs -f --tail=100 + +logs-serv: + docker logs -f --tail=100 urban-lifeline-serv + +logs-web: + docker logs -f --tail=100 urban-lifeline-web + +# 重启单个服务 +restart-serv: + @if [ -z "$(SERVICE)" ]; then \ + echo "用法: make restart-serv SERVICE=gateway"; \ + echo "可用服务: gateway system auth file log message crontab bidding workcase platform ai"; \ + else \ + docker exec urban-lifeline-serv /app/service-manager.sh restart $(SERVICE); \ + fi + +restart-web: + @if [ -z "$(SITE)" ]; then \ + echo "用法: make restart-web SITE=platform"; \ + echo "可用站点: shared platform workcase bidding workcase_wechat"; \ + else \ + docker exec urban-lifeline-web /app/web-manager.sh restart $(SITE); \ + fi diff --git a/docker/urbanLifeline/README.md b/docker/urbanLifeline/README.md new file mode 100644 index 00000000..03968f5d --- /dev/null +++ b/docker/urbanLifeline/README.md @@ -0,0 +1,100 @@ +# Urban Lifeline Docker 部署 + +## 目录结构 + +``` +docker/urbanLifeline/ +├── .env.example # 环境变量示例 +├── README.md # 本文档 +├── serv/ # 后端服务 +│ ├── docker-compose.yml +│ ├── build.sh # 构建脚本 +│ ├── start.sh # 启动脚本 +│ ├── Dockerfile.base # 基础镜像 +│ ├── Dockerfile.template # 服务模板 +│ └── Dockerfile.* # 各服务 Dockerfile +└── web/ # 前端应用 + ├── docker-compose.yml + ├── build.sh # 构建脚本 + ├── nginx.conf # Nginx 配置 + └── Dockerfile.* # 各前端 Dockerfile +``` + +## 服务端口 + +### 后端服务 +| 服务 | 端口 | 说明 | +|------|------|------| +| gateway | 8080 | API 网关 | +| auth | 8081 | 认证服务 | +| system | 8082 | 系统服务 | +| log | 8083 | 日志服务 | +| file | 8084 | 文件服务 | +| message | 8085 | 消息服务 | +| crontab | 8086 | 定时任务 | +| bidding | 8087 | 招标服务 | +| workcase | 8088 | 工单服务 | +| platform | 8089 | 平台服务 | +| ai | 8090 | AI 服务 | + +### 前端应用 +| 应用 | 端口 | 说明 | +|------|------|------| +| platform | 8001 | 管理平台 | +| workcase-web | 8002 | 工单系统 PC | +| bidding-web | 8003 | 招标系统 | +| workcase-wechat | 8004 | 工单微信端 | + +## 快速开始 + +### 1. 准备环境变量 +```bash +cp .env.example .env +# 编辑 .env 配置 +``` + +### 2. 构建后端 +```bash +# 先编译 Java 项目 +cd urbanLifelineServ +mvn clean package -DskipTests + +# 构建 Docker 镜像 +cd docker/urbanLifeline/serv +./build.sh # 构建所有 +./build.sh gateway # 构建单个 +``` + +### 3. 构建前端 +```bash +# 先构建前端项目 +cd urbanLifelineWeb +pnpm install +pnpm build + +# 构建 Docker 镜像 +cd docker/urbanLifeline/web +./build.sh # 构建所有 +./build.sh platform # 构建单个 +``` + +### 4. 启动服务 +```bash +# 启动后端 +cd docker/urbanLifeline/serv +docker-compose --profile serv up -d + +# 启动前端 +cd docker/urbanLifeline/web +docker-compose --profile web up -d +``` + +## 依赖服务 + +确保以下基础设施服务已启动: +- Nacos (服务注册/配置中心) +- PostgreSQL (数据库) +- Redis (缓存) +- MinIO (对象存储) + +可通过 `docker/infra` 目录启动基础设施。 diff --git a/docker/urbanLifeline/serv/Dockerfile.serv b/docker/urbanLifeline/serv/Dockerfile.serv new file mode 100644 index 00000000..e69de29b diff --git a/docker/urbanLifeline/serv/Dockerfile.template b/docker/urbanLifeline/serv/Dockerfile.template deleted file mode 100644 index b3eba33c..00000000 --- a/docker/urbanLifeline/serv/Dockerfile.template +++ /dev/null @@ -1,45 +0,0 @@ -# ================================================ -# Urban Lifeline - 微服务 Dockerfile 模板 -# 用于构建各个后端微服务镜像 -# -# 构建参数: -# SERVICE_NAME - 服务名称 (gateway/system/auth/file/ai/workcase) -# SERVICE_PORT - 服务端口 -# -# 构建命令示例: -# docker build -t urban-lifeline-gateway:latest \ -# --build-arg SERVICE_NAME=gateway \ -# --build-arg SERVICE_PORT=8080 \ -# -f Dockerfile.template . -# ================================================ -FROM urban-lifeline-base-serv:latest - -# 构建参数 -ARG SERVICE_NAME -ARG SERVICE_PORT=8080 - -# 环境变量 -ENV SERVICE_NAME=${SERVICE_NAME} \ - SERVICE_PORT=${SERVICE_PORT} \ - SPRING_PROFILES_ACTIVE=prod \ - JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC" - -# 设置工作目录 -WORKDIR /app - -# 1. 复制启动脚本(不常变化) -COPY docker/urbanLifeline/serv/start.sh /app/start.sh -RUN chmod +x /app/start.sh - -# 2. 复制 JAR 包(最常变化,放最后) -COPY urbanLifelineServ/${SERVICE_NAME}/target/*.jar /app/app.jar - -# 暴露端口 -EXPOSE ${SERVICE_PORT} - -# 健康检查 -HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ - CMD curl -f http://localhost:${SERVICE_PORT}/actuator/health || exit 1 - -# 启动应用 -ENTRYPOINT ["/app/start.sh"] diff --git a/docker/urbanLifeline/serv/build.sh b/docker/urbanLifeline/serv/build.sh new file mode 100644 index 00000000..d705c4b6 --- /dev/null +++ b/docker/urbanLifeline/serv/build.sh @@ -0,0 +1,84 @@ +#!/bin/bash +# ================================================ +# Urban Lifeline - 后端服务镜像构建脚本 +# +# 使用方式: +# ./build.sh # 构建 All-in-One 镜像 (推荐) +# ./build.sh base # 仅构建基础镜像 +# ================================================ + +set -e + +# 项目根目录 (相对于此脚本) +PROJECT_ROOT="$(cd "$(dirname "$0")/../../.." && pwd)" +cd "$PROJECT_ROOT" + +# 镜像版本 +VERSION=${IMAGE_VERSION:-latest} + +# 服务列表 +SERVICES=(gateway system auth file log message crontab bidding workcase platform ai) + +# 构建基础镜像 +build_base() { + echo "==========================================" + echo "构建基础镜像: urban-lifeline-base-serv:${VERSION}" + echo "==========================================" + docker build -t urban-lifeline-base-serv:${VERSION} \ + -f docker/urbanLifeline/serv/Dockerfile.base . + + if [ "$VERSION" != "latest" ]; then + docker tag urban-lifeline-base-serv:${VERSION} urban-lifeline-base-serv:latest + fi +} + +# 构建 All-in-One 镜像 +build() { + echo "==========================================" + echo "构建 All-in-One 镜像: urban-lifeline-serv:${VERSION}" + echo "==========================================" + + # 先构建基础镜像 + build_base + + # 检查所有 JAR 文件 + local missing=0 + for service in "${SERVICES[@]}"; do + if ! ls urbanLifelineServ/${service}/target/*.jar 1> /dev/null 2>&1; then + echo "缺少: urbanLifelineServ/${service}/target/*.jar" + missing=1 + fi + done + + if [ $missing -eq 1 ]; then + echo "" + echo "请先执行 Maven 构建:" + echo " cd urbanLifelineServ && mvn clean package -DskipTests" + exit 1 + fi + + docker build -t urban-lifeline-serv:${VERSION} \ + -f docker/urbanLifeline/serv/Dockerfile.serv . + + if [ "$VERSION" != "latest" ]; then + docker tag urban-lifeline-serv:${VERSION} urban-lifeline-serv:latest + fi + + echo "==========================================" + echo "镜像构建完成!" + echo "" + echo "管理服务:" + echo " docker exec urban-lifeline-serv /app/service-manager.sh status" + echo " docker exec urban-lifeline-serv /app/service-manager.sh restart gateway" + echo "==========================================" +} + +# 主逻辑 +case "${1:-build}" in + base) + build_base + ;; + *) + build + ;; +esac diff --git a/docker/urbanLifeline/serv/docker-compose.yml b/docker/urbanLifeline/serv/docker-compose.yml index c6b157ae..10d277b1 100644 --- a/docker/urbanLifeline/serv/docker-compose.yml +++ b/docker/urbanLifeline/serv/docker-compose.yml @@ -1,180 +1,47 @@ # ================================================ -# Level 2: 后端微服务 -# gateway -> system -> file -> auth -> ai -> workcase +# Urban Lifeline - All-in-One 部署 +# 单容器运行所有后端服务 # ================================================ services: - # ====================== Gateway 网关服务 ====================== - gateway: - image: urban-lifeline-gateway:${IMAGE_VERSION:-latest} - container_name: urban-lifeline-gateway + urban-lifeline-serv: + image: urban-lifeline-serv:${IMAGE_VERSION:-latest} + container_name: urban-lifeline-serv restart: unless-stopped - profiles: ["serv", "all"] networks: - urban-lifeline ports: - - "8080:8080" + - "8080:8080" # gateway + - "8081:8081" # auth + - "8082:8082" # system + - "8083:8083" # log + - "8084:8084" # file + - "8085:8085" # message + - "8086:8086" # crontab + - "8087:8087" # bidding + - "8088:8088" # workcase + - "8089:8089" # platform + - "8090:8090" # ai environment: TZ: Asia/Shanghai SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod} NACOS_SERVER_ADDR: ${NACOS_SERVER_ADDR:-nacos:8848} NACOS_NAMESPACE: ${NACOS_NAMESPACE:-} - JAVA_OPTS: "-Xms256m -Xmx512m" volumes: - - ${LOG_ROOT:-../volumes/logs}/gateway:/app/logs - depends_on: - nacos: - condition: service_healthy + - ${LOG_ROOT:-../../volumes/logs}:/app/logs + # 内存限制 (根据服务器配置调整) + deploy: + resources: + limits: + memory: 4G + reservations: + memory: 2G healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"] interval: 30s timeout: 10s retries: 3 - start_period: 60s - - # ====================== System 系统服务 ====================== - system: - image: urban-lifeline-system:${IMAGE_VERSION:-latest} - container_name: urban-lifeline-system - restart: unless-stopped - profiles: ["serv", "all"] - networks: - - urban-lifeline - expose: - - "8082" - environment: - TZ: Asia/Shanghai - SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod} - NACOS_SERVER_ADDR: ${NACOS_SERVER_ADDR:-nacos:8848} - NACOS_NAMESPACE: ${NACOS_NAMESPACE:-} - JAVA_OPTS: "-Xms256m -Xmx512m" - volumes: - - ${LOG_ROOT:-../volumes/logs}/system:/app/logs - depends_on: - gateway: - condition: service_healthy - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8082/actuator/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - - # ====================== File 文件服务 ====================== - file: - image: urban-lifeline-file:${IMAGE_VERSION:-latest} - container_name: urban-lifeline-file - restart: unless-stopped - profiles: ["serv", "all"] - networks: - - urban-lifeline - expose: - - "8084" - environment: - TZ: Asia/Shanghai - SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod} - NACOS_SERVER_ADDR: ${NACOS_SERVER_ADDR:-nacos:8848} - NACOS_NAMESPACE: ${NACOS_NAMESPACE:-} - MINIO_ENDPOINT: ${MINIO_ENDPOINT:-http://minio:9000} - JAVA_OPTS: "-Xms256m -Xmx512m" - volumes: - - ${LOG_ROOT:-../volumes/logs}/file:/app/logs - depends_on: - system: - condition: service_healthy - minio: - condition: service_healthy - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8084/actuator/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - - # ====================== Auth 认证服务 ====================== - auth: - image: urban-lifeline-auth:${IMAGE_VERSION:-latest} - container_name: urban-lifeline-auth - restart: unless-stopped - profiles: ["serv", "all"] - networks: - - urban-lifeline - expose: - - "8081" - environment: - TZ: Asia/Shanghai - SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod} - NACOS_SERVER_ADDR: ${NACOS_SERVER_ADDR:-nacos:8848} - NACOS_NAMESPACE: ${NACOS_NAMESPACE:-} - JAVA_OPTS: "-Xms256m -Xmx512m" - volumes: - - ${LOG_ROOT:-../volumes/logs}/auth:/app/logs - depends_on: - system: - condition: service_healthy - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8081/actuator/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - - # ====================== AI 智能服务 ====================== - ai: - image: urban-lifeline-ai:${IMAGE_VERSION:-latest} - container_name: urban-lifeline-ai - restart: unless-stopped - profiles: ["serv", "all"] - networks: - - urban-lifeline - expose: - - "8090" - environment: - TZ: Asia/Shanghai - SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod} - NACOS_SERVER_ADDR: ${NACOS_SERVER_ADDR:-nacos:8848} - NACOS_NAMESPACE: ${NACOS_NAMESPACE:-} - DIFY_API_URL: ${DIFY_API_URL:-http://dify-api:5001} - JAVA_OPTS: "-Xms256m -Xmx512m" - volumes: - - ${LOG_ROOT:-../volumes/logs}/ai:/app/logs - depends_on: - file: - condition: service_healthy - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8090/actuator/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - - # ====================== Workcase 工单服务 ====================== - workcase: - image: urban-lifeline-workcase:${IMAGE_VERSION:-latest} - container_name: urban-lifeline-workcase - restart: unless-stopped - profiles: ["serv", "all"] - networks: - - urban-lifeline - expose: - - "8088" - environment: - TZ: Asia/Shanghai - SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod} - NACOS_SERVER_ADDR: ${NACOS_SERVER_ADDR:-nacos:8848} - NACOS_NAMESPACE: ${NACOS_NAMESPACE:-} - JAVA_OPTS: "-Xms256m -Xmx512m" - volumes: - - ${LOG_ROOT:-../volumes/logs}/workcase:/app/logs - depends_on: - ai: - condition: service_healthy - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8088/actuator/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s + start_period: 180s networks: urban-lifeline: diff --git a/docker/urbanLifeline/serv/service-manager.sh b/docker/urbanLifeline/serv/service-manager.sh new file mode 100644 index 00000000..8dfd6309 --- /dev/null +++ b/docker/urbanLifeline/serv/service-manager.sh @@ -0,0 +1,350 @@ +#!/bin/bash +# ================================================ +# Urban Lifeline - 服务管理脚本 +# +# 用法: +# ./service-manager.sh start-all # 启动所有服务 +# ./service-manager.sh start gateway # 启动单个服务 +# ./service-manager.sh stop gateway # 停止单个服务 +# ./service-manager.sh restart gateway # 重启单个服务 +# ./service-manager.sh status # 查看所有服务状态 +# ./service-manager.sh logs gateway # 查看服务日志 +# ================================================ + +set -e + +# 目录配置 +JAR_DIR="/app/jars" +LOG_DIR="/app/logs" +PID_DIR="/app/pids" + +# 服务配置: 服务名=端口:内存 +declare -A SERVICES=( + ["gateway"]="8080:384m" + ["system"]="8082:384m" + ["auth"]="8081:256m" + ["file"]="8084:256m" + ["log"]="8083:192m" + ["message"]="8085:256m" + ["crontab"]="8086:192m" + ["ai"]="8090:384m" + ["bidding"]="8087:256m" + ["platform"]="8089:256m" + ["workcase"]="8088:384m" +) + +# 启动顺序 (有依赖关系) +# BOOT_ORDER=(gateway system auth file log message crontab ai bidding platform workcase) +BOOT_ORDER=(gateway system auth file ai workcase) + +# 颜色输出 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } +log_error() { echo -e "${RED}[ERROR]${NC} $1"; } + +# 获取服务端口 +get_port() { + local service=$1 + echo "${SERVICES[$service]}" | cut -d: -f1 +} + +# 获取服务内存配置 +get_memory() { + local service=$1 + echo "${SERVICES[$service]}" | cut -d: -f2 +} + +# 检查服务是否运行 +is_running() { + local service=$1 + local pid_file="${PID_DIR}/${service}.pid" + + if [ -f "$pid_file" ]; then + local pid=$(cat "$pid_file") + if kill -0 "$pid" 2>/dev/null; then + return 0 + fi + fi + return 1 +} + +# 等待服务就绪 +wait_for_service() { + local service=$1 + local port=$(get_port "$service") + local max_wait=60 + local count=0 + + while [ $count -lt $max_wait ]; do + if curl -sf "http://localhost:${port}/actuator/health" > /dev/null 2>&1; then + return 0 + fi + sleep 2 + count=$((count + 2)) + done + return 1 +} + +# 启动单个服务 +start_service() { + local service=$1 + local port=$(get_port "$service") + local memory=$(get_memory "$service") + local jar_file="${JAR_DIR}/${service}.jar" + local log_file="${LOG_DIR}/${service}.log" + local pid_file="${PID_DIR}/${service}.pid" + + if [ ! -f "$jar_file" ]; then + log_error "JAR 文件不存在: $jar_file" + return 1 + fi + + if is_running "$service"; then + log_warn "$service 已在运行 (PID: $(cat $pid_file))" + return 0 + fi + + log_info "启动 $service (端口: $port, 内存: $memory)..." + + # JVM 参数 + local java_opts="-Xms128m -Xmx${memory} -XX:+UseG1GC -XX:MaxGCPauseMillis=100" + java_opts="$java_opts -Djava.security.egd=file:/dev/./urandom" + java_opts="$java_opts -Dspring.profiles.active=${SPRING_PROFILES_ACTIVE:-prod}" + + # Nacos 配置 + if [ -n "$NACOS_SERVER_ADDR" ]; then + java_opts="$java_opts -Dspring.cloud.nacos.discovery.server-addr=$NACOS_SERVER_ADDR" + java_opts="$java_opts -Dspring.cloud.nacos.config.server-addr=$NACOS_SERVER_ADDR" + fi + + if [ -n "$NACOS_NAMESPACE" ]; then + java_opts="$java_opts -Dspring.cloud.nacos.discovery.namespace=$NACOS_NAMESPACE" + java_opts="$java_opts -Dspring.cloud.nacos.config.namespace=$NACOS_NAMESPACE" + fi + + # 启动服务 + nohup java $java_opts -jar "$jar_file" > "$log_file" 2>&1 & + local pid=$! + echo $pid > "$pid_file" + + log_info "$service 已启动 (PID: $pid)" +} + +# 停止单个服务 +stop_service() { + local service=$1 + local pid_file="${PID_DIR}/${service}.pid" + + if ! is_running "$service"; then + log_warn "$service 未在运行" + return 0 + fi + + local pid=$(cat "$pid_file") + log_info "停止 $service (PID: $pid)..." + + # 优雅关闭 + kill -15 "$pid" 2>/dev/null || true + + # 等待进程退出 + local count=0 + while [ $count -lt 30 ] && kill -0 "$pid" 2>/dev/null; do + sleep 1 + count=$((count + 1)) + done + + # 强制关闭 + if kill -0 "$pid" 2>/dev/null; then + log_warn "强制终止 $service..." + kill -9 "$pid" 2>/dev/null || true + fi + + rm -f "$pid_file" + log_info "$service 已停止" +} + +# 重启单个服务 +restart_service() { + local service=$1 + stop_service "$service" + sleep 2 + start_service "$service" +} + +# 启动所有服务 +start_all() { + log_info "==========================================" + log_info " Urban Lifeline - 启动所有服务" + log_info "==========================================" + + # 等待 Nacos + if [ -n "$NACOS_SERVER_ADDR" ]; then + log_info "等待 Nacos 就绪..." + local nacos_host=$(echo $NACOS_SERVER_ADDR | cut -d: -f1) + local nacos_port=$(echo $NACOS_SERVER_ADDR | cut -d: -f2) + + for i in $(seq 1 30); do + if curl -sf "http://${nacos_host}:${nacos_port}/nacos/" > /dev/null 2>&1; then + log_info "Nacos 已就绪" + break + fi + sleep 2 + done + fi + + # 按顺序启动服务,等待健康检查通过后再启动下一个 + for service in "${BOOT_ORDER[@]}"; do + start_service "$service" + + log_info "等待 $service 健康检查..." + if wait_for_service "$service"; then + log_info "$service 已就绪 ✓" + else + log_warn "$service 健康检查超时,继续启动下一个服务" + fi + done + + log_info "==========================================" + log_info " 所有服务启动完成" + log_info "==========================================" + + show_status + + # 保持容器运行并监控 + monitor_services +} + +# 停止所有服务 +stop_all() { + log_info "停止所有服务..." + + # 逆序停止 + for ((i=${#BOOT_ORDER[@]}-1; i>=0; i--)); do + stop_service "${BOOT_ORDER[$i]}" + done + + log_info "所有服务已停止" +} + +# 查看服务状态 +show_status() { + echo "" + echo "==========================================" + echo " Urban Lifeline 服务状态" + echo "==========================================" + printf "%-12s %-8s %-8s %-10s\n" "服务" "端口" "PID" "状态" + echo "------------------------------------------" + + for service in "${BOOT_ORDER[@]}"; do + local port=$(get_port "$service") + local pid_file="${PID_DIR}/${service}.pid" + local pid="-" + local status="${RED}停止${NC}" + + if [ -f "$pid_file" ]; then + pid=$(cat "$pid_file") + if kill -0 "$pid" 2>/dev/null; then + status="${GREEN}运行中${NC}" + fi + fi + + printf "%-12s %-8s %-8s " "$service" "$port" "$pid" + echo -e "$status" + done + echo "==========================================" +} + +# 查看服务日志 +show_logs() { + local service=$1 + local log_file="${LOG_DIR}/${service}.log" + + if [ ! -f "$log_file" ]; then + log_error "日志文件不存在: $log_file" + return 1 + fi + + tail -f "$log_file" +} + +# 监控服务 (保持容器运行) +monitor_services() { + log_info "进入监控模式..." + + # 捕获退出信号 + trap 'stop_all; exit 0' SIGTERM SIGINT + + while true; do + sleep 30 + + # 检查服务状态,自动重启挂掉的服务 + for service in "${BOOT_ORDER[@]}"; do + if ! is_running "$service"; then + log_warn "$service 已停止,尝试重启..." + start_service "$service" + fi + done + done +} + +# 主入口 +case "${1:-help}" in + start-all) + start_all + ;; + stop-all) + stop_all + ;; + start) + if [ -z "$2" ]; then + log_error "请指定服务名" + exit 1 + fi + start_service "$2" + ;; + stop) + if [ -z "$2" ]; then + log_error "请指定服务名" + exit 1 + fi + stop_service "$2" + ;; + restart) + if [ -z "$2" ]; then + log_error "请指定服务名" + exit 1 + fi + restart_service "$2" + ;; + status) + show_status + ;; + logs) + if [ -z "$2" ]; then + log_error "请指定服务名" + exit 1 + fi + show_logs "$2" + ;; + *) + echo "Urban Lifeline 服务管理" + echo "" + echo "用法: $0 <命令> [服务名]" + echo "" + echo "命令:" + echo " start-all 启动所有服务" + echo " stop-all 停止所有服务" + echo " start <服务> 启动指定服务" + echo " stop <服务> 停止指定服务" + echo " restart <服务> 重启指定服务" + echo " status 查看服务状态" + echo " logs <服务> 查看服务日志" + echo "" + echo "可用服务: ${!SERVICES[*]}" + ;; +esac diff --git a/docker/urbanLifeline/web/Dockerfile b/docker/urbanLifeline/web/Dockerfile deleted file mode 100644 index d560140a..00000000 --- a/docker/urbanLifeline/web/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -# ================================================ -# Urban Lifeline - 前端 Dockerfile 模板 -# 基于 Nginx Alpine,提供静态文件服务 -# -# 构建参数: -# WEB_NAME - 前端项目名称 (platform/workcase) -# -# 构建命令示例: -# docker build -t urban-lifeline-platform:latest \ -# --build-arg WEB_NAME=platform \ -# -f docker/urbanLifeline/web/Dockerfile . -# ================================================ -FROM nginx:alpine - -ARG WEB_NAME - -ENV TZ=Asia/Shanghai - -# 安装基础工具 -RUN apk add --no-cache tzdata curl \ - && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ - && echo "Asia/Shanghai" > /etc/timezone - -# 复制 Nginx 配置 -COPY docker/urbanLifeline/web/nginx.conf /etc/nginx/nginx.conf - -# 复制前端构建产物 -COPY urbanLifelineWeb/packages/${WEB_NAME}/dist/ /usr/share/nginx/html/ - -# 暴露端口 -EXPOSE 80 - -# 健康检查 -HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \ - CMD curl -f http://localhost/ || exit 1 - -CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/urbanLifeline/web/Dockerfile.web b/docker/urbanLifeline/web/Dockerfile.web new file mode 100644 index 00000000..4e0f3f74 --- /dev/null +++ b/docker/urbanLifeline/web/Dockerfile.web @@ -0,0 +1,46 @@ +# ================================================ +# Urban Lifeline - All-in-One 前端镜像 +# 使用 Node.js 静态服务器,端口可配置 +# +# 构建: docker build -t urban-lifeline-web:latest -f Dockerfile.web . +# ================================================ +FROM node:20-alpine + +ENV TZ=Asia/Shanghai + +RUN apk add --no-cache tzdata curl bash \ + && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ + && echo "Asia/Shanghai" > /etc/timezone + +# 安装静态文件服务器 +RUN npm install -g serve + +WORKDIR /app + +# 复制管理脚本 +COPY docker/urbanLifeline/web/web-manager.sh /app/web-manager.sh +RUN chmod +x /app/web-manager.sh + +# 复制 shared 公共模块 (Module Federation 远程模块,必须最先启动) +COPY urbanLifelineWeb/packages/shared/dist/ /app/sites/shared/ + +# 复制所有前端构建产物 +COPY urbanLifelineWeb/packages/platform/dist/ /app/sites/platform/ +COPY urbanLifelineWeb/packages/workcase/dist/ /app/sites/workcase/ +COPY urbanLifelineWeb/packages/bidding/dist/ /app/sites/bidding/ +COPY urbanLifelineWeb/packages/workcase_wechat/dist/ /app/sites/workcase_wechat/ + +# 默认端口 (可通过环境变量覆盖) +ENV SHARED_PORT=8000 \ + PLATFORM_PORT=8001 \ + WORKCASE_PORT=8002 \ + BIDDING_PORT=8003 \ + WORKCASE_WECHAT_PORT=8004 + +EXPOSE 8000 8001 8002 8003 8004 + +HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \ + CMD curl -f http://localhost:${SHARED_PORT}/ || exit 1 + +ENTRYPOINT ["/app/web-manager.sh"] +CMD ["start-all"] diff --git a/docker/urbanLifeline/web/build.sh b/docker/urbanLifeline/web/build.sh new file mode 100644 index 00000000..b004792f --- /dev/null +++ b/docker/urbanLifeline/web/build.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# ================================================ +# Urban Lifeline - 前端镜像构建脚本 +# +# 使用方式: +# ./build.sh # 构建 All-in-One 镜像 +# ================================================ + +set -e + +# 项目根目录 (相对于此脚本) +PROJECT_ROOT="$(cd "$(dirname "$0")/../../.." && pwd)" +cd "$PROJECT_ROOT" + +# 镜像版本 +VERSION=${IMAGE_VERSION:-latest} + +# 前端站点列表 +SITES=(shared platform workcase bidding workcase_wechat) + +# 构建 All-in-One 镜像 +build() { + echo "==========================================" + echo "构建 All-in-One 前端镜像: urban-lifeline-web:${VERSION}" + echo "==========================================" + + # 检查所有 dist 目录 + local missing=0 + for site in "${SITES[@]}"; do + if [ ! -d "urbanLifelineWeb/packages/${site}/dist" ]; then + echo "缺少: urbanLifelineWeb/packages/${site}/dist" + missing=1 + fi + done + + if [ $missing -eq 1 ]; then + echo "" + echo "请先执行前端构建:" + echo " cd urbanLifelineWeb && pnpm build" + exit 1 + fi + + docker build -t urban-lifeline-web:${VERSION} \ + -f docker/urbanLifeline/web/Dockerfile.web . + + if [ "$VERSION" != "latest" ]; then + docker tag urban-lifeline-web:${VERSION} urban-lifeline-web:latest + fi + + echo "==========================================" + echo "镜像构建完成!" + echo "" + echo "管理服务:" + echo " docker exec urban-lifeline-web /app/web-manager.sh status" + echo " docker exec urban-lifeline-web /app/web-manager.sh restart platform" + echo "==========================================" +} + +build diff --git a/docker/urbanLifeline/web/docker-compose.yml b/docker/urbanLifeline/web/docker-compose.yml index 3fba1da4..6ddcaa59 100644 --- a/docker/urbanLifeline/web/docker-compose.yml +++ b/docker/urbanLifeline/web/docker-compose.yml @@ -1,47 +1,40 @@ # ================================================ -# Level 3: 前端应用 -# 每个前端独立 Nginx 容器 -# platform (8001) / workcase (8002) +# Urban Lifeline - All-in-One 前端部署 +# 单容器运行所有前端应用 # ================================================ services: - # ====================== Platform 管理平台 ====================== - platform: - image: urban-lifeline-platform:${IMAGE_VERSION:-latest} - container_name: urban-lifeline-platform + urban-lifeline-web: + image: urban-lifeline-web:${IMAGE_VERSION:-latest} + container_name: urban-lifeline-web restart: unless-stopped - profiles: ["web", "all"] networks: - urban-lifeline - expose: - - "80" + ports: + - "${PLATFORM_PORT:-8001}:${PLATFORM_PORT:-8001}" + - "${WORKCASE_PORT:-8002}:${WORKCASE_PORT:-8002}" + - "${BIDDING_PORT:-8003}:${BIDDING_PORT:-8003}" + - "${WORKCASE_WECHAT_PORT:-8004}:${WORKCASE_WECHAT_PORT:-8004}" environment: TZ: Asia/Shanghai + PLATFORM_PORT: ${PLATFORM_PORT:-8001} + WORKCASE_PORT: ${WORKCASE_PORT:-8002} + BIDDING_PORT: ${BIDDING_PORT:-8003} + WORKCASE_WECHAT_PORT: ${WORKCASE_WECHAT_PORT:-8004} + volumes: + - ${LOG_ROOT:-../../volumes/logs}/web:/app/logs + deploy: + resources: + limits: + memory: 512M + reservations: + memory: 128M healthcheck: - test: ["CMD", "curl", "-f", "http://localhost/"] + test: ["CMD", "curl", "-f", "http://localhost:${PLATFORM_PORT:-8001}/"] interval: 30s timeout: 10s retries: 3 - start_period: 10s - - # ====================== Workcase 工单系统 ====================== - workcase-web: - image: urban-lifeline-workcase-web:${IMAGE_VERSION:-latest} - container_name: urban-lifeline-workcase-web - restart: unless-stopped - profiles: ["web", "all"] - networks: - - urban-lifeline - expose: - - "80" - environment: - TZ: Asia/Shanghai - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost/"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 10s + start_period: 30s networks: urban-lifeline: diff --git a/docker/urbanLifeline/web/web-manager.sh b/docker/urbanLifeline/web/web-manager.sh new file mode 100644 index 00000000..51547cd9 --- /dev/null +++ b/docker/urbanLifeline/web/web-manager.sh @@ -0,0 +1,270 @@ +#!/bin/bash +# ================================================ +# Urban Lifeline - 前端服务管理脚本 +# +# 用法: +# ./web-manager.sh start-all # 启动所有前端 +# ./web-manager.sh start platform # 启动单个前端 +# ./web-manager.sh stop platform # 停止单个前端 +# ./web-manager.sh restart platform # 重启单个前端 +# ./web-manager.sh status # 查看状态 +# ================================================ + +set -e + +SITES_DIR="/app/sites" +PID_DIR="/app/pids" +LOG_DIR="/app/logs" + +mkdir -p "$PID_DIR" "$LOG_DIR" + +# 前端配置: 名称=端口环境变量名:默认端口 +declare -A WEBS=( + ["shared"]="SHARED_PORT:8000" + ["platform"]="PLATFORM_PORT:8001" + ["workcase"]="WORKCASE_PORT:8002" + ["bidding"]="BIDDING_PORT:8003" + ["workcase_wechat"]="WORKCASE_WECHAT_PORT:8004" +) + +# shared 必须最先启动 (其他模块依赖它) +BOOT_ORDER=(shared platform workcase bidding workcase_wechat) + +# 颜色 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } +log_error() { echo -e "${RED}[ERROR]${NC} $1"; } + +# 获取端口 +get_port() { + local web=$1 + local config=${WEBS[$web]} + local env_name=$(echo "$config" | cut -d: -f1) + local default_port=$(echo "$config" | cut -d: -f2) + echo "${!env_name:-$default_port}" +} + +# 检查是否运行 +is_running() { + local web=$1 + local pid_file="${PID_DIR}/${web}.pid" + + if [ -f "$pid_file" ]; then + local pid=$(cat "$pid_file") + if kill -0 "$pid" 2>/dev/null; then + return 0 + fi + fi + return 1 +} + +# 等待服务就绪 +wait_for_web() { + local web=$1 + local port=$(get_port "$web") + local max_wait=30 + local count=0 + + while [ $count -lt $max_wait ]; do + if curl -sf "http://localhost:${port}/" > /dev/null 2>&1; then + return 0 + fi + sleep 1 + count=$((count + 1)) + done + return 1 +} + +# 启动单个前端 +start_web() { + local web=$1 + local port=$(get_port "$web") + local site_dir="${SITES_DIR}/${web}" + local pid_file="${PID_DIR}/${web}.pid" + local log_file="${LOG_DIR}/${web}.log" + + if [ ! -d "$site_dir" ]; then + log_error "站点目录不存在: $site_dir" + return 1 + fi + + if is_running "$web"; then + log_warn "$web 已在运行 (PID: $(cat $pid_file))" + return 0 + fi + + log_info "启动 $web (端口: $port)..." + + # 使用 serve 启动静态服务 + nohup serve -s "$site_dir" -l "$port" > "$log_file" 2>&1 & + local pid=$! + echo $pid > "$pid_file" + + log_info "$web 已启动 (PID: $pid)" +} + +# 停止单个前端 +stop_web() { + local web=$1 + local pid_file="${PID_DIR}/${web}.pid" + + if ! is_running "$web"; then + log_warn "$web 未在运行" + return 0 + fi + + local pid=$(cat "$pid_file") + log_info "停止 $web (PID: $pid)..." + + kill -15 "$pid" 2>/dev/null || true + + local count=0 + while [ $count -lt 10 ] && kill -0 "$pid" 2>/dev/null; do + sleep 1 + count=$((count + 1)) + done + + if kill -0 "$pid" 2>/dev/null; then + kill -9 "$pid" 2>/dev/null || true + fi + + rm -f "$pid_file" + log_info "$web 已停止" +} + +# 重启 +restart_web() { + local web=$1 + stop_web "$web" + sleep 1 + start_web "$web" +} + +# 启动所有 +start_all() { + log_info "==========================================" + log_info " Urban Lifeline - 启动所有前端" + log_info "==========================================" + + for web in "${BOOT_ORDER[@]}"; do + start_web "$web" + + log_info "等待 $web 就绪..." + if wait_for_web "$web"; then + log_info "$web 已就绪 ✓" + else + log_warn "$web 健康检查超时" + fi + done + + log_info "==========================================" + log_info " 所有前端启动完成" + log_info "==========================================" + + show_status + monitor_webs +} + +# 停止所有 +stop_all() { + log_info "停止所有前端..." + for web in "${BOOT_ORDER[@]}"; do + stop_web "$web" + done +} + +# 状态 +show_status() { + echo "" + echo "==========================================" + echo " Urban Lifeline 前端状态" + echo "==========================================" + printf "%-18s %-8s %-8s %-10s\n" "站点" "端口" "PID" "状态" + echo "------------------------------------------" + + for web in "${BOOT_ORDER[@]}"; do + local port=$(get_port "$web") + local pid_file="${PID_DIR}/${web}.pid" + local pid="-" + local status="${RED}停止${NC}" + + if [ -f "$pid_file" ]; then + pid=$(cat "$pid_file") + if kill -0 "$pid" 2>/dev/null; then + status="${GREEN}运行中${NC}" + fi + fi + + printf "%-18s %-8s %-8s " "$web" "$port" "$pid" + echo -e "$status" + done + echo "==========================================" +} + +# 监控 +monitor_webs() { + log_info "进入监控模式..." + trap 'stop_all; exit 0' SIGTERM SIGINT + + while true; do + sleep 30 + for web in "${BOOT_ORDER[@]}"; do + if ! is_running "$web"; then + log_warn "$web 已停止,尝试重启..." + start_web "$web" + fi + done + done +} + +# 主入口 +case "${1:-help}" in + start-all) + start_all + ;; + stop-all) + stop_all + ;; + start) + [ -z "$2" ] && { log_error "请指定站点名"; exit 1; } + start_web "$2" + ;; + stop) + [ -z "$2" ] && { log_error "请指定站点名"; exit 1; } + stop_web "$2" + ;; + restart) + [ -z "$2" ] && { log_error "请指定站点名"; exit 1; } + restart_web "$2" + ;; + status) + show_status + ;; + *) + echo "Urban Lifeline 前端管理" + echo "" + echo "用法: $0 <命令> [站点名]" + echo "" + echo "命令:" + echo " start-all 启动所有前端" + echo " stop-all 停止所有前端" + echo " start <站点> 启动指定站点" + echo " stop <站点> 停止指定站点" + echo " restart <站点> 重启指定站点" + echo " status 查看状态" + echo "" + echo "可用站点: ${!WEBS[*]}" + echo "" + echo "端口配置 (环境变量):" + echo " SHARED_PORT 默认 8000 (公共模块,必须最先启动)" + echo " PLATFORM_PORT 默认 8001" + echo " WORKCASE_PORT 默认 8002" + echo " BIDDING_PORT 默认 8003" + echo " WORKCASE_WECHAT_PORT 默认 8004" + ;; +esac