From 83041e94902428df0ec43a4fcb4c6ea1a05bfb85 Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Sat, 28 Feb 2026 14:56:30 +0800 Subject: [PATCH] =?UTF-8?q?fix(aiot):=20=E4=BF=AE=E5=A4=8D=E6=88=AA?= =?UTF-8?q?=E5=9B=BE=E4=BB=A3=E7=90=86=E4=B8=A4=E4=B8=AAbug=EF=BC=9A?= =?UTF-8?q?=E7=AB=9E=E6=80=81=E6=9D=A1=E4=BB=B6+URL=E5=8F=8C=E9=87=8D?= =?UTF-8?q?=E7=BC=96=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. handleCallback先写Redis缓存再complete Future, 避免等待线程被唤醒后读取缓存为空的竞态条件 2. RestTemplate使用URI.create()传入预签名URL, 避免对已编码的%3B等字符做二次编码导致COS 403 Co-Authored-By: Claude Opus 4.6 --- .../service/impl/AiScreenshotServiceImpl.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiScreenshotServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiScreenshotServiceImpl.java index d8e4a224b..129f95539 100644 --- a/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiScreenshotServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiScreenshotServiceImpl.java @@ -13,6 +13,7 @@ import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; +import java.net.URI; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.HashMap; @@ -161,6 +162,14 @@ public class AiScreenshotServiceImpl implements IAiScreenshotService { return; } + // 先写 Redis 缓存,再唤醒等待线程(避免竞态:线程被唤醒后立即读缓存但缓存还没写入) + String cameraCode = (String) data.get("camera_code"); + String status = (String) data.get("status"); + if ("ok".equals(status) && cameraCode != null) { + String url = (String) data.get("url"); + writeCache(cameraCode, url); + } + CompletableFuture> future = pendingRequests.get(requestId); if (future != null) { future.complete(data); @@ -168,14 +177,6 @@ public class AiScreenshotServiceImpl implements IAiScreenshotService { } else { log.warn("[AI截图] 回调未找到对应请求(可能已超时): requestId={}", requestId); } - - // 写入 Redis 缓存(无论 future 是否存在,缓存都应更新) - String cameraCode = (String) data.get("camera_code"); - String status = (String) data.get("status"); - if ("ok".equals(status) && cameraCode != null) { - String url = (String) data.get("url"); - writeCache(cameraCode, url); - } } /** @@ -217,8 +218,9 @@ public class AiScreenshotServiceImpl implements IAiScreenshotService { return null; } + // 使用 URI.create 避免 RestTemplate 对已编码的预签名 URL 做二次编码 RestTemplate restTemplate = new RestTemplate(); - byte[] imageBytes = restTemplate.getForObject(cosUrl, byte[].class); + byte[] imageBytes = restTemplate.getForObject(URI.create(cosUrl), byte[].class); log.debug("[AI截图] 代理图片成功: cameraCode={}, size={}", cameraCode, imageBytes != null ? imageBytes.length : 0); return imageBytes; } catch (Exception e) {