From b5f708114be076b1cba1fcfa2df78e21200abe8e Mon Sep 17 00:00:00 2001 From: niudehua <657563945@qq.com> Date: Sat, 6 Mar 2021 22:47:00 +0800 Subject: [PATCH 01/17] =?UTF-8?q?knife4j=20=E7=89=88=E6=9C=AC=20=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=202.x=20=E8=A7=A3=E5=86=B3=20swagger=20=E5=86=B2?= =?UTF-8?q?=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../swagger/config/SwaggerAutoConfiguration.java | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index bab83344ee..5ee4b2191d 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ 2.4.2 - 3.0.2 + 2.0.8 5.1.46 1.2.4 diff --git a/src/main/java/cn/iocoder/dashboard/framework/swagger/config/SwaggerAutoConfiguration.java b/src/main/java/cn/iocoder/dashboard/framework/swagger/config/SwaggerAutoConfiguration.java index b550511aff..d544c4a2a2 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/swagger/config/SwaggerAutoConfiguration.java +++ b/src/main/java/cn/iocoder/dashboard/framework/swagger/config/SwaggerAutoConfiguration.java @@ -10,15 +10,17 @@ import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpHeaders; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; -import springfox.documentation.service.*; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.ApiKey; +import springfox.documentation.service.AuthorizationScope; +import springfox.documentation.service.Contact; +import springfox.documentation.service.SecurityReference; +import springfox.documentation.service.SecurityScheme; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; -import springfox.documentation.swagger2.annotations.EnableSwagger2; -import springfox.documentation.service.ApiKey; +import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -30,7 +32,7 @@ import static springfox.documentation.builders.RequestHandlerSelectors.basePacka * @author 芋道源码 */ @Configuration -@EnableSwagger2 +@EnableSwagger2WebMvc @EnableKnife4j @ConditionalOnClass({Docket.class, ApiInfoBuilder.class}) @ConditionalOnProperty(prefix = "yudao.swagger", value = "enable", matchIfMissing = true) From cafb72b5829f5dbbda8dc18a71da622ff8fbcc97 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 7 Mar 2021 11:45:16 +0800 Subject: [PATCH 02/17] =?UTF-8?q?README=20=E5=A2=9E=E5=8A=A0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95=E7=9A=84=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4597c708f0..0bc06b7ed4 100644 --- a/README.md +++ b/README.md @@ -43,11 +43,12 @@ 1. 幂等组件:基于 Redis 实现幂等组件,解决重复请求问题 1. 服务保障:基于 Resilience4j 实现服务的稳定性,包括限流、熔断等功能 1. 日志服务:轻量级日志中心,查看远程服务器的日志 +1. 单元测试:基于 JUnit + Mockito 实现单元测试,保证功能的正确性、代码的质量等 ### 研发工具 1. 表单构建:拖动表单元素生成相应的 HTML 代码 -1. 代码生成:前后端代码的生成(Java、Vue、SQL),支持 CRUD 下载 +1. 代码生成:前后端代码的生成(Java、Vue、SQL、单元测试),支持 CRUD 下载 1. 系统接口:基于 Swagger 自动生成相关的 RESTful API 接口文档 1. 数据库文档:基于 Screw 自动生成数据库文档 @@ -83,7 +84,9 @@ | [Spring Boot Admin](https://github.com/skywalking) | Spring Boot 监控平台 | 8.6.0 | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao) | | [Jackson](https://github.com/FasterXML/jackson) | JSON 工具库 | 2.11.4 | | | [MapStruct](https://mapstruct.org/) | Java Bean 转换 | 1.4.1 | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) | -| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码| 1.16.14 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) | +| [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.16.14 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) | +| [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.7.0 | - | +| [Mockito](https://junit.org/junit5/) | Java Mock 框架 | 3.6.28 | - | **前端** @@ -125,7 +128,7 @@ - + - From 4a23698876bfe47e75502a6ac4dae7e23f902b9b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 7 Mar 2021 20:21:03 +0800 Subject: [PATCH 03/17] =?UTF-8?q?=E5=AE=8C=E5=96=84=20WebConfiguration=20?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=EF=BC=8C=E5=B8=AE=E5=8A=A9=E9=98=85=E8=AF=BB?= =?UTF-8?q?=E7=9A=84=E4=BA=BA=E7=90=86=E8=A7=A3=20/api=20=E5=89=8D?= =?UTF-8?q?=E7=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../《芋道 Spring Boot 安全框架 Spring Security 入门》.md | 1 - .../《芋道 Spring Boot 服务容错 Resilience4j 入门》.md | 1 + .../dashboard/framework/web/config/WebConfiguration.java | 3 ++- 4 files changed, 4 insertions(+), 3 deletions(-) delete mode 100644 src/main/java/cn/iocoder/dashboard/framework/resilience4j/《芋道 Spring Boot 安全框架 Spring Security 入门》.md create mode 100644 src/main/java/cn/iocoder/dashboard/framework/resilience4j/《芋道 Spring Boot 服务容错 Resilience4j 入门》.md diff --git a/README.md b/README.md index 0bc06b7ed4..6af0a5447f 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ | [MapStruct](https://mapstruct.org/) | Java Bean 转换 | 1.4.1 | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao) | | [Lombok](https://projectlombok.org/) | 消除冗长的 Java 代码 | 1.16.14 | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao) | | [JUnit](https://junit.org/junit5/) | Java 单元测试框架 | 5.7.0 | - | -| [Mockito](https://junit.org/junit5/) | Java Mock 框架 | 3.6.28 | - | +| [Mockito](https://github.com/mockito/mockito) | Java Mock 框架 | 3.6.28 | - | **前端** diff --git a/src/main/java/cn/iocoder/dashboard/framework/resilience4j/《芋道 Spring Boot 安全框架 Spring Security 入门》.md b/src/main/java/cn/iocoder/dashboard/framework/resilience4j/《芋道 Spring Boot 安全框架 Spring Security 入门》.md deleted file mode 100644 index 3fa673793a..0000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/resilience4j/《芋道 Spring Boot 安全框架 Spring Security 入门》.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/main/java/cn/iocoder/dashboard/framework/resilience4j/《芋道 Spring Boot 服务容错 Resilience4j 入门》.md b/src/main/java/cn/iocoder/dashboard/framework/resilience4j/《芋道 Spring Boot 服务容错 Resilience4j 入门》.md new file mode 100644 index 0000000000..8d6d0335b1 --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/framework/resilience4j/《芋道 Spring Boot 服务容错 Resilience4j 入门》.md @@ -0,0 +1 @@ + diff --git a/src/main/java/cn/iocoder/dashboard/framework/web/config/WebConfiguration.java b/src/main/java/cn/iocoder/dashboard/framework/web/config/WebConfiguration.java index 9fbe6237ce..b87c490086 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/web/config/WebConfiguration.java +++ b/src/main/java/cn/iocoder/dashboard/framework/web/config/WebConfiguration.java @@ -27,9 +27,10 @@ public class WebConfiguration implements WebMvcConfigurer { @Override public void configurePathMatch(PathMatchConfigurer configurer) { + // 设置 API 前缀,仅仅匹配 controller 包下的 configurer.addPathPrefix(webProperties.getApiPrefix(), clazz -> clazz.isAnnotationPresent(RestController.class) - && clazz.getPackage().getName().startsWith(webProperties.getControllerPackage())); + && clazz.getPackage().getName().startsWith(webProperties.getControllerPackage())); // 仅仅匹配 controller 包 } // ========== Filter 相关 ========== From 5351917b67177f3cfc338f137e3702d0166b55e6 Mon Sep 17 00:00:00 2001 From: niudehua <657563945@qq.com> Date: Sun, 7 Mar 2021 21:06:39 +0800 Subject: [PATCH 04/17] =?UTF-8?q?=E5=BC=95=E5=85=A5=20swagger-annotations?= =?UTF-8?q?=201.5.22=20=E6=8E=92=E9=99=A4knife4j3.0.2=20=E4=B8=AD=20swagge?= =?UTF-8?q?r-annotations=20=E8=A7=A3=E5=86=B3=E4=BE=9D=E8=B5=96=E5=86=B2?= =?UTF-8?q?=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 14 ++++++++++++-- .../swagger/config/SwaggerAutoConfiguration.java | 4 ++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 5ee4b2191d..a601b907d6 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ - 4.0.0 + 4.0.0 cn.iocoder dashboard @@ -24,7 +24,8 @@ 2.4.2 - 2.0.8 + 3.0.2 + 1.5.22 5.1.46 1.2.4 @@ -104,8 +105,17 @@ guava com.google.guava + + swagger-annotations + io.swagger + + + io.swagger + swagger-annotations + ${swagger-annotations.version} + diff --git a/src/main/java/cn/iocoder/dashboard/framework/swagger/config/SwaggerAutoConfiguration.java b/src/main/java/cn/iocoder/dashboard/framework/swagger/config/SwaggerAutoConfiguration.java index d544c4a2a2..21511c37c4 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/swagger/config/SwaggerAutoConfiguration.java +++ b/src/main/java/cn/iocoder/dashboard/framework/swagger/config/SwaggerAutoConfiguration.java @@ -19,7 +19,7 @@ import springfox.documentation.service.SecurityScheme; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; -import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc; +import springfox.documentation.swagger2.annotations.EnableSwagger2; import java.util.Collections; import java.util.List; @@ -32,7 +32,7 @@ import static springfox.documentation.builders.RequestHandlerSelectors.basePacka * @author 芋道源码 */ @Configuration -@EnableSwagger2WebMvc +@EnableSwagger2 @EnableKnife4j @ConditionalOnClass({Docket.class, ApiInfoBuilder.class}) @ConditionalOnProperty(prefix = "yudao.swagger", value = "enable", matchIfMissing = true) From 113e34b2791ec9b54f5c2cd915890057454d92ed Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 7 Mar 2021 22:36:08 +0800 Subject: [PATCH 05/17] =?UTF-8?q?=E5=AE=8C=E5=96=84=20dict=20data=20?= =?UTF-8?q?=E7=9A=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dal/mysql/dict/SysDictDataMapper.java | 4 +- .../dict/impl/SysDictDataServiceImpl.java | 22 +-- .../dashboard/util/collection/ArrayUtils.java | 6 +- .../service/dict/SysDictDataServiceTest.java | 140 +++++++++++++++++- 4 files changed, 150 insertions(+), 22 deletions(-) diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dict/SysDictDataMapper.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dict/SysDictDataMapper.java index a9a4a1d55a..f135a6f8cd 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dict/SysDictDataMapper.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dict/SysDictDataMapper.java @@ -15,9 +15,9 @@ import java.util.List; @Mapper public interface SysDictDataMapper extends BaseMapperX { - default SysDictDataDO selectByDictTypeAndLabel(String dictType, String label) { + default SysDictDataDO selectByDictTypeAndLabel(String dictType, String value) { return selectOne(new QueryWrapper().eq("dict_type", dictType) - .eq("label", label)); + .eq("value", value)); } default int selectCountByDictType(String dictType) { diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictDataServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictDataServiceImpl.java index e8ebb82dc4..029e10d27c 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictDataServiceImpl.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictDataServiceImpl.java @@ -2,7 +2,6 @@ package cn.iocoder.dashboard.modules.system.service.dict.impl; import cn.hutool.core.collection.CollUtil; import cn.iocoder.dashboard.common.enums.CommonStatusEnum; -import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil; import cn.iocoder.dashboard.common.pojo.PageResult; import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataCreateReqVO; @@ -10,9 +9,9 @@ import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataEx import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataPageReqVO; import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataUpdateReqVO; import cn.iocoder.dashboard.modules.system.convert.dict.SysDictDataConvert; -import cn.iocoder.dashboard.modules.system.dal.mysql.dict.SysDictDataMapper; import cn.iocoder.dashboard.modules.system.dal.dataobject.dict.SysDictDataDO; import cn.iocoder.dashboard.modules.system.dal.dataobject.dict.SysDictTypeDO; +import cn.iocoder.dashboard.modules.system.dal.mysql.dict.SysDictDataMapper; import cn.iocoder.dashboard.modules.system.mq.producer.dict.SysDictDataProducer; import cn.iocoder.dashboard.modules.system.service.dict.SysDictDataService; import cn.iocoder.dashboard.modules.system.service.dict.SysDictTypeService; @@ -28,6 +27,7 @@ import java.util.Comparator; import java.util.Date; import java.util.List; +import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; /** @@ -156,7 +156,7 @@ public class SysDictDataServiceImpl implements SysDictDataService { @Override public Long createDictData(SysDictDataCreateReqVO reqVO) { // 校验正确性 - this.checkCreateOrUpdate(null, reqVO.getLabel(), reqVO.getDictType()); + this.checkCreateOrUpdate(null, reqVO.getValue(), reqVO.getDictType()); // 插入字典类型 SysDictDataDO dictData = SysDictDataConvert.INSTANCE.convert(reqVO); dictDataMapper.insert(dictData); @@ -168,7 +168,7 @@ public class SysDictDataServiceImpl implements SysDictDataService { @Override public void updateDictData(SysDictDataUpdateReqVO reqVO) { // 校验正确性 - this.checkCreateOrUpdate(reqVO.getId(), reqVO.getLabel(), reqVO.getDictType()); + this.checkCreateOrUpdate(reqVO.getId(), reqVO.getValue(), reqVO.getDictType()); // 更新字典类型 SysDictDataDO updateObj = SysDictDataConvert.INSTANCE.convert(reqVO); dictDataMapper.updateById(updateObj); @@ -191,13 +191,13 @@ public class SysDictDataServiceImpl implements SysDictDataService { return dictDataMapper.selectCountByDictType(dictType); } - private void checkCreateOrUpdate(Long id, String label, String dictType) { + private void checkCreateOrUpdate(Long id, String value, String dictType) { // 校验自己存在 checkDictDataExists(id); // 校验字典类型有效 checkDictTypeValid(dictType); // 校验字典数据的值的唯一性 - checkDictDataValueUnique(id, dictType, label); + checkDictDataValueUnique(id, dictType, value); } private void checkDictDataValueUnique(Long id, String dictType, String label) { @@ -207,10 +207,10 @@ public class SysDictDataServiceImpl implements SysDictDataService { } // 如果 id 为空,说明不用比较是否为相同 id 的字典数据 if (id == null) { - throw ServiceExceptionUtil.exception(DICT_DATA_VALUE_DUPLICATE); + throw exception(DICT_DATA_VALUE_DUPLICATE); } if (!dictData.getId().equals(id)) { - throw ServiceExceptionUtil.exception(DICT_DATA_VALUE_DUPLICATE); + throw exception(DICT_DATA_VALUE_DUPLICATE); } } @@ -220,17 +220,17 @@ public class SysDictDataServiceImpl implements SysDictDataService { } SysDictDataDO dictData = dictDataMapper.selectById(id); if (dictData == null) { - throw ServiceExceptionUtil.exception(DICT_DATA_NOT_EXISTS); + throw exception(DICT_DATA_NOT_EXISTS); } } private void checkDictTypeValid(String type) { SysDictTypeDO dictType = dictTypeService.getDictType(type); if (dictType == null) { - throw ServiceExceptionUtil.exception(DICT_TYPE_NOT_EXISTS); + throw exception(DICT_TYPE_NOT_EXISTS); } if (!CommonStatusEnum.ENABLE.getStatus().equals(dictType.getStatus())) { - throw ServiceExceptionUtil.exception(DICT_TYPE_NOT_ENABLE); + throw exception(DICT_TYPE_NOT_ENABLE); } } diff --git a/src/main/java/cn/iocoder/dashboard/util/collection/ArrayUtils.java b/src/main/java/cn/iocoder/dashboard/util/collection/ArrayUtils.java index 7dc9fb6543..e404427323 100644 --- a/src/main/java/cn/iocoder/dashboard/util/collection/ArrayUtils.java +++ b/src/main/java/cn/iocoder/dashboard/util/collection/ArrayUtils.java @@ -2,6 +2,8 @@ package cn.iocoder.dashboard.util.collection; import cn.hutool.core.util.ArrayUtil; +import java.util.function.Consumer; + /** * Array 工具类 * @@ -18,11 +20,11 @@ public class ArrayUtils { * @return 结果数组 */ @SafeVarargs - public static T[] append(T object, T... newElements) { + public static Consumer[] append(Consumer object, Consumer... newElements) { if (object == null) { return newElements; } - T[] result = ArrayUtil.newArray(object.getClass(), 1 + newElements.length); + Consumer[] result = ArrayUtil.newArray(Consumer.class, 1 + newElements.length); result[0] = object; System.arraycopy(newElements, 0, result, 1, newElements.length); return result; diff --git a/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java b/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java index 318a4afca9..8d976c31f6 100644 --- a/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java +++ b/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java @@ -12,14 +12,16 @@ import cn.iocoder.dashboard.modules.system.dal.dataobject.dict.SysDictTypeDO; import cn.iocoder.dashboard.modules.system.dal.mysql.dict.SysDictDataMapper; import cn.iocoder.dashboard.modules.system.mq.producer.dict.SysDictDataProducer; import cn.iocoder.dashboard.modules.system.service.dict.impl.SysDictDataServiceImpl; +import cn.iocoder.dashboard.util.collection.ArrayUtils; import cn.iocoder.dashboard.util.object.ObjectUtils; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; import javax.annotation.Resource; import java.util.List; +import java.util.function.Consumer; -import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.DICT_DATA_NOT_EXISTS; +import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals; import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException; import static cn.iocoder.dashboard.util.RandomUtils.*; @@ -107,8 +109,7 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest { SysDictDataCreateReqVO reqVO = randomPojo(SysDictDataCreateReqVO.class, o -> o.setStatus(randomCommonStatus())); // mock 方法 - when(dictTypeService.getDictType(eq(reqVO.getDictType()))) - .thenReturn(randomPojo(SysDictTypeDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()))); + when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); // 调用 Long dictDataId = dictDataService.createDictData(reqVO); @@ -121,21 +122,66 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest { verify(dictDataProducer, times(1)).sendDictDataRefreshMessage(); } + @Test + public void testCreateDictData_dictTypeNotExists() { + // 准备参数 + SysDictDataCreateReqVO reqVO = randomPojo(SysDictDataCreateReqVO.class, + o -> o.setStatus(randomCommonStatus())); + + // 调用, 并断言异常 + assertServiceException(() -> dictDataService.createDictData(reqVO), DICT_TYPE_NOT_EXISTS); + } + + @Test + public void testCreateDictData_dictTypeNotEnable() { + // 准备参数 + SysDictDataCreateReqVO reqVO = randomPojo(SysDictDataCreateReqVO.class, + o -> o.setStatus(randomCommonStatus())); + // mock 方法,数据类型被禁用 + when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn( + randomPojo(SysDictTypeDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + + // 调用, 并断言异常 + assertServiceException(() -> dictDataService.createDictData(reqVO), DICT_TYPE_NOT_ENABLE); + } + + @Test + public void testCreateDictData_dictDataValueDuplicate() { + // 准备参数 + SysDictDataCreateReqVO reqVO = randomPojo(SysDictDataCreateReqVO.class, + o -> o.setStatus(randomCommonStatus())); + // mock 方法,字典类型 + when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); + // mock dictData 重复 value 重复 + dictDataMapper.insert(randomDictDataDO(o -> { + o.setDictType(reqVO.getDictType()); + o.setValue(reqVO.getValue()); // 使用 reqVO 的 value,实现重复 + })); + + // 调用, 并断言异常 + assertServiceException(() -> dictDataService.createDictData(reqVO), DICT_DATA_VALUE_DUPLICATE); + } + @Test public void testUpdateDictData_success() { // mock 数据 - SysDictDataDO dbDictData = randomPojo(SysDictDataDO.class); + SysDictDataDO dbDictData = randomDictDataDO(); dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 // 准备参数 SysDictDataUpdateReqVO reqVO = randomPojo(SysDictDataUpdateReqVO.class, o -> { o.setId(dbDictData.getId()); // 设置更新的 ID + o.setStatus(randomCommonStatus()); }); + // mock 方法,字典类型 + when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); // 调用 dictDataService.updateDictData(reqVO); // 校验是否更新正确 SysDictDataDO dictData = dictDataMapper.selectById(reqVO.getId()); // 获取最新的 assertPojoEquals(reqVO, dictData); + // 校验调用 + verify(dictDataProducer, times(1)).sendDictDataRefreshMessage(); } @Test @@ -147,18 +193,75 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest { assertServiceException(() -> dictDataService.updateDictData(reqVO), DICT_DATA_NOT_EXISTS); } + @Test + public void testUpdateDictData_dictTypeNotExists() { + // mock 数据 + SysDictDataDO dbDictData = randomDictDataDO(); + dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 + // 准备参数 + SysDictDataUpdateReqVO reqVO = randomPojo(SysDictDataUpdateReqVO.class, o -> { + o.setId(dbDictData.getId()); // 设置更新的 ID + o.setStatus(randomCommonStatus()); + }); + + // 调用, 并断言异常 + assertServiceException(() -> dictDataService.updateDictData(reqVO), DICT_TYPE_NOT_EXISTS); + } + + @Test + public void testUpdateDictData_dictTypeNotEnable() { + // mock 数据 + SysDictDataDO dbDictData = randomDictDataDO(); + dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 + // 准备参数 + SysDictDataUpdateReqVO reqVO = randomPojo(SysDictDataUpdateReqVO.class, o -> { + o.setId(dbDictData.getId()); // 设置更新的 ID + o.setStatus(randomCommonStatus()); + }); + // mock 方法,数据类型被禁用 + when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn( + randomPojo(SysDictTypeDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + + // 调用, 并断言异常 + assertServiceException(() -> dictDataService.updateDictData(reqVO), DICT_TYPE_NOT_ENABLE); + } + + @Test + public void testUpdateDictData_dictDataValueDuplicate() { + // mock 数据 + SysDictDataDO dbDictData = randomDictDataDO(); + dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 + // 准备参数 + SysDictDataUpdateReqVO reqVO = randomPojo(SysDictDataUpdateReqVO.class, o -> { + o.setId(dbDictData.getId()); // 设置更新的 ID + o.setStatus(randomCommonStatus()); + }); + // mock 方法,字典类型 + when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); + // mock dictData 重复 value 重复 + dictDataMapper.insert(randomDictDataDO(o -> { + o.setDictType(reqVO.getDictType()); + o.setValue(reqVO.getValue()); // 使用 reqVO 的 value,实现重复 + })); + + // 调用, 并断言异常 + assertServiceException(() -> dictDataService.updateDictData(reqVO), DICT_DATA_VALUE_DUPLICATE); + } + @Test public void testDeleteDictData_success() { // mock 数据 - SysDictDataDO dbDictData = randomPojo(SysDictDataDO.class); + SysDictDataDO dbDictData = randomDictDataDO(); dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 // 准备参数 Long id = dbDictData.getId(); // 调用 dictDataService.deleteDictData(id); - // 校验数据不存在了 - assertNull(dictDataMapper.selectById(id)); + // 校验数据不存在了 + assertNull(dictDataMapper.selectById(id)); + // 校验调用 + verify(dictDataProducer, times(1)).sendDictDataRefreshMessage(); } @Test @@ -170,4 +273,27 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest { assertServiceException(() -> dictDataService.deleteDictData(id), DICT_DATA_NOT_EXISTS); } + // ========== 随机对象 ========== + + @SafeVarargs + private static SysDictDataDO randomDictDataDO(Consumer... consumers) { + Consumer consumer = (o) -> { + o.setStatus(randomCommonStatus()); // 保证 status 的范围 + }; + return randomPojo(SysDictDataDO.class, ArrayUtils.append(consumer, consumers)); + } + + /** + * 生成一个有效的字典类型 + * + * @param type 字典类型 + * @return SysDictTypeDO 对象 + */ + private static SysDictTypeDO randomDictTypeDO(String type) { + return randomPojo(SysDictTypeDO.class, o -> { + o.setType(type); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 是开启 + }); + } + } From 351d08a2c438c094f4bc577c86c68c017276b332 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 7 Mar 2021 23:15:27 +0800 Subject: [PATCH 06/17] =?UTF-8?q?=E5=AE=8C=E5=96=84=20dict=20data=20?= =?UTF-8?q?=E7=9A=84=E6=9C=AC=E5=9C=B0=E7=BC=93=E5=AD=98=E7=9A=84=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dashboard/util/object/ObjectUtils.java | 10 ++++++ .../service/dict/SysDictDataServiceTest.java | 34 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/main/java/cn/iocoder/dashboard/util/object/ObjectUtils.java b/src/main/java/cn/iocoder/dashboard/util/object/ObjectUtils.java index c8b2d2e802..1d652488fa 100644 --- a/src/main/java/cn/iocoder/dashboard/util/object/ObjectUtils.java +++ b/src/main/java/cn/iocoder/dashboard/util/object/ObjectUtils.java @@ -19,4 +19,14 @@ public class ObjectUtils { return result; } + public static > T max(T obj1, T obj2) { + if (obj1 == null) { + return obj2; + } + if (obj2 == null) { + return obj1; + } + return obj1.compareTo(obj2) > 0 ? obj1 : obj2; + } + } diff --git a/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java b/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java index 8d976c31f6..b817072f08 100644 --- a/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java +++ b/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java @@ -14,13 +14,16 @@ import cn.iocoder.dashboard.modules.system.mq.producer.dict.SysDictDataProducer; import cn.iocoder.dashboard.modules.system.service.dict.impl.SysDictDataServiceImpl; import cn.iocoder.dashboard.util.collection.ArrayUtils; import cn.iocoder.dashboard.util.object.ObjectUtils; +import com.google.common.collect.ImmutableTable; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; import javax.annotation.Resource; +import java.util.Date; import java.util.List; import java.util.function.Consumer; +import static cn.hutool.core.bean.BeanUtil.getFieldValue; import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals; import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException; @@ -193,6 +196,37 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest { assertServiceException(() -> dictDataService.updateDictData(reqVO), DICT_DATA_NOT_EXISTS); } + /** + * 测试加载到新的字典数据的情况 + */ + @Test + @SuppressWarnings("unchecked") + public void testInitLocalCache() { + // mock 数据 + SysDictDataDO dictData01 = randomDictDataDO(); + dictDataMapper.insert(dictData01); + SysDictDataDO dictData02 = randomDictDataDO(); + dictDataMapper.insert(dictData02); + + // 调用 + dictDataService.initLocalCache(); + // 断言 labelDictDataCache 缓存 + ImmutableTable labelDictDataCache = + (ImmutableTable) getFieldValue(dictDataService, "labelDictDataCache"); + assertEquals(2, labelDictDataCache.size()); + assertPojoEquals(dictData01, labelDictDataCache.get(dictData01.getDictType(), dictData01.getLabel())); + assertPojoEquals(dictData02, labelDictDataCache.get(dictData02.getDictType(), dictData02.getLabel())); + // 断言 valueDictDataCache 缓存 + ImmutableTable valueDictDataCache = + (ImmutableTable) getFieldValue(dictDataService, "valueDictDataCache"); + assertEquals(2, valueDictDataCache.size()); + assertPojoEquals(dictData01, valueDictDataCache.get(dictData01.getDictType(), dictData01.getValue())); + assertPojoEquals(dictData02, valueDictDataCache.get(dictData02.getDictType(), dictData02.getValue())); + // 断言 maxUpdateTime 缓存 + Date maxUpdateTime = (Date) getFieldValue(dictDataService, "maxUpdateTime"); + assertEquals(ObjectUtils.max(dictData01.getUpdateTime(), dictData02.getUpdateTime()), maxUpdateTime); + } + @Test public void testUpdateDictData_dictTypeNotExists() { // mock 数据 From 2249d76d46d7ece06067db2ca2f2a02b65289870 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 8 Mar 2021 00:21:49 +0800 Subject: [PATCH 07/17] =?UTF-8?q?=E7=AE=80=E5=8C=96=20dict=20data=20?= =?UTF-8?q?=E7=9A=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=EF=BC=8C=E5=B0=86?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E3=80=81=E4=BF=AE=E6=94=B9=E3=80=81=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E6=B6=89=E5=8F=8A=E7=9A=84=E5=85=AC=E7=94=A8=E6=96=B9?= =?UTF-8?q?=E6=B3=95=EF=BC=8C=E5=8D=95=E7=8B=AC=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mybatis/core/mapper/BaseMapperX.java | 4 + .../dal/mysql/dict/SysDictDataMapper.java | 4 +- .../dict/impl/SysDictDataServiceImpl.java | 12 +- .../dict/impl/SysDictTypeServiceImpl.java | 10 +- .../service/dict/SysDictDataServiceTest.java | 240 ++++++++---------- 5 files changed, 122 insertions(+), 148 deletions(-) diff --git a/src/main/java/cn/iocoder/dashboard/framework/mybatis/core/mapper/BaseMapperX.java b/src/main/java/cn/iocoder/dashboard/framework/mybatis/core/mapper/BaseMapperX.java index eace96a265..b86fd762ff 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/mybatis/core/mapper/BaseMapperX.java +++ b/src/main/java/cn/iocoder/dashboard/framework/mybatis/core/mapper/BaseMapperX.java @@ -28,6 +28,10 @@ public interface BaseMapperX extends BaseMapper { return selectOne(new QueryWrapper().eq(field, value)); } + default Integer selectCount(String field, Object value) { + return selectCount(new QueryWrapper().eq(field, value)); + } + default List selectList() { return selectList(new QueryWrapper<>()); } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dict/SysDictDataMapper.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dict/SysDictDataMapper.java index f135a6f8cd..78a4105639 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dict/SysDictDataMapper.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dict/SysDictDataMapper.java @@ -15,13 +15,13 @@ import java.util.List; @Mapper public interface SysDictDataMapper extends BaseMapperX { - default SysDictDataDO selectByDictTypeAndLabel(String dictType, String value) { + default SysDictDataDO selectByDictTypeAndValue(String dictType, String value) { return selectOne(new QueryWrapper().eq("dict_type", dictType) .eq("value", value)); } default int selectCountByDictType(String dictType) { - return selectCount(new QueryWrapper().eq("dict_type", dictType)); + return selectCount("dict_type", dictType); } default PageResult selectPage(SysDictDataPageReqVO reqVO) { diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictDataServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictDataServiceImpl.java index 029e10d27c..603a9933ec 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictDataServiceImpl.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictDataServiceImpl.java @@ -15,6 +15,7 @@ import cn.iocoder.dashboard.modules.system.dal.mysql.dict.SysDictDataMapper; import cn.iocoder.dashboard.modules.system.mq.producer.dict.SysDictDataProducer; import cn.iocoder.dashboard.modules.system.service.dict.SysDictDataService; import cn.iocoder.dashboard.modules.system.service.dict.SysDictTypeService; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableTable; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; @@ -200,8 +201,9 @@ public class SysDictDataServiceImpl implements SysDictDataService { checkDictDataValueUnique(id, dictType, value); } - private void checkDictDataValueUnique(Long id, String dictType, String label) { - SysDictDataDO dictData = dictDataMapper.selectByDictTypeAndLabel(dictType, label); + @VisibleForTesting + public void checkDictDataValueUnique(Long id, String dictType, String value) { + SysDictDataDO dictData = dictDataMapper.selectByDictTypeAndValue(dictType, value); if (dictData == null) { return; } @@ -214,7 +216,8 @@ public class SysDictDataServiceImpl implements SysDictDataService { } } - private void checkDictDataExists(Long id) { + @VisibleForTesting + public void checkDictDataExists(Long id) { if (id == null) { return; } @@ -224,7 +227,8 @@ public class SysDictDataServiceImpl implements SysDictDataService { } } - private void checkDictTypeValid(String type) { + @VisibleForTesting + public void checkDictTypeValid(String type) { SysDictTypeDO dictType = dictTypeService.getDictType(type); if (dictType == null) { throw exception(DICT_TYPE_NOT_EXISTS); diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictTypeServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictTypeServiceImpl.java index 24b6be5e0c..5c75befaad 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictTypeServiceImpl.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictTypeServiceImpl.java @@ -10,6 +10,7 @@ import cn.iocoder.dashboard.modules.system.dal.dataobject.dict.SysDictTypeDO; import cn.iocoder.dashboard.modules.system.dal.mysql.dict.SysDictTypeMapper; import cn.iocoder.dashboard.modules.system.service.dict.SysDictDataService; import cn.iocoder.dashboard.modules.system.service.dict.SysDictTypeService; +import com.google.common.annotations.VisibleForTesting; import org.springframework.stereotype.Service; import javax.annotation.Resource; @@ -97,7 +98,8 @@ public class SysDictTypeServiceImpl implements SysDictTypeService { checkDictTypeUnique(id, type); } - private void checkDictTypeNameUnique(Long id, String type) { + @VisibleForTesting + public void checkDictTypeNameUnique(Long id, String type) { SysDictTypeDO dictType = dictTypeMapper.selectByName(type); if (dictType == null) { return; @@ -111,7 +113,8 @@ public class SysDictTypeServiceImpl implements SysDictTypeService { } } - private void checkDictTypeUnique(Long id, String type) { + @VisibleForTesting + public void checkDictTypeUnique(Long id, String type) { SysDictTypeDO dictType = dictTypeMapper.selectByType(type); if (dictType == null) { return; @@ -125,7 +128,8 @@ public class SysDictTypeServiceImpl implements SysDictTypeService { } } - private SysDictTypeDO checkDictTypeExists(Long id) { + @VisibleForTesting + public SysDictTypeDO checkDictTypeExists(Long id) { if (id == null) { return null; } diff --git a/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java b/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java index b817072f08..82a3dafcbd 100644 --- a/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java +++ b/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java @@ -49,6 +49,37 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest { @MockBean private SysDictDataProducer dictDataProducer; + /** + * 测试加载到新的字典数据的情况 + */ + @Test + @SuppressWarnings("unchecked") + public void testInitLocalCache() { + // mock 数据 + SysDictDataDO dictData01 = randomDictDataDO(); + dictDataMapper.insert(dictData01); + SysDictDataDO dictData02 = randomDictDataDO(); + dictDataMapper.insert(dictData02); + + // 调用 + dictDataService.initLocalCache(); + // 断言 labelDictDataCache 缓存 + ImmutableTable labelDictDataCache = + (ImmutableTable) getFieldValue(dictDataService, "labelDictDataCache"); + assertEquals(2, labelDictDataCache.size()); + assertPojoEquals(dictData01, labelDictDataCache.get(dictData01.getDictType(), dictData01.getLabel())); + assertPojoEquals(dictData02, labelDictDataCache.get(dictData02.getDictType(), dictData02.getLabel())); + // 断言 valueDictDataCache 缓存 + ImmutableTable valueDictDataCache = + (ImmutableTable) getFieldValue(dictDataService, "valueDictDataCache"); + assertEquals(2, valueDictDataCache.size()); + assertPojoEquals(dictData01, valueDictDataCache.get(dictData01.getDictType(), dictData01.getValue())); + assertPojoEquals(dictData02, valueDictDataCache.get(dictData02.getDictType(), dictData02.getValue())); + // 断言 maxUpdateTime 缓存 + Date maxUpdateTime = (Date) getFieldValue(dictDataService, "maxUpdateTime"); + assertEquals(ObjectUtils.max(dictData01.getUpdateTime(), dictData02.getUpdateTime()), maxUpdateTime); + } + @Test public void testGetDictDataPage() { // mock 数据 @@ -125,46 +156,6 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest { verify(dictDataProducer, times(1)).sendDictDataRefreshMessage(); } - @Test - public void testCreateDictData_dictTypeNotExists() { - // 准备参数 - SysDictDataCreateReqVO reqVO = randomPojo(SysDictDataCreateReqVO.class, - o -> o.setStatus(randomCommonStatus())); - - // 调用, 并断言异常 - assertServiceException(() -> dictDataService.createDictData(reqVO), DICT_TYPE_NOT_EXISTS); - } - - @Test - public void testCreateDictData_dictTypeNotEnable() { - // 准备参数 - SysDictDataCreateReqVO reqVO = randomPojo(SysDictDataCreateReqVO.class, - o -> o.setStatus(randomCommonStatus())); - // mock 方法,数据类型被禁用 - when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn( - randomPojo(SysDictTypeDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - - // 调用, 并断言异常 - assertServiceException(() -> dictDataService.createDictData(reqVO), DICT_TYPE_NOT_ENABLE); - } - - @Test - public void testCreateDictData_dictDataValueDuplicate() { - // 准备参数 - SysDictDataCreateReqVO reqVO = randomPojo(SysDictDataCreateReqVO.class, - o -> o.setStatus(randomCommonStatus())); - // mock 方法,字典类型 - when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); - // mock dictData 重复 value 重复 - dictDataMapper.insert(randomDictDataDO(o -> { - o.setDictType(reqVO.getDictType()); - o.setValue(reqVO.getValue()); // 使用 reqVO 的 value,实现重复 - })); - - // 调用, 并断言异常 - assertServiceException(() -> dictDataService.createDictData(reqVO), DICT_DATA_VALUE_DUPLICATE); - } - @Test public void testUpdateDictData_success() { // mock 数据 @@ -187,101 +178,6 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest { verify(dictDataProducer, times(1)).sendDictDataRefreshMessage(); } - @Test - public void testUpdateDictData_notExists() { - // 准备参数 - SysDictDataUpdateReqVO reqVO = randomPojo(SysDictDataUpdateReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> dictDataService.updateDictData(reqVO), DICT_DATA_NOT_EXISTS); - } - - /** - * 测试加载到新的字典数据的情况 - */ - @Test - @SuppressWarnings("unchecked") - public void testInitLocalCache() { - // mock 数据 - SysDictDataDO dictData01 = randomDictDataDO(); - dictDataMapper.insert(dictData01); - SysDictDataDO dictData02 = randomDictDataDO(); - dictDataMapper.insert(dictData02); - - // 调用 - dictDataService.initLocalCache(); - // 断言 labelDictDataCache 缓存 - ImmutableTable labelDictDataCache = - (ImmutableTable) getFieldValue(dictDataService, "labelDictDataCache"); - assertEquals(2, labelDictDataCache.size()); - assertPojoEquals(dictData01, labelDictDataCache.get(dictData01.getDictType(), dictData01.getLabel())); - assertPojoEquals(dictData02, labelDictDataCache.get(dictData02.getDictType(), dictData02.getLabel())); - // 断言 valueDictDataCache 缓存 - ImmutableTable valueDictDataCache = - (ImmutableTable) getFieldValue(dictDataService, "valueDictDataCache"); - assertEquals(2, valueDictDataCache.size()); - assertPojoEquals(dictData01, valueDictDataCache.get(dictData01.getDictType(), dictData01.getValue())); - assertPojoEquals(dictData02, valueDictDataCache.get(dictData02.getDictType(), dictData02.getValue())); - // 断言 maxUpdateTime 缓存 - Date maxUpdateTime = (Date) getFieldValue(dictDataService, "maxUpdateTime"); - assertEquals(ObjectUtils.max(dictData01.getUpdateTime(), dictData02.getUpdateTime()), maxUpdateTime); - } - - @Test - public void testUpdateDictData_dictTypeNotExists() { - // mock 数据 - SysDictDataDO dbDictData = randomDictDataDO(); - dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 - // 准备参数 - SysDictDataUpdateReqVO reqVO = randomPojo(SysDictDataUpdateReqVO.class, o -> { - o.setId(dbDictData.getId()); // 设置更新的 ID - o.setStatus(randomCommonStatus()); - }); - - // 调用, 并断言异常 - assertServiceException(() -> dictDataService.updateDictData(reqVO), DICT_TYPE_NOT_EXISTS); - } - - @Test - public void testUpdateDictData_dictTypeNotEnable() { - // mock 数据 - SysDictDataDO dbDictData = randomDictDataDO(); - dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 - // 准备参数 - SysDictDataUpdateReqVO reqVO = randomPojo(SysDictDataUpdateReqVO.class, o -> { - o.setId(dbDictData.getId()); // 设置更新的 ID - o.setStatus(randomCommonStatus()); - }); - // mock 方法,数据类型被禁用 - when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn( - randomPojo(SysDictTypeDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - - // 调用, 并断言异常 - assertServiceException(() -> dictDataService.updateDictData(reqVO), DICT_TYPE_NOT_ENABLE); - } - - @Test - public void testUpdateDictData_dictDataValueDuplicate() { - // mock 数据 - SysDictDataDO dbDictData = randomDictDataDO(); - dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 - // 准备参数 - SysDictDataUpdateReqVO reqVO = randomPojo(SysDictDataUpdateReqVO.class, o -> { - o.setId(dbDictData.getId()); // 设置更新的 ID - o.setStatus(randomCommonStatus()); - }); - // mock 方法,字典类型 - when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); - // mock dictData 重复 value 重复 - dictDataMapper.insert(randomDictDataDO(o -> { - o.setDictType(reqVO.getDictType()); - o.setValue(reqVO.getValue()); // 使用 reqVO 的 value,实现重复 - })); - - // 调用, 并断言异常 - assertServiceException(() -> dictDataService.updateDictData(reqVO), DICT_DATA_VALUE_DUPLICATE); - } - @Test public void testDeleteDictData_success() { // mock 数据 @@ -299,12 +195,78 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest { } @Test - public void testDeleteDictData_notExists() { - // 准备参数 - Long id = randomLongId(); + public void testCheckDictDataExists_success() { + // mock 数据 + SysDictDataDO dbDictData = randomDictDataDO(); + dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 + + // 调用成功 + dictDataService.checkDictDataExists(dbDictData.getId()); + } + + @Test + public void testCheckDictTypeValid_success() { + // mock 方法,数据类型被禁用 + String type = randomString(); + when(dictTypeService.getDictType(eq(type))).thenReturn(randomDictTypeDO(type)); + + // 调用, 成功 + dictDataService.checkDictTypeValid(type); + } + + @Test + public void testCheckDictTypeValid_notExists() { + assertServiceException(() -> dictDataService.checkDictTypeValid(randomString()), DICT_TYPE_NOT_EXISTS); + } + + @Test + public void testCheckDictTypeValid_notEnable() { + // mock 方法,数据类型被禁用 + String dictType = randomString(); + when(dictTypeService.getDictType(eq(dictType))).thenReturn( + randomPojo(SysDictTypeDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); // 调用, 并断言异常 - assertServiceException(() -> dictDataService.deleteDictData(id), DICT_DATA_NOT_EXISTS); + assertServiceException(() -> dictDataService.checkDictTypeValid(dictType), DICT_TYPE_NOT_ENABLE); + } + + @Test + public void testCheckDictDataValueUnique_success() { + // 调用,成功 + dictDataService.checkDictDataValueUnique(randomLongId(), randomString(), randomString()); + } + + @Test + public void testCheckDictDataValueUnique_valueDuplicateForCreate() { + // 准备参数 + String dictType = randomString(); + String value = randomString(); + // mock 数据 + dictDataMapper.insert(randomDictDataDO(o -> { + o.setDictType(dictType); + o.setValue(value); + })); + + // 调用,校验异常 + assertServiceException(() -> dictDataService.checkDictDataValueUnique(null, dictType, value), + DICT_DATA_VALUE_DUPLICATE); + } + + @Test + public void testCheckDictDataValueUnique_valueDuplicateForUpdate() { + // 准备参数 + Long id = randomLongId(); + String dictType = randomString(); + String value = randomString(); + // mock 数据 + dictDataMapper.insert(randomDictDataDO(o -> { + o.setDictType(dictType); + o.setValue(value); + })); + + // 调用,校验异常 + assertServiceException(() -> dictDataService.checkDictDataValueUnique(id, dictType, value), + DICT_DATA_VALUE_DUPLICATE); } // ========== 随机对象 ========== From c5ec798c10f1f0b50d6c95068d0af5f1dd273ef5 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 8 Mar 2021 00:37:18 +0800 Subject: [PATCH 08/17] =?UTF-8?q?=E7=AE=80=E5=8C=96=20dict=20type=20?= =?UTF-8?q?=E7=9A=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=EF=BC=8C=E5=B0=86?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E3=80=81=E4=BF=AE=E6=94=B9=E3=80=81=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E6=B6=89=E5=8F=8A=E7=9A=84=E5=85=AC=E7=94=A8=E6=96=B9?= =?UTF-8?q?=E6=B3=95=EF=BC=8C=E5=8D=95=E7=8B=AC=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dict/impl/SysDictTypeServiceImpl.java | 4 +- .../service/dict/SysDictDataServiceTest.java | 5 + .../service/dict/SysDictTypeServiceTest.java | 144 ++++++++++-------- 3 files changed, 87 insertions(+), 66 deletions(-) diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictTypeServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictTypeServiceImpl.java index 5c75befaad..c210e8e2b1 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictTypeServiceImpl.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictTypeServiceImpl.java @@ -99,8 +99,8 @@ public class SysDictTypeServiceImpl implements SysDictTypeService { } @VisibleForTesting - public void checkDictTypeNameUnique(Long id, String type) { - SysDictTypeDO dictType = dictTypeMapper.selectByName(type); + public void checkDictTypeNameUnique(Long id, String name) { + SysDictTypeDO dictType = dictTypeMapper.selectByName(name); if (dictType == null) { return; } diff --git a/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java b/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java index 82a3dafcbd..ae029f73b9 100644 --- a/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java +++ b/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java @@ -204,6 +204,11 @@ public class SysDictDataServiceTest extends BaseSpringBootUnitTest { dictDataService.checkDictDataExists(dbDictData.getId()); } + @Test + public void testCheckDictDataExists_notExists() { + assertServiceException(() -> dictDataService.checkDictDataExists(randomLongId()), DICT_DATA_NOT_EXISTS); + } + @Test public void testCheckDictTypeValid_success() { // mock 方法,数据类型被禁用 diff --git a/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictTypeServiceTest.java b/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictTypeServiceTest.java index e25fe2577c..05441a7075 100644 --- a/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictTypeServiceTest.java +++ b/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictTypeServiceTest.java @@ -23,8 +23,8 @@ import static cn.hutool.core.util.RandomUtil.randomEle; import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals; import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException; -import static cn.iocoder.dashboard.util.RandomUtils.randomLongId; -import static cn.iocoder.dashboard.util.RandomUtils.randomPojo; +import static cn.iocoder.dashboard.util.RandomUtils.*; +import static cn.iocoder.dashboard.util.RandomUtils.randomString; import static cn.iocoder.dashboard.util.date.DateUtils.buildTime; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.eq; @@ -142,32 +142,6 @@ public class SysDictTypeServiceTest extends BaseSpringBootUnitTest { assertPojoEquals(reqVO, dictType); } - @Test - public void testCreateDictType_nameDuplicate() { - // mock 数据 - SysDictTypeDO dbDictType = randomDictTypeDO(); - dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 - // 准备参数 - SysDictTypeCreateReqVO reqVO = randomPojo(SysDictTypeCreateReqVO.class, - o -> o.setName(dbDictType.getName())); // 模拟 name 重复 - - // 调用, 并断言异常 - assertServiceException(() -> dictTypeService.createDictType(reqVO), DICT_TYPE_NAME_DUPLICATE); - } - - @Test - public void testCreateDictType_typeDuplicate() { - // mock 数据 - SysDictTypeDO dbDictType = randomDictTypeDO(); - dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 - // 准备参数 - SysDictTypeCreateReqVO reqVO = randomPojo(SysDictTypeCreateReqVO.class, - o -> o.setType(dbDictType.getType())); // 模拟 type 重复 - - // 调用, 并断言异常 - assertServiceException(() -> dictTypeService.createDictType(reqVO), DICT_TYPE_TYPE_DUPLICATE); - } - @Test public void testUpdateDictType_success() { // mock 数据 @@ -186,33 +160,6 @@ public class SysDictTypeServiceTest extends BaseSpringBootUnitTest { assertPojoEquals(reqVO, dictType); } - @Test - public void testUpdateDictType_notExists() { - // 准备参数 - SysDictTypeUpdateReqVO reqVO = randomPojo(SysDictTypeUpdateReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> dictTypeService.updateDictType(reqVO), DICT_TYPE_NOT_EXISTS); - } - - @Test - public void testUpdateDictType_nameDuplicate() { - // mock 数据,稍后更新它 - SysDictTypeDO dbDictType = randomDictTypeDO(); - dictTypeMapper.insert(dbDictType); - // mock 数据,ks稍后模拟重复它的名字 - SysDictTypeDO nameDictType = randomDictTypeDO(); - dictTypeMapper.insert(nameDictType); - // 准备参数 - SysDictTypeUpdateReqVO reqVO = randomPojo(SysDictTypeUpdateReqVO.class, o -> { - o.setId(dbDictType.getId()); // 设置更新的 ID - o.setName(nameDictType.getName()); // 模拟 name 重复 - }); - - // 调用, 并断言异常 - assertServiceException(() -> dictTypeService.updateDictType(reqVO), DICT_TYPE_NAME_DUPLICATE); - } - @Test public void testDeleteDictType_success() { // mock 数据 @@ -227,15 +174,6 @@ public class SysDictTypeServiceTest extends BaseSpringBootUnitTest { assertNull(dictTypeMapper.selectById(id)); } - @Test - public void testDeleteDictType_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> dictTypeService.deleteDictType(id), DICT_TYPE_NOT_EXISTS); - } - @Test public void testDeleteDictType_hasChildren() { // mock 数据 @@ -250,6 +188,84 @@ public class SysDictTypeServiceTest extends BaseSpringBootUnitTest { assertServiceException(() -> dictTypeService.deleteDictType(id), DICT_TYPE_HAS_CHILDREN); } + @Test + public void testCheckDictDataExists_success() { + // mock 数据 + SysDictTypeDO dbDictType = randomDictTypeDO(); + dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 + + // 调用成功 + dictTypeService.checkDictTypeExists(dbDictType.getId()); + } + + @Test + public void testCheckDictDataExists_notExists() { + assertServiceException(() -> dictTypeService.checkDictTypeExists(randomLongId()), DICT_TYPE_NOT_EXISTS); + } + + @Test + public void testCheckDictTypeUnique_success() { + // 调用,成功 + dictTypeService.checkDictTypeUnique(randomLongId(), randomString()); + } + + @Test + public void testCheckDictTypeUnique_valueDuplicateForCreate() { + // 准备参数 + String type = randomString(); + // mock 数据 + dictTypeMapper.insert(randomDictTypeDO(o -> o.setType(type))); + + // 调用,校验异常 + assertServiceException(() -> dictTypeService.checkDictTypeUnique(null, type), + DICT_TYPE_TYPE_DUPLICATE); + } + + @Test + public void testCheckDictTypeUnique_valueDuplicateForUpdate() { + // 准备参数 + Long id = randomLongId(); + String type = randomString(); + // mock 数据 + dictTypeMapper.insert(randomDictTypeDO(o -> o.setType(type))); + + // 调用,校验异常 + assertServiceException(() -> dictTypeService.checkDictTypeUnique(id, type), + DICT_TYPE_TYPE_DUPLICATE); + } + + + @Test + public void testCheckDictTypNameUnique_success() { + // 调用,成功 + dictTypeService.checkDictTypeNameUnique(randomLongId(), randomString()); + } + + @Test + public void testCheckDictTypeNameUnique_valueDuplicateForCreate() { + // 准备参数 + String name = randomString(); + // mock 数据 + dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name))); + + // 调用,校验异常 + assertServiceException(() -> dictTypeService.checkDictTypeNameUnique(null, name), + DICT_TYPE_NAME_DUPLICATE); + } + + @Test + public void testCheckDictTypeNameUnique_valueDuplicateForUpdate() { + // 准备参数 + Long id = randomLongId(); + String name = randomString(); + // mock 数据 + dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name))); + + // 调用,校验异常 + assertServiceException(() -> dictTypeService.checkDictTypeNameUnique(id, name), + DICT_TYPE_NAME_DUPLICATE); + } + // ========== 随机对象 ========== @SafeVarargs From 7b4d7f5e65d89bb872e11e765996be6252595308 Mon Sep 17 00:00:00 2001 From: niudehua <657563945@qq.com> Date: Mon, 8 Mar 2021 00:39:04 +0800 Subject: [PATCH 09/17] sort String -> Integer --- .../modules/system/controller/dept/vo/dept/SysDeptBaseVO.java | 2 +- .../modules/system/controller/dept/vo/post/SysPostBaseVO.java | 2 +- .../modules/system/controller/dept/vo/post/SysPostExcelVO.java | 2 +- .../system/controller/permission/vo/menu/SysMenuBaseVO.java | 2 +- .../system/controller/permission/vo/role/SysRoleBaseVO.java | 2 +- .../dashboard/modules/system/dal/dataobject/dept/SysDeptDO.java | 2 +- .../dashboard/modules/system/dal/dataobject/dept/SysPostDO.java | 2 +- .../modules/system/dal/dataobject/permission/SysMenuDO.java | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/dept/SysDeptBaseVO.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/dept/SysDeptBaseVO.java index fd40fd7deb..922c395fdb 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/dept/SysDeptBaseVO.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/dept/SysDeptBaseVO.java @@ -26,7 +26,7 @@ public class SysDeptBaseVO { @ApiModelProperty(value = "显示顺序不能为空", required = true, example = "1024") @NotBlank(message = "显示顺序不能为空") - private String sort; + private Integer sort; @ApiModelProperty(value = "负责人", example = "芋道") private String leader; diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostBaseVO.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostBaseVO.java index 162f713878..9a7943b0fd 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostBaseVO.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostBaseVO.java @@ -25,7 +25,7 @@ public class SysPostBaseVO { @ApiModelProperty(value = "显示顺序不能为空", required = true, example = "1024") @NotBlank(message = "显示顺序不能为空") - private String sort; + private Integer sort; @ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 SysCommonStatusEnum 枚举类") private Integer status; diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostExcelVO.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostExcelVO.java index 05ac0e962d..74afa919e7 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostExcelVO.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostExcelVO.java @@ -23,7 +23,7 @@ public class SysPostExcelVO { private String name; @ExcelProperty("岗位排序") - private String sort; + private Integer sort; @ExcelProperty(value = "状态", converter = DictConvert.class) @DictFormat(SYS_COMMON_STATUS) diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/permission/vo/menu/SysMenuBaseVO.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/permission/vo/menu/SysMenuBaseVO.java index 818ef8c93a..5882f825ce 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/permission/vo/menu/SysMenuBaseVO.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/permission/vo/menu/SysMenuBaseVO.java @@ -29,7 +29,7 @@ public class SysMenuBaseVO { @ApiModelProperty(value = "显示顺序不能为空", required = true, example = "1024") @NotBlank(message = "显示顺序不能为空") - private String sort; + private Integer sort; @ApiModelProperty(value = "父菜单 ID", required = true, example = "1024") @NotNull(message = "父菜单 ID 不能为空") diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/permission/vo/role/SysRoleBaseVO.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/permission/vo/role/SysRoleBaseVO.java index 80ee8bfcd3..bf9a1b1414 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/permission/vo/role/SysRoleBaseVO.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/permission/vo/role/SysRoleBaseVO.java @@ -25,7 +25,7 @@ public class SysRoleBaseVO { @ApiModelProperty(value = "显示顺序不能为空", required = true, example = "1024") @NotBlank(message = "显示顺序不能为空") - private String sort; + private Integer sort; @ApiModelProperty(value = "角色类型", required = true, example = "1", notes = "见 SysRoleTypeEnum 枚举") private Integer type; diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/dept/SysDeptDO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/dept/SysDeptDO.java index e2218f461d..e116bd3b0f 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/dept/SysDeptDO.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/dept/SysDeptDO.java @@ -35,7 +35,7 @@ public class SysDeptDO extends BaseDO { /** * 显示顺序 */ - private String sort; + private Integer sort; /** * 负责人 */ diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/dept/SysPostDO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/dept/SysPostDO.java index d2eaefd3bd..ba5f3e0ad7 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/dept/SysPostDO.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/dept/SysPostDO.java @@ -34,7 +34,7 @@ public class SysPostDO extends BaseDO { /** * 岗位排序 */ - private String sort; + private Integer sort; /** * 状态 * diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/permission/SysMenuDO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/permission/SysMenuDO.java index ee8f302ddd..82956e9f95 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/permission/SysMenuDO.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/permission/SysMenuDO.java @@ -49,7 +49,7 @@ public class SysMenuDO extends BaseDO { /** * 显示顺序 */ - private String sort; + private Integer sort; /** * 父菜单ID */ From 01be94a7a062633884c9947d64b961b1f056e241 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 8 Mar 2021 00:50:59 +0800 Subject: [PATCH 10/17] =?UTF-8?q?=E7=AE=80=E5=8C=96=20config=20=E7=9A=84?= =?UTF-8?q?=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=EF=BC=8C=E5=B0=86=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=81=E4=BF=AE=E6=94=B9=E3=80=81=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E6=B6=89=E5=8F=8A=E7=9A=84=E5=85=AC=E7=94=A8=E6=96=B9=E6=B3=95?= =?UTF-8?q?=EF=BC=8C=E5=8D=95=E7=8B=AC=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/ruoyi-vue-pro.sql | 27 ++++++-- .../config/impl/InfConfigServiceImpl.java | 7 +- .../service/config/InfConfigServiceTest.java | 68 +++++++++++-------- .../service/dict/SysDictTypeServiceTest.java | 5 +- 4 files changed, 68 insertions(+), 39 deletions(-) diff --git a/sql/ruoyi-vue-pro.sql b/sql/ruoyi-vue-pro.sql index 56a3a98bf3..7b2e2c7abc 100644 --- a/sql/ruoyi-vue-pro.sql +++ b/sql/ruoyi-vue-pro.sql @@ -11,7 +11,7 @@ Target Server Version : 50718 File Encoding : 65001 - Date: 07/03/2021 00:43:34 + Date: 08/03/2021 00:50:29 */ SET NAMES utf8mb4; @@ -43,7 +43,7 @@ CREATE TABLE `inf_api_access_log` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=1822 DEFAULT CHARSET=utf8mb4 COMMENT='API 访问日志表'; +) ENGINE=InnoDB AUTO_INCREMENT=1850 DEFAULT CHARSET=utf8mb4 COMMENT='API 访问日志表'; -- ---------------------------- -- Records of inf_api_access_log @@ -175,7 +175,7 @@ CREATE TABLE `inf_job_log` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=4458 DEFAULT CHARSET=utf8mb4 COMMENT='定时任务日志表'; +) ENGINE=InnoDB AUTO_INCREMENT=4477 DEFAULT CHARSET=utf8mb4 COMMENT='定时任务日志表'; -- ---------------------------- -- Records of inf_job_log @@ -579,7 +579,7 @@ CREATE TABLE `sys_operate_log` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COMMENT='操作日志记录'; +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COMMENT='操作日志记录'; -- ---------------------------- -- Records of sys_operate_log @@ -955,7 +955,7 @@ INSERT INTO `sys_user_session` VALUES ('7b008d71139f48a4903aeaab0bbbef2e', 1, '1 INSERT INTO `sys_user_session` VALUES ('7cbd0fdfb9304f298abb9ee2f58f7230', 1, '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', '', '2021-02-08 20:02:24', '', '2021-02-08 20:02:24', b'0'); INSERT INTO `sys_user_session` VALUES ('830a1687977b44969025177ad3b6d051', 1, '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', '', '2021-02-10 01:56:00', '', '2021-02-10 01:56:00', b'0'); INSERT INTO `sys_user_session` VALUES ('8331dbbca7be4348a3d2d02bf559b65e', 1, '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', '', '2021-02-05 13:29:04', '', '2021-02-10 19:06:26', b'0'); -INSERT INTO `sys_user_session` VALUES ('897714de63634dba9ac235247f9860c2', 1, '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', '', '2021-03-06 06:27:27', '', '2021-03-06 06:27:27', b'0'); +INSERT INTO `sys_user_session` VALUES ('897714de63634dba9ac235247f9860c2', 1, '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', '', '2021-03-06 06:27:27', '', '2021-03-07 00:50:21', b'0'); INSERT INTO `sys_user_session` VALUES ('8d7668c1276344ecbffaf7e4420595d7', 1, '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', '', '2021-02-08 09:56:09', '', '2021-02-19 19:17:42', b'0'); INSERT INTO `sys_user_session` VALUES ('9a3a5b8dee1841b69d70454ce23a53a3', 1, '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', '', '2021-02-26 00:09:49', '', '2021-02-27 01:27:20', b'0'); INSERT INTO `sys_user_session` VALUES ('9cc71dc2d7a24b978db1bfe0e4bae349', 1, '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', '', '2021-01-26 08:30:01', '', '2021-01-26 08:30:01', b'0'); @@ -1006,7 +1006,7 @@ CREATE TABLE `tool_codegen_column` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=369 DEFAULT CHARSET=utf8mb4 COMMENT='代码生成表字段定义'; +) ENGINE=InnoDB AUTO_INCREMENT=381 DEFAULT CHARSET=utf8mb4 COMMENT='代码生成表字段定义'; -- ---------------------------- -- Records of tool_codegen_column @@ -1148,6 +1148,18 @@ INSERT INTO `tool_codegen_column` VALUES (365, 29, 'create_time', 'datetime', ' INSERT INTO `tool_codegen_column` VALUES (366, 29, 'update_by', 'varchar(64)', '更新者', b'1', b'0', '0', 8, 'String', 'updateBy', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '', '2021-03-06 03:52:57', '', '2021-03-06 03:52:57', b'0'); INSERT INTO `tool_codegen_column` VALUES (367, 29, 'update_time', 'datetime', '更新时间', b'0', b'0', '0', 9, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '', '2021-03-06 03:52:57', '', '2021-03-06 03:52:57', b'0'); INSERT INTO `tool_codegen_column` VALUES (368, 29, 'deleted', 'bit(1)', '是否删除', b'0', b'0', '0', 10, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '', '2021-03-06 03:52:57', '', '2021-03-06 03:52:57', b'0'); +INSERT INTO `tool_codegen_column` VALUES (369, 30, 'id', 'bigint(20)', '字典编码', b'0', b'1', '1', 1, 'Long', 'id', '', NULL, b'0', b'1', b'0', '=', b'1', 'input', '', '2021-03-06 06:48:28', '', '2021-03-06 06:48:28', b'0'); +INSERT INTO `tool_codegen_column` VALUES (370, 30, 'sort', 'int(4)', '字典排序', b'0', b'0', '0', 2, 'Integer', 'sort', '', NULL, b'1', b'1', b'0', '=', b'1', 'input', '', '2021-03-06 06:48:28', '', '2021-03-06 06:50:38', b'0'); +INSERT INTO `tool_codegen_column` VALUES (371, 30, 'label', 'varchar(100)', '字典标签', b'0', b'0', '0', 3, 'String', 'label', '', NULL, b'1', b'1', b'1', 'LIKE', b'1', 'input', '', '2021-03-06 06:48:28', '', '2021-03-06 06:50:38', b'0'); +INSERT INTO `tool_codegen_column` VALUES (372, 30, 'value', 'varchar(100)', '字典键值', b'0', b'0', '0', 4, 'String', 'value', '', NULL, b'1', b'1', b'0', '=', b'1', 'input', '', '2021-03-06 06:48:28', '', '2021-03-06 06:50:38', b'0'); +INSERT INTO `tool_codegen_column` VALUES (373, 30, 'dict_type', 'varchar(100)', '字典类型', b'0', b'0', '0', 5, 'String', 'dictType', '', NULL, b'1', b'1', b'1', 'LIKE', b'1', 'select', '', '2021-03-06 06:48:28', '', '2021-03-06 06:50:38', b'0'); +INSERT INTO `tool_codegen_column` VALUES (374, 30, 'status', 'tinyint(4)', '状态(0正常 1停用)', b'0', b'0', '0', 6, 'Integer', 'status', '', NULL, b'1', b'1', b'1', 'LIKE', b'1', 'radio', '', '2021-03-06 06:48:28', '', '2021-03-06 06:50:38', b'0'); +INSERT INTO `tool_codegen_column` VALUES (375, 30, 'remark', 'varchar(500)', '备注', b'1', b'0', '0', 7, 'String', 'remark', '', NULL, b'1', b'1', b'0', '=', b'1', 'input', '', '2021-03-06 06:48:28', '', '2021-03-06 06:50:38', b'0'); +INSERT INTO `tool_codegen_column` VALUES (376, 30, 'create_by', 'varchar(64)', '创建者', b'1', b'0', '0', 8, 'String', 'createBy', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '', '2021-03-06 06:48:28', '', '2021-03-06 06:48:28', b'0'); +INSERT INTO `tool_codegen_column` VALUES (377, 30, 'create_time', 'datetime', '创建时间', b'0', b'0', '0', 9, 'Date', 'createTime', '', NULL, b'0', b'0', b'1', 'BETWEEN', b'1', 'datetime', '', '2021-03-06 06:48:28', '', '2021-03-06 06:48:28', b'0'); +INSERT INTO `tool_codegen_column` VALUES (378, 30, 'update_by', 'varchar(64)', '更新者', b'1', b'0', '0', 10, 'String', 'updateBy', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '', '2021-03-06 06:48:28', '', '2021-03-06 06:48:28', b'0'); +INSERT INTO `tool_codegen_column` VALUES (379, 30, 'update_time', 'datetime', '更新时间', b'0', b'0', '0', 11, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '', '2021-03-06 06:48:28', '', '2021-03-06 06:48:28', b'0'); +INSERT INTO `tool_codegen_column` VALUES (380, 30, 'deleted', 'bit(1)', '是否删除', b'0', b'0', '0', 12, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '', '2021-03-06 06:48:28', '', '2021-03-06 06:48:28', b'0'); COMMIT; -- ---------------------------- @@ -1173,7 +1185,7 @@ CREATE TABLE `tool_codegen_table` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8mb4 COMMENT='代码生成表定义'; +) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8mb4 COMMENT='代码生成表定义'; -- ---------------------------- -- Records of tool_codegen_table @@ -1189,6 +1201,7 @@ INSERT INTO `tool_codegen_table` VALUES (26, 1, 'inf_api_access_log', 'API 访 INSERT INTO `tool_codegen_table` VALUES (27, 1, 'inf_api_error_log', 'API 错误日志', NULL, 'infra', 'apiErrorLog', 'InfApiErrorLog', 'API 错误日志', '芋道源码', 1, 1083, '', '2021-02-26 06:54:49', '', '2021-02-26 07:53:03', b'0'); INSERT INTO `tool_codegen_table` VALUES (28, 1, 'sys_dict_type', '字典类型表', NULL, 'system', 'dictType', 'SysDictType', '字典类型', '芋艿', 1, NULL, '', '2021-03-06 03:45:55', '', '2021-03-06 03:51:02', b'1'); INSERT INTO `tool_codegen_table` VALUES (29, 1, 'sys_dict_type', '字典类型表', NULL, 'system', 'dict', 'SysDictType', '字典类型', '芋艿', 1, NULL, '', '2021-03-06 03:52:57', '', '2021-03-06 04:03:52', b'0'); +INSERT INTO `tool_codegen_table` VALUES (30, 1, 'sys_dict_data', '字典数据表', NULL, 'system', 'type', 'SysDictData', '字典数据', '芋道源码', 1, NULL, '', '2021-03-06 06:48:28', '', '2021-03-06 06:50:47', b'0'); COMMIT; -- ---------------------------- diff --git a/src/main/java/cn/iocoder/dashboard/modules/infra/service/config/impl/InfConfigServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/infra/service/config/impl/InfConfigServiceImpl.java index 41bb5a357e..27e49771d1 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/infra/service/config/impl/InfConfigServiceImpl.java +++ b/src/main/java/cn/iocoder/dashboard/modules/infra/service/config/impl/InfConfigServiceImpl.java @@ -12,6 +12,7 @@ import cn.iocoder.dashboard.modules.infra.dal.dataobject.config.InfConfigDO; import cn.iocoder.dashboard.modules.infra.enums.config.InfConfigTypeEnum; import cn.iocoder.dashboard.modules.infra.mq.producer.config.InfConfigProducer; import cn.iocoder.dashboard.modules.infra.service.config.InfConfigService; +import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -99,7 +100,8 @@ public class InfConfigServiceImpl implements InfConfigService { checkConfigKeyUnique(id, key); } - private InfConfigDO checkConfigExists(Long id) { + @VisibleForTesting + public InfConfigDO checkConfigExists(Long id) { if (id == null) { return null; } @@ -110,7 +112,8 @@ public class InfConfigServiceImpl implements InfConfigService { return config; } - private void checkConfigKeyUnique(Long id, String key) { + @VisibleForTesting + public void checkConfigKeyUnique(Long id, String key) { InfConfigDO config = configMapper.selectByKey(key); if (config == null) { return; diff --git a/src/test/java/cn/iocoder/dashboard/modules/infra/service/config/InfConfigServiceTest.java b/src/test/java/cn/iocoder/dashboard/modules/infra/service/config/InfConfigServiceTest.java index 07691ba00c..94903e6dca 100644 --- a/src/test/java/cn/iocoder/dashboard/modules/infra/service/config/InfConfigServiceTest.java +++ b/src/test/java/cn/iocoder/dashboard/modules/infra/service/config/InfConfigServiceTest.java @@ -24,8 +24,7 @@ import static cn.hutool.core.util.RandomUtil.randomEle; import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.*; import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals; import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException; -import static cn.iocoder.dashboard.util.RandomUtils.randomLongId; -import static cn.iocoder.dashboard.util.RandomUtils.randomPojo; +import static cn.iocoder.dashboard.util.RandomUtils.*; import static cn.iocoder.dashboard.util.date.DateUtils.buildTime; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.times; @@ -145,19 +144,6 @@ public class InfConfigServiceTest extends BaseSpringBootUnitTest { verify(configProducer, times(1)).sendConfigRefreshMessage(); } - @Test - public void testCreateConfig_keyDuplicate() { - // 准备参数 - InfConfigCreateReqVO reqVO = randomPojo(InfConfigCreateReqVO.class); - // mock 数据 - configMapper.insert(randomInfConfigDO(o -> { // @Sql - o.setKey(reqVO.getKey()); // 模拟 key 重复 - })); - - // 调用, 并断言异常 - assertServiceException(() -> configService.createConfig(reqVO), CONFIG_KEY_DUPLICATE); - } - @Test public void testUpdateConfig_success() { // mock 数据 @@ -177,15 +163,6 @@ public class InfConfigServiceTest extends BaseSpringBootUnitTest { verify(configProducer, times(1)).sendConfigRefreshMessage(); } - @Test - public void testUpdateConfig_notExists() { - // 准备参数 - InfConfigUpdateReqVO reqVO = randomPojo(InfConfigUpdateReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> configService.updateConfig(reqVO), CONFIG_NOT_EXISTS); - } - @Test public void testDeleteConfig_success() { // mock 数据 @@ -219,12 +196,49 @@ public class InfConfigServiceTest extends BaseSpringBootUnitTest { } @Test - public void testDeleteConfig_notExists() { + public void testCheckConfigExists_success() { + // mock 数据 + InfConfigDO dbConfigDO = randomInfConfigDO(); + configMapper.insert(dbConfigDO);// @Sql: 先插入出一条存在的数据 + + // 调用成功 + configService.checkConfigExists(dbConfigDO.getId()); + } + + @Test + public void testCheckConfigExist_notExists() { + assertServiceException(() -> configService.checkConfigExists(randomLongId()), CONFIG_NOT_EXISTS); + } + + @Test + public void testCheckConfigKeyUnique_success() { + // 调用,成功 + configService.checkConfigKeyUnique(randomLongId(), randomString()); + } + + @Test + public void testCheckConfigKeyUnique_keyDuplicateForCreate() { + // 准备参数 + String key = randomString(); + // mock 数据 + configMapper.insert(randomInfConfigDO(o -> o.setKey(key))); + + // 调用,校验异常 + assertServiceException(() -> configService.checkConfigKeyUnique(null, key), + CONFIG_KEY_DUPLICATE); + } + + @Test + public void testCheckConfigKeyUnique_keyDuplicateForUpdate() { // 准备参数 Long id = randomLongId(); + String key = randomString(); + // mock 数据 + configMapper.insert(randomInfConfigDO(o -> o.setKey(key))); - // 调用, 并断言异常 - assertServiceException(() -> configService.deleteConfig(id), CONFIG_NOT_EXISTS); + // 调用,校验异常 + assertServiceException(() -> configService.checkConfigKeyUnique(id, key), + CONFIG_KEY_DUPLICATE); } // ========== 随机对象 ========== diff --git a/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictTypeServiceTest.java b/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictTypeServiceTest.java index 05441a7075..ae8d0662b5 100644 --- a/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictTypeServiceTest.java +++ b/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictTypeServiceTest.java @@ -234,7 +234,6 @@ public class SysDictTypeServiceTest extends BaseSpringBootUnitTest { DICT_TYPE_TYPE_DUPLICATE); } - @Test public void testCheckDictTypNameUnique_success() { // 调用,成功 @@ -242,7 +241,7 @@ public class SysDictTypeServiceTest extends BaseSpringBootUnitTest { } @Test - public void testCheckDictTypeNameUnique_valueDuplicateForCreate() { + public void testCheckDictTypeNameUnique_nameDuplicateForCreate() { // 准备参数 String name = randomString(); // mock 数据 @@ -254,7 +253,7 @@ public class SysDictTypeServiceTest extends BaseSpringBootUnitTest { } @Test - public void testCheckDictTypeNameUnique_valueDuplicateForUpdate() { + public void testCheckDictTypeNameUnique_nameDuplicateForUpdate() { // 准备参数 Long id = randomLongId(); String name = randomString(); From 0e5802adb8b30d57b65d6799eb8eb93984e539b7 Mon Sep 17 00:00:00 2001 From: hezhouqun Date: Mon, 8 Mar 2021 11:16:46 +0800 Subject: [PATCH 11/17] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=A1=AB=E5=85=85=EF=BC=8C=E5=A6=82=E6=9E=9C?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E6=98=BE=E5=BC=8F=E7=9A=84=E5=AF=B9=E9=80=9A?= =?UTF-8?q?=E7=94=A8=E5=AD=97=E6=AE=B5=E8=BF=9B=E8=A1=8C=E8=B5=8B=E5=80=BC?= =?UTF-8?q?=EF=BC=8C=E5=88=99=E4=BC=9A=E8=87=AA=E5=8A=A8=E5=A1=AB=E5=85=85?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mybatis/config/DefaultParamHandler.java | 65 +++++++++++++++++++ .../mybatis/core/dataobject/BaseDO.java | 8 +-- 2 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 src/main/java/cn/iocoder/dashboard/framework/mybatis/config/DefaultParamHandler.java diff --git a/src/main/java/cn/iocoder/dashboard/framework/mybatis/config/DefaultParamHandler.java b/src/main/java/cn/iocoder/dashboard/framework/mybatis/config/DefaultParamHandler.java new file mode 100644 index 0000000000..f94100fbce --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/framework/mybatis/config/DefaultParamHandler.java @@ -0,0 +1,65 @@ +package cn.iocoder.dashboard.framework.mybatis.config; + +import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.dashboard.framework.security.core.LoginUser; +import cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.Objects; + +/** + * 通用参数填充实现类 + * + * 如果没有显式的对通用参数进行赋值,这里会对通用参数进行填充、赋值 + * + * @author hexiaowu + */ +@Component +public class DefaultParamHandler implements MetaObjectHandler { + + @Override + public void insertFill(MetaObject metaObject) { + if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BaseDO) { + LoginUser loginUser = SecurityFrameworkUtils.getLoginUser(); + BaseDO baseDO = (BaseDO) metaObject.getOriginalObject(); + Date current = new Date(); + + // 创建时间为空,则以当前时间为插入时间 + if (Objects.isNull(baseDO.getCreateTime())) { + baseDO.setCreateTime(current); + } + // 更新时间为空,则以当前时间为更新时间 + if (Objects.isNull(baseDO.getUpdateTime())) { + baseDO.setUpdateTime(current); + } + // 当前登录用户不为空,创建人为空,则当前登录用户为创建人 + if (Objects.nonNull(loginUser) && Objects.isNull(baseDO.getCreator())) { + baseDO.setCreator(loginUser.getId().toString()); + } + // 当前登录用户不为空,更新人为空,则当前登录用户为更新人 + if (Objects.nonNull(loginUser) && Objects.isNull(baseDO.getUpdater())) { + baseDO.setUpdater(loginUser.getId().toString()); + } + } + } + + @Override + public void updateFill(MetaObject metaObject) { + Object modifyTime = getFieldValByName("updateTime", metaObject); + Object modifier = getFieldValByName("updater", metaObject); + // 获取登录用户信息 + LoginUser loginUser = SecurityFrameworkUtils.getLoginUser(); + + // 更新时间为空,则以当前时间为更新时间 + if (Objects.isNull(modifyTime)) { + setFieldValByName("updateTime", new Date(), metaObject); + } + // 当前登录用户不为空,更新人为空,则当前登录用户为更新人 + if (Objects.nonNull(loginUser) && Objects.isNull(modifier)) { + setFieldValByName("updater", loginUser.getId(), metaObject); + } + } +} diff --git a/src/main/java/cn/iocoder/dashboard/framework/mybatis/core/dataobject/BaseDO.java b/src/main/java/cn/iocoder/dashboard/framework/mybatis/core/dataobject/BaseDO.java index 7f6c76150f..df9ddb22e1 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/mybatis/core/dataobject/BaseDO.java +++ b/src/main/java/cn/iocoder/dashboard/framework/mybatis/core/dataobject/BaseDO.java @@ -21,13 +21,13 @@ public class BaseDO implements Serializable { */ private Date updateTime; /** - * 创建者 TODO 芋艿:迁移成编号 + * 创建者 */ - private String createBy; + private String creator; /** - * 更新者 TODO 芋艿:迁移成编号 + * 更新者 */ - private String updateBy; + private String updater; /** * 是否删除 */ From ce7947b162680def57a51a5f24becd48b4cd02b0 Mon Sep 17 00:00:00 2001 From: hezhouqun Date: Mon, 8 Mar 2021 19:11:38 +0800 Subject: [PATCH 12/17] =?UTF-8?q?=E5=B0=86=20DefaultParamHandler=20?= =?UTF-8?q?=E4=BB=8E=20config=20=E5=8C=85=E7=A7=BB=E5=8A=A8=E5=88=B0=20cor?= =?UTF-8?q?e/handle=20=E5=8C=85=E4=B8=8B=20=E5=B0=86=20DefaultParamHandler?= =?UTF-8?q?=20=E6=94=B9=E5=90=8D=E4=B8=BA=20DefaultDBFieldHandler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../handle/DefaultDBFieldHandler.java} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/main/java/cn/iocoder/dashboard/framework/mybatis/{config/DefaultParamHandler.java => core/handle/DefaultDBFieldHandler.java} (95%) diff --git a/src/main/java/cn/iocoder/dashboard/framework/mybatis/config/DefaultParamHandler.java b/src/main/java/cn/iocoder/dashboard/framework/mybatis/core/handle/DefaultDBFieldHandler.java similarity index 95% rename from src/main/java/cn/iocoder/dashboard/framework/mybatis/config/DefaultParamHandler.java rename to src/main/java/cn/iocoder/dashboard/framework/mybatis/core/handle/DefaultDBFieldHandler.java index f94100fbce..1651ecc479 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/mybatis/config/DefaultParamHandler.java +++ b/src/main/java/cn/iocoder/dashboard/framework/mybatis/core/handle/DefaultDBFieldHandler.java @@ -1,4 +1,4 @@ -package cn.iocoder.dashboard.framework.mybatis.config; +package cn.iocoder.dashboard.framework.mybatis.core.handle; import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.dashboard.framework.security.core.LoginUser; @@ -18,7 +18,7 @@ import java.util.Objects; * @author hexiaowu */ @Component -public class DefaultParamHandler implements MetaObjectHandler { +public class DefaultDBFieldHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { From eca27cd18faef135a9b226334a2b265b074b3353 Mon Sep 17 00:00:00 2001 From: asas6559 <302811456@qq.com> Date: Mon, 8 Mar 2021 20:41:19 +0800 Subject: [PATCH 13/17] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=80=80=E5=87=BA?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=20=E9=80=80=E5=87=BA=E5=90=8E=E9=87=8D?= =?UTF-8?q?=E5=AE=9A=E5=90=91=E5=88=B0=E7=99=BB=E5=BD=95=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/security/config/SecurityConfiguration.java | 2 +- .../security/core/handler/LogoutSuccessHandlerImpl.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java b/src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java index 927c729965..2b9e2202bb 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java +++ b/src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java @@ -152,7 +152,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { .anyRequest().authenticated() .and() .headers().frameOptions().disable(); - httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); + httpSecurity.logout().logoutUrl(webProperties.getApiPrefix() + "/logout").logoutSuccessHandler(logoutSuccessHandler); // 添加 JWT Filter httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); } diff --git a/src/main/java/cn/iocoder/dashboard/framework/security/core/handler/LogoutSuccessHandlerImpl.java b/src/main/java/cn/iocoder/dashboard/framework/security/core/handler/LogoutSuccessHandlerImpl.java index 07826b9ab7..262f56fe2b 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/security/core/handler/LogoutSuccessHandlerImpl.java +++ b/src/main/java/cn/iocoder/dashboard/framework/security/core/handler/LogoutSuccessHandlerImpl.java @@ -1,6 +1,7 @@ package cn.iocoder.dashboard.framework.security.core.handler; import cn.hutool.core.util.StrUtil; +import cn.iocoder.dashboard.common.pojo.CommonResult; import cn.iocoder.dashboard.framework.security.config.SecurityProperties; import cn.iocoder.dashboard.framework.security.core.service.SecurityAuthFrameworkService; import cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils; @@ -36,6 +37,6 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { securityFrameworkService.logout(token); } // 返回成功 - ServletUtils.writeJSON(response, null); + ServletUtils.writeJSON(response, CommonResult.success(null)); } } From ac17cf180598a6eb7154508da773fc55a5834b76 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 8 Mar 2021 21:12:29 +0800 Subject: [PATCH 14/17] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=EF=BC=8C=E6=96=B0=E5=A2=9E=20BaseDbUnitTest?= =?UTF-8?q?=20=E5=92=8C=20BaseDbAndRedisUnitTest=20=E5=9F=BA=E7=B1=BB?= =?UTF-8?q?=EF=BC=8C=E8=A7=A3=E5=86=B3=20SpringBoot=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=EF=BC=8C=E5=AF=BC=E8=87=B4=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E8=BF=87=E6=85=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mybatis/config/MybatisConfiguration.java | 3 +- .../dashboard/BaseDbAndRedisUnitTest.java | 46 +++++++++++++++++++ .../cn/iocoder/dashboard/BaseDbUnitTest.java | 37 +++++++++++++++ .../dashboard/BaseSpringBootUnitTest.java | 1 + .../config/RedisTestConfiguration.java | 2 - .../config/SecurityTestConfiguration.java | 16 ------- .../service/config/InfConfigServiceTest.java | 6 ++- .../service/auth/SysAuthServiceImplTest.java | 22 ++++++++- .../service/dict/SysDictDataServiceTest.java | 6 ++- .../service/dict/SysDictTypeServiceTest.java | 7 +-- src/test/resources/application-unit-test.yaml | 45 +++--------------- src/test/resources/sql/create_tables.sql | 28 +++++------ 12 files changed, 138 insertions(+), 81 deletions(-) create mode 100644 src/test/java/cn/iocoder/dashboard/BaseDbAndRedisUnitTest.java create mode 100644 src/test/java/cn/iocoder/dashboard/BaseDbUnitTest.java delete mode 100644 src/test/java/cn/iocoder/dashboard/config/SecurityTestConfiguration.java diff --git a/src/main/java/cn/iocoder/dashboard/framework/mybatis/config/MybatisConfiguration.java b/src/main/java/cn/iocoder/dashboard/framework/mybatis/config/MybatisConfiguration.java index a955c402ec..2a70b53d4c 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/mybatis/config/MybatisConfiguration.java +++ b/src/main/java/cn/iocoder/dashboard/framework/mybatis/config/MybatisConfiguration.java @@ -13,7 +13,8 @@ import org.springframework.context.annotation.Configuration; * @author 芋道源码 */ @Configuration -@MapperScan(value = "${yudao.info.base-package}", annotationClass = Mapper.class) +@MapperScan(value = "${yudao.info.base-package}", annotationClass = Mapper.class, + lazyInitialization = "${mybatis.lazy-initialization:false}") // Mapper 懒加载,目前仅用于单元测试 public class MybatisConfiguration { @Bean diff --git a/src/test/java/cn/iocoder/dashboard/BaseDbAndRedisUnitTest.java b/src/test/java/cn/iocoder/dashboard/BaseDbAndRedisUnitTest.java new file mode 100644 index 0000000000..59bacb052f --- /dev/null +++ b/src/test/java/cn/iocoder/dashboard/BaseDbAndRedisUnitTest.java @@ -0,0 +1,46 @@ +package cn.iocoder.dashboard; + +import cn.iocoder.dashboard.config.RedisTestConfiguration; +import cn.iocoder.dashboard.framework.datasource.config.DataSourceConfiguration; +import cn.iocoder.dashboard.framework.mybatis.config.MybatisConfiguration; +import cn.iocoder.dashboard.framework.redis.config.RedisConfig; +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure; +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; +import org.redisson.spring.starter.RedissonAutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.jdbc.Sql; + +/** + * 依赖内存 DB 的单元测试 + * + * 注意,Service 层同样适用。对于 Service 层的单元测试,我们针对自己模块的 Mapper 走的是 H2 内存数据库,针对别的模块的 Service 走的是 Mock 方法 + * + * @author 芋道源码 + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbAndRedisUnitTest.Application.class) +@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件 +@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB +public class BaseDbAndRedisUnitTest { + + @Import({ + // DB 配置类 + DataSourceConfiguration.class, // 自己的 DB 配置类 + DataSourceAutoConfiguration.class, // Spring DB 自动配置类 + DruidDataSourceAutoConfigure.class, // Druid 自动配置类 + // MyBatis 配置类 + MybatisConfiguration.class, // 自己的 MyBatis 配置类 + MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类 + // Redis 配置类 + RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer + RedisAutoConfiguration.class, // Spring Redis 自动配置类 + RedisConfig.class, // 自己的 Redis 配置类 + RedissonAutoConfiguration.class, // Redisson 自动高配置类 + }) + public static class Application { + } + +} diff --git a/src/test/java/cn/iocoder/dashboard/BaseDbUnitTest.java b/src/test/java/cn/iocoder/dashboard/BaseDbUnitTest.java new file mode 100644 index 0000000000..8211182792 --- /dev/null +++ b/src/test/java/cn/iocoder/dashboard/BaseDbUnitTest.java @@ -0,0 +1,37 @@ +package cn.iocoder.dashboard; + +import cn.iocoder.dashboard.framework.datasource.config.DataSourceConfiguration; +import cn.iocoder.dashboard.framework.mybatis.config.MybatisConfiguration; +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure; +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.jdbc.Sql; + +/** + * 依赖内存 DB 的单元测试 + * + * 注意,Service 层同样适用。对于 Service 层的单元测试,我们针对自己模块的 Mapper 走的是 H2 内存数据库,针对别的模块的 Service 走的是 Mock 方法 + * + * @author 芋道源码 + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbUnitTest.Application.class) +@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件 +@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB +public class BaseDbUnitTest { + + @Import({ + // DB 配置类 + DataSourceConfiguration.class, // 自己的 DB 配置类 + DataSourceAutoConfiguration.class, // Spring DB 自动配置类 + DruidDataSourceAutoConfigure.class, // Druid 自动配置类 + // MyBatis 配置类 + MybatisConfiguration.class, // 自己的 MyBatis 配置类 + MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类 + }) + public static class Application { + } + +} diff --git a/src/test/java/cn/iocoder/dashboard/BaseSpringBootUnitTest.java b/src/test/java/cn/iocoder/dashboard/BaseSpringBootUnitTest.java index a00fc472d0..fe615dcaff 100644 --- a/src/test/java/cn/iocoder/dashboard/BaseSpringBootUnitTest.java +++ b/src/test/java/cn/iocoder/dashboard/BaseSpringBootUnitTest.java @@ -12,6 +12,7 @@ import javax.annotation.Resource; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) @ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件 @Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB +@Deprecated public class BaseSpringBootUnitTest { @Resource diff --git a/src/test/java/cn/iocoder/dashboard/config/RedisTestConfiguration.java b/src/test/java/cn/iocoder/dashboard/config/RedisTestConfiguration.java index 1cddd9e1c0..c93d766a4f 100644 --- a/src/test/java/cn/iocoder/dashboard/config/RedisTestConfiguration.java +++ b/src/test/java/cn/iocoder/dashboard/config/RedisTestConfiguration.java @@ -8,12 +8,10 @@ import org.springframework.boot.autoconfigure.data.redis.RedisProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Lazy; import java.io.IOException; @Configuration(proxyBeanMethods = false) -@Lazy(false) // 禁用懒加载,因为需要保证 Redis Server 必须先启动 @EnableConfigurationProperties(RedisProperties.class) @AutoConfigureBefore({RedisAutoConfiguration.class, RedissonAutoConfiguration.class}) // 在 Redis 自动配置前,进行初始化 public class RedisTestConfiguration { diff --git a/src/test/java/cn/iocoder/dashboard/config/SecurityTestConfiguration.java b/src/test/java/cn/iocoder/dashboard/config/SecurityTestConfiguration.java deleted file mode 100644 index 08738388a2..0000000000 --- a/src/test/java/cn/iocoder/dashboard/config/SecurityTestConfiguration.java +++ /dev/null @@ -1,16 +0,0 @@ -package cn.iocoder.dashboard.config; - -import org.mockito.Mockito; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.authentication.AuthenticationManager; - -@Configuration -public class SecurityTestConfiguration { - - @Bean - public AuthenticationManager authenticationManager() { - return Mockito.mock(AuthenticationManager.class); - } - -} diff --git a/src/test/java/cn/iocoder/dashboard/modules/infra/service/config/InfConfigServiceTest.java b/src/test/java/cn/iocoder/dashboard/modules/infra/service/config/InfConfigServiceTest.java index 94903e6dca..a01330527c 100644 --- a/src/test/java/cn/iocoder/dashboard/modules/infra/service/config/InfConfigServiceTest.java +++ b/src/test/java/cn/iocoder/dashboard/modules/infra/service/config/InfConfigServiceTest.java @@ -1,6 +1,6 @@ package cn.iocoder.dashboard.modules.infra.service.config; -import cn.iocoder.dashboard.BaseSpringBootUnitTest; +import cn.iocoder.dashboard.BaseDbUnitTest; import cn.iocoder.dashboard.common.pojo.PageResult; import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigCreateReqVO; import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigExportReqVO; @@ -15,6 +15,7 @@ import cn.iocoder.dashboard.util.collection.ArrayUtils; import cn.iocoder.dashboard.util.object.ObjectUtils; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; import javax.annotation.Resource; import java.util.List; @@ -35,7 +36,8 @@ import static org.mockito.Mockito.verify; * * @author 芋道源码 */ -public class InfConfigServiceTest extends BaseSpringBootUnitTest { +@Import(InfConfigServiceImpl.class) +public class InfConfigServiceTest extends BaseDbUnitTest { @Resource private InfConfigServiceImpl configService; diff --git a/src/test/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthServiceImplTest.java b/src/test/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthServiceImplTest.java index 5d9ac58a73..1be999efb2 100644 --- a/src/test/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthServiceImplTest.java +++ b/src/test/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthServiceImplTest.java @@ -1,15 +1,19 @@ package cn.iocoder.dashboard.modules.system.service.auth; -import cn.iocoder.dashboard.BaseSpringBootUnitTest; +import cn.iocoder.dashboard.BaseDbUnitTest; import cn.iocoder.dashboard.common.enums.CommonStatusEnum; import cn.iocoder.dashboard.framework.security.core.LoginUser; import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO; import cn.iocoder.dashboard.modules.system.service.auth.impl.SysAuthServiceImpl; +import cn.iocoder.dashboard.modules.system.service.common.SysCaptchaService; +import cn.iocoder.dashboard.modules.system.service.logger.SysLoginLogService; import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService; import cn.iocoder.dashboard.modules.system.service.user.SysUserService; import cn.iocoder.dashboard.util.AssertUtils; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.userdetails.UsernameNotFoundException; import javax.annotation.Resource; @@ -21,7 +25,13 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; -public class SysAuthServiceImplTest extends BaseSpringBootUnitTest { +/** + * {@link SysAuthServiceImpl} 的单元测试 + * + * @author 芋道源码 + */ +@Import(SysAuthServiceImpl.class) +public class SysAuthServiceImplTest extends BaseDbUnitTest { @Resource private SysAuthServiceImpl authService; @@ -30,6 +40,14 @@ public class SysAuthServiceImplTest extends BaseSpringBootUnitTest { private SysUserService userService; @MockBean private SysPermissionService permissionService; + @MockBean + private AuthenticationManager authenticationManager; + @MockBean + private SysCaptchaService captchaService; + @MockBean + private SysLoginLogService loginLogService; + @MockBean + private SysUserSessionService userSessionService; @Test public void testLoadUserByUsername_success() { diff --git a/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java b/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java index ae029f73b9..79c89518d2 100644 --- a/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java +++ b/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictDataServiceTest.java @@ -1,6 +1,6 @@ package cn.iocoder.dashboard.modules.system.service.dict; -import cn.iocoder.dashboard.BaseSpringBootUnitTest; +import cn.iocoder.dashboard.BaseDbUnitTest; import cn.iocoder.dashboard.common.enums.CommonStatusEnum; import cn.iocoder.dashboard.common.pojo.PageResult; import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataCreateReqVO; @@ -17,6 +17,7 @@ import cn.iocoder.dashboard.util.object.ObjectUtils; import com.google.common.collect.ImmutableTable; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; import javax.annotation.Resource; import java.util.Date; @@ -37,7 +38,8 @@ import static org.mockito.Mockito.*; * * @author 芋道源码 */ -public class SysDictDataServiceTest extends BaseSpringBootUnitTest { +@Import(SysDictDataServiceImpl.class) +public class SysDictDataServiceTest extends BaseDbUnitTest { @Resource private SysDictDataServiceImpl dictDataService; diff --git a/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictTypeServiceTest.java b/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictTypeServiceTest.java index ae8d0662b5..9dd0727333 100644 --- a/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictTypeServiceTest.java +++ b/src/test/java/cn/iocoder/dashboard/modules/system/service/dict/SysDictTypeServiceTest.java @@ -1,6 +1,6 @@ package cn.iocoder.dashboard.modules.system.service.dict; -import cn.iocoder.dashboard.BaseSpringBootUnitTest; +import cn.iocoder.dashboard.BaseDbUnitTest; import cn.iocoder.dashboard.common.enums.CommonStatusEnum; import cn.iocoder.dashboard.common.pojo.PageResult; import cn.iocoder.dashboard.modules.system.controller.dict.vo.type.SysDictTypeCreateReqVO; @@ -14,6 +14,7 @@ import cn.iocoder.dashboard.util.collection.ArrayUtils; import cn.iocoder.dashboard.util.object.ObjectUtils; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; import javax.annotation.Resource; import java.util.List; @@ -24,7 +25,6 @@ import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals; import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException; import static cn.iocoder.dashboard.util.RandomUtils.*; -import static cn.iocoder.dashboard.util.RandomUtils.randomString; import static cn.iocoder.dashboard.util.date.DateUtils.buildTime; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.eq; @@ -35,7 +35,8 @@ import static org.mockito.Mockito.when; * * @author 芋道源码 */ -public class SysDictTypeServiceTest extends BaseSpringBootUnitTest { +@Import(SysDictTypeServiceImpl.class) +public class SysDictTypeServiceTest extends BaseDbUnitTest { @Resource private SysDictTypeServiceImpl dictTypeService; diff --git a/src/test/resources/application-unit-test.yaml b/src/test/resources/application-unit-test.yaml index b8406322cc..70cecc4c3c 100644 --- a/src/test/resources/application-unit-test.yaml +++ b/src/test/resources/application-unit-test.yaml @@ -3,21 +3,6 @@ spring: lazy-initialization: true # 开启懒加载,加快速度 banner-mode: off # 单元测试,禁用 Banner - # 去除的自动配置项 - autoconfigure: - exclude: - - org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration # 单元测试,禁用 SpringSecurity - - org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration # 单元测试,禁用 SpringSecurity - - org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 单元测试,禁用 Quartz - - com.baomidou.lock.spring.boot.autoconfigure.LockAutoConfiguration # 单元测试,禁用 Lock4j 分布式锁 - - org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration # 单元测试,禁用 Scheduler 定时任务 - - org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration # 项目没有使用 Spring Data,所以禁用配置类,加速启动 - -# Swagger 接口文档的自动配置(单元测试,禁用 Swagger) -springfox: - documentation: - auto-startup: false - --- #################### 数据库相关配置 #################### spring: @@ -29,6 +14,9 @@ spring: username: sa password: schema: classpath:sql/create_tables.sql # MySQL 转 H2 的语句,使用 https://www.jooq.org/translate/ 工具 + druid: + async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度 + initial-size: 1 # 单元测试,配置为 1,提升启动速度 # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 redis: @@ -36,17 +24,13 @@ spring: port: 16379 # 端口(单元测试,使用 16379 端口) database: 0 # 数据库索引 +mybatis: + lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 + --- #################### 定时任务相关配置 #################### -# Quartz 配置项,对应 QuartzProperties 配置类(单元测试,禁用 Quartz) - --- #################### 配置中心相关配置 #################### -# Apollo 配置中心 -apollo: - bootstrap: - enabled: false # 单元测试,禁用配置中心 - --- #################### 服务保障相关配置 #################### # Lock4j 配置项(单元测试,禁用 Lock4j) @@ -63,23 +47,6 @@ resilience4j: --- #################### 监控相关配置 #################### -# Actuator 监控端点的配置项 -management: - endpoints: - enabled-by-default: false - -# Spring Boot Admin 配置项 -spring: - boot: - admin: - # Spring Boot Admin Client 客户端的相关配置 - client: - enabled: false - # Spring Boot Admin Server 服务端的相关配置 - context-path: /admin # 配置 Spring - -# 日志文件配置(不需要配置) - --- #################### 芋道相关配置 #################### # 芋道配置项,设置当前项目所有自定义的配置 diff --git a/src/test/resources/sql/create_tables.sql b/src/test/resources/sql/create_tables.sql index 29078224ee..a5fb42520e 100644 --- a/src/test/resources/sql/create_tables.sql +++ b/src/test/resources/sql/create_tables.sql @@ -9,9 +9,9 @@ CREATE TABLE IF NOT EXISTS "inf_config" ( "value" varchar(500) NOT NULL DEFAULT '', "sensitive" bit NOT NULL, "remark" varchar(500) DEFAULT NULL, - "create_by" varchar(64) DEFAULT '', + "creator" varchar(64) DEFAULT '', "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "update_by" varchar(64) DEFAULT '', + "updater" varchar(64) DEFAULT '', "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, "deleted" bit NOT NULL DEFAULT FALSE, PRIMARY KEY ("id") @@ -28,9 +28,9 @@ CREATE TABLE IF NOT EXISTS "sys_dept" ( "phone" varchar(11) DEFAULT NULL, "email" varchar(50) DEFAULT NULL, "status" tinyint NOT NULL, - "create_by" varchar(64) DEFAULT '', + "creator" varchar(64) DEFAULT '', "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "update_by" varchar(64) DEFAULT '', + "updater" varchar(64) DEFAULT '', "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, "deleted" bit NOT NULL DEFAULT FALSE, PRIMARY KEY ("id") @@ -44,9 +44,9 @@ CREATE TABLE IF NOT EXISTS "sys_dict_data" ( "dict_type" varchar(100) NOT NULL DEFAULT '', "status" tinyint NOT NULL DEFAULT '0', "remark" varchar(500) DEFAULT NULL, - "create_by" varchar(64) DEFAULT '', + "creator" varchar(64) DEFAULT '', "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "update_by" varchar(64) DEFAULT '', + "updater" varchar(64) DEFAULT '', "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, "deleted" bit NOT NULL DEFAULT FALSE, PRIMARY KEY ("id") @@ -62,9 +62,9 @@ CREATE TABLE IF NOT EXISTS "sys_role" ( "status" tinyint NOT NULL, "type" tinyint NOT NULL, "remark" varchar(500) DEFAULT NULL, - "create_by" varchar(64) DEFAULT '', + "creator" varchar(64) DEFAULT '', "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "update_by" varchar(64) DEFAULT '', + "updater" varchar(64) DEFAULT '', "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, "deleted" bit NOT NULL DEFAULT FALSE, PRIMARY KEY ("id") @@ -74,9 +74,9 @@ CREATE TABLE IF NOT EXISTS "sys_role_menu" ( "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, "role_id" bigint NOT NULL, "menu_id" bigint NOT NULL, - "create_by" varchar(64) DEFAULT '', + "creator" varchar(64) DEFAULT '', "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "update_by" varchar(64) DEFAULT '', + "updater" varchar(64) DEFAULT '', "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, "deleted" bit NOT NULL DEFAULT FALSE, PRIMARY KEY ("id") @@ -93,9 +93,9 @@ CREATE TABLE IF NOT EXISTS "sys_menu" ( "icon" varchar(100) DEFAULT '#', "component" varchar(255) DEFAULT NULL, "status" tinyint NOT NULL DEFAULT '0', - "create_by" varchar(64) DEFAULT '', + "creator" varchar(64) DEFAULT '', "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "update_by" varchar(64) DEFAULT '', + "updater" varchar(64) DEFAULT '', "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, "deleted" bit NOT NULL DEFAULT FALSE, PRIMARY KEY ("id") @@ -107,9 +107,9 @@ CREATE TABLE "sys_dict_type" ( "type" varchar(100) NOT NULL DEFAULT '', "status" tinyint NOT NULL DEFAULT '0', "remark" varchar(500) DEFAULT NULL, - "create_by" varchar(64) DEFAULT '', + "creator" varchar(64) DEFAULT '', "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "update_by" varchar(64) DEFAULT '', + "updater" varchar(64) DEFAULT '', "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, "deleted" bit NOT NULL DEFAULT FALSE, PRIMARY KEY ("id") From d5ac6e7a199e3e36b46d89445fe1f8fe3a16130a Mon Sep 17 00:00:00 2001 From: asas6559 <302811456@qq.com> Date: Mon, 8 Mar 2021 22:26:47 +0800 Subject: [PATCH 15/17] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=99=BB=E5=87=BA?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E8=AE=B0=E5=BD=95=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/auth/impl/SysAuthServiceImpl.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java index 1cbc7bb0b4..d8deefaaee 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java @@ -160,7 +160,23 @@ public class SysAuthServiceImpl implements SysAuthService { @Override public void logout(String token) { -// AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功")); TODO 需要搞一搞 + // 查询用户信息 + LoginUser loginUser = userSessionService.getLoginUser(token); + if(loginUser == null) return; + // 删除session + userSessionService.deleteUserSession(token); + this.createLogoutLog(loginUser.getUsername(), SysLoginResultEnum.SUCCESS); + } + + private void createLogoutLog(String username, SysLoginResultEnum loginResult) { + SysLoginLogCreateReqVO reqVO = new SysLoginLogCreateReqVO(); + reqVO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType()); + reqVO.setTraceId(TracerUtils.getTraceId()); + reqVO.setUsername(username); + reqVO.setUserAgent(ServletUtils.getUserAgent()); + reqVO.setUserIp(ServletUtils.getClientIP()); + reqVO.setResult(loginResult.getResult()); + loginLogService.createLoginLog(reqVO); } @Override From 4294ed2548dcffe7233cf9858bf6c51e241e238d Mon Sep 17 00:00:00 2001 From: asas6559 <302811456@qq.com> Date: Mon, 8 Mar 2021 22:34:39 +0800 Subject: [PATCH 16/17] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=99=BB=E5=87=BA?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E8=AE=B0=E5=BD=95=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/system/service/auth/impl/SysAuthServiceImpl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java index d8deefaaee..675d26a54d 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java @@ -162,7 +162,9 @@ public class SysAuthServiceImpl implements SysAuthService { public void logout(String token) { // 查询用户信息 LoginUser loginUser = userSessionService.getLoginUser(token); - if(loginUser == null) return; + if(loginUser == null) { + return; + } // 删除session userSessionService.deleteUserSession(token); this.createLogoutLog(loginUser.getUsername(), SysLoginResultEnum.SUCCESS); From d79bf7956cdbafd31d9574f672bf6e872473ae6b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 9 Mar 2021 00:35:04 +0800 Subject: [PATCH 17/17] =?UTF-8?q?SecurityFrameworkUtils=20=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E7=94=A8=E6=88=B7=E4=BF=A1=E6=81=AF=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=88=A4=E6=96=AD=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E4=BF=9D=E8=AF=81=E6=AD=A3=E7=A1=AE=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/util/SecurityFrameworkUtils.java | 24 ++++++++++++++++--- .../service/auth/impl/SysAuthServiceImpl.java | 11 +++++---- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/main/java/cn/iocoder/dashboard/framework/security/core/util/SecurityFrameworkUtils.java b/src/main/java/cn/iocoder/dashboard/framework/security/core/util/SecurityFrameworkUtils.java index eb67479d4b..b6c509b930 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/security/core/util/SecurityFrameworkUtils.java +++ b/src/main/java/cn/iocoder/dashboard/framework/security/core/util/SecurityFrameworkUtils.java @@ -2,7 +2,10 @@ package cn.iocoder.dashboard.framework.security.core.util; import cn.iocoder.dashboard.framework.security.core.LoginUser; import cn.iocoder.dashboard.framework.web.core.util.WebFrameworkUtils; +import org.springframework.lang.Nullable; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.util.StringUtils; @@ -40,9 +43,20 @@ public class SecurityFrameworkUtils { /** * 获取当前用户 + * + * @return 当前用户 */ + @Nullable public static LoginUser getLoginUser() { - return (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + SecurityContext context = SecurityContextHolder.getContext(); + if (context == null) { + return null; + } + Authentication authentication = context.getAuthentication(); + if (authentication == null) { + return null; + } + return (LoginUser) authentication.getPrincipal(); } /** @@ -50,8 +64,10 @@ public class SecurityFrameworkUtils { * * @return 用户编号 */ + @Nullable public static Long getLoginUserId() { - return getLoginUser().getId(); + LoginUser loginUser = getLoginUser(); + return loginUser != null ? loginUser.getId() : null; } /** @@ -59,8 +75,10 @@ public class SecurityFrameworkUtils { * * @return 角色编号数组 */ + @Nullable public static Set getLoginUserRoleIds() { - return getLoginUser().getRoleIds(); + LoginUser loginUser = getLoginUser(); + return loginUser != null ? loginUser.getRoleIds() : null; } /** diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java index 675d26a54d..dc5b3be848 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java @@ -162,22 +162,23 @@ public class SysAuthServiceImpl implements SysAuthService { public void logout(String token) { // 查询用户信息 LoginUser loginUser = userSessionService.getLoginUser(token); - if(loginUser == null) { + if (loginUser == null) { return; } - // 删除session + // 删除 session userSessionService.deleteUserSession(token); - this.createLogoutLog(loginUser.getUsername(), SysLoginResultEnum.SUCCESS); + // 记录登出日子和 + this.createLogoutLog(loginUser.getUsername()); } - private void createLogoutLog(String username, SysLoginResultEnum loginResult) { + private void createLogoutLog(String username) { SysLoginLogCreateReqVO reqVO = new SysLoginLogCreateReqVO(); reqVO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType()); reqVO.setTraceId(TracerUtils.getTraceId()); reqVO.setUsername(username); reqVO.setUserAgent(ServletUtils.getUserAgent()); reqVO.setUserIp(ServletUtils.getClientIP()); - reqVO.setResult(loginResult.getResult()); + reqVO.setResult(SysLoginResultEnum.SUCCESS.getResult()); loginLogService.createLoginLog(reqVO); }