1. java 二維碼生成工具類
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.pdatao.api.controller.file.FileController;
import com.pdatao.api.error.CommunityException;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;@Component
public class MpQrCodeUtil {@ResourceFileController fileController;@Value("${mp.wechat.appid}")private String mpAppId;@Value("${mp.wechat.secret}")private String mpSecretId;@Value("${qrcode.pageHome}")private String pageHome;@Value("${spring.profiles.active:}")private String activeProfile;private static final String API_GET_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";private static final String API_GET_QR_CODE = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s";private static String cachedToken = null;private static long tokenExpireTime = 0;public String getMpQRCode(Long orderId, HttpServletRequest request) throws Exception {String scenes = "id=" + orderId + "&v=1";String envVersion = "";if ("prod-plus".equals(activeProfile)) {envVersion = "release";}return this.getQRCodeWeb(scenes,envVersion,orderId,request);}public String getQRCodeWeb(String scenes, String envVersion, Long orderId, HttpServletRequest request) throws Exception {String accessToken = getToken(mpAppId, mpSecretId);return getQRCode(accessToken, scenes, envVersion, orderId, request);}public static String getToken(String appId, String appSecret) throws Exception {// 1. 檢查緩存是否有效if (cachedToken != null && System.currentTimeMillis() < tokenExpireTime) {return cachedToken;}HttpURLConnection conn = null;try {String url = String.format(API_GET_TOKEN, appId, appSecret);conn = (HttpURLConnection) new URL(url).openConnection();conn.setConnectTimeout(5000);conn.setReadTimeout(5000);conn.setRequestMethod("GET");int statusCode = conn.getResponseCode();if (statusCode != 200) {// 讀取錯誤流String errorJson = IOUtils.toString(conn.getErrorStream(), StandardCharsets.UTF_8);throw new CommunityException("微信接口錯誤: " + errorJson);}JSONObject result = new JSONObject(IOUtils.toString(conn.getInputStream(), StandardCharsets.UTF_8));cachedToken = result.getStr("access_token");long expiresIn = result.getLong("expires_in") * 1000; // 轉為毫秒tokenExpireTime = System.currentTimeMillis() + expiresIn - 600_000;return cachedToken;} finally {if (conn != null) conn.disconnect();}}public String getQRCode(String accessToken, String scenes, String envVersion, Long orderId, HttpServletRequest request) throws Exception {HttpURLConnection httpURLConnection = null;try {URL url = new URL(String.format(API_GET_QR_CODE, accessToken));httpURLConnection = (HttpURLConnection) url.openConnection();httpURLConnection.setRequestMethod("POST");// 提交模式// 發送POST請求必須設置如下兩行httpURLConnection.setDoOutput(true);httpURLConnection.setDoInput(true);// 發送請求參數com.alibaba.fastjson.JSONObject paramJson = new com.alibaba.fastjson.JSONObject();paramJson.put("scene", scenes);paramJson.put("page", pageHome);paramJson.put("env_version", StrUtil.isNotEmpty(envVersion) ? envVersion : "trial");paramJson.put("check_path", false);try (PrintWriter printWriter = new PrintWriter(httpURLConnection.getOutputStream())) {printWriter.write(paramJson.toString());printWriter.flush();}// 檢查響應碼int responseCode = httpURLConnection.getResponseCode();if (responseCode != HttpURLConnection.HTTP_OK) {cachedToken = null;throw new CommunityException("生成二維碼失敗:HTTP錯誤碼 " + responseCode);}// 檢查內容類型String contentType = httpURLConnection.getContentType();if (contentType == null || !contentType.startsWith("image/")) {cachedToken = null;throw new CommunityException("生成二維碼失敗:接口返回非圖片數據(" + (contentType != null ? contentType : "未知內容類型") + ")");}try (InputStream is = httpURLConnection.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();) {byte[] buffer = new byte[1024];int len = -1;while ((len = is.read(buffer)) != -1) {baos.write(buffer, 0, len);}byte[] imageData = baos.toByteArray();// 簡單驗證是否是有效圖片(可選)if (imageData.length == 0) {cachedToken = null;throw new CommunityException("生成二維碼失敗:返回空圖片數據");}MultipartFile multipartFile = new ByteArrayMultipartFile(orderId + "_mpqrcode", // 表單字段名orderId + "_mpqrcode.png", // 文件名"image/png", // 內容類型imageData // 內容);com.alibaba.fastjson.JSONObject json = fileController.upload(multipartFile,request);if (json == null || !json.containsKey("url")) {throw new CommunityException("上傳圖片失敗:響應數據異常");}return json.getString("url");}} catch (Exception e) {e.printStackTrace();throw new CommunityException("生成二維碼失敗:"+e.getMessage());} finally {if (httpURLConnection != null) {httpURLConnection.disconnect();}}}}
2. 部分可自行調整的代碼解釋
FileController: 我自己的上傳圖片到服務器的類
mpAppId:? 小程序appid
mpSecretId: 小程序?SecretId
pageHome: 要跳轉的小程序的頁面地址(例如: ‘pages/userInfo/userInfoHome’)
activeProfile: 我自己的判斷當前運行環境的配置(可以忽略)
@Resource FileController fileController;@Value("${mp.wechat.appid}") private String mpAppId; @Value("${mp.wechat.secret}") private String mpSecretId;@Value("${qrcode.pageHome}") private String pageHome; @Value("${spring.profiles.active:}") private String activeProfile;
getMpQRCode 外部調用的方法,自定義自己需要傳入什么值
Long orderId : 這個是我為了生成二維碼路徑時攜帶的參數
HttpServletRequest request: 這個參數,和二維碼生成邏輯沒有任何關系,我這里使用只是因為上傳圖片的地方需要這個值,這個比較冗余
scenes: 定義頁面地址攜帶什么參數
if ("prod-plus".equals(activeProfile)) {envVersion = "release"; } 這個是為了判斷生成什么環境的二維碼圖片(正式版/ 體驗版)
public String getMpQRCode(Long orderId, HttpServletRequest request) throws Exception {String scenes = "id=" + orderId + "&v=1";String envVersion = "";if ("prod-plus".equals(activeProfile)) {envVersion = "release";}return this.getQRCodeWeb(scenes,envVersion,orderId,request); }
imageData : 這個就是生成的二維碼圖片信息
下面的其他信息,都是為了把這個圖片的信息,上傳到自己項目中保存,最終返回圖片地址
byte[] imageData = baos.toByteArray();// 簡單驗證是否是有效圖片(可選) if (imageData.length == 0) {throw new CommunityException("生成核銷二維碼失敗:返回空圖片數據"); }MultipartFile multipartFile = new ByteArrayMultipartFile(orderId + "_mpqrcode", // 表單字段名orderId + "_mpqrcode.png", // 文件名"image/png", // 內容類型imageData // 內容 ); com.alibaba.fastjson.JSONObject json = fileController.upload(multipartFile,request); if (json == null || !json.containsKey("url")) {throw new CommunityException("上傳二維碼失敗:響應數據異常"); } return json.getString("url");
3.小程序中獲取攜帶的參數
以我上述的參數為例:(微信小程序使用的 uniapp)
onLoad(option) {if (option.scene) {const scene = decodeURIComponent(option.scene);const params = this.parseSceneParams(scene);console.log(params.id)console.log(params.v)}},methods: {parseSceneParams(scene) {const params = {};if (!scene) return params;const pairs = scene.split('&');pairs.forEach(pair => {const [key, value] = pair.split('=');if (key) params[key] = value;});return params;},
}