feat(wx): 添加微信小程序码生成功能
- 新增 getQRCodeUnlimited 方法生成小程序码 - 添加 getLocalAccessToken 方法获取微信 access_token - 更新 WxLoginController 以使用新的二维码生成逻辑- 移除 MqttServiceTest 类,增加 WxDev 类用于微信相关测试 - 更新 Dockerfile 和 docker-compose.yml 以适应新的功能需求
This commit is contained in:
110
src/test/java/com/gxwebsoft/WxDev.java
Normal file
110
src/test/java/com/gxwebsoft/WxDev.java
Normal file
@@ -0,0 +1,110 @@
|
||||
package com.gxwebsoft;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import okhttp3.*;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@Service
|
||||
public class WxDev {
|
||||
|
||||
@Value("${wechat.appid}")
|
||||
private String appId;
|
||||
@Value("${wechat.secret}")
|
||||
private String secret;
|
||||
|
||||
private final StringRedisTemplate redisTemplate;
|
||||
|
||||
private final OkHttpClient http = new OkHttpClient();
|
||||
private final ObjectMapper om = new ObjectMapper();
|
||||
|
||||
/** 简单本地缓存 access_token(生产建议放 Redis) */
|
||||
private final AtomicReference<String> cachedToken = new AtomicReference<>();
|
||||
private volatile long tokenExpireEpoch = 0L; // 过期的 epoch 秒
|
||||
|
||||
public WxDev(StringRedisTemplate redisTemplate) {
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
/** 获取/刷新 access_token */
|
||||
public String getAccessToken() throws IOException {
|
||||
long now = Instant.now().getEpochSecond();
|
||||
System.out.println("cachedToken.get = " + cachedToken.get());
|
||||
if (cachedToken.get() != null && now < tokenExpireEpoch - 60) {
|
||||
return cachedToken.get();
|
||||
}
|
||||
HttpUrl url = HttpUrl.parse("https://api.weixin.qq.com/cgi-bin/token")
|
||||
.newBuilder()
|
||||
.addQueryParameter("grant_type", "client_credential")
|
||||
.addQueryParameter("appid", "wx51962d6ac21f2ed2")
|
||||
.addQueryParameter("secret", "d821f98de8a6c1ba7bc7e0ee84bcbc8e")
|
||||
.build();
|
||||
Request req = new Request.Builder().url(url).get().build();
|
||||
try (Response resp = http.newCall(req).execute()) {
|
||||
String body = resp.body().string();
|
||||
JsonNode json = om.readTree(body);
|
||||
if (json.has("access_token")) {
|
||||
String token = json.get("access_token").asText();
|
||||
int expiresIn = json.get("expires_in").asInt(7200);
|
||||
System.out.println("token1 = " + token);
|
||||
cachedToken.set(token);
|
||||
tokenExpireEpoch = now + expiresIn;
|
||||
return token;
|
||||
} else {
|
||||
throw new IOException("Get access_token failed: " + body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 调用 getwxacodeunlimit,返回图片二进制 */
|
||||
public byte[] getUnlimitedCode(String scene, String page, Integer width,
|
||||
Boolean isHyaline, String envVersion) throws IOException {
|
||||
String accessToken = getAccessToken();
|
||||
System.out.println("accessToken = " + accessToken);
|
||||
HttpUrl url = HttpUrl.parse("https://api.weixin.qq.com/wxa/getwxacodeunlimit")
|
||||
.newBuilder()
|
||||
.addQueryParameter("access_token", accessToken)
|
||||
.build();
|
||||
|
||||
// 构造请求 JSON
|
||||
// 注意:scene 仅支持可见字符,长度上限 32,尽量 URL-safe(字母数字下划线等)
|
||||
// page 必须是已发布小程序内的路径(不带开头斜杠也可)
|
||||
var root = om.createObjectNode();
|
||||
root.put("scene", scene);
|
||||
if (page != null) root.put("page", page);
|
||||
if (width != null) root.put("width", width); // 默认 430,建议 280~1280
|
||||
if (isHyaline != null) root.put("is_hyaline", isHyaline);
|
||||
if (envVersion != null) root.put("env_version", envVersion); // release/trial/develop
|
||||
|
||||
RequestBody reqBody = RequestBody.create(
|
||||
root.toString(), MediaType.parse("application/json; charset=utf-8"));
|
||||
Request req = new Request.Builder().url(url).post(reqBody).build();
|
||||
|
||||
try (Response resp = http.newCall(req).execute()) {
|
||||
if (!resp.isSuccessful()) {
|
||||
throw new IOException("HTTP " + resp.code() + " calling getwxacodeunlimit");
|
||||
}
|
||||
MediaType ct = resp.body().contentType();
|
||||
byte[] bytes = resp.body().bytes();
|
||||
// 微信出错时返回 JSON,需要识别一下
|
||||
if (ct != null && ct.subtype() != null && ct.subtype().contains("json")) {
|
||||
String err = new String(bytes);
|
||||
throw new IOException("WeChat error: " + err);
|
||||
}
|
||||
return bytes; // 成功就是图片二进制(PNG)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getQrCode() throws IOException {
|
||||
final byte[] test = getUnlimitedCode("register", "pages/index/index",180,false,"develop");
|
||||
System.out.println("test = " + test);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package com.gxwebsoft.hjm;
|
||||
|
||||
import com.gxwebsoft.hjm.service.MqttService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* MQTT服务测试类
|
||||
*
|
||||
* @author 科技小王子
|
||||
* @since 2025-07-02
|
||||
*/
|
||||
@SpringBootTest
|
||||
@ActiveProfiles("dev")
|
||||
public class MqttServiceTest {
|
||||
|
||||
@Resource
|
||||
private MqttService mqttService;
|
||||
|
||||
@Test
|
||||
public void testMqttConnection() {
|
||||
System.out.println("MQTT连接状态: " + mqttService.isConnected());
|
||||
System.out.println("MQTT客户端信息: " + mqttService.getClientInfo());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMqttReconnect() {
|
||||
try {
|
||||
mqttService.reconnect();
|
||||
System.out.println("MQTT重连测试完成");
|
||||
} catch (Exception e) {
|
||||
System.err.println("MQTT重连测试失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMqttPublish() {
|
||||
try {
|
||||
if (mqttService.isConnected()) {
|
||||
mqttService.publish("/test/topic", "测试消息");
|
||||
System.out.println("MQTT消息发布测试完成");
|
||||
} else {
|
||||
System.out.println("MQTT未连接,跳过发布测试");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("MQTT消息发布测试失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user