diff --git a/.gitignore b/.gitignore index 755a31a..6ff364a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,6 @@ /*.bat /setup_database.sql /PowerShell启动说明.md -/DEPLOYMENT.md \ No newline at end of file +/DEPLOYMENT.md +/ERROR_CAPTURE_GUIDE.md +/*.sql \ No newline at end of file diff --git a/src/main/java/com/chinaweal/youfool/devops/DevOpsApplication.java b/src/main/java/com/chinaweal/youfool/devops/DevOpsApplication.java index 01c1068..534883e 100644 --- a/src/main/java/com/chinaweal/youfool/devops/DevOpsApplication.java +++ b/src/main/java/com/chinaweal/youfool/devops/DevOpsApplication.java @@ -4,7 +4,11 @@ import com.chinaweal.youfool.devops.config.ErrorLogProperties; import com.chinaweal.youfool.devops.util.ErrorLogUtils; 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; @@ -28,6 +32,14 @@ public class DevOpsApplication extends SpringBootServletInitializer implements A @Autowired private ErrorLogProperties errorLogProperties; + + @Autowired + @Qualifier("devopsDS") + private DataSource devopsDataSource; + + @Autowired + @Qualifier("youfoolDS") + private DataSource youfoolDataSource; @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder applicationBuilder) { @@ -55,6 +67,10 @@ public class DevOpsApplication extends SpringBootServletInitializer implements A errorLogProperties.isDatabaseEnabled(), errorLogProperties.isBusinessEnabled()); } + + // 打印数据库连接信息 + printDatabaseConnectionInfo(); + log.info("===================================================================="); // 记录启动成功信息 @@ -172,4 +188,75 @@ public class DevOpsApplication extends SpringBootServletInitializer implements A } return false; // 默认认为是Java 8 } + + /** + * 打印数据库连接信息 + */ + 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); + } + } } diff --git a/src/main/java/com/chinaweal/youfool/devops/base/controller/DatabaseTestController.java b/src/main/java/com/chinaweal/youfool/devops/base/controller/DatabaseTestController.java new file mode 100644 index 0000000..a6b71e6 --- /dev/null +++ b/src/main/java/com/chinaweal/youfool/devops/base/controller/DatabaseTestController.java @@ -0,0 +1,238 @@ +package com.chinaweal.youfool.devops.base.controller; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +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") +@Api(tags = "数据库测试接口") +@Slf4j +public class DatabaseTestController { + + @Autowired + @Qualifier("devopsDS") + private DataSource devopsDataSource; + + @Autowired + @Qualifier("youfoolDS") + private DataSource youfoolDataSource; + + /** + * 测试数据库连接信息 + */ + @GetMapping("/connection") + @ApiOperation("测试数据库连接信息") + public Map testConnection() { + Map 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") + @ApiOperation("测试ENGINEER表结构") + public Map testEngineerStructure() { + Map 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> fields = new ArrayList<>(); + try (PreparedStatement ps = conn.prepareStatement(fieldsSql); + ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + Map 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") + @ApiOperation("测试ENGINEER表查询") + public Map testEngineerQuery() { + Map 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 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") + @ApiOperation("测试MyBatis数据源配置") + public Map testMyBatisDataSource() { + Map 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; + } +} \ No newline at end of file diff --git a/src/main/java/com/chinaweal/youfool/devops/org/entity/Engineer.java b/src/main/java/com/chinaweal/youfool/devops/org/entity/Engineer.java index 5e4cd0b..db9b872 100644 --- a/src/main/java/com/chinaweal/youfool/devops/org/entity/Engineer.java +++ b/src/main/java/com/chinaweal/youfool/devops/org/entity/Engineer.java @@ -26,7 +26,7 @@ import javax.validation.constraints.NotBlank; @Data @EqualsAndHashCode(callSuper = true) @Accessors(chain = true) -@TableName("ENGINEER") +@TableName("engineer") @ApiModel(value = "Engineer对象", description = "运维工程师") public class Engineer extends SuperEntity { @@ -36,7 +36,7 @@ public class Engineer extends SuperEntity { * 用户ID */ @ApiModelProperty(value = "用户ID") - @TableId(value = "USER_ID", type = IdType.ASSIGN_UUID) + @TableId(value = "user_id", type = IdType.ASSIGN_UUID) private String userId; /** @@ -44,7 +44,7 @@ public class Engineer extends SuperEntity { */ @NotBlank(message = "用户名不能为空") @ApiModelProperty(value = "用户名 登陆账号") - @TableField("USERNAME") + @TableField("username") private String username; /** @@ -52,7 +52,7 @@ public class Engineer extends SuperEntity { */ @NotBlank(message = "密码不能为空") @ApiModelProperty(value = "密码 采用国产SM3") - @TableField("PASSWORD") + @TableField("password") private String password; /** @@ -60,7 +60,7 @@ public class Engineer extends SuperEntity { */ @NotBlank(message = "昵称不能为空") @ApiModelProperty(value = "昵称") - @TableField("NICKNAME") + @TableField("nickname") private String nickname; /** @@ -68,21 +68,21 @@ public class Engineer extends SuperEntity { */ @ApiModelProperty(value = "性别 1:男;2:女;") @NotBlank(message = "性别不能为空") - @TableField("SEX") + @TableField("sex") private String sex; /** * 手机号 */ @ApiModelProperty(value = "手机号") - @TableField("PHONE") + @TableField("phone") private String phone; /** * 邮箱 */ @ApiModelProperty(value = "邮箱") - @TableField("EMAIL") + @TableField("email") private String email; /** @@ -90,35 +90,35 @@ public class Engineer extends SuperEntity { */ @NotBlank(message = "状态不能为空") @ApiModelProperty(value = "状态 1:正常;2:冻结;3:离职") - @TableField("STATUS") + @TableField("status") private String status; /** * 描述 */ @ApiModelProperty(value = "描述") - @TableField("DESCRIPTION") + @TableField("description") private String description; /** * 排序 越小则优先级更高 */ @ApiModelProperty(value = "排序 越小则优先级更高") - @TableField("SORT") + @TableField("sort") private BigDecimal sort; /** * 角色 角色代码集合 */ @ApiModelProperty(value = "角色 角色代码集合") - @TableField("ROLES") + @TableField("roles") private String roles; /** * 所负责的系统 集合 */ @ApiModelProperty(value = "所负责的系统 集合") - @TableField("SOURCE") + @TableField("source") private String source; /** @@ -126,7 +126,7 @@ public class Engineer extends SuperEntity { */ @ApiModelProperty(value = "是否删除 0:未删除、1:已删除") @TableLogic(value = ConstantsUtil.NOT_DELETED, delval = ConstantsUtil.DELETED) - @TableField("IS_DELETED") + @TableField("is_deleted") private String isDeleted; diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 961f1f1..2183f1a 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -32,3 +32,31 @@ applicationName: devOps swagger: enable: true +# 错误日志配置 +error-log: + # 是否启用错误日志文件写入功能 + enabled: true + # 启动错误日志 + startup-enabled: true + # 运行时错误日志 + runtime-enabled: true + # 数据库错误日志 + database-enabled: true + # 业务错误日志 + business-enabled: true + # 启动信息日志 + startup-info-enabled: true + # 日志目录 + log-directory: logs/errors + # 单个日志文件最大大小 + max-file-size: 10MB + # 保留日志文件的最大天数 + max-history: 30 + # 是否在控制台同时输出 + console-output: true + # 详细级别 (0-3, 3为最详细) + detail-level: 3 + # 是否异步写入 + async-write: true + # 异步队列大小 + async-queue-size: 1000 \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index d3d0cc1..bb790cd 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -3,7 +3,7 @@ logging: root: info spring: profiles: - active: prod + active: dev application: name: youfoo-devops datasource: @@ -84,7 +84,7 @@ dbUsernames: AICSCR,AICORG,SPEEQU,ZKRUSER,GOLDENGATE,DEVOPS,SYSTEM,SPEE_OS,DWDDA # 错误日志默认配置 error-log: # 全局开关,默认启用 - enabled: true + enabled: false # 各类型日志开关 startup-enabled: true runtime-enabled: true