Files
smartClean/deploy/docker-app/deploy.sh
xqzp2026 d932a33a75 feat: App 后台 Phase 1 Maven 治理 + Docker 部署
- 创建 xiaoqu-app-parent 统一父 POM,管理 4 个 App 模块依赖版本
- 统一 fastjson(1.2.83)、druid(1.2.27)、jedis(3.1.0) 等版本
- 子模块 POM 改造:继承父 POM,删除硬编码版本号
- 添加 Docker 部署方案(complex + mall 双容器)
- 添加 ARCHITECTURE.md 架构文档(三套 DAO 层、冻结规范、新项目规范)
- 关联需求:REQ-20260421-0011

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 16:56:25 +09:30

366 lines
10 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
#
# SmartClean App 后台一键部署脚本Docker 本地版)
#
# 用法:
# ./deploy.sh # 构建并部署 complex + mall
# ./deploy.sh complex # 仅构建部署 xiaoqu-complex
# ./deploy.sh mall # 仅构建部署 xiaoqu-mall
# ./deploy.sh rollback # 回滚到上一版本
# ./deploy.sh status # 查看容器状态
# ./deploy.sh logs [服务名] # 查看日志
# ./deploy.sh stop # 停止所有容器
# ./deploy.sh clean # 停止并清理(含数据卷)
set -e
DEPLOY_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT_DIR="$(dirname "$(dirname "$DEPLOY_DIR")")"
source "$DEPLOY_DIR/.env"
# ===== 版本号 =====
GIT_HASH=$(cd "$ROOT_DIR" && git rev-parse --short HEAD)
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
VERSION="${TIMESTAMP}-${GIT_HASH}"
BRANCH=$(cd "$ROOT_DIR" && git rev-parse --abbrev-ref HEAD)
COMMIT=$(cd "$ROOT_DIR" && git log -1 --format='%h %s')
BACKUP_FILE="$DEPLOY_DIR/.last-version"
CURRENT_FILE="$DEPLOY_DIR/.current-version"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
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"; }
log_step() { echo -e "${CYAN}[STEP]${NC} $1"; }
if docker compose version > /dev/null 2>&1; then
DC="docker compose -f $DEPLOY_DIR/docker-compose.yml"
else
DC="docker-compose -f $DEPLOY_DIR/docker-compose.yml"
fi
# ===== 飞书通知 =====
notify_feishu() {
local title="$1" content="$2" color="$3"
[ -z "$FEISHU_WEBHOOK" ] && return
curl -s -X POST "$FEISHU_WEBHOOK" \
-H "Content-Type: application/json" \
-d "{
\"msg_type\": \"interactive\",
\"card\": {
\"header\": {\"title\":{\"tag\":\"plain_text\",\"content\":\"$title\"},\"template\":\"$color\"},
\"elements\": [{\"tag\":\"markdown\",\"content\":\"$content\"}]
}
}" > /dev/null 2>&1
}
# ===== 检查 Docker =====
check_docker() {
if ! command -v docker &> /dev/null; then
log_error "Docker 未安装"
exit 1
fi
if ! docker info > /dev/null 2>&1; then
log_error "Docker 未启动"
exit 1
fi
}
# ===== 备份当前版本号 =====
backup_version() {
if [ -f "$CURRENT_FILE" ]; then
cp "$CURRENT_FILE" "$BACKUP_FILE"
log_info "已备份当前版本: $(cat "$BACKUP_FILE")"
fi
}
# ===== 本地构建产物 =====
build_local() {
local target="${1:-all}"
local start_time=$(date +%s)
# 切换 Java 8
export JAVA_HOME="$HOME/.sdkman/candidates/java/8.0.432-zulu"
export PATH="$JAVA_HOME/bin:$PATH"
cd "$ROOT_DIR/backend"
# 先安装 intellectual-publiccomplex 依赖它)
if [ "$target" = "all" ] || [ "$target" = "complex" ]; then
log_info " 安装 xiaoqu-intellectual-public..."
mvn clean install -pl xiaoqu-intellectual-public -DskipTests -q
fi
# 从 app-parent 用 docker profile 构建
cd "$ROOT_DIR/backend/xiaoqu-app-parent"
case "$target" in
complex)
log_step "构建 xiaoqu-complex (-P docker)..."
mvn clean package -pl ../xiaoqu-complex -am -P docker -DskipTests -q
;;
mall)
log_step "构建 xiaoqu-mall (-P docker)..."
mvn clean package -pl ../xiaoqu-mall -am -P docker -DskipTests -q
;;
all)
log_step "构建全部 App 模块 (-P docker)..."
mvn clean package -P docker -DskipTests -q
;;
esac
# 验证产物
case "$target" in
complex|all)
[ ! -f "$ROOT_DIR/backend/xiaoqu-complex/target/ROOT.war" ] && log_error "complex ROOT.war 不存在" && exit 1
log_info " complex: $(du -h "$ROOT_DIR/backend/xiaoqu-complex/target/ROOT.war" | cut -f1)"
;;&
mall|all)
[ ! -f "$ROOT_DIR/backend/xiaoqu-mall/target/ROOT.war" ] && log_error "mall ROOT.war 不存在" && exit 1
log_info " mall: $(du -h "$ROOT_DIR/backend/xiaoqu-mall/target/ROOT.war" | cut -f1)"
;;
esac
local elapsed=$(( $(date +%s) - start_time ))
log_info "本地构建完成 (${elapsed}s)"
}
# ===== 打包 Docker 镜像 =====
build_images() {
local target="${1:-all}"
local start_time=$(date +%s)
cd "$DEPLOY_DIR"
case "$target" in
complex)
log_step "打包 Complex 镜像 ($VERSION)..."
$DC build --no-cache complex
docker tag smartclean-complex:latest smartclean-complex:$VERSION 2>/dev/null || true
;;
mall)
log_step "打包 Mall 镜像 ($VERSION)..."
$DC build --no-cache mall
docker tag smartclean-mall:latest smartclean-mall:$VERSION 2>/dev/null || true
;;
all)
log_step "打包全部镜像 ($VERSION)..."
$DC build --no-cache
docker tag smartclean-complex:latest smartclean-complex:$VERSION 2>/dev/null || true
docker tag smartclean-mall:latest smartclean-mall:$VERSION 2>/dev/null || true
;;
esac
local elapsed=$(( $(date +%s) - start_time ))
log_info "镜像打包完成 (${elapsed}s)"
}
# ===== 部署服务 =====
deploy_services() {
local target="${1:-all}"
cd "$DEPLOY_DIR"
log_step "部署容器..."
case "$target" in
complex)
$DC up -d --no-deps complex
;;
mall)
$DC up -d --no-deps mall
;;
all)
$DC down 2>/dev/null || true
$DC up -d
;;
esac
log_info "容器已启动"
}
# ===== 健康检查 =====
healthcheck() {
local target="${1:-all}"
local max_retries=30
local interval=3
log_step "健康检查..."
if [ "$target" = "all" ] || [ "$target" = "complex" ]; then
log_info " 检查 Complex (http://localhost:$COMPLEX_PORT)..."
for i in $(seq 1 $max_retries); do
if curl -sf "http://localhost:$COMPLEX_PORT/" > /dev/null 2>&1; then
log_info " Complex 健康 (第${i}次检查通过)"
break
fi
if [ $i -eq $max_retries ]; then
log_error " Complex 健康检查失败"
return 1
fi
printf "."
sleep $interval
done
fi
if [ "$target" = "all" ] || [ "$target" = "mall" ]; then
log_info " 检查 Mall (http://localhost:$MALL_PORT)..."
for i in $(seq 1 $max_retries); do
if curl -sf "http://localhost:$MALL_PORT/" > /dev/null 2>&1; then
log_info " Mall 健康 (第${i}次检查通过)"
break
fi
if [ $i -eq $max_retries ]; then
log_error " Mall 健康检查失败"
return 1
fi
printf "."
sleep $interval
done
fi
log_info "健康检查通过"
return 0
}
# ===== 回滚 =====
rollback() {
if [ ! -f "$BACKUP_FILE" ]; then
log_error "没有可回滚的版本"
exit 1
fi
local old_version=$(cat "$BACKUP_FILE")
log_warn "回滚到版本: $old_version"
local has_images=true
docker image inspect smartclean-complex:$old_version > /dev/null 2>&1 || has_images=false
docker image inspect smartclean-mall:$old_version > /dev/null 2>&1 || has_images=false
if [ "$has_images" = false ]; then
log_error "旧版本镜像不存在 ($old_version),无法回滚"
exit 1
fi
export VERSION="$old_version"
cd "$DEPLOY_DIR"
$DC down 2>/dev/null || true
$DC up -d
echo "$old_version" > "$CURRENT_FILE"
log_info "回滚完成"
notify_feishu "SmartClean App 已回滚 (Docker)" \
"**回滚版本:** $old_version" \
"yellow"
}
# ===== 清理旧镜像 =====
cleanup_images() {
log_info "清理旧镜像(保留最近 5 个版本)..."
for name in smartclean-complex smartclean-mall; do
docker images "$name" --format "{{.Tag}}" | grep -v "latest" | sort -r | tail -n +6 | while read tag; do
docker rmi "$name:$tag" 2>/dev/null && echo " 删除 $name:$tag"
done
done
}
# ===== 主流程 =====
TARGET="${1:-all}"
DEPLOY_START=$(date +%s)
case "$TARGET" in
status)
$DC ps
exit 0
;;
logs)
shift
$DC logs -f $@
exit 0
;;
stop)
log_info "停止所有容器..."
$DC down
log_info "已停止"
exit 0
;;
clean)
log_warn "停止所有容器并清理数据卷..."
$DC down -v --rmi all
log_info "已清理"
exit 0
;;
rollback)
check_docker
rollback
exit 0
;;
all|complex|mall)
;;
*)
echo "用法: $0 {all|complex|mall|rollback|status|logs|stop|clean}"
exit 1
;;
esac
echo ""
log_info "======================================"
log_info " SmartClean App 部署 (Docker)"
log_info " 版本: $VERSION"
log_info " 分支: $BRANCH"
log_info " 提交: $COMMIT"
log_info " 目标: $TARGET"
log_info "======================================"
echo ""
# 1. 检查 Docker
check_docker
# 2. 备份当前版本号
backup_version
# 3. 本地构建Maven -P docker
build_local "$TARGET"
# 4. 打包 Docker 镜像
build_images "$TARGET"
# 5. 部署容器
deploy_services "$TARGET"
# 6. 健康检查
if healthcheck "$TARGET"; then
echo "$VERSION" > "$CURRENT_FILE"
cleanup_images
ELAPSED=$(( $(date +%s) - DEPLOY_START ))
echo ""
log_info "======================================"
log_info " App 部署成功!"
log_info " 版本: $VERSION"
log_info " 耗时: ${ELAPSED}s"
log_info " Complex: http://localhost:$COMPLEX_PORT"
log_info " Mall: http://localhost:$MALL_PORT"
log_info "======================================"
notify_feishu "SmartClean App 部署成功 (Docker)" \
"**版本:** $VERSION\\n**分支:** $BRANCH\\n**提交:** $COMMIT\\n**目标:** $TARGET\\n**耗时:** ${ELAPSED}s\\n**Complex:** http://localhost:$COMPLEX_PORT\\n**Mall:** http://localhost:$MALL_PORT" \
"green"
else
log_error "健康检查失败,自动回滚..."
rollback
ELAPSED=$(( $(date +%s) - DEPLOY_START ))
notify_feishu "SmartClean App 部署失败 (Docker已回滚)" \
"**版本:** $VERSION\\n**分支:** $BRANCH\\n**提交:** $COMMIT\\n**耗时:** ${ELAPSED}s\\n**状态:** 健康检查失败,已自动回滚" \
"red"
exit 1
fi