|
@@ -0,0 +1,261 @@
|
|
|
|
|
+#!/bin/bash
|
|
|
|
|
+# ============================================
|
|
|
|
|
+# 中华药典 AI Agent — Java 后端部署脚本
|
|
|
|
|
+# 用法: ./java-deploy.sh {start|stop|restart|status|logs|build|deploy}
|
|
|
|
|
+# ============================================
|
|
|
|
|
+
|
|
|
|
|
+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}')"
|
|
|
|
|
+
|
|
|
|
|
+ echo ""
|
|
|
|
|
+ restart
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+# ============================================
|
|
|
|
|
+# 入口
|
|
|
|
|
+# ============================================
|
|
|
|
|
+
|
|
|
|
|
+case "${1:-start}" in
|
|
|
|
|
+ start) start ;;
|
|
|
|
|
+ stop) stop ;;
|
|
|
|
|
+ restart) restart ;;
|
|
|
|
|
+ status) status ;;
|
|
|
|
|
+ logs) logs "${2:-100}" ;;
|
|
|
|
|
+ logf) logs -f ;;
|
|
|
|
|
+ build) build ;;
|
|
|
|
|
+ deploy) deploy ;;
|
|
|
|
|
+ help|--help|-h)
|
|
|
|
|
+ echo "用法: $0 {start|stop|restart|status|logs|logf|build|deploy}"
|
|
|
|
|
+ echo ""
|
|
|
|
|
+ echo " start 启动后端"
|
|
|
|
|
+ echo " stop 停止后端"
|
|
|
|
|
+ echo " restart 重启后端"
|
|
|
|
|
+ echo " status 查看状态"
|
|
|
|
|
+ echo " logs 查看最近 100 行日志"
|
|
|
|
|
+ echo " logf 实时跟踪日志"
|
|
|
|
|
+ echo " build 拉代码 + 编译"
|
|
|
|
|
+ echo " deploy 拉代码 + 编译 + 重启(一键上线)"
|
|
|
|
|
+ exit 0
|
|
|
|
|
+ ;;
|
|
|
|
|
+ *)
|
|
|
|
|
+ echo "用法: $0 {start|stop|restart|status|logs|logf|build|deploy}"
|
|
|
|
|
+ exit 1
|
|
|
|
|
+ ;;
|
|
|
|
|
+esac
|