From 6b2024b71ededd1b7921ae2f389ee943ab02412e Mon Sep 17 00:00:00 2001 From: matevip Date: Wed, 24 Jun 2026 18:06:45 +0800 Subject: [PATCH] fix(operational): guard blank provider/username keys to prevent export crash --- .../service/OperationalDataExportService.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/mateclaw-server/src/main/java/vip/mate/operational/service/OperationalDataExportService.java b/mateclaw-server/src/main/java/vip/mate/operational/service/OperationalDataExportService.java index 7f3234c5..8957e5c8 100644 --- a/mateclaw-server/src/main/java/vip/mate/operational/service/OperationalDataExportService.java +++ b/mateclaw-server/src/main/java/vip/mate/operational/service/OperationalDataExportService.java @@ -539,7 +539,8 @@ public class OperationalDataExportService { Map byDateMsg = new TreeMap<>(); for (MessageEntity m : msgs) { String date = m.getCreateTime().toLocalDate().toString(); - String prov = m.getRuntimeProvider() != null ? m.getRuntimeProvider() : "-"; + String prov = (m.getRuntimeProvider() != null && !m.getRuntimeProvider().isBlank()) + ? m.getRuntimeProvider() : "-"; String key = date + "|" + prov; long[] acc = byDateProv.computeIfAbsent(key, k -> new long[3]); acc[0] += m.getPromptTokens() != null ? m.getPromptTokens() : 0; @@ -550,7 +551,9 @@ public class OperationalDataExportService { int r = 1; for (Map.Entry e : byDateProv.entrySet()) { - String[] parts = e.getKey().split("\\|"); + // split with limit -1 keeps a trailing empty field (provider "-" guards + // blanks, but never rely on split dropping trailing segments). + String[] parts = e.getKey().split("\\|", -1); long[] v = e.getValue(); Row row = sheet.createRow(r++); createCell(row, 0, parts[0], null); @@ -665,7 +668,8 @@ public class OperationalDataExportService { Map userDuration = new LinkedHashMap<>(); Map userLastActive = new LinkedHashMap<>(); for (ConversationEntity c : convs) { - String key = (c.getWorkspaceId() != null ? c.getWorkspaceId() : 0) + "|" + (c.getUsername() != null ? c.getUsername() : "-"); + String key = (c.getWorkspaceId() != null ? c.getWorkspaceId() : 0) + "|" + + (c.getUsername() != null && !c.getUsername().isBlank() ? c.getUsername() : "-"); userConvIds.computeIfAbsent(key, k -> new HashSet<>()).add(c.getConversationId()); if (c.getCreateTime() != null && c.getLastActiveTime() != null) { userDuration.merge(key, (long) Duration.between(c.getCreateTime(), c.getLastActiveTime()).getSeconds(), Long::sum); @@ -693,7 +697,8 @@ public class OperationalDataExportService { // Aggregate by (ws, username) // Re-query to get the user mapping... For simplicity, aggregate in memory Map convWsMap = convs.stream().collect(Collectors.toMap( - ConversationEntity::getConversationId, c -> (c.getWorkspaceId() != null ? c.getWorkspaceId() : 0L) + "|" + c.getUsername(), (a, b) -> a)); + ConversationEntity::getConversationId, c -> (c.getWorkspaceId() != null ? c.getWorkspaceId() : 0L) + "|" + + (c.getUsername() != null && !c.getUsername().isBlank() ? c.getUsername() : "-"), (a, b) -> a)); for (MessageEntity m : msgs) { String key = convWsMap.get(m.getConversationId()); if (key == null) continue; @@ -710,7 +715,7 @@ public class OperationalDataExportService { int r = 1; for (String key : userConvIds.keySet()) { - String[] parts = key.split("\\|"); + String[] parts = key.split("\\|", -1); long wsId = Long.parseLong(parts[0]); String uname = parts[1]; long[] acc = userMap.getOrDefault(key, new long[5]);