Junit Test Application-38-계좌출금 기능

jaegeunsong97·2023년 8월 9일
0

Junit Bank Application 깃허브

Junit Bank Application 기록 노션

  • 서비스 코드
@Transactional
     public AccountWithdrawResponseDto 계좌출금(AccountWithdrawRequestDto accountWithdrawRequestDto, Long userId) {
          // 1. 0원 체크
          if (accountWithdrawRequestDto.getAmount() <= 0L) {
               throw new CustomApiException("0원 이하의 금액을 입금할 수 없습니다. ");
          }

          // 2. 출금계좌 확인
          Account withdrawAccountPS = accountRepository.findByNumber(accountWithdrawRequestDto.getNumber()).orElseThrow(
                    () -> new CustomApiException("계좌를 찾을 수 없습니다. "));

          // 3. 출금 소유자 확인(로그인한 사람과 비교)
          withdrawAccountPS.checkOwner(userId);

          // 4. 출금 비밀번호 확인
          withdrawAccountPS.checkSamePassword(accountWithdrawRequestDto.getPassword());

          // 5. 출금계좌 잔액 확인
          withdrawAccountPS.checkBalance(accountWithdrawRequestDto.getAmount());

          // 6. 출금하기
          withdrawAccountPS.withdraw(accountWithdrawRequestDto.getAmount());

          // 7. 거래내역 남기기
          Transaction transaction = Transaction.builder()
                    .withdrawAccount(withdrawAccountPS)
                    .depositAccount(null)
                    .withdrawAccountBalance(withdrawAccountPS.getBalance())
                    .depositAccountBalance(null)
                    .amount(accountWithdrawRequestDto.getAmount())
                    .gubun(TransactionEnum.WITHDRAW)
                    .sender(accountWithdrawRequestDto.getNumber() + "")
                    .receiver("ATM")
                    .build();
          Transaction transactionPS = transactionRepository.save(transaction);

          // 8. DTO
          return new AccountWithdrawResponseDto(withdrawAccountPS, transactionPS);
     }
  • dto
@Getter
     @Setter
     public static class AccountWithdrawRequestDto {

          @NotNull
          @Digits(integer = 4, fraction = 4)
          private Long number;
          @NotNull
          @Digits(integer = 4, fraction = 4)
          private Long password;
          @NotNull
          private Long amount;
          @NotEmpty
          @Pattern(regexp = "WITHDRAW")
          private String gubun;
     }
.
.
.
@Getter
     @Setter
     public static class AccountWithdrawResponseDto {

          private Long id; // 계좌 ID
          private Long number; // 계좌번호
          private Long balance; // 잔약
          private TransactionDto transaction;

          public AccountWithdrawResponseDto(Account account, Transaction transaction) {
               this.id = account.getId();
               this.number = account.getNumber();
               this.balance = account.getBalance();
               this.transaction = new TransactionDto(transaction);
          }

          @Getter
          @Setter
          public class TransactionDto {
               private Long id;
               private String gubun;
               private String sender;
               private String receiver;
               private Long amount;
               private String tel;
               private String createdAt;

               public TransactionDto(Transaction transaction) {
                    this.id = transaction.getId();
                    this.gubun = transaction.getGubun().getValue();
                    this.sender = transaction.getSender();
                    this.receiver = transaction.getReceiver();
                    this.amount = transaction.getAmount();
                    this.createdAt = CustomDateUtil.toStringFormat(transaction.getCreateAt());
               }
          }
     }
  • account
package shop.mtcoding.bank.domain.account;

import java.time.LocalDateTime;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import shop.mtcoding.bank.domain.user.User;
import shop.mtcoding.bank.handler.ex.CustomApiException;

@NoArgsConstructor
@Getter
@EntityListeners(AuditingEntityListener.class)
@Table(name = "account_tb", indexes = {
          @Index(name = "idx_account_number", columnList = "number")
})
@Entity
public class Account {

     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
     @Column(unique = true, nullable = false, length = 4)
     private Long number; // 계좌변호
     @Column(nullable = false, length = 4)
     private Long password; // 계좌비번
     @Column(nullable = false)
     private Long balance; // 잔액(기본 1000원)

     @ManyToOne(fetch = FetchType.LAZY)
     private User user;
     @CreatedDate
     @Column(nullable = false)
     private LocalDateTime createAt;
     @LastModifiedDate
     @Column(nullable = false)
     private LocalDateTime updateAt;

     @Builder
     public Account(Long id, Long number, Long password, Long balance, User user, LocalDateTime createAt,
               LocalDateTime updateAt) {
          this.id = id;
          this.number = number;
          this.password = password;
          this.balance = balance;
          this.user = user;
          this.createAt = createAt;
          this.updateAt = updateAt;
     }

     public void checkOwner(Long userId) {
          if (user.getId() != userId) { // lazy -> id는 조회 가능, SELECT 쿼리 X
               throw new CustomApiException("계좌 소유자가 아닙니다. ");
          }
     }

     public void deposit(Long amount) {
          balance += amount;
     }

     public void checkSamePassword(Long password) {
          if (this.password.longValue() != password.longValue()) {
               throw new CustomApiException("계좌 비밀번호 검증에 실패하였습니다. ");
          }
     }

     public void checkBalance(Long amount) {
          if (this.balance < amount) {
               throw new CustomApiException("계좌 잔액이 부족합니다. ");
          }
     }

     public void withdraw(Long amount) {
          checkBalance(amount); // 여기 주목!! 체크를 1번 더 하는 것!
          balance -= amount;
     }
}

주목할 점 : long 비교시 longValue()로 비교


  • 컨트롤러
@PostMapping("/s/account/withdraw")
     public ResponseEntity<?> withdrawAccount(@RequestBody @Valid AccountWithdrawRequestDto accountWithdrawRequestDto,
               BindingResult bindingResult,
               @AuthenticationPrincipal LoginUser loginUser) {
          AccountWithdrawResponseDto accountWithdrawResponseDto = accountService.계좌출금(accountWithdrawRequestDto,
                    loginUser.getUser().getId());
          return new ResponseEntity<>(new ResponseDto<>(1, "계좌출금 성공", accountWithdrawResponseDto), HttpStatus.CREATED);
     }
profile
블로그 이전 : https://medium.com/@jaegeunsong97

0개의 댓글