This commit is contained in:
2026-01-01 11:04:24 +08:00
parent 48da0a4c81
commit 87baa347b7
16 changed files with 1202 additions and 286 deletions

View File

@@ -3,24 +3,23 @@
# 用于统一管理所有服务的启动 # 用于统一管理所有服务的启动
# ================================================ # ================================================
# 使用方法: # 使用方法:
# 启动所有服务: docker compose up -d # 启动所有服务: docker compose --profile all up -d
# 启动基础设施: docker compose --profile infra 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 down
# 查看日志: docker compose logs -f [service_name] # 查看日志: docker compose logs -f [service_name]
#
# All-in-One 模式:
# - urban-lifeline-serv: 所有后端服务 (单容器)
# - urban-lifeline-web: 所有前端应用 (单容器)
# - nginx: 反向代理
# ================================================ # ================================================
name: urban-lifeline name: urban-lifeline
# 引入子目录的 compose 文件 # 引入子目录的 compose 文件
include: include:
# Level 1: 基础设施 # 基础设施 + 业务服务 (All-in-One)
- path: ./infra/docker-compose.yml - path: ./infra/docker-compose.yml
# Level 2: 后端服务
- path: ./urbanLifeline/serv/docker-compose.yml
# Level 3: 前端服务
- path: ./urbanLifeline/web/docker-compose.yml
# 共享网络定义 # 共享网络定义
networks: networks:

View File

@@ -6,7 +6,7 @@
services: services:
# ====================== Nginx 反向代理 ====================== # ====================== Nginx 反向代理 ======================
nginx: nginx:
image: urban-lifeline-web:${IMAGE_VERSION:-latest} image: nginx:alpine
container_name: urban-lifeline-nginx container_name: urban-lifeline-nginx
restart: unless-stopped restart: unless-stopped
profiles: ["infra", "all"] profiles: ["infra", "all"]
@@ -23,6 +23,9 @@ services:
- ./nginx/conf.d:/etc/nginx/conf.d:ro - ./nginx/conf.d:/etc/nginx/conf.d:ro
# SSL 证书(可选) # SSL 证书(可选)
# - ./nginx/ssl:/etc/nginx/ssl:ro # - ./nginx/ssl:/etc/nginx/ssl:ro
depends_on:
- urban-lifeline-serv
- urban-lifeline-web
healthcheck: healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"] test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s interval: 30s
@@ -30,6 +33,73 @@ services:
retries: 3 retries: 3
start_period: 30s 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 注册中心 ======================
nacos: nacos:
image: nacos/nacos-server:v3.1.0 image: nacos/nacos-server:v3.1.0

View File

@@ -1,19 +1,32 @@
# ================================================ # ================================================
# Urban Lifeline - 站点配置 # Urban Lifeline - 站点配置 (All-in-One 模式)
# ================================================ # ================================================
# 上游服务定义 # 上游服务定义 - 后端 All-in-One 容器
upstream gateway { upstream gateway {
server gateway:8080; server urban-lifeline-serv:8080;
keepalive 32; keepalive 32;
} }
# 上游服务定义 - 前端 All-in-One 容器
upstream shared {
server urban-lifeline-web:8000;
}
upstream platform { upstream platform {
server platform:80; server urban-lifeline-web:8001;
} }
upstream workcase-web { 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 { 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 管理平台 # Platform 管理平台
location /platform/ { location /platform/ {
proxy_pass http://platform/; proxy_pass http://platform/;
@@ -39,7 +67,7 @@ server {
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
} }
# Workcase 工单系统 # Workcase 工单系统 PC端
location /workcase/ { location /workcase/ {
proxy_pass http://workcase-web/; proxy_pass http://workcase-web/;
proxy_http_version 1.1; proxy_http_version 1.1;
@@ -49,6 +77,26 @@ server {
proxy_set_header X-Forwarded-Proto $scheme; 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 # 默认首页(重定向到 platform
location = / { location = / {
return 302 /platform/; return 302 /platform/;

View File

@@ -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

View File

@@ -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

View File

@@ -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` 目录启动基础设施。

View File

@@ -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"]

View File

@@ -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

View File

@@ -1,180 +1,47 @@
# ================================================ # ================================================
# Level 2: 后端微服务 # Urban Lifeline - All-in-One 部署
# gateway -> system -> file -> auth -> ai -> workcase # 单容器运行所有后端服务
# ================================================ # ================================================
services: services:
# ====================== Gateway 网关服务 ====================== urban-lifeline-serv:
gateway: image: urban-lifeline-serv:${IMAGE_VERSION:-latest}
image: urban-lifeline-gateway:${IMAGE_VERSION:-latest} container_name: urban-lifeline-serv
container_name: urban-lifeline-gateway
restart: unless-stopped restart: unless-stopped
profiles: ["serv", "all"]
networks: networks:
- urban-lifeline - urban-lifeline
ports: 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: environment:
TZ: Asia/Shanghai TZ: Asia/Shanghai
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod} SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod}
NACOS_SERVER_ADDR: ${NACOS_SERVER_ADDR:-nacos:8848} NACOS_SERVER_ADDR: ${NACOS_SERVER_ADDR:-nacos:8848}
NACOS_NAMESPACE: ${NACOS_NAMESPACE:-} NACOS_NAMESPACE: ${NACOS_NAMESPACE:-}
JAVA_OPTS: "-Xms256m -Xmx512m"
volumes: volumes:
- ${LOG_ROOT:-../volumes/logs}/gateway:/app/logs - ${LOG_ROOT:-../../volumes/logs}:/app/logs
depends_on: # 内存限制 (根据服务器配置调整)
nacos: deploy:
condition: service_healthy resources:
limits:
memory: 4G
reservations:
memory: 2G
healthcheck: healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"] test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 3 retries: 3
start_period: 60s start_period: 180s
# ====================== 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
networks: networks:
urban-lifeline: urban-lifeline:

View File

@@ -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

View File

@@ -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;"]

View File

@@ -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"]

View File

@@ -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

View File

@@ -1,47 +1,40 @@
# ================================================ # ================================================
# Level 3: 前端应用 # Urban Lifeline - All-in-One 前端部署
# 每个前端独立 Nginx 容器 # 单容器运行所有前端应用
# platform (8001) / workcase (8002)
# ================================================ # ================================================
services: services:
# ====================== Platform 管理平台 ====================== urban-lifeline-web:
platform: image: urban-lifeline-web:${IMAGE_VERSION:-latest}
image: urban-lifeline-platform:${IMAGE_VERSION:-latest} container_name: urban-lifeline-web
container_name: urban-lifeline-platform
restart: unless-stopped restart: unless-stopped
profiles: ["web", "all"]
networks: networks:
- urban-lifeline - urban-lifeline
expose: ports:
- "80" - "${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: environment:
TZ: Asia/Shanghai 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: healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/"] test: ["CMD", "curl", "-f", "http://localhost:${PLATFORM_PORT:-8001}/"]
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 3 retries: 3
start_period: 10s start_period: 30s
# ====================== 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
networks: networks:
urban-lifeline: urban-lifeline:

View File

@@ -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