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
@@ -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