diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f42fce1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,97 @@
+# 상품주문서비스 토이프로젝트
+
+## DATE
+2023.04.05 ~ 2023.04.09
+
+
+
+## STACKS
+
+
+
+
+
+
+
+
+### 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
+
+
+
+
+## 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":{...}
} |
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..62ae1d8 100644
--- a/src/main/java/shop/mtcoding/metamall/MetamallApplication.java
+++ b/src/main/java/shop/mtcoding/metamall/MetamallApplication.java
@@ -5,26 +5,56 @@
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.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.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
@SpringBootApplication
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 하면 됨.
- 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").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(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,productRepository,orderSheetRepository);
+ orderSheetSave(product1,product2,tester,productRepository,orderSheetRepository);
};
}
+ 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(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 f5ea4db..152033f 100644
--- a/src/main/java/shop/mtcoding/metamall/config/FilterRegisterConfig.java
+++ b/src/main/java/shop/mtcoding/metamall/config/FilterRegisterConfig.java
@@ -12,8 +12,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..06fd616
--- /dev/null
+++ b/src/main/java/shop/mtcoding/metamall/controller/OrderController.java
@@ -0,0 +1,98 @@
+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.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("상품을 찾을 수 없습니다"));
+ product.order(dto.getCount());
+ 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(order -> order.syncOrderSheet(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() == 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));
+ }
+
+ /**
+ * 모든 주문목록(관리자)
+ */
+ @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() == User.Role.USER) orderSheet = orderSheetRepository.findByIdAndUser(id,user).orElseThrow(
+ () -> 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
new file mode 100644
index 0000000..7732f1d
--- /dev/null
+++ b/src/main/java/shop/mtcoding/metamall/controller/ProductController.java
@@ -0,0 +1,75 @@
+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 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..1c87721
--- /dev/null
+++ b/src/main/java/shop/mtcoding/metamall/core/advice/CheckAdvice.java
@@ -0,0 +1,70 @@
+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){
+ if(!roleCheck(jp, User.Role.USER)) throw new Exception403("권한이 없습니다");
+ }
+
+ @Before("roleSeller()")
+ public void roleSellerCheck(JoinPoint jp){
+ if(!roleCheck(jp, User.Role.SELLER)) throw new Exception403("권한이 없습니다");
+ }
+
+ @Before("roleAdmin()")
+ public void roleAdminCheck(JoinPoint jp){
+ if(!roleCheck(jp, User.Role.ADMIN)) throw new Exception403("권한이 없습니다");
+ }
+
+ private boolean roleCheck(JoinPoint jp, User.Role role){
+ for(Object arg:jp.getArgs()){
+ if(arg instanceof User) return ((User) arg).getRole() == role;
+ }
+ return true;
+ }
+}
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..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;
@@ -31,12 +32,12 @@ 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();
- String role = decodedJWT.getClaim("role").asString();
+ Long id = decodedJWT.getClaim("id").asLong();
+ 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 59f402c..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 Integer id;
- private String role;
+ private Long id;
+ private User.Role role;
@Builder
- public LoginUser(Integer 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/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..27413e4 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,51 @@
package shop.mtcoding.metamall.dto.user;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Builder;
import lombok.Getter;
-import lombok.Setter;
+import shop.mtcoding.metamall.model.user.User;
+
+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 User.Role role;
+
+ @Builder
+ public SignUpDto(String username, String password, String passwordCheck, String email, User.Role 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..afffccd 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 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 6f1238c..0000000
--- a/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProductRepository.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package shop.mtcoding.metamall.model.orderproduct;
-
-import org.springframework.data.jpa.repository.JpaRepository;
-
-public interface OrderProductRepository extends JpaRepository {
-}
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..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,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;
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..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,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 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 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 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.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 and s=:seller")
+ Optional findByIdAndSeller(@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..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,16 +3,17 @@
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
-import lombok.Setter;
+import shop.mtcoding.metamall.core.exception.Exception400;
+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,9 +21,20 @@ 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;
+ 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();
@@ -34,12 +46,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..28cabfb 100644
--- a/src/main/java/shop/mtcoding/metamall/model/user/User.java
+++ b/src/main/java/shop/mtcoding/metamall/model/user/User.java
@@ -1,26 +1,32 @@
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 {
+ public enum Role{
+ USER,SELLER,ADMIN
+ }
@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(관리자)
+ @Enumerated(EnumType.ORDINAL)
+ private Role role; // USER(고객), SELLER(판매자), ADMIN(관리자)
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
@@ -34,8 +40,12 @@ 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) {
+ public User(Long id, String username, String password, String email, Role role, LocalDateTime createdAt) {
this.id = id;
this.username = username;
this.password = password;
@@ -43,4 +53,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..40fbc78
--- /dev/null
+++ b/src/test/java/shop/mtcoding/metamall/controller/OrderControllerTest.java
@@ -0,0 +1,185 @@
+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;
+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;
+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
+ @DirtiesContext
+ @DisplayName("주문")
+ void order() throws JsonProcessingException {
+ //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());
+ 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
+ @DisplayName("주문조회")
+ 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 productListNode = jsonNode.get("data").get(0).get("orderProductList");
+ 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 productListNode = jsonNode.get("data").get(0).get("orderProductList");
+ assertEquals(1,productListNode.size());
+ }
+ }
+
+ @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
+ @DirtiesContext
+ @DisplayName("주문취소")
+ void cancelOrder() throws JsonProcessingException {
+ //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());
+ ObjectMapper om = new ObjectMapper();
+ JsonNode jsonNode = om.readTree(om.writeValueAsString(response.getBody()));
+ JsonNode productListNode = jsonNode.get("data").get("orderProductList");
+ assertEquals(2,productListNode.get(0).get("product").get("qty").asInt());
+ }
+}
\ 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..04b1e63
--- /dev/null
+++ b/src/test/java/shop/mtcoding/metamall/controller/UserControllerTest.java
@@ -0,0 +1,77 @@
+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;
+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
+ @DisplayName("회원가입")
+ void signUp() throws Exception {
+ //given
+ UserRequest.SignUpDto singup = UserRequest.SignUpDto.builder()
+ .username("test1")
+ .email("test1@email.com")
+ .password("1234")
+ .passwordCheck("1234")
+ .role(User.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
+ @DisplayName("로그인")
+ 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..c75d1b1
--- /dev/null
+++ b/src/test/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepositoryTest.java
@@ -0,0 +1,100 @@
+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;
+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
+ private OrderSheetRepository orderSheetRepository;
+
+ @Test
+ 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);
+
+ //when
+ List orderSheet = orderSheetRepository.findByUser(user);
+
+ //then
+ assertEquals(1,orderSheet.size());
+ assertEquals(2,orderSheet.get(0).getOrderProductList().size());
+ assertEquals(user,orderSheet.get(0).getUser());
+ }
+
+ @Test
+ 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(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
+ 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 findByIdAndSeller(){
+ //given
+ long id = 1;
+ User seller = userRepository.findByUsername("seller1").orElse(null);
+
+ //when
+ OrderSheet orderSheet = orderSheetRepository.findByIdAndSeller(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..7785652
--- /dev/null
+++ b/src/test/java/shop/mtcoding/metamall/model/product/ProductRepositoryTest.java
@@ -0,0 +1,58 @@
+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.user.User;
+import shop.mtcoding.metamall.model.user.UserRepository;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@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
new file mode 100644
index 0000000..f6c408f
--- /dev/null
+++ b/src/test/java/shop/mtcoding/metamall/model/user/UserRepositoryTest.java
@@ -0,0 +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