diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/device/vo/DeviceRespVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/device/vo/DeviceRespVO.java index cf8f3b1..8ddfa4c 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/device/vo/DeviceRespVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/device/vo/DeviceRespVO.java @@ -2,9 +2,13 @@ package cn.iocoder.yudao.module.product.controller.admin.device.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; + import java.util.*; + import org.springframework.format.annotation.DateTimeFormat; + import java.time.LocalDateTime; + import com.alibaba.excel.annotation.*; @Schema(description = "管理后台 - 贩卖机信息 Response VO") @@ -28,6 +32,10 @@ public class DeviceRespVO { @ExcelProperty("访问商品地址") private String productUrl; + @Schema(description = "设备上的小程序二维码地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn") + @ExcelProperty("设备上的小程序二维码地址") + private String productDeviceUrl; + @Schema(description = "文件地址", example = "https://www.iocoder.cn") @ExcelProperty("文件地址") private String fileUrl; diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/device/vo/DeviceSaveReqVO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/device/vo/DeviceSaveReqVO.java index 5759860..fade917 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/device/vo/DeviceSaveReqVO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/device/vo/DeviceSaveReqVO.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.product.controller.admin.device.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; + import java.util.*; import javax.validation.constraints.*; @@ -23,6 +24,9 @@ public class DeviceSaveReqVO { // @NotEmpty(message = "访问商品地址不能为空") private String productUrl; + @Schema(description = "设备上的小程序二维码地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn") + private String productDeviceUrl; + @Schema(description = "文件地址", example = "https://www.iocoder.cn") private String fileUrl; diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/device/DeviceDO.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/device/DeviceDO.java index 8656af7..4fc9451 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/device/DeviceDO.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/device/DeviceDO.java @@ -1,9 +1,11 @@ package cn.iocoder.yudao.module.product.dal.dataobject.device; import lombok.*; + import java.util.*; import java.time.LocalDateTime; import java.time.LocalDateTime; + import com.baomidou.mybatisplus.annotation.*; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; @@ -37,8 +39,15 @@ public class DeviceDO extends BaseDO { private String deviceId; /** * 访问商品地址 + * 小程序二维码地址 */ private String productUrl; + + /** + * 设备上的小程序二维码地址 + */ + private String productDeviceUrl; + /** * 文件地址 */ diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/device/DeviceServiceImpl.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/device/DeviceServiceImpl.java index fb2a2f5..ee324bf 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/device/DeviceServiceImpl.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/device/DeviceServiceImpl.java @@ -14,12 +14,18 @@ import org.springframework.http.*; import org.springframework.stereotype.Service; import javax.annotation.Resource; +import javax.imageio.ImageIO; import org.springframework.validation.annotation.Validated; import org.springframework.transaction.annotation.Transactional; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.time.LocalDateTime; import java.util.*; +import java.util.List; import cn.iocoder.yudao.module.product.controller.admin.device.vo.*; import cn.iocoder.yudao.module.product.dal.dataobject.device.DeviceDO; @@ -116,7 +122,7 @@ public class DeviceServiceImpl implements DeviceService { DeviceDO updateObj = BeanUtils.toBean(updateReqVO, DeviceDO.class); DispenseRequestBo request = new DispenseRequestBo(); request.setDeviceId(updateReqVO.getDeviceId()); - request.setMessage(updateReqVO.getProductUrl() + "," + updateReqVO.getFileUrl()); + request.setMessage(updateReqVO.getProductDeviceUrl() + "," + updateReqVO.getFileUrl()); // 设置请求头 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); @@ -157,7 +163,7 @@ public class DeviceServiceImpl implements DeviceService { @Override public String refreshWechantCode(Long id) { DeviceDO deviceDO = deviceMapper.selectById(id); - if ( deviceDO== null|| deviceDO.getDeviceId()==null) { + if (deviceDO == null || deviceDO.getDeviceId() == null) { throw exception(DEVICE_NOT_EXISTS); } @@ -173,6 +179,7 @@ public class DeviceServiceImpl implements DeviceService { try { // 生成微信小程序二维码 byte[] imageByte = socialClientApi.getWxaQrcode(codeObj); + String deviceWechatQrCode = disposeWechatQrcodeImage(imageByte); // 保存二维码 String file = fileApi.createFile(imageByte); if (ObjectUtil.isEmpty(file)) { @@ -181,6 +188,9 @@ public class DeviceServiceImpl implements DeviceService { } // 更新一下 DeviceDO updateObj = DeviceDO.builder().id(id).productUrl(file).build(); + if (ObjectUtil.isNotEmpty(deviceWechatQrCode)) { + updateObj.setProductDeviceUrl(deviceWechatQrCode); + } deviceMapper.updateById(updateObj); // 刷新一下硬件设备 updateDevice(BeanUtil.toBean(deviceMapper.selectById(id), DeviceSaveReqVO.class)); @@ -233,4 +243,46 @@ public class DeviceServiceImpl implements DeviceService { return deviceMapper.updateDeviceLastChannel(deviceId, cabinetChannel); } + // 处理小程序二维码图片 + private String disposeWechatQrcodeImage(byte[] imageByte) { + int baseMapWidth = 1920; + int baseMapHeight = 1080; + + try { + ByteArrayInputStream bais = new ByteArrayInputStream(imageByte); + BufferedImage qrImage = ImageIO.read(bais); + + baseMapWidth = qrImage.getWidth() + 20; + + // 创建白色背景图片 + BufferedImage whiteBackgroundImage = new BufferedImage(baseMapWidth, baseMapHeight, BufferedImage.TYPE_INT_RGB); + + // 填充白色背景 + Graphics2D g2d = whiteBackgroundImage.createGraphics(); + g2d.setColor(Color.WHITE); + g2d.fillRect(0, 0, baseMapWidth, baseMapHeight); + + // 计算二维码在背景图中的居中位置 + int qrWidth = qrImage.getWidth(); + int qrHeight = qrImage.getHeight(); + int x = (baseMapWidth - qrWidth) / 2; + int y = (baseMapHeight - qrHeight) / 2; + + // 将二维码绘制到白色背景的居中位置 + g2d.drawImage(qrImage, x, y, null); + g2d.dispose(); + + // 转换回byte数组 + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(whiteBackgroundImage, "png", baos); + byte[] resultImageInByte = baos.toByteArray(); + + return fileApi.createFile(resultImageInByte); + } catch (Exception e) { + e.printStackTrace(); + log.error("[处理小程序二维码图片错误]:原因: {}", e.getMessage()); + return null; + } + } + } \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java index 8d1927e..d61f800 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java @@ -74,7 +74,7 @@ public class SocialClientServiceImpl implements SocialClientService { /** * 小程序码要打开的小程序版本 - * + *
* 1. release:正式版 * 2. trial:体验版 * 3. developer:开发版 @@ -83,7 +83,7 @@ public class SocialClientServiceImpl implements SocialClientService { public String envVersion; /** * 订阅消息跳转小程序类型 - * + *
* 1. developer:开发版 * 2. trial:体验版 * 3. formal:正式版 @@ -102,10 +102,10 @@ public class SocialClientServiceImpl implements SocialClientService { private StringRedisTemplate stringRedisTemplate; // WxMpService 需要使用到,所以在 Service 注入了它 /** * 缓存 WxMpService 对象 - * + *
* key:使用微信公众号的 appId + secret 拼接,即 {@link SocialClientDO} 的 clientId 和 clientSecret 属性。 * 为什么 key 使用这种格式?因为 {@link SocialClientDO} 在管理后台可以变更,通过这个 key 存储它的单例。 - * + *
* 为什么要做 WxMpService 缓存?因为 WxMpService 构建成本比较大,所以尽量保证它是单例。
*/
private final LoadingCache
* 说明同 {@link #wxMpServiceCache} 变量
*/
private final LoadingCache
* 原因是,不同端(userType)选择某个社交登录(socialType)时,需要通过 {@link #buildAuthRequest(Integer, Integer)} 构建对应的请求
*
* @param id 编号