feat: 알림 분류 — 카드 승인/취소 + 은행 입금/출금·이체 구분
- 파서: 입금→수입, 출금/이체→지출, 카드 승인→지출, 카드 취소/환불→수입(환불) 은행 거래는 은행(BANK) 지갑에, 카드는 카드(CARD) 지갑에 매칭(matchWalletId 일반화) - createFromNotification: 파싱된 수입/지출 타입·지갑종류로 생성 - 파서 테스트 갱신(은행 입금/출금 케이스 포함) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
55c47a43e6
commit
31590343cf
@@ -220,20 +220,21 @@ public class AccountService {
|
||||
public AccountEntryResponse createFromNotification(NotificationRequest req, Long memberId) {
|
||||
CardNotificationParser.Parsed p =
|
||||
notificationParser.parse(req.getTitle(), req.getText(), java.time.LocalDate.now());
|
||||
if (!p.isCard() || p.isCanceled() || p.getAmount() <= 0) {
|
||||
return null; // 결제 승인 알림 아님 / 취소 → 무시
|
||||
if (!p.isRecognized() || p.getAmount() <= 0) {
|
||||
return null; // 결제/이체 알림 아님 → 무시
|
||||
}
|
||||
// 중복방지
|
||||
if (p.getNotifKey() != null && mapper.findByMemberAndNotifKey(memberId, p.getNotifKey()) != null) {
|
||||
return null;
|
||||
}
|
||||
// 카드사 → 등록된 카드(CARD) 매칭
|
||||
Long walletId = matchCardWalletId(p.getIssuer(), memberId);
|
||||
// 카드 결제 → 카드(CARD) 매칭 / 은행 입출금·이체 → 은행(BANK) 매칭
|
||||
String walletType = p.isBankTransfer() ? "BANK" : "CARD";
|
||||
Long walletId = matchWalletId(p.getIssuer(), memberId, walletType);
|
||||
|
||||
AccountEntry entry = AccountEntry.builder()
|
||||
.memberId(memberId)
|
||||
.entryDate(p.getDate() != null ? p.getDate() : java.time.LocalDate.now())
|
||||
.type("EXPENSE")
|
||||
.type(p.isIncome() ? "INCOME" : "EXPENSE")
|
||||
.category(null)
|
||||
.amount(p.getAmount())
|
||||
.memo(p.getMerchant())
|
||||
@@ -260,21 +261,21 @@ public class AccountService {
|
||||
return toResponse(mapper.findByIdAndMember(id, memberId));
|
||||
}
|
||||
|
||||
/** 카드사명으로 등록된 CARD 지갑 id 찾기 (issuer/name 양방향 부분일치). 미매칭이면 카드가 1개뿐일 때 그 카드로. */
|
||||
private Long matchCardWalletId(String issuer, Long memberId) {
|
||||
List<Wallet> cards = walletMapper.findByMember(memberId).stream()
|
||||
.filter(w -> "CARD".equals(w.getType()))
|
||||
/** 카드사/은행명으로 등록된 해당 type(CARD/BANK) 지갑 id 찾기 (issuer/name 유연 매칭).
|
||||
* 미매칭이라도 그 type 지갑이 정확히 1개면 그 지갑으로 기본 매칭. */
|
||||
private Long matchWalletId(String issuer, Long memberId, String walletType) {
|
||||
List<Wallet> wallets = walletMapper.findByMember(memberId).stream()
|
||||
.filter(w -> walletType.equals(w.getType()))
|
||||
.toList();
|
||||
if (issuer != null) {
|
||||
Long matched = cards.stream()
|
||||
Long matched = wallets.stream()
|
||||
.filter(w -> overlaps(w.getIssuer(), issuer) || overlaps(w.getName(), issuer))
|
||||
.map(Wallet::getId)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (matched != null) return matched;
|
||||
}
|
||||
// 카드사 미감지/미매칭이라도 등록된 카드가 정확히 1개면 그 카드로 기본 매칭
|
||||
return cards.size() == 1 ? cards.get(0).getId() : null;
|
||||
return wallets.size() == 1 ? wallets.get(0).getId() : null;
|
||||
}
|
||||
|
||||
/** 표기 차이를 흡수한 카드사 매칭 (KB국민 ↔ 국민 ↔ 국민카드 ↔ KB카드 등) */
|
||||
|
||||
Reference in New Issue
Block a user