From 70e38747fc5428f8fe67eee9ba55fb811e7cda87 Mon Sep 17 00:00:00 2001 From: WonJin Date: Mon, 26 May 2025 23:06:57 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feature:=20=EA=B3=84=EC=A2=8C=20=ED=95=B4?= =?UTF-8?q?=EC=A7=80=20=EB=B0=8F=20=EC=9E=90=EB=8F=99=EC=9D=B4=EC=B2=B4=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -. 계좌 상태가 ACTIVE가 아니면 입금불가 구현 -. 최대 입금한도 100만원을 초과할경우 입근 불가 -.계좌 해지 시 잔액이 존재하면 사용자가 지정한 출금 계좌로 자동 이체 refactor: -. AccountManagementService 생성 및 계좌 해지, 복구, 만료 삭제 책임 분리 - 트랜잭션 응답 Record 객체 일관화 (TransferResponseRecord, DepositRecord 등) - 거래 내역 기간/정렬 조회 기능 refactoring --- .../fin/controller/AccountController.java | 22 ++- .../fin/controller/TransactionController.java | 83 +++++++--- .../java/com/track/fin/dto/CancelBalance.java | 5 + .../java/com/track/fin/dto/UseBalance.java | 4 + .../com/track/fin/record/DepositRecord.java | 2 +- .../fin/record/DepositTransactionRecord.java | 17 +- .../fin/record/TransferRequestRecord.java | 19 ++- .../fin/record/TransferResponseRecord.java | 10 +- .../fin/record/WithdrawalRequestRecord.java | 17 +- .../fin/service/AccountManagementService.java | 90 +++++++++++ .../com/track/fin/service/AccountService.java | 37 +---- .../track/fin/service/TransactionService.java | 147 ++++++++++-------- .../com/track/fin/type/TransactionType.java | 1 - 13 files changed, 316 insertions(+), 138 deletions(-) create mode 100644 src/main/java/com/track/fin/service/AccountManagementService.java diff --git a/src/main/java/com/track/fin/controller/AccountController.java b/src/main/java/com/track/fin/controller/AccountController.java index ff282b3..586b6fb 100644 --- a/src/main/java/com/track/fin/controller/AccountController.java +++ b/src/main/java/com/track/fin/controller/AccountController.java @@ -2,6 +2,7 @@ import com.track.fin.domain.Account; import com.track.fin.record.*; +import com.track.fin.service.AccountManagementService; import com.track.fin.service.AccountService; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; @@ -18,6 +19,7 @@ public class AccountController { private final AccountService accountService; + private final AccountManagementService accountManagementService; @PostMapping public Account createAccount( @@ -42,11 +44,12 @@ public Account getAccount( return accountService.getAccount(id); } - @DeleteMapping - public AccountRecord deleteAccount( - @RequestBody @Valid DeleteAccountRecord deleteAccountRecord + @PostMapping("/restore") + public AccountRecord restoreAccount( + @RequestParam Long userId, + @RequestParam String accountNumber ) { - return accountService.deleteAccount(deleteAccountRecord); + return accountManagementService.restoreAccount(userId, accountNumber); } @GetMapping("/{accountNumber}/transactions") @@ -99,4 +102,15 @@ public boolean validateAutoTransferNotRegistered( return accountService.validateAutoTransferNotRegistered(accountNumber); } + @DeleteMapping + public AccountRecord deleteAccount(@Valid @RequestBody DeleteAccountRecord request) { + return accountManagementService.deleteAccount(request); + } + + @DeleteMapping("/expired/{accountNumber}") + public void deleteExpiredAccount(@PathVariable String accountNumber) { + Account account = accountService.getAccountByNumber(accountNumber); + accountManagementService.deleteIfExpired(account); + } + } diff --git a/src/main/java/com/track/fin/controller/TransactionController.java b/src/main/java/com/track/fin/controller/TransactionController.java index 32fb2bf..889fac8 100644 --- a/src/main/java/com/track/fin/controller/TransactionController.java +++ b/src/main/java/com/track/fin/controller/TransactionController.java @@ -5,38 +5,47 @@ import com.track.fin.exception.AccountException; import com.track.fin.record.*; import com.track.fin.service.TransactionService; +import com.track.fin.type.TransactionType; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.*; +import java.time.LocalDateTime; +import java.util.List; + @Slf4j @RequiredArgsConstructor @RestController +@RequestMapping("/transactions") public class TransactionController { private final TransactionService transactionService; - @PostMapping("transaction/use") - public UseBalance.Response useBalance( - @Valid @RequestBody UseBalance.Request requset + @PostMapping("/use") + public TransferResponseRecord useBalance( + @Valid @RequestBody UseBalance.Request request ) { try { - return UseBalance.Response.from(transactionService.useBalance(requset.getUserId(), - requset.getAccountNumber(), requset.getAmount()) + return transactionService.useBalance( + request.getUserId(), + request.getAccountNumber(), + request.getAmount(), + request.getTransactionMethodType() ); } catch (AccountException e) { - log.error("Failed to use balance. "); - + log.error("잔액 사용 실패: {}", e.getMessage()); transactionService.saveFailedUseTransaction( - requset.getAccountNumber(), - requset.getAmount() + request.getAccountNumber(), + request.getAmount(), + request.getTransactionMethodType() ); throw e; } } - @PostMapping("/transaction/deposit") + @PostMapping("/deposit") public DepositRecord deposit( @Valid @RequestBody DepositTransactionRecord request ) { @@ -45,21 +54,23 @@ public DepositRecord deposit( transactionService.deposit( request.userId(), request.accountNumber(), - request.amount() + request.amount(), + request.transactionMethodType() ) ); } catch (AccountException e) { - log.error("Failed to deposit."); + log.error("입금 실패: {}", e.getMessage()); transactionService.saveFailedDepositTransaction( request.accountNumber(), - request.amount() + request.amount(), + request.transactionMethodType() ); throw e; } } - @PostMapping("/transaction/transfer") + @PostMapping("/transfer") public TransferResponseRecord transfer( @Valid @RequestBody TransferRequestRecord request ) { @@ -68,20 +79,35 @@ public TransferResponseRecord transfer( request.userId(), request.fromAccountNumber(), request.toAccountNumber(), - request.amount() + request.amount(), + request.transactionMethodType() ); } catch (AccountException e) { - log.error("Failed to transfer."); + log.error("이체 실패: {}", e.getMessage()); transactionService.saveFailedTransferTransaction( request.fromAccountNumber(), request.toAccountNumber(), - request.amount() + request.amount(), + request.transactionMethodType() ); throw e; } } - @PostMapping("/transaction/withdraw") + @GetMapping("/transfers") + public List getTransfers( + @RequestParam String accountNumber, + @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startDate, + @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endDate, + @RequestParam(required = false) TransactionType transactionType, + @RequestParam(defaultValue = "false") boolean sortDesc + ) { + return transactionService.getTransferTransactionsByAccount( + accountNumber, startDate, endDate, transactionType, sortDesc + ); + } + + @PostMapping("/withdraw") public WithdrawalRecord withdraw( @Valid @RequestBody WithdrawalRequestRecord request ) { @@ -90,17 +116,22 @@ public WithdrawalRecord withdraw( transactionService.withdraw( request.userId(), request.accountNumber(), - request.amount() + request.amount(), + request.transactionMethodType() ) ); } catch (AccountException e) { - log.error("Failed to withdraw."); - transactionService.saveFailedWithdrawTransaction(request.accountNumber(), request.amount()); + log.error("출금 실패: {}", e.getMessage()); + transactionService.saveFailedWithdrawTransaction( + request.accountNumber(), + request.amount(), + request.transactionMethodType() + ); throw e; } } - @PostMapping("/transaction/cancel") + @PostMapping("/cancel") public TransferResponseRecord cancelBalance( @Valid @RequestBody CancelBalance.Request request ) { @@ -108,19 +139,21 @@ public TransferResponseRecord cancelBalance( return transactionService.cancelBalance( String.valueOf(request.getTransactionId()), request.getAccountNumber(), - request.getAmount() + request.getAmount(), + request.getTransactionMethodType() ); } catch (AccountException e) { log.error("Cancel failed", e); transactionService.saveFailedCancelTransaction( request.getAccountNumber(), - request.getAmount() + request.getAmount(), + request.getTransactionMethodType() ); throw e; } } - @GetMapping("/transaction/{transactionId}") + @GetMapping("/{transactionId}") public TransferResponseRecord queryTransaction( @PathVariable String transactionId) { return transactionService.queryTransactionId(transactionId); diff --git a/src/main/java/com/track/fin/dto/CancelBalance.java b/src/main/java/com/track/fin/dto/CancelBalance.java index 6c9743b..3e6fbd1 100644 --- a/src/main/java/com/track/fin/dto/CancelBalance.java +++ b/src/main/java/com/track/fin/dto/CancelBalance.java @@ -1,5 +1,6 @@ package com.track.fin.dto; +import com.track.fin.type.TransactionMethodType; import com.track.fin.type.TransactionResultType; import jakarta.validation.constraints.*; import lombok.AllArgsConstructor; @@ -24,6 +25,10 @@ public static class Request { @Min(10) @Max(1000_000_000) private Long amount; + + @NotNull + private TransactionMethodType transactionMethodType; + } @Getter diff --git a/src/main/java/com/track/fin/dto/UseBalance.java b/src/main/java/com/track/fin/dto/UseBalance.java index 571bcbb..451c053 100644 --- a/src/main/java/com/track/fin/dto/UseBalance.java +++ b/src/main/java/com/track/fin/dto/UseBalance.java @@ -1,5 +1,6 @@ package com.track.fin.dto; +import com.track.fin.type.TransactionMethodType; import com.track.fin.type.TransactionResultType; import jakarta.validation.constraints.*; import lombok.AllArgsConstructor; @@ -26,6 +27,9 @@ public static class Request { @Min(10) @Max(1000_000_000) private Long amount; + + @NotNull + private TransactionMethodType transactionMethodType; } @Getter diff --git a/src/main/java/com/track/fin/record/DepositRecord.java b/src/main/java/com/track/fin/record/DepositRecord.java index 0a7c6fa..c3c19d6 100644 --- a/src/main/java/com/track/fin/record/DepositRecord.java +++ b/src/main/java/com/track/fin/record/DepositRecord.java @@ -2,13 +2,13 @@ import com.track.fin.domain.Transaction; + public record DepositRecord( String transactionId, String accountNumber, Long amount, Long balanceSnapshot - ) { public static DepositRecord from(Transaction transaction) { diff --git a/src/main/java/com/track/fin/record/DepositTransactionRecord.java b/src/main/java/com/track/fin/record/DepositTransactionRecord.java index c5ffae4..d9fe769 100644 --- a/src/main/java/com/track/fin/record/DepositTransactionRecord.java +++ b/src/main/java/com/track/fin/record/DepositTransactionRecord.java @@ -1,10 +1,25 @@ package com.track.fin.record; +import com.track.fin.type.TransactionMethodType; +import jakarta.validation.constraints.*; + public record DepositTransactionRecord( + @NotNull + @Min(1) Long userId, + + @NotBlank + @Size(min = 10, max = 10) String accountNumber, - Long amount + + @NotNull + @Min(10) + @Max(1_000_000_000) + Long amount, + + @NotNull + TransactionMethodType transactionMethodType ) { } diff --git a/src/main/java/com/track/fin/record/TransferRequestRecord.java b/src/main/java/com/track/fin/record/TransferRequestRecord.java index 00fdfb3..5d29438 100644 --- a/src/main/java/com/track/fin/record/TransferRequestRecord.java +++ b/src/main/java/com/track/fin/record/TransferRequestRecord.java @@ -1,12 +1,29 @@ package com.track.fin.record; +import com.track.fin.type.TransactionMethodType; +import jakarta.validation.constraints.*; + public record TransferRequestRecord( + @NotNull + @Min(1) Long userId, + + @NotBlank + @Size(min = 10, max = 10) String fromAccountNumber, + + @NotBlank + @Size(min = 10, max = 10) String toAccountNumber, - Long amount + @NotNull + @Min(10) + @Max(1_000_000_000) + Long amount, + + @NotNull + TransactionMethodType transactionMethodType ) { } diff --git a/src/main/java/com/track/fin/record/TransferResponseRecord.java b/src/main/java/com/track/fin/record/TransferResponseRecord.java index 8cc05fc..af0eaa1 100644 --- a/src/main/java/com/track/fin/record/TransferResponseRecord.java +++ b/src/main/java/com/track/fin/record/TransferResponseRecord.java @@ -1,6 +1,7 @@ package com.track.fin.record; import com.track.fin.domain.Transaction; +import com.track.fin.type.TransactionType; import java.time.LocalDateTime; @@ -12,7 +13,8 @@ public record TransferResponseRecord( Long amount, Long fromBalanceSnapshot, Long toBalanceSnapshot, - LocalDateTime transactionDate + LocalDateTime transactionDate, + TransactionType transactionType ) { @@ -24,7 +26,8 @@ public static TransferResponseRecord from(Transaction fromTransaction, Transacti fromTransaction.getAmount(), fromTransaction.getBalanceSnapshot(), toTransaction.getBalanceSnapshot(), - fromTransaction.getTransactionDate() + fromTransaction.getTransactionDate(), + fromTransaction.getTransactionType() ); } @@ -36,7 +39,8 @@ public static TransferResponseRecord from(Transaction transaction) { transaction.getAmount(), transaction.getBalanceSnapshot(), null, - transaction.getTransactionDate() + transaction.getTransactionDate(), + transaction.getTransactionType() ); } diff --git a/src/main/java/com/track/fin/record/WithdrawalRequestRecord.java b/src/main/java/com/track/fin/record/WithdrawalRequestRecord.java index 8bc2c43..b125f8b 100644 --- a/src/main/java/com/track/fin/record/WithdrawalRequestRecord.java +++ b/src/main/java/com/track/fin/record/WithdrawalRequestRecord.java @@ -1,11 +1,24 @@ package com.track.fin.record; +import com.track.fin.type.TransactionMethodType; +import jakarta.validation.constraints.*; + public record WithdrawalRequestRecord( + @NotNull + @Min(1) Long userId, + + @NotBlank String accountNumber, - Long amount + + @NotNull + @Min(10) + @Max(1000000000) + Long amount, + + @NotNull + TransactionMethodType transactionMethodType ) { } - diff --git a/src/main/java/com/track/fin/service/AccountManagementService.java b/src/main/java/com/track/fin/service/AccountManagementService.java new file mode 100644 index 0000000..98234c4 --- /dev/null +++ b/src/main/java/com/track/fin/service/AccountManagementService.java @@ -0,0 +1,90 @@ +package com.track.fin.service; + +import com.track.fin.domain.Account; +import com.track.fin.domain.User; +import com.track.fin.exception.AccountException; +import com.track.fin.record.AccountRecord; +import com.track.fin.record.DeleteAccountRecord; +import com.track.fin.repository.AccountRepository; +import com.track.fin.repository.TransactionRepository; +import com.track.fin.type.TransactionMethodType; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; + +import static com.track.fin.design.singleton.AccountValidates.validateDeleteAccount; +import static com.track.fin.design.singleton.AccountValidates.validateRestoreAccount; +import static com.track.fin.type.ErrorCode.*; + +@Service +@RequiredArgsConstructor +public class AccountManagementService { + + private final LoanService loanService; + private final UserService userService; + private final AccountService accountService; + private final TransactionService transactionService; + private final AccountRepository accountRepository; + private final TransactionRepository transactionRepository; + + @Transactional + public AccountRecord deleteAccount(DeleteAccountRecord deleteAccountRecord) { + User user = userService.get(deleteAccountRecord.userId()); + Account closingAccount = accountService.getAccountByNumber(deleteAccountRecord.accountNumber()); + Account withdrawAccount = accountService.getAccountByNumber(deleteAccountRecord.withdrawAccountNumber()); + + validateDeleteAccount(user, closingAccount, withdrawAccount); + + transferRemainingBalanceIfExists(deleteAccountRecord, closingAccount); + + closingAccount.close(); + return AccountRecord.from(accountRepository.save(closingAccount)); + } + + @Transactional + public AccountRecord restoreAccount(Long userId, String accountNumber) { + User user = userService.get(userId); + Account account = accountService.getAccountByNumber(accountNumber); + + validateRestoreAccount(user, account); + account.restore(); + + return AccountRecord.from(accountRepository.save(account)); + } + + @Transactional + public void deleteIfExpired(Account account) { + if (account.getUnregisteredAt() != null && + account.getUnregisteredAt().isBefore(LocalDateTime.now().minusMonths(3))) { + accountRepository.delete(account); + throw new AccountException(ACCOUNT_RESTORE_EXPIRED); + } + } + + private void transferRemainingBalanceIfExists(DeleteAccountRecord record, Account closingAccount) { + if (closingAccount.getBalance() > 0) { + transactionService.transfer( + record.userId(), + record.accountNumber(), + record.withdrawAccountNumber(), + closingAccount.getBalance(), + TransactionMethodType.AUTO + ); + } + } + // TODO : 대출 로직 구현 후 사용 예정 + private void validatePendingLoanOrAutoTransfer(Account account) { + boolean hasLoan = loanService.existsUnpaidLoanByAccount(account); + boolean hasAutoTransfer = transactionRepository.existsByAccountAndTransactionMethodType(account, TransactionMethodType.AUTO); // 수정 + + if (hasLoan) { + throw new AccountException(LOAN_EXISTS); + } + if (hasAutoTransfer) { + throw new AccountException(AUTO_TRANSFER_EXISTS); + } + } + +} diff --git a/src/main/java/com/track/fin/service/AccountService.java b/src/main/java/com/track/fin/service/AccountService.java index 994d35a..fcf15a2 100644 --- a/src/main/java/com/track/fin/service/AccountService.java +++ b/src/main/java/com/track/fin/service/AccountService.java @@ -8,6 +8,7 @@ import com.track.fin.record.DeleteAccountRecord; import com.track.fin.repository.AccountRepository; import com.track.fin.repository.UserRepository; +import com.track.fin.type.TransactionMethodType; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -31,7 +32,6 @@ public class AccountService { private final UserService userService; private final LoanService loanService; private final UserRepository userRepository; - private final TransactionService transactionService; @Transactional public Account createAccount(CreateAccount createAccount) { @@ -68,25 +68,10 @@ public List getAccounts(Long userId) { return accountRepository.findByUserId(userId); } - @Transactional - public AccountRecord deleteAccount(DeleteAccountRecord deleteAccountRecord) { - User user = userService.get(deleteAccountRecord.userId()); - Account closingAccount = getAccountByNumber(deleteAccountRecord.accountNumber()); - Account withdrawAccount = getAccountByNumber(deleteAccountRecord.withdrawAccountNumber()); - - validateDeleteAccount(user, closingAccount, withdrawAccount); - - transactionService.transfer(deleteAccountRecord.userId(), deleteAccountRecord.accountNumber(), deleteAccountRecord.withdrawAccountNumber(), closingAccount.getBalance()); - - closingAccount.close(); - return AccountRecord.from(accountRepository.save(closingAccount)); - } - private void validateCreateAccount(User user) { if (accountRepository.countByUser(user) == 10) { throw new AccountException(MAX_ACCOUNT_PER_USER_10); } - if (hasClosedAccountWithinLastMonth(user)) { throw new AccountException(CANNOT_CREATE_ACCOUNT_DUE_TO_RECENT_CLOSURE); } @@ -103,18 +88,6 @@ private boolean hasClosedAccountWithinLastMonth(User user) { }); } -// private void validatePendingLoanOrAutoTransfer(Account account) { -// boolean hasLoan = loanService.existsUnpaidLoanByAccount(account); -//// boolean hasAutoTransfer = this.existsByAccount(account); -// -// if (hasLoan) { -// throw new AccountException(LOAN_EXISTS); -// } -// if (hasAutoTransfer) { -// throw new AccountException(AUTO_TRANSFER_EXISTS); -// } -// } - @Transactional(readOnly = true) public List getActiveAccounts(Long userId) { User user = userService.get(userId); @@ -132,14 +105,6 @@ public AccountRecord restoreAccount(Long userId, String accountNumber) { return AccountRecord.from(accountRepository.save(account)); } - // TODO: 3개월 지나면 삭제 - private void delete(Account account){ - if (account.getUnregisteredAt().isBefore(LocalDateTime.now().minusMonths(3))) { - accountRepository.delete(account); - throw new AccountException(ACCOUNT_RESTORE_EXPIRED); - } - } - public boolean validateAutoTransferNotRegistered(String accountNumber) { Account account = getAccountByNumber(accountNumber); diff --git a/src/main/java/com/track/fin/service/TransactionService.java b/src/main/java/com/track/fin/service/TransactionService.java index aee617c..05bb7a4 100644 --- a/src/main/java/com/track/fin/service/TransactionService.java +++ b/src/main/java/com/track/fin/service/TransactionService.java @@ -3,10 +3,8 @@ import com.track.fin.domain.Account; import com.track.fin.domain.Transaction; import com.track.fin.domain.User; -import com.track.fin.dto.TransactionDto; import com.track.fin.exception.AccountException; import com.track.fin.record.TransferResponseRecord; -import com.track.fin.repository.AccountRepository; import com.track.fin.repository.TransactionRepository; import com.track.fin.repository.UserRepository; import com.track.fin.type.AccountStatus; @@ -34,31 +32,41 @@ @RequiredArgsConstructor public class TransactionService { + private static final long MAX_DEPOSIT_AMOUNT = 1_000_000L; + private final TransactionRepository transactionRepository; - private final AccountRepository accountRepository; private final UserRepository userRepository; + private final AccountService accountService; + private final UserService userService; @Transactional - public TransactionDto useBalance(Long userId, String accountNumber, Long amount) { + public TransferResponseRecord useBalance(Long userId, String accountNumber, Long amount, TransactionMethodType methodType) { User user = userRepository.findById(userId) .orElseThrow(() -> new AccountException(USER_NOT_FOUND)); - Account account = accountRepository.findByAccountNumber(accountNumber) - .orElseThrow(() -> new AccountException(ACCOUNT_NOT_FOUND)); + + Account account = accountService.getAccountByNumber(accountNumber); validateUserBalance(user, account, amount); account.useBalance(amount); - return TransactionDto.fromEntity( - saveAndGetTransaction(DEPOSIT, SUCCESS, account, amount) - ); + Transaction transaction = saveAndGetTransaction(WITHDRAWAL, SUCCESS, account, amount, methodType); + + return TransferResponseRecord.from(transaction); } - private Transaction saveAndGetTransaction(TransactionType type, TransactionResultType result, Account account, Long amount) { + private Transaction saveAndGetTransaction( + TransactionType type, + TransactionResultType result, + Account account, + Long amount, + TransactionMethodType methodType + ) { return transactionRepository.save( Transaction.builder() .id(UUID.randomUUID().toString().replace("-", "")) .transactionType(type) .transactionResultType(result) + .transactionMethodType(methodType) .account(account) .amount(amount) .balanceSnapshot(account.getBalance()) @@ -71,24 +79,25 @@ public List getTransferTransactionsByAccount( String accountNumber, LocalDateTime startDate, LocalDateTime endDate, + TransactionType transactionType, boolean sortDesc ) { Account account = getAccountByNumber(accountNumber); - List transfers = getTransferTransactions(account, startDate, endDate); + List transfers = getTransferTransactions(account, startDate, endDate, transactionType); List results = convertToTransferRecords(transfers, accountNumber); sortTransferRecords(results, sortDesc); return results; } private Account getAccountByNumber(String accountNumber) { - return accountRepository.findByAccountNumber(accountNumber) - .orElseThrow(() -> new AccountException(ACCOUNT_NOT_FOUND)); + return accountService.getAccountByNumber(accountNumber); } - private List getTransferTransactions(Account account, LocalDateTime start, LocalDateTime end) { - return transactionRepository.findByAccountAndTransactionTypeAndTransactionDateBetween( - account, TransactionType.TRANSFER, start, end - ); + private List getTransferTransactions(Account account, LocalDateTime start, LocalDateTime end, TransactionType type) { + if (type == null) { + return transactionRepository.findByAccountAndTransactionDateBetween(account, start, end); + } + return transactionRepository.findByAccountAndTransactionTypeAndTransactionDateBetween(account, type, start, end); } private List convertToTransferRecords(List transactions, String accountNumber) { @@ -121,30 +130,33 @@ private void sortTransferRecords(List list, boolean sort : a.transactionDate().compareTo(b.transactionDate())); } - public void saveFailedUseTransaction(String accountNumber, Long amount) { - Account account = accountRepository.findByAccountNumber(accountNumber) - .orElseThrow(() -> new AccountException(ACCOUNT_NOT_FOUND)); - saveAndGetTransaction(DEPOSIT, FAIL, account, amount); + public void saveFailedUseTransaction(String accountNumber, Long amount, TransactionMethodType transactionMethodType) { + Account account = accountService.getAccountByNumber(accountNumber); + saveAndGetTransaction(DEPOSIT, FAIL, account, amount, transactionMethodType); } @Transactional - public TransferResponseRecord cancelBalance(String transactionId, String accountNumber, Long amount) { + public TransferResponseRecord cancelBalance( + String transactionId, + String accountNumber, + Long amount, + TransactionMethodType methodType + ) { Transaction transaction = transactionRepository.findById(transactionId) .orElseThrow(() -> new AccountException(TRANSACTION_NOT_FOUND)); - Account account = accountRepository.findByAccountNumber(accountNumber) - .orElseThrow(() -> new AccountException(ACCOUNT_NOT_FOUND)); + + Account account = accountService.getAccountByNumber(accountNumber); validateCancelBalance(transaction, account, amount); account.useBalance(amount); - Transaction newTransaction = saveAndGetTransaction(DEPOSIT, SUCCESS, account, amount); + Transaction newTransaction = saveAndGetTransaction(DEPOSIT, SUCCESS, account, amount, methodType); return TransferResponseRecord.from(newTransaction); } - public void saveFailedCancelTransaction(String accountNumber, Long amount) { - Account account = accountRepository.findByAccountNumber(accountNumber) - .orElseThrow(() -> new AccountException(ACCOUNT_NOT_FOUND)); - saveAndGetTransaction(DEPOSIT, FAIL, account, amount); + public void saveFailedCancelTransaction(String accountNumber, Long amount, TransactionMethodType methodType) { + Account account = accountService.getAccountByNumber(accountNumber); + saveAndGetTransaction(DEPOSIT, FAIL, account, amount, methodType); } public TransferResponseRecord queryTransactionId(String transactionId) { @@ -153,7 +165,6 @@ public TransferResponseRecord queryTransactionId(String transactionId) { return TransferResponseRecord.from(transaction); } - private void validateCancelBalance(Transaction transaction, Account account, Long amount) { if (!Objects.equals(transaction.getAccount().getId(), account.getId())) { throw new AccountException(TRANSACTION_ACCOUNT_UN_MATCH); @@ -176,22 +187,22 @@ private void validateUserBalance(User user, Account account, Long amount) { } @Transactional - public Transaction deposit(Long userId, String accountNumber, Long amount) { + public Transaction deposit(Long userId, String accountNumber, Long amount, TransactionMethodType methodType) { User user = userRepository.findById(userId) .orElseThrow(() -> new AccountException(USER_NOT_FOUND)); - Account account = accountRepository.findByAccountNumber(accountNumber) - .orElseThrow(() -> new AccountException(ACCOUNT_NOT_FOUND)); - validateDeposit(user, account); + Account account = accountService.getAccountByNumber(accountNumber); + + validateDeposit(user, account, amount); + account.deposit(amount); - return saveAndGetTransaction(DEPOSIT, SUCCESS, account, amount); + return saveAndGetTransaction(DEPOSIT, SUCCESS, account, amount, methodType); } - public void saveFailedDepositTransaction(String accountNumber, Long amount) { - Account account = accountRepository.findByAccountNumber(accountNumber) - .orElseThrow(() -> new AccountException(ACCOUNT_NOT_FOUND)); - saveAndGetTransaction(DEPOSIT, FAIL, account, amount); + public void saveFailedDepositTransaction(String accountNumber, Long amount, TransactionMethodType methodType) { + Account account = accountService.getAccountByNumber(accountNumber); + saveAndGetTransaction(DEPOSIT, FAIL, account, amount, methodType); } private void validateDeposit(User user, Account account) { @@ -204,22 +215,26 @@ private void validateDeposit(User user, Account account) { } @Transactional - public Transaction withdraw(Long userId, String accountNumber, Long amount) { + public Transaction withdraw(Long userId, String accountNumber, Long amount, TransactionMethodType methodType) { User user = userRepository.findById(userId) .orElseThrow(() -> new AccountException(USER_NOT_FOUND)); - Account account = accountRepository.findByAccountNumber(accountNumber) - .orElseThrow(() -> new AccountException(ACCOUNT_NOT_FOUND)); + + Account account = accountService.getAccountByNumber(accountNumber); validateWithdraw(user, account, amount); account.withdraw(amount); - return saveAndGetTransaction(WITHDRAWAL, SUCCESS, account, amount); + return saveAndGetTransaction(WITHDRAWAL, SUCCESS, account, amount, methodType); } - public void saveFailedWithdrawTransaction(String accountNumber, Long amount) { - Account account = accountRepository.findByAccountNumber(accountNumber) - .orElseThrow(() -> new AccountException(ACCOUNT_NOT_FOUND)); - saveAndGetTransaction(WITHDRAWAL, FAIL, account, amount); + public void saveFailedWithdrawTransaction(String accountNumber, Long amount, TransactionMethodType methodType) { + try { + Account account = accountService.getAccountByNumber(accountNumber); + saveAndGetTransaction(WITHDRAWAL, FAIL, account, amount, methodType); + } catch (AccountException e) { + log.error("출금 실패 거래 내역 저장 중 오류 발생 - 계좌번호: {}", accountNumber, e); + throw e; + } } private void validateWithdraw(User user, Account account, Long amount) { @@ -235,32 +250,29 @@ private void validateWithdraw(User user, Account account, Long amount) { } @Transactional - public TransferResponseRecord transfer(Long userId, String fromAccountNumber, String toAccountNumber, Long amount) { + public TransferResponseRecord transfer(Long userId, String fromAccountNumber, String toAccountNumber, Long amount, TransactionMethodType methodType) { User user = userRepository.findById(userId) .orElseThrow(() -> new AccountException(USER_NOT_FOUND)); - Account fromAccount = accountRepository.findByAccountNumber(fromAccountNumber) - .orElseThrow(() -> new AccountException(ACCOUNT_NOT_FOUND)); - Account toAccount = accountRepository.findByAccountNumber(toAccountNumber) - .orElseThrow(() -> new AccountException(ACCOUNT_NOT_FOUND)); + Account fromAccount = accountService.getAccountByNumber(fromAccountNumber); + Account toAccount = accountService.getAccountByNumber(toAccountNumber); validateTransfer(user, fromAccount, toAccount, amount); fromAccount.withdraw(amount); toAccount.deposit(amount); - Transaction fromTransaction = saveAndGetTransaction(TRANSFER, SUCCESS, fromAccount, amount); - Transaction toTransaction = saveAndGetTransaction(TRANSFER, SUCCESS, toAccount, amount); + Transaction fromTransaction = saveAndGetTransaction(TRANSFER, SUCCESS, fromAccount, amount, methodType); + Transaction toTransaction = saveAndGetTransaction(TRANSFER, SUCCESS, toAccount, amount, methodType); return TransferResponseRecord.from(fromTransaction, toTransaction); } - public void saveFailedTransferTransaction(String fromAccountNumber, String toAccountNumber, Long amount) { - Account fromAccount = accountRepository.findByAccountNumber(fromAccountNumber) - .orElseThrow(() -> new AccountException(ACCOUNT_NOT_FOUND)); - Account toAccount = accountRepository.findByAccountNumber(toAccountNumber) - .orElseThrow(() -> new AccountException(ACCOUNT_NOT_FOUND)); - saveAndGetTransaction(TRANSFER, FAIL, fromAccount, amount); - saveAndGetTransaction(TRANSFER, FAIL, toAccount, amount); + public void saveFailedTransferTransaction(String fromAccountNumber, String toAccountNumber, Long amount, TransactionMethodType methodType) { + Account fromAccount = accountService.getAccountByNumber(fromAccountNumber); + Account toAccount = accountService.getAccountByNumber(toAccountNumber); + + saveAndGetTransaction(TRANSFER, FAIL, fromAccount, amount, methodType); + saveAndGetTransaction(TRANSFER, FAIL, toAccount, amount, methodType); } private void validateTransfer(User user, Account from, Account to, Long amount) { @@ -278,9 +290,16 @@ private void validateTransfer(User user, Account from, Account to, Long amount) } } - @Transactional(readOnly = true) - public boolean existsByAccountAndTransactionMethodType(Account account) { - return transactionRepository.existsByAccountAndTransactionMethodType(account, TransactionMethodType.AUTO); + private void validateDeposit(User user, Account account, Long amount) { + if (!Objects.equals(user.getId(), account.getUser().getId())) { + throw new AccountException(USER_ACCOUNT_UNMATCH); + } + if (account.getAccountStatus() != AccountStatus.ACTIVE) { + throw new AccountException(ACCOUNT_ALREADY_UNREGISTERED); + } + if (amount > MAX_DEPOSIT_AMOUNT) { + throw new AccountException(AMOUNT_EXCEED_DEPOSIT_LIMIT); + } } } diff --git a/src/main/java/com/track/fin/type/TransactionType.java b/src/main/java/com/track/fin/type/TransactionType.java index 0442ef1..adf7c9c 100644 --- a/src/main/java/com/track/fin/type/TransactionType.java +++ b/src/main/java/com/track/fin/type/TransactionType.java @@ -5,5 +5,4 @@ public enum TransactionType { DEPOSIT, WITHDRAWAL, TRANSFER - } From 148c4a03752ebe7f6756c91d111f7d31f2abac4b Mon Sep 17 00:00:00 2001 From: paransaik Date: Sat, 31 May 2025 13:47:03 +0900 Subject: [PATCH 2/6] =?UTF-8?q?fix:=20=EC=88=9C=ED=99=98=20=EC=B0=B8?= =?UTF-8?q?=EC=A1=B0(=ED=83=80=20repository=20=EC=82=AC=EC=9A=A9)=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fin/controller/AccountController.java | 10 +++++----- .../fin/record/WithdrawalRequestRecord.java | 5 ++++- ...Service.java => AccountFacadeService.java} | 17 +++++++---------- .../com/track/fin/service/AccountService.java | 17 +++++++++++------ .../com/track/fin/service/LoanService.java | 14 +++++--------- .../track/fin/service/TransactionService.java | 19 +++++++++---------- .../java/com/track/fin/type/ErrorCode.java | 1 - 7 files changed, 41 insertions(+), 42 deletions(-) rename src/main/java/com/track/fin/service/{AccountManagementService.java => AccountFacadeService.java} (83%) diff --git a/src/main/java/com/track/fin/controller/AccountController.java b/src/main/java/com/track/fin/controller/AccountController.java index 586b6fb..cd34f89 100644 --- a/src/main/java/com/track/fin/controller/AccountController.java +++ b/src/main/java/com/track/fin/controller/AccountController.java @@ -2,7 +2,7 @@ import com.track.fin.domain.Account; import com.track.fin.record.*; -import com.track.fin.service.AccountManagementService; +import com.track.fin.service.AccountFacadeService; import com.track.fin.service.AccountService; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; @@ -19,7 +19,7 @@ public class AccountController { private final AccountService accountService; - private final AccountManagementService accountManagementService; + private final AccountFacadeService accountFacadeService; @PostMapping public Account createAccount( @@ -49,7 +49,7 @@ public AccountRecord restoreAccount( @RequestParam Long userId, @RequestParam String accountNumber ) { - return accountManagementService.restoreAccount(userId, accountNumber); + return accountFacadeService.restoreAccount(userId, accountNumber); } @GetMapping("/{accountNumber}/transactions") @@ -104,13 +104,13 @@ public boolean validateAutoTransferNotRegistered( @DeleteMapping public AccountRecord deleteAccount(@Valid @RequestBody DeleteAccountRecord request) { - return accountManagementService.deleteAccount(request); + return accountFacadeService.deleteAccount(request); } @DeleteMapping("/expired/{accountNumber}") public void deleteExpiredAccount(@PathVariable String accountNumber) { Account account = accountService.getAccountByNumber(accountNumber); - accountManagementService.deleteIfExpired(account); + accountFacadeService.deleteIfExpired(account); } } diff --git a/src/main/java/com/track/fin/record/WithdrawalRequestRecord.java b/src/main/java/com/track/fin/record/WithdrawalRequestRecord.java index b125f8b..d00d553 100644 --- a/src/main/java/com/track/fin/record/WithdrawalRequestRecord.java +++ b/src/main/java/com/track/fin/record/WithdrawalRequestRecord.java @@ -1,7 +1,10 @@ package com.track.fin.record; import com.track.fin.type.TransactionMethodType; -import jakarta.validation.constraints.*; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; public record WithdrawalRequestRecord( diff --git a/src/main/java/com/track/fin/service/AccountManagementService.java b/src/main/java/com/track/fin/service/AccountFacadeService.java similarity index 83% rename from src/main/java/com/track/fin/service/AccountManagementService.java rename to src/main/java/com/track/fin/service/AccountFacadeService.java index 98234c4..4a57fe2 100644 --- a/src/main/java/com/track/fin/service/AccountManagementService.java +++ b/src/main/java/com/track/fin/service/AccountFacadeService.java @@ -5,8 +5,6 @@ import com.track.fin.exception.AccountException; import com.track.fin.record.AccountRecord; import com.track.fin.record.DeleteAccountRecord; -import com.track.fin.repository.AccountRepository; -import com.track.fin.repository.TransactionRepository; import com.track.fin.type.TransactionMethodType; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -20,14 +18,13 @@ @Service @RequiredArgsConstructor -public class AccountManagementService { +public class AccountFacadeService { - private final LoanService loanService; private final UserService userService; private final AccountService accountService; + private final LoanService loanService; private final TransactionService transactionService; - private final AccountRepository accountRepository; - private final TransactionRepository transactionRepository; + @Transactional public AccountRecord deleteAccount(DeleteAccountRecord deleteAccountRecord) { @@ -40,7 +37,7 @@ public AccountRecord deleteAccount(DeleteAccountRecord deleteAccountRecord) { transferRemainingBalanceIfExists(deleteAccountRecord, closingAccount); closingAccount.close(); - return AccountRecord.from(accountRepository.save(closingAccount)); + return AccountRecord.from(accountService.createAccount(closingAccount)); } @Transactional @@ -51,14 +48,14 @@ public AccountRecord restoreAccount(Long userId, String accountNumber) { validateRestoreAccount(user, account); account.restore(); - return AccountRecord.from(accountRepository.save(account)); + return AccountRecord.from(accountService.createAccount(account)); } @Transactional public void deleteIfExpired(Account account) { if (account.getUnregisteredAt() != null && account.getUnregisteredAt().isBefore(LocalDateTime.now().minusMonths(3))) { - accountRepository.delete(account); + accountService.deleteAccount(account); throw new AccountException(ACCOUNT_RESTORE_EXPIRED); } } @@ -77,7 +74,7 @@ private void transferRemainingBalanceIfExists(DeleteAccountRecord record, Accoun // TODO : 대출 로직 구현 후 사용 예정 private void validatePendingLoanOrAutoTransfer(Account account) { boolean hasLoan = loanService.existsUnpaidLoanByAccount(account); - boolean hasAutoTransfer = transactionRepository.existsByAccountAndTransactionMethodType(account, TransactionMethodType.AUTO); // 수정 + boolean hasAutoTransfer = transactionService.existsByAccountAndTransactionMethodType(account, TransactionMethodType.AUTO); if (hasLoan) { throw new AccountException(LOAN_EXISTS); diff --git a/src/main/java/com/track/fin/service/AccountService.java b/src/main/java/com/track/fin/service/AccountService.java index fcf15a2..d37c34a 100644 --- a/src/main/java/com/track/fin/service/AccountService.java +++ b/src/main/java/com/track/fin/service/AccountService.java @@ -5,10 +5,7 @@ import com.track.fin.exception.AccountException; import com.track.fin.record.AccountRecord; import com.track.fin.record.CreateAccount; -import com.track.fin.record.DeleteAccountRecord; import com.track.fin.repository.AccountRepository; -import com.track.fin.repository.UserRepository; -import com.track.fin.type.TransactionMethodType; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -18,7 +15,8 @@ import java.util.List; import static com.track.fin.design.singleton.AccountUtils.generateUniqueAccountNumber; -import static com.track.fin.design.singleton.AccountValidates.*; +import static com.track.fin.design.singleton.AccountValidates.validateInitialBalance; +import static com.track.fin.design.singleton.AccountValidates.validateRestoreAccount; import static com.track.fin.type.AccountStatus.ACTIVE; import static com.track.fin.type.AccountStatus.CLOSED; import static com.track.fin.type.ErrorCode.*; @@ -30,8 +28,10 @@ public class AccountService { private final AccountRepository accountRepository; private final UserService userService; - private final LoanService loanService; - private final UserRepository userRepository; + + public Account createAccount(Account account) { + return accountRepository.save(account); + } @Transactional public Account createAccount(CreateAccount createAccount) { @@ -124,4 +124,9 @@ public Account findByAccountNumber(String accountNumber) { return accountRepository.findByAccountNumber(accountNumber).orElseThrow(() -> new AccountException(ACCOUNT_NOT_FOUND)); } + public void deleteAccount(Account account) { + accountRepository.findById(account.getId()); + return; + } + } diff --git a/src/main/java/com/track/fin/service/LoanService.java b/src/main/java/com/track/fin/service/LoanService.java index d021052..d9328fc 100644 --- a/src/main/java/com/track/fin/service/LoanService.java +++ b/src/main/java/com/track/fin/service/LoanService.java @@ -5,32 +5,29 @@ import com.track.fin.domain.User; import com.track.fin.exception.AccountException; import com.track.fin.record.CreateLoanRecord; -import com.track.fin.repository.AccountRepository; import com.track.fin.repository.LoanRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import static com.track.fin.type.ErrorCode.ACCOUNT_NOT_FOUND; import static com.track.fin.type.ErrorCode.LOAN_NOT_FOUND; import static com.track.fin.type.LoanStatus.REPAID; @Service @RequiredArgsConstructor public class LoanService { - private final UserService userService; - private final AccountRepository accountRepository; private final LoanRepository loanRepository; + private final AccountService accountService; + private final UserService userService; + @Transactional public Loan createLoan(CreateLoanRecord createLoan) { User user = userService.get(createLoan.userId()); - Account account = accountRepository.findById(createLoan.accountId()) - .orElseThrow(() -> new AccountException(ACCOUNT_NOT_FOUND)); + Account account = accountService.getAccount(createLoan.accountId()); account.afterLoan(); - Loan loan = Loan.from(user, account, createLoan); return loanRepository.save(loan); } @@ -45,8 +42,7 @@ public Loan getLoan(Long loanId) { public Loan updateLoan(Long loanId, CreateLoanRecord updateLoan) { Loan loan = getLoan(loanId); - Account account = accountRepository.findById(updateLoan.accountId()) - .orElseThrow(() -> new AccountException(ACCOUNT_NOT_FOUND)); + Account account = accountService.getAccount(updateLoan.accountId()); User user = userService.get(updateLoan.userId()); loan.update(user, account, updateLoan); diff --git a/src/main/java/com/track/fin/service/TransactionService.java b/src/main/java/com/track/fin/service/TransactionService.java index 05bb7a4..e6adb06 100644 --- a/src/main/java/com/track/fin/service/TransactionService.java +++ b/src/main/java/com/track/fin/service/TransactionService.java @@ -6,7 +6,6 @@ import com.track.fin.exception.AccountException; import com.track.fin.record.TransferResponseRecord; import com.track.fin.repository.TransactionRepository; -import com.track.fin.repository.UserRepository; import com.track.fin.type.AccountStatus; import com.track.fin.type.TransactionMethodType; import com.track.fin.type.TransactionResultType; @@ -35,14 +34,13 @@ public class TransactionService { private static final long MAX_DEPOSIT_AMOUNT = 1_000_000L; private final TransactionRepository transactionRepository; - private final UserRepository userRepository; + private final AccountService accountService; private final UserService userService; @Transactional public TransferResponseRecord useBalance(Long userId, String accountNumber, Long amount, TransactionMethodType methodType) { - User user = userRepository.findById(userId) - .orElseThrow(() -> new AccountException(USER_NOT_FOUND)); + User user = userService.get(userId); Account account = accountService.getAccountByNumber(accountNumber); @@ -188,8 +186,7 @@ private void validateUserBalance(User user, Account account, Long amount) { @Transactional public Transaction deposit(Long userId, String accountNumber, Long amount, TransactionMethodType methodType) { - User user = userRepository.findById(userId) - .orElseThrow(() -> new AccountException(USER_NOT_FOUND)); + User user = userService.get(userId); Account account = accountService.getAccountByNumber(accountNumber); @@ -216,8 +213,7 @@ private void validateDeposit(User user, Account account) { @Transactional public Transaction withdraw(Long userId, String accountNumber, Long amount, TransactionMethodType methodType) { - User user = userRepository.findById(userId) - .orElseThrow(() -> new AccountException(USER_NOT_FOUND)); + User user = userService.get(userId); Account account = accountService.getAccountByNumber(accountNumber); @@ -251,8 +247,7 @@ private void validateWithdraw(User user, Account account, Long amount) { @Transactional public TransferResponseRecord transfer(Long userId, String fromAccountNumber, String toAccountNumber, Long amount, TransactionMethodType methodType) { - User user = userRepository.findById(userId) - .orElseThrow(() -> new AccountException(USER_NOT_FOUND)); + User user = userService.get(userId); Account fromAccount = accountService.getAccountByNumber(fromAccountNumber); Account toAccount = accountService.getAccountByNumber(toAccountNumber); @@ -302,4 +297,8 @@ private void validateDeposit(User user, Account account, Long amount) { } } + public boolean existsByAccountAndTransactionMethodType(Account account, TransactionMethodType auto) { + return transactionRepository.existsByAccountAndTransactionMethodType(account, auto); + } + } diff --git a/src/main/java/com/track/fin/type/ErrorCode.java b/src/main/java/com/track/fin/type/ErrorCode.java index 667358d..659f508 100644 --- a/src/main/java/com/track/fin/type/ErrorCode.java +++ b/src/main/java/com/track/fin/type/ErrorCode.java @@ -32,7 +32,6 @@ public enum ErrorCode { WITHDRAW_ACCOUNT_INACTIVE("출금 계좌가 해지 상태입니다."), CANNOT_CREATE_ACCOUNT_DUE_TO_RECENT_CLOSURE("해지된 계좌가 있어 1개월 내 신규 계좌 개설이 제한됩니다."), ACCOUNT_RESTORE_EXPIRED("계좌 복구 가능 기간이 지났습니다."), - ; private final String description; From 200794b9d066643db9f875a6efcc44917bb906e8 Mon Sep 17 00:00:00 2001 From: WonJin Date: Sun, 1 Jun 2025 15:23:39 +0900 Subject: [PATCH 3/6] =?UTF-8?q?refactor:=20ErrorCode=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/track/fin/domain/Loan.java | 2 +- src/main/java/com/track/fin/type/ErrorCode.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/track/fin/domain/Loan.java b/src/main/java/com/track/fin/domain/Loan.java index ca83eb0..3a13fbd 100644 --- a/src/main/java/com/track/fin/domain/Loan.java +++ b/src/main/java/com/track/fin/domain/Loan.java @@ -71,7 +71,7 @@ public void update(User user, Account account, CreateLoanRecord dto) { this.user = user; this.account = account; this.balance = dto.balance(); - // this.loanDate = LocalDateTime.now(); + this.loanDate = LocalDateTime.now(); this.delinquencyDate = dto.delinquencyDate(); this.loanStatus = dto.loanStatus(); this.loanType = dto.loanType(); diff --git a/src/main/java/com/track/fin/type/ErrorCode.java b/src/main/java/com/track/fin/type/ErrorCode.java index 659f508..dd98b70 100644 --- a/src/main/java/com/track/fin/type/ErrorCode.java +++ b/src/main/java/com/track/fin/type/ErrorCode.java @@ -32,6 +32,8 @@ public enum ErrorCode { WITHDRAW_ACCOUNT_INACTIVE("출금 계좌가 해지 상태입니다."), CANNOT_CREATE_ACCOUNT_DUE_TO_RECENT_CLOSURE("해지된 계좌가 있어 1개월 내 신규 계좌 개설이 제한됩니다."), ACCOUNT_RESTORE_EXPIRED("계좌 복구 가능 기간이 지났습니다."), + AMOUNT_EXCEED_DEPOSIT_LIMIT("입금 금액이 허용된 최대 한도를 초과했습니다") + ; private final String description; From 0b717eeefcf7cc1540b13db74a2da2030e51bbae Mon Sep 17 00:00:00 2001 From: WonJin Date: Tue, 10 Jun 2025 03:17:02 +0900 Subject: [PATCH 4/6] =?UTF-8?q?refactor:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20-.=20=ED=95=84=ED=84=B0=EA=B2=80=EC=83=89=EC=9D=84?= =?UTF-8?q?=20dto=EB=A1=9C=20=EB=A7=8C=EB=93=A4=EC=96=B4=EC=84=9C=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20-.validatePendingLoanOrAutoTransfer=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EB=B6=84=EB=A6=AC=ED=95=98=EC=97=AC=20?= =?UTF-8?q?=EB=8B=A8=EC=9D=BC=EC=B1=85=EC=9E=84=20=EC=9B=90=EC=B9=99=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../track/fin/controller/AccountController.java | 13 ++++--------- .../fin/record/TransferSearchRequestRecord.java | 17 +++++++++++++++++ .../track/fin/service/AccountFacadeService.java | 16 ++++++++-------- 3 files changed, 29 insertions(+), 17 deletions(-) create mode 100644 src/main/java/com/track/fin/record/TransferSearchRequestRecord.java diff --git a/src/main/java/com/track/fin/controller/AccountController.java b/src/main/java/com/track/fin/controller/AccountController.java index cd34f89..1a85d34 100644 --- a/src/main/java/com/track/fin/controller/AccountController.java +++ b/src/main/java/com/track/fin/controller/AccountController.java @@ -55,21 +55,16 @@ public AccountRecord restoreAccount( @GetMapping("/{accountNumber}/transactions") public TransferResponseRecord getTransferTransactions( @PathVariable String accountNumber, - @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startDate, - @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endDate, - @RequestParam(defaultValue = "true") boolean sortDesc, + @ModelAttribute TransferSearchRequestRecord searchRequest, @RequestBody TransferRequestRecord transferRequestRecord ) { return TransferResponseRecord.from(null); } + @GetMapping("/active") - public List getActiveAccounts( - @RequestParam("userId") Long userId - ) { - return accountService.getActiveAccounts(userId).stream() - .map(AccountRecord::from) - .toList(); + public List getActiveAccounts(@RequestParam("userId") Long userId) { + return accountService.getActiveAccounts(userId); } @GetMapping("/{accountId}/collateral") diff --git a/src/main/java/com/track/fin/record/TransferSearchRequestRecord.java b/src/main/java/com/track/fin/record/TransferSearchRequestRecord.java new file mode 100644 index 0000000..302adec --- /dev/null +++ b/src/main/java/com/track/fin/record/TransferSearchRequestRecord.java @@ -0,0 +1,17 @@ +package com.track.fin.record; + +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +public record TransferSearchRequestRecord( + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + LocalDateTime startDate, + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + LocalDateTime endDate, + + Boolean sortDesc, + + String transactionType +) {} diff --git a/src/main/java/com/track/fin/service/AccountFacadeService.java b/src/main/java/com/track/fin/service/AccountFacadeService.java index 4a57fe2..52505bb 100644 --- a/src/main/java/com/track/fin/service/AccountFacadeService.java +++ b/src/main/java/com/track/fin/service/AccountFacadeService.java @@ -72,14 +72,14 @@ private void transferRemainingBalanceIfExists(DeleteAccountRecord record, Accoun } } // TODO : 대출 로직 구현 후 사용 예정 - private void validatePendingLoanOrAutoTransfer(Account account) { - boolean hasLoan = loanService.existsUnpaidLoanByAccount(account); - boolean hasAutoTransfer = transactionService.existsByAccountAndTransactionMethodType(account, TransactionMethodType.AUTO); - - if (hasLoan) { - throw new AccountException(LOAN_EXISTS); - } - if (hasAutoTransfer) { + private void validateUnpaidLoan(Account account) { + if (loanService.existsUnpaidLoanByAccount(account)) { + throw new AccountException(LOAN_EXISTS); + } + } + + private void validateAutoTransfer(Account account) { + if (transactionService.existsByAccountAndTransactionMethodType(account, TransactionMethodType.AUTO)) { throw new AccountException(AUTO_TRANSFER_EXISTS); } } From 88eab2e13e6d58d37d13965cd7edf3197db9afc0 Mon Sep 17 00:00:00 2001 From: WonJin Date: Tue, 10 Jun 2025 03:17:37 +0900 Subject: [PATCH 5/6] =?UTF-8?q?refactor:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20-.=20=ED=95=84=ED=84=B0=EA=B2=80=EC=83=89=EC=9D=84?= =?UTF-8?q?=20dto=EB=A1=9C=20=EB=A7=8C=EB=93=A4=EC=96=B4=EC=84=9C=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20-.validatePendingLoanOrAutoTransfer=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EB=B6=84=EB=A6=AC=ED=95=98=EC=97=AC=20?= =?UTF-8?q?=EB=8B=A8=EC=9D=BC=EC=B1=85=EC=9E=84=20=EC=9B=90=EC=B9=99=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/track/fin/service/AccountService.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/track/fin/service/AccountService.java b/src/main/java/com/track/fin/service/AccountService.java index d37c34a..70fb19e 100644 --- a/src/main/java/com/track/fin/service/AccountService.java +++ b/src/main/java/com/track/fin/service/AccountService.java @@ -89,9 +89,11 @@ private boolean hasClosedAccountWithinLastMonth(User user) { } @Transactional(readOnly = true) - public List getActiveAccounts(Long userId) { + public List getActiveAccounts(Long userId) { User user = userService.get(userId); - return accountRepository.findByUserAndAccountStatus(user, ACTIVE); + return accountRepository.findByUserAndAccountStatus(user, ACTIVE).stream() + .map(AccountRecord::from) + .toList(); } @Transactional From 5d0b3c20d851f25f341dba5752375ddc85537527 Mon Sep 17 00:00:00 2001 From: WonJin Date: Tue, 10 Jun 2025 03:18:34 +0900 Subject: [PATCH 6/6] =?UTF-8?q?squash!=20refactor:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81=20-.=20=ED=95=84=ED=84=B0=EA=B2=80=EC=83=89?= =?UTF-8?q?=EC=9D=84=20dto=EB=A1=9C=20=EB=A7=8C=EB=93=A4=EC=96=B4=EC=84=9C?= =?UTF-8?q?=20=EA=B4=80=EB=A6=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20-.validatePendingLoanOrAutoTransfer=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EB=B6=84=EB=A6=AC=ED=95=98=EC=97=AC=20?= =?UTF-8?q?=EB=8B=A8=EC=9D=BC=EC=B1=85=EC=9E=84=20=EC=9B=90=EC=B9=99=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gradle/7.4.1/checksums/checksums.lock | Bin 17 -> 0 bytes .../dependencies-accessors.lock | Bin 17 -> 0 bytes .../7.4.1/dependencies-accessors/gc.properties | 0 .gradle/7.4.1/fileChanges/last-build.bin | Bin 1 -> 0 bytes .gradle/7.4.1/fileHashes/fileHashes.lock | Bin 17 -> 0 bytes .gradle/7.4.1/gc.properties | 0 .../8.5/executionHistory/executionHistory.lock | Bin 17 -> 17 bytes .gradle/8.5/fileHashes/fileHashes.lock | Bin 17 -> 17 bytes .../buildOutputCleanup/buildOutputCleanup.lock | Bin 17 -> 17 bytes 9 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .gradle/7.4.1/checksums/checksums.lock delete mode 100644 .gradle/7.4.1/dependencies-accessors/dependencies-accessors.lock delete mode 100644 .gradle/7.4.1/dependencies-accessors/gc.properties delete mode 100644 .gradle/7.4.1/fileChanges/last-build.bin delete mode 100644 .gradle/7.4.1/fileHashes/fileHashes.lock delete mode 100644 .gradle/7.4.1/gc.properties diff --git a/.gradle/7.4.1/checksums/checksums.lock b/.gradle/7.4.1/checksums/checksums.lock deleted file mode 100644 index 7a4d7e30f25d96f0721033c1a87e1d6ee1fbfb23..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17 ScmZR!Gv-Xwe)U0v0SW*krvniH diff --git a/.gradle/7.4.1/dependencies-accessors/dependencies-accessors.lock b/.gradle/7.4.1/dependencies-accessors/dependencies-accessors.lock deleted file mode 100644 index d0c96a3f4a540ded79be5ca483794bc8987e8729..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17 ScmZRs4PlqyDV{f#0SW*ggaW1j diff --git a/.gradle/7.4.1/dependencies-accessors/gc.properties b/.gradle/7.4.1/dependencies-accessors/gc.properties deleted file mode 100644 index e69de29..0000000 diff --git a/.gradle/7.4.1/fileChanges/last-build.bin b/.gradle/7.4.1/fileChanges/last-build.bin deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/.gradle/7.4.1/fileHashes/fileHashes.lock b/.gradle/7.4.1/fileHashes/fileHashes.lock deleted file mode 100644 index e2e7780ab5535eccda027b4f96d1ace84eabe34f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17 TcmZRs`SHDB+l}du8K3|FNH1 literal 17 TcmZSnC8i^IVv@vT1}FdkEd&Fm diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock index ec2cf8bf9bd22240b5394f578e50a17a7433cc1e..965ad9cce5639a7a417871b8dcd2e48b399d242b 100644 GIT binary patch literal 17 UcmZSHa=v!jhea!1FhIa;07s$*R{#J2 literal 17 UcmZSHa=v!jhea!1FhBqk07nT0z5oCK