Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ repositories {
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-aop'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation group: 'com.auth0', name: 'java-jwt', version: '4.3.0'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
Expand Down
15 changes: 9 additions & 6 deletions src/main/java/shop/mtcoding/metamall/MetamallApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,27 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import shop.mtcoding.metamall.model.orderproduct.OrderProduct;
import shop.mtcoding.metamall.model.orderproduct.OrderProductRepository;
import shop.mtcoding.metamall.model.ordersheet.OrderSheet;
import shop.mtcoding.metamall.model.ordersheet.OrderSheetRepository;
import shop.mtcoding.metamall.model.order.product.OrderProductRepository;
import shop.mtcoding.metamall.model.order.sheet.OrderSheetRepository;
import shop.mtcoding.metamall.model.product.ProductRepository;
import shop.mtcoding.metamall.model.user.User;
import shop.mtcoding.metamall.model.user.UserRepository;

import java.util.Arrays;

@SpringBootApplication
public class MetamallApplication {

// CommandLineRunner 는 Spring Boot 가 최초 실행될 때 return 값을 실행시켜 줌
@Bean
CommandLineRunner initData(UserRepository userRepository, ProductRepository productRepository, OrderProductRepository orderProductRepository, OrderSheetRepository orderSheetRepository){
return (args)->{
// 여기에서 save 하면 됨.
// bulk Collector는 saveAll 하면 됨.
User ssar = User.builder().username("ssar").password("1234").email("[email protected]").role("USER").build();
userRepository.save(ssar);
User ssar = User.builder().username("ssar").password("1234").email("[email protected]").role("USER").status(true).build();
User seller = User.builder().username("seller").password("1234").email("[email protected]").role("SELLER").status(true).build();
User admin = User.builder().username("admin").password("1234").email("[email protected]").role("ADMIN").status(true).build();
userRepository.saveAll(Arrays.asList(ssar, seller, admin)); // 벌크 컬렉터로 save 시에는 saveAll 사용(컬렉션 들어가야 함)
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ public class FilterRegisterConfig {
public FilterRegistrationBean<?> jwtVerifyFilterAdd() {
FilterRegistrationBean<JwtVerifyFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new JwtVerifyFilter());
registration.addUrlPatterns("/user/*");
registration.addUrlPatterns("/users/*"); // 토큰
registration.addUrlPatterns("/products/*"); // 토큰
registration.addUrlPatterns("/orders/*"); // 토큰
registration.addUrlPatterns("/admin/*"); // ADMIN
registration.addUrlPatterns("/seller/*"); // ADMIN, SELLER
registration.setOrder(1);
return registration;
}
Expand Down
49 changes: 49 additions & 0 deletions src/main/java/shop/mtcoding/metamall/config/MyWebMvcConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package shop.mtcoding.metamall.config;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import shop.mtcoding.metamall.core.interceptor.MyAdminInterceptor;
import shop.mtcoding.metamall.core.interceptor.MySellerInterceptor;
import shop.mtcoding.metamall.core.resolver.MySessionArgumentResolver;

import java.util.List;

@RequiredArgsConstructor
@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {

private final MyAdminInterceptor adminInterceptor;
private final MySellerInterceptor sellerInterceptor;
private final MySessionArgumentResolver mySessionArgumentResolver;

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedHeaders("*")
.allowedMethods("*") // GET, POST, PUT, DELETE (Javascript 요청 허용)
.allowedOriginPatterns("*") // 모든 IP 주소 허용 (프론트 앤드 IP만 허용하게 변경해야함. * 안됨)
.allowCredentials(true)
.exposedHeaders("Authorization"); // 옛날에는 디폴트로 브라우저에 노출되어 있었는데 지금은 아님
}


// AOP는 매개변수 값 확인해서 권한 비교해야할 때 사용
// Interceptor는 세션 권한으로 체크할 때 사용
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(adminInterceptor)
.addPathPatterns("/admin/**");

registry.addInterceptor(sellerInterceptor)
.addPathPatterns("/seller/**");
}

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(mySessionArgumentResolver);
}
}
18 changes: 0 additions & 18 deletions src/main/java/shop/mtcoding/metamall/config/WebMvcConfig.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package shop.mtcoding.metamall.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;
import shop.mtcoding.metamall.core.exception.Exception400;
import shop.mtcoding.metamall.dto.ResponseDTO;
import shop.mtcoding.metamall.dto.user.UserRequest;
import shop.mtcoding.metamall.model.user.User;
import shop.mtcoding.metamall.model.user.UserRepository;

import javax.validation.Valid;

/**
* 권한 변경
*/
@RequiredArgsConstructor
@RestController
public class AdminController {
private final UserRepository userRepository;

@Transactional // 트랜잭션이 시작되지 않으면 강제로 em.flush() 를 할 수 없고, 더티체킹도 할 수 없다. (원래는 서비스에서)
@PutMapping("/admin/user/{id}/role")
public ResponseEntity<?> updateRole(@PathVariable Long id, @RequestBody @Valid UserRequest.RoleUpdateDTO roleUpdateDTO, Errors errors) {
User userPS = userRepository.findById(id)
.orElseThrow(()-> new Exception400("id", "해당 유저를 찾을 수 없습니다"));
userPS.updateRole(roleUpdateDTO.getRole());

ResponseDTO<?> responseDto = new ResponseDTO<>();
return ResponseEntity.ok().body(responseDto);
}
}
141 changes: 141 additions & 0 deletions src/main/java/shop/mtcoding/metamall/controller/OrderController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package shop.mtcoding.metamall.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;
import shop.mtcoding.metamall.core.annotation.MySessionStore;
import shop.mtcoding.metamall.core.exception.Exception400;
import shop.mtcoding.metamall.core.exception.Exception403;
import shop.mtcoding.metamall.core.session.SessionUser;
import shop.mtcoding.metamall.dto.ResponseDTO;
import shop.mtcoding.metamall.dto.order.OrderRequest;
import shop.mtcoding.metamall.model.order.product.OrderProduct;
import shop.mtcoding.metamall.model.order.product.OrderProductRepository;
import shop.mtcoding.metamall.model.order.sheet.OrderSheet;
import shop.mtcoding.metamall.model.order.sheet.OrderSheetRepository;
import shop.mtcoding.metamall.model.product.Product;
import shop.mtcoding.metamall.model.product.ProductRepository;
import shop.mtcoding.metamall.model.user.User;
import shop.mtcoding.metamall.model.user.UserRepository;

import javax.validation.Valid;
import java.util.List;

/**
* 주문하기(고객), 주문목록보기(고객), 주문목록보기(판매자), 주문취소하기(고객), 주문취소하기(판매자)
*/
@RequiredArgsConstructor
@RestController
public class OrderController {
private final OrderProductRepository orderProductRepository;
private final OrderSheetRepository orderSheetRepository;
private final ProductRepository productRepository;
private final UserRepository userRepository;

@Transactional
@PostMapping("/orders")
public ResponseEntity<?> save(@RequestBody @Valid OrderRequest.SaveDTO saveDTO, Errors errors, @MySessionStore SessionUser sessionUser) {
// 1. 세션값으로 유저 찾기
User userPS = userRepository.findById(sessionUser.getId())
.orElseThrow(
() -> new Exception400("id", "해당 유저를 찾을 수 없습니다")
);

// 2. 상품 찾기
List<Product> productListPS =
productRepository.findAllById(saveDTO.getIds());

// 3. 주문 상품
List<OrderProduct> orderProductListPS = saveDTO.toEntity(productListPS);

// 4. 주문서 만들기
Integer totalPrice = orderProductListPS.stream().mapToInt((orderProduct)-> orderProduct.getOrderPrice()).sum();
OrderSheet orderSheet = OrderSheet.builder()
.user(userPS)
.totalPrice(totalPrice)
.build();
OrderSheet orderSheetPS = orderSheetRepository.save(orderSheet);

// 5. 주문서에 상품추가하고 재고감소하기
orderProductListPS.stream().forEach(
(orderProductPS -> {
orderSheetPS.addOrderProduct(orderProductPS);
orderProductPS.getProduct().updateQty(orderProductPS.getCount());
})
);

// 6. 응답하기
ResponseDTO<?> responseDto = new ResponseDTO<>().data(orderSheetPS);
return ResponseEntity.ok().body(responseDto);
}

// 유저 주문서 조회
@GetMapping("/orders")
public ResponseEntity<?> findByUserId(@MySessionStore SessionUser sessionUser){
List<OrderSheet> orderSheetListPS = orderSheetRepository.findByUserId(sessionUser.getId());
ResponseDTO<?> responseDto = new ResponseDTO<>().data(orderSheetListPS);
return ResponseEntity.ok().body(responseDto);
}

// 그림 설명 필요!!
// 배달의 민족은 하나의 판매자에게서만 주문을 할 수 있다. (다른 판매자의 상품이 담기면, 하나만 담을 수 있게 로직이 변한다)
// 쇼핑몰은 여러 판매자에게서 주문을 할 수 있다.

// 판매자 주문서 조회
@GetMapping("/seller/orders")
public ResponseEntity<?> findBySellerId(){
// 판매자는 한명이기 때문에 orderProductRepository.findAll() 해도 된다.
List<OrderSheet> orderSheetListPS = orderSheetRepository.findAll();
ResponseDTO<?> responseDto = new ResponseDTO<>().data(orderSheetListPS);
return ResponseEntity.ok().body(responseDto);
}

// 유저 주문 취소
@DeleteMapping("/orders/{id}")
public ResponseEntity<?> delete(@PathVariable Long id, @MySessionStore SessionUser sessionUser){
// 1. 주문서 찾기
OrderSheet orderSheetPS = orderSheetRepository.findById(id).orElseThrow(
()-> new Exception400("id", "해당 주문을 찾을 수 없습니다")
);

// 2. 해당 주문서의 주인 여부 확인
if(!orderSheetPS.getUser().getId().equals(sessionUser.getId())){
throw new Exception403("권한이 없습니다");
}

// 3. 재고 변경하기
orderSheetPS.getOrderProductList().stream().forEach(orderProduct -> {
orderProduct.getProduct().rollbackQty(orderProduct.getCount());
});

// 4. 주문서 삭제하기 (casecade 옵션)
orderSheetRepository.delete(orderSheetPS);

// 5. 응답하기
ResponseDTO<?> responseDto = new ResponseDTO<>();
return ResponseEntity.ok().body(responseDto);
}

// 판매자 주문 취소
@DeleteMapping("/seller/orders/{id}")
public ResponseEntity<?> deleteSeller(@PathVariable Long id){
// 1. 주문서 찾기
OrderSheet orderSheetPS = orderSheetRepository.findById(id).orElseThrow(
()-> new Exception400("id", "해당 주문을 찾을 수 없습니다")
);

// 2. 재고 변경하기
orderSheetPS.getOrderProductList().stream().forEach(orderProduct -> {
orderProduct.getProduct().rollbackQty(orderProduct.getCount());
});

// 3. 주문서 삭제하기 (casecade 옵션)
orderSheetRepository.delete(orderSheetPS);

// 4. 응답하기
ResponseDTO<?> responseDto = new ResponseDTO<>();
return ResponseEntity.ok().body(responseDto);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package shop.mtcoding.metamall.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;
import shop.mtcoding.metamall.core.annotation.MySessionStore;
import shop.mtcoding.metamall.core.exception.Exception400;
import shop.mtcoding.metamall.core.exception.Exception401;
import shop.mtcoding.metamall.core.session.SessionUser;
import shop.mtcoding.metamall.dto.ResponseDTO;
import shop.mtcoding.metamall.dto.product.ProductRequest;
import shop.mtcoding.metamall.model.product.Product;
import shop.mtcoding.metamall.model.product.ProductRepository;
import shop.mtcoding.metamall.model.user.User;
import shop.mtcoding.metamall.model.user.UserRepository;

import javax.servlet.http.HttpSession;
import javax.validation.Valid;

/**
* 상품등록, 상품목록보기, 상품상세보기, 상품수정하기, 상품삭제하기
*/
@RequiredArgsConstructor
@RestController
public class ProductController {
private final ProductRepository productRepository;
private final UserRepository userRepository;
private final HttpSession session;

@PostMapping("/seller/products")
public ResponseEntity<?> save(
@RequestBody @Valid ProductRequest.SaveDTO saveDTO, Errors errors,
@MySessionStore SessionUser sessionUser){
// 1. 판매자 찾기
User sellerPS = userRepository.findById(sessionUser.getId())
.orElseThrow(
()-> new Exception400("id", "판매자를 찾을 수 없습니다")
);

// 2. 상품 등록하기
Product productPS = productRepository.save(saveDTO.toEntity(sellerPS));
ResponseDTO<?> responseDto = new ResponseDTO<>().data(productPS);
return ResponseEntity.ok().body(responseDto);
}

@GetMapping("/products")
public ResponseEntity<?> findAll(@PageableDefault(size = 10, page = 0, direction = Sort.Direction.DESC) Pageable pageable){
// 1. 상품 찾기
Page<Product> productPagePS = productRepository.findAll(pageable);

// 2. 응답하기
ResponseDTO<?> responseDto = new ResponseDTO<>().data(productPagePS);
return ResponseEntity.ok().body(responseDto);
}

@GetMapping("/products/{id}")
public ResponseEntity<?> findById(@PathVariable Long id){
// 1. 상품 찾기
Product productPS = productRepository.findById(id).orElseThrow(()-> new Exception400("id", "해당 상품을 찾을 수 없습니다"));

// 2. 응답하기
ResponseDTO<?> responseDto = new ResponseDTO<>().data(productPS);
return ResponseEntity.ok().body(responseDto);
}

@Transactional // 더티체킹 하고 싶다면 붙이기!!
@PutMapping("/seller/products/{id}")
public ResponseEntity<?> update(@PathVariable Long id, @RequestBody @Valid ProductRequest.UpdateDTO updateDTO, Errors errors){
// 1. 상품 찾기
Product productPS = productRepository.findById(id).orElseThrow(()-> new Exception400("id", "해당 상품을 찾을 수 없습니다"));

// 2. Update 더티체킹
productPS.update(updateDTO.getName(), updateDTO.getPrice(), updateDTO.getQty());

// 3. 응답하기
ResponseDTO<?> responseDto = new ResponseDTO<>().data(productPS);
return ResponseEntity.ok().body(responseDto);
}

@DeleteMapping("/seller/products/{id}")
public ResponseEntity<?> deleteById(@PathVariable Long id){
Product productPS = productRepository.findById(id).orElseThrow(()-> new Exception400("id", "해당 상품을 찾을 수 없습니다"));
productRepository.delete(productPS);
ResponseDTO<?> responseDto = new ResponseDTO<>();
return ResponseEntity.ok().body(responseDto);
}
}
Loading