From 7ec752235756dd9ed386adbef4b7d152a6ec618d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Thu, 21 Aug 2025 10:21:31 +0800 Subject: [PATCH] =?UTF-8?q?feat(wx):=20=E6=B7=BB=E5=8A=A0=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E5=B0=8F=E7=A8=8B=E5=BA=8F=E7=A0=81=E7=94=9F=E6=88=90?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 getQRCodeUnlimited 方法生成小程序码 - 添加 getLocalAccessToken 方法获取微信 access_token - 更新 WxLoginController 以使用新的二维码生成逻辑- 移除 MqttServiceTest 类,增加 WxDev 类用于微信相关测试 - 更新 Dockerfile 和 docker-compose.yml 以适应新的功能需求 --- Dockerfile | 14 +-- docker-compose.yml | 59 +-------- pom.xml | 12 ++ .../cms/controller/CmsAdController.java | 7 ++ .../java/com/gxwebsoft/cms/entity/CmsAd.java | 3 + .../gxwebsoft/cms/mapper/xml/CmsAdMapper.xml | 3 + .../com/gxwebsoft/cms/param/CmsAdParam.java | 4 + .../gxwebsoft/cms/service/CmsAdService.java | 6 + .../cms/service/impl/CmsAdServiceImpl.java | 7 ++ .../gxwebsoft/common/core/utils/WxUtil.java | 2 + .../system/controller/WxLoginController.java | 113 +++++++++++++++--- .../service/impl/SettingServiceImpl.java | 3 +- src/main/resources/application-prod.yml | 8 +- src/main/resources/application.yml | 2 +- src/test/java/com/gxwebsoft/WxDev.java | 110 +++++++++++++++++ .../com/gxwebsoft/hjm/MqttServiceTest.java | 52 -------- 16 files changed, 265 insertions(+), 140 deletions(-) create mode 100644 src/test/java/com/gxwebsoft/WxDev.java delete mode 100644 src/test/java/com/gxwebsoft/hjm/MqttServiceTest.java diff --git a/Dockerfile b/Dockerfile index 7a90993..7aba49b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,21 +1,19 @@ -# 使用OpenJDK 17作为基础镜像 -FROM openjdk:17-jre-alpine +# 使用更小的 Alpine Linux + OpenJDK 17 镜像 +FROM openjdk:17-jdk-alpine # 设置工作目录 WORKDIR /app -# 创建证书目录 -RUN mkdir -p /app/certs - # 创建日志目录 RUN mkdir -p /app/logs # 创建上传文件目录 RUN mkdir -p /app/uploads -# 添加应用用户(安全考虑) -RUN addgroup -g 1000 appgroup && \ - adduser -D -s /bin/sh -u 1000 -G appgroup appuser +# 安装wget用于健康检查,并添加应用用户(安全考虑) +RUN apk add --no-cache wget && \ + addgroup -g 1000 appgroup && \ + adduser -D -u 1000 -G appgroup appuser # 复制jar包到容器 COPY target/*.jar app.jar diff --git a/docker-compose.yml b/docker-compose.yml index 7d0cd7b..ea6a7d7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,9 +2,9 @@ version: '3.8' services: # 应用服务 - cms-app: + cms-api: build: . - container_name: cms-java-app + container_name: cms-api ports: - "9200:9200" environment: @@ -19,9 +19,6 @@ services: - ./uploads:/app/uploads networks: - cms-network - depends_on: - - mysql - - redis restart: unless-stopped healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9200/actuator/health"] @@ -30,58 +27,6 @@ services: retries: 3 start_period: 60s - # MySQL数据库 - mysql: - image: mysql:8.0 - container_name: cms-mysql - environment: - MYSQL_ROOT_PASSWORD: root123456 - MYSQL_DATABASE: modules - MYSQL_USER: modules - MYSQL_PASSWORD: 8YdLnk7KsPAyDXGA - ports: - - "3308:3306" - volumes: - - mysql_data:/var/lib/mysql - - ./mysql/conf:/etc/mysql/conf.d - - ./mysql/init:/docker-entrypoint-initdb.d - networks: - - cms-network - restart: unless-stopped - command: --default-authentication-plugin=mysql_native_password - - # Redis缓存 - redis: - image: redis:6.2-alpine - container_name: cms-redis - ports: - - "16379:6379" - volumes: - - redis_data:/data - - ./redis/redis.conf:/usr/local/etc/redis/redis.conf - networks: - - cms-network - restart: unless-stopped - command: redis-server /usr/local/etc/redis/redis.conf - - # Nginx反向代理(可选) - nginx: - image: nginx:alpine - container_name: cms-nginx - ports: - - "80:80" - - "443:443" - volumes: - - ./nginx/nginx.conf:/etc/nginx/nginx.conf - - ./nginx/conf.d:/etc/nginx/conf.d - - ./nginx/ssl:/etc/nginx/ssl - - ./uploads:/var/www/uploads - networks: - - cms-network - depends_on: - - cms-app - restart: unless-stopped - networks: cms-network: driver: bridge diff --git a/pom.xml b/pom.xml index 93ef0ff..ee63fc0 100644 --- a/pom.xml +++ b/pom.xml @@ -340,6 +340,18 @@ 0.2.5 + + com.squareup.okhttp3 + okhttp + 4.12.0 + + + + com.github.ben-manes.caffeine + caffeine + 3.1.8 + + com.freewayso image-combiner diff --git a/src/main/java/com/gxwebsoft/cms/controller/CmsAdController.java b/src/main/java/com/gxwebsoft/cms/controller/CmsAdController.java index 3325d00..64a979b 100644 --- a/src/main/java/com/gxwebsoft/cms/controller/CmsAdController.java +++ b/src/main/java/com/gxwebsoft/cms/controller/CmsAdController.java @@ -50,6 +50,13 @@ public class CmsAdController extends BaseController { return success(ad); } + @Operation(summary = "根据code查询广告位") + @GetMapping("/getByCode/{code}") + public ApiResult getByCode(@PathVariable("code") String code) { + final CmsAd ad = cmsAdService.getByIdCode(code); + return success(ad); + } + @Operation(summary = "添加广告位") @PostMapping() public ApiResult save(@RequestBody CmsAd cmsAd) { diff --git a/src/main/java/com/gxwebsoft/cms/entity/CmsAd.java b/src/main/java/com/gxwebsoft/cms/entity/CmsAd.java index 228a6bf..1f6996a 100644 --- a/src/main/java/com/gxwebsoft/cms/entity/CmsAd.java +++ b/src/main/java/com/gxwebsoft/cms/entity/CmsAd.java @@ -39,6 +39,9 @@ public class CmsAd implements Serializable { @Schema(description = "类型") private Integer type; + @Schema(description = "唯一标识") + private String code; + @Schema(description = "栏目ID") private Integer categoryId; diff --git a/src/main/java/com/gxwebsoft/cms/mapper/xml/CmsAdMapper.xml b/src/main/java/com/gxwebsoft/cms/mapper/xml/CmsAdMapper.xml index 8fb5dea..6ee7cbf 100644 --- a/src/main/java/com/gxwebsoft/cms/mapper/xml/CmsAdMapper.xml +++ b/src/main/java/com/gxwebsoft/cms/mapper/xml/CmsAdMapper.xml @@ -15,6 +15,9 @@ AND a.type = #{param.type} + + AND a.code = #{param.code} + AND a.category_id = #{param.categoryId} diff --git a/src/main/java/com/gxwebsoft/cms/param/CmsAdParam.java b/src/main/java/com/gxwebsoft/cms/param/CmsAdParam.java index 7f0a557..59d077f 100644 --- a/src/main/java/com/gxwebsoft/cms/param/CmsAdParam.java +++ b/src/main/java/com/gxwebsoft/cms/param/CmsAdParam.java @@ -29,6 +29,10 @@ public class CmsAdParam extends BaseParam { @Schema(description = "类型") private Integer type; + @Schema(description = "唯一标识") + @QueryField(type = QueryType.EQ) + private String code; + @Schema(description = "栏目ID") @QueryField(type = QueryType.EQ) private Integer categoryId; diff --git a/src/main/java/com/gxwebsoft/cms/service/CmsAdService.java b/src/main/java/com/gxwebsoft/cms/service/CmsAdService.java index 9bd4adb..9cef726 100644 --- a/src/main/java/com/gxwebsoft/cms/service/CmsAdService.java +++ b/src/main/java/com/gxwebsoft/cms/service/CmsAdService.java @@ -39,4 +39,10 @@ public interface CmsAdService extends IService { */ CmsAd getByIdRel(Integer adId); + /** + * 根据code查询 + * + * @return CmsAd + */ + CmsAd getByIdCode(String code); } diff --git a/src/main/java/com/gxwebsoft/cms/service/impl/CmsAdServiceImpl.java b/src/main/java/com/gxwebsoft/cms/service/impl/CmsAdServiceImpl.java index 26318f7..2988b3f 100644 --- a/src/main/java/com/gxwebsoft/cms/service/impl/CmsAdServiceImpl.java +++ b/src/main/java/com/gxwebsoft/cms/service/impl/CmsAdServiceImpl.java @@ -47,4 +47,11 @@ public class CmsAdServiceImpl extends ServiceImpl implements return param.getOne(baseMapper.selectListRel(param)); } + @Override + public CmsAd getByIdCode(String code) { + CmsAdParam param = new CmsAdParam(); + param.setCode(code); + return param.getOne(baseMapper.selectListRel(param)); + } + } diff --git a/src/main/java/com/gxwebsoft/common/core/utils/WxUtil.java b/src/main/java/com/gxwebsoft/common/core/utils/WxUtil.java index 417a354..035ce91 100644 --- a/src/main/java/com/gxwebsoft/common/core/utils/WxUtil.java +++ b/src/main/java/com/gxwebsoft/common/core/utils/WxUtil.java @@ -127,4 +127,6 @@ public class WxUtil { this.qr_code = jsonObject.getString("qr_code"); this.open_userid = jsonObject.getString("open_userid"); } + + } diff --git a/src/main/java/com/gxwebsoft/common/system/controller/WxLoginController.java b/src/main/java/com/gxwebsoft/common/system/controller/WxLoginController.java index de7d00f..2d441a9 100644 --- a/src/main/java/com/gxwebsoft/common/system/controller/WxLoginController.java +++ b/src/main/java/com/gxwebsoft/common/system/controller/WxLoginController.java @@ -1,15 +1,15 @@ package com.gxwebsoft.common.system.controller; import cn.hutool.core.io.FileUtil; -import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.RandomUtil; 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.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.gxwebsoft.common.core.config.ConfigProperties; import com.gxwebsoft.common.core.exception.BusinessException; import com.gxwebsoft.common.core.security.JwtSubject; @@ -25,13 +25,18 @@ import com.gxwebsoft.common.system.result.LoginResult; import com.gxwebsoft.common.system.service.*; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Operation; +import okhttp3.*; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.RequestBody; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.File; +import java.io.IOException; +import java.time.Instant; import java.util.HashMap; import java.util.concurrent.TimeUnit; @@ -43,6 +48,9 @@ import static com.gxwebsoft.common.core.constants.RedisConstants.ACCESS_TOKEN_KE @Tag(name = "微信小程序登录API") public class WxLoginController extends BaseController { private final StringRedisTemplate redisTemplate; + private final OkHttpClient http = new OkHttpClient(); + private final ObjectMapper om = new ObjectMapper(); + private volatile long tokenExpireEpoch = 0L; // 过期的 epoch 秒 @Resource private SettingService settingService; @Resource @@ -64,6 +72,8 @@ public class WxLoginController extends BaseController { @Resource private UserRefereeService userRefereeService; + + public WxLoginController(StringRedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } @@ -118,7 +128,6 @@ public class WxLoginController extends BaseController { UserParam userParam2 = new UserParam(); userParam2.setCode(userParam.getAuthCode()); JSONObject result = getOpenIdByCode(userParam2); - System.out.println("userInfo res:" + result); String openid = result.getString("openid"); // String unionid = result.getString("unionid"); userParam.setOpenid(openid); @@ -288,7 +297,6 @@ public class WxLoginController extends BaseController { String url = apiUrl.concat("?grant_type=client_credential").concat("&appid=").concat(setting.getString("appId")).concat("&secret=").concat(setting.getString("appSecret")); // 执行get请求 String result = HttpUtil.get(url); - System.out.println("result = " + result); // 解析access_token JSONObject response = JSON.parseObject(result); if (response.getString("access_token") != null) { @@ -401,27 +409,96 @@ public class WxLoginController extends BaseController { @Operation(summary = "获取微信小程序码-订单核销码-数量极多的业务场景") @GetMapping("/getOrderQRCodeUnlimited/{orderNo}") - public ApiResult getOrderQRCodeUnlimited(@PathVariable("orderNo") String orderNo) { - String apiUrl = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + getAccessToken(); + public void getOrderQRCodeUnlimited(@PathVariable("orderNo") String orderNo, HttpServletResponse response) throws IOException { + System.out.println("orderNo = " + orderNo); + String apiUrl = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + getLocalAccessToken(); final HashMap map = new HashMap<>(); - map.put("scene", "orderNo=".concat(orderNo)); - map.put("page", "package/admin/order-scan"); - map.put("env_version", "trial"); + map.put("scene", orderNo); + map.put("page", "pages/index/index"); + map.put("env_version", "develop"); + String jsonBody = JSON.toJSONString(map); + System.out.println("请求的 JSON body = " + jsonBody); // 获取图片 Buffer byte[] qrCode = HttpRequest.post(apiUrl) .body(JSON.toJSONString(map)) .execute().bodyBytes(); - System.out.println("qrCode = " + qrCode); - // 保存的文件名称 - final String fileName = CommonUtil.randomUUID8().concat(".png"); - // 保存路径 - String filePath = getUploadDir().concat("qrcode/") + fileName; - File file = FileUtil.writeBytes(qrCode, filePath); - if (file != null) { - return success(config.getFileServer().concat("/qrcode/").concat(fileName)); + // 设置响应头 + response.setContentType("image/png"); + response.setHeader("Cache-Control", "no-cache"); + response.setHeader("Content-Disposition", "inline; filename=encrypted_qrcode.png"); + + // 输出图片 + response.getOutputStream().write(qrCode); + System.out.println("response = " + response); + } + + @Operation(summary = "获取微信小程序码-用户ID") + @GetMapping("/getQRCodeText") + public byte[] getQRCodeText(String scene, String page, Integer width, + Boolean isHyaline, String envVersion) throws IOException { + HttpUrl url = HttpUrl.parse("https://api.weixin.qq.com/wxa/getwxacodeunlimit") + .newBuilder() + .addQueryParameter("access_token", getLocalAccessToken()) + .build(); + + System.out.println("page = " + page); + // 构造请求 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 + + okhttp3.RequestBody reqBody = okhttp3.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"); } - return fail("获取失败", null); + 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) + } + } + + /** 获取/刷新 access_token */ + public String getLocalAccessToken() throws IOException { + long now = Instant.now().getEpochSecond(); + String key ="AccessToken:Local:10550"; + if (redisUtil.get(key) != null && now < tokenExpireEpoch - 60) { + return redisUtil.get(key); + } + 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(); + long expiresIn = json.get("expires_in").asInt(7200); + redisUtil.set(key,token,expiresIn,TimeUnit.SECONDS); + tokenExpireEpoch = now + expiresIn; + return token; + } else { + throw new IOException("Get access_token failed: " + body); + } + } } /** diff --git a/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java b/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java index 3e77732..baf97ec 100644 --- a/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java +++ b/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java @@ -72,9 +72,10 @@ public class SettingServiceImpl extends ServiceImpl impl @Override public JSONObject getBySettingKey(String key) { Setting setting = this.getOne(new QueryWrapper().eq("setting_key", key), false); + System.out.println("setting1 = " + setting); if(setting == null){ if ("mp-weixin".equals(key)) { - throw new BusinessException("小程序未配置"); + throw new BusinessException("小程序未配置1"); } if ("payment".equals(key)) { throw new BusinessException("支付未配置"); diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 57406f0..bbc6266 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -3,17 +3,19 @@ # 数据源配置 spring: datasource: - url: jdbc:mysql://1Panel-mysql-Bqdt:3306/modules?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 + url: jdbc:mysql://8.134.169.209:13306/modules?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 username: modules password: 8YdLnk7KsPAyDXGA driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource + druid: + remove-abandoned: true # redis redis: database: 0 - host: 1Panel-redis-Q1LE - port: 6379 + host: 8.134.169.209 + port: 16379 password: redis_WSDb88 # 日志配置 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a614ed4..8ed09b3 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -157,7 +157,7 @@ shop: tenant-configs: - tenant-id: 10324 tenant-name: "百色中学" - timeout-minutes: 60 # 捐款订单给更长的支付时间 + timeout-minutes: 120 # 捐款订单给更长的支付时间 enabled: true # 可以添加更多租户配置 # - tenant-id: 10550 diff --git a/src/test/java/com/gxwebsoft/WxDev.java b/src/test/java/com/gxwebsoft/WxDev.java new file mode 100644 index 0000000..110a008 --- /dev/null +++ b/src/test/java/com/gxwebsoft/WxDev.java @@ -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 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); + } +} diff --git a/src/test/java/com/gxwebsoft/hjm/MqttServiceTest.java b/src/test/java/com/gxwebsoft/hjm/MqttServiceTest.java deleted file mode 100644 index 5dd64cd..0000000 --- a/src/test/java/com/gxwebsoft/hjm/MqttServiceTest.java +++ /dev/null @@ -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()); - } - } -}