-
Notifications
You must be signed in to change notification settings - Fork 0
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
[#46] 경매 상품 결제(포인트) 기능 추가 #47
Changes from 4 commits
07db482
a7f1358
92ffb6d
a01c0d0
ba26c10
448808b
0aabd8b
772d18d
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,49 @@ | ||
package com.example.moduleapi.controller.point; | ||
|
||
import com.example.moduleapi.controller.request.point.PointAmount; | ||
import com.example.moduleapi.controller.response.point.PointResponse; | ||
import com.example.moduleapi.service.point.PointService; | ||
import com.example.moduledomain.domain.user.CustomUserDetails; | ||
import org.springframework.security.core.annotation.AuthenticationPrincipal; | ||
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.RestController; | ||
|
||
@RestController | ||
@RequestMapping("/api/v1/point") | ||
public class PointController { | ||
|
||
private final PointService pointService; | ||
|
||
public PointController(PointService pointService) { | ||
this.pointService = pointService; | ||
} | ||
|
||
// 포인트 충전 | ||
@PostMapping("/charge") | ||
public PointResponse chargePoint( | ||
@AuthenticationPrincipal CustomUserDetails customUserDetails, | ||
@RequestBody PointAmount pointAmount) { | ||
int totalPoint = pointService.chargePoint(customUserDetails, pointAmount); | ||
PointResponse pointResponse = PointResponse.builder() | ||
.currentPoint(totalPoint) | ||
.message("포인트가 성공적으로 충전되었습니다.") | ||
.build(); | ||
return pointResponse; | ||
} | ||
|
||
// 포인트 사용 | ||
@PostMapping("/deduct") | ||
public PointResponse deductPoint( | ||
@AuthenticationPrincipal CustomUserDetails customUserDetails, | ||
@RequestBody PointAmount pointAmount) { | ||
int totalPoint = pointService.deductPoint(customUserDetails, pointAmount); | ||
PointResponse pointResponse = PointResponse.builder() | ||
.currentPoint(totalPoint) | ||
.message("포인트가 성공적으로 차감되었습니다.") | ||
.build(); | ||
return pointResponse; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.example.moduleapi.controller.request.point; | ||
|
||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@NoArgsConstructor | ||
public class PointAmount { | ||
private int amount; | ||
|
||
public PointAmount(int amount) { | ||
this.amount = amount; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package com.example.moduleapi.controller.response.point; | ||
|
||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@NoArgsConstructor | ||
public class PointResponse { | ||
private int currentPoint; | ||
private String message; | ||
|
||
@Builder | ||
public PointResponse(int currentPoint, String message) { | ||
this.currentPoint = currentPoint; | ||
this.message = message; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.example.moduleapi.exception.point; | ||
|
||
public class PointDeductionFailedException extends RuntimeException { | ||
public PointDeductionFailedException(Long userId) { | ||
super(userId + ": 포인트가 부족합니다."); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,13 @@ | ||
package com.example.moduleapi.service.auction; | ||
|
||
import com.example.moduleapi.controller.request.auction.BidRequest; | ||
import com.example.moduleapi.controller.request.point.PointAmount; | ||
import com.example.moduleapi.controller.response.auction.BidResponse; | ||
import com.example.moduleapi.controller.response.product.ProductFindResponse; | ||
import com.example.moduleapi.exception.auction.BiddingFailException; | ||
import com.example.moduleapi.exception.auction.RedisLockAcquisitionException; | ||
import com.example.moduleapi.exception.auction.RedisLockInterruptedException; | ||
import com.example.moduleapi.service.point.PointService; | ||
import com.example.moduleapi.service.product.ProductFacade; | ||
import com.example.moduledomain.domain.user.CustomUserDetails; | ||
import org.redisson.api.RLock; | ||
|
@@ -25,12 +27,14 @@ public class AuctionService { | |
private final HighestBidSseNotificationService bidSseNotificationService; | ||
private final RedissonClient redissonClient; | ||
private final KafkaProducerService kafkaProducerService; | ||
private final PointService pointService; | ||
|
||
public AuctionService(ProductFacade productFacade, HighestBidSseNotificationService bidSseNotificationService, RedissonClient redissonClient, KafkaProducerService kafkaProducerService) { | ||
public AuctionService(ProductFacade productFacade, HighestBidSseNotificationService bidSseNotificationService, RedissonClient redissonClient, KafkaProducerService kafkaProducerService, PointService pointService) { | ||
this.productFacade = productFacade; | ||
this.bidSseNotificationService = bidSseNotificationService; | ||
this.redissonClient = redissonClient; | ||
this.kafkaProducerService = kafkaProducerService; | ||
this.pointService = pointService; | ||
} | ||
|
||
@Transactional | ||
|
@@ -73,7 +77,6 @@ private Long processBid(CustomUserDetails user, BidRequest bidRequest, Long prod | |
throw new BiddingFailException(user.getUser().getUserId(), bidRequest.getBiddingPrice(), productId); | ||
} | ||
|
||
//updateRedisBidData(user, highestBidMap, bidRequest, productId); | ||
return updateRedisBidData(user, highestBidMap, bidRequest, productId); | ||
} | ||
|
||
|
@@ -83,6 +86,9 @@ private void isBiddingAvailable(CustomUserDetails user, BidRequest bidRequest, L | |
if (biddingRequestTime.isAfter(product.getCloseDate())) { | ||
throw new BiddingFailException(user.getUser().getUserId(), bidRequest.getBiddingPrice(), productId); | ||
} | ||
|
||
PointAmount pointAmount = new PointAmount(bidRequest.getBiddingPrice()); | ||
pointService.deductPoint(user, pointAmount); | ||
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. 경매 입찰이 가능한지 검증하는 메서드쪽에 포인트 차감하는 코드를 추가했습니다 |
||
} | ||
|
||
private Long updateRedisBidData(CustomUserDetails user, RMap<Long, Pair<Long, Long>> bidMap, BidRequest bidRequest, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package com.example.moduleapi.service.point; | ||
|
||
import com.example.moduleapi.controller.request.point.PointAmount; | ||
import com.example.moduleapi.exception.point.PointDeductionFailedException; | ||
import com.example.moduledomain.domain.point.Point; | ||
import com.example.moduledomain.domain.user.CustomUserDetails; | ||
import com.example.moduledomain.domain.user.User; | ||
import com.example.moduledomain.repository.user.PointRepository; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
public class PointService { | ||
private final PointRepository pointRepository; | ||
|
||
public PointService(PointRepository pointRepository) { | ||
this.pointRepository = pointRepository; | ||
} | ||
|
||
// 포인트 충전 | ||
public int chargePoint(CustomUserDetails userDetails, PointAmount pointAmount) { | ||
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. 앗 자꾸... ㅠㅠ |
||
User user = userDetails.getUser(); | ||
Point point = pointRepository.findByUserId(user.getId()); | ||
point.plus(pointAmount.getAmount()); | ||
return point.getAmount(); | ||
} | ||
|
||
// 포인트 차감 | ||
public int deductPoint(CustomUserDetails userDetails, PointAmount pointAmount) { | ||
User user = userDetails.getUser(); | ||
Point point = pointRepository.findByUserId(user.getId()); | ||
|
||
validateDeductPoint(pointAmount, user, point); | ||
|
||
point.minus(pointAmount.getAmount()); | ||
return point.getAmount(); | ||
|
||
} | ||
|
||
private static void validateDeductPoint(PointAmount pointAmount, User user, Point point) { | ||
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. 경매 입찰 시도할때 가격만큼 포인트에서 차감을 시킨 다음에 낙찰받지 못하면 다시 포인트 돌려주는 방식으로 포인트 부족 문제를 해결할 수 있을꺼같은데 이 방식은 어떤지 궁금합니다! 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. 넵 감사합니다! |
||
if (point.getAmount() < pointAmount.getAmount()) { | ||
throw new PointDeductionFailedException(user.getId()); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.example.moduleapi.fixture.point | ||
|
||
import com.example.moduledomain.domain.point.Point | ||
|
||
class PointFixtures { | ||
static Point createPoint(Map map = [:]) { | ||
return Point.builder() | ||
.userId(map.getOrDefault("userId", 1L) as Long) | ||
.amount(map.getOrDefault("amount", 0) as int) | ||
.build() | ||
} | ||
|
||
} | ||
|
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.
요거는 말씀하신거처럼 배치에서 낙찰자 판단하고 거기서 차감하는게 맞지 않을까 싶네요!
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.
앗 그러네요!! 필요없는 API네용.. 삭제하겠습니다!