From 2f5afdb5c946bae3a489b1cb5772f495b07b8971 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 25 Dec 2023 23:33:50 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Member=EF=BC=9A=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=BE=AE=E4=BF=A1=E5=B0=8F=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=20code=20=E7=BB=91=E5=AE=9A=E6=89=8B=E6=9C=BA=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vo/AppAuthWeixinMiniAppLoginReqVO.java | 4 +- .../app/user/AppMemberUserController.java | 8 ++++ ...ppMemberUserUpdateMobileByWeixinReqVO.java | 16 +++++++ .../vo/AppMemberUserUpdateMobileReqVO.java | 8 +--- .../service/user/MemberUserService.java | 16 ++++--- .../service/user/MemberUserServiceImpl.java | 42 ++++++++++++++----- 6 files changed, 70 insertions(+), 24 deletions(-) create mode 100644 yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateMobileByWeixinReqVO.java diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthWeixinMiniAppLoginReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthWeixinMiniAppLoginReqVO.java index fa8724cd0..15adaa8d6 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthWeixinMiniAppLoginReqVO.java +++ b/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; @Builder public class AppAuthWeixinMiniAppLoginReqVO { - @Schema(description = "手机 code,小程序通过 wx.getPhoneNumber 方法获得", requiredMode = Schema.RequiredMode.REQUIRED, example = "hello") + @Schema(description = "手机 code,小程序通过 wx.getPhoneNumber 方法获得", requiredMode = Schema.RequiredMode.REQUIRED, example = "hello") @NotEmpty(message = "手机 code 不能为空") private String phoneCode; - @Schema(description = "登录 code,小程序通过 wx.login 方法获得", requiredMode = Schema.RequiredMode.REQUIRED, example = "word") + @Schema(description = "登录 code,小程序通过 wx.login 方法获得", requiredMode = Schema.RequiredMode.REQUIRED, example = "word") @NotEmpty(message = "登录 code 不能为空") private String loginCode; diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppMemberUserController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppMemberUserController.java index 2c04a6908..91d549fa6 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppMemberUserController.java +++ b/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 { return success(true); } + @PutMapping("/update-mobile-by-weixin") + @Operation(summary = "基于微信小程序的授权码,修改用户手机") + @PreAuthenticated + public CommonResult updateUserMobileByWeixin(@RequestBody @Valid AppMemberUserUpdateMobileByWeixinReqVO reqVO) { + userService.updateUserMobileByWeixin(getLoginUserId(), reqVO); + return success(true); + } + @PutMapping("/update-password") @Operation(summary = "修改用户密码", description = "用户修改密码时使用") @PreAuthenticated diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateMobileByWeixinReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateMobileByWeixinReqVO.java new file mode 100644 index 000000000..88d546a66 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateMobileByWeixinReqVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.member.controller.app.user.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "用户 APP - 基于微信小程序的授权码,修改手机 Request VO") +@Data +public class AppMemberUserUpdateMobileByWeixinReqVO { + + @Schema(description = "手机 code,小程序通过 wx.getPhoneNumber 方法获得", + requiredMode = Schema.RequiredMode.REQUIRED, example = "hello") + @NotEmpty(message = "手机 code 不能为空") + private String code; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateMobileReqVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateMobileReqVO.java index 06d27cbfd..8f4d8b64f 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateMobileReqVO.java +++ b/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; @Schema(description = "用户 APP - 修改手机 Request VO") @Data -@NoArgsConstructor -@AllArgsConstructor -@Builder public class AppMemberUserUpdateMobileReqVO { @Schema(description = "手机验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") @@ -25,14 +22,13 @@ public class AppMemberUserUpdateMobileReqVO { @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") private String code; - @Schema(description = "手机号",requiredMode = Schema.RequiredMode.REQUIRED,example = "15823654487") + @Schema(description = "手机号",requiredMode = Schema.RequiredMode.REQUIRED, example = "15823654487") @NotBlank(message = "手机号不能为空") @Length(min = 8, max = 11, message = "手机号码长度为 8-11 位") @Mobile private String mobile; - @Schema(description = "原手机验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "原手机验证码不能为空") + @Schema(description = "原手机验证码", example = "1024") @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位") @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") private String oldCode; diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java index dcd71ea21..2c07d4b6e 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java +++ b/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; import cn.iocoder.yudao.framework.common.validation.Mobile; import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO; import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO; -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserResetPasswordReqVO; -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateMobileReqVO; -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdatePasswordReqVO; -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateReqVO; +import cn.iocoder.yudao.module.member.controller.app.user.vo.*; import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; import jakarta.validation.Valid; @@ -94,13 +91,21 @@ public interface MemberUserService { void updateUser(Long userId, AppMemberUserUpdateReqVO reqVO); /** - * 【会员】修改手机 + * 【会员】修改手机,基于手机验证码 * * @param userId 用户编号 * @param reqVO 请求信息 */ void updateUserMobile(Long userId, AppMemberUserUpdateMobileReqVO reqVO); + /** + * 【会员】修改手机,基于微信小程序的授权码 + * + * @param userId 用户编号 + * @param reqVO 请求信息 + */ + void updateUserMobileByWeixin(Long userId, AppMemberUserUpdateMobileByWeixinReqVO reqVO); + /** * 【会员】修改密码 * @@ -181,4 +186,5 @@ public interface MemberUserService { * @return 更新结果 */ boolean updateUserPoint(Long userId, Integer point); + } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java index 9fb385475..e44e8b2df 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java +++ b/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; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.lang.Assert; import cn.hutool.core.util.*; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO; import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO; -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserResetPasswordReqVO; -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateMobileReqVO; -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdatePasswordReqVO; -import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateReqVO; +import cn.iocoder.yudao.module.member.controller.app.user.vo.*; import cn.iocoder.yudao.module.member.convert.auth.AuthConvert; import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert; import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; @@ -19,6 +18,8 @@ import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper; import cn.iocoder.yudao.module.member.mq.producer.user.MemberUserProducer; import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; +import cn.iocoder.yudao.module.system.api.social.SocialClientApi; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO; import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; @@ -54,6 +55,9 @@ public class MemberUserServiceImpl implements MemberUserService { @Resource private SmsCodeApi smsCodeApi; + @Resource + private SocialClientApi socialClientApi; + @Resource private PasswordEncoder passwordEncoder; @@ -145,22 +149,38 @@ public class MemberUserServiceImpl implements MemberUserService { @Override @Transactional(rollbackFor = Exception.class) public void updateUserMobile(Long userId, AppMemberUserUpdateMobileReqVO reqVO) { - // 检测用户是否存在 + // 1.1 检测用户是否存在 MemberUserDO user = validateUserExists(userId); - // 校验新手机是否已经被绑定 + // 1.2 校验新手机是否已经被绑定 validateMobileUnique(null, reqVO.getMobile()); - // 校验旧手机和旧验证码 - smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(user.getMobile()).setCode(reqVO.getOldCode()) - .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP())); - // 使用新验证码 + // 2.1 校验旧手机和旧验证码 + // 补充说明:从安全性来说,老手机也校验 oldCode 验证码会更安全。但是由于 uni-app 商城界面暂时没做,所以这里不强制校验 + if (StrUtil.isNotEmpty(reqVO.getOldCode())) { + smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(user.getMobile()).setCode(reqVO.getOldCode()) + .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP())); + } + // 2.2 使用新验证码 smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(reqVO.getMobile()).setCode(reqVO.getCode()) .setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP())); - // 更新用户手机 + // 3. 更新用户手机 memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(reqVO.getMobile()).build()); } + @Override + public void updateUserMobileByWeixin(Long userId, AppMemberUserUpdateMobileByWeixinReqVO reqVO) { + // 1.1 获得对应的手机号信息 + SocialWxPhoneNumberInfoRespDTO phoneNumberInfo = socialClientApi.getWxMaPhoneNumberInfo( + UserTypeEnum.MEMBER.getValue(), reqVO.getCode()); + Assert.notNull(phoneNumberInfo, "获得手机信息失败,结果为空"); + // 1.2 校验新手机是否已经被绑定 + validateMobileUnique(userId, phoneNumberInfo.getPhoneNumber()); + + // 2. 更新用户手机 + memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(phoneNumberInfo.getPhoneNumber()).build()); + } + @Override public void updateUserPassword(Long userId, AppMemberUserUpdatePasswordReqVO reqVO) { // 检测用户是否存在