Skip to content

Commit

Permalink
Merge branch 'develop/v1' into feat/#18
Browse files Browse the repository at this point in the history
  • Loading branch information
cheesecrust authored Jan 31, 2024
2 parents 3f755c4 + 6b875a6 commit 4a63974
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.eum.bank.common.dto.request;

import lombok.Builder;
import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import lombok.Builder;

public class DealRequestDTO {
@Builder
Expand All @@ -19,4 +20,51 @@ public static class Create{
private Long postId;
}


@Getter
public static class completeDeal{
// 거래ID
@NotEmpty(message = "거래ID를 입력해주세요.")
private Long dealId;
// 예치금
@NotEmpty(message = "예치금을 입력해주세요.")
private Long deposit;
// 수신 계좌번호 리스트
@NotEmpty(message = "수신 계좌번호를 입력해주세요.")
private String[] receiverAccountNumbers;
}


@Getter
public static class updateDeal{
// 거래ID
@NotEmpty(message = "거래ID를 입력해주세요.")
private Long dealId;
// 송금자 계좌번호
@NotEmpty(message = "송금자 계좌번호를 입력해주세요.")
private String senderAccountNumber;
// 비밀번호
@NotEmpty(message = "비밀번호를 입력해주세요.")
private String password;
// 예치금
@NotEmpty(message = "예치금을 입력해주세요.")
private Long deposit;
// 최대인원수
@NotEmpty(message = "최대인원수를 입력해주세요.")
private Long numberOfPeople;
}

@Getter
public static class cancelDeal{
// 거래ID
@NotEmpty(message = "거래ID를 입력해주세요.")
private Long dealId;
// 송금자 계좌번호
@NotEmpty(message = "송금자 계좌번호를 입력해주세요.")
private String senderAccountNumber;
// 비밀번호
@NotEmpty(message = "비밀번호를 입력해주세요.")
private String password;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ public static class AccountInfo {
private String accountNumber;
private Long totalBudget;
private Long availableBudget;
private Boolean isBlocked;

// fromEntity
public static AccountInfo fromEntity(Account account) {
return AccountInfo.builder()
.accountNumber(account.getAccountNumber())
.totalBudget(account.getTotalBudget())
.availableBudget(account.getAvailableBudget())
.isBlocked(account.getIsBlocked())
.build();
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/eum/bank/domain/account/entity/Account.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,8 @@ public class Account extends BaseEntity {
// 계좌 사용 가능 잔액
@Column(name = "available_budget", nullable = false)
private Long availableBudget;

// 블락 여부
@Column(name = "is_blocked", nullable = false)
private Boolean isBlocked;
}
4 changes: 4 additions & 0 deletions src/main/java/com/eum/bank/domain/deal/entity/Deal.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ public class Deal extends BaseEntity {
private Account senderAccount;

// 상태
// a: 거래 생성 후 거래 성사 전 (수신계좌가 안엮인 상태)
// b: 거래 성사 후 (수신계좌가 엮인 상태)
// c: 거래 취소 됨
// d: 거래 수행 됨
@Column(name = "status", nullable = false)
private String status;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.eum.bank.repository;

import com.eum.bank.domain.deal.entity.Deal;
import com.eum.bank.domain.deal.entity.DealReceiver;
import org.springframework.data.jpa.repository.JpaRepository;

public interface DealReceiverRepository extends JpaRepository<DealReceiver, Long> {
void deleteByDeal(Deal deal);
}
16 changes: 13 additions & 3 deletions src/main/java/com/eum/bank/service/AccountService.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public APIResponse<?> createAccount(String password) {
.password(passwordEncoder.encode(password))
.totalBudget(0L)
.availableBudget(0L)
.isBlocked(false)
.build();

accountRepository.save(account);
Expand Down Expand Up @@ -77,9 +78,18 @@ public Boolean validateAccountNumber(String accountNumber) {
return true;
}

// 계좌 검증 (계좌 존재여부 + 블락 여부)
public Account validateAccount(String accountNumber) {
Account account = accountRepository.findByAccountNumber(accountNumber).orElseThrow(() -> new IllegalArgumentException("Invalid account number : " + accountNumber));
if(account.getIsBlocked()){
throw new IllegalArgumentException("Blocked account : " + accountNumber);
}
return account;
}

// 계좌번호와 비밀번호로 계좌 조회
public APIResponse<AccountResponseDTO.AccountInfo> getAccount(String accountNumber, String password) {
Account account = accountRepository.findByAccountNumber(accountNumber).orElseThrow(() -> new IllegalArgumentException("Invalid account number"));
Account account = this.validateAccount(accountNumber);

// 비밀번호 검증
if (!passwordEncoder.matches(password, account.getPassword())) {
Expand All @@ -104,8 +114,8 @@ public Account getAccount(String accountNumber) {
// 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"));
Account senderAccount = this.validateAccount(senderAccountNumber);
Account receiverAccount = this.validateAccount(receiverAccountNumber);

// 비밀번호 검증
if (!passwordEncoder.matches(password, senderAccount.getPassword())) {
Expand Down
149 changes: 149 additions & 0 deletions src/main/java/com/eum/bank/service/DealService.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
package com.eum.bank.service;

import com.eum.bank.common.APIResponse;

import com.eum.bank.common.dto.request.DealRequestDTO;
import com.eum.bank.common.enums.SuccessCode;
import com.eum.bank.domain.account.entity.Account;
import com.eum.bank.domain.deal.entity.Deal;
import com.eum.bank.domain.deal.entity.DealReceiver;
import com.eum.bank.repository.DealReceiverRepository;
import com.eum.bank.repository.DealRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@RequiredArgsConstructor
public class DealService {

private final DealRepository dealRepository;
private final AccountService accountService;
private final DealReceiverRepository dealReceiverRepository;
private final PasswordEncoder passwordEncoder;


// 거래 생성
public void createDeal(Account accountNumber, Long deposit, Long maxPeople, Long postId){
Expand All @@ -27,4 +40,140 @@ public void createDeal(Account accountNumber, Long deposit, Long maxPeople, Long

dealRepository.save(deal);
}

// 거래 성사
// 1. 거래상태 a인지 확인
// 2. 수신계좌들 검증
// 3. 최종 예치금 확인해서 차액만큼 가용금액 플러스
// 4. 수신자 계좌번호 거래에 묶기
// 5. 거래상태 b로 변경
// 6. 거래ID 반환
@Transactional
public APIResponse<Long> completeDeal(DealRequestDTO.completeDeal dto) {
// 거래 검증 및 거래 상태 a 인지 검증
Deal deal = this.validateDeal(dto.getDealId(), List.of("a"));

List<String> receiverAccountNumbers = List.of(dto.getReceiverAccountNumbers());

// 송신계좌 검증 및 잔액 확인
// 최종 예치금 - 기존 거래의 예치금 만큼 송신자 계좌의 가용금액을 마이너스
Account senderAccount = accountService.validateAccount(deal.getSenderAccount().getAccountNumber());
Long finalDeposit = dto.getDeposit() - deal.getDeposit();
if (senderAccount.getAvailableBudget() < finalDeposit) {
throw new IllegalArgumentException("잔액이 부족합니다.");
}
senderAccount.setAvailableBudget(senderAccount.getAvailableBudget() - finalDeposit);
deal.setDeposit(dto.getDeposit());

// 수신자 계좌번호 검증하면서 DealReceiver로 만들어서 저장
for (String receiverAccountNumber : receiverAccountNumbers) {
dealReceiverRepository.save(
DealReceiver.builder()
.deal(deal)
.receiverAccount(accountService.validateAccount(receiverAccountNumber))
.build()
);
}

// 거래상태 b로 변경
deal.setStatus("b");

return APIResponse.of(SuccessCode.INSERT_SUCCESS, dealRepository.save(deal).getId());
}

// 거래ID로 존재여부 + 거래상태 검증
private Deal validateDeal(Long dealId, List<String> status) {
Deal deal = dealRepository.findById(dealId)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 거래입니다."));

if (!status.contains(deal.getStatus())) {
throw new IllegalArgumentException("거래 상태가 올바르지 않습니다.");
}

return deal;
}

// 거래 수정
// 1. 거래 ID 확인
// 2. 송금자 계좌 검증
// 3. 비밀번호 검증
// 4. 거래 상태 확인
// 5. 예치금 수정 및 송신자 계좌에 가용금액 플러스
// 6. 거래 인원수 수정
// 7. b일 경우 dealReceiver 삭제
// 8. 거래 상태 a로 변경
// 9. 거래ID 반환
@Transactional
public APIResponse<Long> updateDeal(DealRequestDTO.updateDeal dto) {
// 거래ID로 존재여부 + 거래상태 검증
Deal deal = this.validateDeal(dto.getDealId(), List.of("a", "b"));

// 송금자 계좌 검증
Account senderAccount = accountService.validateAccount(dto.getSenderAccountNumber());

// 비밀번호 검증
if (!passwordEncoder.matches(dto.getPassword(), senderAccount.getPassword())) {
throw new IllegalArgumentException("비밀번호가 올바르지 않습니다.");
}

// 거래 상태 확인
if (deal.getStatus().equals("b")) {
// 거래 상태가 b일 경우 dealReceiver 삭제
dealReceiverRepository.deleteByDeal(deal);
}

// 예치금 수정 및 송신자 계좌에 가용금액 마이너스
Long finalDeposit = dto.getDeposit() - deal.getDeposit();
if (senderAccount.getAvailableBudget() < finalDeposit) {
throw new IllegalArgumentException("잔액이 부족합니다.");
}
senderAccount.setAvailableBudget(senderAccount.getAvailableBudget() - finalDeposit);
deal.setDeposit(dto.getDeposit());

// 거래 인원수 수정
deal.setNumberOfPeople(dto.getNumberOfPeople());

// 거래 상태 a로 변경
deal.setStatus("a");

return APIResponse.of(SuccessCode.UPDATE_SUCCESS, dealRepository.save(deal).getId());
}

// 거래 취소
// 1. 거래 ID 확인
// 2. 송금자 계좌 검증
// 3. 비밀번호 검증
// 4. 거래 상태 확인
// 5. 거래 상태 b일 경우 dealReceiver 삭제
// 6. 송신자 계좌에 가용금액 플러스
// 7. 거래 상태 c로 변경
// 8. 거래ID 반환
@Transactional
public APIResponse<Long> cancelDeal(DealRequestDTO.cancelDeal dto) {
// 거래ID로 존재여부 + 거래상태 검증
Deal deal = this.validateDeal(dto.getDealId(), List.of("a", "b"));

// 송금자 계좌 검증
Account senderAccount = accountService.validateAccount(dto.getSenderAccountNumber());

// 비밀번호 검증
if (!passwordEncoder.matches(dto.getPassword(), senderAccount.getPassword())) {
throw new IllegalArgumentException("비밀번호가 올바르지 않습니다.");
}

// 거래 상태 확인
if (deal.getStatus().equals("b")) {
// 거래 상태가 b일 경우 dealReceiver 삭제
// 근데 삭제해야 하나?
dealReceiverRepository.deleteByDeal(deal);
}

// 송신자 계좌에 가용금액 플러스
senderAccount.setAvailableBudget(senderAccount.getAvailableBudget() + deal.getDeposit());

// 거래 상태 c로 변경
deal.setStatus("c");

return APIResponse.of(SuccessCode.DELETE_SUCCESS, dealRepository.save(deal).getId());
}
}

0 comments on commit 4a63974

Please sign in to comment.