-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[FEAT] 입찰 기능 추가 #13
[FEAT] 입찰 기능 추가 #13
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package org.auction.client.bid.application; | ||
|
||
import java.util.Comparator; | ||
import java.util.List; | ||
|
||
import org.auction.client.bid.interfaces.request.CreateBidRequest; | ||
import org.auction.client.bid.interfaces.response.BidResponse; | ||
import org.auction.client.bid.interfaces.response.CreateBidResponse; | ||
import org.auction.domain.auction.domain.entity.AuctionEntity; | ||
import org.auction.domain.auction.domain.enums.AuctionStatus; | ||
import org.auction.domain.auction.infrastructure.AuctionRepository; | ||
import org.auction.domain.bid.entity.BidEntity; | ||
import org.auction.domain.bid.infrastructure.BidRepository; | ||
import org.auction.domain.user.domain.entity.MemberEntity; | ||
import org.auction.domain.user.infrastructure.MemberRepository; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class BidService { | ||
|
||
private final BidRepository bidRepository; | ||
private final AuctionRepository auctionRepository; | ||
private final MemberRepository memberRepository; | ||
|
||
@Transactional(readOnly = true) | ||
public List<BidResponse> findBidList( | ||
Long auction_id | ||
) { | ||
|
||
AuctionEntity auctionEntity = auctionRepository.findById(auction_id).orElseThrow(); | ||
|
||
return bidRepository.findALlByAuctionEntity(auctionEntity).stream() | ||
.sorted(Comparator.comparing(BidEntity::getCreatedAt).reversed()) | ||
.map(BidResponse::toDto) | ||
.toList(); | ||
Comment on lines
+36
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 부분을 service까지 끌고와서 sorting하지 않고 DB에서 처리해서 가져오기로 했던걸로 기억하는데 아직 수정이 안된걸까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 내부적으로는 그렇게 했었고 추후 멘토링과정에서 '변경이 있을까' 라는 생각에서 남겨두게 되었습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 내부적으로는 DB단에서 하기로 했었습니다만 멘토링과정에서 다르게 될 여지도 있다고 생각해서 이후에 수정하는게 맞다고 생각했습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋습니다! 오늘 멘토링 이후에 수정하도록 하죠! |
||
|
||
} | ||
|
||
@Transactional | ||
public CreateBidResponse addBid( | ||
Long auction_id, | ||
Long buyerId, | ||
CreateBidRequest createBidRequest | ||
) { | ||
|
||
MemberEntity memberEntity = memberRepository.findById(buyerId) | ||
.orElseThrow(() -> new RuntimeException("Member not found")); | ||
|
||
AuctionEntity auctionEntity = auctionRepository.findById(auction_id) | ||
.orElseThrow(() -> new RuntimeException("Auction not found")); | ||
|
||
if (!checkLastBid(memberEntity, createBidRequest.getBidPrice())) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 바로 이전에 내가 입찰을 했으면 입찰 연속으로 못하는 예외는 있나요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 코드에 반영하도록 하겠습니다. 감사합니다. |
||
throw new RuntimeException("Last bid price is incorrect"); | ||
} | ||
|
||
if (!checkAutionStatus(auctionEntity)) { | ||
throw new RuntimeException("Auction status is incorrect"); | ||
} | ||
|
||
BidEntity bidEntity = BidEntity.builder() | ||
.auctionEntity(auctionEntity) | ||
.memberEntity(memberEntity) | ||
.bidPrice(createBidRequest.getBidPrice()) | ||
.build(); | ||
|
||
bidRepository.save(bidEntity); | ||
|
||
return CreateBidResponse.toDto(bidEntity); | ||
|
||
} | ||
|
||
private boolean checkLastBid( | ||
MemberEntity memberEntity, | ||
Long bidPrice | ||
) { | ||
BidEntity beforeBidEntity = bidRepository.findTopByMemberEntityOrderByBidPriceDesc(memberEntity); | ||
|
||
if (beforeBidEntity != null) { | ||
return beforeBidEntity.getBidPrice() < bidPrice; | ||
} | ||
Comment on lines
+80
to
+84
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. repository에서 데이터를 조회하는 경우 null이 존재할 수 있다면 entity를 바로 가져오는 것보다는 Optional로 감싸서 받아오는게 좋다고 생각합니다. |
||
return true; | ||
|
||
} | ||
|
||
private boolean checkAutionStatus(AuctionEntity auctionEntity) { | ||
|
||
if (!(auctionEntity.getAuctionStatus() == AuctionStatus.BIDDING)) { | ||
return false; | ||
} | ||
return true; | ||
|
||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package org.auction.client.bid.interfaces; | ||
|
||
import java.util.List; | ||
|
||
import org.auction.client.bid.interfaces.request.CreateBidRequest; | ||
import org.auction.client.bid.interfaces.response.BidResponse; | ||
import org.auction.client.bid.interfaces.response.CreateBidResponse; | ||
import org.auction.client.common.dto.ResultDto; | ||
import org.springframework.http.ResponseEntity; | ||
|
||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.Parameter; | ||
import io.swagger.v3.oas.annotations.media.Content; | ||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponses; | ||
|
||
public interface BidApi { | ||
|
||
@Operation(summary = "입찰 목록 가져오기", description = "경매의 입찰 목록을 가져옵니다.") | ||
@ApiResponses(value = { | ||
@ApiResponse(responseCode = "200", description = "입찰 목록을 성공적으로 가져왔습니다.", | ||
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ResultDto.class))), | ||
@ApiResponse(responseCode = "400", description = "잘못된 요청입니다.", | ||
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ResultDto.class))), | ||
@ApiResponse(responseCode = "500", description = "서버 오류가 발생했습니다.", | ||
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ResultDto.class))) | ||
}) | ||
ResponseEntity<ResultDto<List<BidResponse>>> bidList( | ||
@Parameter(description = "입찰 목록을 확인 할 경매", required = true) Long auction_id | ||
|
||
); | ||
|
||
@Operation(summary = "입찰하기", description = "해당 경매에 입찰합니다.") | ||
@ApiResponses(value = { | ||
@ApiResponse(responseCode = "200", description = "입찰에 성공하였습니다.", | ||
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ResultDto.class))), | ||
@ApiResponse(responseCode = "400", description = "잘못된 요청입니다.", | ||
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ResultDto.class))), | ||
@ApiResponse(responseCode = "500", description = "서버 오류가 발생했습니다.", | ||
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ResultDto.class))) | ||
}) | ||
ResponseEntity<ResultDto<CreateBidResponse>> bidAdd( | ||
@Parameter(description = "입찰 할 경매", required = true) Long auctionId, | ||
@Parameter(description = "입찰 할 멤버", required = true) Long buyerId, | ||
@Parameter(description = "입찰 금액", required = true) CreateBidRequest createBidRequest | ||
); | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package org.auction.client.bid.interfaces; | ||
|
||
import java.util.List; | ||
|
||
import org.auction.client.bid.application.BidService; | ||
import org.auction.client.bid.interfaces.request.CreateBidRequest; | ||
import org.auction.client.bid.interfaces.response.BidResponse; | ||
import org.auction.client.bid.interfaces.response.CreateBidResponse; | ||
import org.auction.client.common.dto.ResultDto; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
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.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/api/v1/auction") | ||
public class BidController implements BidApi { | ||
|
||
private final BidService bidService; | ||
|
||
@GetMapping("/{auction_id}/bid") | ||
public ResponseEntity<ResultDto<List<BidResponse>>> bidList( | ||
@PathVariable("auction_id") Long auction_id | ||
|
||
) { | ||
|
||
List<BidResponse> bidList = bidService.findBidList(auction_id); | ||
|
||
ResultDto<List<BidResponse>> resultDto = new ResultDto<>( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Response 하는 부분도 수정했으니 제가 월요일에 설명드리겠습니다 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 알겠습니다. |
||
HttpStatus.OK, "성공적으로 입찰을 불러왔습니다.", bidList | ||
); | ||
|
||
return ResponseEntity.ok(resultDto); | ||
|
||
} | ||
|
||
@PostMapping("/{auction_id}/bid") | ||
public ResponseEntity<ResultDto<CreateBidResponse>> bidAdd( | ||
@PathVariable("auction_id") Long auctionId, | ||
@RequestParam("buyer_id") Long buyerId, | ||
@RequestBody CreateBidRequest createBidRequest | ||
) { | ||
|
||
CreateBidResponse createBidResponse = bidService.addBid(auctionId, buyerId, createBidRequest); | ||
|
||
ResultDto<CreateBidResponse> resultDto = new ResultDto<>( | ||
HttpStatus.OK, "성공적으로 입찰 되었습니다.", createBidResponse | ||
); | ||
|
||
return ResponseEntity.ok(resultDto); | ||
|
||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package org.auction.client.bid.interfaces.request; | ||
|
||
public record CreateBidRequest( | ||
Long bidPrice | ||
|
||
) { | ||
public Long getBidPrice() { | ||
return bidPrice; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package org.auction.client.bid.interfaces.response; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
import org.auction.domain.bid.entity.BidEntity; | ||
|
||
import com.fasterxml.jackson.annotation.JsonFormat; | ||
|
||
public record BidResponse( | ||
Long buyerId, | ||
Long bidPrice, | ||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") | ||
LocalDateTime createdAt | ||
) { | ||
public static BidResponse toDto(BidEntity bidEntity) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
해당 코드는 매개변수가 한 눈에 들어올수 있도록 이런식으로 수정하면 좋을 것 같아요! |
||
return new BidResponse(bidEntity.getMemberEntity().getMemberId(), bidEntity.getBidPrice(), | ||
bidEntity.getCreatedAt()); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package org.auction.client.bid.interfaces.response; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
import org.auction.domain.bid.entity.BidEntity; | ||
|
||
public record CreateBidResponse( | ||
Long buyerId, | ||
Long bidPrice, | ||
LocalDateTime createAt | ||
) { | ||
public static CreateBidResponse toDto(BidEntity bidEntity) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 위의 리뷰과 마찬가지 입니다~ |
||
return new CreateBidResponse(bidEntity.getMemberEntity().getMemberId(), bidEntity.getBidPrice(), | ||
bidEntity.getCreatedAt()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package org.auction.domain.bid.entity; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
import org.auction.domain.auction.domain.entity.AuctionEntity; | ||
import org.auction.domain.user.domain.entity.MemberEntity; | ||
import org.springframework.data.annotation.CreatedDate; | ||
import org.springframework.data.jpa.domain.support.AuditingEntityListener; | ||
|
||
import jakarta.persistence.CascadeType; | ||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.EntityListeners; | ||
import jakarta.persistence.FetchType; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.GenerationType; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.JoinColumn; | ||
import jakarta.persistence.ManyToOne; | ||
import lombok.AccessLevel; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Entity(name = "bids") | ||
@Getter | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
@EntityListeners(AuditingEntityListener.class) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이건 뭔가요..? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TimeTrackableEntity 를 상속 받을 필요는 없다고 생각해서 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. create_at만 담당하는 추상 클래스를 만들어서 해당 클래스를 상속받도록 수정하면 좋을 것 같아요~ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 알겠습니다. |
||
public class BidEntity { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
@Column(name = "bid_id") | ||
private Long bidId; | ||
|
||
@Column(name = "bid_price") | ||
private Long bidPrice; | ||
|
||
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cascade 부분 설명 부탁드립니다!! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AuctionEntity 해당 엔티티에 변경이 되면 관련된 BidEntity도 반영되어야 한다고 생각해서 했습니다. |
||
@JoinColumn(name = "auction_id") | ||
private AuctionEntity auctionEntity; | ||
|
||
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) | ||
@JoinColumn(name = "member_id") | ||
private MemberEntity memberEntity; | ||
|
||
@CreatedDate | ||
@Column(name = "created_at", updatable = false, nullable = false) | ||
private LocalDateTime createdAt; | ||
|
||
@Builder | ||
public BidEntity(AuctionEntity auctionEntity, MemberEntity memberEntity, Long bidPrice) { | ||
this.auctionEntity = auctionEntity; | ||
this.memberEntity = memberEntity; | ||
this.bidPrice = bidPrice; | ||
|
||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package org.auction.domain.bid.infrastructure; | ||
|
||
import java.util.List; | ||
|
||
import org.auction.domain.auction.domain.entity.AuctionEntity; | ||
import org.auction.domain.bid.entity.BidEntity; | ||
import org.auction.domain.user.domain.entity.MemberEntity; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface BidRepository extends JpaRepository<BidEntity, Long> { | ||
|
||
BidEntity findTopByMemberEntityOrderByBidPriceDesc(MemberEntity memberEntity); | ||
|
||
List<BidEntity> findALlByAuctionEntity(AuctionEntity auctionEntity); | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
예외 부분 제가 수정했으니, 나중에 제꺼 PR 받으면 추가하면 될 것 같아ㅛ.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
추후 작업이 더 필요한데 아직 진행하지 않은 사항에 대해서는 TODO를 붙이면 좋을 것 같습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 알겠습니다. 앞으로 붙이도록 하겠습니다.