[Spring Boot] 7. Spring Boot Project (Bank v1)_3-6.Fuctions Design_Account detail

김미숙's avatar
Mar 27, 2025
[Spring Boot] 7. Spring Boot Project (Bank v1)_3-6.Fuctions Design_Account detail

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’로 로그인해서 계좌목록 확인하기

notion image
1️⃣ ‘1111’ 계좌 상세목록 확인
notion image
🆗 상단에 계좌 주인의 fullname, 계좌번호, 계좌의 현재 잔액 확인가능
하단에는 ‘1111’ 계좌의 전체 기록 확인가능
notion image
2️⃣ ‘2222’ 계좌 상세목록 확인
notion image
🆗 상단에 계좌 주인의 fullname, 계좌번호, 계좌의 현재 잔액 확인가능
하단에는 ‘2222’ 계좌의 전체 기록 확인가능
notion image
 

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

notion image
1️⃣ ‘3333’ 계좌 상세목록 확인
notion image
🆗 상단에 계좌 주인의 fullname, 계좌번호, 계좌의 현재 잔액 확인가능
하단에는 ‘3333’ 계좌의 전체 기록 확인가능
notion image
Share article

parangdajavous