[프로젝트]중고 경매 사이트 - 제시요(판매자 : 경매중인 물품 - 경매 취소, 조기 종료)

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

화면

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="deleteGoods({{id}})">경매취소 </button> <button type="button" class="btn btn-custom btn-sm" onclick="insertTransaction({{id}})">조기 종료 </button> </div>

JavaScript

// 경매 취소 async function deleteGoods(goodsId){ let url = `/api/v1/bid-delete/${goodsId}` let response = await fetch(url,{ method : "DELETE" }); let responseData = await response.json(); if(responseData.success){ alert("물품 경매를 취소하였습니다.") } else { alert("경매 취소에 실패했습니다.") } window.location.href="/s/mypage-being-auctioned"; } // 경매 조기 종료 async function insertTransaction(goodsId){ let url = `/api/v1/early-transaction`; let response = await fetch(url, { method : "POST", body : goodsId, headers:{ "Content-Type": "application/json", } }); let responseData = await response.json(); if(responseData.success){ alert("경매 조기 종료에 성공하였습니다.") } else{ alert("입찰자가 없어 조기 종료 할 수 없습니다. 경매를 끝내려면 경매 취소 버튼을 눌러주세요") } window.location.href="/s/mypage-being-auctioned"; }

Controller

// 경매 취소 버튼 @DeleteMapping("/api/v1/bid-delete/{goodsId}") @ResponseBody public ResponseEntity<?> deleteBidsByGoods(@PathVariable("goodsId") Integer goodsId) { bidService.cancelAuction(goodsId); CommonResp resp = new CommonResp(true,"성공",null); return new ResponseEntity<>(resp, HttpStatus.OK); } //경매 조기 종료 @PostMapping("/api/v1/early-transaction") @ResponseBody public ResponseEntity<?> endEarlyAuctionGoods(@RequestBody Integer goodsId) { boolean endEarlyAuctionAvailable = bidService.endEarlyAuction1(goodsId); if (endEarlyAuctionAvailable) { bidService.endEarlyAuction2(goodsId); CommonResp resp = new CommonResp(true, "성공", null); return new ResponseEntity<>(resp, HttpStatus.OK); } CommonResp resp = new CommonResp(false, "실패", null); return new ResponseEntity<>(resp, HttpStatus.OK); }

Service

// 경매 취소 @Transactional public void cancelAuction(Integer goodsId) { // 경매 취소할 물품의 경매목록 가져오기 List<Bid> bids = bidRepository.findAllByGoodsId(goodsId); if (bids.isEmpty()) { Optional<Goods> goods = goodsRepository.findById(goodsId); goods.get().cancelAuction(); return; } for (Bid bid : bids) { // 경매에 참여한 입찰자들의 돈을 반환 UserAccount ua = userAccountRepository.findById(bid.getBuyer().getId()); ua.updateUserInfo(bid.getTryPrice()); } // 경매에 참여한 데이터들을 recode 테이블로 옮김 List<Recode> recode = IntStream.range(0, bids.size()).mapToObj(i -> Recode.builder(). buyer(bids.get(i).getBuyer()). goods(bids.get(i).getGoods()). tryPrice(bids.get(i).getTryPrice()). successStatus(0). createdAt(bids.get(i).getCreatedAt()). build()).collect(Collectors.toList()); recodeRepositoryInterfase.saveAll(recode); //물품에 대한 경매에 참여중인 데이터 삭제 bidRepository.deleteByGoodsId(goodsId); //good에 있는 물품의 상태를 변경 - 3 : 경매 취소 Optional<Goods> goods = goodsRepository.findById(goodsId); goods.get().cancelAuction(); } // 조기 종료 - 물품의 status를 변경 @Transactional public boolean endEarlyAuction1(Integer goodsId) { Optional<Goods> goods = goodsRepository.findById(goodsId); List<Bid> bids = bidRepository.findAllByGoodsId(goodsId); if (bids.isEmpty()) { // 입찰자가 없기 때문에 조기 종료라는 개념이 없다. << 경매 취소만 할 수 있도록 해야 함 return false; } goods.get().endAuction(); return true; } // 조기 종료 - part2 @Transactional public void endEarlyAuction2(Integer goodsId) { // 판매자를 확인 Goods goods = goodsRepository.findById(goodsId).get(); // 종료하는 물품의 입찰 정보를 모두 불러옴 List<Bid> bids = bidRepository.findAllByGoodsId(goodsId); // 최고 경매 Bid bid = bids.get(bids.size() - 1); // 낙찰 테이블 등록 transactionRepository.save(Transaction.builder().goods(goods). buyer(bid.getBuyer()). seller(goods.getSeller()). buyerStatus(0). sellerStatus(0). transactionStatus(0). successPrice(bid.getTryPrice()). build()); for (int i = 0; i < bids.size() - 1; i++) { // 최고가를 제외한 나머지 입찰금액 반환 UserAccount ua = userAccountRepository.findById(bids.get(i).getBuyer().getId()); ua.updateUserInfo(bids.get(i).getTryPrice()); } // 경매에 참여한 데이터들을 recode 테이블로 옮김 List<Recode> recode = IntStream.range(0, bids.size()).mapToObj(i -> { int successStatus = (i == bids.size() - 1) ? 1 : 0; return Recode.builder(). buyer(bids.get(i).getBuyer()). goods(bids.get(i).getGoods()). tryPrice(bids.get(i).getTryPrice()). successStatus(successStatus). createdAt(bids.get(i).getCreatedAt()). build(); }).collect(Collectors.toList()); // 한번에 insert recodeRepositoryInterfase.saveAll(recode); // bid에 있는 데이터 삭제 bidRepository.deleteByGoodsId(goodsId); }
 

Repository

// 물품에 대한 경매 리스트 조회 public List<Bid> findAllByGoodsId(Integer id) { String query = """ select * from bid_tb where goods_id = ? """; Query q = em.createNativeQuery(query, Bid.class); q.setParameter(1, id); return q.getResultList(); } // 물품 id로 물품 조회 public Optional<Goods> findById(Integer id) { return Optional.ofNullable(em.find(Goods.class, 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(); } // 물품 id를 이용한 경매 정보 삭제 public void deleteByGoodsId(Integer id) { Query q = em.createNativeQuery("delete from bid_tb where goods_id = ?"); q.setParameter(1, id); q.executeUpdate(); } // 낙찰 테이블에 등록 public void save(Transaction transaction) { em.persist(transaction); }

해설

경매 취소

💡
bids에 있는 경매 물품에 대한 입찰 기록을 조회(입찰자, 입찰금액)
없는 경우 - 물품 상태 정보만 변경
있는 경우 - 입찰자에게 입찰금을 반환 (영속성 컨텍스트를 이용하여 bids테이블 업데이트)
모든 입찰 정보를 recode 테이블로 이전(jpa을 사용하여 recode에 입찰데이터 모두 등록 → bids에 있는 입찰정보 삭제)
goods테이블에서 물품 상태 정보 변경

조기 종료

💡
첫번째 트랜젝션(조기 종료 중간에 입찰이 수행될 수 있기 때문에 분리)
goods 테이블에서 물품 정보 조회
물품에 대한 입찰 정보 조회
물품에 대한 입찰 정보가 있으면 goods의 상태 정보 변경
없으면 false를 반환 - 입찰 정보가 없기 때문에 조기 종료 불가, 경매 취소를 유도
 
두번째 트랜젝션
물품 조회와 입찰 정보를 조회
최고 입찰가를 확인
낙찰 테이블에 등록(물품, 낙찰 정보, 판매 정보)
최고 입찰가를 제외한 나머지 입찰금 반환
모든 입찰 정보를 recode 테이블로 이전(jpa을 사용하여 recode에 입찰데이터 모두 등록 → bids에 있는 입찰정보 삭제)
 
 
Share article

송승현의 블로그