Spring boot

이체 기능

yoooon1212 2024. 8. 8. 17:05
작업 순서

1. transfer.jsp 파일 생성
2. 이체 기능 만들기
3. 주요 파일 전체 코드 확인
4. 동작 테스트

 

 

transfer.jsp 생성
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!-- header.jsp  -->
<%@ include file="/WEB-INF/view/layout/header.jsp"%>

<!-- start of content.jsp(xxx.jsp)   -->
<div class="col-sm-8">
	<h2>이체 요청(인증)</h2>
	<h5>Bank App에 오신걸 환영합니다</h5>
	<form action="/account/transfer" method="post"> 
		<div class="form-group">
			<label for="amount">이체 금액:</label>
			<input type="number" class="form-control" placeholder="Enter amount" id="amount" name="amount" value="300">
		</div>
		<div class="form-group">
			<label for="wAccountNumber">출금 계좌 번호:</label>
			<input type="text" class="form-control" placeholder="Enter account number" id="wAccountNumber" name="wAccountNumber" value="1111">
		</div>
		<div class="form-group">
			<label for="pwd">출금 계좌 비밀번호:</label>
			<input type="password" class="form-control" placeholder="Enter password" id="pwd" name="password" value="1234">
		</div>
		<div class="form-group">
			<label for="dAccountNumber">입금(이체)계좌 번호:</label>
			<input type="text" class="form-control" placeholder="Enter account number" id="dAccountNumber" name="dAccountNumber" value="3333">
		</div>
		<div class="text-right">
			<button type="submit" class="btn btn-primary">이체하기</button>
		</div>
	</form>
</div>
<!-- end of col-sm-8  -->
</div>
</div>
<!-- end of content.jsp(xxx.jsp)   -->

<!-- footer.jsp  -->
<%@ include file="/WEB-INF/view/layout/footer.jsp"%>

 

 

 

결과 확인

 

 

TransferDTO
package com.tenco.bank.dto;

import lombok.Data;

@Data
public class TransferDTO {
	
	private Long amount; // 거래 금액 
	private String wAccountNumber; // 출금계좌 번호  
	private String dAccountNumber; // 입금계좌 번호 
	private String password; // 출금 계좌 비밀번호   
}

 

 

AccountCountroller
	// 이체 페이지 요청
	@GetMapping("/transfer")
	public String transfer() {
		User principal = (User) session.getAttribute(Define.PRINCIPAL);
		if (principal == null) {
			throw new UnAuthorizedException(Define.NOT_AN_AUTHENTICATED_USER, HttpStatus.UNAUTHORIZED);
		}
		return "account/transfer";
	}

	/**
	 * 이체 기능 구현
	 * @param TransferDTO 
	 * @return redirect:/account/list
	 */
	@PostMapping("/transfer")
	public String transferProc(TransferDTO dto) {
		User principal = (User) session.getAttribute(Define.PRINCIPAL);
		
		// 1. 인증 검사
		if (principal == null) {
			throw new UnAuthorizedException(Define.NOT_AN_AUTHENTICATED_USER, HttpStatus.UNAUTHORIZED);
		}
		
		// 2. 유효성 검사
		if (dto.getAmount() == null) {
			throw new DataDeliveryException(Define.ENTER_YOUR_BALANCE, HttpStatus.BAD_REQUEST);
		}

		if (dto.getAmount().longValue() <= 0) {
			throw new DataDeliveryException(Define.W_BALANCE_VALUE, HttpStatus.BAD_REQUEST);
		}

		if (dto.getWAccountNumber() == null || dto.getWAccountNumber().isEmpty()) {
			throw new DataDeliveryException("출금하실 계좌번호를 입력해주세요.", HttpStatus.BAD_REQUEST);
		}
		
		if(dto.getDAccountNumber() == null || dto.getDAccountNumber().isEmpty()) {
			throw new DataDeliveryException("이체하실 계좌번호를 입력해주세요.", HttpStatus.BAD_REQUEST);
		}
		

		if (dto.getPassword() == null || dto.getPassword().isEmpty()) {
			throw new DataDeliveryException(Define.ENTER_YOUR_PASSWORD, HttpStatus.BAD_REQUEST);
		}

		// 서비스 호출
		accountService.updateAccountTransfer(dto, principal.getId());

		return "redirect:/account/list";
	}

 

 

AccountService
	// 이체 기능 만들기
    @Transactional
    public void updateAccountTransfer(TransferDTO dto, Integer principalId) {
		
		// 1. 출금 계좌 정보 조회
		Account wAccountEntity = accountRepository.findByNumber(dto.getWAccountNumber());
		if(wAccountEntity == null) {
			throw new DataDeliveryException(Define.NOT_EXIST_ACCOUNT, HttpStatus.BAD_REQUEST);
		}
		
		// 2. 입금 계좌 정보 조회
		Account dAccountEntity = accountRepository.findByNumber(dto.getDAccountNumber());
		if(dAccountEntity == null) {
			throw new DataDeliveryException("상대방의 계좌 번호가 없습니다.", HttpStatus.INTERNAL_SERVER_ERROR);
		}
		
		// 3. 출금 계좌 본인 소유 확인
		wAccountEntity.checkOwner(principalId);
		
		// 4. 출금 계좌 비밀번호 확인
		wAccountEntity.checkPassword(dto.getPassword());
		
		// 5. 출금 계좌 잔액 여부 확인
		wAccountEntity.checkBalance(dto.getAmount());
		
		// 6. 입금 계좌 객체 상태값 변경 처리
		dAccountEntity.deposit(dto.getAmount());
		
		// 7. 입금 계좌 -- update 처리
		int resultRowCountDeposit = accountRepository.updateById(dAccountEntity);
		
		// 8. 출금 계좌 객체 상태값 변경 처리
		wAccountEntity.withdraw(dto.getAmount());
		
		// 9. 출금 계좌 -- update 처리
		int resultRowCountWithdraw = accountRepository.updateById(wAccountEntity);
		
		
		if(resultRowCountWithdraw != 1 && resultRowCountDeposit != 1) {
			throw new DataDeliveryException(Define.FAILED_PROCESSING, HttpStatus.INTERNAL_SERVER_ERROR);
		}
		
		
		// 10. 거래 내역 등록 처리
		/*
		insert into history_tb(amount, w_balance, d_balance, w_account_id, d_account_id)
		                values(#{amount}, #{wBalance}, #{dBalance}, #{wAccountId}, #{dAccountId} )	
		*/
		
		History history = History.builder()
				.amount(dto.getAmount()) // 이체 금액
				.wAccountId(wAccountEntity.getId()) // 출금 계좌
				.dAccountId(dAccountEntity.getId()) // 입금 계좌 
				.wBalance(wAccountEntity.getBalance()) // 출금 계좌 잔액
				.dBalance(dAccountEntity.getBalance()) // 입금 계좌 잔액
				.build();
		
		int rowResultCount = historyRepository.insert(history);
		if(rowResultCount != 1) {
			throw new DataDeliveryException(Define.FAILED_PROCESSING, HttpStatus.INTERNAL_SERVER_ERROR);
		}
	}

 

이체 기능 흐름


1. 출금 계좌 존재 여부 확인 -- select (객체 리턴 받은 상태)
2. 입금 계좌 존재 여부 확인 -- select (객체 리턴 받은 상태)
3. 출금 계좌 본인 소유 확인 -- 객체 상태값과 세션 아이디 비교
4. 출금 계좌 비밀 번호 확인 -- 객체 상태값과 dto 비밀번호 비교
5. 출금 계좌 잔액 여부 확인 -- 객체 상태값 확인, dto와 비교
6. 입금 계좌 객체 상태값 변경 처리(거래 금액 증가)
7. 입금 계좌 -- update 처리
8. 출금 계좌 객체 상태값 변경 처리(잔액 - 거래금액)
9. 출금 계좌 -- update 처리
10. 거래 내역 등록 처리
11. 트랙잭션 처리

 

History history = new History();
history.setAmount(dto.getAmount());
history.setWBalance(wAccountEntity.getBalance());
history.setDBalance(dAccountEntity.getBalance());
history.setWAccountId(wAccountEntity.getId());
history.setDAccountId(dAccountEntity.getId());

이 코드를 위와 같이 builder를 사용하여 값을 넣을 수 있습니다.

 

 

 

실행 결과

 

계좌 목록에서 이체하기를 누르면 

 

 

 

transfer.jsp 화면이 뜹니다. 

 

이체 금액, 출금 계좌 번호, 출금 계좌 비밀번호, 이체 계좌 번호를 입력하여

이체하기 버튼을 누르면 

 

 

출금한 계좌의 잔액이 1300에서 300을 출금하고 1000이 남은 것을 볼 수 있습니다.

 

DB를 보시면

 

AMOUNT(거래금액) 300

W_ACCOUNT_ID(출금 계좌 ID) 1

D_ACCOUNT_ID(입금(이체) 계좌 ID) 3

W_BALANCE(출금 계좌 잔액) 1000

D_BALANCE(입금(이체) 계좌 잔액) 300

 

값이 들어온 것을 알 수 있습니다. 

 

 


코드를 해석하자면

 

W_ACCOUNT_ID(출금 계좌 ID)가 1번인 길동이 

D_ACCOUNT_ID(입금(이체) 계좌 ID)가 3번인 마이에게

AMOUNT(거래금액)  300을 출금한 것을 알 수 있고, 

 

1번인 길동의 원래 잔액이 1300이었기 때문에 

300을 출금함으로써

W_BALANCE(출금 계좌 잔액)이 1000이 되었습니다. 

 

3번인 마이의 잔액은 0원이었기 때문에

300을 이체받음으로써

D_BALANCE(입금(이체) 계좌 잔액)이 300이 되었습니다.

 

 

이는 우리가 친구에게 돈을 보냈을 때 일어나는 일과 같습니다.


이체 이전 계좌 상황

 

 

이체 이후 계좌 상황