From 34319438447f84330c7aaed84180936265e8aa91 Mon Sep 17 00:00:00 2001 From: jykim-rust Date: Sun, 28 Jan 2024 17:27:51 +0900 Subject: [PATCH 1/4] =?UTF-8?q?:adhesive=5Fbandage:=20issue#11=20=EB=B9=84?= =?UTF-8?q?=EB=B0=80=EB=B2=88=ED=98=B8=EB=A5=BC=20passwordEncoder=EB=A1=9C?= =?UTF-8?q?=20=EC=9D=B8=EC=BD=94=EB=94=A9=ED=95=B4=EC=84=9C=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 ++ .../com/eum/bank/config/PasswordEncoderConfig.java | 14 ++++++++++++++ .../java/com/eum/bank/service/AccountService.java | 6 +++++- 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/eum/bank/config/PasswordEncoderConfig.java diff --git a/build.gradle b/build.gradle index 61359f0..179eccc 100644 --- a/build.gradle +++ b/build.gradle @@ -31,6 +31,8 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation 'io.springfox:springfox-boot-starter:3.0.0' implementation 'io.springfox:springfox-swagger-ui:3.0.0' + implementation 'org.springframework.security:spring-security-crypto:5.7.1' + } diff --git a/src/main/java/com/eum/bank/config/PasswordEncoderConfig.java b/src/main/java/com/eum/bank/config/PasswordEncoderConfig.java new file mode 100644 index 0000000..26171d0 --- /dev/null +++ b/src/main/java/com/eum/bank/config/PasswordEncoderConfig.java @@ -0,0 +1,14 @@ +package com.eum.bank.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +public class PasswordEncoderConfig { + @Bean + public PasswordEncoder passwordEncoder() { + return PasswordEncoderFactories.createDelegatingPasswordEncoder(); + } +} diff --git a/src/main/java/com/eum/bank/service/AccountService.java b/src/main/java/com/eum/bank/service/AccountService.java index 865d3aa..ced31a7 100644 --- a/src/main/java/com/eum/bank/service/AccountService.java +++ b/src/main/java/com/eum/bank/service/AccountService.java @@ -8,6 +8,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.security.crypto.password.PasswordEncoder; import java.util.Random; @Service @@ -15,6 +16,8 @@ public class AccountService { private final AccountRepository accountRepository; + private final PasswordEncoder passwordEncoder; + public APIResponse createAccount(Long password) { @@ -27,7 +30,7 @@ public APIResponse createAccount(Long password) { Account account = Account.builder() .accountNumber(accountNumber) - .password(password.toString()) + .password(passwordEncoder.encode(password.toString())) .totalBudget(0L) .availableBudget(0L) .build(); @@ -68,4 +71,5 @@ public Boolean validateAccountNumber(String accountNumber) { } + } From 4cb8e2ec64712d11390b715339e27fb80eca2cdc Mon Sep 17 00:00:00 2001 From: jykim-rust Date: Sun, 28 Jan 2024 17:50:49 +0900 Subject: [PATCH 2/4] =?UTF-8?q?:adhesive=5Fbandage:=20JPARepo=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EB=A9=94=EC=86=8C=EB=93=9C=EC=97=90=20=EB=A7=9E?= =?UTF-8?q?=EA=B2=8C=20=EA=B3=84=EC=A2=8C=20=EC=A1=B0=ED=9A=8C=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4=20=EB=B0=98=ED=99=98=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/eum/bank/repository/AccountRepository.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/eum/bank/repository/AccountRepository.java b/src/main/java/com/eum/bank/repository/AccountRepository.java index 4aae0e5..71d02c5 100644 --- a/src/main/java/com/eum/bank/repository/AccountRepository.java +++ b/src/main/java/com/eum/bank/repository/AccountRepository.java @@ -3,7 +3,9 @@ import com.eum.bank.domain.account.entity.Account; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface AccountRepository extends JpaRepository { Account save(Account account); - Boolean findByAccountNumber(String accountNumber); + Optional findByAccountNumber(String accountNumber); } \ No newline at end of file From f40d4a300b61f7b370363b046fd03fe839cdcb62 Mon Sep 17 00:00:00 2001 From: jykim-rust Date: Sun, 28 Jan 2024 17:51:08 +0900 Subject: [PATCH 3/4] =?UTF-8?q?:sparkles:=20#12=20=EA=B3=84=EC=A2=8C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/dto/request/AccountRequestDTO.java | 9 +++++++++ .../common/dto/response/AccountResponseDTO.java | 10 +++++++++- .../com/eum/bank/service/AccountService.java | 17 +++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/eum/bank/common/dto/request/AccountRequestDTO.java b/src/main/java/com/eum/bank/common/dto/request/AccountRequestDTO.java index 2654aef..c1a8e4a 100644 --- a/src/main/java/com/eum/bank/common/dto/request/AccountRequestDTO.java +++ b/src/main/java/com/eum/bank/common/dto/request/AccountRequestDTO.java @@ -10,4 +10,13 @@ public static class CreateAccount { @NotEmpty(message = "비밀번호를 입력해주세요.") private Long password; } + + // 계좌 조회 요청 + @Getter + public static class GetAccount { + @NotEmpty(message = "계좌 번호를 입력해주세요.") + private String accountNumber; + @NotEmpty(message = "비밀번호를 입력해주세요.") + private String password; + } } diff --git a/src/main/java/com/eum/bank/common/dto/response/AccountResponseDTO.java b/src/main/java/com/eum/bank/common/dto/response/AccountResponseDTO.java index 6bd1b95..c2a4d92 100644 --- a/src/main/java/com/eum/bank/common/dto/response/AccountResponseDTO.java +++ b/src/main/java/com/eum/bank/common/dto/response/AccountResponseDTO.java @@ -3,7 +3,6 @@ import jakarta.validation.constraints.NotEmpty; import lombok.Builder; import lombok.Getter; -import lombok.Setter; public class AccountResponseDTO { // 계좌 생성 응답 @@ -13,4 +12,13 @@ public static class Create { @NotEmpty(message = "계좌 번호가 생성되어야 합니다.") private String accountNumber; } + + // 계좌 조회 응답 + @Builder + @Getter + public static class AccountInfo { + private String accountNumber; + private Long totalBudget; + private Long availableBudget; + } } diff --git a/src/main/java/com/eum/bank/service/AccountService.java b/src/main/java/com/eum/bank/service/AccountService.java index ced31a7..49c6dbb 100644 --- a/src/main/java/com/eum/bank/service/AccountService.java +++ b/src/main/java/com/eum/bank/service/AccountService.java @@ -1,7 +1,9 @@ package com.eum.bank.service; import com.eum.bank.common.APIResponse; +import com.eum.bank.common.ErrorResponse; import com.eum.bank.common.dto.response.AccountResponseDTO; +import com.eum.bank.common.enums.ErrorCode; import com.eum.bank.common.enums.SuccessCode; import com.eum.bank.domain.account.entity.Account; import com.eum.bank.repository.AccountRepository; @@ -70,6 +72,21 @@ public Boolean validateAccountNumber(String accountNumber) { return true; } + // 계좌번호와 비밀번호로 계좌 조회 + public APIResponse getAccount(String accountNumber, String password) { + Account account = accountRepository.findByAccountNumber(accountNumber).orElseThrow(() -> new IllegalArgumentException("Invalid account number")); + + // 비밀번호 검증 + if (!passwordEncoder.matches(password, account.getPassword())) { + throw new IllegalArgumentException("Invalid password"); + } + return APIResponse.of(SuccessCode.SELECT_SUCCESS, AccountResponseDTO.AccountInfo.builder() + .accountNumber(account.getAccountNumber()) + .totalBudget(account.getTotalBudget()) + .availableBudget(account.getAvailableBudget()) + .build()); + } + } From ca0e133815d62d8d1de92539bceb1bd56eebe00c Mon Sep 17 00:00:00 2001 From: jykim-rust Date: Sun, 28 Jan 2024 18:23:39 +0900 Subject: [PATCH 4/4] =?UTF-8?q?:sparkles:=20#13=20=EA=B3=84=EC=A2=8C=20?= =?UTF-8?q?=EC=9E=90=EC=9C=A0=EC=86=A1=EA=B8=88=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AccountTransferHistoryRequestDTO.java | 41 +++++++++++ .../TotalTransferHistoryRequestDTO.java | 33 +++++++++ .../dto/response/AccountResponseDTO.java | 10 +++ .../TotalTransferHistoryResponseDTO.java | 30 ++++++++ .../com/eum/bank/service/AccountService.java | 71 +++++++++++++++++++ .../AccountTransferHistoryService.java | 19 +++++ .../service/TotalTransferHistoryService.java | 17 +++++ 7 files changed, 221 insertions(+) create mode 100644 src/main/java/com/eum/bank/common/dto/request/AccountTransferHistoryRequestDTO.java create mode 100644 src/main/java/com/eum/bank/common/dto/request/TotalTransferHistoryRequestDTO.java create mode 100644 src/main/java/com/eum/bank/common/dto/response/TotalTransferHistoryResponseDTO.java create mode 100644 src/main/java/com/eum/bank/service/AccountTransferHistoryService.java create mode 100644 src/main/java/com/eum/bank/service/TotalTransferHistoryService.java diff --git a/src/main/java/com/eum/bank/common/dto/request/AccountTransferHistoryRequestDTO.java b/src/main/java/com/eum/bank/common/dto/request/AccountTransferHistoryRequestDTO.java new file mode 100644 index 0000000..54a7ebe --- /dev/null +++ b/src/main/java/com/eum/bank/common/dto/request/AccountTransferHistoryRequestDTO.java @@ -0,0 +1,41 @@ +package com.eum.bank.common.dto.request; + +import com.eum.bank.domain.account.entity.Account; +import com.eum.bank.domain.account.entity.AccountTransferHistory; +import jakarta.validation.constraints.NotEmpty; +import lombok.Builder; +import lombok.Getter; + +public class AccountTransferHistoryRequestDTO { + + // 내역 생성 + @Getter + @Builder + public static class CreateAccountTransferHistory { + @NotEmpty(message = "거래 내역을 생성할 계좌를 입력해주세요.") + private Account ownerAccount; + @NotEmpty(message = "거래 상대 계좌를 입력해주세요.") + private Account oppenentAccount; + @NotEmpty(message = "거래 금액을 입력해주세요.") + private Long transferAmount; + @NotEmpty(message = "거래 유형을 입력해주세요.") + private String transferType; + @NotEmpty(message = "거래 후 잔액을 입력해주세요.") + private Long budgetAfterTransfer; + @NotEmpty(message = "거래 메모를 입력해주세요.") + private String memo; + + // toEntity + public AccountTransferHistory toEntity() { + return AccountTransferHistory.builder() + .ownerAccount(ownerAccount) + .oppenentAccount(oppenentAccount) + .transferAmount(transferAmount) + .transferType(transferType) + .budgetAfterTransfer(budgetAfterTransfer) + .memo(memo) + .build(); + } + } + +} diff --git a/src/main/java/com/eum/bank/common/dto/request/TotalTransferHistoryRequestDTO.java b/src/main/java/com/eum/bank/common/dto/request/TotalTransferHistoryRequestDTO.java new file mode 100644 index 0000000..e08f89d --- /dev/null +++ b/src/main/java/com/eum/bank/common/dto/request/TotalTransferHistoryRequestDTO.java @@ -0,0 +1,33 @@ +package com.eum.bank.common.dto.request; + +import com.eum.bank.domain.account.entity.Account; +import com.eum.bank.domain.account.entity.TotalTransferHistory; +import jakarta.validation.constraints.NotEmpty; +import lombok.Builder; +import lombok.Getter; + +public class TotalTransferHistoryRequestDTO { + + @Getter + @Builder + public static class CreateTotalTransferHistory { + @NotEmpty(message = "송금자 계좌를 입력해주세요.") + private Account senderAccount; + @NotEmpty(message = "수취자 계좌를 입력해주세요.") + private Account receiverAccount; + @NotEmpty(message = "송금 금액을 입력해주세요.") + private Long transferAmount; + @NotEmpty(message = "거래유형을 입력해주세요.") + private String transferType; + + // toEntity + public TotalTransferHistory toEntity() { + return TotalTransferHistory.builder() + .senderAccount(senderAccount) + .receiverAccount(receiverAccount) + .transferAmount(transferAmount) + .transferType(transferType) + .build(); + } + } +} diff --git a/src/main/java/com/eum/bank/common/dto/response/AccountResponseDTO.java b/src/main/java/com/eum/bank/common/dto/response/AccountResponseDTO.java index c2a4d92..5732d2a 100644 --- a/src/main/java/com/eum/bank/common/dto/response/AccountResponseDTO.java +++ b/src/main/java/com/eum/bank/common/dto/response/AccountResponseDTO.java @@ -1,5 +1,6 @@ package com.eum.bank.common.dto.response; +import com.eum.bank.domain.account.entity.Account; import jakarta.validation.constraints.NotEmpty; import lombok.Builder; import lombok.Getter; @@ -20,5 +21,14 @@ public static class AccountInfo { private String accountNumber; private Long totalBudget; private Long availableBudget; + + // fromEntity + public static AccountInfo fromEntity(Account account) { + return AccountInfo.builder() + .accountNumber(account.getAccountNumber()) + .totalBudget(account.getTotalBudget()) + .availableBudget(account.getAvailableBudget()) + .build(); + } } } diff --git a/src/main/java/com/eum/bank/common/dto/response/TotalTransferHistoryResponseDTO.java b/src/main/java/com/eum/bank/common/dto/response/TotalTransferHistoryResponseDTO.java new file mode 100644 index 0000000..ad88629 --- /dev/null +++ b/src/main/java/com/eum/bank/common/dto/response/TotalTransferHistoryResponseDTO.java @@ -0,0 +1,30 @@ +package com.eum.bank.common.dto.response; + +import com.eum.bank.domain.account.entity.TotalTransferHistory; +import lombok.Builder; +import lombok.Getter; + +public class TotalTransferHistoryResponseDTO { + + // 거래 내역 반환 + @Builder + @Getter + public static class GetTotalTransferHistory { + private Long id; + private AccountResponseDTO.AccountInfo senderAccount; + private AccountResponseDTO.AccountInfo receiverAccount; + private Long transferAmount; + private String transferType; + + // fromEntity + public static GetTotalTransferHistory fromEntity(TotalTransferHistory totalTransferHistory) { + return GetTotalTransferHistory.builder() + .id(totalTransferHistory.getId()) + .senderAccount(AccountResponseDTO.AccountInfo.fromEntity(totalTransferHistory.getSenderAccount())) + .receiverAccount(AccountResponseDTO.AccountInfo.fromEntity(totalTransferHistory.getReceiverAccount())) + .transferAmount(totalTransferHistory.getTransferAmount()) + .transferType(totalTransferHistory.getTransferType()) + .build(); + } + } +} diff --git a/src/main/java/com/eum/bank/service/AccountService.java b/src/main/java/com/eum/bank/service/AccountService.java index 49c6dbb..a870c3d 100644 --- a/src/main/java/com/eum/bank/service/AccountService.java +++ b/src/main/java/com/eum/bank/service/AccountService.java @@ -2,11 +2,16 @@ import com.eum.bank.common.APIResponse; import com.eum.bank.common.ErrorResponse; +import com.eum.bank.common.dto.request.AccountTransferHistoryRequestDTO; +import com.eum.bank.common.dto.request.TotalTransferHistoryRequestDTO; import com.eum.bank.common.dto.response.AccountResponseDTO; import com.eum.bank.common.enums.ErrorCode; import com.eum.bank.common.enums.SuccessCode; import com.eum.bank.domain.account.entity.Account; +import com.eum.bank.domain.account.entity.AccountTransferHistory; +import com.eum.bank.domain.account.entity.TotalTransferHistory; import com.eum.bank.repository.AccountRepository; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -19,6 +24,9 @@ public class AccountService { private final AccountRepository accountRepository; private final PasswordEncoder passwordEncoder; + private final AccountTransferHistoryService accountTransferHistoryService; + private final TotalTransferHistoryService totalTransferHistoryService; + public APIResponse createAccount(Long password) { @@ -87,6 +95,69 @@ public APIResponse getAccount(String accountNumb .build()); } + // 자유송금 + // 1. 송금자 계좌, 수신자 계좌 상태 검증 + // 2. 송금자 잔액 확인 + // 3. 송금자 전체금액, 가용금액 마이너스 + // 4. 수신자 전체금액, 가용금액 플러스 + // 5. 통합 거래내역 생성, 각 계좌 거래내역 생성 + @Transactional + public APIResponse transfer(String senderAccountNumber, String receiverAccountNumber, Long amount, String password, String transferType) { + Account senderAccount = accountRepository.findByAccountNumber(senderAccountNumber).orElseThrow(() -> new IllegalArgumentException("Invalid account number")); + Account receiverAccount = accountRepository.findByAccountNumber(receiverAccountNumber).orElseThrow(() -> new IllegalArgumentException("Invalid account number")); + + // 비밀번호 검증 + if (!passwordEncoder.matches(password, senderAccount.getPassword())) { + throw new IllegalArgumentException("Invalid password"); + } + + // 송금자 잔액 검증 + if (senderAccount.getAvailableBudget() < amount) { + throw new IllegalArgumentException("Insufficient balance"); + } + + // 송금자 잔액 마이너스 + senderAccount.setTotalBudget(senderAccount.getTotalBudget() - amount); + senderAccount.setAvailableBudget(senderAccount.getAvailableBudget() - amount); + // 수신자 잔액 플러스 + receiverAccount.setTotalBudget(receiverAccount.getTotalBudget() + amount); + receiverAccount.setAvailableBudget(receiverAccount.getAvailableBudget() + amount); + + // 통합 거래내역 생성 + TotalTransferHistory response = totalTransferHistoryService.save( + TotalTransferHistoryRequestDTO.CreateTotalTransferHistory.builder() + .senderAccount(senderAccount) + .receiverAccount(receiverAccount) + .transferAmount(amount) + .transferType(transferType) + .build() + ); + + // 각 계좌 거래내역 생성 + accountTransferHistoryService.save( + AccountTransferHistoryRequestDTO.CreateAccountTransferHistory.builder() + .ownerAccount(senderAccount) + .oppenentAccount(receiverAccount) + .transferAmount(amount) + .transferType(transferType) + .budgetAfterTransfer(senderAccount.getAvailableBudget()) + .memo("") + .build() + ); + accountTransferHistoryService.save( + AccountTransferHistoryRequestDTO.CreateAccountTransferHistory.builder() + .ownerAccount(receiverAccount) + .oppenentAccount(senderAccount) + .transferAmount(-amount) + .transferType(transferType) + .budgetAfterTransfer(receiverAccount.getAvailableBudget()) + .memo("") + .build() + ); + + return APIResponse.of(SuccessCode.INSERT_SUCCESS, response); + } + } diff --git a/src/main/java/com/eum/bank/service/AccountTransferHistoryService.java b/src/main/java/com/eum/bank/service/AccountTransferHistoryService.java new file mode 100644 index 0000000..4d08da5 --- /dev/null +++ b/src/main/java/com/eum/bank/service/AccountTransferHistoryService.java @@ -0,0 +1,19 @@ +package com.eum.bank.service; + +import com.eum.bank.common.dto.request.AccountTransferHistoryRequestDTO; +import com.eum.bank.domain.account.entity.AccountTransferHistory; +import com.eum.bank.repository.AccountTransferHistoryRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class AccountTransferHistoryService { + + private final AccountTransferHistoryRepository accountTransferHistoryRepository; + + // 저장 + public AccountTransferHistory save(AccountTransferHistoryRequestDTO.CreateAccountTransferHistory dto) { + return accountTransferHistoryRepository.save(dto.toEntity()); + } +} diff --git a/src/main/java/com/eum/bank/service/TotalTransferHistoryService.java b/src/main/java/com/eum/bank/service/TotalTransferHistoryService.java new file mode 100644 index 0000000..9dcb728 --- /dev/null +++ b/src/main/java/com/eum/bank/service/TotalTransferHistoryService.java @@ -0,0 +1,17 @@ +package com.eum.bank.service; + +import com.eum.bank.common.dto.request.TotalTransferHistoryRequestDTO; +import com.eum.bank.domain.account.entity.TotalTransferHistory; +import com.eum.bank.repository.TotalTransferHistoryRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class TotalTransferHistoryService { + private final TotalTransferHistoryRepository totalTransferHistoryRepository; + + public TotalTransferHistory save(TotalTransferHistoryRequestDTO.CreateTotalTransferHistory dto) { + return totalTransferHistoryRepository.save(dto.toEntity()); + } +}