package com.tcm.training.admin.bootstrap; import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.beans.factory.ObjectProvider; import org.springframework.stereotype.Component; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; /** * 启动时校验海诊通动态数据源配置,避免 @DS 未命中时静默回退主库。 */ @Component public class HaizhentongDataSourceStartupValidator implements ApplicationRunner { private static final Logger log = LoggerFactory.getLogger(HaizhentongDataSourceStartupValidator.class); private static final String HAIZHENTONG_DATASOURCE_NAME = "haizhentong"; private final DynamicDataSourceProperties dynamicDataSourceProperties; private final ObjectProvider dynamicRoutingDataSourceProvider; /** * 注入启动校验所需的配置环境与主数据源代理。 */ public HaizhentongDataSourceStartupValidator( DynamicDataSourceProperties dynamicDataSourceProperties, ObjectProvider dynamicRoutingDataSourceProvider ) { this.dynamicDataSourceProperties = dynamicDataSourceProperties; this.dynamicRoutingDataSourceProvider = dynamicRoutingDataSourceProvider; } /** * 在应用启动阶段校验海诊通数据源是否已配置且已注册到动态路由中。 */ @Override public void run(ApplicationArguments args) { Set configuredDataSourceNames = getConfiguredDataSourceNames(); validateConfiguredDataSourceNames(configuredDataSourceNames); DynamicRoutingDataSource dynamicRoutingDataSource = dynamicRoutingDataSourceProvider.getIfAvailable(); if (dynamicRoutingDataSource == null) { log.warn("未获取到 DynamicRoutingDataSource Bean,跳过海诊通数据源运行期注册校验,configured={}", configuredDataSourceNames); return; } Set registeredDataSourceNames = getRegisteredDataSourceNames(dynamicRoutingDataSource); validateRegisteredDataSourceNames(registeredDataSourceNames); log.info("海诊通动态数据源校验通过,configured={}, registered={}", configuredDataSourceNames, registeredDataSourceNames); } /** * 读取配置文件中声明的动态数据源名称列表。 */ private Set getConfiguredDataSourceNames() { Map datasourceMap = dynamicDataSourceProperties.getDatasource(); if (datasourceMap == null) { return Collections.emptySet(); } return new LinkedHashSet(datasourceMap.keySet()); } /** * 校验配置文件中已显式声明海诊通数据源。 */ private void validateConfiguredDataSourceNames(Set configuredDataSourceNames) { if (configuredDataSourceNames.contains(HAIZHENTONG_DATASOURCE_NAME)) { return; } throw new IllegalStateException("海诊通数据源 haizhentong 未配置,请检查 application-local.yml"); } /** * 通过动态数据源路由器反射读取运行期已注册的数据源名称。 */ private Set getRegisteredDataSourceNames(DynamicRoutingDataSource dynamicRoutingDataSource) { Map datasourceMap = dynamicRoutingDataSource.getDataSources(); if (datasourceMap == null) { throw new IllegalStateException("动态数据源路由未返回有效的数据源注册表"); } Set datasourceNames = new LinkedHashSet(); for (Object key : datasourceMap.keySet()) { datasourceNames.add(String.valueOf(key)); } return datasourceNames; } /** * 校验海诊通数据源已被动态数据源组件成功注册。 */ private void validateRegisteredDataSourceNames(Set registeredDataSourceNames) { if (registeredDataSourceNames.contains(HAIZHENTONG_DATASOURCE_NAME)) { return; } throw new IllegalStateException("海诊通数据源 haizhentong 未注册成功,请检查多数据源配置"); } }