Commit 8d22ed8bea2b1bdab0d90113bcaab0f3437a0734

Authored by 杨宇庆
Committed by 芋道源码
1 parent 182511b6

!1156 feat: 支持短信重置后台密码

* feat: 支持短信重置后台密码
* 重构管理后台验证码的验证流程
Showing 11 changed files with 154 additions and 51 deletions
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
... ... @@ -43,6 +43,7 @@ public interface ErrorCodeConstants {
43 43 ErrorCode USER_IS_DISABLE = new ErrorCode(1_002_003_006, "名字为【{}】的用户已被禁用");
44 44 ErrorCode USER_COUNT_MAX = new ErrorCode(1_002_003_008, "创建用户失败,原因:超过租户最大租户配额({})!");
45 45 ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空");
  46 + ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_002_003_010, "该手机号尚未注册");
46 47  
47 48 // ========== 部门模块 1-002-004-000 ==========
48 49 ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门");
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSceneEnum.java
... ... @@ -21,7 +21,9 @@ public enum SmsSceneEnum implements IntArrayValuable {
21 21 MEMBER_UPDATE_PASSWORD(3, "user-update-password", "会员用户 - 修改密码"),
22 22 MEMBER_RESET_PASSWORD(4, "user-reset-password", "会员用户 - 忘记密码"),
23 23  
24   - ADMIN_MEMBER_LOGIN(21, "admin-sms-login", "后台用户 - 手机号登录");
  24 + ADMIN_MEMBER_LOGIN(21, "admin-sms-login", "后台用户 - 手机号登录"),
  25 + ADMIN_MEMBER_REGISTER(22, "admin-sms-register", "后台用户 - 手机号注册"),
  26 + ADMIN_MEMBER_RESET_PASSWORD(23, "admin-reset-password", "后台用户 - 忘记密码");
25 27  
26 28 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SmsSceneEnum::getScene).toArray();
27 29  
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java
... ... @@ -7,7 +7,14 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
7 7 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
8 8 import cn.iocoder.yudao.framework.security.config.SecurityProperties;
9 9 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
10   -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*;
  10 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthLoginReqVO;
  11 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthLoginRespVO;
  12 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthPermissionInfoRespVO;
  13 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthRegisterReqVO;
  14 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthResetPasswordReqVO;
  15 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthSmsLoginReqVO;
  16 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthSmsSendReqVO;
  17 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthSocialLoginReqVO;
11 18 import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
12 19 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
13 20 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
... ... @@ -23,14 +30,19 @@ import io.swagger.v3.oas.annotations.Operation;
23 30 import io.swagger.v3.oas.annotations.Parameter;
24 31 import io.swagger.v3.oas.annotations.Parameters;
25 32 import io.swagger.v3.oas.annotations.tags.Tag;
26   -import lombok.extern.slf4j.Slf4j;
27   -import org.springframework.validation.annotation.Validated;
28   -import org.springframework.web.bind.annotation.*;
29   -
30 33 import jakarta.annotation.Resource;
31 34 import jakarta.annotation.security.PermitAll;
32 35 import jakarta.servlet.http.HttpServletRequest;
33 36 import jakarta.validation.Valid;
  37 +import lombok.extern.slf4j.Slf4j;
  38 +import org.springframework.validation.annotation.Validated;
  39 +import org.springframework.web.bind.annotation.GetMapping;
  40 +import org.springframework.web.bind.annotation.PostMapping;
  41 +import org.springframework.web.bind.annotation.RequestBody;
  42 +import org.springframework.web.bind.annotation.RequestMapping;
  43 +import org.springframework.web.bind.annotation.RequestParam;
  44 +import org.springframework.web.bind.annotation.RestController;
  45 +
34 46 import java.util.Collections;
35 47 import java.util.List;
36 48 import java.util.Set;
... ... @@ -139,6 +151,14 @@ public class AuthController {
139 151 return success(true);
140 152 }
141 153  
  154 + @PostMapping("/reset-password")
  155 + @PermitAll
  156 + @Operation(summary = "重置密码")
  157 + public CommonResult<Boolean> resetPassword(@RequestBody @Valid AuthResetPasswordReqVO reqVO) {
  158 + authService.resetPassword(reqVO);
  159 + return success(true);
  160 + }
  161 +
142 162 // ========== 社交登录相关 ==========
143 163  
144 164 @GetMapping("/social-auth-redirect")
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java
... ... @@ -19,7 +19,7 @@ import jakarta.validation.constraints.Pattern;
19 19 @NoArgsConstructor
20 20 @AllArgsConstructor
21 21 @Builder
22   -public class AuthLoginReqVO {
  22 +public class AuthLoginReqVO extends CaptchaVerificationReqVO {
23 23  
24 24 @Schema(description = "账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma")
25 25 @NotEmpty(message = "登录账号不能为空")
... ... @@ -32,13 +32,6 @@ public class AuthLoginReqVO {
32 32 @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
33 33 private String password;
34 34  
35   - // ========== 图片验证码相关 ==========
36   -
37   - @Schema(description = "验证码,验证码开启时,需要传递", requiredMode = Schema.RequiredMode.REQUIRED,
38   - example = "PfcH6mgr8tpXuMWFjvW6YVaqrswIuwmWI5dsVZSg7sGpWtDCUbHuDEXl3cFB1+VvCC/rAkSwK8Fad52FSuncVg==")
39   - @NotEmpty(message = "验证码不能为空", groups = CodeEnableGroup.class)
40   - private String captchaVerification;
41   -
42 35 // ========== 绑定社交登录时,需要传递如下参数 ==========
43 36  
44 37 @Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
... ... @@ -51,11 +44,6 @@ public class AuthLoginReqVO {
51 44 @Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62")
52 45 private String socialState;
53 46  
54   - /**
55   - * 开启验证码的 Group
56   - */
57   - public interface CodeEnableGroup {}
58   -
59 47 @AssertTrue(message = "授权码不能为空")
60 48 public boolean isSocialCodeValid() {
61 49 return socialType == null || StrUtil.isNotEmpty(socialCode);
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthRegisterReqVO.java
... ... @@ -9,7 +9,7 @@ import jakarta.validation.constraints.*;
9 9  
10 10 @Schema(description = "管理后台 - Register Request VO")
11 11 @Data
12   -public class AuthRegisterReqVO {
  12 +public class AuthRegisterReqVO extends CaptchaVerificationReqVO {
13 13  
14 14 @Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao")
15 15 @NotBlank(message = "用户账号不能为空")
... ... @@ -26,12 +26,4 @@ public class AuthRegisterReqVO {
26 26 @NotEmpty(message = "密码不能为空")
27 27 @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
28 28 private String password;
29   -
30   - // ========== 图片验证码相关 ==========
31   -
32   - @Schema(description = "验证码,验证码开启时,需要传递", requiredMode = Schema.RequiredMode.REQUIRED,
33   - example = "PfcH6mgr8tpXuMWFjvW6YVaqrswIuwmWI5dsVZSg7sGpWtDCUbHuDEXl3cFB1+VvCC/rAkSwK8Fad52FSuncVg==")
34   - @NotEmpty(message = "验证码不能为空", groups = AuthLoginReqVO.CodeEnableGroup.class)
35   - private String captchaVerification;
36   -
37 29 }
38 30 \ No newline at end of file
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthResetPasswordReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.auth.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.Mobile;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import jakarta.validation.constraints.NotEmpty;
  6 +import lombok.AllArgsConstructor;
  7 +import lombok.Builder;
  8 +import lombok.Data;
  9 +import lombok.NoArgsConstructor;
  10 +import org.hibernate.validator.constraints.Length;
  11 +
  12 +@Schema(description = "管理后台 - 短信重置账号密码 Request VO")
  13 +@Data
  14 +@NoArgsConstructor
  15 +@AllArgsConstructor
  16 +@Builder
  17 +public class AuthResetPasswordReqVO {
  18 +
  19 + @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1234")
  20 + @NotEmpty(message = "密码不能为空")
  21 + @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
  22 + private String password;
  23 +
  24 + @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13312341234")
  25 + @NotEmpty(message = "手机号不能为空")
  26 + @Mobile
  27 + private String mobile;
  28 +
  29 + @Schema(description = "手机短信验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
  30 + @NotEmpty(message = "手机手机短信验证码不能为空")
  31 + private String code;
  32 +}
0 33 \ No newline at end of file
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsSendReqVO.java
... ... @@ -17,7 +17,7 @@ import jakarta.validation.constraints.NotNull;
17 17 @NoArgsConstructor
18 18 @AllArgsConstructor
19 19 @Builder
20   -public class AuthSmsSendReqVO {
  20 +public class AuthSmsSendReqVO extends CaptchaVerificationReqVO {
21 21  
22 22 @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma")
23 23 @NotEmpty(message = "手机号不能为空")
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/CaptchaVerificationReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.auth.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - 验证码 Request VO")
  8 +@Data
  9 +public class CaptchaVerificationReqVO {
  10 +
  11 + // ========== 图片验证码相关 ==========
  12 + @Schema(description = "验证码,验证码开启时,需要传递", requiredMode = Schema.RequiredMode.REQUIRED,
  13 + example = "PfcH6mgr8tpXuMWFjvW6YVaqrswIuwmWI5dsVZSg7sGpWtDCUbHuDEXl3cFB1+VvCC/rAkSwK8Fad52FSuncVg==")
  14 + @NotEmpty(message = "验证码不能为空", groups = CodeEnableGroup.class)
  15 + private String captchaVerification;
  16 +
  17 + /**
  18 + * 开启验证码的 Group
  19 + */
  20 + public interface CodeEnableGroup {
  21 + }
  22 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java
... ... @@ -52,7 +52,7 @@ public interface AdminAuthService {
52 52 * @param reqVO 登录信息
53 53 * @return 登录结果
54 54 */
55   - AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO) ;
  55 + AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO);
56 56  
57 57 /**
58 58 * 社交快捷登录,使用 code 授权码
... ... @@ -78,4 +78,11 @@ public interface AdminAuthService {
78 78 */
79 79 AuthLoginRespVO register(AuthRegisterReqVO createReqVO);
80 80  
  81 + /**
  82 + * 重置密码
  83 + *
  84 + * @param reqVO 验证码信息
  85 + */
  86 + void resetPassword(AuthResetPasswordReqVO reqVO);
  87 +
81 88 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
... ... @@ -8,9 +8,17 @@ import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
8 8 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
9 9 import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
10 10 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
  11 +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
11 12 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
12 13 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
13   -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*;
  14 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthLoginReqVO;
  15 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthLoginRespVO;
  16 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthRegisterReqVO;
  17 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthResetPasswordReqVO;
  18 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthSmsLoginReqVO;
  19 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthSmsSendReqVO;
  20 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthSocialLoginReqVO;
  21 +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.CaptchaVerificationReqVO;
14 22 import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
15 23 import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
16 24 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
... ... @@ -22,6 +30,7 @@ import cn.iocoder.yudao.module.system.service.logger.LoginLogService;
22 30 import cn.iocoder.yudao.module.system.service.member.MemberService;
23 31 import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService;
24 32 import cn.iocoder.yudao.module.system.service.social.SocialUserService;
  33 +import cn.iocoder.yudao.module.system.service.tenant.TenantService;
25 34 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
26 35 import com.google.common.annotations.VisibleForTesting;
27 36 import com.xingyuv.captcha.model.common.ResponseModel;
... ... @@ -32,12 +41,20 @@ import jakarta.validation.Validator;
32 41 import lombok.extern.slf4j.Slf4j;
33 42 import org.springframework.beans.factory.annotation.Value;
34 43 import org.springframework.stereotype.Service;
  44 +import org.springframework.transaction.annotation.Transactional;
35 45  
36 46 import java.util.Objects;
37 47  
38 48 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
39 49 import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
40   -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
  50 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.AUTH_LOGIN_BAD_CREDENTIALS;
  51 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.AUTH_LOGIN_CAPTCHA_CODE_ERROR;
  52 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.AUTH_LOGIN_USER_DISABLED;
  53 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.AUTH_MOBILE_NOT_EXISTS;
  54 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.AUTH_REGISTER_CAPTCHA_CODE_ERROR;
  55 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.AUTH_THIRD_LOGIN_NOT_BIND;
  56 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_MOBILE_NOT_EXISTS;
  57 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS;
41 58  
42 59 /**
43 60 * Auth Service 实现类
... ... @@ -64,6 +81,8 @@ public class AdminAuthServiceImpl implements AdminAuthService {
64 81 private CaptchaService captchaService;
65 82 @Resource
66 83 private SmsCodeApi smsCodeApi;
  84 + @Resource
  85 + private TenantService tenantService;
67 86  
68 87 /**
69 88 * 验证码的开关,默认为 true
... ... @@ -111,6 +130,13 @@ public class AdminAuthServiceImpl implements AdminAuthService {
111 130  
112 131 @Override
113 132 public void sendSmsCode(AuthSmsSendReqVO reqVO) {
  133 + // 如果是重置密码场景,需要校验图形验证码是否正确
  134 + if (Objects.equals(SmsSceneEnum.ADMIN_MEMBER_RESET_PASSWORD.getScene(), reqVO.getScene())) {
  135 + ResponseModel response = doValidateCaptcha(reqVO);
  136 + if (!response.isSuccess()) {
  137 + throw exception(AUTH_REGISTER_CAPTCHA_CODE_ERROR, response.getRepMsg());
  138 + }
  139 + }
114 140 // 登录场景,验证是否存在
115 141 if (userService.getUserByMobile(reqVO.getMobile()) == null) {
116 142 throw exception(AUTH_MOBILE_NOT_EXISTS);
... ... @@ -174,16 +200,8 @@ public class AdminAuthServiceImpl implements AdminAuthService {
174 200  
175 201 @VisibleForTesting
176 202 void validateCaptcha(AuthLoginReqVO reqVO) {
177   - // 如果验证码关闭,则不进行校验
178   - if (!captchaEnable) {
179   - return;
180   - }
  203 + ResponseModel response = doValidateCaptcha(reqVO);
181 204 // 校验验证码
182   - ValidationUtils.validate(validator, reqVO, AuthLoginReqVO.CodeEnableGroup.class);
183   - CaptchaVO captchaVO = new CaptchaVO();
184   - captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification());
185   - ResponseModel response = captchaService.verification(captchaVO);
186   - // 验证不通过
187 205 if (!response.isSuccess()) {
188 206 // 创建登录失败日志(验证码不正确)
189 207 createLoginLog(null, reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME, LoginResultEnum.CAPTCHA_CODE_ERROR);
... ... @@ -191,6 +209,17 @@ public class AdminAuthServiceImpl implements AdminAuthService {
191 209 }
192 210 }
193 211  
  212 + private ResponseModel doValidateCaptcha(CaptchaVerificationReqVO reqVO) {
  213 + // 如果验证码关闭,则不进行校验
  214 + if (!captchaEnable) {
  215 + return ResponseModel.success();
  216 + }
  217 + ValidationUtils.validate(validator, reqVO, CaptchaVerificationReqVO.CodeEnableGroup.class);
  218 + CaptchaVO captchaVO = new CaptchaVO();
  219 + captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification());
  220 + return captchaService.verification(captchaVO);
  221 + }
  222 +
194 223 private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) {
195 224 // 插入登陆日志
196 225 createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS);
... ... @@ -261,19 +290,28 @@ public class AdminAuthServiceImpl implements AdminAuthService {
261 290  
262 291 @VisibleForTesting
263 292 void validateCaptcha(AuthRegisterReqVO reqVO) {
264   - // 如果验证码关闭,则不进行校验
265   - if (!captchaEnable) {
266   - return;
267   - }
268   - // 校验验证码
269   - ValidationUtils.validate(validator, reqVO, AuthLoginReqVO.CodeEnableGroup.class);
270   - CaptchaVO captchaVO = new CaptchaVO();
271   - captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification());
272   - ResponseModel response = captchaService.verification(captchaVO);
  293 + ResponseModel response = doValidateCaptcha(reqVO);
273 294 // 验证不通过
274 295 if (!response.isSuccess()) {
275 296 throw exception(AUTH_REGISTER_CAPTCHA_CODE_ERROR, response.getRepMsg());
276 297 }
277 298 }
278 299  
  300 + @Override
  301 + @Transactional(rollbackFor = Exception.class)
  302 + public void resetPassword(AuthResetPasswordReqVO reqVO) {
  303 + AdminUserDO userByMobile = userService.getUserByMobile(reqVO.getMobile());
  304 + if (userByMobile == null) {
  305 + throw exception(USER_MOBILE_NOT_EXISTS);
  306 + }
  307 +
  308 + smsCodeApi.useSmsCode(new SmsCodeUseReqDTO()
  309 + .setCode(reqVO.getCode())
  310 + .setMobile(reqVO.getMobile())
  311 + .setScene(SmsSceneEnum.ADMIN_MEMBER_RESET_PASSWORD.getScene())
  312 + .setUsedIp(getClientIP())
  313 + );
  314 +
  315 + userService.updateUserPassword(userByMobile.getId(), reqVO.getPassword());
  316 + }
279 317 }
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java
... ... @@ -296,7 +296,8 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
296 296 @Test
297 297 public void testValidateCaptcha_constraintViolationException() {
298 298 // 准备参数
299   - AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class).setCaptchaVerification(null);
  299 + AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
  300 + reqVO.setCaptchaVerification(null);
300 301  
301 302 // mock 验证码打开
302 303 ReflectUtil.setFieldValue(authService, "captchaEnable", true);
... ...