[프로젝트]중고 경매 사이트 - 제시요(구매자 : 경매 참여중인 물품 - 입찰 취소, 재입찰)

기능 구현
송송승현's avatar
Dec 24, 2024
[프로젝트]중고 경매 사이트 - 제시요(구매자 : 경매 참여중인 물품 - 입찰 취소, 재입찰)

화면

notion image
notion image
notion image
notion image
notion image

Mustache

<div class="btn-group mt-1"> <button type="button" class="btn btn-custom btn-sm" onclick="cancel({{buyerId}},{{goodsId}},{{buyerTryPrice}})">입찰 취소</button> <button type="button" class="btn btn-custom btn-sm" data-bs-toggle="modal" data-bs-target="#t1-re-bid-modal" onclick ="addReBid({{id}},{{goodsId}},{{buyerTryPrice}})">입찰 금액 수정</button> </div>

JavaScript

async function cancel(userId,goodsId, tryPrice){ let userDetails = await getAuthentication() let url =`/api/v1/cancelBid` let response = await fetch(url, { method : "POST", body : JSON.stringify({ "userId" : userDetails.id, "goodsId":goodsId, "tryPrice" : tryPrice}), headers : { "Content-Type" : "Application/JSON; cherset = utf-8" } }) let responseData = await response.json(); if(!responseData.success){ throw new Error("네트워크 응답에 문제가 있습니다.") }else { alert("입찰을 취소하였습니다.") } window.location.href = "/s/mypage-participating-auction"; } let bid_Id =""; let goods_Id =""; let try_Price =""; function addReBid(id,goodsId,buyerTryPrice){ bid_Id = id; goods_Id = goodsId; try_Price = buyerTryPrice; } async function reRid(){ let re_Bid = document.getElementById("t1-re-bid-price").value; let url = "/api/v1/re-bid" let response =await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body : JSON.stringify({ bidId: bid_Id, goodsId: goods_Id, tryPrice : try_Price, reTryPrice: re_Bid }) }); let responseData = await response.json(); console.log(responseData) if(!responseData.success){ alert(responseData.message); }else{ alert("입찰가를 수정하였습니다.") } window.location.href = `/s/mypage-participating-auction`; }

Controller

// 입찰 취소 @PostMapping("/api/v1/cancelBid") @ResponseBody public ResponseEntity<?> cancelBid(@RequestBody BidRequest.CancelBidDTO cancelBidDTO) { bidService.cancelBid(cancelBidDTO); CommonResp resp = new CommonResp(true,"성공", null); return new ResponseEntity<>(resp, HttpStatus.OK); } // 재입찰 @PostMapping("/api/v1/re-bid") @ResponseBody public ResponseEntity<?> reBid(@AuthenticationPrincipal User user,@RequestBody BidRequest.ReBidRequestDTO dto) { bidService.reBid(user.getId(),dto); CommonResp resp = new CommonResp(true,"성공", null); return new ResponseEntity<>(resp, HttpStatus.OK); }

Service

// 입찰 취소 @Transactional public void cancelBid(BidRequest.CancelBidDTO dto) { bidRepository.deleteBid(dto.getGoodsId(), dto.getUserId()); UserAccount ua = userAccountRepository.findById(dto.getUserId()); ua.updateUserInfo(dto.getTryPrice()); } // 재입찰 @Transactional public void reBid(Integer userId, BidRequest.ReBidRequestDTO dto) { // 동시성 체크 // 물품의 최고 입찰가를 조회 Optional<Bid> highestBid = bidRepository.findByGoodsIdDescWithLock(dto.getGoodsId()); if (highestBid.isPresent()) { Integer currentHighestPrice = highestBid.get().getTryPrice(); if (dto.getReTryPrice() <= currentHighestPrice) { throw new Exception400NotHTML("입찰금액이 현재 최고 입찰 금액보다 높아야합니다."); } } // 유저의 잔액을 조회 - 업데이트 UserAccount ua = userAccountRepository.findById(userId); if ((dto.getReTryPrice() - dto.getTryPrice()) > ua.getHasPrice()) { throw new Exception400("자신의 보유 금액보다 높게 입찰할 수 없습니다."); } ua.minusPrice((dto.getReTryPrice() - dto.getTryPrice())); // 입찰 정보를 조회 - 업데이트 Bid bid = bidRepository.findById(dto.getBidId()); bid.updatePrice(dto.getReTryPrice()); }

Repository

// bid 테이블에서 물품을 입찰한 사람 한명만 delete public void deleteBid(Integer goodsId, Integer userId){ String sql = """ delete from bid_tb where goods_id = ? and buyer_id = ? """; Query q = em.createNativeQuery(sql); q.setParameter(1, goodsId); q.setParameter(2, userId); q.executeUpdate(); } // 유저 id를 이용하여 계좌 정보 조회 public UserAccount findById(Integer userId){ String sql = """ select u from UserAccount u join fetch u.user where u.user.id=:userId """; Query query = em.createQuery(sql); query.setParameter("userId", userId); query.setMaxResults(1); return (UserAccount) query.getSingleResult(); } // 사용자 입찰 시도 시 현재 최고금액 조회 (SELECT ~ FOR UPDATE) public Optional<Bid> findByGoodsIdDescWithLock(Integer id) { try{ Query q = em.createNativeQuery("select * from bid_tb where goods_id = ? order by id desc limit 1 for update", Bid.class); q.setParameter(1, id); return Optional.ofNullable((Bid) q.getSingleResult()); }catch (NoResultException e) { return Optional.empty(); } } // 유저 계좌 정보 조회 public UserAccount findById(Integer userId){ String sql = """ select u from UserAccount u join fetch u.user where u.user.id=:userId """; Query query = em.createQuery(sql); query.setParameter("userId", userId); query.setMaxResults(1); return (UserAccount) query.getSingleResult(); } // 유저의 입찰 정보를 조회 public Bid findById(Integer id) { String sql = "select * from bid_tb where id = ?"; Query q = em.createNativeQuery(sql, Bid.class); q.setParameter(1, id); return (Bid) q.getSingleResult(); }

해설

입찰 취소

💡
유저의 정보, 내가 입찰한 금액, 물품의 정보를 받음
입찰 정보를 삭제
입찰금액을 입찰자에게 반환

재입찰

💡
내가 입찰한 goods에 대한 최고 입찰가를 조회(비관적 락을 통한 동시성 제어)
내가 재입찰할 금액이 최고 입찰금 보다 작으면 throw를 던지고
최고 입찰금 보다 높으면
유저의 계좌 정보를 조회
잔액이 재입찰금 - 이전입찰금 보다 적으면 throw
높으면 입찰 정보롤 조회하여 업데이트
 
Share article

송승현의 블로그