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 eace96a26..b86fd762f 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 f135a6f8c..78a410563 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 029e10d27..603a9933e 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 24b6be5e0..5c75befaa 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 b817072f0..82a3dafcb 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); } // ========== 随机对象 ==========