diff --git a/src/main/java/com/sb/web/account/service/AccountService.java b/src/main/java/com/sb/web/account/service/AccountService.java index 71a74a5..80590ee 100644 --- a/src/main/java/com/sb/web/account/service/AccountService.java +++ b/src/main/java/com/sb/web/account/service/AccountService.java @@ -277,10 +277,9 @@ public class AccountService { return cards.size() == 1 ? cards.get(0).getId() : null; } - /** 양방향 부분일치 (KB국민 ↔ 국민 등) */ + /** 표기 차이를 흡수한 카드사 매칭 (KB국민 ↔ 국민 ↔ 국민카드 ↔ KB카드 등) */ private boolean overlaps(String walletField, String issuer) { - if (walletField == null || issuer == null) return false; - return walletField.contains(issuer) || issuer.contains(walletField); + return CardIssuerMatcher.matches(walletField, issuer); } /* ===================== 상환/납부 ===================== */ diff --git a/src/main/java/com/sb/web/account/service/CardIssuerMatcher.java b/src/main/java/com/sb/web/account/service/CardIssuerMatcher.java new file mode 100644 index 0000000..39ad78f --- /dev/null +++ b/src/main/java/com/sb/web/account/service/CardIssuerMatcher.java @@ -0,0 +1,44 @@ +package com.sb.web.account.service; + +/** + * 카드사명 유연 매칭. + * 알림에서 감지한 카드사와 사용자가 등록한 카드의 「카드사/이름」을, 표기 차이 + * (KB / 국민 / 국민카드 / KB카드, 농협 / NH, 비씨 / BC 등)에 관계없이 같은 카드사로 인식한다. + */ +public final class CardIssuerMatcher { + + private CardIssuerMatcher() { + } + + /** 두 표기가 같은 카드사를 가리키면 true */ + public static boolean matches(String walletField, String issuer) { + String a = canon(walletField); + String b = canon(issuer); + if (a.isEmpty() || b.isEmpty()) { + return false; + } + return a.equals(b) || a.contains(b) || b.contains(a); + } + + /** 공백·접미사(카드/은행/페이/뱅크) 제거 + 동일 카드사 별칭 통합 후 소문자 키 */ + static String canon(String s) { + if (s == null) { + return ""; + } + String x = s.replaceAll("\\s+", "").toLowerCase() + .replace("카드", "").replace("은행", "").replace("페이", "").replace("뱅크", ""); + if (x.contains("kb") || x.contains("국민")) { + return "국민"; + } + if (x.contains("nh") || x.contains("농협")) { + return "농협"; + } + if (x.equals("bc") || x.contains("비씨")) { + return "비씨"; + } + if (x.contains("씨티") || x.contains("시티") || x.contains("citi")) { + return "씨티"; + } + return x; + } +} diff --git a/src/test/java/com/sb/web/account/service/CardIssuerMatcherTest.java b/src/test/java/com/sb/web/account/service/CardIssuerMatcherTest.java new file mode 100644 index 0000000..9fba736 --- /dev/null +++ b/src/test/java/com/sb/web/account/service/CardIssuerMatcherTest.java @@ -0,0 +1,52 @@ +package com.sb.web.account.service; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 카드사명 유연 매칭 단위테스트. + * 사용자가 카드를 어떻게 등록했든(KB카드/국민카드/KB국민/국민) 같은 카드사로 인식되어야 한다. + */ +class CardIssuerMatcherTest { + + @Test + @DisplayName("KB국민카드: 다양한 표기 모두 매칭") + void kbVariants() { + // 알림에서 감지하는 카드사 = "KB국민" + assertThat(CardIssuerMatcher.matches("KB카드", "KB국민")).isTrue(); + assertThat(CardIssuerMatcher.matches("국민카드", "KB국민")).isTrue(); + assertThat(CardIssuerMatcher.matches("KB국민", "KB국민")).isTrue(); + assertThat(CardIssuerMatcher.matches("국민", "KB국민")).isTrue(); + assertThat(CardIssuerMatcher.matches("KB국민카드", "KB국민")).isTrue(); + } + + @Test + @DisplayName("일반 카드사: 접미사 차이 흡수") + void commonIssuers() { + assertThat(CardIssuerMatcher.matches("삼성카드", "삼성")).isTrue(); + assertThat(CardIssuerMatcher.matches("현대", "현대")).isTrue(); + assertThat(CardIssuerMatcher.matches("신한카드", "신한")).isTrue(); + assertThat(CardIssuerMatcher.matches("롯데 카드", "롯데")).isTrue(); + } + + @Test + @DisplayName("별칭 통합: 농협/NH, 비씨/BC") + void aliases() { + assertThat(CardIssuerMatcher.matches("NH농협", "농협")).isTrue(); + assertThat(CardIssuerMatcher.matches("농협카드", "NH")).isTrue(); + assertThat(CardIssuerMatcher.matches("BC카드", "비씨")).isTrue(); + assertThat(CardIssuerMatcher.matches("비씨", "BC")).isTrue(); + } + + @Test + @DisplayName("다른 카드사/빈값은 매칭되지 않음") + void noMatch() { + assertThat(CardIssuerMatcher.matches("삼성카드", "현대")).isFalse(); + assertThat(CardIssuerMatcher.matches("국민카드", "삼성")).isFalse(); + assertThat(CardIssuerMatcher.matches("", "KB국민")).isFalse(); + assertThat(CardIssuerMatcher.matches(null, "국민")).isFalse(); + assertThat(CardIssuerMatcher.matches("삼성", null)).isFalse(); + } +}