Commit 2f5afdb5c946bae3a489b1cb5772f495b07b8971
1 parent
5de6a8bd
✨ Member:增加使用微信小程序 code 绑定手机号
Showing
6 changed files
with
70 additions
and
24 deletions
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); |