package com.gxwebsoft.auto.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gxwebsoft.auto.dto.*;
import com.gxwebsoft.auto.service.QrLoginService;
import com.gxwebsoft.common.core.config.ConfigProperties;
import com.gxwebsoft.common.core.security.JwtSubject;
import com.gxwebsoft.common.core.security.JwtUtil;
import com.gxwebsoft.common.core.utils.CommonUtil;
import com.gxwebsoft.common.core.utils.RedisUtil;
import com.gxwebsoft.common.mq.producer.SyncMessageProducer;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.common.system.entity.UserOauth;
import com.gxwebsoft.common.system.service.UserOauthService;
import com.gxwebsoft.common.system.service.UserService;
import com.gxwebsoft.common.system.service.WxService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

import static com.gxwebsoft.common.core.constants.PlatformConstants.MP_OFFICIAL;
import static com.gxwebsoft.common.core.constants.RedisConstants.*;
import static com.gxwebsoft.common.core.constants.WebsiteConstants.CACHE_KEY_VERIFICATION_CODE_BY_DEV_SMS;

/**
 * 扫码登录服务实现
 *
 * @author 科技小王子
 * @since 2025-08-31
 */
@Slf4j
@Service
public class QrLoginServiceImpl implements QrLoginService {

  @Autowired
  private RedisUtil redisUtil;

  @Autowired
  private UserService userService;

  @Autowired
  private ConfigProperties configProperties;

  @Autowired
  private WxService wxService;

  @Autowired(required = false)
  private UserOauthService userOauthService;

  @Autowired(required = false)
  private SyncMessageProducer syncMessageProducer;

  @Override
  public QrLoginGenerateResponse generateQrLoginToken(Integer tenantId) {
    String token = UUID.randomUUID().toString(true);

    QrLoginData qrLoginData = new QrLoginData();
    qrLoginData.setToken(token);
    qrLoginData.setStatus(QR_LOGIN_STATUS_PENDING);
    qrLoginData.setTenantId(tenantId);
    qrLoginData.setNeedBindPhone(false);
    qrLoginData.setMessage("等待微信扫码");
    qrLoginData.setCreateTime(DateUtil.formatDateTime(DateUtil.date()));
    qrLoginData.setExpireTime(DateUtil.formatDateTime(DateUtil.offsetSecond(DateUtil.date(), QR_LOGIN_TOKEN_TTL.intValue())));

    String redisKey = QR_LOGIN_TOKEN_KEY + token;
    redisUtil.set(redisKey, qrLoginData, QR_LOGIN_TOKEN_TTL, TimeUnit.SECONDS);

    log.info("生成扫码登录token: {}", token);

    QrLoginGenerateResponse response = new QrLoginGenerateResponse();
    response.setToken(token);
    response.setExpiresIn(QR_LOGIN_TOKEN_TTL);
    // 二维码内容：使用自定义协议，前端据此生成base64二维码
    response.setQrCodeContent("websopy://login?token=" + token);
    // 小程序路径（用于小程序扫码直接打开）
    response.setMiniprogramPath("/pages/qr-login?token=" + token);

    // 扫码跳转URL（前端生成二维码时使用此URL）
    try {
      String baseUrl = configProperties.getWechatScanUrl();
      if (StrUtil.isBlank(baseUrl)) {
        baseUrl = "https://websopy.websoft.top";
      }
      String wechatScanUrl = baseUrl + "/wx-scan?token=" + token;
      response.setWechatScanUrl(wechatScanUrl);
      log.info("扫码跳转URL: {}", wechatScanUrl);
    } catch (Exception e) {
      log.warn("获取扫码跳转URL失败: {}", e.getMessage());
      // 降级：使用默认域名
      response.setWechatScanUrl("https://websopy.websoft.top/wx-scan?token=" + token);
    }

    // 生成小程序码（通过微信API生成小程序码，返回Base64图片，扫码后直接打开小程序确认页面）
    try {
      String miniprogramQrCodeBase64 = generateMiniprogramQrCode(token, tenantId);
      if (StrUtil.isNotBlank(miniprogramQrCodeBase64)) {
        response.setMiniprogramQrCode(miniprogramQrCodeBase64);
        log.info("生成小程序码成功（Base64，长度: {}）", miniprogramQrCodeBase64.length());
      }
    } catch (Exception e) {
      log.error("生成小程序码失败: {}", e.getMessage(), e);
      // 生成失败不影响主流程，继续使用H5方式
    }

    return response;
  }

  /**
   * 生成小程序码（用于PC端扫码登录）
   * 调用微信API生成无限制小程序码，返回Base64图片，扫码后直接打开小程序确认页面
   * 具备自动重试机制：首次失败后清理缓存并重试一次
   *
   * @param token    扫码登录token
   * @param tenantId 租户ID
   * @return 小程序码图片Base64字符串
   */
  private String generateMiniprogramQrCode(String token, Integer tenantId) {
    // 构建 access_token 的 Redis key（与 WxService 保持一致）
    String accessTokenKey = "WX_ACCESS_TOKEN:" + (tenantId != null ? tenantId : 10048);

    // 第一次尝试生成
    String result = doGenerateMiniprogramQrCode(token, tenantId, accessTokenKey, false);
    if (result != null) {
      return result;
    }

    // 第一次失败，清理缓存并重试（确保下次能拿到最新的 access_token）
    log.info("小程序码首次生成失败，清理缓存后重试...");
    clearAccessTokenCache(accessTokenKey, tenantId);

    // 第二次尝试生成（强制刷新 token）
    return doGenerateMiniprogramQrCode(token, tenantId, accessTokenKey, true);
  }

  /**
   * 执行小程序码生成
   *
   * @param token          扫码登录token
   * @param tenantId       租户ID
   * @param accessTokenKey access_token 的 Redis key
   * @param forceRefresh    是否强制刷新 access_token
   * @return 小程序码 Base64 字符串，失败返回 null
   */
  private String doGenerateMiniprogramQrCode(String token, Integer tenantId, String accessTokenKey, boolean forceRefresh) {
    try {
      // 获取小程序access_token
      String accessToken = forceRefresh
          ? wxService.getAccessTokenForcibly(tenantId)  // 强制从微信获取新token
          : wxService.getAccessToken(tenantId);

      if (StrUtil.isBlank(accessToken)) {
        log.warn("获取小程序access_token失败，跳过生成小程序码");
        return null;
      }

      // 调用微信API生成小程序码
      String apiUrl = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + accessToken;

      HashMap<String, Object> params = new HashMap<>();
      // scene 必须是字符串，最大 32 字符，直接传 token（32位UUID）刚好满足限制
      // 小程序端通过 router.params.scene 获取此 token
      params.put("scene", token);
      params.put("page", "passport/qr-confirm/index");  // 小程序确认页面路径（子包）
      params.put("env_version", "release");  // release/trial/develop
      params.put("width", 280);  // 二维码宽度
      params.put("auto_color", false);  // 不自动配置颜色

      // 发送请求并获取二进制响应
      byte[] imageBytes = HttpRequest.post(apiUrl)
        .body(JSON.toJSONString(params))
        .timeout(15000)
        .execute().bodyBytes();

      // 判断是否返回图片（二进制）或错误（JSON）
      if (imageBytes == null || imageBytes.length == 0) {
        log.error("生成小程序码API返回空数据");
        return null;
      }

      // 检查是否返回JSON错误（微信API错误时会返回JSON）
      if (imageBytes.length < 100 && new String(imageBytes).startsWith("{")) {
        JSONObject errorResult = JSON.parseObject(new String(imageBytes));
        Integer errCode = errorResult.getInteger("errcode");
        String errMsg = errorResult.getString("errmsg");

        log.error("生成小程序码API返回错误[{}:{}]", errCode, errMsg);
        return null;
      }

      // 将图片字节数组转换为Base64字符串
      String base64Image = cn.hutool.core.codec.Base64.encode(imageBytes);
      // 添加Data URI前缀，使前端可以直接使用
      return "data:image/png;base64," + base64Image;
    } catch (Exception e) {
      log.error("生成小程序码异常: {}", e.getMessage(), e);
      return null;
    }
  }

  /**
   * 判断是否是 token 相关的错误码，需要清理缓存
   * 常见微信 API 错误码：
   * - 40001: 获取access_token时AppSecret错误
   * - 40013: appid无效
   * - 40125: appsecret无效
   * - 42001: access_token超时
   * - 42002: refresh_token超时
   * - 42003: code超时
   * - 44002: post body太长
   * - 44003: 图片太大
   * - 41002: appid不正确
   * - 41008: 缺少access_token参数
   */
  private boolean isTokenRelatedError(Integer errCode, String errMsg) {
    if (errCode == null) {
      return false;
    }
    // token 相关错误码
    return errCode == 40001    // AppSecret错误
        || errCode == 40013    // appid无效
        || errCode == 40125    // appsecret无效
        || errCode == 42001    // access_token超时
        || errCode == 42002    // refresh_token超时
        || errCode == 42003    // code超时
        || errCode == 41002    // appid不正确
        || errCode == 41008   // 缺少access_token参数
        || errCode == 40014    // 不合法的access_token
        || errCode == 40097;   // invalid page
  }

  /**
   * 清理 access_token 缓存
   */
  private void clearAccessTokenCache(String accessTokenKey, Integer tenantId) {
    try {
      redisUtil.delete(accessTokenKey);
      log.info("清理微信access_token缓存[{}], tenantId={}", accessTokenKey, tenantId);
    } catch (Exception e) {
      log.error("清理access_token缓存失败: {}", e.getMessage());
    }
  }

  @Override
  public QrLoginStatusResponse checkQrLoginStatus(String token) {
    if (StrUtil.isBlank(token)) {
      return buildExpiredResponse();
    }

    String redisKey = QR_LOGIN_TOKEN_KEY + token;
    QrLoginData qrLoginData = redisUtil.get(redisKey, QrLoginData.class);
    if (qrLoginData == null) {
      return buildExpiredResponse();
    }

    Date expireAt = parseExpireTime(qrLoginData.getExpireTime());
    if (expireAt == null || DateUtil.date().after(expireAt)) {
      redisUtil.delete(redisKey);
      return buildExpiredResponse();
    }

    long expiresIn = calculateExpiresIn(expireAt);

    if (QR_LOGIN_STATUS_CONFIRMED.equals(qrLoginData.getStatus())
      && StrUtil.isBlank(qrLoginData.getAccessToken())
      && qrLoginData.getUserId() != null) {
      try {
        User user = userService.getAllByUserId(String.valueOf(qrLoginData.getUserId()));
        if (user != null) {
          qrLoginData.setUsername(user.getUsername());
          if (StrUtil.isBlank(user.getPhone())) {
            qrLoginData.setStatus(QR_LOGIN_STATUS_BIND_PHONE);
            qrLoginData.setNeedBindPhone(true);
            qrLoginData.setAccessToken(null);
            qrLoginData.setMessage("请先绑定手机号完成登录");
          } else {
            qrLoginData.setStatus(QR_LOGIN_STATUS_CONFIRMED);
            qrLoginData.setNeedBindPhone(false);
            qrLoginData.setAccessToken(buildAccessToken(user));
            qrLoginData.setMessage(StrUtil.blankToDefault(qrLoginData.getMessage(), "登录成功"));
          }
          long refreshedTtl = Math.max(expiresIn, 120L);
          persistQrLoginData(redisKey, qrLoginData, refreshedTtl, true);
          expiresIn = refreshedTtl;
        }
      } catch (Exception e) {
        log.error("补全扫码登录状态失败，token={}", token, e);
      }
    }

    return buildStatusResponse(qrLoginData, expiresIn);
  }

  @Override
  public QrLoginStatusResponse confirmQrLogin(QrLoginConfirmRequest request) {
    String token = request.getToken();
    Integer userId = request.getUserId();

    if (StrUtil.isBlank(token) || userId == null) {
      throw new RuntimeException("参数不能为空");
    }

    String redisKey = QR_LOGIN_TOKEN_KEY + token;
    QrLoginData qrLoginData = redisUtil.get(redisKey, QrLoginData.class);
    if (qrLoginData == null) {
      throw new RuntimeException("扫码登录token不存在或已过期");
    }

    Date expireAt = parseExpireTime(qrLoginData.getExpireTime());
    if (expireAt == null || DateUtil.date().after(expireAt)) {
      redisUtil.delete(redisKey);
      throw new RuntimeException("扫码登录token已过期");
    }

    User user = userService.getAllByUserId(String.valueOf(userId));
    if (user == null) {
      throw new RuntimeException("用户不存在");
    }
    if (user.getStatus() != null && user.getStatus() != 0) {
      throw new RuntimeException("用户已被冻结");
    }

    String accessToken = buildAccessToken(user);
    qrLoginData.setStatus(QR_LOGIN_STATUS_CONFIRMED);
    qrLoginData.setUserId(userId);
    qrLoginData.setUsername(user.getUsername());
    qrLoginData.setAccessToken(accessToken);
    qrLoginData.setTenantId(user.getTenantId());
    qrLoginData.setNeedBindPhone(false);
    qrLoginData.setMessage("登录成功");
    persistQrLoginData(redisKey, qrLoginData, 120L, true);

    log.info("用户 {} 确认扫码登录，token: {}", user.getUsername(), token);
    return buildStatusResponse(qrLoginData, 120L);
  }

  @Override
  public boolean scanQrCode(String token) {
    if (StrUtil.isBlank(token)) {
      return false;
    }

    String redisKey = QR_LOGIN_TOKEN_KEY + token;
    QrLoginData qrLoginData = redisUtil.get(redisKey, QrLoginData.class);
    if (qrLoginData == null) {
      return false;
    }

    Date expireAt = parseExpireTime(qrLoginData.getExpireTime());
    if (expireAt == null || DateUtil.date().after(expireAt)) {
      redisUtil.delete(redisKey);
      return false;
    }

    if (QR_LOGIN_STATUS_PENDING.equals(qrLoginData.getStatus())) {
      qrLoginData.setStatus(QR_LOGIN_STATUS_SCANNED);
      qrLoginData.setMessage("已识别扫码，等待公众号回调");
      long remainingSeconds = Math.max(1L,
        (expireAt.getTime() - DateUtil.date().getTime()) / 1000);
      redisUtil.set(redisKey, qrLoginData, remainingSeconds, TimeUnit.SECONDS);
      log.info("扫码登录token {} 状态更新为已扫码", token);
      return true;
    }

    return false;
  }

  @Override
  public QrLoginStatusResponse bindPhone(QrLoginBindPhoneRequest request) {
    if (request == null || StrUtil.isBlank(request.getToken()) || StrUtil.isBlank(request.getPhone()) || StrUtil.isBlank(request.getCode())) {
      throw new RuntimeException("参数不能为空");
    }
    if (!CommonUtil.isValidPhoneNumber(request.getPhone())) {
      throw new RuntimeException("请输入有效的手机号码");
    }

    String redisKey = QR_LOGIN_TOKEN_KEY + request.getToken();
    QrLoginData qrLoginData = redisUtil.get(redisKey, QrLoginData.class);
    if (qrLoginData == null) {
      throw new RuntimeException("二维码已过期，请刷新后重试");
    }
    Date expireAt = parseExpireTime(qrLoginData.getExpireTime());
    if (expireAt == null || DateUtil.date().after(expireAt)) {
      redisUtil.delete(redisKey);
      throw new RuntimeException("二维码已过期，请刷新后重试");
    }
    if (!QR_LOGIN_STATUS_BIND_PHONE.equals(qrLoginData.getStatus()) && !Boolean.TRUE.equals(qrLoginData.getNeedBindPhone())) {
      throw new RuntimeException("当前二维码无需绑定手机号");
    }
    if (qrLoginData.getUserId() == null) {
      throw new RuntimeException("绑定账号不存在，请重新扫码");
    }

    String codeKey = "code:" + request.getPhone();
    String smsCode = redisUtil.get(codeKey);
    String devCode = redisUtil.get(CACHE_KEY_VERIFICATION_CODE_BY_DEV_SMS);
    if (StrUtil.isBlank(smsCode) && StrUtil.isBlank(devCode)) {
      throw new RuntimeException("验证码已过期，请重新获取");
    }
    if (!StrUtil.equals(request.getCode(), smsCode) && !StrUtil.equals(request.getCode(), devCode)) {
      throw new RuntimeException("验证码不正确");
    }

    User user = userService.getAllByUserId(String.valueOf(qrLoginData.getUserId()));
    if (user == null) {
      throw new RuntimeException("用户不存在");
    }
    if (user.getStatus() != null && user.getStatus() != 0) {
      throw new RuntimeException("账号已被冻结");
    }

    User existed = userService.getByPhone(request.getPhone());
    if (existed != null && !existed.getUserId().equals(user.getUserId())) {
      throw new RuntimeException("该手机号已绑定其他账号");
    }

    user.setPhone(request.getPhone());
    if (StrUtil.isBlank(user.getNickname()) || "微信公众号用户".equals(user.getNickname())) {
      user.setNickname(DesensitizedUtil.mobilePhone(request.getPhone()));
    }
    if (StrUtil.isBlank(user.getUsername()) || user.getUsername().startsWith("wxoff_")) {
      user.setUsername(request.getPhone());
    }
    userService.updateUser(user);
    redisUtil.delete(codeKey);

    // 绑定手机号成功后，通过MQ异步同步用户数据到 websopy
    if (syncMessageProducer != null) {
      User updatedUser = userService.getAllByUserId(String.valueOf(user.getUserId()));
      if (updatedUser != null) {
        syncMessageProducer.sendUserSyncMessage("websopy", "UPDATE", updatedUser);
        log.info("扫码绑定手机号后发送MQ消息同步用户到websopy: userId={}, phone={}", user.getUserId(), user.getPhone());
      }
    }

    String accessToken = buildAccessToken(user);
    qrLoginData.setStatus(QR_LOGIN_STATUS_CONFIRMED);
    qrLoginData.setUserId(user.getUserId());
    qrLoginData.setUsername(user.getUsername());
    qrLoginData.setTenantId(user.getTenantId());
    qrLoginData.setAccessToken(accessToken);
    qrLoginData.setNeedBindPhone(false);
    qrLoginData.setMessage("手机号绑定成功，正在登录");
    persistQrLoginData(redisKey, qrLoginData, 120L, true);

    return buildStatusResponse(qrLoginData, 120L);
  }

  private String buildAccessToken(User user) {
    JwtSubject jwtSubject = new JwtSubject(user.getUsername(), user.getTenantId());
    return JwtUtil.buildToken(jwtSubject, configProperties.getTokenExpireTime(), configProperties.getTokenKey());
  }

  private Date parseExpireTime(String expireTime) {
    if (StrUtil.isBlank(expireTime)) {
      return null;
    }
    try {
      return DateUtil.parseDateTime(expireTime);
    } catch (Exception e) {
      log.warn("扫码登录 expireTime 解析失败: {}", expireTime, e);
      return null;
    }
  }

  private long calculateExpiresIn(Date expireAt) {
    if (expireAt == null) {
      return 0L;
    }
    return Math.max(0L, (expireAt.getTime() - DateUtil.date().getTime()) / 1000);
  }

  private void persistQrLoginData(String redisKey, QrLoginData qrLoginData, long ttlSeconds, boolean refreshExpireTime) {
    if (refreshExpireTime) {
      qrLoginData.setExpireTime(DateUtil.formatDateTime(DateUtil.offsetSecond(DateUtil.date(), (int) ttlSeconds)));
    }
    redisUtil.set(redisKey, qrLoginData, ttlSeconds, TimeUnit.SECONDS);
  }

  private QrLoginStatusResponse buildExpiredResponse() {
    QrLoginStatusResponse response = new QrLoginStatusResponse(QR_LOGIN_STATUS_EXPIRED, null, null, 0L, null);
    response.setNeedBindPhone(false);
    response.setMessage("二维码已过期，请刷新后重试");
    return response;
  }

  private QrLoginStatusResponse buildStatusResponse(QrLoginData qrLoginData, Long expiresIn) {
    QrLoginStatusResponse response = new QrLoginStatusResponse();
    response.setStatus(qrLoginData.getStatus());
    response.setAccessToken(qrLoginData.getAccessToken());
    response.setExpiresIn(expiresIn);
    response.setTenantId(qrLoginData.getTenantId());
    response.setNeedBindPhone(Boolean.TRUE.equals(qrLoginData.getNeedBindPhone())
      || QR_LOGIN_STATUS_BIND_PHONE.equals(qrLoginData.getStatus()));
    response.setMessage(qrLoginData.getMessage());

    // 设置下一步操作逻辑
    if (QR_LOGIN_STATUS_BIND_PHONE.equals(qrLoginData.getStatus()) || Boolean.TRUE.equals(qrLoginData.getNeedBindPhone())) {
      response.setNextAction("bind_phone");
      response.setRedirectUrl(null);
    } else if (QR_LOGIN_STATUS_CONFIRMED.equals(qrLoginData.getStatus()) && StrUtil.isNotBlank(qrLoginData.getAccessToken())) {
      response.setNextAction("redirect");
      response.setRedirectUrl("/console");
      response.setSuccessMessage("登录成功，即将跳转到控制台");
    } else {
      response.setNextAction("wait");
    }

    if (qrLoginData.getUserId() != null) {
      try {
        User user = userService.getAllByUserId(String.valueOf(qrLoginData.getUserId()));
        if (user != null) {
          user.setPassword(null);
          response.setUserInfo(user);
        }
      } catch (Exception e) {
        log.error("构建扫码登录状态响应时查询用户失败，userId={}", qrLoginData.getUserId(), e);
      }
    }
    return response;
  }

  @Override
  public WechatScanResponse wechatScanConfirm(WechatScanRequest request) {
    String token = request.getToken();
    if (StrUtil.isBlank(token)) {
      return WechatScanResponse.notBound("二维码参数错误");
    }

    String redisKey = QR_LOGIN_TOKEN_KEY + token;
    QrLoginData qrLoginData = redisUtil.get(redisKey, QrLoginData.class);
    if (qrLoginData == null) {
      return WechatScanResponse.notBound("二维码已过期，请刷新重试");
    }
    Date expireAt = parseExpireTime(qrLoginData.getExpireTime());
    if (expireAt == null || DateUtil.date().after(expireAt)) {
      redisUtil.delete(redisKey);
      return WechatScanResponse.notBound("二维码已过期，请刷新重试");
    }

    String unionId = request.getUnionId();
    String openId = request.getOpenId();
    Integer tenantId = qrLoginData.getTenantId();

    if (StrUtil.isBlank(unionId) && StrUtil.isNotBlank(request.getCode())) {
      try {
        JSONObject userAccessToken = wxService.getOfficialUserAccessToken(request.getCode(), tenantId);
        unionId = userAccessToken.getString("unionid");
        openId = userAccessToken.getString("openid");
        log.info("通过授权码获取到 unionId: {}, openId: {}", unionId, openId);
      } catch (Exception e) {
        log.error("通过授权码获取用户信息失败: {}", e.getMessage());
        return WechatScanResponse.notBound("微信授权失败，请重试");
      }
    }

    if (StrUtil.isBlank(unionId) && StrUtil.isBlank(openId)) {
      return WechatScanResponse.notBound("无法获取微信用户信息");
    }

    User user = null;
    if (StrUtil.isNotBlank(unionId)) {
      user = userService.getOne(new LambdaQueryWrapper<User>()
        .eq(User::getUnionid, unionId)
        .eq(User::getDeleted, 0)
        .last("limit 1"));
      log.info("通过 unionId {} 查找用户: {}", unionId, user != null ? user.getUsername() : "未找到");
    }

    if (user == null && StrUtil.isNotBlank(openId)) {
      user = userService.getOne(new LambdaQueryWrapper<User>()
        .eq(User::getOpenid, openId)
        .eq(User::getDeleted, 0)
        .last("limit 1"));
      log.info("通过 openId {} 查找用户: {}", openId, user != null ? user.getUsername() : "未找到");
    }

    if (user == null && (StrUtil.isNotBlank(unionId) || StrUtil.isNotBlank(openId))) {
      try {
        LambdaQueryWrapper<UserOauth> wrapper = new LambdaQueryWrapper<>();
        if (StrUtil.isNotBlank(unionId)) {
          wrapper.eq(UserOauth::getUnionid, unionId);
        } else {
          wrapper.eq(UserOauth::getOauthId, openId);
        }
        wrapper.eq(UserOauth::getDeleted, 0);

        UserOauth userOauth = null;
        if (userOauthService != null) {
          userOauth = userOauthService.getOne(wrapper);
        }

        if (userOauth != null && userOauth.getUserId() != null) {
          user = userService.getAllByUserId(String.valueOf(userOauth.getUserId()));
          log.info("通过 UserOauth 查找到用户: {}", user != null ? user.getUsername() : "未找到");
        }
      } catch (Exception e) {
        log.error("通过 UserOauth 查找用户失败: {}", e.getMessage());
      }
    }

    if (user == null) {
      return WechatScanResponse.notBound("该微信未绑定平台账号，请先在平台注册并绑定微信");
    }
    if (user.getStatus() != null && user.getStatus() != 0) {
      return WechatScanResponse.notBound("账号已被冻结");
    }

    if (StrUtil.isBlank(user.getPhone())) {
      qrLoginData.setStatus(QR_LOGIN_STATUS_BIND_PHONE);
      qrLoginData.setUserId(user.getUserId());
      qrLoginData.setUsername(user.getUsername());
      qrLoginData.setTenantId(user.getTenantId());
      qrLoginData.setAccessToken(null);
      qrLoginData.setNeedBindPhone(true);
      qrLoginData.setMessage("请先绑定手机号完成登录");
      persistQrLoginData(redisKey, qrLoginData, 120L, true);
      return WechatScanResponse.needBind("请先绑定手机号完成登录");
    }

    String accessToken = buildAccessToken(user);
    qrLoginData.setStatus(QR_LOGIN_STATUS_CONFIRMED);
    qrLoginData.setUserId(user.getUserId());
    qrLoginData.setUsername(user.getUsername());
    qrLoginData.setAccessToken(accessToken);
    qrLoginData.setTenantId(user.getTenantId());
    qrLoginData.setNeedBindPhone(false);
    qrLoginData.setMessage("登录成功");
    persistQrLoginData(redisKey, qrLoginData, 120L, true);

    user.setPassword(null);
    return WechatScanResponse.success(accessToken, user, user.getTenantId());
  }
}
