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:
2025-07-18 11:41:11 +08:00
parent c77352877d
commit b150cede84
28 changed files with 3530 additions and 261 deletions
+324
View File
@@ -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
+444
View File
@@ -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 "$@"
+218
View File
@@ -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 "$@"
+218
View File
@@ -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;
import com.wf.captcha.ArithmeticCaptcha;
import com.wf.captcha.ChineseCaptcha;
import com.wf.captcha.GifCaptcha;
import com.wf.captcha.SpecCaptcha;
@@ -18,15 +17,15 @@ import org.springframework.context.annotation.Configuration;
public class CaptchaConfig {
/**
* 算术验证码
* 算术验证码 - 暂时禁用,因为Java 23中JavaScript引擎问题
*/
@Bean("arithmeticCaptcha")
public Captcha arithmeticCaptcha() {
ArithmeticCaptcha captcha = new ArithmeticCaptcha(130, 48);
captcha.setLen(2); // 几位数运算,默认是两位
captcha.getArithmeticString(); // 获取运算的公式:3+2=?
return captcha;
}
// @Bean("arithmeticCaptcha")
// public Captcha arithmeticCaptcha() {
// ArithmeticCaptcha captcha = new ArithmeticCaptcha(130, 48);
// captcha.setLen(2); // 几位数运算,默认是两位
// // captcha.getArithmeticString(); // 获取运算的公式:3+2=?
// return captcha;
// }
/**
* 中文验证码
@@ -3,16 +3,18 @@ package com.emotionmuseum.auth.config;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.request.*;
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.Configuration;
/**
* 第三方登录配置
*
*
* @author emotion-museum
* @since 2025-07-15
*/
@Configuration
@ConditionalOnProperty(name = "oauth.enabled", havingValue = "true", matchIfMissing = false)
public class OAuthConfig {
@Value("${oauth.wechat.client-id:}")
@@ -46,6 +48,7 @@ public class OAuthConfig {
* 微信开放平台登录
*/
@Bean
@ConditionalOnProperty(name = "oauth.wechat.client-id", matchIfMissing = false)
public AuthWeChatOpenRequest weChatOpenRequest() {
return new AuthWeChatOpenRequest(AuthConfig.builder()
.clientId(wechatClientId)
@@ -58,6 +61,7 @@ public class OAuthConfig {
* 微信公众平台登录
*/
@Bean
@ConditionalOnProperty(name = "oauth.wechat-mp.client-id", matchIfMissing = false)
public AuthWeChatMpRequest weChatMpRequest() {
return new AuthWeChatMpRequest(AuthConfig.builder()
.clientId(wechatMpClientId)
@@ -70,6 +74,7 @@ public class OAuthConfig {
* QQ登录
*/
@Bean
@ConditionalOnProperty(name = "oauth.qq.client-id", matchIfMissing = false)
public AuthQqRequest qqRequest() {
return new AuthQqRequest(AuthConfig.builder()
.clientId(qqClientId)
@@ -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:
application:
name: emotion-auth
profiles:
active: ${SPRING_PROFILES_ACTIVE:local}
# 数据源配置
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
# 云服务配置
main:
allow-bean-definition-overriding: true
cloud:
nacos:
discovery:
enabled: false
config:
enabled: false
# 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:
discovery:
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
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
+217
View File
@@ -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 "$@"
@@ -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:
enabled: true
lower-case-service-id: true
# 全局跨域配置
globalcors:
cors-configurations:
'[/**]':
allowed-origins: "*"
allowed-methods: "*"
allowed-headers: "*"
allow-credentials: true
routes:
# 用户服务路由 (包含认证功能)
- id: emotion-user-route
@@ -74,7 +67,7 @@ spring:
predicates:
- Path=/ai/**
filters:
- StripPrefix=0
- StripPrefix=1
# WebSocket聊天服务路由
- id: emotion-websocket-route
@@ -35,14 +35,7 @@ spring:
locator:
enabled: true
lower-case-service-id: true
# 全局跨域配置
globalcors:
cors-configurations:
'[/**]':
allowed-origins: "*"
allowed-methods: "*"
allowed-headers: "*"
allow-credentials: true
routes:
# 用户服务路由 (包含认证功能)
- id: emotion-user-route
@@ -74,7 +67,7 @@ spring:
predicates:
- Path=/ai/**
filters:
- StripPrefix=0
- StripPrefix=1
# WebSocket聊天服务路由
- id: emotion-websocket-route
@@ -35,14 +35,7 @@ spring:
locator:
enabled: true
lower-case-service-id: true
# 全局跨域配置
globalcors:
cors-configurations:
"[/**]":
allowed-origins: "*"
allowed-methods: "*"
allowed-headers: "*"
allow-credentials: true
routes:
# 用户服务路由 (包含认证功能)
- id: emotion-user-route
@@ -74,7 +67,7 @@ spring:
predicates:
- Path=/ai/**
filters:
- StripPrefix=0
- StripPrefix=1
# WebSocket聊天服务路由
- id: emotion-websocket-route
@@ -62,69 +62,159 @@ spring:
enabled: true
lower-case-service-id: true
routes:
# 认证服务路由
- id: emotion-auth
# 认证服务路由 - API路径
- id: emotion-auth-api
uri: lb://emotion-auth
predicates:
- Path=/api/auth/**
filters:
- 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
predicates:
- Path=/api/user/**
filters:
- 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
predicates:
- Path=/api/ai/**
filters:
- StripPrefix=2
# 情绪记录服务路由
- id: emotion-record
# AI对话服务路由 - 直接路径
- 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
predicates:
- Path=/api/record/**
filters:
- 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
predicates:
- Path=/api/growth/**
filters:
- 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
predicates:
- Path=/api/explore/**
filters:
- 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
predicates:
- Path=/api/reward/**
filters:
- 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
predicates:
- Path=/api/stats/**
filters:
- 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:
+217
View File
@@ -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 "$@"
+217
View File
@@ -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 "$@"
+217
View File
@@ -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 "$@"
@@ -80,7 +80,8 @@ public class SecurityConfig {
"/user/login",
"/user/refresh",
"/user/check/**",
"/captcha/**",
"/user/health",
"/captcha/**",
"/oauth/**")
.permitAll()
@@ -56,4 +56,11 @@ public class UserController {
userService.updateLastActiveTime(userId);
return Result.success();
}
@Operation(summary = "健康检查")
@GetMapping("/health")
public Result<Boolean> healthCheck() {
log.info("用户服务健康检查");
return Result.success(true);
}
}
+217
View File
@@ -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 "$@"
+6
View File
@@ -69,6 +69,12 @@
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Load Balancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
@@ -9,7 +9,7 @@ spring:
group: DEFAULT_GROUP
enabled: true
username: nacos
password: Peanut2817*#
password: nacos
metadata:
version: 1.0.0
zone: local
@@ -28,7 +28,7 @@ spring:
file-extension: yml
enabled: false
username: nacos
password: Peanut2817*#
password: nacos
# 数据源配置
datasource:
+326 -69
View File
@@ -1,87 +1,344 @@
#!/bin/bash
# 开心APP前端部署脚本
# 使用方法: ./deploy.sh [环境]
# 环境选项: dev (开发), test (测试), prod (生产)
# 情感博物馆前端部署脚本
# 作者: emotion-museum
# 日期: 2025-07-18
# 支持Jenkins CI/CD部署
set -e
# 默认环境为开发环境
ENV=${1:-dev}
# 配置变量 - 支持Jenkins环境变量覆盖
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
echo "❌ Node.js 未安装,请先安装 Node.js"
exit 1
fi
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
if ! command -v npm &> /dev/null; then
echo "❌ npm 未安装,请先安装 npm"
exit 1
fi
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
echo "✅ Node.js 版本: $(node --version)"
echo "✅ npm 版本: $(npm --version)"
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 "远程服务器连接正常"
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 "📦 安装依赖..."
npm ci
install_dependencies() {
log_info "安装前端依赖..."
if [ -f "package-lock.json" ]; then
npm ci --silent
else
npm install --silent
fi
log_success "依赖安装完成"
}
# 类型检查
echo "🔍 执行类型检查..."
npm run type-check
# 构建前端项目
build_frontend() {
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 "🔍 执行代码检查..."
npm run lint
# 创建远程目录
create_remote_directories() {
log_info "创建远程目录结构..."
ssh $REMOTE_HOST "
mkdir -p $REMOTE_WEB_DIR
mkdir -p $REMOTE_WEB_DIR/backup
mkdir -p /data/logs/nginx
"
log_success "远程目录创建完成"
}
# 构建应用
echo "🏗️ 构建应用..."
if [ "$ENV" = "prod" ]; then
npm run build
else
npm run build
fi
# 备份旧版本
backup_old_version() {
log_info "备份旧版本..."
local backup_name="backup_$(date +%Y%m%d_%H%M%S)"
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 "文件权限设置完成"
}
# 部署到不同环境
case $ENV in
"dev")
echo "🚀 部署到开发环境..."
echo "开发环境通常使用 npm run dev 启动"
;;
"test")
echo "🚀 部署到测试环境..."
echo "将 dist 目录内容部署到测试服务器"
# 这里可以添加具体的部署命令
# 例如: rsync -av dist/ user@test-server:/var/www/html/
;;
"prod")
echo "🚀 部署到生产环境..."
echo "将 dist 目录内容部署到生产服务器"
# 这里可以添加具体的部署命令
# 例如: rsync -av dist/ user@prod-server:/var/www/html/
;;
*)
echo "❌ 未知环境: $ENV"
echo "支持的环境: dev, test, prod"
# 配置Nginx
configure_nginx() {
log_info "配置Nginx..."
# 创建Nginx配置
ssh $REMOTE_HOST "cat > /etc/nginx/sites-available/emotion-museum << 'EOF'
server {
listen 80;
server_name 47.111.10.27;
# 前端静态文件
location /emotion-museum {
alias $REMOTE_WEB_DIR/$FRONTEND_DIR;
index index.html;
try_files \$uri \$uri/ /emotion-museum/index.html;
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control \"public, immutable\";
}
# 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
;;
esac
fi
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 "🎉 部署完成!"
# 显示构建信息
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
# 执行主函数
main "$@"
+4 -1
View File
@@ -10,6 +10,9 @@ export default defineConfig({
'@': resolve(__dirname, 'src'),
},
},
define: {
global: 'globalThis',
},
css: {
preprocessorOptions: {
scss: {
@@ -22,7 +25,7 @@ export default defineConfig({
open: true,
proxy: {
'/api': {
target: 'http://localhost:19001',
target: 'http://localhost:19000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
+308
View File
@@ -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
**维护团队**: 情感博物馆开发团队