Quellcode durchsuchen

小程序微信手机号授权

wjj vor 3 Tagen
Ursprung
Commit
ab0162f37d

+ 142 - 6
fs-user-app/src/main/java/com/fs/app/controller/WxMpController.java

@@ -1,17 +1,24 @@
 package com.fs.app.controller;
 
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
+import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
 import cn.hutool.core.date.DateTime;
+import com.fs.app.annotation.Login;
 import com.fs.app.annotation.UserOperationLog;
+import com.fs.app.enums.ErrorCode;
 import com.fs.app.param.FsUserLoginByMpParam;
 import com.fs.app.utils.JwtUtils;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.core.config.WxMaConfiguration;
 import com.fs.course.mapper.FsCourseSopLogsMapper;
 import com.fs.course.mapper.FsCourseWatchLogMapper;
 import com.fs.his.domain.FsUser;
 import com.fs.his.enums.FsUserOperationEnum;
 import com.fs.his.service.IFsUserService;
 import com.fs.his.utils.ConfigUtil;
+import com.fs.his.utils.PhoneUtil;
 import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.sop.params.QwSopSettingTimeParam;
 import com.fs.system.mapper.SysConfigMapper;
@@ -20,6 +27,7 @@ import com.fs.wx.mp.WxMessageType;
 import com.fs.wx.mp.WxServiceMsgDto;
 import io.swagger.annotations.ApiOperation;
 import lombok.Synchronized;
+import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.bean.WxJsapiSignature;
 import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
 import me.chanjar.weixin.common.bean.menu.WxMenu;
@@ -43,10 +51,10 @@ import java.util.*;
 import java.util.concurrent.TimeUnit;
 
 
-
+@Slf4j
 @RestController
 @RequestMapping("/app/wx/mp")
-public class WxMpController {
+public class WxMpController extends AppBaseController {
   Logger logger= LoggerFactory.getLogger(getClass());
   @Autowired
   private WxMpService wxMpService;
@@ -154,6 +162,138 @@ public class WxMpController {
     }
   }
 
+  @Login
+  @ApiOperation("绑定手机号")
+  @PostMapping("/setIPhoneNumber")
+  public R setIPhoneNumber(@RequestBody FsUserLoginByMpParam param) {
+    log.info("=====================进入小程序授权登录, 入参: {}", param);
+
+    // 参数校验
+    if (StringUtils.isBlank(param.getCode())) {
+      log.warn("绑定手机号失败:code参数为空");
+      return R.error(ErrorCode.PARAM_EMPTY.getCode(), "授权码不能为空");
+    }
+
+    if (StringUtils.isBlank(param.getAppId())) {
+      log.warn("绑定手机号失败:appId参数为空");
+      return R.error(ErrorCode.PARAM_EMPTY.getCode(), "应用ID不能为空");
+    }
+
+    WxMaService wxService = null;
+    try {
+      // 获取微信服务实例
+      wxService = WxMaConfiguration.getMaService(param.getAppId());
+      if (wxService == null) {
+        log.error("绑定手机号失败:未找到对应的微信小程序服务,appId={}", param.getAppId());
+        return R.error(ErrorCode.WX_SERVICE_NOT_FOUND.getCode(), "微信服务初始化失败");
+      }
+
+      // 获取session信息
+      WxMaJscode2SessionResult session = wxService.getUserService().getSessionInfo(param.getCode());
+      if (session == null || StringUtils.isEmpty(session.getOpenid())) {
+        log.warn("绑定手机号失败:获取session信息异常,code={}", param.getCode());
+        return R.error(ErrorCode.WX_SESSION_FAILED.getCode(), "微信授权失败,请重新尝试");
+      }
+
+      // 解密手机号信息
+      WxMaPhoneNumberInfo phoneNoInfo = null;
+      try {
+        phoneNoInfo = wxService.getUserService().getPhoneNoInfo(
+                session.getSessionKey(),
+                param.getEncryptedData(),
+                param.getIv()
+        );
+      } catch (Exception e) {
+        log.error("解密手机号失败:sessionKey={}, encryptedData={}, iv={}",
+                session.getSessionKey(), param.getEncryptedData(), param.getIv(), e);
+        return R.error(ErrorCode.WX_DECRYPT_FAILED.getCode(), "手机号解密失败");
+      }
+
+      if (phoneNoInfo == null || StringUtils.isEmpty(phoneNoInfo.getPhoneNumber())) {
+        log.warn("绑定手机号失败:获取的手机号为空");
+        return R.error(ErrorCode.WX_PHONE_EMPTY.getCode(), "未获取到手机号信息");
+      }
+
+      // 获取当前用户并更新手机号
+      String currentUserId = getUserId();
+      if (StringUtils.isBlank(currentUserId)) {
+        log.error("绑定手机号失败:用户ID获取失败");
+        return R.error(ErrorCode.USER_NOT_LOGIN.getCode(), "用户未登录");
+      }
+
+      Long userId;
+      try {
+        userId = Long.valueOf(currentUserId);
+      } catch (NumberFormatException e) {
+        log.error("绑定手机号失败:用户ID格式错误,userIdStr={}", currentUserId, e);
+        return R.error(ErrorCode.PARAM_FORMAT_ERROR.getCode(), "用户ID格式错误");
+      }
+
+      FsUser user = userService.selectFsUserByUserId(userId);
+      if (user == null) {
+        log.error("绑定手机号失败:用户不存在,userId={}", userId);
+        return R.error(ErrorCode.USER_NOT_EXIST.getCode(), "用户不存在");
+      }
+
+      // 检查手机号是否已被绑定
+      String phoneNumber = phoneNoInfo.getPhoneNumber();
+      if (StringUtils.isNotBlank(user.getPhone())) {
+        log.warn("绑定手机号失败:该用户已绑定手机号,userId={}, existingPhone={}", userId, user.getPhone());
+        return R.ok().put("code",ErrorCode.PHONE_ALREADY_BOUND.getCode()).put("msg", "该账号已绑定手机号").put("user",user);
+      }
+
+      // 检查手机号是否已被其他用户绑定
+      FsUser existingUser = userService.selectFsUserByPhone(phoneNumber);
+      if (existingUser == null) {
+        existingUser = userService.selectFsUserByPhone(PhoneUtil.encryptPhone(phoneNumber));
+      }
+      if (existingUser != null && !existingUser.getUserId().equals(userId)) {
+        log.warn("绑定手机号失败:手机号已被其他用户绑定,phone={}, existingUserId={}",
+                phoneNumber, existingUser.getUserId());
+        return R.error(ErrorCode.PHONE_ALREADY_USED.getCode(), "该手机号已被其他账号绑定");
+      }
+
+      // 更新用户手机号
+      user.setPhone(phoneNumber);
+      int updateResult = userService.updateFsUser(user);
+
+      if (updateResult <= 0) {
+        log.error("绑定手机号失败:数据库更新失败,userId={}, phone={}", userId, phoneNumber);
+        return R.error(ErrorCode.DB_UPDATE_FAILED.getCode(), "手机号绑定失败");
+      }
+
+      log.info("绑定手机号成功:userId={}, phone={}", userId, phoneNumber);
+      return R.ok("手机号绑定成功").put("user", user);
+
+    } catch (WxErrorException e) {
+      log.error("微信服务调用异常:code={}, appId={}, error={}",
+              param.getCode(), param.getAppId(), e.getMessage(), e);
+
+      // 根据微信错误码返回具体提示
+      int wxErrorCode = e.getError().getErrorCode();
+      String errorMsg = getWxErrorMsg(wxErrorCode);
+      return R.error(ErrorCode.WX_API_ERROR.getCode(), errorMsg);
+
+    } catch (Exception e) {
+      log.error("绑定手机号系统异常:param={}", param, e);
+      return R.error(ErrorCode.SYSTEM_ERROR.getCode(), "系统繁忙,请稍后再试");
+    }
+  }
+
+  /**
+   * 根据微信错误码获取友好提示信息
+   */
+  private String getWxErrorMsg(int wxErrorCode) {
+    switch (wxErrorCode) {
+      case 40029: return "授权码无效或已过期";
+      case 45011: return "操作频率限制,请稍后再试";
+      case 40013: return "无效的AppID";
+      case 40125: return "无效的AppSecret";
+      case 41008: return "缺少code参数";
+      default: return "微信服务异常,请稍后再试";
+    }
+  }
+
 
 
   private Date setSendTime(QwSopSettingTimeParam params) {
@@ -196,8 +336,4 @@ public class WxMpController {
       }
 
     }
-
-
-
-
 }

+ 40 - 0
fs-user-app/src/main/java/com/fs/app/enums/ErrorCode.java

@@ -0,0 +1,40 @@
+package com.fs.app.enums;
+
+import lombok.Getter;
+
+@Getter
+public enum ErrorCode {
+    // 系统级错误
+    SYSTEM_ERROR(10000, "系统异常"),
+
+    // 参数相关错误
+    PARAM_EMPTY(10001, "参数不能为空"),
+    PARAM_FORMAT_ERROR(10002, "参数格式错误"),
+
+    // 用户相关错误
+    USER_NOT_LOGIN(20001, "用户未登录"),
+    USER_NOT_EXIST(20002, "用户不存在"),
+    PHONE_ALREADY_BOUND(20003, "手机号已绑定"),
+    PHONE_ALREADY_USED(20004, "手机号已被使用"),
+
+    // 微信相关错误
+    WX_SERVICE_NOT_FOUND(30001, "微信服务未找到"),
+    WX_SESSION_FAILED(30002, "微信会话获取失败"),
+    WX_DECRYPT_FAILED(30003, "微信数据解密失败"),
+    WX_PHONE_EMPTY(30004, "微信手机号为空"),
+    WX_API_ERROR(30005, "微信接口调用失败"),
+
+    // 数据库相关错误
+    DB_UPDATE_FAILED(40001, "数据更新失败"),
+
+    // 成功
+    SUCCESS(0, "成功");
+
+    private final int code;
+    private final String message;
+
+    ErrorCode(int code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+}

+ 7 - 0
fs-user-app/src/main/java/com/fs/app/param/FsUserLoginByMpParam.java

@@ -11,6 +11,13 @@ import java.io.Serializable;
 public class FsUserLoginByMpParam implements Serializable {
     @NotBlank(message = "code参数缺失")
     private String code;
+
+    @ApiModelProperty(value = "小程序完整用户信息的加密数据")
+    private String encryptedData;
+
+    @ApiModelProperty(value = "小程序加密算法的初始向量")
+    private String iv;
+
     private Long videoId;
 
     @NotNull(message = "公司id不能为空")