diff --git a/README.md b/README.md new file mode 100644 index 0000000..0ddb967 --- /dev/null +++ b/README.md @@ -0,0 +1,74 @@ +# Springboot-MetaMall-Project + + +# 기능정리 +* 회원가입 (POST) +* 로그인 (POST) + * JWT 토큰 응답 +* 상품등록 (POST) + * 인증 필요, 판매자 권한 +* 상품목록보기 (GET) + * 인증 필요, 권한 없음 +* 상품상세보기 (GET) + * 인증 필요, 권한 없음 +* 상품수정하기 (PUT) + * 인증 필요, 판매자 권한 +* 상품삭제하기 (DELETE) + * 인증 필요, 판매자 권한 +* 주문하기 (POST) - OrderProduct를 생성하여, OrderSheet에 추가하세요 + * 인증 필요, 고객 권한 +* 고객입장 - 주문목록보기 (GET) + * 인증 필요, 고객 권한, 본인의 주문목록만 볼 수 있어야 함 +* 판매자입장 - 주문목록보기 (GET) + * 인증 필요, 판매자 권한, 모든 주문목록을 볼 수 있음 +* 고객입장 - 주문취소하기 (DELETE) - Casecade 옵션을 활용하세요. (양방향 매핑) + * 인증 필요, 고객 권한, 본인의 주문만 취소할 수 있다. +* 판매자입장 - 주문취소하기 (DELETE) - Casecade 옵션을 활용하세요. (양방향 매핑) + * 인증 필요, 판매자 권한 + + +# REST API + +* 회원가입 +POST http://localhost:8081/join +``` +{ + "username": "ssar", + "password": "1234", + "email": "ssar@nate.com", + "role": "ADMIN" +} +``` + +* 로그인 +POST http://localhost:8081/login +``` +{ + "username": "ssar", + "password": "1234" +} +``` +* 상품 등록 +POST http://localhost:8081/save +``` +{ + "name": "computer", + "price": 30000, + "quantity": 100, + "role": "SELLER" +} +``` +* 상품 전체 조회 +GET http://localhost:8081/listAll + +* 상품 개별 조회 +GET http://localhost:8081/product/1 + +* 상품 개별 수정 +PUT http://localhost:8081/product/1 + +* 상품 삭제 +DELETE http://localhost:8081/product/1 + +# 느낀점 +* 전체 적인 이해도가 부족하여 문제가 발생 시 해결이 잘 되지 않음 diff --git a/build.gradle b/build.gradle index 4943187..05c9628 100644 --- a/build.gradle +++ b/build.gradle @@ -19,6 +19,8 @@ repositories { } dependencies { + implementation 'org.springframework.boot:spring-boot-starter-aop' + implementation 'org.springframework.boot:spring-boot-starter-validation' implementation group: 'com.auth0', name: 'java-jwt', version: '4.3.0' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' diff --git a/src/main/java/shop/mtcoding/metamall/MetamallApplication.java b/src/main/java/shop/mtcoding/metamall/MetamallApplication.java index 487bb62..74e1bc5 100644 --- a/src/main/java/shop/mtcoding/metamall/MetamallApplication.java +++ b/src/main/java/shop/mtcoding/metamall/MetamallApplication.java @@ -4,14 +4,14 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; -import shop.mtcoding.metamall.model.orderproduct.OrderProduct; -import shop.mtcoding.metamall.model.orderproduct.OrderProductRepository; -import shop.mtcoding.metamall.model.ordersheet.OrderSheet; -import shop.mtcoding.metamall.model.ordersheet.OrderSheetRepository; +import shop.mtcoding.metamall.model.order.product.OrderProductRepository; +import shop.mtcoding.metamall.model.order.sheet.OrderSheetRepository; import shop.mtcoding.metamall.model.product.ProductRepository; import shop.mtcoding.metamall.model.user.User; import shop.mtcoding.metamall.model.user.UserRepository; +import java.util.Arrays; + @SpringBootApplication public class MetamallApplication { @@ -20,8 +20,10 @@ 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(); - userRepository.save(ssar); + User ssar = User.builder().username("ssar").password("1234").email("ssar@nate.com").status(true).role("USER").build(); + User seller = User.builder().username("seller").password("1234").email("seller@nate.com").status(true).role("SELLER").build(); + User admin = User.builder().username("admin").password("1234").email("ssar@nate.com").status(true).role("ADMIN").build(); + userRepository.saveAll(Arrays.asList(ssar, seller, admin)); }; } diff --git a/src/main/java/shop/mtcoding/metamall/config/MyFilterRegisterConfig.java b/src/main/java/shop/mtcoding/metamall/config/MyFilterRegisterConfig.java new file mode 100644 index 0000000..01ef8bc --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/config/MyFilterRegisterConfig.java @@ -0,0 +1,23 @@ +package shop.mtcoding.metamall.config; + +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import shop.mtcoding.metamall.core.filter.MyJwtVerifyFilter; + + +@Configuration +public class MyFilterRegisterConfig { + @Bean + public FilterRegistrationBean jwtVerifyFilterAdd() { + FilterRegistrationBean registration = new FilterRegistrationBean<>(); + registration.setFilter(new MyJwtVerifyFilter()); + registration.addUrlPatterns("/user/*"); + registration.addUrlPatterns("/products/*"); + registration.addUrlPatterns("/orders/*"); + registration.addUrlPatterns("/admin/*"); + registration.addUrlPatterns("/seller/*"); + registration.setOrder(1); + return registration; + } +} diff --git a/src/main/java/shop/mtcoding/metamall/config/MyWebMvcConfig.java b/src/main/java/shop/mtcoding/metamall/config/MyWebMvcConfig.java new file mode 100644 index 0000000..3d142e1 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/config/MyWebMvcConfig.java @@ -0,0 +1,49 @@ +package shop.mtcoding.metamall.config; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import shop.mtcoding.metamall.core.interceptor.MyAdminInterceptor; +import shop.mtcoding.metamall.core.interceptor.MySellerInterceptor; +import shop.mtcoding.metamall.core.resolver.MySessionArgumentResolver; + +import java.util.List; + +@RequiredArgsConstructor +@Configuration +public class MyWebMvcConfig implements WebMvcConfigurer { + + private final MyAdminInterceptor adminInterceptor; + private final MySellerInterceptor sellerInterceptor; + private final MySessionArgumentResolver mySessionArgumentResolver; + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedHeaders("*") + .allowedMethods("*") // GET, POST, PUT, DELETE (Javascript 요청 허용) + .allowedOriginPatterns("*") // 모든 IP 주소 허용 (프론트 앤드 IP만 허용하게 변경해야함. * 안됨) + .allowCredentials(true) + .exposedHeaders("Authorization"); // 옛날에는 디폴트로 브라우저에 노출되어 있었는데 지금은 아님 + } + + + // AOP는 매개변수 값 확인해서 권한 비교해야할 때 사용 + // Interceptor는 세션 권한으로 체크할 때 사용 + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(adminInterceptor) + .addPathPatterns("/admin/**"); + + registry.addInterceptor(sellerInterceptor) + .addPathPatterns("/seller/**"); + } + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(mySessionArgumentResolver); + } +} \ No newline at end of file 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..04873a5 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/controller/OrderController.java @@ -0,0 +1,55 @@ +package shop.mtcoding.metamall.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import shop.mtcoding.metamall.core.session.SessionUser; +import shop.mtcoding.metamall.dto.order.OrderProductRequest; +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.HttpServletRequest; +import javax.servlet.http.HttpSession; +import java.util.Optional; + + +@RequiredArgsConstructor +@RequestMapping("/order") +public class OrderController { + + private final ProductRepository productRepository; + private final UserRepository userRepository; + private final HttpSession session; + + // 상품 주문하기 + @PostMapping("/save/{id}") + public ResponseEntity save( + @PathVariable Long id, + @RequestBody OrderProductRequest.OrderDto orderDto, HttpServletRequest request) { + + Optional userOP = userRepository.findById(id); + SessionUser sessionUser = (SessionUser)session.getAttribute("sessionUser"); + + return null; + } + + // 주문 목록 보기 - 사용자 + @GetMapping("/list") + public ResponseEntity orderList() { +/* + List orderList = orderProductRepository.findAll(); + + if(orderList.isEmpty()) + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .body("주문 물품이 존재하지 않습니다"); + + ResponseDto> responseDto = new ResponseDto<>(); + responseDto.satData(orderList); + + return ResponseEntity.ok().body(responseDto); + + */ + return null; + } +} 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..3d34df3 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/controller/ProductController.java @@ -0,0 +1,131 @@ +package shop.mtcoding.metamall.controller; + +import lombok.RequiredArgsConstructor; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +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 javax.servlet.http.HttpServletRequest; +import java.util.*; + +@RequiredArgsConstructor +@RestController +@RequestMapping("/product") +public class ProductController { + + private final ProductRepository productRepository; + + // 상품 등록 + @PostMapping("/save") + public ResponseEntity save( + @RequestBody ProductRequest.ProductDto productDto, + HttpServletRequest request) { + + Product product = Product.builder() + .name(productDto.getName()) + .price(productDto.getPrice()) + .qty(productDto.getQuantity()) + .build(); + + productRepository.save(product); + ResponseDTO responseDto = new ResponseDTO<>().data(product); + + return ResponseEntity.ok().body(responseDto); + } + + // 상품 전체 조회 + @GetMapping("/listAll") + public ResponseEntity productList() { + + List productList = productRepository.findAll(); + + if(productList.isEmpty()) + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .body("물품이 존재하지 않습니다"); + + ResponseDTO responseDto = new ResponseDTO<>().data(productList); + + return ResponseEntity.ok().body(responseDto); + } + + // 상품 개별 조회 + @GetMapping("/{id}") + public ResponseEntity findById(@PathVariable Long id) { + + Optional productOP = productRepository.findById(id); + + if(productOP.isPresent()) { + ResponseDTO responseDto = new ResponseDTO<>().data(productOP); + return ResponseEntity.ok().body(responseDto); + } + + ResponseDTO responseDto = + new ResponseDTO<>().fail( + HttpStatus.BAD_REQUEST, + "Product Not Found", + "존재하지 않는 물품입니다"); + + return ResponseEntity.ok().body(responseDto); + } + + // 상품 개별 수정 + @PutMapping("/{id}") + public ResponseEntity modify( + @PathVariable Long id, + @RequestBody ProductRequest.ProductDto productDto) { + + Optional productOP = productRepository.findById(id); + + if(productOP.isPresent()) { + + //roduct product = productOP.get(); + + Product product = Product.builder() + .name(productDto.getName()) + .price(productDto.getPrice()) + .qty(productDto.getQuantity()) + .build(); + + Product savedProduct = productRepository.save(product); + productRepository.flush(); + + ResponseDTO responseDto = new ResponseDTO<>().data(savedProduct); + return ResponseEntity.ok().body(responseDto); + } + + ResponseDTO responseDto = + new ResponseDTO<>().fail( + HttpStatus.BAD_REQUEST, + "Product Not Found", + "존재하지 않는 물품입니다"); + + return ResponseEntity.ok().body(responseDto); + } + + // 상품 개별 삭제 + @DeleteMapping("/{id}") + public ResponseEntity delete( + @PathVariable Long id, + @RequestBody ProductRequest.ProductDto productDto) { + + Optional productOP = productRepository.findById(id); + + if (productOP.isPresent()) { + productRepository.deleteById(id); + return ResponseEntity.ok().body("삭제 완료 되었습니다"); + } + + ResponseDTO responseDto = + new ResponseDTO<>().fail( + HttpStatus.BAD_REQUEST, + "Product Not Found", + "존재하지 않는 물품입니다"); + + return ResponseEntity.ok().body(responseDto); + } +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/controller/UserController.java b/src/main/java/shop/mtcoding/metamall/controller/UserController.java index ddfee94..ec4c0d8 100644 --- a/src/main/java/shop/mtcoding/metamall/controller/UserController.java +++ b/src/main/java/shop/mtcoding/metamall/controller/UserController.java @@ -1,14 +1,15 @@ package shop.mtcoding.metamall.controller; import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.validation.Errors; import org.springframework.web.bind.annotation.*; 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.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; @@ -16,6 +17,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; +import javax.validation.Valid; +import java.io.IOException; import java.time.LocalDateTime; import java.util.Optional; @@ -28,7 +31,9 @@ public class UserController { private final HttpSession session; @PostMapping("/login") - public ResponseEntity login(@RequestBody UserRequest.LoginDto loginDto, HttpServletRequest request) { + public ResponseEntity login( + @RequestBody UserRequest.LoginDto loginDto, + HttpServletRequest request) { Optional userOP = userRepository.findByUsername(loginDto.getUsername()); if (userOP.isPresent()) { // 1. 유저 정보 꺼내기 @@ -54,10 +59,33 @@ public ResponseEntity login(@RequestBody UserRequest.LoginDto loginDto, HttpS loginLogRepository.save(loginLog); // 6. 응답 DTO 생성 - ResponseDto responseDto = new ResponseDto<>().data(loginUser); + ResponseDTO responseDto = new ResponseDTO<>().data(loginUser); return ResponseEntity.ok().header(JwtProvider.HEADER, jwt).body(responseDto); } else { - throw new Exception400("유저네임 혹은 아이디가 잘못되었습니다"); + throw new Exception400("username", "유저네임 혹은 아이디가 잘못되었습니다"); } } + + @PostMapping("/join") + public ResponseEntity join( + @Valid @RequestBody UserRequest.JoinDto joinDto) throws IOException { + + Optional userOP = userRepository.findByUsername(joinDto.getUsername()); + + if(userOP.isPresent()) { + return ResponseEntity.status(HttpStatus.CONFLICT) + .body("존재하는 회원 입니다"); + } + + User user = User.builder() + .username(joinDto.getUsername()) + .password(joinDto.getPassword()) + .email(joinDto.getEmail()) + .role(joinDto.getRole()) + .build(); + + userRepository.save(user); + + return ResponseEntity.ok(true); + } } diff --git a/src/main/java/shop/mtcoding/metamall/core/advice/MyErrorLogAdvice.java b/src/main/java/shop/mtcoding/metamall/core/advice/MyErrorLogAdvice.java new file mode 100644 index 0000000..039064b --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/advice/MyErrorLogAdvice.java @@ -0,0 +1,45 @@ +package shop.mtcoding.metamall.core.advice; + + +import lombok.NoArgsConstructor; +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.http.converter.HttpMessageNotReadableException; +import org.springframework.stereotype.Component; +import shop.mtcoding.metamall.core.session.SessionUser; +import shop.mtcoding.metamall.model.log.error.ErrorLog; +import shop.mtcoding.metamall.model.log.error.ErrorLogRepository; + +import javax.servlet.http.HttpSession; + +// @myErrorLog 사용 시 MyErrorLogAdvice 발동 +@RequiredArgsConstructor +@Aspect +@Component +public class MyErrorLogAdvice { + private final HttpSession session; + private final ErrorLogRepository errorLogRepository; + + @Pointcut("@annotation(shop.mtcoding.metamall.core.annotation.MyErrorLogRecord)") + public void myErrorLog(){} + + @Before("myErrorLog()") + public void errorLogAdvice(JoinPoint jp) throws HttpMessageNotReadableException { + Object[] args = jp.getArgs(); + + for (Object arg : args) { + // Exception400, 401 등이 다형성에 의해서 Exception에 걸린다. + if(arg instanceof Exception){ + Exception e = (Exception) arg; + SessionUser sessionUser = (SessionUser)session.getAttribute("sessionUser"); + if(sessionUser != null){ + ErrorLog errorLog = ErrorLog.builder().userId(sessionUser.getId()).msg(e.getMessage()).build(); + errorLogRepository.save(errorLog); + } + } + } + } +} 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..859ebc2 100644 --- a/src/main/java/shop/mtcoding/metamall/core/advice/MyExceptionAdvice.java +++ b/src/main/java/shop/mtcoding/metamall/core/advice/MyExceptionAdvice.java @@ -2,10 +2,14 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.NoHandlerFoundException; +import shop.mtcoding.metamall.core.annotation.MyErrorLogRecord; import shop.mtcoding.metamall.core.exception.*; +import shop.mtcoding.metamall.dto.ResponseDTO; import shop.mtcoding.metamall.model.log.error.ErrorLogRepository; @Slf4j @@ -13,30 +17,45 @@ @RestControllerAdvice public class MyExceptionAdvice { - private final ErrorLogRepository errorLogRepository; - + @MyErrorLogRecord @ExceptionHandler(Exception400.class) - public ResponseEntity badRequest(Exception400 e){ + public ResponseEntity badRequest(Exception400 e) { + // e.body() : ResponseDTO + // trace -> debug -> info -> warn -> error + log.debug("DEBUG : " + e.getMessage()); + log.info("INFO : " + e.getMessage()); + log.warn("WARN : " + e.getMessage()); + log.error("ERROR : " + e.getMessage()); return new ResponseEntity<>(e.body(), e.status()); } + @MyErrorLogRecord @ExceptionHandler(Exception401.class) - public ResponseEntity unAuthorized(Exception401 e){ + public ResponseEntity unAuthorized(Exception401 e) { return new ResponseEntity<>(e.body(), e.status()); } + @MyErrorLogRecord @ExceptionHandler(Exception403.class) - public ResponseEntity forbidden(Exception403 e){ + public ResponseEntity forbidden(Exception403 e) { return new ResponseEntity<>(e.body(), e.status()); } - @ExceptionHandler(Exception404.class) - public ResponseEntity notFound(Exception404 e){ - return new ResponseEntity<>(e.body(), e.status()); + @MyErrorLogRecord + @ExceptionHandler(NoHandlerFoundException.class) + public ResponseEntity notFound(NoHandlerFoundException e) { + ResponseDTO responseDTO = new ResponseDTO<>(); + responseDTO.fail(HttpStatus.NOT_FOUND, "notFound", e.getMessage()); + return new ResponseEntity<>(responseDTO, HttpStatus.NOT_FOUND); } - @ExceptionHandler(Exception500.class) - public ResponseEntity serverError(Exception500 e){ - return new ResponseEntity<>(e.body(), e.status()); + // 나머지 모든 예외(알 수 없는 에러) + @MyErrorLogRecord + @ExceptionHandler(Exception.class) + public ResponseEntity serverError(NoHandlerFoundException e) { + ResponseDTO responseDTO = new ResponseDTO<>(); + responseDTO.fail(HttpStatus.INTERNAL_SERVER_ERROR, "unknownServerError", e.getMessage()); + return new ResponseEntity<>(responseDTO, HttpStatus.INTERNAL_SERVER_ERROR); } + } diff --git a/src/main/java/shop/mtcoding/metamall/core/advice/MySameUserIdAdvice.java b/src/main/java/shop/mtcoding/metamall/core/advice/MySameUserIdAdvice.java new file mode 100644 index 0000000..aae7002 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/advice/MySameUserIdAdvice.java @@ -0,0 +1,4 @@ +package shop.mtcoding.metamall.core.advice; + +public class MySameUserIdAdvice { +} diff --git a/src/main/java/shop/mtcoding/metamall/core/advice/MyValidAdvice.java b/src/main/java/shop/mtcoding/metamall/core/advice/MyValidAdvice.java new file mode 100644 index 0000000..d74e882 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/advice/MyValidAdvice.java @@ -0,0 +1,38 @@ +package shop.mtcoding.metamall.core.advice; + +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.stereotype.Component; +import org.springframework.validation.Errors; +import shop.mtcoding.metamall.core.exception.Exception400; + +@Aspect +@Component +public class MyValidAdvice { + @Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)") + public void postMapping() { + } + + @Pointcut("@annotation(org.springframework.web.bind.annotation.PutMapping)") + public void putMapping() { + } + + @Before("postMapping() || putMapping()") + public void validationAdvice(JoinPoint jp) { + Object[] args = jp.getArgs(); + for (Object arg : args) { + if (arg instanceof Errors) { + Errors errors = (Errors) arg; + + if (errors.hasErrors()) { + throw new Exception400( + errors.getFieldErrors().get(0).getField(), + errors.getFieldErrors().get(0).getDefaultMessage() + ); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/core/annotation/MyErrorLogRecord.java b/src/main/java/shop/mtcoding/metamall/core/annotation/MyErrorLogRecord.java new file mode 100644 index 0000000..c284885 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/annotation/MyErrorLogRecord.java @@ -0,0 +1,14 @@ +package shop.mtcoding.metamall.core.annotation; + +import lombok.NoArgsConstructor; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// 이 깃발이 적용된 곳에 에러로그를 기록한다. +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface MyErrorLogRecord { +} diff --git a/src/main/java/shop/mtcoding/metamall/core/annotation/MySameUserIdCheck.java b/src/main/java/shop/mtcoding/metamall/core/annotation/MySameUserIdCheck.java new file mode 100644 index 0000000..24b3700 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/annotation/MySameUserIdCheck.java @@ -0,0 +1,6 @@ +package shop.mtcoding.metamall.core.annotation; + +// Pathvariable과 session id와 같은 지 체크(권한 체크) +// 직접 if()로 만드는 것이 좋다. +public @interface MySameUserIdCheck { +} diff --git a/src/main/java/shop/mtcoding/metamall/core/annotation/MySessionStore.java b/src/main/java/shop/mtcoding/metamall/core/annotation/MySessionStore.java new file mode 100644 index 0000000..de485a7 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/annotation/MySessionStore.java @@ -0,0 +1,17 @@ +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; + +/** + * @GetMapping() + * public String ok(User user) 일때 + * User에 session 값을 자동 주입 + * public String ok(@MySessionStore User user) + */ +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface MySessionStore { +} diff --git a/src/main/java/shop/mtcoding/metamall/core/exception/Exception400.java b/src/main/java/shop/mtcoding/metamall/core/exception/Exception400.java index d1b5fec..1a35a2d 100644 --- a/src/main/java/shop/mtcoding/metamall/core/exception/Exception400.java +++ b/src/main/java/shop/mtcoding/metamall/core/exception/Exception400.java @@ -2,20 +2,32 @@ import lombok.Getter; import org.springframework.http.HttpStatus; -import shop.mtcoding.metamall.dto.ResponseDto; +import shop.mtcoding.metamall.dto.ResponseDTO; +import shop.mtcoding.metamall.dto.ValidDTO; +import javax.validation.Valid; -// 유효성 실패 + +// 유효성 실패, 잘못된 파라메터 요청 +// key : value를 넘겨준다. +// "username", "username의 길이가 초과 되었습니다" @Getter public class Exception400 extends RuntimeException { - public Exception400(String message) { - super(message); + private String key; + private String value; + + public Exception400(String key, String value) { + super(value); + this.key = key; + this.value = value; } - public ResponseDto body(){ - ResponseDto responseDto = new ResponseDto<>(); - responseDto.fail(HttpStatus.BAD_REQUEST, "badRequest", getMessage()); - return responseDto; + public ResponseDTO body(){ + ResponseDTO responseDTO = new ResponseDTO<>(); + ValidDTO validDTO = new ValidDTO(key, value); + + responseDTO.fail(HttpStatus.BAD_REQUEST,"badRequest", validDTO); + return responseDTO; } public HttpStatus status(){ diff --git a/src/main/java/shop/mtcoding/metamall/core/exception/Exception401.java b/src/main/java/shop/mtcoding/metamall/core/exception/Exception401.java index 5d2f310..8dcb131 100644 --- a/src/main/java/shop/mtcoding/metamall/core/exception/Exception401.java +++ b/src/main/java/shop/mtcoding/metamall/core/exception/Exception401.java @@ -3,7 +3,7 @@ import lombok.Getter; import org.springframework.http.HttpStatus; -import shop.mtcoding.metamall.dto.ResponseDto; +import shop.mtcoding.metamall.dto.ResponseDTO; // 인증 안됨 @@ -13,8 +13,8 @@ public Exception401(String message) { super(message); } - public ResponseDto body(){ - ResponseDto responseDto = new ResponseDto<>(); + public ResponseDTO body(){ + ResponseDTO responseDto = new ResponseDTO<>(); responseDto.fail(HttpStatus.UNAUTHORIZED, "unAuthorized", getMessage()); return responseDto; } diff --git a/src/main/java/shop/mtcoding/metamall/core/exception/Exception403.java b/src/main/java/shop/mtcoding/metamall/core/exception/Exception403.java index c8dc137..e65c2fc 100644 --- a/src/main/java/shop/mtcoding/metamall/core/exception/Exception403.java +++ b/src/main/java/shop/mtcoding/metamall/core/exception/Exception403.java @@ -2,7 +2,7 @@ import lombok.Getter; import org.springframework.http.HttpStatus; -import shop.mtcoding.metamall.dto.ResponseDto; +import shop.mtcoding.metamall.dto.ResponseDTO; // 권한 없음 @@ -12,8 +12,8 @@ public Exception403(String message) { super(message); } - public ResponseDto body(){ - ResponseDto responseDto = new ResponseDto<>(); + public ResponseDTO body(){ + ResponseDTO responseDto = new ResponseDTO<>(); responseDto.fail(HttpStatus.FORBIDDEN, "forbidden", getMessage()); return responseDto; } diff --git a/src/main/java/shop/mtcoding/metamall/core/filter/MyJwtVerifyFilter.java b/src/main/java/shop/mtcoding/metamall/core/filter/MyJwtVerifyFilter.java new file mode 100644 index 0000000..c4dbe57 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/filter/MyJwtVerifyFilter.java @@ -0,0 +1,52 @@ +package shop.mtcoding.metamall.core.filter; + + +import com.auth0.jwt.exceptions.SignatureVerificationException; +import com.auth0.jwt.exceptions.TokenExpiredException; +import com.auth0.jwt.interfaces.DecodedJWT; +import shop.mtcoding.metamall.core.exception.Exception400; +import shop.mtcoding.metamall.core.jwt.JwtProvider; +import shop.mtcoding.metamall.core.session.SessionUser; +import shop.mtcoding.metamall.core.util.MyFilterResponseUtils; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; + +public class MyJwtVerifyFilter implements Filter { + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + String prefixJwt = req.getHeader(JwtProvider.HEADER); + + if(prefixJwt == null){ + MyFilterResponseUtils.badRequest( + resp, + new Exception400("authorization", "토큰이 전달되지 않았습니다")); + return; + } + + String jwt = prefixJwt.replace(JwtProvider.TOKEN_PREFIX, ""); + try { + DecodedJWT decodedJWT = JwtProvider.verify(jwt); + Long id = decodedJWT.getClaim("id").asLong(); + String role = decodedJWT.getClaim("role").asString(); + + // 세션을 사용하는 이유는 role(권한) 처리를 하기 위해서이다. + HttpSession session = req.getSession(); + SessionUser sessionUser = SessionUser.builder().id(id).role(role).build(); + session.setAttribute("sessionUser", sessionUser); + System.out.println("세션 생성됨"); + chain.doFilter(req, resp); + }catch (SignatureVerificationException sve){ + MyFilterResponseUtils.unAuthorized(resp, sve); + }catch (TokenExpiredException tee){ + MyFilterResponseUtils.unAuthorized(resp, tee); + } + } + +} diff --git a/src/main/java/shop/mtcoding/metamall/core/interceptor/MyAdminInterceptor.java b/src/main/java/shop/mtcoding/metamall/core/interceptor/MyAdminInterceptor.java new file mode 100644 index 0000000..1428f68 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/interceptor/MyAdminInterceptor.java @@ -0,0 +1,26 @@ +package shop.mtcoding.metamall.core.interceptor; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.HandlerInterceptor; +import shop.mtcoding.metamall.core.exception.Exception403; +import shop.mtcoding.metamall.core.session.SessionUser; +import shop.mtcoding.metamall.core.util.MyFilterResponseUtils; + + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +@Configuration +public class MyAdminInterceptor implements HandlerInterceptor { + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + HttpSession session = request.getSession(); + SessionUser sessionUser = (SessionUser) session.getAttribute("sessionUser"); + + if(!sessionUser.getRole().equals("ADMIN")){ + throw new Exception403("권한이 없습니다"); + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/core/interceptor/MySellerInterceptor.java b/src/main/java/shop/mtcoding/metamall/core/interceptor/MySellerInterceptor.java new file mode 100644 index 0000000..9b044b3 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/interceptor/MySellerInterceptor.java @@ -0,0 +1,27 @@ +package shop.mtcoding.metamall.core.interceptor; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.HandlerInterceptor; +import shop.mtcoding.metamall.core.exception.Exception403; +import shop.mtcoding.metamall.core.session.SessionUser; +import shop.mtcoding.metamall.core.util.MyFilterResponseUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +@Configuration +public class MySellerInterceptor implements HandlerInterceptor { + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + HttpSession session = request.getSession(); + SessionUser sessionUser = (SessionUser) session.getAttribute("sessionUser"); + + if(sessionUser.getRole().equals("SELLER") || sessionUser.getRole().equals("ADMIN")){ + return true; + }else{ + throw new Exception403("권한이 없습니다"); + } + + } +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/core/resolver/MySessionArgumentResolver.java b/src/main/java/shop/mtcoding/metamall/core/resolver/MySessionArgumentResolver.java new file mode 100644 index 0000000..b031dd1 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/resolver/MySessionArgumentResolver.java @@ -0,0 +1,33 @@ +package shop.mtcoding.metamall.core.resolver; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.MethodParameter; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; +import shop.mtcoding.metamall.core.annotation.MySessionStore; +import shop.mtcoding.metamall.core.session.SessionUser; + + +import javax.servlet.http.HttpSession; + +@RequiredArgsConstructor +@Configuration +public class MySessionArgumentResolver implements HandlerMethodArgumentResolver { + + private final HttpSession session; + + @Override + public boolean supportsParameter(MethodParameter parameter) { + boolean check1 = parameter.getParameterAnnotation(MySessionStore.class) != null; + boolean check2 = SessionUser.class.equals(parameter.getParameterType()); + return check1 && check2; + } + + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { + return session.getAttribute("sessionUser"); + } +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/core/session/SessionUser.java b/src/main/java/shop/mtcoding/metamall/core/session/SessionUser.java new file mode 100644 index 0000000..20b138b --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/session/SessionUser.java @@ -0,0 +1,16 @@ +package shop.mtcoding.metamall.core.session; + +import lombok.Builder; +import lombok.Getter; + +@Getter +public class SessionUser { + private Long id; + private String role; + + @Builder + public SessionUser(Long id, String role) { + this.id = id; + this.role = role; + } +} diff --git a/src/main/java/shop/mtcoding/metamall/core/util/MyDateUtil.java b/src/main/java/shop/mtcoding/metamall/core/util/MyDateUtil.java new file mode 100644 index 0000000..3f92cf6 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/util/MyDateUtil.java @@ -0,0 +1,10 @@ +package shop.mtcoding.metamall.core.util; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class MyDateUtil { + public static String toStringFormat(LocalDateTime localDateTime) { + return localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } +} diff --git a/src/main/java/shop/mtcoding/metamall/core/util/MyFilterResponseUtils.java b/src/main/java/shop/mtcoding/metamall/core/util/MyFilterResponseUtils.java new file mode 100644 index 0000000..4b2b38c --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/util/MyFilterResponseUtils.java @@ -0,0 +1,44 @@ +package shop.mtcoding.metamall.core.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.http.HttpStatus; +import shop.mtcoding.metamall.core.exception.Exception400; +import shop.mtcoding.metamall.dto.ResponseDTO; +import shop.mtcoding.metamall.dto.ValidDTO; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +// Filter는 가장 상단에 존재하므로 throw를 날릴 수 없으므로 이곳에서 예외 핸들러를 처리한다. +public class MyFilterResponseUtils { + public static void badRequest(HttpServletResponse resp, Exception400 e) throws IOException { + resp.setStatus(400); + resp.setContentType("application/json; charset=utf-8"); + ValidDTO validDTO = new ValidDTO(e.getKey(), e.getValue()); + ResponseDTO responseDto = + new ResponseDTO<>().fail(HttpStatus.BAD_REQUEST, "badRequest", validDTO); + ObjectMapper om = new ObjectMapper(); + String responseBody = om.writeValueAsString(responseDto); + resp.getWriter().println(responseBody); + } + + public static void unAuthorized(HttpServletResponse resp, Exception e) throws IOException { + resp.setStatus(401); + resp.setContentType("application/json; charset=utf-8"); + ResponseDTO responseDto = + new ResponseDTO<>().fail(HttpStatus.UNAUTHORIZED, "unAuthorized", e.getMessage()); + ObjectMapper om = new ObjectMapper(); + String responseBody = om.writeValueAsString(responseDto); + resp.getWriter().println(responseBody); + } + + public static void forbidden(HttpServletResponse resp, Exception e) throws IOException { + resp.setStatus(403); + resp.setContentType("application/json; charset=utf-8"); + ResponseDTO responseDto = + new ResponseDTO<>().fail(HttpStatus.FORBIDDEN, "forbidden", e.getMessage()); + ObjectMapper om = new ObjectMapper(); + String responseBody = om.writeValueAsString(responseDto); + resp.getWriter().println(responseBody); + } +} diff --git a/src/main/java/shop/mtcoding/metamall/dto/ResponseDto.java b/src/main/java/shop/mtcoding/metamall/dto/ResponseDto.java index 7f190c6..8c90c51 100644 --- a/src/main/java/shop/mtcoding/metamall/dto/ResponseDto.java +++ b/src/main/java/shop/mtcoding/metamall/dto/ResponseDto.java @@ -4,23 +4,27 @@ import org.springframework.http.HttpStatus; @Getter -public class ResponseDto { +public class ResponseDTO { private Integer status; // 에러시에 의미 있음. private String msg; // 에러시에 의미 있음. ex) badRequest private T data; // 에러시에는 구체적인 에러 내용 ex) username이 입력되지 않았습니다 - public ResponseDto(){ + // delete하여 성공했을 경우만 사용한다. + public ResponseDTO(){ this.status = HttpStatus.OK.value(); this.msg = "성공"; this.data = null; } - public ResponseDto data(T data){ + // get, post, put : 성공 시 + // get : 조회 시 data를 담아서 돌려준다. + public ResponseDTO data(T data){ this.data = data; // 응답할 데이터 바디 return this; } - public ResponseDto fail(HttpStatus httpStatus, String msg, T data){ + // 4가지 요청에 대한 실패 시 + public ResponseDTO fail(HttpStatus httpStatus, String msg, T data){ this.status = httpStatus.value(); this.msg = msg; // 에러 제목 this.data = data; // 에러 내용 diff --git a/src/main/java/shop/mtcoding/metamall/dto/ValidDTO.java b/src/main/java/shop/mtcoding/metamall/dto/ValidDTO.java new file mode 100644 index 0000000..cf9eca6 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/dto/ValidDTO.java @@ -0,0 +1,11 @@ +package shop.mtcoding.metamall.dto; + +import lombok.*; + +@Getter +@Setter +@AllArgsConstructor +public class ValidDTO { + private String key; + private String value; +} diff --git a/src/main/java/shop/mtcoding/metamall/dto/order/OrderProductRequest.java b/src/main/java/shop/mtcoding/metamall/dto/order/OrderProductRequest.java new file mode 100644 index 0000000..66be7e9 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/dto/order/OrderProductRequest.java @@ -0,0 +1,14 @@ +package shop.mtcoding.metamall.dto.order; + +import lombok.Getter; +import lombok.Setter; +import shop.mtcoding.metamall.model.product.Product; + +public class OrderProductRequest { + @Getter + @Setter + public static class OrderDto { + private Product product; + private Integer count; + } +} \ No newline at end of file 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..caab5c5 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/dto/product/ProductRequest.java @@ -0,0 +1,19 @@ +package shop.mtcoding.metamall.dto.product; + +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotNull; + +public class ProductRequest { + + @Getter @Setter + public static class ProductDto { + @NotNull + private String name; + @NotNull + private Integer price; + @NotNull + private Integer quantity; + } +} 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..af70616 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,27 @@ package shop.mtcoding.metamall.dto.user; +import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Getter; import lombok.Setter; +import javax.validation.constraints.NotEmpty; + public class UserRequest { @Getter @Setter public static class LoginDto { private String username; private String password; } + + @Getter @Setter + public static class JoinDto { + @NotEmpty + private String username; + @NotEmpty + private String password; + @NotEmpty + private String email; + @NotEmpty + private String role; + } } diff --git a/src/main/java/shop/mtcoding/metamall/model/log/error/ErrorLog.java b/src/main/java/shop/mtcoding/metamall/model/log/error/ErrorLog.java index fbfe7e5..41dad1d 100644 --- a/src/main/java/shop/mtcoding/metamall/model/log/error/ErrorLog.java +++ b/src/main/java/shop/mtcoding/metamall/model/log/error/ErrorLog.java @@ -17,6 +17,7 @@ public class ErrorLog { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(nullable = false, length = 100000) private String msg; private Long userId; diff --git a/src/main/java/shop/mtcoding/metamall/model/order/product/OrderProduct.java b/src/main/java/shop/mtcoding/metamall/model/order/product/OrderProduct.java new file mode 100644 index 0000000..0639370 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/model/order/product/OrderProduct.java @@ -0,0 +1,61 @@ +package shop.mtcoding.metamall.model.order.product; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.validator.constraints.CodePointLength; +import shop.mtcoding.metamall.model.order.sheet.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 +public class OrderProduct { // 주문 상품 + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + // checkpoint : 무한참조 + @ManyToOne + private Product product; + @Column(nullable = false) + private Integer count; // 상품 주문 개수 + @Column(nullable = false) + private Integer orderPrice; // 상품 주문 금액 + @Column(nullable = false) + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + @ManyToOne + private OrderSheet orderSheet; + + @PrePersist + protected void onCreate() { + this.createdAt = LocalDateTime.now(); + } + + @PreUpdate + protected void onUpdate() { + this.updatedAt = LocalDateTime.now(); + } + + // check point + public void syncOderSheet(OrderSheet orderSheet) { + this.orderSheet = orderSheet; + } + + @Builder + public OrderProduct(Long id, Product product, Integer count, Integer orderPrice, LocalDateTime createdAt, LocalDateTime updatedAt, OrderSheet orderSheet) { + this.id = id; + this.product = product; + this.count = count; + this.orderPrice = orderPrice; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + this.orderSheet = orderSheet; + } +} diff --git a/src/main/java/shop/mtcoding/metamall/model/order/product/OrderProductRepository.java b/src/main/java/shop/mtcoding/metamall/model/order/product/OrderProductRepository.java new file mode 100644 index 0000000..1b686dc --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/model/order/product/OrderProductRepository.java @@ -0,0 +1,6 @@ +package shop.mtcoding.metamall.model.order.product; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface OrderProductRepository extends JpaRepository { +} diff --git a/src/main/java/shop/mtcoding/metamall/model/order/sheet/OrderSheet.java b/src/main/java/shop/mtcoding/metamall/model/order/sheet/OrderSheet.java new file mode 100644 index 0000000..c1826cc --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/model/order/sheet/OrderSheet.java @@ -0,0 +1,53 @@ +package shop.mtcoding.metamall.model.order.sheet; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import shop.mtcoding.metamall.model.order.product.OrderProduct; +import shop.mtcoding.metamall.model.user.User; + +import javax.persistence.*; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@NoArgsConstructor +@Setter // DTO 만들면 삭제해야됨 +@Getter +@Table(name = "order_sheet_tb") +@Entity +public class OrderSheet { // 주문서 + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @ManyToOne + private User user; // 주문자 + // check point + @OneToMany(mappedBy = "orderSheet") + private List orderProductList = new ArrayList<>(); // 총 주문 상품 리스트 + @Column(nullable = false) + private Integer totalPrice; // 총 주문 금액 (총 주문 상품 리스트의 orderPrice 합) + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + @PrePersist + protected void onCreate() { + this.createdAt = LocalDateTime.now(); + } + + @PreUpdate + protected void onUpdate() { + this.updatedAt = LocalDateTime.now(); + } + + // 연관관계 메서드 구현 필요 + @Builder + public OrderSheet(Long id, User user, Integer totalPrice, LocalDateTime createdAt, LocalDateTime updatedAt) { + this.id = id; + this.user = user; + this.totalPrice = totalPrice; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } +} diff --git a/src/main/java/shop/mtcoding/metamall/model/order/sheet/OrderSheetRepository.java b/src/main/java/shop/mtcoding/metamall/model/order/sheet/OrderSheetRepository.java new file mode 100644 index 0000000..9d384e4 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/model/order/sheet/OrderSheetRepository.java @@ -0,0 +1,13 @@ +package shop.mtcoding.metamall.model.order.sheet; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +public interface OrderSheetRepository extends JpaRepository { + // 내가 주문한 주문 목록 보기 + @Query("select os from OrderSheet os where os.user .id = :userId") + List findByUserId(@Param("userId") Long userId); +} 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..b293d12 100644 --- a/src/main/java/shop/mtcoding/metamall/model/product/Product.java +++ b/src/main/java/shop/mtcoding/metamall/model/product/Product.java @@ -4,6 +4,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import shop.mtcoding.metamall.model.user.User; import javax.persistence.*; import java.time.LocalDateTime; @@ -17,12 +18,39 @@ public class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @ManyToOne + private User seller; // seller_id + @Column(nullable = false, length = 50) private String name; // 상품 이름 + @Column(nullable = false) private Integer price; // 상품 가격 + @Column(nullable = false) private Integer qty; // 상품 재고 + @Column(nullable = false) private LocalDateTime createdAt; private LocalDateTime updatedAt; + // 판매자가 상품을 등록 후 변경 가능 + // setter 사용금지 + public void update(String name, Integer price, Integer qty) { + this.name = name; + this.price = price; + this.qty = qty; + } + + // 주문 시 재고량 check(구매자) + // 객체의 상태를 변경하는 것은 의미있는 메소드로(setter 사용금지) + public void updateQty(Integer orderCount) { + if(this.qty < orderCount) { + // 주문수량이 재고수량을 초과했습니다. + } + } + + // 주문 취소 (재고수량 변경 - rollback) + public void rollbackQty(Integer orderCount) { + this.qty += orderCount; + } + @PrePersist protected void onCreate() { this.createdAt = LocalDateTime.now(); @@ -34,8 +62,9 @@ protected void onUpdate() { } @Builder - public Product(Long id, String name, Integer price, Integer qty, LocalDateTime createdAt, LocalDateTime updatedAt) { + public Product(Long id, User seller, String name, Integer price, Integer qty, LocalDateTime createdAt, LocalDateTime updatedAt) { this.id = id; + this.seller = seller; this.name = name; this.price = price; this.qty = qty; 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..71cbe19 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,12 @@ 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 { } 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..37931bf 100644 --- a/src/main/java/shop/mtcoding/metamall/model/user/User.java +++ b/src/main/java/shop/mtcoding/metamall/model/user/User.java @@ -1,5 +1,6 @@ package shop.mtcoding.metamall.model.user; +import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -17,10 +18,18 @@ public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(unique = true, nullable = false, length = 20) private String username; + @JsonIgnore + @Column(nullable = false, length = 60) private String password; + @Column(nullable = false, length = 50) private String email; + @Column(nullable = false, length = 10) private String role; // USER(고객), SELLER(판매자), ADMIN(관리자) + @Column(nullable = false, length = 10) + private Boolean status; // true : 활성계정 false : 비활성계정 + @Column(nullable = false) private LocalDateTime createdAt; private LocalDateTime updatedAt; @@ -34,13 +43,27 @@ protected void onUpdate() { this.updatedAt = LocalDateTime.now(); } + // 권한 변경(관리자) + public void updateRole(String role) { + if(this.role.equals(role)) { + // checkpoint 동일한 권한으로 변경할 수 없습니다. + } + } + + // 회원 탈퇴 + public void delete() { + this.status = false; + } + @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, String role, Boolean status, LocalDateTime createdAt, LocalDateTime updatedAt) { this.id = id; this.username = username; this.password = password; this.email = email; this.role = role; + this.status = status; this.createdAt = createdAt; + this.updatedAt = updatedAt; } } 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..2f642f9 100644 --- a/src/main/java/shop/mtcoding/metamall/model/user/UserRepository.java +++ b/src/main/java/shop/mtcoding/metamall/model/user/UserRepository.java @@ -6,7 +6,7 @@ import java.util.Optional; -public interface UserRepository extends JpaRepository { +public interface UserRepository extends JpaRepository { @Query("select u from User u where u.username = :username") Optional findByUsername(@Param("username") String username); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 1d9bd50..eca7302 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -3,7 +3,7 @@ server: encoding: charset: utf-8 force: true - + port: 8081 spring: datasource: url: jdbc:h2:mem:test;MODE=MySQL @@ -20,7 +20,18 @@ spring: properties: hibernate: format_sql: true - default_batch_fetch_size: 100 # in query 자동 작성 + # in query 자동 작성 + default_batch_fetch_size: 100 + # 404 처리하는 법 + mvc: + throw-exception-if-no-handler-found: true + web: + resources: + add-mappings: false + # hibernateLazyInitializer 오류 해결법 + # jackson: + # serialization: + # fail-on-empty-beans: false logging: level: