| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- #!/bin/bash
- # ============================================
- # 中华药典 AI Agent — Java 后端部署脚本
- # 用法: ./java-deploy.sh {start|stop|restart|status|logs|build|deploy|rollback}
- # ============================================
- set -e
- APP_DIR="/opt/pharmacopoeia-ai/backend-java"
- PROJECT_DIR="/opt/pharmacopoeia-ai"
- JAR_FILE="$APP_DIR/target/pharmacopoeia-ai-1.0.0.jar"
- LOG_DIR="/var/log/pharmacopoeia"
- PID_FILE="$APP_DIR/app.pid"
- # 加载 .env 环境变量
- load_env() {
- if [ -f "$PROJECT_DIR/.env" ]; then
- set -a
- source "$PROJECT_DIR/.env"
- set +a
- fi
- }
- load_env
- mkdir -p "$LOG_DIR"
- # ---- 服务端口 ----
- PORT=${SERVER_PORT:-9000}
- HEALTH_URL="http://localhost:${PORT}/health"
- # ============================================
- # 通用函数
- # ============================================
- running() {
- [ -f "$PID_FILE" ] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null
- }
- get_pid() {
- cat "$PID_FILE" 2>/dev/null || echo "N/A"
- }
- health_check() {
- curl -sf "$HEALTH_URL" > /dev/null 2>&1
- }
- # ============================================
- # 启动
- # ============================================
- start() {
- if running; then
- echo "⚠️ 后端已在运行中 (PID: $(get_pid))"
- curl -sf "$HEALTH_URL" | python3 -m json.tool 2>/dev/null || true
- return 0
- fi
- # 自动编译(如果 JAR 不存在)
- if [ ! -f "$JAR_FILE" ]; then
- echo "📦 JAR 不存在,正在编译..."
- cd "$APP_DIR"
- mvn clean package -DskipTests -q
- echo "✅ 编译完成"
- fi
- echo "🚀 启动后端..."
- cd "$APP_DIR"
- JVM_OPTS="-Xms512m -Xmx2g"
- JVM_OPTS="$JVM_OPTS -XX:+UseG1GC"
- JVM_OPTS="$JVM_OPTS -Dfile.encoding=UTF-8"
- JVM_OPTS="$JVM_OPTS -Djava.security.egd=file:/dev/./urandom"
- nohup java $JVM_OPTS -jar "$JAR_FILE" \
- --server.port="$PORT" \
- >> "$LOG_DIR/backend.log" 2>&1 &
- echo $! > "$PID_FILE"
- echo " PID: $(get_pid)"
- # 等启动完成
- echo -n " 等待启动"
- for i in $(seq 1 30); do
- if health_check 2>/dev/null; then
- echo ""
- echo "✅ 启动成功!"
- curl -sf "$HEALTH_URL" | python3 -m json.tool 2>/dev/null || true
- return 0
- fi
- echo -n "."
- sleep 1
- done
- echo ""
- echo "❌ 启动超时,查看日志:"
- tail -30 "$LOG_DIR/backend.log"
- rm -f "$PID_FILE"
- return 1
- }
- # ============================================
- # 停止
- # ============================================
- stop() {
- if ! running; then
- echo "后端未运行"
- rm -f "$PID_FILE"
- return 0
- fi
- PID=$(get_pid)
- echo "🛑 停止后端 (PID: $PID)..."
- kill "$PID"
- for i in $(seq 1 30); do
- if ! kill -0 "$PID" 2>/dev/null; then
- echo "✅ 已停止"
- rm -f "$PID_FILE"
- return 0
- fi
- sleep 1
- done
- echo "⚠️ 超时,强制终止..."
- kill -9 "$PID" 2>/dev/null || true
- rm -f "$PID_FILE"
- echo "✅ 已强制停止"
- }
- # ============================================
- # 重启
- # ============================================
- restart() {
- echo "⏳ 重启后端..."
- stop
- sleep 2
- start
- }
- # ============================================
- # 状态
- # ============================================
- status() {
- echo "========================================"
- echo " 中华药典 AI — Java 后端"
- echo "========================================"
- if running; then
- echo " 状态: ✅ 运行中"
- echo " PID: $(get_pid)"
- echo " 端口: $PORT"
- echo ""
- curl -sf "$HEALTH_URL" | python3 -m json.tool 2>/dev/null || echo " (健康检查无响应)"
- else
- echo " 状态: ❌ 未运行"
- fi
- echo ""
- echo " 日志: $LOG_DIR/backend.log"
- echo "========================================"
- }
- # ============================================
- # 日志
- # ============================================
- logs() {
- if [ "$1" = "-f" ]; then
- tail -f "$LOG_DIR/backend.log"
- else
- echo "=== 最近 100 行日志 ==="
- tail -${1:-100} "$LOG_DIR/backend.log"
- fi
- }
- # ============================================
- # 编译
- # ============================================
- build() {
- echo "📦 拉取最新代码..."
- cd "$PROJECT_DIR"
- git pull
- echo "🔨 编译打包..."
- cd "$APP_DIR"
- mvn clean package -DskipTests -q
- if [ -f "$JAR_FILE" ]; then
- echo "✅ 编译成功: $JAR_FILE"
- ls -lh "$JAR_FILE"
- else
- echo "❌ 编译失败"
- return 1
- fi
- }
- # ============================================
- # 一键部署(拉代码 → 编译 → 备份 → 重启)
- # ============================================
- deploy() {
- echo "========================================"
- echo " 一键部署"
- echo "========================================"
- echo ""
- echo "📥 拉取代码..."
- cd "$PROJECT_DIR"
- git pull
- echo ""
- echo "🔨 编译打包..."
- cd "$APP_DIR"
- mvn clean package -DskipTests -q
- if [ ! -f "$JAR_FILE" ]; then
- echo "❌ 编译失败,取消部署"
- return 1
- fi
- echo "✅ 编译完成: $(ls -lh "$JAR_FILE" | awk '{print $5}')"
- # 备份当前 JAR
- BACKUP_DIR="$APP_DIR/backups"
- mkdir -p "$BACKUP_DIR"
- if [ -f "$JAR_FILE" ]; then
- BACKUP_FILE="$BACKUP_DIR/pharmacopoeia-ai-$(date +%Y%m%d-%H%M%S).jar"
- cp "$JAR_FILE" "$BACKUP_FILE"
- echo "📦 已备份: $BACKUP_FILE"
- # 保留最近 5 个备份
- ls -t "$BACKUP_DIR"/*.jar 2>/dev/null | tail -n +6 | xargs rm -f 2>/dev/null || true
- fi
- echo ""
- restart
- }
- # ============================================
- # 回滚
- # ============================================
- rollback() {
- BACKUP_DIR="$APP_DIR/backups"
- if [ ! -d "$BACKUP_DIR" ] || [ -z "$(ls -A "$BACKUP_DIR" 2>/dev/null)" ]; then
- echo "❌ 没有可用的备份"
- return 1
- fi
- echo "可用备份:"
- ls -lht "$BACKUP_DIR"/*.jar 2>/dev/null | head -10
- echo ""
- if [ -n "$1" ]; then
- ROLLBACK_FILE="$1"
- else
- ROLLBACK_FILE=$(ls -t "$BACKUP_DIR"/*.jar 2>/dev/null | head -1)
- echo "回滚到最新备份: $(basename "$ROLLBACK_FILE")"
- fi
- if [ ! -f "$ROLLBACK_FILE" ]; then
- echo "❌ 备份文件不存在: $ROLLBACK_FILE"
- return 1
- fi
- echo ""
- echo "⏪ 回滚..."
- cp "$ROLLBACK_FILE" "$JAR_FILE"
- echo "✅ 已替换为: $(basename "$ROLLBACK_FILE")"
- restart
- }
- # ============================================
- # 入口
- # ============================================
- case "${1:-start}" in
- start) start ;;
- stop) stop ;;
- restart) restart ;;
- status) status ;;
- logs) logs "${2:-100}" ;;
- logf) logs -f ;;
- build) build ;;
- deploy) deploy ;;
- rollback) rollback "$2" ;;
- help|--help|-h)
- echo "用法: $0 {start|stop|restart|status|logs|logf|build|deploy|rollback}"
- echo ""
- echo " start 启动后端"
- echo " stop 停止后端"
- echo " restart 重启后端"
- echo " status 查看状态"
- echo " logs 查看最近 100 行日志"
- echo " logf 实时跟踪日志"
- echo " build 拉代码 + 编译"
- echo " deploy 拉代码 + 编译 + 备份 + 重启(一键上线)"
- echo " rollback 回滚到上一个备份(可指定备份文件路径)"
- exit 0
- ;;
- *)
- echo "用法: $0 {start|stop|restart|status|logs|logf|build|deploy|rollback}"
- exit 1
- ;;
- esac
|