!82 增加 system 和 infra 的代码覆盖率,提升稳定性

Merge pull request !82 from 芋道源码/feature/vo-optimize
pull/81/MERGE
芋道源码 2023-12-05 12:07:36 +00:00 committed by Gitee
commit 6894a51fef
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
45 changed files with 2048 additions and 424 deletions

View File

@ -73,6 +73,23 @@ public class CollectionUtils {
return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList());
}
public static <T, U> List<U> convertListByFlatMap(Collection<T> from,
Function<T, ? extends Stream<? extends U>> func) {
if (CollUtil.isEmpty(from)) {
return new ArrayList<>();
}
return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());
}
public static <T, U, R> List<R> convertListByFlatMap(Collection<T> from,
Function<? super T, ? extends U> mapper,
Function<U, ? extends Stream<? extends R>> func) {
if (CollUtil.isEmpty(from)) {
return new ArrayList<>();
}
return from.stream().map(mapper).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());
}
public static <K, V> List<V> mergeValuesFromMap(Map<K, List<V>> map) {
return map.values()
.stream()
@ -101,6 +118,23 @@ public class CollectionUtils {
return from.stream().filter(filter).collect(Collectors.toMap(keyFunc, v -> v));
}
public static <T, U> Set<U> convertSetByFlatMap(Collection<T> from,
Function<T, ? extends Stream<? extends U>> func) {
if (CollUtil.isEmpty(from)) {
return new HashSet<>();
}
return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());
}
public static <T, U, R> Set<R> convertSetByFlatMap(Collection<T> from,
Function<? super T, ? extends U> mapper,
Function<U, ? extends Stream<? extends R>> func) {
if (CollUtil.isEmpty(from)) {
return new HashSet<>();
}
return from.stream().map(mapper).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());
}
public static <T, K> Map<K, T> convertMap(Collection<T> from, Function<T, K> keyFunc) {
if (CollUtil.isEmpty(from)) {
return new HashMap<>();
@ -272,38 +306,4 @@ public class CollectionUtils {
return deptId == null ? Collections.emptyList() : Collections.singleton(deptId);
}
public static <T, U> List<U> convertListByFlatMap(Collection<T> from,
Function<T, ? extends Stream<? extends U>> func) {
if (CollUtil.isEmpty(from)) {
return new ArrayList<>();
}
return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());
}
public static <T, U, R> List<R> convertListByFlatMap(Collection<T> from,
Function<? super T, ? extends U> mapper,
Function<U, ? extends Stream<? extends R>> func) {
if (CollUtil.isEmpty(from)) {
return new ArrayList<>();
}
return from.stream().map(mapper).flatMap(func).filter(Objects::nonNull).collect(Collectors.toList());
}
public static <T, U> Set<U> convertSetByFlatMap(Collection<T> from,
Function<T, ? extends Stream<? extends U>> func) {
if (CollUtil.isEmpty(from)) {
return new HashSet<>();
}
return from.stream().flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());
}
public static <T, U, R> Set<R> convertSetByFlatMap(Collection<T> from,
Function<? super T, ? extends U> mapper,
Function<U, ? extends Stream<? extends R>> func) {
if (CollUtil.isEmpty(from)) {
return new HashSet<>();
}
return from.stream().map(mapper).flatMap(func).filter(Objects::nonNull).collect(Collectors.toSet());
}
}

View File

@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.common.util.spring;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
@ -25,7 +25,7 @@ import java.util.Map;
public class SpringExpressionUtils {
/**
* spel
* Spring EL
*/
private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
/**
@ -43,7 +43,7 @@ public class SpringExpressionUtils {
* @param expressionString EL
* @return
*/
public static Object parseExpression(ProceedingJoinPoint joinPoint, String expressionString) {
public static Object parseExpression(JoinPoint joinPoint, String expressionString) {
Map<String, Object> result = parseExpressions(joinPoint, Collections.singletonList(expressionString));
return result.get(expressionString);
}
@ -55,7 +55,7 @@ public class SpringExpressionUtils {
* @param expressionStrings EL
* @return key value
*/
public static Map<String, Object> parseExpressions(ProceedingJoinPoint joinPoint, List<String> expressionStrings) {
public static Map<String, Object> parseExpressions(JoinPoint joinPoint, List<String> expressionStrings) {
// 如果为空,则不进行解析
if (CollUtil.isEmpty(expressionStrings)) {
return MapUtil.newHashMap();

View File

@ -4,6 +4,7 @@ import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ZipUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenCreateListReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenDetailRespVO;
@ -66,7 +67,7 @@ public class CodegenController {
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
public CommonResult<List<CodegenTableRespVO>> getCodegenTableList(@RequestParam(value = "dataSourceConfigId") Long dataSourceConfigId) {
List<CodegenTableDO> list = codegenService.getCodegenTableList(dataSourceConfigId);
return success(CodegenConvert.INSTANCE.convertList05(list));
return success(BeanUtils.toBean(list, CodegenTableRespVO.class));
}
@GetMapping("/table/page")
@ -74,7 +75,7 @@ public class CodegenController {
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
public CommonResult<PageResult<CodegenTableRespVO>> getCodegenTablePage(@Valid CodegenTablePageReqVO pageReqVO) {
PageResult<CodegenTableDO> pageResult = codegenService.getCodegenTablePage(pageReqVO);
return success(CodegenConvert.INSTANCE.convertPage(pageResult));
return success(BeanUtils.toBean(pageResult, CodegenTableRespVO.class));
}
@GetMapping("/detail")
@ -82,7 +83,7 @@ public class CodegenController {
@Parameter(name = "tableId", description = "表编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
public CommonResult<CodegenDetailRespVO> getCodegenDetail(@RequestParam("tableId") Long tableId) {
CodegenTableDO table = codegenService.getCodegenTablePage(tableId);
CodegenTableDO table = codegenService.getCodegenTable(tableId);
List<CodegenColumnDO> columns = codegenService.getCodegenColumnListByTableId(tableId);
// 拼装返回
return success(CodegenConvert.INSTANCE.convert(table, columns));

View File

@ -1,18 +1,11 @@
package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.column.CodegenColumnBaseVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTableBaseVO;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
import com.fasterxml.jackson.annotation.JsonIgnore;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.column.CodegenColumnSaveReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTableSaveReqVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.Valid;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotNull;
import java.util.List;
@ -22,55 +15,10 @@ public class CodegenUpdateReqVO {
@Valid // 校验内嵌的字段
@NotNull(message = "表定义不能为空")
private Table table;
private CodegenTableSaveReqVO table;
@Valid // 校验内嵌的字段
@NotNull(message = "字段定义不能为空")
private List<Column> columns;
@Schema(description = "更新表定义")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Valid
public static class Table extends CodegenTableBaseVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@AssertTrue(message = "上级菜单不能为空,请前往 [修改生成配置 -> 生成信息] 界面,设置“上级菜单”字段")
@JsonIgnore
public boolean isParentMenuIdValid() {
// 生成场景为管理后台时,必须设置上级菜单,不然生成的菜单 SQL 是无父级菜单的
return ObjectUtil.notEqual(getScene(), CodegenSceneEnum.ADMIN.getScene())
|| getParentMenuId() != null;
}
@AssertTrue(message = "关联的父表信息不全")
@JsonIgnore
public boolean isSubValid() {
return ObjectUtil.notEqual(getTemplateType(), CodegenTemplateTypeEnum.SUB)
|| (ObjectUtil.isAllNotEmpty(getMasterTableId(), getSubJoinColumnId(), getSubJoinMany()));
}
@AssertTrue(message = "关联的树表信息不全")
@JsonIgnore
public boolean isTreeValid() {
return ObjectUtil.notEqual(getTemplateType(), CodegenTemplateTypeEnum.TREE)
|| (ObjectUtil.isAllNotEmpty(getTreeParentColumnId(), getTreeNameColumnId()));
}
}
@Schema(description = "更新表定义")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public static class Column extends CodegenColumnBaseVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
}
private List<CodegenColumnSaveReqVO> columns;
}

View File

@ -2,20 +2,70 @@ package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.column;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 代码生成字段定义 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CodegenColumnRespVO extends CodegenColumnBaseVO {
public class CodegenColumnRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "表编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long tableId;
@Schema(description = "字段名", requiredMode = Schema.RequiredMode.REQUIRED, example = "user_age")
private String columnName;
@Schema(description = "字段类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "int(11)")
private String dataType;
@Schema(description = "字段描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "年龄")
private String columnComment;
@Schema(description = "是否允许为空", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean nullable;
@Schema(description = "是否主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean primaryKey;
@Schema(description = "是否自增", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean autoIncrement;
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Integer ordinalPosition;
@Schema(description = "Java 属性类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "userAge")
private String javaType;
@Schema(description = "Java 属性名", requiredMode = Schema.RequiredMode.REQUIRED, example = "Integer")
private String javaField;
@Schema(description = "字典类型", example = "sys_gender")
private String dictType;
@Schema(description = "数据示例", example = "1024")
private String example;
@Schema(description = "是否为 Create 创建操作的字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean createOperation;
@Schema(description = "是否为 Update 更新操作的字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean updateOperation;
@Schema(description = "是否为 List 查询操作的字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean listOperation;
@Schema(description = "List 查询操作的条件类型,参见 CodegenColumnListConditionEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "LIKE")
private String listOperationCondition;
@Schema(description = "是否为 List 查询操作的返回字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean listOperationResult;
@Schema(description = "显示类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "input")
private String htmlType;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;

View File

@ -5,12 +5,12 @@ import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* Base VO VO 使
* VO Swagger
*/
@Schema(description = "管理后台 - 代码生成字段定义创建/修改 Request VO")
@Data
public class CodegenColumnBaseVO {
public class CodegenColumnSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "表编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "表编号不能为空")
@ -38,7 +38,7 @@ public class CodegenColumnBaseVO {
@Schema(description = "是否自增", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否自增不能为空")
private String autoIncrement;
private Boolean autoIncrement;
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@NotNull(message = "排序不能为空")

View File

@ -2,20 +2,64 @@ package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 代码生成表定义 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CodegenTableRespVO extends CodegenTableBaseVO {
public class CodegenTableRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "生成场景,参见 CodegenSceneEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer scene;
@Schema(description = "表名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao")
private String tableName;
@Schema(description = "表描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
private String tableComment;
@Schema(description = "备注", example = "我是备注")
private String remark;
@Schema(description = "模块名", requiredMode = Schema.RequiredMode.REQUIRED, example = "system")
private String moduleName;
@Schema(description = "业务名", requiredMode = Schema.RequiredMode.REQUIRED, example = "codegen")
private String businessName;
@Schema(description = "类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "CodegenTable")
private String className;
@Schema(description = "类描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "代码生成器的表定义")
private String classComment;
@Schema(description = "作者", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
private String author;
@Schema(description = "模板类型,参见 CodegenTemplateTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer templateType;
@Schema(description = "前端类型,参见 CodegenFrontTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
private Integer frontType;
@Schema(description = "父菜单编号", example = "1024")
private Long parentMenuId;
@Schema(description = "主表的编号", example = "2048")
private Long masterTableId;
@Schema(description = "子表关联主表的字段编号", example = "4096")
private Long subJoinColumnId;
@Schema(description = "主表与子表是否一对多", example = "4096")
private Boolean subJoinMany;
@Schema(description = "树表的父字段编号", example = "8192")
private Long treeParentColumnId;
@Schema(description = "树表的名字字段编号", example = "16384")
private Long treeNameColumnId;
@Schema(description = "主键编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer dataSourceConfigId;

View File

@ -1,16 +1,21 @@
package cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotNull;
/**
* Base VO VO 使
* VO Swagger
*/
@Schema(description = "管理后台 - 代码生成表定义创建/修改 Response VO")
@Data
public class CodegenTableBaseVO {
public class CodegenTableSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "生成场景,参见 CodegenSceneEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "导入类型不能为空")
@ -70,4 +75,26 @@ public class CodegenTableBaseVO {
@Schema(description = "树表的名字字段编号", example = "16384")
private Long treeNameColumnId;
@AssertTrue(message = "上级菜单不能为空,请前往 [修改生成配置 -> 生成信息] 界面,设置“上级菜单”字段")
@JsonIgnore
public boolean isParentMenuIdValid() {
// 生成场景为管理后台时,必须设置上级菜单,不然生成的菜单 SQL 是无父级菜单的
return ObjectUtil.notEqual(getScene(), CodegenSceneEnum.ADMIN.getScene())
|| getParentMenuId() != null;
}
@AssertTrue(message = "关联的父表信息不全")
@JsonIgnore
public boolean isSubValid() {
return ObjectUtil.notEqual(getTemplateType(), CodegenTemplateTypeEnum.SUB)
|| (ObjectUtil.isAllNotEmpty(masterTableId, subJoinColumnId, subJoinMany));
}
@AssertTrue(message = "关联的树表信息不全")
@JsonIgnore
public boolean isTreeValid() {
return ObjectUtil.notEqual(templateType, CodegenTemplateTypeEnum.TREE)
|| (ObjectUtil.isAllNotEmpty(treeParentColumnId, treeNameColumnId));
}
}

View File

@ -1,12 +1,11 @@
package cn.iocoder.yudao.module.infra.convert.codegen;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenDetailRespVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenPreviewRespVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.column.CodegenColumnRespVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import com.baomidou.mybatisplus.generator.config.po.TableField;
@ -20,7 +19,6 @@ import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Mapper
public interface CodegenConvert {
@ -54,40 +52,18 @@ public interface CodegenConvert {
return jdbcType.name();
}
// ========== CodegenTableDO 相关 ==========
List<CodegenTableRespVO> convertList05(List<CodegenTableDO> list);
CodegenTableRespVO convert(CodegenTableDO bean);
PageResult<CodegenTableRespVO> convertPage(PageResult<CodegenTableDO> page);
// ========== CodegenTableDO 相关 ==========
List<CodegenColumnRespVO> convertList02(List<CodegenColumnDO> list);
CodegenTableDO convert(CodegenUpdateReqVO.Table bean);
List<CodegenColumnDO> convertList03(List<CodegenUpdateReqVO.Column> columns);
List<DatabaseTableRespVO> convertList04(List<TableInfo> list);
// ========== 其它 ==========
default CodegenDetailRespVO convert(CodegenTableDO table, List<CodegenColumnDO> columns) {
CodegenDetailRespVO respVO = new CodegenDetailRespVO();
respVO.setTable(convert(table));
respVO.setColumns(convertList02(columns));
respVO.setTable(BeanUtils.toBean(table, CodegenTableRespVO.class));
respVO.setColumns(BeanUtils.toBean(columns, CodegenColumnRespVO.class));
return respVO;
}
default List<CodegenPreviewRespVO> convert(Map<String, String> codes) {
return codes.entrySet().stream().map(entry -> {
CodegenPreviewRespVO respVO = new CodegenPreviewRespVO();
respVO.setFilePath(entry.getKey());
respVO.setCode(entry.getValue());
return respVO;
}).collect(Collectors.toList());
return CollectionUtils.convertList(codes.entrySet(),
entry -> new CodegenPreviewRespVO().setFilePath(entry.getKey()).setCode(entry.getValue()));
}
}

View File

@ -70,7 +70,7 @@ public interface CodegenService {
* @param id
* @return
*/
CodegenTableDO getCodegenTablePage(Long id);
CodegenTableDO getCodegenTable(Long id);
/**
*
@ -91,7 +91,6 @@ public interface CodegenService {
/**
*
*
*
* @param dataSourceConfigId
* @param name
* @param comment

View File

@ -3,12 +3,11 @@ package cn.iocoder.yudao.module.infra.service.codegen;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenCreateListReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
import cn.iocoder.yudao.module.infra.convert.codegen.CodegenConvert;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenColumnMapper;
@ -22,6 +21,7 @@ import cn.iocoder.yudao.module.infra.service.db.DatabaseTableService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.google.common.annotations.VisibleForTesting;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -31,6 +31,8 @@ import java.util.function.BiPredicate;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
/**
@ -69,7 +71,7 @@ public class CodegenServiceImpl implements CodegenService {
return ids;
}
public Long createCodegen(Long userId, Long dataSourceConfigId, String tableName) {
private Long createCodegen(Long userId, Long dataSourceConfigId, String tableName) {
// 从数据库中,获得数据库表结构
TableInfo tableInfo = databaseTableService.getTable(dataSourceConfigId, tableName);
// 导入
@ -103,7 +105,8 @@ public class CodegenServiceImpl implements CodegenService {
return table.getId();
}
private void validateTableInfo(TableInfo tableInfo) {
@VisibleForTesting
void validateTableInfo(TableInfo tableInfo) {
if (tableInfo == null) {
throw exception(CODEGEN_IMPORT_TABLE_NULL);
}
@ -139,10 +142,10 @@ public class CodegenServiceImpl implements CodegenService {
}
// 更新 table 表定义
CodegenTableDO updateTableObj = CodegenConvert.INSTANCE.convert(updateReqVO.getTable());
CodegenTableDO updateTableObj = BeanUtils.toBean(updateReqVO.getTable(), CodegenTableDO.class);
codegenTableMapper.updateById(updateTableObj);
// 更新 column 字段定义
List<CodegenColumnDO> updateColumnObjs = CodegenConvert.INSTANCE.convertList03(updateReqVO.getColumns());
List<CodegenColumnDO> updateColumnObjs = BeanUtils.toBean(updateReqVO.getColumns(), CodegenColumnDO.class);
updateColumnObjs.forEach(updateColumnObj -> codegenColumnMapper.updateById(updateColumnObj));
}
@ -161,29 +164,28 @@ public class CodegenServiceImpl implements CodegenService {
}
private void syncCodegen0(Long tableId, TableInfo tableInfo) {
// 校验导入的表和字段非空
// 1. 校验导入的表和字段非空
validateTableInfo(tableInfo);
List<TableField> tableFields = tableInfo.getFields();
// 构建 CodegenColumnDO 数组,只同步新增的字段
// 2. 构建 CodegenColumnDO 数组,只同步新增的字段
List<CodegenColumnDO> codegenColumns = codegenColumnMapper.selectListByTableId(tableId);
Set<String> codegenColumnNames = CollectionUtils.convertSet(codegenColumns, CodegenColumnDO::getColumnName);
Set<String> codegenColumnNames = convertSet(codegenColumns, CodegenColumnDO::getColumnName);
//计算需要修改的字段,插入时重新插入,删除时将原来的删除
BiPredicate<TableField, CodegenColumnDO> pr =
// 3.1 计算需要【修改】的字段,插入时重新插入,删除时将原来的删除
Map<String, CodegenColumnDO> codegenColumnDOMap = convertMap(codegenColumns, CodegenColumnDO::getColumnName);
BiPredicate<TableField, CodegenColumnDO> primaryKeyPredicate =
(tableField, codegenColumn) -> tableField.getMetaInfo().getJdbcType().name().equals(codegenColumn.getDataType())
&& tableField.getMetaInfo().isNullable() == codegenColumn.getNullable()
&& tableField.isKeyFlag() == codegenColumn.getPrimaryKey()
&& tableField.getComment().equals(codegenColumn.getColumnComment());
Map<String, CodegenColumnDO> codegenColumnDOMap = CollectionUtils.convertMap(codegenColumns, CodegenColumnDO::getColumnName);
//需要修改的字段
Set<String> modifyFieldNames = tableFields.stream()
.filter(tableField -> codegenColumnDOMap.get(tableField.getColumnName()) != null
&& !pr.test(tableField, codegenColumnDOMap.get(tableField.getColumnName())))
&& !primaryKeyPredicate.test(tableField, codegenColumnDOMap.get(tableField.getColumnName())))
.map(TableField::getColumnName)
.collect(Collectors.toSet());
// 计算需要删除的字段
Set<String> tableFieldNames = CollectionUtils.convertSet(tableFields, TableField::getName);
// 3.2 计算需要删除的字段
Set<String> tableFieldNames = convertSet(tableFields, TableField::getName);
Set<Long> deleteColumnIds = codegenColumns.stream()
.filter(column -> (!tableFieldNames.contains(column.getColumnName())) || modifyFieldNames.contains(column.getColumnName()))
.map(CodegenColumnDO::getId).collect(Collectors.toSet());
@ -193,10 +195,10 @@ public class CodegenServiceImpl implements CodegenService {
throw exception(CODEGEN_SYNC_NONE_CHANGE);
}
// 插入新增的字段
// 4.1 插入新增的字段
List<CodegenColumnDO> columns = codegenBuilder.buildColumns(tableId, tableFields);
codegenColumnMapper.insertBatch(columns);
// 删除不存在的字段
// 4.2 删除不存在的字段
if (CollUtil.isNotEmpty(deleteColumnIds)) {
codegenColumnMapper.deleteBatchIds(deleteColumnIds);
}
@ -227,7 +229,7 @@ public class CodegenServiceImpl implements CodegenService {
}
@Override
public CodegenTableDO getCodegenTablePage(Long id) {
public CodegenTableDO getCodegenTable(Long id) {
return codegenTableMapper.selectById(id);
}
@ -277,10 +279,10 @@ public class CodegenServiceImpl implements CodegenService {
public List<DatabaseTableRespVO> getDatabaseTableList(Long dataSourceConfigId, String name, String comment) {
List<TableInfo> tables = databaseTableService.getTableList(dataSourceConfigId, name, comment);
// 移除在 Codegen 中,已经存在的
Set<String> existsTables = CollectionUtils.convertSet(
Set<String> existsTables = convertSet(
codegenTableMapper.selectListByDataSourceConfigId(dataSourceConfigId), CodegenTableDO::getTableName);
tables.removeIf(table -> existsTables.contains(table.getName()));
return CodegenConvert.INSTANCE.convertList04(tables);
return BeanUtils.toBean(tables, DatabaseTableRespVO.class);
}
}

View File

@ -269,7 +269,7 @@ const queryParams = reactive({
#foreach ($column in $columns)
#if ($column.listOperation)
#if ($column.listOperationCondition != 'BETWEEN')
$column.javaField: null,
$column.javaField: undefined,
#end
#if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")
$column.javaField: [],

View File

@ -0,0 +1,557 @@
package cn.iocoder.yudao.module.infra.service.codegen;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenCreateListReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.column.CodegenColumnSaveReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenColumnMapper;
import cn.iocoder.yudao.module.infra.dal.mysql.codegen.CodegenTableMapper;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenSceneEnum;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenBuilder;
import cn.iocoder.yudao.module.infra.service.codegen.inner.CodegenEngine;
import cn.iocoder.yudao.module.infra.service.db.DatabaseTableService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* {@link CodegenServiceImpl}
*
* @author
*/
@Import(CodegenServiceImpl.class)
public class CodegenServiceImplTest extends BaseDbUnitTest {
@Resource
private CodegenServiceImpl codegenService;
@Resource
private CodegenTableMapper codegenTableMapper;
@Resource
private CodegenColumnMapper codegenColumnMapper;
@MockBean
private DatabaseTableService databaseTableService;
@MockBean
private AdminUserApi userApi;
@MockBean
private CodegenBuilder codegenBuilder;
@MockBean
private CodegenEngine codegenEngine;
@MockBean
private CodegenProperties codegenProperties;
@Test
public void testCreateCodegenList() {
// 准备参数
Long userId = randomLongId();
CodegenCreateListReqVO reqVO = randomPojo(CodegenCreateListReqVO.class,
o -> o.setDataSourceConfigId(1L).setTableNames(Collections.singletonList("t_yunai")));
// mock 方法TableInfo
TableInfo tableInfo = mock(TableInfo.class);
when(databaseTableService.getTable(eq(1L), eq("t_yunai")))
.thenReturn(tableInfo);
when(tableInfo.getComment()).thenReturn("芋艿");
// mock 方法TableInfo fields
TableField field01 = mock(TableField.class);
when(field01.getComment()).thenReturn("主键");
TableField field02 = mock(TableField.class);
when(field02.getComment()).thenReturn("名字");
List<TableField> fields = Arrays.asList(field01, field02);
when(tableInfo.getFields()).thenReturn(fields);
// mock 方法CodegenTableDO
CodegenTableDO table = randomPojo(CodegenTableDO.class);
when(codegenBuilder.buildTable(same(tableInfo))).thenReturn(table);
// mock 方法AdminUserRespDTO
AdminUserRespDTO user = randomPojo(AdminUserRespDTO.class, o -> o.setNickname("芋头"));
when(userApi.getUser(eq(userId))).thenReturn(success(user));
// mock 方法CodegenColumnDO
List<CodegenColumnDO> columns = randomPojoList(CodegenColumnDO.class);
when(codegenBuilder.buildColumns(eq(table.getId()), same(fields)))
.thenReturn(columns);
// mock 方法CodegenProperties
when(codegenProperties.getFrontType()).thenReturn(CodegenFrontTypeEnum.VUE3.getType());
// 调用
List<Long> result = codegenService.createCodegenList(userId, reqVO);
// 断言
assertEquals(1, result.size());
// 断言CodegenTableDO
CodegenTableDO dbTable = codegenTableMapper.selectList().get(0);
assertPojoEquals(table, dbTable);
assertEquals(1L, dbTable.getDataSourceConfigId());
assertEquals(CodegenSceneEnum.ADMIN.getScene(), dbTable.getScene());
assertEquals(CodegenFrontTypeEnum.VUE3.getType(), dbTable.getFrontType());
assertEquals("芋头", dbTable.getAuthor());
// 断言CodegenColumnDO
List<CodegenColumnDO> dbColumns = codegenColumnMapper.selectList();
assertEquals(columns.size(), dbColumns.size());
assertTrue(dbColumns.get(0).getPrimaryKey());
for (int i = 0; i < dbColumns.size(); i++) {
assertPojoEquals(columns.get(i), dbColumns.get(i));
}
}
@Test
public void testValidateTableInfo() {
// 情况一
assertServiceException(() -> codegenService.validateTableInfo(null),
CODEGEN_IMPORT_TABLE_NULL);
// 情况二
TableInfo tableInfo = mock(TableInfo.class);
assertServiceException(() -> codegenService.validateTableInfo(tableInfo),
CODEGEN_TABLE_INFO_TABLE_COMMENT_IS_NULL);
// 情况三
when(tableInfo.getComment()).thenReturn("芋艿");
assertServiceException(() -> codegenService.validateTableInfo(tableInfo),
CODEGEN_IMPORT_COLUMNS_NULL);
// 情况四
TableField field = mock(TableField.class);
when(field.getName()).thenReturn("name");
when(tableInfo.getFields()).thenReturn(Collections.singletonList(field));
assertServiceException(() -> codegenService.validateTableInfo(tableInfo),
CODEGEN_TABLE_INFO_COLUMN_COMMENT_IS_NULL, field.getName());
}
@Test
public void testUpdateCodegen_notExists() {
// 准备参数
CodegenUpdateReqVO updateReqVO = randomPojo(CodegenUpdateReqVO.class);
// mock 方法
// 调用,并断言
assertServiceException(() -> codegenService.updateCodegen(updateReqVO),
CODEGEN_TABLE_NOT_EXISTS);
}
@Test
public void testUpdateCodegen_sub_masterNotExists() {
// mock 数据
CodegenTableDO table = randomPojo(CodegenTableDO.class,
o -> o.setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
.setScene(CodegenSceneEnum.ADMIN.getScene()));
codegenTableMapper.insert(table);
// 准备参数
CodegenUpdateReqVO updateReqVO = randomPojo(CodegenUpdateReqVO.class,
o -> o.getTable().setId(table.getId())
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType()));
// 调用,并断言
assertServiceException(() -> codegenService.updateCodegen(updateReqVO),
CODEGEN_MASTER_TABLE_NOT_EXISTS, updateReqVO.getTable().getMasterTableId());
}
@Test
public void testUpdateCodegen_sub_columnNotExists() {
// mock 数据
CodegenTableDO subTable = randomPojo(CodegenTableDO.class,
o -> o.setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
.setScene(CodegenSceneEnum.ADMIN.getScene()));
codegenTableMapper.insert(subTable);
// mock 数据master
CodegenTableDO masterTable = randomPojo(CodegenTableDO.class,
o -> o.setTemplateType(CodegenTemplateTypeEnum.MASTER_ERP.getType())
.setScene(CodegenSceneEnum.ADMIN.getScene()));
codegenTableMapper.insert(masterTable);
// 准备参数
CodegenUpdateReqVO updateReqVO = randomPojo(CodegenUpdateReqVO.class,
o -> o.getTable().setId(subTable.getId())
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
.setMasterTableId(masterTable.getId()));
// 调用,并断言
assertServiceException(() -> codegenService.updateCodegen(updateReqVO),
CODEGEN_SUB_COLUMN_NOT_EXISTS, updateReqVO.getTable().getSubJoinColumnId());
}
@Test
public void testUpdateCodegen_success() {
// mock 数据
CodegenTableDO table = randomPojo(CodegenTableDO.class,
o -> o.setTemplateType(CodegenTemplateTypeEnum.ONE.getType())
.setScene(CodegenSceneEnum.ADMIN.getScene()));
codegenTableMapper.insert(table);
CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()));
codegenColumnMapper.insert(column01);
CodegenColumnDO column02 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()));
codegenColumnMapper.insert(column02);
// 准备参数
CodegenUpdateReqVO updateReqVO = randomPojo(CodegenUpdateReqVO.class,
o -> o.getTable().setId(table.getId())
.setTemplateType(CodegenTemplateTypeEnum.ONE.getType())
.setScene(CodegenSceneEnum.ADMIN.getScene()));
CodegenColumnSaveReqVO columnVO01 = randomPojo(CodegenColumnSaveReqVO.class,
o -> o.setId(column01.getId()).setTableId(table.getId()));
CodegenColumnSaveReqVO columnVO02 = randomPojo(CodegenColumnSaveReqVO.class,
o -> o.setId(column02.getId()).setTableId(table.getId()));
updateReqVO.setColumns(Arrays.asList(columnVO01, columnVO02));
// 调用
codegenService.updateCodegen(updateReqVO);
// 断言
CodegenTableDO dbTable = codegenTableMapper.selectById(table.getId());
assertPojoEquals(updateReqVO.getTable(), dbTable);
List<CodegenColumnDO> dbColumns = codegenColumnMapper.selectList();
assertEquals(2, dbColumns.size());
assertPojoEquals(columnVO01, dbColumns.get(0));
assertPojoEquals(columnVO02, dbColumns.get(1));
}
@Test
public void testSyncCodegenFromDB() {
// mock 数据CodegenTableDO
CodegenTableDO table = randomPojo(CodegenTableDO.class, o -> o.setTableName("t_yunai")
.setDataSourceConfigId(1L).setScene(CodegenSceneEnum.ADMIN.getScene()));
codegenTableMapper.insert(table);
CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId())
.setColumnName("id"));
codegenColumnMapper.insert(column01);
CodegenColumnDO column02 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId())
.setColumnName("name"));
codegenColumnMapper.insert(column02);
// 准备参数
Long tableId = table.getId();
// mock 方法TableInfo
TableInfo tableInfo = mock(TableInfo.class);
when(databaseTableService.getTable(eq(1L), eq("t_yunai")))
.thenReturn(tableInfo);
when(tableInfo.getComment()).thenReturn("芋艿");
// mock 方法TableInfo fields
TableField field01 = mock(TableField.class);
when(field01.getComment()).thenReturn("主键");
TableField field03 = mock(TableField.class);
when(field03.getComment()).thenReturn("分类");
List<TableField> fields = Arrays.asList(field01, field03);
when(tableInfo.getFields()).thenReturn(fields);
when(databaseTableService.getTable(eq(1L), eq("t_yunai")))
.thenReturn(tableInfo);
// mock 方法CodegenTableDO
List<CodegenColumnDO> newColumns = randomPojoList(CodegenColumnDO.class);
when(codegenBuilder.buildColumns(eq(table.getId()), argThat(tableFields -> {
assertEquals(2, tableFields.size());
assertSame(tableInfo.getFields(), tableFields);
return true;
}))).thenReturn(newColumns);
// 调用
codegenService.syncCodegenFromDB(tableId);
// 断言
List<CodegenColumnDO> dbColumns = codegenColumnMapper.selectList();
assertEquals(newColumns.size(), dbColumns.size());
assertPojoEquals(newColumns.get(0), dbColumns.get(0));
assertPojoEquals(newColumns.get(1), dbColumns.get(1));
}
@Test
public void testDeleteCodegen_notExists() {
assertServiceException(() -> codegenService.deleteCodegen(randomLongId()),
CODEGEN_TABLE_NOT_EXISTS);
}
@Test
public void testDeleteCodegen_success() {
// mock 数据
CodegenTableDO table = randomPojo(CodegenTableDO.class,
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene()));
codegenTableMapper.insert(table);
CodegenColumnDO column = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()));
codegenColumnMapper.insert(column);
// 准备参数
Long tableId = table.getId();
// 调用
codegenService.deleteCodegen(tableId);
// 断言
assertNull(codegenTableMapper.selectById(tableId));
assertEquals(0, codegenColumnMapper.selectList().size());
}
@Test
public void testGetCodegenTableList() {
// mock 数据
CodegenTableDO table01 = randomPojo(CodegenTableDO.class,
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene()));
codegenTableMapper.insert(table01);
CodegenTableDO table02 = randomPojo(CodegenTableDO.class,
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene()));
codegenTableMapper.insert(table02);
// 准备参数
Long dataSourceConfigId = table01.getDataSourceConfigId();
// 调用
List<CodegenTableDO> result = codegenService.getCodegenTableList(dataSourceConfigId);
// 断言
assertEquals(1, result.size());
assertPojoEquals(table01, result.get(0));
}
@Test
public void testGetCodegenTablePage() {
// mock 数据
CodegenTableDO tableDO = randomPojo(CodegenTableDO.class, o -> {
o.setTableName("t_yunai");
o.setTableComment("芋艿");
o.setClassName("SystemYunai");
o.setCreateTime(buildTime(2021, 3, 10));
}).setScene(CodegenSceneEnum.ADMIN.getScene());
codegenTableMapper.insert(tableDO);
// 测试 tableName 不匹配
codegenTableMapper.insert(cloneIgnoreId(tableDO, o -> o.setTableName(randomString())));
// 测试 tableComment 不匹配
codegenTableMapper.insert(cloneIgnoreId(tableDO, o -> o.setTableComment(randomString())));
// 测试 className 不匹配
codegenTableMapper.insert(cloneIgnoreId(tableDO, o -> o.setClassName(randomString())));
// 测试 createTime 不匹配
codegenTableMapper.insert(cloneIgnoreId(tableDO, logDO -> logDO.setCreateTime(buildTime(2021, 4, 10))));
// 准备参数
CodegenTablePageReqVO reqVO = new CodegenTablePageReqVO();
reqVO.setTableName("yunai");
reqVO.setTableComment("芋");
reqVO.setClassName("Yunai");
reqVO.setCreateTime(buildBetweenTime(2021, 3, 1, 2021, 3, 31));
// 调用
PageResult<CodegenTableDO> pageResult = codegenService.getCodegenTablePage(reqVO);
// 断言,只查到了一条符合条件的
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(tableDO, pageResult.getList().get(0));
}
@Test
public void testGetCodegenTable() {
// mock 数据
CodegenTableDO tableDO = randomPojo(CodegenTableDO.class, o -> o.setScene(CodegenSceneEnum.ADMIN.getScene()));
codegenTableMapper.insert(tableDO);
// 准备参数
Long id = tableDO.getId();
// 调用
CodegenTableDO result = codegenService.getCodegenTable(id);
// 断言
assertPojoEquals(tableDO, result);
}
@Test
public void testGetCodegenColumnListByTableId() {
// mock 数据
CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class);
codegenColumnMapper.insert(column01);
CodegenColumnDO column02 = randomPojo(CodegenColumnDO.class);
codegenColumnMapper.insert(column02);
// 准备参数
Long tableId = column01.getTableId();
// 调用
List<CodegenColumnDO> result = codegenService.getCodegenColumnListByTableId(tableId);
// 断言
assertEquals(1, result.size());
assertPojoEquals(column01, result.get(0));
}
@Test
public void testGenerationCodes_tableNotExists() {
assertServiceException(() -> codegenService.generationCodes(randomLongId()),
CODEGEN_TABLE_NOT_EXISTS);
}
@Test
public void testGenerationCodes_columnNotExists() {
// mock 数据CodegenTableDO
CodegenTableDO table = randomPojo(CodegenTableDO.class,
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())
.setTemplateType(CodegenTemplateTypeEnum.MASTER_NORMAL.getType()));
codegenTableMapper.insert(table);
// 准备参数
Long tableId = table.getId();
// 调用,并断言
assertServiceException(() -> codegenService.generationCodes(tableId),
CODEGEN_COLUMN_NOT_EXISTS);
}
@Test
public void testGenerationCodes_sub_tableNotExists() {
// mock 数据CodegenTableDO
CodegenTableDO table = randomPojo(CodegenTableDO.class,
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())
.setTemplateType(CodegenTemplateTypeEnum.MASTER_NORMAL.getType()));
codegenTableMapper.insert(table);
// mock 数据CodegenColumnDO
CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()));
codegenColumnMapper.insert(column01);
// 准备参数
Long tableId = table.getId();
// 调用,并断言
assertServiceException(() -> codegenService.generationCodes(tableId),
CODEGEN_MASTER_GENERATION_FAIL_NO_SUB_TABLE);
}
@Test
public void testGenerationCodes_sub_columnNotExists() {
// mock 数据CodegenTableDO
CodegenTableDO table = randomPojo(CodegenTableDO.class,
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())
.setTemplateType(CodegenTemplateTypeEnum.MASTER_NORMAL.getType()));
codegenTableMapper.insert(table);
// mock 数据CodegenColumnDO
CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()));
codegenColumnMapper.insert(column01);
// mock 数据sub CodegenTableDO
CodegenTableDO subTable = randomPojo(CodegenTableDO.class,
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
.setMasterTableId(table.getId()));
codegenTableMapper.insert(subTable);
// 准备参数
Long tableId = table.getId();
// 调用,并断言
assertServiceException(() -> codegenService.generationCodes(tableId),
CODEGEN_SUB_COLUMN_NOT_EXISTS, subTable.getId());
}
@Test
public void testGenerationCodes_one_success() {
// mock 数据CodegenTableDO
CodegenTableDO table = randomPojo(CodegenTableDO.class,
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())
.setTemplateType(CodegenTemplateTypeEnum.ONE.getType()));
codegenTableMapper.insert(table);
// mock 数据CodegenColumnDO
CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()));
codegenColumnMapper.insert(column01);
CodegenColumnDO column02 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()));
codegenColumnMapper.insert(column02);
// mock 执行生成
Map<String, String> codes = MapUtil.of(randomString(), randomString());
when(codegenEngine.execute(eq(table), argThat(columns -> {
assertEquals(2, columns.size());
assertEquals(column01, columns.get(0));
assertEquals(column02, columns.get(1));
return true;
}), isNull(), isNull())).thenReturn(codes);
// 准备参数
Long tableId = table.getId();
// 调用
Map<String, String> result = codegenService.generationCodes(tableId);
// 断言
assertSame(codes, result);
}
@Test
public void testGenerationCodes_master_success() {
// mock 数据CodegenTableDO
CodegenTableDO table = randomPojo(CodegenTableDO.class,
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())
.setTemplateType(CodegenTemplateTypeEnum.MASTER_NORMAL.getType()));
codegenTableMapper.insert(table);
// mock 数据CodegenColumnDO
CodegenColumnDO column01 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()));
codegenColumnMapper.insert(column01);
CodegenColumnDO column02 = randomPojo(CodegenColumnDO.class, o -> o.setTableId(table.getId()));
codegenColumnMapper.insert(column02);
// mock 数据sub CodegenTableDO
CodegenTableDO subTable = randomPojo(CodegenTableDO.class,
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
.setMasterTableId(table.getId())
.setSubJoinColumnId(1024L));
codegenTableMapper.insert(subTable);
// mock 数据sub CodegenColumnDO
CodegenColumnDO subColumn01 = randomPojo(CodegenColumnDO.class, o -> o.setId(1024L).setTableId(subTable.getId()));
codegenColumnMapper.insert(subColumn01);
// mock 执行生成
Map<String, String> codes = MapUtil.of(randomString(), randomString());
when(codegenEngine.execute(eq(table), argThat(columns -> {
assertEquals(2, columns.size());
assertEquals(column01, columns.get(0));
assertEquals(column02, columns.get(1));
return true;
}), argThat(tables -> {
assertEquals(1, tables.size());
assertPojoEquals(subTable, tables.get(0));
return true;
}), argThat(columns -> {
assertEquals(1, columns.size());
assertPojoEquals(subColumn01, columns.size());
return true;
}))).thenReturn(codes);
// 准备参数
Long tableId = table.getId();
// 调用
Map<String, String> result = codegenService.generationCodes(tableId);
// 断言
assertSame(codes, result);
}
@Test
public void testGetDatabaseTableList() {
// 准备参数
Long dataSourceConfigId = randomLongId();
String name = randomString();
String comment = randomString();
// mock 方法
TableInfo tableInfo01 = mock(TableInfo.class);
when(tableInfo01.getName()).thenReturn("t_yunai");
when(tableInfo01.getComment()).thenReturn("芋艿");
TableInfo tableInfo02 = mock(TableInfo.class);
when(tableInfo02.getName()).thenReturn("t_yunai_02");
when(tableInfo02.getComment()).thenReturn("芋艿_02");
when(databaseTableService.getTableList(eq(dataSourceConfigId), eq(name), eq(comment)))
.thenReturn(ListUtil.toList(tableInfo01, tableInfo02));
// mock 数据
CodegenTableDO tableDO = randomPojo(CodegenTableDO.class,
o -> o.setScene(CodegenSceneEnum.ADMIN.getScene())
.setTableName("t_yunai_02")
.setDataSourceConfigId(dataSourceConfigId));
codegenTableMapper.insert(tableDO);
// 调用
List<DatabaseTableRespVO> result = codegenService.getDatabaseTableList(dataSourceConfigId, name, comment);
// 断言
assertEquals(1, result.size());
assertEquals("t_yunai", result.get(0).getName());
assertEquals("芋艿", result.get(0).getComment());
}
}

View File

@ -0,0 +1,89 @@
package cn.iocoder.yudao.module.infra.service.codegen.inner;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.IColumnType;
import org.apache.ibatis.type.JdbcType;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import java.util.Collections;
import java.util.List;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class CodegenBuilderTest extends BaseMockitoUnitTest {
@InjectMocks
private CodegenBuilder codegenBuilder;
@Test
public void testBuildTable() {
// 准备参数
TableInfo tableInfo = mock(TableInfo.class);
// mock 方法
when(tableInfo.getName()).thenReturn("system_user");
when(tableInfo.getComment()).thenReturn("用户");
// 调用
CodegenTableDO table = codegenBuilder.buildTable(tableInfo);
// 断言
assertEquals("system_user", table.getTableName());
assertEquals("用户", table.getTableComment());
assertEquals("system", table.getModuleName());
assertEquals("user", table.getBusinessName());
assertEquals("User", table.getClassName());
assertEquals("用户", table.getClassComment());
}
@Test
public void testBuildColumns() {
// 准备参数
Long tableId = randomLongId();
TableField tableField = mock(TableField.class);
List<TableField> tableFields = Collections.singletonList(tableField);
// mock 方法
TableField.MetaInfo metaInfo = mock(TableField.MetaInfo.class);
when(tableField.getMetaInfo()).thenReturn(metaInfo);
when(metaInfo.getJdbcType()).thenReturn(JdbcType.BIGINT);
when(tableField.getComment()).thenReturn("编号");
when(tableField.isKeyFlag()).thenReturn(true);
when(tableField.isKeyIdentityFlag()).thenReturn(true);
IColumnType columnType = mock(IColumnType.class);
when(tableField.getColumnType()).thenReturn(columnType);
when(columnType.getType()).thenReturn("Long");
when(tableField.getName()).thenReturn("id2");
when(tableField.getPropertyName()).thenReturn("id");
// 调用
List<CodegenColumnDO> columns = codegenBuilder.buildColumns(tableId, tableFields);
// 断言
assertEquals(1, columns.size());
CodegenColumnDO column = columns.get(0);
assertEquals(tableId, column.getTableId());
assertEquals("id2", column.getColumnName());
assertEquals("BIGINT", column.getDataType());
assertEquals("编号", column.getColumnComment());
assertFalse(column.getNullable());
assertTrue(column.getPrimaryKey());
assertTrue(column.getAutoIncrement());
assertEquals(1, column.getOrdinalPosition());
assertEquals("Long", column.getJavaType());
assertEquals("id", column.getJavaField());
assertNull(column.getDictType());
assertNotNull(column.getExample());
assertFalse(column.getCreateOperation());
assertTrue(column.getUpdateOperation());
assertFalse(column.getListOperation());
assertEquals("=", column.getListOperationCondition());
assertTrue(column.getListOperationResult());
assertEquals("input", column.getHtmlType());
}
}

View File

@ -0,0 +1,127 @@
package cn.iocoder.yudao.module.infra.service.codegen.inner;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties;
import org.junit.jupiter.api.BeforeEach;
import org.mockito.InjectMocks;
import org.mockito.Spy;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* {@link CodegenEngine}
*
* @author
*/
public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest {
@InjectMocks
protected CodegenEngine codegenEngine;
@Spy
protected CodegenProperties codegenProperties = new CodegenProperties()
.setBasePackage("cn.iocoder.yudao");
@BeforeEach
public void setUp() {
codegenEngine.initGlobalBindingMap();
}
protected static CodegenTableDO getTable(String name) {
String content = ResourceUtil.readUtf8Str("codegen/table/" + name + ".json");
return JsonUtils.parseObject(content, "table", CodegenTableDO.class);
}
protected static List<CodegenColumnDO> getColumnList(String name) {
String content = ResourceUtil.readUtf8Str("codegen/table/" + name + ".json");
List<CodegenColumnDO> list = JsonUtils.parseArray(content, "columns", CodegenColumnDO.class);
list.forEach(column -> {
if (column.getNullable() == null) {
column.setNullable(false);
}
if (column.getCreateOperation() == null) {
column.setCreateOperation(false);
}
if (column.getUpdateOperation() == null) {
column.setUpdateOperation(false);
}
if (column.getListOperation() == null) {
column.setListOperation(false);
}
if (column.getListOperationResult() == null) {
column.setListOperationResult(false);
}
});
return list;
}
@SuppressWarnings("rawtypes")
protected static void assertResult(Map<String, String> result, String path) {
String assertContent = ResourceUtil.readUtf8Str(path + "/assert.json");
List<HashMap> asserts = JsonUtils.parseArray(assertContent, HashMap.class);
assertEquals(asserts.size(), result.size());
// 校验每个文件
asserts.forEach(assertMap -> {
String contentPath = (String) assertMap.get("contentPath");
String filePath = (String) assertMap.get("filePath");
String content = ResourceUtil.readUtf8Str(path + "/" + contentPath);
assertEquals(content, result.get(filePath), filePath + ":不匹配");
});
}
// ==================== 调试专用 ====================
/**
* 使
*
* @param result
* @param path
*/
protected void writeFile(Map<String, String> result, String path) {
// 生成压缩包
String[] paths = result.keySet().toArray(new String[0]);
ByteArrayInputStream[] ins = result.values().stream().map(IoUtil::toUtf8Stream).toArray(ByteArrayInputStream[]::new);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ZipUtil.zip(outputStream, paths, ins);
// 写入文件
FileUtil.writeBytes(outputStream.toByteArray(), path);
}
/**
* 使
*
* @param result
* @param basePath
*/
protected void writeResult(Map<String, String> result, String basePath) {
// 写入文件内容
List<Map<String, String>> asserts = new ArrayList<>();
result.forEach((filePath, fileContent) -> {
String lastFilePath = StrUtil.subAfter(filePath, '/', true);
String contentPath = StrUtil.subAfter(lastFilePath, '.', true)
+ '/' + StrUtil.subBefore(lastFilePath, '.', true);
asserts.add(MapUtil.<String, String>builder().put("filePath", filePath)
.put("contentPath", contentPath).build());
FileUtil.writeUtf8String(fileContent, basePath + "/" + contentPath);
});
// 写入 assert.json 文件
FileUtil.writeUtf8String(JsonUtils.toJsonPrettyString(asserts), basePath +"/assert.json");
}
}

View File

@ -0,0 +1,98 @@
package cn.iocoder.yudao.module.infra.service.codegen.inner;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
// TODO @puhui999单测需要 fix
/**
* {@link CodegenEngine} Vue2 + Element UI
*
* @author
*/
@Disabled
public class CodegenEngineVue2Test extends CodegenEngineAbstractTest {
@Test
public void testExecute_vue2_one() {
// 准备参数
CodegenTableDO table = getTable("student")
.setFrontType(CodegenFrontTypeEnum.VUE2.getType())
.setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
List<CodegenColumnDO> columns = getColumnList("student");
// 调用
Map<String, String> result = codegenEngine.execute(table, columns, null, null);
// 断言
assertResult(result, "codegen/vue2_one");
// writeResult(result, "/root/ruoyi-vue-pro/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_one");
}
@Test
public void testExecute_vue2_tree() {
// 准备参数
CodegenTableDO table = getTable("category")
.setFrontType(CodegenFrontTypeEnum.VUE2.getType())
.setTemplateType(CodegenTemplateTypeEnum.TREE.getType());
List<CodegenColumnDO> columns = getColumnList("category");
// 调用
Map<String, String> result = codegenEngine.execute(table, columns, null, null);
// 断言
assertResult(result, "codegen/vue2_tree");
// writeResult(result, "/root/ruoyi-vue-pro/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue2_tree");
// writeFile(result, "/Users/yunai/test/demo66.zip");
}
@Test
public void testExecute_vue2_master_normal() {
testExecute_vue2_master(CodegenTemplateTypeEnum.MASTER_NORMAL, "codegen/vue2_master_normal");
}
@Test
public void testExecute_vue2_master_erp() {
testExecute_vue2_master(CodegenTemplateTypeEnum.MASTER_ERP, "codegen/vue2_master_erp");
}
@Test
public void testExecute_vue2_master_inner() {
testExecute_vue2_master(CodegenTemplateTypeEnum.MASTER_INNER, "codegen/vue2_master_inner");
}
private void testExecute_vue2_master(CodegenTemplateTypeEnum templateType,
String path) {
// 准备参数
CodegenTableDO table = getTable("student")
.setFrontType(CodegenFrontTypeEnum.VUE2.getType())
.setTemplateType(templateType.getType());
List<CodegenColumnDO> columns = getColumnList("student");
// 准备参数(子表)
CodegenTableDO contactTable = getTable("contact")
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
.setFrontType(CodegenFrontTypeEnum.VUE2.getType())
.setSubJoinColumnId(100L).setSubJoinMany(true);
List<CodegenColumnDO> contactColumns = getColumnList("contact");
// 准备参数(班主任)
CodegenTableDO teacherTable = getTable("teacher")
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
.setFrontType(CodegenFrontTypeEnum.VUE2.getType())
.setSubJoinColumnId(200L).setSubJoinMany(false);
List<CodegenColumnDO> teacherColumns = getColumnList("teacher");
// 调用
Map<String, String> result = codegenEngine.execute(table, columns,
Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns));
// 断言
assertResult(result, path);
// writeResult(result, "/root/ruoyi-vue-pro/yudao-module-infra/yudao-module-infra-biz/src/test/resources/" + path);
// writeFile(result, "/Users/yunai/test/demo11.zip");
}
}

View File

@ -0,0 +1,95 @@
package cn.iocoder.yudao.module.infra.service.codegen.inner;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* {@link CodegenEngine} Vue2 + Element Plus
*
* @author
*/
public class CodegenEngineVue3Test extends CodegenEngineAbstractTest {
@Test
public void testExecute_vue3_one() {
// 准备参数
CodegenTableDO table = getTable("student")
.setFrontType(CodegenFrontTypeEnum.VUE3.getType())
.setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
List<CodegenColumnDO> columns = getColumnList("student");
// 调用
Map<String, String> result = codegenEngine.execute(table, columns, null, null);
// 断言
assertResult(result, "codegen/vue3_one");
// writeResult(result, "/root/ruoyi-vue-pro/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_one");
}
@Test
public void testExecute_vue3_tree() {
// 准备参数
CodegenTableDO table = getTable("category")
.setFrontType(CodegenFrontTypeEnum.VUE3.getType())
.setTemplateType(CodegenTemplateTypeEnum.TREE.getType());
List<CodegenColumnDO> columns = getColumnList("category");
// 调用
Map<String, String> result = codegenEngine.execute(table, columns, null, null);
// 断言
assertResult(result, "codegen/vue3_tree");
// writeResult(result, "/root/ruoyi-vue-pro/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/vue3_tree");
// writeFile(result, "/Users/yunai/test/demo66.zip");
}
@Test
public void testExecute_vue3_master_normal() {
testExecute_vue3_master(CodegenTemplateTypeEnum.MASTER_NORMAL, "codegen/vue3_master_normal");
}
@Test
public void testExecute_vue3_master_erp() {
testExecute_vue3_master(CodegenTemplateTypeEnum.MASTER_ERP, "codegen/vue3_master_erp");
}
@Test
public void testExecute_vue3_master_inner() {
testExecute_vue3_master(CodegenTemplateTypeEnum.MASTER_INNER, "codegen/vue3_master_inner");
}
private void testExecute_vue3_master(CodegenTemplateTypeEnum templateType,
String path) {
// 准备参数
CodegenTableDO table = getTable("student")
.setFrontType(CodegenFrontTypeEnum.VUE3.getType())
.setTemplateType(templateType.getType());
List<CodegenColumnDO> columns = getColumnList("student");
// 准备参数(子表)
CodegenTableDO contactTable = getTable("contact")
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
.setFrontType(CodegenFrontTypeEnum.VUE3.getType())
.setSubJoinColumnId(100L).setSubJoinMany(true);
List<CodegenColumnDO> contactColumns = getColumnList("contact");
// 准备参数(班主任)
CodegenTableDO teacherTable = getTable("teacher")
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
.setFrontType(CodegenFrontTypeEnum.VUE3.getType())
.setSubJoinColumnId(200L).setSubJoinMany(false);
List<CodegenColumnDO> teacherColumns = getColumnList("teacher");
// 调用
Map<String, String> result = codegenEngine.execute(table, columns,
Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns));
// 断言
assertResult(result, path);
// writeResult(result, "/root/ruoyi-vue-pro/yudao-module-infra/yudao-module-infra-biz/src/test/resources/" + path);
// writeFile(result, "/Users/yunai/test/demo11.zip");
}
}

View File

@ -1,4 +0,0 @@
/**
*
*/
package cn.iocoder.yudao.module.infra.service.codegen;

View File

@ -12,9 +12,10 @@ import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.time.Duration;
import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
@ -73,6 +74,26 @@ public class ApiAccessLogServiceImplTest extends BaseDbUnitTest {
assertPojoEquals(apiAccessLogDO, pageResult.getList().get(0));
}
@Test
public void testCleanJobLog() {
// mock 数据
ApiAccessLogDO log01 = randomPojo(ApiAccessLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-3))));
apiAccessLogMapper.insert(log01);
ApiAccessLogDO log02 = randomPojo(ApiAccessLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-1))));
apiAccessLogMapper.insert(log02);
// 准备参数
Integer exceedDay = 2;
Integer deleteLimit = 1;
// 调用
Integer count = apiAccessLogService.cleanAccessLog(exceedDay, deleteLimit);
// 断言
assertEquals(1, count);
List<ApiAccessLogDO> logs = apiAccessLogMapper.selectList();
assertEquals(1, logs.size());
assertEquals(log02, logs.get(0));
}
@Test
public void testCreateApiAccessLog() {
// 准备参数

View File

@ -12,10 +12,11 @@ import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.time.Duration;
import java.util.List;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
@ -122,7 +123,7 @@ public class ApiErrorLogServiceImplTest extends BaseDbUnitTest {
// 调用,并断言异常
assertServiceException(() ->
apiErrorLogService.updateApiErrorLogProcess(id, processStatus, processUserId),
apiErrorLogService.updateApiErrorLogProcess(id, processStatus, processUserId),
API_ERROR_LOG_PROCESSED);
}
@ -135,8 +136,28 @@ public class ApiErrorLogServiceImplTest extends BaseDbUnitTest {
// 调用,并断言异常
assertServiceException(() ->
apiErrorLogService.updateApiErrorLogProcess(id, processStatus, processUserId),
apiErrorLogService.updateApiErrorLogProcess(id, processStatus, processUserId),
API_ERROR_LOG_NOT_FOUND);
}
@Test
public void testCleanJobLog() {
// mock 数据
ApiErrorLogDO log01 = randomPojo(ApiErrorLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-3))));
apiErrorLogMapper.insert(log01);
ApiErrorLogDO log02 = randomPojo(ApiErrorLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-1))));
apiErrorLogMapper.insert(log02);
// 准备参数
Integer exceedDay = 2;
Integer deleteLimit = 1;
// 调用
Integer count = apiErrorLogService.cleanErrorLog(exceedDay, deleteLimit);
// 断言
assertEquals(1, count);
List<ApiErrorLogDO> logs = apiErrorLogMapper.selectList();
assertEquals(1, logs.size());
assertEquals(log02, logs.get(0));
}
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -197,11 +197,11 @@ const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
name: null,
birthday: null,
name: undefined,
birthday: undefined,
birthday: [],
sex: null,
enabled: null,
sex: undefined,
enabled: undefined,
createTime: []
})
const queryFormRef = ref() // 搜索的表单

View File

@ -192,11 +192,11 @@ const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
name: null,
birthday: null,
name: undefined,
birthday: undefined,
birthday: [],
sex: null,
enabled: null,
sex: undefined,
enabled: undefined,
createTime: []
})
const queryFormRef = ref() // 搜索的表单

View File

@ -177,11 +177,11 @@ const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
name: null,
birthday: null,
name: undefined,
birthday: undefined,
birthday: [],
sex: null,
enabled: null,
sex: undefined,
enabled: undefined,
createTime: []
})
const queryFormRef = ref() // 搜索的表单

View File

@ -177,11 +177,11 @@ const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
name: null,
birthday: null,
name: undefined,
birthday: undefined,
birthday: [],
sex: null,
enabled: null,
sex: undefined,
enabled: undefined,
createTime: []
})
const queryFormRef = ref() // 搜索的表单

View File

@ -106,7 +106,7 @@ const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中
const list = ref([]) // 列表的数据
const queryParams = reactive({
name: null
name: undefined
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中

View File

@ -8,3 +8,5 @@ DELETE FROM "infra_api_error_log";
DELETE FROM "infra_file_config";
DELETE FROM "infra_test_demo";
DELETE FROM "infra_data_source_config";
DELETE FROM "infra_codegen_table";
DELETE FROM "infra_codegen_column";

View File

@ -170,3 +170,59 @@ CREATE TABLE IF NOT EXISTS "infra_data_source_config" (
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '数据源配置表';
CREATE TABLE IF NOT EXISTS "infra_codegen_table" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"data_source_config_id" bigint not null,
"scene" tinyint not null DEFAULT 1,
"table_name" varchar(200) NOT NULL,
"table_comment" varchar(500) NOT NULL,
"remark" varchar(500) NOT NULL,
"module_name" varchar(30) NOT NULL,
"business_name" varchar(30) NOT NULL,
"class_name" varchar(100) NOT NULL,
"class_comment" varchar(50) NOT NULL,
"author" varchar(50) NOT NULL,
"template_type" tinyint not null DEFAULT 1,
"front_type" tinyint not null,
"parent_menu_id" bigint not null,
"master_table_id" bigint not null,
"sub_join_column_id" bigint not null,
"sub_join_many" bit not null,
"tree_parent_column_id" bigint not null,
"tree_name_column_id" bigint not null,
"creator" varchar(64) DEFAULT '',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '代码生成表定义表';
CREATE TABLE IF NOT EXISTS "infra_codegen_column" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"table_id" bigint not null,
"column_name" varchar(200) NOT NULL,
"data_type" varchar(100) NOT NULL,
"column_comment" varchar(500) NOT NULL,
"nullable" tinyint not null,
"primary_key" tinyint not null,
"auto_increment" varchar(5) not null,
"ordinal_position" int not null,
"java_type" varchar(32) NOT NULL,
"java_field" varchar(64) NOT NULL,
"dict_type" varchar(200) NOT NULL,
"example" varchar(64) NOT NULL,
"create_operation" bit not null,
"update_operation" bit not null,
"list_operation" bit not null,
"list_operation_condition" varchar(32) not null,
"list_operation_result" bit not null,
"html_type" varchar(32) NOT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '代码生成表字段定义表';

View File

@ -23,7 +23,7 @@ public class SocialUserPageReqVO extends PageParam {
@Schema(description = "用户昵称", example = "李四")
private String nickname;
@Schema(description = "社交 openid", example = "oz-Jdt0kd_jdhUxJHQdBJMlOFN7w\n")
@Schema(description = "社交 openid", example = "oz-Jdt0kd_jdhUxJHQdBJMlOFN7w")
private String openid;
@Schema(description = "创建时间")

View File

@ -14,8 +14,7 @@ import java.util.Set;
@Data
public class UserSaveReqVO {
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "用户编号不能为空")
@Schema(description = "用户编号", example = "1024")
private Long id;
@Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao")

View File

@ -48,7 +48,7 @@ public interface NotifySendService {
String templateCode, Map<String, Object> templateParams);
default void sendBatchNotify(List<String> mobiles, List<Long> userIds, Integer userType,
String templateCode, Map<String, Object> templateParams) {
String templateCode, Map<String, Object> templateParams) {
throw new UnsupportedOperationException("暂时不支持该操作,感兴趣可以实现该功能哟!");
}

View File

@ -6,6 +6,7 @@ import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSaveReqVO;
@ -27,7 +28,6 @@ import javax.annotation.Resource;
import java.util.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
@ -198,7 +198,7 @@ public class RoleServiceImpl implements RoleService {
}
// 这里采用 for 循环从缓存中获取,主要考虑 Spring CacheManager 无法批量操作的问题
RoleServiceImpl self = getSelf();
return convertList(ids, self::getRoleFromCache);
return CollectionUtils.convertList(ids, self::getRoleFromCache);
}
@Override

View File

@ -1,49 +0,0 @@
package cn.iocoder.yudao.module.system.service.permission.bo;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
* Request BO
*
* @author
*/
@Data
public class RoleCreateReqBO {
/**
*
*/
@NotNull(message = "租户编号不能为空")
private Long tenantId;
/**
*
*/
@NotBlank(message = "角色名称不能为空")
@Size(max = 30, message = "角色名称长度不能超过30个字符")
private String name;
/**
*
*/
@NotBlank(message = "角色标志不能为空")
@Size(max = 100, message = "角色标志长度不能超过100个字符")
private String code;
/**
*
*/
@NotNull(message = "显示顺序不能为空")
private Integer sort;
/**
*
*/
@NotNull(message = "角色类型不能为空")
private Integer type;
}

View File

@ -142,7 +142,8 @@ public class SocialClientServiceImpl implements SocialClientService {
* @param userType
* @return AuthRequest
*/
private AuthRequest buildAuthRequest(Integer socialType, Integer userType) {
@VisibleForTesting
AuthRequest buildAuthRequest(Integer socialType, Integer userType) {
// 1. 先查找默认的配置项,从 application-*.yaml 中读取
AuthRequest request = authRequestFactory.get(SocialTypeEnum.valueOfType(socialType).getSource());
Assert.notNull(request, String.format("社交平台(%d) 不存在", socialType));
@ -180,7 +181,8 @@ public class SocialClientServiceImpl implements SocialClientService {
* @param userType
* @return WxMpService
*/
private WxMpService getWxMpService(Integer userType) {
@VisibleForTesting
WxMpService getWxMpService(Integer userType) {
// 第一步,查询 DB 的配置项,获得对应的 WxMpService 对象
SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(
SocialTypeEnum.WECHAT_MP.getType(), userType);
@ -198,7 +200,7 @@ public class SocialClientServiceImpl implements SocialClientService {
* @param clientSecret secret
* @return WxMpService
*/
private WxMpService buildWxMpService(String clientId, String clientSecret) {
public WxMpService buildWxMpService(String clientId, String clientSecret) {
// 第一步,创建 WxMpRedisConfigImpl 对象
WxMpRedisConfigImpl configStorage = new WxMpRedisConfigImpl(
new RedisTemplateWxRedisOps(stringRedisTemplate),
@ -231,7 +233,8 @@ public class SocialClientServiceImpl implements SocialClientService {
* @param userType
* @return WxMpService
*/
private WxMaService getWxMaService(Integer userType) {
@VisibleForTesting
WxMaService getWxMaService(Integer userType) {
// 第一步,查询 DB 的配置项,获得对应的 WxMaService 对象
SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(
SocialTypeEnum.WECHAT_MINI_APP.getType(), userType);
@ -311,7 +314,6 @@ public class SocialClientServiceImpl implements SocialClientService {
* @param userType
* @param socialType
*/
@VisibleForTesting
private void validateSocialClientUnique(Long id, Integer userType, Integer socialType) {
SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(
socialType, userType);

View File

@ -59,7 +59,7 @@ public class SocialUserServiceImpl implements SocialUserService {
}
@Override
@Transactional
@Transactional(rollbackFor = Exception.class)
public String bindSocialUser(SocialUserBindReqDTO reqDTO) {
// 获得社交用户
SocialUserDO socialUser = authSocialUser(reqDTO.getSocialType(), reqDTO.getUserType(),
@ -108,7 +108,6 @@ public class SocialUserServiceImpl implements SocialUserService {
return new SocialUserRespDTO(socialUser.getOpenid(), socialUserBind.getUserId());
}
// TODO 芋艿:调整下单测
/**
*
* {@link ServiceException}

View File

@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
@ -172,5 +173,18 @@ class NotifySendServiceImplTest extends BaseMockitoUnitTest {
NOTIFY_SEND_TEMPLATE_PARAM_MISS, "code");
}
@Test
public void testSendBatchNotify() {
// 准备参数
// mock 方法
// 调用
UnsupportedOperationException exception = Assertions.assertThrows(
UnsupportedOperationException.class,
() -> notifySendService.sendBatchNotify(null, null, null, null, null)
);
// 断言
assertEquals("暂时不支持该操作,感兴趣可以实现该功能哟!", exception.getMessage());
}
}

View File

@ -13,6 +13,8 @@ import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@ -185,6 +187,40 @@ public class MenuServiceImplTest extends BaseDbUnitTest {
assertPojoEquals(menu100, result.get(0));
}
@Test
public void testGetMenuIdListByPermissionFromCache() {
// mock 数据
MenuDO menu100 = randomPojo(MenuDO.class);
menuMapper.insert(menu100);
MenuDO menu101 = randomPojo(MenuDO.class);
menuMapper.insert(menu101);
// 准备参数
String permission = menu100.getPermission();
// 调用
List<Long> ids = menuService.getMenuIdListByPermissionFromCache(permission);
// 断言
assertEquals(1, ids.size());
assertEquals(menu100.getId(), ids.get(0));
}
@Test
public void testGetMenuList_ids() {
// mock 数据
MenuDO menu100 = randomPojo(MenuDO.class);
menuMapper.insert(menu100);
MenuDO menu101 = randomPojo(MenuDO.class);
menuMapper.insert(menu101);
// 准备参数
Collection<Long> ids = Collections.singleton(menu100.getId());
// 调用
List<MenuDO> list = menuService.getMenuList(ids);
// 断言
assertEquals(1, list.size());
assertPojoEquals(menu100, list.get(0));
}
@Test
public void testGetMenu() {
// mock 数据

View File

@ -233,6 +233,39 @@ public class RoleServiceImplTest extends BaseDbUnitTest {
assertPojoEquals(dbRole01, list.get(0));
}
@Test
public void testGetRoleList() {
// mock 数据
RoleDO dbRole01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
roleMapper.insert(dbRole01);
RoleDO dbRole02 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
roleMapper.insert(dbRole02);
// 调用
List<RoleDO> list = roleService.getRoleList();
// 断言
assertEquals(2, list.size());
assertPojoEquals(dbRole01, list.get(0));
assertPojoEquals(dbRole02, list.get(1));
}
@Test
public void testGetRoleList_ids() {
// mock 数据
RoleDO dbRole01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
roleMapper.insert(dbRole01);
RoleDO dbRole02 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
roleMapper.insert(dbRole02);
// 准备参数
Collection<Long> ids = singleton(dbRole01.getId());
// 调用
List<RoleDO> list = roleService.getRoleList(ids);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbRole01, list.get(0));
}
@Test
public void testGetRoleListFromCache() {
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.service.sensitiveword;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordSaveVO;
@ -13,6 +14,8 @@ import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
@ -78,6 +81,35 @@ public class SensitiveWordServiceImplTest extends BaseDbUnitTest {
assertNotNull(sensitiveWordService.getTagSensitiveWordTries().get("测试"));
}
@Test
public void testRefreshLocalCache() {
// mock 数据
SensitiveWordDO wordDO1 = randomPojo(SensitiveWordDO.class, o -> o.setName("傻瓜")
.setTags(singletonList("论坛")).setStatus(CommonStatusEnum.ENABLE.getStatus()));
wordDO1.setUpdateTime(LocalDateTime.now());
sensitiveWordMapper.insert(wordDO1);
sensitiveWordService.initLocalCache();
// mock 数据 ②
SensitiveWordDO wordDO2 = randomPojo(SensitiveWordDO.class, o -> o.setName("笨蛋")
.setTags(singletonList("蔬菜")).setStatus(CommonStatusEnum.ENABLE.getStatus()));
wordDO2.setUpdateTime(LocalDateTimeUtils.addTime(Duration.ofMinutes(1))); // 避免时间相同
sensitiveWordMapper.insert(wordDO2);
// 调用
sensitiveWordService.refreshLocalCache();
// 断言 sensitiveWordTagsCache 缓存
assertEquals(SetUtils.asSet("论坛", "蔬菜"), sensitiveWordService.getSensitiveWordTagSet());
// 断言 sensitiveWordCache
assertEquals(2, sensitiveWordService.getSensitiveWordCache().size());
assertPojoEquals(wordDO1, sensitiveWordService.getSensitiveWordCache().get(0));
assertPojoEquals(wordDO2, sensitiveWordService.getSensitiveWordCache().get(1));
// 断言 tagSensitiveWordTries 缓存
assertNotNull(sensitiveWordService.getDefaultSensitiveWordTrie());
assertEquals(2, sensitiveWordService.getTagSensitiveWordTries().size());
assertNotNull(sensitiveWordService.getTagSensitiveWordTries().get("论坛"));
assertNotNull(sensitiveWordService.getTagSensitiveWordTries().get("蔬菜"));
}
@Test
public void testCreateSensitiveWord_success() {
// 准备参数

View File

@ -16,6 +16,7 @@ import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer;
import cn.iocoder.yudao.module.system.service.member.MemberService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
@ -35,7 +36,7 @@ import static org.mockito.Mockito.*;
public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
@InjectMocks
private SmsSendServiceImpl smsService;
private SmsSendServiceImpl smsSendService;
@Mock
private AdminUserService adminUserService;
@ -80,7 +81,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
eq(content), eq(templateParams))).thenReturn(smsLogId);
// 调用
Long resultSmsLogId = smsService.sendSingleSmsToAdmin(null, userId, templateCode, templateParams);
Long resultSmsLogId = smsSendService.sendSingleSmsToAdmin(null, userId, templateCode, templateParams);
// 断言
assertEquals(smsLogId, resultSmsLogId);
// 断言调用
@ -119,7 +120,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
eq(content), eq(templateParams))).thenReturn(smsLogId);
// 调用
Long resultSmsLogId = smsService.sendSingleSmsToMember(null, userId, templateCode, templateParams);
Long resultSmsLogId = smsSendService.sendSingleSmsToMember(null, userId, templateCode, templateParams);
// 断言
assertEquals(smsLogId, resultSmsLogId);
// 断言调用
@ -159,7 +160,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
eq(content), eq(templateParams))).thenReturn(smsLogId);
// 调用
Long resultSmsLogId = smsService.sendSingleSms(mobile, userId, userType, templateCode, templateParams);
Long resultSmsLogId = smsSendService.sendSingleSms(mobile, userId, userType, templateCode, templateParams);
// 断言
assertEquals(smsLogId, resultSmsLogId);
// 断言调用
@ -199,7 +200,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
eq(content), eq(templateParams))).thenReturn(smsLogId);
// 调用
Long resultSmsLogId = smsService.sendSingleSms(mobile, userId, userType, templateCode, templateParams);
Long resultSmsLogId = smsSendService.sendSingleSms(mobile, userId, userType, templateCode, templateParams);
// 断言
assertEquals(smsLogId, resultSmsLogId);
// 断言调用
@ -214,7 +215,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
// mock 方法
// 调用,并断言异常
assertServiceException(() -> smsService.validateSmsTemplate(templateCode),
assertServiceException(() -> smsSendService.validateSmsTemplate(templateCode),
SMS_SEND_TEMPLATE_NOT_EXISTS);
}
@ -227,7 +228,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
// mock 方法
// 调用,并断言异常
assertServiceException(() -> smsService.buildTemplateParams(template, templateParams),
assertServiceException(() -> smsSendService.buildTemplateParams(template, templateParams),
SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS, "code");
}
@ -237,10 +238,24 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
// mock 方法
// 调用,并断言异常
assertServiceException(() -> smsService.validateMobile(null),
assertServiceException(() -> smsSendService.validateMobile(null),
SMS_SEND_MOBILE_NOT_EXISTS);
}
@Test
public void testSendBatchNotify() {
// 准备参数
// mock 方法
// 调用
UnsupportedOperationException exception = Assertions.assertThrows(
UnsupportedOperationException.class,
() -> smsSendService.sendBatchSms(null, null, null, null, null)
);
// 断言
assertEquals("暂时不支持该操作,感兴趣可以实现该功能哟!", exception.getMessage());
}
@Test
@SuppressWarnings("unchecked")
public void testDoSendSms() throws Throwable {
@ -255,7 +270,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
eq(message.getTemplateParams()))).thenReturn(sendResult);
// 调用
smsService.doSendSms(message);
smsSendService.doSendSms(message);
// 断言
verify(smsLogService).updateSmsSendResult(eq(message.getLogId()),
eq(sendResult.getSuccess()), eq(sendResult.getApiCode()),
@ -274,7 +289,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
List<SmsReceiveRespDTO> receiveResults = randomPojoList(SmsReceiveRespDTO.class);
// 调用
smsService.receiveSmsStatus(channelCode, text);
smsSendService.receiveSmsStatus(channelCode, text);
// 断言
receiveResults.forEach(result -> smsLogService.updateSmsReceiveResult(eq(result.getLogId()), eq(result.getSuccess()),
eq(result.getReceiveTime()), eq(result.getErrorCode()), eq(result.getErrorCode())));

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.system.service.sms;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
@ -21,6 +22,7 @@ import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomEle;
@ -48,6 +50,19 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
@MockBean
private SmsClient smsClient;
@Test
public void testFormatSmsTemplateContent() {
// 准备参数
String content = "正在进行登录操作{operation},您的验证码是{code}";
Map<String, Object> params = MapUtil.<String, Object>builder("operation", "登录")
.put("code", "1234").build();
// 调用
String result = smsTemplateService.formatSmsTemplateContent(content, params);
// 断言
assertEquals("正在进行登录操作登录您的验证码是1234", result);
}
@Test
public void testParseTemplateContentParams() {
// 准备参数
@ -156,6 +171,34 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
assertServiceException(() -> smsTemplateService.deleteSmsTemplate(id), SMS_TEMPLATE_NOT_EXISTS);
}
@Test
public void testGetSmsTemplate() {
// mock 数据
SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO();
smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbSmsTemplate.getId();
// 调用
SmsTemplateDO smsTemplate = smsTemplateService.getSmsTemplate(id);
// 校验
assertPojoEquals(dbSmsTemplate, smsTemplate);
}
@Test
public void testGetSmsTemplateByCodeFromCache() {
// mock 数据
SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO();
smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据
// 准备参数
String code = dbSmsTemplate.getCode();
// 调用
SmsTemplateDO smsTemplate = smsTemplateService.getSmsTemplateByCodeFromCache(code);
// 校验
assertPojoEquals(dbSmsTemplate, smsTemplate);
}
@Test
public void testGetSmsTemplatePage() {
// mock 数据
@ -201,6 +244,22 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
assertPojoEquals(dbSmsTemplate, pageResult.getList().get(0));
}
@Test
public void testGetSmsTemplateCountByChannelId() {
// mock 数据
SmsTemplateDO dbSmsTemplate = randomPojo(SmsTemplateDO.class, o -> o.setChannelId(1L));
smsTemplateMapper.insert(dbSmsTemplate);
// 测试 channelId 不匹配
smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setChannelId(2L)));
// 准备参数
Long channelId = 1L;
// 调用
Long count = smsTemplateService.getSmsTemplateCountByChannelId(channelId);
// 断言
assertEquals(1, count);
}
@Test
public void testValidateSmsChannel_success() {
// 准备参数

View File

@ -1,33 +1,53 @@
package cn.iocoder.yudao.module.system.service.social;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO;
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialClientMapper;
import org.junit.jupiter.api.Disabled;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties;
import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties;
import com.xingyuv.jushauth.config.AuthConfig;
import com.xingyuv.jushauth.model.AuthResponse;
import com.xingyuv.jushauth.model.AuthUser;
import com.xingyuv.jushauth.request.AuthDefaultRequest;
import com.xingyuv.jushauth.request.AuthRequest;
import com.xingyuv.jushauth.utils.AuthStateUtils;
import com.xingyuv.justauth.AuthRequestFactory;
import me.chanjar.weixin.common.bean.WxJsapiSignature;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.core.StringRedisTemplate;
import javax.annotation.Resource;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_CLIENT_NOT_EXISTS;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
// TODO 芋艿:单测后续补充下;
/**
* {@link SocialClientServiceImpl}
*
* @author
*/
@Import(SocialClientServiceImpl.class)
@Disabled
public class SocialClientServiceImplTest extends BaseDbUnitTest {
@Resource
@ -36,10 +56,305 @@ public class SocialClientServiceImplTest extends BaseDbUnitTest {
@Resource
private SocialClientMapper socialClientMapper;
@MockBean
private AuthRequestFactory authRequestFactory;
@MockBean
private WxMpService wxMpService;
@MockBean
private WxMpProperties wxMpProperties;
@MockBean
private StringRedisTemplate stringRedisTemplate;
@MockBean
private WxMaService wxMaService;
@MockBean
private WxMaProperties wxMaProperties;
@Test
public void testGetAuthorizeUrl() {
try (MockedStatic<AuthStateUtils> authStateUtilsMock = mockStatic(AuthStateUtils.class)) {
// 准备参数
Integer socialType = SocialTypeEnum.WECHAT_MP.getType();
Integer userType = randomPojo(UserTypeEnum.class).getValue();
String redirectUri = "sss";
// mock 获得对应的 AuthRequest 实现
AuthRequest authRequest = mock(AuthRequest.class);
when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest);
// mock 方法
authStateUtilsMock.when(AuthStateUtils::createState).thenReturn("aoteman");
when(authRequest.authorize(eq("aoteman"))).thenReturn("https://www.iocoder.cn?redirect_uri=yyy");
// 调用
String url = socialClientService.getAuthorizeUrl(socialType, userType, redirectUri);
// 断言
assertEquals("https://www.iocoder.cn?redirect_uri=sss", url);
}
}
@Test
public void testAuthSocialUser_success() {
// 准备参数
Integer socialType = SocialTypeEnum.WECHAT_MP.getType();
Integer userType = randomPojo(UserTypeEnum.class).getValue();
String code = randomString();
String state = randomString();
// mock 方法AuthRequest
AuthRequest authRequest = mock(AuthRequest.class);
when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest);
// mock 方法AuthResponse
AuthUser authUser = randomPojo(AuthUser.class);
AuthResponse<?> authResponse = new AuthResponse<>(2000, null, authUser);
when(authRequest.login(argThat(authCallback -> {
assertEquals(code, authCallback.getCode());
assertEquals(state, authCallback.getState());
return true;
}))).thenReturn(authResponse);
// 调用
AuthUser result = socialClientService.getAuthUser(socialType, userType, code, state);
// 断言
assertSame(authUser, result);
}
@Test
public void testAuthSocialUser_fail() {
// 准备参数
Integer socialType = SocialTypeEnum.WECHAT_MP.getType();
Integer userType = randomPojo(UserTypeEnum.class).getValue();
String code = randomString();
String state = randomString();
// mock 方法AuthRequest
AuthRequest authRequest = mock(AuthRequest.class);
when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest);
// mock 方法AuthResponse
AuthResponse<?> authResponse = new AuthResponse<>(0, "模拟失败", null);
when(authRequest.login(argThat(authCallback -> {
assertEquals(code, authCallback.getCode());
assertEquals(state, authCallback.getState());
return true;
}))).thenReturn(authResponse);
// 调用并断言
assertServiceException(
() -> socialClientService.getAuthUser(socialType, userType, code, state),
SOCIAL_USER_AUTH_FAILURE, "模拟失败");
}
@Test
public void testBuildAuthRequest_clientNull() {
// 准备参数
Integer socialType = SocialTypeEnum.WECHAT_MP.getType();
Integer userType = randomPojo(SocialTypeEnum.class).getType();
// mock 获得对应的 AuthRequest 实现
AuthRequest authRequest = mock(AuthDefaultRequest.class);
AuthConfig authConfig = (AuthConfig) ReflectUtil.getFieldValue(authRequest, "config");
when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest);
// 调用
AuthRequest result = socialClientService.buildAuthRequest(socialType, userType);
// 断言
assertSame(authRequest, result);
assertSame(authConfig, ReflectUtil.getFieldValue(authConfig, "config"));
}
@Test
public void testBuildAuthRequest_clientDisable() {
// 准备参数
Integer socialType = SocialTypeEnum.WECHAT_MP.getType();
Integer userType = randomPojo(SocialTypeEnum.class).getType();
// mock 获得对应的 AuthRequest 实现
AuthRequest authRequest = mock(AuthDefaultRequest.class);
AuthConfig authConfig = (AuthConfig) ReflectUtil.getFieldValue(authRequest, "config");
when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest);
// mock 数据
SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())
.setUserType(userType).setSocialType(socialType));
socialClientMapper.insert(client);
// 调用
AuthRequest result = socialClientService.buildAuthRequest(socialType, userType);
// 断言
assertSame(authRequest, result);
assertSame(authConfig, ReflectUtil.getFieldValue(authConfig, "config"));
}
@Test
public void testBuildAuthRequest_clientEnable() {
// 准备参数
Integer socialType = SocialTypeEnum.WECHAT_MP.getType();
Integer userType = randomPojo(SocialTypeEnum.class).getType();
// mock 获得对应的 AuthRequest 实现
AuthConfig authConfig = mock(AuthConfig.class);
AuthRequest authRequest = mock(AuthDefaultRequest.class);
ReflectUtil.setFieldValue(authRequest, "config", authConfig);
when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest);
// mock 数据
SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())
.setUserType(userType).setSocialType(socialType));
socialClientMapper.insert(client);
// 调用
AuthRequest result = socialClientService.buildAuthRequest(socialType, userType);
// 断言
assertSame(authRequest, result);
assertNotSame(authConfig, ReflectUtil.getFieldValue(authRequest, "config"));
}
// =================== 微信公众号独有 ===================
@Test
public void testCreateWxMpJsapiSignature() throws WxErrorException {
// 准备参数
Integer userType = randomPojo(UserTypeEnum.class).getValue();
String url = randomString();
// mock 方法
WxJsapiSignature signature = randomPojo(WxJsapiSignature.class);
when(wxMpService.createJsapiSignature(eq(url))).thenReturn(signature);
// 调用
WxJsapiSignature result = socialClientService.createWxMpJsapiSignature(userType, url);
// 断言
assertSame(signature, result);
}
@Test
public void testGetWxMpService_clientNull() {
// 准备参数
Integer userType = randomPojo(UserTypeEnum.class).getValue();
// mock 方法
// 调用
WxMpService result = socialClientService.getWxMpService(userType);
// 断言
assertSame(wxMpService, result);
}
@Test
public void testGetWxMpService_clientDisable() {
// 准备参数
Integer userType = randomPojo(UserTypeEnum.class).getValue();
// mock 数据
SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())
.setUserType(userType).setSocialType(SocialTypeEnum.WECHAT_MP.getType()));
socialClientMapper.insert(client);
// 调用
WxMpService result = socialClientService.getWxMpService(userType);
// 断言
assertSame(wxMpService, result);
}
@Test
public void testGetWxMpService_clientEnable() {
// 准备参数
Integer userType = randomPojo(UserTypeEnum.class).getValue();
// mock 数据
SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())
.setUserType(userType).setSocialType(SocialTypeEnum.WECHAT_MP.getType()));
socialClientMapper.insert(client);
// mock 方法
WxMpProperties.ConfigStorage configStorage = mock(WxMpProperties.ConfigStorage.class);
when(wxMpProperties.getConfigStorage()).thenReturn(configStorage);
// 调用
WxMpService result = socialClientService.getWxMpService(userType);
// 断言
assertNotSame(wxMpService, result);
assertEquals(client.getClientId(), result.getWxMpConfigStorage().getAppId());
assertEquals(client.getClientSecret(), result.getWxMpConfigStorage().getSecret());
}
// =================== 微信小程序独有 ===================
@Test
public void testGetWxMaPhoneNumberInfo_success() throws WxErrorException {
// 准备参数
Integer userType = randomPojo(UserTypeEnum.class).getValue();
String phoneCode = randomString();
// mock 方法
WxMaUserService userService = mock(WxMaUserService.class);
when(wxMaService.getUserService()).thenReturn(userService);
WxMaPhoneNumberInfo phoneNumber = randomPojo(WxMaPhoneNumberInfo.class);
when(userService.getPhoneNoInfo(eq(phoneCode))).thenReturn(phoneNumber);
// 调用
WxMaPhoneNumberInfo result = socialClientService.getWxMaPhoneNumberInfo(userType, phoneCode);
// 断言
assertSame(phoneNumber, result);
}
@Test
public void testGetWxMaPhoneNumberInfo_exception() throws WxErrorException {
// 准备参数
Integer userType = randomPojo(UserTypeEnum.class).getValue();
String phoneCode = randomString();
// mock 方法
WxMaUserService userService = mock(WxMaUserService.class);
when(wxMaService.getUserService()).thenReturn(userService);
WxErrorException wxErrorException = randomPojo(WxErrorException.class);
when(userService.getPhoneNoInfo(eq(phoneCode))).thenThrow(wxErrorException);
// 调用并断言异常
assertServiceException(() -> socialClientService.getWxMaPhoneNumberInfo(userType, phoneCode),
SOCIAL_CLIENT_WEIXIN_MINI_APP_PHONE_CODE_ERROR);
}
@Test
public void testGetWxMaService_clientNull() {
// 准备参数
Integer userType = randomPojo(UserTypeEnum.class).getValue();
// mock 方法
// 调用
WxMaService result = socialClientService.getWxMaService(userType);
// 断言
assertSame(wxMaService, result);
}
@Test
public void testGetWxMaService_clientDisable() {
// 准备参数
Integer userType = randomPojo(UserTypeEnum.class).getValue();
// mock 数据
SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())
.setUserType(userType).setSocialType(SocialTypeEnum.WECHAT_MINI_APP.getType()));
socialClientMapper.insert(client);
// 调用
WxMaService result = socialClientService.getWxMaService(userType);
// 断言
assertSame(wxMaService, result);
}
@Test
public void testGetWxMaService_clientEnable() {
// 准备参数
Integer userType = randomPojo(UserTypeEnum.class).getValue();
// mock 数据
SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())
.setUserType(userType).setSocialType(SocialTypeEnum.WECHAT_MINI_APP.getType()));
socialClientMapper.insert(client);
// mock 方法
WxMaProperties.ConfigStorage configStorage = mock(WxMaProperties.ConfigStorage.class);
when(wxMaProperties.getConfigStorage()).thenReturn(configStorage);
// 调用
WxMaService result = socialClientService.getWxMaService(userType);
// 断言
assertNotSame(wxMaService, result);
assertEquals(client.getClientId(), result.getWxMaConfig().getAppid());
assertEquals(client.getClientSecret(), result.getWxMaConfig().getSecret());
}
// =================== 客户端管理 ===================
@Test
public void testCreateSocialClient_success() {
// 准备参数
SocialClientSaveReqVO reqVO = randomPojo(SocialClientSaveReqVO.class)
SocialClientSaveReqVO reqVO = randomPojo(SocialClientSaveReqVO.class,
o -> o.setSocialType(randomEle(SocialTypeEnum.values()).getType())
.setUserType(randomEle(UserTypeEnum.values()).getValue())
.setStatus(randomCommonStatus()))
.setId(null); // 防止 id 被赋值
// 调用
@ -59,6 +374,9 @@ public class SocialClientServiceImplTest extends BaseDbUnitTest {
// 准备参数
SocialClientSaveReqVO reqVO = randomPojo(SocialClientSaveReqVO.class, o -> {
o.setId(dbSocialClient.getId()); // 设置更新的 ID
o.setSocialType(randomEle(SocialTypeEnum.values()).getType())
.setUserType(randomEle(UserTypeEnum.values()).getValue())
.setStatus(randomCommonStatus());
});
// 调用
@ -101,40 +419,47 @@ public class SocialClientServiceImplTest extends BaseDbUnitTest {
}
@Test
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
public void testGetSocialClient() {
// mock 数据
SocialClientDO dbSocialClient = randomPojo(SocialClientDO.class);
socialClientMapper.insert(dbSocialClient);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbSocialClient.getId();
// 调用
SocialClientDO socialClient = socialClientService.getSocialClient(id);
// 校验数据正确
assertPojoEquals(dbSocialClient, socialClient);
}
@Test
public void testGetSocialClientPage() {
// mock 数据
SocialClientDO dbSocialClient = randomPojo(SocialClientDO.class, o -> { // 等会查询到
o.setName(null);
o.setSocialType(null);
o.setUserType(null);
o.setClientId(null);
o.setClientSecret(null);
o.setStatus(null);
o.setCreateTime(null);
o.setName("芋头");
o.setSocialType(SocialTypeEnum.GITEE.getType());
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setClientId("yudao");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
socialClientMapper.insert(dbSocialClient);
// 测试 name 不匹配
socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setName(null)));
socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setName(randomString())));
// 测试 socialType 不匹配
socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setSocialType(null)));
socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setSocialType(SocialTypeEnum.DINGTALK.getType())));
// 测试 userType 不匹配
socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setUserType(null)));
socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 clientId 不匹配
socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setClientId(null)));
// 测试 clientSecret 不匹配
socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setClientSecret(null)));
socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setClientId("dao")));
// 测试 status 不匹配
socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setStatus(null)));
// 测试 createTime 不匹配
socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setCreateTime(null)));
socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 准备参数
SocialClientPageReqVO reqVO = new SocialClientPageReqVO();
reqVO.setName(null);
reqVO.setSocialType(null);
reqVO.setUserType(null);
reqVO.setClientId(null);
reqVO.setStatus(null);
reqVO.setName("芋");
reqVO.setSocialType(SocialTypeEnum.GITEE.getType());
reqVO.setUserType(UserTypeEnum.ADMIN.getValue());
reqVO.setClientId("yu");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
// 调用
PageResult<SocialClientDO> pageResult = socialClientService.getSocialClientPage(reqVO);

View File

@ -1,21 +1,17 @@
package cn.iocoder.yudao.module.system.service.social;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO;
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper;
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import com.xingyuv.jushauth.enums.AuthResponseStatus;
import com.xingyuv.jushauth.model.AuthCallback;
import com.xingyuv.jushauth.model.AuthResponse;
import com.xingyuv.jushauth.model.AuthUser;
import com.xingyuv.jushauth.request.AuthRequest;
import com.xingyuv.justauth.AuthRequestFactory;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
@ -23,18 +19,27 @@ import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
import static cn.hutool.core.util.RandomUtil.*;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.hutool.core.util.RandomUtil.randomLong;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_AUTH_FAILURE;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_NOT_FOUND;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.when;
/**
* {@link SocialUserServiceImpl}
*
* @author
*/
@Import(SocialUserServiceImpl.class)
@Disabled // TODO 芋艿:后续统一修复
public class SocialUserServiceImplTest extends BaseDbUnitTest {
@Resource
@ -46,119 +51,7 @@ public class SocialUserServiceImplTest extends BaseDbUnitTest {
private SocialUserBindMapper socialUserBindMapper;
@MockBean
private AuthRequestFactory authRequestFactory;
// TODO 芋艿:后续统一修复
// @Test
// public void testGetAuthorizeUrl() {
// try (MockedStatic<AuthStateUtils> authStateUtilsMock = mockStatic(AuthStateUtils.class)) {
// // 准备参数
// Integer type = SocialTypeEnum.WECHAT_MP.getType();
// String redirectUri = "sss";
// // mock 获得对应的 AuthRequest 实现
// AuthRequest authRequest = mock(AuthRequest.class);
// when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest);
// // mock 方法
// authStateUtilsMock.when(AuthStateUtils::createState).thenReturn("aoteman");
// when(authRequest.authorize(eq("aoteman"))).thenReturn("https://www.iocoder.cn?redirect_uri=yyy");
//
// // 调用
// String url = socialUserService.getAuthorizeUrl(type, redirectUri);
// // 断言
// assertEquals("https://www.iocoder.cn?redirect_uri=sss", url);
// }
// }
@Test
public void testAuthSocialUser_exists() {
// 准备参数
Integer socialType = SocialTypeEnum.GITEE.getType();
Integer userType = randomEle(SocialTypeEnum.values()).getType();
String code = "tudou";
String state = "yuanma";
// mock 方法
SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(socialType).setCode(code).setState(state);
socialUserMapper.insert(socialUser);
// 调用
SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state);
// 断言
assertPojoEquals(socialUser, result);
}
@Test
public void testAuthSocialUser_authFailure() {
// 准备参数
Integer socialType = SocialTypeEnum.GITEE.getType();
Integer userType = randomEle(SocialTypeEnum.values()).getType();
// mock 方法
AuthRequest authRequest = mock(AuthRequest.class);
when(authRequestFactory.get(anyString())).thenReturn(authRequest);
AuthResponse<?> authResponse = new AuthResponse<>(0, "模拟失败", null);
when(authRequest.login(any(AuthCallback.class))).thenReturn(authResponse);
// 调用并断言
assertServiceException(
() -> socialUserService.authSocialUser(socialType, userType, randomString(10), randomString(10)),
SOCIAL_USER_AUTH_FAILURE, "模拟失败");
}
@Test
public void testAuthSocialUser_insert() {
// 准备参数
Integer socialType = SocialTypeEnum.GITEE.getType();
Integer userType = randomEle(SocialTypeEnum.values()).getType();
String code = "tudou";
String state = "yuanma";
// mock 方法
AuthRequest authRequest = mock(AuthRequest.class);
when(authRequestFactory.get(eq(SocialTypeEnum.GITEE.getSource()))).thenReturn(authRequest);
AuthUser authUser = randomPojo(AuthUser.class);
AuthResponse<AuthUser> authResponse = new AuthResponse<>(AuthResponseStatus.SUCCESS.getCode(), null, authUser);
when(authRequest.login(any(AuthCallback.class))).thenReturn(authResponse);
// 调用
SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state);
// 断言
assertBindSocialUser(socialType, result, authResponse.getData());
assertEquals(code, result.getCode());
assertEquals(state, result.getState());
}
@Test
public void testAuthSocialUser_update() {
// 准备参数
Integer socialType = SocialTypeEnum.GITEE.getType();
Integer userType = randomEle(SocialTypeEnum.values()).getType();
String code = "tudou";
String state = "yuanma";
// mock 数据
socialUserMapper.insert(randomPojo(SocialUserDO.class).setType(socialType).setOpenid("test_openid"));
// mock 方法
AuthRequest authRequest = mock(AuthRequest.class);
when(authRequestFactory.get(eq(SocialTypeEnum.GITEE.getSource()))).thenReturn(authRequest);
AuthUser authUser = randomPojo(AuthUser.class);
authUser.getToken().setOpenId("test_openid");
AuthResponse<AuthUser> authResponse = new AuthResponse<>(AuthResponseStatus.SUCCESS.getCode(), null, authUser);
when(authRequest.login(any(AuthCallback.class))).thenReturn(authResponse);
// 调用
SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state);
// 断言
assertBindSocialUser(socialType, result, authResponse.getData());
assertEquals(code, result.getCode());
assertEquals(state, result.getState());
}
private void assertBindSocialUser(Integer type, SocialUserDO socialUser, AuthUser authUser) {
assertEquals(authUser.getToken().getAccessToken(), socialUser.getToken());
assertEquals(toJsonString(authUser.getToken()), socialUser.getRawTokenInfo());
assertEquals(authUser.getNickname(), socialUser.getNickname());
assertEquals(authUser.getAvatar(), socialUser.getAvatar());
assertEquals(toJsonString(authUser.getRawUserInfo()), socialUser.getRawUserInfo());
assertEquals(type, socialUser.getType());
assertEquals(authUser.getUuid(), socialUser.getOpenid());
}
private SocialClientService socialClientService;
@Test
public void testGetSocialUserList() {
@ -260,4 +153,136 @@ public class SocialUserServiceImplTest extends BaseDbUnitTest {
assertEquals(socialUserDO.getOpenid(), socialUser.getOpenid());
}
@Test
public void testAuthSocialUser_exists() {
// 准备参数
Integer socialType = SocialTypeEnum.GITEE.getType();
Integer userType = randomEle(SocialTypeEnum.values()).getType();
String code = "tudou";
String state = "yuanma";
// mock 方法
SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(socialType).setCode(code).setState(state);
socialUserMapper.insert(socialUser);
// 调用
SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state);
// 断言
assertPojoEquals(socialUser, result);
}
@Test
public void testAuthSocialUser_notNull() {
// mock 数据
SocialUserDO socialUser = randomPojo(SocialUserDO.class,
o -> o.setType(SocialTypeEnum.GITEE.getType()).setCode("tudou").setState("yuanma"));
socialUserMapper.insert(socialUser);
// 准备参数
Integer socialType = SocialTypeEnum.GITEE.getType();
Integer userType = randomEle(SocialTypeEnum.values()).getType();
String code = "tudou";
String state = "yuanma";
// 调用
SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state);
// 断言
assertPojoEquals(socialUser, result);
}
@Test
public void testAuthSocialUser_insert() {
// 准备参数
Integer socialType = SocialTypeEnum.GITEE.getType();
Integer userType = randomEle(SocialTypeEnum.values()).getType();
String code = "tudou";
String state = "yuanma";
// mock 方法
AuthUser authUser = randomPojo(AuthUser.class);
when(socialClientService.getAuthUser(eq(socialType), eq(userType), eq(code), eq(state))).thenReturn(authUser);
// 调用
SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state);
// 断言
assertBindSocialUser(socialType, result, authUser);
assertEquals(code, result.getCode());
assertEquals(state, result.getState());
}
@Test
public void testAuthSocialUser_update() {
// 准备参数
Integer socialType = SocialTypeEnum.GITEE.getType();
Integer userType = randomEle(SocialTypeEnum.values()).getType();
String code = "tudou";
String state = "yuanma";
// mock 数据
socialUserMapper.insert(randomPojo(SocialUserDO.class).setType(socialType).setOpenid("test_openid"));
// mock 方法
AuthUser authUser = randomPojo(AuthUser.class);
when(socialClientService.getAuthUser(eq(socialType), eq(userType), eq(code), eq(state))).thenReturn(authUser);
// 调用
SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state);
// 断言
assertBindSocialUser(socialType, result, authUser);
assertEquals(code, result.getCode());
assertEquals(state, result.getState());
}
private void assertBindSocialUser(Integer type, SocialUserDO socialUser, AuthUser authUser) {
assertEquals(authUser.getToken().getAccessToken(), socialUser.getToken());
assertEquals(toJsonString(authUser.getToken()), socialUser.getRawTokenInfo());
assertEquals(authUser.getNickname(), socialUser.getNickname());
assertEquals(authUser.getAvatar(), socialUser.getAvatar());
assertEquals(toJsonString(authUser.getRawUserInfo()), socialUser.getRawUserInfo());
assertEquals(type, socialUser.getType());
assertEquals(authUser.getUuid(), socialUser.getOpenid());
}
@Test
public void testGetSocialUser_id() {
// mock 数据
SocialUserDO socialUserDO = randomPojo(SocialUserDO.class);
socialUserMapper.insert(socialUserDO);
// 参数准备
Long id = socialUserDO.getId();
// 调用
SocialUserDO dbSocialUserDO = socialUserService.getSocialUser(id);
// 断言
assertPojoEquals(socialUserDO, dbSocialUserDO);
}
@Test
public void testGetSocialUserPage() {
// mock 数据
SocialUserDO dbSocialUser = randomPojo(SocialUserDO.class, o -> { // 等会查询到
o.setType(SocialTypeEnum.GITEE.getType());
o.setNickname("芋艿");
o.setOpenid("yudaoyuanma");
o.setCreateTime(buildTime(2020, 1, 15));
});
socialUserMapper.insert(dbSocialUser);
// 测试 type 不匹配
socialUserMapper.insert(cloneIgnoreId(dbSocialUser, o -> o.setType(SocialTypeEnum.DINGTALK.getType())));
// 测试 nickname 不匹配
socialUserMapper.insert(cloneIgnoreId(dbSocialUser, o -> o.setNickname(randomString())));
// 测试 openid 不匹配
socialUserMapper.insert(cloneIgnoreId(dbSocialUser, o -> o.setOpenid("java")));
// 测试 createTime 不匹配
socialUserMapper.insert(cloneIgnoreId(dbSocialUser, o -> o.setCreateTime(buildTime(2020, 1, 21))));
// 准备参数
SocialUserPageReqVO reqVO = new SocialUserPageReqVO();
reqVO.setType(SocialTypeEnum.GITEE.getType());
reqVO.setNickname("芋");
reqVO.setOpenid("yudao");
reqVO.setCreateTime(buildBetweenTime(2020, 1, 10, 2020, 1, 20));
// 调用
PageResult<SocialUserDO> pageResult = socialUserService.getSocialUserPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbSocialUser, pageResult.getList().get(0));
}
}

View File

@ -331,6 +331,18 @@ public class TenantServiceImplTest extends BaseDbUnitTest {
assertPojoEquals(result, dbTenant);
}
@Test
public void testGetTenantByWebsite() {
// mock 数据
TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setWebsite("https://www.iocoder.cn"));
tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据
// 调用
TenantDO result = tenantService.getTenantByWebsite("https://www.iocoder.cn");
// 校验存在
assertPojoEquals(result, dbTenant);
}
@Test
public void testGetTenantListByPackageId() {
// mock 数据

View File

@ -361,13 +361,14 @@ CREATE TABLE IF NOT EXISTS "system_social_client" (
"user_type" int NOT NULL,
"client_id" varchar(255) NOT NULL,
"client_secret" varchar(255) NOT NULL,
"agent_id" varchar(255) NOT NULL,
"status" int NOT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint NOT NULL,
"tenant_id" bigint not null default '0',
PRIMARY KEY ("id")
) COMMENT '社交客户端表';