Commit 678e2def97dbaf84a5287efc762b97f23e7593ec

Authored by 芋道源码
Committed by Gitee
2 parents 9d6a0469 cb2ba6da

!60 用户中心修改密码,修改手机功能重做,发送短信 拓展设置对应模板编号

Merge pull request !60 from 宋天/feature/user-register
Showing 20 changed files with 234 additions and 291 deletions
yudao-admin-server/src/main/resources/application-local.yaml
@@ -55,6 +55,19 @@ spring: @@ -55,6 +55,19 @@ spring:
55 username: root 55 username: root
56 password: 123456 56 password: 123456
57 57
  58 + activiti:
  59 + #1.false:默认值,activiti启动时,对比数据库表中保存的版本,如果不匹配。将抛出异常
  60 + #2.true:启动时会对数据库中所有表进行更新操作,如果表存在,不做处理,反之,自动创建表
  61 + #3.create_drop:启动时自动创建表,关闭时自动删除表
  62 + #4.drop_create:启动时,删除旧表,再创建新表
  63 + database-schema-update: true
  64 + #activiti7默认不生成历史信息表,需手动设置开启
  65 + db-history-used: true
  66 + check-process-definitions: true
  67 + #full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数
  68 + history-level: full
  69 +
  70 +
58 # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 71 # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
59 redis: 72 redis:
60 host: 127.0.0.1 # 地址 73 host: 127.0.0.1 # 地址
yudao-user-server/pom.xml
@@ -90,15 +90,6 @@ @@ -90,15 +90,6 @@
90 <artifactId>yudao-spring-boot-starter-test</artifactId> 90 <artifactId>yudao-spring-boot-starter-test</artifactId>
91 <scope>test</scope> 91 <scope>test</scope>
92 </dependency> 92 </dependency>
93 -  
94 - <!-- TODO @宋天:junit 已经在 yudao-spring-boot-starter-test 啦,不用在引入哈 -->  
95 - <!--单元测试相关-->  
96 - <dependency>  
97 - <groupId>junit</groupId>  
98 - <artifactId>junit</artifactId>  
99 - <scope>test</scope>  
100 - </dependency>  
101 -  
102 <!-- 工具类相关 --> 93 <!-- 工具类相关 -->
103 94
104 </dependencies> 95 </dependencies>
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/SysUserProfileController.java
@@ -35,9 +35,6 @@ public class SysUserProfileController { @@ -35,9 +35,6 @@ public class SysUserProfileController {
35 @Resource 35 @Resource
36 private MbrUserService userService; 36 private MbrUserService userService;
37 37
38 - @Resource  
39 - private SysSmsCodeService smsCodeService;  
40 -  
41 @PutMapping("/update-nickname") 38 @PutMapping("/update-nickname")
42 @ApiOperation("修改用户昵称") 39 @ApiOperation("修改用户昵称")
43 @PreAuthenticated 40 @PreAuthenticated
@@ -68,10 +65,6 @@ public class SysUserProfileController { @@ -68,10 +65,6 @@ public class SysUserProfileController {
68 @ApiOperation(value = "修改用户手机") 65 @ApiOperation(value = "修改用户手机")
69 @PreAuthenticated 66 @PreAuthenticated
70 public CommonResult<Boolean> updateMobile(@RequestBody @Valid MbrUserUpdateMobileReqVO reqVO) { 67 public CommonResult<Boolean> updateMobile(@RequestBody @Valid MbrUserUpdateMobileReqVO reqVO) {
71 - // 校验验证码  
72 - // TODO @宋天:统一到 userService.updateMobile 方法里  
73 - smsCodeService.useSmsCode(reqVO.getMobile(),SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), reqVO.getCode(),getClientIP());  
74 -  
75 userService.updateMobile(getLoginUserId(), reqVO); 68 userService.updateMobile(getLoginUserId(), reqVO);
76 return success(true); 69 return success(true);
77 } 70 }
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/vo/MbrUserUpdateMobileReqVO.java
1 package cn.iocoder.yudao.userserver.modules.member.controller.user.vo; 1 package cn.iocoder.yudao.userserver.modules.member.controller.user.vo;
2 2
  3 +import cn.iocoder.yudao.framework.common.validation.Mobile;
3 import io.swagger.annotations.ApiModel; 4 import io.swagger.annotations.ApiModel;
4 import io.swagger.annotations.ApiModelProperty; 5 import io.swagger.annotations.ApiModelProperty;
5 import lombok.AllArgsConstructor; 6 import lombok.AllArgsConstructor;
@@ -27,9 +28,21 @@ public class MbrUserUpdateMobileReqVO { @@ -27,9 +28,21 @@ public class MbrUserUpdateMobileReqVO {
27 28
28 @ApiModelProperty(value = "手机号",required = true,example = "15823654487") 29 @ApiModelProperty(value = "手机号",required = true,example = "15823654487")
29 @NotBlank(message = "手机号不能为空") 30 @NotBlank(message = "手机号不能为空")
30 - // TODO @宋天:手机校验,直接使用 @Mobile 哈  
31 @Length(min = 8, max = 11, message = "手机号码长度为 8-11 位") 31 @Length(min = 8, max = 11, message = "手机号码长度为 8-11 位")
32 - @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式错误") 32 + @Mobile
33 private String mobile; 33 private String mobile;
34 34
  35 + @ApiModelProperty(value = "原手机验证码", required = true, example = "1024")
  36 + @NotEmpty(message = "原手机验证码不能为空")
  37 + @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
  38 + @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
  39 + private String oldCode;
  40 +
  41 + @ApiModelProperty(value = "原手机号",required = true,example = "15823654487")
  42 + @NotBlank(message = "手机号不能为空")
  43 + @Length(min = 8, max = 11, message = "手机号码长度为 8-11 位")
  44 + @Mobile
  45 + private String oldMobile;
  46 +
  47 +
35 } 48 }
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/impl/MbrUserServiceImpl.java
@@ -4,17 +4,20 @@ import cn.hutool.core.io.IoUtil; @@ -4,17 +4,20 @@ import cn.hutool.core.io.IoUtil;
4 import cn.hutool.core.util.IdUtil; 4 import cn.hutool.core.util.IdUtil;
5 import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService; 5 import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService;
6 import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; 6 import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
7 -import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO;  
8 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  8 +import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO;
9 import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO; 9 import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO;
10 import cn.iocoder.yudao.userserver.modules.member.convert.user.UserProfileConvert; 10 import cn.iocoder.yudao.userserver.modules.member.convert.user.UserProfileConvert;
11 import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper; 11 import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper;
12 import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService; 12 import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
13 -import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService; 13 +import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO;
  14 +import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
  15 +import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
14 import com.google.common.annotations.VisibleForTesting; 16 import com.google.common.annotations.VisibleForTesting;
15 import lombok.extern.slf4j.Slf4j; 17 import lombok.extern.slf4j.Slf4j;
16 import org.springframework.security.crypto.password.PasswordEncoder; 18 import org.springframework.security.crypto.password.PasswordEncoder;
17 import org.springframework.stereotype.Service; 19 import org.springframework.stereotype.Service;
  20 +import org.springframework.transaction.annotation.Transactional;
18 21
19 import javax.annotation.Resource; 22 import javax.annotation.Resource;
20 import javax.validation.Valid; 23 import javax.validation.Valid;
@@ -22,7 +25,9 @@ import java.io.InputStream; @@ -22,7 +25,9 @@ import java.io.InputStream;
22 import java.util.Date; 25 import java.util.Date;
23 26
24 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 27 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  28 +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
25 import static cn.iocoder.yudao.userserver.modules.member.enums.MbrErrorCodeConstants.USER_NOT_EXISTS; 29 import static cn.iocoder.yudao.userserver.modules.member.enums.MbrErrorCodeConstants.USER_NOT_EXISTS;
  30 +import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants.USER_SMS_CODE_IS_UNUSED;
26 31
27 /** 32 /**
28 * User Service 实现类 33 * User Service 实现类
@@ -44,7 +49,8 @@ public class MbrUserServiceImpl implements MbrUserService { @@ -44,7 +49,8 @@ public class MbrUserServiceImpl implements MbrUserService {
44 private PasswordEncoder passwordEncoder; 49 private PasswordEncoder passwordEncoder;
45 50
46 @Resource 51 @Resource
47 - private SysAuthService sysAuthService; 52 + private SysSmsCodeService smsCodeService;
  53 +
48 54
49 @Override 55 @Override
50 public MbrUserDO getUserByMobile(String mobile) { 56 public MbrUserDO getUserByMobile(String mobile) {
@@ -120,15 +126,23 @@ public class MbrUserServiceImpl implements MbrUserService { @@ -120,15 +126,23 @@ public class MbrUserServiceImpl implements MbrUserService {
120 } 126 }
121 127
122 @Override 128 @Override
  129 + @Transactional(rollbackFor = Exception.class)
123 public void updateMobile(Long userId, MbrUserUpdateMobileReqVO reqVO) { 130 public void updateMobile(Long userId, MbrUserUpdateMobileReqVO reqVO) {
124 // 检测用户是否存在 131 // 检测用户是否存在
125 - MbrUserDO userDO = checkUserExists(userId);  
126 - // 检测手机与验证码是否匹配  
127 - // TODO @宋天:修改手机的时候。应该要校验,老手机 + 老手机 code;新手机 + 新手机 code  
128 - sysAuthService.checkIfMobileMatchCodeAndDeleteCode(userDO.getMobile(),reqVO.getCode()); 132 + checkUserExists(userId);
  133 +
  134 + // 校验旧手机和旧验证码
  135 + SysSmsCodeDO sysSmsCodeDO = smsCodeService.checkCodeIsExpired(reqVO.getOldMobile(), reqVO.getOldCode(), SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene());
  136 + // 判断旧code是否未被使用,如果是,抛出异常
  137 + if (Boolean.FALSE.equals(sysSmsCodeDO.getUsed())){
  138 + throw exception(USER_SMS_CODE_IS_UNUSED);
  139 + }
  140 +
  141 + // 使用新验证码
  142 + smsCodeService.useSmsCode(reqVO.getMobile(), SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), reqVO.getCode(),getClientIP());
  143 +
129 // 更新用户手机 144 // 更新用户手机
130 - // TODO @宋天:更新的时候,单独创建对象。直接全量更新,会可能导致属性覆盖。可以看看打印出来的 SQL 哈  
131 - userDO.setMobile(reqVO.getMobile()); 145 + MbrUserDO userDO = MbrUserDO.builder().id(userId).mobile(reqVO.getMobile()).build();
132 userMapper.updateById(userDO); 146 userMapper.updateById(userDO);
133 } 147 }
134 148
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.java
@@ -5,10 +5,8 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; @@ -5,10 +5,8 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
5 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; 6 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
7 import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*; 7 import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*;
8 -import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;  
9 import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService; 8 import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService;
10 import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; 9 import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
11 -import com.alibaba.fastjson.JSON;  
12 import io.swagger.annotations.Api; 10 import io.swagger.annotations.Api;
13 import io.swagger.annotations.ApiImplicitParam; 11 import io.swagger.annotations.ApiImplicitParam;
14 import io.swagger.annotations.ApiImplicitParams; 12 import io.swagger.annotations.ApiImplicitParams;
@@ -18,7 +16,6 @@ import org.springframework.validation.annotation.Validated; @@ -18,7 +16,6 @@ import org.springframework.validation.annotation.Validated;
18 import org.springframework.web.bind.annotation.*; 16 import org.springframework.web.bind.annotation.*;
19 17
20 import javax.annotation.Resource; 18 import javax.annotation.Resource;
21 -import javax.servlet.http.HttpServletRequest;  
22 import javax.validation.Valid; 19 import javax.validation.Valid;
23 20
24 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 21 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -57,19 +54,12 @@ public class SysAuthController { @@ -57,19 +54,12 @@ public class SysAuthController {
57 } 54 }
58 55
59 @PostMapping("/send-sms-code") 56 @PostMapping("/send-sms-code")
60 - @ApiOperation(value = "发送手机验证码",notes = "不检测该手机号是否已被注册") 57 + @ApiOperation(value = "发送手机验证码")
61 public CommonResult<Boolean> sendSmsCode(@RequestBody @Valid SysAuthSendSmsReqVO reqVO) { 58 public CommonResult<Boolean> sendSmsCode(@RequestBody @Valid SysAuthSendSmsReqVO reqVO) {
62 smsCodeService.sendSmsCode(reqVO.getMobile(), reqVO.getScene(), getClientIP()); 59 smsCodeService.sendSmsCode(reqVO.getMobile(), reqVO.getScene(), getClientIP());
63 return success(true); 60 return success(true);
64 } 61 }
65 62
66 - @PostMapping("/send-sms-new-code")  
67 - @ApiOperation(value = "发送手机验证码",notes = "检测该手机号是否已被注册,用于修改手机时使用")  
68 - public CommonResult<Boolean> sendSmsNewCode(@RequestBody @Valid SysAuthSendSmsReqVO reqVO) {  
69 - smsCodeService.sendSmsNewCode(reqVO);  
70 - return success(true);  
71 - }  
72 -  
73 @GetMapping("/send-sms-code-login") 63 @GetMapping("/send-sms-code-login")
74 @ApiOperation(value = "向已登录用户发送验证码",notes = "修改手机时验证原手机号使用") 64 @ApiOperation(value = "向已登录用户发送验证码",notes = "修改手机时验证原手机号使用")
75 public CommonResult<Boolean> sendSmsCodeLogin() { 65 public CommonResult<Boolean> sendSmsCodeLogin() {
@@ -93,14 +83,6 @@ public class SysAuthController { @@ -93,14 +83,6 @@ public class SysAuthController {
93 return success(true); 83 return success(true);
94 } 84 }
95 85
96 - @PostMapping("/check-sms-code")  
97 - @ApiOperation(value = "校验验证码是否正确")  
98 - @PreAuthenticated  
99 - public CommonResult<Boolean> checkSmsCode(@RequestBody @Valid SysAuthSmsLoginReqVO reqVO) {  
100 - // TODO @宋天:check 的时候,不应该使用 useSmsCode 哈,这样验证码就直接被使用了。另外,check 开头的方法,更多是校验的逻辑,不会有 update 数据的动作。这点,在方法命名上,也是要注意的  
101 - smsCodeService.useSmsCode(reqVO.getMobile(),SysSmsSceneEnum.CHECK_CODE_BY_SMS.getScene(),reqVO.getCode(),getClientIP());  
102 - return success(true);  
103 - }  
104 86
105 // ========== 社交登录相关 ========== 87 // ========== 社交登录相关 ==========
106 88
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthResetPasswordReqVO.java
1 package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo; 1 package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
2 2
  3 +import cn.iocoder.yudao.framework.common.validation.Mobile;
3 import io.swagger.annotations.ApiModel; 4 import io.swagger.annotations.ApiModel;
4 import io.swagger.annotations.ApiModelProperty; 5 import io.swagger.annotations.ApiModelProperty;
5 import lombok.AllArgsConstructor; 6 import lombok.AllArgsConstructor;
@@ -8,6 +9,7 @@ import lombok.Data; @@ -8,6 +9,7 @@ import lombok.Data;
8 import lombok.NoArgsConstructor; 9 import lombok.NoArgsConstructor;
9 import org.hibernate.validator.constraints.Length; 10 import org.hibernate.validator.constraints.Length;
10 11
  12 +import javax.validation.constraints.NotBlank;
11 import javax.validation.constraints.NotEmpty; 13 import javax.validation.constraints.NotEmpty;
12 import javax.validation.constraints.Pattern; 14 import javax.validation.constraints.Pattern;
13 15
@@ -29,4 +31,8 @@ public class MbrAuthResetPasswordReqVO { @@ -29,4 +31,8 @@ public class MbrAuthResetPasswordReqVO {
29 @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") 31 @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
30 private String code; 32 private String code;
31 33
  34 + @ApiModelProperty(value = "手机号",required = true,example = "15878962356")
  35 + @NotBlank(message = "手机号不能为空")
  36 + @Mobile
  37 + private String mobile;
32 } 38 }
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthCheckCodeReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.framework.common.validation.Mobile;
  5 +import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
  6 +import io.swagger.annotations.ApiModel;
  7 +import io.swagger.annotations.ApiModelProperty;
  8 +import lombok.AllArgsConstructor;
  9 +import lombok.Builder;
  10 +import lombok.Data;
  11 +import lombok.NoArgsConstructor;
  12 +import org.hibernate.validator.constraints.Length;
  13 +
  14 +import javax.validation.constraints.NotBlank;
  15 +import javax.validation.constraints.NotNull;
  16 +import javax.validation.constraints.Pattern;
  17 +
  18 +@ApiModel("校验验证码 Request VO")
  19 +@Data
  20 +@NoArgsConstructor
  21 +@AllArgsConstructor
  22 +@Builder
  23 +public class SysAuthCheckCodeReqVO {
  24 +
  25 + @ApiModelProperty(value = "手机号", example = "15601691234")
  26 + @NotBlank(message = "手机号不能为空")
  27 + @Mobile
  28 + private String mobile;
  29 +
  30 + @ApiModelProperty(value = "手机验证码", required = true, example = "1024")
  31 + @NotBlank(message = "手机验证码不能为空")
  32 + @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
  33 + @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
  34 + private String code;
  35 +
  36 + @ApiModelProperty(value = "发送场景", example = "1", notes = "对应 MbrSmsSceneEnum 枚举")
  37 + @NotNull(message = "发送场景不能为空")
  38 + @InEnum(SysSmsSceneEnum.class)
  39 + private Integer scene;
  40 +}
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/dal/mysql/sms/SysSmsCodeMapper.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
5 import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO; 5 import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO;
6 import org.apache.ibatis.annotations.Mapper; 6 import org.apache.ibatis.annotations.Mapper;
7 7
  8 +
8 @Mapper 9 @Mapper
9 public interface SysSmsCodeMapper extends BaseMapperX<SysSmsCodeDO> { 10 public interface SysSmsCodeMapper extends BaseMapperX<SysSmsCodeDO> {
10 11
@@ -13,14 +14,15 @@ public interface SysSmsCodeMapper extends BaseMapperX&lt;SysSmsCodeDO&gt; { @@ -13,14 +14,15 @@ public interface SysSmsCodeMapper extends BaseMapperX&lt;SysSmsCodeDO&gt; {
13 * 14 *
14 * @param mobile 手机号 15 * @param mobile 手机号
15 * @param scene 发送场景,选填 16 * @param scene 发送场景,选填
  17 + * @param code 验证码 选填
16 * @return 手机验证码 18 * @return 手机验证码
17 */ 19 */
18 - default SysSmsCodeDO selectLastByMobile(String mobile, Integer scene) { 20 + default SysSmsCodeDO selectLastByMobile(String mobile,String code,Integer scene) {
19 return selectOne(new QueryWrapperX<SysSmsCodeDO>() 21 return selectOne(new QueryWrapperX<SysSmsCodeDO>()
20 .eq("mobile", mobile) 22 .eq("mobile", mobile)
21 .eqIfPresent("scene", scene) 23 .eqIfPresent("scene", scene)
  24 + .eqIfPresent("code", code)
22 .orderByDesc("id") 25 .orderByDesc("id")
23 .last("LIMIT 1")); 26 .last("LIMIT 1"));
24 } 27 }
25 -  
26 } 28 }
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/SysErrorCodeConstants.java
@@ -24,9 +24,9 @@ public interface SysErrorCodeConstants { @@ -24,9 +24,9 @@ public interface SysErrorCodeConstants {
24 ErrorCode USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1005001004, "超过每日短信发送数量"); 24 ErrorCode USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1005001004, "超过每日短信发送数量");
25 ErrorCode USER_SMS_CODE_SEND_TOO_FAST = new ErrorCode(1005001005, "短信发送过于频率"); 25 ErrorCode USER_SMS_CODE_SEND_TOO_FAST = new ErrorCode(1005001005, "短信发送过于频率");
26 ErrorCode USER_SMS_CODE_IS_EXISTS = new ErrorCode(1005001006, "手机号已被使用"); 26 ErrorCode USER_SMS_CODE_IS_EXISTS = new ErrorCode(1005001006, "手机号已被使用");
  27 + ErrorCode USER_SMS_CODE_IS_UNUSED = new ErrorCode(1005001006, "验证码未被使用");
27 28
28 // ========== 用户模块 1005002000 ========== 29 // ========== 用户模块 1005002000 ==========
29 ErrorCode USER_NOT_EXISTS = new ErrorCode(1005002001, "用户不存在"); 30 ErrorCode USER_NOT_EXISTS = new ErrorCode(1005002001, "用户不存在");
30 - ErrorCode USER_CODE_FAILED = new ErrorCode(1005002002, "验证码不匹配");  
31 ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1005002003, "密码校验失败"); 31 ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1005002003, "密码校验失败");
32 } 32 }
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/sms/SysSmsSceneEnum.java
@@ -15,15 +15,26 @@ import java.util.Arrays; @@ -15,15 +15,26 @@ import java.util.Arrays;
15 @AllArgsConstructor 15 @AllArgsConstructor
16 public enum SysSmsSceneEnum implements IntArrayValuable { 16 public enum SysSmsSceneEnum implements IntArrayValuable {
17 17
18 - LOGIN_BY_SMS(1, "手机号登陆"),  
19 - CHANGE_MOBILE_BY_SMS(2, "更换手机号"),  
20 - FORGET_MOBILE_BY_SMS(3, "忘记密码"),  
21 - CHECK_CODE_BY_SMS(4, "审核验证码"), 18 + LOGIN_BY_SMS(1,SysSmsTemplateCodeConstants.USER_SMS_LOGIN, "手机号登陆"),
  19 + CHANGE_MOBILE_BY_SMS(2,SysSmsTemplateCodeConstants.USER_SMS_UPDATE_MOBILE, "更换手机号"),
  20 + FORGET_MOBILE_BY_SMS(3,SysSmsTemplateCodeConstants.USER_SMS_RESET_PASSWORD, "忘记密码"),
22 ; 21 ;
23 22
24 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SysSmsSceneEnum::getScene).toArray(); 23 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SysSmsSceneEnum::getScene).toArray();
25 24
  25 + /**
  26 + * 验证那场景编号
  27 + */
26 private final Integer scene; 28 private final Integer scene;
  29 +
  30 + /**
  31 + * 模版编码
  32 + */
  33 + private final String code;
  34 +
  35 + /**
  36 + * 描述
  37 + */
27 private final String name; 38 private final String name;
28 39
29 @Override 40 @Override
@@ -31,4 +42,13 @@ public enum SysSmsSceneEnum implements IntArrayValuable { @@ -31,4 +42,13 @@ public enum SysSmsSceneEnum implements IntArrayValuable {
31 return ARRAYS; 42 return ARRAYS;
32 } 43 }
33 44
  45 + public static String getCodeByScene(Integer scene){
  46 + for (SysSmsSceneEnum value : values()) {
  47 + if (value.getScene().equals(scene)){
  48 + return value.getCode();
  49 + }
  50 + }
  51 + return null;
  52 + }
  53 +
34 } 54 }
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/sms/SysSmsTemplateCodeConstants.java
@@ -12,4 +12,14 @@ public interface SysSmsTemplateCodeConstants { @@ -12,4 +12,14 @@ public interface SysSmsTemplateCodeConstants {
12 */ 12 */
13 String USER_SMS_LOGIN = "user-sms-login"; 13 String USER_SMS_LOGIN = "user-sms-login";
14 14
  15 + /**
  16 + * 用户忘记密码
  17 + */
  18 + String USER_SMS_RESET_PASSWORD = "user-sms-reset-password";
  19 +
  20 + /**
  21 + * 用户更新手机号
  22 + */
  23 + String USER_SMS_UPDATE_MOBILE = "user-sms-update-mobile";
  24 +
15 } 25 }
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/SysAuthService.java
@@ -68,18 +68,11 @@ public interface SysAuthService extends SecurityAuthFrameworkService { @@ -68,18 +68,11 @@ public interface SysAuthService extends SecurityAuthFrameworkService {
68 * @param userId 用户id 68 * @param userId 用户id
69 * @param userReqVO 用户请求实体类 69 * @param userReqVO 用户请求实体类
70 */ 70 */
71 - void updatePassword(Long userId, @Valid MbrAuthUpdatePasswordReqVO userReqVO); 71 + void updatePassword(Long userId,MbrAuthUpdatePasswordReqVO userReqVO);
72 72
73 /** 73 /**
74 * 忘记密码 74 * 忘记密码
75 * @param userReqVO 用户请求实体类 75 * @param userReqVO 用户请求实体类
76 */ 76 */
77 void resetPassword(MbrAuthResetPasswordReqVO userReqVO); 77 void resetPassword(MbrAuthResetPasswordReqVO userReqVO);
78 -  
79 - /**  
80 - * 检测手机与验证码是否匹配  
81 - * @param phone 手机号  
82 - * @param code 验证码  
83 - */  
84 - void checkIfMobileMatchCodeAndDeleteCode(String phone,String code);  
85 } 78 }
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/impl/SysAuthServiceImpl.java
@@ -40,7 +40,6 @@ import org.springframework.stereotype.Service; @@ -40,7 +40,6 @@ import org.springframework.stereotype.Service;
40 import org.springframework.transaction.annotation.Transactional; 40 import org.springframework.transaction.annotation.Transactional;
41 41
42 import javax.annotation.Resource; 42 import javax.annotation.Resource;
43 -import javax.validation.Valid;  
44 import java.util.List; 43 import java.util.List;
45 import java.util.Objects; 44 import java.util.Objects;
46 45
@@ -285,46 +284,30 @@ public class SysAuthServiceImpl implements SysAuthService { @@ -285,46 +284,30 @@ public class SysAuthServiceImpl implements SysAuthService {
285 } 284 }
286 285
287 @Override 286 @Override
288 - public void updatePassword(Long userId, @Valid MbrAuthUpdatePasswordReqVO reqVO) { 287 + public void updatePassword(Long userId,MbrAuthUpdatePasswordReqVO reqVO) {
289 // 检验旧密码 288 // 检验旧密码
290 MbrUserDO userDO = checkOldPassword(userId, reqVO.getOldPassword()); 289 MbrUserDO userDO = checkOldPassword(userId, reqVO.getOldPassword());
291 290
292 // 更新用户密码 291 // 更新用户密码
293 - // TODO @宋天:不要更新整个对象哈  
294 - userDO.setPassword(passwordEncoder.encode(reqVO.getPassword()));  
295 - userMapper.updateById(userDO); 292 + MbrUserDO mbrUserDO = MbrUserDO.builder().build();
  293 + mbrUserDO.setId(userDO.getId());
  294 + mbrUserDO.setPassword(passwordEncoder.encode(reqVO.getPassword()));
  295 + userMapper.updateById(mbrUserDO);
296 } 296 }
297 297
298 @Override 298 @Override
299 public void resetPassword(MbrAuthResetPasswordReqVO reqVO) { 299 public void resetPassword(MbrAuthResetPasswordReqVO reqVO) {
300 - // 根据验证码取出手机号,并查询用户  
301 - String mobile = stringRedisTemplate.opsForValue().get(reqVO.getCode());  
302 - MbrUserDO userDO = userMapper.selectByMobile(mobile);  
303 - if (userDO == null){  
304 - throw exception(USER_NOT_EXISTS);  
305 - }  
306 - // TODO @芋艿 这一步没必要检验验证码与手机是否匹配,因为是根据验证码去redis中查找手机号,然后根据手机号查询用户  
307 - // 也就是说 即便黑客以其他方式将验证码发送到自己手机上,最终还是会根据手机号查询用户然后进行重置密码的操作,不存在安全问题 300 + // 检验用户是否存在
  301 + MbrUserDO userDO = checkUserIfExists(reqVO.getMobile());
308 302
309 - // TODO @宋天:这块微信在讨论下哈~~~  
310 -  
311 - // 校验验证码  
312 - smsCodeService.useSmsCode(userDO.getMobile(), SysSmsSceneEnum.FORGET_MOBILE_BY_SMS.getScene(), reqVO.getCode(),getClientIP()); 303 + // 使用验证码
  304 + smsCodeService.useSmsCode(reqVO.getMobile(),SysSmsSceneEnum.FORGET_MOBILE_BY_SMS.getScene(),reqVO.getCode(),getClientIP());
313 305
314 // 更新密码 306 // 更新密码
315 - userDO.setPassword(passwordEncoder.encode(reqVO.getPassword()));  
316 - userMapper.updateById(userDO);  
317 - }  
318 -  
319 - @Override  
320 - public void checkIfMobileMatchCodeAndDeleteCode(String phone, String code) {  
321 - // 检验用户手机与验证码是否匹配  
322 - String mobile = stringRedisTemplate.opsForValue().get(code);  
323 - if (!phone.equals(mobile)){  
324 - throw exception(USER_CODE_FAILED);  
325 - }  
326 - // 销毁redis中此验证码  
327 - stringRedisTemplate.delete(code); 307 + MbrUserDO mbrUserDO = MbrUserDO.builder().build();
  308 + mbrUserDO.setId(userDO.getId());
  309 + mbrUserDO.setPassword(passwordEncoder.encode(reqVO.getPassword()));
  310 + userMapper.updateById(mbrUserDO);
328 } 311 }
329 312
330 /** 313 /**
@@ -347,6 +330,15 @@ public class SysAuthServiceImpl implements SysAuthService { @@ -347,6 +330,15 @@ public class SysAuthServiceImpl implements SysAuthService {
347 return user; 330 return user;
348 } 331 }
349 332
  333 + public MbrUserDO checkUserIfExists(String mobile) {
  334 + MbrUserDO user = userMapper.selectByMobile(mobile);
  335 + if (user == null) {
  336 + throw exception(USER_NOT_EXISTS);
  337 + }
  338 + return user;
  339 + }
  340 +
  341 +
350 private void createLogoutLog(Long userId, String username) { 342 private void createLogoutLog(Long userId, String username) {
351 SysLoginLogCreateReqDTO reqDTO = new SysLoginLogCreateReqDTO(); 343 SysLoginLogCreateReqDTO reqDTO = new SysLoginLogCreateReqDTO();
352 reqDTO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType()); 344 reqDTO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType());
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/SysSmsCodeService.java
@@ -2,7 +2,7 @@ package cn.iocoder.yudao.userserver.modules.system.service.sms; @@ -2,7 +2,7 @@ package cn.iocoder.yudao.userserver.modules.system.service.sms;
2 2
3 import cn.iocoder.yudao.framework.common.exception.ServiceException; 3 import cn.iocoder.yudao.framework.common.exception.ServiceException;
4 import cn.iocoder.yudao.framework.common.validation.Mobile; 4 import cn.iocoder.yudao.framework.common.validation.Mobile;
5 -import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSendSmsReqVO; 5 +import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO;
6 import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; 6 import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
7 7
8 /** 8 /**
@@ -15,33 +15,38 @@ public interface SysSmsCodeService { @@ -15,33 +15,38 @@ public interface SysSmsCodeService {
15 /** 15 /**
16 * 创建短信验证码,并进行发送 16 * 创建短信验证码,并进行发送
17 * 17 *
18 - * @param mobile 手机号  
19 - * @param scene 发送场景 {@link SysSmsSceneEnum} 18 + * @param mobile 手机号
  19 + * @param scene 发送场景 {@link SysSmsSceneEnum}
20 * @param createIp 发送 IP 20 * @param createIp 发送 IP
21 */ 21 */
22 void sendSmsCode(@Mobile String mobile, Integer scene, String createIp); 22 void sendSmsCode(@Mobile String mobile, Integer scene, String createIp);
23 23
24 /** 24 /**
25 - * 发送短信验证码,并检测手机号是否已被注册  
26 - * @param reqVO 请求实体  
27 - */  
28 - void sendSmsNewCode(SysAuthSendSmsReqVO reqVO);  
29 -  
30 - /**  
31 * 验证短信验证码,并进行使用 25 * 验证短信验证码,并进行使用
32 * 如果正确,则将验证码标记成已使用 26 * 如果正确,则将验证码标记成已使用
33 * 如果错误,则抛出 {@link ServiceException} 异常 27 * 如果错误,则抛出 {@link ServiceException} 异常
34 * 28 *
35 * @param mobile 手机号 29 * @param mobile 手机号
36 - * @param scene 发送场景  
37 - * @param code 验证码 30 + * @param scene 发送场景
  31 + * @param code 验证码
38 * @param usedIp 使用 IP 32 * @param usedIp 使用 IP
39 */ 33 */
40 void useSmsCode(@Mobile String mobile, Integer scene, String code, String usedIp); 34 void useSmsCode(@Mobile String mobile, Integer scene, String code, String usedIp);
41 35
42 /** 36 /**
43 * 根据用户id发送验证码 37 * 根据用户id发送验证码
  38 + *
44 * @param userId 用户id 39 * @param userId 用户id
45 */ 40 */
46 void sendSmsCodeLogin(Long userId); 41 void sendSmsCodeLogin(Long userId);
  42 +
  43 +
  44 + /**
  45 + * 检查验证码是否有效
  46 + * @param mobile 手机
  47 + * @param code 验证码
  48 + * @param scene 使用场景
  49 + * @return 验证码记录
  50 + */
  51 + SysSmsCodeDO checkCodeIsExpired(String mobile, String code, Integer scene);
47 } 52 }
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/impl/SysSmsCodeServiceImpl.java
@@ -4,20 +4,16 @@ import cn.hutool.core.map.MapUtil; @@ -4,20 +4,16 @@ import cn.hutool.core.map.MapUtil;
4 import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; 4 import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
5 import cn.iocoder.yudao.coreservice.modules.system.service.sms.SysSmsCoreService; 5 import cn.iocoder.yudao.coreservice.modules.system.service.sms.SysSmsCoreService;
6 import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService; 6 import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
7 -import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSendSmsReqVO;  
8 import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO; 7 import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO;
9 import cn.iocoder.yudao.userserver.modules.system.dal.mysql.sms.SysSmsCodeMapper; 8 import cn.iocoder.yudao.userserver.modules.system.dal.mysql.sms.SysSmsCodeMapper;
10 import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; 9 import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
11 -import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsTemplateCodeConstants;  
12 import cn.iocoder.yudao.userserver.modules.system.framework.sms.SmsCodeProperties; 10 import cn.iocoder.yudao.userserver.modules.system.framework.sms.SmsCodeProperties;
13 import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; 11 import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
14 -import org.springframework.data.redis.core.StringRedisTemplate;  
15 import org.springframework.stereotype.Service; 12 import org.springframework.stereotype.Service;
16 import org.springframework.validation.annotation.Validated; 13 import org.springframework.validation.annotation.Validated;
17 14
18 import javax.annotation.Resource; 15 import javax.annotation.Resource;
19 import java.util.Date; 16 import java.util.Date;
20 -import java.util.concurrent.TimeUnit;  
21 17
22 import static cn.hutool.core.util.RandomUtil.randomInt; 18 import static cn.hutool.core.util.RandomUtil.randomInt;
23 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -33,11 +29,6 @@ import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConst @@ -33,11 +29,6 @@ import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConst
33 @Validated 29 @Validated
34 public class SysSmsCodeServiceImpl implements SysSmsCodeService { 30 public class SysSmsCodeServiceImpl implements SysSmsCodeService {
35 31
36 - /**  
37 - * 验证码 + 手机 在redis中存储的有效时间,单位:分钟  
38 - */  
39 - private static final Long CODE_TIME = 10L;  
40 -  
41 @Resource 32 @Resource
42 private SmsCodeProperties smsCodeProperties; 33 private SmsCodeProperties smsCodeProperties;
43 34
@@ -50,40 +41,38 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService { @@ -50,40 +41,38 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
50 @Resource 41 @Resource
51 private SysSmsCoreService smsCoreService; 42 private SysSmsCoreService smsCoreService;
52 43
53 - @Resource  
54 - private StringRedisTemplate stringRedisTemplate;  
55 -  
56 @Override 44 @Override
57 public void sendSmsCode(String mobile, Integer scene, String createIp) { 45 public void sendSmsCode(String mobile, Integer scene, String createIp) {
58 // 创建验证码 46 // 创建验证码
59 String code = this.createSmsCode(mobile, scene, createIp); 47 String code = this.createSmsCode(mobile, scene, createIp);
  48 +
  49 + // 获取发送模板
  50 + String codeTemplate = SysSmsSceneEnum.getCodeByScene(scene);
  51 +
  52 + // 如果是更换手机号发送验证码,则需要检测手机号是否被注册
  53 + if (SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene().equals(scene)){
  54 + this.checkMobileIsRegister(mobile,scene);
  55 + }
  56 +
60 // 发送验证码 57 // 发送验证码
61 - // TODO @宋天:这里可以拓展下 SysSmsSceneEnum,支持设置对应的短信模板编号(不同场景的短信文案是不同的)、是否要校验手机号已经注册。这样 Controller 就可以收口成一个接口了。相当于说,不同场景,不同策略  
62 - smsCoreService.sendSingleSmsToMember(mobile, null, SysSmsTemplateCodeConstants.USER_SMS_LOGIN, 58 + smsCoreService.sendSingleSmsToMember(mobile, null, codeTemplate,
63 MapUtil.of("code", code)); 59 MapUtil.of("code", code));
64 -  
65 - // 存储手机号与验证码到redis,用于标记  
66 - // TODO @宋天:SysSmsCodeDO 表应该足够,无需增加额外的 redis 存储哇  
67 - // TODO @宋天:Redis 相关的操作,不要散落到业务层,而是写一个它对应的 RedisDAO。这样,实现业务与技术的解耦  
68 - // TODO @宋天:直接使用 code 作为 key,会存在 2 个问题:1)code 可能会冲突,多个手机号之间;2)缺少前缀。例如说 sms_code_${code}  
69 - stringRedisTemplate.opsForValue().set(code,mobile,CODE_TIME, TimeUnit.MINUTES);  
70 } 60 }
71 61
72 - @Override  
73 - public void sendSmsNewCode(SysAuthSendSmsReqVO reqVO) { 62 + public void checkMobileIsRegister(String mobile, Integer scene) {
74 // 检测手机号是否已被使用 63 // 检测手机号是否已被使用
75 - MbrUserDO userByMobile = mbrUserService.getUserByMobile(reqVO.getMobile()); 64 + MbrUserDO userByMobile = mbrUserService.getUserByMobile(mobile);
76 if (userByMobile != null){ 65 if (userByMobile != null){
77 throw exception(USER_SMS_CODE_IS_EXISTS); 66 throw exception(USER_SMS_CODE_IS_EXISTS);
78 } 67 }
79 68
80 // 发送短信 69 // 发送短信
81 - this.sendSmsCode(reqVO.getMobile(),reqVO.getScene(),getClientIP()); 70 + this.sendSmsCode(mobile,scene,getClientIP());
82 } 71 }
83 72
84 private String createSmsCode(String mobile, Integer scene, String ip) { 73 private String createSmsCode(String mobile, Integer scene, String ip) {
85 // 校验是否可以发送验证码,不用筛选场景 74 // 校验是否可以发送验证码,不用筛选场景
86 - SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null); 75 + SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null,null);
87 if (lastSmsCode != null) { 76 if (lastSmsCode != null) {
88 if (lastSmsCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限。 77 if (lastSmsCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限。
89 throw exception(USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY); 78 throw exception(USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY);
@@ -107,21 +96,14 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService { @@ -107,21 +96,14 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
107 96
108 @Override 97 @Override
109 public void useSmsCode(String mobile, Integer scene, String code, String usedIp) { 98 public void useSmsCode(String mobile, Integer scene, String code, String usedIp) {
110 - // 校验验证码  
111 - SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, scene);  
112 - if (lastSmsCode == null) { // 若验证码不存在,抛出异常  
113 - throw exception(USER_SMS_CODE_NOT_FOUND);  
114 - }  
115 - if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime()  
116 - >= smsCodeProperties.getExpireTimes().toMillis()) { // 验证码已过期  
117 - throw exception(USER_SMS_CODE_EXPIRED);  
118 - }  
119 - if (lastSmsCode.getUsed()) { // 验证码已使用 99 +
  100 + // 检测验证码是否有效
  101 + SysSmsCodeDO lastSmsCode = this.checkCodeIsExpired(mobile, code, scene);
  102 +
  103 + // 判断验证码是否已被使用
  104 + if (Boolean.TRUE.equals(lastSmsCode.getUsed())) {
120 throw exception(USER_SMS_CODE_USED); 105 throw exception(USER_SMS_CODE_USED);
121 } 106 }
122 - if (!lastSmsCode.getCode().equals(code)) {  
123 - throw exception(USER_SMS_CODE_NOT_CORRECT);  
124 - }  
125 107
126 // 使用验证码 108 // 使用验证码
127 smsCodeMapper.updateById(SysSmsCodeDO.builder().id(lastSmsCode.getId()) 109 smsCodeMapper.updateById(SysSmsCodeDO.builder().id(lastSmsCode.getId())
@@ -138,4 +120,22 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService { @@ -138,4 +120,22 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
138 this.sendSmsCode(user.getMobile(),SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), getClientIP()); 120 this.sendSmsCode(user.getMobile(),SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), getClientIP());
139 } 121 }
140 122
  123 + @Override
  124 + public SysSmsCodeDO checkCodeIsExpired(String mobile, String code, Integer scene) {
  125 + // 校验验证码
  126 + SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile,code,scene);
  127 +
  128 + // 若验证码不存在,抛出异常
  129 + if (lastSmsCode == null) {
  130 + throw exception(USER_SMS_CODE_NOT_FOUND);
  131 + }
  132 + if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime()
  133 + >= smsCodeProperties.getExpireTimes().toMillis()) { // 验证码已过期
  134 + throw exception(USER_SMS_CODE_EXPIRED);
  135 + }
  136 +
  137 + return lastSmsCode;
  138 +
  139 + }
  140 +
141 } 141 }
yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/member/controller/SysUserProfileControllerTest.java deleted
1 -package cn.iocoder.yudao.userserver.modules.member.controller;  
2 -  
3 -import cn.iocoder.yudao.userserver.modules.member.controller.user.SysUserProfileController;  
4 -import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;  
5 -import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;  
6 -import org.junit.Before;  
7 -import org.junit.Ignore;  
8 -import org.junit.Test;  
9 -import org.mockito.InjectMocks;  
10 -import org.mockito.Mock;  
11 -import org.mockito.MockitoAnnotations;  
12 -import org.springframework.http.MediaType;  
13 -import org.springframework.test.web.servlet.MockMvc;  
14 -import org.springframework.test.web.servlet.result.MockMvcResultHandlers;  
15 -import org.springframework.test.web.servlet.setup.MockMvcBuilders;  
16 -  
17 -  
18 -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;  
19 -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;  
20 -  
21 -/**  
22 - * {@link SysUserProfileController} 的单元测试类  
23 - *  
24 - * @author 宋天  
25 - */  
26 -// TODO @宋天:controller 的单测可以不写哈,因为收益太低了。未来我们做 qa 自动化测试  
27 -public class SysUserProfileControllerTest {  
28 -  
29 - private MockMvc mockMvc;  
30 -  
31 - @InjectMocks  
32 - private SysUserProfileController sysUserProfileController;  
33 -  
34 - @Mock  
35 - private MbrUserService userService;  
36 -  
37 - @Mock  
38 - private SysSmsCodeService smsCodeService;  
39 -  
40 - @Before // TODO @宋天:使用 junit5 哈  
41 - public void setup() {  
42 - // 初始化  
43 - MockitoAnnotations.openMocks(this);  
44 -  
45 - // 构建mvc环境  
46 - mockMvc = MockMvcBuilders.standaloneSetup(sysUserProfileController).build();  
47 - }  
48 -  
49 - @Test  
50 - @Ignore  
51 - public void testUpdateMobile_success() throws Exception {  
52 - //模拟接口调用  
53 - this.mockMvc.perform(post("/system/user/profile/update-mobile")  
54 - .contentType(MediaType.APPLICATION_JSON_VALUE)  
55 - .content("{\"mobile\":\"15819844280\",\"code\":\"123456\"}}"))  
56 - .andExpect(status().isOk())  
57 - .andDo(MockMvcResultHandlers.print());  
58 -// TODO @宋天:方法的结尾,不用空行哈  
59 - }  
60 -  
61 -  
62 -}  
yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/member/service/MbrUserServiceImplTest.java
1 package cn.iocoder.yudao.userserver.modules.member.service; 1 package cn.iocoder.yudao.userserver.modules.member.service;
2 2
  3 +import cn.hutool.core.util.RandomUtil;
3 import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService; 4 import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService;
4 import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; 5 import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
@@ -10,8 +11,7 @@ import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfo @@ -10,8 +11,7 @@ import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfo
10 import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO; 11 import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpdateMobileReqVO;
11 import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper; 12 import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper;
12 import cn.iocoder.yudao.userserver.modules.member.service.user.impl.MbrUserServiceImpl; 13 import cn.iocoder.yudao.userserver.modules.member.service.user.impl.MbrUserServiceImpl;
13 -import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSendSmsReqVO;  
14 -import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; 14 +import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO;
15 import cn.iocoder.yudao.userserver.modules.system.service.auth.impl.SysAuthServiceImpl; 15 import cn.iocoder.yudao.userserver.modules.system.service.auth.impl.SysAuthServiceImpl;
16 import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; 16 import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
17 import org.junit.jupiter.api.Test; 17 import org.junit.jupiter.api.Test;
@@ -27,6 +27,7 @@ import java.util.function.Consumer; @@ -27,6 +27,7 @@ import java.util.function.Consumer;
27 import static cn.hutool.core.util.RandomUtil.*; 27 import static cn.hutool.core.util.RandomUtil.*;
28 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; 28 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
29 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; 29 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
  30 +import static cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS;
30 import static org.junit.jupiter.api.Assertions.assertEquals; 31 import static org.junit.jupiter.api.Assertions.assertEquals;
31 import static org.mockito.Mockito.*; 32 import static org.mockito.Mockito.*;
32 33
@@ -115,23 +116,24 @@ public class MbrUserServiceImplTest extends BaseDbAndRedisUnitTest { @@ -115,23 +116,24 @@ public class MbrUserServiceImplTest extends BaseDbAndRedisUnitTest {
115 userDO.setMobile(oldMobile); 116 userDO.setMobile(oldMobile);
116 userMapper.insert(userDO); 117 userMapper.insert(userDO);
117 118
118 - // 验证旧手机  
119 - sysSmsCodeService.sendSmsCodeLogin(userDO.getId());  
120 119
121 - // 验证旧手机验证码是否正确  
122 - sysSmsCodeService.useSmsCode(oldMobile,SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(),"123","1.1.1.1");  
123 - // 验证新手机  
124 - SysAuthSendSmsReqVO smsReqVO = new SysAuthSendSmsReqVO();  
125 - smsReqVO.setMobile(oldMobile);  
126 - smsReqVO.setScene(SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene());  
127 - sysSmsCodeService.sendSmsNewCode(smsReqVO); 120 + // 旧手机和旧验证码
  121 + SysSmsCodeDO codeDO = new SysSmsCodeDO();
  122 + String oldCode = RandomUtil.randomString(4);
  123 + codeDO.setMobile(userDO.getMobile());
  124 + codeDO.setCode(oldCode);
  125 + codeDO.setScene(CHANGE_MOBILE_BY_SMS.getScene());
  126 + codeDO.setUsed(Boolean.FALSE);
  127 + when(sysSmsCodeService.checkCodeIsExpired(codeDO.getMobile(),codeDO.getCode(),codeDO.getScene())).thenReturn(codeDO);
128 128
129 // 更新手机号 129 // 更新手机号
130 String newMobile = randomNumbers(11); 130 String newMobile = randomNumbers(11);
131 - String code = randomNumbers(4); 131 + String newCode = randomNumbers(4);
132 MbrUserUpdateMobileReqVO reqVO = new MbrUserUpdateMobileReqVO(); 132 MbrUserUpdateMobileReqVO reqVO = new MbrUserUpdateMobileReqVO();
133 reqVO.setMobile(newMobile); 133 reqVO.setMobile(newMobile);
134 - reqVO.setCode(code); 134 + reqVO.setCode(newCode);
  135 + reqVO.setOldMobile(oldMobile);
  136 + reqVO.setOldCode(oldCode);
135 mbrUserService.updateMobile(userDO.getId(),reqVO); 137 mbrUserService.updateMobile(userDO.getId(),reqVO);
136 138
137 assertEquals(mbrUserService.getUser(userDO.getId()).getMobile(),newMobile); 139 assertEquals(mbrUserService.getUser(userDO.getId()).getMobile(),newMobile);
yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/system/controller/SysAuthControllerTest.java deleted
1 -package cn.iocoder.yudao.userserver.modules.system.controller;  
2 -  
3 -import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialCoreService;  
4 -import cn.iocoder.yudao.userserver.modules.system.controller.auth.SysAuthController;  
5 -import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService;  
6 -import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;  
7 -import org.junit.Before;  
8 -import org.junit.Test;  
9 -import org.mockito.InjectMocks;  
10 -import org.mockito.Mock;  
11 -import org.mockito.MockitoAnnotations;  
12 -import org.springframework.http.MediaType;  
13 -import org.springframework.test.web.servlet.MockMvc;  
14 -import org.springframework.test.web.servlet.result.MockMvcResultHandlers;  
15 -import org.springframework.test.web.servlet.setup.MockMvcBuilders;  
16 -  
17 -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;  
18 -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;  
19 -  
20 -/**  
21 - * {@link SysAuthController} 的单元测试类  
22 - *  
23 - * @author 宋天  
24 - */  
25 -public class SysAuthControllerTest {  
26 -  
27 - private MockMvc mockMvc;  
28 -  
29 - @InjectMocks  
30 - private SysAuthController sysAuthController;  
31 -  
32 - @Mock  
33 - private SysAuthService authService;  
34 - @Mock  
35 - private SysSmsCodeService smsCodeService;  
36 - @Mock  
37 - private SysSocialCoreService socialService;  
38 -  
39 - @Before  
40 - public void setup() {  
41 - // 初始化  
42 - MockitoAnnotations.openMocks(this);  
43 -  
44 - // 构建mvc环境  
45 - mockMvc = MockMvcBuilders.standaloneSetup(sysAuthController).build();  
46 - }  
47 -  
48 - @Test  
49 - public void testResetPassword_success() throws Exception {  
50 - // 模拟接口调用  
51 - this.mockMvc.perform(post("/reset-password")  
52 - .contentType(MediaType.APPLICATION_JSON)  
53 - .content("{\"password\":\"1123\",\"code\":\"123456\"}}"))  
54 - .andExpect(status().isOk())  
55 - .andDo(MockMvcResultHandlers.print());  
56 - }  
57 -  
58 - @Test  
59 - public void testUpdatePassword_success() throws Exception {  
60 - // 模拟接口调用  
61 - this.mockMvc.perform(post("/update-password")  
62 - .contentType(MediaType.APPLICATION_JSON)  
63 - .content("{\"password\":\"1123\",\"code\":\"123456\",\"oldPassword\":\"1123\"}}"))  
64 - .andExpect(status().isOk())  
65 - .andDo(MockMvcResultHandlers.print());  
66 - }  
67 -  
68 -}  
yudao-user-server/src/test/java/cn/iocoder/yudao/userserver/modules/system/service/SysAuthServiceTest.java
@@ -23,7 +23,6 @@ import org.springframework.security.authentication.AuthenticationManager; @@ -23,7 +23,6 @@ import org.springframework.security.authentication.AuthenticationManager;
23 import org.springframework.security.crypto.password.PasswordEncoder; 23 import org.springframework.security.crypto.password.PasswordEncoder;
24 24
25 import javax.annotation.Resource; 25 import javax.annotation.Resource;
26 -import java.util.concurrent.TimeUnit;  
27 import java.util.function.Consumer; 26 import java.util.function.Consumer;
28 27
29 import static cn.hutool.core.util.RandomUtil.randomEle; 28 import static cn.hutool.core.util.RandomUtil.randomEle;
@@ -99,17 +98,15 @@ public class SysAuthServiceTest extends BaseDbAndRedisUnitTest { @@ -99,17 +98,15 @@ public class SysAuthServiceTest extends BaseDbAndRedisUnitTest {
99 // 随机验证码 98 // 随机验证码
100 String code = randomNumbers(4); 99 String code = randomNumbers(4);
101 100
102 - MbrAuthResetPasswordReqVO reqVO = MbrAuthResetPasswordReqVO.builder()  
103 - .password(password)  
104 - .code(code)  
105 - .build();  
106 - // 放入code+手机号  
107 - stringRedisTemplate.opsForValue().set(code,userDO.getMobile(),10, TimeUnit.MINUTES);  
108 -  
109 // mock 101 // mock
110 when(passwordEncoder.encode(password)).thenReturn(password); 102 when(passwordEncoder.encode(password)).thenReturn(password);
111 103
112 // 更新用户密码 104 // 更新用户密码
  105 + MbrAuthResetPasswordReqVO reqVO = new MbrAuthResetPasswordReqVO();
  106 + reqVO.setMobile(userDO.getMobile());
  107 + reqVO.setPassword(password);
  108 + reqVO.setCode(code);
  109 +
113 authService.resetPassword(reqVO); 110 authService.resetPassword(reqVO);
114 assertEquals(mbrUserMapper.selectById(userDO.getId()).getPassword(),password); 111 assertEquals(mbrUserMapper.selectById(userDO.getId()).getPassword(),password);
115 } 112 }