#!/bin/sh set -eu WEBAPP_ROOT="/home/webapp" ENV_ROOT="$WEBAPP_ROOT/env" APP_ROOT="$WEBAPP_ROOT/loan-pricing" JAVA_HOME="$ENV_ROOT/java" NGINX_HOME="$ENV_ROOT/nginx" NGINX_CONF="$NGINX_HOME/conf/nginx.conf" BACKEND_DIR="$APP_ROOT/backend" FRONTEND_DIR="$APP_ROOT/frontend" FRONTEND_DIST_DIR="$FRONTEND_DIR/dist" BACKUP_DIR="$APP_ROOT/backup" LOG_DIR="$APP_ROOT/logs" RUN_DIR="$APP_ROOT/run" TMP_DIR="$APP_ROOT/tmp" BACKEND_JAR="$BACKEND_DIR/ruoyi-admin.jar" FRONTEND_PORT=63311 JAVA_RESTART_SCRIPT="$WEBAPP_ROOT/restart_java.sh" timestamp() { date "+%Y-%m-%d %H:%M:%S" } log_info() { printf '[%s] %s\n' "$(timestamp)" "$1" } log_error() { printf '[%s] %s\n' "$(timestamp)" "$1" >&2 } usage() { cat <<'EOF' 用法: ./bin/prod/deploy_release.sh <发布压缩包路径> EOF } require_root() { if [ "$(id -u)" -ne 0 ]; then log_error "请使用 root 用户执行部署脚本" exit 1 fi } require_command() { if ! command -v "$1" >/dev/null 2>&1; then log_error "缺少命令: $1" exit 1 fi } ensure_runtime_dirs() { mkdir -p "$BACKEND_DIR" "$FRONTEND_DIR" "$BACKUP_DIR" "$LOG_DIR" "$RUN_DIR" "$TMP_DIR" } cleanup() { if [ -n "${WORK_DIR:-}" ] && [ -d "$WORK_DIR" ]; then rm -rf "$WORK_DIR" fi } extract_release_package() { release_archive="$1" release_extract_dir="$2" mkdir -p "$release_extract_dir" case "$release_archive" in *.zip) unzip -oq "$release_archive" -d "$release_extract_dir" ;; *.tar.gz|*.tgz) tar -xzf "$release_archive" -C "$release_extract_dir" ;; *.tar) tar -xf "$release_archive" -C "$release_extract_dir" ;; *) log_error "不支持的发布包格式: $release_archive" exit 1 ;; esac } assert_single_file() { search_dir="$1" file_name="$2" description="$3" count=$(find "$search_dir" -type f -name "$file_name" | wc -l | tr -d ' ') if [ "$count" -ne 1 ]; then log_error "$description 数量不正确,期望 1 个,实际 $count 个" exit 1 fi find "$search_dir" -type f -name "$file_name" | head -n 1 } assert_single_jar() { search_dir="$1" count=$(find "$search_dir" -type f -name '*.jar' | wc -l | tr -d ' ') if [ "$count" -ne 1 ]; then log_error "后端 jar 数量不正确,期望 1 个,实际 $count 个" exit 1 fi find "$search_dir" -type f -name '*.jar' | head -n 1 } backup_current_release() { backup_stamp=$(date "+%Y%m%d%H%M%S") CURRENT_BACKUP_DIR="$BACKUP_DIR/$backup_stamp" mkdir -p "$CURRENT_BACKUP_DIR/backend" "$CURRENT_BACKUP_DIR/frontend" if [ -f "$BACKEND_JAR" ]; then cp -a "$BACKEND_JAR" "$CURRENT_BACKUP_DIR/backend/" fi if [ -d "$FRONTEND_DIST_DIR" ]; then cp -a "$FRONTEND_DIST_DIR" "$CURRENT_BACKUP_DIR/frontend/" fi log_info "旧版本已备份到: $CURRENT_BACKUP_DIR" } deploy_backend() { source_jar="$1" rm -f "$BACKEND_DIR"/*.jar cp "$source_jar" "$BACKEND_JAR" } resolve_frontend_source_dir() { unzip_dir="$1" if [ -f "$unzip_dir/index.html" ]; then printf '%s\n' "$unzip_dir" return 0 fi if [ -f "$unzip_dir/dist/index.html" ]; then printf '%s\n' "$unzip_dir/dist" return 0 fi candidate=$(find "$unzip_dir" -type f -name 'index.html' | head -n 1) if [ -z "${candidate:-}" ]; then log_error "dist.zip 解压后未找到 index.html" exit 1 fi dirname "$candidate" } deploy_frontend() { dist_zip="$1" dist_unpack_dir="$WORK_DIR/frontend" mkdir -p "$dist_unpack_dir" unzip -oq "$dist_zip" -d "$dist_unpack_dir" frontend_source_dir=$(resolve_frontend_source_dir "$dist_unpack_dir") rm -rf "$FRONTEND_DIST_DIR" mkdir -p "$FRONTEND_DIST_DIR" cp -a "$frontend_source_dir"/. "$FRONTEND_DIST_DIR"/ } reload_nginx() { nginx_pid_file="$RUN_DIR/nginx.pid" "$NGINX_HOME/sbin/nginx" -t -c "$NGINX_CONF" if [ -f "$nginx_pid_file" ]; then nginx_pid=$(cat "$nginx_pid_file" 2>/dev/null || true) if [ -n "${nginx_pid:-}" ] && kill -0 "$nginx_pid" 2>/dev/null; then "$NGINX_HOME/sbin/nginx" -c "$NGINX_CONF" -s reload log_info "Nginx 已重载,前端端口: $FRONTEND_PORT" return 0 fi fi "$NGINX_HOME/sbin/nginx" -c "$NGINX_CONF" log_info "Nginx 已启动,前端端口: $FRONTEND_PORT" } main() { if [ "$#" -ne 1 ]; then usage exit 1 fi require_root require_command tar require_command unzip require_command find release_archive="$1" if [ ! -f "$release_archive" ]; then log_error "发布压缩包不存在: $release_archive" exit 1 fi if [ ! -x "$JAVA_HOME/bin/java" ]; then log_error "未检测到 Java,请先执行 ./bin/prod/install_env.sh" exit 1 fi if [ ! -x "$NGINX_HOME/sbin/nginx" ]; then log_error "未检测到 Nginx,请先执行 ./bin/prod/install_env.sh" exit 1 fi if [ ! -x "$JAVA_RESTART_SCRIPT" ]; then log_error "未检测到 Java 重启脚本: $JAVA_RESTART_SCRIPT" exit 1 fi ensure_runtime_dirs WORK_DIR=$(mktemp -d "$TMP_DIR/release.XXXXXX") trap cleanup EXIT INT TERM extract_release_package "$release_archive" "$WORK_DIR/package" backend_jar_source=$(assert_single_jar "$WORK_DIR/package") frontend_dist_source=$(assert_single_file "$WORK_DIR/package" 'dist.zip' '前端 dist.zip') backup_current_release "$JAVA_RESTART_SCRIPT" stop deploy_backend "$backend_jar_source" deploy_frontend "$frontend_dist_source" "$JAVA_RESTART_SCRIPT" start reload_nginx log_info "部署完成" log_info "后端 jar: $BACKEND_JAR" log_info "前端目录: $FRONTEND_DIST_DIR" log_info "备份目录: $CURRENT_BACKUP_DIR" } main "$@"