- 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)
文章评论