Pārlūkot izejas kodu

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

YunaiV 1 gadu atpakaļ
vecāks
revīzija
2f5afdb5c9

+ 2 - 2
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;
 

+ 8 - 0
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<Boolean> updateUserMobileByWeixin(@RequestBody @Valid AppMemberUserUpdateMobileByWeixinReqVO reqVO) {
+        userService.updateUserMobileByWeixin(getLoginUserId(), reqVO);
+        return success(true);
+    }
+
     @PutMapping("/update-password")
     @Operation(summary = "修改用户密码", description = "用户修改密码时使用")
     @PreAuthenticated

+ 16 - 0
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;
+
+}

+ 2 - 6
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;

+ 11 - 5
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);
+
 }

+ 31 - 11
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) {
         // 检测用户是否存在