Skip to content
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: 벌레 상품 구매 기능 구현 #107

Merged
merged 12 commits into from
Nov 20, 2023
33 changes: 33 additions & 0 deletions src/main/java/com/moabam/api/application/bug/BugService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,34 @@
import static com.moabam.api.domain.bug.BugActionType.*;
import static com.moabam.api.domain.bug.BugType.*;
import static com.moabam.api.domain.product.ProductType.*;
import static com.moabam.global.error.model.ErrorMessage.*;
import static java.util.Objects.*;

import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.moabam.api.application.member.MemberService;
import com.moabam.api.application.payment.PaymentMapper;
import com.moabam.api.application.product.ProductMapper;
import com.moabam.api.domain.bug.BugHistory;
import com.moabam.api.domain.bug.BugType;
import com.moabam.api.domain.bug.repository.BugHistorySearchRepository;
import com.moabam.api.domain.coupon.Coupon;
import com.moabam.api.domain.coupon.repository.CouponRepository;
import com.moabam.api.domain.member.Member;
import com.moabam.api.domain.payment.Payment;
import com.moabam.api.domain.payment.repository.PaymentRepository;
import com.moabam.api.domain.product.Product;
import com.moabam.api.domain.product.repository.ProductRepository;
import com.moabam.api.dto.bug.BugResponse;
import com.moabam.api.dto.bug.TodayBugResponse;
import com.moabam.api.dto.product.ProductsResponse;
import com.moabam.api.dto.product.PurchaseProductRequest;
import com.moabam.api.dto.product.PurchaseProductResponse;
import com.moabam.global.common.util.ClockHolder;
import com.moabam.global.error.exception.NotFoundException;

import lombok.RequiredArgsConstructor;

Expand All @@ -32,6 +42,8 @@ public class BugService {
private final MemberService memberService;
private final BugHistorySearchRepository bugHistorySearchRepository;
private final ProductRepository productRepository;
private final PaymentRepository paymentRepository;
private final CouponRepository couponRepository;
private final ClockHolder clockHolder;

public BugResponse getBug(Long memberId) {
Expand All @@ -54,10 +66,31 @@ public ProductsResponse getBugProducts() {
return ProductMapper.toProductsResponse(products);
}

@Transactional
public PurchaseProductResponse purchaseBugProduct(Long memberId, Long productId, PurchaseProductRequest request) {
Product product = getById(productId);
Payment payment = PaymentMapper.toEntity(memberId, product);

if (!isNull(request.couponId())) {
// TODO: (임시) CouponWallet 에 존재하는 할인 쿠폰인지 확인 @홍혁준
Coupon coupon = couponRepository.findById(request.couponId())
.orElseThrow(() -> new NotFoundException(NOT_FOUND_COUPON));
payment.applyCoupon(coupon);
Comment on lines +75 to +78
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A: 오호! 만들어지면 알려드리겠습니다..!

}
paymentRepository.save(payment);

return ProductMapper.toPurchaseProductResponse(payment);
}

private int calculateBugQuantity(List<BugHistory> bugHistory, BugType bugType) {
return bugHistory.stream()
.filter(history -> bugType.equals(history.getBugType()))
.mapToInt(BugHistory::getQuantity)
.sum();
}

private Product getById(Long productId) {
return productRepository.findById(productId)
.orElseThrow(() -> new NotFoundException(PRODUCT_NOT_FOUND));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.moabam.api.application.payment;

import com.moabam.api.domain.payment.Order;
import com.moabam.api.domain.payment.Payment;
import com.moabam.api.domain.product.Product;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class PaymentMapper {

public static Payment toEntity(Long memberId, Product product) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Payment와 Order의 합의점을 찾지 못해서 toEntity가 되어버린걸까요!?

Order order = Order.builder()
.name(product.getName())
.amount(product.getPrice())
.build();

return Payment.builder()
.memberId(memberId)
.product(product)
.order(order)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

import java.util.List;

import com.moabam.api.domain.payment.Payment;
import com.moabam.api.domain.product.Product;
import com.moabam.api.dto.product.ProductResponse;
import com.moabam.api.dto.product.ProductsResponse;
import com.moabam.api.dto.product.PurchaseProductResponse;
import com.moabam.global.common.util.StreamUtils;

import lombok.AccessLevel;
Expand All @@ -28,4 +30,12 @@ public static ProductsResponse toProductsResponse(List<Product> products) {
.products(StreamUtils.map(products, ProductMapper::toProductResponse))
.build();
}

public static PurchaseProductResponse toPurchaseProductResponse(Payment payment) {
return PurchaseProductResponse.builder()
.paymentId(payment.getId())
.orderName(payment.getOrder().getName())
.price(payment.getOrder().getAmount())
.build();
}
}
50 changes: 50 additions & 0 deletions src/main/java/com/moabam/api/domain/payment/Order.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.moabam.api.domain.payment;

import static com.moabam.global.error.model.ErrorMessage.*;
import static java.lang.Math.*;
import static java.util.Objects.*;

import com.moabam.global.error.exception.BadRequestException;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Embeddable
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Order {

private static final int MIN_AMOUNT = 0;

@Column(name = "order_id")
private String id;

@Column(name = "order_name", nullable = false)
private String name;

@Column(name = "amount", nullable = false)
private int amount;

@Builder
private Order(String id, String name, int amount) {
this.id = id;
this.name = requireNonNull(name);
this.amount = validateAmount(amount);
}

private int validateAmount(int amount) {
if (amount < MIN_AMOUNT) {
throw new BadRequestException(INVALID_ORDER_AMOUNT);
}

return amount;
}

public void discountAmount(int price) {
this.amount = max(MIN_AMOUNT, amount - price);
}
}
73 changes: 73 additions & 0 deletions src/main/java/com/moabam/api/domain/payment/Payment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.moabam.api.domain.payment;

import static java.util.Objects.*;

import com.moabam.api.domain.coupon.Coupon;
import com.moabam.api.domain.product.Product;
import com.moabam.global.common.entity.BaseTimeEntity;

import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
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 jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@Table(name = "payment")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Payment extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;

@Column(name = "member_id", updatable = false, nullable = false)
private Long memberId;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "product_id", updatable = false, nullable = false)
private Product product;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "coupon_id")
private Coupon coupon;

@Embedded
private Order order;

@Column(name = "payment_key")
private String paymentKey;

@Enumerated(value = EnumType.STRING)
@Column(name = "status", nullable = false)
private PaymentStatus status;

@Builder
public Payment(Long memberId, Product product, Coupon coupon, Order order, String paymentKey,
PaymentStatus status) {
this.memberId = requireNonNull(memberId);
this.product = requireNonNull(product);
this.coupon = coupon;
this.order = requireNonNull(order);
this.paymentKey = paymentKey;
this.status = requireNonNullElse(status, PaymentStatus.REQUEST);
}

public void applyCoupon(Coupon coupon) {
this.order.discountAmount(coupon.getPoint());
this.coupon = coupon;
}
}
10 changes: 10 additions & 0 deletions src/main/java/com/moabam/api/domain/payment/PaymentStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.moabam.api.domain.payment;

public enum PaymentStatus {

// 임시
REQUEST,
DONE,
FAIL,
REFUND;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.moabam.api.domain.payment.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.moabam.api.domain.payment.Payment;

public interface PaymentRepository extends JpaRepository<Payment, Long> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.moabam.api.dto.product;

import javax.annotation.Nullable;

public record PurchaseProductRequest(
@Nullable Long couponId
) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.moabam.api.dto.product;

import lombok.Builder;

@Builder
public record PurchaseProductResponse(
Long paymentId,
String orderName,
int price
) {

}
13 changes: 13 additions & 0 deletions src/main/java/com/moabam/api/presentation/BugController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import org.springframework.http.HttpStatus;
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.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -10,6 +13,8 @@
import com.moabam.api.dto.bug.BugResponse;
import com.moabam.api.dto.bug.TodayBugResponse;
import com.moabam.api.dto.product.ProductsResponse;
import com.moabam.api.dto.product.PurchaseProductRequest;
import com.moabam.api.dto.product.PurchaseProductResponse;
import com.moabam.global.auth.annotation.CurrentMember;
import com.moabam.global.auth.model.AuthorizationMember;

Expand Down Expand Up @@ -39,4 +44,12 @@ public TodayBugResponse getTodayBug(@CurrentMember AuthorizationMember member) {
public ProductsResponse getBugProducts() {
return bugService.getBugProducts();
}

@PostMapping("/products/{productId}/purchase")
@ResponseStatus(HttpStatus.OK)
public PurchaseProductResponse purchaseBugProduct(@CurrentMember AuthorizationMember member,
@PathVariable Long productId,
@RequestBody PurchaseProductRequest request) {
return bugService.purchaseBugProduct(member.id(), productId, request);
}
}
3 changes: 3 additions & 0 deletions src/main/java/com/moabam/global/error/model/ErrorMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ public enum ErrorMessage {
INVALID_PRICE("가격은 0 이상이어야 합니다."),
INVALID_QUANTITY("수량은 1 이상이어야 합니다."),
INVALID_LEVEL("레벨은 1 이상이어야 합니다."),
INVALID_ORDER_AMOUNT("주문 금액은 0 이상이어야 합니다."),

PRODUCT_NOT_FOUND("존재하지 않는 상품입니다."),

FAILED_FCM_INIT("파이어베이스 설정을 실패했습니다."),
NOT_FOUND_FCM_TOKEN("해당 유저는 접속 중이 아닙니다."),
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/config
12 changes: 6 additions & 6 deletions src/main/resources/static/docs/coupon.html
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ <h4 id="_요청" class="discrete">요청</h4>
<div class="content">
<pre class="highlight nowrap"><code class="language-http" data-lang="http">POST /admins/coupons HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 200
Content-Length: 192
Host: localhost:8080

{
Expand All @@ -483,7 +483,7 @@ <h4 id="_응답" class="discrete">응답</h4>
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json
Content-Length: 64
Content-Length: 62

{
"message" : "쿠폰의 이름이 중복되었습니다."
Expand Down Expand Up @@ -514,7 +514,7 @@ <h4 id="_응답_2" class="discrete">응답</h4>
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json
Content-Length: 58
Content-Length: 56

{
"message" : "존재하지 않는 쿠폰입니다."
Expand Down Expand Up @@ -546,7 +546,7 @@ <h4 id="_응답_3" class="discrete">응답</h4>
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json
Content-Length: 58
Content-Length: 56

{
"message" : "존재하지 않는 쿠폰입니다."
Expand All @@ -569,7 +569,7 @@ <h4 id="_요청_4">요청</h4>
<div class="content">
<pre class="highlight nowrap"><code class="language-http" data-lang="http">POST /coupons/search HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 88
Content-Length: 84
Host: localhost:8080

{
Expand Down Expand Up @@ -627,7 +627,7 @@ <h3 id="_쿠폰_사용_진행_중">쿠폰 사용 (진행 중)</h3>
<div id="footer">
<div id="footer-text">
Version 0.0.1-SNAPSHOT<br>
Last updated 2023-11-16 18:30:03 +0900
Last updated 2023-11-13 18:48:03 +0900
</div>
</div>
</body>
Expand Down
Loading