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>
This commit is contained in:
365
deploy/docker-app/deploy.sh
Executable file
365
deploy/docker-app/deploy.sh
Executable file
@@ -0,0 +1,365 @@
|
||||
#!/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-public(complex 依赖它)
|
||||
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
|
||||
Reference in New Issue
Block a user