周凯,个人博客

  • 前端
  • 嵌入式
  • 工具
  • 后端
  • 随笔
个人记录
  1. 首页
  2. 后端
  3. java
  4. spring-boot
  5. 正文

微信小程序,动态生成二维码,获取不限制的小程序码

2023年 4月 12日 941点热度 0人点赞 0条评论
  • WxChatUtil 工具
package jnpf.util.wxutil;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import jnpf.config.WxConfig;
import jnpf.exception.BusinessException;
import jnpf.util.JsonUtil;
import jnpf.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.HTTP;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Base64;
import java.util.HashMap;
import java.util.Objects;

/**
 * @author 周凯
 * @date 2023/4/11
 * 微信相关Utils
 */
@Slf4j
@Component
public class WxChatUtil {
    @Autowired
    private WxConfig wxConfig;
    @Autowired
    private RedisUtil redisUtil;
    private String ACCESS_TOKEN_URL = "";
    private static final String QRCODE_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=";
    private static final String ACCESS_TOKEN = "ACCESS_TOKEN";
    // 缓存失效时间 默认是2个小时
    private static final Long CACHE_EXPIRATION_TIME = 7200L;

    /**
     * 获取小程序的Access Token
     *
     * @return Access Token
     */
    public String getAccessToken() {
        ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + this.wxConfig.getAppid() + "&secret=" + this.wxConfig.getSecret();
        String accessToken = null;
        // 查询redis accessToken是否过期
        Object accessTokenValue = this.redisUtil.getString(ACCESS_TOKEN);
        if (Objects.nonNull(accessTokenValue)) {
            accessToken = (String) accessTokenValue;
        } else {
            try {
                URL url = new URL(ACCESS_TOKEN_URL);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                connection.connect();
                InputStream inputStream = connection.getInputStream();
                byte[] data = new byte[inputStream.available()];
                inputStream.read(data);
                String result = new String(data, "UTF-8");
                JSONObject json = JSONObject.parseObject(result);
                accessToken = json.getString("access_token");
                if (Strings.isNotBlank(accessToken)) {
                    this.redisUtil.insert(ACCESS_TOKEN, accessToken, CACHE_EXPIRATION_TIME);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return accessToken;
    }

    /**
     * 生成二维码
     *
     * @param page  路径
     * @param scene 额外参数
     * @param width 宽度
     * @return 小程序码的字节数组
     * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/qr-code/getUnlimitedQRCode.html#HTTPS-%E8%B0%83%E7%94%A8
     */
    public String generateQRCode(String page, String scene, int width) throws IOException {
        String qrcode = null;
        String accessToken = getAccessToken();
        if (accessToken != null && !accessToken.isEmpty()) {
            String url = QRCODE_URL + accessToken;
            CloseableHttpClient httpClient = HttpClientBuilder.create().build();
            HashMap<String, Object> map = new HashMap<>();
            map.put("page", page);
            map.put("check_path", false);
            map.put("scene", scene);
            map.put("width", width);
            map.put("env_version", "trial");
            HttpPost httpPost = new HttpPost(url);  // 接口
            httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json");
            String body = JSON.toJSONString(map);           //必须是json模式的 post
            StringEntity entity;
            entity = new StringEntity(body);
            entity.setContentType("image/png");

            httpPost.setEntity(entity);
            HttpResponse response = httpClient.execute(httpPost);
            InputStream inputStream = response.getEntity().getContent();

            // 将获取流转为base64格式
            String result = "";
            byte[] data = null;
            ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
            byte[] buff = new byte[100];
            int rc = 0;
            while ((rc = inputStream.read(buff, 0, 100)) > 0) {
                swapStream.write(buff, 0, rc);
            }
            data = swapStream.toByteArray();

            result = new String(Base64.getEncoder().encode(data));
            qrcode = result;
        } else {
            throw new BusinessException("微信token获取失败!");
        }
        return qrcode;
    }
}
  • service调用
  @Override
  public String getMiniProgramQrCode() throws IOException {
        String page = "pages/bind_wechat/index";
        UserInfo userInfo = this.userProvider.get();
        String format = String.format("userId=%s&userAccount=%s", userInfo.getUserId(), userInfo.getUserAccount());
        return this.wxChatUtil.generateQRCode(page, format, 280);
  }
  • controller调用
    @GetMapping("/getBindWxQr")
    public ActionResult getUserIdQr() throws IOException {
        String qrCode = this.wxLoginService.getMiniProgramQrCode();
        String data = "data:image/png;base64," + qrCode;
        return ActionResult.success("成功", data);
    }
  • 前端直接调用img展示就可以

坑点:

  • 获取小程序码返回的是BuffImage需要转换为base64
  • 调用生成二维码传参的时候,check_path设置为false,默认检测路径是否存在(如果对应env_version版本的路径存在了,不需要处理),不存在无法生成二维码
  • 调用生成二维码传参的时候,scene参数形式是"name=1&id=2"格式,格式必须是这样,最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式)
  • 如果发现生成的二维码图片无法展示,可以看看响应返回是否已经异常

其他

  • 小程序端调用

    • 在对应页面的onLoad中
      // 安装qs包
      import qs from 'qs';
    
      onLoad(query) {
            console.log(query, 'onLoad query')
            if (query) {
                const scene = decodeURIComponent(query.scene)
                console.log("解析数据为: ", scene)
                this.userInfo = qs.parse(scene);
                console.log(this.userInfo, 'this.userInfo')
            }
       }

参考

  • 微信相关接口文档:获取不限制的小程序码 | 微信开放文档 (qq.com)

🎯 拓展阅读提示

本文涉及的内容已同步至公众号后台,我会在那里分享更多深度内容和实用技巧

→ 点击关注:一行梦境

公众号二维码
本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: 暂无
最后更新:2023年 4月 12日

周凯

这个人很懒,什么都没留下

打赏 点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

COPYRIGHT © 2022-现在 周凯,个人博客. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

蒙ICP备18004897号