Commit 7ce7baa2d2207657192b93845969ae200ee89f91

Authored by YunaiV
1 parent c4020779

增加数据源管理

Showing 24 changed files with 794 additions and 3 deletions
yudao-dependencies/pom.xml
@@ -45,6 +45,7 @@ @@ -45,6 +45,7 @@
45 <activiti.version>7.1.0.M6</activiti.version> 45 <activiti.version>7.1.0.M6</activiti.version>
46 <flowable.version>6.7.0</flowable.version> 46 <flowable.version>6.7.0</flowable.version>
47 <!-- 工具类相关 --> 47 <!-- 工具类相关 -->
  48 + <jasypt-spring-boot-starter.version>3.0.4</jasypt-spring-boot-starter.version>
48 <lombok.version>1.18.20</lombok.version> 49 <lombok.version>1.18.20</lombok.version>
49 <mapstruct.version>1.4.1.Final</mapstruct.version> 50 <mapstruct.version>1.4.1.Final</mapstruct.version>
50 <hutool.version>5.6.1</hutool.version> 51 <hutool.version>5.6.1</hutool.version>
@@ -429,6 +430,12 @@ @@ -429,6 +430,12 @@
429 </dependency> 430 </dependency>
430 431
431 <dependency> 432 <dependency>
  433 + <groupId>com.github.ulisesbocchio</groupId>
  434 + <artifactId>jasypt-spring-boot-starter</artifactId> <!-- 加解密 -->
  435 + <version>${jasypt-spring-boot-starter.version}</version>
  436 + </dependency>
  437 +
  438 + <dependency>
432 <groupId>cn.iocoder.boot</groupId> 439 <groupId>cn.iocoder.boot</groupId>
433 <artifactId>yudao-spring-boot-starter-excel</artifactId> 440 <artifactId>yudao-spring-boot-starter-excel</artifactId>
434 <version>${revision}</version> 441 <version>${revision}</version>
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/DatabaseUtils.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.mybatis.core.util;
  2 +
  3 +import java.sql.Connection;
  4 +import java.sql.DriverManager;
  5 +
  6 +/**
  7 + * 数据库工具类
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +public class DatabaseUtils {
  12 +
  13 + /**
  14 + * 判断连接是否正确
  15 + *
  16 + * @param url 数据源连接
  17 + * @param username 账号
  18 + * @param password 密码
  19 + * @return 是否正确
  20 + */
  21 + public static boolean isConnectionOK(String url, String username, String password) {
  22 + try (Connection ignored = DriverManager.getConnection(url, username, password)) {
  23 + return true;
  24 + } catch (Exception ex) {
  25 + return false;
  26 + }
  27 + }
  28 +
  29 +}
yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java
@@ -49,4 +49,8 @@ public interface ErrorCodeConstants { @@ -49,4 +49,8 @@ public interface ErrorCodeConstants {
49 ErrorCode FILE_CONFIG_NOT_EXISTS = new ErrorCode(1001006000, "文件配置不存在"); 49 ErrorCode FILE_CONFIG_NOT_EXISTS = new ErrorCode(1001006000, "文件配置不存在");
50 ErrorCode FILE_CONFIG_DELETE_FAIL_MASTER = new ErrorCode(1001006001, "该文件配置不允许删除,原因:它是主配置,删除会导致无法上传文件"); 50 ErrorCode FILE_CONFIG_DELETE_FAIL_MASTER = new ErrorCode(1001006001, "该文件配置不允许删除,原因:它是主配置,删除会导致无法上传文件");
51 51
  52 + // ========== 数据源配置 1001007000 ==========
  53 + ErrorCode DATA_SOURCE_CONFIG_NOT_EXISTS = new ErrorCode(1001007000, "数据源配置不存在");
  54 + ErrorCode DATA_SOURCE_CONFIG_NOT_OK = new ErrorCode(1001007001, "数据源配置不正确,无法进行连接");
  55 +
52 } 56 }
yudao-module-infra/yudao-module-infra-biz/pom.xml
@@ -80,6 +80,11 @@ @@ -80,6 +80,11 @@
80 80
81 <!-- 工具类相关 --> 81 <!-- 工具类相关 -->
82 <dependency> 82 <dependency>
  83 + <groupId>com.github.ulisesbocchio</groupId>
  84 + <artifactId>jasypt-spring-boot-starter</artifactId> <!-- 加解密 -->
  85 + </dependency>
  86 +
  87 + <dependency>
83 <groupId>cn.iocoder.boot</groupId> 88 <groupId>cn.iocoder.boot</groupId>
84 <artifactId>yudao-spring-boot-starter-excel</artifactId> 89 <artifactId>yudao-spring-boot-starter-excel</artifactId>
85 </dependency> 90 </dependency>
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/DataSourceConfigController.java 0 → 100755
  1 +package cn.iocoder.yudao.module.infra.controller.admin.db;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigCreateReqVO;
  5 +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigRespVO;
  6 +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigUpdateReqVO;
  7 +import cn.iocoder.yudao.module.infra.convert.db.DataSourceConfigConvert;
  8 +import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
  9 +import cn.iocoder.yudao.module.infra.service.db.DataSourceConfigService;
  10 +import io.swagger.annotations.Api;
  11 +import io.swagger.annotations.ApiImplicitParam;
  12 +import io.swagger.annotations.ApiOperation;
  13 +import org.springframework.security.access.prepost.PreAuthorize;
  14 +import org.springframework.validation.annotation.Validated;
  15 +import org.springframework.web.bind.annotation.*;
  16 +
  17 +import javax.annotation.Resource;
  18 +import javax.validation.Valid;
  19 +import java.util.List;
  20 +
  21 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  22 +
  23 +@Api(tags = "管理后台 - 数据源配置")
  24 +@RestController
  25 +@RequestMapping("/infra/data-source-config")
  26 +@Validated
  27 +public class DataSourceConfigController {
  28 +
  29 + @Resource
  30 + private DataSourceConfigService dataSourceConfigService;
  31 +
  32 + @PostMapping("/create")
  33 + @ApiOperation("创建数据源配置")
  34 + @PreAuthorize("@ss.hasPermission('infra:data-source-config:create')")
  35 + public CommonResult<Long> createDataSourceConfig(@Valid @RequestBody DataSourceConfigCreateReqVO createReqVO) {
  36 + return success(dataSourceConfigService.createDataSourceConfig(createReqVO));
  37 + }
  38 +
  39 + @PutMapping("/update")
  40 + @ApiOperation("更新数据源配置")
  41 + @PreAuthorize("@ss.hasPermission('infra:data-source-config:update')")
  42 + public CommonResult<Boolean> updateDataSourceConfig(@Valid @RequestBody DataSourceConfigUpdateReqVO updateReqVO) {
  43 + dataSourceConfigService.updateDataSourceConfig(updateReqVO);
  44 + return success(true);
  45 + }
  46 +
  47 + @DeleteMapping("/delete")
  48 + @ApiOperation("删除数据源配置")
  49 + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
  50 + @PreAuthorize("@ss.hasPermission('infra:data-source-config:delete')")
  51 + public CommonResult<Boolean> deleteDataSourceConfig(@RequestParam("id") Long id) {
  52 + dataSourceConfigService.deleteDataSourceConfig(id);
  53 + return success(true);
  54 + }
  55 +
  56 + @GetMapping("/get")
  57 + @ApiOperation("获得数据源配置")
  58 + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
  59 + @PreAuthorize("@ss.hasPermission('infra:data-source-config:query')")
  60 + public CommonResult<DataSourceConfigRespVO> getDataSourceConfig(@RequestParam("id") Long id) {
  61 + DataSourceConfigDO dataSourceConfig = dataSourceConfigService.getDataSourceConfig(id);
  62 + return success(DataSourceConfigConvert.INSTANCE.convert(dataSourceConfig));
  63 + }
  64 +
  65 + @GetMapping("/list")
  66 + @ApiOperation("获得数据源配置列表")
  67 + @PreAuthorize("@ss.hasPermission('infra:data-source-config:query')")
  68 + public CommonResult<List<DataSourceConfigRespVO>> getDataSourceConfigList() {
  69 + List<DataSourceConfigDO> list = dataSourceConfigService.getDataSourceConfigList();
  70 + return success(DataSourceConfigConvert.INSTANCE.convertList(list));
  71 + }
  72 +
  73 +}
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/doc/DbDocController.java renamed to yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/DatabaseDocController.java
1 -package cn.iocoder.yudao.module.infra.controller.admin.doc; 1 +package cn.iocoder.yudao.module.infra.controller.admin.db;
2 2
3 import cn.hutool.core.io.FileUtil; 3 import cn.hutool.core.io.FileUtil;
4 import cn.hutool.core.util.IdUtil; 4 import cn.hutool.core.util.IdUtil;
@@ -30,7 +30,7 @@ import java.util.Arrays; @@ -30,7 +30,7 @@ import java.util.Arrays;
30 @Api(tags = "管理后台 - 数据库文档") 30 @Api(tags = "管理后台 - 数据库文档")
31 @RestController 31 @RestController
32 @RequestMapping("/infra/db-doc") 32 @RequestMapping("/infra/db-doc")
33 -public class DbDocController { 33 +public class DatabaseDocController {
34 34
35 @Resource 35 @Resource
36 private DynamicDataSourceProperties dynamicDataSourceProperties; 36 private DynamicDataSourceProperties dynamicDataSourceProperties;
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigBaseVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.infra.controller.admin.db.vo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import io.swagger.annotations.*;
  6 +import javax.validation.constraints.*;
  7 +
  8 +/**
  9 +* 数据源配置 Base VO,提供给添加、修改、详细的子 VO 使用
  10 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
  11 +*/
  12 +@Data
  13 +public class DataSourceConfigBaseVO {
  14 +
  15 + @ApiModelProperty(value = "参数名称", required = true, example = "test")
  16 + @NotNull(message = "参数名称不能为空")
  17 + private String name;
  18 +
  19 + @ApiModelProperty(value = "数据源连接", required = true, example = "jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro")
  20 + @NotNull(message = "数据源连接不能为空")
  21 + private String url;
  22 +
  23 + @ApiModelProperty(value = "用户名", required = true, example = "root")
  24 + @NotNull(message = "用户名不能为空")
  25 + private String username;
  26 +
  27 +}
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigCreateReqVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.infra.controller.admin.db.vo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import io.swagger.annotations.*;
  6 +import javax.validation.constraints.*;
  7 +
  8 +@ApiModel("管理后台 - 数据源配置创建 Request VO")
  9 +@Data
  10 +@EqualsAndHashCode(callSuper = true)
  11 +@ToString(callSuper = true)
  12 +public class DataSourceConfigCreateReqVO extends DataSourceConfigBaseVO {
  13 +
  14 + @ApiModelProperty(value = "密码", required = true, example = "123456")
  15 + @NotNull(message = "密码不能为空")
  16 + private String password;
  17 +
  18 +}
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigRespVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.infra.controller.admin.db.vo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import io.swagger.annotations.*;
  6 +
  7 +@ApiModel("管理后台 - 数据源配置 Response VO")
  8 +@Data
  9 +@EqualsAndHashCode(callSuper = true)
  10 +@ToString(callSuper = true)
  11 +public class DataSourceConfigRespVO extends DataSourceConfigBaseVO {
  12 +
  13 + @ApiModelProperty(value = "主键编号", required = true, example = "1024")
  14 + private Integer id;
  15 +
  16 + @ApiModelProperty(value = "创建时间", required = true)
  17 + private Date createTime;
  18 +
  19 +}
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigUpdateReqVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.infra.controller.admin.db.vo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import io.swagger.annotations.*;
  6 +import javax.validation.constraints.*;
  7 +
  8 +@ApiModel("管理后台 - 数据源配置更新 Request VO")
  9 +@Data
  10 +@EqualsAndHashCode(callSuper = true)
  11 +@ToString(callSuper = true)
  12 +public class DataSourceConfigUpdateReqVO extends DataSourceConfigBaseVO {
  13 +
  14 + @ApiModelProperty(value = "主键编号", required = true, example = "1024")
  15 + @NotNull(message = "主键编号不能为空")
  16 + private Long id;
  17 +
  18 + @ApiModelProperty(value = "密码", required = true, example = "123456")
  19 + @NotNull(message = "密码不能为空")
  20 + private String password;
  21 +
  22 +}
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/db/DataSourceConfigConvert.java 0 → 100755
  1 +package cn.iocoder.yudao.module.infra.convert.db;
  2 +
  3 +import java.util.*;
  4 +
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +
  7 +import org.mapstruct.Mapper;
  8 +import org.mapstruct.factory.Mappers;
  9 +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.*;
  10 +import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
  11 +
  12 +/**
  13 + * 数据源配置 Convert
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Mapper
  18 +public interface DataSourceConfigConvert {
  19 +
  20 + DataSourceConfigConvert INSTANCE = Mappers.getMapper(DataSourceConfigConvert.class);
  21 +
  22 + DataSourceConfigDO convert(DataSourceConfigCreateReqVO bean);
  23 +
  24 + DataSourceConfigDO convert(DataSourceConfigUpdateReqVO bean);
  25 +
  26 + DataSourceConfigRespVO convert(DataSourceConfigDO bean);
  27 +
  28 + List<DataSourceConfigRespVO> convertList(List<DataSourceConfigDO> list);
  29 +
  30 +}
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/db/DataSourceConfigDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.infra.dal.dataobject.db;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import com.baomidou.mybatisplus.annotation.TableName;
  5 +import lombok.Data;
  6 +
  7 +/**
  8 + * 数据源配置
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +@TableName("infra_data_source_config")
  13 +@Data
  14 +public class DataSourceConfigDO extends BaseDO {
  15 +
  16 + /**
  17 + * 主键编号
  18 + */
  19 + private Long id;
  20 + /**
  21 + * 连接名
  22 + */
  23 + private String name;
  24 +
  25 + /**
  26 + * 数据源连接
  27 + */
  28 + private String url;
  29 + /**
  30 + * 用户名
  31 + */
  32 + private String username;
  33 + /**
  34 + * 密码
  35 + */
  36 + private String password;
  37 +
  38 +}
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/db/DataSourceConfigMapper.java 0 → 100755
  1 +package cn.iocoder.yudao.module.infra.dal.mysql.db;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
  5 +import org.apache.ibatis.annotations.Mapper;
  6 +
  7 +/**
  8 + * 数据源配置 Mapper
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +@Mapper
  13 +public interface DataSourceConfigMapper extends BaseMapperX<DataSourceConfigDO> {
  14 +}
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigService.java 0 → 100755
  1 +package cn.iocoder.yudao.module.infra.service.db;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigCreateReqVO;
  5 +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigUpdateReqVO;
  6 +import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
  7 +import org.w3c.dom.stylesheets.LinkStyle;
  8 +
  9 +import javax.validation.Valid;
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * 数据源配置 Service 接口
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +public interface DataSourceConfigService {
  18 +
  19 + /**
  20 + * 创建数据源配置
  21 + *
  22 + * @param createReqVO 创建信息
  23 + * @return 编号
  24 + */
  25 + Long createDataSourceConfig(@Valid DataSourceConfigCreateReqVO createReqVO);
  26 +
  27 + /**
  28 + * 更新数据源配置
  29 + *
  30 + * @param updateReqVO 更新信息
  31 + */
  32 + void updateDataSourceConfig(@Valid DataSourceConfigUpdateReqVO updateReqVO);
  33 +
  34 + /**
  35 + * 删除数据源配置
  36 + *
  37 + * @param id 编号
  38 + */
  39 + void deleteDataSourceConfig(Long id);
  40 +
  41 + /**
  42 + * 获得数据源配置
  43 + *
  44 + * @param id 编号
  45 + * @return 数据源配置
  46 + */
  47 + DataSourceConfigDO getDataSourceConfig(Long id);
  48 +
  49 + /**
  50 + * 获得数据源配置列表
  51 + *
  52 + * @return 数据源配置列表
  53 + */
  54 + List<DataSourceConfigDO> getDataSourceConfigList();
  55 +
  56 +}
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImpl.java 0 → 100755
  1 +package cn.iocoder.yudao.module.infra.service.db;
  2 +
  3 +import cn.hutool.db.DbUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.mybatis.core.util.DatabaseUtils;
  6 +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigCreateReqVO;
  7 +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigUpdateReqVO;
  8 +import cn.iocoder.yudao.module.infra.convert.db.DataSourceConfigConvert;
  9 +import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
  10 +import cn.iocoder.yudao.module.infra.dal.mysql.db.DataSourceConfigMapper;
  11 +import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
  12 +import org.jasypt.encryption.StringEncryptor;
  13 +import org.springframework.stereotype.Service;
  14 +import org.springframework.validation.annotation.Validated;
  15 +
  16 +import javax.annotation.Resource;
  17 +
  18 +import java.sql.Connection;
  19 +import java.util.List;
  20 +
  21 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  22 +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_EXISTS;
  23 +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_OK;
  24 +
  25 +/**
  26 + * 数据源配置 Service 实现类
  27 + *
  28 + * @author 芋道源码
  29 + */
  30 +@Service
  31 +@Validated
  32 +public class DataSourceConfigServiceImpl implements DataSourceConfigService {
  33 +
  34 + @Resource
  35 + private DataSourceConfigMapper dataSourceConfigMapper;
  36 +
  37 + @Resource
  38 + private StringEncryptor stringEncryptor;
  39 +
  40 + @Override
  41 + public Long createDataSourceConfig(DataSourceConfigCreateReqVO createReqVO) {
  42 + DataSourceConfigDO dataSourceConfig = DataSourceConfigConvert.INSTANCE.convert(createReqVO);
  43 + checkConnectionOK(dataSourceConfig);
  44 +
  45 + // 插入
  46 + dataSourceConfig.setPassword(stringEncryptor.encrypt(createReqVO.getPassword()));
  47 + dataSourceConfigMapper.insert(dataSourceConfig);
  48 + // 返回
  49 + return dataSourceConfig.getId();
  50 + }
  51 +
  52 + @Override
  53 + public void updateDataSourceConfig(DataSourceConfigUpdateReqVO updateReqVO) {
  54 + // 校验存在
  55 + validateDataSourceConfigExists(updateReqVO.getId());
  56 + DataSourceConfigDO updateObj = DataSourceConfigConvert.INSTANCE.convert(updateReqVO);
  57 + checkConnectionOK(updateObj);
  58 +
  59 + // 更新
  60 + updateObj.setPassword(stringEncryptor.encrypt(updateObj.getPassword()));
  61 + dataSourceConfigMapper.updateById(updateObj);
  62 + }
  63 +
  64 + @Override
  65 + public void deleteDataSourceConfig(Long id) {
  66 + // 校验存在
  67 + validateDataSourceConfigExists(id);
  68 + // 删除
  69 + dataSourceConfigMapper.deleteById(id);
  70 + }
  71 +
  72 + private void validateDataSourceConfigExists(Long id) {
  73 + if (dataSourceConfigMapper.selectById(id) == null) {
  74 + throw exception(DATA_SOURCE_CONFIG_NOT_EXISTS);
  75 + }
  76 + }
  77 +
  78 + @Override
  79 + public DataSourceConfigDO getDataSourceConfig(Long id) {
  80 + DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(id);
  81 + dataSourceConfig.setPassword(stringEncryptor.decrypt(dataSourceConfig.getPassword()));
  82 + return dataSourceConfig;
  83 + }
  84 +
  85 + @Override
  86 + public List<DataSourceConfigDO> getDataSourceConfigList() {
  87 + return dataSourceConfigMapper.selectList();
  88 + }
  89 +
  90 + private void checkConnectionOK(DataSourceConfigDO config) {
  91 + boolean success = DatabaseUtils.isConnectionOK(config.getUrl(), config.getUsername(), config.getPassword());
  92 + if (!success) {
  93 + throw exception(DATA_SOURCE_CONFIG_NOT_OK);
  94 + }
  95 + }
  96 +
  97 +}
yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImplTest.java 0 → 100755
  1 +package cn.iocoder.yudao.module.infra.service.db;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.util.DatabaseUtils;
  4 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  5 +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigCreateReqVO;
  6 +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigUpdateReqVO;
  7 +import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO;
  8 +import cn.iocoder.yudao.module.infra.dal.mysql.db.DataSourceConfigMapper;
  9 +import org.jasypt.encryption.StringEncryptor;
  10 +import org.junit.jupiter.api.Test;
  11 +import org.mockito.MockedStatic;
  12 +import org.springframework.boot.test.mock.mockito.MockBean;
  13 +import org.springframework.context.annotation.Import;
  14 +
  15 +import javax.annotation.Resource;
  16 +
  17 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
  18 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  19 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
  20 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  21 +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_EXISTS;
  22 +import static org.junit.jupiter.api.Assertions.*;
  23 +import static org.mockito.Mockito.*;
  24 +
  25 +/**
  26 + * {@link DataSourceConfigServiceImpl} 的单元测试类
  27 + *
  28 + * @author 芋道源码
  29 + */
  30 +@Import(DataSourceConfigServiceImpl.class)
  31 +public class DataSourceConfigServiceImplTest extends BaseDbUnitTest {
  32 +
  33 + @Resource
  34 + private DataSourceConfigServiceImpl dataSourceConfigService;
  35 +
  36 + @Resource
  37 + private DataSourceConfigMapper dataSourceConfigMapper;
  38 +
  39 + @MockBean
  40 + private StringEncryptor stringEncryptor;
  41 +
  42 + @Test
  43 + public void testCreateDataSourceConfig_success() {
  44 + try (MockedStatic<DatabaseUtils> databaseUtilsMock = mockStatic(DatabaseUtils.class)) {
  45 + // 准备参数
  46 + DataSourceConfigCreateReqVO reqVO = randomPojo(DataSourceConfigCreateReqVO.class);
  47 + // mock 方法
  48 + when(stringEncryptor.encrypt(eq(reqVO.getPassword()))).thenReturn("123456");
  49 + databaseUtilsMock.when(() -> DatabaseUtils.isConnectionOK(eq(reqVO.getUrl()),
  50 + eq(reqVO.getUsername()), eq(reqVO.getPassword()))).thenReturn(true);
  51 +
  52 + // 调用
  53 + Long dataSourceConfigId = dataSourceConfigService.createDataSourceConfig(reqVO);
  54 + // 断言
  55 + assertNotNull(dataSourceConfigId);
  56 + // 校验记录的属性是否正确
  57 + DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(dataSourceConfigId);
  58 + assertPojoEquals(reqVO, dataSourceConfig, "password");
  59 + assertEquals("123456", dataSourceConfig.getPassword());
  60 + }
  61 + }
  62 +
  63 + @Test
  64 + public void testUpdateDataSourceConfig_success() {
  65 + try (MockedStatic<DatabaseUtils> databaseUtilsMock = mockStatic(DatabaseUtils.class)) {
  66 + // mock 数据
  67 + DataSourceConfigDO dbDataSourceConfig = randomPojo(DataSourceConfigDO.class);
  68 + dataSourceConfigMapper.insert(dbDataSourceConfig);// @Sql: 先插入出一条存在的数据
  69 + // 准备参数
  70 + DataSourceConfigUpdateReqVO reqVO = randomPojo(DataSourceConfigUpdateReqVO.class, o -> {
  71 + o.setId(dbDataSourceConfig.getId()); // 设置更新的 ID
  72 + });
  73 + // mock 方法
  74 + when(stringEncryptor.encrypt(eq(reqVO.getPassword()))).thenReturn("123456");
  75 + databaseUtilsMock.when(() -> DatabaseUtils.isConnectionOK(eq(reqVO.getUrl()),
  76 + eq(reqVO.getUsername()), eq(reqVO.getPassword()))).thenReturn(true);
  77 +
  78 + // 调用
  79 + dataSourceConfigService.updateDataSourceConfig(reqVO);
  80 + // 校验是否更新正确
  81 + DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(reqVO.getId()); // 获取最新的
  82 + assertPojoEquals(reqVO, dataSourceConfig, "password");
  83 + assertEquals("123456", dataSourceConfig.getPassword());
  84 + }
  85 + }
  86 +
  87 + @Test
  88 + public void testUpdateDataSourceConfig_notExists() {
  89 + // 准备参数
  90 + DataSourceConfigUpdateReqVO reqVO = randomPojo(DataSourceConfigUpdateReqVO.class);
  91 +
  92 + // 调用, 并断言异常
  93 + assertServiceException(() -> dataSourceConfigService.updateDataSourceConfig(reqVO), DATA_SOURCE_CONFIG_NOT_EXISTS);
  94 + }
  95 +
  96 + @Test
  97 + public void testDeleteDataSourceConfig_success() {
  98 + // mock 数据
  99 + DataSourceConfigDO dbDataSourceConfig = randomPojo(DataSourceConfigDO.class);
  100 + dataSourceConfigMapper.insert(dbDataSourceConfig);// @Sql: 先插入出一条存在的数据
  101 + // 准备参数
  102 + Long id = dbDataSourceConfig.getId();
  103 +
  104 + // 调用
  105 + dataSourceConfigService.deleteDataSourceConfig(id);
  106 + // 校验数据不存在了
  107 + assertNull(dataSourceConfigMapper.selectById(id));
  108 + }
  109 +
  110 + @Test
  111 + public void testDeleteDataSourceConfig_notExists() {
  112 + // 准备参数
  113 + Long id = randomLongId();
  114 +
  115 + // 调用, 并断言异常
  116 + assertServiceException(() -> dataSourceConfigService.deleteDataSourceConfig(id), DATA_SOURCE_CONFIG_NOT_EXISTS);
  117 + }
  118 +
  119 +}
yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/clean.sql
@@ -9,3 +9,4 @@ DELETE FROM &quot;infra_file&quot;; @@ -9,3 +9,4 @@ DELETE FROM &quot;infra_file&quot;;
9 DELETE FROM "infra_api_error_log"; 9 DELETE FROM "infra_api_error_log";
10 DELETE FROM "infra_test_demo"; 10 DELETE FROM "infra_test_demo";
11 DELETE FROM "infra_file_config"; 11 DELETE FROM "infra_file_config";
  12 +DELETE FROM "infra_data_source_config";
yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/create_tables.sql
@@ -168,3 +168,17 @@ CREATE TABLE IF NOT EXISTS &quot;infra_test_demo&quot; ( @@ -168,3 +168,17 @@ CREATE TABLE IF NOT EXISTS &quot;infra_test_demo&quot; (
168 "deleted" bit NOT NULL DEFAULT FALSE, 168 "deleted" bit NOT NULL DEFAULT FALSE,
169 PRIMARY KEY ("id") 169 PRIMARY KEY ("id")
170 ) COMMENT '字典类型表'; 170 ) COMMENT '字典类型表';
  171 +
  172 +CREATE TABLE IF NOT EXISTS "infra_data_source_config" (
  173 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  174 + "name" varchar(100) NOT NULL,
  175 + "url" varchar(1024) NOT NULL,
  176 + "username" varchar(255) NOT NULL,
  177 + "password" varchar(255) NOT NULL,
  178 + "creator" varchar(64) DEFAULT '',
  179 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  180 + "updater" varchar(64) DEFAULT '',
  181 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  182 + "deleted" bit NOT NULL DEFAULT FALSE,
  183 + PRIMARY KEY ("id")
  184 +) COMMENT '数据源配置表';
yudao-module-member/yudao-module-member-biz/pom.xml
@@ -77,8 +77,8 @@ @@ -77,8 +77,8 @@
77 <artifactId>yudao-spring-boot-starter-test</artifactId> 77 <artifactId>yudao-spring-boot-starter-test</artifactId>
78 <scope>test</scope> 78 <scope>test</scope>
79 </dependency> 79 </dependency>
80 - <!-- 工具类相关 -->  
81 80
  81 + <!-- 工具类相关 -->
82 </dependencies> 82 </dependencies>
83 83
84 </project> 84 </project>
yudao-server/src/main/resources/application-dev.yaml
@@ -61,6 +61,10 @@ spring: @@ -61,6 +61,10 @@ spring:
61 port: 6379 # 端口 61 port: 6379 # 端口
62 database: 1 # 数据库索引 62 database: 1 # 数据库索引
63 63
  64 +jasypt:
  65 + encryptor:
  66 + password: yuanma # 加解密的秘钥
  67 +
64 --- #################### 定时任务相关配置 #################### 68 --- #################### 定时任务相关配置 ####################
65 69
66 # Quartz 配置项,对应 QuartzProperties 配置类 70 # Quartz 配置项,对应 QuartzProperties 配置类
yudao-server/src/main/resources/application-local.yaml
@@ -61,6 +61,10 @@ spring: @@ -61,6 +61,10 @@ spring:
61 port: 6379 # 端口 61 port: 6379 # 端口
62 database: 0 # 数据库索引 62 database: 0 # 数据库索引
63 63
  64 +jasypt:
  65 + encryptor:
  66 + password: yuanma # 加解密的秘钥
  67 +
64 --- #################### 定时任务相关配置 #################### 68 --- #################### 定时任务相关配置 ####################
65 69
66 # Quartz 配置项,对应 QuartzProperties 配置类 70 # Quartz 配置项,对应 QuartzProperties 配置类
yudao-server/src/main/resources/application.yaml
@@ -123,6 +123,7 @@ yudao: @@ -123,6 +123,7 @@ yudao:
123 - infra_job 123 - infra_job
124 - infra_job_log 124 - infra_job_log
125 - infra_job_log 125 - infra_job_log
  126 + - infra_data_source_config
126 sms-code: # 短信验证码相关的配置项 127 sms-code: # 短信验证码相关的配置项
127 expire-times: 10m 128 expire-times: 10m
128 send-frequency: 1m 129 send-frequency: 1m
yudao-ui-admin/src/api/infra/dataSourceConfig.js 0 → 100755
  1 +import request from '@/utils/request'
  2 +
  3 +// 创建数据源配置
  4 +export function createDataSourceConfig(data) {
  5 + return request({
  6 + url: '/infra/data-source-config/create',
  7 + method: 'post',
  8 + data: data
  9 + })
  10 +}
  11 +
  12 +// 更新数据源配置
  13 +export function updateDataSourceConfig(data) {
  14 + return request({
  15 + url: '/infra/data-source-config/update',
  16 + method: 'put',
  17 + data: data
  18 + })
  19 +}
  20 +
  21 +// 删除数据源配置
  22 +export function deleteDataSourceConfig(id) {
  23 + return request({
  24 + url: '/infra/data-source-config/delete?id=' + id,
  25 + method: 'delete'
  26 + })
  27 +}
  28 +
  29 +// 获得数据源配置
  30 +export function getDataSourceConfig(id) {
  31 + return request({
  32 + url: '/infra/data-source-config/get?id=' + id,
  33 + method: 'get'
  34 + })
  35 +}
  36 +
  37 +// 获得数据源配置列表
  38 +export function getDataSourceConfigList() {
  39 + return request({
  40 + url: '/infra/data-source-config/list',
  41 + method: 'get',
  42 + })
  43 +}
yudao-ui-admin/src/views/infra/dataSourceConfig/index.vue 0 → 100755
  1 +<template>
  2 + <div class="app-container">
  3 + <!-- 操作工具栏 -->
  4 + <el-row :gutter="10" class="mb8">
  5 + <el-col :span="1.5">
  6 + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
  7 + v-hasPermi="['infra:data-source-config:create']">新增</el-button>
  8 + </el-col>
  9 + </el-row>
  10 +
  11 + <!-- 列表 -->
  12 + <el-table v-loading="loading" :data="list">
  13 + <el-table-column label="主键编号" align="center" prop="id" />
  14 + <el-table-column label="参数名称" align="center" prop="name" />
  15 + <el-table-column label="数据源连接" align="center" prop="url" />
  16 + <el-table-column label="用户名" align="center" prop="username" />
  17 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  18 + <template slot-scope="scope">
  19 + <span>{{ parseTime(scope.row.createTime) }}</span>
  20 + </template>
  21 + </el-table-column>
  22 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  23 + <template slot-scope="scope">
  24 + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
  25 + v-hasPermi="['infra:data-source-config:update']">修改</el-button>
  26 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  27 + v-hasPermi="['infra:data-source-config:delete']">删除</el-button>
  28 + </template>
  29 + </el-table-column>
  30 + </el-table>
  31 +
  32 + <!-- 对话框(添加 / 修改) -->
  33 + <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
  34 + <el-form ref="form" :model="form" :rules="rules" label-width="100px">
  35 + <el-form-item label="参数名称" prop="name">
  36 + <el-input v-model="form.name" placeholder="请输入参数名称" />
  37 + </el-form-item>
  38 + <el-form-item label="数据源连接" prop="url">
  39 + <el-input v-model="form.url" placeholder="请输入数据源连接" />
  40 + </el-form-item>
  41 + <el-form-item label="用户名" prop="username">
  42 + <el-input v-model="form.username" placeholder="请输入用户名" />
  43 + </el-form-item>
  44 + <el-form-item label="密码" prop="password">
  45 + <el-input v-model="form.password" placeholder="请输入密码" />
  46 + </el-form-item>
  47 + </el-form>
  48 + <div slot="footer" class="dialog-footer">
  49 + <el-button type="primary" @click="submitForm">确 定</el-button>
  50 + <el-button @click="cancel">取 消</el-button>
  51 + </div>
  52 + </el-dialog>
  53 + </div>
  54 +</template>
  55 +
  56 +<script>
  57 +import { createDataSourceConfig, updateDataSourceConfig, deleteDataSourceConfig, getDataSourceConfig, getDataSourceConfigList } from "@/api/infra/dataSourceConfig";
  58 +
  59 +export default {
  60 + name: "DataSourceConfig",
  61 + components: {
  62 + },
  63 + data() {
  64 + return {
  65 + // 遮罩层
  66 + loading: true,
  67 + // 总条数
  68 + total: 0,
  69 + // 数据源配置列表
  70 + list: [],
  71 + // 弹出层标题
  72 + title: "",
  73 + // 是否显示弹出层
  74 + open: false,
  75 + // 表单参数
  76 + form: {},
  77 + // 表单校验
  78 + rules: {
  79 + name: [{ required: true, message: "参数名称不能为空", trigger: "blur" }],
  80 + url: [{ required: true, message: "数据源连接不能为空", trigger: "blur" }],
  81 + username: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
  82 + password: [{ required: true, message: "密码不能为空", trigger: "blur" }],
  83 + }
  84 + };
  85 + },
  86 + created() {
  87 + this.getList();
  88 + },
  89 + methods: {
  90 + /** 查询列表 */
  91 + getList() {
  92 + this.loading = true;
  93 + // 执行查询
  94 + getDataSourceConfigList().then(response => {
  95 + this.list = response.data;
  96 + this.loading = false;
  97 + });
  98 + },
  99 + /** 取消按钮 */
  100 + cancel() {
  101 + this.open = false;
  102 + this.reset();
  103 + },
  104 + /** 表单重置 */
  105 + reset() {
  106 + this.form = {
  107 + id: undefined,
  108 + name: undefined,
  109 + url: undefined,
  110 + username: undefined,
  111 + password: undefined,
  112 + };
  113 + this.resetForm("form");
  114 + },
  115 + /** 新增按钮操作 */
  116 + handleAdd() {
  117 + this.reset();
  118 + this.open = true;
  119 + this.title = "添加数据源配置";
  120 + },
  121 + /** 修改按钮操作 */
  122 + handleUpdate(row) {
  123 + this.reset();
  124 + const id = row.id;
  125 + getDataSourceConfig(id).then(response => {
  126 + this.form = response.data;
  127 + this.open = true;
  128 + this.title = "修改数据源配置";
  129 + });
  130 + },
  131 + /** 提交按钮 */
  132 + submitForm() {
  133 + this.$refs["form"].validate(valid => {
  134 + if (!valid) {
  135 + return;
  136 + }
  137 + // 修改的提交
  138 + if (this.form.id != null) {
  139 + updateDataSourceConfig(this.form).then(response => {
  140 + this.$modal.msgSuccess("修改成功");
  141 + this.open = false;
  142 + this.getList();
  143 + });
  144 + return;
  145 + }
  146 + // 添加的提交
  147 + createDataSourceConfig(this.form).then(response => {
  148 + this.$modal.msgSuccess("新增成功");
  149 + this.open = false;
  150 + this.getList();
  151 + });
  152 + });
  153 + },
  154 + /** 删除按钮操作 */
  155 + handleDelete(row) {
  156 + const id = row.id;
  157 + this.$modal.confirm('是否确认删除数据源配置编号为"' + id + '"的数据项?').then(function() {
  158 + return deleteDataSourceConfig(id);
  159 + }).then(() => {
  160 + this.getList();
  161 + this.$modal.msgSuccess("删除成功");
  162 + }).catch(() => {});
  163 + }
  164 + }
  165 +};
  166 +</script>