Commit 5430cc292e6b1a2a1ec895ff575f5bdaf303c181

Authored by 宋天
2 parents ccb56b3b 08cfe716

Merge remote-tracking branch 'origin/feature/user-register' into feature/user-register

Showing 18 changed files with 156 additions and 226 deletions
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/tool/service/codegen/impl/ToolCodegenEngine.java
... ... @@ -191,7 +191,7 @@ public class ToolCodegenEngine {
191 191 }
192 192  
193 193 private static String javaFilePath(String path) {
194   - return "java/${basePackage}/${table.moduleName}/" + path + ".java";
  194 + return "java/${basePackage}/modules/${table.moduleName}/" + path + ".java";
195 195 }
196 196  
197 197 private static String vueTemplatePath(String path) {
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/tool/service/codegen/impl/ToolCodegenSQLParser.java
... ... @@ -64,10 +64,17 @@ public class ToolCodegenSQLParser {
64 64 private static ToolSchemaTableDO parseTable(SQLCreateTableStatement statement) {
65 65 return ToolSchemaTableDO.builder()
66 66 .tableName(statement.getTableSource().getTableName(true))
67   - .tableComment(((SQLCharExpr) statement.getComment()).getText())
  67 + .tableComment(getCommentText(statement))
68 68 .build();
69 69 }
70 70  
  71 + private static String getCommentText(SQLCreateTableStatement statement) {
  72 + if (statement == null || statement.getComment() == null) {
  73 + return "";
  74 + }
  75 + return ((SQLCharExpr) statement.getComment()).getText();
  76 + }
  77 +
71 78 private static List<ToolSchemaColumnDO> parseColumns(SQLCreateTableStatement statement) {
72 79 List<ToolSchemaColumnDO> columns = new ArrayList<>();
73 80 statement.getTableElementList().forEach(element -> parseColumn(columns, element));
... ...
yudao-user-server/pom.xml
... ... @@ -90,15 +90,6 @@
90 90 <artifactId>yudao-spring-boot-starter-test</artifactId>
91 91 <scope>test</scope>
92 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 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 35 @Resource
36 36 private MbrUserService userService;
37 37  
38   - @Resource
39   - private SysSmsCodeService smsCodeService;
40   -
41 38 @PutMapping("/update-nickname")
42 39 @ApiOperation("修改用户昵称")
43 40 @PreAuthenticated
... ... @@ -68,10 +65,6 @@ public class SysUserProfileController {
68 65 @ApiOperation(value = "修改用户手机")
69 66 @PreAuthenticated
70 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 68 userService.updateMobile(getLoginUserId(), reqVO);
76 69 return success(true);
77 70 }
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/controller/user/vo/MbrUserUpdateMobileReqVO.java
1 1 package cn.iocoder.yudao.userserver.modules.member.controller.user.vo;
2 2  
  3 +import cn.iocoder.yudao.framework.common.validation.Mobile;
3 4 import io.swagger.annotations.ApiModel;
4 5 import io.swagger.annotations.ApiModelProperty;
5 6 import lombok.AllArgsConstructor;
... ... @@ -27,9 +28,21 @@ public class MbrUserUpdateMobileReqVO {
27 28  
28 29 @ApiModelProperty(value = "手机号",required = true,example = "15823654487")
29 30 @NotBlank(message = "手机号不能为空")
30   - // TODO @宋天:手机校验,直接使用 @Mobile 哈
31 31 @Length(min = 8, max = 11, message = "手机号码长度为 8-11 位")
32   - @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式错误")
  32 + @Mobile
33 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
... ... @@ -10,7 +10,10 @@ import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserUpda
10 10 import cn.iocoder.yudao.userserver.modules.member.convert.user.UserProfileConvert;
11 11 import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper;
12 12 import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
  13 +import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO;
  14 +import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
13 15 import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService;
  16 +import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
14 17 import com.google.common.annotations.VisibleForTesting;
15 18 import lombok.extern.slf4j.Slf4j;
16 19 import org.springframework.security.crypto.password.PasswordEncoder;
... ... @@ -22,7 +25,10 @@ import java.io.InputStream;
22 25 import java.util.Date;
23 26  
24 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 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;
  31 +import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants.USER_SMS_CODE_NOT_CORRECT;
26 32  
27 33 /**
28 34 * User Service 实现类
... ... @@ -46,6 +52,10 @@ public class MbrUserServiceImpl implements MbrUserService {
46 52 @Resource
47 53 private SysAuthService sysAuthService;
48 54  
  55 + @Resource
  56 + private SysSmsCodeService smsCodeService;
  57 +
  58 +
49 59 @Override
50 60 public MbrUserDO getUserByMobile(String mobile) {
51 61 return userMapper.selectByMobile(mobile);
... ... @@ -122,12 +132,21 @@ public class MbrUserServiceImpl implements MbrUserService {
122 132 @Override
123 133 public void updateMobile(Long userId, MbrUserUpdateMobileReqVO reqVO) {
124 134 // 检测用户是否存在
125   - MbrUserDO userDO = checkUserExists(userId);
126   - // 检测手机与验证码是否匹配
127   - // TODO @宋天:修改手机的时候。应该要校验,老手机 + 老手机 code;新手机 + 新手机 code
128   - sysAuthService.checkIfMobileMatchCodeAndDeleteCode(userDO.getMobile(),reqVO.getCode());
  135 + checkUserExists(userId);
  136 + // 校验验证码,并标记为已使用
  137 + smsCodeService.useSmsCode(reqVO.getMobile(), SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), reqVO.getCode(),getClientIP());
  138 +
  139 + // 检测新手机和旧手机的验证码是否在30分钟内
  140 + SysSmsCodeDO smsOldCodeDO = smsCodeService.checkCodeIsExpired(reqVO.getOldMobile(), reqVO.getOldCode(), SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene());
  141 + SysSmsCodeDO smsNewCodeDO = smsCodeService.checkCodeIsExpired(reqVO.getMobile(), reqVO.getCode(), SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene());
  142 +
  143 + // 判断新旧code是否未被使用,如果是,抛出异常
  144 + if (Boolean.FALSE.equals(smsOldCodeDO.getUsed()) || Boolean.FALSE.equals(smsNewCodeDO.getUsed())){
  145 + throw exception(USER_SMS_CODE_IS_UNUSED);
  146 + }
  147 +
129 148 // 更新用户手机
130   - // TODO @宋天:更新的时候,单独创建对象。直接全量更新,会可能导致属性覆盖。可以看看打印出来的 SQL 哈
  149 + MbrUserDO userDO = MbrUserDO.builder().build();
131 150 userDO.setMobile(reqVO.getMobile());
132 151 userMapper.updateById(userDO);
133 152 }
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.java
... ... @@ -93,14 +93,6 @@ public class SysAuthController {
93 93 return success(true);
94 94 }
95 95  
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 96  
105 97 // ========== 社交登录相关 ==========
106 98  
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthResetPasswordReqVO.java
1 1 package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo;
2 2  
  3 +import cn.iocoder.yudao.framework.common.validation.Mobile;
3 4 import io.swagger.annotations.ApiModel;
4 5 import io.swagger.annotations.ApiModelProperty;
5 6 import lombok.AllArgsConstructor;
... ... @@ -8,6 +9,7 @@ import lombok.Data;
8 9 import lombok.NoArgsConstructor;
9 10 import org.hibernate.validator.constraints.Length;
10 11  
  12 +import javax.validation.constraints.NotBlank;
11 13 import javax.validation.constraints.NotEmpty;
12 14 import javax.validation.constraints.Pattern;
13 15  
... ... @@ -29,4 +31,8 @@ public class MbrAuthResetPasswordReqVO {
29 31 @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
30 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 5 import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO;
6 6 import org.apache.ibatis.annotations.Mapper;
7 7  
  8 +
8 9 @Mapper
9 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 * @param mobile 手机号
15 16 * @param scene 发送场景,选填
  17 + * @param code 验证码 选填
16 18 * @return 手机验证码
17 19 */
18   - default SysSmsCodeDO selectLastByMobile(String mobile, Integer scene) {
  20 + default SysSmsCodeDO selectLastByMobile(String mobile,String code,Integer scene) {
19 21 return selectOne(new QueryWrapperX<SysSmsCodeDO>()
20 22 .eq("mobile", mobile)
21 23 .eqIfPresent("scene", scene)
  24 + .eqIfPresent("code", code)
22 25 .orderByDesc("id")
23 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 24 ErrorCode USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1005001004, "超过每日短信发送数量");
25 25 ErrorCode USER_SMS_CODE_SEND_TOO_FAST = new ErrorCode(1005001005, "短信发送过于频率");
26 26 ErrorCode USER_SMS_CODE_IS_EXISTS = new ErrorCode(1005001006, "手机号已被使用");
  27 + ErrorCode USER_SMS_CODE_IS_UNUSED = new ErrorCode(1005001006, "手机号未被使用");
27 28  
28 29 // ========== 用户模块 1005002000 ==========
29 30 ErrorCode USER_NOT_EXISTS = new ErrorCode(1005002001, "用户不存在");
30   - ErrorCode USER_CODE_FAILED = new ErrorCode(1005002002, "验证码不匹配");
31 31 ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1005002003, "密码校验失败");
32 32 }
... ...
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 68 * @param userId 用户id
69 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 75 * @param userReqVO 用户请求实体类
76 76 */
77 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
... ... @@ -280,46 +280,30 @@ public class SysAuthServiceImpl implements SysAuthService {
280 280 }
281 281  
282 282 @Override
283   - public void updatePassword(Long userId, @Valid MbrAuthUpdatePasswordReqVO reqVO) {
  283 + public void updatePassword(Long userId,MbrAuthUpdatePasswordReqVO reqVO) {
284 284 // 检验旧密码
285 285 MbrUserDO userDO = checkOldPassword(userId, reqVO.getOldPassword());
286 286  
287 287 // 更新用户密码
288   - // TODO @宋天:不要更新整个对象哈
289   - userDO.setPassword(passwordEncoder.encode(reqVO.getPassword()));
290   - userMapper.updateById(userDO);
  288 + MbrUserDO mbrUserDO = MbrUserDO.builder().build();
  289 + mbrUserDO.setId(userDO.getId());
  290 + mbrUserDO.setPassword(passwordEncoder.encode(reqVO.getPassword()));
  291 + userMapper.updateById(mbrUserDO);
291 292 }
292 293  
293 294 @Override
294 295 public void resetPassword(MbrAuthResetPasswordReqVO reqVO) {
295   - // 根据验证码取出手机号,并查询用户
296   - String mobile = stringRedisTemplate.opsForValue().get(reqVO.getCode());
297   - MbrUserDO userDO = userMapper.selectByMobile(mobile);
298   - if (userDO == null){
299   - throw exception(USER_NOT_EXISTS);
300   - }
301   - // TODO @芋艿 这一步没必要检验验证码与手机是否匹配,因为是根据验证码去redis中查找手机号,然后根据手机号查询用户
302   - // 也就是说 即便黑客以其他方式将验证码发送到自己手机上,最终还是会根据手机号查询用户然后进行重置密码的操作,不存在安全问题
  296 + // 检验用户是否存在
  297 + MbrUserDO userDO = checkUserIfExists(reqVO.getMobile());
303 298  
304   - // TODO @宋天:这块微信在讨论下哈~~~
305   -
306   - // 校验验证码
307   - smsCodeService.useSmsCode(userDO.getMobile(), SysSmsSceneEnum.FORGET_MOBILE_BY_SMS.getScene(), reqVO.getCode(),getClientIP());
  299 + // 使用验证码
  300 + smsCodeService.useSmsCode(reqVO.getMobile(),SysSmsSceneEnum.FORGET_MOBILE_BY_SMS.getScene(),reqVO.getCode(),getClientIP());
308 301  
309 302 // 更新密码
310   - userDO.setPassword(passwordEncoder.encode(reqVO.getPassword()));
311   - userMapper.updateById(userDO);
312   - }
313   -
314   - @Override
315   - public void checkIfMobileMatchCodeAndDeleteCode(String phone, String code) {
316   - // 检验用户手机与验证码是否匹配
317   - String mobile = stringRedisTemplate.opsForValue().get(code);
318   - if (!phone.equals(mobile)){
319   - throw exception(USER_CODE_FAILED);
320   - }
321   - // 销毁redis中此验证码
322   - stringRedisTemplate.delete(code);
  303 + MbrUserDO mbrUserDO = MbrUserDO.builder().build();
  304 + mbrUserDO.setId(userDO.getId());
  305 + mbrUserDO.setPassword(passwordEncoder.encode(reqVO.getPassword()));
  306 + userMapper.updateById(mbrUserDO);
323 307 }
324 308  
325 309 /**
... ... @@ -342,6 +326,15 @@ public class SysAuthServiceImpl implements SysAuthService {
342 326 return user;
343 327 }
344 328  
  329 + public MbrUserDO checkUserIfExists(String mobile) {
  330 + MbrUserDO user = userMapper.selectByMobile(mobile);
  331 + if (user == null) {
  332 + throw exception(USER_NOT_EXISTS);
  333 + }
  334 + return user;
  335 + }
  336 +
  337 +
345 338 private void createLogoutLog(Long userId, String username) {
346 339 SysLoginLogCreateReqDTO reqDTO = new SysLoginLogCreateReqDTO();
347 340 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,10 @@ package cn.iocoder.yudao.userserver.modules.system.service.sms;
2 2  
3 3 import cn.iocoder.yudao.framework.common.exception.ServiceException;
4 4 import cn.iocoder.yudao.framework.common.validation.Mobile;
  5 +import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthCheckCodeReqVO;
5 6 import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSendSmsReqVO;
  7 +import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSmsLoginReqVO;
  8 +import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO;
6 9 import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
7 10  
8 11 /**
... ... @@ -44,4 +47,14 @@ public interface SysSmsCodeService {
44 47 * @param userId 用户id
45 48 */
46 49 void sendSmsCodeLogin(Long userId);
  50 +
  51 + /**
  52 + * 检测手机验证码是否有效
  53 + * @param mobile 手机号
  54 + * @param code 验证码
  55 + * @param scene 发送场景 {@link SysSmsSceneEnum}
  56 + * @return SysSmsCodeDO 手机验证码
  57 + */
  58 + SysSmsCodeDO checkCodeIsExpired(String mobile, String code, Integer scene);
  59 +
47 60 }
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/sms/impl/SysSmsCodeServiceImpl.java
... ... @@ -4,6 +4,7 @@ import cn.hutool.core.map.MapUtil;
4 4 import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
5 5 import cn.iocoder.yudao.coreservice.modules.system.service.sms.SysSmsCoreService;
6 6 import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
  7 +import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthCheckCodeReqVO;
7 8 import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSendSmsReqVO;
8 9 import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO;
9 10 import cn.iocoder.yudao.userserver.modules.system.dal.mysql.sms.SysSmsCodeMapper;
... ... @@ -11,13 +12,11 @@ import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
11 12 import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsTemplateCodeConstants;
12 13 import cn.iocoder.yudao.userserver.modules.system.framework.sms.SmsCodeProperties;
13 14 import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
14   -import org.springframework.data.redis.core.StringRedisTemplate;
15 15 import org.springframework.stereotype.Service;
16 16 import org.springframework.validation.annotation.Validated;
17 17  
18 18 import javax.annotation.Resource;
19 19 import java.util.Date;
20   -import java.util.concurrent.TimeUnit;
21 20  
22 21 import static cn.hutool.core.util.RandomUtil.randomInt;
23 22 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
... ... @@ -33,11 +32,6 @@ import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConst
33 32 @Validated
34 33 public class SysSmsCodeServiceImpl implements SysSmsCodeService {
35 34  
36   - /**
37   - * 验证码 + 手机 在redis中存储的有效时间,单位:分钟
38   - */
39   - private static final Long CODE_TIME = 10L;
40   -
41 35 @Resource
42 36 private SmsCodeProperties smsCodeProperties;
43 37  
... ... @@ -50,9 +44,6 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
50 44 @Resource
51 45 private SysSmsCoreService smsCoreService;
52 46  
53   - @Resource
54   - private StringRedisTemplate stringRedisTemplate;
55   -
56 47 @Override
57 48 public void sendSmsCode(String mobile, Integer scene, String createIp) {
58 49 // 创建验证码
... ... @@ -61,12 +52,6 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
61 52 // TODO @宋天:这里可以拓展下 SysSmsSceneEnum,支持设置对应的短信模板编号(不同场景的短信文案是不同的)、是否要校验手机号已经注册。这样 Controller 就可以收口成一个接口了。相当于说,不同场景,不同策略
62 53 smsCoreService.sendSingleSmsToMember(mobile, null, SysSmsTemplateCodeConstants.USER_SMS_LOGIN,
63 54 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 55 }
71 56  
72 57 @Override
... ... @@ -83,7 +68,7 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
83 68  
84 69 private String createSmsCode(String mobile, Integer scene, String ip) {
85 70 // 校验是否可以发送验证码,不用筛选场景
86   - SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null);
  71 + SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null,null);
87 72 if (lastSmsCode != null) {
88 73 if (lastSmsCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限。
89 74 throw exception(USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY);
... ... @@ -108,7 +93,7 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
108 93 @Override
109 94 public void useSmsCode(String mobile, Integer scene, String code, String usedIp) {
110 95 // 校验验证码
111   - SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, scene);
  96 + SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null,scene);
112 97 if (lastSmsCode == null) { // 若验证码不存在,抛出异常
113 98 throw exception(USER_SMS_CODE_NOT_FOUND);
114 99 }
... ... @@ -138,4 +123,21 @@ public class SysSmsCodeServiceImpl implements SysSmsCodeService {
138 123 this.sendSmsCode(user.getMobile(),SysSmsSceneEnum.CHANGE_MOBILE_BY_SMS.getScene(), getClientIP());
139 124 }
140 125  
  126 + @Override
  127 + public SysSmsCodeDO checkCodeIsExpired(String mobile, String code, Integer scene) {
  128 + SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, code, scene);
  129 +
  130 + // 检测验证码是否存在
  131 + if (lastSmsCode == null){
  132 + throw exception(USER_SMS_CODE_EXPIRED);
  133 + }
  134 +
  135 + // 检测验证码是否过期
  136 + if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime()
  137 + >= smsCodeProperties.getExpireTimes().toMillis()) {
  138 + throw exception(USER_SMS_CODE_EXPIRED);
  139 + }
  140 + return lastSmsCode;
  141 + }
  142 +
141 143 }
... ...
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.Test;
8   -import org.mockito.InjectMocks;
9   -import org.mockito.Mock;
10   -import org.mockito.MockitoAnnotations;
11   -import org.springframework.http.MediaType;
12   -import org.springframework.test.web.servlet.MockMvc;
13   -import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
14   -import org.springframework.test.web.servlet.setup.MockMvcBuilders;
15   -
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 SysUserProfileController} 的单元测试类
22   - *
23   - * @author 宋天
24   - */
25   -// TODO @宋天:controller 的单测可以不写哈,因为收益太低了。未来我们做 qa 自动化测试
26   -public class SysUserProfileControllerTest {
27   -
28   - private MockMvc mockMvc;
29   -
30   - @InjectMocks
31   - private SysUserProfileController sysUserProfileController;
32   -
33   - @Mock
34   - private MbrUserService userService;
35   -
36   - @Mock
37   - private SysSmsCodeService smsCodeService;
38   -
39   - @Before // TODO @宋天:使用 junit5 哈
40   - public void setup() {
41   - // 初始化
42   - MockitoAnnotations.openMocks(this);
43   -
44   - // 构建mvc环境
45   - mockMvc = MockMvcBuilders.standaloneSetup(sysUserProfileController).build();
46   - }
47   -
48   - @Test
49   - public void testUpdateMobile_success() throws Exception {
50   - //模拟接口调用
51   - this.mockMvc.perform(post("/system/user/profile/update-mobile")
52   - .contentType(MediaType.APPLICATION_JSON_VALUE)
53   - .content("{\"mobile\":\"15819844280\",\"code\":\"123456\"}}"))
54   - .andExpect(status().isOk())
55   - .andDo(MockMvcResultHandlers.print());
56   -// TODO @宋天:方法的结尾,不用空行哈
57   - }
58   -
59   -
60   -}
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.SysSocialService;
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.http.HttpHeaders.AUTHORIZATION;
18   -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
19   -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
20   -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
21   -
22   -/**
23   - * {@link SysAuthController} 的单元测试类
24   - *
25   - * @author 宋天
26   - */
27   -public class SysAuthControllerTest {
28   -
29   - private MockMvc mockMvc;
30   -
31   - @InjectMocks
32   - private SysAuthController sysAuthController;
33   -
34   - @Mock
35   - private SysAuthService authService;
36   - @Mock
37   - private SysSmsCodeService smsCodeService;
38   - @Mock
39   - private SysSocialService socialService;
40   -
41   -
42   - @Before
43   - public void setup() {
44   - // 初始化
45   - MockitoAnnotations.openMocks(this);
46   -
47   - // 构建mvc环境
48   - mockMvc = MockMvcBuilders.standaloneSetup(sysAuthController).build();
49   - }
50   -
51   - @Test
52   - public void testResetPassword_success() throws Exception {
53   - //模拟接口调用
54   - this.mockMvc.perform(post("/reset-password")
55   - .contentType(MediaType.APPLICATION_JSON)
56   - .content("{\"password\":\"1123\",\"code\":\"123456\"}}"))
57   - .andExpect(status().isOk())
58   - .andDo(MockMvcResultHandlers.print());
59   -
60   - }
61   -
62   - @Test
63   - public void testUpdatePassword_success() throws Exception {
64   - //模拟接口调用
65   - this.mockMvc.perform(post("/update-password")
66   - .contentType(MediaType.APPLICATION_JSON)
67   - .content("{\"password\":\"1123\",\"code\":\"123456\",\"oldPassword\":\"1123\"}}"))
68   - .andExpect(status().isOk())
69   - .andDo(MockMvcResultHandlers.print());
70   -
71   - }
72   -
73   -
74   -
75   -}
更新日志.md
... ... @@ -22,6 +22,7 @@
22 22  
23 23 * 新增用户前台的昵称、头像的修改
24 24 * 修复通知和字典查询权限 Bug
  25 +* 修复代码生成器的 Java 类路径缺少 modules 目录的 Bug
25 26  
26 27 TODO
27 28  
... ...