springboot改3.x,基础框架3引入,springai+springalibabaai引入

This commit is contained in:
黎润豪 2025-08-19 10:37:09 +08:00
parent 1145963ea4
commit e62cb1dfa9
11 changed files with 81 additions and 719 deletions

77
pom.xml
View File

@ -23,8 +23,6 @@
<maven.compiler.target>21</maven.compiler.target>
<maven.compiler.release>21</maven.compiler.release>
<skipTests>true</skipTests>
<log4j.version>2.17.1</log4j.version>
<logback.version>1.2.9</logback.version>
<shiro.version>1.12.0</shiro.version>
<!-- JDK 21兼容版本 -->
<lombok.version>1.18.34</lombok.version>
@ -36,18 +34,17 @@
<spring-ai.version>0.8.1</spring-ai.version>
<!-- OkHttp for HTTP client (used by Spring AI) -->
<okhttp.version>4.12.0</okhttp.version>
<!-- Jackson for JSON processing -->
<jackson.version>2.13.5</jackson.version>
<!-- Testing frameworks -->
<testcontainers.version>1.19.8</testcontainers.version>
<h2.version>2.2.224</h2.version>
</properties>
<dependencies>
<!--Spring Boot Web Starter-->
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents.client5/httpclient5 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.5</version>
</dependency>
<!-- Thymeleaf Template Engine for AI Management UI -->
@ -71,10 +68,10 @@
-->
<!-- Spring Boot WebFlux for async operations (required by Spring AI) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-webflux</artifactId>-->
<!-- </dependency>-->
<!-- OkHttp HTTP client for better connection management -->
<dependency>
@ -83,13 +80,6 @@
<version>${okhttp.version}</version>
</dependency>
<!-- Jackson for JSON processing (used by Spring AI) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- Spring Boot Actuator for monitoring and health checks -->
<dependency>
<groupId>org.springframework.boot</groupId>
@ -109,10 +99,11 @@
</dependency>
<!-- Resilience4j for circuit breaker and rate limiting -->
<!-- https://mvnrepository.com/artifact/io.github.resilience4j/resilience4j-spring-boot3 -->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>1.7.1</version>
<artifactId>resilience4j-spring-boot3</artifactId>
<version>2.3.0</version>
</dependency>
<!--基础框架youfool-framework-boot-->
<dependency>
@ -132,6 +123,12 @@
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-spring-boot-starter</artifactId>
<version>1.5.16</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Java工具类库-->
<dependency>
@ -226,12 +223,12 @@
<version>1.5.1</version>
</dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
<!-- <dependency>-->
<!-- <groupId>javax.websocket</groupId>-->
<!-- <artifactId>javax.websocket-api</artifactId>-->
<!-- <version>1.1</version>-->
<!-- <scope>provided</scope>-->
<!-- </dependency>-->
<!--图片压缩-->
<dependency>
<groupId>net.coobird</groupId>
@ -244,34 +241,6 @@
<artifactId>spire.xls</artifactId>
<version>3.9.1</version>
</dependency>
<!-- log4j -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- logback -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- Spring Boot validation starter (required for @Valid annotations) -->
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@ -2,16 +2,13 @@ package com.chinaweal.youfool.devops;
// Temporarily commented out for Spring AI compatibility
// import com.chinaweal.youfool.devops.ai.config.AIRepairIntegrationConfig;
import com.chinaweal.youfool.devops.config.ErrorLogProperties;
import com.chinaweal.youfool.devops.common.utils.ErrorLogUtils;
import com.chinaweal.youfool.devops.config.ErrorLogProperties;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import javax.sql.DataSource;
import java.sql.Connection;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
@ -32,22 +29,10 @@ public class DevOpsApplication extends SpringBootServletInitializer implements A
private String version;
@Value("${description:运维管理系统}")
private String description;
@Autowired
private ErrorLogProperties errorLogProperties;
// Temporarily commented out for Spring AI compatibility
// @Autowired
// private AIRepairIntegrationConfig aiRepairIntegrationConfig;
@Autowired
@Qualifier("devopsDS")
private DataSource devopsDataSource;
@Autowired
@Qualifier("youfoolDS")
private DataSource youfoolDataSource;
@Autowired(required = false)
private CircuitBreakerRegistry circuitBreakerRegistry;
@ -67,18 +52,18 @@ public class DevOpsApplication extends SpringBootServletInitializer implements A
log.info("====== Java环境{}", getJavaVersionInfo());
log.info("====== 接口文档路径:/doc.html账号admin、密码123456。注如果乱码请指定VM -Dfile.encoding=UTF-8");
log.info("====== Druid Monitor路径/druid账号admin、密码123456");
// 显示错误日志配置状态
if (errorLogProperties != null) {
log.info("====== 错误日志配置:启用={}, 目录={}",
errorLogProperties.isEnabled(), errorLogProperties.getLogDirectory());
log.info("====== 错误日志类型:启动={}, 运行时={}, 数据库={}, 业务={}",
errorLogProperties.isStartupEnabled(),
errorLogProperties.isRuntimeEnabled(),
errorLogProperties.isDatabaseEnabled(),
errorLogProperties.isBusinessEnabled());
log.info("====== 错误日志配置:启用={}, 目录={}",
errorLogProperties.isEnabled(), errorLogProperties.getLogDirectory());
log.info("====== 错误日志类型:启动={}, 运行时={}, 数据库={}, 业务={}",
errorLogProperties.isStartupEnabled(),
errorLogProperties.isRuntimeEnabled(),
errorLogProperties.isDatabaseEnabled(),
errorLogProperties.isBusinessEnabled());
}
// TODO: AI修缮集成配置状态 - Temporarily disabled due to Spring AI compatibility issues
/*
if (aiRepairIntegrationConfig != null) {
@ -99,7 +84,7 @@ public class DevOpsApplication extends SpringBootServletInitializer implements A
", 异步处理:" + aiRepairIntegrationConfig.isAsyncProcessing());
}
*/
// 重置熔断器以确保服务可用
if (circuitBreakerRegistry != null) {
try {
@ -111,15 +96,13 @@ public class DevOpsApplication extends SpringBootServletInitializer implements A
} else {
log.info("====== AI功能已启用 - embedding服务和向量化功能可用");
}
// 打印数据库连接信息
printDatabaseConnectionInfo();
log.info("====================================================================");
// 记录启动成功信息
ErrorLogUtils.logStartupInfo("应用启动成功 - " + applicationName + " v" + version);
} catch (Exception e) {
log.error("启动成功回调处理异常", e);
ErrorLogUtils.saveStartupError("启动成功回调异常", e);
@ -131,23 +114,23 @@ public class DevOpsApplication extends SpringBootServletInitializer implements A
try {
// 记录启动开始
ErrorLogUtils.logStartupInfo("开始启动应用,参数: " + Arrays.toString(args));
// 设置默认的JVM参数以解决Java模块系统兼容性问题
setJavaModuleOptions();
// 启动Spring Boot应用
SpringApplication app = new SpringApplication(DevOpsApplication.class);
// 添加启动失败监听器
app.addListeners(event -> {
if (event instanceof org.springframework.boot.context.event.ApplicationFailedEvent) {
org.springframework.boot.context.event.ApplicationFailedEvent failedEvent =
(org.springframework.boot.context.event.ApplicationFailedEvent) event;
org.springframework.boot.context.event.ApplicationFailedEvent failedEvent =
(org.springframework.boot.context.event.ApplicationFailedEvent) event;
Throwable exception = failedEvent.getException();
log.error("应用启动失败", exception);
ErrorLogUtils.saveStartupError("应用启动失败", exception);
// 输出友好的错误信息
System.err.println("\n==================== 应用启动失败 ====================");
System.err.println("错误信息已保存到: logs/errors/startup-error-*.log");
@ -155,30 +138,30 @@ public class DevOpsApplication extends SpringBootServletInitializer implements A
System.err.println("=================================================\n");
}
});
app.run(args);
// 添加JVM关闭钩子以确保异步日志写入器正确关闭
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
log.info("应用正在关闭,清理错误日志资源...");
ErrorLogUtils.shutdown();
}));
} catch (Exception e) {
log.error("应用启动异常", e);
ErrorLogUtils.saveStartupError("主方法启动异常", e);
// 输出友好的错误信息到控制台
System.err.println("\n==================== 应用启动异常 ====================");
System.err.println("错误信息已保存到: logs/errors/startup-error-*.log");
System.err.println("详细错误信息: " + ErrorLogUtils.formatErrorInfo("启动异常", e));
System.err.println("=================================================\n");
// 重新抛出异常以确保程序正确退出
throw new RuntimeException("应用启动失败", e);
}
}
/**
* 设置Java版本兼容性参数
*/
@ -186,19 +169,19 @@ public class DevOpsApplication extends SpringBootServletInitializer implements A
try {
String javaVersion = System.getProperty("java.version");
ErrorLogUtils.logStartupInfo("当前Java版本: " + javaVersion);
// 只在Java 9+版本设置模块参数
if (isJava9OrHigher()) {
String javaToolOptions = System.getProperty("JAVA_TOOL_OPTIONS", "");
// 检查是否已经设置了必要的模块参数
if (!javaToolOptions.contains("--add-opens java.base/java.util=ALL-UNNAMED")) {
String newOptions = javaToolOptions +
" --add-opens java.base/java.util=ALL-UNNAMED" +
" --add-opens java.base/java.lang=ALL-UNNAMED" +
" --add-opens java.base/java.lang.reflect=ALL-UNNAMED" +
" --add-opens java.base/java.time=ALL-UNNAMED";
String newOptions = javaToolOptions +
" --add-opens java.base/java.util=ALL-UNNAMED" +
" --add-opens java.base/java.lang=ALL-UNNAMED" +
" --add-opens java.base/java.lang.reflect=ALL-UNNAMED" +
" --add-opens java.base/java.time=ALL-UNNAMED";
System.setProperty("JAVA_TOOL_OPTIONS", newOptions.trim());
ErrorLogUtils.logStartupInfo("已设置Java模块兼容性参数: " + newOptions.trim());
}
@ -210,7 +193,7 @@ public class DevOpsApplication extends SpringBootServletInitializer implements A
ErrorLogUtils.saveStartupError("Java版本检查失败", e);
}
}
/**
* 检查是否为Java 9或更高版本
*/
@ -232,7 +215,7 @@ public class DevOpsApplication extends SpringBootServletInitializer implements A
}
return true; // JDK 21环境下默认为true
}
/**
* 获取当前Java版本信息
*/
@ -246,75 +229,4 @@ public class DevOpsApplication extends SpringBootServletInitializer implements A
return "Java version unknown";
}
}
/**
* 打印数据库连接信息
*/
private void printDatabaseConnectionInfo() {
try {
log.info("========== 数据库连接信息 ==========");
// 打印devops数据源信息
try (Connection conn = devopsDataSource.getConnection()) {
String url = conn.getMetaData().getURL();
String username = conn.getMetaData().getUserName();
String databaseName = conn.getCatalog();
log.info("🔗 Devops数据源:");
log.info(" URL: {}", url);
log.info(" 用户: {}", username);
log.info(" 数据库: {}", databaseName);
ErrorLogUtils.logStartupInfo("Devops数据源连接: " + url + ", 用户: " + username + ", 数据库: " + databaseName);
} catch (Exception e) {
log.error("❌ 获取devops数据源信息失败", e);
ErrorLogUtils.saveStartupError("获取devops数据源信息失败", e);
}
// 打印youfool数据源信息
try (Connection conn = youfoolDataSource.getConnection()) {
String url = conn.getMetaData().getURL();
String username = conn.getMetaData().getUserName();
String databaseName = conn.getCatalog();
log.info("🔗 Youfool数据源:");
log.info(" URL: {}", url);
log.info(" 用户: {}", username);
log.info(" 数据库: {}", databaseName);
ErrorLogUtils.logStartupInfo("Youfool数据源连接: " + url + ", 用户: " + username + ", 数据库: " + databaseName);
} catch (Exception e) {
log.error("❌ 获取youfool数据源信息失败", e);
ErrorLogUtils.saveStartupError("获取youfool数据源信息失败", e);
}
// 测试ENGINEER表访问
try (Connection conn = devopsDataSource.getConnection()) {
// 简单测试查询
String testSql = "SELECT COUNT(*) as total FROM engineer";
try (java.sql.PreparedStatement ps = conn.prepareStatement(testSql);
java.sql.ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
int total = rs.getInt("total");
log.info("✅ ENGINEER表测试: 共{}条记录", total);
ErrorLogUtils.logStartupInfo("ENGINEER表访问测试成功共" + total + "条记录");
}
} catch (Exception e) {
log.error("❌ ENGINEER表访问测试失败", e);
ErrorLogUtils.saveStartupError("ENGINEER表访问测试失败", e);
}
} catch (Exception e) {
log.error("❌ 数据库连接测试失败", e);
ErrorLogUtils.saveStartupError("数据库连接测试失败", e);
}
log.info("=====================================");
} catch (Exception e) {
log.error("❌ 打印数据库连接信息时发生异常", e);
ErrorLogUtils.saveStartupError("打印数据库连接信息异常", e);
}
}
}

View File

@ -1,238 +0,0 @@
package com.chinaweal.youfool.devops.base.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 数据库测试控制器
* 用于测试ENGINEER表的SQL查询问题
*/
@RestController
@RequestMapping("/api/test")
@Tag(name = "数据库测试接口")
@Slf4j
public class DatabaseTestController {
@Autowired
@Qualifier("devopsDS")
private DataSource devopsDataSource;
@Autowired
@Qualifier("youfoolDS")
private DataSource youfoolDataSource;
/**
* 测试数据库连接信息
*/
@GetMapping("/connection")
@Operation(summary = "测试数据库连接信息")
public Map<String, Object> testConnection() {
Map<String, Object> result = new HashMap<>();
try {
// 测试devops数据源
try (Connection conn = devopsDataSource.getConnection()) {
result.put("devops_url", conn.getMetaData().getURL());
result.put("devops_username", conn.getMetaData().getUserName());
result.put("devops_database", conn.getCatalog());
}
// 测试youfool数据源
try (Connection conn = youfoolDataSource.getConnection()) {
result.put("youfool_url", conn.getMetaData().getURL());
result.put("youfool_username", conn.getMetaData().getUserName());
result.put("youfool_database", conn.getCatalog());
}
result.put("success", true);
result.put("message", "数据库连接测试成功");
} catch (Exception e) {
log.error("数据库连接测试失败", e);
result.put("success", false);
result.put("message", "连接测试失败: " + e.getMessage());
}
return result;
}
/**
* 测试ENGINEER表结构
*/
@GetMapping("/engineer-structure")
@Operation(summary = "测试ENGINEER表结构")
public Map<String, Object> testEngineerStructure() {
Map<String, Object> result = new HashMap<>();
try {
// 使用devops数据源测试
try (Connection conn = devopsDataSource.getConnection()) {
// 检查表是否存在
String checkTableSql = "SELECT COUNT(*) as table_count FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'engineer'";
try (PreparedStatement ps = conn.prepareStatement(checkTableSql);
ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
result.put("table_exists", rs.getInt("table_count") > 0);
}
}
// 获取字段信息
String fieldsSql = "SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'engineer' ORDER BY ordinal_position";
List<Map<String, Object>> fields = new ArrayList<>();
try (PreparedStatement ps = conn.prepareStatement(fieldsSql);
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
Map<String, Object> field = new HashMap<>();
field.put("name", rs.getString("column_name"));
field.put("type", rs.getString("data_type"));
field.put("nullable", rs.getString("is_nullable"));
fields.add(field);
}
}
result.put("fields", fields);
result.put("datasource", "devops");
result.put("success", true);
}
} catch (Exception e) {
log.error("ENGINEER表结构测试失败", e);
result.put("success", false);
result.put("message", "测试失败: " + e.getMessage());
result.put("error", e.getClass().getSimpleName());
}
return result;
}
/**
* 测试问题SQL - 分步执行
*/
@GetMapping("/engineer-query")
@Operation(summary = "测试ENGINEER表查询")
public Map<String, Object> testEngineerQuery() {
Map<String, Object> result = new HashMap<>();
try {
// 使用devops数据源测试
try (Connection conn = devopsDataSource.getConnection()) {
result.put("connection_url", conn.getMetaData().getURL());
result.put("connection_user", conn.getMetaData().getUserName());
// 测试1: 简单查询
String simpleSql = "SELECT COUNT(*) as total FROM ENGINEER";
try (PreparedStatement ps = conn.prepareStatement(simpleSql);
ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
result.put("total_records", rs.getInt("total"));
}
result.put("simple_query", "✓ 成功");
} catch (Exception e) {
result.put("simple_query", "✗ 失败: " + e.getMessage());
}
// 测试2: 测试USER_ID字段
String userIdSql = "SELECT USER_ID FROM ENGINEER LIMIT 1";
try (PreparedStatement ps = conn.prepareStatement(userIdSql);
ResultSet rs = ps.executeQuery()) {
result.put("user_id_query", "✓ 成功");
if (rs.next()) {
result.put("sample_user_id", rs.getString("USER_ID"));
}
} catch (Exception e) {
result.put("user_id_query", "✗ 失败: " + e.getMessage());
}
// 测试3: 测试完整的问题SQL
String fullSql = "SELECT USER_ID, USERNAME, PASSWORD, NICKNAME, SEX, PHONE, EMAIL, STATUS, " +
"DESCRIPTION, SORT, ROLES, SOURCE, IS_DELETED, create_by, " +
"create_time, update_by, update_time FROM ENGINEER WHERE IS_DELETED='0' LIMIT 1";
try (PreparedStatement ps = conn.prepareStatement(fullSql);
ResultSet rs = ps.executeQuery()) {
result.put("full_query", "✓ 成功");
if (rs.next()) {
Map<String, Object> sampleData = new HashMap<>();
sampleData.put("USER_ID", rs.getString("USER_ID"));
sampleData.put("USERNAME", rs.getString("USERNAME"));
sampleData.put("IS_DELETED", rs.getString("IS_DELETED"));
result.put("sample_data", sampleData);
}
} catch (Exception e) {
result.put("full_query", "✗ 失败: " + e.getMessage());
result.put("full_query_error", e.getClass().getSimpleName());
}
// 测试4: 测试带参数的查询(模拟实际应用场景)
String paramSql = "SELECT USER_ID, USERNAME FROM ENGINEER WHERE IS_DELETED='0' AND USERNAME = ?";
try (PreparedStatement ps = conn.prepareStatement(paramSql)) {
ps.setString(1, "test");
try (ResultSet rs = ps.executeQuery()) {
result.put("param_query", "✓ 成功");
}
} catch (Exception e) {
result.put("param_query", "✗ 失败: " + e.getMessage());
result.put("param_query_error", e.getClass().getSimpleName());
}
result.put("success", true);
}
} catch (Exception e) {
log.error("ENGINEER查询测试失败", e);
result.put("success", false);
result.put("message", "查询测试失败: " + e.getMessage());
result.put("error_class", e.getClass().getSimpleName());
result.put("error_cause", e.getCause() != null ? e.getCause().getMessage() : null);
}
return result;
}
/**
* 测试MyBatis查询使用相同的数据源配置
*/
@GetMapping("/mybatis-test")
@Operation(summary = "测试MyBatis数据源配置")
public Map<String, Object> testMyBatisDataSource() {
Map<String, Object> result = new HashMap<>();
try {
// 检查数据源配置
result.put("devops_datasource_class", devopsDataSource.getClass().getSimpleName());
result.put("youfool_datasource_class", youfoolDataSource.getClass().getSimpleName());
// 测试两个数据源是否指向同一个数据库
try (Connection devopsConn = devopsDataSource.getConnection();
Connection youfoolConn = youfoolDataSource.getConnection()) {
result.put("devops_url", devopsConn.getMetaData().getURL());
result.put("youfool_url", youfoolConn.getMetaData().getURL());
result.put("same_database", devopsConn.getMetaData().getURL().equals(youfoolConn.getMetaData().getURL()));
}
result.put("success", true);
} catch (Exception e) {
log.error("MyBatis数据源测试失败", e);
result.put("success", false);
result.put("message", "测试失败: " + e.getMessage());
}
return result;
}
}

View File

@ -1,8 +1,7 @@
package com.chinaweal.youfool.devops.base.mapper;
import com.chinaweal.youfool.devops.base.entity.Dict;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
import com.chinaweal.youfool.devops.base.entity.Dict;
/**
* <p>
@ -12,7 +11,6 @@ import org.springframework.stereotype.Repository;
* @author chinaweal
* @since 2020-07-07
*/
@Repository("devopsDictMapper")
public interface DictMapper extends BaseMapper<Dict> {
}

View File

@ -25,7 +25,7 @@ import java.util.List;
* @since 2020-07-07
*/
@Service("devopsDictServiceImpl")
@Transactional("devopsTransactionManager")
//@Transactional("devopsTransactionManager")
public class DictServiceImpl extends ServiceImpl<DictMapper, Dict> implements IDictService {
@Override

View File

@ -1,194 +0,0 @@
package com.chinaweal.youfool.devops.config;
import com.chinaweal.youfool.devops.common.utils.ErrorLogUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 数据库健康检查组件
* 在应用启动时检查数据库连接状态
* 支持配置开关控制
*/
@Component
@ConditionalOnProperty(name = "error-log.database-enabled", havingValue = "true", matchIfMissing = true)
@Slf4j
public class DatabaseHealthChecker implements ApplicationRunner {
@Autowired
private ErrorLogProperties errorLogProperties;
@Autowired
@Qualifier("devopsDS")
private DataSource devopsDataSource;
@Autowired
@Qualifier("youfoolDS")
private DataSource youfoolDataSource;
@Override
public void run(ApplicationArguments args) {
// 检查是否启用了数据库健康检查
if (errorLogProperties == null || !errorLogProperties.isEnabled() || !errorLogProperties.isDatabaseEnabled()) {
log.info("数据库健康检查已禁用");
return;
}
log.info("开始数据库健康检查...");
ErrorLogUtils.logStartupInfo("开始数据库健康检查");
List<String> checkResults = new ArrayList<>();
boolean allHealthy = true;
// 检查DevOps数据源
boolean devopsHealthy = checkDataSource("devopsDS", devopsDataSource);
checkResults.add("DevOps数据源: " + (devopsHealthy ? "正常" : "异常"));
if (!devopsHealthy) allHealthy = false;
// 检查YouFool数据源
boolean youfoolHealthy = checkDataSource("youfoolDS", youfoolDataSource);
checkResults.add("YouFool数据源: " + (youfoolHealthy ? "正常" : "异常"));
if (!youfoolHealthy) allHealthy = false;
// 记录检查结果
String resultSummary = "数据库健康检查完成 - " +
(allHealthy ? "所有数据源正常" : "存在异常数据源");
log.info("数据库健康检查结果:");
for (String result : checkResults) {
log.info(" - {}", result);
}
ErrorLogUtils.logStartupInfo(resultSummary + ": " + String.join(", ", checkResults));
if (!allHealthy) {
log.warn("数据库健康检查发现问题,请检查数据库配置和连接状态");
}
}
/**
* 检查单个数据源的健康状态
*/
private boolean checkDataSource(String dataSourceName, DataSource dataSource) {
try {
log.debug("检查数据源: {}", dataSourceName);
try (Connection connection = dataSource.getConnection()) {
// 检查连接是否有效
if (connection == null || connection.isClosed()) {
log.error("数据源 {} 连接无效", dataSourceName);
ErrorLogUtils.saveDatabaseError(dataSourceName + "连接无效",
new RuntimeException("数据库连接为null或已关闭"));
return false;
}
// 执行简单查询测试连接
try (PreparedStatement ps = connection.prepareStatement("SELECT 1");
ResultSet rs = ps.executeQuery()) {
if (rs.next() && rs.getInt(1) == 1) {
log.debug("数据源 {} 连接测试成功", dataSourceName);
// 获取数据库基本信息
String dbInfo = getDatabaseInfo(connection);
log.info("数据源 {} 信息: {}", dataSourceName, dbInfo);
ErrorLogUtils.logStartupInfo(dataSourceName + " 连接成功 - " + dbInfo);
return true;
} else {
log.error("数据源 {} 查询测试失败", dataSourceName);
ErrorLogUtils.saveDatabaseError(dataSourceName + "查询测试失败",
new RuntimeException("SELECT 1 查询返回异常结果"));
return false;
}
}
}
} catch (Exception e) {
log.error("数据源 {} 健康检查失败", dataSourceName, e);
ErrorLogUtils.saveDatabaseError(dataSourceName + "健康检查失败", e);
return false;
}
}
/**
* 获取数据库基本信息
*/
private String getDatabaseInfo(Connection connection) {
try {
String dbName = connection.getMetaData().getDatabaseProductName();
String dbVersion = connection.getMetaData().getDatabaseProductVersion();
String driverName = connection.getMetaData().getDriverName();
String driverVersion = connection.getMetaData().getDriverVersion();
String url = connection.getMetaData().getURL();
String userName = connection.getMetaData().getUserName();
return String.format("%s %s (Driver: %s %s, URL: %s, User: %s)",
dbName, dbVersion, driverName, driverVersion, url, userName);
} catch (Exception e) {
log.warn("获取数据库信息失败", e);
return "无法获取数据库信息";
}
}
/**
* 手动触发数据库健康检查供其他组件调用
*/
public boolean performHealthCheck() {
// 检查是否启用了数据库健康检查
if (errorLogProperties == null || !errorLogProperties.isEnabled() || !errorLogProperties.isDatabaseEnabled()) {
log.warn("数据库健康检查已禁用,跳过手动检查");
return true; // 返回true表示没有问题因为检查被禁用
}
log.info("手动触发数据库健康检查");
boolean devopsHealthy = checkDataSource("devopsDS", devopsDataSource);
boolean youfoolHealthy = checkDataSource("youfoolDS", youfoolDataSource);
boolean allHealthy = devopsHealthy && youfoolHealthy;
String result = "手动健康检查结果: DevOps=" +
(devopsHealthy ? "正常" : "异常") + ", YouFool=" +
(youfoolHealthy ? "正常" : "异常");
log.info(result);
ErrorLogUtils.logStartupInfo(result);
return allHealthy;
}
/**
* 检查数据库健康状态别名方法用于SystemStatusTool等
*/
public boolean checkDatabaseHealth() {
return performHealthCheck();
}
/**
* 获取健康检查配置状态
*/
public Map<String, Object> getHealthCheckStatus() {
Map<String, Object> status = new HashMap<>();
status.put("enabled", errorLogProperties != null &&
errorLogProperties.isEnabled() &&
errorLogProperties.isDatabaseEnabled());
status.put("errorLogEnabled", errorLogProperties != null && errorLogProperties.isEnabled());
status.put("databaseCheckEnabled", errorLogProperties != null && errorLogProperties.isDatabaseEnabled());
return status;
}
}

View File

@ -6,14 +6,15 @@ import com.chinaweal.youfool.framework.springboot.mybatis.plus.CommonMetaObjectH
import org.apache.commons.lang3.ArrayUtils;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
/**
* 运维模块的数据源
*
@ -21,14 +22,15 @@ import javax.sql.DataSource;
*/
@Configuration
@MapperScan(basePackages = {"com.chinaweal.youfool.devops.repair.**.mapper", "com.chinaweal.youfool.devops.base.**.mapper", "com.chinaweal.youfool.devops.leaderassign.**.mapper", "com.chinaweal.youfool.devops.org.mapper", "com.chinaweal.youfool.devops.ai.**.mapper"}, sqlSessionTemplateRef = "devopsSqlSessionTemplate")
@MapperScan(basePackages = {"com.chinaweal.youfool.framework.springboot.cms.**.mapper", "com.chinaweal.youfool.devops.**.mapper"})
public class DevopsDataSource {
@Autowired
private CommonMetaObjectHandler commonMetaObjectHandler;
@Bean(name = "devopsSqlSessionFactory")
public MybatisSqlSessionFactoryBean sqlSessionFactory(@Qualifier("devopsDS") DataSource dataSource) throws Exception {
@Bean
@Primary
public MybatisSqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
bean.setDataSource(dataSource);
GlobalConfig globalConfig = new GlobalConfig();

View File

@ -1,9 +1,5 @@
package com.chinaweal.youfool.devops.config;
import com.chinaweal.youfool.framework.springboot.filter.RepeatlyReadFilter;
import com.chinaweal.youfool.framework.springboot.filter.RestLogFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
@ -11,29 +7,4 @@ import org.springframework.context.annotation.Configuration;
*/
@Configuration
public class FilterConfig {
/**
* 日志记录过滤器
*/
@Bean
public FilterRegistrationBean<RestLogFilter> restLogFilter() {
FilterRegistrationBean<RestLogFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new RestLogFilter());
registrationBean.addUrlPatterns("/*");
registrationBean.setName("restLogFilter");
registrationBean.setOrder(-99);
return registrationBean;
}
/**
* 开启重复读取request流用于日志
*/
@Bean
public FilterRegistrationBean<RepeatlyReadFilter> repeatlyReadFilter() {
FilterRegistrationBean<RepeatlyReadFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new RepeatlyReadFilter());
registrationBean.addUrlPatterns("/*");
registrationBean.setName("repeatlyReadFilter");
registrationBean.setOrder(-100);
return registrationBean;
}
}

View File

@ -1,59 +0,0 @@
//package com.chinaweal.youfool.devops.config;
//
//import com.alibaba.druid.pool.DruidDataSource;
//import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
//import com.baomidou.mybatisplus.core.config.GlobalConfig;
//import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
//import com.chinaweal.youfool.framework.springboot.mybatis.plus.CommonMetaObjectHandler;
//import org.apache.ibatis.session.SqlSessionFactory;
//import org.mybatis.spring.SqlSessionTemplate;
//import org.mybatis.spring.annotation.MapperScan;
//import org.springframework.beans.factory.annotation.Qualifier;
//import org.springframework.boot.context.properties.ConfigurationProperties;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.core.io.ClassPathResource;
//import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
//import org.springframework.jdbc.datasource.DataSourceTransactionManager;
//
//import javax.sql.DataSource;
//
///**
// * youfool基础的数据源
// *
// * @author itluck
// */
//
//@Configuration
//@MapperScan(basePackages = "com.chinaweal.youfool.framework.springboot.**.mapper", sqlSessionTemplateRef = "youfoolSqlSessionTemplate")
//public class YoufoolDataSource {
//
//
// @Bean(name = "youfoolDS", initMethod = "init", destroyMethod = "close")
// @ConfigurationProperties(prefix = "spring.datasource.youfool")
// public DruidDataSource dataSource() {
// return DruidDataSourceBuilder.create().build();
// }
//
// @Bean(name = "youfoolSqlSessionFactory")
// public MybatisSqlSessionFactoryBean sqlSessionFactory(@Qualifier("youfoolDS") DataSource dataSource) throws Exception {
// MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
// bean.setDataSource(dataSource);
// GlobalConfig globalConfig = new GlobalConfig();
// globalConfig.setMetaObjectHandler(new CommonMetaObjectHandler());
// bean.setGlobalConfig(globalConfig);
// bean.setConfigLocation(new ClassPathResource("youfool/mybatis/mybatis-config.xml"));
// bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:youfool/mybatis/mapper/**/*.xml"));
// return bean;
// }
//
// @Bean(name = "youfoolTransactionManager")
// public DataSourceTransactionManager transactionManager(@Qualifier("youfoolDS") DataSource dataSource) {
// return new DataSourceTransactionManager(dataSource);
// }
//
// @Bean(name = "youfoolSqlSessionTemplate")
// public SqlSessionTemplate sqlSessionTemplate(@Qualifier("youfoolSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
// return new SqlSessionTemplate(sqlSessionFactory);
// }
//}

View File

@ -1,13 +1,11 @@
package com.chinaweal.youfool.devops.websocket.server;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@ -18,13 +16,11 @@ import java.util.concurrent.ConcurrentHashMap;
*
* @author
*/
@Slf4j
@ServerEndpoint("/websocket/businessSys/{userId}")
@Component
@Slf4j
public class BusinessSysServer {
private static final Logger log = LoggerFactory.getLogger(BusinessSysServer.class);
/**
* 存储起来一个用户账号可能多页面打开存储列表
*/

View File

@ -45,6 +45,11 @@ spring:
max-request-size: 100MB
# Spring AI Configuration for Alibaba Qwen/DashScope
ai:
dashscope:
api-key: ${DASHSCOPE_API_KEY:sk-288824ef003e4e02bb963b8b3024b06a}
chat:
options:
model: ${QW_MODEL:qwen-plus}
openai:
# DashScope API configuration (OpenAI-compatible)
base-url: https://dashscope.aliyuncs.com/compatible-mode/v1