feat: 添加完整的容器化部署脚本系统
✨ 新增功能: - 全量部署脚本 (backend/deploy-all.sh) - 支持一键部署所有微服务 - 单服务部署脚本 - 每个微服务独立部署脚本 - 前端部署脚本 (web-flowith/deploy.sh) - Vue应用自动构建部署 - Jenkins CI/CD 支持 - 完整的Pipeline配置 �� 部署特性: - 容错机制: 单个服务失败不影响其他服务部署 - 详细报告: 完整的部署状态统计和错误日志 - 容器化: 使用Docker进行服务部署 - 健康检查: 自动验证服务启动状态 - 版本备份: 自动备份旧版本支持快速回滚 🛠️ 技术改进: - emotion-auth服务启动问题修复 - 跨域配置优化 - 数据库连接配置统一 - OAuth服务实现完善 - WebSocket依赖更新 📚 文档: - Jenkins部署说明文档 - 部署脚本使用指南 - 故障排查手册 🌐 部署环境: - 目标服务器: 47.111.10.27 - 容器化部署到 /data/builds - 前端部署到 /data/www/emotion-museum - 支持test/prod环境配置
This commit is contained in:
@@ -0,0 +1,324 @@
|
|||||||
|
# 情感博物馆 Jenkins 部署说明
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
本文档描述如何在Jenkins中配置和使用部署脚本来自动化部署情感博物馆项目的后端微服务和前端应用。
|
||||||
|
|
||||||
|
## 部署脚本说明
|
||||||
|
|
||||||
|
### 1. 后端微服务部署
|
||||||
|
|
||||||
|
#### 主部署脚本
|
||||||
|
- **文件路径**: `backend/deploy-all.sh`
|
||||||
|
- **功能**: 一键部署所有后端微服务
|
||||||
|
- **特性**:
|
||||||
|
- 支持单个服务失败不影响其他服务部署
|
||||||
|
- 详细的部署日志和错误报告
|
||||||
|
- 支持Jenkins环境变量
|
||||||
|
- 容器化部署到远程服务器
|
||||||
|
|
||||||
|
#### 单服务部署脚本
|
||||||
|
每个微服务模块都有独立的部署脚本:
|
||||||
|
- `backend/emotion-gateway/deploy.sh` - 网关服务
|
||||||
|
- `backend/emotion-user/deploy.sh` - 用户服务
|
||||||
|
- `backend/emotion-ai/deploy.sh` - AI服务
|
||||||
|
- `backend/emotion-record/deploy.sh` - 记录服务
|
||||||
|
- `backend/emotion-growth/deploy.sh` - 成长服务
|
||||||
|
- `backend/emotion-websocket/deploy.sh` - WebSocket服务
|
||||||
|
- `backend/emotion-auth/deploy.sh` - 认证服务
|
||||||
|
|
||||||
|
### 2. 前端应用部署
|
||||||
|
|
||||||
|
#### 前端部署脚本
|
||||||
|
- **文件路径**: `web-flowith/deploy.sh`
|
||||||
|
- **功能**: 构建并部署前端应用到远程服务器
|
||||||
|
- **特性**:
|
||||||
|
- 自动构建Vue应用
|
||||||
|
- 备份旧版本
|
||||||
|
- 配置Nginx反向代理
|
||||||
|
- 健康检查
|
||||||
|
|
||||||
|
## Jenkins配置
|
||||||
|
|
||||||
|
### 1. 环境变量配置
|
||||||
|
|
||||||
|
在Jenkins中配置以下环境变量:
|
||||||
|
|
||||||
|
#### 必需变量
|
||||||
|
```bash
|
||||||
|
# 部署目标服务器
|
||||||
|
DEPLOY_HOST=root@47.111.10.27
|
||||||
|
|
||||||
|
# 后端部署配置
|
||||||
|
REMOTE_BUILD_DIR=/data/builds
|
||||||
|
REMOTE_DOCKER_DIR=/data/docker
|
||||||
|
DEPLOY_ENV=test
|
||||||
|
PROJECT_NAME=emotion-museum
|
||||||
|
|
||||||
|
# 前端部署配置
|
||||||
|
REMOTE_WEB_DIR=/data/www/emotion-museum
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 可选变量
|
||||||
|
```bash
|
||||||
|
# 数据库配置
|
||||||
|
MYSQL_HOST=47.111.10.27
|
||||||
|
MYSQL_PORT=3306
|
||||||
|
MYSQL_DATABASE=emotion_museum
|
||||||
|
MYSQL_USERNAME=root
|
||||||
|
|
||||||
|
# Redis配置
|
||||||
|
REDIS_HOST=47.111.10.27
|
||||||
|
REDIS_PORT=6379
|
||||||
|
REDIS_PASSWORD=
|
||||||
|
REDIS_DATABASE=0
|
||||||
|
|
||||||
|
# Nacos配置
|
||||||
|
NACOS_SERVER_ADDR=47.111.10.27:8848
|
||||||
|
NACOS_USERNAME=nacos
|
||||||
|
NACOS_PASSWORD=Peanut2817*#
|
||||||
|
|
||||||
|
# AI服务配置
|
||||||
|
COZE_API_TOKEN=your_coze_api_token
|
||||||
|
|
||||||
|
# JWT配置
|
||||||
|
JWT_SECRET=emotion-museum-secret-key-2025
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Jenkins Pipeline 配置
|
||||||
|
|
||||||
|
#### 后端微服务Pipeline
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
|
||||||
|
environment {
|
||||||
|
DEPLOY_HOST = 'root@47.111.10.27'
|
||||||
|
DEPLOY_ENV = 'test'
|
||||||
|
PROJECT_NAME = 'emotion-museum'
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Checkout') {
|
||||||
|
steps {
|
||||||
|
git branch: 'main', url: 'your-git-repo-url'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Deploy Backend Services') {
|
||||||
|
steps {
|
||||||
|
dir('backend') {
|
||||||
|
sh './deploy-all.sh'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
// 发送部署结果通知
|
||||||
|
emailext (
|
||||||
|
subject: "部署结果: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
|
||||||
|
body: "部署状态: ${currentBuild.result}\n构建链接: ${env.BUILD_URL}",
|
||||||
|
to: "your-email@example.com"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 前端应用Pipeline
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
|
||||||
|
environment {
|
||||||
|
DEPLOY_HOST = 'root@47.111.10.27'
|
||||||
|
REMOTE_WEB_DIR = '/data/www/emotion-museum'
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Checkout') {
|
||||||
|
steps {
|
||||||
|
git branch: 'main', url: 'your-git-repo-url'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Deploy Frontend') {
|
||||||
|
steps {
|
||||||
|
dir('web-flowith') {
|
||||||
|
sh './deploy.sh'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
emailext (
|
||||||
|
subject: "前端部署结果: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
|
||||||
|
body: "部署状态: ${currentBuild.result}\n访问地址: http://47.111.10.27/emotion-museum/",
|
||||||
|
to: "your-email@example.com"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 单服务部署Job配置
|
||||||
|
|
||||||
|
为每个微服务创建单独的Jenkins Job:
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
|
||||||
|
parameters {
|
||||||
|
choice(
|
||||||
|
name: 'SERVICE_NAME',
|
||||||
|
choices: ['emotion-gateway', 'emotion-user', 'emotion-ai', 'emotion-record', 'emotion-growth', 'emotion-websocket', 'emotion-auth'],
|
||||||
|
description: '选择要部署的服务'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
environment {
|
||||||
|
DEPLOY_HOST = 'root@47.111.10.27'
|
||||||
|
DEPLOY_ENV = 'test'
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Checkout') {
|
||||||
|
steps {
|
||||||
|
git branch: 'main', url: 'your-git-repo-url'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Deploy Single Service') {
|
||||||
|
steps {
|
||||||
|
dir("backend/${params.SERVICE_NAME}") {
|
||||||
|
sh './deploy.sh'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 部署流程
|
||||||
|
|
||||||
|
### 1. 全量部署流程
|
||||||
|
|
||||||
|
1. **代码检出**: 从Git仓库拉取最新代码
|
||||||
|
2. **环境检查**: 检查远程服务器连接和本地构建环境
|
||||||
|
3. **服务构建**: 使用Maven构建所有微服务
|
||||||
|
4. **容器部署**: 逐个部署服务到Docker容器
|
||||||
|
5. **健康检查**: 验证服务启动状态
|
||||||
|
6. **部署报告**: 生成详细的部署结果报告
|
||||||
|
|
||||||
|
### 2. 单服务部署流程
|
||||||
|
|
||||||
|
1. **服务构建**: 构建指定的微服务
|
||||||
|
2. **容器更新**: 停止旧容器,启动新容器
|
||||||
|
3. **健康检查**: 验证服务状态
|
||||||
|
4. **结果反馈**: 返回部署结果
|
||||||
|
|
||||||
|
### 3. 前端部署流程
|
||||||
|
|
||||||
|
1. **依赖安装**: 安装Node.js依赖
|
||||||
|
2. **项目构建**: 构建Vue生产版本
|
||||||
|
3. **文件上传**: 上传构建产物到服务器
|
||||||
|
4. **Nginx配置**: 配置反向代理和静态文件服务
|
||||||
|
5. **健康检查**: 验证前端页面和API代理
|
||||||
|
|
||||||
|
## 监控和日志
|
||||||
|
|
||||||
|
### 1. 部署日志
|
||||||
|
|
||||||
|
- **位置**: Jenkins构建日志
|
||||||
|
- **内容**: 详细的部署步骤和错误信息
|
||||||
|
- **格式**: 带时间戳和颜色标识的结构化日志
|
||||||
|
|
||||||
|
### 2. 应用日志
|
||||||
|
|
||||||
|
- **后端日志**: `/data/logs/emotion-museum/`
|
||||||
|
- **Nginx日志**: `/data/logs/nginx/`
|
||||||
|
- **容器日志**: `docker logs <container_name>`
|
||||||
|
|
||||||
|
### 3. 健康检查
|
||||||
|
|
||||||
|
- **后端服务**: `http://47.111.10.27:<port>/actuator/health`
|
||||||
|
- **前端应用**: `http://47.111.10.27/emotion-museum/`
|
||||||
|
- **API代理**: `http://47.111.10.27/api/`
|
||||||
|
|
||||||
|
## 故障排查
|
||||||
|
|
||||||
|
### 1. 常见问题
|
||||||
|
|
||||||
|
#### 服务启动失败
|
||||||
|
```bash
|
||||||
|
# 查看容器日志
|
||||||
|
docker logs <service_name>
|
||||||
|
|
||||||
|
# 查看容器状态
|
||||||
|
docker ps -a | grep emotion
|
||||||
|
|
||||||
|
# 重启服务
|
||||||
|
docker restart <service_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 网络连接问题
|
||||||
|
```bash
|
||||||
|
# 检查端口占用
|
||||||
|
netstat -tlnp | grep <port>
|
||||||
|
|
||||||
|
# 检查防火墙
|
||||||
|
ufw status
|
||||||
|
|
||||||
|
# 测试服务连通性
|
||||||
|
curl -f http://localhost:<port>/actuator/health
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 前端访问问题
|
||||||
|
```bash
|
||||||
|
# 检查Nginx配置
|
||||||
|
nginx -t
|
||||||
|
|
||||||
|
# 重载Nginx配置
|
||||||
|
systemctl reload nginx
|
||||||
|
|
||||||
|
# 查看Nginx日志
|
||||||
|
tail -f /data/logs/nginx/emotion-museum-error.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 回滚操作
|
||||||
|
|
||||||
|
#### 后端服务回滚
|
||||||
|
```bash
|
||||||
|
# 停止当前容器
|
||||||
|
docker stop <service_name>
|
||||||
|
|
||||||
|
# 启动备份版本
|
||||||
|
docker run -d --name <service_name> <backup_image>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 前端应用回滚
|
||||||
|
```bash
|
||||||
|
# 恢复备份版本
|
||||||
|
cd /data/www/emotion-museum/backup
|
||||||
|
cp -r backup_<timestamp> ../web-flowith
|
||||||
|
```
|
||||||
|
|
||||||
|
## 安全注意事项
|
||||||
|
|
||||||
|
1. **SSH密钥管理**: 确保Jenkins服务器的SSH密钥安全
|
||||||
|
2. **环境变量**: 敏感信息使用Jenkins凭据管理
|
||||||
|
3. **网络安全**: 限制服务器访问权限
|
||||||
|
4. **日志安全**: 避免在日志中暴露敏感信息
|
||||||
|
|
||||||
|
## 联系信息
|
||||||
|
|
||||||
|
如有问题,请联系开发团队:
|
||||||
|
- 邮箱: dev@emotion-museum.com
|
||||||
|
- 文档更新: 2025-07-18
|
||||||
Executable
+444
@@ -0,0 +1,444 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# 情感博物馆 - 全服务容器化部署脚本
|
||||||
|
# 作者: emotion-museum
|
||||||
|
# 日期: 2025-07-18
|
||||||
|
# 支持Jenkins CI/CD部署
|
||||||
|
|
||||||
|
# 不要在遇到错误时立即退出,让所有模块都尝试部署
|
||||||
|
set +e
|
||||||
|
|
||||||
|
# 配置变量 - 支持Jenkins环境变量覆盖
|
||||||
|
REMOTE_HOST="${DEPLOY_HOST:-root@47.111.10.27}"
|
||||||
|
REMOTE_BUILD_DIR="${REMOTE_BUILD_DIR:-/data/builds}"
|
||||||
|
REMOTE_DOCKER_COMPOSE_DIR="${REMOTE_DOCKER_DIR:-/data/docker}"
|
||||||
|
PROFILE="${DEPLOY_ENV:-test}"
|
||||||
|
PROJECT_NAME="${PROJECT_NAME:-emotion-museum}"
|
||||||
|
|
||||||
|
# Jenkins构建信息
|
||||||
|
BUILD_NUMBER="${BUILD_NUMBER:-manual}"
|
||||||
|
JOB_NAME="${JOB_NAME:-local-deploy}"
|
||||||
|
BUILD_URL="${BUILD_URL:-}"
|
||||||
|
|
||||||
|
# 颜色输出
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# 日志函数
|
||||||
|
log_info() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 服务列表
|
||||||
|
SERVICES=(
|
||||||
|
"emotion-gateway:19000"
|
||||||
|
"emotion-user:19001"
|
||||||
|
"emotion-ai:19002"
|
||||||
|
"emotion-record:19003"
|
||||||
|
"emotion-growth:19004"
|
||||||
|
"emotion-websocket:19007"
|
||||||
|
"emotion-auth:19008"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 部署状态跟踪
|
||||||
|
declare -A DEPLOYMENT_STATUS
|
||||||
|
declare -A DEPLOYMENT_ERRORS
|
||||||
|
declare -A DEPLOYMENT_TIMES
|
||||||
|
TOTAL_SERVICES=${#SERVICES[@]}
|
||||||
|
SUCCESSFUL_DEPLOYMENTS=0
|
||||||
|
FAILED_DEPLOYMENTS=0
|
||||||
|
|
||||||
|
# 检查远程服务器连接
|
||||||
|
check_remote_connection() {
|
||||||
|
log_info "检查远程服务器连接..."
|
||||||
|
if ssh -o ConnectTimeout=10 $REMOTE_HOST "echo 'Connection successful'" > /dev/null 2>&1; then
|
||||||
|
log_success "远程服务器连接正常"
|
||||||
|
else
|
||||||
|
log_error "无法连接到远程服务器 $REMOTE_HOST"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 创建远程目录
|
||||||
|
create_remote_directories() {
|
||||||
|
log_info "创建远程目录结构..."
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
mkdir -p $REMOTE_BUILD_DIR
|
||||||
|
mkdir -p $REMOTE_DOCKER_COMPOSE_DIR
|
||||||
|
mkdir -p /data/logs/emotion-museum
|
||||||
|
mkdir -p /data/config/emotion-museum
|
||||||
|
"
|
||||||
|
log_success "远程目录创建完成"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 构建所有服务
|
||||||
|
build_all_services() {
|
||||||
|
log_info "开始构建所有微服务..."
|
||||||
|
|
||||||
|
# 先构建父项目
|
||||||
|
log_info "构建父项目..."
|
||||||
|
mvn clean install -DskipTests -q
|
||||||
|
|
||||||
|
for service_info in "${SERVICES[@]}"; do
|
||||||
|
service_name=$(echo $service_info | cut -d':' -f1)
|
||||||
|
log_info "构建服务: $service_name"
|
||||||
|
|
||||||
|
cd $service_name
|
||||||
|
if mvn clean package -DskipTests -Ptest -q; then
|
||||||
|
log_success "服务 $service_name 构建成功"
|
||||||
|
else
|
||||||
|
log_error "服务 $service_name 构建失败"
|
||||||
|
cd ..
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
cd ..
|
||||||
|
done
|
||||||
|
|
||||||
|
log_success "所有服务构建完成"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 部署单个服务
|
||||||
|
deploy_service() {
|
||||||
|
local service_name=$1
|
||||||
|
local service_port=$2
|
||||||
|
local start_time=$(date +%s)
|
||||||
|
|
||||||
|
log_info "开始部署服务: $service_name"
|
||||||
|
DEPLOYMENT_STATUS[$service_name]="DEPLOYING"
|
||||||
|
|
||||||
|
# 检查jar包是否存在
|
||||||
|
local jar_file="${service_name}/target/${service_name}-1.0.0.jar"
|
||||||
|
if [ ! -f "$jar_file" ]; then
|
||||||
|
local error_msg="JAR包不存在: $jar_file"
|
||||||
|
log_error "$error_msg"
|
||||||
|
DEPLOYMENT_STATUS[$service_name]="FAILED"
|
||||||
|
DEPLOYMENT_ERRORS[$service_name]="$error_msg"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 删除远程旧jar包
|
||||||
|
log_info "删除远程旧jar包: $service_name"
|
||||||
|
ssh $REMOTE_HOST "rm -f $REMOTE_BUILD_DIR/${service_name}-*.jar"
|
||||||
|
|
||||||
|
# 上传新jar包
|
||||||
|
log_info "上传jar包: $service_name"
|
||||||
|
if scp "$jar_file" "$REMOTE_HOST:$REMOTE_BUILD_DIR/${service_name}-1.0.0.jar"; then
|
||||||
|
log_success "jar包上传成功: $service_name"
|
||||||
|
else
|
||||||
|
log_error "jar包上传失败: $service_name"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建Dockerfile
|
||||||
|
create_dockerfile $service_name $service_port
|
||||||
|
|
||||||
|
# 停止并删除旧容器
|
||||||
|
log_info "停止旧容器: $service_name"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
docker stop ${service_name} 2>/dev/null || true
|
||||||
|
docker rm ${service_name} 2>/dev/null || true
|
||||||
|
docker rmi ${PROJECT_NAME}/${service_name}:latest 2>/dev/null || true
|
||||||
|
"
|
||||||
|
|
||||||
|
# 构建Docker镜像
|
||||||
|
log_info "构建Docker镜像: $service_name"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
cd $REMOTE_DOCKER_COMPOSE_DIR
|
||||||
|
docker build -t ${PROJECT_NAME}/${service_name}:latest -f Dockerfile.${service_name} .
|
||||||
|
"
|
||||||
|
|
||||||
|
# 启动新容器
|
||||||
|
log_info "启动新容器: $service_name"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
docker run -d \\
|
||||||
|
--name ${service_name} \\
|
||||||
|
--network emotion-network \\
|
||||||
|
-p ${service_port}:${service_port} \\
|
||||||
|
-v /data/logs/emotion-museum:/app/logs \\
|
||||||
|
-e SPRING_PROFILES_ACTIVE=${PROFILE} \\
|
||||||
|
-e MYSQL_HOST=47.111.10.27 \\
|
||||||
|
-e MYSQL_PORT=3306 \\
|
||||||
|
-e MYSQL_DATABASE=emotion_museum \\
|
||||||
|
-e MYSQL_USERNAME=root \\
|
||||||
|
-e REDIS_HOST=47.111.10.27 \\
|
||||||
|
-e REDIS_PORT=6379 \\
|
||||||
|
-e REDIS_PASSWORD= \\
|
||||||
|
-e REDIS_DATABASE=0 \\
|
||||||
|
-e NACOS_SERVER_ADDR=47.111.10.27:8848 \\
|
||||||
|
-e NACOS_USERNAME=nacos \\
|
||||||
|
-e NACOS_PASSWORD=Peanut2817*# \\
|
||||||
|
--restart unless-stopped \\
|
||||||
|
${PROJECT_NAME}/${service_name}:latest
|
||||||
|
"
|
||||||
|
|
||||||
|
# 等待服务启动
|
||||||
|
log_info "等待服务启动: $service_name"
|
||||||
|
sleep 10
|
||||||
|
|
||||||
|
# 检查容器状态
|
||||||
|
if ssh $REMOTE_HOST "docker ps | grep ${service_name}" > /dev/null 2>&1; then
|
||||||
|
log_success "服务 $service_name 启动成功"
|
||||||
|
|
||||||
|
# 显示容器日志
|
||||||
|
log_info "显示服务日志 (最后10行): $service_name"
|
||||||
|
ssh $REMOTE_HOST "docker logs --tail 10 ${service_name}" 2>/dev/null || true
|
||||||
|
|
||||||
|
# 记录成功状态
|
||||||
|
local end_time=$(date +%s)
|
||||||
|
local duration=$((end_time - start_time))
|
||||||
|
DEPLOYMENT_STATUS[$service_name]="SUCCESS"
|
||||||
|
DEPLOYMENT_TIMES[$service_name]="${duration}s"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
local error_msg="服务启动失败"
|
||||||
|
log_error "服务 $service_name 启动失败"
|
||||||
|
log_error "错误日志:"
|
||||||
|
local error_logs=$(ssh $REMOTE_HOST "docker logs ${service_name}" 2>&1 || echo "无法获取日志")
|
||||||
|
echo "$error_logs"
|
||||||
|
|
||||||
|
# 记录失败状态
|
||||||
|
local end_time=$(date +%s)
|
||||||
|
local duration=$((end_time - start_time))
|
||||||
|
DEPLOYMENT_STATUS[$service_name]="FAILED"
|
||||||
|
DEPLOYMENT_ERRORS[$service_name]="$error_msg: $error_logs"
|
||||||
|
DEPLOYMENT_TIMES[$service_name]="${duration}s"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 创建Dockerfile
|
||||||
|
create_dockerfile() {
|
||||||
|
local service_name=$1
|
||||||
|
local service_port=$2
|
||||||
|
|
||||||
|
log_info "创建Dockerfile: $service_name"
|
||||||
|
|
||||||
|
ssh $REMOTE_HOST "cat > $REMOTE_DOCKER_COMPOSE_DIR/Dockerfile.${service_name} << 'EOF'
|
||||||
|
FROM openjdk:17-jre-slim
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 安装必要的工具
|
||||||
|
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# 复制jar包
|
||||||
|
COPY $REMOTE_BUILD_DIR/${service_name}-1.0.0.jar app.jar
|
||||||
|
|
||||||
|
# 创建日志目录
|
||||||
|
RUN mkdir -p /app/logs
|
||||||
|
|
||||||
|
# 设置时区
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/\$TZ /etc/localtime && echo \$TZ > /etc/timezone
|
||||||
|
|
||||||
|
# 暴露端口
|
||||||
|
EXPOSE ${service_port}
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \\
|
||||||
|
CMD curl -f http://localhost:${service_port}/actuator/health || exit 1
|
||||||
|
|
||||||
|
# 启动应用
|
||||||
|
ENTRYPOINT [\"java\", \"-Djava.security.egd=file:/dev/./urandom\", \"-Xms512m\", \"-Xmx1024m\", \"-jar\", \"app.jar\"]
|
||||||
|
EOF"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 创建Docker网络
|
||||||
|
create_docker_network() {
|
||||||
|
log_info "创建Docker网络..."
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
docker network create emotion-network 2>/dev/null || true
|
||||||
|
"
|
||||||
|
log_success "Docker网络创建完成"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
health_check() {
|
||||||
|
log_info "执行服务健康检查..."
|
||||||
|
|
||||||
|
for service_info in "${SERVICES[@]}"; do
|
||||||
|
service_name=$(echo $service_info | cut -d':' -f1)
|
||||||
|
service_port=$(echo $service_info | cut -d':' -f2)
|
||||||
|
|
||||||
|
log_info "检查服务健康状态: $service_name"
|
||||||
|
|
||||||
|
# 等待服务完全启动
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
if ssh $REMOTE_HOST "curl -f -s http://localhost:${service_port}/actuator/health" > /dev/null 2>&1; then
|
||||||
|
log_success "服务 $service_name 健康检查通过"
|
||||||
|
else
|
||||||
|
log_warning "服务 $service_name 健康检查失败,可能仍在启动中"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# 显示详细部署报告
|
||||||
|
show_deployment_report() {
|
||||||
|
local total_time=$1
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "========================================"
|
||||||
|
echo " 部署完成报告"
|
||||||
|
echo "========================================"
|
||||||
|
echo "项目名称: $PROJECT_NAME"
|
||||||
|
echo "部署环境: $PROFILE"
|
||||||
|
echo "目标服务器: $REMOTE_HOST"
|
||||||
|
echo "部署时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
|
echo "总耗时: ${total_time}s"
|
||||||
|
if [ "$BUILD_NUMBER" != "manual" ]; then
|
||||||
|
echo "Jenkins构建: #$BUILD_NUMBER"
|
||||||
|
echo "Jenkins任务: $JOB_NAME"
|
||||||
|
[ -n "$BUILD_URL" ] && echo "构建链接: $BUILD_URL"
|
||||||
|
fi
|
||||||
|
echo "========================================"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "📊 部署统计:"
|
||||||
|
echo " 总服务数: $TOTAL_SERVICES"
|
||||||
|
echo " 成功部署: $SUCCESSFUL_DEPLOYMENTS"
|
||||||
|
echo " 失败部署: $FAILED_DEPLOYMENTS"
|
||||||
|
echo " 成功率: $(( SUCCESSFUL_DEPLOYMENTS * 100 / TOTAL_SERVICES ))%"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "📋 服务部署详情:"
|
||||||
|
printf "%-20s %-10s %-10s %s\n" "服务名称" "状态" "耗时" "备注"
|
||||||
|
echo "----------------------------------------"
|
||||||
|
|
||||||
|
for service_info in "${SERVICES[@]}"; do
|
||||||
|
service_name=$(echo $service_info | cut -d':' -f1)
|
||||||
|
service_port=$(echo $service_info | cut -d':' -f2)
|
||||||
|
status=${DEPLOYMENT_STATUS[$service_name]:-"UNKNOWN"}
|
||||||
|
time=${DEPLOYMENT_TIMES[$service_name]:-"N/A"}
|
||||||
|
|
||||||
|
case $status in
|
||||||
|
"SUCCESS")
|
||||||
|
printf "%-20s ${GREEN}%-10s${NC} %-10s %s\n" "$service_name" "✅ 成功" "$time" "http://47.111.10.27:$service_port"
|
||||||
|
;;
|
||||||
|
"FAILED")
|
||||||
|
printf "%-20s ${RED}%-10s${NC} %-10s %s\n" "$service_name" "❌ 失败" "$time" "查看错误日志"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf "%-20s ${YELLOW}%-10s${NC} %-10s %s\n" "$service_name" "⚠️ 未知" "$time" "状态异常"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 显示失败服务的错误信息
|
||||||
|
if [ $FAILED_DEPLOYMENTS -gt 0 ]; then
|
||||||
|
echo "❌ 失败服务错误详情:"
|
||||||
|
echo "----------------------------------------"
|
||||||
|
for service_info in "${SERVICES[@]}"; do
|
||||||
|
service_name=$(echo $service_info | cut -d':' -f1)
|
||||||
|
if [ "${DEPLOYMENT_STATUS[$service_name]}" = "FAILED" ]; then
|
||||||
|
echo "🔸 $service_name:"
|
||||||
|
echo " ${DEPLOYMENT_ERRORS[$service_name]}" | head -3
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 显示当前运行的容器状态
|
||||||
|
echo "🐳 当前容器运行状态:"
|
||||||
|
echo "----------------------------------------"
|
||||||
|
ssh $REMOTE_HOST "docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' | grep emotion || echo '没有运行的emotion相关容器'"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "========================================"
|
||||||
|
|
||||||
|
# 根据部署结果设置退出码
|
||||||
|
if [ $FAILED_DEPLOYMENTS -eq 0 ]; then
|
||||||
|
echo "🎉 所有服务部署成功!"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "⚠️ 部分服务部署失败,请检查错误日志"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 主函数
|
||||||
|
main() {
|
||||||
|
local start_time=$(date +%s)
|
||||||
|
|
||||||
|
log_info "🚀 开始全服务容器化部署..."
|
||||||
|
log_info "目标服务器: $REMOTE_HOST"
|
||||||
|
log_info "部署环境: $PROFILE"
|
||||||
|
log_info "服务总数: $TOTAL_SERVICES"
|
||||||
|
|
||||||
|
# 检查连接
|
||||||
|
if ! check_remote_connection; then
|
||||||
|
log_error "远程服务器连接失败,部署终止"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建目录
|
||||||
|
create_remote_directories
|
||||||
|
|
||||||
|
# 创建Docker网络
|
||||||
|
create_docker_network
|
||||||
|
|
||||||
|
# 构建服务
|
||||||
|
if ! build_all_services; then
|
||||||
|
log_error "服务构建失败,部署终止"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 部署所有服务 - 单个失败不影响其他服务
|
||||||
|
log_info "开始逐个部署服务..."
|
||||||
|
for service_info in "${SERVICES[@]}"; do
|
||||||
|
service_name=$(echo $service_info | cut -d':' -f1)
|
||||||
|
service_port=$(echo $service_info | cut -d':' -f2)
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
log_info "[$((SUCCESSFUL_DEPLOYMENTS + FAILED_DEPLOYMENTS + 1))/$TOTAL_SERVICES] 部署服务: $service_name"
|
||||||
|
|
||||||
|
if deploy_service $service_name $service_port; then
|
||||||
|
SUCCESSFUL_DEPLOYMENTS=$((SUCCESSFUL_DEPLOYMENTS + 1))
|
||||||
|
log_success "✅ 服务 $service_name 部署成功"
|
||||||
|
else
|
||||||
|
FAILED_DEPLOYMENTS=$((FAILED_DEPLOYMENTS + 1))
|
||||||
|
log_error "❌ 服务 $service_name 部署失败,继续部署其他服务..."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
log_info "执行服务健康检查..."
|
||||||
|
health_check
|
||||||
|
|
||||||
|
# 计算总耗时
|
||||||
|
local end_time=$(date +%s)
|
||||||
|
local total_time=$((end_time - start_time))
|
||||||
|
|
||||||
|
# 显示详细报告
|
||||||
|
show_deployment_report $total_time
|
||||||
|
|
||||||
|
# 根据部署结果设置退出码
|
||||||
|
if [ $FAILED_DEPLOYMENTS -eq 0 ]; then
|
||||||
|
log_success "🎉 全服务容器化部署完成!"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
log_warning "⚠️ 部分服务部署失败,请查看详细报告"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 执行主函数
|
||||||
|
main "$@"
|
||||||
Executable
+218
@@ -0,0 +1,218 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# emotion-ai 单独部署脚本
|
||||||
|
# 作者: emotion-museum
|
||||||
|
# 日期: 2025-07-18
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 配置变量
|
||||||
|
SERVICE_NAME="emotion-ai"
|
||||||
|
SERVICE_PORT="19002"
|
||||||
|
REMOTE_HOST="root@47.111.10.27"
|
||||||
|
REMOTE_BUILD_DIR="/data/builds"
|
||||||
|
REMOTE_DOCKER_COMPOSE_DIR="/data/docker"
|
||||||
|
PROFILE="test"
|
||||||
|
PROJECT_NAME="emotion-museum"
|
||||||
|
|
||||||
|
# 颜色输出
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
# 日志函数
|
||||||
|
log_info() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查远程服务器连接
|
||||||
|
check_remote_connection() {
|
||||||
|
log_info "检查远程服务器连接..."
|
||||||
|
if ssh -o ConnectTimeout=10 $REMOTE_HOST "echo 'Connection successful'" > /dev/null 2>&1; then
|
||||||
|
log_success "远程服务器连接正常"
|
||||||
|
else
|
||||||
|
log_error "无法连接到远程服务器 $REMOTE_HOST"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 构建服务
|
||||||
|
build_service() {
|
||||||
|
log_info "构建服务: $SERVICE_NAME"
|
||||||
|
|
||||||
|
# 构建父项目依赖
|
||||||
|
cd ..
|
||||||
|
mvn clean install -DskipTests -q
|
||||||
|
cd $SERVICE_NAME
|
||||||
|
|
||||||
|
# 构建当前服务
|
||||||
|
if mvn clean package -DskipTests -Ptest -q; then
|
||||||
|
log_success "服务 $SERVICE_NAME 构建成功"
|
||||||
|
else
|
||||||
|
log_error "服务 $SERVICE_NAME 构建失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 创建Dockerfile
|
||||||
|
create_dockerfile() {
|
||||||
|
log_info "创建Dockerfile: $SERVICE_NAME"
|
||||||
|
|
||||||
|
ssh $REMOTE_HOST "cat > $REMOTE_DOCKER_COMPOSE_DIR/Dockerfile.${SERVICE_NAME} << 'EOF'
|
||||||
|
FROM openjdk:17-jre-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY $REMOTE_BUILD_DIR/${SERVICE_NAME}-1.0.0.jar app.jar
|
||||||
|
|
||||||
|
RUN mkdir -p /app/logs
|
||||||
|
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/\$TZ /etc/localtime && echo \$TZ > /etc/timezone
|
||||||
|
|
||||||
|
EXPOSE ${SERVICE_PORT}
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \\
|
||||||
|
CMD curl -f http://localhost:${SERVICE_PORT}/actuator/health || exit 1
|
||||||
|
|
||||||
|
ENTRYPOINT [\"java\", \"-Djava.security.egd=file:/dev/./urandom\", \"-Xms512m\", \"-Xmx1024m\", \"-jar\", \"app.jar\"]
|
||||||
|
EOF"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 部署服务
|
||||||
|
deploy_service() {
|
||||||
|
log_info "开始部署服务: $SERVICE_NAME"
|
||||||
|
|
||||||
|
# 检查jar包
|
||||||
|
local jar_file="target/${SERVICE_NAME}-1.0.0.jar"
|
||||||
|
if [ ! -f "$jar_file" ]; then
|
||||||
|
log_error "JAR包不存在: $jar_file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建远程目录
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
mkdir -p $REMOTE_BUILD_DIR
|
||||||
|
mkdir -p $REMOTE_DOCKER_COMPOSE_DIR
|
||||||
|
mkdir -p /data/logs/emotion-museum
|
||||||
|
"
|
||||||
|
|
||||||
|
# 删除旧jar包
|
||||||
|
log_info "删除远程旧jar包"
|
||||||
|
ssh $REMOTE_HOST "rm -f $REMOTE_BUILD_DIR/${SERVICE_NAME}-*.jar"
|
||||||
|
|
||||||
|
# 上传新jar包
|
||||||
|
log_info "上传jar包"
|
||||||
|
if scp "$jar_file" "$REMOTE_HOST:$REMOTE_BUILD_DIR/${SERVICE_NAME}-1.0.0.jar"; then
|
||||||
|
log_success "jar包上传成功"
|
||||||
|
else
|
||||||
|
log_error "jar包上传失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建Dockerfile
|
||||||
|
create_dockerfile
|
||||||
|
|
||||||
|
# 停止旧容器
|
||||||
|
log_info "停止旧容器"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
docker stop ${SERVICE_NAME} 2>/dev/null || true
|
||||||
|
docker rm ${SERVICE_NAME} 2>/dev/null || true
|
||||||
|
docker rmi ${PROJECT_NAME}/${SERVICE_NAME}:latest 2>/dev/null || true
|
||||||
|
"
|
||||||
|
|
||||||
|
# 创建Docker网络
|
||||||
|
ssh $REMOTE_HOST "docker network create emotion-network 2>/dev/null || true"
|
||||||
|
|
||||||
|
# 构建镜像
|
||||||
|
log_info "构建Docker镜像"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
cd $REMOTE_DOCKER_COMPOSE_DIR
|
||||||
|
docker build -t ${PROJECT_NAME}/${SERVICE_NAME}:latest -f Dockerfile.${SERVICE_NAME} .
|
||||||
|
"
|
||||||
|
|
||||||
|
# 启动容器
|
||||||
|
log_info "启动新容器"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
docker run -d \\
|
||||||
|
--name ${SERVICE_NAME} \\
|
||||||
|
--network emotion-network \\
|
||||||
|
-p ${SERVICE_PORT}:${SERVICE_PORT} \\
|
||||||
|
-v /data/logs/emotion-museum:/app/logs \\
|
||||||
|
-e SPRING_PROFILES_ACTIVE=${PROFILE} \\
|
||||||
|
-e MYSQL_HOST=47.111.10.27 \\
|
||||||
|
-e MYSQL_PORT=3306 \\
|
||||||
|
-e MYSQL_DATABASE=emotion_museum \\
|
||||||
|
-e MYSQL_USERNAME=root \\
|
||||||
|
-e REDIS_HOST=47.111.10.27 \\
|
||||||
|
-e REDIS_PORT=6379 \\
|
||||||
|
-e REDIS_PASSWORD= \\
|
||||||
|
-e REDIS_DATABASE=0 \\
|
||||||
|
-e NACOS_SERVER_ADDR=47.111.10.27:8848 \\
|
||||||
|
-e NACOS_USERNAME=nacos \\
|
||||||
|
-e NACOS_PASSWORD=Peanut2817*# \\
|
||||||
|
-e COZE_API_TOKEN=\${COZE_API_TOKEN} \\
|
||||||
|
--restart unless-stopped \\
|
||||||
|
${PROJECT_NAME}/${SERVICE_NAME}:latest
|
||||||
|
"
|
||||||
|
|
||||||
|
# 等待启动
|
||||||
|
log_info "等待服务启动..."
|
||||||
|
sleep 15
|
||||||
|
|
||||||
|
# 检查状态
|
||||||
|
if ssh $REMOTE_HOST "docker ps | grep ${SERVICE_NAME}" > /dev/null; then
|
||||||
|
log_success "服务启动成功"
|
||||||
|
|
||||||
|
# 显示日志
|
||||||
|
log_info "服务日志 (最后20行):"
|
||||||
|
ssh $REMOTE_HOST "docker logs --tail 20 ${SERVICE_NAME}"
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
log_info "执行健康检查..."
|
||||||
|
sleep 10
|
||||||
|
if ssh $REMOTE_HOST "curl -f -s http://localhost:${SERVICE_PORT}/actuator/health" > /dev/null 2>&1; then
|
||||||
|
log_success "健康检查通过"
|
||||||
|
else
|
||||||
|
log_warning "健康检查失败,服务可能仍在启动中"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_error "服务启动失败"
|
||||||
|
ssh $REMOTE_HOST "docker logs ${SERVICE_NAME}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 主函数
|
||||||
|
main() {
|
||||||
|
log_info "开始部署 $SERVICE_NAME 服务"
|
||||||
|
log_info "目标服务器: $REMOTE_HOST"
|
||||||
|
log_info "服务端口: $SERVICE_PORT"
|
||||||
|
log_info "部署环境: $PROFILE"
|
||||||
|
|
||||||
|
check_remote_connection
|
||||||
|
build_service
|
||||||
|
deploy_service
|
||||||
|
|
||||||
|
log_success "$SERVICE_NAME 服务部署完成!"
|
||||||
|
log_info "访问地址: http://47.111.10.27:$SERVICE_PORT"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 执行主函数
|
||||||
|
main "$@"
|
||||||
Executable
+218
@@ -0,0 +1,218 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# emotion-auth 单独部署脚本
|
||||||
|
# 作者: emotion-museum
|
||||||
|
# 日期: 2025-07-18
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 配置变量
|
||||||
|
SERVICE_NAME="emotion-auth"
|
||||||
|
SERVICE_PORT="19008"
|
||||||
|
REMOTE_HOST="root@47.111.10.27"
|
||||||
|
REMOTE_BUILD_DIR="/data/builds"
|
||||||
|
REMOTE_DOCKER_COMPOSE_DIR="/data/docker"
|
||||||
|
PROFILE="test"
|
||||||
|
PROJECT_NAME="emotion-museum"
|
||||||
|
|
||||||
|
# 颜色输出
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
# 日志函数
|
||||||
|
log_info() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查远程服务器连接
|
||||||
|
check_remote_connection() {
|
||||||
|
log_info "检查远程服务器连接..."
|
||||||
|
if ssh -o ConnectTimeout=10 $REMOTE_HOST "echo 'Connection successful'" > /dev/null 2>&1; then
|
||||||
|
log_success "远程服务器连接正常"
|
||||||
|
else
|
||||||
|
log_error "无法连接到远程服务器 $REMOTE_HOST"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 构建服务
|
||||||
|
build_service() {
|
||||||
|
log_info "构建服务: $SERVICE_NAME"
|
||||||
|
|
||||||
|
# 构建父项目依赖
|
||||||
|
cd ..
|
||||||
|
mvn clean install -DskipTests -q
|
||||||
|
cd $SERVICE_NAME
|
||||||
|
|
||||||
|
# 构建当前服务
|
||||||
|
if mvn clean package -DskipTests -Ptest -q; then
|
||||||
|
log_success "服务 $SERVICE_NAME 构建成功"
|
||||||
|
else
|
||||||
|
log_error "服务 $SERVICE_NAME 构建失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 创建Dockerfile
|
||||||
|
create_dockerfile() {
|
||||||
|
log_info "创建Dockerfile: $SERVICE_NAME"
|
||||||
|
|
||||||
|
ssh $REMOTE_HOST "cat > $REMOTE_DOCKER_COMPOSE_DIR/Dockerfile.${SERVICE_NAME} << 'EOF'
|
||||||
|
FROM openjdk:17-jre-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY $REMOTE_BUILD_DIR/${SERVICE_NAME}-1.0.0.jar app.jar
|
||||||
|
|
||||||
|
RUN mkdir -p /app/logs
|
||||||
|
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/\$TZ /etc/localtime && echo \$TZ > /etc/timezone
|
||||||
|
|
||||||
|
EXPOSE ${SERVICE_PORT}
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \\
|
||||||
|
CMD curl -f http://localhost:${SERVICE_PORT}/actuator/health || exit 1
|
||||||
|
|
||||||
|
ENTRYPOINT [\"java\", \"-Djava.security.egd=file:/dev/./urandom\", \"-Xms512m\", \"-Xmx1024m\", \"-jar\", \"app.jar\"]
|
||||||
|
EOF"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 部署服务
|
||||||
|
deploy_service() {
|
||||||
|
log_info "开始部署服务: $SERVICE_NAME"
|
||||||
|
|
||||||
|
# 检查jar包
|
||||||
|
local jar_file="target/${SERVICE_NAME}-1.0.0.jar"
|
||||||
|
if [ ! -f "$jar_file" ]; then
|
||||||
|
log_error "JAR包不存在: $jar_file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建远程目录
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
mkdir -p $REMOTE_BUILD_DIR
|
||||||
|
mkdir -p $REMOTE_DOCKER_COMPOSE_DIR
|
||||||
|
mkdir -p /data/logs/emotion-museum
|
||||||
|
"
|
||||||
|
|
||||||
|
# 删除旧jar包
|
||||||
|
log_info "删除远程旧jar包"
|
||||||
|
ssh $REMOTE_HOST "rm -f $REMOTE_BUILD_DIR/${SERVICE_NAME}-*.jar"
|
||||||
|
|
||||||
|
# 上传新jar包
|
||||||
|
log_info "上传jar包"
|
||||||
|
if scp "$jar_file" "$REMOTE_HOST:$REMOTE_BUILD_DIR/${SERVICE_NAME}-1.0.0.jar"; then
|
||||||
|
log_success "jar包上传成功"
|
||||||
|
else
|
||||||
|
log_error "jar包上传失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建Dockerfile
|
||||||
|
create_dockerfile
|
||||||
|
|
||||||
|
# 停止旧容器
|
||||||
|
log_info "停止旧容器"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
docker stop ${SERVICE_NAME} 2>/dev/null || true
|
||||||
|
docker rm ${SERVICE_NAME} 2>/dev/null || true
|
||||||
|
docker rmi ${PROJECT_NAME}/${SERVICE_NAME}:latest 2>/dev/null || true
|
||||||
|
"
|
||||||
|
|
||||||
|
# 创建Docker网络
|
||||||
|
ssh $REMOTE_HOST "docker network create emotion-network 2>/dev/null || true"
|
||||||
|
|
||||||
|
# 构建镜像
|
||||||
|
log_info "构建Docker镜像"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
cd $REMOTE_DOCKER_COMPOSE_DIR
|
||||||
|
docker build -t ${PROJECT_NAME}/${SERVICE_NAME}:latest -f Dockerfile.${SERVICE_NAME} .
|
||||||
|
"
|
||||||
|
|
||||||
|
# 启动容器
|
||||||
|
log_info "启动新容器"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
docker run -d \\
|
||||||
|
--name ${SERVICE_NAME} \\
|
||||||
|
--network emotion-network \\
|
||||||
|
-p ${SERVICE_PORT}:${SERVICE_PORT} \\
|
||||||
|
-v /data/logs/emotion-museum:/app/logs \\
|
||||||
|
-e SPRING_PROFILES_ACTIVE=${PROFILE} \\
|
||||||
|
-e MYSQL_HOST=47.111.10.27 \\
|
||||||
|
-e MYSQL_PORT=3306 \\
|
||||||
|
-e MYSQL_DATABASE=emotion_museum \\
|
||||||
|
-e MYSQL_USERNAME=root \\
|
||||||
|
-e REDIS_HOST=47.111.10.27 \\
|
||||||
|
-e REDIS_PORT=6379 \\
|
||||||
|
-e REDIS_PASSWORD= \\
|
||||||
|
-e REDIS_DATABASE=0 \\
|
||||||
|
-e NACOS_SERVER_ADDR=47.111.10.27:8848 \\
|
||||||
|
-e NACOS_USERNAME=nacos \\
|
||||||
|
-e NACOS_PASSWORD=Peanut2817*# \\
|
||||||
|
-e JWT_SECRET=emotion-museum-secret-key-2025 \\
|
||||||
|
--restart unless-stopped \\
|
||||||
|
${PROJECT_NAME}/${SERVICE_NAME}:latest
|
||||||
|
"
|
||||||
|
|
||||||
|
# 等待启动
|
||||||
|
log_info "等待服务启动..."
|
||||||
|
sleep 15
|
||||||
|
|
||||||
|
# 检查状态
|
||||||
|
if ssh $REMOTE_HOST "docker ps | grep ${SERVICE_NAME}" > /dev/null; then
|
||||||
|
log_success "服务启动成功"
|
||||||
|
|
||||||
|
# 显示日志
|
||||||
|
log_info "服务日志 (最后20行):"
|
||||||
|
ssh $REMOTE_HOST "docker logs --tail 20 ${SERVICE_NAME}"
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
log_info "执行健康检查..."
|
||||||
|
sleep 10
|
||||||
|
if ssh $REMOTE_HOST "curl -f -s http://localhost:${SERVICE_PORT}/actuator/health" > /dev/null 2>&1; then
|
||||||
|
log_success "健康检查通过"
|
||||||
|
else
|
||||||
|
log_warning "健康检查失败,服务可能仍在启动中"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_error "服务启动失败"
|
||||||
|
ssh $REMOTE_HOST "docker logs ${SERVICE_NAME}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 主函数
|
||||||
|
main() {
|
||||||
|
log_info "开始部署 $SERVICE_NAME 服务"
|
||||||
|
log_info "目标服务器: $REMOTE_HOST"
|
||||||
|
log_info "服务端口: $SERVICE_PORT"
|
||||||
|
log_info "部署环境: $PROFILE"
|
||||||
|
|
||||||
|
check_remote_connection
|
||||||
|
build_service
|
||||||
|
deploy_service
|
||||||
|
|
||||||
|
log_success "$SERVICE_NAME 服务部署完成!"
|
||||||
|
log_info "访问地址: http://47.111.10.27:$SERVICE_PORT"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 执行主函数
|
||||||
|
main "$@"
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.emotionmuseum.auth.config;
|
package com.emotionmuseum.auth.config;
|
||||||
|
|
||||||
import com.wf.captcha.ArithmeticCaptcha;
|
|
||||||
import com.wf.captcha.ChineseCaptcha;
|
import com.wf.captcha.ChineseCaptcha;
|
||||||
import com.wf.captcha.GifCaptcha;
|
import com.wf.captcha.GifCaptcha;
|
||||||
import com.wf.captcha.SpecCaptcha;
|
import com.wf.captcha.SpecCaptcha;
|
||||||
@@ -18,15 +17,15 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
public class CaptchaConfig {
|
public class CaptchaConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 算术验证码
|
* 算术验证码 - 暂时禁用,因为Java 23中JavaScript引擎问题
|
||||||
*/
|
*/
|
||||||
@Bean("arithmeticCaptcha")
|
// @Bean("arithmeticCaptcha")
|
||||||
public Captcha arithmeticCaptcha() {
|
// public Captcha arithmeticCaptcha() {
|
||||||
ArithmeticCaptcha captcha = new ArithmeticCaptcha(130, 48);
|
// ArithmeticCaptcha captcha = new ArithmeticCaptcha(130, 48);
|
||||||
captcha.setLen(2); // 几位数运算,默认是两位
|
// captcha.setLen(2); // 几位数运算,默认是两位
|
||||||
captcha.getArithmeticString(); // 获取运算的公式:3+2=?
|
// // captcha.getArithmeticString(); // 获取运算的公式:3+2=?
|
||||||
return captcha;
|
// return captcha;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 中文验证码
|
* 中文验证码
|
||||||
|
|||||||
@@ -3,16 +3,18 @@ package com.emotionmuseum.auth.config;
|
|||||||
import me.zhyd.oauth.config.AuthConfig;
|
import me.zhyd.oauth.config.AuthConfig;
|
||||||
import me.zhyd.oauth.request.*;
|
import me.zhyd.oauth.request.*;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 第三方登录配置
|
* 第三方登录配置
|
||||||
*
|
*
|
||||||
* @author emotion-museum
|
* @author emotion-museum
|
||||||
* @since 2025-07-15
|
* @since 2025-07-15
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@ConditionalOnProperty(name = "oauth.enabled", havingValue = "true", matchIfMissing = false)
|
||||||
public class OAuthConfig {
|
public class OAuthConfig {
|
||||||
|
|
||||||
@Value("${oauth.wechat.client-id:}")
|
@Value("${oauth.wechat.client-id:}")
|
||||||
@@ -46,6 +48,7 @@ public class OAuthConfig {
|
|||||||
* 微信开放平台登录
|
* 微信开放平台登录
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
|
@ConditionalOnProperty(name = "oauth.wechat.client-id", matchIfMissing = false)
|
||||||
public AuthWeChatOpenRequest weChatOpenRequest() {
|
public AuthWeChatOpenRequest weChatOpenRequest() {
|
||||||
return new AuthWeChatOpenRequest(AuthConfig.builder()
|
return new AuthWeChatOpenRequest(AuthConfig.builder()
|
||||||
.clientId(wechatClientId)
|
.clientId(wechatClientId)
|
||||||
@@ -58,6 +61,7 @@ public class OAuthConfig {
|
|||||||
* 微信公众平台登录
|
* 微信公众平台登录
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
|
@ConditionalOnProperty(name = "oauth.wechat-mp.client-id", matchIfMissing = false)
|
||||||
public AuthWeChatMpRequest weChatMpRequest() {
|
public AuthWeChatMpRequest weChatMpRequest() {
|
||||||
return new AuthWeChatMpRequest(AuthConfig.builder()
|
return new AuthWeChatMpRequest(AuthConfig.builder()
|
||||||
.clientId(wechatMpClientId)
|
.clientId(wechatMpClientId)
|
||||||
@@ -70,6 +74,7 @@ public class OAuthConfig {
|
|||||||
* QQ登录
|
* QQ登录
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
|
@ConditionalOnProperty(name = "oauth.qq.client-id", matchIfMissing = false)
|
||||||
public AuthQqRequest qqRequest() {
|
public AuthQqRequest qqRequest() {
|
||||||
return new AuthQqRequest(AuthConfig.builder()
|
return new AuthQqRequest(AuthConfig.builder()
|
||||||
.clientId(qqClientId)
|
.clientId(qqClientId)
|
||||||
|
|||||||
+101
@@ -0,0 +1,101 @@
|
|||||||
|
package com.emotionmuseum.auth.service.impl;
|
||||||
|
|
||||||
|
import com.emotionmuseum.auth.dto.OAuthLoginRequest;
|
||||||
|
import com.emotionmuseum.auth.service.OAuthService;
|
||||||
|
import com.emotionmuseum.auth.vo.LoginResponse;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第三方登录服务实现类
|
||||||
|
*
|
||||||
|
* @author emotion-museum
|
||||||
|
* @since 2025-07-15
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class OAuthServiceImpl implements OAuthService {
|
||||||
|
|
||||||
|
@Value("${oauth.wechat.client-id:}")
|
||||||
|
private String wechatClientId;
|
||||||
|
|
||||||
|
@Value("${oauth.wechat.client-secret:}")
|
||||||
|
private String wechatClientSecret;
|
||||||
|
|
||||||
|
@Value("${oauth.wechat.redirect-uri:}")
|
||||||
|
private String wechatRedirectUri;
|
||||||
|
|
||||||
|
@Value("${oauth.qq.client-id:}")
|
||||||
|
private String qqClientId;
|
||||||
|
|
||||||
|
@Value("${oauth.qq.client-secret:}")
|
||||||
|
private String qqClientSecret;
|
||||||
|
|
||||||
|
@Value("${oauth.qq.redirect-uri:}")
|
||||||
|
private String qqRedirectUri;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAuthUrl(String platform) {
|
||||||
|
log.info("获取第三方登录授权URL, platform: {}", platform);
|
||||||
|
|
||||||
|
switch (platform.toLowerCase()) {
|
||||||
|
case "wechat":
|
||||||
|
return buildWechatAuthUrl();
|
||||||
|
case "qq":
|
||||||
|
return buildQQAuthUrl();
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("不支持的第三方平台: " + platform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoginResponse oauthLogin(OAuthLoginRequest request) {
|
||||||
|
log.info("第三方登录, platform: {}, code: {}", request.getPlatform(), request.getCode());
|
||||||
|
|
||||||
|
// TODO: 实现第三方登录逻辑
|
||||||
|
// 1. 根据code获取access_token
|
||||||
|
// 2. 根据access_token获取用户信息
|
||||||
|
// 3. 查询或创建用户
|
||||||
|
// 4. 生成JWT token
|
||||||
|
|
||||||
|
throw new UnsupportedOperationException("第三方登录功能暂未实现");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getOAuthUserInfo(String platform, String code, String state) {
|
||||||
|
log.info("获取第三方用户信息, platform: {}, code: {}, state: {}", platform, code, state);
|
||||||
|
|
||||||
|
// TODO: 实现获取第三方用户信息逻辑
|
||||||
|
|
||||||
|
throw new UnsupportedOperationException("获取第三方用户信息功能暂未实现");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建微信授权URL
|
||||||
|
*/
|
||||||
|
private String buildWechatAuthUrl() {
|
||||||
|
if (wechatClientId.isEmpty()) {
|
||||||
|
throw new IllegalStateException("微信OAuth配置未完成");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 构建微信授权URL
|
||||||
|
return "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + wechatClientId +
|
||||||
|
"&redirect_uri=" + wechatRedirectUri +
|
||||||
|
"&response_type=code&scope=snsapi_userinfo&state=wechat#wechat_redirect";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建QQ授权URL
|
||||||
|
*/
|
||||||
|
private String buildQQAuthUrl() {
|
||||||
|
if (qqClientId.isEmpty()) {
|
||||||
|
throw new IllegalStateException("QQ OAuth配置未完成");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 构建QQ授权URL
|
||||||
|
return "https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=" + qqClientId +
|
||||||
|
"&redirect_uri=" + qqRedirectUri +
|
||||||
|
"&state=qq&scope=get_user_info";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
server:
|
||||||
|
port: 19008
|
||||||
|
|
||||||
|
spring:
|
||||||
|
# 数据源配置
|
||||||
|
datasource:
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
url: jdbc:mysql://localhost:3306/emotion_museum?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||||
|
username: root
|
||||||
|
password: 123456
|
||||||
|
|
||||||
|
# 连接池配置
|
||||||
|
hikari:
|
||||||
|
minimum-idle: 5
|
||||||
|
maximum-pool-size: 20
|
||||||
|
idle-timeout: 300000
|
||||||
|
connection-timeout: 20000
|
||||||
|
max-lifetime: 1200000
|
||||||
|
pool-name: EmotionAuthHikariCP
|
||||||
|
|
||||||
|
# Redis配置
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
host: localhost
|
||||||
|
port: 6379
|
||||||
|
password:
|
||||||
|
database: 0
|
||||||
|
timeout: 5000ms
|
||||||
|
lettuce:
|
||||||
|
pool:
|
||||||
|
max-active: 20
|
||||||
|
max-idle: 10
|
||||||
|
min-idle: 5
|
||||||
|
max-wait: 2000ms
|
||||||
|
|
||||||
|
# 云服务配置
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
discovery:
|
||||||
|
server-addr: localhost:8848
|
||||||
|
username: nacos
|
||||||
|
password: Peanut2817*#
|
||||||
|
config:
|
||||||
|
server-addr: localhost:8848
|
||||||
|
username: nacos
|
||||||
|
password: Peanut2817*#
|
||||||
|
|
||||||
|
# MyBatis Plus配置
|
||||||
|
mybatis-plus:
|
||||||
|
configuration:
|
||||||
|
map-underscore-to-camel-case: true
|
||||||
|
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
|
global-config:
|
||||||
|
db-config:
|
||||||
|
id-type: assign_uuid
|
||||||
|
logic-delete-field: deleted
|
||||||
|
logic-delete-value: 1
|
||||||
|
logic-not-delete-value: 0
|
||||||
|
|
||||||
|
# 监控配置
|
||||||
|
management:
|
||||||
|
endpoints:
|
||||||
|
web:
|
||||||
|
exposure:
|
||||||
|
include: health,info,metrics,prometheus
|
||||||
|
endpoint:
|
||||||
|
health:
|
||||||
|
show-details: always
|
||||||
|
metrics:
|
||||||
|
export:
|
||||||
|
prometheus:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# 日志配置
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
com.emotionmuseum: debug
|
||||||
|
com.baomidou.mybatisplus: debug
|
||||||
|
pattern:
|
||||||
|
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level [%logger{50}] - %msg%n"
|
||||||
|
|
||||||
|
# JWT配置
|
||||||
|
jwt:
|
||||||
|
secret: emotion-museum-secret-key-2025
|
||||||
|
expiration: 86400
|
||||||
|
refresh-expiration: 604800
|
||||||
|
|
||||||
|
# 验证码配置
|
||||||
|
captcha:
|
||||||
|
type: arithmetic
|
||||||
|
length: 4
|
||||||
|
expire-time: 300
|
||||||
|
|
||||||
|
# OAuth配置
|
||||||
|
oauth:
|
||||||
|
wechat:
|
||||||
|
client-id:
|
||||||
|
client-secret:
|
||||||
|
redirect-uri:
|
||||||
|
qq:
|
||||||
|
client-id:
|
||||||
|
client-secret:
|
||||||
|
redirect-uri:
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
server:
|
||||||
|
port: 19008
|
||||||
|
|
||||||
|
spring:
|
||||||
|
# 数据源配置
|
||||||
|
datasource:
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||||
|
username: ${MYSQL_USERNAME}
|
||||||
|
password: EmotionMuseum2025*#
|
||||||
|
|
||||||
|
# 连接池配置
|
||||||
|
hikari:
|
||||||
|
minimum-idle: 10
|
||||||
|
maximum-pool-size: 50
|
||||||
|
idle-timeout: 300000
|
||||||
|
connection-timeout: 20000
|
||||||
|
max-lifetime: 1200000
|
||||||
|
pool-name: EmotionAuthHikariCP
|
||||||
|
|
||||||
|
# Redis配置
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
host: ${REDIS_HOST}
|
||||||
|
port: ${REDIS_PORT}
|
||||||
|
password: ${REDIS_PASSWORD}
|
||||||
|
database: ${REDIS_DATABASE}
|
||||||
|
timeout: 5000ms
|
||||||
|
lettuce:
|
||||||
|
pool:
|
||||||
|
max-active: 50
|
||||||
|
max-idle: 20
|
||||||
|
min-idle: 10
|
||||||
|
max-wait: 2000ms
|
||||||
|
|
||||||
|
# 云服务配置
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
discovery:
|
||||||
|
server-addr: ${NACOS_SERVER_ADDR}
|
||||||
|
username: ${NACOS_USERNAME}
|
||||||
|
password: EmotionMuseum2025*#
|
||||||
|
config:
|
||||||
|
server-addr: ${NACOS_SERVER_ADDR}
|
||||||
|
username: ${NACOS_USERNAME}
|
||||||
|
password: EmotionMuseum2025*#
|
||||||
|
|
||||||
|
# MyBatis Plus配置
|
||||||
|
mybatis-plus:
|
||||||
|
configuration:
|
||||||
|
map-underscore-to-camel-case: true
|
||||||
|
log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl
|
||||||
|
global-config:
|
||||||
|
db-config:
|
||||||
|
id-type: assign_uuid
|
||||||
|
logic-delete-field: deleted
|
||||||
|
logic-delete-value: 1
|
||||||
|
logic-not-delete-value: 0
|
||||||
|
|
||||||
|
# 监控配置
|
||||||
|
management:
|
||||||
|
endpoints:
|
||||||
|
web:
|
||||||
|
exposure:
|
||||||
|
include: health,info,metrics,prometheus
|
||||||
|
endpoint:
|
||||||
|
health:
|
||||||
|
show-details: when-authorized
|
||||||
|
metrics:
|
||||||
|
export:
|
||||||
|
prometheus:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# 日志配置
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
com.emotionmuseum: info
|
||||||
|
com.baomidou.mybatisplus: warn
|
||||||
|
root: warn
|
||||||
|
pattern:
|
||||||
|
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level [%logger{50}] - %msg%n"
|
||||||
|
|
||||||
|
# JWT配置
|
||||||
|
jwt:
|
||||||
|
secret: ${JWT_SECRET}
|
||||||
|
expiration: ${JWT_EXPIRATION:86400}
|
||||||
|
refresh-expiration: ${JWT_REFRESH_EXPIRATION:604800}
|
||||||
|
|
||||||
|
# 验证码配置
|
||||||
|
captcha:
|
||||||
|
type: arithmetic
|
||||||
|
length: 4
|
||||||
|
expire-time: 300
|
||||||
|
|
||||||
|
# OAuth配置
|
||||||
|
oauth:
|
||||||
|
wechat:
|
||||||
|
client-id: ${WECHAT_CLIENT_ID}
|
||||||
|
client-secret: ${WECHAT_CLIENT_SECRET}
|
||||||
|
redirect-uri: ${WECHAT_REDIRECT_URI}
|
||||||
|
qq:
|
||||||
|
client-id: ${QQ_CLIENT_ID}
|
||||||
|
client-secret: ${QQ_CLIENT_SECRET}
|
||||||
|
redirect-uri: ${QQ_REDIRECT_URI}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
server:
|
||||||
|
port: 19008
|
||||||
|
|
||||||
|
spring:
|
||||||
|
# 数据源配置
|
||||||
|
datasource:
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||||
|
username: ${MYSQL_USERNAME}
|
||||||
|
password: EmotionMuseum2025*#
|
||||||
|
|
||||||
|
# 连接池配置
|
||||||
|
hikari:
|
||||||
|
minimum-idle: 5
|
||||||
|
maximum-pool-size: 20
|
||||||
|
idle-timeout: 300000
|
||||||
|
connection-timeout: 20000
|
||||||
|
max-lifetime: 1200000
|
||||||
|
pool-name: EmotionAuthHikariCP
|
||||||
|
|
||||||
|
# Redis配置
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
host: ${REDIS_HOST}
|
||||||
|
port: ${REDIS_PORT}
|
||||||
|
password: ${REDIS_PASSWORD}
|
||||||
|
database: ${REDIS_DATABASE}
|
||||||
|
timeout: 5000ms
|
||||||
|
lettuce:
|
||||||
|
pool:
|
||||||
|
max-active: 20
|
||||||
|
max-idle: 10
|
||||||
|
min-idle: 5
|
||||||
|
max-wait: 2000ms
|
||||||
|
|
||||||
|
# 云服务配置
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
discovery:
|
||||||
|
server-addr: ${NACOS_SERVER_ADDR}
|
||||||
|
username: ${NACOS_USERNAME}
|
||||||
|
password: EmotionMuseum2025*#
|
||||||
|
config:
|
||||||
|
server-addr: ${NACOS_SERVER_ADDR}
|
||||||
|
username: ${NACOS_USERNAME}
|
||||||
|
password: EmotionMuseum2025*#
|
||||||
|
|
||||||
|
# MyBatis Plus配置
|
||||||
|
mybatis-plus:
|
||||||
|
configuration:
|
||||||
|
map-underscore-to-camel-case: true
|
||||||
|
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
|
global-config:
|
||||||
|
db-config:
|
||||||
|
id-type: assign_uuid
|
||||||
|
logic-delete-field: deleted
|
||||||
|
logic-delete-value: 1
|
||||||
|
logic-not-delete-value: 0
|
||||||
|
|
||||||
|
# 监控配置
|
||||||
|
management:
|
||||||
|
endpoints:
|
||||||
|
web:
|
||||||
|
exposure:
|
||||||
|
include: health,info,metrics,prometheus
|
||||||
|
endpoint:
|
||||||
|
health:
|
||||||
|
show-details: always
|
||||||
|
metrics:
|
||||||
|
export:
|
||||||
|
prometheus:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# 日志配置
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
com.emotionmuseum: info
|
||||||
|
com.baomidou.mybatisplus: info
|
||||||
|
pattern:
|
||||||
|
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level [%logger{50}] - %msg%n"
|
||||||
|
|
||||||
|
# JWT配置
|
||||||
|
jwt:
|
||||||
|
secret: ${JWT_SECRET:emotion-museum-secret-key-2025}
|
||||||
|
expiration: ${JWT_EXPIRATION:86400}
|
||||||
|
refresh-expiration: ${JWT_REFRESH_EXPIRATION:604800}
|
||||||
|
|
||||||
|
# 验证码配置
|
||||||
|
captcha:
|
||||||
|
type: arithmetic
|
||||||
|
length: 4
|
||||||
|
expire-time: 300
|
||||||
|
|
||||||
|
# OAuth配置
|
||||||
|
oauth:
|
||||||
|
wechat:
|
||||||
|
client-id: ${WECHAT_CLIENT_ID}
|
||||||
|
client-secret: ${WECHAT_CLIENT_SECRET}
|
||||||
|
redirect-uri: ${WECHAT_REDIRECT_URI}
|
||||||
|
qq:
|
||||||
|
client-id: ${QQ_CLIENT_ID}
|
||||||
|
client-secret: ${QQ_CLIENT_SECRET}
|
||||||
|
redirect-uri: ${QQ_REDIRECT_URI}
|
||||||
@@ -1,143 +1,18 @@
|
|||||||
server:
|
|
||||||
port: 19001
|
|
||||||
servlet:
|
|
||||||
context-path: /
|
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: emotion-auth
|
name: emotion-auth
|
||||||
profiles:
|
profiles:
|
||||||
active: ${SPRING_PROFILES_ACTIVE:local}
|
active: ${SPRING_PROFILES_ACTIVE:local}
|
||||||
|
main:
|
||||||
# 数据源配置
|
allow-bean-definition-overriding: true
|
||||||
datasource:
|
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
|
||||||
url: jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:emotion_museum}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
|
||||||
username: ${MYSQL_USERNAME:root}
|
|
||||||
password: ${MYSQL_PASSWORD:EmotionMuseum2025*#}
|
|
||||||
|
|
||||||
# 连接池配置
|
|
||||||
hikari:
|
|
||||||
minimum-idle: 5
|
|
||||||
maximum-pool-size: 20
|
|
||||||
idle-timeout: 300000
|
|
||||||
connection-timeout: 20000
|
|
||||||
max-lifetime: 1200000
|
|
||||||
pool-name: EmotionAuthHikariCP
|
|
||||||
|
|
||||||
# Redis配置
|
|
||||||
data:
|
|
||||||
redis:
|
|
||||||
host: ${REDIS_HOST:localhost}
|
|
||||||
port: ${REDIS_PORT:6379}
|
|
||||||
password: ${REDIS_PASSWORD:}
|
|
||||||
database: ${REDIS_DATABASE:0}
|
|
||||||
timeout: 5000ms
|
|
||||||
lettuce:
|
|
||||||
pool:
|
|
||||||
max-active: 20
|
|
||||||
max-idle: 10
|
|
||||||
min-idle: 5
|
|
||||||
max-wait: 2000ms
|
|
||||||
|
|
||||||
# 云服务配置
|
|
||||||
cloud:
|
cloud:
|
||||||
nacos:
|
nacos:
|
||||||
discovery:
|
|
||||||
enabled: false
|
|
||||||
config:
|
config:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
discovery:
|
||||||
# MyBatis Plus配置
|
|
||||||
mybatis-plus:
|
|
||||||
configuration:
|
|
||||||
map-underscore-to-camel-case: true
|
|
||||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
|
||||||
global-config:
|
|
||||||
db-config:
|
|
||||||
id-type: assign_uuid
|
|
||||||
logic-delete-field: deleted
|
|
||||||
logic-delete-value: 1
|
|
||||||
logic-not-delete-value: 0
|
|
||||||
|
|
||||||
# 监控配置
|
|
||||||
management:
|
|
||||||
endpoints:
|
|
||||||
web:
|
|
||||||
exposure:
|
|
||||||
include: health,info,metrics,prometheus
|
|
||||||
endpoint:
|
|
||||||
health:
|
|
||||||
show-details: always
|
|
||||||
metrics:
|
|
||||||
export:
|
|
||||||
prometheus:
|
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
# 日志配置
|
|
||||||
logging:
|
|
||||||
level:
|
|
||||||
com.emotionmuseum: debug
|
|
||||||
com.baomidou.mybatisplus: debug
|
|
||||||
pattern:
|
|
||||||
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level [%logger{50}] - %msg%n"
|
|
||||||
|
|
||||||
# JWT配置
|
|
||||||
jwt:
|
|
||||||
secret: emotion-museum-secret-key-2025
|
|
||||||
expiration: 86400
|
|
||||||
refresh-expiration: 604800
|
|
||||||
|
|
||||||
# 验证码配置
|
|
||||||
captcha:
|
|
||||||
type: arithmetic
|
|
||||||
length: 4
|
|
||||||
expire-time: 300
|
|
||||||
|
|
||||||
# OAuth配置
|
|
||||||
oauth:
|
|
||||||
wechat:
|
|
||||||
client-id: ${WECHAT_CLIENT_ID:}
|
|
||||||
client-secret: ${WECHAT_CLIENT_SECRET:}
|
|
||||||
redirect-uri: ${WECHAT_REDIRECT_URI:}
|
|
||||||
qq:
|
|
||||||
client-id: ${QQ_CLIENT_ID:}
|
|
||||||
client-secret: ${QQ_CLIENT_SECRET:}
|
|
||||||
redirect-uri: ${QQ_REDIRECT_URI:}
|
|
||||||
|
|
||||||
---
|
|
||||||
# 本地开发环境配置
|
|
||||||
spring:
|
|
||||||
config:
|
|
||||||
activate:
|
|
||||||
on-profile: local
|
|
||||||
datasource:
|
|
||||||
url: jdbc:mysql://localhost:3306/emotion_museum?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
|
||||||
data:
|
|
||||||
redis:
|
|
||||||
host: localhost
|
|
||||||
port: 6379
|
|
||||||
cloud:
|
|
||||||
nacos:
|
|
||||||
discovery:
|
|
||||||
server-addr: localhost:8848
|
|
||||||
config:
|
|
||||||
server-addr: localhost:8848
|
server-addr: localhost:8848
|
||||||
|
username: nacos
|
||||||
|
password: Peanut2817*#
|
||||||
|
|
||||||
---
|
|
||||||
# 测试环境配置
|
|
||||||
spring:
|
|
||||||
config:
|
|
||||||
activate:
|
|
||||||
on-profile: test
|
|
||||||
datasource:
|
|
||||||
url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
|
||||||
|
|
||||||
---
|
|
||||||
# 生产环境配置
|
|
||||||
spring:
|
|
||||||
config:
|
|
||||||
activate:
|
|
||||||
on-profile: prod
|
|
||||||
datasource:
|
|
||||||
url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
|
||||||
|
|||||||
Executable
+217
@@ -0,0 +1,217 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# emotion-gateway 单独部署脚本
|
||||||
|
# 作者: emotion-museum
|
||||||
|
# 日期: 2025-07-18
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 配置变量
|
||||||
|
SERVICE_NAME="emotion-gateway"
|
||||||
|
SERVICE_PORT="19000"
|
||||||
|
REMOTE_HOST="root@47.111.10.27"
|
||||||
|
REMOTE_BUILD_DIR="/data/builds"
|
||||||
|
REMOTE_DOCKER_COMPOSE_DIR="/data/docker"
|
||||||
|
PROFILE="test"
|
||||||
|
PROJECT_NAME="emotion-museum"
|
||||||
|
|
||||||
|
# 颜色输出
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
# 日志函数
|
||||||
|
log_info() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查远程服务器连接
|
||||||
|
check_remote_connection() {
|
||||||
|
log_info "检查远程服务器连接..."
|
||||||
|
if ssh -o ConnectTimeout=10 $REMOTE_HOST "echo 'Connection successful'" > /dev/null 2>&1; then
|
||||||
|
log_success "远程服务器连接正常"
|
||||||
|
else
|
||||||
|
log_error "无法连接到远程服务器 $REMOTE_HOST"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 构建服务
|
||||||
|
build_service() {
|
||||||
|
log_info "构建服务: $SERVICE_NAME"
|
||||||
|
|
||||||
|
# 构建父项目依赖
|
||||||
|
cd ..
|
||||||
|
mvn clean install -DskipTests -q
|
||||||
|
cd $SERVICE_NAME
|
||||||
|
|
||||||
|
# 构建当前服务
|
||||||
|
if mvn clean package -DskipTests -Ptest -q; then
|
||||||
|
log_success "服务 $SERVICE_NAME 构建成功"
|
||||||
|
else
|
||||||
|
log_error "服务 $SERVICE_NAME 构建失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 创建Dockerfile
|
||||||
|
create_dockerfile() {
|
||||||
|
log_info "创建Dockerfile: $SERVICE_NAME"
|
||||||
|
|
||||||
|
ssh $REMOTE_HOST "cat > $REMOTE_DOCKER_COMPOSE_DIR/Dockerfile.${SERVICE_NAME} << 'EOF'
|
||||||
|
FROM openjdk:17-jre-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY $REMOTE_BUILD_DIR/${SERVICE_NAME}-1.0.0.jar app.jar
|
||||||
|
|
||||||
|
RUN mkdir -p /app/logs
|
||||||
|
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/\$TZ /etc/localtime && echo \$TZ > /etc/timezone
|
||||||
|
|
||||||
|
EXPOSE ${SERVICE_PORT}
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \\
|
||||||
|
CMD curl -f http://localhost:${SERVICE_PORT}/actuator/health || exit 1
|
||||||
|
|
||||||
|
ENTRYPOINT [\"java\", \"-Djava.security.egd=file:/dev/./urandom\", \"-Xms512m\", \"-Xmx1024m\", \"-jar\", \"app.jar\"]
|
||||||
|
EOF"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 部署服务
|
||||||
|
deploy_service() {
|
||||||
|
log_info "开始部署服务: $SERVICE_NAME"
|
||||||
|
|
||||||
|
# 检查jar包
|
||||||
|
local jar_file="target/${SERVICE_NAME}-1.0.0.jar"
|
||||||
|
if [ ! -f "$jar_file" ]; then
|
||||||
|
log_error "JAR包不存在: $jar_file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建远程目录
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
mkdir -p $REMOTE_BUILD_DIR
|
||||||
|
mkdir -p $REMOTE_DOCKER_COMPOSE_DIR
|
||||||
|
mkdir -p /data/logs/emotion-museum
|
||||||
|
"
|
||||||
|
|
||||||
|
# 删除旧jar包
|
||||||
|
log_info "删除远程旧jar包"
|
||||||
|
ssh $REMOTE_HOST "rm -f $REMOTE_BUILD_DIR/${SERVICE_NAME}-*.jar"
|
||||||
|
|
||||||
|
# 上传新jar包
|
||||||
|
log_info "上传jar包"
|
||||||
|
if scp "$jar_file" "$REMOTE_HOST:$REMOTE_BUILD_DIR/${SERVICE_NAME}-1.0.0.jar"; then
|
||||||
|
log_success "jar包上传成功"
|
||||||
|
else
|
||||||
|
log_error "jar包上传失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建Dockerfile
|
||||||
|
create_dockerfile
|
||||||
|
|
||||||
|
# 停止旧容器
|
||||||
|
log_info "停止旧容器"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
docker stop ${SERVICE_NAME} 2>/dev/null || true
|
||||||
|
docker rm ${SERVICE_NAME} 2>/dev/null || true
|
||||||
|
docker rmi ${PROJECT_NAME}/${SERVICE_NAME}:latest 2>/dev/null || true
|
||||||
|
"
|
||||||
|
|
||||||
|
# 创建Docker网络
|
||||||
|
ssh $REMOTE_HOST "docker network create emotion-network 2>/dev/null || true"
|
||||||
|
|
||||||
|
# 构建镜像
|
||||||
|
log_info "构建Docker镜像"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
cd $REMOTE_DOCKER_COMPOSE_DIR
|
||||||
|
docker build -t ${PROJECT_NAME}/${SERVICE_NAME}:latest -f Dockerfile.${SERVICE_NAME} .
|
||||||
|
"
|
||||||
|
|
||||||
|
# 启动容器
|
||||||
|
log_info "启动新容器"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
docker run -d \\
|
||||||
|
--name ${SERVICE_NAME} \\
|
||||||
|
--network emotion-network \\
|
||||||
|
-p ${SERVICE_PORT}:${SERVICE_PORT} \\
|
||||||
|
-v /data/logs/emotion-museum:/app/logs \\
|
||||||
|
-e SPRING_PROFILES_ACTIVE=${PROFILE} \\
|
||||||
|
-e MYSQL_HOST=47.111.10.27 \\
|
||||||
|
-e MYSQL_PORT=3306 \\
|
||||||
|
-e MYSQL_DATABASE=emotion_museum \\
|
||||||
|
-e MYSQL_USERNAME=root \\
|
||||||
|
-e REDIS_HOST=47.111.10.27 \\
|
||||||
|
-e REDIS_PORT=6379 \\
|
||||||
|
-e REDIS_PASSWORD= \\
|
||||||
|
-e REDIS_DATABASE=0 \\
|
||||||
|
-e NACOS_SERVER_ADDR=47.111.10.27:8848 \\
|
||||||
|
-e NACOS_USERNAME=nacos \\
|
||||||
|
-e NACOS_PASSWORD=Peanut2817*# \\
|
||||||
|
--restart unless-stopped \\
|
||||||
|
${PROJECT_NAME}/${SERVICE_NAME}:latest
|
||||||
|
"
|
||||||
|
|
||||||
|
# 等待启动
|
||||||
|
log_info "等待服务启动..."
|
||||||
|
sleep 15
|
||||||
|
|
||||||
|
# 检查状态
|
||||||
|
if ssh $REMOTE_HOST "docker ps | grep ${SERVICE_NAME}" > /dev/null; then
|
||||||
|
log_success "服务启动成功"
|
||||||
|
|
||||||
|
# 显示日志
|
||||||
|
log_info "服务日志 (最后20行):"
|
||||||
|
ssh $REMOTE_HOST "docker logs --tail 20 ${SERVICE_NAME}"
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
log_info "执行健康检查..."
|
||||||
|
sleep 10
|
||||||
|
if ssh $REMOTE_HOST "curl -f -s http://localhost:${SERVICE_PORT}/actuator/health" > /dev/null 2>&1; then
|
||||||
|
log_success "健康检查通过"
|
||||||
|
else
|
||||||
|
log_warning "健康检查失败,服务可能仍在启动中"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_error "服务启动失败"
|
||||||
|
ssh $REMOTE_HOST "docker logs ${SERVICE_NAME}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 主函数
|
||||||
|
main() {
|
||||||
|
log_info "开始部署 $SERVICE_NAME 服务"
|
||||||
|
log_info "目标服务器: $REMOTE_HOST"
|
||||||
|
log_info "服务端口: $SERVICE_PORT"
|
||||||
|
log_info "部署环境: $PROFILE"
|
||||||
|
|
||||||
|
check_remote_connection
|
||||||
|
build_service
|
||||||
|
deploy_service
|
||||||
|
|
||||||
|
log_success "$SERVICE_NAME 服务部署完成!"
|
||||||
|
log_info "访问地址: http://47.111.10.27:$SERVICE_PORT"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 执行主函数
|
||||||
|
main "$@"
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
package com.emotionmuseum.gateway.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
|
import org.springframework.web.cors.reactive.CorsWebFilter;
|
||||||
|
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CORS配置
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class CorsConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CorsWebFilter corsWebFilter() {
|
||||||
|
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||||
|
|
||||||
|
// 允许所有来源
|
||||||
|
corsConfiguration.addAllowedOriginPattern("*");
|
||||||
|
|
||||||
|
// 允许所有请求头
|
||||||
|
corsConfiguration.addAllowedHeader("*");
|
||||||
|
|
||||||
|
// 允许所有请求方法
|
||||||
|
corsConfiguration.addAllowedMethod("*");
|
||||||
|
|
||||||
|
// 不允许携带凭证(这样就可以使用 * 作为来源)
|
||||||
|
corsConfiguration.setAllowCredentials(false);
|
||||||
|
|
||||||
|
// 预检请求的缓存时间
|
||||||
|
corsConfiguration.setMaxAge(3600L);
|
||||||
|
|
||||||
|
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||||
|
source.registerCorsConfiguration("/**", corsConfiguration);
|
||||||
|
|
||||||
|
return new CorsWebFilter(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,14 +35,7 @@ spring:
|
|||||||
locator:
|
locator:
|
||||||
enabled: true
|
enabled: true
|
||||||
lower-case-service-id: true
|
lower-case-service-id: true
|
||||||
# 全局跨域配置
|
|
||||||
globalcors:
|
|
||||||
cors-configurations:
|
|
||||||
'[/**]':
|
|
||||||
allowed-origins: "*"
|
|
||||||
allowed-methods: "*"
|
|
||||||
allowed-headers: "*"
|
|
||||||
allow-credentials: true
|
|
||||||
routes:
|
routes:
|
||||||
# 用户服务路由 (包含认证功能)
|
# 用户服务路由 (包含认证功能)
|
||||||
- id: emotion-user-route
|
- id: emotion-user-route
|
||||||
@@ -74,7 +67,7 @@ spring:
|
|||||||
predicates:
|
predicates:
|
||||||
- Path=/ai/**
|
- Path=/ai/**
|
||||||
filters:
|
filters:
|
||||||
- StripPrefix=0
|
- StripPrefix=1
|
||||||
|
|
||||||
# WebSocket聊天服务路由
|
# WebSocket聊天服务路由
|
||||||
- id: emotion-websocket-route
|
- id: emotion-websocket-route
|
||||||
|
|||||||
@@ -35,14 +35,7 @@ spring:
|
|||||||
locator:
|
locator:
|
||||||
enabled: true
|
enabled: true
|
||||||
lower-case-service-id: true
|
lower-case-service-id: true
|
||||||
# 全局跨域配置
|
|
||||||
globalcors:
|
|
||||||
cors-configurations:
|
|
||||||
'[/**]':
|
|
||||||
allowed-origins: "*"
|
|
||||||
allowed-methods: "*"
|
|
||||||
allowed-headers: "*"
|
|
||||||
allow-credentials: true
|
|
||||||
routes:
|
routes:
|
||||||
# 用户服务路由 (包含认证功能)
|
# 用户服务路由 (包含认证功能)
|
||||||
- id: emotion-user-route
|
- id: emotion-user-route
|
||||||
@@ -74,7 +67,7 @@ spring:
|
|||||||
predicates:
|
predicates:
|
||||||
- Path=/ai/**
|
- Path=/ai/**
|
||||||
filters:
|
filters:
|
||||||
- StripPrefix=0
|
- StripPrefix=1
|
||||||
|
|
||||||
# WebSocket聊天服务路由
|
# WebSocket聊天服务路由
|
||||||
- id: emotion-websocket-route
|
- id: emotion-websocket-route
|
||||||
|
|||||||
@@ -35,14 +35,7 @@ spring:
|
|||||||
locator:
|
locator:
|
||||||
enabled: true
|
enabled: true
|
||||||
lower-case-service-id: true
|
lower-case-service-id: true
|
||||||
# 全局跨域配置
|
|
||||||
globalcors:
|
|
||||||
cors-configurations:
|
|
||||||
"[/**]":
|
|
||||||
allowed-origins: "*"
|
|
||||||
allowed-methods: "*"
|
|
||||||
allowed-headers: "*"
|
|
||||||
allow-credentials: true
|
|
||||||
routes:
|
routes:
|
||||||
# 用户服务路由 (包含认证功能)
|
# 用户服务路由 (包含认证功能)
|
||||||
- id: emotion-user-route
|
- id: emotion-user-route
|
||||||
@@ -74,7 +67,7 @@ spring:
|
|||||||
predicates:
|
predicates:
|
||||||
- Path=/ai/**
|
- Path=/ai/**
|
||||||
filters:
|
filters:
|
||||||
- StripPrefix=0
|
- StripPrefix=1
|
||||||
|
|
||||||
# WebSocket聊天服务路由
|
# WebSocket聊天服务路由
|
||||||
- id: emotion-websocket-route
|
- id: emotion-websocket-route
|
||||||
|
|||||||
@@ -62,69 +62,159 @@ spring:
|
|||||||
enabled: true
|
enabled: true
|
||||||
lower-case-service-id: true
|
lower-case-service-id: true
|
||||||
routes:
|
routes:
|
||||||
# 认证服务路由
|
# 认证服务路由 - API路径
|
||||||
- id: emotion-auth
|
- id: emotion-auth-api
|
||||||
uri: lb://emotion-auth
|
uri: lb://emotion-auth
|
||||||
predicates:
|
predicates:
|
||||||
- Path=/api/auth/**
|
- Path=/api/auth/**
|
||||||
filters:
|
filters:
|
||||||
- StripPrefix=2
|
- StripPrefix=2
|
||||||
|
|
||||||
# 用户服务路由
|
# 认证服务路由 - 直接路径
|
||||||
- id: emotion-user
|
- id: emotion-auth-direct
|
||||||
|
uri: lb://emotion-auth
|
||||||
|
predicates:
|
||||||
|
- Path=/auth/**
|
||||||
|
filters:
|
||||||
|
- StripPrefix=1
|
||||||
|
|
||||||
|
# 用户服务路由 - API路径
|
||||||
|
- id: emotion-user-api
|
||||||
uri: lb://emotion-user
|
uri: lb://emotion-user
|
||||||
predicates:
|
predicates:
|
||||||
- Path=/api/user/**
|
- Path=/api/user/**
|
||||||
filters:
|
filters:
|
||||||
- StripPrefix=2
|
- StripPrefix=2
|
||||||
|
|
||||||
# AI对话服务路由
|
# 用户服务路由 - 直接路径
|
||||||
- id: emotion-ai
|
- id: emotion-user-direct
|
||||||
|
uri: lb://emotion-user
|
||||||
|
predicates:
|
||||||
|
- Path=/user/**
|
||||||
|
filters:
|
||||||
|
- StripPrefix=1
|
||||||
|
|
||||||
|
# AI对话服务路由 - API路径
|
||||||
|
- id: emotion-ai-api
|
||||||
uri: lb://emotion-ai
|
uri: lb://emotion-ai
|
||||||
predicates:
|
predicates:
|
||||||
- Path=/api/ai/**
|
- Path=/api/ai/**
|
||||||
filters:
|
filters:
|
||||||
- StripPrefix=2
|
- StripPrefix=2
|
||||||
|
|
||||||
# 情绪记录服务路由
|
# AI对话服务路由 - 直接路径
|
||||||
- id: emotion-record
|
- id: emotion-ai-direct
|
||||||
|
uri: lb://emotion-ai
|
||||||
|
predicates:
|
||||||
|
- Path=/ai/**
|
||||||
|
filters:
|
||||||
|
- StripPrefix=1
|
||||||
|
|
||||||
|
# WebSocket聊天服务路由 - API路径
|
||||||
|
- id: emotion-websocket-api
|
||||||
|
uri: lb://emotion-websocket
|
||||||
|
predicates:
|
||||||
|
- Path=/api/websocket/**
|
||||||
|
filters:
|
||||||
|
- StripPrefix=2
|
||||||
|
|
||||||
|
# WebSocket聊天服务路由 - 直接路径
|
||||||
|
- id: emotion-websocket-direct
|
||||||
|
uri: lb://emotion-websocket
|
||||||
|
predicates:
|
||||||
|
- Path=/websocket/**
|
||||||
|
filters:
|
||||||
|
- StripPrefix=1
|
||||||
|
|
||||||
|
# 情绪记录服务路由 - API路径
|
||||||
|
- id: emotion-record-api
|
||||||
uri: lb://emotion-record
|
uri: lb://emotion-record
|
||||||
predicates:
|
predicates:
|
||||||
- Path=/api/record/**
|
- Path=/api/record/**
|
||||||
filters:
|
filters:
|
||||||
- StripPrefix=2
|
- StripPrefix=2
|
||||||
|
|
||||||
# 成长课题服务路由
|
# 情绪记录服务路由 - 直接路径
|
||||||
- id: emotion-growth
|
- id: emotion-record-direct
|
||||||
|
uri: lb://emotion-record
|
||||||
|
predicates:
|
||||||
|
- Path=/record/**
|
||||||
|
filters:
|
||||||
|
- StripPrefix=1
|
||||||
|
|
||||||
|
# 成长课题服务路由 - API路径
|
||||||
|
- id: emotion-growth-api
|
||||||
uri: lb://emotion-growth
|
uri: lb://emotion-growth
|
||||||
predicates:
|
predicates:
|
||||||
- Path=/api/growth/**
|
- Path=/api/growth/**
|
||||||
filters:
|
filters:
|
||||||
- StripPrefix=2
|
- StripPrefix=2
|
||||||
|
|
||||||
# 地图探索服务路由
|
# 成长课题服务路由 - 直接路径
|
||||||
- id: emotion-explore
|
- id: emotion-growth-direct
|
||||||
|
uri: lb://emotion-growth
|
||||||
|
predicates:
|
||||||
|
- Path=/growth/**
|
||||||
|
filters:
|
||||||
|
- StripPrefix=1
|
||||||
|
|
||||||
|
# 地图探索服务路由 - API路径
|
||||||
|
- id: emotion-explore-api
|
||||||
uri: lb://emotion-explore
|
uri: lb://emotion-explore
|
||||||
predicates:
|
predicates:
|
||||||
- Path=/api/explore/**
|
- Path=/api/explore/**
|
||||||
filters:
|
filters:
|
||||||
- StripPrefix=2
|
- StripPrefix=2
|
||||||
|
|
||||||
# 成就奖励服务路由
|
# 地图探索服务路由 - 直接路径
|
||||||
- id: emotion-reward
|
- id: emotion-explore-direct
|
||||||
|
uri: lb://emotion-explore
|
||||||
|
predicates:
|
||||||
|
- Path=/explore/**
|
||||||
|
filters:
|
||||||
|
- StripPrefix=1
|
||||||
|
|
||||||
|
# 成就奖励服务路由 - API路径
|
||||||
|
- id: emotion-reward-api
|
||||||
uri: lb://emotion-reward
|
uri: lb://emotion-reward
|
||||||
predicates:
|
predicates:
|
||||||
- Path=/api/reward/**
|
- Path=/api/reward/**
|
||||||
filters:
|
filters:
|
||||||
- StripPrefix=2
|
- StripPrefix=2
|
||||||
|
|
||||||
# 统计分析服务路由
|
# 成就奖励服务路由 - 直接路径
|
||||||
- id: emotion-stats
|
- id: emotion-reward-direct
|
||||||
|
uri: lb://emotion-reward
|
||||||
|
predicates:
|
||||||
|
- Path=/reward/**
|
||||||
|
filters:
|
||||||
|
- StripPrefix=1
|
||||||
|
|
||||||
|
# 统计分析服务路由 - API路径
|
||||||
|
- id: emotion-stats-api
|
||||||
uri: lb://emotion-stats
|
uri: lb://emotion-stats
|
||||||
predicates:
|
predicates:
|
||||||
- Path=/api/stats/**
|
- Path=/api/stats/**
|
||||||
filters:
|
filters:
|
||||||
- StripPrefix=2
|
- StripPrefix=2
|
||||||
|
|
||||||
|
# 统计分析服务路由 - 直接路径
|
||||||
|
- id: emotion-stats-direct
|
||||||
|
uri: lb://emotion-stats
|
||||||
|
predicates:
|
||||||
|
- Path=/stats/**
|
||||||
|
filters:
|
||||||
|
- StripPrefix=1
|
||||||
|
|
||||||
|
# 验证码服务路由 (通过认证服务)
|
||||||
|
- id: captcha-service
|
||||||
|
uri: lb://emotion-auth
|
||||||
|
predicates:
|
||||||
|
- Path=/captcha/**
|
||||||
|
filters:
|
||||||
|
- StripPrefix=1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 全局过滤器 (暂时禁用,需要实现对应的过滤器类)
|
# 全局过滤器 (暂时禁用,需要实现对应的过滤器类)
|
||||||
# default-filters:
|
# default-filters:
|
||||||
|
|||||||
Executable
+217
@@ -0,0 +1,217 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# emotion-growth 单独部署脚本
|
||||||
|
# 作者: emotion-museum
|
||||||
|
# 日期: 2025-07-18
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 配置变量
|
||||||
|
SERVICE_NAME="emotion-growth"
|
||||||
|
SERVICE_PORT="19004"
|
||||||
|
REMOTE_HOST="root@47.111.10.27"
|
||||||
|
REMOTE_BUILD_DIR="/data/builds"
|
||||||
|
REMOTE_DOCKER_COMPOSE_DIR="/data/docker"
|
||||||
|
PROFILE="test"
|
||||||
|
PROJECT_NAME="emotion-museum"
|
||||||
|
|
||||||
|
# 颜色输出
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
# 日志函数
|
||||||
|
log_info() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查远程服务器连接
|
||||||
|
check_remote_connection() {
|
||||||
|
log_info "检查远程服务器连接..."
|
||||||
|
if ssh -o ConnectTimeout=10 $REMOTE_HOST "echo 'Connection successful'" > /dev/null 2>&1; then
|
||||||
|
log_success "远程服务器连接正常"
|
||||||
|
else
|
||||||
|
log_error "无法连接到远程服务器 $REMOTE_HOST"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 构建服务
|
||||||
|
build_service() {
|
||||||
|
log_info "构建服务: $SERVICE_NAME"
|
||||||
|
|
||||||
|
# 构建父项目依赖
|
||||||
|
cd ..
|
||||||
|
mvn clean install -DskipTests -q
|
||||||
|
cd $SERVICE_NAME
|
||||||
|
|
||||||
|
# 构建当前服务
|
||||||
|
if mvn clean package -DskipTests -Ptest -q; then
|
||||||
|
log_success "服务 $SERVICE_NAME 构建成功"
|
||||||
|
else
|
||||||
|
log_error "服务 $SERVICE_NAME 构建失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 创建Dockerfile
|
||||||
|
create_dockerfile() {
|
||||||
|
log_info "创建Dockerfile: $SERVICE_NAME"
|
||||||
|
|
||||||
|
ssh $REMOTE_HOST "cat > $REMOTE_DOCKER_COMPOSE_DIR/Dockerfile.${SERVICE_NAME} << 'EOF'
|
||||||
|
FROM openjdk:17-jre-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY $REMOTE_BUILD_DIR/${SERVICE_NAME}-1.0.0.jar app.jar
|
||||||
|
|
||||||
|
RUN mkdir -p /app/logs
|
||||||
|
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/\$TZ /etc/localtime && echo \$TZ > /etc/timezone
|
||||||
|
|
||||||
|
EXPOSE ${SERVICE_PORT}
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \\
|
||||||
|
CMD curl -f http://localhost:${SERVICE_PORT}/actuator/health || exit 1
|
||||||
|
|
||||||
|
ENTRYPOINT [\"java\", \"-Djava.security.egd=file:/dev/./urandom\", \"-Xms512m\", \"-Xmx1024m\", \"-jar\", \"app.jar\"]
|
||||||
|
EOF"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 部署服务
|
||||||
|
deploy_service() {
|
||||||
|
log_info "开始部署服务: $SERVICE_NAME"
|
||||||
|
|
||||||
|
# 检查jar包
|
||||||
|
local jar_file="target/${SERVICE_NAME}-1.0.0.jar"
|
||||||
|
if [ ! -f "$jar_file" ]; then
|
||||||
|
log_error "JAR包不存在: $jar_file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建远程目录
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
mkdir -p $REMOTE_BUILD_DIR
|
||||||
|
mkdir -p $REMOTE_DOCKER_COMPOSE_DIR
|
||||||
|
mkdir -p /data/logs/emotion-museum
|
||||||
|
"
|
||||||
|
|
||||||
|
# 删除旧jar包
|
||||||
|
log_info "删除远程旧jar包"
|
||||||
|
ssh $REMOTE_HOST "rm -f $REMOTE_BUILD_DIR/${SERVICE_NAME}-*.jar"
|
||||||
|
|
||||||
|
# 上传新jar包
|
||||||
|
log_info "上传jar包"
|
||||||
|
if scp "$jar_file" "$REMOTE_HOST:$REMOTE_BUILD_DIR/${SERVICE_NAME}-1.0.0.jar"; then
|
||||||
|
log_success "jar包上传成功"
|
||||||
|
else
|
||||||
|
log_error "jar包上传失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建Dockerfile
|
||||||
|
create_dockerfile
|
||||||
|
|
||||||
|
# 停止旧容器
|
||||||
|
log_info "停止旧容器"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
docker stop ${SERVICE_NAME} 2>/dev/null || true
|
||||||
|
docker rm ${SERVICE_NAME} 2>/dev/null || true
|
||||||
|
docker rmi ${PROJECT_NAME}/${SERVICE_NAME}:latest 2>/dev/null || true
|
||||||
|
"
|
||||||
|
|
||||||
|
# 创建Docker网络
|
||||||
|
ssh $REMOTE_HOST "docker network create emotion-network 2>/dev/null || true"
|
||||||
|
|
||||||
|
# 构建镜像
|
||||||
|
log_info "构建Docker镜像"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
cd $REMOTE_DOCKER_COMPOSE_DIR
|
||||||
|
docker build -t ${PROJECT_NAME}/${SERVICE_NAME}:latest -f Dockerfile.${SERVICE_NAME} .
|
||||||
|
"
|
||||||
|
|
||||||
|
# 启动容器
|
||||||
|
log_info "启动新容器"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
docker run -d \\
|
||||||
|
--name ${SERVICE_NAME} \\
|
||||||
|
--network emotion-network \\
|
||||||
|
-p ${SERVICE_PORT}:${SERVICE_PORT} \\
|
||||||
|
-v /data/logs/emotion-museum:/app/logs \\
|
||||||
|
-e SPRING_PROFILES_ACTIVE=${PROFILE} \\
|
||||||
|
-e MYSQL_HOST=47.111.10.27 \\
|
||||||
|
-e MYSQL_PORT=3306 \\
|
||||||
|
-e MYSQL_DATABASE=emotion_museum \\
|
||||||
|
-e MYSQL_USERNAME=root \\
|
||||||
|
-e REDIS_HOST=47.111.10.27 \\
|
||||||
|
-e REDIS_PORT=6379 \\
|
||||||
|
-e REDIS_PASSWORD= \\
|
||||||
|
-e REDIS_DATABASE=0 \\
|
||||||
|
-e NACOS_SERVER_ADDR=47.111.10.27:8848 \\
|
||||||
|
-e NACOS_USERNAME=nacos \\
|
||||||
|
-e NACOS_PASSWORD=Peanut2817*# \\
|
||||||
|
--restart unless-stopped \\
|
||||||
|
${PROJECT_NAME}/${SERVICE_NAME}:latest
|
||||||
|
"
|
||||||
|
|
||||||
|
# 等待启动
|
||||||
|
log_info "等待服务启动..."
|
||||||
|
sleep 15
|
||||||
|
|
||||||
|
# 检查状态
|
||||||
|
if ssh $REMOTE_HOST "docker ps | grep ${SERVICE_NAME}" > /dev/null; then
|
||||||
|
log_success "服务启动成功"
|
||||||
|
|
||||||
|
# 显示日志
|
||||||
|
log_info "服务日志 (最后20行):"
|
||||||
|
ssh $REMOTE_HOST "docker logs --tail 20 ${SERVICE_NAME}"
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
log_info "执行健康检查..."
|
||||||
|
sleep 10
|
||||||
|
if ssh $REMOTE_HOST "curl -f -s http://localhost:${SERVICE_PORT}/actuator/health" > /dev/null 2>&1; then
|
||||||
|
log_success "健康检查通过"
|
||||||
|
else
|
||||||
|
log_warning "健康检查失败,服务可能仍在启动中"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_error "服务启动失败"
|
||||||
|
ssh $REMOTE_HOST "docker logs ${SERVICE_NAME}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 主函数
|
||||||
|
main() {
|
||||||
|
log_info "开始部署 $SERVICE_NAME 服务"
|
||||||
|
log_info "目标服务器: $REMOTE_HOST"
|
||||||
|
log_info "服务端口: $SERVICE_PORT"
|
||||||
|
log_info "部署环境: $PROFILE"
|
||||||
|
|
||||||
|
check_remote_connection
|
||||||
|
build_service
|
||||||
|
deploy_service
|
||||||
|
|
||||||
|
log_success "$SERVICE_NAME 服务部署完成!"
|
||||||
|
log_info "访问地址: http://47.111.10.27:$SERVICE_PORT"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 执行主函数
|
||||||
|
main "$@"
|
||||||
Executable
+217
@@ -0,0 +1,217 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# emotion-record 单独部署脚本
|
||||||
|
# 作者: emotion-museum
|
||||||
|
# 日期: 2025-07-18
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 配置变量
|
||||||
|
SERVICE_NAME="emotion-record"
|
||||||
|
SERVICE_PORT="19003"
|
||||||
|
REMOTE_HOST="root@47.111.10.27"
|
||||||
|
REMOTE_BUILD_DIR="/data/builds"
|
||||||
|
REMOTE_DOCKER_COMPOSE_DIR="/data/docker"
|
||||||
|
PROFILE="test"
|
||||||
|
PROJECT_NAME="emotion-museum"
|
||||||
|
|
||||||
|
# 颜色输出
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
# 日志函数
|
||||||
|
log_info() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查远程服务器连接
|
||||||
|
check_remote_connection() {
|
||||||
|
log_info "检查远程服务器连接..."
|
||||||
|
if ssh -o ConnectTimeout=10 $REMOTE_HOST "echo 'Connection successful'" > /dev/null 2>&1; then
|
||||||
|
log_success "远程服务器连接正常"
|
||||||
|
else
|
||||||
|
log_error "无法连接到远程服务器 $REMOTE_HOST"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 构建服务
|
||||||
|
build_service() {
|
||||||
|
log_info "构建服务: $SERVICE_NAME"
|
||||||
|
|
||||||
|
# 构建父项目依赖
|
||||||
|
cd ..
|
||||||
|
mvn clean install -DskipTests -q
|
||||||
|
cd $SERVICE_NAME
|
||||||
|
|
||||||
|
# 构建当前服务
|
||||||
|
if mvn clean package -DskipTests -Ptest -q; then
|
||||||
|
log_success "服务 $SERVICE_NAME 构建成功"
|
||||||
|
else
|
||||||
|
log_error "服务 $SERVICE_NAME 构建失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 创建Dockerfile
|
||||||
|
create_dockerfile() {
|
||||||
|
log_info "创建Dockerfile: $SERVICE_NAME"
|
||||||
|
|
||||||
|
ssh $REMOTE_HOST "cat > $REMOTE_DOCKER_COMPOSE_DIR/Dockerfile.${SERVICE_NAME} << 'EOF'
|
||||||
|
FROM openjdk:17-jre-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY $REMOTE_BUILD_DIR/${SERVICE_NAME}-1.0.0.jar app.jar
|
||||||
|
|
||||||
|
RUN mkdir -p /app/logs
|
||||||
|
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/\$TZ /etc/localtime && echo \$TZ > /etc/timezone
|
||||||
|
|
||||||
|
EXPOSE ${SERVICE_PORT}
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \\
|
||||||
|
CMD curl -f http://localhost:${SERVICE_PORT}/actuator/health || exit 1
|
||||||
|
|
||||||
|
ENTRYPOINT [\"java\", \"-Djava.security.egd=file:/dev/./urandom\", \"-Xms512m\", \"-Xmx1024m\", \"-jar\", \"app.jar\"]
|
||||||
|
EOF"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 部署服务
|
||||||
|
deploy_service() {
|
||||||
|
log_info "开始部署服务: $SERVICE_NAME"
|
||||||
|
|
||||||
|
# 检查jar包
|
||||||
|
local jar_file="target/${SERVICE_NAME}-1.0.0.jar"
|
||||||
|
if [ ! -f "$jar_file" ]; then
|
||||||
|
log_error "JAR包不存在: $jar_file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建远程目录
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
mkdir -p $REMOTE_BUILD_DIR
|
||||||
|
mkdir -p $REMOTE_DOCKER_COMPOSE_DIR
|
||||||
|
mkdir -p /data/logs/emotion-museum
|
||||||
|
"
|
||||||
|
|
||||||
|
# 删除旧jar包
|
||||||
|
log_info "删除远程旧jar包"
|
||||||
|
ssh $REMOTE_HOST "rm -f $REMOTE_BUILD_DIR/${SERVICE_NAME}-*.jar"
|
||||||
|
|
||||||
|
# 上传新jar包
|
||||||
|
log_info "上传jar包"
|
||||||
|
if scp "$jar_file" "$REMOTE_HOST:$REMOTE_BUILD_DIR/${SERVICE_NAME}-1.0.0.jar"; then
|
||||||
|
log_success "jar包上传成功"
|
||||||
|
else
|
||||||
|
log_error "jar包上传失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建Dockerfile
|
||||||
|
create_dockerfile
|
||||||
|
|
||||||
|
# 停止旧容器
|
||||||
|
log_info "停止旧容器"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
docker stop ${SERVICE_NAME} 2>/dev/null || true
|
||||||
|
docker rm ${SERVICE_NAME} 2>/dev/null || true
|
||||||
|
docker rmi ${PROJECT_NAME}/${SERVICE_NAME}:latest 2>/dev/null || true
|
||||||
|
"
|
||||||
|
|
||||||
|
# 创建Docker网络
|
||||||
|
ssh $REMOTE_HOST "docker network create emotion-network 2>/dev/null || true"
|
||||||
|
|
||||||
|
# 构建镜像
|
||||||
|
log_info "构建Docker镜像"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
cd $REMOTE_DOCKER_COMPOSE_DIR
|
||||||
|
docker build -t ${PROJECT_NAME}/${SERVICE_NAME}:latest -f Dockerfile.${SERVICE_NAME} .
|
||||||
|
"
|
||||||
|
|
||||||
|
# 启动容器
|
||||||
|
log_info "启动新容器"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
docker run -d \\
|
||||||
|
--name ${SERVICE_NAME} \\
|
||||||
|
--network emotion-network \\
|
||||||
|
-p ${SERVICE_PORT}:${SERVICE_PORT} \\
|
||||||
|
-v /data/logs/emotion-museum:/app/logs \\
|
||||||
|
-e SPRING_PROFILES_ACTIVE=${PROFILE} \\
|
||||||
|
-e MYSQL_HOST=47.111.10.27 \\
|
||||||
|
-e MYSQL_PORT=3306 \\
|
||||||
|
-e MYSQL_DATABASE=emotion_museum \\
|
||||||
|
-e MYSQL_USERNAME=root \\
|
||||||
|
-e REDIS_HOST=47.111.10.27 \\
|
||||||
|
-e REDIS_PORT=6379 \\
|
||||||
|
-e REDIS_PASSWORD= \\
|
||||||
|
-e REDIS_DATABASE=0 \\
|
||||||
|
-e NACOS_SERVER_ADDR=47.111.10.27:8848 \\
|
||||||
|
-e NACOS_USERNAME=nacos \\
|
||||||
|
-e NACOS_PASSWORD=Peanut2817*# \\
|
||||||
|
--restart unless-stopped \\
|
||||||
|
${PROJECT_NAME}/${SERVICE_NAME}:latest
|
||||||
|
"
|
||||||
|
|
||||||
|
# 等待启动
|
||||||
|
log_info "等待服务启动..."
|
||||||
|
sleep 15
|
||||||
|
|
||||||
|
# 检查状态
|
||||||
|
if ssh $REMOTE_HOST "docker ps | grep ${SERVICE_NAME}" > /dev/null; then
|
||||||
|
log_success "服务启动成功"
|
||||||
|
|
||||||
|
# 显示日志
|
||||||
|
log_info "服务日志 (最后20行):"
|
||||||
|
ssh $REMOTE_HOST "docker logs --tail 20 ${SERVICE_NAME}"
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
log_info "执行健康检查..."
|
||||||
|
sleep 10
|
||||||
|
if ssh $REMOTE_HOST "curl -f -s http://localhost:${SERVICE_PORT}/actuator/health" > /dev/null 2>&1; then
|
||||||
|
log_success "健康检查通过"
|
||||||
|
else
|
||||||
|
log_warning "健康检查失败,服务可能仍在启动中"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_error "服务启动失败"
|
||||||
|
ssh $REMOTE_HOST "docker logs ${SERVICE_NAME}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 主函数
|
||||||
|
main() {
|
||||||
|
log_info "开始部署 $SERVICE_NAME 服务"
|
||||||
|
log_info "目标服务器: $REMOTE_HOST"
|
||||||
|
log_info "服务端口: $SERVICE_PORT"
|
||||||
|
log_info "部署环境: $PROFILE"
|
||||||
|
|
||||||
|
check_remote_connection
|
||||||
|
build_service
|
||||||
|
deploy_service
|
||||||
|
|
||||||
|
log_success "$SERVICE_NAME 服务部署完成!"
|
||||||
|
log_info "访问地址: http://47.111.10.27:$SERVICE_PORT"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 执行主函数
|
||||||
|
main "$@"
|
||||||
Executable
+217
@@ -0,0 +1,217 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# emotion-user 单独部署脚本
|
||||||
|
# 作者: emotion-museum
|
||||||
|
# 日期: 2025-07-18
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 配置变量
|
||||||
|
SERVICE_NAME="emotion-user"
|
||||||
|
SERVICE_PORT="19001"
|
||||||
|
REMOTE_HOST="root@47.111.10.27"
|
||||||
|
REMOTE_BUILD_DIR="/data/builds"
|
||||||
|
REMOTE_DOCKER_COMPOSE_DIR="/data/docker"
|
||||||
|
PROFILE="test"
|
||||||
|
PROJECT_NAME="emotion-museum"
|
||||||
|
|
||||||
|
# 颜色输出
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
# 日志函数
|
||||||
|
log_info() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查远程服务器连接
|
||||||
|
check_remote_connection() {
|
||||||
|
log_info "检查远程服务器连接..."
|
||||||
|
if ssh -o ConnectTimeout=10 $REMOTE_HOST "echo 'Connection successful'" > /dev/null 2>&1; then
|
||||||
|
log_success "远程服务器连接正常"
|
||||||
|
else
|
||||||
|
log_error "无法连接到远程服务器 $REMOTE_HOST"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 构建服务
|
||||||
|
build_service() {
|
||||||
|
log_info "构建服务: $SERVICE_NAME"
|
||||||
|
|
||||||
|
# 构建父项目依赖
|
||||||
|
cd ..
|
||||||
|
mvn clean install -DskipTests -q
|
||||||
|
cd $SERVICE_NAME
|
||||||
|
|
||||||
|
# 构建当前服务
|
||||||
|
if mvn clean package -DskipTests -Ptest -q; then
|
||||||
|
log_success "服务 $SERVICE_NAME 构建成功"
|
||||||
|
else
|
||||||
|
log_error "服务 $SERVICE_NAME 构建失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 创建Dockerfile
|
||||||
|
create_dockerfile() {
|
||||||
|
log_info "创建Dockerfile: $SERVICE_NAME"
|
||||||
|
|
||||||
|
ssh $REMOTE_HOST "cat > $REMOTE_DOCKER_COMPOSE_DIR/Dockerfile.${SERVICE_NAME} << 'EOF'
|
||||||
|
FROM openjdk:17-jre-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY $REMOTE_BUILD_DIR/${SERVICE_NAME}-1.0.0.jar app.jar
|
||||||
|
|
||||||
|
RUN mkdir -p /app/logs
|
||||||
|
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/\$TZ /etc/localtime && echo \$TZ > /etc/timezone
|
||||||
|
|
||||||
|
EXPOSE ${SERVICE_PORT}
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \\
|
||||||
|
CMD curl -f http://localhost:${SERVICE_PORT}/actuator/health || exit 1
|
||||||
|
|
||||||
|
ENTRYPOINT [\"java\", \"-Djava.security.egd=file:/dev/./urandom\", \"-Xms512m\", \"-Xmx1024m\", \"-jar\", \"app.jar\"]
|
||||||
|
EOF"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 部署服务
|
||||||
|
deploy_service() {
|
||||||
|
log_info "开始部署服务: $SERVICE_NAME"
|
||||||
|
|
||||||
|
# 检查jar包
|
||||||
|
local jar_file="target/${SERVICE_NAME}-1.0.0.jar"
|
||||||
|
if [ ! -f "$jar_file" ]; then
|
||||||
|
log_error "JAR包不存在: $jar_file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建远程目录
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
mkdir -p $REMOTE_BUILD_DIR
|
||||||
|
mkdir -p $REMOTE_DOCKER_COMPOSE_DIR
|
||||||
|
mkdir -p /data/logs/emotion-museum
|
||||||
|
"
|
||||||
|
|
||||||
|
# 删除旧jar包
|
||||||
|
log_info "删除远程旧jar包"
|
||||||
|
ssh $REMOTE_HOST "rm -f $REMOTE_BUILD_DIR/${SERVICE_NAME}-*.jar"
|
||||||
|
|
||||||
|
# 上传新jar包
|
||||||
|
log_info "上传jar包"
|
||||||
|
if scp "$jar_file" "$REMOTE_HOST:$REMOTE_BUILD_DIR/${SERVICE_NAME}-1.0.0.jar"; then
|
||||||
|
log_success "jar包上传成功"
|
||||||
|
else
|
||||||
|
log_error "jar包上传失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建Dockerfile
|
||||||
|
create_dockerfile
|
||||||
|
|
||||||
|
# 停止旧容器
|
||||||
|
log_info "停止旧容器"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
docker stop ${SERVICE_NAME} 2>/dev/null || true
|
||||||
|
docker rm ${SERVICE_NAME} 2>/dev/null || true
|
||||||
|
docker rmi ${PROJECT_NAME}/${SERVICE_NAME}:latest 2>/dev/null || true
|
||||||
|
"
|
||||||
|
|
||||||
|
# 创建Docker网络
|
||||||
|
ssh $REMOTE_HOST "docker network create emotion-network 2>/dev/null || true"
|
||||||
|
|
||||||
|
# 构建镜像
|
||||||
|
log_info "构建Docker镜像"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
cd $REMOTE_DOCKER_COMPOSE_DIR
|
||||||
|
docker build -t ${PROJECT_NAME}/${SERVICE_NAME}:latest -f Dockerfile.${SERVICE_NAME} .
|
||||||
|
"
|
||||||
|
|
||||||
|
# 启动容器
|
||||||
|
log_info "启动新容器"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
docker run -d \\
|
||||||
|
--name ${SERVICE_NAME} \\
|
||||||
|
--network emotion-network \\
|
||||||
|
-p ${SERVICE_PORT}:${SERVICE_PORT} \\
|
||||||
|
-v /data/logs/emotion-museum:/app/logs \\
|
||||||
|
-e SPRING_PROFILES_ACTIVE=${PROFILE} \\
|
||||||
|
-e MYSQL_HOST=47.111.10.27 \\
|
||||||
|
-e MYSQL_PORT=3306 \\
|
||||||
|
-e MYSQL_DATABASE=emotion_museum \\
|
||||||
|
-e MYSQL_USERNAME=root \\
|
||||||
|
-e REDIS_HOST=47.111.10.27 \\
|
||||||
|
-e REDIS_PORT=6379 \\
|
||||||
|
-e REDIS_PASSWORD= \\
|
||||||
|
-e REDIS_DATABASE=0 \\
|
||||||
|
-e NACOS_SERVER_ADDR=47.111.10.27:8848 \\
|
||||||
|
-e NACOS_USERNAME=nacos \\
|
||||||
|
-e NACOS_PASSWORD=Peanut2817*# \\
|
||||||
|
--restart unless-stopped \\
|
||||||
|
${PROJECT_NAME}/${SERVICE_NAME}:latest
|
||||||
|
"
|
||||||
|
|
||||||
|
# 等待启动
|
||||||
|
log_info "等待服务启动..."
|
||||||
|
sleep 15
|
||||||
|
|
||||||
|
# 检查状态
|
||||||
|
if ssh $REMOTE_HOST "docker ps | grep ${SERVICE_NAME}" > /dev/null; then
|
||||||
|
log_success "服务启动成功"
|
||||||
|
|
||||||
|
# 显示日志
|
||||||
|
log_info "服务日志 (最后20行):"
|
||||||
|
ssh $REMOTE_HOST "docker logs --tail 20 ${SERVICE_NAME}"
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
log_info "执行健康检查..."
|
||||||
|
sleep 10
|
||||||
|
if ssh $REMOTE_HOST "curl -f -s http://localhost:${SERVICE_PORT}/actuator/health" > /dev/null 2>&1; then
|
||||||
|
log_success "健康检查通过"
|
||||||
|
else
|
||||||
|
log_warning "健康检查失败,服务可能仍在启动中"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_error "服务启动失败"
|
||||||
|
ssh $REMOTE_HOST "docker logs ${SERVICE_NAME}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 主函数
|
||||||
|
main() {
|
||||||
|
log_info "开始部署 $SERVICE_NAME 服务"
|
||||||
|
log_info "目标服务器: $REMOTE_HOST"
|
||||||
|
log_info "服务端口: $SERVICE_PORT"
|
||||||
|
log_info "部署环境: $PROFILE"
|
||||||
|
|
||||||
|
check_remote_connection
|
||||||
|
build_service
|
||||||
|
deploy_service
|
||||||
|
|
||||||
|
log_success "$SERVICE_NAME 服务部署完成!"
|
||||||
|
log_info "访问地址: http://47.111.10.27:$SERVICE_PORT"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 执行主函数
|
||||||
|
main "$@"
|
||||||
+2
-1
@@ -80,7 +80,8 @@ public class SecurityConfig {
|
|||||||
"/user/login",
|
"/user/login",
|
||||||
"/user/refresh",
|
"/user/refresh",
|
||||||
"/user/check/**",
|
"/user/check/**",
|
||||||
"/captcha/**",
|
"/user/health",
|
||||||
|
"/captcha/**",
|
||||||
"/oauth/**")
|
"/oauth/**")
|
||||||
.permitAll()
|
.permitAll()
|
||||||
|
|
||||||
|
|||||||
+7
@@ -56,4 +56,11 @@ public class UserController {
|
|||||||
userService.updateLastActiveTime(userId);
|
userService.updateLastActiveTime(userId);
|
||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "健康检查")
|
||||||
|
@GetMapping("/health")
|
||||||
|
public Result<Boolean> healthCheck() {
|
||||||
|
log.info("用户服务健康检查");
|
||||||
|
return Result.success(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Executable
+217
@@ -0,0 +1,217 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# emotion-websocket 单独部署脚本
|
||||||
|
# 作者: emotion-museum
|
||||||
|
# 日期: 2025-07-18
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 配置变量
|
||||||
|
SERVICE_NAME="emotion-websocket"
|
||||||
|
SERVICE_PORT="19007"
|
||||||
|
REMOTE_HOST="root@47.111.10.27"
|
||||||
|
REMOTE_BUILD_DIR="/data/builds"
|
||||||
|
REMOTE_DOCKER_COMPOSE_DIR="/data/docker"
|
||||||
|
PROFILE="test"
|
||||||
|
PROJECT_NAME="emotion-museum"
|
||||||
|
|
||||||
|
# 颜色输出
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
# 日志函数
|
||||||
|
log_info() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查远程服务器连接
|
||||||
|
check_remote_connection() {
|
||||||
|
log_info "检查远程服务器连接..."
|
||||||
|
if ssh -o ConnectTimeout=10 $REMOTE_HOST "echo 'Connection successful'" > /dev/null 2>&1; then
|
||||||
|
log_success "远程服务器连接正常"
|
||||||
|
else
|
||||||
|
log_error "无法连接到远程服务器 $REMOTE_HOST"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 构建服务
|
||||||
|
build_service() {
|
||||||
|
log_info "构建服务: $SERVICE_NAME"
|
||||||
|
|
||||||
|
# 构建父项目依赖
|
||||||
|
cd ..
|
||||||
|
mvn clean install -DskipTests -q
|
||||||
|
cd $SERVICE_NAME
|
||||||
|
|
||||||
|
# 构建当前服务
|
||||||
|
if mvn clean package -DskipTests -Ptest -q; then
|
||||||
|
log_success "服务 $SERVICE_NAME 构建成功"
|
||||||
|
else
|
||||||
|
log_error "服务 $SERVICE_NAME 构建失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 创建Dockerfile
|
||||||
|
create_dockerfile() {
|
||||||
|
log_info "创建Dockerfile: $SERVICE_NAME"
|
||||||
|
|
||||||
|
ssh $REMOTE_HOST "cat > $REMOTE_DOCKER_COMPOSE_DIR/Dockerfile.${SERVICE_NAME} << 'EOF'
|
||||||
|
FROM openjdk:17-jre-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY $REMOTE_BUILD_DIR/${SERVICE_NAME}-1.0.0.jar app.jar
|
||||||
|
|
||||||
|
RUN mkdir -p /app/logs
|
||||||
|
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/\$TZ /etc/localtime && echo \$TZ > /etc/timezone
|
||||||
|
|
||||||
|
EXPOSE ${SERVICE_PORT}
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \\
|
||||||
|
CMD curl -f http://localhost:${SERVICE_PORT}/actuator/health || exit 1
|
||||||
|
|
||||||
|
ENTRYPOINT [\"java\", \"-Djava.security.egd=file:/dev/./urandom\", \"-Xms512m\", \"-Xmx1024m\", \"-jar\", \"app.jar\"]
|
||||||
|
EOF"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 部署服务
|
||||||
|
deploy_service() {
|
||||||
|
log_info "开始部署服务: $SERVICE_NAME"
|
||||||
|
|
||||||
|
# 检查jar包
|
||||||
|
local jar_file="target/${SERVICE_NAME}-1.0.0.jar"
|
||||||
|
if [ ! -f "$jar_file" ]; then
|
||||||
|
log_error "JAR包不存在: $jar_file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建远程目录
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
mkdir -p $REMOTE_BUILD_DIR
|
||||||
|
mkdir -p $REMOTE_DOCKER_COMPOSE_DIR
|
||||||
|
mkdir -p /data/logs/emotion-museum
|
||||||
|
"
|
||||||
|
|
||||||
|
# 删除旧jar包
|
||||||
|
log_info "删除远程旧jar包"
|
||||||
|
ssh $REMOTE_HOST "rm -f $REMOTE_BUILD_DIR/${SERVICE_NAME}-*.jar"
|
||||||
|
|
||||||
|
# 上传新jar包
|
||||||
|
log_info "上传jar包"
|
||||||
|
if scp "$jar_file" "$REMOTE_HOST:$REMOTE_BUILD_DIR/${SERVICE_NAME}-1.0.0.jar"; then
|
||||||
|
log_success "jar包上传成功"
|
||||||
|
else
|
||||||
|
log_error "jar包上传失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建Dockerfile
|
||||||
|
create_dockerfile
|
||||||
|
|
||||||
|
# 停止旧容器
|
||||||
|
log_info "停止旧容器"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
docker stop ${SERVICE_NAME} 2>/dev/null || true
|
||||||
|
docker rm ${SERVICE_NAME} 2>/dev/null || true
|
||||||
|
docker rmi ${PROJECT_NAME}/${SERVICE_NAME}:latest 2>/dev/null || true
|
||||||
|
"
|
||||||
|
|
||||||
|
# 创建Docker网络
|
||||||
|
ssh $REMOTE_HOST "docker network create emotion-network 2>/dev/null || true"
|
||||||
|
|
||||||
|
# 构建镜像
|
||||||
|
log_info "构建Docker镜像"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
cd $REMOTE_DOCKER_COMPOSE_DIR
|
||||||
|
docker build -t ${PROJECT_NAME}/${SERVICE_NAME}:latest -f Dockerfile.${SERVICE_NAME} .
|
||||||
|
"
|
||||||
|
|
||||||
|
# 启动容器
|
||||||
|
log_info "启动新容器"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
docker run -d \\
|
||||||
|
--name ${SERVICE_NAME} \\
|
||||||
|
--network emotion-network \\
|
||||||
|
-p ${SERVICE_PORT}:${SERVICE_PORT} \\
|
||||||
|
-v /data/logs/emotion-museum:/app/logs \\
|
||||||
|
-e SPRING_PROFILES_ACTIVE=${PROFILE} \\
|
||||||
|
-e MYSQL_HOST=47.111.10.27 \\
|
||||||
|
-e MYSQL_PORT=3306 \\
|
||||||
|
-e MYSQL_DATABASE=emotion_museum \\
|
||||||
|
-e MYSQL_USERNAME=root \\
|
||||||
|
-e REDIS_HOST=47.111.10.27 \\
|
||||||
|
-e REDIS_PORT=6379 \\
|
||||||
|
-e REDIS_PASSWORD= \\
|
||||||
|
-e REDIS_DATABASE=0 \\
|
||||||
|
-e NACOS_SERVER_ADDR=47.111.10.27:8848 \\
|
||||||
|
-e NACOS_USERNAME=nacos \\
|
||||||
|
-e NACOS_PASSWORD=Peanut2817*# \\
|
||||||
|
--restart unless-stopped \\
|
||||||
|
${PROJECT_NAME}/${SERVICE_NAME}:latest
|
||||||
|
"
|
||||||
|
|
||||||
|
# 等待启动
|
||||||
|
log_info "等待服务启动..."
|
||||||
|
sleep 15
|
||||||
|
|
||||||
|
# 检查状态
|
||||||
|
if ssh $REMOTE_HOST "docker ps | grep ${SERVICE_NAME}" > /dev/null; then
|
||||||
|
log_success "服务启动成功"
|
||||||
|
|
||||||
|
# 显示日志
|
||||||
|
log_info "服务日志 (最后20行):"
|
||||||
|
ssh $REMOTE_HOST "docker logs --tail 20 ${SERVICE_NAME}"
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
log_info "执行健康检查..."
|
||||||
|
sleep 10
|
||||||
|
if ssh $REMOTE_HOST "curl -f -s http://localhost:${SERVICE_PORT}/actuator/health" > /dev/null 2>&1; then
|
||||||
|
log_success "健康检查通过"
|
||||||
|
else
|
||||||
|
log_warning "健康检查失败,服务可能仍在启动中"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_error "服务启动失败"
|
||||||
|
ssh $REMOTE_HOST "docker logs ${SERVICE_NAME}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 主函数
|
||||||
|
main() {
|
||||||
|
log_info "开始部署 $SERVICE_NAME 服务"
|
||||||
|
log_info "目标服务器: $REMOTE_HOST"
|
||||||
|
log_info "服务端口: $SERVICE_PORT"
|
||||||
|
log_info "部署环境: $PROFILE"
|
||||||
|
|
||||||
|
check_remote_connection
|
||||||
|
build_service
|
||||||
|
deploy_service
|
||||||
|
|
||||||
|
log_success "$SERVICE_NAME 服务部署完成!"
|
||||||
|
log_info "访问地址: http://47.111.10.27:$SERVICE_PORT"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 执行主函数
|
||||||
|
main "$@"
|
||||||
@@ -69,6 +69,12 @@
|
|||||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Load Balancer -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- MySQL -->
|
<!-- MySQL -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>mysql</groupId>
|
<groupId>mysql</groupId>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ spring:
|
|||||||
group: DEFAULT_GROUP
|
group: DEFAULT_GROUP
|
||||||
enabled: true
|
enabled: true
|
||||||
username: nacos
|
username: nacos
|
||||||
password: Peanut2817*#
|
password: nacos
|
||||||
metadata:
|
metadata:
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
zone: local
|
zone: local
|
||||||
@@ -28,7 +28,7 @@ spring:
|
|||||||
file-extension: yml
|
file-extension: yml
|
||||||
enabled: false
|
enabled: false
|
||||||
username: nacos
|
username: nacos
|
||||||
password: Peanut2817*#
|
password: nacos
|
||||||
|
|
||||||
# 数据源配置
|
# 数据源配置
|
||||||
datasource:
|
datasource:
|
||||||
|
|||||||
+326
-69
@@ -1,87 +1,344 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# 开心APP前端部署脚本
|
# 情感博物馆前端部署脚本
|
||||||
# 使用方法: ./deploy.sh [环境]
|
# 作者: emotion-museum
|
||||||
# 环境选项: dev (开发), test (测试), prod (生产)
|
# 日期: 2025-07-18
|
||||||
|
# 支持Jenkins CI/CD部署
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# 默认环境为开发环境
|
# 配置变量 - 支持Jenkins环境变量覆盖
|
||||||
ENV=${1:-dev}
|
REMOTE_HOST="${DEPLOY_HOST:-root@47.111.10.27}"
|
||||||
|
REMOTE_WEB_DIR="${REMOTE_WEB_DIR:-/data/www/emotion-museum}"
|
||||||
|
FRONTEND_DIR="web-flowith"
|
||||||
|
PROJECT_NAME="emotion-museum-frontend"
|
||||||
|
|
||||||
echo "🚀 开始部署开心APP前端应用 - 环境: $ENV"
|
# Jenkins构建信息
|
||||||
|
BUILD_NUMBER="${BUILD_NUMBER:-manual}"
|
||||||
|
JOB_NAME="${JOB_NAME:-local-deploy}"
|
||||||
|
BUILD_URL="${BUILD_URL:-}"
|
||||||
|
|
||||||
# 检查Node.js和npm
|
# 颜色输出
|
||||||
if ! command -v node &> /dev/null; then
|
RED='\033[0;31m'
|
||||||
echo "❌ Node.js 未安装,请先安装 Node.js"
|
GREEN='\033[0;32m'
|
||||||
exit 1
|
YELLOW='\033[1;33m'
|
||||||
fi
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
if ! command -v npm &> /dev/null; then
|
# 日志函数
|
||||||
echo "❌ npm 未安装,请先安装 npm"
|
log_info() {
|
||||||
exit 1
|
echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
fi
|
}
|
||||||
|
|
||||||
echo "✅ Node.js 版本: $(node --version)"
|
log_success() {
|
||||||
echo "✅ npm 版本: $(npm --version)"
|
echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查远程服务器连接
|
||||||
|
check_remote_connection() {
|
||||||
|
log_info "检查远程服务器连接..."
|
||||||
|
if ssh -o ConnectTimeout=10 $REMOTE_HOST "echo 'Connection successful'" > /dev/null 2>&1; then
|
||||||
|
log_success "远程服务器连接正常"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_error "无法连接到远程服务器 $REMOTE_HOST"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查本地环境
|
||||||
|
check_local_environment() {
|
||||||
|
log_info "检查本地构建环境..."
|
||||||
|
|
||||||
|
# 检查Node.js
|
||||||
|
if ! command -v node &> /dev/null; then
|
||||||
|
log_error "Node.js 未安装"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查npm
|
||||||
|
if ! command -v npm &> /dev/null; then
|
||||||
|
log_error "npm 未安装"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Node.js 版本: $(node --version)"
|
||||||
|
log_info "npm 版本: $(npm --version)"
|
||||||
|
log_success "本地环境检查通过"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
# 安装依赖
|
# 安装依赖
|
||||||
echo "📦 安装依赖..."
|
install_dependencies() {
|
||||||
npm ci
|
log_info "安装前端依赖..."
|
||||||
|
|
||||||
|
if [ -f "package-lock.json" ]; then
|
||||||
|
npm ci --silent
|
||||||
|
else
|
||||||
|
npm install --silent
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "依赖安装完成"
|
||||||
|
}
|
||||||
|
|
||||||
# 类型检查
|
# 构建前端项目
|
||||||
echo "🔍 执行类型检查..."
|
build_frontend() {
|
||||||
npm run type-check
|
log_info "构建前端项目..."
|
||||||
|
|
||||||
|
# 设置生产环境变量
|
||||||
|
export NODE_ENV=production
|
||||||
|
export VITE_API_BASE_URL=http://47.111.10.27:19000
|
||||||
|
|
||||||
|
# 执行构建
|
||||||
|
if npm run build; then
|
||||||
|
log_success "前端项目构建成功"
|
||||||
|
|
||||||
|
# 检查构建产物
|
||||||
|
if [ -d "dist" ]; then
|
||||||
|
local dist_size=$(du -sh dist | cut -f1)
|
||||||
|
log_info "构建产物大小: $dist_size"
|
||||||
|
log_info "构建产物文件:"
|
||||||
|
ls -la dist/ | head -10
|
||||||
|
else
|
||||||
|
log_error "构建产物目录不存在"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_error "前端项目构建失败"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# 代码检查
|
# 创建远程目录
|
||||||
echo "🔍 执行代码检查..."
|
create_remote_directories() {
|
||||||
npm run lint
|
log_info "创建远程目录结构..."
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
mkdir -p $REMOTE_WEB_DIR
|
||||||
|
mkdir -p $REMOTE_WEB_DIR/backup
|
||||||
|
mkdir -p /data/logs/nginx
|
||||||
|
"
|
||||||
|
log_success "远程目录创建完成"
|
||||||
|
}
|
||||||
|
|
||||||
# 构建应用
|
# 备份旧版本
|
||||||
echo "🏗️ 构建应用..."
|
backup_old_version() {
|
||||||
if [ "$ENV" = "prod" ]; then
|
log_info "备份旧版本..."
|
||||||
npm run build
|
|
||||||
else
|
local backup_name="backup_$(date +%Y%m%d_%H%M%S)"
|
||||||
npm run build
|
|
||||||
fi
|
ssh $REMOTE_HOST "
|
||||||
|
if [ -d '$REMOTE_WEB_DIR/$FRONTEND_DIR' ]; then
|
||||||
|
mv '$REMOTE_WEB_DIR/$FRONTEND_DIR' '$REMOTE_WEB_DIR/backup/$backup_name'
|
||||||
|
echo '旧版本已备份到: $REMOTE_WEB_DIR/backup/$backup_name'
|
||||||
|
|
||||||
|
# 只保留最近5个备份
|
||||||
|
cd '$REMOTE_WEB_DIR/backup'
|
||||||
|
ls -t | tail -n +6 | xargs -r rm -rf
|
||||||
|
else
|
||||||
|
echo '没有发现旧版本,跳过备份'
|
||||||
|
fi
|
||||||
|
"
|
||||||
|
|
||||||
|
log_success "备份完成"
|
||||||
|
}
|
||||||
|
|
||||||
echo "✅ 构建完成!"
|
# 部署前端文件
|
||||||
|
deploy_frontend() {
|
||||||
|
log_info "部署前端文件到远程服务器..."
|
||||||
|
|
||||||
|
# 上传构建产物
|
||||||
|
if scp -r dist/ "$REMOTE_HOST:$REMOTE_WEB_DIR/$FRONTEND_DIR/"; then
|
||||||
|
log_success "前端文件上传成功"
|
||||||
|
else
|
||||||
|
log_error "前端文件上传失败"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 设置文件权限
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
chown -R www-data:www-data '$REMOTE_WEB_DIR/$FRONTEND_DIR' 2>/dev/null || true
|
||||||
|
chmod -R 755 '$REMOTE_WEB_DIR/$FRONTEND_DIR'
|
||||||
|
"
|
||||||
|
|
||||||
|
log_success "文件权限设置完成"
|
||||||
|
}
|
||||||
|
|
||||||
# 部署到不同环境
|
# 配置Nginx
|
||||||
case $ENV in
|
configure_nginx() {
|
||||||
"dev")
|
log_info "配置Nginx..."
|
||||||
echo "🚀 部署到开发环境..."
|
|
||||||
echo "开发环境通常使用 npm run dev 启动"
|
# 创建Nginx配置
|
||||||
;;
|
ssh $REMOTE_HOST "cat > /etc/nginx/sites-available/emotion-museum << 'EOF'
|
||||||
"test")
|
server {
|
||||||
echo "🚀 部署到测试环境..."
|
listen 80;
|
||||||
echo "将 dist 目录内容部署到测试服务器"
|
server_name 47.111.10.27;
|
||||||
# 这里可以添加具体的部署命令
|
|
||||||
# 例如: rsync -av dist/ user@test-server:/var/www/html/
|
# 前端静态文件
|
||||||
;;
|
location /emotion-museum {
|
||||||
"prod")
|
alias $REMOTE_WEB_DIR/$FRONTEND_DIR;
|
||||||
echo "🚀 部署到生产环境..."
|
index index.html;
|
||||||
echo "将 dist 目录内容部署到生产服务器"
|
try_files \$uri \$uri/ /emotion-museum/index.html;
|
||||||
# 这里可以添加具体的部署命令
|
|
||||||
# 例如: rsync -av dist/ user@prod-server:/var/www/html/
|
# 静态资源缓存
|
||||||
;;
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
*)
|
expires 1y;
|
||||||
echo "❌ 未知环境: $ENV"
|
add_header Cache-Control \"public, immutable\";
|
||||||
echo "支持的环境: dev, test, prod"
|
}
|
||||||
|
|
||||||
|
# HTML文件不缓存
|
||||||
|
location ~* \.html$ {
|
||||||
|
expires -1;
|
||||||
|
add_header Cache-Control \"no-cache, no-store, must-revalidate\";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# API代理到后端网关
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://127.0.0.1:19000/;
|
||||||
|
proxy_set_header Host \$host;
|
||||||
|
proxy_set_header X-Real-IP \$remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||||
|
|
||||||
|
# WebSocket支持
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade \$http_upgrade;
|
||||||
|
proxy_set_header Connection \"upgrade\";
|
||||||
|
}
|
||||||
|
|
||||||
|
# 日志配置
|
||||||
|
access_log /data/logs/nginx/emotion-museum-access.log;
|
||||||
|
error_log /data/logs/nginx/emotion-museum-error.log;
|
||||||
|
}
|
||||||
|
EOF"
|
||||||
|
|
||||||
|
# 启用站点
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
ln -sf /etc/nginx/sites-available/emotion-museum /etc/nginx/sites-enabled/
|
||||||
|
nginx -t && systemctl reload nginx
|
||||||
|
" 2>/dev/null || log_warning "Nginx配置可能需要手动检查"
|
||||||
|
|
||||||
|
log_success "Nginx配置完成"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
health_check() {
|
||||||
|
log_info "执行健康检查..."
|
||||||
|
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# 检查前端页面
|
||||||
|
if curl -f -s "http://47.111.10.27/emotion-museum/" > /dev/null 2>&1; then
|
||||||
|
log_success "前端页面访问正常"
|
||||||
|
else
|
||||||
|
log_warning "前端页面访问异常,请检查Nginx配置"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查API代理
|
||||||
|
if curl -f -s "http://47.111.10.27/api/user/health" > /dev/null 2>&1; then
|
||||||
|
log_success "API代理正常"
|
||||||
|
else
|
||||||
|
log_warning "API代理异常,请检查后端服务状态"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 显示部署报告
|
||||||
|
show_deployment_report() {
|
||||||
|
local total_time=$1
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "========================================"
|
||||||
|
echo " 前端部署完成报告"
|
||||||
|
echo "========================================"
|
||||||
|
echo "项目名称: $PROJECT_NAME"
|
||||||
|
echo "目标服务器: $REMOTE_HOST"
|
||||||
|
echo "部署路径: $REMOTE_WEB_DIR/$FRONTEND_DIR"
|
||||||
|
echo "部署时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
|
echo "总耗时: ${total_time}s"
|
||||||
|
if [ "$BUILD_NUMBER" != "manual" ]; then
|
||||||
|
echo "Jenkins构建: #$BUILD_NUMBER"
|
||||||
|
echo "Jenkins任务: $JOB_NAME"
|
||||||
|
[ -n "$BUILD_URL" ] && echo "构建链接: $BUILD_URL"
|
||||||
|
fi
|
||||||
|
echo "========================================"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "🌐 访问地址:"
|
||||||
|
echo " 前端页面: http://47.111.10.27/emotion-museum/"
|
||||||
|
echo " API接口: http://47.111.10.27/api/"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "📁 远程文件信息:"
|
||||||
|
ssh $REMOTE_HOST "
|
||||||
|
echo '部署目录大小:'
|
||||||
|
du -sh '$REMOTE_WEB_DIR/$FRONTEND_DIR' 2>/dev/null || echo '无法获取目录大小'
|
||||||
|
echo ''
|
||||||
|
echo '主要文件:'
|
||||||
|
ls -la '$REMOTE_WEB_DIR/$FRONTEND_DIR' 2>/dev/null | head -10 || echo '无法列出文件'
|
||||||
|
"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "========================================"
|
||||||
|
echo "🎉 前端部署完成!"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 主函数
|
||||||
|
main() {
|
||||||
|
local start_time=$(date +%s)
|
||||||
|
|
||||||
|
log_info "🚀 开始前端部署..."
|
||||||
|
log_info "目标服务器: $REMOTE_HOST"
|
||||||
|
log_info "部署路径: $REMOTE_WEB_DIR/$FRONTEND_DIR"
|
||||||
|
|
||||||
|
# 检查环境
|
||||||
|
if ! check_local_environment; then
|
||||||
|
log_error "本地环境检查失败"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
fi
|
||||||
esac
|
|
||||||
|
if ! check_remote_connection; then
|
||||||
|
log_error "远程服务器连接失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 安装依赖
|
||||||
|
install_dependencies
|
||||||
|
|
||||||
|
# 构建项目
|
||||||
|
build_frontend
|
||||||
|
|
||||||
|
# 创建远程目录
|
||||||
|
create_remote_directories
|
||||||
|
|
||||||
|
# 备份旧版本
|
||||||
|
backup_old_version
|
||||||
|
|
||||||
|
# 部署文件
|
||||||
|
deploy_frontend
|
||||||
|
|
||||||
|
# 配置Nginx
|
||||||
|
configure_nginx
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
health_check
|
||||||
|
|
||||||
|
# 计算总耗时
|
||||||
|
local end_time=$(date +%s)
|
||||||
|
local total_time=$((end_time - start_time))
|
||||||
|
|
||||||
|
# 显示报告
|
||||||
|
show_deployment_report $total_time
|
||||||
|
|
||||||
|
log_success "🎉 前端部署完成!"
|
||||||
|
}
|
||||||
|
|
||||||
echo "🎉 部署完成!"
|
# 执行主函数
|
||||||
|
main "$@"
|
||||||
# 显示构建信息
|
|
||||||
if [ -d "dist" ]; then
|
|
||||||
echo ""
|
|
||||||
echo "📊 构建统计:"
|
|
||||||
echo "构建目录: $(pwd)/dist"
|
|
||||||
echo "文件数量: $(find dist -type f | wc -l)"
|
|
||||||
echo "总大小: $(du -sh dist | cut -f1)"
|
|
||||||
echo ""
|
|
||||||
echo "主要文件:"
|
|
||||||
ls -la dist/
|
|
||||||
fi
|
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ export default defineConfig({
|
|||||||
'@': resolve(__dirname, 'src'),
|
'@': resolve(__dirname, 'src'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
define: {
|
||||||
|
global: 'globalThis',
|
||||||
|
},
|
||||||
css: {
|
css: {
|
||||||
preprocessorOptions: {
|
preprocessorOptions: {
|
||||||
scss: {
|
scss: {
|
||||||
@@ -22,7 +25,7 @@ export default defineConfig({
|
|||||||
open: true,
|
open: true,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://localhost:19001',
|
target: 'http://localhost:19000',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path) => path.replace(/^\/api/, '')
|
rewrite: (path) => path.replace(/^\/api/, '')
|
||||||
}
|
}
|
||||||
|
|||||||
+308
@@ -0,0 +1,308 @@
|
|||||||
|
# 情感博物馆部署脚本使用说明
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
本项目提供了完整的自动化部署脚本,支持本地部署和Jenkins CI/CD部署。所有脚本都经过优化,支持错误处理、详细日志输出和部署状态报告。
|
||||||
|
|
||||||
|
## 脚本列表
|
||||||
|
|
||||||
|
### 后端微服务部署脚本
|
||||||
|
|
||||||
|
#### 1. 全量部署脚本
|
||||||
|
- **路径**: `backend/deploy-all.sh`
|
||||||
|
- **功能**: 一键部署所有后端微服务
|
||||||
|
- **特性**:
|
||||||
|
- ✅ 单个服务失败不影响其他服务
|
||||||
|
- ✅ 详细的部署报告和错误日志
|
||||||
|
- ✅ 支持Jenkins环境变量
|
||||||
|
- ✅ 容器化部署
|
||||||
|
|
||||||
|
#### 2. 单服务部署脚本
|
||||||
|
每个微服务都有独立的部署脚本:
|
||||||
|
- `backend/emotion-gateway/deploy.sh` - API网关服务
|
||||||
|
- `backend/emotion-user/deploy.sh` - 用户管理服务
|
||||||
|
- `backend/emotion-ai/deploy.sh` - AI聊天服务
|
||||||
|
- `backend/emotion-record/deploy.sh` - 记录管理服务
|
||||||
|
- `backend/emotion-growth/deploy.sh` - 成长跟踪服务
|
||||||
|
- `backend/emotion-websocket/deploy.sh` - WebSocket服务
|
||||||
|
- `backend/emotion-auth/deploy.sh` - 认证服务
|
||||||
|
|
||||||
|
### 前端应用部署脚本
|
||||||
|
|
||||||
|
#### 3. 前端部署脚本
|
||||||
|
- **路径**: `web-flowith/deploy.sh`
|
||||||
|
- **功能**: 构建并部署Vue前端应用
|
||||||
|
- **特性**:
|
||||||
|
- ✅ 自动构建和优化
|
||||||
|
- ✅ 备份旧版本
|
||||||
|
- ✅ Nginx配置
|
||||||
|
- ✅ 健康检查
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
### 本地部署
|
||||||
|
|
||||||
|
#### 部署所有后端服务
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
./deploy-all.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 部署单个后端服务
|
||||||
|
```bash
|
||||||
|
cd backend/emotion-gateway
|
||||||
|
./deploy.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 部署前端应用
|
||||||
|
```bash
|
||||||
|
cd web-flowith
|
||||||
|
./deploy.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Jenkins部署
|
||||||
|
|
||||||
|
#### 环境变量配置
|
||||||
|
在Jenkins中设置以下环境变量:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 必需变量
|
||||||
|
DEPLOY_HOST=root@47.111.10.27
|
||||||
|
DEPLOY_ENV=test
|
||||||
|
PROJECT_NAME=emotion-museum
|
||||||
|
|
||||||
|
# 可选变量(使用默认值)
|
||||||
|
REMOTE_BUILD_DIR=/data/builds
|
||||||
|
REMOTE_DOCKER_DIR=/data/docker
|
||||||
|
REMOTE_WEB_DIR=/data/www/emotion-museum
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Pipeline脚本示例
|
||||||
|
```groovy
|
||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
|
||||||
|
environment {
|
||||||
|
DEPLOY_HOST = 'root@47.111.10.27'
|
||||||
|
DEPLOY_ENV = 'test'
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Deploy Backend') {
|
||||||
|
steps {
|
||||||
|
dir('backend') {
|
||||||
|
sh './deploy-all.sh'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Deploy Frontend') {
|
||||||
|
steps {
|
||||||
|
dir('web-flowith') {
|
||||||
|
sh './deploy.sh'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 部署配置
|
||||||
|
|
||||||
|
### 服务端口分配
|
||||||
|
- **API网关**: 19000
|
||||||
|
- **用户服务**: 19001
|
||||||
|
- **AI服务**: 19002
|
||||||
|
- **记录服务**: 19003
|
||||||
|
- **成长服务**: 19004
|
||||||
|
- **WebSocket服务**: 19007
|
||||||
|
- **认证服务**: 19008
|
||||||
|
|
||||||
|
### 远程服务器目录结构
|
||||||
|
```
|
||||||
|
/data/
|
||||||
|
├── builds/ # JAR包存储目录
|
||||||
|
│ ├── emotion-gateway-1.0.0.jar
|
||||||
|
│ ├── emotion-user-1.0.0.jar
|
||||||
|
│ └── ...
|
||||||
|
├── docker/ # Docker配置目录
|
||||||
|
│ ├── Dockerfile.emotion-gateway
|
||||||
|
│ ├── Dockerfile.emotion-user
|
||||||
|
│ └── ...
|
||||||
|
├── www/emotion-museum/ # 前端文件目录
|
||||||
|
│ ├── web-flowith/ # 前端应用
|
||||||
|
│ └── backup/ # 前端备份
|
||||||
|
└── logs/ # 日志目录
|
||||||
|
├── emotion-museum/ # 应用日志
|
||||||
|
└── nginx/ # Nginx日志
|
||||||
|
```
|
||||||
|
|
||||||
|
## 部署报告示例
|
||||||
|
|
||||||
|
### 后端服务部署报告
|
||||||
|
```
|
||||||
|
========================================
|
||||||
|
部署完成报告
|
||||||
|
========================================
|
||||||
|
项目名称: emotion-museum
|
||||||
|
部署环境: test
|
||||||
|
目标服务器: root@47.111.10.27
|
||||||
|
部署时间: 2025-07-18 14:30:25
|
||||||
|
总耗时: 180s
|
||||||
|
Jenkins构建: #42
|
||||||
|
========================================
|
||||||
|
|
||||||
|
📊 部署统计:
|
||||||
|
总服务数: 7
|
||||||
|
成功部署: 6
|
||||||
|
失败部署: 1
|
||||||
|
成功率: 85%
|
||||||
|
|
||||||
|
📋 服务部署详情:
|
||||||
|
服务名称 状态 耗时 备注
|
||||||
|
----------------------------------------
|
||||||
|
emotion-gateway ✅ 成功 25s http://47.111.10.27:19000
|
||||||
|
emotion-user ✅ 成功 30s http://47.111.10.27:19001
|
||||||
|
emotion-ai ✅ 成功 35s http://47.111.10.27:19002
|
||||||
|
emotion-record ✅ 成功 28s http://47.111.10.27:19003
|
||||||
|
emotion-growth ✅ 成功 32s http://47.111.10.27:19004
|
||||||
|
emotion-websocket ✅ 成功 20s http://47.111.10.27:19007
|
||||||
|
emotion-auth ❌ 失败 10s 查看错误日志
|
||||||
|
|
||||||
|
🐳 当前容器运行状态:
|
||||||
|
----------------------------------------
|
||||||
|
NAMES STATUS PORTS
|
||||||
|
emotion-gateway Up 2 minutes 0.0.0.0:19000->19000/tcp
|
||||||
|
emotion-user Up 2 minutes 0.0.0.0:19001->19001/tcp
|
||||||
|
emotion-ai Up 1 minute 0.0.0.0:19002->19002/tcp
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 前端部署报告
|
||||||
|
```
|
||||||
|
========================================
|
||||||
|
前端部署完成报告
|
||||||
|
========================================
|
||||||
|
项目名称: emotion-museum-frontend
|
||||||
|
目标服务器: root@47.111.10.27
|
||||||
|
部署路径: /data/www/emotion-museum/web-flowith
|
||||||
|
部署时间: 2025-07-18 14:35:10
|
||||||
|
总耗时: 45s
|
||||||
|
========================================
|
||||||
|
|
||||||
|
🌐 访问地址:
|
||||||
|
前端页面: http://47.111.10.27/emotion-museum/
|
||||||
|
API接口: http://47.111.10.27/api/
|
||||||
|
|
||||||
|
📁 远程文件信息:
|
||||||
|
部署目录大小: 2.3M
|
||||||
|
主要文件:
|
||||||
|
-rw-r--r-- 1 www-data www-data 1.2K index.html
|
||||||
|
drwxr-xr-x 2 www-data www-data 4.0K assets/
|
||||||
|
drwxr-xr-x 2 www-data www-data 4.0K images/
|
||||||
|
```
|
||||||
|
|
||||||
|
## 故障排查
|
||||||
|
|
||||||
|
### 常见问题及解决方案
|
||||||
|
|
||||||
|
#### 1. SSH连接失败
|
||||||
|
```bash
|
||||||
|
# 检查SSH密钥
|
||||||
|
ssh -T root@47.111.10.27
|
||||||
|
|
||||||
|
# 检查网络连通性
|
||||||
|
ping 47.111.10.27
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. 服务构建失败
|
||||||
|
```bash
|
||||||
|
# 检查Java版本
|
||||||
|
java -version
|
||||||
|
|
||||||
|
# 检查Maven配置
|
||||||
|
mvn -version
|
||||||
|
|
||||||
|
# 清理并重新构建
|
||||||
|
mvn clean compile
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. 容器启动失败
|
||||||
|
```bash
|
||||||
|
# 查看容器日志
|
||||||
|
docker logs <container_name>
|
||||||
|
|
||||||
|
# 检查端口占用
|
||||||
|
netstat -tlnp | grep <port>
|
||||||
|
|
||||||
|
# 检查Docker网络
|
||||||
|
docker network ls
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. 前端构建失败
|
||||||
|
```bash
|
||||||
|
# 检查Node.js版本
|
||||||
|
node --version
|
||||||
|
|
||||||
|
# 清理依赖重新安装
|
||||||
|
rm -rf node_modules package-lock.json
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### 日志查看
|
||||||
|
|
||||||
|
#### 部署日志
|
||||||
|
- Jenkins构建日志中包含完整的部署过程
|
||||||
|
- 每个步骤都有详细的时间戳和状态信息
|
||||||
|
|
||||||
|
#### 应用日志
|
||||||
|
```bash
|
||||||
|
# 查看容器日志
|
||||||
|
docker logs -f <service_name>
|
||||||
|
|
||||||
|
# 查看应用日志文件
|
||||||
|
tail -f /data/logs/emotion-museum/<service_name>.log
|
||||||
|
|
||||||
|
# 查看Nginx日志
|
||||||
|
tail -f /data/logs/nginx/emotion-museum-access.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## 最佳实践
|
||||||
|
|
||||||
|
### 1. 部署前检查
|
||||||
|
- ✅ 确认代码已提交到正确分支
|
||||||
|
- ✅ 检查远程服务器资源使用情况
|
||||||
|
- ✅ 备份重要数据
|
||||||
|
- ✅ 通知相关人员部署计划
|
||||||
|
|
||||||
|
### 2. 部署过程监控
|
||||||
|
- ✅ 实时查看部署日志
|
||||||
|
- ✅ 监控服务器资源使用
|
||||||
|
- ✅ 检查服务健康状态
|
||||||
|
- ✅ 验证功能正常性
|
||||||
|
|
||||||
|
### 3. 部署后验证
|
||||||
|
- ✅ 访问前端页面确认正常
|
||||||
|
- ✅ 测试API接口功能
|
||||||
|
- ✅ 检查日志无异常
|
||||||
|
- ✅ 监控服务性能指标
|
||||||
|
|
||||||
|
### 4. 回滚准备
|
||||||
|
- ✅ 保留旧版本备份
|
||||||
|
- ✅ 准备回滚脚本
|
||||||
|
- ✅ 制定回滚计划
|
||||||
|
- ✅ 测试回滚流程
|
||||||
|
|
||||||
|
## 联系支持
|
||||||
|
|
||||||
|
如遇到部署问题,请:
|
||||||
|
|
||||||
|
1. **查看部署日志**: 详细的错误信息通常在日志中
|
||||||
|
2. **检查服务状态**: 使用健康检查接口验证服务状态
|
||||||
|
3. **联系开发团队**: 提供完整的错误日志和环境信息
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**文档版本**: v1.0
|
||||||
|
**更新时间**: 2025-07-18
|
||||||
|
**维护团队**: 情感博物馆开发团队
|
||||||
Reference in New Issue
Block a user