feat: 添加自动化部署方案(Docker + 远程服务器两套方案)

- 新增 deploy/docker/:Docker 本机模拟部署,含 Dockerfile、docker-compose、deploy.sh 一键脚本
- 新增 deploy/remote/:远程服务器部署,含 SSH 自动上传、重启、回滚脚本
- 新增 deploy/README.md:完整使用手册,含现状分析、落地调整工作清单、命令速查
- 新增 build.sh/start.sh:本地构建和启动脚本(含飞书通知)
- 新增前端 .env.docker 环境配置,API 指向测试服务器
- 前端 package.json 新增 build-docker 命令
- 更新 .gitignore:排除 IDE 配置、SQL 数据、Docker 敏感文件
- 前端 UI 样式优化(多个页面组件)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
xqzp2026
2026-04-15 18:41:15 +09:30
parent d27abbb529
commit 8373460096
71 changed files with 8003 additions and 350 deletions

237
build.sh Executable file
View File

@@ -0,0 +1,237 @@
#!/bin/bash
#
# SmartClean 构建打包脚本(带飞书通知)
# 用法:
# ./build.sh # 构建所有模块public + web + task + frontend
# ./build.sh web # 仅构建后端 web (含 public)
# ./build.sh task # 仅构建后端 task (含 public)
# ./build.sh backend # 构建后端 web + task
# ./build.sh front # 仅构建前端
# ./build.sh front-test # 构建前端测试环境包
# ===== 飞书 Webhook 配置 =====
FEISHU_WEBHOOK="https://open.feishu.cn/open-apis/bot/v2/hook/5703e8cc-6998-46a6-af9d-8c5102cc8c1e"
# ===== 初始化 =====
export SDKMAN_DIR="$HOME/.sdkman"
source "$SDKMAN_DIR/bin/sdkman-init.sh"
sdk use java 8.0.432-zulu > /dev/null 2>&1
ROOT_DIR="$(cd "$(dirname "$0")" && pwd)"
PUBLIC_DIR="$ROOT_DIR/backend/xiaoqu-intellectual-public"
WEB_DIR="$ROOT_DIR/backend/xiaoqu-intellectual-web"
TASK_DIR="$ROOT_DIR/backend/xiaoqu-intellectual-task"
FRONT_DIR="$ROOT_DIR/frontend/witcleansystem"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
BUILD_START_TIME=$(date +%s)
BUILD_RESULTS=()
HAS_ERROR=0
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# ===== 飞书通知函数 =====
send_feishu_notify() {
local title="$1"
local content="$2"
local color="$3" # green / red / yellow
# 构建富文本消息
local json_content
json_content=$(cat <<EOJSON
{
"msg_type": "interactive",
"card": {
"header": {
"title": {
"tag": "plain_text",
"content": "$title"
},
"template": "$color"
},
"elements": [
{
"tag": "markdown",
"content": "$content"
}
]
}
}
EOJSON
)
curl -s -X POST "$FEISHU_WEBHOOK" \
-H "Content-Type: application/json" \
-d "$json_content" > /dev/null 2>&1
}
# ===== 构建函数 =====
build_public() {
log_info "编译 public 模块..."
local start=$(date +%s)
cd "$PUBLIC_DIR"
local output
output=$(mvn clean install -q -DskipTests 2>&1)
local rc=$?
local elapsed=$(( $(date +%s) - start ))
if [ $rc -eq 0 ]; then
log_info "public 模块编译成功 (${elapsed}s)"
BUILD_RESULTS+=("✅ **public 模块** — 编译成功 (${elapsed}s)")
else
log_error "public 模块编译失败"
HAS_ERROR=1
BUILD_RESULTS+=("❌ **public 模块** — 编译失败 (${elapsed}s)")
BUILD_RESULTS+=("错误信息: $(echo "$output" | tail -5 | sed 's/"/\\"/g')")
return 1
fi
}
build_web() {
log_info "打包 Web 服务 (ROOT.war)..."
local start=$(date +%s)
cd "$WEB_DIR"
local output
output=$(mvn clean package -q -DskipTests 2>&1)
local rc=$?
local elapsed=$(( $(date +%s) - start ))
if [ $rc -eq 0 ]; then
local war_file="$WEB_DIR/target/ROOT.war"
local war_size=""
if [ -f "$war_file" ]; then
war_size=" | 文件大小: $(du -h "$war_file" | cut -f1)"
fi
log_info "Web 服务打包成功 (${elapsed}s)$war_size"
BUILD_RESULTS+=("✅ **Web 服务 (ROOT.war)** — 打包成功 (${elapsed}s)$war_size")
else
log_error "Web 服务打包失败"
HAS_ERROR=1
BUILD_RESULTS+=("❌ **Web 服务** — 打包失败 (${elapsed}s)")
BUILD_RESULTS+=("错误信息: $(echo "$output" | tail -5 | sed 's/"/\\"/g')")
return 1
fi
}
build_task() {
log_info "打包 Task 服务..."
local start=$(date +%s)
cd "$TASK_DIR"
local output
output=$(mvn clean package -q -DskipTests 2>&1)
local rc=$?
local elapsed=$(( $(date +%s) - start ))
if [ $rc -eq 0 ]; then
log_info "Task 服务打包成功 (${elapsed}s)"
BUILD_RESULTS+=("✅ **Task 服务** — 打包成功 (${elapsed}s)")
else
log_error "Task 服务打包失败"
HAS_ERROR=1
BUILD_RESULTS+=("❌ **Task 服务** — 打包失败 (${elapsed}s)")
BUILD_RESULTS+=("错误信息: $(echo "$output" | tail -5 | sed 's/"/\\"/g')")
return 1
fi
}
build_front() {
local mode="${1:-build}" # build 或 build-test
local mode_label="生产"
[ "$mode" = "build-test" ] && mode_label="测试"
log_info "构建前端 (${mode_label}环境)..."
local start=$(date +%s)
cd "$FRONT_DIR"
if [ ! -d "node_modules" ]; then
log_warn "未检测到 node_modules, 正在安装依赖..."
npm install
fi
local output
output=$(npm run "$mode" 2>&1)
local rc=$?
local elapsed=$(( $(date +%s) - start ))
if [ $rc -eq 0 ]; then
log_info "前端构建成功 (${mode_label}, ${elapsed}s)"
BUILD_RESULTS+=("✅ **前端 (${mode_label})** — 构建成功 (${elapsed}s)")
else
log_error "前端构建失败"
HAS_ERROR=1
BUILD_RESULTS+=("❌ **前端 (${mode_label})** — 构建失败 (${elapsed}s)")
BUILD_RESULTS+=("错误信息: $(echo "$output" | tail -5 | sed 's/"/\\"/g')")
return 1
fi
}
# ===== 发送构建汇总通知 =====
send_build_summary() {
local total_elapsed=$(( $(date +%s) - BUILD_START_TIME ))
local branch=$(cd "$ROOT_DIR" && git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
local commit=$(cd "$ROOT_DIR" && git log -1 --format='%h %s' 2>/dev/null || echo "unknown")
local build_target="${1:-all}"
local hostname=$(hostname)
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
local status_emoji="✅"
local title_status="成功"
local color="green"
if [ $HAS_ERROR -ne 0 ]; then
status_emoji="❌"
title_status="失败"
color="red"
fi
# 拼接结果列表
local detail=""
for item in "${BUILD_RESULTS[@]}"; do
detail="${detail}\n${item}"
done
local content="**构建目标:** ${build_target}\\n**分支:** ${branch}\\n**最新提交:** ${commit}\\n**构建机器:** ${hostname}\\n**时间:** ${timestamp}\\n**总耗时:** ${total_elapsed}s\\n\\n---\\n${detail}"
send_feishu_notify "${status_emoji} SmartClean 构建${title_status}" "$content" "$color"
log_info "飞书通知已发送"
}
# ===== 主流程 =====
TARGET="${1:-all}"
case "$TARGET" in
web)
build_public && build_web
;;
task)
build_public && build_task
;;
backend)
build_public && build_web && build_task
;;
front)
build_front "build"
;;
front-test)
build_front "build-test"
;;
all)
build_public && build_web && build_task
build_front "build"
;;
*)
echo "用法: $0 {all|web|task|backend|front|front-test}"
exit 1
;;
esac
send_build_summary "$TARGET"
if [ $HAS_ERROR -ne 0 ]; then
exit 1
fi