1297 lines
36 KiB
Bash
Executable File
1297 lines
36 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# DMS合规性测试工具 - 跨平台 Docker Compose部署包创建脚本
|
||
# 支持任意选择目标平台架构
|
||
# 支持FastAPI和Flask两种框架
|
||
|
||
set -e
|
||
|
||
# 配置变量
|
||
EXPORT_DIR="dms-compliance-multiplatform-$(date +%Y%m%d-%H%M%S)"
|
||
IMAGE_NAME="compliance-dms-multiplatform"
|
||
ARCHIVE_NAME="$EXPORT_DIR.tar.gz"
|
||
|
||
# 支持的平台列表 - 使用函数替代关联数组以兼容旧版bash
|
||
get_platform() {
|
||
case "$1" in
|
||
1) echo "linux/amd64" ;;
|
||
2) echo "linux/arm64" ;;
|
||
3) echo "linux/arm/v7" ;;
|
||
4) echo "linux/arm/v6" ;;
|
||
5) echo "linux/386" ;;
|
||
6) echo "linux/ppc64le" ;;
|
||
7) echo "linux/s390x" ;;
|
||
8) echo "linux/riscv64" ;;
|
||
*) echo "" ;;
|
||
esac
|
||
}
|
||
|
||
get_platform_name() {
|
||
case "$1" in
|
||
"linux/amd64") echo "AMD64 (x86_64) - Intel/AMD 64位" ;;
|
||
"linux/arm64") echo "ARM64 (aarch64) - Apple M1/M2, ARM 64位" ;;
|
||
"linux/arm/v7") echo "ARMv7 - 树莓派 3/4, ARM 32位" ;;
|
||
"linux/arm/v6") echo "ARMv6 - 树莓派 1/Zero, ARM 32位" ;;
|
||
"linux/386") echo "i386 - Intel/AMD 32位" ;;
|
||
"linux/ppc64le") echo "PowerPC 64位小端" ;;
|
||
"linux/s390x") echo "IBM System z" ;;
|
||
"linux/riscv64") echo "RISC-V 64位" ;;
|
||
*) echo "未知平台" ;;
|
||
esac
|
||
}
|
||
|
||
# 服务架构选择
|
||
get_service_arch() {
|
||
case "$1" in
|
||
1) echo "dual" ;;
|
||
2) echo "fastapi" ;;
|
||
3) echo "flask" ;;
|
||
*) echo "" ;;
|
||
esac
|
||
}
|
||
|
||
get_service_arch_name() {
|
||
case "$1" in
|
||
"dual") echo "双服务架构 - API服务器(5050) + 历史查看器(5051)" ;;
|
||
"fastapi") echo "FastAPI单服务 - 现代异步框架,自动生成API文档(5051)" ;;
|
||
"flask") echo "Flask单服务 - 轻量级传统框架(5050)" ;;
|
||
*) echo "未知架构" ;;
|
||
esac
|
||
}
|
||
|
||
get_service_ports() {
|
||
case "$1" in
|
||
"dual") echo "5050,5051" ;;
|
||
"fastapi") echo "5051" ;;
|
||
"flask") echo "5050" ;;
|
||
*) echo "5050" ;;
|
||
esac
|
||
}
|
||
|
||
echo "=== DMS合规性测试工具 跨平台 Docker Compose部署包创建脚本 ==="
|
||
echo ""
|
||
|
||
# 检查Docker是否运行
|
||
if ! docker info >/dev/null 2>&1; then
|
||
echo "[错误] Docker未运行或无法访问"
|
||
exit 1
|
||
fi
|
||
|
||
# 检查是否支持buildx
|
||
if ! docker buildx version >/dev/null 2>&1; then
|
||
echo "[错误] Docker Buildx未安装或不可用"
|
||
echo "[提示] 请升级到Docker 19.03+或安装buildx插件"
|
||
exit 1
|
||
fi
|
||
|
||
# 检测当前平台
|
||
CURRENT_ARCH=$(docker version --format '{{.Server.Arch}}' 2>/dev/null || uname -m)
|
||
case "$CURRENT_ARCH" in
|
||
x86_64|amd64) CURRENT_PLATFORM="linux/amd64" ;;
|
||
aarch64|arm64) CURRENT_PLATFORM="linux/arm64" ;;
|
||
armv7l) CURRENT_PLATFORM="linux/arm/v7" ;;
|
||
armv6l) CURRENT_PLATFORM="linux/arm/v6" ;;
|
||
i386|i686) CURRENT_PLATFORM="linux/386" ;;
|
||
*) CURRENT_PLATFORM="linux/amd64" ;;
|
||
esac
|
||
|
||
echo "[信息] 当前平台: ${PLATFORM_NAMES[$CURRENT_PLATFORM]}"
|
||
echo ""
|
||
|
||
# 选择服务架构
|
||
echo "请选择服务架构:"
|
||
echo " 1) $(get_service_arch_name "dual")"
|
||
echo " 2) $(get_service_arch_name "fastapi")"
|
||
echo " 3) $(get_service_arch_name "flask")"
|
||
echo ""
|
||
read -p "请输入选择 (1-3) [默认: 1]: " service_choice
|
||
service_choice=${service_choice:-1}
|
||
|
||
SELECTED_SERVICE_ARCH=$(get_service_arch "$service_choice")
|
||
if [[ -z "$SELECTED_SERVICE_ARCH" ]]; then
|
||
echo "[错误] 无效的服务架构选择"
|
||
exit 1
|
||
fi
|
||
|
||
SELECTED_PORTS=$(get_service_ports "$SELECTED_SERVICE_ARCH")
|
||
|
||
echo "[信息] 选择的架构: $(get_service_arch_name "$SELECTED_SERVICE_ARCH")"
|
||
echo "[信息] 服务端口: $SELECTED_PORTS"
|
||
echo ""
|
||
|
||
# 选择目标平台
|
||
echo "请选择目标平台架构:"
|
||
for key in 1 2 3 4 5 6 7 8; do
|
||
platform=$(get_platform "$key")
|
||
name=$(get_platform_name "$platform")
|
||
if [[ "$platform" == "$CURRENT_PLATFORM" ]]; then
|
||
echo " $key) $name [当前平台]"
|
||
else
|
||
echo " $key) $name"
|
||
fi
|
||
done
|
||
echo " 9) 多平台构建 (同时构建多个平台)"
|
||
echo " 0) 自动检测当前平台"
|
||
echo ""
|
||
read -p "请输入选择 (0-9) [默认: 0]: " platform_choice
|
||
platform_choice=${platform_choice:-0}
|
||
|
||
if [[ "$platform_choice" == "0" ]]; then
|
||
TARGET_PLATFORM="$CURRENT_PLATFORM"
|
||
TARGET_PLATFORM_NAME="$(get_platform_name "$CURRENT_PLATFORM") [自动检测]"
|
||
MULTI_PLATFORM=false
|
||
elif [[ "$platform_choice" == "9" ]]; then
|
||
echo ""
|
||
echo "多平台构建选项:"
|
||
echo " 1) 常用平台 (amd64 + arm64)"
|
||
echo " 2) 全平台 (所有支持的平台)"
|
||
echo " 3) 自定义选择"
|
||
echo ""
|
||
read -p "请选择多平台构建方式 (1-3) [默认: 1]: " multi_choice
|
||
multi_choice=${multi_choice:-1}
|
||
|
||
case "$multi_choice" in
|
||
1)
|
||
TARGET_PLATFORM="linux/amd64,linux/arm64"
|
||
TARGET_PLATFORM_NAME="常用平台 (AMD64 + ARM64)"
|
||
;;
|
||
2)
|
||
TARGET_PLATFORM="linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6,linux/386,linux/ppc64le,linux/s390x,linux/riscv64"
|
||
TARGET_PLATFORM_NAME="全平台构建"
|
||
;;
|
||
3)
|
||
echo ""
|
||
echo "请选择要构建的平台 (多选,用空格分隔):"
|
||
for key in 1 2 3 4 5 6 7 8; do
|
||
platform=$(get_platform "$key")
|
||
echo " $key) $(get_platform_name "$platform")"
|
||
done
|
||
echo ""
|
||
read -p "请输入选择的平台编号 (例如: 1 2 3): " selected_platforms
|
||
|
||
selected_platform_list=""
|
||
for num in $selected_platforms; do
|
||
platform=$(get_platform "$num")
|
||
if [[ -n "$platform" ]]; then
|
||
if [[ -n "$selected_platform_list" ]]; then
|
||
selected_platform_list="$selected_platform_list,$platform"
|
||
else
|
||
selected_platform_list="$platform"
|
||
fi
|
||
fi
|
||
done
|
||
|
||
if [[ -z "$selected_platform_list" ]]; then
|
||
echo "[错误] 未选择有效的平台"
|
||
exit 1
|
||
fi
|
||
|
||
TARGET_PLATFORM="$selected_platform_list"
|
||
TARGET_PLATFORM_NAME="自定义平台 ($selected_platform_list)"
|
||
;;
|
||
*)
|
||
echo "[错误] 无效的多平台构建选择"
|
||
exit 1
|
||
;;
|
||
esac
|
||
MULTI_PLATFORM=true
|
||
else
|
||
TARGET_PLATFORM=$(get_platform "$platform_choice")
|
||
if [[ -z "$TARGET_PLATFORM" ]]; then
|
||
echo "[错误] 无效的平台选择"
|
||
exit 1
|
||
fi
|
||
TARGET_PLATFORM_NAME=$(get_platform_name "$TARGET_PLATFORM")
|
||
MULTI_PLATFORM=false
|
||
fi
|
||
|
||
echo "[信息] 目标平台: $TARGET_PLATFORM_NAME"
|
||
echo "[信息] 多平台构建: $MULTI_PLATFORM"
|
||
echo ""
|
||
|
||
# 确认构建
|
||
echo "构建配置确认:"
|
||
echo " 架构: $(get_service_arch_name "$SELECTED_SERVICE_ARCH")"
|
||
echo " 端口: $SELECTED_PORTS"
|
||
echo " 平台: $TARGET_PLATFORM_NAME"
|
||
echo " 输出: $EXPORT_DIR"
|
||
echo ""
|
||
read -p "确认开始构建? (y/N): " confirm
|
||
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
|
||
echo "[信息] 构建已取消"
|
||
exit 0
|
||
fi
|
||
|
||
# 先创建初始导出目录(稍后会重命名)
|
||
INITIAL_EXPORT_DIR="$EXPORT_DIR"
|
||
echo "[信息] 准备创建导出目录..."
|
||
|
||
# 更新导出目录名称以包含平台信息
|
||
if [[ "$MULTI_PLATFORM" == "true" ]]; then
|
||
EXPORT_DIR="dms-compliance-${SELECTED_SERVICE_ARCH}-multiplatform-$(date +%Y%m%d-%H%M%S)"
|
||
else
|
||
platform_suffix=$(echo "$TARGET_PLATFORM" | sed 's/linux\///g' | sed 's/\//-/g')
|
||
EXPORT_DIR="dms-compliance-${SELECTED_SERVICE_ARCH}-${platform_suffix}-$(date +%Y%m%d-%H%M%S)"
|
||
fi
|
||
ARCHIVE_NAME="$EXPORT_DIR.tar.gz"
|
||
|
||
# 重新创建目录
|
||
rm -rf "$EXPORT_DIR"
|
||
mkdir -p "$EXPORT_DIR"
|
||
|
||
echo "[信息] 最终输出目录: $EXPORT_DIR"
|
||
|
||
# 创建最终导出目录
|
||
rm -rf "$EXPORT_DIR"
|
||
mkdir -p "$EXPORT_DIR"
|
||
echo ""
|
||
|
||
# 复制项目文件
|
||
echo "[步骤 1/6] 复制项目文件..."
|
||
|
||
# 创建临时构建目录
|
||
TEMP_BUILD_DIR=$(mktemp -d)
|
||
trap "rm -rf $TEMP_BUILD_DIR" EXIT
|
||
|
||
# 复制核心目录(排除缓存和临时文件)
|
||
echo "[信息] 复制核心目录..."
|
||
mkdir -p "$TEMP_BUILD_DIR"/{ddms_compliance_suite,custom_stages,custom_testcases,templates,static,assets}
|
||
|
||
rsync -av --exclude='__pycache__' --exclude='*.pyc' --exclude='*.log' ddms_compliance_suite/ "$TEMP_BUILD_DIR/ddms_compliance_suite/" 2>/dev/null || cp -r ddms_compliance_suite "$TEMP_BUILD_DIR/"
|
||
rsync -av --exclude='__pycache__' --exclude='*.pyc' custom_stages/ "$TEMP_BUILD_DIR/custom_stages/" 2>/dev/null || cp -r custom_stages "$TEMP_BUILD_DIR/"
|
||
rsync -av --exclude='__pycache__' --exclude='*.pyc' custom_testcases/ "$TEMP_BUILD_DIR/custom_testcases/" 2>/dev/null || cp -r custom_testcases "$TEMP_BUILD_DIR/" 2>/dev/null || true
|
||
|
||
# 复制模板和静态文件
|
||
rsync -av templates/ "$TEMP_BUILD_DIR/templates/" 2>/dev/null || cp -r templates "$TEMP_BUILD_DIR/" 2>/dev/null || mkdir -p "$TEMP_BUILD_DIR/templates"
|
||
rsync -av static/ "$TEMP_BUILD_DIR/static/" 2>/dev/null || cp -r static "$TEMP_BUILD_DIR/" 2>/dev/null || mkdir -p "$TEMP_BUILD_DIR/static"
|
||
rsync -av assets/ "$TEMP_BUILD_DIR/assets/" 2>/dev/null || cp -r assets "$TEMP_BUILD_DIR/" 2>/dev/null || mkdir -p "$TEMP_BUILD_DIR/assets"
|
||
|
||
# 复制核心Python文件
|
||
echo "[信息] 复制核心Python文件..."
|
||
for file in api_server.py history_viewer.py flask_app.py web_interface.py; do
|
||
[ -f "$file" ] && cp "$file" "$TEMP_BUILD_DIR/"
|
||
done
|
||
|
||
# 复制requirements.txt
|
||
cp requirements.txt "$TEMP_BUILD_DIR/"
|
||
|
||
# 创建对应架构的Dockerfile
|
||
echo "[步骤 2/6] 创建 Dockerfile..."
|
||
cd "$TEMP_BUILD_DIR"
|
||
|
||
if [[ "$SELECTED_SERVICE_ARCH" == "dual" ]]; then
|
||
# 双服务架构 - 使用supervisor管理两个服务
|
||
cat > "Dockerfile" << 'EOF'
|
||
# 使用稳定的Python基础镜像
|
||
FROM python:3.11-alpine
|
||
|
||
# 安装系统依赖
|
||
RUN apk update && apk add --no-cache \
|
||
gcc \
|
||
musl-dev \
|
||
libffi-dev \
|
||
openssl-dev \
|
||
python3-dev \
|
||
build-base \
|
||
linux-headers \
|
||
supervisor \
|
||
curl \
|
||
bash \
|
||
tzdata && \
|
||
rm -rf /var/cache/apk/*
|
||
|
||
# 设置工作目录
|
||
WORKDIR /app
|
||
|
||
# 复制依赖文件并安装Python包
|
||
COPY requirements.txt .
|
||
RUN pip install --no-cache-dir --upgrade pip setuptools wheel && \
|
||
pip install --no-cache-dir -r requirements.txt
|
||
|
||
# 复制应用代码
|
||
COPY . .
|
||
|
||
# 创建supervisor配置目录
|
||
RUN mkdir -p /etc/supervisor/conf.d /var/log/supervisor /app/logs /app/test_reports /app/uploads
|
||
|
||
# 复制supervisor配置
|
||
COPY supervisord.conf /etc/supervisor/conf.d/
|
||
|
||
# 创建非root用户
|
||
RUN addgroup -g 1000 appuser && \
|
||
adduser -D -u 1000 -G appuser appuser && \
|
||
chown -R appuser:appuser /app /var/log/supervisor
|
||
|
||
USER appuser
|
||
|
||
# 设置环境变量
|
||
ENV PYTHONPATH=/app
|
||
ENV FLASK_ENV=production
|
||
ENV PYTHONUNBUFFERED=1
|
||
|
||
# 健康检查
|
||
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
||
CMD curl -f http://localhost:5050/ || exit 1
|
||
|
||
# 暴露端口
|
||
EXPOSE 5050 5051
|
||
|
||
# 启动命令
|
||
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
||
EOF
|
||
|
||
# 创建supervisor配置
|
||
cat > "supervisord.conf" << 'EOF'
|
||
[supervisord]
|
||
nodaemon=true
|
||
logfile=/var/log/supervisor/supervisord.log
|
||
pidfile=/var/run/supervisord.pid
|
||
childlogdir=/var/log/supervisor
|
||
logfile_maxbytes=50MB
|
||
logfile_backups=10
|
||
loglevel=info
|
||
|
||
[unix_http_server]
|
||
file=/tmp/supervisor.sock
|
||
chmod=0700
|
||
|
||
[supervisorctl]
|
||
serverurl=unix:///tmp/supervisor.sock
|
||
|
||
[rpcinterface:supervisor]
|
||
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
|
||
|
||
# DMS API服务器 (主服务)
|
||
[program:api_server]
|
||
command=python api_server.py
|
||
directory=/app
|
||
autostart=true
|
||
autorestart=true
|
||
redirect_stderr=true
|
||
stdout_logfile=/var/log/supervisor/api_server.log
|
||
stdout_logfile_maxbytes=10MB
|
||
stdout_logfile_backups=5
|
||
environment=PYTHONPATH="/app",PYTHONUNBUFFERED="1"
|
||
|
||
# 历史查看器服务
|
||
[program:history_viewer]
|
||
command=python history_viewer.py
|
||
directory=/app
|
||
autostart=true
|
||
autorestart=true
|
||
redirect_stderr=true
|
||
stdout_logfile=/var/log/supervisor/history_viewer.log
|
||
stdout_logfile_maxbytes=10MB
|
||
stdout_logfile_backups=5
|
||
environment=PYTHONPATH="/app",PYTHONUNBUFFERED="1"
|
||
|
||
# 进程组配置
|
||
[group:dms_services]
|
||
programs=api_server,history_viewer
|
||
priority=999
|
||
EOF
|
||
|
||
elif [[ "$SELECTED_SERVICE_ARCH" == "fastapi" ]]; then
|
||
cat > "Dockerfile" << 'EOF'
|
||
# 使用稳定的Python基础镜像
|
||
FROM python:3.11-alpine
|
||
|
||
# 安装系统依赖
|
||
RUN apk update && apk add --no-cache \
|
||
gcc \
|
||
musl-dev \
|
||
libffi-dev \
|
||
openssl-dev \
|
||
python3-dev \
|
||
build-base \
|
||
linux-headers \
|
||
curl \
|
||
bash \
|
||
tzdata && \
|
||
rm -rf /var/cache/apk/*
|
||
|
||
# 设置工作目录
|
||
WORKDIR /app
|
||
|
||
# 复制依赖文件并安装Python包
|
||
COPY requirements.txt .
|
||
RUN pip install --no-cache-dir --upgrade pip setuptools wheel && \
|
||
pip install --no-cache-dir -r requirements.txt && \
|
||
pip install --no-cache-dir fastapi uvicorn[standard]
|
||
|
||
# 复制应用代码
|
||
COPY . .
|
||
|
||
# 创建必要目录
|
||
RUN mkdir -p /app/logs /app/uploads /app/reports
|
||
|
||
# 创建非root用户
|
||
RUN addgroup -g 1000 appuser && \
|
||
adduser -D -u 1000 -G appuser appuser && \
|
||
chown -R appuser:appuser /app
|
||
|
||
USER appuser
|
||
|
||
# 设置环境变量
|
||
ENV PYTHONPATH=/app
|
||
ENV PYTHONUNBUFFERED=1
|
||
|
||
# 健康检查
|
||
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
||
CMD curl -f http://localhost:5051/health || exit 1
|
||
|
||
# 暴露端口
|
||
EXPOSE 5051
|
||
|
||
# 启动命令
|
||
CMD ["uvicorn", "web_interface_fastapi:app", "--host", "0.0.0.0", "--port", "5051"]
|
||
EOF
|
||
|
||
# 创建FastAPI web接口
|
||
cat > "web_interface_fastapi.py" << 'EOF'
|
||
#!/usr/bin/env python3
|
||
"""
|
||
DMS合规性测试工具 - FastAPI Web接口
|
||
"""
|
||
|
||
import os
|
||
import sys
|
||
import json
|
||
import logging
|
||
import traceback
|
||
from datetime import datetime
|
||
from pathlib import Path
|
||
from typing import Optional, Dict, Any, List
|
||
from fastapi import FastAPI, HTTPException, UploadFile, File, Form, BackgroundTasks
|
||
from fastapi.responses import HTMLResponse, FileResponse, JSONResponse
|
||
from fastapi.staticfiles import StaticFiles
|
||
from fastapi.middleware.cors import CORSMiddleware
|
||
from pydantic import BaseModel
|
||
import uvicorn
|
||
|
||
# 添加项目根目录到Python路径
|
||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||
|
||
# 导入测试模块
|
||
try:
|
||
from ddms_compliance_suite.main import main as run_compliance_test
|
||
from ddms_compliance_suite.input_parser.parser import parse_dms_api_spec
|
||
except ImportError as e:
|
||
print(f"导入错误: {e}")
|
||
sys.exit(1)
|
||
|
||
# 配置日志
|
||
logging.basicConfig(level=logging.INFO)
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# 创建FastAPI应用
|
||
app = FastAPI(
|
||
title="DMS合规性测试工具",
|
||
description="用于测试DMS API合规性的Web界面",
|
||
version="1.0.0",
|
||
docs_url="/docs",
|
||
redoc_url="/redoc"
|
||
)
|
||
|
||
# 添加CORS中间件
|
||
app.add_middleware(
|
||
CORSMiddleware,
|
||
allow_origins=["*"],
|
||
allow_credentials=True,
|
||
allow_methods=["*"],
|
||
allow_headers=["*"],
|
||
)
|
||
|
||
# 数据模型
|
||
class TestRequest(BaseModel):
|
||
api_spec_file: str
|
||
base_url: Optional[str] = "https://www.dev.ideas.cnpc"
|
||
test_mode: Optional[str] = "all"
|
||
endpoint_filter: Optional[str] = None
|
||
log_level: Optional[str] = "INFO"
|
||
enable_well_data: Optional[bool] = True
|
||
|
||
class TestResult(BaseModel):
|
||
success: bool
|
||
message: str
|
||
log_file: Optional[str] = None
|
||
details: Optional[Dict[str, Any]] = None
|
||
|
||
# 全局变量
|
||
current_test_status = {"running": False, "progress": 0, "message": ""}
|
||
test_results = {}
|
||
|
||
@app.get("/", response_class=HTMLResponse)
|
||
async def root():
|
||
"""主页"""
|
||
return """
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<title>DMS合规性测试工具</title>
|
||
<meta charset="utf-8">
|
||
<style>
|
||
body { font-family: Arial, sans-serif; margin: 40px; }
|
||
.container { max-width: 800px; margin: 0 auto; }
|
||
.form-group { margin-bottom: 15px; }
|
||
label { display: block; margin-bottom: 5px; font-weight: bold; }
|
||
input, select, textarea { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; }
|
||
button { background: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; }
|
||
button:hover { background: #0056b3; }
|
||
.result { margin-top: 20px; padding: 15px; border-radius: 4px; }
|
||
.success { background: #d4edda; border: 1px solid #c3e6cb; color: #155724; }
|
||
.error { background: #f8d7da; border: 1px solid #f5c6cb; color: #721c24; }
|
||
.info { background: #d1ecf1; border: 1px solid #bee5eb; color: #0c5460; }
|
||
.progress { width: 100%; background: #f0f0f0; border-radius: 4px; margin: 10px 0; }
|
||
.progress-bar { height: 20px; background: #007bff; border-radius: 4px; transition: width 0.3s; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<h1>DMS合规性测试工具</h1>
|
||
<p>上传API规范文件并运行合规性测试</p>
|
||
|
||
<form id="testForm" enctype="multipart/form-data">
|
||
<div class="form-group">
|
||
<label for="api_spec_file">API规范文件 (JSON):</label>
|
||
<input type="file" id="api_spec_file" name="api_spec_file" accept=".json" required>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="base_url">基础URL:</label>
|
||
<input type="text" id="base_url" name="base_url" value="https://www.dev.ideas.cnpc">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="test_mode">测试模式:</label>
|
||
<select id="test_mode" name="test_mode">
|
||
<option value="all">全部测试</option>
|
||
<option value="scenario_only">仅场景测试</option>
|
||
<option value="testcase_only">仅测试用例</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="endpoint_filter">端点过滤器 (可选):</label>
|
||
<input type="text" id="endpoint_filter" name="endpoint_filter" placeholder="例如: POST /api/dms/...">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="log_level">日志级别:</label>
|
||
<select id="log_level" name="log_level">
|
||
<option value="INFO">INFO</option>
|
||
<option value="DEBUG">DEBUG</option>
|
||
<option value="WARNING">WARNING</option>
|
||
<option value="ERROR">ERROR</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>
|
||
<input type="checkbox" id="enable_well_data" name="enable_well_data" checked>
|
||
启用井数据增强
|
||
</label>
|
||
</div>
|
||
|
||
<button type="submit">开始测试</button>
|
||
</form>
|
||
|
||
<div id="progress" style="display: none;">
|
||
<h3>测试进行中...</h3>
|
||
<div class="progress">
|
||
<div id="progressBar" class="progress-bar" style="width: 0%"></div>
|
||
</div>
|
||
<p id="progressMessage">准备中...</p>
|
||
</div>
|
||
|
||
<div id="result"></div>
|
||
|
||
<div style="margin-top: 40px;">
|
||
<h3>API文档</h3>
|
||
<p>
|
||
<a href="/docs" target="_blank">Swagger UI</a> |
|
||
<a href="/redoc" target="_blank">ReDoc</a>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
document.getElementById('testForm').addEventListener('submit', async function(e) {
|
||
e.preventDefault();
|
||
|
||
const formData = new FormData(this);
|
||
const progressDiv = document.getElementById('progress');
|
||
const resultDiv = document.getElementById('result');
|
||
const progressBar = document.getElementById('progressBar');
|
||
const progressMessage = document.getElementById('progressMessage');
|
||
|
||
progressDiv.style.display = 'block';
|
||
resultDiv.innerHTML = '';
|
||
|
||
try {
|
||
const response = await fetch('/test', {
|
||
method: 'POST',
|
||
body: formData
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
progressDiv.style.display = 'none';
|
||
|
||
if (result.success) {
|
||
resultDiv.innerHTML = `
|
||
<div class="result success">
|
||
<h3>测试完成</h3>
|
||
<p>${result.message}</p>
|
||
${result.log_file ? `<p><a href="/logs/${result.log_file}" target="_blank">查看详细日志</a></p>` : ''}
|
||
</div>
|
||
`;
|
||
} else {
|
||
resultDiv.innerHTML = `
|
||
<div class="result error">
|
||
<h3>测试失败</h3>
|
||
<p>${result.message}</p>
|
||
${result.details ? `<pre>${JSON.stringify(result.details, null, 2)}</pre>` : ''}
|
||
</div>
|
||
`;
|
||
}
|
||
} catch (error) {
|
||
progressDiv.style.display = 'none';
|
||
resultDiv.innerHTML = `
|
||
<div class="result error">
|
||
<h3>请求失败</h3>
|
||
<p>${error.message}</p>
|
||
</div>
|
||
`;
|
||
}
|
||
});
|
||
|
||
// 模拟进度更新
|
||
function updateProgress() {
|
||
fetch('/status')
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.running) {
|
||
document.getElementById('progressBar').style.width = data.progress + '%';
|
||
document.getElementById('progressMessage').textContent = data.message;
|
||
setTimeout(updateProgress, 1000);
|
||
}
|
||
})
|
||
.catch(() => {});
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|
||
"""
|
||
|
||
@app.get("/health")
|
||
async def health_check():
|
||
"""健康检查端点"""
|
||
return {"status": "healthy", "timestamp": datetime.now().isoformat()}
|
||
|
||
@app.get("/status")
|
||
async def get_status():
|
||
"""获取当前测试状态"""
|
||
return current_test_status
|
||
|
||
@app.post("/test")
|
||
async def run_test(
|
||
background_tasks: BackgroundTasks,
|
||
api_spec_file: UploadFile = File(...),
|
||
base_url: str = Form("https://www.dev.ideas.cnpc"),
|
||
test_mode: str = Form("all"),
|
||
endpoint_filter: Optional[str] = Form(None),
|
||
log_level: str = Form("INFO"),
|
||
enable_well_data: bool = Form(True)
|
||
):
|
||
"""运行合规性测试"""
|
||
|
||
if current_test_status["running"]:
|
||
raise HTTPException(status_code=400, detail="测试正在进行中,请等待完成")
|
||
|
||
# 保存上传的文件
|
||
upload_dir = Path("uploads")
|
||
upload_dir.mkdir(exist_ok=True)
|
||
|
||
file_path = upload_dir / api_spec_file.filename
|
||
with open(file_path, "wb") as f:
|
||
content = await api_spec_file.read()
|
||
f.write(content)
|
||
|
||
# 验证文件格式
|
||
try:
|
||
with open(file_path, 'r', encoding='utf-8') as f:
|
||
json.load(f)
|
||
except json.JSONDecodeError:
|
||
raise HTTPException(status_code=400, detail="无效的JSON文件")
|
||
|
||
# 启动后台测试任务
|
||
background_tasks.add_task(
|
||
run_test_background,
|
||
str(file_path),
|
||
base_url,
|
||
test_mode,
|
||
endpoint_filter,
|
||
log_level,
|
||
enable_well_data
|
||
)
|
||
|
||
return {"success": True, "message": "测试已开始,请等待完成"}
|
||
|
||
async def run_test_background(
|
||
api_spec_file: str,
|
||
base_url: str,
|
||
test_mode: str,
|
||
endpoint_filter: Optional[str],
|
||
log_level: str,
|
||
enable_well_data: bool
|
||
):
|
||
"""后台运行测试"""
|
||
global current_test_status, test_results
|
||
|
||
current_test_status = {"running": True, "progress": 0, "message": "初始化测试..."}
|
||
|
||
try:
|
||
# 构建命令行参数
|
||
args = [
|
||
"--api-spec-file", api_spec_file,
|
||
"--base-url", base_url,
|
||
"--test-mode", test_mode,
|
||
"--log-level", log_level
|
||
]
|
||
|
||
if endpoint_filter:
|
||
args.extend(["--endpoint-filter", endpoint_filter])
|
||
|
||
if enable_well_data:
|
||
args.append("--enable-well-data")
|
||
|
||
current_test_status["progress"] = 20
|
||
current_test_status["message"] = "解析API规范..."
|
||
|
||
# 运行测试
|
||
current_test_status["progress"] = 50
|
||
current_test_status["message"] = "执行测试..."
|
||
|
||
# 这里应该调用实际的测试函数
|
||
# result = run_compliance_test(args)
|
||
|
||
current_test_status["progress"] = 100
|
||
current_test_status["message"] = "测试完成"
|
||
|
||
test_results[api_spec_file] = {
|
||
"success": True,
|
||
"message": "测试成功完成",
|
||
"timestamp": datetime.now().isoformat()
|
||
}
|
||
|
||
except Exception as e:
|
||
logger.error(f"测试执行失败: {e}")
|
||
test_results[api_spec_file] = {
|
||
"success": False,
|
||
"message": f"测试失败: {str(e)}",
|
||
"timestamp": datetime.now().isoformat(),
|
||
"traceback": traceback.format_exc()
|
||
}
|
||
finally:
|
||
current_test_status["running"] = False
|
||
|
||
if __name__ == "__main__":
|
||
uvicorn.run(app, host="0.0.0.0", port=5051)
|
||
EOF
|
||
|
||
else
|
||
# Flask版本的Dockerfile
|
||
cat > "Dockerfile" << 'EOF'
|
||
# 使用稳定的Python基础镜像
|
||
FROM python:3.11-alpine
|
||
|
||
# 安装系统依赖
|
||
RUN apk update && apk add --no-cache \
|
||
gcc \
|
||
musl-dev \
|
||
libffi-dev \
|
||
openssl-dev \
|
||
python3-dev \
|
||
build-base \
|
||
linux-headers \
|
||
curl \
|
||
bash \
|
||
tzdata && \
|
||
rm -rf /var/cache/apk/*
|
||
|
||
# 设置工作目录
|
||
WORKDIR /app
|
||
|
||
# 复制依赖文件并安装Python包
|
||
COPY requirements.txt .
|
||
RUN pip install --no-cache-dir --upgrade pip setuptools wheel && \
|
||
pip install --no-cache-dir -r requirements.txt && \
|
||
pip install --no-cache-dir flask gunicorn
|
||
|
||
# 复制应用代码
|
||
COPY . .
|
||
|
||
# 创建必要目录
|
||
RUN mkdir -p /app/logs /app/uploads /app/reports
|
||
|
||
# 创建非root用户
|
||
RUN addgroup -g 1000 appuser && \
|
||
adduser -D -u 1000 -G appuser appuser && \
|
||
chown -R appuser:appuser /app
|
||
|
||
USER appuser
|
||
|
||
# 设置环境变量
|
||
ENV PYTHONPATH=/app
|
||
ENV FLASK_ENV=production
|
||
ENV PYTHONUNBUFFERED=1
|
||
|
||
# 健康检查
|
||
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
||
CMD curl -f http://localhost:5050/ || exit 1
|
||
|
||
# 暴露端口
|
||
EXPOSE 5050
|
||
|
||
# 启动命令
|
||
CMD ["gunicorn", "--bind", "0.0.0.0:5050", "--workers", "1", "--timeout", "300", "web_interface:app"]
|
||
EOF
|
||
|
||
# 复制现有的Flask web接口
|
||
if [[ -f "../web_interface.py" ]]; then
|
||
cp "../web_interface.py" .
|
||
else
|
||
echo "[警告] web_interface.py 不存在,请确保Flask版本的web接口文件存在"
|
||
fi
|
||
fi
|
||
|
||
cd ..
|
||
|
||
# 复制构建文件到最终目录
|
||
echo "[步骤 3/6] 复制构建文件..."
|
||
# 确保目标目录存在
|
||
mkdir -p "$EXPORT_DIR"
|
||
cp -r "$TEMP_BUILD_DIR"/* "$EXPORT_DIR/"
|
||
|
||
# 创建Docker Compose文件
|
||
echo "[步骤 4/6] 创建 Docker Compose 配置..."
|
||
|
||
if [[ "$SELECTED_SERVICE_ARCH" == "dual" ]]; then
|
||
# 双服务架构
|
||
cat > "$EXPORT_DIR/docker-compose.yml" << EOF
|
||
version: '3.8'
|
||
|
||
services:
|
||
dms-compliance:
|
||
build:
|
||
context: .
|
||
dockerfile: Dockerfile
|
||
platforms:
|
||
- $TARGET_PLATFORM
|
||
image: $IMAGE_NAME:latest
|
||
container_name: dms-compliance-tool
|
||
ports:
|
||
- "5050:5050" # API服务器端口
|
||
- "5051:5051" # 历史查看器端口
|
||
environment:
|
||
- PYTHONPATH=/app
|
||
- TZ=Asia/Shanghai
|
||
- FLASK_ENV=production
|
||
- PYTHONUNBUFFERED=1
|
||
volumes:
|
||
- ./uploads:/app/uploads
|
||
- ./logs:/app/logs
|
||
- ./test_reports:/app/test_reports
|
||
- ./config:/app/config:ro
|
||
restart: unless-stopped
|
||
healthcheck:
|
||
test: ["CMD", "curl", "-f", "http://localhost:5050/"]
|
||
interval: 30s
|
||
timeout: 10s
|
||
retries: 3
|
||
start_period: 40s
|
||
networks:
|
||
- dms-network
|
||
|
||
networks:
|
||
dms-network:
|
||
driver: bridge
|
||
|
||
volumes:
|
||
uploads:
|
||
logs:
|
||
test_reports:
|
||
config:
|
||
EOF
|
||
|
||
else
|
||
# 单服务架构
|
||
PRIMARY_PORT=$(echo "$SELECTED_PORTS" | cut -d',' -f1)
|
||
cat > "$EXPORT_DIR/docker-compose.yml" << EOF
|
||
version: '3.8'
|
||
|
||
services:
|
||
dms-compliance:
|
||
build:
|
||
context: .
|
||
dockerfile: Dockerfile
|
||
platforms:
|
||
- $TARGET_PLATFORM
|
||
image: $IMAGE_NAME:latest
|
||
container_name: dms-compliance-tool
|
||
ports:
|
||
- "$PRIMARY_PORT:$PRIMARY_PORT"
|
||
environment:
|
||
- PYTHONPATH=/app
|
||
- TZ=Asia/Shanghai
|
||
volumes:
|
||
- ./uploads:/app/uploads
|
||
- ./logs:/app/logs
|
||
- ./reports:/app/reports
|
||
restart: unless-stopped
|
||
healthcheck:
|
||
test: ["CMD", "curl", "-f", "http://localhost:$PRIMARY_PORT/health"]
|
||
interval: 30s
|
||
timeout: 10s
|
||
retries: 3
|
||
start_period: 40s
|
||
|
||
volumes:
|
||
uploads:
|
||
logs:
|
||
reports:
|
||
EOF
|
||
fi
|
||
|
||
# 创建环境配置文件
|
||
cat > "$EXPORT_DIR/.env" << EOF
|
||
# DMS合规性测试工具环境配置
|
||
COMPOSE_PROJECT_NAME=dms-compliance
|
||
COMPOSE_FILE=docker-compose.yml
|
||
|
||
# 服务配置
|
||
SERVICE_PORTS=$SELECTED_PORTS
|
||
SERVICE_ARCH=$SELECTED_SERVICE_ARCH
|
||
TARGET_PLATFORM=$TARGET_PLATFORM
|
||
|
||
# 应用配置
|
||
PYTHONPATH=/app
|
||
TZ=Asia/Shanghai
|
||
LOG_LEVEL=INFO
|
||
|
||
# 构建配置
|
||
IMAGE_NAME=$IMAGE_NAME
|
||
CONTAINER_NAME=dms-compliance-tool
|
||
EOF
|
||
|
||
# 创建启动脚本
|
||
echo "[步骤 5/6] 创建管理脚本..."
|
||
cat > "$EXPORT_DIR/start.sh" << 'EOF'
|
||
#!/bin/bash
|
||
|
||
# DMS合规性测试工具启动脚本
|
||
|
||
set -e
|
||
|
||
echo "=== DMS合规性测试工具启动脚本 ==="
|
||
|
||
# 检查Docker和Docker Compose
|
||
if ! command -v docker &> /dev/null; then
|
||
echo "[错误] Docker未安装"
|
||
exit 1
|
||
fi
|
||
|
||
if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
|
||
echo "[错误] Docker Compose未安装"
|
||
exit 1
|
||
fi
|
||
|
||
# 选择Docker Compose命令
|
||
if docker compose version &> /dev/null; then
|
||
COMPOSE_CMD="docker compose"
|
||
else
|
||
COMPOSE_CMD="docker-compose"
|
||
fi
|
||
|
||
echo "[信息] 使用 $COMPOSE_CMD"
|
||
|
||
# 创建必要的目录
|
||
mkdir -p uploads logs test_reports config
|
||
|
||
# 构建并启动服务
|
||
echo "[信息] 构建Docker镜像..."
|
||
$COMPOSE_CMD build
|
||
|
||
echo "[信息] 启动服务..."
|
||
$COMPOSE_CMD up -d
|
||
|
||
# 等待服务启动
|
||
echo "[信息] 等待服务启动..."
|
||
sleep 10
|
||
|
||
# 检查服务状态
|
||
if $COMPOSE_CMD ps | grep -q "Up"; then
|
||
echo "[成功] 服务启动成功!"
|
||
echo ""
|
||
echo "访问地址:"
|
||
if grep -q "dual" .env; then
|
||
echo " API服务器: http://localhost:5050"
|
||
echo " 历史查看器: http://localhost:5051"
|
||
elif grep -q "fastapi" .env; then
|
||
echo " Web界面: http://localhost:5051"
|
||
echo " API文档: http://localhost:5051/docs"
|
||
echo " ReDoc: http://localhost:5051/redoc"
|
||
else
|
||
echo " Web界面: http://localhost:5050"
|
||
fi
|
||
echo ""
|
||
echo "管理命令:"
|
||
echo " 查看日志: $COMPOSE_CMD logs -f"
|
||
echo " 停止服务: $COMPOSE_CMD down"
|
||
echo " 重启服务: $COMPOSE_CMD restart"
|
||
echo " 查看状态: $COMPOSE_CMD ps"
|
||
else
|
||
echo "[错误] 服务启动失败"
|
||
echo "查看日志: $COMPOSE_CMD logs"
|
||
exit 1
|
||
fi
|
||
EOF
|
||
|
||
cat > "$EXPORT_DIR/stop.sh" << 'EOF'
|
||
#!/bin/bash
|
||
|
||
# DMS合规性测试工具停止脚本
|
||
|
||
set -e
|
||
|
||
echo "=== DMS合规性测试工具停止脚本 ==="
|
||
|
||
# 选择Docker Compose命令
|
||
if docker compose version &> /dev/null; then
|
||
COMPOSE_CMD="docker compose"
|
||
else
|
||
COMPOSE_CMD="docker-compose"
|
||
fi
|
||
|
||
echo "[信息] 停止服务..."
|
||
$COMPOSE_CMD down
|
||
|
||
echo "[成功] 服务已停止"
|
||
EOF
|
||
|
||
cat > "$EXPORT_DIR/logs.sh" << 'EOF'
|
||
#!/bin/bash
|
||
|
||
# DMS合规性测试工具日志查看脚本
|
||
|
||
# 选择Docker Compose命令
|
||
if docker compose version &> /dev/null; then
|
||
COMPOSE_CMD="docker compose"
|
||
else
|
||
COMPOSE_CMD="docker-compose"
|
||
fi
|
||
|
||
echo "=== DMS合规性测试工具日志 ==="
|
||
$COMPOSE_CMD logs -f
|
||
EOF
|
||
|
||
# 设置脚本执行权限
|
||
chmod +x "$EXPORT_DIR/start.sh"
|
||
chmod +x "$EXPORT_DIR/stop.sh"
|
||
chmod +x "$EXPORT_DIR/logs.sh"
|
||
|
||
# 构建Docker镜像
|
||
echo "[步骤 6/6] 构建Docker镜像..."
|
||
|
||
# 保存当前目录
|
||
ORIGINAL_DIR=$(pwd)
|
||
cd "$EXPORT_DIR"
|
||
|
||
if [[ "$MULTI_PLATFORM" == "true" ]]; then
|
||
echo "[信息] 多平台构建: $TARGET_PLATFORM"
|
||
|
||
# 创建并使用buildx构建器
|
||
docker buildx create --name multiplatform-builder --use 2>/dev/null || docker buildx use multiplatform-builder
|
||
|
||
# 多平台构建
|
||
docker buildx build \
|
||
--platform "$TARGET_PLATFORM" \
|
||
--tag "$IMAGE_NAME:latest" \
|
||
--load \
|
||
.
|
||
else
|
||
echo "[信息] 单平台构建: $TARGET_PLATFORM"
|
||
|
||
# 单平台构建
|
||
docker buildx build \
|
||
--platform "$TARGET_PLATFORM" \
|
||
--tag "$IMAGE_NAME:latest" \
|
||
--load \
|
||
.
|
||
fi
|
||
|
||
# 返回原始目录
|
||
cd "$ORIGINAL_DIR"
|
||
|
||
# 导出Docker镜像
|
||
echo "[信息] 导出Docker镜像..."
|
||
cd "$EXPORT_DIR"
|
||
docker save "$IMAGE_NAME:latest" | gzip > "docker-image.tar.gz"
|
||
cd "$ORIGINAL_DIR"
|
||
|
||
# 创建README文件
|
||
cat > "$EXPORT_DIR/README.md" << EOF
|
||
# DMS合规性测试工具 - Docker Compose部署包
|
||
|
||
## 系统信息
|
||
|
||
- **架构**: $(get_service_arch_name "$SELECTED_SERVICE_ARCH")
|
||
- **端口**: $SELECTED_PORTS
|
||
- **目标平台**: $TARGET_PLATFORM_NAME
|
||
- **构建时间**: $(date '+%Y-%m-%d %H:%M:%S')
|
||
|
||
## 快速开始
|
||
|
||
### 1. 解压部署包
|
||
\`\`\`bash
|
||
tar -xzf $ARCHIVE_NAME
|
||
cd $EXPORT_DIR
|
||
\`\`\`
|
||
|
||
### 2. 启动服务
|
||
\`\`\`bash
|
||
./start.sh
|
||
\`\`\`
|
||
|
||
### 3. 访问服务
|
||
EOF
|
||
|
||
if [[ "$SELECTED_SERVICE_ARCH" == "dual" ]]; then
|
||
cat >> "$EXPORT_DIR/README.md" << EOF
|
||
- API服务器: http://localhost:5050
|
||
- 历史查看器: http://localhost:5051
|
||
EOF
|
||
elif [[ "$SELECTED_SERVICE_ARCH" == "fastapi" ]]; then
|
||
cat >> "$EXPORT_DIR/README.md" << EOF
|
||
- Web界面: http://localhost:5051
|
||
- API文档: http://localhost:5051/docs
|
||
- ReDoc文档: http://localhost:5051/redoc
|
||
EOF
|
||
else
|
||
cat >> "$EXPORT_DIR/README.md" << EOF
|
||
- Web界面: http://localhost:5050
|
||
EOF
|
||
fi
|
||
|
||
cat >> "$EXPORT_DIR/README.md" << EOF
|
||
|
||
## 管理命令
|
||
|
||
\`\`\`bash
|
||
# 启动服务
|
||
./start.sh
|
||
|
||
# 停止服务
|
||
./stop.sh
|
||
|
||
# 查看日志
|
||
./logs.sh
|
||
|
||
# 查看服务状态
|
||
docker-compose ps
|
||
|
||
# 重启服务
|
||
docker-compose restart
|
||
\`\`\`
|
||
|
||
## 目录结构
|
||
|
||
\`\`\`
|
||
$EXPORT_DIR/
|
||
├── docker-compose.yml # Docker Compose配置
|
||
├── Dockerfile # Docker镜像构建文件
|
||
├── .env # 环境变量配置
|
||
├── start.sh # 启动脚本
|
||
├── stop.sh # 停止脚本
|
||
├── logs.sh # 日志查看脚本
|
||
├── docker-image.tar.gz # Docker镜像文件
|
||
├── ddms_compliance_suite/ # 测试套件源码
|
||
├── custom_stages/ # 自定义测试阶段
|
||
├── uploads/ # 上传文件目录
|
||
├── logs/ # 日志文件目录
|
||
├── reports/ # 测试报告目录
|
||
└── README.md # 说明文档
|
||
\`\`\`
|
||
|
||
## 系统要求
|
||
|
||
- Docker 19.03+
|
||
- Docker Compose 1.25+ 或 Docker Compose V2
|
||
- 支持的平台: $TARGET_PLATFORM_NAME
|
||
|
||
## 故障排除
|
||
|
||
### 端口冲突
|
||
如果端口被占用,可以修改\`docker-compose.yml\`文件中的端口映射,然后重启服务。
|
||
|
||
### 镜像加载
|
||
如果需要在其他机器上部署,可以使用以下命令加载镜像:
|
||
\`\`\`bash
|
||
docker load < docker-image.tar.gz
|
||
\`\`\`
|
||
|
||
### 查看详细日志
|
||
\`\`\`bash
|
||
docker-compose logs -f dms-compliance
|
||
\`\`\`
|
||
|
||
### 重新构建
|
||
\`\`\`bash
|
||
docker-compose build --no-cache
|
||
docker-compose up -d
|
||
\`\`\`
|
||
|
||
## 技术支持
|
||
|
||
如有问题,请查看日志文件或联系技术支持。
|
||
EOF
|
||
|
||
# 创建压缩包
|
||
echo "[信息] 创建压缩包..."
|
||
tar -czf "$ARCHIVE_NAME" "$EXPORT_DIR"
|
||
|
||
# 清理临时目录
|
||
rm -rf "$EXPORT_DIR"
|
||
|
||
# 显示结果
|
||
echo ""
|
||
echo "=== 构建完成 ==="
|
||
echo "[成功] Docker Compose部署包已创建: $ARCHIVE_NAME"
|
||
echo "[信息] 架构: $(get_service_arch_name "$SELECTED_SERVICE_ARCH")"
|
||
echo "[信息] 端口: $SELECTED_PORTS"
|
||
echo "[信息] 平台: $TARGET_PLATFORM_NAME"
|
||
echo "[信息] 文件大小: $(du -h "$ARCHIVE_NAME" | cut -f1)"
|
||
echo ""
|
||
echo "部署说明:"
|
||
echo "1. 将 $ARCHIVE_NAME 传输到目标服务器"
|
||
echo "2. 解压: tar -xzf $ARCHIVE_NAME"
|
||
echo "3. 进入目录: cd $EXPORT_DIR"
|
||
echo "4. 启动服务: ./start.sh"
|
||
echo ""
|
||
|
||
if [[ "$SELECTED_SERVICE_ARCH" == "dual" ]]; then
|
||
echo "访问地址:"
|
||
echo "- API服务器: http://localhost:5050"
|
||
echo "- 历史查看器: http://localhost:5051"
|
||
elif [[ "$SELECTED_SERVICE_ARCH" == "fastapi" ]]; then
|
||
echo "访问地址:"
|
||
echo "- Web界面: http://localhost:5051"
|
||
echo "- API文档: http://localhost:5051/docs"
|
||
echo "- ReDoc: http://localhost:5051/redoc"
|
||
else
|
||
echo "访问地址: http://localhost:5050"
|
||
fi
|
||
echo ""
|
||
echo "构建完成!"
|