|
|
@ -84,20 +84,21 @@ public class AppletApiWaterServiceImpl implements AppletApiWaterService { |
|
|
// 获取必要的配置信息 |
|
|
// 获取必要的配置信息 |
|
|
String xcxSharePage = appletConfigService.getContentByCode("xcxSharePage"); |
|
|
String xcxSharePage = appletConfigService.getContentByCode("xcxSharePage"); |
|
|
String backgroundImageUrl = appletConfigService.getContentByCode("qr_code_bg"); |
|
|
String backgroundImageUrl = appletConfigService.getContentByCode("qr_code_bg"); |
|
|
|
|
|
|
|
|
|
|
|
// 获取二维码位置配置参数 |
|
|
|
|
|
int qrCodeX = appletConfigService.getContentByCodeAsInt("qr_code_x"); |
|
|
|
|
|
int qrCodeY = appletConfigService.getContentByCodeAsInt("qr_code_y"); |
|
|
|
|
|
|
|
|
// 优化缓存策略:使用更精确的缓存key,包含所有影响因素(包括背景图片URL) |
|
|
|
|
|
String cacheKey = String.format("inviteCode:final:%s:%s:%s:%s", user.getId(), trial, xcxSharePage.hashCode(), backgroundImageUrl.hashCode()); |
|
|
|
|
|
String imageCacheKey = String.format("inviteCode:image:%s:%s:%s:%s", user.getId(), trial, xcxSharePage.hashCode(), backgroundImageUrl.hashCode()); |
|
|
|
|
|
|
|
|
// 优化缓存策略:使用更精确的缓存key,包含所有影响因素(包括背景图片URL和二维码位置配置) |
|
|
|
|
|
String cacheKey = String.format("inviteCode:final:%s:%s:%s:%s:%s:%s", |
|
|
|
|
|
user.getId(), trial, xcxSharePage.hashCode(), backgroundImageUrl.hashCode(), qrCodeX, qrCodeY); |
|
|
|
|
|
|
|
|
// 首先检查是否有最终合成图片的缓存 |
|
|
|
|
|
byte[] cachedImage = (byte[]) redisUtil.get(imageCacheKey); |
|
|
|
|
|
if (cachedImage != null) { |
|
|
|
|
|
log.info("从缓存返回邀请码图片,用户ID: {}", user.getId()); |
|
|
|
|
|
return cachedImage; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 移除图片数据的Redis缓存,避免类型转换错误 |
|
|
|
|
|
// 直接检查OSS中是否已存在最终图片 |
|
|
|
|
|
|
|
|
// 检查OSS中是否已存在最终图片 |
|
|
// 检查OSS中是否已存在最终图片 |
|
|
String finalPath = "invite/final/" + user.getId() + "_" + xcxSharePage + "_" + trial + ".jpg"; |
|
|
|
|
|
|
|
|
String finalPath = String.format("invite/final/%s_%s_%s_%s_%s_%s.jpg", |
|
|
|
|
|
user.getId(), trial, xcxSharePage.hashCode(), backgroundImageUrl.hashCode(), qrCodeX, qrCodeY); |
|
|
try { |
|
|
try { |
|
|
InputStream ossFile = OssBootUtil.getOssFile(finalPath, null); |
|
|
InputStream ossFile = OssBootUtil.getOssFile(finalPath, null); |
|
|
if (ossFile != null) { |
|
|
if (ossFile != null) { |
|
|
@ -112,8 +113,7 @@ public class AppletApiWaterServiceImpl implements AppletApiWaterService { |
|
|
buffer.flush(); |
|
|
buffer.flush(); |
|
|
byte[] ossImageBytes = buffer.toByteArray(); |
|
|
byte[] ossImageBytes = buffer.toByteArray(); |
|
|
|
|
|
|
|
|
// 将OSS中的图片缓存到Redis,设置24小时过期 |
|
|
|
|
|
redisUtil.set(imageCacheKey, ossImageBytes, 24 * 60 * 60); |
|
|
|
|
|
|
|
|
// 直接返回OSS中的图片,不再缓存到Redis |
|
|
log.info("从OSS返回邀请码图片,用户ID: {}", user.getId()); |
|
|
log.info("从OSS返回邀请码图片,用户ID: {}", user.getId()); |
|
|
return ossImageBytes; |
|
|
return ossImageBytes; |
|
|
} finally { |
|
|
} finally { |
|
|
@ -125,26 +125,15 @@ public class AppletApiWaterServiceImpl implements AppletApiWaterService { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
// 检查是否有小程序码的缓存 |
|
|
|
|
|
String qrCodeCacheKey = String.format("inviteCode:qrcode:%s:%s:%s", user.getId(), trial, xcxSharePage.hashCode()); |
|
|
|
|
|
byte[] cachedQrCode = (byte[]) redisUtil.get(qrCodeCacheKey); |
|
|
|
|
|
|
|
|
|
|
|
byte[] qrCodeBytes; |
|
|
|
|
|
if (cachedQrCode != null) { |
|
|
|
|
|
log.info("使用缓存的小程序码,用户ID: {}", user.getId()); |
|
|
|
|
|
qrCodeBytes = cachedQrCode; |
|
|
|
|
|
} else { |
|
|
|
|
|
log.info("生成新的小程序码,用户ID: {}", user.getId()); |
|
|
|
|
|
qrCodeBytes = generateWxQrCode(user, xcxSharePage, trial); |
|
|
|
|
|
// 缓存小程序码,设置7天过期(小程序码相对稳定) |
|
|
|
|
|
redisUtil.set(qrCodeCacheKey, qrCodeBytes, 7 * 24 * 60 * 60); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 直接生成小程序码,移除Redis缓存避免类型转换错误 |
|
|
|
|
|
log.info("生成小程序码,用户ID: {}", user.getId()); |
|
|
|
|
|
byte[] qrCodeBytes = generateWxQrCode(user, xcxSharePage, trial); |
|
|
|
|
|
|
|
|
// 生成最终合成图片 |
|
|
|
|
|
byte[] finalImage = this.generateAndCombineImagesFromUrl2(qrCodeBytes, backgroundImageUrl); |
|
|
|
|
|
|
|
|
// 生成最终合成图片,传递已获取的配置参数避免重复调用 |
|
|
|
|
|
byte[] finalImage = this.generateAndCombineImagesFromUrl2(qrCodeBytes, backgroundImageUrl, qrCodeX, qrCodeY); |
|
|
|
|
|
|
|
|
// 异步上传到OSS并缓存 |
|
|
|
|
|
uploadAndCacheAsync(finalImage, finalPath, imageCacheKey); |
|
|
|
|
|
|
|
|
// 异步上传到OSS(移除Redis缓存) |
|
|
|
|
|
uploadToOssAsync(finalImage, finalPath); |
|
|
|
|
|
|
|
|
return finalImage; |
|
|
return finalImage; |
|
|
} catch (Exception e) { |
|
|
} catch (Exception e) { |
|
|
@ -187,10 +176,10 @@ public class AppletApiWaterServiceImpl implements AppletApiWaterService { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 异步上传到OSS并缓存到Redis |
|
|
|
|
|
|
|
|
* 异步上传到OSS |
|
|
*/ |
|
|
*/ |
|
|
private void uploadAndCacheAsync(byte[] imageBytes, String ossPath, String cacheKey) { |
|
|
|
|
|
// 使用线程池异步执行上传和缓存操作 |
|
|
|
|
|
|
|
|
private void uploadToOssAsync(byte[] imageBytes, String ossPath) { |
|
|
|
|
|
// 使用线程池异步执行上传操作 |
|
|
CompletableFuture.runAsync(() -> { |
|
|
CompletableFuture.runAsync(() -> { |
|
|
try { |
|
|
try { |
|
|
// 上传到OSS |
|
|
// 上传到OSS |
|
|
@ -199,48 +188,28 @@ public class AppletApiWaterServiceImpl implements AppletApiWaterService { |
|
|
} catch (Exception e) { |
|
|
} catch (Exception e) { |
|
|
log.error("异步上传OSS失败,路径: {}", ossPath, e); |
|
|
log.error("异步上传OSS失败,路径: {}", ossPath, e); |
|
|
} |
|
|
} |
|
|
}, asyncExecutor).thenRunAsync(() -> { |
|
|
|
|
|
try { |
|
|
|
|
|
// 缓存到Redis,设置24小时过期 |
|
|
|
|
|
redisUtil.set(cacheKey, imageBytes, 24 * 60 * 60); |
|
|
|
|
|
log.info("异步缓存完成,key: {}", cacheKey); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
log.error("异步缓存失败,key: {}", cacheKey, e); |
|
|
|
|
|
} |
|
|
|
|
|
}, asyncExecutor); |
|
|
}, asyncExecutor); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public byte[] generateAndCombineImagesFromUrl2(byte[] qrCodeImageByte, String backgroundUrl) { |
|
|
|
|
|
|
|
|
public byte[] generateAndCombineImagesFromUrl2(byte[] qrCodeImageByte, String backgroundUrl, int qr_code_x, int qr_code_y) { |
|
|
File file = null; |
|
|
File file = null; |
|
|
try { |
|
|
try { |
|
|
// 缓存背景图片,避免重复下载 |
|
|
|
|
|
String bgCacheKey = "background_image:" + backgroundUrl.hashCode(); |
|
|
|
|
|
BufferedImage backgroundImage = (BufferedImage) redisUtil.get(bgCacheKey); |
|
|
|
|
|
|
|
|
|
|
|
if (backgroundImage == null) { |
|
|
|
|
|
// 从URL加载背景图像 |
|
|
|
|
|
URL backgroundImageUrl = new URL(backgroundUrl); |
|
|
|
|
|
backgroundImage = ImageIO.read(backgroundImageUrl); |
|
|
|
|
|
// 缓存背景图片,设置1小时过期 |
|
|
|
|
|
redisUtil.set(bgCacheKey, backgroundImage, 60 * 60); |
|
|
|
|
|
log.debug("背景图片已缓存"); |
|
|
|
|
|
} else { |
|
|
|
|
|
log.debug("使用缓存的背景图片"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 直接从URL加载背景图像,移除Redis缓存避免类型转换问题 |
|
|
|
|
|
URL backgroundImageUrl = new URL(backgroundUrl); |
|
|
|
|
|
BufferedImage backgroundImage = ImageIO.read(backgroundImageUrl); |
|
|
|
|
|
log.debug("从URL加载背景图片: {}", backgroundUrl); |
|
|
|
|
|
|
|
|
// 从字节数组加载小程序码图像 |
|
|
// 从字节数组加载小程序码图像 |
|
|
BufferedImage qrCodeImage = ImageIO.read(new ByteArrayInputStream(qrCodeImageByte)); |
|
|
BufferedImage qrCodeImage = ImageIO.read(new ByteArrayInputStream(qrCodeImageByte)); |
|
|
|
|
|
|
|
|
// 获取配置参数 |
|
|
|
|
|
int qr_code_y = appletConfigService.getContentByCodeAsInt("qr_code_y"); |
|
|
|
|
|
int qr_code_x = appletConfigService.getContentByCodeAsInt("qr_code_x"); |
|
|
|
|
|
|
|
|
// 使用传入的配置参数,避免重复调用配置服务 |
|
|
|
|
|
|
|
|
// 创建一个新的BufferedImage来保存合并后的图像(复制背景图片) |
|
|
|
|
|
|
|
|
// 直接使用背景图片作为基础,避免创建新的图像导致黑边 |
|
|
BufferedImage combinedImage = new BufferedImage( |
|
|
BufferedImage combinedImage = new BufferedImage( |
|
|
backgroundImage.getWidth(), |
|
|
backgroundImage.getWidth(), |
|
|
backgroundImage.getHeight(), |
|
|
backgroundImage.getHeight(), |
|
|
BufferedImage.TYPE_INT_RGB |
|
|
|
|
|
|
|
|
backgroundImage.getType() // 使用背景图片的原始类型,保持透明度 |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
Graphics2D g2d = combinedImage.createGraphics(); |
|
|
Graphics2D g2d = combinedImage.createGraphics(); |
|
|
@ -250,8 +219,8 @@ public class AppletApiWaterServiceImpl implements AppletApiWaterService { |
|
|
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); |
|
|
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); |
|
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
|
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
|
|
|
|
|
|
|
|
// 绘制背景图像 |
|
|
|
|
|
g2d.drawImage(backgroundImage, 0, 0, null); |
|
|
|
|
|
|
|
|
// 先绘制背景图像,完全覆盖画布 |
|
|
|
|
|
g2d.drawImage(backgroundImage, 0, 0, backgroundImage.getWidth(), backgroundImage.getHeight(), null); |
|
|
|
|
|
|
|
|
int wh = backgroundImage.getWidth() / 3; |
|
|
int wh = backgroundImage.getWidth() / 3; |
|
|
|
|
|
|
|
|
@ -259,7 +228,7 @@ public class AppletApiWaterServiceImpl implements AppletApiWaterService { |
|
|
int qrCodeX = (backgroundImage.getWidth() - wh) / 2; |
|
|
int qrCodeX = (backgroundImage.getWidth() - wh) / 2; |
|
|
int qrCodeY = (int) ((backgroundImage.getHeight() - wh) * 0.6); |
|
|
int qrCodeY = (int) ((backgroundImage.getHeight() - wh) * 0.6); |
|
|
|
|
|
|
|
|
// 绘制小程序码图像 |
|
|
|
|
|
|
|
|
// 绘制小程序码图像,使用传入的位置参数 |
|
|
g2d.drawImage(qrCodeImage, qrCodeX + qr_code_x, qrCodeY + qr_code_y, wh, wh, null); |
|
|
g2d.drawImage(qrCodeImage, qrCodeX + qr_code_x, qrCodeY + qr_code_y, wh, wh, null); |
|
|
|
|
|
|
|
|
// 释放Graphics2D资源 |
|
|
// 释放Graphics2D资源 |
|
|
|