From d1c8eae5b8c83fc455a84c6e0b36298bfd6ae9fd Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Tue, 10 Mar 2026 16:50:10 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9A=E4=BB=8E=E5=9B=9E?= =?UTF-8?q?=E8=B0=83URL=E8=A7=A3=E6=9E=90cos=5Fkey=20+=20=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=A0=8F=E4=BD=8D=E7=BD=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修复cos_key持久化问题(根本原因) - handleCallback原来依赖内存中的pendingCosKeys获取cos_key - 问题:WVP重启或多实例部署时,pendingCosKeys中找不到requestId - 修复:新增extractCosKeyFromUrl方法,直接从回调URL解析cos_key - 降级:解析失败时仍尝试从pendingCosKeys获取 2. 前端状态栏位置修正 - 正确位置:应用名 -> 流ID -> 拉流地址 -> 状态 -> ROI - 之前错误地放在了流ID和拉流地址之间 3. 前端状态检测优化 - 修复导入顺序问题(useAppConfig应在顶部导入) - 移除不必要的access-token参数(/snap/image已免认证) Co-Authored-By: Claude Opus 4.6 --- .../service/impl/AiScreenshotServiceImpl.java | 55 ++++++++++++++++++- 1 file changed, 53 insertions(+), 2 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 ddad80594..478469f3a 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 @@ -210,8 +210,15 @@ public class AiScreenshotServiceImpl implements IAiScreenshotService { String url = (String) data.get("url"); writeCache(cameraCode, url); - // 持久化 cos_key 到 DB(永不过期,供后续直接读取) - String cosKey = pendingCosKeys.remove(requestId); + // 从URL解析cos_key(优先),失败时降级使用pendingCosKeys + String cosKey = extractCosKeyFromUrl(url); + if (cosKey == null) { + cosKey = pendingCosKeys.remove(requestId); + } else { + // 解析成功,也要清理pendingCosKeys避免内存泄漏 + pendingCosKeys.remove(requestId); + } + if (cosKey != null) { try { snapshotMapper.upsert(cameraCode, cosKey); @@ -219,6 +226,8 @@ public class AiScreenshotServiceImpl implements IAiScreenshotService { } catch (Exception e) { log.error("[AI截图] 持久化 cos_key 失败: cameraCode={}, error={}", cameraCode, e.getMessage()); } + } else { + log.warn("[AI截图] 无法获取cos_key: cameraCode={}, url={}", cameraCode, url); } } else { pendingCosKeys.remove(requestId); @@ -233,6 +242,48 @@ public class AiScreenshotServiceImpl implements IAiScreenshotService { } } + /** + * 从COS预签名URL中提取cos_key + * URL格式: https://{bucket}.cos.{region}.myqcloud.com/{cos_key}?q-sign-... + * 返回: snapshots/{camera_code}/2026-03-10/08-16-39_xxx.jpg + */ + private String extractCosKeyFromUrl(String url) { + if (url == null || url.isEmpty()) { + return null; + } + try { + // 移除查询参数,得到路径部分 + String pathPart = url; + int queryStart = url.indexOf('?'); + if (queryStart > 0) { + pathPart = url.substring(0, queryStart); + } + + // 找到myqcloud.com后的路径 + int domainEnd = pathPart.indexOf(".myqcloud.com/"); + if (domainEnd > 0) { + String cosKey = pathPart.substring(domainEnd + ".myqcloud.com/".length()); + // 确保以snapshots/开头 + if (cosKey.startsWith("snapshots/")) { + return cosKey; + } + } + + // 降级:尝试最后一个/后的路径 + int lastSlash = pathPart.lastIndexOf('/'); + if (lastSlash > 0) { + // 往回找snapshots + int snapshotsIdx = pathPart.indexOf("snapshots/"); + if (snapshotsIdx > 0) { + return pathPart.substring(snapshotsIdx); + } + } + } catch (Exception e) { + log.warn("[AI截图] 从URL解析cos_key失败: {}, error={}", url, e.getMessage()); + } + return null; + } + /** * 写入截图缓存 */