Merge remote-tracking branch 'origin/main'

This commit is contained in:
zhangji committed 2025-08-22 13:53:20 +08:00
commit ea2bff19a9
5 files changed
+81 -8

No files matched your search

@@ -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;
@@ -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;
@@ -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;
/**
* 文件地址
*/
@@ -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;
}
}
}
@@ -74,7 +74,7 @@ public class SocialClientServiceImpl implements SocialClientService {
/**
* 小程序码要打开的小程序版本
*
* <p>
* 1. release:正式版
* 2. trial:体验版
* 3. developer:开发版
@@ -83,7 +83,7 @@ public class SocialClientServiceImpl implements SocialClientService {
public String envVersion;
/**
* 订阅消息跳转小程序类型
*
* <p>
* 1. developer:开发版
* 2. trial:体验版
* 3. formal:正式版
@@ -102,10 +102,10 @@ public class SocialClientServiceImpl implements SocialClientService {
private StringRedisTemplate stringRedisTemplate; // WxMpService 需要使用到,所以在 Service 注入了它
/**
* 缓存 WxMpService 对象
*
* <p>
* key:使用微信公众号的 appId + secret 拼接,即 {@link SocialClientDO} 的 clientId 和 clientSecret 属性。
* 为什么 key 使用这种格式?因为 {@link SocialClientDO} 在管理后台可以变更,通过这个 key 存储它的单例。
*
* <p>
* 为什么要做 WxMpService 缓存?因为 WxMpService 构建成本比较大,所以尽量保证它是单例。
*/
private final LoadingCache<String, WxMpService> wxMpServiceCache = CacheUtils.buildAsyncReloadingCache(
@@ -126,7 +126,7 @@ public class SocialClientServiceImpl implements SocialClientService {
private WxMaProperties wxMaProperties;
/**
* 缓存 WxMaService 对象
*
* <p>
* 说明同 {@link #wxMpServiceCache} 变量
*/
private final LoadingCache<String, WxMaService> wxMaServiceCache = CacheUtils.buildAsyncReloadingCache(
@@ -407,7 +407,7 @@ public class SocialClientServiceImpl implements SocialClientService {
/**
* 校验社交应用是否重复,需要保证 userType + socialType 唯一
*
* <p>
* 原因是,不同端(userType)选择某个社交登录(socialType)时,需要通过 {@link #buildAuthRequest(Integer, Integer)} 构建对应的请求
*
* @param id 编号