[Spring Boot] 7. Spring Boot Project (Bank v1)_3-6.Fuctions Design_Account detail
Mar 27, 2025
AccountResponse
package com.metacoding.bankv1.account;
import com.metacoding.bankv1.user.User;
import lombok.Data;
import java.util.List;
public class AccountResponse {
@Data
public static class DetailDTO {
private String fullname;
private int number;
private int balance;
private List<HistoryDTO> histories;
public DetailDTO(User sessionUser, Account account, List<HistoryDTO> histories) {
this.fullname = sessionUser.getFullname();
this.number = account.getNumber();
this.balance = account.getBalance();
this.histories = histories;
}
}
// 두번째 쿼리
@Data
public static class HistoryDTO {
private String createdAt;
private int withdrawNumber;
private int depositNumber;
private int amount;
private int balance;
private String type;
public HistoryDTO(String createdAt, int withdrawNumber, int depositNumber, int amount, int balance, String type) {
this.createdAt = createdAt;
this.withdrawNumber = withdrawNumber;
this.depositNumber = depositNumber;
this.amount = amount;
this.balance = balance;
this.type = type;
}
}
}
HistoryRepository
package com.metacoding.bankv1.account.history;
import com.metacoding.bankv1.account.AccountResponse;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.List;
@RequiredArgsConstructor
@Repository
public class HistoryRepository {
private final EntityManager em;
public void save(int withdrawNumber, int depositNumber, int amount, int withdrawBalance, int depositBalance) {
Query query = em.createNativeQuery("insert into history_tb(withdraw_number, deposit_number, amount, withdraw_balance, deposit_balance, created_at) values (?, ?, ?, ?, ?,now())");
query.setParameter(1, withdrawNumber);
query.setParameter(2, depositNumber);
query.setParameter(3, amount);
query.setParameter(4, withdrawBalance);
query.setParameter(5, depositBalance);
query.executeUpdate();
}
public List<AccountResponse.HistoryDTO> findByAllNumber(Integer number) {
Query query = em.createNativeQuery("select substr(created_at,1,16) created_at, withdraw_number, deposit_number, amount, case when withdraw_number = ? then withdraw_balance else deposit_balance end as \"balance\", case when withdraw_number = ? then '출금' else '입금' end as \"type\" from history_tb where withdraw_number = ? or deposit_number = ?");
query.setParameter(1, number);
query.setParameter(2, number);
query.setParameter(3, number);
query.setParameter(4, number);
List<Object[]> objectList = (List<Object[]>) query.getResultList();
List<AccountResponse.HistoryDTO> historyList = new ArrayList<>();
for (Object[] objects : objectList) {
AccountResponse.HistoryDTO historyDTO = new AccountResponse.HistoryDTO(
(String) objects[0],
(int) objects[1],
(int) objects[2],
(int) objects[3],
(int) objects[4],
(String) objects[5]
);
historyList.add(historyDTO);
}
return historyList;
}
}
AccountController
package com.metacoding.bankv1.account;
import com.metacoding.bankv1.user.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@RequiredArgsConstructor
@Controller
public class AccountController {
private final AccountService accountService;
private final HttpSession session;
@GetMapping("/")
public String home() {
return "home";
}
@GetMapping("/account/save-form")
public String saveForm() {
// 인증체크 (반복되는 부가로직)
User sessionUser = (User) session.getAttribute("sessionUser");
if (sessionUser == null) throw new RuntimeException("로그인 후 사용해주세요");
return "account/save-form";
}
@PostMapping("/account/save")
public String save(AccountRequest.SaveDTO saveDTO) {
// 인증체크 (반복되는 부가로직)
User sessionUser = (User) session.getAttribute("sessionUser");
if (sessionUser == null) throw new RuntimeException("로그인 후 사용해주세요");
accountService.계좌생성(saveDTO, sessionUser.getId());
return "redirect:/account";
}
@GetMapping("/account")
public String list(HttpServletRequest request) {
// 인증체크 (반복되는 부가로직)
User sessionUser = (User) session.getAttribute("sessionUser");
if (sessionUser == null) throw new RuntimeException("로그인 후 사용해주세요");
List<Account> accountList = accountService.나의계좌목록(sessionUser.getId());
request.setAttribute("models", accountList);
return "account/list";
}
@GetMapping("/account/transfer-form")
public String transferForm() {
// 인증체크 (반복되는 공통부가로직)
User sessionUser = (User) session.getAttribute("sessionUser");
if (sessionUser == null) throw new RuntimeException("로그인 후 사용해주세요");
return "account/transfer-form";
}
@PostMapping("/account/transfer")
public String transfer(AccountRequest.TransferDTO transferDTO) {
// 인증체크 (반복되는 공통부가로직)
User sessionUser = (User) session.getAttribute("sessionUser");
if (sessionUser == null) throw new RuntimeException("로그인 후 사용해주세요");
accountService.계좌이체(transferDTO, sessionUser.getId());
return "redirect:/"; // TODO
}
// /account/1111?type=전체
@GetMapping("/account/{number}")
public String detail(@PathVariable("number") int number, @RequestParam(value = "type", required = false, defaultValue = "전체") String type, HttpServletRequest request) {
// System.out.println("number = " + number);
// System.out.println("type = " + type);
// 인증체크 (반복되는 공통부가로직)
User sessionUser = (User) session.getAttribute("sessionUser");
if (sessionUser == null) throw new RuntimeException("로그인 후 사용해주세요");
AccountResponse.DetailDTO detailDTO = accountService.계좌상세보기(number, type, sessionUser); // sessionUser.getId()로 권한체크 필요
request.setAttribute("model", detailDTO);
return "account/detail";
}
}
AccountService
package com.metacoding.bankv1.account;
import com.metacoding.bankv1.account.history.HistoryRepository;
import com.metacoding.bankv1.user.User;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@RequiredArgsConstructor
@Service
public class AccountService {
private final AccountRepository accountRepository;
private final HistoryRepository historyRepository;
@Transactional
public void 계좌생성(AccountRequest.SaveDTO saveDTO, int userId) {
accountRepository.save(saveDTO.getNumber(), saveDTO.getPassword(), saveDTO.getBalance(), userId);
}
public List<Account> 나의계좌목록(Integer userId) {
return accountRepository.findAllByUserId(userId);
}
@Transactional
public void 계좌이체(AccountRequest.TransferDTO transferDTO, int userId) {
// 1. 출금 계좌 조회
Account withdrawAccount = accountRepository.findByNumber(transferDTO.getWithdrawNumber());
// 2. 출금 계좌 없으면 RunTimeException
if (withdrawAccount == null) throw new RuntimeException("출금 계좌가 존재하지 않습니다");
// 3. 입금계좌 조회
Account depositAccount = accountRepository.findByNumber(transferDTO.getDepositNumber());
// 4. 입금 계좌 없으면 RunTimeException
if (depositAccount == null) throw new RuntimeException("입금 계좌가 존재하지 않습니다");
// 5. 출금 계좌의 잔액 검사
if (withdrawAccount.getBalance() < transferDTO.getAmount()) {
throw new RuntimeException("출금 계좌의 잔액: " + withdrawAccount.getBalance() + ", 이체하려는 금액: " + transferDTO.getAmount());
}
// 6. 출금 계좌의 비밀번호 획인해서 동일인인지 체크
if (!(withdrawAccount.getPassword().equals(transferDTO.getWithdrawPassword()))) {
throw new RuntimeException("출금계좌 비밀번호가 틀렸습니다");
}
// 7. 출금 계좌의 주인과 로그인 유저가 동일 인물인지 권한 확인
if (!(withdrawAccount.getUserId().equals(userId))) { // Integer는 125이하까지만 값을 비교해주기 때문에 equals로 비교하는게 좋다
throw new RuntimeException("출금계좌의 권한이 없습니다");
}
// 8. Account Update -> 출금계좌
int withdrawBalance = withdrawAccount.getBalance();
withdrawBalance = withdrawBalance - transferDTO.getAmount();
accountRepository.updateByNumber(withdrawBalance, withdrawAccount.getPassword(), withdrawAccount.getNumber());
// 9. Account Update -> 입금계좌
int depositBalance = depositAccount.getBalance();
depositBalance = depositBalance + transferDTO.getAmount();
accountRepository.updateByNumber(depositBalance, depositAccount.getPassword(), depositAccount.getNumber());
// 10. History save
historyRepository.save(transferDTO.getWithdrawNumber(), transferDTO.getDepositNumber(), transferDTO.getAmount(), withdrawAccount.getBalance(), depositAccount.getBalance()); // 검증이 끝났기 때문에 transferDTO에서 가져온다
}
public AccountResponse.DetailDTO 계좌상세보기(int number, String type, User sessionUser) {
// 1. 계좌 존재 확인 (부가로직)
Account account = accountRepository.findByNumber(number);
if (account == null) throw new RuntimeException("출금 계좌가 존재하지 않습니다");
// 2. 계좌 주인 확인 (부가로직)
if (!(account.getUserId().equals(sessionUser.getId()))) { // Integer는 125이하까지만 값을 비교해주기 때문에 equals로 비교하는게 좋다
throw new RuntimeException("해당 계좌의 권한이 없습니다");
}
// 3. 조회 (핵심로직)
List<AccountResponse.HistoryDTO> historyList = historyRepository.findByAllNumber(number);
AccountResponse.DetailDTO detailDTO = new AccountResponse.DetailDTO(sessionUser, account, historyList);
return detailDTO;
}
}
detail
{{>layout/header}}
<!--마진 : mt, mr, ml, mb (1~5) ex) mt-5-->
<div class="container mt-2">
<div class="mt-4 p-5 bg-light text-dark rounded-4">
<p>{{model.fullname}}님 계좌</p>
<p>계좌번호 : {{model.number}}</p>
<p>계좌잔액 : {{model.balance}}원</p>
</div>
<div class="mt-3 mb-3">
<button type="button" class="btn btn-outline-primary">전체</button>
<button type="button" class="btn btn-outline-primary">입금</button>
<button type="button" class="btn btn-outline-primary">출금</button>
</div>
<table class="table table-hover">
<thead>
<tr>
<th>날짜</th>
<th>출금계좌</th>
<th>입금계좌</th>
<th>금액</th>
<th>계좌잔액</th>
<th>입금/출금</th>
</tr>
</thead>
<tbody>
{{#model.histories}}
<tr>
<td>{{createdAt}}</td>
<td>{{withdrawNumber}}</td>
<td>{{depositNumber}}</td>
<td>{{amount}}원</td>
<td>{{balance}}원</td>
<td>{{type}}</td>
</tr>
{{/model.histories}}
</tbody>
</table>
</div>
{{>layout/footer}}
1. user ‘ssar’로 로그인해서 계좌목록 확인하기

1️⃣ ‘1111’ 계좌 상세목록 확인

🆗 상단에 계좌 주인의 fullname, 계좌번호, 계좌의 현재 잔액 확인가능
하단에는 ‘1111’ 계좌의 전체 기록 확인가능

2️⃣ ‘2222’ 계좌 상세목록 확인

🆗 상단에 계좌 주인의 fullname, 계좌번호, 계좌의 현재 잔액 확인가능
하단에는 ‘2222’ 계좌의 전체 기록 확인가능

2. user ‘cos’로 로그인해서 계좌목록 확인하기

1️⃣ ‘3333’ 계좌 상세목록 확인

🆗 상단에 계좌 주인의 fullname, 계좌번호, 계좌의 현재 잔액 확인가능
하단에는 ‘3333’ 계좌의 전체 기록 확인가능

Share article