diff --git a/src/main/java/com/gxwebsoft/auto/dto/QrLoginGenerateResponse.java b/src/main/java/com/gxwebsoft/auto/dto/QrLoginGenerateResponse.java index d0e1469..5837631 100644 --- a/src/main/java/com/gxwebsoft/auto/dto/QrLoginGenerateResponse.java +++ b/src/main/java/com/gxwebsoft/auto/dto/QrLoginGenerateResponse.java @@ -26,9 +26,12 @@ public class QrLoginGenerateResponse { @Schema(description = "微信小程序页面路径") private String miniprogramPath; - @Schema(description = "微信小程序码图片URL") + @Schema(description = "微信小程序码图片URL(已废弃,改用base64)") private String miniprogramQrCodeUrl; + @Schema(description = "微信小程序码图片Base64(扫码后直接打开小程序,优先使用)") + private String miniprogramQrCode; + @Schema(description = "过期时间(秒)") private Long expiresIn; diff --git a/src/main/java/com/gxwebsoft/auto/service/impl/QrLoginServiceImpl.java b/src/main/java/com/gxwebsoft/auto/service/impl/QrLoginServiceImpl.java index fe42a19..0f818be 100644 --- a/src/main/java/com/gxwebsoft/auto/service/impl/QrLoginServiceImpl.java +++ b/src/main/java/com/gxwebsoft/auto/service/impl/QrLoginServiceImpl.java @@ -4,6 +4,9 @@ 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.*; @@ -22,8 +25,8 @@ 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; @@ -99,13 +102,82 @@ public class QrLoginServiceImpl implements QrLoginService { response.setWechatScanUrl("https://websopy.websoft.top/wx-scan?token=" + token); } - // 不再生成小程序码图片文件,改为前端通过qrCodeContent生成base64二维码 - // 不再生成公众号带参数二维码URL,前端使用wechatScanUrl生成二维码 - // miniprogramQrCodeUrl 和 wechatQrCodeUrl 保持null,前端自动使用本地生成 + // 生成小程序码(通过微信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) { + try { + // 获取小程序access_token + String accessToken = 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 params = new HashMap<>(); + params.put("scene", "token=" + token); // 场景值,扫码后会透传 + params.put("page", "pages/public/qr-confirm/index"); // 小程序确认页面路径 + params.put("env_version", "release"); // 正式版小程序 + params.put("width", 280); // 二维码宽度 + params.put("auto_color", false); // 不自动配置颜色 + HashMap lineColor = new HashMap<>(); + lineColor.put("r", 0); + lineColor.put("g", 122); + lineColor.put("b", 255); + params.put("line_color", lineColor); // 二维码颜色 + + // 发送请求并获取二进制响应 + byte[] imageBytes = HttpRequest.post(apiUrl) + .body(JSON.toJSONString(params)) + .timeout(15000) + .execute().bytes(); + + // 判断是否返回图片(二进制)或错误(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)); + log.error("生成小程序码API返回错误: {}", errorResult); + 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; + } + } + @Override public QrLoginStatusResponse checkQrLoginStatus(String token) { if (StrUtil.isBlank(token)) {