diff --git a/src/main/java/kst4contest/controller/ScoreService.java b/src/main/java/kst4contest/controller/ScoreService.java index f0a191b..6571cfa 100644 --- a/src/main/java/kst4contest/controller/ScoreService.java +++ b/src/main/java/kst4contest/controller/ScoreService.java @@ -227,12 +227,11 @@ public final class ScoreService { ChatMember chosen = null; if (preferredCat != null) { - for (ChatMember v : variants) { - if (v != null && v.getChatCategory() == preferredCat) { - chosen = v; - break; - } - } + chosen = variants.stream() + .filter(Objects::nonNull) + .filter(v -> isSameChatCategory(v.getChatCategory(), preferredCat)) + .max(Comparator.comparingLong(ChatMember::getActivityTimeLastInEpoch)) + .orElse(null); } if (chosen == null) { @@ -247,6 +246,14 @@ public final class ScoreService { return representative; } + + private static boolean isSameChatCategory(ChatCategory left, ChatCategory right) { + return left != null + && right != null + && left.getCategoryNumber() == right.getCategoryNumber(); + } + + /** * Projects the immutable score snapshot back into ChatMember display fields so * the normal station table can sort/filter by score without knowing the score diff --git a/src/main/java/kst4contest/view/Kst4ContestApplication.java b/src/main/java/kst4contest/view/Kst4ContestApplication.java index b64dcd2..2cc73e9 100644 --- a/src/main/java/kst4contest/view/Kst4ContestApplication.java +++ b/src/main/java/kst4contest/view/Kst4ContestApplication.java @@ -4291,32 +4291,24 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL } - private ChatMember resolveChatMemberForTopCandidate(kst4contest.controller.ScoreService.TopCandidate c) { + private ChatMember resolveChatMemberForTopCandidate( + kst4contest.controller.ScoreService.TopCandidate c + ) { - String callRaw = c.getCallSignRaw(); - ChatCategory preferredCategory = c.getPreferredChatCategory(); - - // 1) Prefer exact (callRaw + category) match - synchronized (chatcontroller.getLst_chatMemberList()) { - for (ChatMember m : chatcontroller.getLst_chatMemberList()) { - if (m == null) continue; - if (m.getCallSignRaw() == null) continue; - if (!m.getCallSignRaw().equalsIgnoreCase(callRaw)) continue; - - if (preferredCategory != null && preferredCategory.equals(m.getChatCategory())) { - return m; - } - } - - // 2) Fallback: any variant with the same callsignRaw - for (ChatMember m : chatcontroller.getLst_chatMemberList()) { - if (m == null) continue; - if (m.getCallSignRaw() == null) continue; - if (m.getCallSignRaw().equalsIgnoreCase(callRaw)) return m; - } + if (c == null + || c.getDisplayCallSign() == null + || c.getPreferredChatCategory() == null) { + return null; } - return null; + /* + * Resolve the concrete active login by full callsign and category. Falling + * back to callSignRaw could select a different suffix in the same category. + */ + return chatcontroller.findActiveChatMember( + c.getDisplayCallSign(), + c.getPreferredChatCategory() + ); }