Skip to content

Commit

Permalink
Merge pull request #13 from C4-ComeTrue/feature/kariskan_step3
Browse files Browse the repository at this point in the history
Feature/kariskan step3
  • Loading branch information
kariskan authored May 29, 2024
2 parents 05355ff + 2b9b59e commit 017f356
Show file tree
Hide file tree
Showing 37 changed files with 941 additions and 8 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.apache.commons:commons-lang3:3.13.0'
implementation 'org.apache.commons:commons-collections4:4.4'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,18 @@ public void confirmOrder(Long orderId, Consumer consumer) {
order.updateOrderStatus(CONFIRM);
List<OrderProduct> orderProducts = orderProductReadService.findByOrderJoinFetchProductAndSeller(orderId);
addSellerBalance(orderProducts);
addOrderCount(orderProducts);

savePointLog(consumer, order, true);
}

/**
* 주문 시 product에 대한 구매 횟수를 증가함
*/
private void addOrderCount(List<OrderProduct> orderProducts) {
orderProducts.forEach(orderProduct -> orderProduct.getProduct().increaseOrderCount());
}

private void saveOrderInfo(PurchaseProductRequest request, Consumer consumer, Order order,
List<OrderProduct> orderProducts, long totalAmount) {
orderProductJdbcRepository.saveAllBatch(orderProducts);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ private void validateRequest(UpdateDeliveryStatusRequest request, Delivery deliv
*/
private boolean isInvalidChangeStatus(DeliveryStatus future, DeliveryStatus current) {
return future.isPending()
|| (future.isDelivering() && !current.isPending())
|| (future.isDelivered() && !current.isDelivering());
|| (future.isDelivering() && !current.isPending())
|| (future.isDelivered() && !current.isDelivering());
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package org.c4marathon.assignment.domain.order.repository;

import java.util.List;

import org.c4marathon.assignment.domain.order.entity.Order;
import org.springframework.data.jpa.repository.JpaRepository;

public interface OrderRepository extends JpaRepository<Order, Long> {

boolean existsByConsumerIdAndIdIn(Long consumerId, List<Long> ids);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
Expand All @@ -20,7 +21,12 @@
import lombok.NoArgsConstructor;

@Entity
@Table(name = "order_product_tbl")
@Table(
name = "order_product_tbl",
indexes = {
@Index(name = "created_at_idx", columnList = "created_at")
}
)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class OrderProduct extends BaseEntity {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package org.c4marathon.assignment.domain.orderproduct.repository;

import java.time.LocalDateTime;
import java.util.List;

import org.c4marathon.assignment.domain.orderproduct.entity.OrderProduct;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

Expand All @@ -14,4 +16,16 @@ public interface OrderProductRepository extends JpaRepository<OrderProduct, Long

@Query("select op from OrderProduct op join fetch op.product join fetch op.product.seller where op.order.id = :id")
List<OrderProduct> findByOrderJoinFetchProductAndSeller(@Param("id") Long orderId);

@Query(value = "select order_id from order_product_tbl op where op.product_id = :product_id", nativeQuery = true)
List<Long> findOrderIdByProductId(@Param("product_id") Long id);

@Modifying
@Query(value = """
delete
from order_product_tbl op
where op.created_at <= :dateTime
limit :pageSize
""", nativeQuery = true)
int deleteOrderProductTable(@Param("dateTime") LocalDateTime dateTime, @Param("pageSize") int pageSize);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.List;

import org.c4marathon.assignment.domain.order.repository.OrderRepository;
import org.c4marathon.assignment.domain.orderproduct.entity.OrderProduct;
import org.c4marathon.assignment.domain.orderproduct.repository.OrderProductRepository;
import org.springframework.stereotype.Service;
Expand All @@ -14,6 +15,7 @@
public class OrderProductReadService {

private final OrderProductRepository orderProductRepository;
private final OrderRepository orderRepository;

/**
* Product와 조인한 OrderProduct list를 orderId로 조회
Expand All @@ -30,4 +32,13 @@ public List<OrderProduct> findByOrderJoinFetchProduct(Long orderId) {
public List<OrderProduct> findByOrderJoinFetchProductAndSeller(Long orderId) {
return orderProductRepository.findByOrderJoinFetchProductAndSeller(orderId);
}

/**
* consumer id와 product id로 구매 이력 조회
*/
@Transactional(readOnly = true)
public boolean existsByConsumerIdAndProductId(Long consumerId, Long productId) {
List<Long> orderIds = orderProductRepository.findOrderIdByProductId(productId);
return orderRepository.existsByConsumerIdAndIdIn(consumerId, orderIds);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.c4marathon.assignment.domain.product.controller;

import org.c4marathon.assignment.domain.product.dto.request.ProductSearchRequest;
import org.c4marathon.assignment.domain.product.dto.response.ProductSearchResponse;
import org.c4marathon.assignment.domain.product.service.ProductReadService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
@RequestMapping("/products")
public class ProductController {

private final ProductReadService productReadService;

@GetMapping
public ProductSearchResponse searchProduct(
@Valid @ModelAttribute ProductSearchRequest request
) {
return productReadService.searchProduct(request);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.c4marathon.assignment.domain.product.dto.request;

import static java.util.Objects.*;

import java.time.LocalDateTime;

import org.c4marathon.assignment.global.constant.SortType;

import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Builder;

public record ProductSearchRequest(
@NotNull @Size(min = 2, message = "keyword length less than 2") String keyword,
@NotNull SortType sortType,
LocalDateTime createdAt,
Long productId,
Long amount,
Long orderCount,
Double score,
int pageSize
) {

@Builder
public ProductSearchRequest(
String keyword,
SortType sortType,
LocalDateTime createdAt,
Long productId,
Long amount,
Long orderCount,
Double score,
int pageSize
) {
this.keyword = keyword;
this.sortType = sortType;
this.createdAt = requireNonNullElse(createdAt, LocalDateTime.now());
this.productId = requireNonNullElse(productId, Long.MIN_VALUE);
this.amount = requireNonNullElse(amount, getDefaultAmount(sortType));
this.orderCount = requireNonNullElse(orderCount, Long.MAX_VALUE);
this.score = requireNonNullElse(score, Double.MAX_VALUE);
this.pageSize = pageSize;
}

private Long getDefaultAmount(SortType sortType) {
return sortType == SortType.PRICE_ASC ? Long.MIN_VALUE : Long.MAX_VALUE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.c4marathon.assignment.domain.product.dto.response;

import java.time.LocalDateTime;

public record ProductSearchEntry(
long id,
String name,
String description,
long amount,
int stock,
LocalDateTime createdAt,
Long orderCount,
Double avgScore
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.c4marathon.assignment.domain.product.dto.response;

import java.util.List;

public record ProductSearchResponse(
List<ProductSearchEntry> productSearchEntries
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import static org.c4marathon.assignment.global.constant.ProductStatus.*;

import java.math.BigDecimal;

import org.c4marathon.assignment.domain.base.entity.BaseEntity;
import org.c4marathon.assignment.domain.seller.entity.Seller;
import org.c4marathon.assignment.global.constant.ProductStatus;
Expand All @@ -28,7 +30,12 @@
@Table(
name = "product_tbl",
indexes = {
@Index(name = "product_name_seller_id_idx", columnList = "name,seller_id")
@Index(name = "product_name_seller_id_idx", columnList = "name,seller_id"),
@Index(name = "amount_product_id_idx", columnList = "amount, product_id"),
@Index(name = "amount_desc_product_id_idx", columnList = "amount desc, product_id"),
@Index(name = "created_at_product_id_idx", columnList = "created_at desc, product_id"),
@Index(name = "avg_score_desc_product_id_idx", columnList = "avg_score desc, product_id"),
@Index(name = "order_count_desc_product_id_idx", columnList = "order_count desc, product_id")
}
)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
Expand Down Expand Up @@ -64,6 +71,14 @@ public class Product extends BaseEntity {
@Column(name = "status", columnDefinition = "VARCHAR(20)")
private ProductStatus productStatus;

@NotNull
@Column(name = "avg_score", columnDefinition = "DECIMAL(5, 4) DEFAULT 0.0000")
private BigDecimal avgScore;

@NotNull
@Column(name = "order_count", columnDefinition = "BIGINT DEFAULT 0")
private Long orderCount;

@Builder
public Product(
String name,
Expand All @@ -78,6 +93,8 @@ public Product(
this.stock = stock;
this.seller = seller;
this.productStatus = IN_STOCK;
this.orderCount = 0L;
this.avgScore = BigDecimal.ZERO;
}

public void decreaseStock(Integer quantity) {
Expand All @@ -90,4 +107,12 @@ public void decreaseStock(Integer quantity) {
public boolean isSoldOut() {
return this.productStatus == OUT_OF_STOCK;
}

public void increaseOrderCount() {
this.orderCount++;
}

public void updateAvgScore(BigDecimal avgScore) {
this.avgScore = avgScore;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.c4marathon.assignment.domain.product.repository;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;
import org.c4marathon.assignment.domain.product.dto.request.ProductSearchRequest;
import org.c4marathon.assignment.domain.product.dto.response.ProductSearchEntry;

@Mapper
public interface ProductMapper {
List<ProductSearchEntry> selectByCondition(ProductSearchRequest request);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package org.c4marathon.assignment.domain.product.service;

import org.c4marathon.assignment.domain.product.dto.request.ProductSearchRequest;
import org.c4marathon.assignment.domain.product.dto.response.ProductSearchResponse;
import org.c4marathon.assignment.domain.product.entity.Product;
import org.c4marathon.assignment.domain.product.repository.ProductMapper;
import org.c4marathon.assignment.domain.product.repository.ProductRepository;
import org.c4marathon.assignment.domain.seller.entity.Seller;
import org.c4marathon.assignment.global.error.ErrorCode;
Expand All @@ -14,6 +17,7 @@
public class ProductReadService {

private final ProductRepository productRepository;
private final ProductMapper productMapper;

/**
* Seller와 product.name으로 Product 존재 여부 확인
Expand All @@ -31,4 +35,17 @@ public Product findById(Long id) {
return productRepository.findByIdJoinFetch(id)
.orElseThrow(() -> ErrorCode.PRODUCT_NOT_FOUND.baseException("id: %d", id));
}

/**
* sortType에 해당하는 조건으로 pagination
* Newest: 최신순, created_at desc, product_id asc
* PriceAsc: 가격 낮은 순, amount asc, product_id asc
* PriceDesc: 가격 높은 순, amount desc, product_id asc
* Popularity: 인기 순(주문 많은 순), order_count desc, product_id asc
* TopRated: 평점 높은 순(review score), avg_score desc, product_id asc
*/
@Transactional(readOnly = true)
public ProductSearchResponse searchProduct(ProductSearchRequest request) {
return new ProductSearchResponse(productMapper.selectByCondition(request));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.c4marathon.assignment.domain.review.controller;

import org.c4marathon.assignment.domain.consumer.entity.Consumer;
import org.c4marathon.assignment.domain.review.dto.request.ReviewCreateRequest;
import org.c4marathon.assignment.domain.review.service.ReviewService;
import org.c4marathon.assignment.global.auth.ConsumerThreadLocal;
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;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
@RequestMapping("/reviews")
public class ReviewController {

private final ReviewService reviewService;

@PostMapping
public void createReview(@Valid @RequestBody ReviewCreateRequest request) {
Consumer consumer = ConsumerThreadLocal.get();
reviewService.createReview(consumer, request);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.c4marathon.assignment.domain.review.dto.request;

import org.hibernate.validator.constraints.Range;

import jakarta.validation.constraints.Size;

public record ReviewCreateRequest(
@Range(min = 1, max = 5, message = "invalid score range")
int score,
@Size(max = 100, message = "comment length more than 100")
String comment,
long productId
) {
}
Loading

0 comments on commit 017f356

Please sign in to comment.