Commit 2f5afdb5c946bae3a489b1cb5772f495b07b8971

Authored by YunaiV
1 parent 5de6a8bd

✨ Member:增加使用微信小程序 code 绑定手机号

yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthWeixinMiniAppLoginReqVO.java
@@ -15,11 +15,11 @@ import jakarta.validation.constraints.NotEmpty; @@ -15,11 +15,11 @@ import jakarta.validation.constraints.NotEmpty;
15 @Builder 15 @Builder
16 public class AppAuthWeixinMiniAppLoginReqVO { 16 public class AppAuthWeixinMiniAppLoginReqVO {
17 17
18 - @Schema(description = "手机 code,小程序通过 wx.getPhoneNumber 方法获得", requiredMode = Schema.RequiredMode.REQUIRED, example = "hello") 18 + @Schema(description = "手机 code,小程序通过 wx.getPhoneNumber 方法获得", requiredMode = Schema.RequiredMode.REQUIRED, example = "hello")
19 @NotEmpty(message = "手机 code 不能为空") 19 @NotEmpty(message = "手机 code 不能为空")
20 private String phoneCode; 20 private String phoneCode;
21 21
22 - @Schema(description = "登录 code,小程序通过 wx.login 方法获得", requiredMode = Schema.RequiredMode.REQUIRED, example = "word") 22 + @Schema(description = "登录 code,小程序通过 wx.login 方法获得", requiredMode = Schema.RequiredMode.REQUIRED, example = "word")
23 @NotEmpty(message = "登录 code 不能为空") 23 @NotEmpty(message = "登录 code 不能为空")
24 private String loginCode; 24 private String loginCode;
25 25
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppMemberUserController.java
@@ -57,6 +57,14 @@ public class AppMemberUserController { @@ -57,6 +57,14 @@ public class AppMemberUserController {
57 return success(true); 57 return success(true);
58 } 58 }
59 59
  60 + @PutMapping("/update-mobile-by-weixin")
  61 + @Operation(summary = "基于微信小程序的授权码,修改用户手机")
  62 + @PreAuthenticated
  63 + public CommonResult<Boolean> updateUserMobileByWeixin(@RequestBody @Valid AppMemberUserUpdateMobileByWeixinReqVO reqVO) {
  64 + userService.updateUserMobileByWeixin(getLoginUserId(), reqVO);
  65 + return success(true);
  66 + }
  67 +
60 @PutMapping("/update-password") 68 @PutMapping("/update-password")
61 @Operation(summary = "修改用户密码", description = "用户修改密码时使用") 69 @Operation(summary = "修改用户密码", description = "用户修改密码时使用")
62 @PreAuthenticated 70 @PreAuthenticated
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateMobileByWeixinReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.member.controller.app.user.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 = "用户 APP - 基于微信小程序的授权码,修改手机 Request VO")
  8 +@Data
  9 +public class AppMemberUserUpdateMobileByWeixinReqVO {
  10 +
  11 + @Schema(description = "手机 code,小程序通过 wx.getPhoneNumber 方法获得",
  12 + requiredMode = Schema.RequiredMode.REQUIRED, example = "hello")
  13 + @NotEmpty(message = "手机 code 不能为空")
  14 + private String code;
  15 +
  16 +}
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateMobileReqVO.java
@@ -14,9 +14,6 @@ import jakarta.validation.constraints.Pattern; @@ -14,9 +14,6 @@ import jakarta.validation.constraints.Pattern;
14 14
15 @Schema(description = "用户 APP - 修改手机 Request VO") 15 @Schema(description = "用户 APP - 修改手机 Request VO")
16 @Data 16 @Data
17 -@NoArgsConstructor  
18 -@AllArgsConstructor  
19 -@Builder  
20 public class AppMemberUserUpdateMobileReqVO { 17 public class AppMemberUserUpdateMobileReqVO {
21 18
22 @Schema(description = "手机验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 19 @Schema(description = "手机验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@@ -25,14 +22,13 @@ public class AppMemberUserUpdateMobileReqVO { @@ -25,14 +22,13 @@ public class AppMemberUserUpdateMobileReqVO {
25 @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") 22 @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
26 private String code; 23 private String code;
27 24
28 - @Schema(description = "手机号",requiredMode = Schema.RequiredMode.REQUIRED,example = "15823654487") 25 + @Schema(description = "手机号",requiredMode = Schema.RequiredMode.REQUIRED, example = "15823654487")
29 @NotBlank(message = "手机号不能为空") 26 @NotBlank(message = "手机号不能为空")
30 @Length(min = 8, max = 11, message = "手机号码长度为 8-11 位") 27 @Length(min = 8, max = 11, message = "手机号码长度为 8-11 位")
31 @Mobile 28 @Mobile
32 private String mobile; 29 private String mobile;
33 30
34 - @Schema(description = "原手机验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")  
35 - @NotEmpty(message = "原手机验证码不能为空") 31 + @Schema(description = "原手机验证码", example = "1024")
36 @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位") 32 @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
37 @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") 33 @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
38 private String oldCode; 34 private String oldCode;
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java
@@ -5,10 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -5,10 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.validation.Mobile; 5 import cn.iocoder.yudao.framework.common.validation.Mobile;
6 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO; 6 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO;
7 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO; 7 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO;
8 -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserResetPasswordReqVO;  
9 -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateMobileReqVO;  
10 -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdatePasswordReqVO;  
11 -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateReqVO; 8 +import cn.iocoder.yudao.module.member.controller.app.user.vo.*;
12 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; 9 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
13 10
14 import jakarta.validation.Valid; 11 import jakarta.validation.Valid;
@@ -94,7 +91,7 @@ public interface MemberUserService { @@ -94,7 +91,7 @@ public interface MemberUserService {
94 void updateUser(Long userId, AppMemberUserUpdateReqVO reqVO); 91 void updateUser(Long userId, AppMemberUserUpdateReqVO reqVO);
95 92
96 /** 93 /**
97 - * 【会员】修改手机 94 + * 【会员】修改手机,基于手机验证码
98 * 95 *
99 * @param userId 用户编号 96 * @param userId 用户编号
100 * @param reqVO 请求信息 97 * @param reqVO 请求信息
@@ -102,6 +99,14 @@ public interface MemberUserService { @@ -102,6 +99,14 @@ public interface MemberUserService {
102 void updateUserMobile(Long userId, AppMemberUserUpdateMobileReqVO reqVO); 99 void updateUserMobile(Long userId, AppMemberUserUpdateMobileReqVO reqVO);
103 100
104 /** 101 /**
  102 + * 【会员】修改手机,基于微信小程序的授权码
  103 + *
  104 + * @param userId 用户编号
  105 + * @param reqVO 请求信息
  106 + */
  107 + void updateUserMobileByWeixin(Long userId, AppMemberUserUpdateMobileByWeixinReqVO reqVO);
  108 +
  109 + /**
105 * 【会员】修改密码 110 * 【会员】修改密码
106 * 111 *
107 * @param userId 用户编号 112 * @param userId 用户编号
@@ -181,4 +186,5 @@ public interface MemberUserService { @@ -181,4 +186,5 @@ public interface MemberUserService {
181 * @return 更新结果 186 * @return 更新结果
182 */ 187 */
183 boolean updateUserPoint(Long userId, Integer point); 188 boolean updateUserPoint(Long userId, Integer point);
  189 +
184 } 190 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java
@@ -2,16 +2,15 @@ package cn.iocoder.yudao.module.member.service.user; @@ -2,16 +2,15 @@ package cn.iocoder.yudao.module.member.service.user;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.collection.ListUtil; 4 import cn.hutool.core.collection.ListUtil;
  5 +import cn.hutool.core.lang.Assert;
5 import cn.hutool.core.util.*; 6 import cn.hutool.core.util.*;
6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  8 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
9 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO; 11 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO;
10 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO; 12 import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO;
11 -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserResetPasswordReqVO;  
12 -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateMobileReqVO;  
13 -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdatePasswordReqVO;  
14 -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateReqVO; 13 +import cn.iocoder.yudao.module.member.controller.app.user.vo.*;
15 import cn.iocoder.yudao.module.member.convert.auth.AuthConvert; 14 import cn.iocoder.yudao.module.member.convert.auth.AuthConvert;
16 import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert; 15 import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert;
17 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; 16 import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
@@ -19,6 +18,8 @@ import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper; @@ -19,6 +18,8 @@ import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
19 import cn.iocoder.yudao.module.member.mq.producer.user.MemberUserProducer; 18 import cn.iocoder.yudao.module.member.mq.producer.user.MemberUserProducer;
20 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; 19 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
21 import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; 20 import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
  21 +import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
  22 +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
22 import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; 23 import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
23 import com.google.common.annotations.VisibleForTesting; 24 import com.google.common.annotations.VisibleForTesting;
24 import lombok.extern.slf4j.Slf4j; 25 import lombok.extern.slf4j.Slf4j;
@@ -55,6 +56,9 @@ public class MemberUserServiceImpl implements MemberUserService { @@ -55,6 +56,9 @@ public class MemberUserServiceImpl implements MemberUserService {
55 private SmsCodeApi smsCodeApi; 56 private SmsCodeApi smsCodeApi;
56 57
57 @Resource 58 @Resource
  59 + private SocialClientApi socialClientApi;
  60 +
  61 + @Resource
58 private PasswordEncoder passwordEncoder; 62 private PasswordEncoder passwordEncoder;
59 63
60 @Resource 64 @Resource
@@ -145,23 +149,39 @@ public class MemberUserServiceImpl implements MemberUserService { @@ -145,23 +149,39 @@ public class MemberUserServiceImpl implements MemberUserService {
145 @Override 149 @Override
146 @Transactional(rollbackFor = Exception.class) 150 @Transactional(rollbackFor = Exception.class)
147 public void updateUserMobile(Long userId, AppMemberUserUpdateMobileReqVO reqVO) { 151 public void updateUserMobile(Long userId, AppMemberUserUpdateMobileReqVO reqVO) {
148 - // 检测用户是否存在 152 + // 1.1 检测用户是否存在
149 MemberUserDO user = validateUserExists(userId); 153 MemberUserDO user = validateUserExists(userId);
150 - // 校验新手机是否已经被绑定 154 + // 1.2 校验新手机是否已经被绑定
151 validateMobileUnique(null, reqVO.getMobile()); 155 validateMobileUnique(null, reqVO.getMobile());
152 156
153 - // 校验旧手机和旧验证码  
154 - smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(user.getMobile()).setCode(reqVO.getOldCode())  
155 - .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP()));  
156 - // 使用新验证码 157 + // 2.1 校验旧手机和旧验证码
  158 + // 补充说明:从安全性来说,老手机也校验 oldCode 验证码会更安全。但是由于 uni-app 商城界面暂时没做,所以这里不强制校验
  159 + if (StrUtil.isNotEmpty(reqVO.getOldCode())) {
  160 + smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(user.getMobile()).setCode(reqVO.getOldCode())
  161 + .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP()));
  162 + }
  163 + // 2.2 使用新验证码
157 smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(reqVO.getMobile()).setCode(reqVO.getCode()) 164 smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(reqVO.getMobile()).setCode(reqVO.getCode())
158 .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP())); 165 .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP()));
159 166
160 - // 更新用户手机 167 + // 3. 更新用户手机
161 memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(reqVO.getMobile()).build()); 168 memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(reqVO.getMobile()).build());
162 } 169 }
163 170
164 @Override 171 @Override
  172 + public void updateUserMobileByWeixin(Long userId, AppMemberUserUpdateMobileByWeixinReqVO reqVO) {
  173 + // 1.1 获得对应的手机号信息
  174 + SocialWxPhoneNumberInfoRespDTO phoneNumberInfo = socialClientApi.getWxMaPhoneNumberInfo(
  175 + UserTypeEnum.MEMBER.getValue(), reqVO.getCode());
  176 + Assert.notNull(phoneNumberInfo, "获得手机信息失败,结果为空");
  177 + // 1.2 校验新手机是否已经被绑定
  178 + validateMobileUnique(userId, phoneNumberInfo.getPhoneNumber());
  179 +
  180 + // 2. 更新用户手机
  181 + memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(phoneNumberInfo.getPhoneNumber()).build());
  182 + }
  183 +
  184 + @Override
165 public void updateUserPassword(Long userId, AppMemberUserUpdatePasswordReqVO reqVO) { 185 public void updateUserPassword(Long userId, AppMemberUserUpdatePasswordReqVO reqVO) {
166 // 检测用户是否存在 186 // 检测用户是否存在
167 MemberUserDO user = validateUserExists(userId); 187 MemberUserDO user = validateUserExists(userId);