From 785523dd4f318170bae34e154b61d6af1bcb4bfd Mon Sep 17 00:00:00 2001 From: ju-ei8ht Date: Sun, 9 Apr 2023 23:56:17 +0900 Subject: [PATCH 1/5] =?UTF-8?q?toy-project1:=20test=20code=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EC=98=88=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 56 ++++++++ build.gradle | 2 + .../metamall/MetamallApplication.java | 30 +++- .../metamall/config/FilterRegisterConfig.java | 11 +- .../metamall/config/WebMvcConfig.java | 17 +++ .../config/interceptor/LoginInterceptor.java | 21 +++ .../metamall/controller/OrderController.java | 97 +++++++++++++ .../controller/ProductController.java | 77 ++++++++++ .../metamall/controller/UserController.java | 42 +++--- .../metamall/core/advice/CheckAdvice.java | 77 ++++++++++ .../metamall/core/advice/LogAdvice.java | 69 +++++++++ .../metamall/core/advice/LoginAdvice.java | 58 ++++++++ .../core/advice/MyExceptionAdvice.java | 5 - .../metamall/core/annotation/Auth.java | 11 ++ .../metamall/core/annotation/PwdCk.java | 11 ++ .../metamall/core/annotation/RoleCk.java | 21 +++ .../metamall/core/filter/JwtVerifyFilter.java | 2 +- .../metamall/core/session/LoginUser.java | 4 +- .../mtcoding/metamall/dto/ResponseDto.java | 1 + .../metamall/dto/order/OrderRequest.java | 32 +++++ .../metamall/dto/product/ProductRequest.java | 25 ++++ .../metamall/dto/user/UserRequest.java | 46 +++++- .../model/orderproduct/OrderProduct.java | 18 ++- .../orderproduct/OrderProductRepository.java | 5 + .../metamall/model/ordersheet/OrderSheet.java | 14 +- .../ordersheet/OrderSheetRepository.java | 20 +++ .../metamall/model/product/Product.java | 17 ++- .../model/product/ProductRepository.java | 13 ++ .../mtcoding/metamall/model/user/User.java | 14 +- .../metamall/model/user/UserRepository.java | 2 + .../controller/OrderControllerTest.java | 136 ++++++++++++++++++ .../controller/ProductControllerTest.java | 136 ++++++++++++++++++ .../controller/UserControllerTest.java | 74 ++++++++++ .../ordersheet/OrderSheetRepositoryTest.java | 75 ++++++++++ .../model/product/ProductRepositoryTest.java | 12 ++ .../model/user/UserRepositoryTest.java | 12 ++ 36 files changed, 1212 insertions(+), 51 deletions(-) create mode 100644 README.md create mode 100644 src/main/java/shop/mtcoding/metamall/config/interceptor/LoginInterceptor.java create mode 100644 src/main/java/shop/mtcoding/metamall/controller/OrderController.java create mode 100644 src/main/java/shop/mtcoding/metamall/controller/ProductController.java create mode 100644 src/main/java/shop/mtcoding/metamall/core/advice/CheckAdvice.java create mode 100644 src/main/java/shop/mtcoding/metamall/core/advice/LogAdvice.java create mode 100644 src/main/java/shop/mtcoding/metamall/core/advice/LoginAdvice.java create mode 100644 src/main/java/shop/mtcoding/metamall/core/annotation/Auth.java create mode 100644 src/main/java/shop/mtcoding/metamall/core/annotation/PwdCk.java create mode 100644 src/main/java/shop/mtcoding/metamall/core/annotation/RoleCk.java create mode 100644 src/main/java/shop/mtcoding/metamall/dto/order/OrderRequest.java create mode 100644 src/main/java/shop/mtcoding/metamall/dto/product/ProductRequest.java create mode 100644 src/test/java/shop/mtcoding/metamall/controller/OrderControllerTest.java create mode 100644 src/test/java/shop/mtcoding/metamall/controller/ProductControllerTest.java create mode 100644 src/test/java/shop/mtcoding/metamall/controller/UserControllerTest.java create mode 100644 src/test/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepositoryTest.java create mode 100644 src/test/java/shop/mtcoding/metamall/model/product/ProductRepositoryTest.java create mode 100644 src/test/java/shop/mtcoding/metamall/model/user/UserRepositoryTest.java diff --git a/README.md b/README.md new file mode 100644 index 0000000..0176619 --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +# 상품주문서비스 토이프로젝트 +___ +## DATE +2023.04.05 ~ 2023.04.09 +___ +## STACKS + +![Spring Boot](https://img.shields.io/badge/Spring_Boot-F2F4F9?style=for-the-badge&logo=spring-boot) +![JWT](https://img.shields.io/badge/JWT-000000?style=for-the-badge&logo=JSON%20web%20tokens&logoColor=white) +![Git](https://img.shields.io/badge/GIT-E44C30?style=for-the-badge&logo=git&logoColor=white) +![GitHub](https://img.shields.io/badge/GitHub-100000?style=for-the-badge&logo=github&logoColor=white) + +### DEPENDENCIES +``` +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' +compileOnly 'org.projectlombok:lombok' +developmentOnly 'org.springframework.boot:spring-boot-devtools' +runtimeOnly 'com.h2database:h2' +annotationProcessor 'org.projectlombok:lombok' +testImplementation 'org.springframework.boot:spring-boot-starter-test' +``` +___ +## 기능 +___ +### 회원가입 +### 로그인 +- jwt 응답 +### 상품등록 +- 인증필요, 판매자권한 +### 상풍목록보기 +- 인증필요, 권한없음 +### 상품상세보기 +- 인증필요, 권한없음 +### 상품수정하기 +- 인증필요, 판매자권한 +### 주문하기 +- 인증필요, 고객권한 +### 주문목록보기 +- 인증필요, 고객권한(본인주문), 판매자권한(본인주문), 관리자권한(모든주문) +### 주문취소하기 +- CasCase 활용 +- 인증필요, 고객권한, 판매자권한 +___ +## ERD +[ERD](https://dbdiagram.io/d/642e523a5758ac5f172722f4) +___ +## TREE +___ +## API +| Page | Action | Method | URL | Request | Response | 비고 | +|------|--------| ------ |----------------|------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------| --- | +| 회원가입 | 회원가입 | POST | /api/user/join | {
"username":"username",
"password":"password",

"password_check":"passsword",
"email":"email@email.com",
"role":"USER"
} | {"status":200,
"msg":"성공",
"data":{...}
} | | +| 로그인 | 로그인 | POST | /api/user/login | {
"username":"username",
"password":"password"
} | {"status":200,
"msg":"성공",
"data":{...}
|| + diff --git a/build.gradle b/build.gradle index 4943187..cca989c 100644 --- a/build.gradle +++ b/build.gradle @@ -22,6 +22,8 @@ dependencies { 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' + implementation 'org.springframework.boot:spring-boot-starter-aop' + implementation 'org.springframework.boot:spring-boot-starter-validation' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' runtimeOnly 'com.h2database:h2' diff --git a/src/main/java/shop/mtcoding/metamall/MetamallApplication.java b/src/main/java/shop/mtcoding/metamall/MetamallApplication.java index 487bb62..978f10a 100644 --- a/src/main/java/shop/mtcoding/metamall/MetamallApplication.java +++ b/src/main/java/shop/mtcoding/metamall/MetamallApplication.java @@ -4,14 +4,23 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; +import shop.mtcoding.metamall.core.exception.Exception404; 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.product.Product; import shop.mtcoding.metamall.model.product.ProductRepository; import shop.mtcoding.metamall.model.user.User; import shop.mtcoding.metamall.model.user.UserRepository; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + @SpringBootApplication public class MetamallApplication { @@ -21,7 +30,26 @@ CommandLineRunner initData(UserRepository userRepository, ProductRepository prod // 여기에서 save 하면 됨. // bulk Collector는 saveAll 하면 됨. User ssar = User.builder().username("ssar").password("1234").email("ssar@nate.com").role("USER").build(); - userRepository.save(ssar); + User tester = User.builder().username("tester").password("1234").email("tester@nate.com").role("USER").build(); + User seller1 = User.builder().username("seller1").password("5678").email("seller1@email.com").role("SELLER").build(); + User seller2 = User.builder().username("seller2").password("5678").email("seller2@email.com").role("SELLER").build(); + User admin = User.builder().username("admin").password("0000").email("admin@email.com").role("ADMIN").build(); + List users = Stream.of(ssar,tester,seller1,seller2,admin).collect(Collectors.toList()); + userRepository.saveAll(users); + + Product product1 = Product.builder().user(seller1).name("제품1").qty(1).price(10000).build(); + Product product2 = Product.builder().user(seller2).name("제품2").qty(2).price(1000).build(); + List products = Stream.of(product1,product2).collect(Collectors.toList()); + productRepository.saveAll(products); + + List orders = Stream.of( + OrderProduct.builder().product(product1).count(1).orderPrice(product1.getPrice()).build(), + OrderProduct.builder().product(product2).count(1).orderPrice(product2.getPrice()).build()) + .collect(Collectors.toList()); + int totalPrice = orders.stream().mapToInt(OrderProduct::getOrderPrice).sum(); + OrderSheet orderSheet = OrderSheet.builder().user(ssar).totalPrice(totalPrice).build(); + orders.forEach(orderSheet::addOrderProductList); + orderSheetRepository.save(orderSheet); }; } diff --git a/src/main/java/shop/mtcoding/metamall/config/FilterRegisterConfig.java b/src/main/java/shop/mtcoding/metamall/config/FilterRegisterConfig.java index f5ea4db..c019b46 100644 --- a/src/main/java/shop/mtcoding/metamall/config/FilterRegisterConfig.java +++ b/src/main/java/shop/mtcoding/metamall/config/FilterRegisterConfig.java @@ -1,10 +1,13 @@ package shop.mtcoding.metamall.config; import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import shop.mtcoding.metamall.core.filter.JwtVerifyFilter; +import javax.servlet.DispatcherType; + @Configuration public class FilterRegisterConfig { @@ -12,8 +15,12 @@ public class FilterRegisterConfig { public FilterRegistrationBean jwtVerifyFilterAdd() { FilterRegistrationBean registration = new FilterRegistrationBean<>(); registration.setFilter(new JwtVerifyFilter()); - registration.addUrlPatterns("/user/*"); - registration.setOrder(1); + registration.addUrlPatterns("/api/products"); + registration.addUrlPatterns("/api/product/*"); + registration.addUrlPatterns("/api/orders"); + registration.addUrlPatterns("/api/order/*"); + registration.addUrlPatterns("/api/admin/*"); +// registration.setOrder(1); return registration; } } diff --git a/src/main/java/shop/mtcoding/metamall/config/WebMvcConfig.java b/src/main/java/shop/mtcoding/metamall/config/WebMvcConfig.java index 64f5d9b..d5dea83 100644 --- a/src/main/java/shop/mtcoding/metamall/config/WebMvcConfig.java +++ b/src/main/java/shop/mtcoding/metamall/config/WebMvcConfig.java @@ -1,11 +1,28 @@ package shop.mtcoding.metamall.config; +import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; 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.config.interceptor.LoginInterceptor; @Configuration +@RequiredArgsConstructor public class WebMvcConfig implements WebMvcConfigurer { + + private final LoginInterceptor loginInterceptor; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(loginInterceptor) + .addPathPatterns("/api/products") + .addPathPatterns("/api/product/*") + .addPathPatterns("/api/orders") + .addPathPatterns("/api/order/*") + .addPathPatterns("/api/admin/*"); + } + @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") diff --git a/src/main/java/shop/mtcoding/metamall/config/interceptor/LoginInterceptor.java b/src/main/java/shop/mtcoding/metamall/config/interceptor/LoginInterceptor.java new file mode 100644 index 0000000..bc70953 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/config/interceptor/LoginInterceptor.java @@ -0,0 +1,21 @@ +package shop.mtcoding.metamall.config.interceptor; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.HandlerInterceptor; +import shop.mtcoding.metamall.core.exception.Exception401; +import shop.mtcoding.metamall.core.session.LoginUser; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +@Configuration +public class LoginInterceptor implements HandlerInterceptor { + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + HttpSession session = request.getSession(); + LoginUser loginUser = (LoginUser) session.getAttribute("loginUser"); + if(loginUser == null) throw new Exception401("잘못된 접근입니다"); + return true; + } +} diff --git a/src/main/java/shop/mtcoding/metamall/controller/OrderController.java b/src/main/java/shop/mtcoding/metamall/controller/OrderController.java new file mode 100644 index 0000000..00e18e2 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/controller/OrderController.java @@ -0,0 +1,97 @@ +package shop.mtcoding.metamall.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import shop.mtcoding.metamall.core.annotation.Auth; +import shop.mtcoding.metamall.core.annotation.RoleCk; +import shop.mtcoding.metamall.core.exception.Exception404; +import shop.mtcoding.metamall.dto.ResponseDto; +import shop.mtcoding.metamall.dto.order.OrderRequest; +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.product.Product; +import shop.mtcoding.metamall.model.product.ProductRepository; +import shop.mtcoding.metamall.model.user.User; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Validated +@Transactional +@RestController +@RequestMapping("/api") +@RequiredArgsConstructor +public class OrderController { + + private final OrderSheetRepository orderSheetRepository; + private final ProductRepository productRepository; + + /** + * 주문(고객) + */ + @RoleCk.User + @PostMapping("/order") + public ResponseEntity order(@Auth User user, @RequestBody OrderRequest.OrderDto orderDto){ + List orders = orderDto.getProducts().stream() + .map(dto -> { + Product product = productRepository.findById(dto.getProductId()).orElseThrow( + () -> new Exception404("상품을 찾을 수 없습니다")); + return OrderProduct.builder() + .product(product) + .count(dto.getCount()) + .orderPrice(product.getPrice()*dto.getCount()) + .build(); + }).collect(Collectors.toList()); + int totalPrice = orders.stream().mapToInt(OrderProduct::getOrderPrice).sum(); + OrderSheet orderSheet = OrderSheet.builder() + .user(user) + .totalPrice(totalPrice) + .build(); + orders.forEach(orderSheet::addOrderProductList); + orderSheet = orderSheetRepository.save(orderSheet); + return ResponseEntity.ok().body(new ResponseDto<>().data(orderSheet)); + } + + /** + * 주문목록 + */ + @Transactional(readOnly = true) + @GetMapping("/orders") + public ResponseEntity getOrders(@Auth User user){ + List orderSheets = new ArrayList<>(); + if(user.getRole().equals("USER")) orderSheets = orderSheetRepository.findByUser(user); + else if(user.getRole().equals("SELLER")) orderSheets = orderSheetRepository.findBySeller(user); + return ResponseEntity.ok().body(new ResponseDto<>().data(orderSheets)); + } + + /** + * 모든 주문목록(관리자) + */ + @RoleCk.Admin + @Transactional(readOnly = true) + @GetMapping("/admin/orders") + public ResponseEntity getAllOrders(@Auth User user){ + List orderSheets = orderSheetRepository.findAll(); + return ResponseEntity.ok().body(new ResponseDto<>().data(orderSheets)); + } + + /** + * 주문취소 + */ + @DeleteMapping("/order/{id}") + public ResponseEntity cancelOrder(@Auth User user, @PathVariable Long id){ + OrderSheet orderSheet = null; + if(user.getRole().equals("USER")) orderSheet = orderSheetRepository.findByIdAndUser(id,user).orElseThrow( + () -> new Exception404("주문이 없습니다")); + else if(user.getRole().equals("SELLER")) orderSheet = orderSheetRepository.findByIdOrSeller(id,user).orElseThrow( + () -> new Exception404("주문이 없습니다")); + orderSheetRepository.delete(orderSheet); + return ResponseEntity.ok().body(new ResponseDto<>().data(orderSheet)); + } +} diff --git a/src/main/java/shop/mtcoding/metamall/controller/ProductController.java b/src/main/java/shop/mtcoding/metamall/controller/ProductController.java new file mode 100644 index 0000000..964841e --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/controller/ProductController.java @@ -0,0 +1,77 @@ +package shop.mtcoding.metamall.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import shop.mtcoding.metamall.core.annotation.Auth; +import shop.mtcoding.metamall.core.annotation.RoleCk; +import shop.mtcoding.metamall.core.exception.Exception404; +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 java.util.List; + +@Validated +@Transactional +@RestController +@RequestMapping("/api") +@RequiredArgsConstructor +public class ProductController { + + private final ProductRepository productRepository; + + /** + * 상품등록(판매자) + */ + @RoleCk.Seller + @PostMapping("/product") + public ResponseEntity registerProduct(@Auth User seller, @RequestBody ProductRequest.ProductDto productDto){ + Product product = productRepository.save(Product.builder() + .name(productDto.getName()) + .price(productDto.getPrice()) + .qty(productDto.getQty()) + .user(seller) + .build()); + return ResponseEntity.ok().body(new ResponseDto<>().data(product)); + } + + /** + * 상품목록 + */ + @Transactional(readOnly = true) + @GetMapping("/products") + public ResponseEntity getProducts(@Auth User user){ + List products = productRepository.findAll(); + return ResponseEntity.ok().body(new ResponseDto<>().data(products)); + } + + /** + * 상품상세 + */ + @Transactional(readOnly = true) + @GetMapping("/product/{id}") + public ResponseEntity getProductDetails(@Auth User user, @PathVariable Long id){ + Product product = productRepository.findById(id).orElseThrow( + () -> new Exception404("상품을 찾을 수 없습니다")); + return ResponseEntity.ok().body(new ResponseDto<>().data(product)); + } + + /** + * 상품수정(판매자) + */ + @RoleCk.Seller + @PutMapping("/product/{id}") + public ResponseEntity updateProduct(@Auth User user, @PathVariable Long id, @RequestBody ProductRequest.ProductDto dto){ + Product product = productRepository.findByIdAndUser(id,user).orElseThrow( + () -> new Exception404("상품을 찾을 수 없습니다")); + product.update(dto); + return ResponseEntity.ok().body(new ResponseDto<>().data(product)); + } +} diff --git a/src/main/java/shop/mtcoding/metamall/controller/UserController.java b/src/main/java/shop/mtcoding/metamall/controller/UserController.java index ddfee94..bc4b2ea 100644 --- a/src/main/java/shop/mtcoding/metamall/controller/UserController.java +++ b/src/main/java/shop/mtcoding/metamall/controller/UserController.java @@ -2,33 +2,48 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; +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 shop.mtcoding.metamall.core.annotation.PwdCk; import shop.mtcoding.metamall.core.exception.Exception400; import shop.mtcoding.metamall.core.exception.Exception401; import shop.mtcoding.metamall.core.jwt.JwtProvider; import shop.mtcoding.metamall.dto.ResponseDto; import shop.mtcoding.metamall.dto.user.UserRequest; -import shop.mtcoding.metamall.dto.user.UserResponse; -import shop.mtcoding.metamall.model.log.login.LoginLog; -import shop.mtcoding.metamall.model.log.login.LoginLogRepository; import shop.mtcoding.metamall.model.user.User; import shop.mtcoding.metamall.model.user.UserRepository; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; +import javax.validation.Valid; import java.time.LocalDateTime; import java.util.Optional; -@RequiredArgsConstructor +@Validated +@Transactional @RestController +@RequestMapping("/api/user") +@RequiredArgsConstructor public class UserController { private final UserRepository userRepository; - private final LoginLogRepository loginLogRepository; - private final HttpSession session; + + @PostMapping("/signup") + public ResponseEntity signUp(@RequestBody @Valid @PwdCk UserRequest.SignUpDto signUpDto){ + User signUpUser = userRepository.save(User.builder() + .username(signUpDto.getUsername()) + .password(signUpDto.getPassword()) + .email(signUpDto.getEmail()) + .role(signUpDto.getRole()) + .build()); + return ResponseEntity.ok().body(new ResponseDto<>().data(signUpUser)); + } @PostMapping("/login") - public ResponseEntity login(@RequestBody UserRequest.LoginDto loginDto, HttpServletRequest request) { + public ResponseEntity login(@RequestBody @Valid UserRequest.LoginDto loginDto, HttpServletRequest request) { Optional userOP = userRepository.findByUsername(loginDto.getUsername()); if (userOP.isPresent()) { // 1. 유저 정보 꺼내기 @@ -43,15 +58,10 @@ public ResponseEntity login(@RequestBody UserRequest.LoginDto loginDto, HttpS String jwt = JwtProvider.create(userOP.get()); // 4. 최종 로그인 날짜 기록 (더티체킹 - update 쿼리 발생) - loginUser.setUpdatedAt(LocalDateTime.now()); + loginUser.updateLastLoginDate(LocalDateTime.now()); // 5. 로그 테이블 기록 - LoginLog loginLog = LoginLog.builder() - .userId(loginUser.getId()) - .userAgent(request.getHeader("User-Agent")) - .clientIP(request.getRemoteAddr()) - .build(); - loginLogRepository.save(loginLog); + //AOP로 // 6. 응답 DTO 생성 ResponseDto responseDto = new ResponseDto<>().data(loginUser); diff --git a/src/main/java/shop/mtcoding/metamall/core/advice/CheckAdvice.java b/src/main/java/shop/mtcoding/metamall/core/advice/CheckAdvice.java new file mode 100644 index 0000000..a7dd515 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/advice/CheckAdvice.java @@ -0,0 +1,77 @@ +package shop.mtcoding.metamall.core.advice; + +import lombok.RequiredArgsConstructor; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import shop.mtcoding.metamall.core.exception.Exception400; +import shop.mtcoding.metamall.core.exception.Exception403; +import shop.mtcoding.metamall.dto.user.UserRequest; +import shop.mtcoding.metamall.model.user.User; + +import javax.servlet.http.HttpSession; + +@Aspect +@Order(2) +@Component +@RequiredArgsConstructor +public class CheckAdvice { + + private final HttpSession session; + + @Pointcut("@annotation(shop.mtcoding.metamall.core.annotation.PwdCk)") + public void pwd(){} + + @Pointcut("@annotation(shop.mtcoding.metamall.core.annotation.RoleCk.User)") + public void roleUser(){} + + @Pointcut("@annotation(shop.mtcoding.metamall.core.annotation.RoleCk.Seller)") + public void roleSeller(){} + + @Pointcut("@annotation(shop.mtcoding.metamall.core.annotation.RoleCk.Admin)") + public void roleAdmin(){} + + @Before("pwd()") + public void pwdCheck(JoinPoint jp){ + boolean valid = true; + for(Object arg : jp.getArgs()){ + if(arg instanceof UserRequest.SignUpDto){ + UserRequest.SignUpDto dto = (UserRequest.SignUpDto) arg; + valid = dto.getPassword().equals(dto.getPasswordCheck()); + } + } + if(!valid) throw new Exception400("비밀번호가 일치하지 않습니다"); + } + + @Before("roleUser()") + public void roleUserCheck(JoinPoint jp){ + boolean role = roleCheck(jp, "SELLER"); + if(!role) throw new Exception403("권한이 없습니다"); + } + + @Before("roleSeller()") + public void roleSellerCheck(JoinPoint jp){ + boolean role = roleCheck(jp, "USER"); + if(!role) throw new Exception403("권한이 없습니다"); + } + + @Before("roleAdmin()") + public void roleAdminCheck(JoinPoint jp){ + boolean role = roleCheck(jp, "ADMIN"); + if(!role) throw new Exception403("권한이 없습니다"); + } + + private boolean roleCheck(JoinPoint jp, String role){ + boolean result = true; + for(Object arg:jp.getArgs()){ + if(arg instanceof User) { + if(role.equals("ADMIN")) result = ((User) arg).getRole().equals(role); + else result = !((User) arg).getRole().equals(role); + } + } + return result; + } +} diff --git a/src/main/java/shop/mtcoding/metamall/core/advice/LogAdvice.java b/src/main/java/shop/mtcoding/metamall/core/advice/LogAdvice.java new file mode 100644 index 0000000..ce27425 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/advice/LogAdvice.java @@ -0,0 +1,69 @@ +package shop.mtcoding.metamall.core.advice; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.After; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import shop.mtcoding.metamall.core.session.LoginUser; +import shop.mtcoding.metamall.dto.ResponseDto; +import shop.mtcoding.metamall.model.log.error.ErrorLog; +import shop.mtcoding.metamall.model.log.error.ErrorLogRepository; +import shop.mtcoding.metamall.model.log.login.LoginLog; +import shop.mtcoding.metamall.model.log.login.LoginLogRepository; +import shop.mtcoding.metamall.model.user.User; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +@Slf4j +@Aspect +@Component +@RequiredArgsConstructor +public class LogAdvice { + private final LoginLogRepository loginLogRepository; + private final ErrorLogRepository errorLogRepository; + private final HttpSession session; + + @AfterReturning(pointcut = "execution(public * login(..,*))", returning = "response") + public void loginLogAdvice(JoinPoint jp, ResponseEntity response) { + if (response.getStatusCode() == HttpStatus.OK) { + HttpServletRequest request = null; + for (Object arg : jp.getArgs()) { + if (arg instanceof HttpServletRequest) request = (HttpServletRequest) arg; + } + + ResponseDto responseDto = (ResponseDto) response.getBody(); + User loginUser = (User) responseDto.getData(); + + LoginLog loginLog = LoginLog.builder() + .userId(loginUser.getId()) + .userAgent(request.getHeader("User-Agent")) + .clientIP(request.getRemoteAddr()) + .build(); + loginLogRepository.save(loginLog); + } + } + + @After("@annotation(org.springframework.web.bind.annotation.ExceptionHandler)") + public void errorLogAdvice(JoinPoint jp) { + LoginUser loginSession = (LoginUser) session.getAttribute("loginUser"); + for (Object arg : jp.getArgs()) { + if (arg instanceof RuntimeException) { + if (loginSession != null) { + ErrorLog errorLog = ErrorLog.builder() + .userId(Long.valueOf(loginSession.getId())) + .msg(((RuntimeException) arg).getMessage()) + .build(); + errorLogRepository.save(errorLog); + return; + } + log.error(((RuntimeException) arg).getMessage()); + } + } + } +} diff --git a/src/main/java/shop/mtcoding/metamall/core/advice/LoginAdvice.java b/src/main/java/shop/mtcoding/metamall/core/advice/LoginAdvice.java new file mode 100644 index 0000000..9465266 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/advice/LoginAdvice.java @@ -0,0 +1,58 @@ +package shop.mtcoding.metamall.core.advice; + +import lombok.RequiredArgsConstructor; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import shop.mtcoding.metamall.core.annotation.Auth; +import shop.mtcoding.metamall.core.exception.Exception401; +import shop.mtcoding.metamall.core.session.LoginUser; +import shop.mtcoding.metamall.model.user.User; +import shop.mtcoding.metamall.model.user.UserRepository; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.stream.IntStream; + +@Aspect +@Order(1) +@Component +@RequiredArgsConstructor +public class LoginAdvice { + private final UserRepository userRepository; + + @Around("execution(* shop.mtcoding.metamall.controller.*.*(..))") + public Object loginUserAdviceAround(ProceedingJoinPoint jp) throws Throwable { + MethodSignature signature = (MethodSignature) jp.getSignature(); + Method method = signature.getMethod(); + Object[] args = jp.getArgs(); + Parameter[] parameters = method.getParameters(); + + int loginUserAopIndex = IntStream.range(0, parameters.length) + .filter(i -> parameters[i].isAnnotationPresent(Auth.class)) + .findFirst() + .orElse(-1); + + if (loginUserAopIndex >= 0) { + HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + HttpSession session = req.getSession(); + LoginUser sessionUser = (LoginUser) session.getAttribute("loginUser"); + User principal = userRepository.findById(sessionUser.getId()).orElseThrow( + () -> new Exception401("인증되지 않았습니다") + ); + args[loginUserAopIndex] = principal; + + // 해당 메서드 실행 + return jp.proceed(args); + } + + return jp.proceed(); + } +} diff --git a/src/main/java/shop/mtcoding/metamall/core/advice/MyExceptionAdvice.java b/src/main/java/shop/mtcoding/metamall/core/advice/MyExceptionAdvice.java index 50ebee2..e139e55 100644 --- a/src/main/java/shop/mtcoding/metamall/core/advice/MyExceptionAdvice.java +++ b/src/main/java/shop/mtcoding/metamall/core/advice/MyExceptionAdvice.java @@ -1,20 +1,15 @@ package shop.mtcoding.metamall.core.advice; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import shop.mtcoding.metamall.core.exception.*; -import shop.mtcoding.metamall.model.log.error.ErrorLogRepository; -@Slf4j @RequiredArgsConstructor @RestControllerAdvice public class MyExceptionAdvice { - private final ErrorLogRepository errorLogRepository; - @ExceptionHandler(Exception400.class) public ResponseEntity badRequest(Exception400 e){ return new ResponseEntity<>(e.body(), e.status()); diff --git a/src/main/java/shop/mtcoding/metamall/core/annotation/Auth.java b/src/main/java/shop/mtcoding/metamall/core/annotation/Auth.java new file mode 100644 index 0000000..d38f150 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/annotation/Auth.java @@ -0,0 +1,11 @@ +package shop.mtcoding.metamall.core.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface Auth { +} diff --git a/src/main/java/shop/mtcoding/metamall/core/annotation/PwdCk.java b/src/main/java/shop/mtcoding/metamall/core/annotation/PwdCk.java new file mode 100644 index 0000000..b100639 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/annotation/PwdCk.java @@ -0,0 +1,11 @@ +package shop.mtcoding.metamall.core.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface PwdCk { +} diff --git a/src/main/java/shop/mtcoding/metamall/core/annotation/RoleCk.java b/src/main/java/shop/mtcoding/metamall/core/annotation/RoleCk.java new file mode 100644 index 0000000..1de584b --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/annotation/RoleCk.java @@ -0,0 +1,21 @@ +package shop.mtcoding.metamall.core.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +public class RoleCk { + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface User{} + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface Seller{} + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface Admin{} +} diff --git a/src/main/java/shop/mtcoding/metamall/core/filter/JwtVerifyFilter.java b/src/main/java/shop/mtcoding/metamall/core/filter/JwtVerifyFilter.java index 870bf93..d8f2c33 100644 --- a/src/main/java/shop/mtcoding/metamall/core/filter/JwtVerifyFilter.java +++ b/src/main/java/shop/mtcoding/metamall/core/filter/JwtVerifyFilter.java @@ -31,7 +31,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha String jwt = prefixJwt.replace(JwtProvider.TOKEN_PREFIX, ""); try { DecodedJWT decodedJWT = JwtProvider.verify(jwt); - int id = decodedJWT.getClaim("id").asInt(); + Long id = decodedJWT.getClaim("id").asLong(); String role = decodedJWT.getClaim("role").asString(); // 세션을 사용하는 이유는 권한처리를 하기 위해서이다. diff --git a/src/main/java/shop/mtcoding/metamall/core/session/LoginUser.java b/src/main/java/shop/mtcoding/metamall/core/session/LoginUser.java index 59f402c..a38bf5c 100644 --- a/src/main/java/shop/mtcoding/metamall/core/session/LoginUser.java +++ b/src/main/java/shop/mtcoding/metamall/core/session/LoginUser.java @@ -5,11 +5,11 @@ @Getter public class LoginUser { - private Integer id; + private Long id; private String role; @Builder - public LoginUser(Integer id, String role) { + public LoginUser(Long id, String role) { this.id = id; this.role = role; } diff --git a/src/main/java/shop/mtcoding/metamall/dto/ResponseDto.java b/src/main/java/shop/mtcoding/metamall/dto/ResponseDto.java index 7f190c6..7a661aa 100644 --- a/src/main/java/shop/mtcoding/metamall/dto/ResponseDto.java +++ b/src/main/java/shop/mtcoding/metamall/dto/ResponseDto.java @@ -1,6 +1,7 @@ package shop.mtcoding.metamall.dto; import lombok.Getter; +import lombok.NoArgsConstructor; import org.springframework.http.HttpStatus; @Getter diff --git a/src/main/java/shop/mtcoding/metamall/dto/order/OrderRequest.java b/src/main/java/shop/mtcoding/metamall/dto/order/OrderRequest.java new file mode 100644 index 0000000..4e31551 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/dto/order/OrderRequest.java @@ -0,0 +1,32 @@ +package shop.mtcoding.metamall.dto.order; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +public class OrderRequest { + @Getter + @NoArgsConstructor + public static class OrderDto{ + private List products; + + public OrderDto(List products) { + this.products = products; + } + } + @Getter + public static class OrderProductDto{ + @JsonProperty("product_id") + private Long productId; + private int count; + + @Builder + public OrderProductDto(Long productId, int count) { + this.productId = productId; + this.count = count; + } + } +} diff --git a/src/main/java/shop/mtcoding/metamall/dto/product/ProductRequest.java b/src/main/java/shop/mtcoding/metamall/dto/product/ProductRequest.java new file mode 100644 index 0000000..a80e8ec --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/dto/product/ProductRequest.java @@ -0,0 +1,25 @@ +package shop.mtcoding.metamall.dto.product; + +import lombok.Builder; +import lombok.Getter; + +import javax.validation.constraints.NotNull; + +public class ProductRequest { + @Getter + public static class ProductDto{ + @NotNull + private String name; + @NotNull + private int price; + @NotNull + private int qty; + + @Builder + public ProductDto(String name, int price, int qty) { + this.name = name; + this.price = price; + this.qty = qty; + } + } +} diff --git a/src/main/java/shop/mtcoding/metamall/dto/user/UserRequest.java b/src/main/java/shop/mtcoding/metamall/dto/user/UserRequest.java index 80947db..b28f232 100644 --- a/src/main/java/shop/mtcoding/metamall/dto/user/UserRequest.java +++ b/src/main/java/shop/mtcoding/metamall/dto/user/UserRequest.java @@ -1,12 +1,50 @@ package shop.mtcoding.metamall.dto.user; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; import lombok.Getter; -import lombok.Setter; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; public class UserRequest { - @Getter @Setter + @Getter public static class LoginDto { - private String username; - private String password; + @NotEmpty + private final String username; + @NotEmpty + private final String password; + + @Builder + public LoginDto(String username, String password) { + this.username = username; + this.password = password; + } + } + + @Getter + public static class SignUpDto{ + @NotEmpty + private final String username; + @NotEmpty + private final String password; + @NotEmpty + @JsonProperty("password_check") + private String passwordCheck; + @NotEmpty + @Pattern(regexp = "\\w+@\\w+\\.\\w{2,}") + private final String email; + @NotNull + private final String role; + + @Builder + public SignUpDto(String username, String password, String passwordCheck, String email, String role) { + this.username = username; + this.password = password; + this.passwordCheck = passwordCheck; + this.email = email; + this.role = role; + } } } diff --git a/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProduct.java b/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProduct.java index 165905e..f6e5063 100644 --- a/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProduct.java +++ b/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProduct.java @@ -1,32 +1,32 @@ package shop.mtcoding.metamall.model.orderproduct; +import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; import shop.mtcoding.metamall.model.ordersheet.OrderSheet; import shop.mtcoding.metamall.model.product.Product; import javax.persistence.*; import java.time.LocalDateTime; -@NoArgsConstructor -@Setter // DTO 만들면 삭제해야됨 @Getter @Table(name = "order_product_tb") @Entity +@NoArgsConstructor public class OrderProduct { // 주문 상품 @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) private Product product; private Integer count; // 상품 주문 개수 private Integer orderPrice; // 상품 주문 금액 private LocalDateTime createdAt; private LocalDateTime updatedAt; - - @ManyToOne + @JsonIgnore + @ManyToOne(fetch = FetchType.LAZY,optional = false) + @JoinColumn(name="order_sheet_id",nullable = false) private OrderSheet orderSheet; @PrePersist @@ -49,4 +49,10 @@ public OrderProduct(Long id, Product product, Integer count, Integer orderPrice, this.updatedAt = updatedAt; this.orderSheet = orderSheet; } + + public void setOrderSheet(OrderSheet orderSheet){ + if(this.orderSheet != null) this.orderSheet.getOrderProductList().remove(this); + this.orderSheet = orderSheet; + orderSheet.getOrderProductList().add(this); + } } diff --git a/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProductRepository.java b/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProductRepository.java index 6f1238c..ae823cd 100644 --- a/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProductRepository.java +++ b/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProductRepository.java @@ -1,6 +1,11 @@ package shop.mtcoding.metamall.model.orderproduct; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import shop.mtcoding.metamall.model.ordersheet.OrderSheet; public interface OrderProductRepository extends JpaRepository { + @Query("select op from OrderProduct op join fetch op.orderSheet os where os=:os") + OrderProduct findByOrderSheet(@Param("os") OrderSheet os); } diff --git a/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheet.java b/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheet.java index 7638710..0ab0530 100644 --- a/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheet.java +++ b/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheet.java @@ -3,9 +3,7 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; import shop.mtcoding.metamall.model.orderproduct.OrderProduct; -import shop.mtcoding.metamall.model.product.Product; import shop.mtcoding.metamall.model.user.User; import javax.persistence.*; @@ -13,18 +11,17 @@ import java.util.ArrayList; import java.util.List; -@NoArgsConstructor -@Setter // DTO 만들면 삭제해야됨 @Getter @Table(name = "order_sheet_tb") @Entity +@NoArgsConstructor public class OrderSheet { // 주문서 @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) private User user; // 주문자 - @OneToMany(mappedBy = "orderSheet") + @OneToMany(mappedBy = "orderSheet", cascade = CascadeType.ALL, orphanRemoval = true) private List orderProductList = new ArrayList<>(); // 총 주문 상품 리스트 private Integer totalPrice; // 총 주문 금액 (총 주문 상품 리스트의 orderPrice 합) private LocalDateTime createdAt; @@ -50,4 +47,9 @@ public OrderSheet(Long id, User user, Integer totalPrice, LocalDateTime createdA this.createdAt = createdAt; this.updatedAt = updatedAt; } + + public void addOrderProductList(OrderProduct orderProduct){ + orderProductList.add(orderProduct); + orderProduct.setOrderSheet(this); + } } diff --git a/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepository.java b/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepository.java index 5d59249..a910ea5 100644 --- a/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepository.java +++ b/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepository.java @@ -1,6 +1,26 @@ package shop.mtcoding.metamall.model.ordersheet; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import shop.mtcoding.metamall.model.user.User; + +import java.util.List; +import java.util.Optional; public interface OrderSheetRepository extends JpaRepository { + + @Query("select os from OrderSheet os left join fetch os.user u left join fetch os.orderProductList op left join fetch op.product p left join fetch p.seller s") + List findAll(); + @Query("select os from OrderSheet os left join fetch os.orderProductList op left join fetch op.product p left join fetch p.seller s where os.user=:user") + List findByUser(@Param("user") User user); + + @Query("select os from OrderSheet os left join fetch os.user u left join fetch os.orderProductList op left join fetch op.product p where p.seller=:seller") + List findBySeller(@Param("seller") User seller); + + @Query("select os from OrderSheet os left join fetch os.user u left join fetch os.orderProductList op where os.id=:id and u=:user") + Optional findByIdAndUser(@Param("id") Long id, @Param("user") User user); + + @Query("select os from OrderSheet os left join fetch os.user u left join fetch os.orderProductList op left join fetch op.product p left join fetch p.seller s where os.id=:id or s=:seller") + Optional findByIdOrSeller(@Param("id") Long id, @Param("seller") User seller); } diff --git a/src/main/java/shop/mtcoding/metamall/model/product/Product.java b/src/main/java/shop/mtcoding/metamall/model/product/Product.java index bc8c618..fd67ac4 100644 --- a/src/main/java/shop/mtcoding/metamall/model/product/Product.java +++ b/src/main/java/shop/mtcoding/metamall/model/product/Product.java @@ -3,16 +3,16 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; +import shop.mtcoding.metamall.dto.product.ProductRequest; +import shop.mtcoding.metamall.model.user.User; import javax.persistence.*; import java.time.LocalDateTime; -@NoArgsConstructor -@Setter // DTO 만들면 삭제해야됨 @Getter @Table(name = "product_tb") @Entity +@NoArgsConstructor public class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -20,6 +20,8 @@ public class Product { private String name; // 상품 이름 private Integer price; // 상품 가격 private Integer qty; // 상품 재고 + @ManyToOne(fetch = FetchType.LAZY) + private User seller; private LocalDateTime createdAt; private LocalDateTime updatedAt; @@ -34,12 +36,19 @@ protected void onUpdate() { } @Builder - public Product(Long id, String name, Integer price, Integer qty, LocalDateTime createdAt, LocalDateTime updatedAt) { + public Product(Long id, String name, Integer price, Integer qty, User user, LocalDateTime createdAt, LocalDateTime updatedAt) { this.id = id; this.name = name; this.price = price; this.qty = qty; + this.seller = user; this.createdAt = createdAt; this.updatedAt = updatedAt; } + + public void update(ProductRequest.ProductDto dto){ + this.name = dto.getName(); + this.price = dto.getPrice(); + this.qty = dto.getQty(); + } } diff --git a/src/main/java/shop/mtcoding/metamall/model/product/ProductRepository.java b/src/main/java/shop/mtcoding/metamall/model/product/ProductRepository.java index ba5def3..df49e7d 100644 --- a/src/main/java/shop/mtcoding/metamall/model/product/ProductRepository.java +++ b/src/main/java/shop/mtcoding/metamall/model/product/ProductRepository.java @@ -1,6 +1,19 @@ package shop.mtcoding.metamall.model.product; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import shop.mtcoding.metamall.model.user.User; + +import java.util.List; +import java.util.Optional; public interface ProductRepository extends JpaRepository { + + @Query("select p from Product p join fetch p.seller s") + List findAll(); + @Query("select p from Product p join fetch p.seller s where p.id=:id") + Optional findById(@Param("id") Long id); + @Query("select p from Product p join fetch p.seller s where p.id=:id and s=:user") + Optional findByIdAndUser(@Param("id") Long id, @Param("user") User user); } diff --git a/src/main/java/shop/mtcoding/metamall/model/user/User.java b/src/main/java/shop/mtcoding/metamall/model/user/User.java index c929ce5..1a9dffe 100644 --- a/src/main/java/shop/mtcoding/metamall/model/user/User.java +++ b/src/main/java/shop/mtcoding/metamall/model/user/User.java @@ -1,23 +1,25 @@ package shop.mtcoding.metamall.model.user; +import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; import javax.persistence.*; import java.time.LocalDateTime; -@NoArgsConstructor -@Setter // DTO 만들면 삭제해야됨 @Getter @Table(name = "user_tb") @Entity +@NoArgsConstructor public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(unique = true, nullable = false) private String username; + @JsonIgnore + @Column(nullable = false) private String password; private String email; private String role; // USER(고객), SELLER(판매자), ADMIN(관리자) @@ -34,6 +36,10 @@ protected void onUpdate() { this.updatedAt = LocalDateTime.now(); } + public void updateLastLoginDate(LocalDateTime updatedAt) { + this.updatedAt = updatedAt; + } + @Builder public User(Long id, String username, String password, String email, String role, LocalDateTime createdAt) { this.id = id; @@ -43,4 +49,4 @@ public User(Long id, String username, String password, String email, String role this.role = role; this.createdAt = createdAt; } -} +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/model/user/UserRepository.java b/src/main/java/shop/mtcoding/metamall/model/user/UserRepository.java index 293a101..9964230 100644 --- a/src/main/java/shop/mtcoding/metamall/model/user/UserRepository.java +++ b/src/main/java/shop/mtcoding/metamall/model/user/UserRepository.java @@ -8,6 +8,8 @@ public interface UserRepository extends JpaRepository { + Optional findById(Long id); + @Query("select u from User u where u.username = :username") Optional findByUsername(@Param("username") String username); } diff --git a/src/test/java/shop/mtcoding/metamall/controller/OrderControllerTest.java b/src/test/java/shop/mtcoding/metamall/controller/OrderControllerTest.java new file mode 100644 index 0000000..36ebbcd --- /dev/null +++ b/src/test/java/shop/mtcoding/metamall/controller/OrderControllerTest.java @@ -0,0 +1,136 @@ +package shop.mtcoding.metamall.controller; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.*; +import shop.mtcoding.metamall.core.jwt.JwtProvider; +import shop.mtcoding.metamall.dto.ResponseDto; +import shop.mtcoding.metamall.dto.order.OrderRequest; +import shop.mtcoding.metamall.model.user.User; +import shop.mtcoding.metamall.model.user.UserRepository; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class OrderControllerTest { + + @Autowired + TestRestTemplate testRestTemplate; + @Autowired + private UserRepository userRepository; + + private HttpHeaders headers(User user){ + String jwt = JwtProvider.create(user); + + HttpHeaders requestHeaders = new HttpHeaders(); + requestHeaders.setContentType(MediaType.APPLICATION_JSON); + requestHeaders.setAccept(List.of(MediaType.APPLICATION_JSON)); + requestHeaders.add(JwtProvider.HEADER,jwt); + + return requestHeaders; + } + + @Test + @DisplayName("주문") + void order(){ + //given + OrderRequest.OrderProductDto product1 = OrderRequest.OrderProductDto.builder() + .productId(1L) + .count(1) + .build(); + OrderRequest.OrderProductDto product2 = OrderRequest.OrderProductDto.builder() + .productId(2L) + .count(1) + .build(); + List products = Stream.of(product1,product2).collect(Collectors.toList()); + OrderRequest.OrderDto order = new OrderRequest.OrderDto(products); + User ssar = userRepository.findByUsername("ssar").orElse(null); + HttpHeaders headers = headers(ssar); + HttpEntity requestEntity = new HttpEntity<>(order, headers); + + //when + ResponseEntity response = testRestTemplate + .postForEntity( + "/api/order", + requestEntity, + ResponseDto.class + ); + + //then + assertEquals(HttpStatus.OK, response.getStatusCode()); + + } + + @Test + @DisplayName("주문조회") + void getOrders(){ + //given + User ssar = userRepository.findByUsername("ssar").orElse(null); + HttpHeaders headers = headers(ssar); + HttpEntity requestEntity = new HttpEntity<>(headers); + + //when + + ResponseEntity response = testRestTemplate + .exchange( + "/api/orders", + HttpMethod.GET, + requestEntity, + ResponseDto.class + ); + + //then + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + @DisplayName("모든 주문조회(관리자)") + void getAllOrders(){ + //given + User ssar = userRepository.findByUsername("admin").orElse(null); + HttpHeaders headers = headers(ssar); + HttpEntity requestEntity = new HttpEntity<>(headers); + + //when + + ResponseEntity response = testRestTemplate + .exchange( + "/api/admin/orders", + HttpMethod.GET, + requestEntity, + ResponseDto.class + ); + + //then + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + @DisplayName("주문취소") + void cancelOrder() { + //given + long id = 1; + User ssar = userRepository.findByUsername("ssar").orElse(null); + HttpHeaders headers = headers(ssar); + HttpEntity requestEntity = new HttpEntity<>(headers); + + //when + ResponseEntity response = testRestTemplate + .exchange( + "/api/order/"+id, + HttpMethod.DELETE, + requestEntity, + ResponseDto.class + ); + + //then + assertEquals(HttpStatus.OK, response.getStatusCode()); + } +} \ No newline at end of file diff --git a/src/test/java/shop/mtcoding/metamall/controller/ProductControllerTest.java b/src/test/java/shop/mtcoding/metamall/controller/ProductControllerTest.java new file mode 100644 index 0000000..11133e0 --- /dev/null +++ b/src/test/java/shop/mtcoding/metamall/controller/ProductControllerTest.java @@ -0,0 +1,136 @@ +package shop.mtcoding.metamall.controller; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.*; +import shop.mtcoding.metamall.core.jwt.JwtProvider; +import shop.mtcoding.metamall.dto.ResponseDto; +import shop.mtcoding.metamall.dto.product.ProductRequest; +import shop.mtcoding.metamall.model.user.User; +import shop.mtcoding.metamall.model.user.UserRepository; + +import java.util.List; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class ProductControllerTest { + + @Autowired + TestRestTemplate testRestTemplate; + + @Autowired + private UserRepository userRepository; + + private HttpHeaders headers(User user){ + String jwt = JwtProvider.create(user); + + HttpHeaders requestHeaders = new HttpHeaders(); + requestHeaders.setContentType(MediaType.APPLICATION_JSON); + requestHeaders.setAccept(List.of(MediaType.APPLICATION_JSON)); + requestHeaders.add(JwtProvider.HEADER,jwt); + + return requestHeaders; + } + + @Test + @DisplayName("상품 등록") + void registerProduct(){ + //given + User seller1 = userRepository.findByUsername("seller1").orElse(null); + HttpHeaders headers = headers(seller1); + ProductRequest.ProductDto product = ProductRequest.ProductDto.builder() + .name("제품3") + .price(100000) + .qty(1) + .build(); + HttpEntity requestEntity = new HttpEntity<>(product, headers); + + //when + + ResponseEntity response = testRestTemplate + .postForEntity( + "/api/product", + requestEntity, + ResponseDto.class + ); + + //then + Assertions.assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + @DisplayName("상품 조회") + void getProducts() { + //given + User ssar = userRepository.findByUsername("ssar").orElse(null); + HttpHeaders headers = headers(ssar); + HttpEntity requestEntity = new HttpEntity<>(headers); + + //when + + ResponseEntity response = testRestTemplate + .exchange( + "/api/products", + HttpMethod.GET, + requestEntity, + ResponseDto.class + ); + + //then + Assertions.assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + @DisplayName("상품 상세") + void getProductDetails() { + //given + long id = 1; + User ssar = userRepository.findByUsername("ssar").orElse(null); + HttpHeaders headers = headers(ssar); + HttpEntity requestEntity = new HttpEntity<>(headers); + + //when + + ResponseEntity response = testRestTemplate + .exchange( + "/api/product/"+id, + HttpMethod.GET, + requestEntity, + ResponseDto.class + ); + + //then + Assertions.assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + @DisplayName("상품 수정") + void updateProduct() { + //given + long id = 1; + User seller1 = userRepository.findByUsername("seller1").orElse(null); + HttpHeaders headers = headers(seller1); + ProductRequest.ProductDto product = ProductRequest.ProductDto.builder() + .name("제품3") + .price(10000) + .qty(1) + .build(); + HttpEntity requestEntity = new HttpEntity<>(product, headers); + + //when + + ResponseEntity response = testRestTemplate + .exchange( + "/api/product/"+id, + HttpMethod.PUT, + requestEntity, + ResponseDto.class + ); + + //then + Assertions.assertEquals(HttpStatus.OK, response.getStatusCode()); + } +} \ No newline at end of file diff --git a/src/test/java/shop/mtcoding/metamall/controller/UserControllerTest.java b/src/test/java/shop/mtcoding/metamall/controller/UserControllerTest.java new file mode 100644 index 0000000..90154ee --- /dev/null +++ b/src/test/java/shop/mtcoding/metamall/controller/UserControllerTest.java @@ -0,0 +1,74 @@ +package shop.mtcoding.metamall.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.ResultMatcher; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import shop.mtcoding.metamall.dto.user.UserRequest; +import shop.mtcoding.metamall.model.user.User; +import shop.mtcoding.metamall.model.user.UserRepository; + +@AutoConfigureMockMvc +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) +class UserControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Test + void signUp() throws Exception { + //given + UserRequest.SignUpDto singup = UserRequest.SignUpDto.builder() + .username("test1") + .email("test1@email.com") + .password("1234") + .passwordCheck("1234") + .role("USER") + .build(); + String requestBody = new ObjectMapper().writeValueAsString(singup); + + //when + MockHttpServletRequestBuilder builder = MockMvcRequestBuilders + .post("/api/user/signup") + .content(requestBody) + .contentType(MediaType.APPLICATION_JSON); + ResultActions actions = mockMvc.perform(builder); + + //then + ResultMatcher isOk = MockMvcResultMatchers.status().isOk(); + actions.andExpect(isOk) + .andDo(MockMvcResultHandlers.print()); + } + + @Test + void login() throws Exception { + //given + UserRequest.LoginDto login = UserRequest.LoginDto.builder() + .username("ssar") + .password("1234") + .build(); + String requestBody = new ObjectMapper().writeValueAsString(login); + + //when + MockHttpServletRequestBuilder builder = MockMvcRequestBuilders + .post("/api/user/login") + .content(requestBody) + .contentType(MediaType.APPLICATION_JSON); + ResultActions actions = mockMvc.perform(builder); + + //then + ResultMatcher isOk = MockMvcResultMatchers.status().isOk(); + actions.andExpect(isOk) + .andDo(MockMvcResultHandlers.print()); + } +} \ No newline at end of file diff --git a/src/test/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepositoryTest.java b/src/test/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepositoryTest.java new file mode 100644 index 0000000..1697a40 --- /dev/null +++ b/src/test/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepositoryTest.java @@ -0,0 +1,75 @@ +package shop.mtcoding.metamall.model.ordersheet; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import shop.mtcoding.metamall.model.product.ProductRepository; +import shop.mtcoding.metamall.model.user.User; +import shop.mtcoding.metamall.model.user.UserRepository; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@DataJpaTest +class OrderSheetRepositoryTest { + + @Autowired + private UserRepository userRepository; + @Autowired + ProductRepository productRepository; + @Autowired + private OrderSheetRepository orderSheetRepository; + + @Test + void findByUser() { + //given + User user = userRepository.findByUsername("ssar").orElse(null); + + //when + List orderSheet = orderSheetRepository.findByUser(user); + + //then + assertEquals(1,orderSheet.size()); + assertEquals(user,orderSheet.get(0).getUser()); + } + + @Test + void findBySeller() { + //given + User seller = userRepository.findByUsername("seller1").orElse(null); + + //when + List orderSheet = orderSheetRepository.findBySeller(seller); + + //then + assertEquals(1,orderSheet.size()); + } + + @Test + void findByIdAndUser() { + //given + long id = 1; + User user = userRepository.findByUsername("ssar").orElse(null); + + //when + OrderSheet orderSheet = orderSheetRepository.findByIdAndUser(id,user).orElse(null); + + //then + assertEquals(1,orderSheet.getId()); + assertEquals(user,orderSheet.getUser()); + } + + @Test + void findByIdOrSeller(){ + //given + long id = 1; + User seller = userRepository.findByUsername("seller1").orElse(null); + + //when + OrderSheet orderSheet = orderSheetRepository.findByIdOrSeller(id,seller).orElse(null); + + //then + assertEquals(id,orderSheet.getId()); + } +} \ No newline at end of file diff --git a/src/test/java/shop/mtcoding/metamall/model/product/ProductRepositoryTest.java b/src/test/java/shop/mtcoding/metamall/model/product/ProductRepositoryTest.java new file mode 100644 index 0000000..dee2325 --- /dev/null +++ b/src/test/java/shop/mtcoding/metamall/model/product/ProductRepositoryTest.java @@ -0,0 +1,12 @@ +package shop.mtcoding.metamall.model.product; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ProductRepositoryTest { + + @Test + void findByIdAndUser() { + } +} \ No newline at end of file diff --git a/src/test/java/shop/mtcoding/metamall/model/user/UserRepositoryTest.java b/src/test/java/shop/mtcoding/metamall/model/user/UserRepositoryTest.java new file mode 100644 index 0000000..fe15d8d --- /dev/null +++ b/src/test/java/shop/mtcoding/metamall/model/user/UserRepositoryTest.java @@ -0,0 +1,12 @@ +package shop.mtcoding.metamall.model.user; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class UserRepositoryTest { + + @Test + void findByUsername() { + } +} \ No newline at end of file From 0e8d521f1a1ed1678ebbb101d20d4a9beba0ce13 Mon Sep 17 00:00:00 2001 From: ju-ei8ht Date: Mon, 10 Apr 2023 18:00:59 +0900 Subject: [PATCH 2/5] user role string to enum, repository query left join fetch, add to test code --- .../metamall/MetamallApplication.java | 32 ++++---- .../metamall/controller/OrderController.java | 9 +-- .../metamall/core/advice/CheckAdvice.java | 19 ++--- .../metamall/core/filter/JwtVerifyFilter.java | 5 +- .../metamall/core/jwt/JwtProvider.java | 2 +- .../metamall/core/session/LoginUser.java | 5 +- .../metamall/dto/user/UserRequest.java | 5 +- .../metamall/model/ordersheet/OrderSheet.java | 2 + .../ordersheet/OrderSheetRepository.java | 13 +-- .../mtcoding/metamall/model/user/User.java | 8 +- .../controller/OrderControllerTest.java | 80 ++++++++++++++----- .../controller/UserControllerTest.java | 5 +- .../ordersheet/OrderSheetRepositoryTest.java | 40 ++++++++-- .../model/product/ProductRepositoryTest.java | 47 +++++++++++ .../model/user/UserRepositoryTest.java | 14 ++++ 15 files changed, 212 insertions(+), 74 deletions(-) diff --git a/src/main/java/shop/mtcoding/metamall/MetamallApplication.java b/src/main/java/shop/mtcoding/metamall/MetamallApplication.java index 978f10a..186c999 100644 --- a/src/main/java/shop/mtcoding/metamall/MetamallApplication.java +++ b/src/main/java/shop/mtcoding/metamall/MetamallApplication.java @@ -29,29 +29,33 @@ CommandLineRunner initData(UserRepository userRepository, ProductRepository prod return (args)->{ // 여기에서 save 하면 됨. // bulk Collector는 saveAll 하면 됨. - User ssar = User.builder().username("ssar").password("1234").email("ssar@nate.com").role("USER").build(); - User tester = User.builder().username("tester").password("1234").email("tester@nate.com").role("USER").build(); - User seller1 = User.builder().username("seller1").password("5678").email("seller1@email.com").role("SELLER").build(); - User seller2 = User.builder().username("seller2").password("5678").email("seller2@email.com").role("SELLER").build(); - User admin = User.builder().username("admin").password("0000").email("admin@email.com").role("ADMIN").build(); + User ssar = User.builder().username("ssar").password("1234").email("ssar@nate.com").role(User.Role.USER).build(); + User tester = User.builder().username("tester").password("1234").email("tester@nate.com").role(User.Role.USER).build(); + User seller1 = User.builder().username("seller1").password("5678").email("seller1@email.com").role(User.Role.SELLER).build(); + User seller2 = User.builder().username("seller2").password("5678").email("seller2@email.com").role(User.Role.SELLER).build(); + User admin = User.builder().username("admin").password("0000").email("admin@email.com").role(User.Role.ADMIN).build(); List users = Stream.of(ssar,tester,seller1,seller2,admin).collect(Collectors.toList()); userRepository.saveAll(users); - Product product1 = Product.builder().user(seller1).name("제품1").qty(1).price(10000).build(); + Product product1 = Product.builder().user(seller1).name("제품1").qty(2).price(10000).build(); Product product2 = Product.builder().user(seller2).name("제품2").qty(2).price(1000).build(); List products = Stream.of(product1,product2).collect(Collectors.toList()); productRepository.saveAll(products); - List orders = Stream.of( - OrderProduct.builder().product(product1).count(1).orderPrice(product1.getPrice()).build(), - OrderProduct.builder().product(product2).count(1).orderPrice(product2.getPrice()).build()) - .collect(Collectors.toList()); - int totalPrice = orders.stream().mapToInt(OrderProduct::getOrderPrice).sum(); - OrderSheet orderSheet = OrderSheet.builder().user(ssar).totalPrice(totalPrice).build(); - orders.forEach(orderSheet::addOrderProductList); - orderSheetRepository.save(orderSheet); + orderSheetSave(product1,product2,ssar,orderSheetRepository); + orderSheetSave(product1,product2,tester,orderSheetRepository); }; } + private void orderSheetSave(Product product1, Product product2, User user, OrderSheetRepository orderSheetRepository){ + List orders = Stream.of( + OrderProduct.builder().product(product1).count(1).orderPrice(product1.getPrice()).build(), + OrderProduct.builder().product(product2).count(1).orderPrice(product2.getPrice()).build()) + .collect(Collectors.toList()); + int totalPrice = orders.stream().mapToInt(OrderProduct::getOrderPrice).sum(); + OrderSheet orderSheet = OrderSheet.builder().user(user).totalPrice(totalPrice).build(); + orders.forEach(orderSheet::addOrderProductList); + orderSheetRepository.save(orderSheet); + } public static void main(String[] args) { SpringApplication.run(MetamallApplication.class, args); diff --git a/src/main/java/shop/mtcoding/metamall/controller/OrderController.java b/src/main/java/shop/mtcoding/metamall/controller/OrderController.java index 00e18e2..20bdab0 100644 --- a/src/main/java/shop/mtcoding/metamall/controller/OrderController.java +++ b/src/main/java/shop/mtcoding/metamall/controller/OrderController.java @@ -11,7 +11,6 @@ import shop.mtcoding.metamall.dto.ResponseDto; import shop.mtcoding.metamall.dto.order.OrderRequest; 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.product.Product; @@ -65,8 +64,8 @@ public ResponseEntity order(@Auth User user, @RequestBody OrderRequest.OrderD @GetMapping("/orders") public ResponseEntity getOrders(@Auth User user){ List orderSheets = new ArrayList<>(); - if(user.getRole().equals("USER")) orderSheets = orderSheetRepository.findByUser(user); - else if(user.getRole().equals("SELLER")) orderSheets = orderSheetRepository.findBySeller(user); + if(user.getRole() == User.Role.USER) orderSheets = orderSheetRepository.findByUser(user); + else if(user.getRole() == User.Role.SELLER) orderSheets = orderSheetRepository.findBySeller(user); return ResponseEntity.ok().body(new ResponseDto<>().data(orderSheets)); } @@ -87,9 +86,9 @@ public ResponseEntity getAllOrders(@Auth User user){ @DeleteMapping("/order/{id}") public ResponseEntity cancelOrder(@Auth User user, @PathVariable Long id){ OrderSheet orderSheet = null; - if(user.getRole().equals("USER")) orderSheet = orderSheetRepository.findByIdAndUser(id,user).orElseThrow( + if(user.getRole() == User.Role.USER) orderSheet = orderSheetRepository.findByIdAndUser(id,user).orElseThrow( () -> new Exception404("주문이 없습니다")); - else if(user.getRole().equals("SELLER")) orderSheet = orderSheetRepository.findByIdOrSeller(id,user).orElseThrow( + else if(user.getRole() == User.Role.SELLER) orderSheet = orderSheetRepository.findByIdAndSeller(id,user).orElseThrow( () -> new Exception404("주문이 없습니다")); orderSheetRepository.delete(orderSheet); return ResponseEntity.ok().body(new ResponseDto<>().data(orderSheet)); diff --git a/src/main/java/shop/mtcoding/metamall/core/advice/CheckAdvice.java b/src/main/java/shop/mtcoding/metamall/core/advice/CheckAdvice.java index a7dd515..1c87721 100644 --- a/src/main/java/shop/mtcoding/metamall/core/advice/CheckAdvice.java +++ b/src/main/java/shop/mtcoding/metamall/core/advice/CheckAdvice.java @@ -48,30 +48,23 @@ public void pwdCheck(JoinPoint jp){ @Before("roleUser()") public void roleUserCheck(JoinPoint jp){ - boolean role = roleCheck(jp, "SELLER"); - if(!role) throw new Exception403("권한이 없습니다"); + if(!roleCheck(jp, User.Role.USER)) throw new Exception403("권한이 없습니다"); } @Before("roleSeller()") public void roleSellerCheck(JoinPoint jp){ - boolean role = roleCheck(jp, "USER"); - if(!role) throw new Exception403("권한이 없습니다"); + if(!roleCheck(jp, User.Role.SELLER)) throw new Exception403("권한이 없습니다"); } @Before("roleAdmin()") public void roleAdminCheck(JoinPoint jp){ - boolean role = roleCheck(jp, "ADMIN"); - if(!role) throw new Exception403("권한이 없습니다"); + if(!roleCheck(jp, User.Role.ADMIN)) throw new Exception403("권한이 없습니다"); } - private boolean roleCheck(JoinPoint jp, String role){ - boolean result = true; + private boolean roleCheck(JoinPoint jp, User.Role role){ for(Object arg:jp.getArgs()){ - if(arg instanceof User) { - if(role.equals("ADMIN")) result = ((User) arg).getRole().equals(role); - else result = !((User) arg).getRole().equals(role); - } + if(arg instanceof User) return ((User) arg).getRole() == role; } - return result; + return true; } } diff --git a/src/main/java/shop/mtcoding/metamall/core/filter/JwtVerifyFilter.java b/src/main/java/shop/mtcoding/metamall/core/filter/JwtVerifyFilter.java index d8f2c33..bcf1bc6 100644 --- a/src/main/java/shop/mtcoding/metamall/core/filter/JwtVerifyFilter.java +++ b/src/main/java/shop/mtcoding/metamall/core/filter/JwtVerifyFilter.java @@ -10,6 +10,7 @@ import shop.mtcoding.metamall.core.jwt.JwtProvider; import shop.mtcoding.metamall.core.session.LoginUser; import shop.mtcoding.metamall.dto.ResponseDto; +import shop.mtcoding.metamall.model.user.User; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; @@ -32,11 +33,11 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha try { DecodedJWT decodedJWT = JwtProvider.verify(jwt); Long id = decodedJWT.getClaim("id").asLong(); - String role = decodedJWT.getClaim("role").asString(); + int role = decodedJWT.getClaim("role").asInt(); // 세션을 사용하는 이유는 권한처리를 하기 위해서이다. HttpSession session = req.getSession(); - LoginUser loginUser = LoginUser.builder().id(id).role(role).build(); + LoginUser loginUser = LoginUser.builder().id(id).role(User.Role.values()[role]).build(); session.setAttribute("loginUser", loginUser); chain.doFilter(req, resp); }catch (SignatureVerificationException sve){ diff --git a/src/main/java/shop/mtcoding/metamall/core/jwt/JwtProvider.java b/src/main/java/shop/mtcoding/metamall/core/jwt/JwtProvider.java index 93a4bae..e40d4ca 100644 --- a/src/main/java/shop/mtcoding/metamall/core/jwt/JwtProvider.java +++ b/src/main/java/shop/mtcoding/metamall/core/jwt/JwtProvider.java @@ -24,7 +24,7 @@ public static String create(User user) { .withSubject(SUBJECT) .withExpiresAt(new Date(System.currentTimeMillis() + EXP)) .withClaim("id", user.getId()) - .withClaim("role", user.getRole()) + .withClaim("role", user.getRole().ordinal()) .sign(Algorithm.HMAC512(SECRET)); System.out.println("디버그 : 토큰 생성됨"); return TOKEN_PREFIX + jwt; diff --git a/src/main/java/shop/mtcoding/metamall/core/session/LoginUser.java b/src/main/java/shop/mtcoding/metamall/core/session/LoginUser.java index a38bf5c..ffe18ff 100644 --- a/src/main/java/shop/mtcoding/metamall/core/session/LoginUser.java +++ b/src/main/java/shop/mtcoding/metamall/core/session/LoginUser.java @@ -2,14 +2,15 @@ import lombok.Builder; import lombok.Getter; +import shop.mtcoding.metamall.model.user.User; @Getter public class LoginUser { private Long id; - private String role; + private User.Role role; @Builder - public LoginUser(Long id, String role) { + public LoginUser(Long id, User.Role role) { this.id = id; this.role = role; } diff --git a/src/main/java/shop/mtcoding/metamall/dto/user/UserRequest.java b/src/main/java/shop/mtcoding/metamall/dto/user/UserRequest.java index b28f232..27413e4 100644 --- a/src/main/java/shop/mtcoding/metamall/dto/user/UserRequest.java +++ b/src/main/java/shop/mtcoding/metamall/dto/user/UserRequest.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Builder; import lombok.Getter; +import shop.mtcoding.metamall.model.user.User; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; @@ -36,10 +37,10 @@ public static class SignUpDto{ @Pattern(regexp = "\\w+@\\w+\\.\\w{2,}") private final String email; @NotNull - private final String role; + private final User.Role role; @Builder - public SignUpDto(String username, String password, String passwordCheck, String email, String role) { + public SignUpDto(String username, String password, String passwordCheck, String email, User.Role role) { this.username = username; this.password = password; this.passwordCheck = passwordCheck; diff --git a/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheet.java b/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheet.java index 0ab0530..60d06d8 100644 --- a/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheet.java +++ b/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheet.java @@ -3,6 +3,8 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; import shop.mtcoding.metamall.model.orderproduct.OrderProduct; import shop.mtcoding.metamall.model.user.User; diff --git a/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepository.java b/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepository.java index a910ea5..e3fc501 100644 --- a/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepository.java +++ b/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepository.java @@ -1,5 +1,6 @@ package shop.mtcoding.metamall.model.ordersheet; +import org.hibernate.annotations.BatchSize; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -10,17 +11,17 @@ public interface OrderSheetRepository extends JpaRepository { - @Query("select os from OrderSheet os left join fetch os.user u left join fetch os.orderProductList op left join fetch op.product p left join fetch p.seller s") + @Query("select distinct os from OrderSheet os left join fetch os.user u left join fetch os.orderProductList op left join fetch op.product p left join fetch p.seller s") List findAll(); - @Query("select os from OrderSheet os left join fetch os.orderProductList op left join fetch op.product p left join fetch p.seller s where os.user=:user") + @Query("select distinct os from OrderSheet os left join fetch os.orderProductList op left join fetch op.product p left join fetch p.seller s where os.user=:user") List findByUser(@Param("user") User user); - @Query("select os from OrderSheet os left join fetch os.user u left join fetch os.orderProductList op left join fetch op.product p where p.seller=:seller") + @Query("select distinct os from OrderSheet os left join fetch os.user u left join fetch os.orderProductList op left join fetch op.product p where p.seller=:seller") List findBySeller(@Param("seller") User seller); - @Query("select os from OrderSheet os left join fetch os.user u left join fetch os.orderProductList op where os.id=:id and u=:user") + @Query("select os from OrderSheet os left join fetch os.orderProductList op left join fetch op.product p left join fetch p.seller s where os.id=:id and os.user=:user") Optional findByIdAndUser(@Param("id") Long id, @Param("user") User user); - @Query("select os from OrderSheet os left join fetch os.user u left join fetch os.orderProductList op left join fetch op.product p left join fetch p.seller s where os.id=:id or s=:seller") - Optional findByIdOrSeller(@Param("id") Long id, @Param("seller") User seller); + @Query("select os from OrderSheet os left join fetch os.user u left join fetch os.orderProductList op left join fetch op.product p left join fetch p.seller s where os.id=:id and s=:seller") + Optional findByIdAndSeller(@Param("id") Long id, @Param("seller") User seller); } diff --git a/src/main/java/shop/mtcoding/metamall/model/user/User.java b/src/main/java/shop/mtcoding/metamall/model/user/User.java index 1a9dffe..28cabfb 100644 --- a/src/main/java/shop/mtcoding/metamall/model/user/User.java +++ b/src/main/java/shop/mtcoding/metamall/model/user/User.java @@ -13,6 +13,9 @@ @Entity @NoArgsConstructor public class User { + public enum Role{ + USER,SELLER,ADMIN + } @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @@ -22,7 +25,8 @@ public class User { @Column(nullable = false) private String password; private String email; - private String role; // USER(고객), SELLER(판매자), ADMIN(관리자) + @Enumerated(EnumType.ORDINAL) + private Role role; // USER(고객), SELLER(판매자), ADMIN(관리자) private LocalDateTime createdAt; private LocalDateTime updatedAt; @@ -41,7 +45,7 @@ public void updateLastLoginDate(LocalDateTime updatedAt) { } @Builder - public User(Long id, String username, String password, String email, String role, LocalDateTime createdAt) { + public User(Long id, String username, String password, String email, Role role, LocalDateTime createdAt) { this.id = id; this.username = username; this.password = password; diff --git a/src/test/java/shop/mtcoding/metamall/controller/OrderControllerTest.java b/src/test/java/shop/mtcoding/metamall/controller/OrderControllerTest.java index 36ebbcd..150e84d 100644 --- a/src/test/java/shop/mtcoding/metamall/controller/OrderControllerTest.java +++ b/src/test/java/shop/mtcoding/metamall/controller/OrderControllerTest.java @@ -1,6 +1,10 @@ package shop.mtcoding.metamall.controller; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -68,26 +72,64 @@ void order(){ } - @Test + @Nested @DisplayName("주문조회") - void getOrders(){ - //given - User ssar = userRepository.findByUsername("ssar").orElse(null); - HttpHeaders headers = headers(ssar); - HttpEntity requestEntity = new HttpEntity<>(headers); - - //when - - ResponseEntity response = testRestTemplate - .exchange( - "/api/orders", - HttpMethod.GET, - requestEntity, - ResponseDto.class - ); - - //then - assertEquals(HttpStatus.OK, response.getStatusCode()); + class Orders{ + @Test + @DisplayName("USER") + void getOrders1() throws JsonProcessingException { + //given + User ssar = userRepository.findByUsername("ssar").orElse(null); + HttpHeaders headers = headers(ssar); + HttpEntity requestEntity = new HttpEntity<>(headers); + + //when + + ResponseEntity response = testRestTemplate + .exchange( + "/api/orders", + HttpMethod.GET, + requestEntity, + ResponseDto.class + ); + + //then + assertEquals(HttpStatus.OK, response.getStatusCode()); + ObjectMapper om = new ObjectMapper(); + JsonNode jsonNode = om.readTree(om.writeValueAsString(response.getBody())); + JsonNode orderSheetNode = jsonNode.get("data"); + JsonNode productListNode = jsonNode.get("data").get(0).get("orderProductList"); + assertEquals(1,orderSheetNode.size()); + assertEquals(2,productListNode.size()); + } + + @Test + @DisplayName("SELLER") + void getOrders2() throws JsonProcessingException { + //given + User seller = userRepository.findByUsername("seller1").orElse(null); + HttpHeaders headers = headers(seller); + HttpEntity requestEntity = new HttpEntity<>(headers); + + //when + + ResponseEntity response = testRestTemplate + .exchange( + "/api/orders", + HttpMethod.GET, + requestEntity, + ResponseDto.class + ); + + //then + assertEquals(HttpStatus.OK, response.getStatusCode()); + ObjectMapper om = new ObjectMapper(); + JsonNode jsonNode = om.readTree(om.writeValueAsString(response.getBody())); + JsonNode orderSheetNode = jsonNode.get("data"); + JsonNode productListNode = jsonNode.get("data").get(0).get("orderProductList"); + assertEquals(2,orderSheetNode.size()); + assertEquals(1,productListNode.size()); + } } @Test diff --git a/src/test/java/shop/mtcoding/metamall/controller/UserControllerTest.java b/src/test/java/shop/mtcoding/metamall/controller/UserControllerTest.java index 90154ee..04b1e63 100644 --- a/src/test/java/shop/mtcoding/metamall/controller/UserControllerTest.java +++ b/src/test/java/shop/mtcoding/metamall/controller/UserControllerTest.java @@ -1,6 +1,7 @@ package shop.mtcoding.metamall.controller; import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; @@ -26,6 +27,7 @@ class UserControllerTest { private MockMvc mockMvc; @Test + @DisplayName("회원가입") void signUp() throws Exception { //given UserRequest.SignUpDto singup = UserRequest.SignUpDto.builder() @@ -33,7 +35,7 @@ void signUp() throws Exception { .email("test1@email.com") .password("1234") .passwordCheck("1234") - .role("USER") + .role(User.Role.USER) .build(); String requestBody = new ObjectMapper().writeValueAsString(singup); @@ -51,6 +53,7 @@ void signUp() throws Exception { } @Test + @DisplayName("로그인") void login() throws Exception { //given UserRequest.LoginDto login = UserRequest.LoginDto.builder() diff --git a/src/test/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepositoryTest.java b/src/test/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepositoryTest.java index 1697a40..d24585f 100644 --- a/src/test/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepositoryTest.java +++ b/src/test/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepositoryTest.java @@ -1,5 +1,9 @@ package shop.mtcoding.metamall.model.ordersheet; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; @@ -17,12 +21,23 @@ class OrderSheetRepositoryTest { @Autowired private UserRepository userRepository; @Autowired - ProductRepository productRepository; - @Autowired private OrderSheetRepository orderSheetRepository; @Test - void findByUser() { + void findAll(){ + //given + + //when + List orderSheets = orderSheetRepository.findAll(); + + //then + assertEquals(2,orderSheets.size()); + assertEquals("ssar",orderSheets.get(0).getUser().getUsername()); + assertEquals("tester",orderSheets.get(1).getUser().getUsername()); + } + + @Test + void findByUser(){ //given User user = userRepository.findByUsername("ssar").orElse(null); @@ -31,19 +46,30 @@ void findByUser() { //then assertEquals(1,orderSheet.size()); + assertEquals(2,orderSheet.get(0).getOrderProductList().size()); assertEquals(user,orderSheet.get(0).getUser()); } @Test - void findBySeller() { + void findBySeller() throws JsonProcessingException { //given User seller = userRepository.findByUsername("seller1").orElse(null); //when List orderSheet = orderSheetRepository.findBySeller(seller); + ObjectMapper om = new ObjectMapper(); + om.registerModule(new JavaTimeModule()); + om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + for(OrderSheet sheet : orderSheet){ + System.out.println(om.writeValueAsString(sheet)); + } //then - assertEquals(1,orderSheet.size()); + assertEquals(2,orderSheet.size()); + assertEquals(1,orderSheet.get(0).getOrderProductList().size()); + assertEquals(seller,orderSheet.get(0).getOrderProductList().get(0).getProduct().getSeller()); + assertEquals("ssar",orderSheet.get(0).getUser().getUsername()); + assertEquals("tester",orderSheet.get(1).getUser().getUsername()); } @Test @@ -61,13 +87,13 @@ void findByIdAndUser() { } @Test - void findByIdOrSeller(){ + void findByIdAndSeller(){ //given long id = 1; User seller = userRepository.findByUsername("seller1").orElse(null); //when - OrderSheet orderSheet = orderSheetRepository.findByIdOrSeller(id,seller).orElse(null); + OrderSheet orderSheet = orderSheetRepository.findByIdAndSeller(id,seller).orElse(null); //then assertEquals(id,orderSheet.getId()); diff --git a/src/test/java/shop/mtcoding/metamall/model/product/ProductRepositoryTest.java b/src/test/java/shop/mtcoding/metamall/model/product/ProductRepositoryTest.java index dee2325..18f3768 100644 --- a/src/test/java/shop/mtcoding/metamall/model/product/ProductRepositoryTest.java +++ b/src/test/java/shop/mtcoding/metamall/model/product/ProductRepositoryTest.java @@ -1,12 +1,59 @@ package shop.mtcoding.metamall.model.product; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import shop.mtcoding.metamall.model.ordersheet.OrderSheet; +import shop.mtcoding.metamall.model.user.User; +import shop.mtcoding.metamall.model.user.UserRepository; + +import java.util.List; import static org.junit.jupiter.api.Assertions.*; +@DataJpaTest class ProductRepositoryTest { + @Autowired + private ProductRepository productRepository; + + @Autowired + private UserRepository userRepository; + + @Test + void findAll() { + //given + + //when + List products = productRepository.findAll(); + + //then + assertEquals(2,products.size()); + } + + @Test + void findById() { + //given + long id = 1; + + //when + Product product = productRepository.findById(id).orElse(null); + + //then + assertEquals(id,product.getId()); + } + @Test void findByIdAndUser() { + //given + long id = 1; + User seller = userRepository.findByUsername("seller1").orElse(null); + + //when + Product product = productRepository.findByIdAndUser(id,seller).orElse(null); + + //then + assertEquals(id,product.getId()); + assertEquals(seller,product.getSeller()); } } \ No newline at end of file diff --git a/src/test/java/shop/mtcoding/metamall/model/user/UserRepositoryTest.java b/src/test/java/shop/mtcoding/metamall/model/user/UserRepositoryTest.java index fe15d8d..f6c408f 100644 --- a/src/test/java/shop/mtcoding/metamall/model/user/UserRepositoryTest.java +++ b/src/test/java/shop/mtcoding/metamall/model/user/UserRepositoryTest.java @@ -1,12 +1,26 @@ package shop.mtcoding.metamall.model.user; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import static org.junit.jupiter.api.Assertions.*; +@DataJpaTest class UserRepositoryTest { + @Autowired + private UserRepository userRepository; + @Test void findByUsername() { + //given + String username = "ssar"; + + //when + User user = userRepository.findByUsername(username).orElse(null); + + //then + assertEquals(username,user.getUsername()); } } \ No newline at end of file From df1eeacf80cc18fb1914c211a825fb4bedb9880f Mon Sep 17 00:00:00 2001 From: juyoung <107831692+ju-ei8ht@users.noreply.github.com> Date: Mon, 10 Apr 2023 18:23:31 +0900 Subject: [PATCH 3/5] Update README.md --- README.md | 69 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 0176619..f42fce1 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ # 상품주문서비스 토이프로젝트 -___ + ## DATE 2023.04.05 ~ 2023.04.09 -___ + +
+ ## STACKS ![Spring Boot](https://img.shields.io/badge/Spring_Boot-F2F4F9?style=for-the-badge&logo=spring-boot) @@ -10,47 +12,86 @@ ___ ![Git](https://img.shields.io/badge/GIT-E44C30?style=for-the-badge&logo=git&logoColor=white) ![GitHub](https://img.shields.io/badge/GitHub-100000?style=for-the-badge&logo=github&logoColor=white) +
+ ### DEPENDENCIES ``` 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' +implementation 'org.springframework.boot:spring-boot-starter-aop' +implementation 'org.springframework.boot:spring-boot-starter-validation' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' ``` -___ + +
+ ## 기능 -___ + ### 회원가입 + +
+ ### 로그인 - jwt 응답 + +
+ ### 상품등록 - 인증필요, 판매자권한 + +
+ ### 상풍목록보기 - 인증필요, 권한없음 + +
+ ### 상품상세보기 - 인증필요, 권한없음 + +
+ ### 상품수정하기 - 인증필요, 판매자권한 + +
+ ### 주문하기 - 인증필요, 고객권한 + +
+ ### 주문목록보기 - 인증필요, 고객권한(본인주문), 판매자권한(본인주문), 관리자권한(모든주문) + +
+ ### 주문취소하기 - CasCase 활용 - 인증필요, 고객권한, 판매자권한 -___ + +
+ ## ERD -[ERD](https://dbdiagram.io/d/642e523a5758ac5f172722f4) -___ -## TREE -___ -## API -| Page | Action | Method | URL | Request | Response | 비고 | -|------|--------| ------ |----------------|------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------| --- | -| 회원가입 | 회원가입 | POST | /api/user/join | {
"username":"username",
"password":"password",

"password_check":"passsword",
"email":"email@email.com",
"role":"USER"
} | {"status":200,
"msg":"성공",
"data":{...}
} | | -| 로그인 | 로그인 | POST | /api/user/login | {
"username":"username",
"password":"password"
} | {"status":200,
"msg":"성공",
"data":{...}
|| +![ERD](https://user-images.githubusercontent.com/107831692/230870048-a42ba4a0-a6d3-43ce-ab69-bdbd64121380.png) + +
+## API +| Page | Action | Method | URL | Request | Response | +|------|----------------------|--------|------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------| +| 회원가입 | signUp | POST | /api/user/signup | {
"username":"username",
"password":"password",

"password_check":"passsword",
"email":"email@email.com",
"role":"USER"
} | {
"status":200,
"msg":"성공",
"data":{...}
} | +| 로그인 | login | POST | /api/user/login | {
"username":"username",
"password":"password"
} | {
"status":200,
"msg":"성공",
"data":{...}
| +| 상품등록 | registerProduct | POST | /api/product | {
"name":"name",
"price":1000,
"qty":1
} | {
"status":200,
"msg":"성공",
"data":{...}
}| +| 상품목록 | getProducts | GET | /api/products | | {
"status":200,
"msg":"성공",
"data":{...}
} | +| 상품상세 | getProductDetails | GET | /api/product/{id} | | {
"status":200,
"msg":"성공",
"data":{...}
} | +| 상품수정 | updateProduct | PUT | /api/product/{id} | {
"name":"name",
"price":1000,
"qty":1
} | {
"status":200,
"msg":"성공",
"data":{...}
} | +| 주문 | order | POST | /api/order | {
"products":[
{
"count":1,
"product_id":1
},
{
"count":1,
"product_id":2
},...
]
} | {
"status":200,
"msg":"성공",
"data":{...}
} | +| 주문목록 | getOrders | GET | /api/orders | | {
"status":200,
"msg":"성공",
"data":{...}
} | +| 주문목록(관리자) | getAllOrders | GET | /api/admin/orders | | {
"status":200,
"msg":"성공",
"data":{...}
} | +| 주문취소 | cancelOrder | DELETE | /api/order/{id} | | {
"status":200,
"msg":"성공",
"data":{...}
} | From 25e9a9dd9931179d6dc29c60e224b1cfbe528ba3 Mon Sep 17 00:00:00 2001 From: ju-ei8ht Date: Tue, 11 Apr 2023 18:58:16 +0900 Subject: [PATCH 4/5] add to product qty update-cancel method, add to orderController test code for qty validation --- .../metamall/MetamallApplication.java | 30 +++++++++---------- .../metamall/config/FilterRegisterConfig.java | 3 -- .../metamall/controller/OrderController.java | 6 ++-- .../controller/ProductController.java | 2 -- .../model/orderproduct/OrderProduct.java | 2 +- .../orderproduct/OrderProductRepository.java | 11 ------- .../metamall/model/ordersheet/OrderSheet.java | 7 ----- .../ordersheet/OrderSheetRepository.java | 1 - .../metamall/model/product/Product.java | 10 +++++++ .../controller/OrderControllerTest.java | 19 +++++++----- .../ordersheet/OrderSheetRepositoryTest.java | 1 - .../model/product/ProductRepositoryTest.java | 3 +- 12 files changed, 42 insertions(+), 53 deletions(-) delete mode 100644 src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProductRepository.java diff --git a/src/main/java/shop/mtcoding/metamall/MetamallApplication.java b/src/main/java/shop/mtcoding/metamall/MetamallApplication.java index 186c999..62ae1d8 100644 --- a/src/main/java/shop/mtcoding/metamall/MetamallApplication.java +++ b/src/main/java/shop/mtcoding/metamall/MetamallApplication.java @@ -4,9 +4,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; -import shop.mtcoding.metamall.core.exception.Exception404; 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.product.Product; @@ -14,9 +12,7 @@ import shop.mtcoding.metamall.model.user.User; import shop.mtcoding.metamall.model.user.UserRepository; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -25,7 +21,7 @@ public class MetamallApplication { @Bean - CommandLineRunner initData(UserRepository userRepository, ProductRepository productRepository, OrderProductRepository orderProductRepository, OrderSheetRepository orderSheetRepository){ + CommandLineRunner initData(UserRepository userRepository, ProductRepository productRepository, OrderSheetRepository orderSheetRepository){ return (args)->{ // 여기에서 save 하면 됨. // bulk Collector는 saveAll 하면 됨. @@ -37,26 +33,28 @@ CommandLineRunner initData(UserRepository userRepository, ProductRepository prod List users = Stream.of(ssar,tester,seller1,seller2,admin).collect(Collectors.toList()); userRepository.saveAll(users); - Product product1 = Product.builder().user(seller1).name("제품1").qty(2).price(10000).build(); - Product product2 = Product.builder().user(seller2).name("제품2").qty(2).price(1000).build(); + Product product1 = Product.builder().user(seller1).name("제품1").qty(3).price(10000).build(); + Product product2 = Product.builder().user(seller2).name("제품2").qty(3).price(1000).build(); List products = Stream.of(product1,product2).collect(Collectors.toList()); productRepository.saveAll(products); - orderSheetSave(product1,product2,ssar,orderSheetRepository); - orderSheetSave(product1,product2,tester,orderSheetRepository); + orderSheetSave(product1,product2,ssar,productRepository,orderSheetRepository); + orderSheetSave(product1,product2,tester,productRepository,orderSheetRepository); }; } - private void orderSheetSave(Product product1, Product product2, User user, OrderSheetRepository orderSheetRepository){ - List orders = Stream.of( - OrderProduct.builder().product(product1).count(1).orderPrice(product1.getPrice()).build(), - OrderProduct.builder().product(product2).count(1).orderPrice(product2.getPrice()).build()) - .collect(Collectors.toList()); + + private void orderSheetSave(Product product1, Product product2, User user, ProductRepository productRepository, OrderSheetRepository orderSheetRepository){ + product1.order(1); + product2.order(1); + productRepository.saveAll(Arrays.asList(product1,product2)); + List orders = Arrays.asList( + OrderProduct.builder().product(product1).count(1).orderPrice(product1.getPrice()).build(), + OrderProduct.builder().product(product2).count(1).orderPrice(product2.getPrice()).build()); int totalPrice = orders.stream().mapToInt(OrderProduct::getOrderPrice).sum(); OrderSheet orderSheet = OrderSheet.builder().user(user).totalPrice(totalPrice).build(); - orders.forEach(orderSheet::addOrderProductList); + orders.forEach(order -> order.syncOrderSheet(orderSheet)); orderSheetRepository.save(orderSheet); } - public static void main(String[] args) { SpringApplication.run(MetamallApplication.class, args); } diff --git a/src/main/java/shop/mtcoding/metamall/config/FilterRegisterConfig.java b/src/main/java/shop/mtcoding/metamall/config/FilterRegisterConfig.java index c019b46..152033f 100644 --- a/src/main/java/shop/mtcoding/metamall/config/FilterRegisterConfig.java +++ b/src/main/java/shop/mtcoding/metamall/config/FilterRegisterConfig.java @@ -1,13 +1,10 @@ package shop.mtcoding.metamall.config; import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import shop.mtcoding.metamall.core.filter.JwtVerifyFilter; -import javax.servlet.DispatcherType; - @Configuration public class FilterRegisterConfig { diff --git a/src/main/java/shop/mtcoding/metamall/controller/OrderController.java b/src/main/java/shop/mtcoding/metamall/controller/OrderController.java index 20bdab0..06fd616 100644 --- a/src/main/java/shop/mtcoding/metamall/controller/OrderController.java +++ b/src/main/java/shop/mtcoding/metamall/controller/OrderController.java @@ -41,6 +41,7 @@ public ResponseEntity order(@Auth User user, @RequestBody OrderRequest.OrderD .map(dto -> { Product product = productRepository.findById(dto.getProductId()).orElseThrow( () -> new Exception404("상품을 찾을 수 없습니다")); + product.order(dto.getCount()); return OrderProduct.builder() .product(product) .count(dto.getCount()) @@ -52,8 +53,8 @@ public ResponseEntity order(@Auth User user, @RequestBody OrderRequest.OrderD .user(user) .totalPrice(totalPrice) .build(); - orders.forEach(orderSheet::addOrderProductList); - orderSheet = orderSheetRepository.save(orderSheet); + orders.forEach(order -> order.syncOrderSheet(orderSheet)); + orderSheetRepository.save(orderSheet); return ResponseEntity.ok().body(new ResponseDto<>().data(orderSheet)); } @@ -90,6 +91,7 @@ public ResponseEntity cancelOrder(@Auth User user, @PathVariable Long id){ () -> new Exception404("주문이 없습니다")); else if(user.getRole() == User.Role.SELLER) orderSheet = orderSheetRepository.findByIdAndSeller(id,user).orElseThrow( () -> new Exception404("주문이 없습니다")); + orderSheet.getOrderProductList().forEach(op -> op.getProduct().cancelOrder(op.getCount())); orderSheetRepository.delete(orderSheet); return ResponseEntity.ok().body(new ResponseDto<>().data(orderSheet)); } diff --git a/src/main/java/shop/mtcoding/metamall/controller/ProductController.java b/src/main/java/shop/mtcoding/metamall/controller/ProductController.java index 964841e..7732f1d 100644 --- a/src/main/java/shop/mtcoding/metamall/controller/ProductController.java +++ b/src/main/java/shop/mtcoding/metamall/controller/ProductController.java @@ -13,9 +13,7 @@ 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 java.util.List; @Validated diff --git a/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProduct.java b/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProduct.java index f6e5063..afffccd 100644 --- a/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProduct.java +++ b/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProduct.java @@ -50,7 +50,7 @@ public OrderProduct(Long id, Product product, Integer count, Integer orderPrice, this.orderSheet = orderSheet; } - public void setOrderSheet(OrderSheet orderSheet){ + public void syncOrderSheet(OrderSheet orderSheet){ if(this.orderSheet != null) this.orderSheet.getOrderProductList().remove(this); this.orderSheet = orderSheet; orderSheet.getOrderProductList().add(this); diff --git a/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProductRepository.java b/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProductRepository.java deleted file mode 100644 index ae823cd..0000000 --- a/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProductRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package shop.mtcoding.metamall.model.orderproduct; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; -import shop.mtcoding.metamall.model.ordersheet.OrderSheet; - -public interface OrderProductRepository extends JpaRepository { - @Query("select op from OrderProduct op join fetch op.orderSheet os where os=:os") - OrderProduct findByOrderSheet(@Param("os") OrderSheet os); -} diff --git a/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheet.java b/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheet.java index 60d06d8..c8c9c6a 100644 --- a/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheet.java +++ b/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheet.java @@ -3,8 +3,6 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import org.hibernate.annotations.Fetch; -import org.hibernate.annotations.FetchMode; import shop.mtcoding.metamall.model.orderproduct.OrderProduct; import shop.mtcoding.metamall.model.user.User; @@ -49,9 +47,4 @@ public OrderSheet(Long id, User user, Integer totalPrice, LocalDateTime createdA this.createdAt = createdAt; this.updatedAt = updatedAt; } - - public void addOrderProductList(OrderProduct orderProduct){ - orderProductList.add(orderProduct); - orderProduct.setOrderSheet(this); - } } diff --git a/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepository.java b/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepository.java index e3fc501..c54bfc4 100644 --- a/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepository.java +++ b/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepository.java @@ -1,6 +1,5 @@ package shop.mtcoding.metamall.model.ordersheet; -import org.hibernate.annotations.BatchSize; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; diff --git a/src/main/java/shop/mtcoding/metamall/model/product/Product.java b/src/main/java/shop/mtcoding/metamall/model/product/Product.java index fd67ac4..921cf85 100644 --- a/src/main/java/shop/mtcoding/metamall/model/product/Product.java +++ b/src/main/java/shop/mtcoding/metamall/model/product/Product.java @@ -3,6 +3,7 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import shop.mtcoding.metamall.core.exception.Exception400; import shop.mtcoding.metamall.dto.product.ProductRequest; import shop.mtcoding.metamall.model.user.User; @@ -25,6 +26,15 @@ public class Product { private LocalDateTime createdAt; private LocalDateTime updatedAt; + public void order(int count){ + if(this.qty < count) throw new Exception400("재고가 없습니다"); + this.qty -= count; + } + + public void cancelOrder(int count){ + this.qty += count; + } + @PrePersist protected void onCreate() { this.createdAt = LocalDateTime.now(); diff --git a/src/test/java/shop/mtcoding/metamall/controller/OrderControllerTest.java b/src/test/java/shop/mtcoding/metamall/controller/OrderControllerTest.java index 150e84d..58b4569 100644 --- a/src/test/java/shop/mtcoding/metamall/controller/OrderControllerTest.java +++ b/src/test/java/shop/mtcoding/metamall/controller/OrderControllerTest.java @@ -43,7 +43,7 @@ private HttpHeaders headers(User user){ @Test @DisplayName("주문") - void order(){ + void order() throws JsonProcessingException { //given OrderRequest.OrderProductDto product1 = OrderRequest.OrderProductDto.builder() .productId(1L) @@ -69,7 +69,11 @@ void order(){ //then assertEquals(HttpStatus.OK, response.getStatusCode()); - + ObjectMapper om = new ObjectMapper(); + JsonNode jsonNode = om.readTree(om.writeValueAsString(response.getBody())); + JsonNode productListNode = jsonNode.get("data").get("orderProductList"); + assertEquals(2,productListNode.size()); + assertEquals(0,productListNode.get(0).get("product").get("qty").asInt()); } @Nested @@ -97,9 +101,7 @@ void getOrders1() throws JsonProcessingException { assertEquals(HttpStatus.OK, response.getStatusCode()); ObjectMapper om = new ObjectMapper(); JsonNode jsonNode = om.readTree(om.writeValueAsString(response.getBody())); - JsonNode orderSheetNode = jsonNode.get("data"); JsonNode productListNode = jsonNode.get("data").get(0).get("orderProductList"); - assertEquals(1,orderSheetNode.size()); assertEquals(2,productListNode.size()); } @@ -125,9 +127,7 @@ void getOrders2() throws JsonProcessingException { assertEquals(HttpStatus.OK, response.getStatusCode()); ObjectMapper om = new ObjectMapper(); JsonNode jsonNode = om.readTree(om.writeValueAsString(response.getBody())); - JsonNode orderSheetNode = jsonNode.get("data"); JsonNode productListNode = jsonNode.get("data").get(0).get("orderProductList"); - assertEquals(2,orderSheetNode.size()); assertEquals(1,productListNode.size()); } } @@ -156,8 +156,9 @@ void getAllOrders(){ @Test @DisplayName("주문취소") - void cancelOrder() { + void cancelOrder() throws JsonProcessingException { //given + order(); long id = 1; User ssar = userRepository.findByUsername("ssar").orElse(null); HttpHeaders headers = headers(ssar); @@ -174,5 +175,9 @@ void cancelOrder() { //then assertEquals(HttpStatus.OK, response.getStatusCode()); + ObjectMapper om = new ObjectMapper(); + JsonNode jsonNode = om.readTree(om.writeValueAsString(response.getBody())); + JsonNode productListNode = jsonNode.get("data").get("orderProductList"); + assertEquals(1,productListNode.get(0).get("product").get("qty").asInt()); } } \ No newline at end of file diff --git a/src/test/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepositoryTest.java b/src/test/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepositoryTest.java index d24585f..c75d1b1 100644 --- a/src/test/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepositoryTest.java +++ b/src/test/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepositoryTest.java @@ -7,7 +7,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import shop.mtcoding.metamall.model.product.ProductRepository; import shop.mtcoding.metamall.model.user.User; import shop.mtcoding.metamall.model.user.UserRepository; diff --git a/src/test/java/shop/mtcoding/metamall/model/product/ProductRepositoryTest.java b/src/test/java/shop/mtcoding/metamall/model/product/ProductRepositoryTest.java index 18f3768..7785652 100644 --- a/src/test/java/shop/mtcoding/metamall/model/product/ProductRepositoryTest.java +++ b/src/test/java/shop/mtcoding/metamall/model/product/ProductRepositoryTest.java @@ -3,13 +3,12 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import shop.mtcoding.metamall.model.ordersheet.OrderSheet; import shop.mtcoding.metamall.model.user.User; import shop.mtcoding.metamall.model.user.UserRepository; import java.util.List; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; @DataJpaTest class ProductRepositoryTest { From c31070afe9c696cfaaeb79c885e6bdd6f61ff3f7 Mon Sep 17 00:00:00 2001 From: ju-ei8ht Date: Tue, 11 Apr 2023 20:23:39 +0900 Subject: [PATCH 5/5] add @DirtiesContext to reset changes in test --- .../mtcoding/metamall/controller/OrderControllerTest.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test/java/shop/mtcoding/metamall/controller/OrderControllerTest.java b/src/test/java/shop/mtcoding/metamall/controller/OrderControllerTest.java index 58b4569..40fbc78 100644 --- a/src/test/java/shop/mtcoding/metamall/controller/OrderControllerTest.java +++ b/src/test/java/shop/mtcoding/metamall/controller/OrderControllerTest.java @@ -10,6 +10,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.*; +import org.springframework.test.annotation.DirtiesContext; import shop.mtcoding.metamall.core.jwt.JwtProvider; import shop.mtcoding.metamall.dto.ResponseDto; import shop.mtcoding.metamall.dto.order.OrderRequest; @@ -42,6 +43,7 @@ private HttpHeaders headers(User user){ } @Test + @DirtiesContext @DisplayName("주문") void order() throws JsonProcessingException { //given @@ -155,10 +157,10 @@ void getAllOrders(){ } @Test + @DirtiesContext @DisplayName("주문취소") void cancelOrder() throws JsonProcessingException { //given - order(); long id = 1; User ssar = userRepository.findByUsername("ssar").orElse(null); HttpHeaders headers = headers(ssar); @@ -178,6 +180,6 @@ void cancelOrder() throws JsonProcessingException { ObjectMapper om = new ObjectMapper(); JsonNode jsonNode = om.readTree(om.writeValueAsString(response.getBody())); JsonNode productListNode = jsonNode.get("data").get("orderProductList"); - assertEquals(1,productListNode.get(0).get("product").get("qty").asInt()); + assertEquals(2,productListNode.get(0).get("product").get("qty").asInt()); } } \ No newline at end of file