mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-31 10:18:42 +08:00 
			
		
		
		
	Merge branch 'rouyi/master' into feature/notice_test
# Conflicts: # src/test/resources/sql/create_tables.sql
This commit is contained in:
		| @@ -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://github.com/mockito/mockito) | Java Mock 框架 | 3.6.28 | - | | ||||
|  | ||||
| **前端** | ||||
|  | ||||
| @@ -125,7 +128,7 @@ | ||||
|     </tr> | ||||
|     <tr> | ||||
|         <td><img src="https://oscimg.oschina.net/oscnet/b6115bc8c31de52951982e509930b20684a.jpg"/></td> | ||||
|         <td><img src="https://oscimg.oschina.net/oscnet/up-6d73c2140ce694e3de4c05035fdc1868d4c.png"/></td> | ||||
|         <td> - </td> | ||||
|     </tr> | ||||
| </table> | ||||
|  | ||||
|   | ||||
							
								
								
									
										28
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								pom.xml
									
									
									
									
									
								
							| @@ -2,7 +2,7 @@ | ||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||||
|          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||
| 	<modelVersion>4.0.0</modelVersion> | ||||
|     <modelVersion>4.0.0</modelVersion> | ||||
|  | ||||
|     <groupId>cn.iocoder</groupId> | ||||
|     <artifactId>dashboard</artifactId> | ||||
| @@ -25,6 +25,7 @@ | ||||
|         <spring.boot.version>2.4.2</spring.boot.version> | ||||
|         <!-- Web 相关 --> | ||||
|         <knife4j.version>3.0.2</knife4j.version> | ||||
|         <swagger-annotations.version>1.5.22</swagger-annotations.version> | ||||
|         <!-- DB 相关 --> | ||||
|         <mysql-connector-java.version>5.1.46</mysql-connector-java.version> | ||||
|         <druid.version>1.2.4</druid.version> | ||||
| @@ -100,8 +101,21 @@ | ||||
|                     <artifactId>mapstruct</artifactId> | ||||
|                     <groupId>org.mapstruct</groupId> <!-- 避免冲突 --> | ||||
|                 </exclusion> | ||||
|                 <exclusion> | ||||
|                     <artifactId>guava</artifactId> | ||||
|                     <groupId>com.google.guava</groupId> | ||||
|                 </exclusion> | ||||
|                 <exclusion> | ||||
|                     <artifactId>swagger-annotations</artifactId> | ||||
|                     <groupId>io.swagger</groupId> | ||||
|                 </exclusion> | ||||
|             </exclusions> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>io.swagger</groupId> | ||||
|             <artifactId>swagger-annotations</artifactId> | ||||
|             <version>${swagger-annotations.version}</version> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- DB 相关 --> | ||||
|         <dependency> | ||||
| @@ -144,6 +158,12 @@ | ||||
|             <groupId>com.baomidou</groupId> | ||||
|             <artifactId>lock4j-redisson-spring-boot-starter</artifactId> | ||||
|             <version>${lock4j.version}</version> | ||||
|             <exclusions> | ||||
|                 <exclusion> | ||||
|                     <artifactId>redisson-spring-boot-starter</artifactId> | ||||
|                     <groupId>org.redisson</groupId> | ||||
|                 </exclusion> | ||||
|             </exclusions> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
| @@ -175,6 +195,12 @@ | ||||
|             <groupId>org.springframework.boot</groupId> | ||||
|             <artifactId>spring-boot-starter-test</artifactId> | ||||
|             <scope>test</scope> | ||||
|             <exclusions> | ||||
|                 <exclusion> | ||||
|                     <artifactId>asm</artifactId> | ||||
|                     <groupId>org.ow2.asm</groupId> | ||||
|                 </exclusion> | ||||
|             </exclusions> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|   | ||||
| @@ -39,7 +39,7 @@ | ||||
|       </el-form-item> | ||||
|       <el-form-item label="创建时间"> | ||||
|         <el-date-picker | ||||
|           v-model="dateRange" | ||||
|           v-model="dateRangeCreateTime" | ||||
|           size="small" | ||||
|           style="width: 240px" | ||||
|           value-format="yyyy-MM-dd" | ||||
| @@ -177,7 +177,7 @@ export default { | ||||
|       // 状态数据字典 | ||||
|       statusOptions: [], | ||||
|       // 日期范围 | ||||
|       dateRange: [], | ||||
|       dateRangeCreateTime: [], | ||||
|       // 查询参数 | ||||
|       queryParams: { | ||||
|         pageNo: 1, | ||||
| @@ -211,15 +211,15 @@ export default { | ||||
|     /** 查询字典类型列表 */ | ||||
|     getList() { | ||||
|       this.loading = true; | ||||
|       listType(this.addDateRange(this.queryParams, [ | ||||
|         this.dateRange[0] ? this.dateRange[0] + ' 00:00:00' : undefined, | ||||
|         this.dateRange[1] ? this.dateRange[1] + ' 23:59:59' : undefined, | ||||
|       ])).then(response => { | ||||
|           this.typeList = response.data.list; | ||||
|           this.total = response.data.total; | ||||
|           this.loading = false; | ||||
|         } | ||||
|       ); | ||||
|       // 处理查询参数 | ||||
|       let params = {...this.queryParams}; | ||||
|       this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime'); | ||||
|       // 执行查询 | ||||
|       listType(params).then(response => { | ||||
|         this.typeList = response.data.list; | ||||
|         this.total = response.data.total; | ||||
|         this.loading = false; | ||||
|       }); | ||||
|     }, | ||||
|     // 字典状态字典翻译 | ||||
|     statusFormat(row, column) { | ||||
| @@ -248,7 +248,7 @@ export default { | ||||
|     }, | ||||
|     /** 重置按钮操作 */ | ||||
|     resetQuery() { | ||||
|       this.dateRange = []; | ||||
|       this.dateRangeCreateTime = []; | ||||
|       this.resetForm("queryForm"); | ||||
|       this.handleQuery(); | ||||
|     }, | ||||
| @@ -304,19 +304,21 @@ export default { | ||||
|     }, | ||||
|     /** 导出按钮操作 */ | ||||
|     handleExport() { | ||||
|       const queryParams = this.addDateRange(this.queryParams, [ | ||||
|         this.dateRange[0] ? this.dateRange[0] + ' 00:00:00' : undefined, | ||||
|         this.dateRange[1] ? this.dateRange[1] + ' 23:59:59' : undefined, | ||||
|       ]); | ||||
|       this.$confirm('是否确认导出所有类型数据项?', "警告", { | ||||
|           confirmButtonText: "确定", | ||||
|           cancelButtonText: "取消", | ||||
|           type: "warning" | ||||
|         }).then(function() { | ||||
|           return exportType(queryParams); | ||||
|         }).then(response => { | ||||
|           this.downloadExcel(response, '数据类型.xls'); | ||||
|         }) | ||||
|       // 处理查询参数 | ||||
|       let params = {...this.queryParams}; | ||||
|       params.pageNo = undefined; | ||||
|       params.pageSize = undefined; | ||||
|       this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime'); | ||||
|       // 执行导出 | ||||
|       this.$confirm('是否确认导出所有字典类型数据项?', "警告", { | ||||
|         confirmButtonText: "确定", | ||||
|         cancelButtonText: "取消", | ||||
|         type: "warning" | ||||
|       }).then(function() { | ||||
|         return exportType(params); | ||||
|       }).then(response => { | ||||
|         this.downloadExcel(response, '字典类型.xls'); | ||||
|       }) | ||||
|     } | ||||
|   } | ||||
| }; | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -2,6 +2,7 @@ package cn.iocoder.dashboard.common.exception.util; | ||||
|  | ||||
| import cn.iocoder.dashboard.common.exception.ErrorCode; | ||||
| import cn.iocoder.dashboard.common.exception.ServiceException; | ||||
| import com.google.common.annotations.VisibleForTesting; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| @@ -91,7 +92,8 @@ public class ServiceExceptionUtil { | ||||
|      * @param params         参数 | ||||
|      * @return 格式化后的提示 | ||||
|      */ | ||||
|     private static String doFormat(int code, String messagePattern, Object... params) { | ||||
|     @VisibleForTesting | ||||
|     public static String doFormat(int code, String messagePattern, Object... params) { | ||||
|         StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50); | ||||
|         int i = 0; | ||||
|         int j; | ||||
|   | ||||
| @@ -13,14 +13,17 @@ import java.io.Serializable; | ||||
| @Data | ||||
| public class PageParam implements Serializable { | ||||
|  | ||||
|     private static final Integer PAGE_NO = 1; | ||||
|     private static final Integer PAGE_SIZE = 10; | ||||
|  | ||||
|     @ApiModelProperty(value = "页码,从 1 开始", required = true,example = "1") | ||||
|     @NotNull(message = "页码不能为空") | ||||
|     @Min(value = 1, message = "页码最小值为 1") | ||||
|     private Integer pageNo; | ||||
|     private Integer pageNo = PAGE_NO; | ||||
|  | ||||
|     @ApiModelProperty(value = "每页条数,最大值为 100", required = true, example = "10") | ||||
|     @NotNull(message = "每页条数不能为空") | ||||
|     @Range(min = 1, max = 100, message = "条数范围为 [1, 100]") | ||||
|     private Integer pageSize; | ||||
|     private Integer pageSize = PAGE_SIZE; | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|     /** | ||||
|      * 是否删除 | ||||
|      */ | ||||
|   | ||||
| @@ -0,0 +1,65 @@ | ||||
| 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; | ||||
| 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 DefaultDBFieldHandler 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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -24,12 +24,16 @@ public interface BaseMapperX<T> extends BaseMapper<T> { | ||||
|         return new PageResult<>(mpPage.getRecords(), mpPage.getTotal()); | ||||
|     } | ||||
|  | ||||
|     default List<T> selectList() { | ||||
|         return selectList(new QueryWrapper<>()); | ||||
|     } | ||||
|  | ||||
|     default T selectOne(String field, Object value) { | ||||
|         return selectOne(new QueryWrapper<T>().eq(field, value)); | ||||
|     } | ||||
|  | ||||
|     default Integer selectCount(String field, Object value) { | ||||
|         return selectCount(new QueryWrapper<T>().eq(field, value)); | ||||
|     } | ||||
|  | ||||
|     default List<T> selectList() { | ||||
|         return selectList(new QueryWrapper<>()); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1 +0,0 @@ | ||||
| <http://www.iocoder.cn/Spring-Boot/Spring-Security/?github> | ||||
| @@ -0,0 +1 @@ | ||||
| <https://www.iocoder.cn/Spring-Boot/Resilience4j/?yudao> | ||||
| @@ -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 java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
|  | ||||
|   | ||||
| @@ -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 相关 ========== | ||||
|   | ||||
| @@ -10,7 +10,7 @@ import cn.iocoder.dashboard.common.exception.ErrorCode; | ||||
| public interface InfErrorCodeConstants { | ||||
|  | ||||
|     // ========== 参数配置 1001000000 ========== | ||||
|     ErrorCode CONFIG_NOT_FOUND = new ErrorCode(1001000001, "参数配置不存在"); | ||||
|     ErrorCode CONFIG_NOT_EXISTS = new ErrorCode(1001000001, "参数配置不存在"); | ||||
|     ErrorCode CONFIG_KEY_DUPLICATE = new ErrorCode(1001000002, "参数配置 key 重复"); | ||||
|     ErrorCode CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE = new ErrorCode(1001000003, "不能删除类型为系统内置的参数配置"); | ||||
|     ErrorCode CONFIG_GET_VALUE_ERROR_IF_SENSITIVE = new ErrorCode(1001000004, "不允许获取敏感配置到前端"); | ||||
|   | ||||
| @@ -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,18 +100,20 @@ public class InfConfigServiceImpl implements InfConfigService { | ||||
|         checkConfigKeyUnique(id, key); | ||||
|     } | ||||
|  | ||||
|     private InfConfigDO checkConfigExists(Long id) { | ||||
|     @VisibleForTesting | ||||
|     public InfConfigDO checkConfigExists(Long id) { | ||||
|         if (id == null) { | ||||
|             return null; | ||||
|         } | ||||
|         InfConfigDO config = configMapper.selectById(id); | ||||
|         if (config == null) { | ||||
|             throw ServiceExceptionUtil.exception(CONFIG_NOT_FOUND); | ||||
|             throw ServiceExceptionUtil.exception(CONFIG_NOT_EXISTS); | ||||
|         } | ||||
|         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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -32,7 +32,7 @@ public class SysDictDataController { | ||||
|     @GetMapping("/list-all-simple") | ||||
|     // 无需添加权限认证,因为前端全局都需要 | ||||
|     public CommonResult<List<SysDictDataSimpleVO>> listSimpleDictDatas() { | ||||
|         List<SysDictDataDO> list = dictDataService.listDictDatas(); | ||||
|         List<SysDictDataDO> list = dictDataService.getDictDataList(); | ||||
|         return success(SysDictDataConvert.INSTANCE.convertList(list)); | ||||
|     } | ||||
|  | ||||
| @@ -40,7 +40,7 @@ public class SysDictDataController { | ||||
|     @GetMapping("/page") | ||||
| //    @PreAuthorize("@ss.hasPermi('system:dict:list')") | ||||
|     public CommonResult<PageResult<SysDictDataRespVO>> pageDictTypes(@Validated SysDictDataPageReqVO reqVO) { | ||||
|         return success(SysDictDataConvert.INSTANCE.convertPage(dictDataService.pageDictDatas(reqVO))); | ||||
|         return success(SysDictDataConvert.INSTANCE.convertPage(dictDataService.getDictDataPage(reqVO))); | ||||
|     } | ||||
|  | ||||
|     @ApiOperation("/查询字典数据详细") | ||||
| @@ -83,7 +83,7 @@ public class SysDictDataController { | ||||
| //    @Log(title = "字典类型", businessType = BusinessType.EXPORT) | ||||
| //    @PreAuthorize("@ss.hasPermi('system:dict:export')") | ||||
|     public void export(HttpServletResponse response, @Validated SysDictDataExportReqVO reqVO) throws IOException { | ||||
|         List<SysDictDataDO> list = dictDataService.listDictDatas(reqVO); | ||||
|         List<SysDictDataDO> list = dictDataService.getDictDataList(reqVO); | ||||
|         List<SysDictDataExcelVO> excelDataList = SysDictDataConvert.INSTANCE.convertList02(list); | ||||
|         // 输出 | ||||
|         ExcelUtils.write(response, "字典数据.xls", "数据列表", | ||||
|   | ||||
| @@ -32,7 +32,7 @@ public class SysDictTypeController { | ||||
|     @GetMapping("/page") | ||||
| //    @PreAuthorize("@ss.hasPermi('system:dict:list')") | ||||
|     public CommonResult<PageResult<SysDictTypeRespVO>> pageDictTypes(@Validated SysDictTypePageReqVO reqVO) { | ||||
|         return success(SysDictTypeConvert.INSTANCE.convertPage(dictTypeService.pageDictTypes(reqVO))); | ||||
|         return success(SysDictTypeConvert.INSTANCE.convertPage(dictTypeService.getDictTypePage(reqVO))); | ||||
|     } | ||||
|  | ||||
|     @ApiOperation("/查询字典类型详细") | ||||
| @@ -75,7 +75,7 @@ public class SysDictTypeController { | ||||
|     @ApiOperation(value = "获得全部字典类型列表", notes = "包括开启 + 禁用的字典类型,主要用于前端的下拉选项") | ||||
|     // 无需添加权限认证,因为前端全局都需要 | ||||
|     public CommonResult<List<SysDictTypeSimpleRespVO>> listSimpleDictTypes() { | ||||
|         List<SysDictTypeDO> list = dictTypeService.listDictTypes(); | ||||
|         List<SysDictTypeDO> list = dictTypeService.getDictTypeList(); | ||||
|         return success(SysDictTypeConvert.INSTANCE.convertList(list)); | ||||
|     } | ||||
|  | ||||
| @@ -84,7 +84,7 @@ public class SysDictTypeController { | ||||
| //    @Log(title = "字典类型", businessType = BusinessType.EXPORT) | ||||
| //    @PreAuthorize("@ss.hasPermi('system:dict:export')") | ||||
|     public void export(HttpServletResponse response, @Validated SysDictTypeExportReqVO reqVO) throws IOException { | ||||
|         List<SysDictTypeDO> list = dictTypeService.listDictTypes(reqVO); | ||||
|         List<SysDictTypeDO> list = dictTypeService.getDictTypeList(reqVO); | ||||
|         List<SysDictTypeExcelVO> excelTypeList = SysDictTypeConvert.INSTANCE.convertList02(list); | ||||
|         // 输出 | ||||
|         ExcelUtils.write(response, "字典类型.xls", "类型列表", | ||||
|   | ||||
| @@ -16,7 +16,7 @@ public class SysDictDataBaseVO { | ||||
|  | ||||
|     @ApiModelProperty(value = "显示顺序不能为空", required = true, example = "1024") | ||||
|     @NotBlank(message = "显示顺序不能为空") | ||||
|     private String sort; | ||||
|     private Integer sort; | ||||
|  | ||||
|     @ApiModelProperty(value = "字典标签", required = true, example = "芋道") | ||||
|     @NotBlank(message = "字典标签不能为空") | ||||
|   | ||||
| @@ -5,7 +5,6 @@ import io.swagger.annotations.ApiModelProperty; | ||||
| import lombok.Data; | ||||
| import org.springframework.format.annotation.DateTimeFormat; | ||||
|  | ||||
| import javax.validation.constraints.Size; | ||||
| import java.util.Date; | ||||
|  | ||||
| import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | ||||
| @@ -18,18 +17,17 @@ public class SysDictTypeExportReqVO { | ||||
|     private String name; | ||||
|  | ||||
|     @ApiModelProperty(value = "字典类型", example = "sys_common_sex", notes = "模糊匹配") | ||||
|     @Size(max = 100, message = "字典类型类型长度不能超过100个字符") | ||||
|     private String type; | ||||
|  | ||||
|     @ApiModelProperty(value = "展示状态", example = "1", notes = "参见 SysCommonStatusEnum 枚举类") | ||||
|     private Integer status; | ||||
|  | ||||
|     @ApiModelProperty(value = "开始时间", example = "2020-10-24") | ||||
|     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||||
|     private Date beginTime; | ||||
|     @ApiModelProperty(value = "开始创建时间") | ||||
|     private Date beginCreateTime; | ||||
|  | ||||
|     @ApiModelProperty(value = "结束时间", example = "2020-10-24") | ||||
|     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||||
|     private Date endTime; | ||||
|     @ApiModelProperty(value = "结束创建时间") | ||||
|     private Date endCreateTime; | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -27,12 +27,12 @@ public class SysDictTypePageReqVO extends PageParam { | ||||
|     @ApiModelProperty(value = "展示状态", example = "1", notes = "参见 SysCommonStatusEnum 枚举类") | ||||
|     private Integer status; | ||||
|  | ||||
|     @ApiModelProperty(value = "开始时间", example = "2020-10-24") | ||||
|     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||||
|     private Date beginTime; | ||||
|     @ApiModelProperty(value = "开始创建时间") | ||||
|     private Date beginCreateTime; | ||||
|  | ||||
|     @ApiModelProperty(value = "结束时间", example = "2020-10-24") | ||||
|     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||||
|     private Date endTime; | ||||
|     @ApiModelProperty(value = "结束创建时间") | ||||
|     private Date endCreateTime; | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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 不能为空") | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -35,7 +35,7 @@ public class SysDeptDO extends BaseDO { | ||||
|     /** | ||||
|      * 显示顺序 | ||||
|      */ | ||||
|     private String sort; | ||||
|     private Integer sort; | ||||
|     /** | ||||
|      * 负责人 | ||||
|      */ | ||||
|   | ||||
| @@ -34,7 +34,7 @@ public class SysPostDO extends BaseDO { | ||||
|     /** | ||||
|      * 岗位排序 | ||||
|      */ | ||||
|     private String sort; | ||||
|     private Integer sort; | ||||
|     /** | ||||
|      * 状态 | ||||
|      * | ||||
|   | ||||
| @@ -33,7 +33,7 @@ public class SysDictTypeDO extends BaseDO { | ||||
|     /** | ||||
|      * 字典类型 | ||||
|      */ | ||||
|     @TableField("dict_type") | ||||
|     @TableField("`type`") | ||||
|     private String type; | ||||
|     /** | ||||
|      * 状态 | ||||
|   | ||||
| @@ -49,7 +49,7 @@ public class SysMenuDO extends BaseDO { | ||||
|     /** | ||||
|      * 显示顺序 | ||||
|      */ | ||||
|     private String sort; | ||||
|     private Integer sort; | ||||
|     /** | ||||
|      * 父菜单ID | ||||
|      */ | ||||
|   | ||||
| @@ -15,13 +15,13 @@ import java.util.List; | ||||
| @Mapper | ||||
| public interface SysDictDataMapper extends BaseMapperX<SysDictDataDO> { | ||||
|  | ||||
|     default SysDictDataDO selectByDictTypeAndLabel(String dictType, String label) { | ||||
|     default SysDictDataDO selectByDictTypeAndValue(String dictType, String value) { | ||||
|         return selectOne(new QueryWrapper<SysDictDataDO>().eq("dict_type", dictType) | ||||
|                 .eq("label", label)); | ||||
|                 .eq("value", value)); | ||||
|     } | ||||
|  | ||||
|     default int selectCountByDictType(String dictType) { | ||||
|         return selectCount(new QueryWrapper<SysDictDataDO>().eq("dict_type", dictType)); | ||||
|         return selectCount("dict_type", dictType); | ||||
|     } | ||||
|  | ||||
|     default PageResult<SysDictDataDO> selectPage(SysDictDataPageReqVO reqVO) { | ||||
|   | ||||
| @@ -16,20 +16,21 @@ public interface SysDictTypeMapper extends BaseMapperX<SysDictTypeDO> { | ||||
|     default PageResult<SysDictTypeDO> selectPage(SysDictTypePageReqVO reqVO) { | ||||
|         return selectPage(reqVO, new QueryWrapperX<SysDictTypeDO>() | ||||
|                 .likeIfPresent("name", reqVO.getName()) | ||||
|                 .likeIfPresent("dict_type", reqVO.getType()) | ||||
|                 .likeIfPresent("`type`", reqVO.getType()) | ||||
|                 .eqIfPresent("status", reqVO.getStatus()) | ||||
|                 .betweenIfPresent("create_time", reqVO.getBeginTime(), reqVO.getEndTime())); | ||||
|                 .betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())); | ||||
|     } | ||||
|  | ||||
|     default List<SysDictTypeDO> selectList(SysDictTypeExportReqVO reqVO) { | ||||
|         return selectList(new QueryWrapperX<SysDictTypeDO>().likeIfPresent("name", reqVO.getName()) | ||||
|                         .likeIfPresent("dict_type", reqVO.getType()) | ||||
|                         .eqIfPresent("status", reqVO.getStatus()) | ||||
|                         .betweenIfPresent("create_time", reqVO.getBeginTime(), reqVO.getEndTime())); | ||||
|         return selectList(new QueryWrapperX<SysDictTypeDO>() | ||||
|                 .likeIfPresent("name", reqVO.getName()) | ||||
|                 .likeIfPresent("`type`", reqVO.getType()) | ||||
|                 .eqIfPresent("status", reqVO.getStatus()) | ||||
|                 .betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())); | ||||
|     } | ||||
|  | ||||
|     default SysDictTypeDO selectByType(String type) { | ||||
|         return selectOne(new QueryWrapperX<SysDictTypeDO>().eq("dict_type", type)); | ||||
|         return selectOne(new QueryWrapperX<SysDictTypeDO>().eq("`type`", type)); | ||||
|     } | ||||
|  | ||||
|     default SysDictTypeDO selectByName(String name) { | ||||
|   | ||||
| @@ -58,14 +58,14 @@ public interface SysErrorCodeConstants { | ||||
|     ErrorCode POST_CODE_DUPLICATE = new ErrorCode(1002005001, "已经存在该标识的岗位"); | ||||
|  | ||||
|     // ========== 字典类型 1002006000 ========== | ||||
|     ErrorCode DICT_TYPE_NOT_FOUND = new ErrorCode(1002006001, "当前字典类型不存在"); | ||||
|     ErrorCode DICT_TYPE_NOT_EXISTS = new ErrorCode(1002006001, "当前字典类型不存在"); | ||||
|     ErrorCode DICT_TYPE_NOT_ENABLE = new ErrorCode(1002006002, "字典类型不处于开启状态,不允许选择"); | ||||
|     ErrorCode DICT_TYPE_NAME_DUPLICATE = new ErrorCode(1002006003, "已经存在该名字的字典类型"); | ||||
|     ErrorCode DICT_TYPE_TYPE_DUPLICATE = new ErrorCode(1002006004, "已经存在该类型的字典类型"); | ||||
|     ErrorCode DICT_TYPE_HAS_CHILDREN = new ErrorCode(1002006004, "无法删除,该字典类型还有字典数据"); | ||||
|  | ||||
|     // ========== 字典数据 1002007000 ========== | ||||
|     ErrorCode DICT_DATA_NOT_FOUND = new ErrorCode(1002007001, "当前字典数据不存在"); | ||||
|     ErrorCode DICT_DATA_NOT_EXISTS = new ErrorCode(1002007001, "当前字典数据不存在"); | ||||
|     ErrorCode DICT_DATA_NOT_ENABLE = new ErrorCode(1002007002, "字典数据不处于开启状态,不允许选择"); | ||||
|     ErrorCode DICT_DATA_VALUE_DUPLICATE = new ErrorCode(1002007003, "已经存在该值的字典数据"); | ||||
|  | ||||
|   | ||||
| @@ -27,7 +27,7 @@ public interface SysDictDataService extends DictDataFrameworkService { | ||||
|      * | ||||
|      * @return 字典数据全列表 | ||||
|      */ | ||||
|     List<SysDictDataDO> listDictDatas(); | ||||
|     List<SysDictDataDO> getDictDataList(); | ||||
|  | ||||
|     /** | ||||
|      * 获得字典数据分页列表 | ||||
| @@ -35,7 +35,7 @@ public interface SysDictDataService extends DictDataFrameworkService { | ||||
|      * @param reqVO 分页请求 | ||||
|      * @return 字典数据分页列表 | ||||
|      */ | ||||
|     PageResult<SysDictDataDO> pageDictDatas(SysDictDataPageReqVO reqVO); | ||||
|     PageResult<SysDictDataDO> getDictDataPage(SysDictDataPageReqVO reqVO); | ||||
|  | ||||
|     /** | ||||
|      * 获得字典数据列表 | ||||
| @@ -43,7 +43,7 @@ public interface SysDictDataService extends DictDataFrameworkService { | ||||
|      * @param reqVO 列表请求 | ||||
|      * @return 字典数据列表 | ||||
|      */ | ||||
|     List<SysDictDataDO> listDictDatas(SysDictDataExportReqVO reqVO); | ||||
|     List<SysDictDataDO> getDictDataList(SysDictDataExportReqVO reqVO); | ||||
|  | ||||
|     /** | ||||
|      * 获得字典数据详情 | ||||
|   | ||||
| @@ -22,7 +22,7 @@ public interface SysDictTypeService { | ||||
|      * @param reqVO 分页请求 | ||||
|      * @return 字典类型分页列表 | ||||
|      */ | ||||
|     PageResult<SysDictTypeDO> pageDictTypes(SysDictTypePageReqVO reqVO); | ||||
|     PageResult<SysDictTypeDO> getDictTypePage(SysDictTypePageReqVO reqVO); | ||||
|  | ||||
|     /** | ||||
|      * 获得字典类型列表 | ||||
| @@ -30,7 +30,7 @@ public interface SysDictTypeService { | ||||
|      * @param reqVO 列表请求 | ||||
|      * @return 字典类型列表 | ||||
|      */ | ||||
|     List<SysDictTypeDO> listDictTypes(SysDictTypeExportReqVO reqVO); | ||||
|     List<SysDictTypeDO> getDictTypeList(SysDictTypeExportReqVO reqVO); | ||||
|  | ||||
|     /** | ||||
|      * 获得字典类型详情 | ||||
| @@ -75,6 +75,6 @@ public interface SysDictTypeService { | ||||
|      * | ||||
|      * @return 字典类型列表 | ||||
|      */ | ||||
|     List<SysDictTypeDO> listDictTypes(); | ||||
|     List<SysDictTypeDO> getDictTypeList(); | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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,12 +9,13 @@ 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; | ||||
| import com.google.common.annotations.VisibleForTesting; | ||||
| import com.google.common.collect.ImmutableTable; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.springframework.scheduling.annotation.Scheduled; | ||||
| @@ -28,6 +28,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.*; | ||||
|  | ||||
| /** | ||||
| @@ -130,19 +131,19 @@ public class SysDictDataServiceImpl implements SysDictDataService { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public List<SysDictDataDO> listDictDatas() { | ||||
|     public List<SysDictDataDO> getDictDataList() { | ||||
|         List<SysDictDataDO> list = dictDataMapper.selectList(); | ||||
|         list.sort(COMPARATOR_TYPE_AND_SORT); | ||||
|         return list; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public PageResult<SysDictDataDO> pageDictDatas(SysDictDataPageReqVO reqVO) { | ||||
|     public PageResult<SysDictDataDO> getDictDataPage(SysDictDataPageReqVO reqVO) { | ||||
|         return dictDataMapper.selectPage(reqVO); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public List<SysDictDataDO> listDictDatas(SysDictDataExportReqVO reqVO) { | ||||
|     public List<SysDictDataDO> getDictDataList(SysDictDataExportReqVO reqVO) { | ||||
|         List<SysDictDataDO> list = dictDataMapper.selectList(reqVO); | ||||
|         list.sort(COMPARATOR_TYPE_AND_SORT); | ||||
|         return list; | ||||
| @@ -156,7 +157,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 +169,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,46 +192,49 @@ 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); | ||||
|         // 校验字典数据的值的唯一性 | ||||
|         checkDictDataValueUnique(id, dictType, label); | ||||
|         // 校验字典类型有效 | ||||
|         checkDictTypeValid(dictType); | ||||
|         // 校验字典数据的值的唯一性 | ||||
|         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; | ||||
|         } | ||||
|         // 如果 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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void checkDictDataExists(Long id) { | ||||
|     @VisibleForTesting | ||||
|     public void checkDictDataExists(Long id) { | ||||
|         if (id == null) { | ||||
|             return; | ||||
|         } | ||||
|         SysDictDataDO dictData = dictDataMapper.selectById(id); | ||||
|         if (dictData == null) { | ||||
|             throw ServiceExceptionUtil.exception(DICT_DATA_NOT_FOUND); | ||||
|             throw exception(DICT_DATA_NOT_EXISTS); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void checkDictTypeValid(String type) { | ||||
|     @VisibleForTesting | ||||
|     public void checkDictTypeValid(String type) { | ||||
|         SysDictTypeDO dictType = dictTypeService.getDictType(type); | ||||
|         if (dictType == null) { | ||||
|             throw ServiceExceptionUtil.exception(DICT_TYPE_NOT_FOUND); | ||||
|             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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,21 +1,22 @@ | ||||
| package cn.iocoder.dashboard.modules.system.service.dict.impl; | ||||
|  | ||||
| import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil; | ||||
| import cn.iocoder.dashboard.common.pojo.PageResult; | ||||
| import cn.iocoder.dashboard.modules.system.controller.dict.vo.type.SysDictTypeCreateReqVO; | ||||
| import cn.iocoder.dashboard.modules.system.controller.dict.vo.type.SysDictTypeExportReqVO; | ||||
| import cn.iocoder.dashboard.modules.system.controller.dict.vo.type.SysDictTypePageReqVO; | ||||
| import cn.iocoder.dashboard.modules.system.controller.dict.vo.type.SysDictTypeUpdateReqVO; | ||||
| import cn.iocoder.dashboard.modules.system.convert.dict.SysDictTypeConvert; | ||||
| import cn.iocoder.dashboard.modules.system.dal.mysql.dict.SysDictTypeMapper; | ||||
| 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; | ||||
| import java.util.List; | ||||
|  | ||||
| import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception; | ||||
| import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; | ||||
|  | ||||
| /** | ||||
| @@ -33,12 +34,12 @@ public class SysDictTypeServiceImpl implements SysDictTypeService { | ||||
|     private SysDictTypeMapper dictTypeMapper; | ||||
|  | ||||
|     @Override | ||||
|     public PageResult<SysDictTypeDO> pageDictTypes(SysDictTypePageReqVO reqVO) { | ||||
|     public PageResult<SysDictTypeDO> getDictTypePage(SysDictTypePageReqVO reqVO) { | ||||
|         return dictTypeMapper.selectPage(reqVO); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public List<SysDictTypeDO> listDictTypes(SysDictTypeExportReqVO reqVO) { | ||||
|     public List<SysDictTypeDO> getDictTypeList(SysDictTypeExportReqVO reqVO) { | ||||
|         return dictTypeMapper.selectList(reqVO); | ||||
|     } | ||||
|  | ||||
| @@ -77,14 +78,14 @@ public class SysDictTypeServiceImpl implements SysDictTypeService { | ||||
|         SysDictTypeDO dictType = this.checkDictTypeExists(id); | ||||
|         // 校验是否有字典数据 | ||||
|         if (dictDataService.countByDictType(dictType.getType()) > 0) { | ||||
|             throw ServiceExceptionUtil.exception(DICT_TYPE_HAS_CHILDREN); | ||||
|             throw exception(DICT_TYPE_HAS_CHILDREN); | ||||
|         } | ||||
|         // 删除字典类型 | ||||
|         dictTypeMapper.deleteById(id); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public List<SysDictTypeDO> listDictTypes() { | ||||
|     public List<SysDictTypeDO> getDictTypeList() { | ||||
|         return dictTypeMapper.selectList(); | ||||
|     } | ||||
|  | ||||
| @@ -97,41 +98,44 @@ public class SysDictTypeServiceImpl implements SysDictTypeService { | ||||
|         checkDictTypeUnique(id, type); | ||||
|     } | ||||
|  | ||||
|     private void checkDictTypeNameUnique(Long id, String type) { | ||||
|         SysDictTypeDO dictType = dictTypeMapper.selectByName(type); | ||||
|     @VisibleForTesting | ||||
|     public void checkDictTypeNameUnique(Long id, String name) { | ||||
|         SysDictTypeDO dictType = dictTypeMapper.selectByName(name); | ||||
|         if (dictType == null) { | ||||
|             return; | ||||
|         } | ||||
|         // 如果 id 为空,说明不用比较是否为相同 id 的字典类型 | ||||
|         if (id == null) { | ||||
|             throw ServiceExceptionUtil.exception(DICT_TYPE_NAME_DUPLICATE); | ||||
|             throw exception(DICT_TYPE_NAME_DUPLICATE); | ||||
|         } | ||||
|         if (!dictType.getId().equals(id)) { | ||||
|             throw ServiceExceptionUtil.exception(DICT_TYPE_NAME_DUPLICATE); | ||||
|             throw exception(DICT_TYPE_NAME_DUPLICATE); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void checkDictTypeUnique(Long id, String type) { | ||||
|     @VisibleForTesting | ||||
|     public void checkDictTypeUnique(Long id, String type) { | ||||
|         SysDictTypeDO dictType = dictTypeMapper.selectByType(type); | ||||
|         if (dictType == null) { | ||||
|             return; | ||||
|         } | ||||
|         // 如果 id 为空,说明不用比较是否为相同 id 的字典类型 | ||||
|         if (id == null) { | ||||
|             throw ServiceExceptionUtil.exception(DICT_TYPE_TYPE_DUPLICATE); | ||||
|             throw exception(DICT_TYPE_TYPE_DUPLICATE); | ||||
|         } | ||||
|         if (!dictType.getId().equals(id)) { | ||||
|             throw ServiceExceptionUtil.exception(DICT_TYPE_TYPE_DUPLICATE); | ||||
|             throw exception(DICT_TYPE_TYPE_DUPLICATE); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private SysDictTypeDO checkDictTypeExists(Long id) { | ||||
|     @VisibleForTesting | ||||
|     public SysDictTypeDO checkDictTypeExists(Long id) { | ||||
|         if (id == null) { | ||||
|             return null; | ||||
|         } | ||||
|         SysDictTypeDO dictType = dictTypeMapper.selectById(id); | ||||
|         if (dictType == null) { | ||||
|             throw ServiceExceptionUtil.exception(DICT_TYPE_NOT_FOUND); | ||||
|             throw exception(DICT_TYPE_NOT_EXISTS); | ||||
|         } | ||||
|         return dictType; | ||||
|     } | ||||
|   | ||||
| @@ -53,9 +53,7 @@ public class ToolCodegenEngine { | ||||
|      * value:生成的路径 | ||||
|      */ | ||||
|     private static final Map<String, String> TEMPLATES = MapUtil.<String, String>builder(new LinkedHashMap<>()) // 有序 | ||||
|             // Java | ||||
|             .put(javaTemplatePath("controller/controller"), | ||||
|                     javaFilePath("controller/${table.businessName}/${table.className}Controller")) | ||||
|             // Java Main | ||||
|             .put(javaTemplatePath("controller/vo/baseVO"), | ||||
|                     javaFilePath("controller/${table.businessName}/vo/${table.className}BaseVO")) | ||||
|             .put(javaTemplatePath("controller/vo/createReqVO"), | ||||
| @@ -70,6 +68,8 @@ public class ToolCodegenEngine { | ||||
|                     javaFilePath("controller/${table.businessName}/vo/${table.className}ExportReqVO")) | ||||
|             .put(javaTemplatePath("controller/vo/excelVO"), | ||||
|                     javaFilePath("controller/${table.businessName}/vo/${table.className}ExcelVO")) | ||||
|             .put(javaTemplatePath("controller/controller"), | ||||
|                     javaFilePath("controller/${table.businessName}/${table.className}Controller")) | ||||
|             .put(javaTemplatePath("convert/convert"), | ||||
|                     javaFilePath("convert/${table.businessName}/${table.className}Convert")) | ||||
|             .put(javaTemplatePath("dal/do"), | ||||
| @@ -78,10 +78,13 @@ public class ToolCodegenEngine { | ||||
|                     javaFilePath("dal/mysql/${table.businessName}/${table.className}Mapper")) | ||||
|             .put(javaTemplatePath("enums/errorcode"), | ||||
|                     javaFilePath("enums/${simpleModuleName_upperFirst}ErrorCodeConstants")) | ||||
|             .put(javaTemplatePath("service/service"), | ||||
|                     javaFilePath("service/${table.businessName}/${table.className}Service")) | ||||
|             .put(javaTemplatePath("service/serviceImpl"), | ||||
|                     javaFilePath("service/${table.businessName}/impl/${table.className}ServiceImpl")) | ||||
|             .put(javaTemplatePath("service/service"), | ||||
|                     javaFilePath("service/${table.businessName}/${table.className}Service")) | ||||
|             // Java Test | ||||
|             .put(javaTemplatePath("test/serviceTest"), | ||||
|                     javaFilePath("service/${table.businessName}/${table.className}ServiceTest")) | ||||
|             // Vue | ||||
|             .put(vueTemplatePath("views/index.vue"), | ||||
|                     vueFilePath("views/${table.moduleName}/${classNameVar}/index.vue")) | ||||
|   | ||||
| @@ -0,0 +1,33 @@ | ||||
| package cn.iocoder.dashboard.util.collection; | ||||
|  | ||||
| import cn.hutool.core.util.ArrayUtil; | ||||
|  | ||||
| import java.util.function.Consumer; | ||||
|  | ||||
| /** | ||||
|  * Array 工具类 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| public class ArrayUtils { | ||||
|  | ||||
|     /** | ||||
|      * 将 object 和 newElements 合并成一个数组 | ||||
|      * | ||||
|      * @param object 对象 | ||||
|      * @param newElements 数组 | ||||
|      * @param <T> 泛型 | ||||
|      * @return 结果数组 | ||||
|      */ | ||||
|     @SafeVarargs | ||||
|     public static <T> Consumer<T>[] append(Consumer<T> object, Consumer<T>... newElements) { | ||||
|         if (object == null) { | ||||
|             return newElements; | ||||
|         } | ||||
|         Consumer<T>[] result = ArrayUtil.newArray(Consumer.class, 1 + newElements.length); | ||||
|         result[0] = object; | ||||
|         System.arraycopy(newElements, 0, result, 1, newElements.length); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,6 +1,7 @@ | ||||
| package cn.iocoder.dashboard.util.date; | ||||
|  | ||||
| import java.time.Duration; | ||||
| import java.util.Calendar; | ||||
| import java.util.Date; | ||||
|  | ||||
| /** | ||||
| @@ -22,4 +23,40 @@ public class DateUtils { | ||||
|         return endTime.getTime() - startTime.getTime(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 创建指定时间 | ||||
|      * | ||||
|      * @param year        年 | ||||
|      * @param mouth       月 | ||||
|      * @param day         日 | ||||
|      * @return 指定时间 | ||||
|      */ | ||||
|     public static Date buildTime(int year, int mouth, int day) { | ||||
|         return buildTime(year, mouth, day, 0, 0, 0); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 创建指定时间 | ||||
|      * | ||||
|      * @param year        年 | ||||
|      * @param mouth       月 | ||||
|      * @param day         日 | ||||
|      * @param hour        小时 | ||||
|      * @param minute      分钟 | ||||
|      * @param second      秒 | ||||
|      * @return 指定时间 | ||||
|      */ | ||||
|     public static Date buildTime(int year, int mouth, int day, | ||||
|                                  int hour, int minute, int second) { | ||||
|         Calendar calendar = Calendar.getInstance(); | ||||
|         calendar.set(Calendar.YEAR, year); | ||||
|         calendar.set(Calendar.MONTH, mouth - 1); | ||||
|         calendar.set(Calendar.DAY_OF_MONTH, day); | ||||
|         calendar.set(Calendar.HOUR_OF_DAY, hour); | ||||
|         calendar.set(Calendar.MINUTE, minute); | ||||
|         calendar.set(Calendar.SECOND, second); | ||||
|         calendar.set(Calendar.MILLISECOND, 0); // 一般情况下,都是 0 毫秒 | ||||
|         return calendar.getTime(); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,32 @@ | ||||
| package cn.iocoder.dashboard.util.object; | ||||
|  | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
|  | ||||
| import java.util.function.Consumer; | ||||
|  | ||||
| /** | ||||
|  * Object 工具类 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| public class ObjectUtils { | ||||
|  | ||||
|     public static <T> T clone(T object, Consumer<T> consumer) { | ||||
|         T result = ObjectUtil.clone(object); | ||||
|         if (result != null) { | ||||
|             consumer.accept(result); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     public static <T extends Comparable<T>> T max(T obj1, T obj2) { | ||||
|         if (obj1 == null) { | ||||
|             return obj2; | ||||
|         } | ||||
|         if (obj2 == null) { | ||||
|             return obj1; | ||||
|         } | ||||
|         return obj1.compareTo(obj2) > 0 ? obj1 : obj2; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -154,7 +154,7 @@ yudao: | ||||
|   file: | ||||
|     base-path: http://127.0.0.1:${server.port}/${yudao.web.api-prefix}/file/get/ | ||||
|   codegen: | ||||
|     base-package: ${yudao.info.base-package}.modules | ||||
|     base-package: ${yudao.info.base-package} | ||||
|     db-schemas: ${spring.datasource.name} | ||||
|   xss: | ||||
|     enable: false | ||||
|   | ||||
| @@ -154,7 +154,7 @@ yudao: | ||||
|   file: | ||||
|     base-path: http://127.0.0.1:${server.port}/${yudao.web.api-prefix}/file/get/ | ||||
|   codegen: | ||||
|     base-package: ${yudao.info.base-package}.modules | ||||
|     base-package: ${yudao.info.base-package} | ||||
|     db-schemas: ${spring.datasource.name} | ||||
|   xss: | ||||
|     enable: false | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| package ${basePackage}.${table.moduleName}.controller.${table.businessName}; | ||||
| package ${basePackage}.modules.${table.moduleName}.controller.${table.businessName}; | ||||
|  | ||||
| import org.springframework.web.bind.annotation.*; | ||||
| import javax.annotation.Resource; | ||||
| @@ -22,10 +22,10 @@ import ${ExcelUtilsClassName}; | ||||
| import ${OperateLogClassName}; | ||||
| import static ${OperateTypeEnumClassName}.*; | ||||
|  | ||||
| import ${basePackage}.${table.moduleName}.controller.${table.businessName}.vo.*; | ||||
| import ${basePackage}.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO; | ||||
| import ${basePackage}.${table.moduleName}.convert.${table.businessName}.${table.className}Convert; | ||||
| import ${basePackage}.${table.moduleName}.service.${table.businessName}.${table.className}Service; | ||||
| import ${basePackage}.modules.${table.moduleName}.controller.${table.businessName}.vo.*; | ||||
| import ${basePackage}.modules.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO; | ||||
| import ${basePackage}.modules.${table.moduleName}.convert.${table.businessName}.${table.className}Convert; | ||||
| import ${basePackage}.modules.${table.moduleName}.service.${table.businessName}.${table.className}Service; | ||||
|  | ||||
| @Api(tags = "${table.classComment}") | ||||
| @RestController | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| package ${basePackage}.${table.moduleName}.controller.${table.businessName}.vo; | ||||
| package ${basePackage}.modules.${table.moduleName}.controller.${table.businessName}.vo; | ||||
|  | ||||
| import lombok.*; | ||||
| import java.util.*; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| package ${basePackage}.${table.moduleName}.controller.${table.businessName}.vo; | ||||
| package ${basePackage}.modules.${table.moduleName}.controller.${table.businessName}.vo; | ||||
|  | ||||
| import lombok.*; | ||||
| import java.util.*; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| package ${basePackage}.${table.moduleName}.controller.${table.businessName}.vo; | ||||
| package ${basePackage}.modules.${table.moduleName}.controller.${table.businessName}.vo; | ||||
|  | ||||
| import lombok.*; | ||||
| import java.util.*; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| package ${basePackage}.${table.moduleName}.controller.${table.businessName}.vo; | ||||
| package ${basePackage}.modules.${table.moduleName}.controller.${table.businessName}.vo; | ||||
|  | ||||
| import lombok.*; | ||||
| import java.util.*; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| package ${basePackage}.${table.moduleName}.controller.${table.businessName}.vo; | ||||
| package ${basePackage}.modules.${table.moduleName}.controller.${table.businessName}.vo; | ||||
|  | ||||
| import lombok.*; | ||||
| import java.util.*; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| package ${basePackage}.${table.moduleName}.controller.${table.businessName}.vo; | ||||
| package ${basePackage}.modules.${table.moduleName}.controller.${table.businessName}.vo; | ||||
|  | ||||
| import lombok.*; | ||||
| import java.util.*; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| package ${basePackage}.${table.moduleName}.controller.${table.businessName}.vo; | ||||
| package ${basePackage}.modules.${table.moduleName}.controller.${table.businessName}.vo; | ||||
|  | ||||
| import lombok.*; | ||||
| import java.util.*; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| package ${basePackage}.${table.moduleName}.convert.${table.businessName}; | ||||
| package ${basePackage}.modules.${table.moduleName}.convert.${table.businessName}; | ||||
|  | ||||
| import java.util.*; | ||||
|  | ||||
| @@ -6,8 +6,8 @@ import ${PageResultClassName}; | ||||
|  | ||||
| import org.mapstruct.Mapper; | ||||
| import org.mapstruct.factory.Mappers; | ||||
| import ${basePackage}.${table.moduleName}.controller.${table.businessName}.vo.*; | ||||
| import ${basePackage}.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO; | ||||
| import ${basePackage}.modules.${table.moduleName}.controller.${table.businessName}.vo.*; | ||||
| import ${basePackage}.modules.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO; | ||||
|  | ||||
| /** | ||||
|  * ${table.classComment} Convert | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| package ${basePackage}.${table.moduleName}.dal.dataobject.${table.businessName}; | ||||
| package ${basePackage}.modules.${table.moduleName}.dal.dataobject.${table.businessName}; | ||||
|  | ||||
| import lombok.*; | ||||
| import java.util.*; | ||||
|   | ||||
| @@ -1,13 +1,13 @@ | ||||
| package ${basePackage}.${table.moduleName}.dal.mysql.${table.businessName}; | ||||
| package ${basePackage}.modules.${table.moduleName}.dal.mysql.${table.businessName}; | ||||
|  | ||||
| import java.util.*; | ||||
|  | ||||
| import ${PageResultClassName}; | ||||
| import ${QueryWrapperClassName}; | ||||
| import ${BaseMapperClassName}; | ||||
| import ${basePackage}.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO; | ||||
| import ${basePackage}.modules.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO; | ||||
| import org.apache.ibatis.annotations.Mapper; | ||||
| import ${basePackage}.${table.moduleName}.controller.${table.businessName}.vo.*; | ||||
| import ${basePackage}.modules.${table.moduleName}.controller.${table.businessName}.vo.*; | ||||
|  | ||||
| ## 字段模板 | ||||
| #macro(listCondition) | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| package ${basePackage}.${table.moduleName}.service.${table.businessName}; | ||||
| package ${basePackage}.modules.${table.moduleName}.service.${table.businessName}; | ||||
|  | ||||
| import java.util.*; | ||||
| import javax.validation.*; | ||||
| import ${basePackage}.${table.moduleName}.controller.${table.businessName}.vo.*; | ||||
| import ${basePackage}.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO; | ||||
| import ${basePackage}.modules.${table.moduleName}.controller.${table.businessName}.vo.*; | ||||
| import ${basePackage}.modules.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO; | ||||
| import ${PageResultClassName}; | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| package ${basePackage}.${table.moduleName}.service.${table.businessName}.impl; | ||||
| package ${basePackage}.modules.${table.moduleName}.service.${table.businessName}.impl; | ||||
|  | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Service; | ||||
| @@ -6,17 +6,17 @@ import javax.annotation.Resource; | ||||
| import org.springframework.validation.annotation.Validated; | ||||
|  | ||||
| import java.util.*; | ||||
| import ${basePackage}.${table.moduleName}.controller.${table.businessName}.vo.*; | ||||
| import ${basePackage}.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO; | ||||
| import ${basePackage}.modules.${table.moduleName}.controller.${table.businessName}.vo.*; | ||||
| import ${basePackage}.modules.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO; | ||||
| import ${PageResultClassName}; | ||||
|  | ||||
| import ${basePackage}.${table.moduleName}.convert.${table.businessName}.${table.className}Convert; | ||||
| import ${basePackage}.${table.moduleName}.dal.mysql.${table.businessName}.${table.className}Mapper; | ||||
| import ${basePackage}.${table.moduleName}.service.${table.businessName}.${table.className}Service; | ||||
| import ${basePackage}.modules.${table.moduleName}.convert.${table.businessName}.${table.className}Convert; | ||||
| import ${basePackage}.modules.${table.moduleName}.dal.mysql.${table.businessName}.${table.className}Mapper; | ||||
| import ${basePackage}.modules.${table.moduleName}.service.${table.businessName}.${table.className}Service; | ||||
|  | ||||
| import ${ServiceExceptionUtilClassName}; | ||||
|  | ||||
| import static ${basePackage}.${table.moduleName}.enums.${simpleModuleName_upperFirst}ErrorCodeConstants.*; | ||||
| import static ${basePackage}.modules.${table.moduleName}.enums.${simpleModuleName_upperFirst}ErrorCodeConstants.*; | ||||
|  | ||||
| /** | ||||
|  * ${table.classComment} Service 实现类 | ||||
|   | ||||
							
								
								
									
										161
									
								
								src/main/resources/codegen/java/test/serviceTest.vm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								src/main/resources/codegen/java/test/serviceTest.vm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,161 @@ | ||||
| package ${basePackage}.modules.${table.moduleName}.service.${table.businessName}; | ||||
|  | ||||
| import ${basePackage}.BaseSpringBootUnitTest; | ||||
|  | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.springframework.boot.test.mock.mockito.MockBean; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
|  | ||||
| import cn.iocoder.dashboard.BaseSpringBootUnitTest; | ||||
| import ${basePackage}.modules.${table.moduleName}.service.${table.businessName}.impl.${table.className}ServiceImpl; | ||||
| import ${basePackage}.modules.${table.moduleName}.controller.${table.businessName}.vo.*; | ||||
| import ${basePackage}.modules.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO; | ||||
| import ${basePackage}.modules.${table.moduleName}.dal.mysql.${table.businessName}.${table.className}Mapper; | ||||
| import ${basePackage}.util.object.ObjectUtils; | ||||
| import ${PageResultClassName}; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.util.*; | ||||
|  | ||||
| import static cn.hutool.core.util.RandomUtil.*; | ||||
| import static ${basePackage}.modules.${table.moduleName}.enums.${simpleModuleName_upperFirst}ErrorCodeConstants.*; | ||||
| import static ${basePackage}.util.AssertUtils.*; | ||||
| import static ${basePackage}.util.RandomUtils.*; | ||||
| import static ${basePackage}.util.date.DateUtils.*; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.Mockito.*; | ||||
|  | ||||
| ## 字段模板 | ||||
| #macro(getPageCondition $VO) | ||||
|        // mock 数据 | ||||
|        ${table.className}DO db${simpleClassName} = randomPojo(${table.className}DO.class, o -> { // 等会查询到 | ||||
|        #foreach ($column in $columns) | ||||
|        #if (${column.listOperation}) | ||||
|        #set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字母大写 | ||||
|            o.set$JavaField(null); | ||||
|        #end | ||||
|        #end | ||||
|        }); | ||||
|        ${classNameVar}Mapper.insert(db${simpleClassName}); | ||||
|        #foreach ($column in $columns) | ||||
|        #if (${column.listOperation}) | ||||
|        #set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字母大写 | ||||
|        // 测试 ${column.javaField} 不匹配 | ||||
|        ${classNameVar}Mapper.insert(ObjectUtils.clone(db${simpleClassName}, o -> o.set$JavaField(null))); | ||||
|        #end | ||||
|        #end | ||||
|        // 准备参数 | ||||
|        ${table.className}${VO} reqVO = new ${table.className}${VO}(); | ||||
|        #foreach ($column in $columns) | ||||
|        #if (${column.listOperation}) | ||||
|        #set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字母大写 | ||||
|        #if (${column.listOperationCondition} == "BETWEEN")## BETWEEN 的情况 | ||||
|        reqVO.setBegin${JavaField}(null); | ||||
|        reqVO.setEnd${JavaField}(null); | ||||
|        #else | ||||
|        reqVO.set$JavaField(null); | ||||
|        #end | ||||
|        #end | ||||
|        #end | ||||
| #end | ||||
| /** | ||||
| * {@link ${table.className}ServiceImpl} 的单元测试类 | ||||
| * | ||||
| * @author ${table.author} | ||||
| */ | ||||
| public class ${table.className}ServiceTest extends BaseSpringBootUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private ${table.className}ServiceImpl ${classNameVar}Service; | ||||
|  | ||||
|     @Resource | ||||
|     private ${table.className}Mapper ${classNameVar}Mapper; | ||||
|  | ||||
|     @Test | ||||
|     public void testCreate${simpleClassName}_success() { | ||||
|         // 准备参数 | ||||
|         ${table.className}CreateReqVO reqVO = randomPojo(${table.className}CreateReqVO.class); | ||||
|  | ||||
|         // 调用 | ||||
|         Long ${classNameVar}Id = ${classNameVar}Service.create${simpleClassName}(reqVO); | ||||
|         // 断言 | ||||
|         assertNotNull(${classNameVar}Id); | ||||
|         // 校验记录的属性是否正确 | ||||
|         ${table.className}DO ${classNameVar} = ${classNameVar}Mapper.selectById(${classNameVar}Id); | ||||
|         assertPojoEquals(reqVO, ${classNameVar}); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdate${simpleClassName}_success() { | ||||
|         // mock 数据 | ||||
|         ${table.className}DO db${simpleClassName} = randomPojo(${table.className}DO.class); | ||||
|         ${classNameVar}Mapper.insert(db${simpleClassName});// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         ${table.className}UpdateReqVO reqVO = randomPojo(${table.className}UpdateReqVO.class, o -> { | ||||
|             o.setId(db${simpleClassName}.getId()); // 设置更新的 ID | ||||
|         }); | ||||
|  | ||||
|         // 调用 | ||||
|         ${classNameVar}Service.update${simpleClassName}(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         ${table.className}DO ${classNameVar} = ${classNameVar}Mapper.selectById(reqVO.getId()); // 获取最新的 | ||||
|         assertPojoEquals(reqVO, ${classNameVar}); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdate${simpleClassName}_notExists() { | ||||
|         // 准备参数 | ||||
|         ${table.className}UpdateReqVO reqVO = randomPojo(${table.className}UpdateReqVO.class); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> ${classNameVar}Service.update${simpleClassName}(reqVO), ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDelete${simpleClassName}_success() { | ||||
|         // mock 数据 | ||||
|         ${table.className}DO db${simpleClassName} = randomPojo(${table.className}DO.class); | ||||
|         ${classNameVar}Mapper.insert(db${simpleClassName});// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = db${simpleClassName}.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         ${classNameVar}Service.delete${simpleClassName}(id); | ||||
|        // 校验数据不存在了 | ||||
|        assertNull(${classNameVar}Mapper.selectById(id)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDelete${simpleClassName}_notExists() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> ${classNameVar}Service.delete${simpleClassName}(id), ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @Test // TODO 请修改 null 为需要的值 | ||||
|     public void testGet${simpleClassName}Page() { | ||||
|        #getPageCondition("PageReqVO") | ||||
|  | ||||
|        // 调用 | ||||
|        PageResult<${table.className}DO> pageResult = ${classNameVar}Service.get${simpleClassName}Page(reqVO); | ||||
|        // 断言 | ||||
|        assertEquals(1, pageResult.getTotal()); | ||||
|        assertEquals(1, pageResult.getList().size()); | ||||
|        assertPojoEquals(db${simpleClassName}, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test // TODO 请修改 null 为需要的值 | ||||
|     public void testGet${simpleClassName}List() { | ||||
|        #getPageCondition("ExportReqVO") | ||||
|  | ||||
|        // 调用 | ||||
|        List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(reqVO); | ||||
|        // 断言 | ||||
|        assertEquals(1, list.size()); | ||||
|        assertPojoEquals(db${simpleClassName}, list.get(0)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,103 +0,0 @@ | ||||
| package cn.iocoder.dashboard.modules.infra.service.config; | ||||
|  | ||||
| import cn.iocoder.dashboard.BaseSpringBootUnitTest; | ||||
| import cn.iocoder.dashboard.common.exception.ServiceException; | ||||
| import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigCreateReqVO; | ||||
| import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigUpdateReqVO; | ||||
| import cn.iocoder.dashboard.modules.infra.dal.dataobject.config.InfConfigDO; | ||||
| import cn.iocoder.dashboard.modules.infra.dal.mysql.config.InfConfigMapper; | ||||
| 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.impl.InfConfigServiceImpl; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.springframework.boot.test.mock.mockito.MockBean; | ||||
|  | ||||
| import javax.annotation.Resource; | ||||
| import java.util.function.Consumer; | ||||
|  | ||||
| import static cn.hutool.core.util.RandomUtil.randomEle; | ||||
| import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.CONFIG_KEY_DUPLICATE; | ||||
| import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals; | ||||
| import static cn.iocoder.dashboard.util.RandomUtils.randomPojo; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.Mockito.times; | ||||
| import static org.mockito.Mockito.verify; | ||||
|  | ||||
| public class InfConfigServiceImplTest extends BaseSpringBootUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private InfConfigServiceImpl configService; | ||||
|  | ||||
|     @Resource | ||||
|     private InfConfigMapper configMapper; | ||||
|  | ||||
|     @MockBean | ||||
|     private InfConfigProducer configProducer; | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateConfig_success() { | ||||
|         // 准备参数 | ||||
|         InfConfigCreateReqVO reqVO = randomInfConfigCreateReqVO(); | ||||
|         // mock | ||||
|  | ||||
|         // 调用 | ||||
|         Long configId = configService.createConfig(reqVO); | ||||
|         // 断言 | ||||
|         assertNotNull(configId); | ||||
|         // 校验记录的属性是否正确 | ||||
|         InfConfigDO config = configMapper.selectById(configId); | ||||
|         assertPojoEquals(reqVO, config); | ||||
|         assertEquals(InfConfigTypeEnum.CUSTOM.getType(), config.getType()); | ||||
|         // 校验调用 | ||||
|         verify(configProducer, times(1)).sendConfigRefreshMessage(); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateConfig_keyDuplicate() { | ||||
|         // 准备参数 | ||||
|         InfConfigCreateReqVO reqVO = randomInfConfigCreateReqVO(); | ||||
|         // mock 数据 | ||||
|         configMapper.insert(randomInfConfigDO(o -> { // @Sql | ||||
|             o.setKey(reqVO.getKey()); // 模拟 key 重复 | ||||
|         })); | ||||
|  | ||||
|         // 调用 | ||||
|         ServiceException serviceException = assertThrows(ServiceException.class, () -> configService.createConfig(reqVO)); | ||||
|         // 断言 | ||||
|         assertPojoEquals(CONFIG_KEY_DUPLICATE, serviceException); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateConfig_success() { | ||||
|         // 准备参数 | ||||
|         InfConfigUpdateReqVO reqVO = randomInfConfigUpdateReqVO(); | ||||
|         // mock 数据 | ||||
|         configMapper.insert(randomInfConfigDO(o -> o.setId(reqVO.getId())));// @Sql: 先插入出一条存在的数据 | ||||
|  | ||||
|         // 调用 | ||||
|         configService.updateConfig(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         InfConfigDO config = configMapper.selectById(reqVO.getId()); // 获取最新的 | ||||
|         assertPojoEquals(reqVO, config); | ||||
|         // 校验调用 | ||||
|         verify(configProducer, times(1)).sendConfigRefreshMessage(); | ||||
|     } | ||||
|  | ||||
|     // ========== 随机对象 ========== | ||||
|  | ||||
|     @SafeVarargs | ||||
|     private static InfConfigDO randomInfConfigDO(Consumer<InfConfigDO>... consumers) { | ||||
|         InfConfigDO config = randomPojo(InfConfigDO.class, consumers); | ||||
|         config.setType(randomEle(InfConfigTypeEnum.values()).getType()); | ||||
|         return config; | ||||
|     } | ||||
|  | ||||
|     private static InfConfigCreateReqVO randomInfConfigCreateReqVO() { | ||||
|         return randomPojo(InfConfigCreateReqVO.class); | ||||
|     } | ||||
|  | ||||
|     private static InfConfigUpdateReqVO randomInfConfigUpdateReqVO() { | ||||
|         return randomPojo(InfConfigUpdateReqVO.class); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,254 @@ | ||||
| package cn.iocoder.dashboard.modules.infra.service.config; | ||||
|  | ||||
| import cn.iocoder.dashboard.BaseSpringBootUnitTest; | ||||
| 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; | ||||
| import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigPageReqVO; | ||||
| import cn.iocoder.dashboard.modules.infra.controller.config.vo.InfConfigUpdateReqVO; | ||||
| import cn.iocoder.dashboard.modules.infra.dal.dataobject.config.InfConfigDO; | ||||
| import cn.iocoder.dashboard.modules.infra.dal.mysql.config.InfConfigMapper; | ||||
| 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.impl.InfConfigServiceImpl; | ||||
| 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.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.*; | ||||
| import static cn.iocoder.dashboard.util.date.DateUtils.buildTime; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.Mockito.times; | ||||
| import static org.mockito.Mockito.verify; | ||||
|  | ||||
| /** | ||||
|  * {@link InfConfigServiceImpl} 的单元测试类 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| public class InfConfigServiceTest extends BaseSpringBootUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private InfConfigServiceImpl configService; | ||||
|  | ||||
|     @Resource | ||||
|     private InfConfigMapper configMapper; | ||||
|     @MockBean | ||||
|     private InfConfigProducer configProducer; | ||||
|  | ||||
|     @Test | ||||
|     public void testGetConfigPage() { | ||||
|         // mock 数据 | ||||
|         InfConfigDO dbConfig = randomInfConfigDO(o -> { // 等会查询到 | ||||
|             o.setName("芋艿"); | ||||
|             o.setKey("yunai"); | ||||
|             o.setType(InfConfigTypeEnum.SYSTEM.getType()); | ||||
|             o.setCreateTime(buildTime(2021, 2, 1)); | ||||
|         }); | ||||
|         configMapper.insert(dbConfig); | ||||
|         // 测试 name 不匹配 | ||||
|         configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setName("土豆"))); | ||||
|         // 测试 key 不匹配 | ||||
|         configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setKey("tudou"))); | ||||
|         // 测试 type 不匹配 | ||||
|         configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setType(InfConfigTypeEnum.CUSTOM.getType()))); | ||||
|         // 测试 createTime 不匹配 | ||||
|         configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setCreateTime(buildTime(2021, 1, 1)))); | ||||
|         // 准备参数 | ||||
|         InfConfigPageReqVO reqVO = new InfConfigPageReqVO(); | ||||
|         reqVO.setName("艿"); | ||||
|         reqVO.setKey("nai"); | ||||
|         reqVO.setType(InfConfigTypeEnum.SYSTEM.getType()); | ||||
|         reqVO.setBeginTime(buildTime(2021, 1, 15)); | ||||
|         reqVO.setEndTime(buildTime(2021, 2, 15)); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<InfConfigDO> pageResult = configService.getConfigPage(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(dbConfig, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetConfigList() { | ||||
|         // mock 数据 | ||||
|         InfConfigDO dbConfig = randomInfConfigDO(o -> { // 等会查询到 | ||||
|             o.setName("芋艿"); | ||||
|             o.setKey("yunai"); | ||||
|             o.setType(InfConfigTypeEnum.SYSTEM.getType()); | ||||
|             o.setCreateTime(buildTime(2021, 2, 1)); | ||||
|         }); | ||||
|         configMapper.insert(dbConfig); | ||||
|         // 测试 name 不匹配 | ||||
|         configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setName("土豆"))); | ||||
|         // 测试 key 不匹配 | ||||
|         configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setKey("tudou"))); | ||||
|         // 测试 type 不匹配 | ||||
|         configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setType(InfConfigTypeEnum.CUSTOM.getType()))); | ||||
|         // 测试 createTime 不匹配 | ||||
|         configMapper.insert(ObjectUtils.clone(dbConfig, o -> o.setCreateTime(buildTime(2021, 1, 1)))); | ||||
|         // 准备参数 | ||||
|         InfConfigExportReqVO reqVO = new InfConfigExportReqVO(); | ||||
|         reqVO.setName("艿"); | ||||
|         reqVO.setKey("nai"); | ||||
|         reqVO.setType(InfConfigTypeEnum.SYSTEM.getType()); | ||||
|         reqVO.setBeginTime(buildTime(2021, 1, 15)); | ||||
|         reqVO.setEndTime(buildTime(2021, 2, 15)); | ||||
|  | ||||
|         // 调用 | ||||
|         List<InfConfigDO> list = configService.getConfigList(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, list.size()); | ||||
|         assertPojoEquals(dbConfig, list.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetConfigByKey() { | ||||
|         // mock 数据 | ||||
|         InfConfigDO dbConfig = randomInfConfigDO(); | ||||
|         configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         String key = dbConfig.getKey(); | ||||
|  | ||||
|         // 调用 | ||||
|         InfConfigDO config = configService.getConfigByKey(key); | ||||
|         // 断言 | ||||
|         assertNotNull(config); | ||||
|         assertPojoEquals(dbConfig, config); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateConfig_success() { | ||||
|         // 准备参数 | ||||
|         InfConfigCreateReqVO reqVO = randomPojo(InfConfigCreateReqVO.class); | ||||
|  | ||||
|         // 调用 | ||||
|         Long configId = configService.createConfig(reqVO); | ||||
|         // 断言 | ||||
|         assertNotNull(configId); | ||||
|         // 校验记录的属性是否正确 | ||||
|         InfConfigDO config = configMapper.selectById(configId); | ||||
|         assertPojoEquals(reqVO, config); | ||||
|         assertEquals(InfConfigTypeEnum.CUSTOM.getType(), config.getType()); | ||||
|         // 校验调用 | ||||
|         verify(configProducer, times(1)).sendConfigRefreshMessage(); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateConfig_success() { | ||||
|         // mock 数据 | ||||
|         InfConfigDO dbConfig = randomInfConfigDO(); | ||||
|         configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         InfConfigUpdateReqVO reqVO = randomPojo(InfConfigUpdateReqVO.class, o -> { | ||||
|             o.setId(dbConfig.getId()); // 设置更新的 ID | ||||
|         }); | ||||
|  | ||||
|         // 调用 | ||||
|         configService.updateConfig(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         InfConfigDO config = configMapper.selectById(reqVO.getId()); // 获取最新的 | ||||
|         assertPojoEquals(reqVO, config); | ||||
|         // 校验调用 | ||||
|         verify(configProducer, times(1)).sendConfigRefreshMessage(); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteConfig_success() { | ||||
|         // mock 数据 | ||||
|         InfConfigDO dbConfig = randomInfConfigDO(o -> { | ||||
|             o.setType(InfConfigTypeEnum.CUSTOM.getType()); // 只能删除 CUSTOM 类型 | ||||
|         }); | ||||
|         configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbConfig.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         configService.deleteConfig(id); | ||||
|         // 校验数据不存在了 | ||||
|         assertNull(configMapper.selectById(id)); | ||||
|         // 校验调用 | ||||
|         verify(configProducer, times(1)).sendConfigRefreshMessage(); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteConfig_canNotDeleteSystemType() { | ||||
|         // mock 数据 | ||||
|         InfConfigDO dbConfig = randomInfConfigDO(o -> { | ||||
|             o.setType(InfConfigTypeEnum.SYSTEM.getType()); // SYSTEM 不允许删除 | ||||
|         }); | ||||
|         configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbConfig.getId(); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         assertServiceException(() -> configService.deleteConfig(id), CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     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.checkConfigKeyUnique(id, key), | ||||
|                 CONFIG_KEY_DUPLICATE); | ||||
|     } | ||||
|  | ||||
|     // ========== 随机对象 ========== | ||||
|  | ||||
|     @SafeVarargs | ||||
|     private static InfConfigDO randomInfConfigDO(Consumer<InfConfigDO>... consumers) { | ||||
|         Consumer<InfConfigDO> consumer = (o) -> { | ||||
|             o.setType(randomEle(InfConfigTypeEnum.values()).getType()); // 保证 key 的范围 | ||||
|         }; | ||||
|         return randomPojo(InfConfigDO.class, ArrayUtils.append(consumer, consumers)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -61,7 +61,7 @@ public class SysAuthServiceImplTest extends BaseSpringBootUnitTest { | ||||
|     @Test | ||||
|     public void testMockLogin_success() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLong(); | ||||
|         Long userId = randomLongId(); | ||||
|         // mock 方法 01 | ||||
|         SysUserDO user = randomUserDO(o -> o.setId(userId)); | ||||
|         when(userService.getUser(eq(userId))).thenReturn(user); | ||||
| @@ -80,7 +80,7 @@ public class SysAuthServiceImplTest extends BaseSpringBootUnitTest { | ||||
|     @Test | ||||
|     public void testMockLogin_userNotFound() { | ||||
|         // 准备参数 | ||||
|         Long userId = randomLong(); | ||||
|         Long userId = randomLongId(); | ||||
|         // mock 方法 | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|   | ||||
| @@ -0,0 +1,300 @@ | ||||
| package cn.iocoder.dashboard.modules.system.service.dict; | ||||
|  | ||||
| import cn.iocoder.dashboard.BaseSpringBootUnitTest; | ||||
| 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; | ||||
| import cn.iocoder.dashboard.modules.system.controller.dict.vo.data.SysDictDataExportReqVO; | ||||
| 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.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.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; | ||||
| import static cn.iocoder.dashboard.util.RandomUtils.*; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import static org.mockito.ArgumentMatchers.eq; | ||||
| import static org.mockito.Mockito.*; | ||||
|  | ||||
| /** | ||||
| * {@link SysDictDataServiceImpl} 的单元测试类 | ||||
| * | ||||
| * @author 芋道源码 | ||||
| */ | ||||
| public class SysDictDataServiceTest extends BaseSpringBootUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private SysDictDataServiceImpl dictDataService; | ||||
|  | ||||
|     @Resource | ||||
|     private SysDictDataMapper dictDataMapper; | ||||
|     @MockBean | ||||
|     private SysDictTypeService dictTypeService; | ||||
|     @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<String, String, SysDictDataDO> labelDictDataCache = | ||||
|                 (ImmutableTable<String, String, SysDictDataDO>) 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<String, String, SysDictDataDO> valueDictDataCache = | ||||
|                 (ImmutableTable<String, String, SysDictDataDO>) 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 数据 | ||||
|         SysDictDataDO dbDictData = randomPojo(SysDictDataDO.class, o -> { // 等会查询到 | ||||
|             o.setLabel("芋艿"); | ||||
|             o.setDictType("yunai"); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         }); | ||||
|         dictDataMapper.insert(dbDictData); | ||||
|         // 测试 label 不匹配 | ||||
|         dictDataMapper.insert(ObjectUtils.clone(dbDictData, o -> o.setLabel("艿"))); | ||||
|         // 测试 dictType 不匹配 | ||||
|         dictDataMapper.insert(ObjectUtils.clone(dbDictData, o -> o.setDictType("nai"))); | ||||
|         // 测试 status 不匹配 | ||||
|         dictDataMapper.insert(ObjectUtils.clone(dbDictData, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|         // 准备参数 | ||||
|         SysDictDataPageReqVO reqVO = new SysDictDataPageReqVO(); | ||||
|         reqVO.setLabel("芋"); | ||||
|         reqVO.setDictType("yu"); | ||||
|         reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|  | ||||
|         // 调用 | ||||
|         PageResult<SysDictDataDO> pageResult = dictDataService.getDictDataPage(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, pageResult.getTotal()); | ||||
|         assertEquals(1, pageResult.getList().size()); | ||||
|         assertPojoEquals(dbDictData, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDictDataList() { | ||||
|         // mock 数据 | ||||
|         SysDictDataDO dbDictData = randomPojo(SysDictDataDO.class, o -> { // 等会查询到 | ||||
|             o.setLabel("芋艿"); | ||||
|             o.setDictType("yunai"); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         }); | ||||
|         dictDataMapper.insert(dbDictData); | ||||
|         // 测试 label 不匹配 | ||||
|         dictDataMapper.insert(ObjectUtils.clone(dbDictData, o -> o.setLabel("艿"))); | ||||
|         // 测试 dictType 不匹配 | ||||
|         dictDataMapper.insert(ObjectUtils.clone(dbDictData, o -> o.setDictType("nai"))); | ||||
|         // 测试 status 不匹配 | ||||
|         dictDataMapper.insert(ObjectUtils.clone(dbDictData, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|         // 准备参数 | ||||
|         SysDictDataExportReqVO reqVO = new SysDictDataExportReqVO(); | ||||
|         reqVO.setLabel("芋"); | ||||
|         reqVO.setDictType("yu"); | ||||
|         reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|  | ||||
|         // 调用 | ||||
|         List<SysDictDataDO> list = dictDataService.getDictDataList(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, list.size()); | ||||
|         assertPojoEquals(dbDictData, list.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateDictData_success() { | ||||
|         // 准备参数 | ||||
|         SysDictDataCreateReqVO reqVO = randomPojo(SysDictDataCreateReqVO.class, | ||||
|                 o -> o.setStatus(randomCommonStatus())); | ||||
|         // mock 方法 | ||||
|         when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); | ||||
|  | ||||
|         // 调用 | ||||
|         Long dictDataId = dictDataService.createDictData(reqVO); | ||||
|         // 断言 | ||||
|         assertNotNull(dictDataId); | ||||
|         // 校验记录的属性是否正确 | ||||
|         SysDictDataDO dictData = dictDataMapper.selectById(dictDataId); | ||||
|         assertPojoEquals(reqVO, dictData); | ||||
|         // 校验调用 | ||||
|         verify(dictDataProducer, times(1)).sendDictDataRefreshMessage(); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateDictData_success() { | ||||
|         // 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())); | ||||
|  | ||||
|         // 调用 | ||||
|         dictDataService.updateDictData(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         SysDictDataDO dictData = dictDataMapper.selectById(reqVO.getId()); // 获取最新的 | ||||
|         assertPojoEquals(reqVO, dictData); | ||||
|         // 校验调用 | ||||
|         verify(dictDataProducer, times(1)).sendDictDataRefreshMessage(); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteDictData_success() { | ||||
|         // mock 数据 | ||||
|         SysDictDataDO dbDictData = randomDictDataDO(); | ||||
|         dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbDictData.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         dictDataService.deleteDictData(id); | ||||
|         // 校验数据不存在了 | ||||
|         assertNull(dictDataMapper.selectById(id)); | ||||
|         // 校验调用 | ||||
|         verify(dictDataProducer, times(1)).sendDictDataRefreshMessage(); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCheckDictDataExists_success() { | ||||
|         // mock 数据 | ||||
|         SysDictDataDO dbDictData = randomDictDataDO(); | ||||
|         dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 | ||||
|  | ||||
|         // 调用成功 | ||||
|         dictDataService.checkDictDataExists(dbDictData.getId()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCheckDictDataExists_notExists() { | ||||
|         assertServiceException(() -> dictDataService.checkDictDataExists(randomLongId()), DICT_DATA_NOT_EXISTS); | ||||
|     } | ||||
|  | ||||
|     @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.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); | ||||
|     } | ||||
|  | ||||
|     // ========== 随机对象 ========== | ||||
|  | ||||
|     @SafeVarargs | ||||
|     private static SysDictDataDO randomDictDataDO(Consumer<SysDictDataDO>... consumers) { | ||||
|         Consumer<SysDictDataDO> 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 是开启 | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,278 @@ | ||||
| package cn.iocoder.dashboard.modules.system.service.dict; | ||||
|  | ||||
| import cn.iocoder.dashboard.BaseSpringBootUnitTest; | ||||
| 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; | ||||
| import cn.iocoder.dashboard.modules.system.controller.dict.vo.type.SysDictTypeExportReqVO; | ||||
| import cn.iocoder.dashboard.modules.system.controller.dict.vo.type.SysDictTypePageReqVO; | ||||
| import cn.iocoder.dashboard.modules.system.controller.dict.vo.type.SysDictTypeUpdateReqVO; | ||||
| 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.impl.SysDictTypeServiceImpl; | ||||
| 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.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.*; | ||||
| 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; | ||||
| import static org.mockito.Mockito.when; | ||||
|  | ||||
| /** | ||||
| * {@link SysDictTypeServiceImpl} 的单元测试类 | ||||
| * | ||||
| * @author 芋道源码 | ||||
| */ | ||||
| public class SysDictTypeServiceTest extends BaseSpringBootUnitTest { | ||||
|  | ||||
|     @Resource | ||||
|     private SysDictTypeServiceImpl dictTypeService; | ||||
|  | ||||
|     @Resource | ||||
|     private SysDictTypeMapper dictTypeMapper; | ||||
|     @MockBean | ||||
|     private SysDictDataService dictDataService; | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDictTypePage() { | ||||
|        // mock 数据 | ||||
|        SysDictTypeDO dbDictType = randomPojo(SysDictTypeDO.class, o -> { // 等会查询到 | ||||
|            o.setName("yunai"); | ||||
|            o.setType("芋艿"); | ||||
|            o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|            o.setCreateTime(buildTime(2021, 1, 15)); | ||||
|        }); | ||||
|        dictTypeMapper.insert(dbDictType); | ||||
|        // 测试 name 不匹配 | ||||
|        dictTypeMapper.insert(ObjectUtils.clone(dbDictType, o -> o.setName("tudou"))); | ||||
|        // 测试 type 不匹配 | ||||
|        dictTypeMapper.insert(ObjectUtils.clone(dbDictType, o -> o.setType("土豆"))); | ||||
|        // 测试 status 不匹配 | ||||
|        dictTypeMapper.insert(ObjectUtils.clone(dbDictType, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|        // 测试 createTime 不匹配 | ||||
|        dictTypeMapper.insert(ObjectUtils.clone(dbDictType, o -> o.setCreateTime(buildTime(2021, 1, 1)))); | ||||
|        // 准备参数 | ||||
|        SysDictTypePageReqVO reqVO = new SysDictTypePageReqVO(); | ||||
|        reqVO.setName("nai"); | ||||
|        reqVO.setType("艿"); | ||||
|        reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|        reqVO.setBeginCreateTime(buildTime(2021, 1, 10)); | ||||
|        reqVO.setEndCreateTime(buildTime(2021, 1, 20)); | ||||
|  | ||||
|        // 调用 | ||||
|        PageResult<SysDictTypeDO> pageResult = dictTypeService.getDictTypePage(reqVO); | ||||
|        // 断言 | ||||
|        assertEquals(1, pageResult.getTotal()); | ||||
|        assertEquals(1, pageResult.getList().size()); | ||||
|        assertPojoEquals(dbDictType, pageResult.getList().get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDictTypeList() { | ||||
|         // mock 数据 | ||||
|         SysDictTypeDO dbDictType = randomPojo(SysDictTypeDO.class, o -> { // 等会查询到 | ||||
|             o.setName("yunai"); | ||||
|             o.setType("芋艿"); | ||||
|             o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|             o.setCreateTime(buildTime(2021, 1, 15)); | ||||
|         }); | ||||
|         dictTypeMapper.insert(dbDictType); | ||||
|         // 测试 name 不匹配 | ||||
|         dictTypeMapper.insert(ObjectUtils.clone(dbDictType, o -> o.setName("tudou"))); | ||||
|         // 测试 type 不匹配 | ||||
|         dictTypeMapper.insert(ObjectUtils.clone(dbDictType, o -> o.setType("土豆"))); | ||||
|         // 测试 status 不匹配 | ||||
|         dictTypeMapper.insert(ObjectUtils.clone(dbDictType, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); | ||||
|         // 测试 createTime 不匹配 | ||||
|         dictTypeMapper.insert(ObjectUtils.clone(dbDictType, o -> o.setCreateTime(buildTime(2021, 1, 1)))); | ||||
|         // 准备参数 | ||||
|         SysDictTypeExportReqVO reqVO = new SysDictTypeExportReqVO(); | ||||
|         reqVO.setName("nai"); | ||||
|         reqVO.setType("艿"); | ||||
|         reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||||
|         reqVO.setBeginCreateTime(buildTime(2021, 1, 10)); | ||||
|         reqVO.setEndCreateTime(buildTime(2021, 1, 20)); | ||||
|  | ||||
|         // 调用 | ||||
|         List<SysDictTypeDO> list = dictTypeService.getDictTypeList(reqVO); | ||||
|         // 断言 | ||||
|         assertEquals(1, list.size()); | ||||
|         assertPojoEquals(dbDictType, list.get(0)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetDictType() { | ||||
|         // mock 数据 | ||||
|         SysDictTypeDO dbDictType = randomDictTypeDO(); | ||||
|         dictTypeMapper.insert(dbDictType); | ||||
|         // 准备参数 | ||||
|         String type = dbDictType.getType(); | ||||
|  | ||||
|         // 调用 | ||||
|         SysDictTypeDO dictType = dictTypeService.getDictType(type); | ||||
|         // 断言 | ||||
|         assertNotNull(dictType); | ||||
|         assertPojoEquals(dbDictType, dictType); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCreateDictType_success() { | ||||
|         // 准备参数 | ||||
|         SysDictTypeCreateReqVO reqVO = randomPojo(SysDictTypeCreateReqVO.class, | ||||
|                 o -> o.setStatus(randomEle(CommonStatusEnum.values()).getStatus())); | ||||
|  | ||||
|         // 调用 | ||||
|         Long dictTypeId = dictTypeService.createDictType(reqVO); | ||||
|         // 断言 | ||||
|         assertNotNull(dictTypeId); | ||||
|         // 校验记录的属性是否正确 | ||||
|         SysDictTypeDO dictType = dictTypeMapper.selectById(dictTypeId); | ||||
|         assertPojoEquals(reqVO, dictType); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateDictType_success() { | ||||
|         // mock 数据 | ||||
|         SysDictTypeDO dbDictType = randomDictTypeDO(); | ||||
|         dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         SysDictTypeUpdateReqVO reqVO = randomPojo(SysDictTypeUpdateReqVO.class, o -> { | ||||
|             o.setId(dbDictType.getId()); // 设置更新的 ID | ||||
|             o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); | ||||
|         }); | ||||
|  | ||||
|         // 调用 | ||||
|         dictTypeService.updateDictType(reqVO); | ||||
|         // 校验是否更新正确 | ||||
|         SysDictTypeDO dictType = dictTypeMapper.selectById(reqVO.getId()); // 获取最新的 | ||||
|         assertPojoEquals(reqVO, dictType); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteDictType_success() { | ||||
|         // mock 数据 | ||||
|         SysDictTypeDO dbDictType = randomDictTypeDO(); | ||||
|         dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbDictType.getId(); | ||||
|  | ||||
|         // 调用 | ||||
|         dictTypeService.deleteDictType(id); | ||||
|         // 校验数据不存在了 | ||||
|         assertNull(dictTypeMapper.selectById(id)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteDictType_hasChildren() { | ||||
|         // mock 数据 | ||||
|         SysDictTypeDO dbDictType = randomDictTypeDO(); | ||||
|         dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 | ||||
|         // 准备参数 | ||||
|         Long id = dbDictType.getId(); | ||||
|         // mock 方法 | ||||
|         when(dictDataService.countByDictType(eq(dbDictType.getType()))).thenReturn(1); | ||||
|  | ||||
|         // 调用, 并断言异常 | ||||
|         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_nameDuplicateForCreate() { | ||||
|         // 准备参数 | ||||
|         String name = randomString(); | ||||
|         // mock 数据 | ||||
|         dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name))); | ||||
|  | ||||
|         // 调用,校验异常 | ||||
|         assertServiceException(() -> dictTypeService.checkDictTypeNameUnique(null, name), | ||||
|                 DICT_TYPE_NAME_DUPLICATE); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testCheckDictTypeNameUnique_nameDuplicateForUpdate() { | ||||
|         // 准备参数 | ||||
|         Long id = randomLongId(); | ||||
|         String name = randomString(); | ||||
|         // mock 数据 | ||||
|         dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name))); | ||||
|  | ||||
|         // 调用,校验异常 | ||||
|         assertServiceException(() -> dictTypeService.checkDictTypeNameUnique(id, name), | ||||
|                 DICT_TYPE_NAME_DUPLICATE); | ||||
|     } | ||||
|  | ||||
|     // ========== 随机对象 ========== | ||||
|  | ||||
|     @SafeVarargs | ||||
|     private static SysDictTypeDO randomDictTypeDO(Consumer<SysDictTypeDO>... consumers) { | ||||
|         Consumer<SysDictTypeDO> consumer = (o) -> { | ||||
|             o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 | ||||
|         }; | ||||
|         return randomPojo(SysDictTypeDO.class, ArrayUtils.append(consumer, consumers)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1 +0,0 @@ | ||||
| package cn.iocoder.dashboard.modules.system.service; | ||||
| @@ -4,11 +4,15 @@ import cn.hutool.core.util.ArrayUtil; | ||||
| import cn.hutool.core.util.ReflectUtil; | ||||
| import cn.iocoder.dashboard.common.exception.ErrorCode; | ||||
| import cn.iocoder.dashboard.common.exception.ServiceException; | ||||
| import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil; | ||||
| import org.junit.jupiter.api.Assertions; | ||||
| import org.junit.jupiter.api.function.Executable; | ||||
|  | ||||
| import java.lang.reflect.Field; | ||||
| import java.util.Arrays; | ||||
|  | ||||
| import static org.junit.jupiter.api.Assertions.assertThrows; | ||||
|  | ||||
| /** | ||||
|  * 单元测试,assert 断言工具类 | ||||
|  * | ||||
| @@ -47,14 +51,19 @@ public class AssertUtils { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 比对抛出的 ServiceException 是否匹配 | ||||
|      * 执行方法,校验抛出的 Service 是否符合条件 | ||||
|      * | ||||
|      * @param executable 业务异常 | ||||
|      * @param errorCode 错误码对象 | ||||
|      * @param serviceException 业务异常 | ||||
|      * @param messageParams 消息参数 | ||||
|      */ | ||||
|     public static void assertPojoEquals(ErrorCode errorCode, ServiceException serviceException) { | ||||
|     public static void assertServiceException(Executable executable, ErrorCode errorCode, Object... messageParams) { | ||||
|         // 调用方法 | ||||
|         ServiceException serviceException = assertThrows(ServiceException.class, executable); | ||||
|         // 校验错误码 | ||||
|         Assertions.assertEquals(errorCode.getCode(), serviceException.getCode(), "错误码不匹配"); | ||||
|         Assertions.assertEquals(errorCode.getMessage(), serviceException.getMessage(), "错误提示不匹配"); | ||||
|         String message = ServiceExceptionUtil.doFormat(errorCode.getCode(), errorCode.getMessage(), messageParams); | ||||
|         Assertions.assertEquals(message, serviceException.getMessage(), "错误提示不匹配"); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package cn.iocoder.dashboard.util; | ||||
|  | ||||
| import cn.hutool.core.util.ArrayUtil; | ||||
| import cn.hutool.core.util.RandomUtil; | ||||
| import cn.iocoder.dashboard.common.enums.CommonStatusEnum; | ||||
| import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO; | ||||
| import uk.co.jemos.podam.api.PodamFactory; | ||||
| import uk.co.jemos.podam.api.PodamFactoryImpl; | ||||
| @@ -46,7 +47,7 @@ public class RandomUtils { | ||||
|         return RandomUtil.randomString(RANDOM_STRING_LENGTH); | ||||
|     } | ||||
|  | ||||
|     public static Long randomLong() { | ||||
|     public static Long randomLongId() { | ||||
|         return RandomUtil.randomLong(0, Long.MAX_VALUE); | ||||
|     } | ||||
|  | ||||
| @@ -67,6 +68,10 @@ public class RandomUtils { | ||||
|                 .map(i -> randomPojo(clazz)).collect(Collectors.toSet()); | ||||
|     } | ||||
|  | ||||
|     public static Integer randomCommonStatus() { | ||||
|         return RandomUtil.randomEle(CommonStatusEnum.values()).getStatus(); | ||||
|     } | ||||
|  | ||||
|     @SafeVarargs | ||||
|     public static SysUserDO randomUserDO(Consumer<SysUserDO>... consumers) { | ||||
|         return randomPojo(SysUserDO.class, consumers); | ||||
|   | ||||
| @@ -7,3 +7,4 @@ DELETE FROM "sys_dict_data"; | ||||
| DELETE FROM "sys_role"; | ||||
| DELETE FROM "sys_role_menu"; | ||||
| DELETE FROM "sys_menu"; | ||||
| DELETE FROM "sys_dict_type"; | ||||
|   | ||||
| @@ -101,16 +101,16 @@ CREATE TABLE IF NOT EXISTS "sys_menu" ( | ||||
|     PRIMARY KEY ("id") | ||||
| ) COMMENT '菜单权限表'; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "sys_notice" ( | ||||
| CREATE TABLE "sys_dict_type" ( | ||||
|     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||||
|     "title" varchar(50) NOT NULL, | ||||
|     "content" text NOT NULL, | ||||
|     "notice_type" tinyint(4) NOT NULL, | ||||
|     "status" tinyint(4) NOT NULL DEFAULT '0', | ||||
|     "name" varchar(100) NOT NULL DEFAULT '', | ||||
|     "type" varchar(100) NOT NULL DEFAULT '', | ||||
|     "status" tinyint NOT NULL DEFAULT '0', | ||||
|     "remark" varchar(500) DEFAULT NULL, | ||||
|     "create_by" varchar(64) DEFAULT '', | ||||
|     "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||
|     "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||
|     "update_by" varchar(64) DEFAULT '', | ||||
|     "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | ||||
|     "deleted" bit NOT NULL DEFAULT '0', | ||||
|     "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||
|     "deleted" bit NOT NULL DEFAULT FALSE, | ||||
|     PRIMARY KEY ("id") | ||||
| ) COMMENT='通知公告表'; | ||||
| ) COMMENT '字典类型表'; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 budliang
					budliang