feat: 카드 결제 알림 자동인식(미확인 내역) 백엔드
- account_entry pending/notif_key 컬럼 추가(멱등 마이그레이션)
- CardNotificationParser: 카드사·금액·가맹점·승인/취소 파싱
- POST /entries/notification(미확인 지출 생성, 중복방지, 카드 자동매칭)
- POST /entries/{id}/confirm(확정), GET /entries/pending/count
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
c1f97364aa
commit
8fec057752
@@ -6,6 +6,8 @@ import com.sb.web.account.domain.Wallet;
|
||||
import com.sb.web.account.dto.AccountEntryRequest;
|
||||
import com.sb.web.account.dto.AccountEntryResponse;
|
||||
import com.sb.web.account.dto.AccountSummary;
|
||||
import com.sb.web.account.dto.ConfirmRequest;
|
||||
import com.sb.web.account.dto.NotificationRequest;
|
||||
import com.sb.web.account.dto.AccountTagRequest;
|
||||
import com.sb.web.account.dto.AccountTagResponse;
|
||||
import com.sb.web.account.dto.CategoryStat;
|
||||
@@ -43,6 +45,7 @@ public class AccountService {
|
||||
private final AccountTagMapper tagMapper;
|
||||
private final WalletMapper walletMapper;
|
||||
private final InvestService investService;
|
||||
private final CardNotificationParser notificationParser;
|
||||
|
||||
/* ===================== 항목 ===================== */
|
||||
|
||||
@@ -202,6 +205,73 @@ public class AccountService {
|
||||
mapper.delete(id, memberId);
|
||||
}
|
||||
|
||||
/* ===================== 카드 결제 알림 자동인식 ===================== */
|
||||
|
||||
/** 미확인(확인 필요) 내역 개수 */
|
||||
public int countPending(Long memberId) {
|
||||
return mapper.countPending(memberId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 카드 결제 푸시 알림 → 미확인(pending) 지출 내역 생성.
|
||||
* 결제 알림이 아니거나 중복이면 null 반환(생성 안 함). 취소 알림도 일단 무시(v1).
|
||||
*/
|
||||
@Transactional
|
||||
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.getNotifKey() != null && mapper.findByMemberAndNotifKey(memberId, p.getNotifKey()) != null) {
|
||||
return null;
|
||||
}
|
||||
// 카드사 → 등록된 카드(CARD) 매칭
|
||||
Long walletId = matchCardWalletId(p.getIssuer(), memberId);
|
||||
|
||||
AccountEntry entry = AccountEntry.builder()
|
||||
.memberId(memberId)
|
||||
.entryDate(p.getDate() != null ? p.getDate() : java.time.LocalDate.now())
|
||||
.type("EXPENSE")
|
||||
.category(null)
|
||||
.amount(p.getAmount())
|
||||
.memo(p.getMerchant())
|
||||
.walletId(walletId)
|
||||
.toWalletId(null)
|
||||
.installmentMonths(null)
|
||||
.pending(true)
|
||||
.notifKey(p.getNotifKey())
|
||||
.build();
|
||||
mapper.insert(entry);
|
||||
return toResponse(mapper.findByIdAndMember(entry.getId(), memberId));
|
||||
}
|
||||
|
||||
/** 미확인 내역 확정 (분류/계좌 보정 후 pending 해제) */
|
||||
@Transactional
|
||||
public AccountEntryResponse confirm(Long id, ConfirmRequest req, Long memberId) {
|
||||
AccountEntry e = mustFind(id, memberId);
|
||||
if (!Boolean.TRUE.equals(e.getPending())) {
|
||||
return toResponse(e); // 이미 확정된 건
|
||||
}
|
||||
String category = req != null ? req.getCategory() : null;
|
||||
Long walletId = req != null ? req.getWalletId() : null;
|
||||
mapper.confirm(id, memberId, category, walletId);
|
||||
return toResponse(mapper.findByIdAndMember(id, memberId));
|
||||
}
|
||||
|
||||
/** 카드사명으로 등록된 CARD 지갑 id 찾기 (issuer/name 부분일치) */
|
||||
private Long matchCardWalletId(String issuer, Long memberId) {
|
||||
if (issuer == null) return null;
|
||||
return walletMapper.findByMember(memberId).stream()
|
||||
.filter(w -> "CARD".equals(w.getType()))
|
||||
.filter(w -> (w.getIssuer() != null && w.getIssuer().contains(issuer))
|
||||
|| (w.getName() != null && w.getName().contains(issuer)))
|
||||
.map(Wallet::getId)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/* ===================== 상환/납부 ===================== */
|
||||
|
||||
/** 대출/카드 상환·납부: 원금은 이체(부채↓), 이자는 지출로 분리해 2건 생성 */
|
||||
|
||||
Reference in New Issue
Block a user