diff --git a/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java b/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java index c7ef4999..33d3b566 100644 --- a/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java +++ b/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java @@ -78,6 +78,7 @@ public class SecurityConfig { // .requestMatchers("/threedroamb/**").permitAll() // .requestMatchers("/overview/**").permitAll() .requestMatchers("/wt/**").permitAll() + .requestMatchers("/datasource/**").permitAll() .requestMatchers("/system/acctPasswordPolicy/**").permitAll() .requestMatchers("/fb/**").permitAll() // .requestMatchers("/base/**").permitAll() diff --git a/backend/src/main/java/com/yfd/platform/datasource/DynamicDataSource.java b/backend/src/main/java/com/yfd/platform/datasource/DynamicDataSource.java index 2d5f62b6..b47b6a47 100644 --- a/backend/src/main/java/com/yfd/platform/datasource/DynamicDataSource.java +++ b/backend/src/main/java/com/yfd/platform/datasource/DynamicDataSource.java @@ -119,7 +119,7 @@ public class DynamicDataSource extends AbstractRoutingDataSource { @SuppressWarnings("unchecked") public Set getRegisteredKeys() { Map resolved = getResolvedDataSources(); - return resolved == null ? Set.of() : resolved.keySet(); + return resolved.keySet(); } /** diff --git a/backend/src/main/java/com/yfd/platform/datasource/DynamicDataSourceController.java b/backend/src/main/java/com/yfd/platform/datasource/DynamicDataSourceController.java index 4b68eb40..1c722ba0 100644 --- a/backend/src/main/java/com/yfd/platform/datasource/DynamicDataSourceController.java +++ b/backend/src/main/java/com/yfd/platform/datasource/DynamicDataSourceController.java @@ -37,6 +37,9 @@ public class DynamicDataSourceController { @Resource private DynamicDataSource dynamicDataSource; + @Resource + private DynamicDataSourceTestService dynamicDataSourceTestService; + /** * 列出所有已注册的数据源(含系统内置 dm-master/dm-slave) */ @@ -108,4 +111,109 @@ public class DynamicDataSourceController { return ResponseResult.error("未找到数据源: " + key); } } + + /** + * 测试指定数据源连通性,并执行一条查询验证 + * + * @param dsKey 数据源路由 key + * @param sql 可选 SQL(默认自动选择 SELECT 1 或 SELECT 1 FROM DUAL) + * @return 查询结果 + */ + @GetMapping("/test/{dsKey}") + @Operation(summary = "测试数据源连通性(执行查询验证)") + public ResponseResult testDataSource(@PathVariable("dsKey") String dsKey, + @RequestParam(value = "sql", required = false) String sql) { + DataSource ds = dynamicDataSource.getDataSourceByKey(dsKey); + if (ds == null) { + return ResponseResult.error("未找到数据源: " + dsKey + ",请先注册或查看 /datasource/list"); + } + + // 自动选择默认验证 SQL + if (sql == null || sql.isBlank()) { + String lowerKey = dsKey.toLowerCase(); + sql = lowerKey.contains("oracle") ? "SELECT 1 FROM DUAL" : "SELECT 1"; + } + final String finalSql = sql; + + try { + JdbcTemplate jt = new JdbcTemplate(ds); + List> result = jt.execute((StatementCallback>>) stmt -> { + try (ResultSet rs = stmt.executeQuery(finalSql)) { + ResultSetMetaData meta = rs.getMetaData(); + int colCount = meta.getColumnCount(); + List> rows = new ArrayList<>(); + while (rs.next()) { + Map row = new LinkedHashMap<>(); + for (int i = 1; i <= colCount; i++) { + row.put(meta.getColumnLabel(i), rs.getObject(i)); + } + rows.add(row); + } + return rows; + } + }); + + Map data = new LinkedHashMap<>(); + data.put("dsKey", dsKey); + data.put("sql", sql); + data.put("dsType", ds.getClass().getSimpleName()); + data.put("rowCount", result.size()); + data.put("rows", result); + + return ResponseResult.successData(data); + } catch (Exception e) { + Map data = new LinkedHashMap<>(); + data.put("dsKey", dsKey); + data.put("sql", sql); + data.put("error", e.getMessage()); + return ResponseResult.error("数据源查询失败: " + e.getMessage()); + } + } + + /** + * 使用 @TargetDataSource 注解测试数据源(AOP 路由方式) + *

+ * 根据 dsKey 路由到对应的注解方法,由 AOP 自动切换数据源,验证完整的注解路由链路。 + * 支持以下 key:dm-master / dm-slave / oracle-master / oracle-slave / devtw-oracle-master / devtw-oracle-slave + */ + @GetMapping("/test-anno/{dsKey}") + @Operation(summary = "注解方式测试数据源(@TargetDataSource AOP 路由)") + public ResponseResult testDataSourceByAnnotation(@PathVariable("dsKey") String dsKey) { + try { + List> rows; + switch (dsKey) { + case "dm-master": + rows = dynamicDataSourceTestService.testDmMaster(); + break; + case "dm-slave": + rows = dynamicDataSourceTestService.testDmSlave(); + break; + case "oracle-master": + rows = dynamicDataSourceTestService.testOracleMaster(); + break; + case "oracle-slave": + rows = dynamicDataSourceTestService.testOracleSlave(); + break; + case "ext-oracle-master": + rows = dynamicDataSourceTestService.testDevtwOracleMaster(); + break; + case "ext-oracle-slave": + rows = dynamicDataSourceTestService.testDevtwOracleSlave(); + break; + default: + return ResponseResult.error("未预定义注解方法的数据源 key: " + dsKey + + ",可选: dm-master / dm-slave / oracle-master / oracle-slave / devtw-oracle-master / devtw-oracle-slave"); + } + + // 方法执行完后 AOP 已自动清除上下文,在这里获取不到路由 key + Map data = new LinkedHashMap<>(); + data.put("dsKey", dsKey); + data.put("method", "动态数据源切换方式"); + data.put("rowCount", rows.size()); + data.put("rows", rows); + return ResponseResult.successData(data); + } catch (Exception e) { + return ResponseResult.error("注解方式数据源测试失败: " + e.getMessage()); + } + } } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/datasource/DynamicDataSourceTestService.java b/backend/src/main/java/com/yfd/platform/datasource/DynamicDataSourceTestService.java new file mode 100644 index 00000000..22020bb6 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/datasource/DynamicDataSourceTestService.java @@ -0,0 +1,50 @@ +package com.yfd.platform.datasource; + +import com.yfd.platform.common.MicroservicDynamicSQLMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 动态数据源测试服务,使用 @TargetDataSource 注解切换数据源。 + *

+ * 每个方法对应一个固定数据源 key,通过注解指定,验证 AOP 路由是否正常工作。 + */ +@Service +public class DynamicDataSourceTestService { + + @Resource + private MicroservicDynamicSQLMapper microservicDynamicSQLMapper; + + @TargetDataSource(DataSourceKeys.DM_MASTER) + public List> testDmMaster() { + return microservicDynamicSQLMapper.getAllList("SELECT 1 AS VAL FROM DUAL", null); + } + + @TargetDataSource(DataSourceKeys.DM_SLAVE) + public List> testDmSlave() { + return microservicDynamicSQLMapper.getAllList("SELECT 1 AS VAL FROM DUAL", null); + } + + @TargetDataSource("oracle-master") + public List> testOracleMaster() { + return microservicDynamicSQLMapper.getAllList("SELECT 1 AS VAL FROM DUAL", null); + } + + @TargetDataSource("oracle-slave") + public List> testOracleSlave() { + return microservicDynamicSQLMapper.getAllList("SELECT 1 AS VAL FROM DUAL", null); + } + + @TargetDataSource("ext-oracle-master") + public List> testDevtwOracleMaster() { + return microservicDynamicSQLMapper.getAllList("select count(*) from SD_WTRV_R WHERE is_deleted = 0", null); + } + + @TargetDataSource("ext-oracle-slave") + public List> testDevtwOracleSlave() { + return microservicDynamicSQLMapper.getAllList("select count(*) from SD_WTRV_R WHERE is_deleted = 0", null); + } +} \ No newline at end of file