From a8ad3423360b135c6ac3ea8e5eccec39f34a9bbc Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Fri, 19 Jun 2026 02:50:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=9B=86=E6=88=90=E7=AC=A6=E5=8F=B7?= =?UTF-8?q?=E5=8C=96=E6=9C=8D=E5=8A=A1=20-=20=E5=AE=9E=E7=8E=B0=20triggerS?= =?UTF-8?q?ymbolicationAsync?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 注入 SourcemapService 到 LogService - 实现 triggerSymbolicationAsync 调用符号化服务 - 支持 RN/Android/iOS/Flutter 堆栈符号化 - 新增 symbolicator.url 配置 Co-Authored-By: Claude --- .../xuqm/bugcollect/service/LogService.java | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/xuqm-bugcollect-service/src/main/java/com/xuqm/bugcollect/service/LogService.java b/xuqm-bugcollect-service/src/main/java/com/xuqm/bugcollect/service/LogService.java index 59882e7..656f4e6 100644 --- a/xuqm-bugcollect-service/src/main/java/com/xuqm/bugcollect/service/LogService.java +++ b/xuqm-bugcollect-service/src/main/java/com/xuqm/bugcollect/service/LogService.java @@ -31,19 +31,25 @@ public class LogService { private final LogIssueUserRepository issueUserRepository; private final LogEventRepository eventRepository; private final WebhookService webhookService; + private final SourcemapService sourcemapService; private final ObjectMapper objectMapper; + @org.springframework.beans.factory.annotation.Value("${symbolicator.url:http://xuqm-symbolicator:3000}") + private String symbolicatorUrl; + public LogService(LogIssueRepository issueRepository, LogIssueEventRepository issueEventRepository, LogIssueUserRepository issueUserRepository, LogEventRepository eventRepository, WebhookService webhookService, + SourcemapService sourcemapService, ObjectMapper objectMapper) { this.issueRepository = issueRepository; this.issueEventRepository = issueEventRepository; this.issueUserRepository = issueUserRepository; this.eventRepository = eventRepository; this.webhookService = webhookService; + this.sourcemapService = sourcemapService; this.objectMapper = objectMapper; } @@ -149,6 +155,79 @@ public class LogService { @Async void triggerSymbolicationAsync(Long issueId, String appKey, String platform, String release) { log.debug("Symbolication triggered issueId={} appKey={} platform={} release={}", issueId, appKey, platform, release); + + // 查找 sourcemap 文件 + String storageKey = sourcemapService.findSourceMap(appKey, platform, release, "index"); + if (storageKey == null) { + log.debug("No sourcemap found for appKey={} platform={} release={}", appKey, platform, release); + return; + } + + // 查找事件 + LogIssueEventEntity event = issueEventRepository.findById(issueId).orElse(null); + if (event == null || event.getStack() == null || event.getStack().isBlank()) { + return; + } + + try { + // 读取 sourcemap 内容 + String sourcemapContent = sourcemapService.readSourceMapContent(storageKey); + if (sourcemapContent == null) { + log.warn("Failed to read sourcemap content for storageKey={}", storageKey); + return; + } + + // 确定平台类型 + String symbolicatePlatform = mapPlatform(platform); + + // 调用符号化服务 + String symbolicated = callSymbolicator(symbolicatePlatform, event.getStack(), sourcemapContent); + if (symbolicated != null && !symbolicated.equals(event.getStack())) { + event.setStackSymbolicated(symbolicated); + issueEventRepository.save(event); + log.info("Symbolicated issueId={} platform={}", issueId, platform); + } + } catch (Exception e) { + log.error("Symbolication failed for issueId={}: {}", issueId, e.getMessage(), e); + } + } + + private String mapPlatform(String platform) { + if (platform == null) return "rn"; + return switch (platform.toLowerCase()) { + case "android" -> "android"; + case "ios" -> "ios"; + case "flutter" -> "flutter"; + default -> "rn"; // RN/Electron/Vue3 都使用 JS sourcemap + }; + } + + private String callSymbolicator(String platform, String stacktrace, String sourcemapContent) { + try { + org.springframework.web.client.RestTemplate restTemplate = new org.springframework.web.client.RestTemplate(); + java.util.Map request = java.util.Map.of( + "platform", platform, + "stacktrace", stacktrace, + "sourcemap", sourcemapContent + ); + + org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders(); + headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON); + org.springframework.http.HttpEntity> entity = new org.springframework.http.HttpEntity<>(request, headers); + + java.util.Map response = restTemplate.postForObject( + symbolicatorUrl + "/api/symbolicate", + entity, + java.util.Map.class + ); + + if (response != null && Boolean.TRUE.equals(response.get("success"))) { + return (String) response.get("symbolicated"); + } + } catch (Exception e) { + log.error("Symbolicator service call failed: {}", e.getMessage()); + } + return null; } @Transactional