Commit b5e22fc2 authored by 0Tyler's avatar 0Tyler

feat: checkout api

parent 1b6ac182
package edu.prlab.ecsimple.common.exception;
public class BusinessException extends RuntimeException {
public BusinessException(String message) {
super(message);
}
}
package edu.prlab.ecsimple.common.handler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.lang.NonNull;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
@Slf4j
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(Exception.class)
protected ResponseEntity<Object> handleAll(Exception ex, WebRequest request) {
ex.printStackTrace();
return handleExceptionInternal(ex, ex.getMessage(), new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request);
}
@NonNull
@Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(
@NonNull HttpMessageNotReadableException ex, @NonNull HttpHeaders headers, @NonNull HttpStatus status, @NonNull WebRequest request) {
return new ResponseEntity<>(ex.getMessage(), new HttpHeaders(), HttpStatus.BAD_REQUEST);
}
}
...@@ -2,10 +2,14 @@ package edu.prlab.ecsimple.common.mapper; ...@@ -2,10 +2,14 @@ package edu.prlab.ecsimple.common.mapper;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
public class MapperUtil { public class MapperUtil {
private final static ObjectMapper mapper = new ObjectMapper(); private final static ObjectMapper mapper = new ObjectMapper();
public static JsonNode objectToJsonNode(Object obj) { public static JsonNode objectToJsonNode(Object obj) {
mapper.registerModule(new JavaTimeModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return mapper.valueToTree(obj); return mapper.valueToTree(obj);
} }
} }
...@@ -40,16 +40,5 @@ public class DefaultDataBean implements ApplicationRunner { ...@@ -40,16 +40,5 @@ public class DefaultDataBean implements ApplicationRunner {
.tags(List.of("tag2", "tag3")) .tags(List.of("tag2", "tag3"))
.quantity(20) .quantity(20)
.build()); .build());
TheOrder order1 = orderService.create(TheOrder.builder()
.email("email")
.state("state")
.checkoutDate(LocalDateTime.now())
.build())
.orElseThrow(() -> new EntityNotFoundException("no order"));
Item item1 = orderService.create(Item.builder()
.quantity(1)
.build())
.orElseThrow(() -> new EntityNotFoundException("no Item"));
orderService.addItemToOrder(order1.getIid(), item1.getSku());
} }
} }
...@@ -13,12 +13,10 @@ import javax.persistence.*; ...@@ -13,12 +13,10 @@ import javax.persistence.*;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Entity @Entity
public class Item { public class LineItem {
@Id @Id
@GenericGenerator(name = "uuid2", strategy = "uuid2") @Column(length = 36, nullable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY, generator = "uuid2")
@Column(length = 36, nullable = false, updatable = false)
private String sku; private String sku;
@Column @Column
......
...@@ -9,7 +9,6 @@ import javax.persistence.*; ...@@ -9,7 +9,6 @@ import javax.persistence.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List;
@Data @Data
@Builder @Builder
...@@ -25,11 +24,14 @@ public class TheOrder { ...@@ -25,11 +24,14 @@ public class TheOrder {
@Builder.Default @Builder.Default
@ManyToMany(fetch = FetchType.EAGER) @ManyToMany(fetch = FetchType.EAGER)
private Collection<Item> items = new ArrayList<>(); private Collection<LineItem> lineItems = new ArrayList<>();
@Column @Column
private String email; private String email;
@Column
private String creditCard;
@Column(columnDefinition = "TIMESTAMP") @Column(columnDefinition = "TIMESTAMP")
private LocalDateTime checkoutDate; private LocalDateTime checkoutDate;
......
package edu.prlab.ecsimple.model; package edu.prlab.ecsimple.model;
import edu.prlab.ecsimple.domain.Item; import edu.prlab.ecsimple.domain.LineItem;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import javax.persistence.FetchType;
import javax.persistence.ManyToMany;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
...@@ -17,7 +15,7 @@ import java.util.Collection; ...@@ -17,7 +15,7 @@ import java.util.Collection;
@AllArgsConstructor @AllArgsConstructor
public class OrderModel { public class OrderModel {
@Builder.Default @Builder.Default
private Collection<Item> lineItems = new ArrayList<>(); private Collection<LineItem> lineItems = new ArrayList<>();
private String email; private String email;
private String creditCard; private String creditCard;
} }
package edu.prlab.ecsimple.repo; package edu.prlab.ecsimple.repo;
import edu.prlab.ecsimple.domain.Item; import edu.prlab.ecsimple.domain.LineItem;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@Repository @Repository
public interface ItemRepo extends JpaRepository<Item, String> { public interface ItemRepo extends JpaRepository<LineItem, String> {
} }
package edu.prlab.ecsimple.service; package edu.prlab.ecsimple.service;
import edu.prlab.ecsimple.domain.Item; import edu.prlab.ecsimple.domain.LineItem;
import edu.prlab.ecsimple.domain.TheOrder; import edu.prlab.ecsimple.domain.TheOrder;
import edu.prlab.ecsimple.model.OrderModel; import edu.prlab.ecsimple.model.OrderModel;
...@@ -8,7 +8,7 @@ import java.util.Optional; ...@@ -8,7 +8,7 @@ import java.util.Optional;
public interface OrderService { public interface OrderService {
Optional<TheOrder> create(TheOrder theOrder); Optional<TheOrder> create(TheOrder theOrder);
Optional<Item> create(Item item); Optional<LineItem> addItemFromProduct(LineItem lineItem);
void addItemToOrder(Integer orderId, String sku); void addItemToOrder(Integer orderId, String sku);
Optional<TheOrder> checkout(OrderModel orderModel); Optional<TheOrder> checkout(OrderModel orderModel);
......
package edu.prlab.ecsimple.service; package edu.prlab.ecsimple.service;
import edu.prlab.ecsimple.common.exception.BusinessException;
import edu.prlab.ecsimple.common.exception.EntityNotFoundException; import edu.prlab.ecsimple.common.exception.EntityNotFoundException;
import edu.prlab.ecsimple.domain.Item; import edu.prlab.ecsimple.domain.LineItem;
import edu.prlab.ecsimple.domain.TheOrder; import edu.prlab.ecsimple.domain.TheOrder;
import edu.prlab.ecsimple.model.OrderModel; import edu.prlab.ecsimple.model.OrderModel;
import edu.prlab.ecsimple.model.ResponseModel;
import edu.prlab.ecsimple.repo.ItemRepo; import edu.prlab.ecsimple.repo.ItemRepo;
import edu.prlab.ecsimple.repo.OrderRepo; import edu.prlab.ecsimple.repo.OrderRepo;
import edu.prlab.ecsimple.repo.ProductRepo;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.Collection;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
...@@ -25,6 +25,8 @@ public class OrderServiceImpl implements OrderService { ...@@ -25,6 +25,8 @@ public class OrderServiceImpl implements OrderService {
private final OrderRepo orderRepo; private final OrderRepo orderRepo;
private final ItemRepo itemRepo; private final ItemRepo itemRepo;
private final ProductRepo productRepo;
@Override @Override
public Optional<TheOrder> create(TheOrder theOrder) { public Optional<TheOrder> create(TheOrder theOrder) {
return Optional.of(theOrder) return Optional.of(theOrder)
...@@ -32,16 +34,25 @@ public class OrderServiceImpl implements OrderService { ...@@ -32,16 +34,25 @@ public class OrderServiceImpl implements OrderService {
} }
@Override @Override
public Optional<Item> create(Item item) { public Optional<LineItem> addItemFromProduct(LineItem lineItem) {
return Optional.of(item) return Optional.of(lineItem)
.map(it -> itemRepo.save(item)); .map(it -> productRepo.findById(it.getSku())
.map(product -> {
var leftQty = product.getQuantity() - lineItem.getQuantity();
if (leftQty < 0) {
throw new BusinessException("There is not enough stock with product: " + product.getSku());
}
product.setQuantity(leftQty);
return itemRepo.save(lineItem);
})
.orElseThrow(() -> new EntityNotFoundException("Not found Product: " + it.getSku())));
} }
@Override @Override
public void addItemToOrder(Integer orderId, String sku) { public void addItemToOrder(Integer orderId, String sku) {
orderRepo.findById(orderId) orderRepo.findById(orderId)
.map(order -> itemRepo.findById(sku) .map(order -> itemRepo.findById(sku)
.map(item -> order.getItems().add(item)) .map(item -> order.getLineItems().add(item))
.orElseThrow(() -> new EntityNotFoundException("No found this order"))) .orElseThrow(() -> new EntityNotFoundException("No found this order")))
.orElseThrow(() -> new EntityNotFoundException("No found this order")); .orElseThrow(() -> new EntityNotFoundException("No found this order"));
} }
...@@ -52,14 +63,17 @@ public class OrderServiceImpl implements OrderService { ...@@ -52,14 +63,17 @@ public class OrderServiceImpl implements OrderService {
return Optional.of(orderModel) return Optional.of(orderModel)
.map(it -> orderRepo.save(TheOrder.builder() .map(it -> orderRepo.save(TheOrder.builder()
.email(orderModel.getEmail()) .email(orderModel.getEmail())
.creditCard(orderModel.getCreditCard())
.state("created") .state("created")
.checkoutDate(LocalDateTime.now()) .checkoutDate(LocalDateTime.now())
.build())) .build()))
.map(order -> { .map(order -> {
orderModel.getLineItems().stream() Collection<LineItem> lineItems = orderModel.getLineItems().stream()
.map(it -> create(it) .map(it -> addItemFromProduct(it)
.orElseThrow(() -> new RuntimeException("save item error"))) .orElseThrow(() -> new RuntimeException("Save lineItem error")))
.forEach(it -> addItemToOrder(order.getIid(), it.getSku())); .peek(it -> addItemToOrder(order.getIid(), it.getSku()))
.collect(Collectors.toList());
order.setLineItems(lineItems);
return order; return order;
}); });
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment