diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 1c871cf32..14a4b1dfc 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -45,6 +45,7 @@ <activiti.version>7.1.0.M6</activiti.version> <flowable.version>6.7.0</flowable.version> <!-- 工具类相关 --> + <jasypt-spring-boot-starter.version>3.0.4</jasypt-spring-boot-starter.version> <lombok.version>1.18.20</lombok.version> <mapstruct.version>1.4.1.Final</mapstruct.version> <hutool.version>5.6.1</hutool.version> @@ -428,6 +429,12 @@ <version>${revision}</version> </dependency> + <dependency> + <groupId>com.github.ulisesbocchio</groupId> + <artifactId>jasypt-spring-boot-starter</artifactId> <!-- 加解密 --> + <version>${jasypt-spring-boot-starter.version}</version> + </dependency> + <dependency> <groupId>cn.iocoder.boot</groupId> <artifactId>yudao-spring-boot-starter-excel</artifactId> diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/DatabaseUtils.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/DatabaseUtils.java new file mode 100644 index 000000000..b282656c5 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/DatabaseUtils.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.framework.mybatis.core.util; + +import java.sql.Connection; +import java.sql.DriverManager; + +/** + * 数据库工具类 + * + * @author 芋道源码 + */ +public class DatabaseUtils { + + /** + * 判断连接是否正确 + * + * @param url 数据源连接 + * @param username 账号 + * @param password 密码 + * @return 是否正确 + */ + public static boolean isConnectionOK(String url, String username, String password) { + try (Connection ignored = DriverManager.getConnection(url, username, password)) { + return true; + } catch (Exception ex) { + return false; + } + } + +} diff --git a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java b/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java index 1528955f9..8dd1a991f 100644 --- a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java +++ b/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 { ErrorCode FILE_CONFIG_NOT_EXISTS = new ErrorCode(1001006000, "文件配置不存在"); ErrorCode FILE_CONFIG_DELETE_FAIL_MASTER = new ErrorCode(1001006001, "该文件配置不允许删除,原因:它是主配置,删除会导致无法上传文件"); + // ========== 数据源配置 1001007000 ========== + ErrorCode DATA_SOURCE_CONFIG_NOT_EXISTS = new ErrorCode(1001007000, "数据源配置不存在"); + ErrorCode DATA_SOURCE_CONFIG_NOT_OK = new ErrorCode(1001007001, "数据源配置不正确,无法进行连接"); + } diff --git a/yudao-module-infra/yudao-module-infra-biz/pom.xml b/yudao-module-infra/yudao-module-infra-biz/pom.xml index 5c4c16d43..cd30fd21e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/pom.xml +++ b/yudao-module-infra/yudao-module-infra-biz/pom.xml @@ -79,6 +79,11 @@ </dependency> <!-- 工具类相关 --> + <dependency> + <groupId>com.github.ulisesbocchio</groupId> + <artifactId>jasypt-spring-boot-starter</artifactId> <!-- 加解密 --> + </dependency> + <dependency> <groupId>cn.iocoder.boot</groupId> <artifactId>yudao-spring-boot-starter-excel</artifactId> diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/DataSourceConfigController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/DataSourceConfigController.java new file mode 100755 index 000000000..6b535ae98 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/DataSourceConfigController.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.infra.controller.admin.db; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigCreateReqVO; +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigRespVO; +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigUpdateReqVO; +import cn.iocoder.yudao.module.infra.convert.db.DataSourceConfigConvert; +import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO; +import cn.iocoder.yudao.module.infra.service.db.DataSourceConfigService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Api(tags = "管理后台 - 数据源配置") +@RestController +@RequestMapping("/infra/data-source-config") +@Validated +public class DataSourceConfigController { + + @Resource + private DataSourceConfigService dataSourceConfigService; + + @PostMapping("/create") + @ApiOperation("创建数据源配置") + @PreAuthorize("@ss.hasPermission('infra:data-source-config:create')") + public CommonResult<Long> createDataSourceConfig(@Valid @RequestBody DataSourceConfigCreateReqVO createReqVO) { + return success(dataSourceConfigService.createDataSourceConfig(createReqVO)); + } + + @PutMapping("/update") + @ApiOperation("更新数据源配置") + @PreAuthorize("@ss.hasPermission('infra:data-source-config:update')") + public CommonResult<Boolean> updateDataSourceConfig(@Valid @RequestBody DataSourceConfigUpdateReqVO updateReqVO) { + dataSourceConfigService.updateDataSourceConfig(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @ApiOperation("删除数据源配置") + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('infra:data-source-config:delete')") + public CommonResult<Boolean> deleteDataSourceConfig(@RequestParam("id") Long id) { + dataSourceConfigService.deleteDataSourceConfig(id); + return success(true); + } + + @GetMapping("/get") + @ApiOperation("获得数据源配置") + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) + @PreAuthorize("@ss.hasPermission('infra:data-source-config:query')") + public CommonResult<DataSourceConfigRespVO> getDataSourceConfig(@RequestParam("id") Long id) { + DataSourceConfigDO dataSourceConfig = dataSourceConfigService.getDataSourceConfig(id); + return success(DataSourceConfigConvert.INSTANCE.convert(dataSourceConfig)); + } + + @GetMapping("/list") + @ApiOperation("获得数据源配置列表") + @PreAuthorize("@ss.hasPermission('infra:data-source-config:query')") + public CommonResult<List<DataSourceConfigRespVO>> getDataSourceConfigList() { + List<DataSourceConfigDO> list = dataSourceConfigService.getDataSourceConfigList(); + return success(DataSourceConfigConvert.INSTANCE.convertList(list)); + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/doc/DbDocController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/DatabaseDocController.java similarity index 98% rename from yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/doc/DbDocController.java rename to yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/DatabaseDocController.java index 83d9db798..eb35d4555 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/doc/DbDocController.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/DatabaseDocController.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.infra.controller.admin.doc; +package cn.iocoder.yudao.module.infra.controller.admin.db; import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.IdUtil; @@ -30,7 +30,7 @@ import java.util.Arrays; @Api(tags = "管理后台 - 数据库文档") @RestController @RequestMapping("/infra/db-doc") -public class DbDocController { +public class DatabaseDocController { @Resource private DynamicDataSourceProperties dynamicDataSourceProperties; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigBaseVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigBaseVO.java new file mode 100755 index 000000000..ae3d97dce --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigBaseVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.infra.controller.admin.db.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; +import javax.validation.constraints.*; + +/** +* 数据源配置 Base VO,提供给添加、修改、详细的子 VO 使用 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 +*/ +@Data +public class DataSourceConfigBaseVO { + + @ApiModelProperty(value = "参数名称", required = true, example = "test") + @NotNull(message = "参数名称不能为空") + private String name; + + @ApiModelProperty(value = "数据源连接", required = true, example = "jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro") + @NotNull(message = "数据源连接不能为空") + private String url; + + @ApiModelProperty(value = "用户名", required = true, example = "root") + @NotNull(message = "用户名不能为空") + private String username; + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigCreateReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigCreateReqVO.java new file mode 100755 index 000000000..13085f9b1 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigCreateReqVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.infra.controller.admin.db.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; +import javax.validation.constraints.*; + +@ApiModel("管理后台 - 数据源配置创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DataSourceConfigCreateReqVO extends DataSourceConfigBaseVO { + + @ApiModelProperty(value = "密码", required = true, example = "123456") + @NotNull(message = "密码不能为空") + private String password; + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigRespVO.java new file mode 100755 index 000000000..af63aa0dd --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.infra.controller.admin.db.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; + +@ApiModel("管理后台 - 数据源配置 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DataSourceConfigRespVO extends DataSourceConfigBaseVO { + + @ApiModelProperty(value = "主键编号", required = true, example = "1024") + private Integer id; + + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigUpdateReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigUpdateReqVO.java new file mode 100755 index 000000000..134f3e9df --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigUpdateReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.infra.controller.admin.db.vo; + +import lombok.*; +import java.util.*; +import io.swagger.annotations.*; +import javax.validation.constraints.*; + +@ApiModel("管理后台 - 数据源配置更新 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DataSourceConfigUpdateReqVO extends DataSourceConfigBaseVO { + + @ApiModelProperty(value = "主键编号", required = true, example = "1024") + @NotNull(message = "主键编号不能为空") + private Long id; + + @ApiModelProperty(value = "密码", required = true, example = "123456") + @NotNull(message = "密码不能为空") + private String password; + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/db/DataSourceConfigConvert.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/db/DataSourceConfigConvert.java new file mode 100755 index 000000000..d81d85cee --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/db/DataSourceConfigConvert.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.infra.convert.db; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO; + +/** + * 数据源配置 Convert + * + * @author 芋道源码 + */ +@Mapper +public interface DataSourceConfigConvert { + + DataSourceConfigConvert INSTANCE = Mappers.getMapper(DataSourceConfigConvert.class); + + DataSourceConfigDO convert(DataSourceConfigCreateReqVO bean); + + DataSourceConfigDO convert(DataSourceConfigUpdateReqVO bean); + + DataSourceConfigRespVO convert(DataSourceConfigDO bean); + + List<DataSourceConfigRespVO> convertList(List<DataSourceConfigDO> list); + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/db/DataSourceConfigDO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/db/DataSourceConfigDO.java new file mode 100644 index 000000000..6508d1d18 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/db/DataSourceConfigDO.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.db; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 数据源配置 + * + * @author 芋道源码 + */ +@TableName("infra_data_source_config") +@Data +public class DataSourceConfigDO extends BaseDO { + + /** + * 主键编号 + */ + private Long id; + /** + * 连接名 + */ + private String name; + + /** + * 数据源连接 + */ + private String url; + /** + * 用户名 + */ + private String username; + /** + * 密码 + */ + private String password; + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/db/DataSourceConfigMapper.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/db/DataSourceConfigMapper.java new file mode 100755 index 000000000..5ad8be428 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/db/DataSourceConfigMapper.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.db; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 数据源配置 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface DataSourceConfigMapper extends BaseMapperX<DataSourceConfigDO> { +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigService.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigService.java new file mode 100755 index 000000000..b5e9e21ee --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigService.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.infra.service.db; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigCreateReqVO; +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigUpdateReqVO; +import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO; +import org.w3c.dom.stylesheets.LinkStyle; + +import javax.validation.Valid; +import java.util.List; + +/** + * 数据源配置 Service 接口 + * + * @author 芋道源码 + */ +public interface DataSourceConfigService { + + /** + * 创建数据源配置 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createDataSourceConfig(@Valid DataSourceConfigCreateReqVO createReqVO); + + /** + * 更新数据源配置 + * + * @param updateReqVO 更新信息 + */ + void updateDataSourceConfig(@Valid DataSourceConfigUpdateReqVO updateReqVO); + + /** + * 删除数据源配置 + * + * @param id 编号 + */ + void deleteDataSourceConfig(Long id); + + /** + * 获得数据源配置 + * + * @param id 编号 + * @return 数据源配置 + */ + DataSourceConfigDO getDataSourceConfig(Long id); + + /** + * 获得数据源配置列表 + * + * @return 数据源配置列表 + */ + List<DataSourceConfigDO> getDataSourceConfigList(); + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImpl.java new file mode 100755 index 000000000..e2f1a6559 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImpl.java @@ -0,0 +1,97 @@ +package cn.iocoder.yudao.module.infra.service.db; + +import cn.hutool.db.DbUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.util.DatabaseUtils; +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigCreateReqVO; +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigUpdateReqVO; +import cn.iocoder.yudao.module.infra.convert.db.DataSourceConfigConvert; +import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO; +import cn.iocoder.yudao.module.infra.dal.mysql.db.DataSourceConfigMapper; +import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils; +import org.jasypt.encryption.StringEncryptor; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; + +import java.sql.Connection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +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 StringEncryptor stringEncryptor; + + @Override + public Long createDataSourceConfig(DataSourceConfigCreateReqVO createReqVO) { + DataSourceConfigDO dataSourceConfig = DataSourceConfigConvert.INSTANCE.convert(createReqVO); + checkConnectionOK(dataSourceConfig); + + // 插入 + dataSourceConfig.setPassword(stringEncryptor.encrypt(createReqVO.getPassword())); + dataSourceConfigMapper.insert(dataSourceConfig); + // 返回 + return dataSourceConfig.getId(); + } + + @Override + public void updateDataSourceConfig(DataSourceConfigUpdateReqVO updateReqVO) { + // 校验存在 + validateDataSourceConfigExists(updateReqVO.getId()); + DataSourceConfigDO updateObj = DataSourceConfigConvert.INSTANCE.convert(updateReqVO); + checkConnectionOK(updateObj); + + // 更新 + updateObj.setPassword(stringEncryptor.encrypt(updateObj.getPassword())); + dataSourceConfigMapper.updateById(updateObj); + } + + @Override + public void deleteDataSourceConfig(Long id) { + // 校验存在 + validateDataSourceConfigExists(id); + // 删除 + dataSourceConfigMapper.deleteById(id); + } + + private void validateDataSourceConfigExists(Long id) { + if (dataSourceConfigMapper.selectById(id) == null) { + throw exception(DATA_SOURCE_CONFIG_NOT_EXISTS); + } + } + + @Override + public DataSourceConfigDO getDataSourceConfig(Long id) { + DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(id); + dataSourceConfig.setPassword(stringEncryptor.decrypt(dataSourceConfig.getPassword())); + return dataSourceConfig; + } + + @Override + public List<DataSourceConfigDO> getDataSourceConfigList() { + return dataSourceConfigMapper.selectList(); + } + + private void checkConnectionOK(DataSourceConfigDO config) { + boolean success = DatabaseUtils.isConnectionOK(config.getUrl(), config.getUsername(), config.getPassword()); + if (!success) { + throw exception(DATA_SOURCE_CONFIG_NOT_OK); + } + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImplTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImplTest.java new file mode 100755 index 000000000..5c51b198e --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImplTest.java @@ -0,0 +1,119 @@ +package cn.iocoder.yudao.module.infra.service.db; + +import cn.iocoder.yudao.framework.mybatis.core.util.DatabaseUtils; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigCreateReqVO; +import cn.iocoder.yudao.module.infra.controller.admin.db.vo.DataSourceConfigUpdateReqVO; +import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO; +import cn.iocoder.yudao.module.infra.dal.mysql.db.DataSourceConfigMapper; +import org.jasypt.encryption.StringEncryptor; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import javax.annotation.Resource; + +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * {@link DataSourceConfigServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(DataSourceConfigServiceImpl.class) +public class DataSourceConfigServiceImplTest extends BaseDbUnitTest { + + @Resource + private DataSourceConfigServiceImpl dataSourceConfigService; + + @Resource + private DataSourceConfigMapper dataSourceConfigMapper; + + @MockBean + private StringEncryptor stringEncryptor; + + @Test + public void testCreateDataSourceConfig_success() { + try (MockedStatic<DatabaseUtils> databaseUtilsMock = mockStatic(DatabaseUtils.class)) { + // 准备参数 + DataSourceConfigCreateReqVO reqVO = randomPojo(DataSourceConfigCreateReqVO.class); + // mock 方法 + when(stringEncryptor.encrypt(eq(reqVO.getPassword()))).thenReturn("123456"); + databaseUtilsMock.when(() -> DatabaseUtils.isConnectionOK(eq(reqVO.getUrl()), + eq(reqVO.getUsername()), eq(reqVO.getPassword()))).thenReturn(true); + + // 调用 + Long dataSourceConfigId = dataSourceConfigService.createDataSourceConfig(reqVO); + // 断言 + assertNotNull(dataSourceConfigId); + // 校验记录的属性是否正确 + DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(dataSourceConfigId); + assertPojoEquals(reqVO, dataSourceConfig, "password"); + assertEquals("123456", dataSourceConfig.getPassword()); + } + } + + @Test + public void testUpdateDataSourceConfig_success() { + try (MockedStatic<DatabaseUtils> databaseUtilsMock = mockStatic(DatabaseUtils.class)) { + // mock 数据 + DataSourceConfigDO dbDataSourceConfig = randomPojo(DataSourceConfigDO.class); + dataSourceConfigMapper.insert(dbDataSourceConfig);// @Sql: 先插入出一条存在的数据 + // 准备参数 + DataSourceConfigUpdateReqVO reqVO = randomPojo(DataSourceConfigUpdateReqVO.class, o -> { + o.setId(dbDataSourceConfig.getId()); // 设置更新的 ID + }); + // mock 方法 + when(stringEncryptor.encrypt(eq(reqVO.getPassword()))).thenReturn("123456"); + databaseUtilsMock.when(() -> DatabaseUtils.isConnectionOK(eq(reqVO.getUrl()), + eq(reqVO.getUsername()), eq(reqVO.getPassword()))).thenReturn(true); + + // 调用 + dataSourceConfigService.updateDataSourceConfig(reqVO); + // 校验是否更新正确 + DataSourceConfigDO dataSourceConfig = dataSourceConfigMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, dataSourceConfig, "password"); + assertEquals("123456", dataSourceConfig.getPassword()); + } + } + + @Test + public void testUpdateDataSourceConfig_notExists() { + // 准备参数 + DataSourceConfigUpdateReqVO reqVO = randomPojo(DataSourceConfigUpdateReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> dataSourceConfigService.updateDataSourceConfig(reqVO), DATA_SOURCE_CONFIG_NOT_EXISTS); + } + + @Test + public void testDeleteDataSourceConfig_success() { + // mock 数据 + DataSourceConfigDO dbDataSourceConfig = randomPojo(DataSourceConfigDO.class); + dataSourceConfigMapper.insert(dbDataSourceConfig);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbDataSourceConfig.getId(); + + // 调用 + dataSourceConfigService.deleteDataSourceConfig(id); + // 校验数据不存在了 + assertNull(dataSourceConfigMapper.selectById(id)); + } + + @Test + public void testDeleteDataSourceConfig_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> dataSourceConfigService.deleteDataSourceConfig(id), DATA_SOURCE_CONFIG_NOT_EXISTS); + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/clean.sql b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/clean.sql index cc8316837..eb05d3d5a 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/clean.sql +++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/clean.sql @@ -9,3 +9,4 @@ DELETE FROM "infra_file"; DELETE FROM "infra_api_error_log"; DELETE FROM "infra_test_demo"; DELETE FROM "infra_file_config"; +DELETE FROM "infra_data_source_config"; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/create_tables.sql b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/create_tables.sql index 3c86102e6..53d8594fe 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/create_tables.sql +++ b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/create_tables.sql @@ -168,3 +168,17 @@ CREATE TABLE IF NOT EXISTS "infra_test_demo" ( "deleted" bit NOT NULL DEFAULT FALSE, PRIMARY KEY ("id") ) COMMENT '字典类型表'; + +CREATE TABLE IF NOT EXISTS "infra_data_source_config" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar(100) NOT NULL, + "url" varchar(1024) NOT NULL, + "username" varchar(255) NOT NULL, + "password" varchar(255) NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '数据源配置表'; diff --git a/yudao-module-member/yudao-module-member-biz/pom.xml b/yudao-module-member/yudao-module-member-biz/pom.xml index f1b48a819..11780b8a0 100644 --- a/yudao-module-member/yudao-module-member-biz/pom.xml +++ b/yudao-module-member/yudao-module-member-biz/pom.xml @@ -77,8 +77,8 @@ <artifactId>yudao-spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> - <!-- 工具类相关 --> + <!-- 工具类相关 --> </dependencies> </project> diff --git a/yudao-server/src/main/resources/application-dev.yaml b/yudao-server/src/main/resources/application-dev.yaml index 256c5beb8..0c11d2f1b 100644 --- a/yudao-server/src/main/resources/application-dev.yaml +++ b/yudao-server/src/main/resources/application-dev.yaml @@ -61,6 +61,10 @@ spring: port: 6379 # 端口 database: 1 # 数据库索引 +jasypt: + encryptor: + password: yuanma # 加解密的秘钥 + --- #################### 定时任务相关配置 #################### # Quartz 配置项,对应 QuartzProperties 配置类 diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index 0d5f777d0..d8ce9deee 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -61,6 +61,10 @@ spring: port: 6379 # 端口 database: 0 # 数据库索引 +jasypt: + encryptor: + password: yuanma # 加解密的秘钥 + --- #################### 定时任务相关配置 #################### # Quartz 配置项,对应 QuartzProperties 配置类 diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index 76d5e0454..247af4d03 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -123,6 +123,7 @@ yudao: - infra_job - infra_job_log - infra_job_log + - infra_data_source_config sms-code: # 短信验证码相关的配置项 expire-times: 10m send-frequency: 1m diff --git a/yudao-ui-admin/src/api/infra/dataSourceConfig.js b/yudao-ui-admin/src/api/infra/dataSourceConfig.js new file mode 100755 index 000000000..2d96b49cc --- /dev/null +++ b/yudao-ui-admin/src/api/infra/dataSourceConfig.js @@ -0,0 +1,43 @@ +import request from '@/utils/request' + +// 创建数据源配置 +export function createDataSourceConfig(data) { + return request({ + url: '/infra/data-source-config/create', + method: 'post', + data: data + }) +} + +// 更新数据源配置 +export function updateDataSourceConfig(data) { + return request({ + url: '/infra/data-source-config/update', + method: 'put', + data: data + }) +} + +// 删除数据源配置 +export function deleteDataSourceConfig(id) { + return request({ + url: '/infra/data-source-config/delete?id=' + id, + method: 'delete' + }) +} + +// 获得数据源配置 +export function getDataSourceConfig(id) { + return request({ + url: '/infra/data-source-config/get?id=' + id, + method: 'get' + }) +} + +// 获得数据源配置列表 +export function getDataSourceConfigList() { + return request({ + url: '/infra/data-source-config/list', + method: 'get', + }) +} diff --git a/yudao-ui-admin/src/views/infra/dataSourceConfig/index.vue b/yudao-ui-admin/src/views/infra/dataSourceConfig/index.vue new file mode 100755 index 000000000..ddd2800a2 --- /dev/null +++ b/yudao-ui-admin/src/views/infra/dataSourceConfig/index.vue @@ -0,0 +1,166 @@ +<template> + <div class="app-container"> + <!-- 操作工具栏 --> + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" + v-hasPermi="['infra:data-source-config:create']">新增</el-button> + </el-col> + </el-row> + + <!-- 列表 --> + <el-table v-loading="loading" :data="list"> + <el-table-column label="主键编号" align="center" prop="id" /> + <el-table-column label="参数名称" align="center" prop="name" /> + <el-table-column label="数据源连接" align="center" prop="url" /> + <el-table-column label="用户名" align="center" prop="username" /> + <el-table-column label="创建时间" align="center" prop="createTime" width="180"> + <template slot-scope="scope"> + <span>{{ parseTime(scope.row.createTime) }}</span> + </template> + </el-table-column> + <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> + <template slot-scope="scope"> + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" + v-hasPermi="['infra:data-source-config:update']">修改</el-button> + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" + v-hasPermi="['infra:data-source-config:delete']">删除</el-button> + </template> + </el-table-column> + </el-table> + + <!-- 对话框(添加 / 修改) --> + <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> + <el-form ref="form" :model="form" :rules="rules" label-width="100px"> + <el-form-item label="参数名称" prop="name"> + <el-input v-model="form.name" placeholder="请输入参数名称" /> + </el-form-item> + <el-form-item label="数据源连接" prop="url"> + <el-input v-model="form.url" placeholder="请输入数据源连接" /> + </el-form-item> + <el-form-item label="用户名" prop="username"> + <el-input v-model="form.username" placeholder="请输入用户名" /> + </el-form-item> + <el-form-item label="密码" prop="password"> + <el-input v-model="form.password" placeholder="请输入密码" /> + </el-form-item> + </el-form> + <div slot="footer" class="dialog-footer"> + <el-button type="primary" @click="submitForm">确 定</el-button> + <el-button @click="cancel">取 消</el-button> + </div> + </el-dialog> + </div> +</template> + +<script> +import { createDataSourceConfig, updateDataSourceConfig, deleteDataSourceConfig, getDataSourceConfig, getDataSourceConfigList } from "@/api/infra/dataSourceConfig"; + +export default { + name: "DataSourceConfig", + components: { + }, + data() { + return { + // 遮罩层 + loading: true, + // 总条数 + total: 0, + // 数据源配置列表 + list: [], + // 弹出层标题 + title: "", + // 是否显示弹出层 + open: false, + // 表单参数 + form: {}, + // 表单校验 + rules: { + name: [{ required: true, message: "参数名称不能为空", trigger: "blur" }], + url: [{ required: true, message: "数据源连接不能为空", trigger: "blur" }], + username: [{ required: true, message: "用户名不能为空", trigger: "blur" }], + password: [{ required: true, message: "密码不能为空", trigger: "blur" }], + } + }; + }, + created() { + this.getList(); + }, + methods: { + /** 查询列表 */ + getList() { + this.loading = true; + // 执行查询 + getDataSourceConfigList().then(response => { + this.list = response.data; + this.loading = false; + }); + }, + /** 取消按钮 */ + cancel() { + this.open = false; + this.reset(); + }, + /** 表单重置 */ + reset() { + this.form = { + id: undefined, + name: undefined, + url: undefined, + username: undefined, + password: undefined, + }; + this.resetForm("form"); + }, + /** 新增按钮操作 */ + handleAdd() { + this.reset(); + this.open = true; + this.title = "添加数据源配置"; + }, + /** 修改按钮操作 */ + handleUpdate(row) { + this.reset(); + const id = row.id; + getDataSourceConfig(id).then(response => { + this.form = response.data; + this.open = true; + this.title = "修改数据源配置"; + }); + }, + /** 提交按钮 */ + submitForm() { + this.$refs["form"].validate(valid => { + if (!valid) { + return; + } + // 修改的提交 + if (this.form.id != null) { + updateDataSourceConfig(this.form).then(response => { + this.$modal.msgSuccess("修改成功"); + this.open = false; + this.getList(); + }); + return; + } + // 添加的提交 + createDataSourceConfig(this.form).then(response => { + this.$modal.msgSuccess("新增成功"); + this.open = false; + this.getList(); + }); + }); + }, + /** 删除按钮操作 */ + handleDelete(row) { + const id = row.id; + this.$modal.confirm('是否确认删除数据源配置编号为"' + id + '"的数据项?').then(function() { + return deleteDataSourceConfig(id); + }).then(() => { + this.getList(); + this.$modal.msgSuccess("删除成功"); + }).catch(() => {}); + } + } +}; +</script>