Merge pull request #14 from Development-team-1/feature/owner-order-backend-structure

Feature/owner order backend structure
This commit is contained in:
백창훈
2022-02-04 17:06:40 +09:00
committed by GitHub
52 changed files with 1266 additions and 13 deletions

View File

@@ -3,9 +3,11 @@ package com.justpickup.orderservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class OrderServiceApplication {
public static void main(String[] args) {

View File

@@ -0,0 +1,86 @@
package com.justpickup.orderservice.domain.order.dto;
import com.justpickup.orderservice.domain.order.entity.Order;
import com.justpickup.orderservice.domain.order.entity.OrderStatus;
import com.justpickup.orderservice.domain.orderItem.dto.OrderItemDto;
import com.justpickup.orderservice.domain.orderItem.entity.OrderItem;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
@Getter @NoArgsConstructor
public class OrderDto {
private Long id;
private Long userId;
private String userName;
private Long userCouponId;
private Long orderPrice;
private LocalDateTime orderTime;
private Long usedPoint;
private OrderStatus orderStatus;
// private TransactionDto transactionDto;
private List<OrderItemDto> orderItemDtoList;
@Builder
public OrderDto(Long id, Long userId, Long userCouponId, Long orderPrice, LocalDateTime orderTime,
Long usedPoint, OrderStatus orderStatus, List<OrderItemDto> orderItemDtoList) {
this.id = id;
this.userId = userId;
this.userCouponId = userCouponId;
this.orderPrice = orderPrice;
this.orderTime = orderTime;
this.usedPoint = usedPoint;
this.orderStatus = orderStatus;
this.orderItemDtoList = orderItemDtoList;
}
// == 생성 메소드 == //
public static OrderDto createPrimitiveField(Order order) {
return OrderDto.builder()
.id(order.getId())
.userId(order.getUserId())
.userCouponId(order.getUserCouponId())
.orderPrice(order.getOrderPrice())
.orderTime(order.getOrderTime())
.usedPoint(order.getUsedPoint())
.orderStatus(order.getOrderStatus())
.build();
}
public static OrderDto createFullField(Order order) {
List<OrderItem> orderItems = order.getOrderItems();
List<OrderItemDto> orderItemDtoList = orderItems.stream()
.map(OrderItemDto::createPrimitiveField)
.collect(Collectors.toList());
return OrderDto.builder()
.id(order.getId())
.userId(order.getUserId())
.userCouponId(order.getUserCouponId())
.orderPrice(order.getOrderPrice())
.orderTime(order.getOrderTime())
.usedPoint(order.getUsedPoint())
.orderStatus(order.getOrderStatus())
.orderItemDtoList(orderItemDtoList)
.build();
}
// == 변수 변경 메소드 == //
public void setUserName(String userName) {
this.userName = userName;
}
}

View File

@@ -1,5 +1,7 @@
package com.justpickup.orderservice.domain.order.entity;
import com.justpickup.orderservice.domain.order.dto.OrderDto;
import com.justpickup.orderservice.domain.orderItem.dto.OrderItemDto;
import com.justpickup.orderservice.domain.orderItem.entity.OrderItem;
import com.justpickup.orderservice.domain.transaction.entity.Transaction;
import com.justpickup.orderservice.global.entity.BaseEntity;
@@ -10,6 +12,7 @@ import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
@Entity
@Table(name = "orders")
@@ -34,7 +37,7 @@ public class Order extends BaseEntity {
@Enumerated(EnumType.STRING)
private OrderStatus orderStatus;
@OneToOne(mappedBy = "order")
@OneToOne(mappedBy = "order", fetch = FetchType.LAZY)
private Transaction transaction;
@OneToMany(mappedBy = "order")

View File

@@ -0,0 +1,8 @@
package com.justpickup.orderservice.domain.order.repository;
import com.justpickup.orderservice.domain.order.entity.Order;
import org.springframework.data.jpa.repository.JpaRepository;
public interface OrderRepository extends JpaRepository<Order, Long> {
}

View File

@@ -0,0 +1,35 @@
package com.justpickup.orderservice.domain.order.repository;
import com.justpickup.orderservice.domain.order.entity.Order;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
import static com.justpickup.orderservice.domain.order.entity.QOrder.order;
@Repository
@RequiredArgsConstructor
public class OrderRepositoryCustom {
private final JPAQueryFactory queryFactory;
public List<Order> findOrderMainBetweenOrderDate(LocalDate orderDate) {
LocalDateTime start = orderDate.atStartOfDay();
LocalDateTime end = LocalDateTime.of(orderDate, LocalTime.of(23, 59, 59));
return queryFactory
.selectFrom(order)
.join(order.orderItems).fetchJoin()
.join(order.transaction).fetchJoin()
.where(
order.orderTime.between(start, end)
)
.distinct()
.fetch();
}
}

View File

@@ -0,0 +1,10 @@
package com.justpickup.orderservice.domain.order.service;
import com.justpickup.orderservice.domain.order.dto.OrderDto;
import java.time.LocalDate;
import java.util.List;
public interface OrderService {
List<OrderDto> findOrderMain(LocalDate localDate);
}

View File

@@ -0,0 +1,54 @@
package com.justpickup.orderservice.domain.order.service;
import com.justpickup.orderservice.domain.order.dto.OrderDto;
import com.justpickup.orderservice.domain.order.repository.OrderRepository;
import com.justpickup.orderservice.domain.order.repository.OrderRepositoryCustom;
import com.justpickup.orderservice.global.client.store.GetItemResponse;
import com.justpickup.orderservice.global.client.store.StoreClient;
import com.justpickup.orderservice.global.client.user.UserClient;
import com.justpickup.orderservice.global.client.user.GetCustomerResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
@Slf4j
public class OrderServiceImpl implements OrderService {
private final OrderRepository orderRepository;
private final OrderRepositoryCustom orderRepositoryCustom;
private final StoreClient storeClient;
private final UserClient userClient;
@Override
public List<OrderDto> findOrderMain(LocalDate orderDate) {
// 주문 가져오기
List<OrderDto> orderDtoList = orderRepositoryCustom.findOrderMainBetweenOrderDate(orderDate)
.stream()
.map(OrderDto::createFullField)
.collect(Collectors.toList());
// 사용자명 및 아이템 이름 가져오기
orderDtoList.forEach(orderDto -> {
GetCustomerResponse getCustomerResponse = userClient.getUser(orderDto.getUserId())
.getData();
orderDto.setUserName(getCustomerResponse.getUserName());
orderDto.getOrderItemDtoList()
.forEach(orderItemDto -> {
GetItemResponse getItemResponse = storeClient.getItem(orderItemDto.getItemId())
.getData();
orderItemDto.setItemName(getItemResponse.getName());
});
});
return orderDtoList;
}
}

View File

@@ -0,0 +1,94 @@
package com.justpickup.orderservice.domain.order.web;
import com.justpickup.orderservice.domain.order.dto.OrderDto;
import com.justpickup.orderservice.domain.order.entity.OrderStatus;
import com.justpickup.orderservice.domain.order.service.OrderService;
import com.justpickup.orderservice.domain.orderItem.dto.OrderItemDto;
import com.justpickup.orderservice.global.dto.Result;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import javax.validation.constraints.Pattern;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequiredArgsConstructor
@Slf4j
public class OrderController {
private final OrderService orderService;
@GetMapping("/orderMain")
public ResponseEntity orderMain(@Valid OrderMainRequest orderMainRequest) {
List<OrderDto> orderDto = orderService.findOrderMain(orderMainRequest.convertOrderTimeToLocalDate());
List<OrderMainResponse> orderMainResponses = orderDto.stream()
.map(OrderMainResponse::new)
.collect(Collectors.toList());
return ResponseEntity.status(HttpStatus.OK)
.body(Result.createSuccessResult(orderMainResponses));
}
@Data @NoArgsConstructor @AllArgsConstructor
static class OrderMainRequest {
// yyyy-mm-dd 형태를 가지는 패턴 조사
@Pattern(regexp = "^(19|20)\\d{2}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[0-1])$",
message = "YYYY-MM-DD 형식에 맞게 작성되지 않았습니다.")
private String orderTime;
public LocalDate convertOrderTimeToLocalDate() {
return LocalDate.parse(orderTime, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}
}
@Data @NoArgsConstructor @AllArgsConstructor
static class OrderMainResponse {
private Long orderId;
private Long userId;
private String userName;
private List<OrderItemResponse> orderItemResponses;
private OrderStatus orderStatus;
private String orderTime;
public OrderMainResponse(OrderDto orderDto) {
List<OrderItemResponse> orderItemDtoList = orderDto.getOrderItemDtoList()
.stream()
.map(OrderItemResponse::new)
.collect(Collectors.toList());
this.orderId = orderDto.getId();
this.userId = orderDto.getUserId();
this.userName = orderDto.getUserName();
this.orderItemResponses = orderItemDtoList;
this.orderStatus = orderDto.getOrderStatus();
this.orderTime = orderDto.getOrderTime()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
}
@Data
static class OrderItemResponse {
private Long orderItemId;
private Long itemId;
private String itemName;
public OrderItemResponse(OrderItemDto orderItemDto) {
this.orderItemId = orderItemDto.getId();
this.itemId = orderItemDto.getItemId();
this.itemName = orderItemDto.getItemName();
}
}
}

View File

@@ -0,0 +1,45 @@
package com.justpickup.orderservice.domain.orderItem.dto;
import com.justpickup.orderservice.domain.orderItem.entity.OrderItem;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter @NoArgsConstructor
public class OrderItemDto {
private Long id;
private Long itemId;
private String itemName;
// private List<OrderItemOptionDto> orderItemOptionDtoList;
private Long price;
private Long count;
// == 생성 메소드 == //
@Builder
public OrderItemDto(Long id, Long itemId, Long price, Long count) {
this.id = id;
this.itemId = itemId;
this.price = price;
this.count = count;
}
public static OrderItemDto createPrimitiveField(OrderItem orderItem) {
return OrderItemDto.builder()
.id(orderItem.getId())
.itemId(orderItem.getItemId())
.price(orderItem.getPrice())
.count(orderItem.getCount())
.build();
}
// == 변수 변경 메소드 == //
public void setItemName(String itemName) {
this.itemName = itemName;
}
}

View File

@@ -1,6 +1,7 @@
package com.justpickup.orderservice.domain.orderItem.entity;
import com.justpickup.orderservice.domain.order.entity.Order;
import com.justpickup.orderservice.domain.orderItem.dto.OrderItemDto;
import com.justpickup.orderservice.domain.orderItemOption.entity.OrderItemOption;
import com.justpickup.orderservice.global.entity.BaseEntity;
import lombok.AccessLevel;

View File

@@ -0,0 +1,7 @@
package com.justpickup.orderservice.domain.orderItem.repository;
import com.justpickup.orderservice.domain.orderItem.entity.OrderItem;
import org.springframework.data.jpa.repository.JpaRepository;
public interface OrderItemRepository extends JpaRepository<OrderItem, Long> {
}

View File

@@ -0,0 +1,11 @@
package com.justpickup.orderservice.global.client.exception;
import com.justpickup.orderservice.global.exception.CustomException;
import org.springframework.http.HttpStatus;
public class FeignClientException extends CustomException {
public FeignClientException(HttpStatus status, String message) {
super(status, message);
}
}

View File

@@ -0,0 +1,38 @@
package com.justpickup.orderservice.global.client.exception;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.justpickup.orderservice.global.dto.Result;
import feign.Response;
import feign.codec.ErrorDecoder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
@RequiredArgsConstructor
@Slf4j
public class FeignClientExceptionErrorDecoder implements ErrorDecoder {
private final ObjectMapper objectMapper;
@Override
public Exception decode(String methodKey, Response response) {
String message = null;
if (response.body() != null) {
try {
Result result = objectMapper.readValue(response.body().asInputStream(), Result.class);
message = result.getMessage();
} catch (IOException e) {
String catchErrorMessage = "Error Deserializing response body from failed feign request response.";
log.warn(methodKey + catchErrorMessage, e);
return new FeignClientException(HttpStatus.INTERNAL_SERVER_ERROR, "고객센터로 문의해주세요.");
}
}
return new FeignClientException(HttpStatus.INTERNAL_SERVER_ERROR, message);
}
}

View File

@@ -0,0 +1,12 @@
package com.justpickup.orderservice.global.client.store;
import com.justpickup.orderservice.global.entity.Yn;
import lombok.Data;
@Data
public class GetItemResponse {
private Long id;
private String name;
private Yn salesYn;
private Long price;
}

View File

@@ -0,0 +1,13 @@
package com.justpickup.orderservice.global.client.store;
import com.justpickup.orderservice.global.dto.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name = "STORE-SERVICE", url = "127.0.0.1:8001/store-service")
public interface StoreClient {
@GetMapping("/item/{itemId}")
Result<GetItemResponse> getItem(@PathVariable("itemId") Long itemId);
}

View File

@@ -0,0 +1,10 @@
package com.justpickup.orderservice.global.client.user;
import lombok.Data;
@Data
public class GetCustomerResponse {
private Long userId;
private String userName;
private String phoneNumber;
}

View File

@@ -0,0 +1,13 @@
package com.justpickup.orderservice.global.client.user;
import com.justpickup.orderservice.global.dto.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name = "USER-SERVICE", url = "127.0.0.1:8001/user-service")
public interface UserClient {
@GetMapping("/customer/{userId}")
Result<GetCustomerResponse> getUser(@PathVariable("userId") Long userId);
}

View File

@@ -0,0 +1,14 @@
package com.justpickup.orderservice.global.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignClientConfig {
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}

View File

@@ -0,0 +1,22 @@
package com.justpickup.orderservice.global.config;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Configuration
@EnableJpaAuditing
public class QueryDslConfig {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(this.entityManager);
}
}

View File

@@ -0,0 +1,5 @@
package com.justpickup.orderservice.global.dto;
public enum Code {
SUCCESS, ERROR
}

View File

@@ -0,0 +1,36 @@
package com.justpickup.orderservice.global.dto;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data @NoArgsConstructor
public class Result<T> {
private Code code;
private String message;
private T data;
@Builder
public Result(Code code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public static Result createErrorResult(String message) {
return Result.builder()
.code(Code.ERROR)
.message(message)
.data(null)
.build();
}
// 해당 <T> 는 클래스의 T와 다름
public static <T> Result createSuccessResult(T data) {
return Result.builder()
.code(Code.SUCCESS)
.message("")
.data(data)
.build();
}
}

View File

@@ -0,0 +1,5 @@
package com.justpickup.orderservice.global.entity;
public enum Yn {
Y, N
}

View File

@@ -0,0 +1,17 @@
package com.justpickup.orderservice.global.exception;
import com.justpickup.orderservice.global.dto.Result;
import lombok.Getter;
import org.springframework.http.HttpStatus;
@Getter
public class CustomException extends RuntimeException {
private HttpStatus status;
private Result errorResult;
protected CustomException(HttpStatus status, String message) {
this.status = status;
this.errorResult = Result.createErrorResult(message);
}
}

View File

@@ -0,0 +1,55 @@
package com.justpickup.orderservice.global.exception;
import com.justpickup.orderservice.global.dto.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(CustomException.class)
public ResponseEntity customExceptionHandler(CustomException ce) {
HttpStatus status = ce.getStatus();
Result errorResult = ce.getErrorResult();
return ResponseEntity.status(status)
.body(errorResult);
}
@ExceptionHandler(BindException.class)
public ResponseEntity bindExceptionHandler(BindException exception) {
return getValidationErrorBody(exception);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException exception) {
return getValidationErrorBody(exception);
}
private ResponseEntity getValidationErrorBody(BindException exception) {
BindingResult bindingResult = exception.getBindingResult();
StringBuilder builder = new StringBuilder();
bindingResult.getFieldErrors()
.forEach(fieldError -> {
builder.append("[");
builder.append(fieldError.getField());
builder.append("](은)는 ");
builder.append(fieldError.getDefaultMessage());
builder.append(" 입력된 값: [");
builder.append(fieldError.getRejectedValue());
builder.append("]");
});
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(Result.createErrorResult(builder.toString()));
}
}

View File

@@ -26,6 +26,9 @@ dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j'
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
// https://mvnrepository.com/artifact/io.netty/netty-resolver-dns-native-macos
implementation 'io.netty:netty-resolver-dns-native-macos:4.1.68.Final:osx-aarch_64'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'

View File

@@ -11,3 +11,31 @@ eureka:
spring:
application:
name: owner-apigateway-service
cloud:
gateway:
routes:
- id: owner-frontend-service
uri: lb://OWNER-FRONTEND-SERVICE
predicates:
- Path=/owner-frontend-service/**
filters:
- RewritePath=/owner-frontend-service/(?<segment>.*),/$\{segment}
- id: order-service
uri: lb://ORDER-SERVCIE
predicates:
- Path=/order-service/**
filters:
- RewritePath=/order-service/(?<segment>.*),/$\{segment}
- id: store-service
uri: lb://STORE-SERVCIE
predicates:
- Path=/store-service/**
filters:
- RewritePath=/store-service/(?<segment>.*),/$\{segment}
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/user-service/**
filters:
- RewritePath=/user-service/(?<segment>.*),/$\{segment}

View File

@@ -2,8 +2,10 @@ package com.justpickup.ownerfrontendservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class OwnerFrontendServiceApplication {
public static void main(String[] args) {

View File

@@ -0,0 +1,15 @@
package com.justpickup.ownerfrontendservice.domain.order.web;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
@Slf4j
public class OrderController {
@GetMapping("/order")
public String order() {
return "/domain/order/order";
}
}

View File

@@ -0,0 +1,4 @@
const ownerApiGatewayIp = "http://127.0.0.1:8001/";
const url = {
orderService : ownerApiGatewayIp + "order-service/"
}

View File

@@ -0,0 +1,105 @@
<!DOCTYPE html>
<html lang="ko"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/layout}"
>
<head></head>
<div layout:fragment="content">
<!-- Page Heading -->
<h1 class="h3 mb-4 text-gray-800">주문</h1>
<!-- Date Section -->
<h5 class="h5 mb-4 text-gray-800">2022년 01년 14일</h5>
<!-- Card Section -->
<div class="row row-cols-md-3">
<!-- card start -->
<div class="col mb-4">
<div class="card">
<div class="card-header text-center font-weight-bold">
닉네임
</div>
<div class="card-body">
<h5 class="card-title">
<span class="d-inline-block text-truncate" style="max-width: 80%;">
메뉴1, 메뉴2, 메뉴3
</span>
</h5>
<p class="card-text">
<small class="text-muted">오전 11:03</small>
</p>
<a href="#" class="btn btn-outline-dark btn-sm">상세보기</a>
</div>
<div class="card-footer rounded bg-primary text-white text-center" role="button">
주문접수하기
</div>
</div>
</div>
<!-- card end -->
<!-- card start -->
<div class="col mb-4">
<div class="card">
<div class="card-header text-center font-weight-bold">
닉네임
</div>
<div class="card-body">
<h5 class="card-title">
<span class="d-inline-block text-truncate" style="max-width: 80%;">
메뉴1, 메뉴2, 메뉴3, 메뉴4, 메뉴5, 메뉴6
</span>
</h5>
<p class="card-text">
<small class="text-muted">오후 04:03</small>
</p>
<a href="#" class="btn btn-outline-dark btn-sm">상세보기</a>
</div>
<div class="card-footer rounded bg-dark text-white text-center" role="button">
수령완료
</div>
</div>
</div>
<!-- card end -->
<!-- card start -->
<div class="col mb-4">
<div class="card">
<div class="card-header text-center font-weight-bold">
닉네임
</div>
<div class="card-body">
<h5 class="card-title">
<span class="d-inline-block text-truncate" style="max-width: 80%;">
메뉴1, 메뉴2
</span>
</h5>
<p class="card-text">
<small class="text-muted">오후 10:45</small>
</p>
<a href="#" class="btn btn-outline-dark btn-sm">상세보기</a>
</div>
<div class="card-footer rounded bg-success text-white text-center" role="button">
조리완료
</div>
</div>
</div>
<!-- card end -->
</div>
<script type="text/javascript">
function init() {
}
function setEvent() {
}
</script>
</div>
</html>

View File

@@ -118,7 +118,7 @@
</h6>
<a class="dropdown-item d-flex align-items-center" href="#">
<div class="dropdown-list-image mr-3">
<img class="rounded-circle" src="/img/undraw_profile_1.svg"
<img class="rounded-circle" src="img/undraw_profile_1.svg"
alt="...">
<div class="status-indicator bg-success"></div>
</div>
@@ -130,7 +130,7 @@
</a>
<a class="dropdown-item d-flex align-items-center" href="#">
<div class="dropdown-list-image mr-3">
<img class="rounded-circle" src="/img/undraw_profile_2.svg"
<img class="rounded-circle" src="img/undraw_profile_2.svg"
alt="...">
<div class="status-indicator"></div>
</div>
@@ -142,7 +142,7 @@
</a>
<a class="dropdown-item d-flex align-items-center" href="#">
<div class="dropdown-list-image mr-3">
<img class="rounded-circle" src="/img/undraw_profile_3.svg"
<img class="rounded-circle" src="img/undraw_profile_3.svg"
alt="...">
<div class="status-indicator bg-warning"></div>
</div>
@@ -176,7 +176,7 @@
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="mr-2 d-none d-lg-inline text-gray-600 small">Douglas McGee</span>
<img class="img-profile rounded-circle"
src="/img/undraw_profile.svg">
src="img/undraw_profile.svg">
</a>
<!-- Dropdown - User Information -->
<div class="dropdown-menu dropdown-menu-right shadow animated--grow-in"

View File

@@ -11,13 +11,13 @@
<title>Just Pick-up : 점주용</title>
<!-- Custom fonts for this template-->
<link href="/vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css">
<link href="vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css">
<link
href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i"
rel="stylesheet">
<!-- Custom styles for this template-->
<link href="/css/sb-admin-2.min.css" rel="stylesheet">
<link href="css/sb-admin-2.min.css" rel="stylesheet">
</head>
<body id="page-top">
@@ -43,18 +43,19 @@
</div>
<!-- Bootstrap core JavaScript-->
<script src="/vendor/jquery/jquery.min.js"></script>
<script src="/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="vendor/jquery/jquery.min.js"></script>
<script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- Core plugin JavaScript-->
<script src="/vendor/jquery-easing/jquery.easing.min.js"></script>
<script src="vendor/jquery-easing/jquery.easing.min.js"></script>
<!-- Custom scripts for all pages-->
<script src="/js/sb-admin-2.min.js"></script>
<script src="js/sb-admin-2.min.js"></script>
<!-- Page level plugins -->
<script src="/vendor/chart.js/Chart.min.js"></script>
<script src="vendor/chart.js/Chart.min.js"></script>
<script src="common.js"></script>
<script>
$(function() {
init();

View File

@@ -0,0 +1,49 @@
package com.justpickup.storeservice.domain.item.dto;
import com.justpickup.storeservice.domain.item.entity.Item;
import com.justpickup.storeservice.global.entity.Yn;
import lombok.Builder;
import lombok.Getter;
@Getter
public class ItemDto {
private Long id;
private String name;
private Yn salesYn;
private Long price;
/*
private PhotoDto photoDto;
private CategoryDto categoryDto;
private StoreDto storeDto;
private List<ItemOptionDto> itemOptionDtoList;
*/
// == 생성 메소드 == //
@Builder
public ItemDto(Long id, String name, Yn salesYn, Long price) {
this.id = id;
this.name = name;
this.salesYn = salesYn;
this.price = price;
}
public static ItemDto createItemDto(Item item) {
return ItemDto.builder()
.id(item.getId())
.name(item.getName())
.price(item.getPrice())
.salesYn(item.getSalesYn())
.build();
}
// TODO: 2022/02/03 queryDsl 쿼리 생성 시 구현 필요
// public static ItemDto createFullItemDto(Item item) {
// return null
// }
}

View File

@@ -0,0 +1,7 @@
package com.justpickup.storeservice.domain.item.repository;
import com.justpickup.storeservice.domain.item.entity.Item;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ItemRepository extends JpaRepository<Item, Long> {
}

View File

@@ -0,0 +1,8 @@
package com.justpickup.storeservice.domain.item.service;
import com.justpickup.storeservice.domain.item.dto.ItemDto;
public interface ItemService {
ItemDto findItemByItemId(Long itemId);
}

View File

@@ -0,0 +1,29 @@
package com.justpickup.storeservice.domain.item.service;
import com.justpickup.storeservice.domain.item.dto.ItemDto;
import com.justpickup.storeservice.domain.item.entity.Item;
import com.justpickup.storeservice.domain.item.repository.ItemRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.NoSuchElementException;
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
@Slf4j
public class ItemServiceImpl implements ItemService {
private final ItemRepository itemRepository;
@Override
public ItemDto findItemByItemId(Long itemId) {
Item findItem = itemRepository.findById(itemId)
.orElseThrow(NoSuchElementException::new);
return ItemDto.createItemDto(findItem);
}
}

View File

@@ -0,0 +1,48 @@
package com.justpickup.storeservice.domain.item.web;
import com.justpickup.storeservice.domain.item.dto.ItemDto;
import com.justpickup.storeservice.domain.item.service.ItemService;
import com.justpickup.storeservice.global.dto.Result;
import com.justpickup.storeservice.global.entity.Yn;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
@RequestMapping("/item")
public class ItemController {
private final ItemService itemService;
@GetMapping("/{itemId}")
public ResponseEntity getItem(@PathVariable("itemId") Long itemId) {
ItemDto itemByItemId = itemService.findItemByItemId(itemId);
GetItemResponse getItemResponse = new GetItemResponse(itemByItemId);
return ResponseEntity.status(HttpStatus.OK)
.body(Result.createSuccessResult(getItemResponse));
}
@Data @NoArgsConstructor @AllArgsConstructor
static class GetItemResponse {
private Long id;
private String name;
private Yn salesYn;
private Long price;
public GetItemResponse(ItemDto itemDto) {
this.id = itemDto.getId();
this.name = itemDto.getName();
this.salesYn = itemDto.getSalesYn();
this.price = itemDto.getPrice();
}
}
}

View File

@@ -0,0 +1,5 @@
package com.justpickup.storeservice.global.dto;
public enum Code {
SUCCESS, ERROR
}

View File

@@ -0,0 +1,36 @@
package com.justpickup.storeservice.global.dto;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data @NoArgsConstructor
public class Result<T> {
private Code code;
private String message;
private T data;
@Builder
public Result(Code code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public static Result createErrorResult(String message) {
return Result.builder()
.code(Code.ERROR)
.message(message)
.data(null)
.build();
}
// 해당 <T> 는 클래스의 T와 다름
public static <T> Result createSuccessResult(T data) {
return Result.builder()
.code(Code.SUCCESS)
.message("")
.data(data)
.build();
}
}

View File

@@ -0,0 +1,17 @@
package com.justpickup.storeservice.global.exception;
import com.justpickup.storeservice.global.dto.Result;
import lombok.Getter;
import org.springframework.http.HttpStatus;
@Getter
public class CustomException extends RuntimeException {
private HttpStatus status;
private Result errorResult;
protected CustomException(HttpStatus status, String message) {
this.status = status;
this.errorResult = Result.createErrorResult(message);
}
}

View File

@@ -0,0 +1,55 @@
package com.justpickup.storeservice.global.exception;
import com.justpickup.storeservice.global.dto.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(CustomException.class)
public ResponseEntity customExceptionHandler(CustomException ce) {
HttpStatus status = ce.getStatus();
Result errorResult = ce.getErrorResult();
return ResponseEntity.status(status)
.body(errorResult);
}
@ExceptionHandler(BindException.class)
public ResponseEntity bindExceptionHandler(BindException exception) {
return getValidationErrorBody(exception);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException exception) {
return getValidationErrorBody(exception);
}
private ResponseEntity getValidationErrorBody(BindException exception) {
BindingResult bindingResult = exception.getBindingResult();
StringBuilder builder = new StringBuilder();
bindingResult.getFieldErrors()
.forEach(fieldError -> {
builder.append("[");
builder.append(fieldError.getField());
builder.append("](은)는 ");
builder.append(fieldError.getDefaultMessage());
builder.append(" 입력된 값: [");
builder.append(fieldError.getRejectedValue());
builder.append("]");
});
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(Result.createErrorResult(builder.toString()));
}
}

View File

@@ -0,0 +1,12 @@
package com.justpickup.userservice.domain.user.dto;
import com.justpickup.userservice.domain.user.entity.Customer;
import lombok.Getter;
@Getter
public class CustomerDto extends UserDto {
public CustomerDto(Customer customer) {
super(customer);
}
}

View File

@@ -0,0 +1,20 @@
package com.justpickup.userservice.domain.user.dto;
import com.justpickup.userservice.domain.user.entity.Customer;
import lombok.Getter;
@Getter
public class UserDto {
private Long id;
private String password;
private String name;
private String phoneNumber;
// == 생성 메소드 == //
public UserDto(Customer customer) {
this.id = customer.getId();
this.password = customer.getPassword();
this.name = customer.getName();
this.phoneNumber = customer.getPhoneNumber();
}
}

View File

@@ -0,0 +1,11 @@
package com.justpickup.userservice.domain.user.exception;
import com.justpickup.userservice.global.exception.CustomException;
import org.springframework.http.HttpStatus;
public class NotExistUserException extends CustomException {
public NotExistUserException(String message) {
super(HttpStatus.CONFLICT, message);
}
}

View File

@@ -0,0 +1,7 @@
package com.justpickup.userservice.domain.user.repository;
import com.justpickup.userservice.domain.user.entity.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CustomerRepository extends JpaRepository<Customer, Long> {
}

View File

@@ -0,0 +1,7 @@
package com.justpickup.userservice.domain.user.service;
import com.justpickup.userservice.domain.user.dto.CustomerDto;
public interface UserService {
CustomerDto findCustomerByUserId(Long userId);
}

View File

@@ -0,0 +1,27 @@
package com.justpickup.userservice.domain.user.service;
import com.justpickup.userservice.domain.user.dto.CustomerDto;
import com.justpickup.userservice.domain.user.entity.Customer;
import com.justpickup.userservice.domain.user.exception.NotExistUserException;
import com.justpickup.userservice.domain.user.repository.CustomerRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
@Slf4j
public class UserServiceImpl implements UserService {
private final CustomerRepository customerRepository;
@Override
public CustomerDto findCustomerByUserId(Long userId) {
Customer customer = customerRepository.findById(userId)
.orElseThrow(() -> new NotExistUserException("존재하지 않는 사용자 입니다."));
return new CustomerDto(customer);
}
}

View File

@@ -0,0 +1,48 @@
package com.justpickup.userservice.domain.user.web;
import com.justpickup.userservice.domain.user.dto.CustomerDto;
import com.justpickup.userservice.domain.user.service.UserService;
import com.justpickup.userservice.global.dto.Result;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.RestController;
@RestController
@RequiredArgsConstructor
@Slf4j
public class UserController {
private final UserService userService;
@GetMapping("/customer/{userId}")
public ResponseEntity getCustomer(@PathVariable("userId") Long userId) {
CustomerDto customerDto = userService.findCustomerByUserId(userId);
GetCustomerResponse getCustomerResponse = new GetCustomerResponse(customerDto);
return ResponseEntity.status(HttpStatus.OK)
.body(Result.createSuccessResult(getCustomerResponse));
}
@Data @NoArgsConstructor @AllArgsConstructor
static class GetCustomerResponse {
private Long userId;
private String userName;
private String phoneNumber;
public GetCustomerResponse(CustomerDto customerDto) {
this.userId = customerDto.getId();
this.userName = customerDto.getName();
this.phoneNumber = customerDto.getPhoneNumber();
}
}
}

View File

@@ -0,0 +1,5 @@
package com.justpickup.userservice.global.dto;
public enum Code {
SUCCESS, ERROR
}

View File

@@ -0,0 +1,36 @@
package com.justpickup.userservice.global.dto;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data @NoArgsConstructor
public class Result<T> {
private Code code;
private String message;
private T data;
@Builder
public Result(Code code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public static Result createErrorResult(String message) {
return Result.builder()
.code(Code.ERROR)
.message(message)
.data(null)
.build();
}
// 해당 <T> 는 클래스의 T와 다름
public static <T> Result createSuccessResult(T data) {
return Result.builder()
.code(Code.SUCCESS)
.message("")
.data(data)
.build();
}
}

View File

@@ -0,0 +1,17 @@
package com.justpickup.userservice.global.exception;
import com.justpickup.userservice.global.dto.Result;
import lombok.Getter;
import org.springframework.http.HttpStatus;
@Getter
public class CustomException extends RuntimeException {
private HttpStatus status;
private Result errorResult;
protected CustomException(HttpStatus status, String message) {
this.status = status;
this.errorResult = Result.createErrorResult(message);
}
}

View File

@@ -0,0 +1,55 @@
package com.justpickup.userservice.global.exception;
import com.justpickup.userservice.global.dto.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(CustomException.class)
public ResponseEntity customExceptionHandler(CustomException ce) {
HttpStatus status = ce.getStatus();
Result errorResult = ce.getErrorResult();
return ResponseEntity.status(status)
.body(errorResult);
}
@ExceptionHandler(BindException.class)
public ResponseEntity bindExceptionHandler(BindException exception) {
return getValidationErrorBody(exception);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException exception) {
return getValidationErrorBody(exception);
}
private ResponseEntity getValidationErrorBody(BindException exception) {
BindingResult bindingResult = exception.getBindingResult();
StringBuilder builder = new StringBuilder();
bindingResult.getFieldErrors()
.forEach(fieldError -> {
builder.append("[");
builder.append(fieldError.getField());
builder.append("](은)는 ");
builder.append(fieldError.getDefaultMessage());
builder.append(" 입력된 값: [");
builder.append(fieldError.getRejectedValue());
builder.append("]");
});
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(Result.createErrorResult(builder.toString()));
}
}