1、结构调整

2、抽象工厂优化
This commit is contained in:
2026-03-31 19:35:21 +08:00
parent 87757b352c
commit ebdbdbeb41
667 changed files with 1240 additions and 50173 deletions

View File

@@ -2,21 +2,20 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.njcn</groupId>
<artifactId>msgpush-module-system</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>msgpush-module-system-api</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<artifactId>msgpush-module-system-api</artifactId>
<description>
system 模块 API,暴露给其它模块调用
系统模块接口,暴露给其它模块调用
</description>
<dependencies>
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-common</artifactId>
@@ -24,9 +23,8 @@
<!-- Web 相关 -->
<dependency>
<groupId>org.springdoc</groupId> <!-- 接口文档:使用最新版本的 Swagger 模型 -->
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<scope>provided</scope>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
</dependency>
<!-- 参数校验 -->
@@ -45,4 +43,4 @@
</dependencies>
</project>
</project>

View File

@@ -16,7 +16,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@FeignClient(name = ApiConstants.NAME)
@Tag(name = "RPC 服务 - 部门")
public interface DeptApi {

View File

@@ -17,7 +17,7 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@FeignClient(name = ApiConstants.NAME)
@Tag(name = "RPC 服务 - 岗位")
public interface PostApi {

View File

@@ -16,10 +16,7 @@ public class DeptRespDTO {
@Schema(description = "父部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long parentId;
@Schema(description = "负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long leaderUserId;
@Schema(description = "部门状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status; // 参见 CommonStatusEnum 枚举
private Integer status;
}

View File

@@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@FeignClient(name = ApiConstants.NAME)
@Tag(name = "RPC 服务 - 字典数据")
public interface DictDataApi extends DictDataCommonApi {

View File

@@ -1,24 +0,0 @@
package com.njcn.msgpush.module.system.api.logger;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.module.system.api.logger.dto.LoginLogCreateReqDTO;
import com.njcn.msgpush.module.system.enums.ApiConstants;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import jakarta.validation.Valid;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 登录日志")
public interface LoginLogApi {
String PREFIX = ApiConstants.PREFIX + "/login-log";
@PostMapping(PREFIX + "/create")
@Operation(summary = "创建登录日志")
CommonResult<Boolean> createLoginLog(@Valid @RequestBody LoginLogCreateReqDTO reqDTO);
}

View File

@@ -12,7 +12,7 @@ import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@FeignClient(name = ApiConstants.NAME)
@Tag(name = "RPC 服务 - 操作日志")
public interface OperateLogApi extends OperateLogCommonApi {

View File

@@ -1,28 +0,0 @@
package com.njcn.msgpush.module.system.api.mail;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.module.system.api.mail.dto.MailSendSingleToUserReqDTO;
import com.njcn.msgpush.module.system.enums.ApiConstants;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.RequestBody;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 邮件发送")
public interface MailSendApi {
String PREFIX = ApiConstants.PREFIX + "/mail/send";
@PostMapping(PREFIX + "/send-single-admin")
@Operation(summary = "发送单条邮件给 Admin 用户", description = "在 mail 为空时,使用 userId 加载对应 Admin 的邮箱")
CommonResult<Long> sendSingleMailToAdmin(@Valid @RequestBody MailSendSingleToUserReqDTO reqDTO);
@PostMapping(PREFIX + "/send-single-member")
@Operation(summary = "发送单条邮件给 Member 用户", description = "在 mail 为空时,使用 userId 加载对应 Member 的邮箱")
CommonResult<Long> sendSingleMailToMember(@Valid @RequestBody MailSendSingleToUserReqDTO reqDTO);
}

View File

@@ -1,50 +0,0 @@
package com.njcn.msgpush.module.system.api.mail.dto;
import lombok.Data;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;
/**
* 邮件发送 Request DTO
*
* @author wangjingqi
*/
@Data
public class MailSendSingleToUserReqDTO {
/**
* 用户编号
*
* 如果非空,则加载对应用户的邮箱,添加到 {@link #toMails} 中
*/
private Long userId;
/**
* 收件邮箱
*/
private List<@Email String> toMails;
/**
* 抄送邮箱
*/
private List<@Email String> ccMails;
/**
* 密送邮箱
*/
private List<@Email String> bccMails;
/**
* 邮件模板编号
*/
@NotNull(message = "邮件模板编号不能为空")
private String templateCode;
/**
* 邮件模板参数
*/
private Map<String, Object> templateParams;
}

View File

@@ -1,28 +0,0 @@
package com.njcn.msgpush.module.system.api.notify;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import com.njcn.msgpush.module.system.enums.ApiConstants;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.RequestBody;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 站内信发送")
public interface NotifyMessageSendApi {
String PREFIX = ApiConstants.PREFIX + "/notify/send";
@PostMapping(PREFIX + "/send-single-admin")
@Operation(summary = "发送单条站内信给 Admin 用户")
CommonResult<Long> sendSingleMessageToAdmin(@Valid @RequestBody NotifySendSingleToUserReqDTO reqDTO);
@PostMapping(PREFIX + "/send-single-member")
@Operation(summary = "发送单条站内信给 Member 用户")
CommonResult<Long> sendSingleMessageToMember(@Valid @RequestBody NotifySendSingleToUserReqDTO reqDTO);
}

View File

@@ -1,23 +0,0 @@
package com.njcn.msgpush.module.system.api.notify.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.Map;
@Schema(description = "RPC 服务 - 站内信发送给 Admin 或者 Member 用户 Request DTO")
@Data
public class NotifySendSingleToUserReqDTO {
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "用户编号不能为空")
private Long userId;
@Schema(description = "站内信模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "USER_SEND")
@NotEmpty(message = "站内信模板编号不能为空")
private String templateCode;
@Schema(description = "邮件模板参数")
private Map<String, Object> templateParams;
}

View File

@@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
import java.util.Set;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@FeignClient(name = ApiConstants.NAME)
@Tag(name = "RPC 服务 - 权限")
public interface PermissionApi extends PermissionCommonApi {

View File

@@ -1,25 +0,0 @@
package com.njcn.msgpush.module.system.api.permission;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.module.system.enums.ApiConstants;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
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 RoleApi {
String PREFIX = ApiConstants.PREFIX + "/role";
@GetMapping(PREFIX + "/valid")
@Operation(summary = "校验角色是否合法")
@Parameter(name = "ids", description = "角色编号数组", example = "1,2", required = true)
CommonResult<Boolean> validRoleList(@RequestParam("ids") Collection<Long> ids);
}

View File

@@ -4,9 +4,7 @@ import com.fhs.core.trans.vo.VO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Set;
@Schema(description = "RPC 服务 - Admin 用户 Response DTO")
@Schema(description = "RPC 服务 - 管理后台用户 Response DTO")
@Data
public class AdminUserRespDTO implements VO {
@@ -22,8 +20,8 @@ public class AdminUserRespDTO implements VO {
@Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long deptId;
@Schema(description = "岗位编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 3]")
private Set<Long> postIds;
@Schema(description = "岗位编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long positionId;
@Schema(description = "手机号码", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300")
private String mobile;

View File

@@ -14,9 +14,9 @@ public class ApiConstants {
*
* 注意,需要保证和 spring.application.name 保持一致
*/
public static final String NAME = "system-server";
public static final String NAME = RpcConstants.SYSTEM_NAME;
public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/system";
public static final String PREFIX = RpcConstants.SYSTEM_PREFIX;
public static final String VERSION = "1.0.0";

View File

@@ -1,26 +1,23 @@
package com.njcn.msgpush.module.system.enums;
/**
* System 字典类型的枚举类
*
* @author hongawen
* System dictionary type constants.
*/
public interface DictTypeConstants {
String USER_TYPE = "user_type"; // 用户类型
String COMMON_STATUS = "common_status"; // 系统状态
String USER_TYPE = "user_type";
String COMMON_STATUS = "common_status";
// ========== SYSTEM 模块 ==========
// ========== system ==========
String USER_SEX = "system_user_sex";
String LOGIN_TYPE = "system_login_type";
String LOGIN_RESULT = "system_login_result";
String USER_SEX = "system_user_sex"; // 用户性别
String DATA_SCOPE = "system_data_scope"; // 数据范围
String LOGIN_TYPE = "system_login_type"; // 登录日志的类型
String LOGIN_RESULT = "system_login_result"; // 登录结果
String SMS_CHANNEL_CODE = "system_sms_channel_code"; // 短信渠道编码
String SMS_TEMPLATE_TYPE = "system_sms_template_type"; // 短信模板类型
String SMS_SEND_STATUS = "system_sms_send_status"; // 短信发送状态
String SMS_RECEIVE_STATUS = "system_sms_receive_status"; // 短信接收状态
String JOB_STATUS = "infra_job_status";
String JOB_LOG_STATUS = "infra_job_log_status";
String API_ERROR_LOG_PROCESS_STATUS = "infra_api_error_log_process_status";
String CONFIG_TYPE = "infra_config_type";
String BOOLEAN_STRING = "infra_boolean_string";
String OPERATE_TYPE = "infra_operate_type";
}

View File

@@ -1,5 +1,6 @@
package com.njcn.msgpush.module.system.enums;
import com.njcn.msgpush.framework.common.exception.ErrorCode;
/**
@@ -13,7 +14,10 @@ public interface ErrorCodeConstants {
ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1_002_000_000, "登录失败,账号密码不正确");
ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1_002_000_001, "登录失败,账号被禁用");
ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1_002_000_004, "验证码不正确,原因:{}");
ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1_002_000_005, "未绑定账号,需要进行绑定");
ErrorCode AUTH_MOBILE_NOT_EXISTS = new ErrorCode(1_002_000_007, "手机号不存在");
ErrorCode AUTH_REGISTER_CAPTCHA_CODE_ERROR = new ErrorCode(1_002_000_008, "验证码不正确,原因:{}");
ErrorCode AUTH_LOGIN_USER_RESIGNED = new ErrorCode(1_002_000_009, "登录失败,账号已离职");
// ========== 菜单模块 1-002-001-000 ==========
ErrorCode MENU_NAME_DUPLICATE = new ErrorCode(1_002_001_000, "已经存在该名字的菜单");
@@ -23,12 +27,17 @@ public interface ErrorCodeConstants {
ErrorCode MENU_EXISTS_CHILDREN = new ErrorCode(1_002_001_004, "存在子菜单,无法删除");
ErrorCode MENU_PARENT_NOT_DIR_OR_MENU = new ErrorCode(1_002_001_005, "父菜单的类型必须是目录或者菜单");
ErrorCode MENU_COMPONENT_NAME_DUPLICATE = new ErrorCode(1_002_001_006, "已经存在该组件名的菜单");
ErrorCode MENU_NOT_ENABLE = new ErrorCode(1_002_001_007, "名字为【{}】的菜单已被禁用");
ErrorCode MENU_ROUTE_KIND_INVALID = new ErrorCode(1_002_001_008, "路由类型不合法");
ErrorCode MENU_ROUTE_PROPS_JSON_INVALID = new ErrorCode(1_002_001_009, "路由 props JSON 不合法");
ErrorCode MENU_ROUTE_IFRAME_URL_REQUIRED = new ErrorCode(1_002_001_010, "iframe 路由必须配置 props.url");
ErrorCode MENU_ROUTE_NAME_DUPLICATE = new ErrorCode(1_002_001_011, "路由名重复,请检查菜单数据:{}");
// ========== 角色模块 1-002-002-000 ==========
ErrorCode ROLE_NOT_EXISTS = new ErrorCode(1_002_002_000, "角色不存在");
ErrorCode ROLE_NAME_DUPLICATE = new ErrorCode(1_002_002_001, "已经存在名为【{}】的角色");
ErrorCode ROLE_CODE_DUPLICATE = new ErrorCode(1_002_002_002, "已经存在标识为【{}】的角色");
ErrorCode ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE = new ErrorCode(1_002_002_003, "不能操作类型为系统内置的角色");
ErrorCode ROLE_CAN_NOT_DELETE_SYSTEM_TYPE_ROLE = new ErrorCode(1_002_002_003, "不能删除类型为系统内置的角色");
ErrorCode ROLE_IS_DISABLE = new ErrorCode(1_002_002_004, "名字为【{}】的角色已被禁用");
ErrorCode ROLE_ADMIN_CODE_ERROR = new ErrorCode(1_002_002_005, "标识【{}】不能使用");
@@ -40,9 +49,10 @@ public interface ErrorCodeConstants {
ErrorCode USER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_002_003_004, "导入用户数据不能为空!");
ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1_002_003_005, "用户密码校验失败");
ErrorCode USER_IS_DISABLE = new ErrorCode(1_002_003_006, "名字为【{}】的用户已被禁用");
ErrorCode USER_COUNT_MAX = new ErrorCode(1_002_003_008, "创建用户失败,原因:超过租户最大租户配额({})");
ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空");
ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_002_003_010, "该手机号尚未注册");
ErrorCode USER_REGISTER_DISABLED = new ErrorCode(1_002_003_011, "注册功能已关闭");
ErrorCode USER_IS_RESIGNED = new ErrorCode(1_002_003_012, "名字为【{}】的用户已离职");
// ========== 部门模块 1-002-004-000 ==========
ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门");
@@ -50,14 +60,17 @@ public interface ErrorCodeConstants {
ErrorCode DEPT_NOT_FOUND = new ErrorCode(1_002_004_002, "当前部门不存在");
ErrorCode DEPT_EXITS_CHILDREN = new ErrorCode(1_002_004_003, "存在子部门,无法删除");
ErrorCode DEPT_PARENT_ERROR = new ErrorCode(1_002_004_004, "不能设置自己为父部门");
ErrorCode DEPT_CODE_DUPLICATE = new ErrorCode(1_002_004_005, "已经存在编码为【{}】的部门");
ErrorCode DEPT_NOT_ENABLE = new ErrorCode(1_002_004_006, "部门({})不处于开启状态,不允许选择");
ErrorCode DEPT_PARENT_IS_CHILD = new ErrorCode(1_002_004_007, "不能设置自己的子部门为父部门");
ErrorCode DEPT_ORG_TYPE_INVALID = new ErrorCode(1_002_004_008, "组织类型({})不合法");
// ========== 岗位模块 1-002-005-000 ==========
ErrorCode POST_NOT_FOUND = new ErrorCode(1_002_005_000, "当前岗位不存在");
ErrorCode POST_NOT_ENABLE = new ErrorCode(1_002_005_001, "岗位({}) 不处于开启状态,不允许选择");
ErrorCode POST_NAME_DUPLICATE = new ErrorCode(1_002_005_002, "已经存在该名字的岗位");
ErrorCode POST_CODE_DUPLICATE = new ErrorCode(1_002_005_003, "已经存在该标识的岗位");
ErrorCode POST_TYPE_INVALID = new ErrorCode(1_002_005_004, "岗位类型({})不合法");
// ========== 字典类型 1-002-006-000 ==========
ErrorCode DICT_TYPE_NOT_EXISTS = new ErrorCode(1_002_006_001, "当前字典类型不存在");
@@ -74,6 +87,10 @@ public interface ErrorCodeConstants {
// ========== 通知公告 1-002-008-000 ==========
ErrorCode NOTICE_NOT_FOUND = new ErrorCode(1_002_008_001, "当前通知公告不存在");
// ========= 文件相关 1-001-003-000 =================
ErrorCode FILE_PATH_EXISTS = new ErrorCode(1_001_003_000, "文件路径已存在");
ErrorCode FILE_NOT_EXISTS = new ErrorCode(1_001_003_001, "文件不存在");
ErrorCode FILE_IS_EMPTY = new ErrorCode(1_001_003_002, "文件为空");
// ========== OAuth2 客户端 1-002-020-000 =========
ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1_002_020_000, "OAuth2 客户端不存在");
@@ -93,6 +110,11 @@ public interface ErrorCodeConstants {
ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1_002_022_000, "code 不存在");
ErrorCode OAUTH2_CODE_EXPIRE = new ErrorCode(1_002_022_001, "code 已过期");
// ========== OAuth2 Token 1-002-023-000 =========
ErrorCode OAUTH2_ACCESS_TOKEN_EXPIRE = new ErrorCode(1_002_023_000, "访问令牌已过期");
ErrorCode OAUTH2_REFRESH_TOKEN_EXPIRE = new ErrorCode(1_002_023_001, "刷新令牌已过期");
// ========== 站内信模版 1-002-026-000 ==========
ErrorCode NOTIFY_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_026_000, "站内信模版不存在");
ErrorCode NOTIFY_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_002_026_001, "已经存在编码为【{}】的站内信模板");
@@ -102,4 +124,24 @@ public interface ErrorCodeConstants {
// ========== 站内信发送 1-002-028-000 ==========
ErrorCode NOTIFY_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_028_000, "模板参数({})缺失");
// ========== API 错误日志 1-001-002-000 ==========
ErrorCode API_ERROR_LOG_NOT_FOUND = new ErrorCode(1_001_002_000, "API 错误日志不存在");
ErrorCode API_ERROR_LOG_PROCESSED = new ErrorCode(1_001_002_001, "API 错误日志已处理");
// ========== 文件配置 1-001-006-000 ==========
ErrorCode FILE_CONFIG_NOT_EXISTS = new ErrorCode(1_001_006_000, "文件配置不存在");
ErrorCode FILE_CONFIG_DELETE_FAIL_MASTER = new ErrorCode(1_001_006_001, "该文件配置不允许删除,原因:它是主配置,删除会导致无法上传文件");
// ========== 参数配置 1-001-000-000 ==========
ErrorCode CONFIG_NOT_EXISTS = new ErrorCode(1_001_000_001, "参数配置不存在");
ErrorCode CONFIG_KEY_DUPLICATE = new ErrorCode(1_001_000_002, "参数配置 key 重复");
ErrorCode CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE = new ErrorCode(1_001_000_003, "不能删除类型为系统内置的参数配置");
ErrorCode CONFIG_GET_VALUE_ERROR_IF_VISIBLE = new ErrorCode(1_001_000_004, "获取参数配置失败,原因:不允许获取不可见配置");
ErrorCode ORG_LEADER_RELATION_NOT_FOUND = new ErrorCode(1_002_004_100, "当前组织负责人关系不存在");
ErrorCode ORG_LEADER_USER_NOT_IN_DEPT = new ErrorCode(1_002_004_101, "用户({})不在当前组织范围内,不能设置为负责人");
ErrorCode ORG_LEADER_EFFECTIVE_RANGE_INVALID = new ErrorCode(1_002_004_102, "负责人生效时间区间不合法");
ErrorCode ORG_LEADER_RELATION_OVERLAP = new ErrorCode(1_002_004_103, "同一组织下该用户的负责人时间区间存在重叠");
}

View File

@@ -4,24 +4,18 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 登录日志的类型枚举
* Login log type enum.
*/
@Getter
@AllArgsConstructor
public enum LoginLogTypeEnum {
LOGIN_USERNAME(100), // 使用账号登录
LOGIN_SOCIAL(101), // 使用社交登录
LOGIN_MOBILE(103), // 使用手机登陆
LOGIN_SMS(104), // 使用短信登陆
LOGIN_USERNAME(100),
LOGIN_MOBILE(103),
LOGOUT_SELF(200), // 自己主动登出
LOGOUT_DELETE(202), // 强制退出
;
LOGOUT_SELF(200),
LOGOUT_DELETE(202);
/**
* 日志类型
*/
private final Integer type;
}

View File

@@ -13,6 +13,7 @@ public enum LoginResultEnum {
SUCCESS(0), // 成功
BAD_CREDENTIALS(10), // 账号或密码不正确
USER_DISABLED(20), // 用户被禁用
USER_RESIGNED(21), // 用户已离职
CAPTCHA_NOT_FOUND(30), // 图片验证码不存在
CAPTCHA_CODE_ERROR(31), // 图片验证码不正确

View File

@@ -1,24 +0,0 @@
package com.njcn.msgpush.module.system.enums.mail;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 邮件的发送状态枚举
*
* @author wangjingyi
* @since 2022/4/10 13:39
*/
@Getter
@AllArgsConstructor
public enum MailSendStatusEnum {
INIT(0), // 初始化
SUCCESS(10), // 发送成功
FAILURE(20), // 发送失败
IGNORE(30), // 忽略,即不发送
;
private final int status;
}

View File

@@ -6,7 +6,7 @@ import lombok.Getter;
/**
* 通知模板类型枚举
*
* @author HUIHUI
* @author hongawen
*/
@Getter
@AllArgsConstructor

View File

@@ -1,40 +0,0 @@
package com.njcn.msgpush.module.system.enums.permission;
import com.njcn.msgpush.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 数据范围枚举类
*
* 用于实现数据级别的权限
*
* @author hongawen
*/
@Getter
@AllArgsConstructor
public enum DataScopeEnum implements ArrayValuable<Integer> {
ALL(1), // 全部数据权限
DEPT_CUSTOM(2), // 指定部门数据权限
DEPT_ONLY(3), // 部门数据权限
DEPT_AND_CHILD(4), // 部门及以下数据权限
SELF(5); // 仅本人数据权限
/**
* 范围
*/
private final Integer scope;
public static final Integer[] ARRAYS = Arrays.stream(values()).map(DataScopeEnum::getScope).toArray(Integer[]::new);
@Override
public Integer[] array() {
return ARRAYS;
}
}

View File

@@ -4,27 +4,22 @@ import com.njcn.msgpush.framework.common.util.object.ObjectUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 角色标识枚举
*/
@Getter
@AllArgsConstructor
public enum RoleCodeEnum {
SUPER_ADMIN("super_admin", "超级管理员"); // CRM 系统专用
;
SUPER_ADMIN("super_admin", "超级管理员"),
CRM_ADMIN("crm_admin", "CRM 管理员");
/**
* 角色编码
*/
private final String code;
/**
* 名字
*/
private final String name;
public static boolean isSuperAdmin(String code) {
return ObjectUtils.equalsAny(code, SUPER_ADMIN.getCode());
}
public static boolean isBuiltIn(String code) {
return ObjectUtils.equalsAny(code, SUPER_ADMIN.getCode(), CRM_ADMIN.getCode());
}
}

View File

@@ -1,132 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.njcn</groupId>
<artifactId>msgpush-module-system</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>msgpush-module-system-server</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>
system 模块下,我们放通用业务,支撑上层的核心业务。
例如说:用户、部门、权限、数据字典等等
</description>
<dependencies>
<!-- Spring Cloud 基础 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-env</artifactId>
</dependency>
<!-- 依赖服务 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-module-system-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-module-infra-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- 业务组件 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-biz-ip</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-security</artifactId>
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-mybatis</artifactId>
</dependency>
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-redis</artifactId>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-rpc</artifactId>
</dependency>
<!-- Registry 注册中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Config 配置中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- 消息队列相关 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-mq</artifactId>
</dependency>
<!-- 服务保障相关 TODO 芋艿:暂时去掉 -->
<!-- <dependency>-->
<!-- <groupId>com.njcn</groupId>-->
<!-- <artifactId>msgpush-spring-boot-starter-protection</artifactId>-->
<!-- </dependency>-->
<!-- Test 测试相关 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-excel</artifactId>
</dependency>
<dependency>
<groupId>com.anji-plus</groupId>
<artifactId>captcha-spring-boot-starter</artifactId> <!-- 验证码,一般用于登录使用 -->
</dependency>
</dependencies>
<build>
<!-- 设置构建的 jar 包名 -->
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- 打包 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,26 +0,0 @@
package com.njcn.msgpush.module.system.api.logger;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.module.system.api.logger.dto.LoginLogCreateReqDTO;
import com.njcn.msgpush.module.system.service.logger.LoginLogService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class LoginLogApiImpl implements LoginLogApi {
@Resource
private LoginLogService loginLogService;
@Override
public CommonResult<Boolean> createLoginLog(LoginLogCreateReqDTO reqDTO) {
loginLogService.createLoginLog(reqDTO);
return success(true);
}
}

View File

@@ -1,32 +0,0 @@
package com.njcn.msgpush.module.system.api.notify;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import com.njcn.msgpush.module.system.service.notify.NotifySendService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class NotifyMessageSendApiImpl implements NotifyMessageSendApi {
@Resource
private NotifySendService notifySendService;
@Override
public CommonResult<Long> sendSingleMessageToAdmin(NotifySendSingleToUserReqDTO reqDTO) {
return success(notifySendService.sendSingleNotifyToAdmin(reqDTO.getUserId(),
reqDTO.getTemplateCode(), reqDTO.getTemplateParams()));
}
@Override
public CommonResult<Long> sendSingleMessageToMember(NotifySendSingleToUserReqDTO reqDTO) {
return success(notifySendService.sendSingleNotifyToMember(reqDTO.getUserId(),
reqDTO.getTemplateCode(), reqDTO.getTemplateParams()));
}
}

View File

@@ -1,25 +0,0 @@
package com.njcn.msgpush.module.system.api.permission;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.module.system.service.permission.RoleService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import java.util.Collection;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class RoleApiImpl implements RoleApi {
@Resource
private RoleService roleService;
@Override
public CommonResult<Boolean> validRoleList(Collection<Long> ids) {
roleService.validateRoleList(ids);
return success(true);
}
}

View File

@@ -1,51 +0,0 @@
### 请求 /login 接口 => 成功
POST {{baseUrl}}/system/auth/login
Content-Type: application/json
tag: Yunai.local
{
"username": "admin",
"password": "admin123",
"uuid": "3acd87a09a4f48fb9118333780e94883",
"code": "1024"
}
### 请求 /login 接口【加密 AES】 => 成功
POST {{baseUrl}}/system/auth/login
Content-Type: application/json
tag: Yunai.local
X-API-ENCRYPT: true
WvSX9MOrenyGfBhEM0g1/hHgq8ocktMZ9OwAJ6MOG5FUrzYF/rG5JF1eMptQM1wT73VgDS05l/37WeRtad+JrqChAul/sR/SdOsUKqjBhvvQx1JVhzxr6s8uUP67aKTSZ6Psv7O32ELxXrzSaQvG5CInzz3w6sLtbNNLd1kXe6Q=
### 请求 /login 接口【加密 RSA】 => 成功
POST {{baseUrl}}/system/auth/login
Content-Type: application/json
tag: Yunai.local
X-API-ENCRYPT: true
e7QZTork9ZV5CmgZvSd+cHZk3xdUxKtowLM02kOha+gxHK2H/daU8nVBYS3+bwuDRy5abf+Pz1QJJGVAEd27wwrXBmupOOA/bhpuzzDwcRuJRD+z+YgiNoEXFDRHERxPYlPqAe9zAHtihD0ceub1AjybQsEsROew4C3Q602XYW0=
### 请求 /login 接口 => 成功(无验证码)
POST {{baseUrl}}/system/auth/login
Content-Type: application/json
{
"username": "admin",
"password": "admin123"
}
### 请求 /get-permission-info 接口 => 成功
GET {{baseUrl}}/system/auth/get-permission-info
Authorization: Bearer {{token}}
### 请求 /list-menus 接口 => 成功
GET {{baseUrl}}/system/list-menus
Authorization: Bearer {{token}}
#Authorization: Bearer a6aa7714a2e44c95aaa8a2c5adc2a67a

View File

@@ -1,32 +0,0 @@
package com.njcn.msgpush.module.system.controller.admin.auth.vo;
import com.njcn.msgpush.framework.common.validation.Mobile;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;
@Schema(description = "管理后台 - 短信重置账号密码 Request VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuthResetPasswordReqVO {
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1234")
@NotEmpty(message = "密码不能为空")
@Length(min = 4, max = 16, message = "密码长度为 4-16 位")
private String password;
@Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13312341234")
@NotEmpty(message = "手机号不能为空")
@Mobile
private String mobile;
@Schema(description = "手机短信验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
@NotEmpty(message = "手机手机短信验证码不能为空")
private String code;
}

View File

@@ -1,4 +0,0 @@
### 请求 /menu/list 接口 => 成功
GET {{baseUrl}}/system/dict-data/list-all-simple
Authorization: Bearer {{token}}

View File

@@ -1,5 +0,0 @@
### 获得地区树
GET {{baseUrl}}/system/area/tree
Authorization: Bearer {{token}}

View File

@@ -1,4 +0,0 @@
### 请求 /system/operate-log/page 接口 => 成功
GET {{baseUrl}}/system/operate-log/page
Authorization: Bearer {{token}}

View File

@@ -1,23 +0,0 @@
### 请求 /login 接口 => 成功
POST {{baseUrl}}/system/oauth2-client/create
Content-Type: application/json
Authorization: Bearer {{token}}
{
"id": "1",
"secret": "admin123",
"name": "灿能源码",
"logo": "https://www.iocoder.cn/images/favicon.ico",
"description": "我是描述",
"status": 0,
"accessTokenValiditySeconds": 180,
"refreshTokenValiditySeconds": 8640,
"redirectUris": ["https://www.iocoder.cn"],
"autoApprove": true,
"authorizedGrantTypes": ["password"],
"scopes": ["user_info"],
"authorities": ["system:user:query"],
"resource_ids": ["1024"],
"additionalInformation": "{}"
}

View File

@@ -1,62 +0,0 @@
### 请求 /system/oauth2/authorize 接口 => 成功
GET {{baseUrl}}/system/oauth2/authorize?clientId=default
Authorization: Bearer {{token}}
### 请求 /system/oauth2/authorize + token 接口 => 成功
POST {{baseUrl}}/system/oauth2/authorize
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{token}}
response_type=token&client_id=default&scope={"user.read": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true
### 请求 /system/oauth2/authorize + code 接口 => 成功
POST {{baseUrl}}/system/oauth2/authorize
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{token}}
response_type=code&client_id=default&scope={"user.read": true}&redirect_uri=https://www.iocoder.cn&auto_approve=false
### 请求 /system/oauth2/token + code 接口 => 成功
POST {{baseUrl}}/system/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
grant_type=authorization_code&redirect_uri=https://www.iocoder.cn&code=189956c07a174588a97157eabef2f93a
### 请求 /system/oauth2/token + password 接口 => 成功
POST {{baseUrl}}/system/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
grant_type=password&username=admin&password=admin123&scope=user.read
### 请求 /system/oauth2/token + client_credentials 接口 => 成功
POST {{baseUrl}}/system/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
grant_type=client_credentials&scope=user.read
### 请求 /system/oauth2/token + refresh_token 接口 => 成功
POST {{baseUrl}}/system/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
grant_type=refresh_token&refresh_token=00895465d6994f72a9d926ceeed0f588
### 请求 /system/oauth2/token + DELETE 接口 => 成功
DELETE {{baseUrl}}/system/oauth2/token?token=ca8a188f464441d6949c51493a2b7596
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
### 请求 /system/oauth2/check-token 接口 => 成功
POST {{baseUrl}}/system/oauth2/check-token?token=620d307c5b4148df8a98dd6c6c547106
Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==

View File

@@ -1,14 +0,0 @@
### 请求 /system/oauth2/user/get 接口 => 成功
GET {{baseUrl}}/system/oauth2/user/get
Authorization: Bearer 47f9c74ec11041f193b777ebb95c3b0d
### 请求 /system/oauth2/user/update 接口 => 成功
PUT {{baseUrl}}/system/oauth2/user/update
Content-Type: application/json
Authorization: Bearer 47f9c74ec11041f193b777ebb95c3b0d
{
"nickname": "灿能源码"
}

View File

@@ -1,37 +0,0 @@
package com.njcn.msgpush.module.system.controller.admin.oauth2.vo.open;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Schema(description = "管理后台 - 【开放接口】校验令牌 Response VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OAuth2OpenCheckTokenRespVO {
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "666")
@JsonProperty("user_id")
private Long userId;
@Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@JsonProperty("user_type")
private Integer userType;
@Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "car")
@JsonProperty("client_id")
private String clientId;
@Schema(description = "授权范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "user_info")
private List<String> scopes;
@Schema(description = "访问令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "tudou")
@JsonProperty("access_token")
private String accessToken;
@Schema(description = "过期时间,时间戳 / 1000即单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "1593092157")
private Long exp;
}

View File

@@ -1,4 +0,0 @@
### 请求 /menu/list 接口 => 成功
GET {{baseUrl}}/system/menu/list
Authorization: Bearer {{token}}

View File

@@ -1,42 +0,0 @@
### /role/create 成功
POST {{baseUrl}}/system/role/create
Authorization: Bearer {{token}}
Content-Type: application/json
{
"name": "测试角色",
"code": "test",
"sort": 0
}
### /role/update 成功
POST {{baseUrl}}/system/role/update
Authorization: Bearer {{token}}
Content-Type: application/json
{
"id": 100,
"name": "测试角色",
"code": "test",
"sort": 10
}
### /resource/delete 成功
POST {{baseUrl}}/system/role/delete
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{token}}
roleId=14
### /role/get 成功
GET {{baseUrl}}/system/role/get?id=100
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{token}}
### /role/page 成功
GET {{baseUrl}}/system/role/page?pageNo=1&pageSize=10
Authorization: Bearer {{token}}

View File

@@ -1,28 +0,0 @@
package com.njcn.msgpush.module.system.controller.admin.permission.vo.permission;
import com.njcn.msgpush.framework.common.validation.InEnum;
import com.njcn.msgpush.module.system.enums.permission.DataScopeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
import java.util.Collections;
import java.util.Set;
@Schema(description = "管理后台 - 赋予角色数据权限 Request VO")
@Data
public class PermissionAssignRoleDataScopeReqVO {
@Schema(description = "角色编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "角色编号不能为空")
private Long roleId;
@Schema(description = "数据范围,参见 DataScopeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "数据范围不能为空")
@InEnum(value = DataScopeEnum.class, message = "数据范围必须是 {value}")
private Integer dataScope;
@Schema(description = "部门编号列表,只有范围类型为 DEPT_CUSTOM 时,该字段才需要", example = "1,3,5")
private Set<Long> dataScopeDeptIds = Collections.emptySet(); // 兜底
}

View File

@@ -1,10 +0,0 @@
### 请求 /system/user/page 接口 => 没有权限
GET {{baseUrl}}/system/user/page?pageNo=1&pageSize=10
Authorization: Bearer {{token}}
#Authorization: Bearer test100
### 请求 /system/user/page 接口(测试访问别的租户)
GET {{baseUrl}}/system/user/page?pageNo=1&pageSize=10
Authorization: Bearer {{token}}

View File

@@ -1,4 +0,0 @@
### 请求 /system/user/profile/get 接口 => 没有权限
GET {{baseUrl}}/system/user/profile/get
Authorization: Bearer {{token}}

View File

@@ -1,78 +0,0 @@
package com.njcn.msgpush.module.system.convert.auth;
import cn.hutool.core.collection.CollUtil;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.module.system.controller.admin.auth.vo.AuthPermissionInfoRespVO;
import com.njcn.msgpush.module.system.dal.dataobject.permission.MenuDO;
import com.njcn.msgpush.module.system.dal.dataobject.permission.RoleDO;
import com.njcn.msgpush.module.system.dal.dataobject.user.AdminUserDO;
import com.njcn.msgpush.module.system.enums.permission.MenuTypeEnum;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import org.slf4j.LoggerFactory;
import java.util.*;
import static com.njcn.msgpush.framework.common.util.collection.CollectionUtils.convertSet;
import static com.njcn.msgpush.framework.common.util.collection.CollectionUtils.filterList;
import static com.njcn.msgpush.module.system.dal.dataobject.permission.MenuDO.ID_ROOT;
@Mapper
public interface AuthConvert {
AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class);
default AuthPermissionInfoRespVO convert(AdminUserDO user, List<RoleDO> roleList, List<MenuDO> menuList) {
return AuthPermissionInfoRespVO.builder()
.user(BeanUtils.toBean(user, AuthPermissionInfoRespVO.UserVO.class))
.roles(convertSet(roleList, RoleDO::getCode))
// 权限标识信息
.permissions(convertSet(menuList, MenuDO::getPermission))
// 菜单树
.menus(buildMenuTree(menuList))
.build();
}
AuthPermissionInfoRespVO.MenuVO convertTreeNode(MenuDO menu);
/**
* 将菜单列表,构建成菜单树
*
* @param menuList 菜单列表
* @return 菜单树
*/
default List<AuthPermissionInfoRespVO.MenuVO> buildMenuTree(List<MenuDO> menuList) {
if (CollUtil.isEmpty(menuList)) {
return Collections.emptyList();
}
// 移除按钮
menuList.removeIf(menu -> menu.getType().equals(MenuTypeEnum.BUTTON.getType()));
// 排序,保证菜单的有序性
menuList.sort(Comparator.comparing(MenuDO::getSort));
// 构建菜单树
// 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。
Map<Long, AuthPermissionInfoRespVO.MenuVO> treeNodeMap = new LinkedHashMap<>();
menuList.forEach(menu -> treeNodeMap.put(menu.getId(), AuthConvert.INSTANCE.convertTreeNode(menu)));
// 处理父子关系
treeNodeMap.values().stream().filter(node -> !node.getParentId().equals(ID_ROOT)).forEach(childNode -> {
// 获得父节点
AuthPermissionInfoRespVO.MenuVO parentNode = treeNodeMap.get(childNode.getParentId());
if (parentNode == null) {
LoggerFactory.getLogger(getClass()).error("[buildRouterTree][resource({}) 找不到父资源({})]",
childNode.getId(), childNode.getParentId());
return;
}
// 将自己添加到父节点中
if (parentNode.getChildren() == null) {
parentNode.setChildren(new ArrayList<>());
}
parentNode.getChildren().add(childNode);
});
// 获得到所有的根节点
return filterList(treeNodeMap.values(), node -> ID_ROOT.equals(node.getParentId()));
}
}

View File

@@ -1,40 +0,0 @@
package com.njcn.msgpush.module.system.dal.dataobject.dept;
import com.njcn.msgpush.framework.mybatis.core.dataobject.BaseDO;
import com.njcn.msgpush.module.system.dal.dataobject.user.AdminUserDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 用户和岗位关联
*
* @author ruoyi
*/
@TableName("system_user_post")
@KeySequence("system_user_post_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
public class UserPostDO extends BaseDO {
/**
* 自增主键
*/
@TableId
private Long id;
/**
* 用户 ID
*
* 关联 {@link AdminUserDO#getId()}
*/
private Long userId;
/**
* 角色 ID
*
* 关联 {@link PostDO#getId()}
*/
private Long postId;
}

View File

@@ -1,60 +0,0 @@
package com.njcn.msgpush.module.system.dal.dataobject.mail;
import com.njcn.msgpush.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 邮箱账号 DO
*
* 用途:配置发送邮箱的账号
*
* @author wangjingyi
* @since 2022-03-21
*/
@TableName(value = "system_mail_account", autoResultMap = true)
@KeySequence("system_mail_account_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
public class MailAccountDO extends BaseDO {
/**
* 主键
*/
@TableId
private Long id;
/**
* 邮箱
*/
private String mail;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* SMTP 服务器域名
*/
private String host;
/**
* SMTP 服务器端口
*/
private Integer port;
/**
* 是否开启 SSL
*/
private Boolean sslEnable;
/**
* 是否开启 STARTTLS
*/
private Boolean starttlsEnable;
}

View File

@@ -1,138 +0,0 @@
package com.njcn.msgpush.module.system.dal.dataobject.mail;
import com.njcn.msgpush.framework.common.enums.UserTypeEnum;
import com.njcn.msgpush.framework.mybatis.core.dataobject.BaseDO;
import com.njcn.msgpush.framework.mybatis.core.type.StringListTypeHandler;
import com.njcn.msgpush.module.system.enums.mail.MailSendStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.*;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* 邮箱日志 DO
* 记录每一次邮件的发送
*
* @author wangjingyi
* @since 2022-03-21
*/
@TableName(value = "system_mail_log", autoResultMap = true)
@KeySequence("system_mail_log_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MailLogDO extends BaseDO implements Serializable {
/**
* 日志编号,自增
*/
private Long id;
/**
* 用户编码
*/
private Long userId;
/**
* 用户类型
*
* 枚举 {@link UserTypeEnum}
*/
private Integer userType;
/**
* 接收邮箱地址
*/
@TableField(typeHandler = StringListTypeHandler.class)
private List<String> toMails;
/**
* 接收邮箱地址
*/
@TableField(typeHandler = StringListTypeHandler.class)
private List<String> ccMails;
/**
* 密送邮箱地址
*/
@TableField(typeHandler = StringListTypeHandler.class)
private List<String> bccMails;
/**
* 邮箱账号编号
*
* 关联 {@link MailAccountDO#getId()}
*/
private Long accountId;
/**
* 发送邮箱地址
*
* 冗余 {@link MailAccountDO#getMail()}
*/
private String fromMail;
// ========= 模板相关字段 =========
/**
* 模版编号
*
* 关联 {@link MailTemplateDO#getId()}
*/
private Long templateId;
/**
* 模版编码
*
* 冗余 {@link MailTemplateDO#getCode()}
*/
private String templateCode;
/**
* 模版发送人名称
*
* 冗余 {@link MailTemplateDO#getNickname()}
*/
private String templateNickname;
/**
* 模版标题
*/
private String templateTitle;
/**
* 模版内容
*
* 基于 {@link MailTemplateDO#getContent()} 格式化后的内容
*/
private String templateContent;
/**
* 模版参数
*
* 基于 {@link MailTemplateDO#getParams()} 输入后的参数
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private Map<String, Object> templateParams;
// ========= 发送相关字段 =========
/**
* 发送状态
*
* 枚举 {@link MailSendStatusEnum}
*/
private Integer sendStatus;
/**
* 发送时间
*/
private LocalDateTime sendTime;
/**
* 发送返回的消息 ID
*/
private String sendMessageId;
/**
* 发送异常
*/
private String sendException;
}

View File

@@ -1,74 +0,0 @@
package com.njcn.msgpush.module.system.dal.dataobject.mail;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 邮件模版 DO
*
* @author wangjingyi
* @since 2022-03-21
*/
@TableName(value = "system_mail_template", autoResultMap = true)
@KeySequence("system_mail_template_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
public class MailTemplateDO extends BaseDO {
/**
* 主键
*/
private Long id;
/**
* 模版名称
*/
private String name;
/**
* 模版编号
*/
private String code;
/**
* 发送的邮箱账号编号
*
* 关联 {@link MailAccountDO#getId()}
*/
private Long accountId;
/**
* 发送人名称
*/
private String nickname;
/**
* 标题
*/
private String title;
/**
* 内容
*/
private String content;
/**
* 参数数组(自动根据内容生成)
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> params;
/**
* 状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
/**
* 备注
*/
private String remark;
}

View File

@@ -1,32 +0,0 @@
package com.njcn.msgpush.module.system.dal.mysql.dept;
import com.njcn.msgpush.framework.mybatis.core.mapper.BaseMapperX;
import com.njcn.msgpush.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.njcn.msgpush.module.system.dal.dataobject.dept.UserPostDO;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
@Mapper
public interface UserPostMapper extends BaseMapperX<UserPostDO> {
default List<UserPostDO> selectListByUserId(Long userId) {
return selectList(UserPostDO::getUserId, userId);
}
default void deleteByUserIdAndPostId(Long userId, Collection<Long> postIds) {
delete(new LambdaQueryWrapperX<UserPostDO>()
.eq(UserPostDO::getUserId, userId)
.in(UserPostDO::getPostId, postIds));
}
default List<UserPostDO> selectListByPostIds(Collection<Long> postIds) {
return selectList(UserPostDO::getPostId, postIds);
}
default void deleteByUserId(Long userId) {
delete(Wrappers.lambdaUpdate(UserPostDO.class).eq(UserPostDO::getUserId, userId));
}
}

View File

@@ -1,110 +0,0 @@
package com.njcn.msgpush.module.system.dal.redis;
import com.njcn.msgpush.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
/**
* System Redis Key 枚举类
*
* @author hongawen
*/
public interface RedisKeyConstants {
/**
* 指定部门的所有子部门编号数组的缓存
* <p>
* KEY 格式dept_children_ids:{id}
* VALUE 数据类型String 子部门编号集合
*/
String DEPT_CHILDREN_ID_LIST = "dept_children_ids";
/**
* 角色的缓存
* <p>
* KEY 格式role:{id}
* VALUE 数据类型String 角色信息
*/
String ROLE = "role";
/**
* 用户拥有的角色编号的缓存
* <p>
* KEY 格式user_role_ids:{userId}
* VALUE 数据类型String 角色编号集合
*/
String USER_ROLE_ID_LIST = "user_role_ids";
/**
* 拥有指定菜单的角色编号的缓存
* <p>
* KEY 格式user_role_ids:{menuId}
* VALUE 数据类型String 角色编号集合
*/
String MENU_ROLE_ID_LIST = "menu_role_ids";
/**
* 拥有权限对应的菜单编号数组的缓存
* <p>
* KEY 格式permission_menu_ids:{permission}
* VALUE 数据类型String 菜单编号数组
*/
String PERMISSION_MENU_ID_LIST = "permission_menu_ids";
/**
* OAuth2 客户端的缓存
* <p>
* KEY 格式oauth_client:{id}
* VALUE 数据类型String 客户端信息
*/
String OAUTH_CLIENT = "oauth_client";
/**
* 访问令牌的缓存
* <p>
* KEY 格式oauth2_access_token:{token}
* VALUE 数据类型String 访问令牌信息 {@link OAuth2AccessTokenDO}
* <p>
* 由于动态过期时间,使用 RedisTemplate 操作
*/
String OAUTH2_ACCESS_TOKEN = "oauth2_access_token:%s";
/**
* 站内信模版的缓存
* <p>
* KEY 格式notify_template:{code}
* VALUE 数据格式String 模版信息
*/
String NOTIFY_TEMPLATE = "notify_template";
/**
* 邮件账号的缓存
* <p>
* KEY 格式mail_account:{id}
* VALUE 数据格式String 账号信息
*/
String MAIL_ACCOUNT = "mail_account";
/**
* 邮件模版的缓存
* <p>
* KEY 格式mail_template:{code}
* VALUE 数据格式String 模版信息
*/
String MAIL_TEMPLATE = "mail_template";
/**
* 短信模版的缓存
* <p>
* KEY 格式sms_template:{id}
* VALUE 数据格式String 模版信息
*/
String SMS_TEMPLATE = "sms_template";
/**
* 小程序订阅模版的缓存
*
* KEY 格式wxa_subscribe_template:{userType}
* VALUE 数据格式 String, 模版信息
*/
String WXA_SUBSCRIBE_TEMPLATE = "wxa_subscribe_template";
}

View File

@@ -1,58 +0,0 @@
package com.njcn.msgpush.module.system.mq.message.mail;
import lombok.Data;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.Collection;
import java.util.List;
/**
* 邮箱发送消息
*
* @author hongawen
*/
@Data
public class MailSendMessage {
/**
* 邮件日志编号
*/
@NotNull(message = "邮件日志编号不能为空")
private Long logId;
/**
* 接收邮件地址
*/
@NotEmpty(message = "接收邮件地址不能为空")
private Collection<String> toMails;
/**
* 抄送邮件地址
*/
private Collection<String> ccMails;
/**
* 密送邮件地址
*/
private Collection<String> bccMails;
/**
* 邮件账号编号
*/
@NotNull(message = "邮件账号编号不能为空")
private Long accountId;
/**
* 邮件发件人
*/
private String nickname;
/**
* 邮件标题
*/
@NotEmpty(message = "邮件标题不能为空")
private String title;
/**
* 邮件内容
*/
@NotEmpty(message = "邮件内容不能为空")
private String content;
}

View File

@@ -1,42 +0,0 @@
package com.njcn.msgpush.module.system.mq.message.sms;
import com.njcn.msgpush.framework.common.core.KeyValue;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
import java.util.List;
/**
* 短信发送消息
*
* @author hongawen
*/
@Data
public class SmsSendMessage {
/**
* 短信日志编号
*/
@NotNull(message = "短信日志编号不能为空")
private Long logId;
/**
* 手机号
*/
@NotNull(message = "手机号不能为空")
private String mobile;
/**
* 短信渠道编号
*/
@NotNull(message = "短信渠道编号不能为空")
private Long channelId;
/**
* 短信 API 的模板编号
*/
@NotNull(message = "短信 API 的模板编号不能为空")
private String apiTemplateId;
/**
* 短信模板参数
*/
private List<KeyValue<String, Object>> templateParams;
}

View File

@@ -1,51 +0,0 @@
package com.njcn.msgpush.module.system.mq.producer.mail;
import com.njcn.msgpush.module.system.mq.message.mail.MailSendMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import jakarta.annotation.Resource;
import java.util.Collection;
import java.util.List;
import static java.util.Collections.singletonList;
/**
* Mail 邮件相关消息的 Producer
*
* @author wangjingyi
* @since 2021/4/19 13:33
*/
@Slf4j
@Component
public class MailProducer {
@Resource
private ApplicationContext applicationContext;
/**
* 发送 {@link MailSendMessage} 消息
*
* @param sendLogId 发送日志编码
* @param toMails 接收邮件地址
* @param ccMails 抄送邮件地址
* @param bccMails 密送邮件地址
* @param accountId 邮件账号编号
* @param nickname 邮件发件人
* @param title 邮件标题
* @param content 邮件内容
*/
public void sendMailSendMessage(Long sendLogId,
Collection<String> toMails, Collection<String> ccMails, Collection<String> bccMails,
Long accountId, String nickname, String title, String content) {
MailSendMessage message = new MailSendMessage()
.setLogId(sendLogId)
.setToMails(toMails).setCcMails(ccMails).setBccMails(bccMails)
.setAccountId(accountId).setNickname(nickname)
.setTitle(title).setContent(content);
applicationContext.publishEvent(message);
}
}

View File

@@ -1,41 +0,0 @@
package com.njcn.msgpush.module.system.mq.producer.sms;
import com.njcn.msgpush.framework.common.core.KeyValue;
import com.njcn.msgpush.module.system.mq.message.sms.SmsSendMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import jakarta.annotation.Resource;
import java.util.List;
/**
* Sms 短信相关消息的 Producer
*
* @author zzf
* @since 2021/3/9 16:35
*/
@Slf4j
@Component
public class SmsProducer {
@Resource
private ApplicationContext applicationContext;
/**
* 发送 {@link SmsSendMessage} 消息
*
* @param logId 短信日志编号
* @param mobile 手机号
* @param channelId 渠道编号
* @param apiTemplateId 短信模板编号
* @param templateParams 短信模板参数
*/
public void sendSmsSendMessage(Long logId, String mobile,
Long channelId, String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
SmsSendMessage message = new SmsSendMessage().setLogId(logId).setMobile(mobile);
message.setChannelId(channelId).setApiTemplateId(apiTemplateId).setTemplateParams(templateParams);
applicationContext.publishEvent(message);
}
}

View File

@@ -1,8 +0,0 @@
/**
* system 模块下,我们放通用业务,支撑上层的核心业务。
* 例如说:用户、部门、权限、数据字典等等
*
* 1. Controller URL以 /system/ 开头,避免和其它 Module 冲突
* 2. DataObject 表名:以 system_ 开头,方便在数据库中区分
*/
package com.njcn.msgpush.module.system;

View File

@@ -1,94 +0,0 @@
package com.njcn.msgpush.module.system.service.permission;
import com.njcn.msgpush.module.system.controller.admin.permission.vo.menu.MenuListReqVO;
import com.njcn.msgpush.module.system.controller.admin.permission.vo.menu.MenuSaveVO;
import com.njcn.msgpush.module.system.dal.dataobject.permission.MenuDO;
import java.util.Collection;
import java.util.List;
/**
* 菜单 Service 接口
*
* @author hongawen
*/
public interface MenuService {
/**
* 创建菜单
*
* @param createReqVO 菜单信息
* @return 创建出来的菜单编号
*/
Long createMenu(MenuSaveVO createReqVO);
/**
* 更新菜单
*
* @param updateReqVO 菜单信息
*/
void updateMenu(MenuSaveVO updateReqVO);
/**
* 删除菜单
*
* @param id 菜单编号
*/
void deleteMenu(Long id);
/**
* 批量删除菜单
*
* @param ids 菜单编号数组
*/
void deleteMenuList(List<Long> ids);
/**
* 获得所有菜单列表
*
* @return 菜单列表
*/
List<MenuDO> getMenuList();
/**
* 过滤掉关闭的菜单及其子菜单
*
* @param list 菜单列表
* @return 过滤后的菜单列表
*/
List<MenuDO> filterDisableMenus(List<MenuDO> list);
/**
* 筛选菜单列表
*
* @param reqVO 筛选条件请求 VO
* @return 菜单列表
*/
List<MenuDO> getMenuList(MenuListReqVO reqVO);
/**
* 获得权限对应的菜单编号数组
*
* @param permission 权限标识
* @return 数组
*/
List<Long> getMenuIdListByPermissionFromCache(String permission);
/**
* 获得菜单
*
* @param id 菜单编号
* @return 菜单
*/
MenuDO getMenu(Long id);
/**
* 获得菜单数组
*
* @param ids 菜单编号数组
* @return 菜单数组
*/
List<MenuDO> getMenuList(Collection<Long> ids);
}

View File

@@ -1,56 +0,0 @@
<configuration>
<!-- 参考 org/springframework/boot/logging/logback/defaults.xml 配置,优化 CONSOLE_LOG_PATTERN、FILE_LOG_PATTERN -->
<!-- 格式化输出:%d 表示日期,%thread 表示线程名,%-5level级别从左显示 5 个字符宽度,%msg日志消息%n是换行符 -->
<!-- CONSOLE_LOG_PATTERN 相比 FILE_LOG_PATTERN 多了 highlight、cyan 等高亮 -->
<property name="CONSOLE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}:%L) - %msg%n"/>
<property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n"/>
<!-- 控制台 Appender -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">     
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 文件 Appender -->
<!-- 参考 Spring Boot 的 file-appender.xml 编写 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<!-- 日志文件名 -->
<file>${LOG_FILE}</file>
<!-- 滚动策略:基于【每天 + 大小】创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <!-- 日志文件输出的文件名 -->
<maxHistory>30</maxHistory> <!-- 日志文件的保留天数 -->
<maxFileSize>10MB</maxFileSize> <!-- 日志文件,到达多少容量,进行滚动 -->
</rollingPolicy>
</appender>
<!-- 异步写入日志,提升性能 -->
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold> <!-- 不丢失日志。默认的,如果队列的 80% 已满,则会丢弃 TRACT、DEBUG、INFO 级别的日志 -->
<queueSize>512</queueSize> <!-- 更改默认的队列的深度,该值会影响性能。默认值为 256 -->
<appender-ref ref="FILE"/>
</appender>
<!-- SkyWalking AppenderGRPC 日志收集,实现日志中心 -->
<!--
<appender name="SKYWALKING" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<pattern>[%tid] ${FILE_LOG_PATTERN}</pattern>
</layout>
</encoder>
</appender>
-->
<root level="INFO">
<appender-ref ref="STDOUT"/>
<!-- 本地环境下如果不想【FILE】打印日志可以注释掉本行 -->
<appender-ref ref="ASYNC"/>
<!-- 如果想接入【SkyWalking 日志服务】,可以取消注释掉本行 -->
<!-- <appender-ref ref="SKYWALKING"/> -->
</root>
</configuration>

View File

@@ -1,337 +0,0 @@
package com.njcn.msgpush.module.system.controller.admin.oauth2;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil;
import com.njcn.msgpush.framework.common.core.KeyValue;
import com.njcn.msgpush.framework.common.enums.UserTypeEnum;
import com.njcn.msgpush.framework.common.exception.ErrorCode;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.util.collection.SetUtils;
import com.njcn.msgpush.framework.common.util.object.ObjectUtils;
import com.njcn.msgpush.framework.test.core.ut.BaseMockitoUnitTest;
import com.njcn.msgpush.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO;
import com.njcn.msgpush.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAuthorizeInfoRespVO;
import com.njcn.msgpush.module.system.controller.admin.oauth2.vo.open.OAuth2OpenCheckTokenRespVO;
import com.njcn.msgpush.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import com.njcn.msgpush.module.system.dal.dataobject.oauth2.OAuth2ApproveDO;
import com.njcn.msgpush.module.system.dal.dataobject.oauth2.OAuth2ClientDO;
import com.njcn.msgpush.module.system.enums.oauth2.OAuth2GrantTypeEnum;
import com.njcn.msgpush.module.system.service.oauth2.OAuth2ApproveService;
import com.njcn.msgpush.module.system.service.oauth2.OAuth2ClientService;
import com.njcn.msgpush.module.system.service.oauth2.OAuth2GrantService;
import com.njcn.msgpush.module.system.service.oauth2.OAuth2TokenService;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import jakarta.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import static com.njcn.msgpush.framework.common.util.collection.SetUtils.asSet;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertServiceException;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.randomPojo;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.randomString;
import static java.util.Arrays.asList;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* {@link OAuth2OpenController} 的单元测试
*
* @author hongawen
*/
public class OAuth2OpenControllerTest extends BaseMockitoUnitTest {
@InjectMocks
private OAuth2OpenController oauth2OpenController;
@Mock
private OAuth2GrantService oauth2GrantService;
@Mock
private OAuth2ClientService oauth2ClientService;
@Mock
private OAuth2ApproveService oauth2ApproveService;
@Mock
private OAuth2TokenService oauth2TokenService;
@Test
public void testPostAccessToken_authorizationCode() {
// 准备参数
String granType = OAuth2GrantTypeEnum.AUTHORIZATION_CODE.getGrantType();
String code = randomString();
String redirectUri = randomString();
String state = randomString();
HttpServletRequest request = mockRequest("test_client_id", "test_client_secret");
// mock 方法client
OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("test_client_id");
when(oauth2ClientService.validOAuthClientFromCache(eq("test_client_id"), eq("test_client_secret"), eq(granType), eq(new ArrayList<>()), eq(redirectUri))).thenReturn(client);
// mock 方法(访问令牌)
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class)
.setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 30000L, ChronoUnit.MILLIS));
when(oauth2GrantService.grantAuthorizationCodeForAccessToken(eq("test_client_id"),
eq(code), eq(redirectUri), eq(state))).thenReturn(accessTokenDO);
// 调用
CommonResult<OAuth2OpenAccessTokenRespVO> result = oauth2OpenController.postAccessToken(request, granType,
code, redirectUri, state, null, null, null, null);
// 断言
assertEquals(0, result.getCode());
assertPojoEquals(accessTokenDO, result.getData());
assertTrue(ObjectUtils.equalsAny(result.getData().getExpiresIn(), 29L, 30L)); // 执行过程会过去几毫秒
}
@Test
public void testPostAccessToken_password() {
// 准备参数
String granType = OAuth2GrantTypeEnum.PASSWORD.getGrantType();
String username = randomString();
String password = randomString();
String scope = "write read";
HttpServletRequest request = mockRequest("test_client_id", "test_client_secret");
// mock 方法client
OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("test_client_id");
when(oauth2ClientService.validOAuthClientFromCache(eq("test_client_id"), eq("test_client_secret"),
eq(granType), eq(Lists.newArrayList("write", "read")), isNull())).thenReturn(client);
// mock 方法(访问令牌)
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class)
.setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 30000L, ChronoUnit.MILLIS));
when(oauth2GrantService.grantPassword(eq(username), eq(password), eq("test_client_id"),
eq(Lists.newArrayList("write", "read")))).thenReturn(accessTokenDO);
// 调用
CommonResult<OAuth2OpenAccessTokenRespVO> result = oauth2OpenController.postAccessToken(request, granType,
null, null, null, username, password, scope, null);
// 断言
assertEquals(0, result.getCode());
assertPojoEquals(accessTokenDO, result.getData());
assertTrue(ObjectUtils.equalsAny(result.getData().getExpiresIn(), 29L, 30L)); // 执行过程会过去几毫秒
}
@Test
public void testPostAccessToken_refreshToken() {
// 准备参数
String granType = OAuth2GrantTypeEnum.REFRESH_TOKEN.getGrantType();
String refreshToken = randomString();
String password = randomString();
HttpServletRequest request = mockRequest("test_client_id", "test_client_secret");
// mock 方法client
OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("test_client_id");
when(oauth2ClientService.validOAuthClientFromCache(eq("test_client_id"), eq("test_client_secret"),
eq(granType), eq(Lists.newArrayList()), isNull())).thenReturn(client);
// mock 方法(访问令牌)
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class)
.setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 30000L, ChronoUnit.MILLIS));
when(oauth2GrantService.grantRefreshToken(eq(refreshToken), eq("test_client_id"))).thenReturn(accessTokenDO);
// 调用
CommonResult<OAuth2OpenAccessTokenRespVO> result = oauth2OpenController.postAccessToken(request, granType,
null, null, null, null, password, null, refreshToken);
// 断言
assertEquals(0, result.getCode());
assertPojoEquals(accessTokenDO, result.getData());
assertTrue(ObjectUtils.equalsAny(result.getData().getExpiresIn(), 29L, 30L)); // 执行过程会过去几毫秒
}
@Test
public void testPostAccessToken_implicit() {
// 调用,并断言
assertServiceException(() -> oauth2OpenController.postAccessToken(null,
OAuth2GrantTypeEnum.IMPLICIT.getGrantType(), null, null, null,
null, null, null, null),
new ErrorCode(400, "Token 接口不支持 implicit 授权模式"));
}
@Test
public void testRevokeToken() {
// 准备参数
HttpServletRequest request = mockRequest("demo_client_id", "demo_client_secret");
String token = randomString();
// mock 方法client
OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("demo_client_id");
when(oauth2ClientService.validOAuthClientFromCache(eq("demo_client_id"),
eq("demo_client_secret"), isNull(), isNull(), isNull())).thenReturn(client);
// mock 方法(移除)
when(oauth2GrantService.revokeToken(eq("demo_client_id"), eq(token))).thenReturn(true);
// 调用
CommonResult<Boolean> result = oauth2OpenController.revokeToken(request, token);
// 断言
assertEquals(0, result.getCode());
assertTrue(result.getData());
}
@Test
public void testCheckToken() {
// 准备参数
HttpServletRequest request = mockRequest("demo_client_id", "demo_client_secret");
String token = randomString();
// mock 方法
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class).setUserType(UserTypeEnum.ADMIN.getValue()).setExpiresTime(LocalDateTimeUtil.of(1653485731195L));
when(oauth2TokenService.checkAccessToken(eq(token))).thenReturn(accessTokenDO);
// 调用
CommonResult<OAuth2OpenCheckTokenRespVO> result = oauth2OpenController.checkToken(request, token);
// 断言
assertEquals(0, result.getCode());
assertPojoEquals(accessTokenDO, result.getData());
assertEquals(1653485731L, result.getData().getExp()); // 执行过程会过去几毫秒
}
@Test
public void testAuthorize() {
// 准备参数
String clientId = randomString();
// mock 方法client
OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("demo_client_id").setScopes(ListUtil.toList("read", "write", "all"));
when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(client);
// mock 方法approve
List<OAuth2ApproveDO> approves = asList(
randomPojo(OAuth2ApproveDO.class).setScope("read").setApproved(true),
randomPojo(OAuth2ApproveDO.class).setScope("write").setApproved(false));
when(oauth2ApproveService.getApproveList(isNull(), eq(UserTypeEnum.ADMIN.getValue()), eq(clientId))).thenReturn(approves);
// 调用
CommonResult<OAuth2OpenAuthorizeInfoRespVO> result = oauth2OpenController.authorize(clientId);
// 断言
assertEquals(0, result.getCode());
assertPojoEquals(client, result.getData().getClient());
assertEquals(new KeyValue<>("read", true), result.getData().getScopes().get(0));
assertEquals(new KeyValue<>("write", false), result.getData().getScopes().get(1));
assertEquals(new KeyValue<>("all", false), result.getData().getScopes().get(2));
}
@Test
public void testApproveOrDeny_grantTypeError() {
// 调用,并断言
assertServiceException(() -> oauth2OpenController.approveOrDeny(randomString(), null,
null, null, null, null),
new ErrorCode(400, "response_type 参数值只允许 code 和 token"));
}
@Test // autoApprove = true但是不通过
public void testApproveOrDeny_autoApproveNo() {
// 准备参数
String responseType = "code";
String clientId = randomString();
String scope = "{\"read\": true, \"write\": false}";
String redirectUri = randomString();
String state = randomString();
// mock 方法
OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class);
when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("authorization_code"),
eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client);
// 调用
CommonResult<String> result = oauth2OpenController.approveOrDeny(responseType, clientId,
scope, redirectUri, true, state);
// 断言
assertEquals(0, result.getCode());
assertNull(result.getData());
}
@Test // autoApprove = false但是不通过
public void testApproveOrDeny_ApproveNo() {
// 准备参数
String responseType = "token";
String clientId = randomString();
String scope = "{\"read\": true, \"write\": false}";
String redirectUri = "https://www.iocoder.cn";
String state = "test";
// mock 方法
OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class);
when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("implicit"),
eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client);
// 调用
CommonResult<String> result = oauth2OpenController.approveOrDeny(responseType, clientId,
scope, redirectUri, false, state);
// 断言
assertEquals(0, result.getCode());
assertEquals("https://www.iocoder.cn#error=access_denied&error_description=User%20denied%20access&state=test", result.getData());
}
@Test // autoApprove = true通过 + token
public void testApproveOrDeny_autoApproveWithToken() {
// 准备参数
String responseType = "token";
String clientId = randomString();
String scope = "{\"read\": true, \"write\": false}";
String redirectUri = "https://www.iocoder.cn";
String state = "test";
// mock 方法client)
OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId(clientId).setAdditionalInformation(null);
when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("implicit"),
eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client);
// mock 方法(场景一)
when(oauth2ApproveService.checkForPreApproval(isNull(), eq(UserTypeEnum.ADMIN.getValue()),
eq(clientId), eq(SetUtils.asSet("read", "write")))).thenReturn(true);
// mock 方法(访问令牌)
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class)
.setAccessToken("test_access_token").setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 30010L, ChronoUnit.MILLIS));
when(oauth2GrantService.grantImplicit(isNull(), eq(UserTypeEnum.ADMIN.getValue()),
eq(clientId), eq(ListUtil.toList("read")))).thenReturn(accessTokenDO);
// 调用
CommonResult<String> result = oauth2OpenController.approveOrDeny(responseType, clientId,
scope, redirectUri, true, state);
// 断言
assertEquals(0, result.getCode());
assertThat(result.getData(), anyOf( // 29 和 30 都有一定概率,主要是时间计算
is("https://www.iocoder.cn#access_token=test_access_token&token_type=bearer&state=test&expires_in=29&scope=read"),
is("https://www.iocoder.cn#access_token=test_access_token&token_type=bearer&state=test&expires_in=30&scope=read")
));
}
@Test // autoApprove = false通过 + code
public void testApproveOrDeny_approveWithCode() {
// 准备参数
String responseType = "code";
String clientId = randomString();
String scope = "{\"read\": true, \"write\": false}";
String redirectUri = "https://www.iocoder.cn";
String state = "test";
// mock 方法client)
OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId(clientId).setAdditionalInformation(null);
when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("authorization_code"),
eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client);
// mock 方法(场景二)
when(oauth2ApproveService.updateAfterApproval(isNull(), eq(UserTypeEnum.ADMIN.getValue()), eq(clientId),
eq(MapUtil.builder(new LinkedHashMap<String, Boolean>()).put("read", true).put("write", false).build())))
.thenReturn(true);
// mock 方法(访问令牌)
String authorizationCode = "test_code";
when(oauth2GrantService.grantAuthorizationCodeForCode(isNull(), eq(UserTypeEnum.ADMIN.getValue()),
eq(clientId), eq(ListUtil.toList("read")), eq(redirectUri), eq(state))).thenReturn(authorizationCode);
// 调用
CommonResult<String> result = oauth2OpenController.approveOrDeny(responseType, clientId,
scope, redirectUri, false, state);
// 断言
assertEquals(0, result.getCode());
assertEquals("https://www.iocoder.cn?code=test_code&state=test", result.getData());
}
private HttpServletRequest mockRequest(String clientId, String secret) {
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getParameter(eq("client_id"))).thenReturn(clientId);
when(request.getParameter(eq("client_secret"))).thenReturn(secret);
return request;
}
}

View File

@@ -1,235 +0,0 @@
package com.njcn.msgpush.module.system.service.auth;
import cn.hutool.core.util.ReflectUtil;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.common.enums.UserTypeEnum;
import com.njcn.msgpush.framework.test.core.ut.BaseDbUnitTest;
import com.njcn.msgpush.module.system.controller.admin.auth.vo.*;
import com.njcn.msgpush.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import com.njcn.msgpush.module.system.dal.dataobject.user.AdminUserDO;
import com.njcn.msgpush.module.system.enums.logger.LoginLogTypeEnum;
import com.njcn.msgpush.module.system.enums.logger.LoginResultEnum;
import com.njcn.msgpush.module.system.service.logger.LoginLogService;
import com.njcn.msgpush.module.system.service.member.MemberService;
import com.njcn.msgpush.module.system.service.oauth2.OAuth2TokenService;
import com.njcn.msgpush.module.system.service.user.AdminUserService;
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.service.CaptchaService;
import jakarta.annotation.Resource;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertServiceException;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.randomPojo;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.randomString;
import static com.njcn.msgpush.module.system.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
@Import(AdminAuthServiceImpl.class)
public class AdminAuthServiceImplTest extends BaseDbUnitTest {
@Resource
private AdminAuthServiceImpl authService;
@MockitoBean
private AdminUserService userService;
@MockitoBean
private CaptchaService captchaService;
@MockitoBean
private LoginLogService loginLogService;
@MockitoBean
private OAuth2TokenService oauth2TokenService;
@MockitoBean
private MemberService memberService;
@MockitoBean
private Validator validator;
@BeforeEach
public void setUp() {
authService.setCaptchaEnable(true);
// 注入一个 Validator 对象
ReflectUtil.setFieldValue(authService, "validator",
Validation.buildDefaultValidatorFactory().getValidator());
}
@Test
public void testAuthenticate_success() {
// 准备参数
String username = randomString();
String password = randomString();
// mock user 数据
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username)
.setPassword(password).setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(userService.getUserByUsername(eq(username))).thenReturn(user);
// mock password 匹配
when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true);
// 调用
AdminUserDO loginUser = authService.authenticate(username, password);
// 校验
assertPojoEquals(user, loginUser);
}
@Test
public void testAuthenticate_userNotFound() {
// 准备参数
String username = randomString();
String password = randomString();
// 调用, 并断言异常
assertServiceException(() -> authService.authenticate(username, password),
AUTH_LOGIN_BAD_CREDENTIALS);
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult())
&& o.getUserId() == null)
);
}
@Test
public void testAuthenticate_badCredentials() {
// 准备参数
String username = randomString();
String password = randomString();
// mock user 数据
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username)
.setPassword(password).setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(userService.getUserByUsername(eq(username))).thenReturn(user);
// 调用, 并断言异常
assertServiceException(() -> authService.authenticate(username, password),
AUTH_LOGIN_BAD_CREDENTIALS);
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult())
&& o.getUserId().equals(user.getId()))
);
}
@Test
public void testAuthenticate_userDisabled() {
// 准备参数
String username = randomString();
String password = randomString();
// mock user 数据
AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username)
.setPassword(password).setStatus(CommonStatusEnum.DISABLE.getStatus()));
when(userService.getUserByUsername(eq(username))).thenReturn(user);
// mock password 匹配
when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true);
// 调用, 并断言异常
assertServiceException(() -> authService.authenticate(username, password),
AUTH_LOGIN_USER_DISABLED);
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(LoginResultEnum.USER_DISABLED.getResult())
&& o.getUserId().equals(user.getId()))
);
}
@Test
public void testValidateCaptcha_successWithEnable() {
// 准备参数
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
// mock 验证通过
when(captchaService.verification(argThat(captchaVO -> {
assertEquals(reqVO.getCaptchaVerification(), captchaVO.getCaptchaVerification());
return true;
}))).thenReturn(ResponseModel.success());
// 调用,无需断言
authService.validateCaptcha(reqVO);
}
@Test
public void testValidateCaptcha_successWithDisable() {
// 准备参数
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
// mock 验证码关闭
authService.setCaptchaEnable(false);
// 调用,无需断言
authService.validateCaptcha(reqVO);
}
@Test
public void testCaptcha_fail() {
// 准备参数
AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
// mock 验证通过
when(captchaService.verification(argThat(captchaVO -> {
assertEquals(reqVO.getCaptchaVerification(), captchaVO.getCaptchaVerification());
return true;
}))).thenReturn(ResponseModel.errorMsg("就是不对"));
// 调用, 并断言异常
assertServiceException(() -> authService.validateCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_CODE_ERROR, "就是不对");
// 校验调用参数
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType())
&& o.getResult().equals(LoginResultEnum.CAPTCHA_CODE_ERROR.getResult()))
);
}
@Test
public void testRefreshToken() {
// 准备参数
String refreshToken = randomString();
// mock 方法
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class);
when(oauth2TokenService.refreshAccessToken(eq(refreshToken), eq("default")))
.thenReturn(accessTokenDO);
// 调用
AuthLoginRespVO loginRespVO = authService.refreshToken(refreshToken);
// 断言
assertPojoEquals(accessTokenDO, loginRespVO);
}
@Test
public void testLogout_success() {
// 准备参数
String token = randomString();
// mock
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L)
.setUserType(UserTypeEnum.ADMIN.getValue()));
when(oauth2TokenService.removeAccessToken(eq(token))).thenReturn(accessTokenDO);
// 调用
authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
// 校验调用参数
verify(loginLogService).createLoginLog(
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType())
&& o.getResult().equals(LoginResultEnum.SUCCESS.getResult()))
);
// 调用,并校验
}
@Test
public void testLogout_fail() {
// 准备参数
String token = randomString();
// 调用
authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
// 校验调用参数
verify(loginLogService, never()).createLoginLog(any());
}
}

View File

@@ -1,324 +0,0 @@
package com.njcn.msgpush.module.system.service.dept;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.common.util.object.ObjectUtils;
import com.njcn.msgpush.framework.test.core.ut.BaseDbUnitTest;
import com.njcn.msgpush.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import com.njcn.msgpush.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO;
import com.njcn.msgpush.module.system.dal.dataobject.dept.DeptDO;
import com.njcn.msgpush.module.system.dal.mysql.dept.DeptMapper;
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.Set;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertServiceException;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.*;
import static com.njcn.msgpush.module.system.enums.ErrorCodeConstants.*;
import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link DeptServiceImpl} 的单元测试类
*
* @author niudehua
*/
@Import(DeptServiceImpl.class)
public class DeptServiceImplTest extends BaseDbUnitTest {
@Resource
private DeptServiceImpl deptService;
@Resource
private DeptMapper deptMapper;
@Test
public void testCreateDept() {
// 准备参数
DeptSaveReqVO reqVO = randomPojo(DeptSaveReqVO.class, o -> {
o.setId(null); // 防止 id 被设置
o.setParentId(DeptDO.PARENT_ID_ROOT);
o.setStatus(randomCommonStatus());
});
// 调用
Long deptId = deptService.createDept(reqVO);
// 断言
assertNotNull(deptId);
// 校验记录的属性是否正确
DeptDO deptDO = deptMapper.selectById(deptId);
assertPojoEquals(reqVO, deptDO, "id");
}
@Test
public void testUpdateDept() {
// 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(randomCommonStatus());
});
// 调用
deptService.updateDept(reqVO);
// 校验是否更新正确
DeptDO deptDO = deptMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, deptDO);
}
@Test
public void testDeleteDept_success() {
// mock 数据
DeptDO dbDeptDO = randomPojo(DeptDO.class);
deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbDeptDO.getId();
// 调用
deptService.deleteDept(id);
// 校验数据不存在了
assertNull(deptMapper.selectById(id));
}
@Test
public void testDeleteDept_exitsChildren() {
// mock 数据
DeptDO parentDept = randomPojo(DeptDO.class);
deptMapper.insert(parentDept);// @Sql: 先插入出一条存在的数据
// 准备参数
DeptDO childrenDeptDO = randomPojo(DeptDO.class, o -> {
o.setParentId(parentDept.getId());
o.setStatus(randomCommonStatus());
});
// 插入子部门
deptMapper.insert(childrenDeptDO);
// 调用, 并断言异常
assertServiceException(() -> deptService.deleteDept(parentDept.getId()), DEPT_EXITS_CHILDREN);
}
@Test
public void testDeleteDeptList_success() {
// mock 数据
DeptDO deptDO1 = randomPojo(DeptDO.class);
deptMapper.insert(deptDO1);
DeptDO deptDO2 = randomPojo(DeptDO.class);
deptMapper.insert(deptDO2);
// 准备参数
List<Long> ids = Arrays.asList(deptDO1.getId(), deptDO2.getId());
// 调用
deptService.deleteDeptList(ids);
// 校验数据不存在了
assertNull(deptMapper.selectById(deptDO1.getId()));
assertNull(deptMapper.selectById(deptDO2.getId()));
}
@Test
public void testDeleteDeptList_exitsChildren() {
// mock 数据
DeptDO parentDept = randomPojo(DeptDO.class);
deptMapper.insert(parentDept);
DeptDO childrenDeptDO = randomPojo(DeptDO.class, o -> {
o.setParentId(parentDept.getId());
o.setStatus(randomCommonStatus());
});
deptMapper.insert(childrenDeptDO);
DeptDO anotherDept = randomPojo(DeptDO.class);
deptMapper.insert(anotherDept);
// 准备参数(包含有子部门的 parentDept
List<Long> ids = Arrays.asList(parentDept.getId(), anotherDept.getId());
// 调用, 并断言异常
assertServiceException(() -> deptService.deleteDeptList(ids), DEPT_EXITS_CHILDREN);
}
@Test
public void testValidateParentDept_parentError() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> deptService.validateParentDept(id, id),
DEPT_PARENT_ERROR);
}
@Test
public void testValidateParentDept_parentIsChild() {
// mock 数据(父节点)
DeptDO parentDept = randomPojo(DeptDO.class);
deptMapper.insert(parentDept);
// mock 数据(子节点)
DeptDO childDept = randomPojo(DeptDO.class, o -> {
o.setParentId(parentDept.getId());
});
deptMapper.insert(childDept);
// 准备参数
Long id = parentDept.getId();
Long parentId = childDept.getId();
// 调用, 并断言异常
assertServiceException(() -> deptService.validateParentDept(id, parentId), DEPT_PARENT_IS_CHILD);
}
@Test
public void testValidateNameUnique_duplicate() {
// mock 数据
DeptDO deptDO = randomPojo(DeptDO.class);
deptMapper.insert(deptDO);
// 准备参数
Long id = randomLongId();
Long parentId = deptDO.getParentId();
String name = deptDO.getName();
// 调用, 并断言异常
assertServiceException(() -> deptService.validateDeptNameUnique(id, parentId, name),
DEPT_NAME_DUPLICATE);
}
@Test
public void testGetDept() {
// mock 数据
DeptDO deptDO = randomPojo(DeptDO.class);
deptMapper.insert(deptDO);
// 准备参数
Long id = deptDO.getId();
// 调用
DeptDO dbDept = deptService.getDept(id);
// 断言
assertEquals(deptDO, dbDept);
}
@Test
public void testGetDeptList_ids() {
// mock 数据
DeptDO deptDO01 = randomPojo(DeptDO.class);
deptMapper.insert(deptDO01);
DeptDO deptDO02 = randomPojo(DeptDO.class);
deptMapper.insert(deptDO02);
// 准备参数
List<Long> ids = Arrays.asList(deptDO01.getId(), deptDO02.getId());
// 调用
List<DeptDO> deptDOList = deptService.getDeptList(ids);
// 断言
assertEquals(2, deptDOList.size());
assertEquals(deptDO01, deptDOList.get(0));
assertEquals(deptDO02, deptDOList.get(1));
}
@Test
public void testGetDeptList_reqVO() {
// mock 数据
DeptDO dept = randomPojo(DeptDO.class, o -> { // 等会查询到
o.setName("开发部");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
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());
// 调用
List<DeptDO> sysDeptDOS = deptService.getDeptList(reqVO);
// 断言
assertEquals(1, sysDeptDOS.size());
assertPojoEquals(dept, sysDeptDOS.get(0));
}
@Test
public void testGetChildDeptList() {
// mock 数据1 级别子节点)
DeptDO dept1 = randomPojo(DeptDO.class, o -> o.setName("1"));
deptMapper.insert(dept1);
DeptDO dept2 = randomPojo(DeptDO.class, o -> o.setName("2"));
deptMapper.insert(dept2);
// mock 数据2 级子节点)
DeptDO dept1a = randomPojo(DeptDO.class, o -> o.setName("1-a").setParentId(dept1.getId()));
deptMapper.insert(dept1a);
DeptDO dept2a = randomPojo(DeptDO.class, o -> o.setName("2-a").setParentId(dept2.getId()));
deptMapper.insert(dept2a);
// 准备参数
Long id = dept1.getParentId();
// 调用
List<DeptDO> result = deptService.getChildDeptList(id);
// 断言
assertEquals(result.size(), 2);
assertPojoEquals(dept1, result.get(0));
assertPojoEquals(dept1a, result.get(1));
}
@Test
public void testGetChildDeptListFromCache() {
// mock 数据1 级别子节点)
DeptDO dept1 = randomPojo(DeptDO.class, o -> o.setName("1"));
deptMapper.insert(dept1);
DeptDO dept2 = randomPojo(DeptDO.class, o -> o.setName("2"));
deptMapper.insert(dept2);
// mock 数据2 级子节点)
DeptDO dept1a = randomPojo(DeptDO.class, o -> o.setName("1-a").setParentId(dept1.getId()));
deptMapper.insert(dept1a);
DeptDO dept2a = randomPojo(DeptDO.class, o -> o.setName("2-a").setParentId(dept2.getId()));
deptMapper.insert(dept2a);
// 准备参数
Long id = dept1.getParentId();
// 调用
Set<Long> result = deptService.getChildDeptIdListFromCache(id);
// 断言
assertEquals(result.size(), 2);
assertTrue(result.contains(dept1.getId()));
assertTrue(result.contains(dept1a.getId()));
}
@Test
public void testValidateDeptList_success() {
// mock 数据
DeptDO deptDO = randomPojo(DeptDO.class).setStatus(CommonStatusEnum.ENABLE.getStatus());
deptMapper.insert(deptDO);
// 准备参数
List<Long> ids = singletonList(deptDO.getId());
// 调用,无需断言
deptService.validateDeptList(ids);
}
@Test
public void testValidateDeptList_notFound() {
// 准备参数
List<Long> ids = singletonList(randomLongId());
// 调用, 并断言异常
assertServiceException(() -> deptService.validateDeptList(ids), DEPT_NOT_FOUND);
}
@Test
public void testValidateDeptList_notEnable() {
// mock 数据
DeptDO deptDO = randomPojo(DeptDO.class).setStatus(CommonStatusEnum.DISABLE.getStatus());
deptMapper.insert(deptDO);
// 准备参数
List<Long> ids = singletonList(deptDO.getId());
// 调用, 并断言异常
assertServiceException(() -> deptService.validateDeptList(ids), DEPT_NOT_ENABLE, deptDO.getName());
}
}

View File

@@ -1,248 +0,0 @@
package com.njcn.msgpush.module.system.service.dept;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.common.util.collection.ArrayUtils;
import com.njcn.msgpush.framework.test.core.ut.BaseDbUnitTest;
import com.njcn.msgpush.module.system.controller.admin.dept.vo.post.PostPageReqVO;
import com.njcn.msgpush.module.system.controller.admin.dept.vo.post.PostSaveReqVO;
import com.njcn.msgpush.module.system.dal.dataobject.dept.PostDO;
import com.njcn.msgpush.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.njcn.msgpush.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertServiceException;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.*;
import static com.njcn.msgpush.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()));
// 断言
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));
}
}

View File

@@ -1,352 +0,0 @@
package com.njcn.msgpush.module.system.service.dict;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.common.util.collection.ArrayUtils;
import com.njcn.msgpush.framework.test.core.ut.BaseDbUnitTest;
import com.njcn.msgpush.module.system.controller.admin.dict.vo.data.DictDataPageReqVO;
import com.njcn.msgpush.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO;
import com.njcn.msgpush.module.system.dal.dataobject.dict.DictDataDO;
import com.njcn.msgpush.module.system.dal.dataobject.dict.DictTypeDO;
import com.njcn.msgpush.module.system.dal.mysql.dict.DictDataMapper;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.util.List;
import java.util.function.Consumer;
import static com.njcn.msgpush.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertServiceException;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.*;
import static com.njcn.msgpush.module.system.enums.ErrorCodeConstants.*;
import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@Import(DictDataServiceImpl.class)
public class DictDataServiceImplTest extends BaseDbUnitTest {
@Resource
private DictDataServiceImpl dictDataService;
@Resource
private DictDataMapper dictDataMapper;
@MockitoBean
private DictTypeService dictTypeService;
@Test
public void testGetDictDataList() {
// mock 数据
DictDataDO dictDataDO01 = randomDictDataDO().setDictType("yunai").setSort(2)
.setStatus(CommonStatusEnum.ENABLE.getStatus());
dictDataMapper.insert(dictDataDO01);
DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setSort(1)
.setStatus(CommonStatusEnum.ENABLE.getStatus());
dictDataMapper.insert(dictDataDO02);
DictDataDO dictDataDO03 = randomDictDataDO().setDictType("yunai").setSort(3)
.setStatus(CommonStatusEnum.DISABLE.getStatus());
dictDataMapper.insert(dictDataDO03);
DictDataDO dictDataDO04 = randomDictDataDO().setDictType("yunai2").setSort(3)
.setStatus(CommonStatusEnum.DISABLE.getStatus());
dictDataMapper.insert(dictDataDO04);
// 准备参数
Integer status = CommonStatusEnum.ENABLE.getStatus();
String dictType = "yunai";
// 调用
List<DictDataDO> dictDataDOList = dictDataService.getDictDataList(status, dictType);
// 断言
assertEquals(2, dictDataDOList.size());
assertPojoEquals(dictDataDO02, dictDataDOList.get(0));
assertPojoEquals(dictDataDO01, dictDataDOList.get(1));
}
@Test
public void testGetDictDataPage() {
// mock 数据
DictDataDO dbDictData = randomPojo(DictDataDO.class, o -> { // 等会查询到
o.setLabel("芋艿");
o.setDictType("yunai");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
dictDataMapper.insert(dbDictData);
// 测试 label 不匹配
dictDataMapper.insert(cloneIgnoreId(dbDictData, o -> o.setLabel("")));
// 测试 dictType 不匹配
dictDataMapper.insert(cloneIgnoreId(dbDictData, o -> o.setDictType("nai")));
// 测试 status 不匹配
dictDataMapper.insert(cloneIgnoreId(dbDictData, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 准备参数
DictDataPageReqVO reqVO = new DictDataPageReqVO();
reqVO.setLabel("");
reqVO.setDictType("yunai");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
// 调用
PageResult<DictDataDO> pageResult = dictDataService.getDictDataPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbDictData, pageResult.getList().get(0));
}
@Test
public void testGetDictData() {
// mock 数据
DictDataDO dbDictData = randomDictDataDO();
dictDataMapper.insert(dbDictData);
// 准备参数
Long id = dbDictData.getId();
// 调用
DictDataDO dictData = dictDataService.getDictData(id);
// 断言
assertPojoEquals(dbDictData, dictData);
}
@Test
public void testCreateDictData_success() {
// 准备参数
DictDataSaveReqVO reqVO = randomPojo(DictDataSaveReqVO.class,
o -> o.setStatus(randomCommonStatus()))
.setId(null); // 防止 id 被赋值
// mock 方法
when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType()));
// 调用
Long dictDataId = dictDataService.createDictData(reqVO);
// 断言
assertNotNull(dictDataId);
// 校验记录的属性是否正确
DictDataDO dictData = dictDataMapper.selectById(dictDataId);
assertPojoEquals(reqVO, dictData, "id");
}
@Test
public void testUpdateDictData_success() {
// mock 数据
DictDataDO dbDictData = randomDictDataDO();
dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据
// 准备参数
DictDataSaveReqVO reqVO = randomPojo(DictDataSaveReqVO.class, o -> {
o.setId(dbDictData.getId()); // 设置更新的 ID
o.setStatus(randomCommonStatus());
});
// mock 方法,字典类型
when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType()));
// 调用
dictDataService.updateDictData(reqVO);
// 校验是否更新正确
DictDataDO dictData = dictDataMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, dictData);
}
@Test
public void testDeleteDictData_success() {
// mock 数据
DictDataDO dbDictData = randomDictDataDO();
dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbDictData.getId();
// 调用
dictDataService.deleteDictData(id);
// 校验数据不存在了
assertNull(dictDataMapper.selectById(id));
}
@Test
public void testValidateDictDataExists_success() {
// mock 数据
DictDataDO dbDictData = randomDictDataDO();
dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据
// 调用成功
dictDataService.validateDictDataExists(dbDictData.getId());
}
@Test
public void testValidateDictDataExists_notExists() {
assertServiceException(() -> dictDataService.validateDictDataExists(randomLongId()), DICT_DATA_NOT_EXISTS);
}
@Test
public void testValidateDictTypeExists_success() {
// mock 方法,数据类型被禁用
String type = randomString();
when(dictTypeService.getDictType(eq(type))).thenReturn(randomDictTypeDO(type));
// 调用, 成功
dictDataService.validateDictTypeExists(type);
}
@Test
public void testValidateDictTypeExists_notExists() {
assertServiceException(() -> dictDataService.validateDictTypeExists(randomString()), DICT_TYPE_NOT_EXISTS);
}
@Test
public void testValidateDictTypeExists_notEnable() {
// mock 方法,数据类型被禁用
String dictType = randomString();
when(dictTypeService.getDictType(eq(dictType))).thenReturn(
randomPojo(DictTypeDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 调用, 并断言异常
assertServiceException(() -> dictDataService.validateDictTypeExists(dictType), DICT_TYPE_NOT_ENABLE);
}
@Test
public void testValidateDictDataValueUnique_success() {
// 调用,成功
dictDataService.validateDictDataValueUnique(randomLongId(), randomString(), randomString());
}
@Test
public void testValidateDictDataValueUnique_valueDuplicateForCreate() {
// 准备参数
String dictType = randomString();
String value = randomString();
// mock 数据
dictDataMapper.insert(randomDictDataDO(o -> {
o.setDictType(dictType);
o.setValue(value);
}));
// 调用,校验异常
assertServiceException(() -> dictDataService.validateDictDataValueUnique(null, dictType, value),
DICT_DATA_VALUE_DUPLICATE);
}
@Test
public void testValidateDictDataValueUnique_valueDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
String dictType = randomString();
String value = randomString();
// mock 数据
dictDataMapper.insert(randomDictDataDO(o -> {
o.setDictType(dictType);
o.setValue(value);
}));
// 调用,校验异常
assertServiceException(() -> dictDataService.validateDictDataValueUnique(id, dictType, value),
DICT_DATA_VALUE_DUPLICATE);
}
@Test
public void testGetDictDataCountByDictType() {
// mock 数据
dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("yunai")));
dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("tudou")));
dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("yunai")));
// 准备参数
String dictType = "yunai";
// 调用
long count = dictDataService.getDictDataCountByDictType(dictType);
// 校验
assertEquals(2L, count);
}
@Test
public void testValidateDictDataList_success() {
// mock 数据
DictDataDO dictDataDO = randomDictDataDO().setStatus(CommonStatusEnum.ENABLE.getStatus());
dictDataMapper.insert(dictDataDO);
// 准备参数
String dictType = dictDataDO.getDictType();
List<String> values = singletonList(dictDataDO.getValue());
// 调用,无需断言
dictDataService.validateDictDataList(dictType, values);
}
@Test
public void testValidateDictDataList_notFound() {
// 准备参数
String dictType = randomString();
List<String> values = singletonList(randomString());
// 调用, 并断言异常
assertServiceException(() -> dictDataService.validateDictDataList(dictType, values), DICT_DATA_NOT_EXISTS);
}
@Test
public void testValidateDictDataList_notEnable() {
// mock 数据
DictDataDO dictDataDO = randomDictDataDO().setStatus(CommonStatusEnum.DISABLE.getStatus());
dictDataMapper.insert(dictDataDO);
// 准备参数
String dictType = dictDataDO.getDictType();
List<String> values = singletonList(dictDataDO.getValue());
// 调用, 并断言异常
assertServiceException(() -> dictDataService.validateDictDataList(dictType, values),
DICT_DATA_NOT_ENABLE, dictDataDO.getLabel());
}
@Test
public void testGetDictData_dictType() {
// mock 数据
DictDataDO dictDataDO = randomDictDataDO().setDictType("yunai").setValue("1");
dictDataMapper.insert(dictDataDO);
DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setValue("2");
dictDataMapper.insert(dictDataDO02);
// 准备参数
String dictType = "yunai";
String value = "1";
// 调用
DictDataDO dbDictData = dictDataService.getDictData(dictType, value);
// 断言
assertEquals(dictDataDO, dbDictData);
}
@Test
public void testParseDictData() {
// mock 数据
DictDataDO dictDataDO = randomDictDataDO().setDictType("yunai").setLabel("1");
dictDataMapper.insert(dictDataDO);
DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setLabel("2");
dictDataMapper.insert(dictDataDO02);
// 准备参数
String dictType = "yunai";
String label = "1";
// 调用
DictDataDO dbDictData = dictDataService.parseDictData(dictType, label);
// 断言
assertEquals(dictDataDO, dbDictData);
}
// ========== 随机对象 ==========
@SafeVarargs
private static DictDataDO randomDictDataDO(Consumer<DictDataDO>... consumers) {
Consumer<DictDataDO> consumer = (o) -> {
o.setStatus(randomCommonStatus()); // 保证 status 的范围
};
return randomPojo(DictDataDO.class, ArrayUtils.append(consumer, consumers));
}
/**
* 生成一个有效的字典类型
*
* @param type 字典类型
* @return DictTypeDO 对象
*/
private static DictTypeDO randomDictTypeDO(String type) {
return randomPojo(DictTypeDO.class, o -> {
o.setType(type);
o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 是开启
});
}
}

View File

@@ -1,271 +0,0 @@
package com.njcn.msgpush.module.system.service.dict;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.common.util.collection.ArrayUtils;
import com.njcn.msgpush.framework.test.core.ut.BaseDbUnitTest;
import com.njcn.msgpush.module.system.controller.admin.dict.vo.type.DictTypePageReqVO;
import com.njcn.msgpush.module.system.controller.admin.dict.vo.type.DictTypeSaveReqVO;
import com.njcn.msgpush.module.system.dal.dataobject.dict.DictTypeDO;
import com.njcn.msgpush.module.system.dal.mysql.dict.DictTypeMapper;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.util.List;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static com.njcn.msgpush.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static com.njcn.msgpush.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static com.njcn.msgpush.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertServiceException;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.*;
import static com.njcn.msgpush.module.system.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@Import(DictTypeServiceImpl.class)
public class DictTypeServiceImplTest extends BaseDbUnitTest {
@Resource
private DictTypeServiceImpl dictTypeService;
@Resource
private DictTypeMapper dictTypeMapper;
@MockitoBean
private DictDataService dictDataService;
@Test
public void testGetDictTypePage() {
// mock 数据
DictTypeDO dbDictType = randomPojo(DictTypeDO.class, o -> { // 等会查询到
o.setName("yunai");
o.setType("芋艿");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2021, 1, 15));
});
dictTypeMapper.insert(dbDictType);
// 测试 name 不匹配
dictTypeMapper.insert(cloneIgnoreId(dbDictType, o -> o.setName("tudou")));
// 测试 type 不匹配
dictTypeMapper.insert(cloneIgnoreId(dbDictType, o -> o.setType("土豆")));
// 测试 status 不匹配
dictTypeMapper.insert(cloneIgnoreId(dbDictType, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配
dictTypeMapper.insert(cloneIgnoreId(dbDictType, o -> o.setCreateTime(buildTime(2021, 1, 1))));
// 准备参数
DictTypePageReqVO reqVO = new DictTypePageReqVO();
reqVO.setName("nai");
reqVO.setType("");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setCreateTime(buildBetweenTime(2021, 1, 10, 2021, 1, 20));
// 调用
PageResult<DictTypeDO> pageResult = dictTypeService.getDictTypePage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbDictType, pageResult.getList().get(0));
}
@Test
public void testGetDictType_id() {
// mock 数据
DictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);
// 准备参数
Long id = dbDictType.getId();
// 调用
DictTypeDO dictType = dictTypeService.getDictType(id);
// 断言
assertNotNull(dictType);
assertPojoEquals(dbDictType, dictType);
}
@Test
public void testGetDictType_type() {
// mock 数据
DictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);
// 准备参数
String type = dbDictType.getType();
// 调用
DictTypeDO dictType = dictTypeService.getDictType(type);
// 断言
assertNotNull(dictType);
assertPojoEquals(dbDictType, dictType);
}
@Test
public void testCreateDictType_success() {
// 准备参数
DictTypeSaveReqVO reqVO = randomPojo(DictTypeSaveReqVO.class,
o -> o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()))
.setId(null); // 避免 id 被赋值
// 调用
Long dictTypeId = dictTypeService.createDictType(reqVO);
// 断言
assertNotNull(dictTypeId);
// 校验记录的属性是否正确
DictTypeDO dictType = dictTypeMapper.selectById(dictTypeId);
assertPojoEquals(reqVO, dictType, "id");
}
@Test
public void testUpdateDictType_success() {
// mock 数据
DictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据
// 准备参数
DictTypeSaveReqVO reqVO = randomPojo(DictTypeSaveReqVO.class, o -> {
o.setId(dbDictType.getId()); // 设置更新的 ID
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus());
});
// 调用
dictTypeService.updateDictType(reqVO);
// 校验是否更新正确
DictTypeDO dictType = dictTypeMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, dictType);
}
@Test
public void testDeleteDictType_success() {
// mock 数据
DictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbDictType.getId();
// 调用
dictTypeService.deleteDictType(id);
// 校验数据不存在了
assertNull(dictTypeMapper.selectById(id));
}
@Test
public void testDeleteDictType_hasChildren() {
// mock 数据
DictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbDictType.getId();
// mock 方法
when(dictDataService.getDictDataCountByDictType(eq(dbDictType.getType()))).thenReturn(1L);
// 调用, 并断言异常
assertServiceException(() -> dictTypeService.deleteDictType(id), DICT_TYPE_HAS_CHILDREN);
}
@Test
public void testGetDictTypeList() {
// 准备参数
DictTypeDO dictTypeDO01 = randomDictTypeDO();
dictTypeMapper.insert(dictTypeDO01);
DictTypeDO dictTypeDO02 = randomDictTypeDO();
dictTypeMapper.insert(dictTypeDO02);
// mock 方法
// 调用
List<DictTypeDO> dictTypeDOList = dictTypeService.getDictTypeList();
// 断言
assertEquals(2, dictTypeDOList.size());
assertPojoEquals(dictTypeDO01, dictTypeDOList.get(0));
assertPojoEquals(dictTypeDO02, dictTypeDOList.get(1));
}
@Test
public void testValidateDictDataExists_success() {
// mock 数据
DictTypeDO dbDictType = randomDictTypeDO();
dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据
// 调用成功
dictTypeService.validateDictTypeExists(dbDictType.getId());
}
@Test
public void testValidateDictDataExists_notExists() {
assertServiceException(() -> dictTypeService.validateDictTypeExists(randomLongId()), DICT_TYPE_NOT_EXISTS);
}
@Test
public void testValidateDictTypeUnique_success() {
// 调用,成功
dictTypeService.validateDictTypeUnique(randomLongId(), randomString());
}
@Test
public void testValidateDictTypeUnique_valueDuplicateForCreate() {
// 准备参数
String type = randomString();
// mock 数据
dictTypeMapper.insert(randomDictTypeDO(o -> o.setType(type)));
// 调用,校验异常
assertServiceException(() -> dictTypeService.validateDictTypeUnique(null, type),
DICT_TYPE_TYPE_DUPLICATE);
}
@Test
public void testValidateDictTypeUnique_valueDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
String type = randomString();
// mock 数据
dictTypeMapper.insert(randomDictTypeDO(o -> o.setType(type)));
// 调用,校验异常
assertServiceException(() -> dictTypeService.validateDictTypeUnique(id, type),
DICT_TYPE_TYPE_DUPLICATE);
}
@Test
public void testValidateDictTypNameUnique_success() {
// 调用,成功
dictTypeService.validateDictTypeNameUnique(randomLongId(), randomString());
}
@Test
public void testValidateDictTypeNameUnique_nameDuplicateForCreate() {
// 准备参数
String name = randomString();
// mock 数据
dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name)));
// 调用,校验异常
assertServiceException(() -> dictTypeService.validateDictTypeNameUnique(null, name),
DICT_TYPE_NAME_DUPLICATE);
}
@Test
public void testValidateDictTypeNameUnique_nameDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
String name = randomString();
// mock 数据
dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name)));
// 调用,校验异常
assertServiceException(() -> dictTypeService.validateDictTypeNameUnique(id, name),
DICT_TYPE_NAME_DUPLICATE);
}
// ========== 随机对象 ==========
@SafeVarargs
private static DictTypeDO randomDictTypeDO(Consumer<DictTypeDO>... consumers) {
Consumer<DictTypeDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
};
return randomPojo(DictTypeDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@@ -1,76 +0,0 @@
package com.njcn.msgpush.module.system.service.logger;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.test.core.ut.BaseDbUnitTest;
import com.njcn.msgpush.module.system.api.logger.dto.LoginLogCreateReqDTO;
import com.njcn.msgpush.module.system.controller.admin.logger.vo.loginlog.LoginLogPageReqVO;
import com.njcn.msgpush.module.system.dal.dataobject.logger.LoginLogDO;
import com.njcn.msgpush.module.system.dal.mysql.logger.LoginLogMapper;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import jakarta.annotation.Resource;
import static com.njcn.msgpush.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static com.njcn.msgpush.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static com.njcn.msgpush.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.randomPojo;
import static com.njcn.msgpush.module.system.enums.logger.LoginResultEnum.CAPTCHA_CODE_ERROR;
import static com.njcn.msgpush.module.system.enums.logger.LoginResultEnum.SUCCESS;
import static org.junit.jupiter.api.Assertions.assertEquals;
@Import(LoginLogServiceImpl.class)
public class LoginLogServiceImplTest extends BaseDbUnitTest {
@Resource
private LoginLogServiceImpl loginLogService;
@Resource
private LoginLogMapper loginLogMapper;
@Test
public void testGetLoginLogPage() {
// mock 数据
LoginLogDO loginLogDO = randomPojo(LoginLogDO.class, o -> {
o.setUserIp("192.168.199.16");
o.setUsername("wang");
o.setResult(SUCCESS.getResult());
o.setCreateTime(buildTime(2021, 3, 6));
});
loginLogMapper.insert(loginLogDO);
// 测试 status 不匹配
loginLogMapper.insert(cloneIgnoreId(loginLogDO, o -> o.setResult(CAPTCHA_CODE_ERROR.getResult())));
// 测试 ip 不匹配
loginLogMapper.insert(cloneIgnoreId(loginLogDO, o -> o.setUserIp("192.168.128.18")));
// 测试 username 不匹配
loginLogMapper.insert(cloneIgnoreId(loginLogDO, o -> o.setUsername("yunai")));
// 测试 createTime 不匹配
loginLogMapper.insert(cloneIgnoreId(loginLogDO, o -> o.setCreateTime(buildTime(2021, 2, 6))));
// 构造调用参数
LoginLogPageReqVO reqVO = new LoginLogPageReqVO();
reqVO.setUsername("wang");
reqVO.setUserIp("192.168.199");
reqVO.setStatus(true);
reqVO.setCreateTime(buildBetweenTime(2021, 3, 5, 2021, 3, 7));
// 调用
PageResult<LoginLogDO> pageResult = loginLogService.getLoginLogPage(reqVO);
// 断言,只查到了一条符合条件的
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(loginLogDO, pageResult.getList().get(0));
}
@Test
public void testCreateLoginLog() {
LoginLogCreateReqDTO reqDTO = randomPojo(LoginLogCreateReqDTO.class);
// 调用
loginLogService.createLoginLog(reqDTO);
// 断言
LoginLogDO loginLogDO = loginLogMapper.selectOne(null);
assertPojoEquals(reqDTO, loginLogDO);
}
}

View File

@@ -1,113 +0,0 @@
package com.njcn.msgpush.module.system.service.logger;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.test.core.ut.BaseDbUnitTest;
import com.njcn.msgpush.framework.test.core.util.RandomUtils;
import com.njcn.msgpush.framework.common.biz.system.logger.dto.OperateLogCreateReqDTO;
import com.njcn.msgpush.module.system.api.logger.dto.OperateLogPageReqDTO;
import com.njcn.msgpush.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO;
import com.njcn.msgpush.module.system.dal.dataobject.logger.OperateLogDO;
import com.njcn.msgpush.module.system.dal.mysql.logger.OperateLogMapper;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import static com.njcn.msgpush.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static com.njcn.msgpush.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static com.njcn.msgpush.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
@Import({OperateLogServiceImpl.class})
public class OperateLogServiceImplTest extends BaseDbUnitTest {
@Resource
private OperateLogService operateLogServiceImpl;
@Resource
private OperateLogMapper operateLogMapper;
@Test
public void testCreateOperateLog() {
OperateLogCreateReqDTO reqVO = RandomUtils.randomPojo(OperateLogCreateReqDTO.class);
// 调研
operateLogServiceImpl.createOperateLog(reqVO);
// 断言
OperateLogDO operateLogDO = operateLogMapper.selectOne(null);
assertPojoEquals(reqVO, operateLogDO);
}
@Test
public void testGetOperateLogPage_vo() {
// 构造操作日志
OperateLogDO operateLogDO = RandomUtils.randomPojo(OperateLogDO.class, o -> {
o.setUserId(2048L);
o.setBizId(999L);
o.setType("订单");
o.setSubType("创建订单");
o.setAction("修改编号为 1 的用户信息");
o.setCreateTime(buildTime(2021, 3, 6));
});
operateLogMapper.insert(operateLogDO);
// 测试 userId 不匹配
operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setUserId(1024L)));
// 测试 bizId 不匹配
operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setBizId(888L)));
// 测试 type 不匹配
operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setType("退款")));
// 测试 subType 不匹配
operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setSubType("创建退款")));
// 测试 action 不匹配
operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setAction("修改编号为 1 退款信息")));
// 测试 createTime 不匹配
operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setCreateTime(buildTime(2021, 2, 6))));
// 构造调用参数
OperateLogPageReqVO reqVO = new OperateLogPageReqVO();
reqVO.setUserId(2048L);
reqVO.setBizId(999L);
reqVO.setType("");
reqVO.setSubType("订单");
reqVO.setAction("用户信息");
reqVO.setCreateTime(buildBetweenTime(2021, 3, 5, 2021, 3, 7));
// 调用
PageResult<OperateLogDO> pageResult = operateLogServiceImpl.getOperateLogPage(reqVO);
// 断言,只查到了一条符合条件的
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(operateLogDO, pageResult.getList().get(0));
}
@Test
public void testGetOperateLogPage_dto() {
// 构造操作日志
OperateLogDO operateLogDO = RandomUtils.randomPojo(OperateLogDO.class, o -> {
o.setUserId(2048L);
o.setBizId(999L);
o.setType("订单");
});
operateLogMapper.insert(operateLogDO);
// 测试 userId 不匹配
operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setUserId(1024L)));
// 测试 bizId 不匹配
operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setBizId(888L)));
// 测试 type 不匹配
operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setType("退款")));
// 构造调用参数
OperateLogPageReqDTO reqDTO = new OperateLogPageReqDTO();
reqDTO.setUserId(2048L);
reqDTO.setBizId(999L);
reqDTO.setType("订单");
// 调用
PageResult<OperateLogDO> pageResult = operateLogServiceImpl.getOperateLogPage(reqDTO);
// 断言,只查到了一条符合条件的
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(operateLogDO, pageResult.getList().get(0));
}
}

View File

@@ -1,130 +0,0 @@
package com.njcn.msgpush.module.system.service.notice;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.test.core.ut.BaseDbUnitTest;
import com.njcn.msgpush.module.system.controller.admin.notice.vo.NoticePageReqVO;
import com.njcn.msgpush.module.system.controller.admin.notice.vo.NoticeSaveReqVO;
import com.njcn.msgpush.module.system.dal.dataobject.notice.NoticeDO;
import com.njcn.msgpush.module.system.dal.mysql.notice.NoticeMapper;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import jakarta.annotation.Resource;
import static com.njcn.msgpush.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertServiceException;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.randomLongId;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.randomPojo;
import static com.njcn.msgpush.module.system.enums.ErrorCodeConstants.NOTICE_NOT_FOUND;
import static org.junit.jupiter.api.Assertions.*;
@Import(NoticeServiceImpl.class)
class NoticeServiceImplTest extends BaseDbUnitTest {
@Resource
private NoticeServiceImpl noticeService;
@Resource
private NoticeMapper noticeMapper;
@Test
public void testGetNoticePage_success() {
// 插入前置数据
NoticeDO dbNotice = randomPojo(NoticeDO.class, o -> {
o.setTitle("尼古拉斯赵四来啦!");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
noticeMapper.insert(dbNotice);
// 测试 title 不匹配
noticeMapper.insert(cloneIgnoreId(dbNotice, o -> o.setTitle("尼古拉斯凯奇也来啦!")));
// 测试 status 不匹配
noticeMapper.insert(cloneIgnoreId(dbNotice, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 准备参数
NoticePageReqVO reqVO = new NoticePageReqVO();
reqVO.setTitle("尼古拉斯赵四来啦!");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
// 调用
PageResult<NoticeDO> pageResult = noticeService.getNoticePage(reqVO);
// 验证查询结果经过筛选
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbNotice, pageResult.getList().get(0));
}
@Test
public void testGetNotice_success() {
// 插入前置数据
NoticeDO dbNotice = randomPojo(NoticeDO.class);
noticeMapper.insert(dbNotice);
// 查询
NoticeDO notice = noticeService.getNotice(dbNotice.getId());
// 验证插入与读取对象是否一致
assertNotNull(notice);
assertPojoEquals(dbNotice, notice);
}
@Test
public void testCreateNotice_success() {
// 准备参数
NoticeSaveReqVO reqVO = randomPojo(NoticeSaveReqVO.class)
.setId(null); // 避免 id 被赋值
// 调用
Long noticeId = noticeService.createNotice(reqVO);
// 校验插入属性是否正确
assertNotNull(noticeId);
NoticeDO notice = noticeMapper.selectById(noticeId);
assertPojoEquals(reqVO, notice, "id");
}
@Test
public void testUpdateNotice_success() {
// 插入前置数据
NoticeDO dbNoticeDO = randomPojo(NoticeDO.class);
noticeMapper.insert(dbNoticeDO);
// 准备更新参数
NoticeSaveReqVO reqVO = randomPojo(NoticeSaveReqVO.class, o -> o.setId(dbNoticeDO.getId()));
// 更新
noticeService.updateNotice(reqVO);
// 检验是否更新成功
NoticeDO notice = noticeMapper.selectById(reqVO.getId());
assertPojoEquals(reqVO, notice);
}
@Test
public void testDeleteNotice_success() {
// 插入前置数据
NoticeDO dbNotice = randomPojo(NoticeDO.class);
noticeMapper.insert(dbNotice);
// 删除
noticeService.deleteNotice(dbNotice.getId());
// 检查是否删除成功
assertNull(noticeMapper.selectById(dbNotice.getId()));
}
@Test
public void testValidateNoticeExists_success() {
// 插入前置数据
NoticeDO dbNotice = randomPojo(NoticeDO.class);
noticeMapper.insert(dbNotice);
// 成功调用
noticeService.validateNoticeExists(dbNotice.getId());
}
@Test
public void testValidateNoticeExists_noExists() {
assertServiceException(() ->
noticeService.validateNoticeExists(randomLongId()), NOTICE_NOT_FOUND);
}
}

View File

@@ -1,276 +0,0 @@
package com.njcn.msgpush.module.system.service.notify;
import cn.hutool.core.map.MapUtil;
import com.njcn.msgpush.framework.common.enums.UserTypeEnum;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.test.core.ut.BaseDbUnitTest;
import com.njcn.msgpush.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO;
import com.njcn.msgpush.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO;
import com.njcn.msgpush.module.system.dal.dataobject.notify.NotifyMessageDO;
import com.njcn.msgpush.module.system.dal.dataobject.notify.NotifyTemplateDO;
import com.njcn.msgpush.module.system.dal.mysql.notify.NotifyMessageMapper;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static com.njcn.msgpush.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static com.njcn.msgpush.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static com.njcn.msgpush.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link NotifyMessageServiceImpl} 的单元测试类
*
* @author hongawen
*/
@Import(NotifyMessageServiceImpl.class)
public class NotifyMessageServiceImplTest extends BaseDbUnitTest {
@Resource
private NotifyMessageServiceImpl notifyMessageService;
@Resource
private NotifyMessageMapper notifyMessageMapper;
@Test
public void testCreateNotifyMessage_success() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class);
String templateContent = randomString();
Map<String, Object> templateParams = randomTemplateParams();
// mock 方法
// 调用
Long messageId = notifyMessageService.createNotifyMessage(userId, userType,
template, templateContent, templateParams);
// 断言
NotifyMessageDO message = notifyMessageMapper.selectById(messageId);
assertNotNull(message);
assertEquals(userId, message.getUserId());
assertEquals(userType, message.getUserType());
assertEquals(template.getId(), message.getTemplateId());
assertEquals(template.getCode(), message.getTemplateCode());
assertEquals(template.getType(), message.getTemplateType());
assertEquals(template.getNickname(), message.getTemplateNickname());
assertEquals(templateContent, message.getTemplateContent());
assertEquals(templateParams, message.getTemplateParams());
assertEquals(false, message.getReadStatus());
assertNull(message.getReadTime());
}
@Test
public void testGetNotifyMessagePage() {
// mock 数据
NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到
o.setUserId(1L);
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setTemplateCode("test_01");
o.setTemplateType(10);
o.setCreateTime(buildTime(2022, 1, 2));
o.setTemplateParams(randomTemplateParams());
});
notifyMessageMapper.insert(dbNotifyMessage);
// 测试 userId 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L)));
// 测试 userType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 templateCode 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setTemplateCode("test_11")));
// 测试 templateType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setTemplateType(20)));
// 测试 createTime 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setCreateTime(buildTime(2022, 2, 1))));
// 准备参数
NotifyMessagePageReqVO reqVO = new NotifyMessagePageReqVO();
reqVO.setUserId(1L);
reqVO.setUserType(UserTypeEnum.ADMIN.getValue());
reqVO.setTemplateCode("est_01");
reqVO.setTemplateType(10);
reqVO.setCreateTime(buildBetweenTime(2022, 1, 1, 2022, 1, 10));
// 调用
PageResult<NotifyMessageDO> pageResult = notifyMessageService.getNotifyMessagePage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbNotifyMessage, pageResult.getList().get(0));
}
@Test
public void testGetNotifyMessage() {
// mock 数据
NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class,
o -> o.setTemplateParams(randomTemplateParams()));
notifyMessageMapper.insert(dbNotifyMessage);
// 准备参数
Long id = dbNotifyMessage.getId();
// 调用
NotifyMessageDO notifyMessage = notifyMessageService.getNotifyMessage(id);
assertPojoEquals(dbNotifyMessage, notifyMessage);
}
@Test
public void testGetMyNotifyMessagePage() {
// mock 数据
NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到
o.setUserId(1L);
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setReadStatus(true);
o.setCreateTime(buildTime(2022, 1, 2));
o.setTemplateParams(randomTemplateParams());
});
notifyMessageMapper.insert(dbNotifyMessage);
// 测试 userId 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L)));
// 测试 userType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 readStatus 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(false)));
// 测试 createTime 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setCreateTime(buildTime(2022, 2, 1))));
// 准备参数
Long userId = 1L;
Integer userType = UserTypeEnum.ADMIN.getValue();
NotifyMessageMyPageReqVO reqVO = new NotifyMessageMyPageReqVO();
reqVO.setReadStatus(true);
reqVO.setCreateTime(buildBetweenTime(2022, 1, 1, 2022, 1, 10));
// 调用
PageResult<NotifyMessageDO> pageResult = notifyMessageService.getMyMyNotifyMessagePage(reqVO, userId, userType);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbNotifyMessage, pageResult.getList().get(0));
}
@Test
public void testGetUnreadNotifyMessageList() {
// mock 数据
NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到
o.setUserId(1L);
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setReadStatus(false);
o.setTemplateParams(randomTemplateParams());
});
notifyMessageMapper.insert(dbNotifyMessage);
// 测试 userId 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L)));
// 测试 userType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 readStatus 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true)));
// 准备参数
Long userId = 1L;
Integer userType = UserTypeEnum.ADMIN.getValue();
Integer size = 10;
// 调用
List<NotifyMessageDO> list = notifyMessageService.getUnreadNotifyMessageList(userId, userType, size);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbNotifyMessage, list.get(0));
}
@Test
public void testGetUnreadNotifyMessageCount() {
// mock 数据
NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到
o.setUserId(1L);
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setReadStatus(false);
o.setTemplateParams(randomTemplateParams());
});
notifyMessageMapper.insert(dbNotifyMessage);
// 测试 userId 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L)));
// 测试 userType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 readStatus 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true)));
// 准备参数
Long userId = 1L;
Integer userType = UserTypeEnum.ADMIN.getValue();
// 调用,并断言
assertEquals(1, notifyMessageService.getUnreadNotifyMessageCount(userId, userType));
}
@Test
public void testUpdateNotifyMessageRead() {
// mock 数据
NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到
o.setUserId(1L);
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setReadStatus(false);
o.setReadTime(null);
o.setTemplateParams(randomTemplateParams());
});
notifyMessageMapper.insert(dbNotifyMessage);
// 测试 userId 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L)));
// 测试 userType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 readStatus 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true)));
// 准备参数
Collection<Long> ids = Arrays.asList(dbNotifyMessage.getId(), dbNotifyMessage.getId() + 1,
dbNotifyMessage.getId() + 2, dbNotifyMessage.getId() + 3);
Long userId = 1L;
Integer userType = UserTypeEnum.ADMIN.getValue();
// 调用
int updateCount = notifyMessageService.updateNotifyMessageRead(ids, userId, userType);
// 断言
assertEquals(1, updateCount);
NotifyMessageDO notifyMessage = notifyMessageMapper.selectById(dbNotifyMessage.getId());
assertTrue(notifyMessage.getReadStatus());
assertNotNull(notifyMessage.getReadTime());
}
@Test
public void testUpdateAllNotifyMessageRead() {
// mock 数据
NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到
o.setUserId(1L);
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setReadStatus(false);
o.setReadTime(null);
o.setTemplateParams(randomTemplateParams());
});
notifyMessageMapper.insert(dbNotifyMessage);
// 测试 userId 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L)));
// 测试 userType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 readStatus 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true)));
// 准备参数
Long userId = 1L;
Integer userType = UserTypeEnum.ADMIN.getValue();
// 调用
int updateCount = notifyMessageService.updateAllNotifyMessageRead(userId, userType);
// 断言
assertEquals(1, updateCount);
NotifyMessageDO notifyMessage = notifyMessageMapper.selectById(dbNotifyMessage.getId());
assertTrue(notifyMessage.getReadStatus());
assertNotNull(notifyMessage.getReadTime());
}
private static Map<String, Object> randomTemplateParams() {
return MapUtil.<String, Object>builder().put(randomString(), randomString())
.put(randomString(), randomString()).build();
}
}

View File

@@ -1,190 +0,0 @@
package com.njcn.msgpush.module.system.service.notify;
import cn.hutool.core.map.MapUtil;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.common.enums.UserTypeEnum;
import com.njcn.msgpush.framework.test.core.ut.BaseMockitoUnitTest;
import com.njcn.msgpush.module.system.dal.dataobject.notify.NotifyTemplateDO;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.HashMap;
import java.util.Map;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertServiceException;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.*;
import static com.njcn.msgpush.module.system.enums.ErrorCodeConstants.NOTICE_NOT_FOUND;
import static com.njcn.msgpush.module.system.enums.ErrorCodeConstants.NOTIFY_SEND_TEMPLATE_PARAM_MISS;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
class NotifySendServiceImplTest extends BaseMockitoUnitTest {
@InjectMocks
private NotifySendServiceImpl notifySendService;
@Mock
private NotifyTemplateService notifyTemplateService;
@Mock
private NotifyMessageService notifyMessageService;
@Test
public void testSendSingleNotifyToAdmin() {
// 准备参数
Long userId = randomLongId();
String templateCode = randomString();
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
.put("op", "login").build();
// mock NotifyTemplateService 的方法
NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> {
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setContent("验证码为{code}, 操作为{op}");
o.setParams(Lists.newArrayList("code", "op"));
});
when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
String content = randomString();
when(notifyTemplateService.formatNotifyTemplateContent(eq(template.getContent()), eq(templateParams)))
.thenReturn(content);
// mock NotifyMessageService 的方法
Long messageId = randomLongId();
when(notifyMessageService.createNotifyMessage(eq(userId), eq(UserTypeEnum.ADMIN.getValue()),
eq(template), eq(content), eq(templateParams))).thenReturn(messageId);
// 调用
Long resultMessageId = notifySendService.sendSingleNotifyToAdmin(userId, templateCode, templateParams);
// 断言
assertEquals(messageId, resultMessageId);
}
@Test
public void testSendSingleNotifyToMember() {
// 准备参数
Long userId = randomLongId();
String templateCode = randomString();
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
.put("op", "login").build();
// mock NotifyTemplateService 的方法
NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> {
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setContent("验证码为{code}, 操作为{op}");
o.setParams(Lists.newArrayList("code", "op"));
});
when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
String content = randomString();
when(notifyTemplateService.formatNotifyTemplateContent(eq(template.getContent()), eq(templateParams)))
.thenReturn(content);
// mock NotifyMessageService 的方法
Long messageId = randomLongId();
when(notifyMessageService.createNotifyMessage(eq(userId), eq(UserTypeEnum.MEMBER.getValue()),
eq(template), eq(content), eq(templateParams))).thenReturn(messageId);
// 调用
Long resultMessageId = notifySendService.sendSingleNotifyToMember(userId, templateCode, templateParams);
// 断言
assertEquals(messageId, resultMessageId);
}
/**
* 发送成功,当短信模板开启时
*/
@Test
public void testSendSingleNotify_successWhenMailTemplateEnable() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String templateCode = randomString();
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
.put("op", "login").build();
// mock NotifyTemplateService 的方法
NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> {
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setContent("验证码为{code}, 操作为{op}");
o.setParams(Lists.newArrayList("code", "op"));
});
when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
String content = randomString();
when(notifyTemplateService.formatNotifyTemplateContent(eq(template.getContent()), eq(templateParams)))
.thenReturn(content);
// mock NotifyMessageService 的方法
Long messageId = randomLongId();
when(notifyMessageService.createNotifyMessage(eq(userId), eq(userType),
eq(template), eq(content), eq(templateParams))).thenReturn(messageId);
// 调用
Long resultMessageId = notifySendService.sendSingleNotify(userId, userType, templateCode, templateParams);
// 断言
assertEquals(messageId, resultMessageId);
}
/**
* 发送成功,当短信模板关闭时
*/
@Test
public void testSendSingleMail_successWhenSmsTemplateDisable() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String templateCode = randomString();
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
.put("op", "login").build();
// mock NotifyTemplateService 的方法
NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> {
o.setStatus(CommonStatusEnum.DISABLE.getStatus());
o.setContent("验证码为{code}, 操作为{op}");
o.setParams(Lists.newArrayList("code", "op"));
});
when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
// 调用
Long resultMessageId = notifySendService.sendSingleNotify(userId, userType, templateCode, templateParams);
// 断言
assertNull(resultMessageId);
verify(notifyTemplateService, never()).formatNotifyTemplateContent(anyString(), anyMap());
verify(notifyMessageService, never()).createNotifyMessage(anyLong(), anyInt(), any(), anyString(), anyMap());
}
@Test
public void testCheckMailTemplateValid_notExists() {
// 准备参数
String templateCode = randomString();
// mock 方法
// 调用,并断言异常
assertServiceException(() -> notifySendService.validateNotifyTemplate(templateCode),
NOTICE_NOT_FOUND);
}
@Test
public void testCheckTemplateParams_paramMiss() {
// 准备参数
NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class,
o -> o.setParams(Lists.newArrayList("code")));
Map<String, Object> templateParams = new HashMap<>();
// mock 方法
// 调用,并断言异常
assertServiceException(() -> notifySendService.validateTemplateParams(template, templateParams),
NOTIFY_SEND_TEMPLATE_PARAM_MISS, "code");
}
@Test
public void testSendBatchNotify() {
// 准备参数
// mock 方法
// 调用
UnsupportedOperationException exception = Assertions.assertThrows(
UnsupportedOperationException.class,
() -> notifySendService.sendBatchNotify(null, null, null, null, null)
);
// 断言
assertEquals("暂时不支持该操作,感兴趣可以实现该功能哟!", exception.getMessage());
}
}

View File

@@ -1,178 +0,0 @@
package com.njcn.msgpush.module.system.service.notify;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.test.core.ut.BaseDbUnitTest;
import com.njcn.msgpush.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO;
import com.njcn.msgpush.module.system.controller.admin.notify.vo.template.NotifyTemplateSaveReqVO;
import com.njcn.msgpush.module.system.dal.dataobject.notify.NotifyTemplateDO;
import com.njcn.msgpush.module.system.dal.mysql.notify.NotifyTemplateMapper;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import jakarta.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import static com.njcn.msgpush.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static com.njcn.msgpush.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static com.njcn.msgpush.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertServiceException;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.*;
import static com.njcn.msgpush.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link NotifyTemplateServiceImpl} 的单元测试类
*
* @author hongawen
*/
@Import(NotifyTemplateServiceImpl.class)
public class NotifyTemplateServiceImplTest extends BaseDbUnitTest {
@Resource
private NotifyTemplateServiceImpl notifyTemplateService;
@Resource
private NotifyTemplateMapper notifyTemplateMapper;
@Test
public void testCreateNotifyTemplate_success() {
// 准备参数
NotifyTemplateSaveReqVO reqVO = randomPojo(NotifyTemplateSaveReqVO.class,
o -> o.setStatus(randomCommonStatus()))
.setId(null); // 防止 id 被赋值
// 调用
Long notifyTemplateId = notifyTemplateService.createNotifyTemplate(reqVO);
// 断言
assertNotNull(notifyTemplateId);
// 校验记录的属性是否正确
NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(notifyTemplateId);
assertPojoEquals(reqVO, notifyTemplate, "id");
}
@Test
public void testUpdateNotifyTemplate_success() {
// mock 数据
NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class);
notifyTemplateMapper.insert(dbNotifyTemplate);// @Sql: 先插入出一条存在的数据
// 准备参数
NotifyTemplateSaveReqVO reqVO = randomPojo(NotifyTemplateSaveReqVO.class, o -> {
o.setId(dbNotifyTemplate.getId()); // 设置更新的 ID
o.setStatus(randomCommonStatus());
});
// 调用
notifyTemplateService.updateNotifyTemplate(reqVO);
// 校验是否更新正确
NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, notifyTemplate);
}
@Test
public void testUpdateNotifyTemplate_notExists() {
// 准备参数
NotifyTemplateSaveReqVO reqVO = randomPojo(NotifyTemplateSaveReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> notifyTemplateService.updateNotifyTemplate(reqVO), NOTIFY_TEMPLATE_NOT_EXISTS);
}
@Test
public void testDeleteNotifyTemplate_success() {
// mock 数据
NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class);
notifyTemplateMapper.insert(dbNotifyTemplate);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbNotifyTemplate.getId();
// 调用
notifyTemplateService.deleteNotifyTemplate(id);
// 校验数据不存在了
assertNull(notifyTemplateMapper.selectById(id));
}
@Test
public void testDeleteNotifyTemplate_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> notifyTemplateService.deleteNotifyTemplate(id), NOTIFY_TEMPLATE_NOT_EXISTS);
}
@Test
public void testGetNotifyTemplatePage() {
// mock 数据
NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class, o -> { // 等会查询到
o.setName("芋头");
o.setCode("test_01");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2022, 2, 3));
});
notifyTemplateMapper.insert(dbNotifyTemplate);
// 测试 name 不匹配
notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setName("")));
// 测试 code 不匹配
notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCode("test_02")));
// 测试 status 不匹配
notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配
notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCreateTime(buildTime(2022, 1, 5))));
// 准备参数
NotifyTemplatePageReqVO reqVO = new NotifyTemplatePageReqVO();
reqVO.setName("");
reqVO.setCode("est_01");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setCreateTime(buildBetweenTime(2022, 2, 1, 2022, 2, 5));
// 调用
PageResult<NotifyTemplateDO> pageResult = notifyTemplateService.getNotifyTemplatePage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbNotifyTemplate, pageResult.getList().get(0));
}
@Test
public void testGetNotifyTemplate() {
// mock 数据
NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class);
notifyTemplateMapper.insert(dbNotifyTemplate);
// 准备参数
Long id = dbNotifyTemplate.getId();
// 调用
NotifyTemplateDO notifyTemplate = notifyTemplateService.getNotifyTemplate(id);
// 断言
assertPojoEquals(dbNotifyTemplate, notifyTemplate);
}
@Test
public void testGetNotifyTemplateByCodeFromCache() {
// mock 数据
NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class);
notifyTemplateMapper.insert(dbNotifyTemplate);
// 准备参数
String code = dbNotifyTemplate.getCode();
// 调用
NotifyTemplateDO notifyTemplate = notifyTemplateService.getNotifyTemplateByCodeFromCache(code);
// 断言
assertPojoEquals(dbNotifyTemplate, notifyTemplate);
}
@Test
public void testFormatNotifyTemplateContent() {
// 准备参数
Map<String, Object> params = new HashMap<>();
params.put("name", "小红");
params.put("what", "");
// 调用,并断言
assertEquals("小红,你好,饭吃了吗?",
notifyTemplateService.formatNotifyTemplateContent("{name},你好,{what}吃了吗?", params));
}
}

View File

@@ -1,272 +0,0 @@
package com.njcn.msgpush.module.system.service.oauth2;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import com.njcn.msgpush.framework.common.enums.UserTypeEnum;
import com.njcn.msgpush.framework.common.util.date.DateUtils;
import com.njcn.msgpush.framework.test.core.ut.BaseDbUnitTest;
import com.njcn.msgpush.module.system.dal.dataobject.oauth2.OAuth2ApproveDO;
import com.njcn.msgpush.module.system.dal.dataobject.oauth2.OAuth2ClientDO;
import com.njcn.msgpush.module.system.dal.mysql.oauth2.OAuth2ApproveMapper;
import jakarta.annotation.Resource;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static cn.hutool.core.util.RandomUtil.*;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.*;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.randomString;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
/**
* {@link OAuth2ApproveServiceImpl} 的单元测试类
*
* @author hongawen
*/
@Import(OAuth2ApproveServiceImpl.class)
public class OAuth2ApproveServiceImplTest extends BaseDbUnitTest {
@Resource
private OAuth2ApproveServiceImpl oauth2ApproveService;
@Resource
private OAuth2ApproveMapper oauth2ApproveMapper;
@MockitoBean
private OAuth2ClientService oauth2ClientService;
@Test
public void checkForPreApproval_clientAutoApprove() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String clientId = randomString();
List<String> requestedScopes = Lists.newArrayList("read");
// mock 方法
when(oauth2ClientService.validOAuthClientFromCache(eq(clientId)))
.thenReturn(randomPojo(OAuth2ClientDO.class).setAutoApproveScopes(requestedScopes));
// 调用
boolean success = oauth2ApproveService.checkForPreApproval(userId, userType,
clientId, requestedScopes);
// 断言
assertTrue(success);
List<OAuth2ApproveDO> result = oauth2ApproveMapper.selectList();
assertEquals(1, result.size());
assertEquals(userId, result.get(0).getUserId());
assertEquals(userType, result.get(0).getUserType());
assertEquals(clientId, result.get(0).getClientId());
assertEquals("read", result.get(0).getScope());
assertTrue(result.get(0).getApproved());
assertFalse(DateUtils.isExpired(result.get(0).getExpiresTime()));
}
@Test
public void checkForPreApproval_approve() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String clientId = randomString();
List<String> requestedScopes = Lists.newArrayList("read");
// mock 方法
when(oauth2ClientService.validOAuthClientFromCache(eq(clientId)))
.thenReturn(randomPojo(OAuth2ClientDO.class).setAutoApproveScopes(null));
// mock 数据
OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class).setUserId(userId)
.setUserType(userType).setClientId(clientId).setScope("read")
.setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 1L, ChronoUnit.DAYS)).setApproved(true); // 同意
oauth2ApproveMapper.insert(approve);
// 调用
boolean success = oauth2ApproveService.checkForPreApproval(userId, userType,
clientId, requestedScopes);
// 断言
assertTrue(success);
}
@Test
public void checkForPreApproval_reject() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String clientId = randomString();
List<String> requestedScopes = Lists.newArrayList("read");
// mock 方法
when(oauth2ClientService.validOAuthClientFromCache(eq(clientId)))
.thenReturn(randomPojo(OAuth2ClientDO.class).setAutoApproveScopes(null));
// mock 数据
OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class).setUserId(userId)
.setUserType(userType).setClientId(clientId).setScope("read")
.setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 1L, ChronoUnit.DAYS)).setApproved(false); // 拒绝
oauth2ApproveMapper.insert(approve);
// 调用
boolean success = oauth2ApproveService.checkForPreApproval(userId, userType,
clientId, requestedScopes);
// 断言
assertFalse(success);
}
@Test
public void testUpdateAfterApproval_none() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String clientId = randomString();
// 调用
boolean success = oauth2ApproveService.updateAfterApproval(userId, userType, clientId,
null);
// 断言
assertTrue(success);
List<OAuth2ApproveDO> result = oauth2ApproveMapper.selectList();
assertEquals(0, result.size());
}
@Test
public void testUpdateAfterApproval_approved() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String clientId = randomString();
Map<String, Boolean> requestedScopes = new LinkedHashMap<>(); // 有序,方便判断
requestedScopes.put("read", true);
requestedScopes.put("write", false);
// mock 方法
// 调用
boolean success = oauth2ApproveService.updateAfterApproval(userId, userType, clientId,
requestedScopes);
// 断言
assertTrue(success);
List<OAuth2ApproveDO> result = oauth2ApproveMapper.selectList();
assertEquals(2, result.size());
// read
assertEquals(userId, result.get(0).getUserId());
assertEquals(userType, result.get(0).getUserType());
assertEquals(clientId, result.get(0).getClientId());
assertEquals("read", result.get(0).getScope());
assertTrue(result.get(0).getApproved());
assertFalse(DateUtils.isExpired(result.get(0).getExpiresTime()));
// write
assertEquals(userId, result.get(1).getUserId());
assertEquals(userType, result.get(1).getUserType());
assertEquals(clientId, result.get(1).getClientId());
assertEquals("write", result.get(1).getScope());
assertFalse(result.get(1).getApproved());
assertFalse(DateUtils.isExpired(result.get(1).getExpiresTime()));
}
@Test
public void testUpdateAfterApproval_reject() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String clientId = randomString();
Map<String, Boolean> requestedScopes = new LinkedHashMap<>();
requestedScopes.put("write", false);
// mock 方法
// 调用
boolean success = oauth2ApproveService.updateAfterApproval(userId, userType, clientId,
requestedScopes);
// 断言
assertFalse(success);
List<OAuth2ApproveDO> result = oauth2ApproveMapper.selectList();
assertEquals(1, result.size());
// write
assertEquals(userId, result.get(0).getUserId());
assertEquals(userType, result.get(0).getUserType());
assertEquals(clientId, result.get(0).getClientId());
assertEquals("write", result.get(0).getScope());
assertFalse(result.get(0).getApproved());
assertFalse(DateUtils.isExpired(result.get(0).getExpiresTime()));
}
@Test
public void testGetApproveList() {
// 准备参数
Long userId = 10L;
Integer userType = UserTypeEnum.ADMIN.getValue();
String clientId = randomString();
// mock 数据
OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class).setUserId(userId)
.setUserType(userType).setClientId(clientId).setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 1L, ChronoUnit.DAYS));
oauth2ApproveMapper.insert(approve); // 未过期
oauth2ApproveMapper.insert(ObjectUtil.clone(approve).setId(null)
.setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), -1L, ChronoUnit.DAYS))); // 已过期
// 调用
List<OAuth2ApproveDO> result = oauth2ApproveService.getApproveList(userId, userType, clientId);
// 断言
assertEquals(1, result.size());
// TODO @芋艿expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。
assertPojoEquals(approve, result.get(0), "expiresTime");
}
@Test
public void testSaveApprove_insert() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String clientId = randomString();
String scope = randomString();
Boolean approved = randomBoolean();
LocalDateTime expireTime = LocalDateTime.ofInstant(randomDay(1, 30).toInstant(), ZoneId.systemDefault());
// mock 方法
// 调用
oauth2ApproveService.saveApprove(userId, userType, clientId,
scope, approved, expireTime);
// 断言
List<OAuth2ApproveDO> result = oauth2ApproveMapper.selectList();
assertEquals(1, result.size());
assertEquals(userId, result.get(0).getUserId());
assertEquals(userType, result.get(0).getUserType());
assertEquals(clientId, result.get(0).getClientId());
assertEquals(scope, result.get(0).getScope());
assertEquals(approved, result.get(0).getApproved());
assertEquals(expireTime, result.get(0).getExpiresTime());
}
@Test
public void testSaveApprove_update() {
// mock 数据
OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class);
oauth2ApproveMapper.insert(approve);
// 准备参数
Long userId = approve.getUserId();
Integer userType = approve.getUserType();
String clientId = approve.getClientId();
String scope = approve.getScope();
Boolean approved = randomBoolean();
LocalDateTime expireTime = LocalDateTime.ofInstant(randomDay(1, 30).toInstant(), ZoneId.systemDefault());
// mock 方法
// 调用
oauth2ApproveService.saveApprove(userId, userType, clientId,
scope, approved, expireTime);
// 断言
List<OAuth2ApproveDO> result = oauth2ApproveMapper.selectList();
assertEquals(1, result.size());
assertEquals(approve.getId(), result.get(0).getId());
assertEquals(userId, result.get(0).getUserId());
assertEquals(userType, result.get(0).getUserType());
assertEquals(clientId, result.get(0).getClientId());
assertEquals(scope, result.get(0).getScope());
assertEquals(approved, result.get(0).getApproved());
assertEquals(expireTime, result.get(0).getExpiresTime());
}
}

View File

@@ -1,220 +0,0 @@
package com.njcn.msgpush.module.system.service.oauth2;
import cn.hutool.extra.spring.SpringUtil;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.test.core.ut.BaseDbUnitTest;
import com.njcn.msgpush.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO;
import com.njcn.msgpush.module.system.controller.admin.oauth2.vo.client.OAuth2ClientSaveReqVO;
import com.njcn.msgpush.module.system.dal.dataobject.oauth2.OAuth2ClientDO;
import com.njcn.msgpush.module.system.dal.mysql.oauth2.OAuth2ClientMapper;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.context.annotation.Import;
import java.util.Collections;
import static com.njcn.msgpush.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertServiceException;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.*;
import static com.njcn.msgpush.module.system.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mockStatic;
/**
* {@link OAuth2ClientServiceImpl} 的单元测试类
*
* @author hongawen
*/
@Import(OAuth2ClientServiceImpl.class)
public class OAuth2ClientServiceImplTest extends BaseDbUnitTest {
@Resource
private OAuth2ClientServiceImpl oauth2ClientService;
@Resource
private OAuth2ClientMapper oauth2ClientMapper;
@Test
public void testCreateOAuth2Client_success() {
// 准备参数
OAuth2ClientSaveReqVO reqVO = randomPojo(OAuth2ClientSaveReqVO.class,
o -> o.setLogo(randomString()))
.setId(null); // 防止 id 被赋值
// 调用
Long oauth2ClientId = oauth2ClientService.createOAuth2Client(reqVO);
// 断言
assertNotNull(oauth2ClientId);
// 校验记录的属性是否正确
OAuth2ClientDO oAuth2Client = oauth2ClientMapper.selectById(oauth2ClientId);
assertPojoEquals(reqVO, oAuth2Client, "id");
}
@Test
public void testUpdateOAuth2Client_success() {
// mock 数据
OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class);
oauth2ClientMapper.insert(dbOAuth2Client);// @Sql: 先插入出一条存在的数据
// 准备参数
OAuth2ClientSaveReqVO reqVO = randomPojo(OAuth2ClientSaveReqVO.class, o -> {
o.setId(dbOAuth2Client.getId()); // 设置更新的 ID
o.setLogo(randomString());
});
// 调用
oauth2ClientService.updateOAuth2Client(reqVO);
// 校验是否更新正确
OAuth2ClientDO oAuth2Client = oauth2ClientMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, oAuth2Client);
}
@Test
public void testUpdateOAuth2Client_notExists() {
// 准备参数
OAuth2ClientSaveReqVO reqVO = randomPojo(OAuth2ClientSaveReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> oauth2ClientService.updateOAuth2Client(reqVO), OAUTH2_CLIENT_NOT_EXISTS);
}
@Test
public void testDeleteOAuth2Client_success() {
// mock 数据
OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class);
oauth2ClientMapper.insert(dbOAuth2Client);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbOAuth2Client.getId();
// 调用
oauth2ClientService.deleteOAuth2Client(id);
// 校验数据不存在了
assertNull(oauth2ClientMapper.selectById(id));
}
@Test
public void testDeleteOAuth2Client_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> oauth2ClientService.deleteOAuth2Client(id), OAUTH2_CLIENT_NOT_EXISTS);
}
@Test
public void testValidateClientIdExists_withId() {
// mock 数据
OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("tudou");
oauth2ClientMapper.insert(client);
// 准备参数
Long id = randomLongId();
String clientId = "tudou";
// 调用,不会报错
assertServiceException(() -> oauth2ClientService.validateClientIdExists(id, clientId), OAUTH2_CLIENT_EXISTS);
}
@Test
public void testValidateClientIdExists_noId() {
// mock 数据
OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("tudou");
oauth2ClientMapper.insert(client);
// 准备参数
String clientId = "tudou";
// 调用,不会报错
assertServiceException(() -> oauth2ClientService.validateClientIdExists(null, clientId), OAUTH2_CLIENT_EXISTS);
}
@Test
public void testGetOAuth2Client() {
// mock 数据
OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class);
oauth2ClientMapper.insert(clientDO);
// 准备参数
Long id = clientDO.getId();
// 调用,并断言
OAuth2ClientDO dbClientDO = oauth2ClientService.getOAuth2Client(id);
assertPojoEquals(clientDO, dbClientDO);
}
@Test
public void testGetOAuth2ClientFromCache() {
// mock 数据
OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class);
oauth2ClientMapper.insert(clientDO);
// 准备参数
String clientId = clientDO.getClientId();
// 调用,并断言
OAuth2ClientDO dbClientDO = oauth2ClientService.getOAuth2ClientFromCache(clientId);
assertPojoEquals(clientDO, dbClientDO);
}
@Test
public void testGetOAuth2ClientPage() {
// mock 数据
OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class, o -> { // 等会查询到
o.setName("潜龙");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
oauth2ClientMapper.insert(dbOAuth2Client);
// 测试 name 不匹配
oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setName("凤凰")));
// 测试 status 不匹配
oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 准备参数
OAuth2ClientPageReqVO reqVO = new OAuth2ClientPageReqVO();
reqVO.setName("");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
// 调用
PageResult<OAuth2ClientDO> pageResult = oauth2ClientService.getOAuth2ClientPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbOAuth2Client, pageResult.getList().get(0));
}
@Test
public void testValidOAuthClientFromCache() {
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(OAuth2ClientServiceImpl.class)))
.thenReturn(oauth2ClientService);
// mock 方法
OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("default")
.setStatus(CommonStatusEnum.ENABLE.getStatus());
oauth2ClientMapper.insert(client);
OAuth2ClientDO client02 = randomPojo(OAuth2ClientDO.class).setClientId("disable")
.setStatus(CommonStatusEnum.DISABLE.getStatus());
oauth2ClientMapper.insert(client02);
// 调用,并断言
assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache(randomString(),
null, null, null, null), OAUTH2_CLIENT_NOT_EXISTS);
assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("disable",
null, null, null, null), OAUTH2_CLIENT_DISABLE);
assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default",
randomString(), null, null, null), OAUTH2_CLIENT_CLIENT_SECRET_ERROR);
assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default",
null, randomString(), null, null), OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS);
assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default",
null, null, Collections.singleton(randomString()), null), OAUTH2_CLIENT_SCOPE_OVER);
assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default",
null, null, null, "test"), OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH, "test");
// 成功调用1参数完整
OAuth2ClientDO result = oauth2ClientService.validOAuthClientFromCache(client.getClientId(), client.getSecret(),
client.getAuthorizedGrantTypes().get(0), client.getScopes(), client.getRedirectUris().get(0));
assertPojoEquals(client, result);
// 成功调用2只有 clientId 参数)
result = oauth2ClientService.validOAuthClientFromCache(client.getClientId());
assertPojoEquals(client, result);
}
}
}

View File

@@ -1,101 +0,0 @@
package com.njcn.msgpush.module.system.service.oauth2;
import cn.hutool.core.util.RandomUtil;
import com.njcn.msgpush.framework.common.enums.UserTypeEnum;
import com.njcn.msgpush.framework.common.util.date.DateUtils;
import com.njcn.msgpush.framework.test.core.ut.BaseDbUnitTest;
import com.njcn.msgpush.module.system.dal.dataobject.oauth2.OAuth2CodeDO;
import com.njcn.msgpush.module.system.dal.mysql.oauth2.OAuth2CodeMapper;
import jakarta.annotation.Resource;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import java.time.LocalDateTime;
import java.util.List;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertServiceException;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.*;
import static com.njcn.msgpush.module.system.enums.ErrorCodeConstants.OAUTH2_CODE_EXPIRE;
import static com.njcn.msgpush.module.system.enums.ErrorCodeConstants.OAUTH2_CODE_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link OAuth2CodeServiceImpl} 的单元测试类
*
* @author hongawen
*/
@Import(OAuth2CodeServiceImpl.class)
class OAuth2CodeServiceImplTest extends BaseDbUnitTest {
@Resource
private OAuth2CodeServiceImpl oauth2CodeService;
@Resource
private OAuth2CodeMapper oauth2CodeMapper;
@Test
public void testCreateAuthorizationCode() {
// 准备参数
Long userId = randomLongId();
Integer userType = RandomUtil.randomEle(UserTypeEnum.values()).getValue();
String clientId = randomString();
List<String> scopes = Lists.newArrayList("read", "write");
String redirectUri = randomString();
String state = randomString();
// 调用
OAuth2CodeDO codeDO = oauth2CodeService.createAuthorizationCode(userId, userType, clientId,
scopes, redirectUri, state);
// 断言
OAuth2CodeDO dbCodeDO = oauth2CodeMapper.selectByCode(codeDO.getCode());
// TODO @芋艿expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。
assertPojoEquals(codeDO, dbCodeDO, "expiresTime", "createTime", "updateTime", "deleted");
assertEquals(userId, codeDO.getUserId());
assertEquals(userType, codeDO.getUserType());
assertEquals(clientId, codeDO.getClientId());
assertEquals(scopes, codeDO.getScopes());
assertEquals(redirectUri, codeDO.getRedirectUri());
assertEquals(state, codeDO.getState());
assertFalse(DateUtils.isExpired(codeDO.getExpiresTime()));
}
@Test
public void testConsumeAuthorizationCode_null() {
// 调用,并断言
assertServiceException(() -> oauth2CodeService.consumeAuthorizationCode(randomString()),
OAUTH2_CODE_NOT_EXISTS);
}
@Test
public void testConsumeAuthorizationCode_expired() {
// 准备参数
String code = "test_code";
// mock 数据
OAuth2CodeDO codeDO = randomPojo(OAuth2CodeDO.class).setCode(code)
.setExpiresTime(LocalDateTime.now().minusDays(1));
oauth2CodeMapper.insert(codeDO);
// 调用,并断言
assertServiceException(() -> oauth2CodeService.consumeAuthorizationCode(code),
OAUTH2_CODE_EXPIRE);
}
@Test
public void testConsumeAuthorizationCode_success() {
// 准备参数
String code = "test_code";
// mock 数据
OAuth2CodeDO codeDO = randomPojo(OAuth2CodeDO.class).setCode(code)
.setExpiresTime(LocalDateTime.now().plusDays(1));
oauth2CodeMapper.insert(codeDO);
// 调用
OAuth2CodeDO result = oauth2CodeService.consumeAuthorizationCode(code);
// TODO @芋艿expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。
assertPojoEquals(codeDO, result, "expiresTime");
assertNull(oauth2CodeMapper.selectByCode(code));
}
}

View File

@@ -1,166 +0,0 @@
package com.njcn.msgpush.module.system.service.oauth2;
import com.njcn.msgpush.framework.common.enums.UserTypeEnum;
import com.njcn.msgpush.framework.test.core.ut.BaseMockitoUnitTest;
import com.njcn.msgpush.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import com.njcn.msgpush.module.system.dal.dataobject.oauth2.OAuth2CodeDO;
import com.njcn.msgpush.module.system.dal.dataobject.user.AdminUserDO;
import com.njcn.msgpush.module.system.service.auth.AdminAuthService;
import com.google.common.collect.Lists;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.List;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.*;
import static java.util.Collections.emptyList;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
/**
* {@link OAuth2GrantServiceImpl} 的单元测试
*
* @author hongawen
*/
public class OAuth2GrantServiceImplTest extends BaseMockitoUnitTest {
@InjectMocks
private OAuth2GrantServiceImpl oauth2GrantService;
@Mock
private OAuth2TokenService oauth2TokenService;
@Mock
private OAuth2CodeService oauth2CodeService;
@Mock
private AdminAuthService adminAuthService;
@Test
public void testGrantImplicit() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String clientId = randomString();
List<String> scopes = Lists.newArrayList("read", "write");
// mock 方法
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class);
when(oauth2TokenService.createAccessToken(eq(userId), eq(userType),
eq(clientId), eq(scopes))).thenReturn(accessTokenDO);
// 调用,并断言
assertPojoEquals(accessTokenDO, oauth2GrantService.grantImplicit(
userId, userType, clientId, scopes));
}
@Test
public void testGrantAuthorizationCodeForCode() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String clientId = randomString();
List<String> scopes = Lists.newArrayList("read", "write");
String redirectUri = randomString();
String state = randomString();
// mock 方法
OAuth2CodeDO codeDO = randomPojo(OAuth2CodeDO.class);
when(oauth2CodeService.createAuthorizationCode(eq(userId), eq(userType),
eq(clientId), eq(scopes), eq(redirectUri), eq(state))).thenReturn(codeDO);
// 调用,并断言
assertEquals(codeDO.getCode(), oauth2GrantService.grantAuthorizationCodeForCode(userId, userType,
clientId, scopes, redirectUri, state));
}
@Test
public void testGrantAuthorizationCodeForAccessToken() {
// 准备参数
String clientId = randomString();
String code = randomString();
List<String> scopes = Lists.newArrayList("read", "write");
String redirectUri = randomString();
String state = randomString();
// mock 方法code
OAuth2CodeDO codeDO = randomPojo(OAuth2CodeDO.class, o -> {
o.setClientId(clientId);
o.setRedirectUri(redirectUri);
o.setState(state);
o.setScopes(scopes);
});
when(oauth2CodeService.consumeAuthorizationCode(eq(code))).thenReturn(codeDO);
// mock 方法(创建令牌)
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class);
when(oauth2TokenService.createAccessToken(eq(codeDO.getUserId()), eq(codeDO.getUserType()),
eq(codeDO.getClientId()), eq(codeDO.getScopes()))).thenReturn(accessTokenDO);
// 调用,并断言
assertPojoEquals(accessTokenDO, oauth2GrantService.grantAuthorizationCodeForAccessToken(
clientId, code, redirectUri, state));
}
@Test
public void testGrantPassword() {
// 准备参数
String username = randomString();
String password = randomString();
String clientId = randomString();
List<String> scopes = Lists.newArrayList("read", "write");
// mock 方法(认证)
AdminUserDO user = randomPojo(AdminUserDO.class);
when(adminAuthService.authenticate(eq(username), eq(password))).thenReturn(user);
// mock 方法(访问令牌)
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class);
when(oauth2TokenService.createAccessToken(eq(user.getId()), eq(UserTypeEnum.ADMIN.getValue()),
eq(clientId), eq(scopes))).thenReturn(accessTokenDO);
// 调用,并断言
assertPojoEquals(accessTokenDO, oauth2GrantService.grantPassword(
username, password, clientId, scopes));
}
@Test
public void testGrantRefreshToken() {
// 准备参数
String refreshToken = randomString();
String clientId = randomString();
// mock 方法
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class);
when(oauth2TokenService.refreshAccessToken(eq(refreshToken), eq(clientId)))
.thenReturn(accessTokenDO);
// 调用,并断言
assertPojoEquals(accessTokenDO, oauth2GrantService.grantRefreshToken(
refreshToken, clientId));
}
@Test
public void testRevokeToken_clientIdError() {
// 准备参数
String clientId = randomString();
String accessToken = randomString();
// mock 方法
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class);
when(oauth2TokenService.getAccessToken(eq(accessToken))).thenReturn(accessTokenDO);
// 调用,并断言
assertFalse(oauth2GrantService.revokeToken(clientId, accessToken));
}
@Test
public void testRevokeToken_success() {
// 准备参数
String clientId = randomString();
String accessToken = randomString();
// mock 方法(访问令牌)
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class).setClientId(clientId);
when(oauth2TokenService.getAccessToken(eq(accessToken))).thenReturn(accessTokenDO);
// mock 方法(移除)
when(oauth2TokenService.removeAccessToken(eq(accessToken))).thenReturn(accessTokenDO);
// 调用,并断言
assertTrue(oauth2GrantService.revokeToken(clientId, accessToken));
}
}

View File

@@ -1,326 +0,0 @@
package com.njcn.msgpush.module.system.service.oauth2;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.njcn.msgpush.framework.common.enums.UserTypeEnum;
import com.njcn.msgpush.framework.common.exception.ErrorCode;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.common.util.date.DateUtils;
import com.njcn.msgpush.framework.test.core.ut.BaseDbAndRedisUnitTest;
import com.njcn.msgpush.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO;
import com.njcn.msgpush.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import com.njcn.msgpush.module.system.dal.dataobject.oauth2.OAuth2ClientDO;
import com.njcn.msgpush.module.system.dal.dataobject.oauth2.OAuth2RefreshTokenDO;
import com.njcn.msgpush.module.system.dal.dataobject.user.AdminUserDO;
import com.njcn.msgpush.module.system.dal.mysql.oauth2.OAuth2AccessTokenMapper;
import com.njcn.msgpush.module.system.dal.mysql.oauth2.OAuth2RefreshTokenMapper;
import com.njcn.msgpush.module.system.dal.redis.oauth2.OAuth2AccessTokenRedisDAO;
import com.njcn.msgpush.module.system.service.user.AdminUserService;
import jakarta.annotation.Resource;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.time.LocalDateTime;
import java.util.List;
import static com.njcn.msgpush.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertServiceException;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
/**
* {@link OAuth2TokenServiceImpl} 的单元测试类
*
* @author hongawen
*/
@Import({OAuth2TokenServiceImpl.class, OAuth2AccessTokenRedisDAO.class})
public class OAuth2TokenServiceImplTest extends BaseDbAndRedisUnitTest {
@Resource
private OAuth2TokenServiceImpl oauth2TokenService;
@Resource
private OAuth2AccessTokenMapper oauth2AccessTokenMapper;
@Resource
private OAuth2RefreshTokenMapper oauth2RefreshTokenMapper;
@Resource
private OAuth2AccessTokenRedisDAO oauth2AccessTokenRedisDAO;
@MockitoBean
private OAuth2ClientService oauth2ClientService;
@MockitoBean
private AdminUserService adminUserService;
@Test
public void testCreateAccessToken() {
// 准备参数
Long userId = randomLongId();
Integer userType = UserTypeEnum.ADMIN.getValue();
String clientId = randomString();
List<String> scopes = Lists.newArrayList("read", "write");
// mock 方法
OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class).setClientId(clientId)
.setAccessTokenValiditySeconds(30).setRefreshTokenValiditySeconds(60);
when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(clientDO);
// mock 数据(用户)
AdminUserDO user = randomPojo(AdminUserDO.class);
when(adminUserService.getUser(userId)).thenReturn(user);
// 调用
OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, userType, clientId, scopes);
// 断言访问令牌
OAuth2AccessTokenDO dbAccessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(accessTokenDO.getAccessToken());
// TODO @芋艿expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。
assertPojoEquals(accessTokenDO, dbAccessTokenDO, "expiresTime", "createTime", "updateTime", "deleted");
assertEquals(userId, accessTokenDO.getUserId());
assertEquals(userType, accessTokenDO.getUserType());
assertEquals(2, accessTokenDO.getUserInfo().size());
assertEquals(user.getNickname(), accessTokenDO.getUserInfo().get("nickname"));
assertEquals(user.getDeptId().toString(), accessTokenDO.getUserInfo().get("deptId"));
assertEquals(clientId, accessTokenDO.getClientId());
assertEquals(scopes, accessTokenDO.getScopes());
assertFalse(DateUtils.isExpired(accessTokenDO.getExpiresTime()));
// 断言访问令牌的缓存
OAuth2AccessTokenDO redisAccessTokenDO = oauth2AccessTokenRedisDAO.get(accessTokenDO.getAccessToken());
// TODO @芋艿expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。
assertPojoEquals(accessTokenDO, redisAccessTokenDO, "expiresTime", "createTime", "updateTime", "deleted");
// 断言刷新令牌
OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectList().get(0);
assertPojoEquals(accessTokenDO, refreshTokenDO, "id", "expiresTime", "createTime", "updateTime", "deleted");
assertFalse(DateUtils.isExpired(refreshTokenDO.getExpiresTime()));
}
@Test
public void testRefreshAccessToken_null() {
// 准备参数
String refreshToken = randomString();
String clientId = randomString();
// mock 方法
// 调用,并断言
assertServiceException(() -> oauth2TokenService.refreshAccessToken(refreshToken, clientId),
new ErrorCode(400, "无效的刷新令牌"));
}
@Test
public void testRefreshAccessToken_clientIdError() {
// 准备参数
String refreshToken = randomString();
String clientId = randomString();
// mock 方法
OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class).setClientId(clientId);
when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(clientDO);
// mock 数据(访问令牌)
OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class)
.setRefreshToken(refreshToken).setClientId("error");
oauth2RefreshTokenMapper.insert(refreshTokenDO);
// 调用,并断言
assertServiceException(() -> oauth2TokenService.refreshAccessToken(refreshToken, clientId),
new ErrorCode(400, "刷新令牌的客户端编号不正确"));
}
@Test
public void testRefreshAccessToken_expired() {
// 准备参数
String refreshToken = randomString();
String clientId = randomString();
// mock 方法
OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class).setClientId(clientId);
when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(clientDO);
// mock 数据(访问令牌)
OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class)
.setRefreshToken(refreshToken).setClientId(clientId)
.setExpiresTime(LocalDateTime.now().minusDays(1));
oauth2RefreshTokenMapper.insert(refreshTokenDO);
// 调用,并断言
assertServiceException(() -> oauth2TokenService.refreshAccessToken(refreshToken, clientId),
new ErrorCode(401, "刷新令牌已过期"));
assertEquals(0, oauth2AccessTokenMapper.selectCount());
}
@Test
public void testRefreshAccessToken_success() {
// 准备参数
String refreshToken = randomString();
String clientId = randomString();
// mock 方法
OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class).setClientId(clientId)
.setAccessTokenValiditySeconds(30);
when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(clientDO);
// mock 数据(访问令牌)
OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class, o ->
o.setRefreshToken(refreshToken).setClientId(clientId)
.setExpiresTime(LocalDateTime.now().plusDays(1))
.setUserType(UserTypeEnum.ADMIN.getValue()));
oauth2RefreshTokenMapper.insert(refreshTokenDO);
// mock 数据(访问令牌)
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class).setRefreshToken(refreshToken)
.setUserType(refreshTokenDO.getUserType());
oauth2AccessTokenMapper.insert(accessTokenDO);
oauth2AccessTokenRedisDAO.set(accessTokenDO);
// mock 数据(用户)
AdminUserDO user = randomPojo(AdminUserDO.class);
when(adminUserService.getUser(refreshTokenDO.getUserId())).thenReturn(user);
// 调用
OAuth2AccessTokenDO newAccessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, clientId);
// 断言,老的访问令牌被删除
assertNull(oauth2AccessTokenMapper.selectByAccessToken(accessTokenDO.getAccessToken()));
assertNull(oauth2AccessTokenRedisDAO.get(accessTokenDO.getAccessToken()));
// 断言,新的访问令牌
OAuth2AccessTokenDO dbAccessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(newAccessTokenDO.getAccessToken());
// TODO @芋艿expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。
assertPojoEquals(newAccessTokenDO, dbAccessTokenDO, "expiresTime", "createTime", "updateTime", "deleted");
assertPojoEquals(newAccessTokenDO, refreshTokenDO, "id", "expiresTime", "createTime", "updateTime", "deleted",
"creator", "updater");
assertFalse(DateUtils.isExpired(newAccessTokenDO.getExpiresTime()));
// 断言,新的访问令牌的缓存
OAuth2AccessTokenDO redisAccessTokenDO = oauth2AccessTokenRedisDAO.get(newAccessTokenDO.getAccessToken());
// TODO @芋艿expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。
assertPojoEquals(newAccessTokenDO, redisAccessTokenDO, "expiresTime", "createTime", "updateTime", "deleted");
}
@Test
public void testGetAccessToken() {
// mock 数据(访问令牌)
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class)
.setExpiresTime(LocalDateTime.now().plusDays(1));
oauth2AccessTokenMapper.insert(accessTokenDO);
// 准备参数
String accessToken = accessTokenDO.getAccessToken();
// 调用
OAuth2AccessTokenDO result = oauth2TokenService.getAccessToken(accessToken);
// 断言
// TODO @芋艿expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。
assertPojoEquals(accessTokenDO, result, "expiresTime", "createTime", "updateTime", "deleted",
"creator", "updater");
// TODO @芋艿expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。
assertPojoEquals(accessTokenDO, oauth2AccessTokenRedisDAO.get(accessToken), "expiresTime", "createTime", "updateTime", "deleted",
"creator", "updater");
}
@Test
public void testCheckAccessToken_null() {
// 调研,并断言
assertServiceException(() -> oauth2TokenService.checkAccessToken(randomString()),
new ErrorCode(401, "访问令牌不存在"));
}
@Test
public void testCheckAccessToken_expired() {
// mock 数据(访问令牌)
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class)
.setExpiresTime(LocalDateTime.now().minusDays(1));
oauth2AccessTokenMapper.insert(accessTokenDO);
// 准备参数
String accessToken = accessTokenDO.getAccessToken();
// 调研,并断言
assertServiceException(() -> oauth2TokenService.checkAccessToken(accessToken),
new ErrorCode(401, "访问令牌已过期"));
}
@Test
public void testCheckAccessToken_refreshToken() {
// mock 数据(访问令牌)
OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class)
.setUserId(0L)
.setExpiresTime(LocalDateTime.now().plusDays(1));
oauth2RefreshTokenMapper.insert(refreshTokenDO);
// 准备参数
String accessToken = refreshTokenDO.getRefreshToken();
// 调研,并断言
OAuth2AccessTokenDO result = oauth2TokenService.getAccessToken(accessToken);
// 断言
assertPojoEquals(refreshTokenDO, result, "expiresTime", "createTime", "updateTime", "deleted",
"creator", "updater");
}
@Test
public void testCheckAccessToken_success() {
// mock 数据(访问令牌)
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class)
.setExpiresTime(LocalDateTime.now().plusDays(1));
oauth2AccessTokenMapper.insert(accessTokenDO);
// 准备参数
String accessToken = accessTokenDO.getAccessToken();
// 调研,并断言
OAuth2AccessTokenDO result = oauth2TokenService.getAccessToken(accessToken);
// 断言
// TODO @芋艿expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。
assertPojoEquals(accessTokenDO, result, "expiresTime", "createTime", "updateTime", "deleted",
"creator", "updater");
}
@Test
public void testRemoveAccessToken_null() {
// 调用,并断言
assertNull(oauth2TokenService.removeAccessToken(randomString()));
}
@Test
public void testRemoveAccessToken_success() {
// mock 数据(访问令牌)
OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class)
.setExpiresTime(LocalDateTime.now().plusDays(1));
oauth2AccessTokenMapper.insert(accessTokenDO);
// mock 数据(刷新令牌)
OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class)
.setRefreshToken(accessTokenDO.getRefreshToken());
oauth2RefreshTokenMapper.insert(refreshTokenDO);
// 调用
OAuth2AccessTokenDO result = oauth2TokenService.removeAccessToken(accessTokenDO.getAccessToken());
// TODO @芋艿expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。
assertPojoEquals(accessTokenDO, result, "expiresTime", "createTime", "updateTime", "deleted",
"creator", "updater");
// 断言数据
assertNull(oauth2AccessTokenMapper.selectByAccessToken(accessTokenDO.getAccessToken()));
assertNull(oauth2RefreshTokenMapper.selectByRefreshToken(accessTokenDO.getRefreshToken()));
assertNull(oauth2AccessTokenRedisDAO.get(accessTokenDO.getAccessToken()));
}
@Test
public void testGetAccessTokenPage() {
// mock 数据
OAuth2AccessTokenDO dbAccessToken = randomPojo(OAuth2AccessTokenDO.class, o -> { // 等会查询到
o.setUserId(10L);
o.setUserType(1);
o.setClientId("test_client");
o.setExpiresTime(LocalDateTime.now().plusDays(1));
});
oauth2AccessTokenMapper.insert(dbAccessToken);
// 测试 userId 不匹配
oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setUserId(20L)));
// 测试 userType 不匹配
oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setUserType(2)));
// 测试 userType 不匹配
oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setClientId("it_client")));
// 测试 expireTime 不匹配
oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setExpiresTime(LocalDateTimeUtil.now())));
// 准备参数
OAuth2AccessTokenPageReqVO reqVO = new OAuth2AccessTokenPageReqVO();
reqVO.setUserId(10L);
reqVO.setUserType(1);
reqVO.setClientId("test");
// 调用
PageResult<OAuth2AccessTokenDO> pageResult = oauth2TokenService.getAccessTokenPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
// TODO @芋艿expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。
assertPojoEquals(dbAccessToken, pageResult.getList().get(0), "expiresTime");
}
}

View File

@@ -1,304 +0,0 @@
package com.njcn.msgpush.module.system.service.permission;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.test.core.ut.BaseDbUnitTest;
import com.njcn.msgpush.module.system.controller.admin.permission.vo.menu.MenuListReqVO;
import com.njcn.msgpush.module.system.controller.admin.permission.vo.menu.MenuSaveVO;
import com.njcn.msgpush.module.system.dal.dataobject.permission.MenuDO;
import com.njcn.msgpush.module.system.dal.mysql.permission.MenuMapper;
import com.njcn.msgpush.module.system.enums.permission.MenuTypeEnum;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import static com.njcn.msgpush.framework.common.util.collection.SetUtils.asSet;
import static com.njcn.msgpush.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertServiceException;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.*;
import static com.njcn.msgpush.module.system.dal.dataobject.permission.MenuDO.ID_ROOT;
import static com.njcn.msgpush.module.system.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
@Import(MenuServiceImpl.class)
public class MenuServiceImplTest extends BaseDbUnitTest {
@Resource
private MenuServiceImpl menuService;
@Resource
private MenuMapper menuMapper;
@MockitoBean
private PermissionService permissionService;
@Test
public void testCreateMenu_success() {
// mock 数据(构造父菜单)
MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU,
"parent", 0L);
menuMapper.insert(menuDO);
Long parentId = menuDO.getId();
// 准备参数
MenuSaveVO reqVO = randomPojo(MenuSaveVO.class, o -> {
o.setParentId(parentId);
o.setName("testSonName");
o.setType(MenuTypeEnum.MENU.getType());
}).setId(null); // 防止 id 被赋值
Long menuId = menuService.createMenu(reqVO);
// 校验记录的属性是否正确
MenuDO dbMenu = menuMapper.selectById(menuId);
assertPojoEquals(reqVO, dbMenu, "id");
}
@Test
public void testUpdateMenu_success() {
// mock 数据(构造父子菜单)
MenuDO sonMenuDO = createParentAndSonMenu();
Long sonId = sonMenuDO.getId();
// 准备参数
MenuSaveVO reqVO = randomPojo(MenuSaveVO.class, o -> {
o.setId(sonId);
o.setName("testSonName"); // 修改名字
o.setParentId(sonMenuDO.getParentId());
o.setType(MenuTypeEnum.MENU.getType());
});
// 调用
menuService.updateMenu(reqVO);
// 校验记录的属性是否正确
MenuDO dbMenu = menuMapper.selectById(sonId);
assertPojoEquals(reqVO, dbMenu);
}
@Test
public void testUpdateMenu_sonIdNotExist() {
// 准备参数
MenuSaveVO reqVO = randomPojo(MenuSaveVO.class);
// 调用,并断言异常
assertServiceException(() -> menuService.updateMenu(reqVO), MENU_NOT_EXISTS);
}
@Test
public void testDeleteMenu_success() {
// mock 数据
MenuDO menuDO = randomPojo(MenuDO.class);
menuMapper.insert(menuDO);
// 准备参数
Long id = menuDO.getId();
// 调用
menuService.deleteMenu(id);
// 断言
MenuDO dbMenuDO = menuMapper.selectById(id);
assertNull(dbMenuDO);
verify(permissionService).processMenuDeleted(id);
}
@Test
public void testDeleteMenu_menuNotExist() {
assertServiceException(() -> menuService.deleteMenu(randomLongId()),
MENU_NOT_EXISTS);
}
@Test
public void testDeleteMenu_existChildren() {
// mock 数据(构造父子菜单)
MenuDO sonMenu = createParentAndSonMenu();
// 准备参数
Long parentId = sonMenu.getParentId();
// 调用并断言异常
assertServiceException(() -> menuService.deleteMenu(parentId), MENU_EXISTS_CHILDREN);
}
@Test
public void testGetMenuList_all() {
// mock 数据
MenuDO menu100 = randomPojo(MenuDO.class);
menuMapper.insert(menu100);
MenuDO menu101 = randomPojo(MenuDO.class);
menuMapper.insert(menu101);
// 准备参数
// 调用
List<MenuDO> list = menuService.getMenuList();
// 断言
assertEquals(2, list.size());
assertPojoEquals(menu100, list.get(0));
assertPojoEquals(menu101, list.get(1));
}
@Test
public void testGetMenuList() {
// mock 数据
MenuDO menuDO = randomPojo(MenuDO.class, o -> o.setName("芋艿").setStatus(CommonStatusEnum.ENABLE.getStatus()));
menuMapper.insert(menuDO);
// 测试 status 不匹配
menuMapper.insert(cloneIgnoreId(menuDO, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 name 不匹配
menuMapper.insert(cloneIgnoreId(menuDO, o -> o.setName("")));
// 准备参数
MenuListReqVO reqVO = new MenuListReqVO().setName("").setStatus(CommonStatusEnum.ENABLE.getStatus());
// 调用
List<MenuDO> result = menuService.getMenuList(reqVO);
// 断言
assertEquals(1, result.size());
assertPojoEquals(menuDO, result.get(0));
}
@Test
public void testGetMenuIdListByPermissionFromCache() {
// mock 数据
MenuDO menu100 = randomPojo(MenuDO.class);
menuMapper.insert(menu100);
MenuDO menu101 = randomPojo(MenuDO.class);
menuMapper.insert(menu101);
// 准备参数
String permission = menu100.getPermission();
// 调用
List<Long> ids = menuService.getMenuIdListByPermissionFromCache(permission);
// 断言
assertEquals(1, ids.size());
assertEquals(menu100.getId(), ids.get(0));
}
@Test
public void testGetMenuList_ids() {
// mock 数据
MenuDO menu100 = randomPojo(MenuDO.class);
menuMapper.insert(menu100);
MenuDO menu101 = randomPojo(MenuDO.class);
menuMapper.insert(menu101);
// 准备参数
Collection<Long> ids = Collections.singleton(menu100.getId());
// 调用
List<MenuDO> list = menuService.getMenuList(ids);
// 断言
assertEquals(1, list.size());
assertPojoEquals(menu100, list.get(0));
}
@Test
public void testGetMenu() {
// mock 数据
MenuDO menu = randomPojo(MenuDO.class);
menuMapper.insert(menu);
// 准备参数
Long id = menu.getId();
// 调用
MenuDO dbMenu = menuService.getMenu(id);
// 断言
assertPojoEquals(menu, dbMenu);
}
@Test
public void testValidateParentMenu_success() {
// mock 数据
MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", 0L);
menuMapper.insert(menuDO);
// 准备参数
Long parentId = menuDO.getId();
// 调用,无需断言
menuService.validateParentMenu(parentId, null);
}
@Test
public void testValidateParentMenu_canNotSetSelfToBeParent() {
// 调用,并断言异常
assertServiceException(() -> menuService.validateParentMenu(1L, 1L),
MENU_PARENT_ERROR);
}
@Test
public void testValidateParentMenu_parentNotExist() {
// 调用,并断言异常
assertServiceException(() -> menuService.validateParentMenu(randomLongId(), null),
MENU_PARENT_NOT_EXISTS);
}
@Test
public void testValidateParentMenu_parentTypeError() {
// mock 数据
MenuDO menuDO = buildMenuDO(MenuTypeEnum.BUTTON, "parent", 0L);
menuMapper.insert(menuDO);
// 准备参数
Long parentId = menuDO.getId();
// 调用,并断言异常
assertServiceException(() -> menuService.validateParentMenu(parentId, null),
MENU_PARENT_NOT_DIR_OR_MENU);
}
@Test
public void testValidateMenu_Name_success() {
// mock 父子菜单
MenuDO sonMenu = createParentAndSonMenu();
// 准备参数
Long parentId = sonMenu.getParentId();
Long otherSonMenuId = randomLongId();
String otherSonMenuName = randomString();
// 调用,无需断言
menuService.validateMenuName(parentId, otherSonMenuName, otherSonMenuId);
}
@Test
public void testValidateMenu_sonMenuNameNameDuplicate() {
// mock 父子菜单
MenuDO sonMenu = createParentAndSonMenu();
// 准备参数
Long parentId = sonMenu.getParentId();
Long otherSonMenuId = randomLongId();
String otherSonMenuName = sonMenu.getName(); //相同名称
// 调用,并断言异常
assertServiceException(() -> menuService.validateMenuName(parentId, otherSonMenuName, otherSonMenuId),
MENU_NAME_DUPLICATE);
}
// ====================== 初始化方法 ======================
/**
* 插入父子菜单,返回子菜单
*
* @return 子菜单
*/
private MenuDO createParentAndSonMenu() {
// 构造父子菜单
MenuDO parentMenuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", ID_ROOT);
menuMapper.insert(parentMenuDO);
// 构建子菜单
MenuDO sonMenuDO = buildMenuDO(MenuTypeEnum.MENU, "testSonName",
parentMenuDO.getParentId());
menuMapper.insert(sonMenuDO);
return sonMenuDO;
}
private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId) {
return buildMenuDO(type, name, parentId, randomCommonStatus());
}
private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId, Integer status) {
return randomPojo(MenuDO.class, o -> o.setId(null).setName(name).setParentId(parentId)
.setType(type.getType()).setStatus(status));
}
}

View File

@@ -1,527 +0,0 @@
package com.njcn.msgpush.module.system.service.permission;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.njcn.msgpush.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.test.core.ut.BaseDbUnitTest;
import com.njcn.msgpush.module.system.dal.dataobject.dept.DeptDO;
import com.njcn.msgpush.module.system.dal.dataobject.permission.MenuDO;
import com.njcn.msgpush.module.system.dal.dataobject.permission.RoleDO;
import com.njcn.msgpush.module.system.dal.dataobject.permission.RoleMenuDO;
import com.njcn.msgpush.module.system.dal.dataobject.permission.UserRoleDO;
import com.njcn.msgpush.module.system.dal.dataobject.user.AdminUserDO;
import com.njcn.msgpush.module.system.dal.mysql.permission.RoleMenuMapper;
import com.njcn.msgpush.module.system.dal.mysql.permission.UserRoleMapper;
import com.njcn.msgpush.module.system.enums.permission.DataScopeEnum;
import com.njcn.msgpush.module.system.service.dept.DeptService;
import com.njcn.msgpush.module.system.service.user.AdminUserService;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import static cn.hutool.core.collection.ListUtil.toList;
import static com.njcn.msgpush.framework.common.util.collection.SetUtils.asSet;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.randomLongId;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.randomPojo;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
@Import({PermissionServiceImpl.class})
public class PermissionServiceTest extends BaseDbUnitTest {
@Resource
private PermissionServiceImpl permissionService;
@Resource
private RoleMenuMapper roleMenuMapper;
@Resource
private UserRoleMapper userRoleMapper;
@MockitoBean
private RoleService roleService;
@MockitoBean
private MenuService menuService;
@MockitoBean
private DeptService deptService;
@MockitoBean
private AdminUserService userService;
@Test
public void testHasAnyPermissions_superAdmin() {
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class)))
.thenReturn(permissionService);
// 准备参数
Long userId = 1L;
String[] roles = new String[]{"system:user:query", "system:user:create"};
// mock 用户登录的角色
userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L));
RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L)
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role));
// mock 其它方法
when(roleService.hasAnySuperAdmin(eq(asSet(100L)))).thenReturn(true);
// 调用,并断言
assertTrue(permissionService.hasAnyPermissions(userId, roles));
}
}
@Test
public void testHasAnyPermissions_normal() {
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class)))
.thenReturn(permissionService);
// 准备参数
Long userId = 1L;
String[] roles = new String[]{"system:user:query", "system:user:create"};
// mock 用户登录的角色
userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L));
RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L)
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role));
// mock 菜单
Long menuId = 1000L;
when(menuService.getMenuIdListByPermissionFromCache(
eq("system:user:create"))).thenReturn(singletonList(menuId));
roleMenuMapper.insert(randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1000L));
// 调用,并断言
assertTrue(permissionService.hasAnyPermissions(userId, roles));
}
}
@Test
public void testHasAnyRoles() {
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class)))
.thenReturn(permissionService);
// 准备参数
Long userId = 1L;
String[] roles = new String[]{"yunai", "tudou"};
// mock 用户与角色的缓存
userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L));
RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L).setCode("tudou")
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role));
// 调用,并断言
assertTrue(permissionService.hasAnyRoles(userId, roles));
}
}
// ========== 角色-菜单的相关方法 ==========
@Test
public void testAssignRoleMenu() {
// 准备参数
Long roleId = 1L;
Set<Long> menuIds = asSet(200L, 300L);
// mock 数据
RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(1L).setMenuId(100L);
roleMenuMapper.insert(roleMenu01);
RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(1L).setMenuId(200L);
roleMenuMapper.insert(roleMenu02);
// 调用
permissionService.assignRoleMenu(roleId, menuIds);
// 断言
List<RoleMenuDO> roleMenuList = roleMenuMapper.selectList();
assertEquals(2, roleMenuList.size());
assertEquals(1L, roleMenuList.get(0).getRoleId());
assertEquals(200L, roleMenuList.get(0).getMenuId());
assertEquals(1L, roleMenuList.get(1).getRoleId());
assertEquals(300L, roleMenuList.get(1).getMenuId());
}
@Test
public void testProcessRoleDeleted() {
// 准备参数
Long roleId = randomLongId();
// mock 数据 UserRole
UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setRoleId(roleId)); // 被删除
userRoleMapper.insert(userRoleDO01);
UserRoleDO userRoleDO02 = randomPojo(UserRoleDO.class); // 不被删除
userRoleMapper.insert(userRoleDO02);
// mock 数据 RoleMenu
RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setRoleId(roleId)); // 被删除
roleMenuMapper.insert(roleMenuDO01);
RoleMenuDO roleMenuDO02 = randomPojo(RoleMenuDO.class); // 不被删除
roleMenuMapper.insert(roleMenuDO02);
// 调用
permissionService.processRoleDeleted(roleId);
// 断言数据 RoleMenuDO
List<RoleMenuDO> dbRoleMenus = roleMenuMapper.selectList();
assertEquals(1, dbRoleMenus.size());
assertPojoEquals(dbRoleMenus.get(0), roleMenuDO02);
// 断言数据 UserRoleDO
List<UserRoleDO> dbUserRoles = userRoleMapper.selectList();
assertEquals(1, dbUserRoles.size());
assertPojoEquals(dbUserRoles.get(0), userRoleDO02);
}
@Test
public void testProcessMenuDeleted() {
// 准备参数
Long menuId = randomLongId();
// mock 数据
RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setMenuId(menuId)); // 被删除
roleMenuMapper.insert(roleMenuDO01);
RoleMenuDO roleMenuDO02 = randomPojo(RoleMenuDO.class); // 不被删除
roleMenuMapper.insert(roleMenuDO02);
// 调用
permissionService.processMenuDeleted(menuId);
// 断言数据
List<RoleMenuDO> dbRoleMenus = roleMenuMapper.selectList();
assertEquals(1, dbRoleMenus.size());
assertPojoEquals(dbRoleMenus.get(0), roleMenuDO02);
}
@Test
public void testGetRoleMenuIds_superAdmin() {
// 准备参数
Long roleId = 100L;
// mock 方法
when(roleService.hasAnySuperAdmin(eq(singleton(100L)))).thenReturn(true);
List<MenuDO> menuList = singletonList(randomPojo(MenuDO.class).setId(1L));
when(menuService.getMenuList()).thenReturn(menuList);
// 调用
Set<Long> menuIds = permissionService.getRoleMenuListByRoleId(roleId);
// 断言
assertEquals(singleton(1L), menuIds);
}
@Test
public void testGetRoleMenuIds_normal() {
// 准备参数
Long roleId = 100L;
// mock 数据
RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1L);
roleMenuMapper.insert(roleMenu01);
RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(2L);
roleMenuMapper.insert(roleMenu02);
// 调用
Set<Long> menuIds = permissionService.getRoleMenuListByRoleId(roleId);
// 断言
assertEquals(asSet(1L, 2L), menuIds);
}
@Test
public void testGetMenuRoleIdListByMenuIdFromCache() {
// 准备参数
Long menuId = 1L;
// mock 数据
RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1L);
roleMenuMapper.insert(roleMenu01);
RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(200L).setMenuId(1L);
roleMenuMapper.insert(roleMenu02);
// 调用
Set<Long> roleIds = permissionService.getMenuRoleIdListByMenuIdFromCache(menuId);
// 断言
assertEquals(asSet(100L, 200L), roleIds);
}
// ========== 用户-角色的相关方法 ==========
@Test
public void testAssignUserRole() {
// 准备参数
Long userId = 1L;
Set<Long> roleIds = asSet(200L, 300L);
// mock 数据
UserRoleDO userRole01 = randomPojo(UserRoleDO.class).setUserId(1L).setRoleId(100L);
userRoleMapper.insert(userRole01);
UserRoleDO userRole02 = randomPojo(UserRoleDO.class).setUserId(1L).setRoleId(200L);
userRoleMapper.insert(userRole02);
// 调用
permissionService.assignUserRole(userId, roleIds);
// 断言
List<UserRoleDO> userRoleDOList = userRoleMapper.selectList();
assertEquals(2, userRoleDOList.size());
assertEquals(1L, userRoleDOList.get(0).getUserId());
assertEquals(200L, userRoleDOList.get(0).getRoleId());
assertEquals(1L, userRoleDOList.get(1).getUserId());
assertEquals(300L, userRoleDOList.get(1).getRoleId());
}
@Test
public void testProcessUserDeleted() {
// 准备参数
Long userId = randomLongId();
// mock 数据
UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(userId)); // 被删除
userRoleMapper.insert(userRoleDO01);
UserRoleDO userRoleDO02 = randomPojo(UserRoleDO.class); // 不被删除
userRoleMapper.insert(userRoleDO02);
// 调用
permissionService.processUserDeleted(userId);
// 断言数据
List<UserRoleDO> dbUserRoles = userRoleMapper.selectList();
assertEquals(1, dbUserRoles.size());
assertPojoEquals(dbUserRoles.get(0), userRoleDO02);
}
@Test
public void testGetUserRoleIdListByUserId() {
// 准备参数
Long userId = 1L;
// mock 数据
UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L));
userRoleMapper.insert(userRoleDO01);
UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L));
userRoleMapper.insert(roleMenuDO02);
// 调用
Set<Long> result = permissionService.getUserRoleIdListByUserId(userId);
// 断言
assertEquals(asSet(10L, 20L), result);
}
@Test
public void testGetUserRoleIdListByUserIdFromCache() {
// 准备参数
Long userId = 1L;
// mock 数据
UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L));
userRoleMapper.insert(userRoleDO01);
UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L));
userRoleMapper.insert(roleMenuDO02);
// 调用
Set<Long> result = permissionService.getUserRoleIdListByUserIdFromCache(userId);
// 断言
assertEquals(asSet(10L, 20L), result);
}
@Test
public void testGetUserRoleIdsFromCache() {
// 准备参数
Long userId = 1L;
// mock 数据
UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L));
userRoleMapper.insert(userRoleDO01);
UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L));
userRoleMapper.insert(roleMenuDO02);
// 调用
Set<Long> result = permissionService.getUserRoleIdListByUserIdFromCache(userId);
// 断言
assertEquals(asSet(10L, 20L), result);
}
@Test
public void testGetUserRoleIdListByRoleId() {
// 准备参数
Collection<Long> roleIds = asSet(10L, 20L);
// mock 数据
UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L));
userRoleMapper.insert(userRoleDO01);
UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(2L).setRoleId(20L));
userRoleMapper.insert(roleMenuDO02);
// 调用
Set<Long> result = permissionService.getUserRoleIdListByRoleId(roleIds);
// 断言
assertEquals(asSet(1L, 2L), result);
}
@Test
public void testGetEnableUserRoleListByUserIdFromCache() {
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class)))
.thenReturn(permissionService);
// 准备参数
Long userId = 1L;
// mock 用户登录的角色
userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L));
userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(200L));
RoleDO role01 = randomPojo(RoleDO.class, o -> o.setId(100L)
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
RoleDO role02 = randomPojo(RoleDO.class, o -> o.setId(200L)
.setStatus(CommonStatusEnum.DISABLE.getStatus()));
when(roleService.getRoleListFromCache(eq(asSet(100L, 200L))))
.thenReturn(toList(role01, role02));
// 调用
List<RoleDO> result = permissionService.getEnableUserRoleListByUserIdFromCache(userId);
// 断言
assertEquals(1, result.size());
assertPojoEquals(role01, result.get(0));
}
}
// ========== 用户-部门的相关方法 ==========
@Test
public void testAssignRoleDataScope() {
// 准备参数
Long roleId = 1L;
Integer dataScope = 2;
Set<Long> dataScopeDeptIds = asSet(10L, 20L);
// 调用
permissionService.assignRoleDataScope(roleId, dataScope, dataScopeDeptIds);
// 断言
verify(roleService).updateRoleDataScope(eq(roleId), eq(dataScope), eq(dataScopeDeptIds));
}
@Test
public void testGetDeptDataPermission_All() {
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class)))
.thenReturn(permissionService);
// 准备参数
Long userId = 1L;
// mock 用户的角色编号
userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L));
// mock 获得用户的角色
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.ALL.getScope())
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO));
// 调用
DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);
// 断言
assertTrue(result.getAll());
assertFalse(result.getSelf());
assertTrue(CollUtil.isEmpty(result.getDeptIds()));
}
}
@Test
public void testGetDeptDataPermission_DeptCustom() {
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class)))
.thenReturn(permissionService);
// 准备参数
Long userId = 1L;
// mock 用户的角色编号
userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L));
// mock 获得用户的角色
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_CUSTOM.getScope())
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO));
// mock 部门的返回
when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L),
null, null); // 最后返回 null 的目的,看看会不会重复调用
// 调用
DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);
// 断言
assertFalse(result.getAll());
assertFalse(result.getSelf());
assertEquals(roleDO.getDataScopeDeptIds().size() + 1, result.getDeptIds().size());
assertTrue(CollUtil.containsAll(result.getDeptIds(), roleDO.getDataScopeDeptIds()));
assertTrue(CollUtil.contains(result.getDeptIds(), 3L));
}
}
@Test
public void testGetDeptDataPermission_DeptOnly() {
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class)))
.thenReturn(permissionService);
// 准备参数
Long userId = 1L;
// mock 用户的角色编号
userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L));
// mock 获得用户的角色
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_ONLY.getScope())
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO));
// mock 部门的返回
when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L),
null, null); // 最后返回 null 的目的,看看会不会重复调用
// 调用
DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);
// 断言
assertFalse(result.getAll());
assertFalse(result.getSelf());
assertEquals(1, result.getDeptIds().size());
assertTrue(CollUtil.contains(result.getDeptIds(), 3L));
}
}
@Test
public void testGetDeptDataPermission_DeptAndChild() {
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class)))
.thenReturn(permissionService);
// 准备参数
Long userId = 1L;
// mock 用户的角色编号
userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L));
// mock 获得用户的角色
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_AND_CHILD.getScope())
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO));
// mock 部门的返回
when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L),
null, null); // 最后返回 null 的目的,看看会不会重复调用
// mock 方法(部门)
DeptDO deptDO = randomPojo(DeptDO.class);
when(deptService.getChildDeptIdListFromCache(eq(3L))).thenReturn(singleton(deptDO.getId()));
// 调用
DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);
// 断言
assertFalse(result.getAll());
assertFalse(result.getSelf());
assertEquals(2, result.getDeptIds().size());
assertTrue(CollUtil.contains(result.getDeptIds(), deptDO.getId()));
assertTrue(CollUtil.contains(result.getDeptIds(), 3L));
}
}
@Test
public void testGetDeptDataPermission_Self() {
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class)))
.thenReturn(permissionService);
// 准备参数
Long userId = 1L;
// mock 用户的角色编号
userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L));
// mock 获得用户的角色
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.SELF.getScope())
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO));
// 调用
DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId);
// 断言
assertFalse(result.getAll());
assertTrue(result.getSelf());
assertTrue(CollUtil.isEmpty(result.getDeptIds()));
}
}
}

View File

@@ -1,355 +0,0 @@
package com.njcn.msgpush.module.system.service.permission;
import cn.hutool.extra.spring.SpringUtil;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.test.core.ut.BaseDbUnitTest;
import com.njcn.msgpush.module.system.controller.admin.permission.vo.role.RolePageReqVO;
import com.njcn.msgpush.module.system.controller.admin.permission.vo.role.RoleSaveReqVO;
import com.njcn.msgpush.module.system.dal.dataobject.permission.RoleDO;
import com.njcn.msgpush.module.system.dal.mysql.permission.RoleMapper;
import com.njcn.msgpush.module.system.enums.permission.DataScopeEnum;
import com.njcn.msgpush.module.system.enums.permission.RoleTypeEnum;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static com.njcn.msgpush.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static com.njcn.msgpush.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static com.njcn.msgpush.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertServiceException;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.*;
import static com.njcn.msgpush.module.system.enums.ErrorCodeConstants.*;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.verify;
@Import(RoleServiceImpl.class)
public class RoleServiceImplTest extends BaseDbUnitTest {
@Resource
private RoleServiceImpl roleService;
@Resource
private RoleMapper roleMapper;
@MockitoBean
private PermissionService permissionService;
@Test
public void testCreateRole() {
// 准备参数
RoleSaveReqVO reqVO = randomPojo(RoleSaveReqVO.class)
.setId(null) // 防止 id 被赋值
.setStatus(randomCommonStatus());
// 调用
Long roleId = roleService.createRole(reqVO, null);
// 断言
RoleDO roleDO = roleMapper.selectById(roleId);
assertPojoEquals(reqVO, roleDO, "id");
assertEquals(RoleTypeEnum.CUSTOM.getType(), roleDO.getType());
assertEquals(DataScopeEnum.ALL.getScope(), roleDO.getDataScope());
}
@Test
public void testUpdateRole() {
// mock 数据
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType()));
roleMapper.insert(roleDO);
// 准备参数
Long id = roleDO.getId();
RoleSaveReqVO reqVO = randomPojo(RoleSaveReqVO.class, o -> o.setId(id)
.setStatus(randomCommonStatus()));
// 调用
roleService.updateRole(reqVO);
// 断言
RoleDO newRoleDO = roleMapper.selectById(id);
assertPojoEquals(reqVO, newRoleDO);
}
@Test
public void testUpdateRoleDataScope() {
// mock 数据
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType()));
roleMapper.insert(roleDO);
// 准备参数
Long id = roleDO.getId();
Integer dataScope = randomEle(DataScopeEnum.values()).getScope();
Set<Long> dataScopeRoleIds = randomSet(Long.class);
// 调用
roleService.updateRoleDataScope(id, dataScope, dataScopeRoleIds);
// 断言
RoleDO dbRoleDO = roleMapper.selectById(id);
assertEquals(dataScope, dbRoleDO.getDataScope());
assertEquals(dataScopeRoleIds, dbRoleDO.getDataScopeDeptIds());
}
@Test
public void testDeleteRole() {
// mock 数据
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType()));
roleMapper.insert(roleDO);
// 参数准备
Long id = roleDO.getId();
// 调用
roleService.deleteRole(id);
// 断言
assertNull(roleMapper.selectById(id));
// verify 删除相关数据
verify(permissionService).processRoleDeleted(id);
}
@Test
public void testValidateRoleDuplicate_success() {
// 调用,不会抛异常
roleService.validateRoleDuplicate(randomString(), randomString(), null);
}
@Test
public void testValidateRoleDuplicate_nameDuplicate() {
// mock 数据
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setName("role_name"));
roleMapper.insert(roleDO);
// 准备参数
String name = "role_name";
// 调用,并断言异常
assertServiceException(() -> roleService.validateRoleDuplicate(name, randomString(), null),
ROLE_NAME_DUPLICATE, name);
}
@Test
public void testValidateRoleDuplicate_codeDuplicate() {
// mock 数据
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setCode("code"));
roleMapper.insert(roleDO);
// 准备参数
String code = "code";
// 调用,并断言异常
assertServiceException(() -> roleService.validateRoleDuplicate(randomString(), code, null),
ROLE_CODE_DUPLICATE, code);
}
@Test
public void testValidateUpdateRole_success() {
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType()));
roleMapper.insert(roleDO);
// 准备参数
Long id = roleDO.getId();
// 调用,无异常
roleService.validateRoleForUpdate(id);
}
@Test
public void testValidateUpdateRole_roleIdNotExist() {
assertServiceException(() -> roleService.validateRoleForUpdate(randomLongId()), ROLE_NOT_EXISTS);
}
@Test
public void testValidateUpdateRole_systemRoleCanNotBeUpdate() {
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.SYSTEM.getType()));
roleMapper.insert(roleDO);
// 准备参数
Long id = roleDO.getId();
assertServiceException(() -> roleService.validateRoleForUpdate(id),
ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE);
}
@Test
public void testGetRole() {
// mock 数据
RoleDO roleDO = randomPojo(RoleDO.class);
roleMapper.insert(roleDO);
// 参数准备
Long id = roleDO.getId();
// 调用
RoleDO dbRoleDO = roleService.getRole(id);
// 断言
assertPojoEquals(roleDO, dbRoleDO);
}
@Test
public void testGetRoleFromCache() {
// mock 数据(缓存)
RoleDO roleDO = randomPojo(RoleDO.class);
roleMapper.insert(roleDO);
// 参数准备
Long id = roleDO.getId();
// 调用
RoleDO dbRoleDO = roleService.getRoleFromCache(id);
// 断言
assertPojoEquals(roleDO, dbRoleDO);
}
@Test
public void testGetRoleListByStatus() {
// mock 数据
RoleDO dbRole01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
roleMapper.insert(dbRole01);
RoleDO dbRole02 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
roleMapper.insert(dbRole02);
// 调用
List<RoleDO> list = roleService.getRoleListByStatus(
singleton(CommonStatusEnum.ENABLE.getStatus()));
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbRole01, list.get(0));
}
@Test
public void testGetRoleList() {
// mock 数据
RoleDO dbRole01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
roleMapper.insert(dbRole01);
RoleDO dbRole02 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
roleMapper.insert(dbRole02);
// 调用
List<RoleDO> list = roleService.getRoleList();
// 断言
assertEquals(2, list.size());
assertPojoEquals(dbRole01, list.get(0));
assertPojoEquals(dbRole02, list.get(1));
}
@Test
public void testGetRoleList_ids() {
// mock 数据
RoleDO dbRole01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
roleMapper.insert(dbRole01);
RoleDO dbRole02 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
roleMapper.insert(dbRole02);
// 准备参数
Collection<Long> ids = singleton(dbRole01.getId());
// 调用
List<RoleDO> list = roleService.getRoleList(ids);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbRole01, list.get(0));
}
@Test
public void testGetRoleListFromCache() {
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class)))
.thenReturn(roleService);
// mock 数据
RoleDO dbRole = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
roleMapper.insert(dbRole);
// 测试 id 不匹配
roleMapper.insert(cloneIgnoreId(dbRole, o -> {}));
// 准备参数
Collection<Long> ids = singleton(dbRole.getId());
// 调用
List<RoleDO> list = roleService.getRoleListFromCache(ids);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbRole, list.get(0));
}
}
@Test
public void testGetRolePage() {
// mock 数据
RoleDO dbRole = randomPojo(RoleDO.class, o -> { // 等会查询到
o.setName("土豆");
o.setCode("tudou");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2022, 2, 8));
});
roleMapper.insert(dbRole);
// 测试 name 不匹配
roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setName("红薯")));
// 测试 code 不匹配
roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setCode("hong")));
// 测试 createTime 不匹配
roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setCreateTime(buildTime(2022, 2, 16))));
// 准备参数
RolePageReqVO reqVO = new RolePageReqVO();
reqVO.setName("土豆");
reqVO.setCode("tu");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setCreateTime(buildBetweenTime(2022, 2, 1, 2022, 2, 12));
// 调用
PageResult<RoleDO> pageResult = roleService.getRolePage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbRole, pageResult.getList().get(0));
}
@Test
public void testHasAnySuperAdmin_true() {
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class)))
.thenReturn(roleService);
// mock 数据
RoleDO dbRole = randomPojo(RoleDO.class).setCode("super_admin");
roleMapper.insert(dbRole);
// 准备参数
Long id = dbRole.getId();
// 调用,并调用
assertTrue(roleService.hasAnySuperAdmin(singletonList(id)));
}
}
@Test
public void testValidateRoleList_success() {
// mock 数据
RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
roleMapper.insert(roleDO);
// 准备参数
List<Long> ids = singletonList(roleDO.getId());
// 调用,无需断言
roleService.validateRoleList(ids);
}
@Test
public void testValidateRoleList_notFound() {
// 准备参数
List<Long> ids = singletonList(randomLongId());
// 调用, 并断言异常
assertServiceException(() -> roleService.validateRoleList(ids), ROLE_NOT_EXISTS);
}
@Test
public void testValidateRoleList_notEnable() {
// mock 数据
RoleDO RoleDO = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
roleMapper.insert(RoleDO);
// 准备参数
List<Long> ids = singletonList(RoleDO.getId());
// 调用, 并断言异常
assertServiceException(() -> roleService.validateRoleList(ids), ROLE_IS_DISABLE, RoleDO.getName());
}
}

View File

@@ -1,744 +0,0 @@
package com.njcn.msgpush.module.system.service.user;
import cn.hutool.core.util.RandomUtil;
import com.njcn.msgpush.framework.common.enums.CommonStatusEnum;
import com.njcn.msgpush.framework.common.exception.ServiceException;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.common.util.collection.ArrayUtils;
import com.njcn.msgpush.framework.common.util.collection.CollectionUtils;
import com.njcn.msgpush.framework.test.core.ut.BaseDbUnitTest;
import com.njcn.msgpush.module.infra.api.config.ConfigApi;
import com.njcn.msgpush.module.infra.api.file.FileApi;
import com.njcn.msgpush.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO;
import com.njcn.msgpush.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO;
import com.njcn.msgpush.module.system.controller.admin.user.vo.user.UserImportExcelVO;
import com.njcn.msgpush.module.system.controller.admin.user.vo.user.UserImportRespVO;
import com.njcn.msgpush.module.system.controller.admin.user.vo.user.UserPageReqVO;
import com.njcn.msgpush.module.system.controller.admin.user.vo.user.UserSaveReqVO;
import com.njcn.msgpush.module.system.dal.dataobject.dept.DeptDO;
import com.njcn.msgpush.module.system.dal.dataobject.dept.PostDO;
import com.njcn.msgpush.module.system.dal.dataobject.dept.UserPostDO;
import com.njcn.msgpush.module.system.dal.dataobject.user.AdminUserDO;
import com.njcn.msgpush.module.system.dal.mysql.dept.UserPostMapper;
import com.njcn.msgpush.module.system.dal.mysql.user.AdminUserMapper;
import com.njcn.msgpush.module.system.enums.common.SexEnum;
import com.njcn.msgpush.module.system.service.dept.DeptService;
import com.njcn.msgpush.module.system.service.dept.PostService;
import com.njcn.msgpush.module.system.service.oauth2.OAuth2TokenService;
import com.njcn.msgpush.module.system.service.permission.PermissionService;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.stubbing.Answer;
import org.springframework.context.annotation.Import;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
import static com.njcn.msgpush.framework.common.util.collection.SetUtils.asSet;
import static com.njcn.msgpush.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static com.njcn.msgpush.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static com.njcn.msgpush.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertPojoEquals;
import static com.njcn.msgpush.framework.test.core.util.AssertUtils.assertServiceException;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.*;
import static com.njcn.msgpush.module.system.enums.ErrorCodeConstants.*;
import static com.njcn.msgpush.module.system.service.user.AdminUserServiceImpl.USER_INIT_PASSWORD_KEY;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
import static org.assertj.core.util.Lists.newArrayList;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
@Import(AdminUserServiceImpl.class)
public class AdminUserServiceImplTest extends BaseDbUnitTest {
@Resource
private AdminUserServiceImpl userService;
@Resource
private AdminUserMapper userMapper;
@Resource
private UserPostMapper userPostMapper;
@MockitoBean
private DeptService deptService;
@MockitoBean
private PostService postService;
@MockitoBean
private PermissionService permissionService;
@MockitoBean
private PasswordEncoder passwordEncoder;
@MockitoBean
private FileApi fileApi;
@MockitoBean
private ConfigApi configApi;
@MockitoBean
private OAuth2TokenService oauth2TokenService;
@BeforeEach
public void before() {
// mock 初始化密码
when(configApi.getConfigValueByKey(USER_INIT_PASSWORD_KEY)).thenReturn(success("msgpushyuanma"));
}
@Test
public void testCreatUser_success() {
// 准备参数
UserSaveReqVO reqVO = randomPojo(UserSaveReqVO.class, o -> {
o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex());
o.setMobile(randomString());
o.setPostIds(asSet(1L, 2L));
}).setId(null); // 避免 id 被赋值
// mock deptService 的方法
DeptDO dept = randomPojo(DeptDO.class, o -> {
o.setId(reqVO.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// mock postService 的方法
List<PostDO> posts = CollectionUtils.convertList(reqVO.getPostIds(), postId ->
randomPojo(PostDO.class, o -> {
o.setId(postId);
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
}));
when(postService.getPostList(eq(reqVO.getPostIds()), isNull())).thenReturn(posts);
// mock passwordEncoder 的方法
when(passwordEncoder.encode(eq(reqVO.getPassword()))).thenReturn("msgpushyuanma");
// 调用
Long userId = userService.createUser(reqVO);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertPojoEquals(reqVO, user, "password", "id");
assertEquals("msgpushyuanma", user.getPassword());
assertEquals(CommonStatusEnum.ENABLE.getStatus(), user.getStatus());
// 断言关联岗位
List<UserPostDO> userPosts = userPostMapper.selectListByUserId(user.getId());
assertEquals(1L, userPosts.get(0).getPostId());
assertEquals(2L, userPosts.get(1).getPostId());
}
@Test
public void testUpdateUser_success() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO(o -> o.setPostIds(asSet(1L, 2L)));
userMapper.insert(dbUser);
userPostMapper.insert(new UserPostDO().setUserId(dbUser.getId()).setPostId(1L));
userPostMapper.insert(new UserPostDO().setUserId(dbUser.getId()).setPostId(2L));
// 准备参数
UserSaveReqVO reqVO = randomPojo(UserSaveReqVO.class, o -> {
o.setId(dbUser.getId());
o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex());
o.setMobile(randomString());
o.setPostIds(asSet(2L, 3L));
});
// mock deptService 的方法
DeptDO dept = randomPojo(DeptDO.class, o -> {
o.setId(reqVO.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// mock postService 的方法
List<PostDO> posts = CollectionUtils.convertList(reqVO.getPostIds(), postId ->
randomPojo(PostDO.class, o -> {
o.setId(postId);
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
}));
when(postService.getPostList(eq(reqVO.getPostIds()), isNull())).thenReturn(posts);
// 调用
userService.updateUser(reqVO);
// 断言
AdminUserDO user = userMapper.selectById(reqVO.getId());
assertPojoEquals(reqVO, user, "password");
// 断言关联岗位
List<UserPostDO> userPosts = userPostMapper.selectListByUserId(user.getId());
assertEquals(2L, userPosts.get(0).getPostId());
assertEquals(3L, userPosts.get(1).getPostId());
}
@Test
public void testUpdateUserLogin() {
// mock 数据
AdminUserDO user = randomAdminUserDO(o -> o.setLoginDate(null));
userMapper.insert(user);
// 准备参数
Long id = user.getId();
String loginIp = randomString();
// 调用
userService.updateUserLogin(id, loginIp);
// 断言
AdminUserDO dbUser = userMapper.selectById(id);
assertEquals(loginIp, dbUser.getLoginIp());
assertNotNull(dbUser.getLoginDate());
}
@Test
public void testUpdateUserProfile_success() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
UserProfileUpdateReqVO reqVO = randomPojo(UserProfileUpdateReqVO.class, o -> {
o.setMobile(randomString());
o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex());
o.setAvatar(randomURL());
});
// 调用
userService.updateUserProfile(userId, reqVO);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertPojoEquals(reqVO, user);
}
@Test
public void testUpdateUserPassword_success() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO(o -> o.setPassword("encode:tudou"));
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
UserProfileUpdatePasswordReqVO reqVO = randomPojo(UserProfileUpdatePasswordReqVO.class, o -> {
o.setOldPassword("tudou");
o.setNewPassword("yuanma");
});
// mock 方法
when(passwordEncoder.encode(anyString())).then(
(Answer<String>) invocationOnMock -> "encode:" + invocationOnMock.getArgument(0));
when(passwordEncoder.matches(eq(reqVO.getOldPassword()), eq(dbUser.getPassword()))).thenReturn(true);
// 调用
userService.updateUserPassword(userId, reqVO);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertEquals("encode:yuanma", user.getPassword());
}
@Test
public void testUpdateUserPassword02_success() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
String password = "msgpush";
// mock 方法
when(passwordEncoder.encode(anyString())).then(
(Answer<String>) invocationOnMock -> "encode:" + invocationOnMock.getArgument(0));
// 调用
userService.updateUserPassword(userId, password);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertEquals("encode:" + password, user.getPassword());
}
@Test
public void testUpdateUserStatus() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
Integer status = randomCommonStatus();
// 调用
userService.updateUserStatus(userId, status);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertEquals(status, user.getStatus());
}
@Test
public void testDeleteUser_success(){
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
// 调用数据
userService.deleteUser(userId);
// 校验结果
assertNull(userMapper.selectById(userId));
// 校验调用次数
verify(permissionService, times(1)).processUserDeleted(eq(userId));
}
@Test
public void testGetUserByUsername() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
String username = dbUser.getUsername();
// 调用
AdminUserDO user = userService.getUserByUsername(username);
// 断言
assertPojoEquals(dbUser, user);
}
@Test
public void testGetUserByMobile() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
String mobile = dbUser.getMobile();
// 调用
AdminUserDO user = userService.getUserByMobile(mobile);
// 断言
assertPojoEquals(dbUser, user);
}
@Test
public void testGetUserPage() {
// mock 数据
AdminUserDO dbUser = initGetUserPageData();
// 准备参数
UserPageReqVO reqVO = new UserPageReqVO();
reqVO.setUsername("tu");
reqVO.setMobile("1560");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setCreateTime(buildBetweenTime(2020, 12, 1, 2020, 12, 24));
reqVO.setDeptId(1L); // 其中1L 是 2L 的父部门
// mock 方法
List<DeptDO> deptList = newArrayList(randomPojo(DeptDO.class, o -> o.setId(2L)));
when(deptService.getChildDeptList(eq(reqVO.getDeptId()))).thenReturn(deptList);
// 调用
PageResult<AdminUserDO> pageResult = userService.getUserPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbUser, pageResult.getList().get(0));
}
/**
* 初始化 getUserPage 方法的测试数据
*/
private AdminUserDO initGetUserPageData() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO(o -> { // 等会查询到
o.setUsername("tudou");
o.setMobile("15601691300");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2020, 12, 12));
o.setDeptId(2L);
});
userMapper.insert(dbUser);
// 测试 username 不匹配
userMapper.insert(cloneIgnoreId(dbUser, o -> o.setUsername("dou")));
// 测试 mobile 不匹配
userMapper.insert(cloneIgnoreId(dbUser, o -> o.setMobile("18818260888")));
// 测试 status 不匹配
userMapper.insert(cloneIgnoreId(dbUser, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配
userMapper.insert(cloneIgnoreId(dbUser, o -> o.setCreateTime(buildTime(2020, 11, 11))));
// 测试 dept 不匹配
userMapper.insert(cloneIgnoreId(dbUser, o -> o.setDeptId(0L)));
return dbUser;
}
@Test
public void testGetUser() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
// 调用
AdminUserDO user = userService.getUser(userId);
// 断言
assertPojoEquals(dbUser, user);
}
@Test
public void testGetUserListByDeptIds() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO(o -> o.setDeptId(1L));
userMapper.insert(dbUser);
// 测试 deptId 不匹配
userMapper.insert(cloneIgnoreId(dbUser, o -> o.setDeptId(2L)));
// 准备参数
Collection<Long> deptIds = singleton(1L);
// 调用
List<AdminUserDO> list = userService.getUserListByDeptIds(deptIds);
// 断言
assertEquals(1, list.size());
assertEquals(dbUser, list.get(0));
}
/**
* 情况一,校验不通过,导致插入失败
*/
@Test
public void testImportUserList_01() {
// 准备参数
UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
o.setEmail(randomEmail());
o.setMobile(randomMobile());
});
// mock 方法,模拟失败
doThrow(new ServiceException(DEPT_NOT_FOUND)).when(deptService).validateDeptList(any());
// 调用
UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true);
// 断言
assertEquals(0, respVO.getCreateUsernames().size());
assertEquals(0, respVO.getUpdateUsernames().size());
assertEquals(1, respVO.getFailureUsernames().size());
assertEquals(DEPT_NOT_FOUND.getMsg(), respVO.getFailureUsernames().get(importUser.getUsername()));
}
/**
* 情况二,不存在,进行插入
*/
@Test
public void testImportUserList_02() {
// 准备参数
UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
o.setEmail(randomEmail());
o.setMobile(randomMobile());
});
// mock deptService 的方法
DeptDO dept = randomPojo(DeptDO.class, o -> {
o.setId(importUser.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// mock passwordEncoder 的方法
when(passwordEncoder.encode(eq("msgpushyuanma"))).thenReturn("java");
// 调用
UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true);
// 断言
assertEquals(1, respVO.getCreateUsernames().size());
AdminUserDO user = userMapper.selectByUsername(respVO.getCreateUsernames().get(0));
assertPojoEquals(importUser, user);
assertEquals("java", user.getPassword());
assertEquals(0, respVO.getUpdateUsernames().size());
assertEquals(0, respVO.getFailureUsernames().size());
}
/**
* 情况三,存在,但是不强制更新
*/
@Test
public void testImportUserList_03() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
o.setUsername(dbUser.getUsername());
o.setEmail(randomEmail());
o.setMobile(randomMobile());
});
// mock deptService 的方法
DeptDO dept = randomPojo(DeptDO.class, o -> {
o.setId(importUser.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// 调用
UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), false);
// 断言
assertEquals(0, respVO.getCreateUsernames().size());
assertEquals(0, respVO.getUpdateUsernames().size());
assertEquals(1, respVO.getFailureUsernames().size());
assertEquals(USER_USERNAME_EXISTS.getMsg(), respVO.getFailureUsernames().get(importUser.getUsername()));
}
/**
* 情况四,存在,强制更新
*/
@Test
public void testImportUserList_04() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
// 准备参数
UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
o.setUsername(dbUser.getUsername());
o.setEmail(randomEmail());
o.setMobile(randomMobile());
});
// mock deptService 的方法
DeptDO dept = randomPojo(DeptDO.class, o -> {
o.setId(importUser.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// 调用
UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true);
// 断言
assertEquals(0, respVO.getCreateUsernames().size());
assertEquals(1, respVO.getUpdateUsernames().size());
AdminUserDO user = userMapper.selectByUsername(respVO.getUpdateUsernames().get(0));
assertPojoEquals(importUser, user);
assertEquals(0, respVO.getFailureUsernames().size());
}
@Test
public void testValidateUserExists_notExists() {
assertServiceException(() -> userService.validateUserExists(randomLongId()), USER_NOT_EXISTS);
}
@Test
public void testValidateUsernameUnique_usernameExistsForCreate() {
// 准备参数
String username = randomString();
// mock 数据
userMapper.insert(randomAdminUserDO(o -> o.setUsername(username)));
// 调用,校验异常
assertServiceException(() -> userService.validateUsernameUnique(null, username),
USER_USERNAME_EXISTS);
}
@Test
public void testValidateUsernameUnique_usernameExistsForUpdate() {
// 准备参数
Long id = randomLongId();
String username = randomString();
// mock 数据
userMapper.insert(randomAdminUserDO(o -> o.setUsername(username)));
// 调用,校验异常
assertServiceException(() -> userService.validateUsernameUnique(id, username),
USER_USERNAME_EXISTS);
}
@Test
public void testValidateEmailUnique_emailExistsForCreate() {
// 准备参数
String email = randomString();
// mock 数据
userMapper.insert(randomAdminUserDO(o -> o.setEmail(email)));
// 调用,校验异常
assertServiceException(() -> userService.validateEmailUnique(null, email),
USER_EMAIL_EXISTS);
}
@Test
public void testValidateEmailUnique_emailExistsForUpdate() {
// 准备参数
Long id = randomLongId();
String email = randomString();
// mock 数据
userMapper.insert(randomAdminUserDO(o -> o.setEmail(email)));
// 调用,校验异常
assertServiceException(() -> userService.validateEmailUnique(id, email),
USER_EMAIL_EXISTS);
}
@Test
public void testValidateMobileUnique_mobileExistsForCreate() {
// 准备参数
String mobile = randomString();
// mock 数据
userMapper.insert(randomAdminUserDO(o -> o.setMobile(mobile)));
// 调用,校验异常
assertServiceException(() -> userService.validateMobileUnique(null, mobile),
USER_MOBILE_EXISTS);
}
@Test
public void testValidateMobileUnique_mobileExistsForUpdate() {
// 准备参数
Long id = randomLongId();
String mobile = randomString();
// mock 数据
userMapper.insert(randomAdminUserDO(o -> o.setMobile(mobile)));
// 调用,校验异常
assertServiceException(() -> userService.validateMobileUnique(id, mobile),
USER_MOBILE_EXISTS);
}
@Test
public void testValidateOldPassword_notExists() {
assertServiceException(() -> userService.validateOldPassword(randomLongId(), randomString()),
USER_NOT_EXISTS);
}
@Test
public void testValidateOldPassword_passwordFailed() {
// mock 数据
AdminUserDO user = randomAdminUserDO();
userMapper.insert(user);
// 准备参数
Long id = user.getId();
String oldPassword = user.getPassword();
// 调用,校验异常
assertServiceException(() -> userService.validateOldPassword(id, oldPassword),
USER_PASSWORD_FAILED);
// 校验调用
verify(passwordEncoder, times(1)).matches(eq(oldPassword), eq(user.getPassword()));
}
@Test
public void testUserListByPostIds() {
// 准备参数
Collection<Long> postIds = asSet(10L, 20L);
// mock user1 数据
AdminUserDO user1 = randomAdminUserDO(o -> o.setPostIds(asSet(10L, 30L)));
userMapper.insert(user1);
userPostMapper.insert(new UserPostDO().setUserId(user1.getId()).setPostId(10L));
userPostMapper.insert(new UserPostDO().setUserId(user1.getId()).setPostId(30L));
// mock user2 数据
AdminUserDO user2 = randomAdminUserDO(o -> o.setPostIds(singleton(100L)));
userMapper.insert(user2);
userPostMapper.insert(new UserPostDO().setUserId(user2.getId()).setPostId(100L));
// 调用
List<AdminUserDO> result = userService.getUserListByPostIds(postIds);
// 断言
assertEquals(1, result.size());
assertEquals(user1, result.get(0));
}
@Test
public void testGetUserList() {
// mock 数据
AdminUserDO user = randomAdminUserDO();
userMapper.insert(user);
// 测试 id 不匹配
userMapper.insert(randomAdminUserDO());
// 准备参数
Collection<Long> ids = singleton(user.getId());
// 调用
List<AdminUserDO> result = userService.getUserList(ids);
// 断言
assertEquals(1, result.size());
assertEquals(user, result.get(0));
}
@Test
public void testGetUserMap() {
// mock 数据
AdminUserDO user = randomAdminUserDO();
userMapper.insert(user);
// 测试 id 不匹配
userMapper.insert(randomAdminUserDO());
// 准备参数
Collection<Long> ids = singleton(user.getId());
// 调用
Map<Long, AdminUserDO> result = userService.getUserMap(ids);
// 断言
assertEquals(1, result.size());
assertEquals(user, result.get(user.getId()));
}
@Test
public void testGetUserListByNickname() {
// mock 数据
AdminUserDO user = randomAdminUserDO(o -> o.setNickname("芋头"));
userMapper.insert(user);
// 测试 nickname 不匹配
userMapper.insert(randomAdminUserDO(o -> o.setNickname("源码")));
// 准备参数
String nickname = "";
// 调用
List<AdminUserDO> result = userService.getUserListByNickname(nickname);
// 断言
assertEquals(1, result.size());
assertEquals(user, result.get(0));
}
@Test
public void testGetUserListByStatus() {
// mock 数据
AdminUserDO user = randomAdminUserDO(o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
userMapper.insert(user);
// 测试 status 不匹配
userMapper.insert(randomAdminUserDO(o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())));
// 准备参数
Integer status = CommonStatusEnum.DISABLE.getStatus();
// 调用
List<AdminUserDO> result = userService.getUserListByStatus(status);
// 断言
assertEquals(1, result.size());
assertEquals(user, result.get(0));
}
@Test
public void testValidateUserList_success() {
// mock 数据
AdminUserDO userDO = randomAdminUserDO().setStatus(CommonStatusEnum.ENABLE.getStatus());
userMapper.insert(userDO);
// 准备参数
List<Long> ids = singletonList(userDO.getId());
// 调用,无需断言
userService.validateUserList(ids);
}
@Test
public void testValidateUserList_notFound() {
// 准备参数
List<Long> ids = singletonList(randomLongId());
// 调用, 并断言异常
assertServiceException(() -> userService.validateUserList(ids), USER_NOT_EXISTS);
}
@Test
public void testValidateUserList_notEnable() {
// mock 数据
AdminUserDO userDO = randomAdminUserDO().setStatus(CommonStatusEnum.DISABLE.getStatus());
userMapper.insert(userDO);
// 准备参数
List<Long> ids = singletonList(userDO.getId());
// 调用, 并断言异常
assertServiceException(() -> userService.validateUserList(ids), USER_IS_DISABLE,
userDO.getNickname());
}
// ========== 随机对象 ==========
@SafeVarargs
private static AdminUserDO randomAdminUserDO(Consumer<AdminUserDO>... consumers) {
Consumer<AdminUserDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
};
return randomPojo(AdminUserDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@@ -1,53 +0,0 @@
spring:
main:
lazy-initialization: true # 开启懒加载,加快速度
banner-mode: off # 单元测试,禁用 Banner
--- #################### 数据库相关配置 ####################
spring:
# 数据源配置项
datasource:
name: ruoyi-vue-pro
url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式DATABASE_TO_UPPER 配置表和字段使用小写
driver-class-name: org.h2.Driver
username: sa
password:
druid:
async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度
initial-size: 1 # 单元测试,配置为 1提升启动速度
sql:
init:
schema-locations: classpath:/sql/create_tables.sql
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 16379 # 端口(单元测试,使用 16379 端口)
database: 0 # 数据库索引
mybatis:
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
mybatis-plus:
global-config:
db-config:
id-type: AUTO # H2 主键递增
--- #################### 配置中心相关配置 ####################
--- #################### 服务保障相关配置 ####################
# Lock4j 配置项(单元测试,禁用 Lock4j
--- #################### 监控相关配置 ####################
--- #################### 灿能相关配置 ####################
# 灿能配置项,设置当前项目所有自定义的配置
msgpush:
info:
base-package: com.njcn.msgpush.module

View File

@@ -1,4 +0,0 @@
<configuration>
<!-- 引用 Spring Boot 的 logback 基础配置 -->
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
</configuration>

View File

@@ -2,23 +2,36 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.njcn</groupId>
<artifactId>msgpush</artifactId>
<artifactId>cn-msgpush</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<modules>
<module>msgpush-module-system-api</module>
<module>msgpush-module-system-server</module>
</modules>
<artifactId>msgpush-module-system</artifactId>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>
system 模块下,我们放通用业务,支撑上层的核心业务。
例如说:用户、部门、权限、数据字典等等
RDMS 系统模块
该模块用来放通用业务,支撑上层的核心业务,例如说:用户、部门、权限、数据字典等等
也包含一些基础服务和运维服务,例如说:定时任务、文件存储等等。
</description>
<modules>
<module>msgpush-module-system-api</module>
<module>msgpush-module-system-boot</module>
</modules>
</project>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-common</artifactId>
</dependency>
</dependencies>
</project>