diff --git a/src/main/java/org/umc/valuedi/domain/asset/repository/bank/bankAccount/BankAccountRepository.java b/src/main/java/org/umc/valuedi/domain/asset/repository/bank/bankAccount/BankAccountRepository.java index d817e779..77032a4a 100644 --- a/src/main/java/org/umc/valuedi/domain/asset/repository/bank/bankAccount/BankAccountRepository.java +++ b/src/main/java/org/umc/valuedi/domain/asset/repository/bank/bankAccount/BankAccountRepository.java @@ -2,6 +2,10 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.umc.valuedi.domain.asset.entity.BankAccount; +import org.umc.valuedi.domain.connection.entity.CodefConnection; + +import java.util.List; public interface BankAccountRepository extends JpaRepository, BankAccountRepositoryCustom { + List findByCodefConnection(CodefConnection codefConnection); } diff --git a/src/main/java/org/umc/valuedi/domain/asset/service/command/AssetSyncService.java b/src/main/java/org/umc/valuedi/domain/asset/service/command/AssetSyncService.java index 55a6e611..d65e91c7 100644 --- a/src/main/java/org/umc/valuedi/domain/asset/service/command/AssetSyncService.java +++ b/src/main/java/org/umc/valuedi/domain/asset/service/command/AssetSyncService.java @@ -1,10 +1,10 @@ package org.umc.valuedi.domain.asset.service.command; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.umc.valuedi.domain.asset.entity.BankAccount; import org.umc.valuedi.domain.asset.entity.BankTransaction; @@ -12,8 +12,8 @@ import org.umc.valuedi.domain.asset.entity.CardApproval; import org.umc.valuedi.domain.asset.repository.bank.bankAccount.BankAccountRepository; import org.umc.valuedi.domain.asset.repository.bank.bankTransaction.BankTransactionRepository; -import org.umc.valuedi.domain.asset.repository.card.cardApproval.CardApprovalRepository; import org.umc.valuedi.domain.asset.repository.card.card.CardRepository; +import org.umc.valuedi.domain.asset.repository.card.cardApproval.CardApprovalRepository; import org.umc.valuedi.domain.connection.entity.CodefConnection; import org.umc.valuedi.domain.connection.enums.BusinessType; import org.umc.valuedi.domain.ledger.service.command.LedgerSyncService; @@ -26,7 +26,6 @@ @Slf4j @Service @RequiredArgsConstructor -@Transactional public class AssetSyncService { private static final int DEFAULT_SYNC_PERIOD_MONTHS = 3; @@ -37,145 +36,154 @@ public class AssetSyncService { private final CardRepository cardRepository; private final CardApprovalRepository cardApprovalRepository; private final LedgerSyncService ledgerSyncService; - private final ObjectMapper objectMapper; /** * 금융사 종류에 따라 자산 동기화 분기 */ public void syncAssets(CodefConnection connection) { + log.info("[AssetSync] 시작 - Connection ID: {}, Org: {}", connection.getId(), connection.getOrganization()); + if (connection.getBusinessType() == BusinessType.BK) { syncBankAssets(connection); } else if (connection.getBusinessType() == BusinessType.CD) { syncCardAssets(connection); } + + log.info("[AssetSync] 완료 - Connection ID: {}", connection.getId()); } /** * 은행 관련 자산 동기화 (계좌 목록, 거래 내역) */ private void syncBankAssets(CodefConnection connection) { - log.info("은행 자산 동기화 시작 - Connection ID: {}", connection.getId()); - - // 보유 계좌 목록 조회 및 저장 + // API 호출 (트랜잭션 없음) List accounts = codefAssetService.getBankAccounts(connection); - - try { - bankAccountRepository.saveAll(accounts); - } catch (DataIntegrityViolationException e) { - log.warn("계좌 저장 중 중복 발생 - 개별 저장 시도"); - for (BankAccount account : accounts) { - try { - bankAccountRepository.save(account); - } catch (DataIntegrityViolationException ex) { - // 이미 존재하는 계좌는 무시 - } - } - } - log.info("보유 계좌 목록 동기화 완료 - {}개 계좌", accounts.size()); + if (accounts.isEmpty()) return; - // 각 계좌별 거래 내역 조회 및 저장 - boolean anyUpdated = false; - for (BankAccount account : accounts) { - if (syncBankTransactions(connection, account)) { - anyUpdated = true; + // DB 저장 (별도 트랜잭션) + saveBankAccounts(accounts); + + // 동기화된 모든 계좌 조회 + List allAccounts = bankAccountRepository.findByCodefConnection(connection); + int updatedAccountCount = 0; + + for (BankAccount account : allAccounts) { + if (syncBankTransactionsForAccount(connection, account)) { + updatedAccountCount++; } } - if (anyUpdated) { + // 가계부 동기화 (필요시) + if (updatedAccountCount > 0) { syncLedger(connection.getMember()); } - - log.info("은행 자산 동기화 완료 - Connection ID: {}", connection.getId()); + + log.info("[AssetSync] 은행 동기화 요약 - 계좌: {}개, 거래내역 업데이트 계좌: {}개", allAccounts.size(), updatedAccountCount); } /** * 카드 관련 자산 동기화 (카드 목록, 승인 내역) */ private void syncCardAssets(CodefConnection connection) { - log.info("카드사 자산 동기화 시작 - Connection ID: {}", connection.getId()); - - // 보유 카드 목록 조회 및 저장 + // API 호출 (트랜잭션 없음) List cards = codefAssetService.getCards(connection); - - try { - cardRepository.saveAll(cards); - } catch (DataIntegrityViolationException e) { - log.warn("카드 저장 중 중복 발생 - 개별 저장 시도"); - for (Card card : cards) { - try { - cardRepository.save(card); - } catch (DataIntegrityViolationException ex) { - // 이미 존재하는 카드는 무시 - } - } - } - log.info("보유 카드 목록 동기화 완료 - {}개 카드", cards.size()); + if (cards.isEmpty()) return; + + // DB 저장 (별도 트랜잭션) + saveCards(cards); + + // API 호출 및 DB 저장: 카드 승인 내역 동기화 + boolean approvalsSynced = syncCardApprovals(connection); - // 전체 승인 내역 조회 및 카드 매칭 후 저장 - if (syncCardApprovals(connection)) { + // 가계부 동기화 (필요시) + if (approvalsSynced) { syncLedger(connection.getMember()); } - log.info("카드사 자산 동기화 완료 - Connection ID: {}", connection.getId()); + log.info("[AssetSync] 카드 동기화 요약 - 카드: {}개, 승인내역 업데이트: {}", cards.size(), approvalsSynced ? "성공" : "없음"); } /** - * 가계부 동기화 헬퍼 메서드 + * 특정 계좌의 거래 내역 동기화 (트랜잭션 없음) */ - private void syncLedger(Member member) { - // 기존 syncTransactions 대신 rebuildLedger 호출 - // 범위: 최근 3개월 (기존 정책 유지) - ledgerSyncService.rebuildLedger(member, LocalDate.now().minusMonths(DEFAULT_SYNC_PERIOD_MONTHS), LocalDate.now()); + private boolean syncBankTransactionsForAccount(CodefConnection connection, BankAccount account) { + // API 호출 + List transactions = codefAssetService.getBankTransactions(connection, account); + if (transactions.isEmpty()) return false; + + // DB 저장 (별도 트랜잭션) + saveBankTransactions(transactions); + return true; } /** - * 특정 계좌의 거래 내역 동기화 + * 카드 승인 내역 동기화 (트랜잭션 없음) */ - private boolean syncBankTransactions(CodefConnection connection, BankAccount account) { - log.info("계좌 거래내역 동기화 시작 - Account: {}", account.getAccountDisplay()); - - List transactions = codefAssetService.getBankTransactions(connection, account); - - if (transactions.isEmpty()) { - return false; - } + private boolean syncCardApprovals(CodefConnection connection) { + List cards = cardRepository.findByCodefConnection(connection); + if (cards.isEmpty()) return false; - bankTransactionRepository.bulkInsert(transactions); - log.info("계좌 거래내역 Bulk Insert 완료 - {}건", transactions.size()); + // API 호출: 3개월치 승인 내역 조회 (트랜잭션 없음) + LocalDate endDate = LocalDate.now(); + LocalDate startDate = endDate.minusMonths(DEFAULT_SYNC_PERIOD_MONTHS); + + List approvals = codefAssetService.getCardApprovals(connection, cards, startDate, endDate); + if (approvals.isEmpty()) return false; + + // DB 저장 (별도 트랜잭션) + saveCardApprovals(approvals); return true; } /** - * 카드 승인 내역 동기화 (전체 조회 후 매칭) + * 가계부 동기화 헬퍼 메서드 */ - private boolean syncCardApprovals(CodefConnection connection) { - log.info("카드 승인내역 동기화 시작 - Connection ID: {}", connection.getId()); + private void syncLedger(Member member) { + // LedgerSyncService의 rebuildLedger는 이미 @Transactional 이므로 그대로 호출 + ledgerSyncService.rebuildLedger(member, LocalDate.now().minusMonths(DEFAULT_SYNC_PERIOD_MONTHS), LocalDate.now()); + } - // 해당 연동의 모든 카드 목록 조회 (DB) - List cards = cardRepository.findByCodefConnection(connection); - if (cards.isEmpty()) { - log.warn("연동된 카드가 없어 승인내역 동기화를 건너뜁니다."); - return false; - } - try { + // --- DB 저장을 위한 트랜잭션 메서드 --- - connection.getCardList().clear(); - connection.getCardList().addAll(cards); - } catch (Exception e) { - log.warn("Connection 객체의 카드 리스트 갱신 중 오류 (무시하고 진행): {}", e.getMessage()); + @Transactional(propagation = Propagation.REQUIRES_NEW) + public void saveBankAccounts(List accounts) { + if (accounts.isEmpty()) return; + try { + bankAccountRepository.saveAll(accounts); + } catch (DataIntegrityViolationException e) { + for (BankAccount account : accounts) { + try { + bankAccountRepository.save(account); + } catch (DataIntegrityViolationException ex) { + // 이미 존재하는 계좌는 무시 + } + } } + } - // 전체 승인 내역 조회 (API) - // CodefAssetService 내부에서 CodefAssetConverter를 통해 매칭까지 완료된 리스트 반환 - List approvals = codefAssetService.getCardApprovals(connection); - if (approvals.isEmpty()) { - log.info("조회된 승인내역이 없습니다."); - return false; + @Transactional(propagation = Propagation.REQUIRES_NEW) + public void saveBankTransactions(List transactions) { + bankTransactionRepository.bulkInsert(transactions); + } + + @Transactional(propagation = Propagation.REQUIRES_NEW) + public void saveCards(List cards) { + if (cards.isEmpty()) return; + try { + cardRepository.saveAll(cards); + } catch (DataIntegrityViolationException e) { + for (Card card : cards) { + try { + cardRepository.save(card); + } catch (DataIntegrityViolationException ex) { + // 이미 존재하는 카드는 무시 + } + } } + } - // 저장 + @Transactional(propagation = Propagation.REQUIRES_NEW) + public void saveCardApprovals(List approvals) { cardApprovalRepository.bulkInsert(approvals); - log.info("카드 승인내역 Bulk Insert 완료 - {}건", approvals.size()); - return true; } } diff --git a/src/main/java/org/umc/valuedi/domain/connection/service/command/ConnectionCommandService.java b/src/main/java/org/umc/valuedi/domain/connection/service/command/ConnectionCommandService.java index 1e824c3d..6d50036e 100644 --- a/src/main/java/org/umc/valuedi/domain/connection/service/command/ConnectionCommandService.java +++ b/src/main/java/org/umc/valuedi/domain/connection/service/command/ConnectionCommandService.java @@ -32,20 +32,23 @@ public class ConnectionCommandService { * 금융사 계정 연동 */ public void connect(Long memberId, ConnectionReqDTO.Connect request) { + log.info("[ConnectionCommandService] [connect] START - Member ID: {}, Org: {}", memberId, request.getOrganization()); Member member = memberRepository.findById(memberId) .orElseThrow(() -> new MemberException(MemberErrorCode.MEMBER_NOT_FOUND)); codefAccountService.connectAccount(member, request); - log.info("금융사 연동 완료 - memberId: {}, organization: {}", memberId, request.getOrganization()); + log.info("[ConnectionCommandService] [connect] END - Member ID: {}", memberId); } /** * 금융사 연동 해제 */ public void disconnect(Long memberId, Long connectionId) { + log.info("[ConnectionCommandService] [disconnect] START - Member ID: {}, Connection ID: {}", memberId, connectionId); CodefConnection connection = codefConnectionRepository.findByIdWithMember(connectionId) .orElseThrow(() -> new ConnectionException(ConnectionErrorCode.CONNECTION_NOT_FOUND)); if (!connection.getMember().getId().equals(memberId)) { + log.error("[ConnectionCommandService] [disconnect] ERROR - Access Denied. Member ID {} tried to access Connection ID {}", memberId, connectionId); throw new ConnectionException(ConnectionErrorCode.CONNECTION_ACCESS_DENIED); } @@ -60,7 +63,9 @@ public void disconnect(Long memberId, Long connectionId) { if (connection.getBusinessType() == BusinessType.BK) { // 은행 계좌와 연결된 목표 Soft Delete 처리 (서브쿼리 사용) + log.debug("[ConnectionCommandService] [disconnect] Deactivating goals for bank connection"); goalRepository.softDeleteGoalsByConnectionId(connection.getId()); } + log.info("[ConnectionCommandService] [disconnect] END - Member ID: {}", memberId); } } diff --git a/src/main/java/org/umc/valuedi/domain/connection/service/event/ConnectionEventListener.java b/src/main/java/org/umc/valuedi/domain/connection/service/event/ConnectionEventListener.java index 3a37d111..4dc16f2b 100644 --- a/src/main/java/org/umc/valuedi/domain/connection/service/event/ConnectionEventListener.java +++ b/src/main/java/org/umc/valuedi/domain/connection/service/event/ConnectionEventListener.java @@ -19,13 +19,10 @@ public class ConnectionEventListener { @Async("assetFetchExecutor") @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) public void handleConnectionSuccess(ConnectionSuccessEvent event) { - log.info("금융사 연동 성공 이벤트 수신 - Connection ID: {}, Organization: {}", - event.getConnection().getId(), event.getConnection().getOrganization()); - try { assetSyncService.syncAssets(event.getConnection()); } catch (Exception e) { - log.error("자산 동기화 중 오류 발생", e); + log.error("[ConnectionEventListener] [handleConnectionSuccess] ERROR - 자산 동기화 중 오류 발생. Connection ID: {}", event.getConnection().getId(), e); } } } diff --git a/src/main/java/org/umc/valuedi/global/config/AsyncConfig.java b/src/main/java/org/umc/valuedi/global/config/AsyncConfig.java index 56632d65..035146d9 100644 --- a/src/main/java/org/umc/valuedi/global/config/AsyncConfig.java +++ b/src/main/java/org/umc/valuedi/global/config/AsyncConfig.java @@ -30,6 +30,8 @@ public Executor assetFetchExecutor() { executor.setMaxPoolSize(10); // 최대 스레드 수 executor.setQueueCapacity(100); // 큐 용량 executor.setThreadNamePrefix("AssetFetch-"); + executor.setWaitForTasksToCompleteOnShutdown(true); + executor.setAwaitTerminationSeconds(120); executor.initialize(); return executor; } diff --git a/src/main/java/org/umc/valuedi/global/external/codef/service/CodefAccountService.java b/src/main/java/org/umc/valuedi/global/external/codef/service/CodefAccountService.java index e065cb3a..a11317da 100644 --- a/src/main/java/org/umc/valuedi/global/external/codef/service/CodefAccountService.java +++ b/src/main/java/org/umc/valuedi/global/external/codef/service/CodefAccountService.java @@ -8,9 +8,6 @@ import org.umc.valuedi.domain.connection.dto.event.ConnectionSuccessEvent; import org.umc.valuedi.domain.connection.enums.BusinessType; import org.umc.valuedi.domain.member.entity.Member; -import org.umc.valuedi.domain.member.exception.MemberException; -import org.umc.valuedi.domain.member.exception.code.MemberErrorCode; -import org.umc.valuedi.domain.member.repository.MemberRepository; import org.umc.valuedi.global.external.codef.client.CodefApiClient; import org.umc.valuedi.domain.connection.dto.req.ConnectionReqDTO; import org.umc.valuedi.global.external.codef.dto.CodefApiResponse; @@ -58,6 +55,7 @@ public void connectAccount(Member member, ConnectionReqDTO.Connect request) { targetConnectedId = handleAddition(existingConnectedId, requestBody); } saveConnectionRecord(member, targetConnectedId, request.getOrganization(), request.getBusinessTypeEnum()); + log.info("[CodefAccount] 연동 성공 - Member: {}, Org: {}", member.getId(), request.getOrganization()); } /** @@ -66,7 +64,7 @@ public void connectAccount(Member member, ConnectionReqDTO.Connect request) { public void deleteAccount(String connectedId, String organization, BusinessType businessType) { Map accountMap = new HashMap<>(); accountMap.put("organization", organization); - accountMap.put("businessType", businessType); + accountMap.put("businessType", businessType.toString()); accountMap.put("countryCode", "KR"); accountMap.put("clientType", "P"); accountMap.put("loginType", "1"); @@ -81,8 +79,10 @@ public void deleteAccount(String connectedId, String organization, BusinessType CodefApiResponse response = codefApiClient.deleteAccount(requestBody); if (!response.isSuccess()) { + log.error("[CodefAccount] 삭제 실패 - Msg: {}", response.getResult().getMessage()); throw new CodefException(CodefErrorCode.CODEF_API_DELETE_FAILED); } + log.info("[CodefAccount] 연동 해제 성공 - Org: {}", organization); } /** @@ -91,7 +91,6 @@ public void deleteAccount(String connectedId, String organization, BusinessType private String handleFirstCreation(Map requestBody) { CodefApiResponse> response = codefApiClient.createConnectedId(requestBody); if (!response.isSuccess()) { - log.error("CODEF 계정 생성 실패: {}", response.getResult().getMessage()); throw new CodefException(CodefErrorCode.CODEF_API_CREATE_FAILED); } Map data = response.getData(); @@ -103,15 +102,11 @@ private String handleFirstCreation(Map requestBody) { return connectedId; } - /** - * 기존 ID에 기관 추가 처리 - */ private String handleAddition(String connectedId, Map requestBody) { requestBody.put("connectedId", connectedId); // 기존 ID를 바디에 주입 CodefApiResponse> response = codefApiClient.addAccountToConnectedId(requestBody); if (!response.isSuccess()) { - log.error("CODEF 계정 추가 실패: {}", response.getResult() != null ? response.getResult().getMessage() : "no result"); throw new CodefException(CodefErrorCode.CODEF_API_ADD_FAILED); } // 추가 성공 시에도 기존 아이디를 그대로 반환 @@ -133,9 +128,6 @@ private void saveConnectionRecord(Member member, String connectedId, String orga .member(member) .build(); member.addCodefConnection(connection); - log.info("기관 [{}] 연동 정보 저장 완료", organization); - - // 이벤트 발행 eventPublisher.publishEvent(new ConnectionSuccessEvent(connection)); } } diff --git a/src/main/java/org/umc/valuedi/global/external/codef/service/CodefAssetService.java b/src/main/java/org/umc/valuedi/global/external/codef/service/CodefAssetService.java index bf750544..dcb82e8a 100644 --- a/src/main/java/org/umc/valuedi/global/external/codef/service/CodefAssetService.java +++ b/src/main/java/org/umc/valuedi/global/external/codef/service/CodefAssetService.java @@ -24,8 +24,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; @Slf4j @Service @@ -43,7 +41,7 @@ public List getBankAccounts(CodefConnection connection) { CodefApiResponse response = codefApiClient.getBankAccounts(requestBody); if (!response.isSuccess()) { - log.error("CODEF 보유 계좌 목록 조회 실패: {}", response.getResult().getMessage()); + log.error("[CodefAsset] 계좌 조회 실패 - Msg: {}", response.getResult().getMessage()); throw new CodefException(CodefErrorCode.CODEF_API_BANK_ACCOUNT_LIST_FAILED); } @@ -84,9 +82,9 @@ public List getBankTransactions(CodefConnection connection, Ban if (msg.contains("일치하는 정보가 없습니다") || msg.contains("존재하지 않습니다") || msg.contains("보유계좌")) { // 경고 레벨로 낮춤 (정상적인 예외 상황) - log.warn("거래내역 조회 불가 계좌 (건너뜀) - 계좌명: {}, 메시지: {}", account.getAccountName(), msg); + log.warn("[CodefAssetService] [getBankTransactions] WARN - Message: {}, Account: {}", msg, account.getAccountDisplay()); } else { - log.error("CODEF 계좌 거래 내역 조회 API 오류 - 계좌: {}, 에러: {}", account.getAccountDisplay(), msg); + log.error("[CodefAssetService] [getBankTransactions] ERROR - Message: {}, Account: {}", msg, account.getAccountDisplay()); } return List.of(); @@ -106,21 +104,19 @@ public List getCards(CodefConnection connection) { CodefApiResponse response = codefApiClient.getCardList(requestBody); if (!response.isSuccess()) { - log.error("CODEF 보유 카드 목록 조회 실패: {}", response.getResult().getMessage()); + log.error("[CodefAsset] 카드 조회 실패 - Msg: {}", response.getResult().getMessage()); throw new CodefException(CodefErrorCode.CODEF_API_CARD_LIST_FAILED); } Object responseData = response.getData(); List cardList = new ArrayList<>(); - + if (responseData instanceof Map) { - CodefAssetResDTO.Card card = objectMapper.convertValue(responseData, CodefAssetResDTO.Card.class); - cardList.add(card); + cardList.add(objectMapper.convertValue(responseData, CodefAssetResDTO.Card.class)); } else if (responseData instanceof List) { - List cards = objectMapper.convertValue(responseData, new TypeReference>() {}); - cardList.addAll(cards); + cardList.addAll(objectMapper.convertValue(responseData, new TypeReference<>() {})); } else { - log.error("CODEF 보유 카드 목록 응답 형식이 예상과 다릅니다. Data: {}", responseData); + log.error("[CodefAsset] 카드 조회 응답 오류 - Data: {}", responseData); throw new CodefException(CodefErrorCode.CODEF_API_CARD_LIST_FAILED); } @@ -138,13 +134,13 @@ public List getCardApprovals(CodefConnection connection, LocalDate // 이 메서드는 이제 connection 내부의 cardList를 사용하므로, 외부에서 cardList가 채워져 있어야 함. return getCardApprovals(connection, connection.getCardList(), startDate, endDate); } - + // AssetFetchWorker가 사용할 새로운 오버로딩 메서드 public List getCardApprovals(CodefConnection connection, List cards, LocalDate startDate, LocalDate endDate) { Map requestBody = new HashMap<>(); requestBody.put("connectedId", connection.getConnectedId()); requestBody.put("organization", connection.getOrganization()); - + requestBody.put("startDate", startDate.format(DateTimeFormatter.BASIC_ISO_DATE)); requestBody.put("endDate", endDate.format(DateTimeFormatter.BASIC_ISO_DATE)); requestBody.put("orderBy", "0"); @@ -154,7 +150,7 @@ public List getCardApprovals(CodefConnection connection, List response = codefApiClient.getCardApprovals(requestBody); if (!response.isSuccess()) { - log.error("CODEF 카드 승인 내역 조회 실패: {}", response.getResult().getMessage()); + log.error("[CodefAsset] 승인내역 조회 실패 - Msg: {}", response.getResult().getMessage()); return List.of(); } @@ -165,7 +161,6 @@ public List getCardApprovals(CodefConnection connection, List getCardList( - String connectedId, - List cardOrganizations) { - - List allCards = new ArrayList<>(); - - // 각 카드사별로 조회 - for (String organization : cardOrganizations) { - try { - List cards = - getCardListByOrganization(connectedId, organization); - allCards.addAll(cards); - } catch (Exception e) { - log.error("카드사 {} 조회 중 에러 발생", organization, e); - } - } - - log.info("전체 조회된 카드 개수: {}", allCards.size()); - return allCards; - } - - /** - * 특정 카드사의 보유 카드 목록 조회 - */ - private List getCardListByOrganization( - String connectedId, - String organization) { - - try { - Map requestBody = new HashMap<>(); - requestBody.put("connectedId", connectedId); - requestBody.put("organization", organization); - - // CODEF API 호출 - CodefApiResponse response = codefApiClient.getCardList(requestBody); - - if (!response.isSuccess()) { - log.error("CODEF 카드 목록 조회 실패 [{}] - code: {}, message: {}", - organization, - response.getResult().getCode(), - response.getResult().getMessage()); - return Collections.emptyList(); - } - - return parseCardListResponse(response.getData(), organization); - - } catch (Exception e) { - log.error("카드사 {} 조회 중 에러 발생", organization, e); - return Collections.emptyList(); - } - } - - /** - * CODEF 응답 데이터를 DTO로 변환 - */ - private List parseCardListResponse( - Object data, - String organization) { - - if (data == null) { - return Collections.emptyList(); - } - - try { - Map dataMap = (Map) data; - - if (dataMap.containsKey("resCardList")) { - List> cardList = - (List>) dataMap.get("resCardList"); - - if (cardList == null || cardList.isEmpty()) { - return Collections.emptyList(); - } - - log.info("카드사 {} - 조회된 카드 {}개", organization, cardList.size()); - - return cardList.stream() - .map(card -> buildCardConnection(card, organization)) - .toList(); - } - if (dataMap.containsKey("resCardName")) { - return List.of(buildCardConnection(dataMap, organization)); - } - return Collections.emptyList(); - - } catch (Exception e) { - return Collections.emptyList(); - } - } - - /** - * 카드 데이터를 DTO로 변환하는 헬퍼 메서드 - */ - private CardResDTO.CardConnection buildCardConnection( - Map card, - String organization) { - - return CardResDTO.CardConnection.builder() - .cardId((String) card.get("resCardId")) - .cardName((String) card.get("resCardName")) - .cardNum((String) card.get("resCardNo")) - .cardCompany(organization) - .cardCompanyCode(organization) - .build(); - } -} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0e488a13..c18f2cc2 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -35,6 +35,17 @@ spring: connectiontimeout: 5000 timeout: 5000 writetimeout: 5000 + cloud: + openfeign: + client: + config: + default: + connectTimeout: 5000 + readTimeout: 5000 + CodefApiClient: + connectTimeout: 10000 + readTimeout: 120000 # 120초 + codef: client-id: ${CODEF_CLIENT_ID} client-secret: ${CODEF_CLIENT_SECRET}