1、完善DeptServiceImpl单元测试覆盖率90%;2、新增security mock能力;3、移除postservice相关代码;4、移除部分无用模块的单测代码;

This commit is contained in:
gaoqr
2025-09-10 16:54:02 +08:00
parent ecae82b507
commit 283759c90d
32 changed files with 586 additions and 1643 deletions
@@ -111,9 +111,6 @@ public class DefaultDBFieldHandler implements MetaObjectHandler {
setFieldValByName("updater", loginUserName, metaObject); setFieldValByName("updater", loginUserName, metaObject);
} }
// 从请求中获取到 Token
String authorization = obtainAuthorization(AUTHORIZATION_HEADER_NAME, TOKEN_PARAM_NAME);
Long organId = null; Long organId = null;
/** /**
@@ -121,6 +118,9 @@ public class DefaultDBFieldHandler implements MetaObjectHandler {
* 这里的组织ID字段填入即使为空也可,后续的请求中都会带有token,再从缓存中获取到 token 数据,再获取组织ID,填入即可 * 这里的组织ID字段填入即使为空也可,后续的请求中都会带有token,再从缓存中获取到 token 数据,再获取组织ID,填入即可
*/ */
try { try {
// 从请求中获取到 Token
String authorization = obtainAuthorization(AUTHORIZATION_HEADER_NAME, TOKEN_PARAM_NAME);
// 获取 token 对应的缓存数据,并得到组织 ID // 获取 token 对应的缓存数据,并得到组织 ID
OAuth2AccessTokenDO oAuth2AccessTokenDO = get(authorization); OAuth2AccessTokenDO oAuth2AccessTokenDO = get(authorization);
organId = oAuth2AccessTokenDO.getOrganId(); organId = oAuth2AccessTokenDO.getOrganId();
@@ -73,6 +73,11 @@
<artifactId>bizlog-sdk</artifactId> <artifactId>bizlog-sdk</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>
@@ -138,6 +138,18 @@ public class SecurityFrameworkUtils {
} }
/**
* 获取当前用户的部门ID
*
*/
public static Long getUserDeptId() {
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
if (loginUser == null) {
throw new ServiceException(GlobalErrorCodeConstants.UNAUTHORIZED);
}
return loginUser.getDeptId();
}
/** /**
* 当前用户是否超级管理员 * 当前用户是否超级管理员
* *
@@ -0,0 +1,22 @@
package com.cf.imes.framework.security.test;
import org.springframework.security.test.context.support.WithSecurityContext;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* security mock loginuser annotation
*
* @author Gqr
* @since 2025/9/8 11:16
*/
@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithMockLoginUserSecurityContextFactory.class)
public @interface WithMockLoginUser {
String username() default "";
long deptId() default 0L;
boolean isSuperAdmin() default false;
}
@@ -0,0 +1,36 @@
package com.cf.imes.framework.security.test;
import cn.hutool.core.util.RandomUtil;
import com.cf.imes.framework.security.core.LoginUser;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.test.context.support.WithSecurityContextFactory;
import java.util.Collections;
/**
* security mock loginuser
*
* @author Gqr
* @since 2025/9/8 11:17
*/
public class WithMockLoginUserSecurityContextFactory implements WithSecurityContextFactory<WithMockLoginUser> {
@Override
public SecurityContext createSecurityContext(WithMockLoginUser annotation) {
SecurityContext context = SecurityContextHolder.createEmptyContext();
LoginUser loginUser = new LoginUser();
loginUser.setNickname(RandomUtil.randomString(10));
loginUser.setDeptId(RandomUtil.randomLong());
loginUser.setOrganId(RandomUtil.randomLong());
loginUser.setIsSupAdmin(annotation.isSuperAdmin());
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
loginUser, null, Collections.emptyList());
context.setAuthentication(authenticationToken);
return context;
}
}
@@ -0,0 +1,4 @@
/**
* 提供random mock对象策略 基类
*/
package com.cf.imes.framework.test.core.podam;
@@ -0,0 +1,21 @@
package com.cf.imes.framework.test.core.podam.strategy;
import cn.hutool.core.util.RandomUtil;
import uk.co.jemos.podam.common.AttributeStrategy;
import java.lang.annotation.Annotation;
import java.util.List;
/**
* random mock对象 @NumberValid策略
*
* @author Gqr
* @since 2025/9/9 14:17
*/
public class NumberValidStrategy implements AttributeStrategy<Integer> {
@Override
public Integer getValue(Class<?> aClass, List<Annotation> list) {
return RandomUtil.randomInt(0, Integer.MAX_VALUE);
}
}
@@ -5,8 +5,11 @@ import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.RandomUtil;
import com.cf.imes.framework.common.enums.CommonStatusEnum; import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.validation.NumberValid;
import com.cf.imes.framework.test.core.podam.strategy.NumberValidStrategy;
import uk.co.jemos.podam.api.PodamFactory; import uk.co.jemos.podam.api.PodamFactory;
import uk.co.jemos.podam.api.PodamFactoryImpl; import uk.co.jemos.podam.api.PodamFactoryImpl;
import uk.co.jemos.podam.api.RandomDataProviderStrategy;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -63,6 +66,7 @@ public class RandomUtils {
} }
return RandomUtil.randomBoolean(); return RandomUtil.randomBoolean();
}); });
((RandomDataProviderStrategy) PODAM_FACTORY.getStrategy()).addOrReplaceAttributeStrategy(NumberValid.class, new NumberValidStrategy());
} }
public static String randomString() { public static String randomString() {
@@ -1,7 +1,6 @@
package com.cf.imes.module.system.api.dept; package com.cf.imes.module.system.api.dept;
import com.cf.imes.framework.common.pojo.CommonResult; import com.cf.imes.framework.common.pojo.CommonResult;
import com.cf.imes.framework.common.util.collection.CollectionUtils;
import com.cf.imes.module.system.api.dept.dto.DeptRespDTO; import com.cf.imes.module.system.api.dept.dto.DeptRespDTO;
import com.cf.imes.module.system.enums.ApiConstants; import com.cf.imes.module.system.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
@@ -12,11 +11,6 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@FeignClient(name = ApiConstants.NAME) // TODO 晨丰:fallbackFactory = @FeignClient(name = ApiConstants.NAME) // TODO 晨丰:fallbackFactory =
@Tag(name = "RPC 服务 - 部门") @Tag(name = "RPC 服务 - 部门")
public interface DeptApi { public interface DeptApi {
@@ -28,30 +22,9 @@ public interface DeptApi {
@Parameter(name = "id", description = "部门编号", example = "1024", required = true) @Parameter(name = "id", description = "部门编号", example = "1024", required = true)
CommonResult<DeptRespDTO> getDept(@RequestParam("id") Long id); CommonResult<DeptRespDTO> getDept(@RequestParam("id") Long id);
@GetMapping(PREFIX + "/list")
@Operation(summary = "获得部门信息数组")
@Parameter(name = "ids", description = "部门编号数组", example = "1,2", required = true)
CommonResult<List<DeptRespDTO>> getDeptList(@RequestParam("ids") Collection<Long> ids);
@GetMapping(PREFIX + "/valid")
@Operation(summary = "校验部门是否合法")
@Parameter(name = "ids", description = "部门编号数组", example = "1,2", required = true)
CommonResult<Boolean> validateDeptList(@RequestParam("ids") Collection<Long> ids);
@GetMapping(PREFIX + "/legitimacy/{deptId}") @GetMapping(PREFIX + "/legitimacy/{deptId}")
@Operation(summary = "校验部门是否合法") @Operation(summary = "校验部门是否合法")
@Parameter(name = "id", description = "部门编号", example = "1024", required = true) @Parameter(name = "id", description = "部门编号", example = "1024", required = true)
CommonResult<Boolean> validateDept(@PathVariable("deptId") Long deptId); CommonResult<Boolean> validateDept(@PathVariable("deptId") Long deptId);
/**
* 获得指定编号的部门 Map
*
* @param ids 部门编号数组
* @return 部门 Map
*/
default Map<Long, DeptRespDTO> getDeptMap(Set<Long> ids) {
List<DeptRespDTO> list = getDeptList(ids).getCheckedData();
return CollectionUtils.convertMap(list, DeptRespDTO::getId);
}
} }
@@ -1,25 +0,0 @@
package com.cf.imes.module.system.api.dept;
import com.cf.imes.framework.common.pojo.CommonResult;
import com.cf.imes.module.system.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
@FeignClient(name = ApiConstants.NAME) // TODO 晨丰:fallbackFactory =
@Tag(name = "RPC 服务 - 岗位")
public interface PostApi {
String PREFIX = ApiConstants.PREFIX + "/post";
@GetMapping(PREFIX + "/valid")
@Operation(summary = "校验岗位是否合法")
@Parameter(name = "ids", description = "岗位编号数组", example = "1,2", required = true)
CommonResult<Boolean> validPostList(@RequestParam("ids") Collection<Long> ids);
}
@@ -9,8 +9,6 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import java.util.Collection;
import java.util.List;
import static com.cf.imes.framework.common.pojo.CommonResult.success; import static com.cf.imes.framework.common.pojo.CommonResult.success;
@@ -27,18 +25,6 @@ public class DeptApiImpl implements DeptApi {
return success(BeanUtils.toBean(dept, DeptRespDTO.class)); return success(BeanUtils.toBean(dept, DeptRespDTO.class));
} }
@Override
public CommonResult<List<DeptRespDTO>> getDeptList(Collection<Long> ids) {
List<DeptDO> depts = deptService.getDeptList(ids);
return success(BeanUtils.toBean(depts, DeptRespDTO.class));
}
@Override
public CommonResult<Boolean> validateDeptList(Collection<Long> ids) {
deptService.validateDeptList(ids);
return success(true);
}
@Override @Override
public CommonResult<Boolean> validateDept(Long deptId) { public CommonResult<Boolean> validateDept(Long deptId) {
deptService.validDept(deptId); deptService.validDept(deptId);
@@ -1,26 +0,0 @@
package com.cf.imes.module.system.api.dept;
import com.cf.imes.framework.common.pojo.CommonResult;
import com.cf.imes.module.system.service.dept.PostService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import java.util.Collection;
import static com.cf.imes.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class PostApiImpl implements PostApi {
@Resource
private PostService postService;
@Override
public CommonResult<Boolean> validPostList(Collection<Long> ids) {
postService.validatePostList(ids);
return success(true);
}
}
@@ -1,114 +0,0 @@
package com.cf.imes.module.system.controller.admin.dept;
import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.pojo.CommonResult;
import com.cf.imes.framework.common.pojo.PageParam;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.object.BeanUtils;
import com.cf.imes.framework.excel.core.util.ExcelUtils;
import com.cf.imes.framework.operatelog.core.annotations.OperateLog;
import com.cf.imes.module.system.controller.admin.dept.vo.post.PostPageReqVO;
import com.cf.imes.module.system.controller.admin.dept.vo.post.PostRespVO;
import com.cf.imes.module.system.controller.admin.dept.vo.post.PostSaveReqVO;
import com.cf.imes.module.system.controller.admin.dept.vo.post.PostSimpleRespVO;
import com.cf.imes.module.system.dal.dataobject.dept.PostDO;
import com.cf.imes.module.system.service.dept.PostService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import static com.cf.imes.framework.common.pojo.CommonResult.success;
import static com.cf.imes.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@Tag(name = "管理后台 - 岗位")
@RestController
@RequestMapping("/system/post")
@Validated
public class PostController {
@Resource
private PostService postService;
@PostMapping("/create")
@Operation(summary = "创建岗位")
@PreAuthorize("@ss.hasPermission('system:post:create')")
public CommonResult<Long> createPost(@Valid @RequestBody PostSaveReqVO createReqVO) {
Long postId = postService.createPost(createReqVO);
return success(postId);
}
@PutMapping("/update")
@Operation(summary = "修改岗位")
@PreAuthorize("@ss.hasPermission('system:post:update')")
public CommonResult<Boolean> updatePost(@Valid @RequestBody PostSaveReqVO updateReqVO) {
postService.updatePost(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除岗位")
@PreAuthorize("@ss.hasPermission('system:post:delete')")
public CommonResult<Boolean> deletePost(@RequestParam("id") Long id) {
postService.deletePost(id);
return success(true);
}
@GetMapping(value = "/get")
@Operation(summary = "获得岗位信息")
@Parameter(name = "id", description = "岗位编号", required = true, example = "1024")
// @PreAuthorize("@ss.hasPermission('system:post:query')")
public CommonResult<PostRespVO> getPost(@RequestParam("id") Long id) {
PostDO post = postService.getPost(id);
return success(BeanUtils.toBean(post, PostRespVO.class));
}
@GetMapping(value = {"/list-all-simple", "simple-list"})
@Operation(summary = "获取岗位全列表", description = "只包含被开启的岗位,主要用于前端的下拉选项")
public CommonResult<List<PostSimpleRespVO>> getSimplePostList(@RequestParam(value = "organId", required = false) Long organId) {
// 获得岗位列表,只要开启状态的
List<PostDO> list = postService.getPostList(null, Collections.singleton(CommonStatusEnum.ENABLE.getStatus()), organId);
// 排序后,返回给前端
list.sort(Comparator.comparing(PostDO::getSort));
return success(BeanUtils.toBean(list, PostSimpleRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得岗位分页列表")
@PreAuthorize("@ss.hasPermission('system:post:query')")
public CommonResult<PageResult<PostRespVO>> getPostPage(@Validated PostPageReqVO pageReqVO) {
PageResult<PostDO> pageResult = postService.getPostPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, PostRespVO.class));
}
@GetMapping("/export")
@Operation(summary = "岗位管理")
@PreAuthorize("@ss.hasPermission('system:post:export')")
@OperateLog(type = EXPORT)
public void export(HttpServletResponse response, @Validated PostPageReqVO reqVO) throws IOException {
reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<PostDO> list = postService.getPostPage(reqVO).getList();
AtomicLong sort = new AtomicLong();
list.forEach(f->{
sort.addAndGet(1);
f.setId(sort.get());
});
// 输出
ExcelUtils.write(response, "岗位数据.xls", "岗位列表", PostRespVO.class,
BeanUtils.toBean(list, PostRespVO.class));
}
}
@@ -5,7 +5,6 @@ import com.cf.imes.framework.common.validation.NumberValid;
import com.cf.imes.module.system.validation.common.CommonStatus; import com.cf.imes.module.system.validation.common.CommonStatus;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import org.hibernate.validator.constraints.Length;
import jakarta.validation.constraints.Email; import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
@@ -38,7 +37,7 @@ public class DeptSaveReqVO {
private Integer sort; private Integer sort;
@Schema(description = "负责人", example = "2048") @Schema(description = "负责人", example = "2048")
@Length(max = 10, message = "负责人长度不能超过10个字符") @Size(max = 30, message = "负责人长度不能超过30个字符")
private String leader; private String leader;
@Schema(description = "联系电话", example = "15601691000") @Schema(description = "联系电话", example = "15601691000")
@@ -8,7 +8,6 @@ import com.cf.imes.module.system.controller.admin.user.vo.profile.UserProfileUpd
import com.cf.imes.module.system.dal.dataobject.dept.DeptDO; import com.cf.imes.module.system.dal.dataobject.dept.DeptDO;
import com.cf.imes.module.system.dal.dataobject.user.AdminUserDO; import com.cf.imes.module.system.dal.dataobject.user.AdminUserDO;
import com.cf.imes.module.system.service.dept.DeptService; import com.cf.imes.module.system.service.dept.DeptService;
import com.cf.imes.module.system.service.dept.PostService;
import com.cf.imes.module.system.service.user.AdminUserService; import com.cf.imes.module.system.service.user.AdminUserService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@@ -42,8 +41,6 @@ public class OAuth2UserController {
private AdminUserService userService; private AdminUserService userService;
@Resource @Resource
private DeptService deptService; private DeptService deptService;
@Resource
private PostService postService;
@GetMapping("/get") @GetMapping("/get")
@Operation(summary = "获得用户基本信息") @Operation(summary = "获得用户基本信息")
@@ -57,11 +54,6 @@ public class OAuth2UserController {
DeptDO dept = deptService.getDept(user.getDeptId()); DeptDO dept = deptService.getDept(user.getDeptId());
resp.setDept(BeanUtils.toBean(dept, OAuth2UserInfoRespVO.Dept.class)); resp.setDept(BeanUtils.toBean(dept, OAuth2UserInfoRespVO.Dept.class));
} }
// // 获得岗位信息
// if (CollUtil.isNotEmpty(user.getPostIds())) {
// List<PostDO> posts = postService.getPostList(user.getPostIds());
// resp.setPosts(BeanUtils.toBean(posts, OAuth2UserInfoRespVO.Post.class));
// }
return success(resp); return success(resp);
} }
@@ -6,6 +6,7 @@ import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.pojo.CommonResult; import com.cf.imes.framework.common.pojo.CommonResult;
import com.cf.imes.framework.common.pojo.PageParam; import com.cf.imes.framework.common.pojo.PageParam;
import com.cf.imes.framework.common.pojo.PageResult; import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.collection.CollectionUtils;
import com.cf.imes.framework.common.util.object.BeanUtils; import com.cf.imes.framework.common.util.object.BeanUtils;
import com.cf.imes.framework.excel.core.util.ExcelUtils; import com.cf.imes.framework.excel.core.util.ExcelUtils;
import com.cf.imes.framework.mybatis.core.query.MPJLambdaWrapperX; import com.cf.imes.framework.mybatis.core.query.MPJLambdaWrapperX;
@@ -121,8 +122,8 @@ public class UserController {
return success(new PageResult<>(pageResult.getTotal())); return success(new PageResult<>(pageResult.getTotal()));
} }
// 拼接数据 // 拼接数据
Map<Long, DeptDO> deptMap = deptService.getDeptMap( Map<Long, DeptDO> deptMap = CollectionUtils.convertMap(
convertList(pageResult.getList(), AdminUserDO::getDeptId)); deptService.getDeptList(convertList(pageResult.getList(), AdminUserDO::getDeptId)), DeptDO::getId);
return success(new PageResult<>(UserConvert.INSTANCE.convertList(pageResult.getList(), deptMap), return success(new PageResult<>(UserConvert.INSTANCE.convertList(pageResult.getList(), deptMap),
pageResult.getTotal())); pageResult.getTotal()));
} }
@@ -155,8 +156,7 @@ public class UserController {
@RequestParam(value = "deptId", required = false) Long deptId) { @RequestParam(value = "deptId", required = false) Long deptId) {
List<AdminUserDO> list = userService.getUserListByStatus(CommonStatusEnum.ENABLE.getStatus(), organId, deptId); List<AdminUserDO> list = userService.getUserListByStatus(CommonStatusEnum.ENABLE.getStatus(), organId, deptId);
// 拼接数据 // 拼接数据
Map<Long, DeptDO> deptMap = deptService.getDeptMap( Map<Long, DeptDO> deptMap = CollectionUtils.convertMap(deptService.getDeptList(convertList(list, AdminUserDO::getDeptId)), DeptDO::getId);
convertList(list, AdminUserDO::getDeptId));
return success(UserConvert.INSTANCE.convertSimpleList(list, deptMap)); return success(UserConvert.INSTANCE.convertSimpleList(list, deptMap));
} }
@@ -194,8 +194,7 @@ public class UserController {
Map<Long, List<UserPostDTO>> listMap = userPostDTOS.stream().collect(Collectors.groupingBy(UserPostDTO::getUserId)); Map<Long, List<UserPostDTO>> listMap = userPostDTOS.stream().collect(Collectors.groupingBy(UserPostDTO::getUserId));
// 输出 Excel // 输出 Excel
Map<Long, DeptDO> deptMap = deptService.getDeptMap( Map<Long, DeptDO> deptMap = CollectionUtils.convertMap(deptService.getDeptList(convertList(list, AdminUserDO::getDeptId)), DeptDO::getId);
convertList(list, AdminUserDO::getDeptId));
List<UserExportRespVO> userRespVOS = UserConvert.INSTANCE.convertExportList(list, deptMap); List<UserExportRespVO> userRespVOS = UserConvert.INSTANCE.convertExportList(list, deptMap);
userRespVOS.forEach(e-> { userRespVOS.forEach(e-> {
List<UserPostDTO> userPostDTOS1 = listMap.get(e.getId()); List<UserPostDTO> userPostDTOS1 = listMap.get(e.getId());
@@ -12,7 +12,6 @@ import com.cf.imes.module.system.dal.dataobject.permission.RoleDO;
import com.cf.imes.module.system.dal.dataobject.social.SocialUserDO; import com.cf.imes.module.system.dal.dataobject.social.SocialUserDO;
import com.cf.imes.module.system.dal.dataobject.user.AdminUserDO; import com.cf.imes.module.system.dal.dataobject.user.AdminUserDO;
import com.cf.imes.module.system.service.dept.DeptService; import com.cf.imes.module.system.service.dept.DeptService;
import com.cf.imes.module.system.service.dept.PostService;
import com.cf.imes.module.system.service.permission.PermissionService; import com.cf.imes.module.system.service.permission.PermissionService;
import com.cf.imes.module.system.service.permission.RoleService; import com.cf.imes.module.system.service.permission.RoleService;
import com.cf.imes.module.system.service.social.SocialUserService; import com.cf.imes.module.system.service.social.SocialUserService;
@@ -45,8 +44,6 @@ public class UserProfileController {
@Resource @Resource
private DeptService deptService; private DeptService deptService;
@Resource @Resource
private PostService postService;
@Resource
private PermissionService permissionService; private PermissionService permissionService;
@Resource @Resource
private RoleService roleService; private RoleService roleService;
@@ -62,8 +59,6 @@ public class UserProfileController {
List<RoleDO> userRoles = roleService.getRoleListFromCache(permissionService.getUserRoleIdListByUserId(user.getId())); List<RoleDO> userRoles = roleService.getRoleListFromCache(permissionService.getUserRoleIdListByUserId(user.getId()));
// 获得部门信息 // 获得部门信息
DeptDO dept = user.getDeptId() != null ? deptService.getDept(user.getDeptId()) : null; DeptDO dept = user.getDeptId() != null ? deptService.getDept(user.getDeptId()) : null;
// // 获得岗位信息
// List<PostDO> posts = CollUtil.isNotEmpty(user.getPostIds()) ? postService.getPostList(user.getPostIds()) : null;
// 获得社交用户信息 // 获得社交用户信息
List<SocialUserDO> socialUsers = socialService.getSocialUserList(user.getId(), UserTypeEnum.ADMIN.getValue()); List<SocialUserDO> socialUsers = socialService.getSocialUserList(user.getId(), UserTypeEnum.ADMIN.getValue());
return success(UserConvert.INSTANCE.convert(user, userRoles, dept, null, socialUsers)); return success(UserConvert.INSTANCE.convert(user, userRoles, dept, null, socialUsers));
@@ -3,12 +3,8 @@ package com.cf.imes.module.system.dal.mysql.dept;
import com.cf.imes.framework.mybatis.core.mapper.BaseMapperX; import com.cf.imes.framework.mybatis.core.mapper.BaseMapperX;
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX; import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cf.imes.framework.organ.core.context.OrganContextHolder; import com.cf.imes.framework.organ.core.context.OrganContextHolder;
import com.cf.imes.framework.organ.core.security.OrganSecurityWebFilter;
import com.cf.imes.framework.security.core.LoginUser;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.module.system.controller.admin.dept.vo.dept.DeptListReqVO; import com.cf.imes.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import com.cf.imes.module.system.dal.dataobject.dept.DeptDO; import com.cf.imes.module.system.dal.dataobject.dept.DeptDO;
import com.cf.imes.module.system.dal.dataobject.user.AdminUserDO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import java.util.Collection; import java.util.Collection;
@@ -18,19 +14,12 @@ import java.util.Objects;
@Mapper @Mapper
public interface DeptMapper extends BaseMapperX<DeptDO> { public interface DeptMapper extends BaseMapperX<DeptDO> {
default List<DeptDO> selectList(DeptListReqVO reqVO) { default List<DeptDO> selectList(DeptListReqVO reqVO, Long organId) {
LambdaQueryWrapperX<DeptDO> lambdaQueryWrapperX = new LambdaQueryWrapperX<>(); LambdaQueryWrapperX<DeptDO> lambdaQueryWrapperX = new LambdaQueryWrapperX<>();
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
assert loginUser != null;
Boolean isSupAdmin = loginUser.getIsSupAdmin();
if(isSupAdmin && !Objects.isNull(reqVO.getOrganId())) {
lambdaQueryWrapperX.eqIfPresent(DeptDO::getOrganId, reqVO.getOrganId());
}else{
lambdaQueryWrapperX.eqIfPresent(DeptDO::getOrganId, loginUser.getOrganId());
}
return selectList(lambdaQueryWrapperX return selectList(lambdaQueryWrapperX
.likeIfPresent(DeptDO::getName, reqVO.getName()) .likeIfPresent(DeptDO::getName, reqVO.getName())
.eqIfPresent(DeptDO::getStatus, reqVO.getStatus())); .eqIfPresent(DeptDO::getStatus, reqVO.getStatus())
.eqIfPresent(DeptDO::getOrganId, organId));
} }
default DeptDO selectByParentIdAndName(Long parentId, String name, Long organId) { default DeptDO selectByParentIdAndName(Long parentId, String name, Long organId) {
@@ -0,0 +1,20 @@
package com.cf.imes.module.system.framework.redis.config;
import com.cf.imes.module.system.util.redis.SystemRedisUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
/**
* system 模块的Redisa Configuration
*
* @author Gqr
* @since 2025/9/10 10:07
*/
@Configuration(proxyBeanMethods = false)
public class ChenfengSystemRedisConfiguration {
@Bean
public SystemRedisUtils systemRedisUtils(StringRedisTemplate stringRedisTemplate) {
return new SystemRedisUtils(stringRedisTemplate);
}
}
@@ -0,0 +1,4 @@
/**
* 占位
*/
package com.cf.imes.module.system.framework.redis;
@@ -1,13 +1,11 @@
package com.cf.imes.module.system.service.dept; package com.cf.imes.module.system.service.dept;
import com.cf.imes.framework.common.util.collection.CollectionUtils;
import com.cf.imes.module.system.controller.admin.dept.vo.dept.DeptListReqVO; import com.cf.imes.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import com.cf.imes.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO; import com.cf.imes.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.dept.DeptDO; import com.cf.imes.module.system.dal.dataobject.dept.DeptDO;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
/** /**
@@ -63,17 +61,6 @@ public interface DeptService {
*/ */
List<DeptDO> getDeptList(DeptListReqVO reqVO); List<DeptDO> getDeptList(DeptListReqVO reqVO);
/**
* 获得指定编号的部门 Map
*
* @param ids 部门编号数组
* @return 部门 Map
*/
default Map<Long, DeptDO> getDeptMap(Collection<Long> ids) {
List<DeptDO> list = getDeptList(ids);
return CollectionUtils.convertMap(list, DeptDO::getId);
}
/** /**
* 获得指定部门的所有子部门 * 获得指定部门的所有子部门
* *
@@ -90,15 +77,6 @@ public interface DeptService {
*/ */
Set<Long> getChildDeptIdListFromCache(Long id); Set<Long> getChildDeptIdListFromCache(Long id);
/**
* 校验部门们是否有效。如下情况,视为无效:
* 1. 部门编号不存在
* 2. 部门被禁用
*
* @param ids 角色编号数组
*/
void validateDeptList(Collection<Long> ids);
/** /**
* 校验部门是否存在且有效(包括上级) * 校验部门是否存在且有效(包括上级)
* *
@@ -106,13 +84,6 @@ public interface DeptService {
*/ */
void validDept(Long deptId); void validDept(Long deptId);
/**
* 用户登陆时部门校验
*
* @param deptId
*/
void validUserLoginDept(Long deptId);
/** /**
* 移除没有归属(上级)的部门 * 移除没有归属(上级)的部门
* *
@@ -6,7 +6,6 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.cf.imes.framework.common.enums.CommonStatusEnum; import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.exception.util.ServiceExceptionUtil; import com.cf.imes.framework.common.exception.util.ServiceExceptionUtil;
import com.cf.imes.framework.common.util.json.JsonUtils;
import com.cf.imes.framework.common.util.object.BeanUtils; import com.cf.imes.framework.common.util.object.BeanUtils;
import com.cf.imes.framework.organ.core.context.OrganContextHolder; import com.cf.imes.framework.organ.core.context.OrganContextHolder;
import com.cf.imes.framework.security.core.LoginUser; import com.cf.imes.framework.security.core.LoginUser;
@@ -14,19 +13,16 @@ import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.module.system.controller.admin.dept.vo.dept.DeptListReqVO; import com.cf.imes.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import com.cf.imes.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO; import com.cf.imes.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.dept.DeptDO; import com.cf.imes.module.system.dal.dataobject.dept.DeptDO;
import com.cf.imes.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import com.cf.imes.module.system.dal.mysql.dept.DeptMapper; import com.cf.imes.module.system.dal.mysql.dept.DeptMapper;
import com.cf.imes.module.system.dal.redis.RedisKeyConstants; import com.cf.imes.module.system.dal.redis.RedisKeyConstants;
import com.cf.imes.module.system.enums.ErrorCodeConstants; import com.cf.imes.module.system.enums.ErrorCodeConstants;
import com.cf.imes.module.system.service.user.AdminUserService; import com.cf.imes.module.system.service.user.AdminUserService;
import com.cf.imes.module.system.util.redis.SystemRedisUtils;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -36,7 +32,6 @@ import java.util.stream.Collectors;
import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.cf.imes.framework.common.util.collection.CollectionUtils.convertSet; import static com.cf.imes.framework.common.util.collection.CollectionUtils.convertSet;
import static com.cf.imes.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.DEPT_USER_OPER_NOT_ALLOW; import static com.cf.imes.module.system.enums.ErrorCodeConstants.DEPT_USER_OPER_NOT_ALLOW;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PARENT_DEPT_USER_OPER_NOT_ALLOW; import static com.cf.imes.module.system.enums.ErrorCodeConstants.PARENT_DEPT_USER_OPER_NOT_ALLOW;
@@ -58,15 +53,12 @@ public class DeptServiceImpl implements DeptService {
private AdminUserService userService; private AdminUserService userService;
@Resource @Resource
private StringRedisTemplate stringRedisTemplate; private SystemRedisUtils systemRedisUtils;
@Override @Override
@CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST,
allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存 allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存
public Long createDept(DeptSaveReqVO createReqVO) { public Long createDept(DeptSaveReqVO createReqVO) {
if (createReqVO.getParentId() == null) {
createReqVO.setParentId(DeptDO.PARENT_ID_ROOT);
}
// 校验父部门的有效性 // 校验父部门的有效性
validateParentDept(null, createReqVO.getParentId()); validateParentDept(null, createReqVO.getParentId());
// 校验部门名的唯一性 // 校验部门名的唯一性
@@ -83,9 +75,6 @@ public class DeptServiceImpl implements DeptService {
allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存 allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存
public void updateDept(DeptSaveReqVO updateReqVO) { public void updateDept(DeptSaveReqVO updateReqVO) {
Long deptId = updateReqVO.getId(); Long deptId = updateReqVO.getId();
if (updateReqVO.getParentId() == null) {
updateReqVO.setParentId(DeptDO.PARENT_ID_ROOT);
}
// 校验自己存在 // 校验自己存在
validateDeptExists(deptId); validateDeptExists(deptId);
// 校验父部门的有效性 // 校验父部门的有效性
@@ -100,7 +89,7 @@ public class DeptServiceImpl implements DeptService {
deptMapper.updateById(updateObj); deptMapper.updateById(updateObj);
if (CommonStatusEnum.DISABLE.getStatus().equals(updateReqVO.getStatus())) { if (CommonStatusEnum.DISABLE.getStatus().equals(updateReqVO.getStatus())) {
// 移除部门下用户的token // 移除部门下用户的token
scanAndCompareDeptAndDelToken(String.format(OAUTH2_ACCESS_TOKEN, "*"), deptId); systemRedisUtils.scanAndCompareDeptAndDelToken(deptId);
} }
} }
@@ -125,43 +114,15 @@ public class DeptServiceImpl implements DeptService {
// 删除关联的用户 // 删除关联的用户
userService.deleteDeptUsers(id); userService.deleteDeptUsers(id);
// 移除部门下用户的token // 移除部门下用户的token
scanAndCompareDeptAndDelToken(String.format(OAUTH2_ACCESS_TOKEN, "*"), id); systemRedisUtils.scanAndCompareDeptAndDelToken(id);
}
/**
* 轮训redis token,移除对应部门下的用户的token
*
* @param keyPattern
* @param deptId
*/
private void scanAndCompareDeptAndDelToken(String keyPattern, Long deptId) {
// 根据keyPattern scan匹配的redis key
List<String> matchKeys = new ArrayList<>();
Cursor<String> cursor = stringRedisTemplate.scan(ScanOptions.scanOptions().match(keyPattern).count(200).build());
while (cursor.hasNext()) {
matchKeys.add(cursor.next());
}
cursor.close();
if (CollUtil.isNotEmpty(matchKeys)) {
for (String key : matchKeys) {
// 获取key下的用户信息
OAuth2AccessTokenDO oAuth2AccessTokenDO = JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(key), OAuth2AccessTokenDO.class);
if (ObjectUtil.equal(deptId, oAuth2AccessTokenDO.getDeptId())) {
// 用户id匹配上了删除redis中的token缓存
stringRedisTemplate.delete(key);
return;
}
}
}
} }
/** /**
* 检查是否操作当前部门 * 检查是否操作当前部门
*/ */
private void checkCurrentWhenOperate(Long deptId) { private void checkCurrentWhenOperate(Long deptId) {
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser(); Long currentDeptId = SecurityFrameworkUtils.getUserDeptId();
Long currentDeptId = loginUser.getDeptId(); if (ObjectUtil.equal(deptId, currentDeptId)) {
if (ObjectUtil.isNotNull(loginUser) && ObjectUtil.equal(deptId, currentDeptId)) {
throw exception(DEPT_USER_OPER_NOT_ALLOW); throw exception(DEPT_USER_OPER_NOT_ALLOW);
} }
// 递归查询所有下级,如果当前用户在范围内不允许操作 // 递归查询所有下级,如果当前用户在范围内不允许操作
@@ -260,7 +221,17 @@ public class DeptServiceImpl implements DeptService {
@Override @Override
public List<DeptDO> getDeptList(DeptListReqVO reqVO) { public List<DeptDO> getDeptList(DeptListReqVO reqVO) {
List<DeptDO> list = deptMapper.selectList(reqVO); LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
assert loginUser != null;
Boolean isSupAdmin = loginUser.getIsSupAdmin();
Long organId;
// 只有超管的情况下可以查看请求来源的组织下部门
if (isSupAdmin && !Objects.isNull(reqVO.getOrganId())) {
organId = reqVO.getOrganId();
} else {
organId = loginUser.getOrganId();
}
List<DeptDO> list = deptMapper.selectList(reqVO, organId);
list.sort(Comparator.comparing(DeptDO::getSort)); list.sort(Comparator.comparing(DeptDO::getSort));
return list; return list;
} }
@@ -291,25 +262,6 @@ public class DeptServiceImpl implements DeptService {
return convertSet(children, DeptDO::getId); return convertSet(children, DeptDO::getId);
} }
@Override
public void validateDeptList(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return;
}
// 获得科室信息
Map<Long, DeptDO> deptMap = getDeptMap(ids);
// 校验
ids.forEach(id -> {
DeptDO dept = deptMap.get(id);
if (dept == null) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.DEPT_NOT_FOUND);
}
if (!CommonStatusEnum.ENABLE.getStatus().equals(dept.getStatus())) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.DEPT_NOT_ENABLE, dept.getName());
}
});
}
@Override @Override
public void validDept(Long deptId) { public void validDept(Long deptId) {
DeptDO deptDO = deptMapper.selectById(deptId); DeptDO deptDO = deptMapper.selectById(deptId);
@@ -324,21 +276,6 @@ public class DeptServiceImpl implements DeptService {
} }
// 用户登陆时部门校验,部门不存在时也可以进行登录
@Override
public void validUserLoginDept(Long deptId) {
DeptDO deptDO = deptMapper.selectById(deptId);
if (deptDO == null) {
return;
}
if (!CommonStatusEnum.ENABLE.getStatus().equals(deptDO.getStatus())) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.DEPT_NOT_ALLOWED_LOGIN, deptDO.getName());
}
// 递归校验上级部门
validParentDept(deptDO);
}
/** /**
* 递归校验上级部门 * 递归校验上级部门
* *
@@ -414,7 +351,6 @@ public class DeptServiceImpl implements DeptService {
currentId = currentDept.getParentId(); currentId = currentDept.getParentId();
} }
topLevelCache.put(deptDO.getId(), false);
return false; // 默认返回false return false; // 默认返回false
} }
@@ -1,86 +0,0 @@
package com.cf.imes.module.system.service.dept;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.module.system.controller.admin.dept.vo.post.PostPageReqVO;
import com.cf.imes.module.system.controller.admin.dept.vo.post.PostSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.dept.PostDO;
import org.springframework.lang.Nullable;
import java.util.Collection;
import java.util.List;
/**
* 岗位 Service 接口
*
* @author 晨丰科技
*/
public interface PostService {
/**
* 创建岗位
*
* @param createReqVO 岗位信息
* @return 岗位编号
*/
Long createPost(PostSaveReqVO createReqVO);
/**
* 更新岗位
*
* @param updateReqVO 岗位信息
*/
void updatePost(PostSaveReqVO updateReqVO);
/**
* 删除岗位信息
*
* @param id 岗位编号
*/
void deletePost(Long id);
/**
* 获得岗位列表
*
* @param ids 岗位编号数组
* @return 部门列表
*/
List<PostDO> getPostList(@Nullable Collection<Long> ids);
/**
* 获得符合条件的岗位列表
*
* @param ids 岗位编号数组。如果为空,不进行筛选
* @param statuses 状态数组。如果为空,不进行筛选
* @return 部门列表
*/
List<PostDO> getPostList(@Nullable Collection<Long> ids,
@Nullable Collection<Integer> statuses,
Long organId
);
/**
* 获得岗位分页列表
*
* @param reqVO 分页条件
* @return 部门分页列表
*/
PageResult<PostDO> getPostPage(PostPageReqVO reqVO);
/**
* 获得岗位信息
*
* @param id 岗位编号
* @return 岗位信息
*/
PostDO getPost(Long id);
/**
* 校验岗位们是否有效。如下情况,视为无效:
* 1. 岗位编号不存在
* 2. 岗位被禁用
*
* @param ids 岗位编号数组
*/
void validatePostList(Collection<Long> ids);
}
@@ -1,154 +0,0 @@
package com.cf.imes.module.system.service.dept;
import cn.hutool.core.collection.CollUtil;
import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.exception.util.ServiceExceptionUtil;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.object.BeanUtils;
import com.cf.imes.module.system.controller.admin.dept.vo.post.PostPageReqVO;
import com.cf.imes.module.system.controller.admin.dept.vo.post.PostSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.dept.PostDO;
import com.cf.imes.module.system.dal.mysql.dept.PostMapper;
import com.cf.imes.module.system.enums.ErrorCodeConstants;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.cf.imes.framework.common.util.collection.CollectionUtils.convertMap;
/**
* 岗位 Service 实现类
*
* @author 晨丰科技
*/
@Service
@Validated
public class PostServiceImpl implements PostService {
@Resource
private PostMapper postMapper;
@Override
public Long createPost(PostSaveReqVO createReqVO) {
// 校验正确性
validatePostForCreateOrUpdate(null, createReqVO.getName(), createReqVO.getCode());
// 插入岗位
PostDO post = BeanUtils.toBean(createReqVO, PostDO.class);
postMapper.insert(post);
return post.getId();
}
@Override
public void updatePost(PostSaveReqVO updateReqVO) {
// 校验正确性
validatePostForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getName(), updateReqVO.getCode());
// 更新岗位
PostDO updateObj = BeanUtils.toBean(updateReqVO, PostDO.class);
postMapper.updateById(updateObj);
}
@Override
public void deletePost(Long id) {
// 校验是否存在
validatePostExists(id);
// 删除部门
postMapper.deleteById(id);
}
private void validatePostForCreateOrUpdate(Long id, String name, String code) {
// 校验自己存在
validatePostExists(id);
// 校验岗位名的唯一性
validatePostNameUnique(id, name);
// 校验岗位编码的唯一性
validatePostCodeUnique(id, code);
}
private void validatePostNameUnique(Long id, String name) {
PostDO post = postMapper.selectByName(name);
if (post == null) {
return;
}
// 如果 id 为空,说明不用比较是否为相同 id 的岗位
if (id == null) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.POST_NAME_DUPLICATE);
}
if (!post.getId().equals(id)) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.POST_NAME_DUPLICATE);
}
}
private void validatePostCodeUnique(Long id, String code) {
PostDO post = postMapper.selectByCode(code);
if (post == null) {
return;
}
// 如果 id 为空,说明不用比较是否为相同 id 的岗位
if (id == null) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.POST_CODE_DUPLICATE);
}
if (!post.getId().equals(id)) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.POST_CODE_DUPLICATE);
}
}
private void validatePostExists(Long id) {
if (id == null) {
return;
}
if (postMapper.selectById(id) == null) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.POST_NOT_FOUND);
}
}
@Override
public List<PostDO> getPostList(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return Collections.emptyList();
}
return postMapper.selectBatchIds(ids);
}
@Override
public List<PostDO> getPostList(Collection<Long> ids, Collection<Integer> statuses, Long organId) {
return postMapper.selectList(ids, statuses, organId);
}
@Override
public PageResult<PostDO> getPostPage(PostPageReqVO reqVO) {
return postMapper.selectPage(reqVO);
}
@Override
public PostDO getPost(Long id) {
return postMapper.selectById(id);
}
@Override
public void validatePostList(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return;
}
// 获得岗位信息
List<PostDO> posts = postMapper.selectBatchIds(ids);
Map<Long, PostDO> postMap = convertMap(posts, PostDO::getId);
// 校验
ids.forEach(id -> {
PostDO post = postMap.get(id);
if (post == null) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.POST_NOT_FOUND);
}
if (!CommonStatusEnum.ENABLE.getStatus().equals(post.getStatus())) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.POST_NOT_ENABLE, post.getName());
}
});
}
}
@@ -42,7 +42,6 @@ import com.cf.imes.module.system.enums.logger.LoginLogTypeEnum;
import com.cf.imes.module.system.enums.logger.LoginResultEnum; import com.cf.imes.module.system.enums.logger.LoginResultEnum;
import com.cf.imes.module.system.enums.sms.SmsSceneEnum; import com.cf.imes.module.system.enums.sms.SmsSceneEnum;
import com.cf.imes.module.system.service.dept.DeptService; import com.cf.imes.module.system.service.dept.DeptService;
import com.cf.imes.module.system.service.dept.PostService;
import com.cf.imes.module.system.service.logger.LoginLogService; import com.cf.imes.module.system.service.logger.LoginLogService;
import com.cf.imes.module.system.service.organ.OrganService; import com.cf.imes.module.system.service.organ.OrganService;
import com.cf.imes.module.system.service.permission.PermissionService; import com.cf.imes.module.system.service.permission.PermissionService;
@@ -90,8 +89,6 @@ public class AdminUserServiceImpl implements AdminUserService {
@Resource @Resource
private DeptService deptService; private DeptService deptService;
@Resource @Resource
private PostService postService;
@Resource
private PermissionService permissionService; private PermissionService permissionService;
@Resource @Resource
private PasswordEncoder passwordEncoder; private PasswordEncoder passwordEncoder;
@@ -544,8 +541,6 @@ public class AdminUserServiceImpl implements AdminUserService {
// 校验部门处于开启状态 // 校验部门处于开启状态
deptService.validDept(deptId); deptService.validDept(deptId);
} }
// // 校验岗位处于开启状态
// postService.validatePostList(postIds);
return adminUserDO; return adminUserDO;
} }
@@ -0,0 +1,53 @@
package com.cf.imes.module.system.util.redis;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.cf.imes.framework.common.util.json.JsonUtils;
import com.cf.imes.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import lombok.AllArgsConstructor;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.ArrayList;
import java.util.List;
import static com.cf.imes.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN;
/**
* system 模块 redis处理工具
*
* @author Gqr
* @since 2025/9/9 17:42
*/
@AllArgsConstructor
public class SystemRedisUtils {
private StringRedisTemplate stringRedisTemplate;
/**
* 轮训redis token,移除对应部门下的用户的token
*
* @param deptId
*/
public void scanAndCompareDeptAndDelToken(Long deptId) {
// 根据keyPattern scan匹配的redis key
List<String> matchKeys = new ArrayList<>();
Cursor<String> cursor = stringRedisTemplate.scan(ScanOptions.scanOptions().match(String.format(OAUTH2_ACCESS_TOKEN, "*")).count(200).build());
while (cursor.hasNext()) {
matchKeys.add(cursor.next());
}
cursor.close();
if (CollUtil.isNotEmpty(matchKeys)) {
for (String key : matchKeys) {
// 获取key下的用户信息
OAuth2AccessTokenDO oAuth2AccessTokenDO = JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(key), OAuth2AccessTokenDO.class);
if (ObjectUtil.equal(deptId, oAuth2AccessTokenDO.getDeptId())) {
// 用户id匹配上了删除redis中的token缓存
stringRedisTemplate.delete(key);
return;
}
}
}
}
}
@@ -2,16 +2,26 @@ package com.cf.imes.module.system.service.dept;
import com.cf.imes.framework.common.enums.CommonStatusEnum; import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.util.object.ObjectUtils; import com.cf.imes.framework.common.util.object.ObjectUtils;
import com.cf.imes.framework.test.core.ut.BaseDbUnitTest; import com.cf.imes.framework.organ.core.context.OrganContextHolder;
import com.cf.imes.framework.security.core.LoginUser;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.framework.security.test.WithMockLoginUser;
import com.cf.imes.framework.test.core.ut.BaseDbAndRedisUnitTest;
import com.cf.imes.module.system.controller.admin.dept.vo.dept.DeptListReqVO; import com.cf.imes.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import com.cf.imes.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO; import com.cf.imes.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.dept.DeptDO; import com.cf.imes.module.system.dal.dataobject.dept.DeptDO;
import com.cf.imes.module.system.dal.dataobject.user.AdminUserDO;
import com.cf.imes.module.system.dal.mysql.dept.DeptMapper; import com.cf.imes.module.system.dal.mysql.dept.DeptMapper;
import com.cf.imes.module.system.service.user.AdminUserService;
import com.cf.imes.module.system.util.redis.SystemRedisUtils;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@@ -19,8 +29,9 @@ import static com.cf.imes.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException; import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException;
import static com.cf.imes.framework.test.core.util.RandomUtils.*; import static com.cf.imes.framework.test.core.util.RandomUtils.*;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.*; import static com.cf.imes.module.system.enums.ErrorCodeConstants.*;
import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.when;
/** /**
* {@link DeptServiceImpl} 的单元测试类 * {@link DeptServiceImpl} 的单元测试类
@@ -28,13 +39,19 @@ import static org.junit.jupiter.api.Assertions.*;
* @author niudehua * @author niudehua
*/ */
@Import(DeptServiceImpl.class) @Import(DeptServiceImpl.class)
public class DeptServiceImplTest extends BaseDbUnitTest { public class DeptServiceImplTest extends BaseDbAndRedisUnitTest {
@Resource @Resource
private DeptServiceImpl deptService; private DeptServiceImpl deptService;
@Resource @Resource
private DeptMapper deptMapper; private DeptMapper deptMapper;
@MockitoBean
private AdminUserService userService;
@MockitoBean
private SystemRedisUtils systemRedisUtils;
@Test @Test
public void testCreateDept() { public void testCreateDept() {
// 准备参数 // 准备参数
@@ -54,7 +71,8 @@ public class DeptServiceImplTest extends BaseDbUnitTest {
} }
@Test @Test
public void testUpdateDept() { @WithMockLoginUser
public void testUpdateDept_enable() {
// mock 数据 // mock 数据
DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus())); DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus()));
deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据 deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据
@@ -63,7 +81,7 @@ public class DeptServiceImplTest extends BaseDbUnitTest {
// 设置更新的 ID // 设置更新的 ID
o.setParentId(DeptDO.PARENT_ID_ROOT); o.setParentId(DeptDO.PARENT_ID_ROOT);
o.setId(dbDeptDO.getId()); o.setId(dbDeptDO.getId());
o.setStatus(randomCommonStatus()); o.setStatus(CommonStatusEnum.ENABLE.getStatus());
}); });
// 调用 // 调用
@@ -74,6 +92,50 @@ public class DeptServiceImplTest extends BaseDbUnitTest {
} }
@Test @Test
@WithMockLoginUser
public void testUpdateDept_disable() {
// mock 数据
DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus()));
deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据
// 准备参数
DeptSaveReqVO reqVO = randomPojo(DeptSaveReqVO.class, o -> {
// 设置更新的 ID
o.setParentId(DeptDO.PARENT_ID_ROOT);
o.setId(dbDeptDO.getId());
o.setStatus(CommonStatusEnum.DISABLE.getStatus());
});
// 调用
deptService.updateDept(reqVO);
// 校验是否更新正确
DeptDO deptDO = deptMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, deptDO);
}
@Test
@WithMockLoginUser
public void testUpdateDept_parentDeptUserOperNotAllow() {
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
// mock 数据
DeptDO parentDeptDO = randomPojo(DeptDO.class, o -> o.setId(randomLongId()));
DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> {
o.setId(loginUser.getDeptId());
o.setParentId(parentDeptDO.getId());
});
deptMapper.insert(parentDeptDO);
deptMapper.insert(dbDeptDO);
// 准备参数
DeptSaveReqVO reqVO = randomPojo(DeptSaveReqVO.class, o -> {
o.setParentId(DeptDO.PARENT_ID_ROOT);
o.setId(parentDeptDO.getId());
});
assertServiceException(() -> deptService.updateDept(reqVO),
PARENT_DEPT_USER_OPER_NOT_ALLOW);
}
@Test
@WithMockLoginUser
public void testDeleteDept_success() { public void testDeleteDept_success() {
// mock 数据 // mock 数据
DeptDO dbDeptDO = randomPojo(DeptDO.class); DeptDO dbDeptDO = randomPojo(DeptDO.class);
@@ -87,6 +149,36 @@ public class DeptServiceImplTest extends BaseDbUnitTest {
assertNull(deptMapper.selectById(id)); assertNull(deptMapper.selectById(id));
} }
@Test
@WithMockLoginUser
public void testDeleteDept_deptUserOperNotAllow() {
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
// mock 数据
DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setId(loginUser.getDeptId()));
deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbDeptDO.getId();
assertServiceException(() -> deptService.deleteDept(id),
DEPT_USER_OPER_NOT_ALLOW);
}
@Test
@WithMockLoginUser
public void testDeleteDept_deptUserExists(){
// mock 数据
DeptDO dbDeptDO = randomPojo(DeptDO.class);
deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbDeptDO.getId();
when(userService.getUserListByDeptIds(anyList()))
.thenReturn(Collections.singletonList(new AdminUserDO()));
assertServiceException(() -> deptService.deleteDept(id),
DEPT_EXISTS_USER);
}
@Test @Test
public void testDeleteDept_exitsChildren() { public void testDeleteDept_exitsChildren() {
// mock 数据 // mock 数据
@@ -104,6 +196,22 @@ public class DeptServiceImplTest extends BaseDbUnitTest {
assertServiceException(() -> deptService.deleteDept(parentDept.getId()), DEPT_EXITS_CHILDREN); assertServiceException(() -> deptService.deleteDept(parentDept.getId()), DEPT_EXITS_CHILDREN);
} }
@Test
public void testValidateDeptExists_idIsNull() {
// 调用,不抛异常
deptService.validateDeptExists(null);
}
@Test
public void testValidateDeptExists_exists() {
// mock 数据
DeptDO deptDO = randomPojo(DeptDO.class);
deptMapper.insert(deptDO);
// 调用,不抛异常
deptService.validateDeptExists(deptDO.getId());
}
@Test @Test
public void testValidateDeptExists_notFound() { public void testValidateDeptExists_notFound() {
// 准备参数 // 准备参数
@@ -123,15 +231,73 @@ public class DeptServiceImplTest extends BaseDbUnitTest {
DEPT_PARENT_ERROR); DEPT_PARENT_ERROR);
} }
@Test
public void testValidateParentDept_parentNotExists() {
Long id = randomLongId();
Long parentId = randomLongId(); // 假设不存在的 ID
assertServiceException(() -> deptService.validateParentDept(id, parentId),
DEPT_PARENT_NOT_EXITS);
}
@Test
public void testValidateParentDept_parentDisabled() {
DeptDO parentDept = randomPojo(DeptDO.class);
parentDept.setStatus(CommonStatusEnum.DISABLE.getStatus());
deptMapper.insert(parentDept);
Long id = randomLongId();
Long parentId = parentDept.getId();
assertServiceException(() -> deptService.validateParentDept(id, parentId),
PARENT_DEPT_DISABLE, parentDept.getName());
}
@Test
public void testValidateParentDept_multiLevelParentDisabled() {
// 构造三级部门:grandParent -> parent -> child
DeptDO grandParent = randomPojo(DeptDO.class);
grandParent.setStatus(CommonStatusEnum.DISABLE.getStatus());
deptMapper.insert(grandParent);
DeptDO parent = randomPojo(DeptDO.class, o -> o.setParentId(grandParent.getId()));
parent.setStatus(CommonStatusEnum.ENABLE.getStatus());
deptMapper.insert(parent);
Long id = randomLongId();
Long parentId = parent.getId();
assertServiceException(() -> deptService.validateParentDept(id, parentId),
PARENT_DEPT_DISABLE, grandParent.getName());
}
@Test
public void testValidateParentDept_validHierarchy() {
DeptDO grandParent = randomPojo(DeptDO.class);
grandParent.setStatus(CommonStatusEnum.ENABLE.getStatus());
deptMapper.insert(grandParent);
DeptDO parent = randomPojo(DeptDO.class, o -> o.setParentId(grandParent.getId()));
parent.setStatus(CommonStatusEnum.ENABLE.getStatus());
deptMapper.insert(parent);
Long id = randomLongId();
Long parentId = parent.getId();
// 不抛异常
deptService.validateParentDept(id, parentId);
}
@Test @Test
public void testValidateParentDept_parentIsChild() { public void testValidateParentDept_parentIsChild() {
// mock 数据(父节点) // mock 数据(父节点)
DeptDO parentDept = randomPojo(DeptDO.class); DeptDO parentDept = randomPojo(DeptDO.class);
parentDept.setStatus(CommonStatusEnum.ENABLE.getStatus());
deptMapper.insert(parentDept); deptMapper.insert(parentDept);
// mock 数据(子节点) // mock 数据(子节点)
DeptDO childDept = randomPojo(DeptDO.class, o -> { DeptDO childDept = randomPojo(DeptDO.class, o -> {
o.setParentId(parentDept.getId()); o.setParentId(parentDept.getId());
}); });
childDept.setStatus(CommonStatusEnum.ENABLE.getStatus());
deptMapper.insert(childDept); deptMapper.insert(childDept);
// 准备参数 // 准备参数
@@ -152,12 +318,64 @@ public class DeptServiceImplTest extends BaseDbUnitTest {
Long id = randomLongId(); Long id = randomLongId();
Long parentId = deptDO.getParentId(); Long parentId = deptDO.getParentId();
String name = deptDO.getName(); String name = deptDO.getName();
Long organId = deptDO.getOrganId();
// 调用, 并断言异常 // 调用, 并断言异常
assertServiceException(() -> deptService.validateDeptNameUnique(id, parentId, name, null), assertServiceException(() -> deptService.validateDeptNameUnique(id, parentId, name, organId),
DEPT_NAME_DUPLICATE); DEPT_NAME_DUPLICATE);
} }
@Test
public void testValidateDeptNameUnique_newDeptDuplicate() {
// mock 数据
DeptDO deptDO = randomPojo(DeptDO.class);
deptMapper.insert(deptDO);
// 准备参数:id = null,模拟新增
Long id = null;
Long parentId = deptDO.getParentId();
String name = deptDO.getName();
Long organId = deptDO.getOrganId();
// 调用,并断言异常
assertServiceException(() -> deptService.validateDeptNameUnique(id, parentId, name, organId),
DEPT_NAME_DUPLICATE);
}
@Test
public void testValidateDeptNameUnique_updateOtherDeptDuplicate() {
// mock 数据
DeptDO deptDO = randomPojo(DeptDO.class);
deptMapper.insert(deptDO);
// 准备参数:id 不同,模拟更新到重名部门
Long id = randomLongId(); // 与 deptDO 的 id 不同
Long parentId = deptDO.getParentId();
String name = deptDO.getName();
Long organId = deptDO.getOrganId();
// 调用,并断言异常
assertServiceException(() -> deptService.validateDeptNameUnique(id, parentId, name, organId),
DEPT_NAME_DUPLICATE);
}
@Test
public void testValidateDeptNameUnique_updateSameDept() {
// mock 数据
DeptDO deptDO = randomPojo(DeptDO.class);
deptMapper.insert(deptDO);
// 准备参数:id 相同,表示更新自己
Long id = deptDO.getId();
Long parentId = deptDO.getParentId();
String name = deptDO.getName();
Long organId = deptDO.getOrganId();
// 调用,不抛异常
deptService.validateDeptNameUnique(id, parentId, name, organId);
}
@Test @Test
public void testGetDept() { public void testGetDept() {
// mock 数据 // mock 数据
@@ -191,11 +409,47 @@ public class DeptServiceImplTest extends BaseDbUnitTest {
} }
@Test @Test
public void testGetDeptList_reqVO() { public void testGetDeptList_emptyids() {
assertEquals(0, deptService.getDeptList(Collections.emptyList()).size());
}
@Test
@WithMockLoginUser(isSuperAdmin = true)
public void testGetDeptList_superadmin_reqVO() {
Long organId = SecurityFrameworkUtils.getUserOrganId();
// mock 数据 // mock 数据
DeptDO dept = randomPojo(DeptDO.class, o -> { // 等会查询到 DeptDO dept = randomPojo(DeptDO.class, o -> { // 等会查询到
o.setName("开发部"); o.setName("开发部");
o.setStatus(CommonStatusEnum.ENABLE.getStatus()); o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setOrganId(organId);
});
deptMapper.insert(dept);
// 测试 name 不匹配
deptMapper.insert(ObjectUtils.cloneIgnoreId(dept, o -> o.setName("")));
// 测试 status 不匹配
deptMapper.insert(ObjectUtils.cloneIgnoreId(dept, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 准备参数
DeptListReqVO reqVO = new DeptListReqVO();
reqVO.setName("");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setOrganId(organId);
// 调用
List<DeptDO> sysDeptDOS = deptService.getDeptList(reqVO);
// 断言
assertEquals(1, sysDeptDOS.size());
assertPojoEquals(dept, sysDeptDOS.get(0));
}
@Test
@WithMockLoginUser
public void testGetDeptList_commonuser_reqVO() {
Long organId = SecurityFrameworkUtils.getUserOrganId();
// mock 数据
DeptDO dept = randomPojo(DeptDO.class, o -> { // 等会查询到
o.setName("开发部");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setOrganId(organId);
}); });
deptMapper.insert(dept); deptMapper.insert(dept);
// 测试 name 不匹配 // 测试 name 不匹配
@@ -261,36 +515,125 @@ public class DeptServiceImplTest extends BaseDbUnitTest {
} }
@Test @Test
public void testValidateDeptList_success() { public void testValidDept_NotFound() {
// mock 数据 // 部门不存在
DeptDO deptDO = randomPojo(DeptDO.class).setStatus(CommonStatusEnum.ENABLE.getStatus()); assertServiceException(() -> deptService.validDept(1L), DEPT_NOT_FOUND);
deptMapper.insert(deptDO); }
// 准备参数
List<Long> ids = singletonList(deptDO.getId()); @Test
public void testValidDept_Disabled() {
// 部门状态不可用
DeptDO disableDbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
deptMapper.insert(disableDbDeptDO);
assertServiceException(() -> deptService.validDept(disableDbDeptDO.getId()), DEPT_DISABLE, disableDbDeptDO.getName());
}
@Test
public void testValidDept_ParentNotFound() {
DeptDO enabledbDeptDO = randomPojo(DeptDO.class, o -> {
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
deptMapper.insert(enabledbDeptDO);
assertServiceException(() -> deptService.validDept(enabledbDeptDO.getId()), DEPT_PARENT_NOT_EXITS);
}
@Test
public void testValidDept_ParentDisabled() {
DeptDO disabledParentDetpDO = randomPojo(DeptDO.class, o -> {
o.setId(null);
o.setStatus(CommonStatusEnum.DISABLE.getStatus());
});
deptMapper.insert(disabledParentDetpDO);
DeptDO enabledbDeptDO = randomPojo(DeptDO.class, o -> {
o.setId(null);
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setParentId(disabledParentDetpDO.getId());
});
deptMapper.insert(enabledbDeptDO);
assertServiceException(() -> deptService.validDept(enabledbDeptDO.getId()), DEPT_DISABLE, disabledParentDetpDO.getName());
}
@Test
public void testValidDept_success() {
DeptDO disabledParentDetpDO = randomPojo(DeptDO.class, o -> {
o.setId(null);
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setParentId(DeptDO.PARENT_ID_ROOT);
});
deptMapper.insert(disabledParentDetpDO);
DeptDO enabledbDeptDO = randomPojo(DeptDO.class, o -> {
o.setId(null);
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setParentId(disabledParentDetpDO.getId());
});
deptMapper.insert(enabledbDeptDO);
// 调用,无需断言 // 调用,无需断言
deptService.validateDeptList(ids); assertDoesNotThrow(()-> deptService.validDept(enabledbDeptDO.getId()));
} }
@Test @Test
public void testValidateDeptList_notFound() { public void removeUnowndDept_empty() {
// 准备参数 List<DeptDO> deptDOS = deptService.removeUnowndDept(Collections.emptyList());
List<Long> ids = singletonList(randomLongId()); assertEquals(deptDOS.size(), 0);
// 调用, 并断言异常
assertServiceException(() -> deptService.validateDeptList(ids), DEPT_NOT_FOUND);
} }
@Test @Test
public void testValidateDeptList_notEnable() { public void removeUnowndDept_success() {
// mock 数据 DeptDO oneParent = randomPojo(DeptDO.class, o -> o.setParentId(DeptDO.PARENT_ID_ROOT));
DeptDO deptDO = randomPojo(DeptDO.class).setStatus(CommonStatusEnum.DISABLE.getStatus()); DeptDO onechild1 = randomPojo(DeptDO.class, o -> o.setParentId(oneParent.getId()));
deptMapper.insert(deptDO); DeptDO onechild2 = randomPojo(DeptDO.class, o -> o.setParentId(oneParent.getId()));
// 准备参数 List<DeptDO> paramDeptList = List.of(oneParent, onechild1, onechild2);
List<Long> ids = singletonList(deptDO.getId()); List<DeptDO> deptDOS = deptService.removeUnowndDept(paramDeptList);
assertEquals(deptDOS.size(), paramDeptList.size());
// 调用, 并断言异常
assertServiceException(() -> deptService.validateDeptList(ids), DEPT_NOT_ENABLE, deptDO.getName());
} }
@Test
public void removeUnowndDept_ParentNotFound() {
DeptDO oneParent = randomPojo(DeptDO.class, o -> o.setParentId(DeptDO.PARENT_ID_ROOT));
DeptDO one = randomPojo(DeptDO.class, o -> o.setParentId(oneParent.getId()));
DeptDO two = randomPojo(DeptDO.class);
List<DeptDO> paramDeptList = List.of(oneParent, one, two);
List<DeptDO> deptDOS = deptService.removeUnowndDept(paramDeptList);
assertNotEquals(deptDOS.size(), paramDeptList.size());
}
@Test
public void getAllInvalidDeptIds_success() {
Long organId = randomLongId();
DeptDO oneParent = randomPojo(DeptDO.class, o -> {
o.setId(randomLongId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setOrganId(organId);
o.setParentId(DeptDO.PARENT_ID_ROOT);
});
Long parentId = oneParent.getId();
DeptDO one = randomPojo(DeptDO.class, o -> {
o.setStatus(randomCommonStatus());
o.setOrganId(organId);
o.setParentId(parentId);
});
DeptDO two = randomPojo(DeptDO.class, o -> {
o.setStatus(randomCommonStatus());
o.setOrganId(organId);
o.setParentId(parentId);
});
DeptDO three = randomPojo(DeptDO.class, o -> {
o.setStatus(randomCommonStatus());
o.setOrganId(organId);
o.setParentId(parentId);
});
List<DeptDO> deptDOS = List.of(oneParent, one, two, three);
// 批量插入
deptMapper.insertBatch(deptDOS);
long disableCount = deptDOS.stream().filter(o -> CommonStatusEnum.DISABLE.getStatus().equals(o.getStatus())).count();
Set<Long> allInvalidDeptIds = deptService.getAllInvalidDeptIds(organId);
assertEquals(disableCount, allInvalidDeptIds.size());
OrganContextHolder.setOrganId(organId);
Set<Long> noOrganAllInvalidDeptIds = deptService.getAllInvalidDeptIds(null);
assertEquals(disableCount, noOrganAllInvalidDeptIds.size());
}
} }
@@ -1,248 +0,0 @@
package com.cf.imes.module.system.service.dept;
import com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.collection.ArrayUtils;
import com.cf.imes.framework.test.core.ut.BaseDbUnitTest;
import com.cf.imes.module.system.controller.admin.dept.vo.post.PostPageReqVO;
import com.cf.imes.module.system.controller.admin.dept.vo.post.PostSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.dept.PostDO;
import com.cf.imes.module.system.dal.mysql.dept.PostMapper;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import jakarta.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static com.cf.imes.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static com.cf.imes.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException;
import static com.cf.imes.framework.test.core.util.RandomUtils.*;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.*;
import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link PostServiceImpl} 的单元测试类
*
* @author niudehua
*/
@Import(PostServiceImpl.class)
public class PostServiceImplTest extends BaseDbUnitTest {
@Resource
private PostServiceImpl postService;
@Resource
private PostMapper postMapper;
@Test
public void testCreatePost_success() {
// 准备参数
PostSaveReqVO reqVO = randomPojo(PostSaveReqVO.class,
o -> o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()))
.setId(null); // 防止 id 被设置
// 调用
Long postId = postService.createPost(reqVO);
// 断言
assertNotNull(postId);
// 校验记录的属性是否正确
PostDO post = postMapper.selectById(postId);
assertPojoEquals(reqVO, post, "id");
}
@Test
public void testUpdatePost_success() {
// mock 数据
PostDO postDO = randomPostDO();
postMapper.insert(postDO);// @Sql: 先插入出一条存在的数据
// 准备参数
PostSaveReqVO reqVO = randomPojo(PostSaveReqVO.class, o -> {
// 设置更新的 ID
o.setId(postDO.getId());
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus());
});
// 调用
postService.updatePost(reqVO);
// 校验是否更新正确
PostDO post = postMapper.selectById(reqVO.getId());
assertPojoEquals(reqVO, post);
}
@Test
public void testDeletePost_success() {
// mock 数据
PostDO postDO = randomPostDO();
postMapper.insert(postDO);
// 准备参数
Long id = postDO.getId();
// 调用
postService.deletePost(id);
assertNull(postMapper.selectById(id));
}
@Test
public void testValidatePost_notFoundForDelete() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> postService.deletePost(id), POST_NOT_FOUND);
}
@Test
public void testValidatePost_nameDuplicateForCreate() {
// mock 数据
PostDO postDO = randomPostDO();
postMapper.insert(postDO);// @Sql: 先插入出一条存在的数据
// 准备参数
PostSaveReqVO reqVO = randomPojo(PostSaveReqVO.class,
// 模拟 name 重复
o -> o.setName(postDO.getName()));
assertServiceException(() -> postService.createPost(reqVO), POST_NAME_DUPLICATE);
}
@Test
public void testValidatePost_codeDuplicateForUpdate() {
// mock 数据
PostDO postDO = randomPostDO();
postMapper.insert(postDO);
// mock 数据:稍后模拟重复它的 code
PostDO codePostDO = randomPostDO();
postMapper.insert(codePostDO);
// 准备参数
PostSaveReqVO reqVO = randomPojo(PostSaveReqVO.class, o -> {
// 设置更新的 ID
o.setId(postDO.getId());
// 模拟 code 重复
o.setCode(codePostDO.getCode());
});
// 调用, 并断言异常
assertServiceException(() -> postService.updatePost(reqVO), POST_CODE_DUPLICATE);
}
@Test
public void testGetPostPage() {
// mock 数据
PostDO postDO = randomPojo(PostDO.class, o -> {
o.setName("码仔");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
postMapper.insert(postDO);
// 测试 name 不匹配
postMapper.insert(cloneIgnoreId(postDO, o -> o.setName("程序员")));
// 测试 status 不匹配
postMapper.insert(cloneIgnoreId(postDO, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 准备参数
PostPageReqVO reqVO = new PostPageReqVO();
reqVO.setName("");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
// 调用
PageResult<PostDO> pageResult = postService.getPostPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(postDO, pageResult.getList().get(0));
}
@Test
public void testGetPostList() {
// mock 数据
PostDO postDO01 = randomPojo(PostDO.class);
postMapper.insert(postDO01);
// 测试 id 不匹配
PostDO postDO02 = randomPojo(PostDO.class);
postMapper.insert(postDO02);
// 准备参数
List<Long> ids = singletonList(postDO01.getId());
// 调用
List<PostDO> list = postService.getPostList(ids);
// 断言
assertEquals(1, list.size());
assertPojoEquals(postDO01, list.get(0));
}
@Test
public void testGetPostList_idsAndStatus() {
// mock 数据
PostDO postDO01 = randomPojo(PostDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
postMapper.insert(postDO01);
// 测试 status 不匹配
PostDO postDO02 = randomPojo(PostDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
postMapper.insert(postDO02);
// 准备参数
List<Long> ids = Arrays.asList(postDO01.getId(), postDO02.getId());
// 调用
List<PostDO> list = postService.getPostList(ids, singletonList(CommonStatusEnum.ENABLE.getStatus()),1L);
// 断言
assertEquals(1, list.size());
assertPojoEquals(postDO01, list.get(0));
}
@Test
public void testGetPost() {
// mock 数据
PostDO dbPostDO = randomPostDO();
postMapper.insert(dbPostDO);
// 准备参数
Long id = dbPostDO.getId();
// 调用
PostDO post = postService.getPost(id);
// 断言
assertNotNull(post);
assertPojoEquals(dbPostDO, post);
}
@Test
public void testValidatePostList_success() {
// mock 数据
PostDO postDO = randomPostDO().setStatus(CommonStatusEnum.ENABLE.getStatus());
postMapper.insert(postDO);
// 准备参数
List<Long> ids = singletonList(postDO.getId());
// 调用,无需断言
postService.validatePostList(ids);
}
@Test
public void testValidatePostList_notFound() {
// 准备参数
List<Long> ids = singletonList(randomLongId());
// 调用, 并断言异常
assertServiceException(() -> postService.validatePostList(ids), POST_NOT_FOUND);
}
@Test
public void testValidatePostList_notEnable() {
// mock 数据
PostDO postDO = randomPostDO().setStatus(CommonStatusEnum.DISABLE.getStatus());
postMapper.insert(postDO);
// 准备参数
List<Long> ids = singletonList(postDO.getId());
// 调用, 并断言异常
assertServiceException(() -> postService.validatePostList(ids), POST_NOT_ENABLE,
postDO.getName());
}
@SafeVarargs
private static PostDO randomPostDO(Consumer<PostDO>... consumers) {
Consumer<PostDO> consumer = (o) -> {
o.setStatus(randomCommonStatus()); // 保证 status 的范围
};
return randomPojo(PostDO.class, ArrayUtils.append(consumer, consumers));
}
}
@@ -1,472 +0,0 @@
package com.cf.imes.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 com.cf.imes.framework.common.enums.CommonStatusEnum;
import com.cf.imes.framework.common.enums.UserTypeEnum;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.test.core.ut.BaseDbUnitTest;
import com.cf.imes.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO;
import com.cf.imes.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.social.SocialClientDO;
import com.cf.imes.module.system.dal.mysql.social.SocialClientMapper;
import com.cf.imes.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.cf.imes.module.system.framework.justauth.core.AuthRequestFactory;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthDefaultRequest;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
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 jakarta.annotation.Resource;
import static com.cf.imes.framework.test.core.util.RandomUtils.*;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static com.cf.imes.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static com.cf.imes.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* {@link SocialClientServiceImpl} 的单元测试类
*
* @author 晨丰科技
*/
@Import(SocialClientServiceImpl.class)
public class SocialClientServiceImplTest extends BaseDbUnitTest {
@Resource
private SocialClientServiceImpl socialClientService;
@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.cf.com?redirect_uri=yyy");
// 调用
String url = socialClientService.getAuthorizeUrl(socialType, userType, redirectUri);
// 断言
assertEquals("https://www.cf.com?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<AuthUser> 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<AuthUser> 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,
o -> o.setSocialType(randomEle(SocialTypeEnum.values()).getType())
.setUserType(randomEle(UserTypeEnum.values()).getValue())
.setStatus(randomCommonStatus()))
.setId(null); // 防止 id 被赋值
// 调用
Long socialClientId = socialClientService.createSocialClient(reqVO);
// 断言
assertNotNull(socialClientId);
// 校验记录的属性是否正确
SocialClientDO socialClient = socialClientMapper.selectById(socialClientId);
assertPojoEquals(reqVO, socialClient, "id");
}
@Test
public void testUpdateSocialClient_success() {
// mock 数据
SocialClientDO dbSocialClient = randomPojo(SocialClientDO.class);
socialClientMapper.insert(dbSocialClient);// @Sql: 先插入出一条存在的数据
// 准备参数
SocialClientSaveReqVO reqVO = randomPojo(SocialClientSaveReqVO.class, o -> {
o.setId(dbSocialClient.getId()); // 设置更新的 ID
o.setSocialType(randomEle(SocialTypeEnum.values()).getType())
.setUserType(randomEle(UserTypeEnum.values()).getValue())
.setStatus(randomCommonStatus());
});
// 调用
socialClientService.updateSocialClient(reqVO);
// 校验是否更新正确
SocialClientDO socialClient = socialClientMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, socialClient);
}
@Test
public void testUpdateSocialClient_notExists() {
// 准备参数
SocialClientSaveReqVO reqVO = randomPojo(SocialClientSaveReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> socialClientService.updateSocialClient(reqVO), SOCIAL_CLIENT_NOT_EXISTS);
}
@Test
public void testDeleteSocialClient_success() {
// mock 数据
SocialClientDO dbSocialClient = randomPojo(SocialClientDO.class);
socialClientMapper.insert(dbSocialClient);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbSocialClient.getId();
// 调用
socialClientService.deleteSocialClient(id);
// 校验数据不存在了
assertNull(socialClientMapper.selectById(id));
}
@Test
public void testDeleteSocialClient_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> socialClientService.deleteSocialClient(id), SOCIAL_CLIENT_NOT_EXISTS);
}
@Test
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("芋头");
o.setSocialType(SocialTypeEnum.GITEE.getType());
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setClientId("chenfeng");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
socialClientMapper.insert(dbSocialClient);
// 测试 name 不匹配
socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setName(randomString())));
// 测试 socialType 不匹配
socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setSocialType(SocialTypeEnum.DINGTALK.getType())));
// 测试 userType 不匹配
socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 clientId 不匹配
socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setClientId("dao")));
// 测试 status 不匹配
socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 准备参数
SocialClientPageReqVO reqVO = new SocialClientPageReqVO();
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);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbSocialClient, pageResult.getList().get(0));
}
}
@@ -1,288 +0,0 @@
package com.cf.imes.module.system.service.social;
import com.cf.imes.framework.common.enums.UserTypeEnum;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.test.core.ut.BaseDbUnitTest;
import com.cf.imes.module.system.api.social.dto.SocialUserBindReqDTO;
import com.cf.imes.module.system.api.social.dto.SocialUserRespDTO;
import com.cf.imes.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO;
import com.cf.imes.module.system.dal.dataobject.social.SocialUserBindDO;
import com.cf.imes.module.system.dal.dataobject.social.SocialUserDO;
import com.cf.imes.module.system.dal.mysql.social.SocialUserBindMapper;
import com.cf.imes.module.system.dal.mysql.social.SocialUserMapper;
import com.cf.imes.module.system.enums.social.SocialTypeEnum;
import me.zhyd.oauth.model.AuthUser;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import jakarta.annotation.Resource;
import java.util.List;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.hutool.core.util.RandomUtil.randomLong;
import static com.cf.imes.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static com.cf.imes.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static com.cf.imes.framework.common.util.json.JsonUtils.toJsonString;
import static com.cf.imes.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static com.cf.imes.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException;
import static com.cf.imes.framework.test.core.util.RandomUtils.randomPojo;
import static com.cf.imes.framework.test.core.util.RandomUtils.randomString;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.SOCIAL_USER_NOT_FOUND;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.when;
/**
* {@link SocialUserServiceImpl} 的单元测试类
*
* @author 晨丰科技
*/
@Import(SocialUserServiceImpl.class)
public class SocialUserServiceImplTest extends BaseDbUnitTest {
@Resource
private SocialUserServiceImpl socialUserService;
@Resource
private SocialUserMapper socialUserMapper;
@Resource
private SocialUserBindMapper socialUserBindMapper;
@MockBean
private SocialClientService socialClientService;
@Test
public void testGetSocialUserList() {
Long userId = 1L;
Integer userType = UserTypeEnum.ADMIN.getValue();
// mock 获得社交用户
SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(SocialTypeEnum.GITEE.getType());
socialUserMapper.insert(socialUser); // 可被查到
socialUserMapper.insert(randomPojo(SocialUserDO.class)); // 不可被查到
// mock 获得绑定
socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class) // 可被查询到
.setUserId(userId).setUserType(userType).setSocialType(SocialTypeEnum.GITEE.getType())
.setSocialUserId(socialUser.getId()));
socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class) // 不可被查询到
.setUserId(2L).setUserType(userType).setSocialType(SocialTypeEnum.DINGTALK.getType()));
// 调用
List<SocialUserDO> result = socialUserService.getSocialUserList(userId, userType);
// 断言
assertEquals(1, result.size());
assertPojoEquals(socialUser, result.get(0));
}
@Test
public void testBindSocialUser() {
// 准备参数
SocialUserBindReqDTO reqDTO = new SocialUserBindReqDTO()
.setUserId(1L).setUserType(UserTypeEnum.ADMIN.getValue())
.setSocialType(SocialTypeEnum.GITEE.getType()).setCode("test_code").setState("test_state");
// mock 数据:获得社交用户
SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(reqDTO.getSocialType())
.setCode(reqDTO.getCode()).setState(reqDTO.getState());
socialUserMapper.insert(socialUser);
// mock 数据:用户可能之前已经绑定过该社交类型
socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class).setUserId(1L).setUserType(UserTypeEnum.ADMIN.getValue())
.setSocialType(SocialTypeEnum.GITEE.getType()).setSocialUserId(-1L));
// mock 数据:社交用户可能之前绑定过别的用户
socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class).setUserType(UserTypeEnum.ADMIN.getValue())
.setSocialType(SocialTypeEnum.GITEE.getType()).setSocialUserId(socialUser.getId()));
// 调用
String openid = socialUserService.bindSocialUser(reqDTO);
// 断言
List<SocialUserBindDO> socialUserBinds = socialUserBindMapper.selectList();
assertEquals(1, socialUserBinds.size());
assertEquals(socialUser.getOpenid(), openid);
}
@Test
public void testUnbindSocialUser_success() {
// 准备参数
Long userId = 1L;
Integer userType = UserTypeEnum.ADMIN.getValue();
Integer type = SocialTypeEnum.GITEE.getType();
String openid = "test_openid";
// mock 数据:社交用户
SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(type).setOpenid(openid);
socialUserMapper.insert(socialUser);
// mock 数据:社交绑定关系
SocialUserBindDO socialUserBind = randomPojo(SocialUserBindDO.class).setUserType(userType)
.setUserId(userId).setSocialType(type);
socialUserBindMapper.insert(socialUserBind);
// 调用
socialUserService.unbindSocialUser(userId, userType, type, openid);
// 断言
assertEquals(0, socialUserBindMapper.selectCount(null).intValue());
}
@Test
public void testUnbindSocialUser_notFound() {
// 调用,并断言
assertServiceException(
() -> socialUserService.unbindSocialUser(randomLong(), UserTypeEnum.ADMIN.getValue(),
SocialTypeEnum.GITEE.getType(), "test_openid"),
SOCIAL_USER_NOT_FOUND);
}
@Test
public void testGetSocialUser() {
// 准备参数
Integer userType = UserTypeEnum.ADMIN.getValue();
Integer type = SocialTypeEnum.GITEE.getType();
String code = "tudou";
String state = "yuanma";
// mock 社交用户
SocialUserDO socialUserDO = randomPojo(SocialUserDO.class).setType(type).setCode(code).setState(state);
socialUserMapper.insert(socialUserDO);
// mock 社交用户的绑定
Long userId = randomLong();
SocialUserBindDO socialUserBind = randomPojo(SocialUserBindDO.class).setUserType(userType).setUserId(userId)
.setSocialType(type).setSocialUserId(socialUserDO.getId());
socialUserBindMapper.insert(socialUserBind);
// 调用
SocialUserRespDTO socialUser = socialUserService.getSocialUserByCode(userType, type, code, state);
// 断言
assertEquals(userId, socialUser.getUserId());
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("chenfengyuanma");
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("chenfeng");
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));
}
}
@@ -23,7 +23,6 @@ import com.cf.imes.module.system.dal.mysql.dept.UserPostMapper;
import com.cf.imes.module.system.dal.mysql.user.AdminUserMapper; import com.cf.imes.module.system.dal.mysql.user.AdminUserMapper;
import com.cf.imes.module.system.enums.common.SexEnum; import com.cf.imes.module.system.enums.common.SexEnum;
import com.cf.imes.module.system.service.dept.DeptService; import com.cf.imes.module.system.service.dept.DeptService;
import com.cf.imes.module.system.service.dept.PostService;
import com.cf.imes.module.system.service.permission.PermissionService; import com.cf.imes.module.system.service.permission.PermissionService;
import com.cf.imes.module.system.service.organ.OrganService; import com.cf.imes.module.system.service.organ.OrganService;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -69,8 +68,6 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
@MockBean @MockBean
private DeptService deptService; private DeptService deptService;
@MockBean @MockBean
private PostService postService;
@MockBean
private PermissionService permissionService; private PermissionService permissionService;
@MockBean @MockBean
private PasswordEncoder passwordEncoder; private PasswordEncoder passwordEncoder;
@@ -419,7 +416,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> { UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
}); });
// mock 方法,模拟失败 // mock 方法,模拟失败
doThrow(new ServiceException(DEPT_NOT_FOUND)).when(deptService).validateDeptList(any()); doThrow(new ServiceException(DEPT_NOT_FOUND)).when(deptService).validDept(any());
// 调用 // 调用
UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true, 1L); UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true, 1L);
@@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS "system_dept" (
"name" varchar(30) NOT NULL DEFAULT '', "name" varchar(30) NOT NULL DEFAULT '',
"parent_id" bigint NOT NULL DEFAULT '0', "parent_id" bigint NOT NULL DEFAULT '0',
"sort" int NOT NULL DEFAULT '0', "sort" int NOT NULL DEFAULT '0',
"leader_user_id" bigint DEFAULT NULL, "leader" varchar(30) DEFAULT NULL,
"phone" varchar(11) DEFAULT NULL, "phone" varchar(11) DEFAULT NULL,
"email" varchar(50) DEFAULT NULL, "email" varchar(50) DEFAULT NULL,
"status" tinyint NOT NULL, "status" tinyint NOT NULL,
@@ -12,7 +12,7 @@ CREATE TABLE IF NOT EXISTS "system_dept" (
"updater" varchar(64) DEFAULT '', "updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE, "deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint not null default '0', "organ_id" bigint not null default '0',
PRIMARY KEY ("id") PRIMARY KEY ("id")
) COMMENT '部门表'; ) COMMENT '部门表';
@@ -405,7 +405,7 @@ CREATE TABLE IF NOT EXISTS "system_social_user_bind" (
PRIMARY KEY ("id") PRIMARY KEY ("id")
) COMMENT '社交用户的绑定'; ) COMMENT '社交用户的绑定';
CREATE TABLE IF NOT EXISTS "system_tenant" ( CREATE TABLE IF NOT EXISTS "system_organization" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(63) NOT NULL, "name" varchar(63) NOT NULL,
"contact_user_id" bigint NOT NULL DEFAULT '0', "contact_user_id" bigint NOT NULL DEFAULT '0',