package cn.iocoder.yudao.module.infra.service.db; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils; import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigImportExcelVO; import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigImportReqVO; import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigImportRespVO; import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigSaveReqVO; import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO; import cn.iocoder.yudao.module.infra.dal.mysql.db.DataSourceConfigMapper; import com.baomidou.dynamic.datasource.creator.DataSourceProperty; import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_IMPORT_LIST_IS_EMPTY; import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_IMPORT_NAME_EXISTS; import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_DB_TYPE_UNSUPPORTED; import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_EXISTS; import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_OK; /** * 数据源配置 Service 实现类 * * @author 超级管理员 */ @Service @Validated public class DataSourceConfigServiceImpl implements DataSourceConfigService { @Resource private DataSourceConfigMapper dataSourceConfigMapper; @Resource private DynamicDataSourceProperties dynamicDataSourceProperties; @Override public Long createDataSourceConfig(DataSourceConfigSaveReqVO createReqVO) { DataSourceConfigDO config = BeanUtils.toBean(createReqVO, DataSourceConfigDO.class); validateConnectionOK(config); // 插入 dataSourceConfigMapper.insert(config); // 返回 return config.getId(); } @Override public void updateDataSourceConfig(DataSourceConfigSaveReqVO updateReqVO) { // 校验存在 validateDataSourceConfigExists(updateReqVO.getId()); DataSourceConfigDO updateObj = BeanUtils.toBean(updateReqVO, DataSourceConfigDO.class); validateConnectionOK(updateObj); // 更新 dataSourceConfigMapper.updateById(updateObj); } @Override public void deleteDataSourceConfig(Long id) { // 校验存在 validateDataSourceConfigExists(id); // 删除 dataSourceConfigMapper.deleteById(id); } @Override public void deleteDataSourceConfigList(List ids) { dataSourceConfigMapper.deleteByIds(ids); } private void validateDataSourceConfigExists(Long id) { if (dataSourceConfigMapper.selectById(id) == null) { throw exception(DATA_SOURCE_CONFIG_NOT_EXISTS); } } @Override public DataSourceConfigDO getDataSourceConfig(Long id) { // 如果 id 为 0,默认为 master 的数据源 if (Objects.equals(id, DataSourceConfigDO.ID_MASTER)) { return buildMasterDataSourceConfig(); } // 从 DB 中读取 return dataSourceConfigMapper.selectById(id); } @Override public List getDataSourceConfigList() { List result = dataSourceConfigMapper.selectList(); // 补充 master 数据源 result.add(0, buildMasterDataSourceConfig()); return result; } private void validateConnectionOK(DataSourceConfigDO config) { validateDatabaseTypeSupported(config.getUrl()); boolean success = JdbcUtils.isConnectionOK(config.getUrl(), config.getUsername(), config.getPassword()); if (!success) { throw exception(DATA_SOURCE_CONFIG_NOT_OK); } } /** * 默认轻量包只携带 MySQL 驱动。提前给出明确提示,避免缺少驱动时表现成含糊的连接失败。 */ private void validateDatabaseTypeSupported(String url) { if (!StrUtil.startWithIgnoreCase(url, "jdbc:mysql:")) { throw exception(DATA_SOURCE_CONFIG_DB_TYPE_UNSUPPORTED, url); } } private DataSourceConfigDO buildMasterDataSourceConfig() { String primary = dynamicDataSourceProperties.getPrimary(); DataSourceProperty dataSourceProperty = dynamicDataSourceProperties.getDatasource().get(primary); return new DataSourceConfigDO().setId(DataSourceConfigDO.ID_MASTER).setName(primary) .setUrl(dataSourceProperty.getUrl()) .setUsername(dataSourceProperty.getUsername()) .setPassword(dataSourceProperty.getPassword()); } @Override @Transactional(rollbackFor = Exception.class) public DataSourceConfigImportRespVO importDataSourceConfigList(List importDataSourceConfigs, DataSourceConfigImportReqVO importReqVO) { // 1. 校验非空 importDataSourceConfigs = filterList(importDataSourceConfigs, item -> StrUtil.isNotBlank(item.getName())); if (CollUtil.isEmpty(importDataSourceConfigs)) { throw exception(DATA_SOURCE_CONFIG_IMPORT_LIST_IS_EMPTY); } // 2. 逐条处理 DataSourceConfigImportRespVO respVO = DataSourceConfigImportRespVO.builder() .createDataSourceConfigNames(new ArrayList<>()) .updateDataSourceConfigNames(new ArrayList<>()) .failureDataSourceConfigNames(new LinkedHashMap<>()) .build(); importDataSourceConfigs.forEach(importConfig -> { try { validateDatabaseTypeSupported(importConfig.getUrl()); DataSourceConfigDO existConfig = dataSourceConfigMapper.selectByName(importConfig.getName()); if (existConfig == null) { // 新增 DataSourceConfigDO config = BeanUtils.toBean(importConfig, DataSourceConfigDO.class); dataSourceConfigMapper.insert(config); respVO.getCreateDataSourceConfigNames().add(importConfig.getName()); return; } // 已存在,判断是否允许更新 if (!importReqVO.getUpdateSupport()) { respVO.getFailureDataSourceConfigNames().put(importConfig.getName(), StrUtil.format(DATA_SOURCE_CONFIG_IMPORT_NAME_EXISTS.getMsg(), importConfig.getName())); return; } DataSourceConfigDO updateConfig = BeanUtils.toBean(importConfig, DataSourceConfigDO.class) .setId(existConfig.getId()); dataSourceConfigMapper.updateById(updateConfig); respVO.getUpdateDataSourceConfigNames().add(importConfig.getName()); } catch (ServiceException ex) { respVO.getFailureDataSourceConfigNames().put(importConfig.getName(), ex.getMessage()); } }); return respVO; } }