diff --git a/README.md b/README.md index e75d4aa0f..46f420736 100644 --- a/README.md +++ b/README.md @@ -21,3 +21,12 @@ ### 프로그래밍 요구 사항 1. Authorization 헤더가 유효하지 않거나 토큰이 유효하지 않은 경우 401 Unauthorized를 반환한다. 2. 잘못된 로그인, 비밀번호 찾기, 비밀번호 변경 요청은 403 Forbidden을 반환한다. + +# 3단계 위시 리스트 + +## 요구사항 +### 기능 요구사항 +이전 단계에서 로그인 후 받은 토큰을 사용하여 사용자별 위시 리스트 기능을 구현한다. +1. 위시 리스트에 등록된 상품 목록을 조회할 수 있다. +2. 위시 리스트에 상품을 추가할 수 있다. +3. 위시 리스트에 담긴 상품을 삭제할 수 있다. diff --git a/build.gradle b/build.gradle index d0cbc00ec..b2878d275 100644 --- a/build.gradle +++ b/build.gradle @@ -24,13 +24,13 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'javax.xml.bind:jaxb-api:2.3.1' implementation 'org.springframework.boot:spring-boot-starter-validation' - runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6' - runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6' - implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'io.jsonwebtoken:jjwt-impl:0.11.2' + implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2' runtimeOnly 'com.h2database:h2' - compileOnly 'io.jsonwebtoken:jjwt-api:0.12.6' + compileOnly 'io.jsonwebtoken:jjwt-api:0.11.2' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + } tasks.named('test') { diff --git a/src/main/java/gift/Annotation/LoginMemberArgumentResolver.java b/src/main/java/gift/Annotation/LoginMemberArgumentResolver.java new file mode 100644 index 000000000..b0c3ea428 --- /dev/null +++ b/src/main/java/gift/Annotation/LoginMemberArgumentResolver.java @@ -0,0 +1,56 @@ +package gift.Annotation; + +import gift.Model.User; +import gift.Service.UserService; +import gift.Utils.JwtUtil; +import io.jsonwebtoken.Claims; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +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; + +@Component +public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver { + private final UserService userService; + private final JwtUtil jwtUtil; + + @Autowired + public LoginMemberArgumentResolver(UserService userService, JwtUtil jwtUtil) { + this.userService = userService; + this.jwtUtil = jwtUtil; + } + + @Override + public boolean supportsParameter(MethodParameter methodParameter) { + return methodParameter.getParameterType().equals(User.class); + } + + @Override + public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception { + HttpServletRequest request = (HttpServletRequest) nativeWebRequest.getNativeRequest(); + + // 쿠키에서 JWT 토큰 추출 + String token = null; + if (request.getCookies() != null) { + for (Cookie cookie : request.getCookies()) { + if ("token".equals(cookie.getName())) { + token = cookie.getValue(); + break; + } + } + } + + if (token == null) { + throw new IllegalArgumentException("JWT token not found in cookies"); + } + + Claims claims = jwtUtil.decodeToken(token); //decode + String userEmail = claims.getSubject(); // subject를 email로 설정했기 때문에 userEmail로 사용 + return userService.findByEmail(userEmail); //null이라면 인증된 것이 아닐 것이고 null이 아니라면 인증된 것 + } +} diff --git a/src/main/java/gift/Annotation/LoginMemberResolver.java b/src/main/java/gift/Annotation/LoginMemberResolver.java new file mode 100644 index 000000000..d8c93ba94 --- /dev/null +++ b/src/main/java/gift/Annotation/LoginMemberResolver.java @@ -0,0 +1,11 @@ +package gift.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 LoginMemberResolver { +} diff --git a/src/main/java/gift/Config/WebConfig.java b/src/main/java/gift/Config/WebConfig.java new file mode 100644 index 000000000..12ffb2f78 --- /dev/null +++ b/src/main/java/gift/Config/WebConfig.java @@ -0,0 +1,22 @@ +package gift.Config; + +import gift.Annotation.LoginMemberArgumentResolver; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Autowired + private LoginMemberArgumentResolver loginMemberResolverHandlerMethodArgumentResolver; + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(loginMemberResolverHandlerMethodArgumentResolver); + } + +} diff --git a/src/main/java/gift/Connection/DBConnection.java b/src/main/java/gift/Connection/DBConnection.java deleted file mode 100644 index 64a04ce19..000000000 --- a/src/main/java/gift/Connection/DBConnection.java +++ /dev/null @@ -1,16 +0,0 @@ -package gift.Connection; - -import java.sql.Connection; -import java.sql.DriverManager; -import org.springframework.stereotype.Component; - -@Component -public class DBConnection { - - public static Connection getConnection() throws Exception { - var url = "jdbc:h2:mem:product"; - var user = "sa"; - var password = ""; - return DriverManager.getConnection(url, user, password); - } -} diff --git a/src/main/java/gift/Controller/GlobalExceptionHandler.java b/src/main/java/gift/Controller/GlobalExceptionHandler.java index e0dc3643f..e536ca7b8 100644 --- a/src/main/java/gift/Controller/GlobalExceptionHandler.java +++ b/src/main/java/gift/Controller/GlobalExceptionHandler.java @@ -1,5 +1,10 @@ package gift.Controller; +import gift.Exception.ForbiddenException; +import gift.Exception.UnauthorizedException; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import jakarta.servlet.http.HttpServletRequest; import org.springframework.ui.Model; import org.springframework.validation.FieldError; @@ -20,11 +25,21 @@ public String handleMethodArgumentNotValid(MethodArgumentNotValidException ex, M // 에러가 발생한 URL에 따라 다시 해당 페이지로 돌아가기 String requestUrl = request.getRequestURI(); - if (requestUrl.equals("/api/products/create")) { - return "product_form"; // product_form 뷰로 포워딩 - } else if (requestUrl.startsWith("/api/products/update")) { + if (requestUrl.equals("/api/products/create") || requestUrl.startsWith("/api/products/update")) { return "product_form"; // product_form 뷰로 포워딩 } + return "redirect:/api/products"; } + + @ExceptionHandler(UnauthorizedException.class) + public ResponseEntity handleUnauthorizedException(UnauthorizedException ex) { + return new ResponseEntity<>(ex.getMessage(), HttpStatus.UNAUTHORIZED); + } + + @ExceptionHandler(ForbiddenException.class) + public ResponseEntity handleForbiddenException(ForbiddenException ex) { + return new ResponseEntity<>(ex.getMessage(), HttpStatus.FORBIDDEN); + } + } diff --git a/src/main/java/gift/Controller/ProductController.java b/src/main/java/gift/Controller/ProductController.java index 1d4ece5db..dbfdf9510 100644 --- a/src/main/java/gift/Controller/ProductController.java +++ b/src/main/java/gift/Controller/ProductController.java @@ -2,7 +2,9 @@ import gift.Model.Product; import gift.Service.ProductService; + import java.util.Optional; + import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import jakarta.servlet.http.HttpServletRequest; @@ -39,10 +41,10 @@ public String getAllProductsByUser(Model model) { @RequestMapping(value = "/api/products/create", method = {RequestMethod.GET, RequestMethod.POST}) public String createProduct(@Valid @ModelAttribute Product product, HttpServletRequest request, Model model) { - if("GET".equalsIgnoreCase(request.getMethod())) { + if ("GET".equalsIgnoreCase(request.getMethod())) { model.addAttribute("product", new Product()); return "product_form"; - } else if("POST".equalsIgnoreCase(request.getMethod())) { + } else if ("POST".equalsIgnoreCase(request.getMethod())) { model.addAttribute("product", product); productService.saveProduct(product); return "redirect:/api/products"; @@ -52,11 +54,11 @@ public String createProduct(@Valid @ModelAttribute Product product, HttpServletR @RequestMapping(value = "/api/products/update/{id}", method = {RequestMethod.GET, RequestMethod.POST}) public String updateProductById(@PathVariable Long id, @Valid @ModelAttribute Product productDetails, HttpServletRequest request, Model model) { - if("GET".equalsIgnoreCase(request.getMethod())) { + if ("GET".equalsIgnoreCase(request.getMethod())) { Optional optionalProduct = productService.getProductById(id); model.addAttribute("product", optionalProduct.get()); return "product_form"; - } else if("POST".equalsIgnoreCase(request.getMethod())) { + } else if ("POST".equalsIgnoreCase(request.getMethod())) { productService.updateProduct(id, productDetails); return "redirect:/api/products"; } @@ -70,4 +72,5 @@ public String deleteProduct(@PathVariable Long id, Model model) { productService.deleteProduct(id); return "redirect:/api/products"; // 제품 목록 페이지로 리디렉션 } + } diff --git a/src/main/java/gift/Controller/UserController.java b/src/main/java/gift/Controller/UserController.java index e8ebbdb90..fe405fbfe 100644 --- a/src/main/java/gift/Controller/UserController.java +++ b/src/main/java/gift/Controller/UserController.java @@ -1,52 +1,57 @@ package gift.Controller; -import gift.Model.Product; import gift.Model.User; import gift.Service.UserService; -import jakarta.servlet.http.HttpServletRequest; +import gift.Utils.JwtUtil; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; - -import java.util.List; +import org.springframework.web.bind.annotation.*; +import jakarta.servlet.http.HttpServletRequest; @Controller public class UserController { - private UserService userService; - private JwtTokenProvider tokenProvider; + private final UserService userService; + private final JwtUtil jwtUtil; @Autowired - public UserController(UserService userService, JwtTokenProvider tokenProvider) { + public UserController(UserService userService, JwtUtil jwtUtil) { this.userService = userService; - this.tokenProvider = tokenProvider; + this.jwtUtil = jwtUtil; } - @RequestMapping(value = "/login", method = {RequestMethod.GET, RequestMethod.POST}) - public String login(@ModelAttribute User user, Model model, HttpServletRequest request) { - if ("GET".equalsIgnoreCase(request.getMethod())) { - model.addAttribute("user", new User()); - return "login"; - } else if ("POST".equalsIgnoreCase(request.getMethod())) { - model.addAttribute("user", user); - List users = userService.login(user); - if (users.isEmpty()) { - model.addAttribute("error", "유효하지 않은 아이디거나 비밀번호입니다."); - return "login"; - } + @GetMapping("/login") + public String login(Model model) { + model.addAttribute("user", new User()); + return "login"; + } + + @PostMapping(value = "/login") + public String login(@ModelAttribute User user, Model model, HttpServletResponse response) { + String email = user.getEmail(); + String password = user.getPassword(); - String email = user.getEmail(); + boolean isAuthenticated = userService.authenticate(email, password); + if (isAuthenticated) { boolean isAdmin = userService.isAdmin(email); - String jwt = tokenProvider.generateToken(user, isAdmin); - model.addAttribute("token", jwt); + User authenticatedUser = userService.findByEmail(email); + String token = jwtUtil.generateToken(authenticatedUser, isAdmin); + // Set token in HttpOnly cookie + Cookie cookie = new Cookie("token", token); + cookie.setHttpOnly(true); + cookie.setPath("/"); + response.addCookie(cookie); + if (isAdmin) { return "redirect:/api/products"; } return "redirect:/products"; } + + model.addAttribute("error", "Authentication failed"); return "login"; } @@ -58,6 +63,8 @@ public String register(@ModelAttribute User user, Model model, HttpServletReques } else if ("POST".equalsIgnoreCase(request.getMethod())) { model.addAttribute("user", user); userService.register(user); + model.addAttribute("message", "회원가입에 성공했습니다."); + return "login"; } return "login"; } diff --git a/src/main/java/gift/Controller/WishListController.java b/src/main/java/gift/Controller/WishListController.java new file mode 100644 index 000000000..be247e8a0 --- /dev/null +++ b/src/main/java/gift/Controller/WishListController.java @@ -0,0 +1,50 @@ +package gift.Controller; + +import gift.Annotation.LoginMemberResolver; +import gift.Model.User; +import gift.Model.WishListItem; +import gift.Service.WishListService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Controller +public class WishListController { + private final WishListService wishlistService; + + @Autowired + public WishListController(WishListService wishlistService) { + this.wishlistService = wishlistService; + } + + @GetMapping("/wishlist") + public String getWishlist(@LoginMemberResolver User user, Model model) { + List wishlist = wishlistService.getWishlist(user.getId()); + model.addAttribute("wishlist", wishlist); + return "wishlist"; + } + + @PostMapping("/wishlist/add") + public String addWishlistItem(@LoginMemberResolver User user, @RequestBody WishListItem wishListItem) { + if(user == null) { + return "redirect:/login"; + } + wishListItem.setUserId(user.getId()); + wishlistService.addWishlistItem(wishListItem); + return "redirect:/products"; + } + + @PostMapping("/wishlist/remove/{productId}") + public String removeWishlistItem(@LoginMemberResolver User user, @RequestBody WishListItem wishListItem) { + if(user == null) { + return "redirect:/login"; + } + + wishListItem.setUserId(user.getId()); + wishlistService.removeWishlistItem(wishListItem); + return "redirect:/wishlist"; + } +} diff --git a/src/main/java/gift/Exception/ForbiddenException.java b/src/main/java/gift/Exception/ForbiddenException.java new file mode 100644 index 000000000..cc48acc25 --- /dev/null +++ b/src/main/java/gift/Exception/ForbiddenException.java @@ -0,0 +1,11 @@ +package gift.Exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(HttpStatus.FORBIDDEN) +public class ForbiddenException extends RuntimeException { + public ForbiddenException(String message) { + super(message); + } +} diff --git a/src/main/java/gift/Exception/UnauthorizedException.java b/src/main/java/gift/Exception/UnauthorizedException.java new file mode 100644 index 000000000..2ff78ad99 --- /dev/null +++ b/src/main/java/gift/Exception/UnauthorizedException.java @@ -0,0 +1,11 @@ +package gift.Exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(HttpStatus.UNAUTHORIZED) +public class UnauthorizedException extends RuntimeException { + public UnauthorizedException(String message) { + super(message); + } +} diff --git a/src/main/java/gift/Model/Product.java b/src/main/java/gift/Model/Product.java index ff7aa0a1f..6a91c8bdf 100644 --- a/src/main/java/gift/Model/Product.java +++ b/src/main/java/gift/Model/Product.java @@ -12,7 +12,6 @@ public class Product { @Pattern(regexp = "^(?!.*카카오).*$", message = "\"카카오\"가 포함된 문구는 담당 MD와 협의한 경우에만 사용할 수 있습니다.") private String name; private int price; - private double price; private String imageUrl; private boolean isDeleted; @@ -33,7 +32,7 @@ public String getName() { return name; } - public double getPrice() { + public int getPrice() { return price; } diff --git a/src/main/java/gift/Model/User.java b/src/main/java/gift/Model/User.java index 0f4aa5132..eed95f4f0 100644 --- a/src/main/java/gift/Model/User.java +++ b/src/main/java/gift/Model/User.java @@ -7,7 +7,9 @@ public class User { private String password; private boolean isAdmin; - public User() {} + public User() { + + } public User(int id, String email, String password, String name, boolean isAdmin) { this.id = id; diff --git a/src/main/java/gift/Model/WishListItem.java b/src/main/java/gift/Model/WishListItem.java new file mode 100644 index 000000000..70cf3cb66 --- /dev/null +++ b/src/main/java/gift/Model/WishListItem.java @@ -0,0 +1,61 @@ +package gift.Model; + +public class WishListItem { + private int userId; + private int productId; + private int count; // 담은 개수 + private int quantity;//뺄 개수 + private String productName; + private int price; + + public WishListItem() { + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public int getProductId() { + return productId; + } + + public void setProductId(int productId) { + this.productId = productId; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + + public String getProductName() { + return productName; + } + + public void setProductName(String productName) { + this.productName = productName; + } + + public int getPrice() { + return price; + } + + public void setPrice(int price) { + this.price = price; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } +} diff --git a/src/main/java/gift/Repository/ProductRepository.java b/src/main/java/gift/Repository/ProductRepository.java index 9b9cdea45..820c62c13 100644 --- a/src/main/java/gift/Repository/ProductRepository.java +++ b/src/main/java/gift/Repository/ProductRepository.java @@ -25,15 +25,15 @@ public ProductRepository(JdbcTemplate jdbcTemplate) { @PostConstruct private void createProductTable() { String sql = """ - create table if not exists products ( - id bigint auto_increment, - name varchar(255), - price integer, - imageUrl varchar(1000), - isDeleted boolean default false, - primary key (id) - ) - """; + create table if not exists products ( + id bigint auto_increment, + name varchar(255), + price integer, + imageUrl varchar(1000), + isDeleted boolean default false, + primary key (id) + ) + """; jdbcTemplate.execute(sql); } diff --git a/src/main/java/gift/Repository/UserRepository.java b/src/main/java/gift/Repository/UserRepository.java index 66cc72a92..6e77c0362 100644 --- a/src/main/java/gift/Repository/UserRepository.java +++ b/src/main/java/gift/Repository/UserRepository.java @@ -1,7 +1,6 @@ package gift.Repository; import gift.Model.User; -import gift.Model.UserRowMapper; import jakarta.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -48,7 +47,7 @@ public void save(User user) { public List login(User user) { String sql = """ - SELECT email, password + SELECT * FROM Users WHERE email = ? AND password = ? """; @@ -58,7 +57,6 @@ public List login(User user) { public User findByEmail(String email) { String sql = "SELECT * FROM Users WHERE email = ?"; List users = jdbcTemplate.query(sql, new UserRowMapper(), email); - System.out.println(users.get(0).isAdmin()); return users.isEmpty() ? null : users.get(0); } } diff --git a/src/main/java/gift/Repository/UserRowMapper.java b/src/main/java/gift/Repository/UserRowMapper.java new file mode 100644 index 000000000..39d04576f --- /dev/null +++ b/src/main/java/gift/Repository/UserRowMapper.java @@ -0,0 +1,21 @@ +package gift.Repository; + +import gift.Model.User; +import org.springframework.jdbc.core.RowMapper; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class UserRowMapper implements RowMapper { + + @Override + public User mapRow(ResultSet rs, int rowNum) throws SQLException { + int id = rs.getInt("id"); + String email = rs.getString("email"); + String name = rs.getString("name"); + String password = rs.getString("password"); + boolean isAdmin = rs.getBoolean("isAdmin"); + + return new User(id, email, password, name, isAdmin); + } +} diff --git a/src/main/java/gift/Repository/WishListRepository.java b/src/main/java/gift/Repository/WishListRepository.java new file mode 100644 index 000000000..43e9907e1 --- /dev/null +++ b/src/main/java/gift/Repository/WishListRepository.java @@ -0,0 +1,76 @@ +package gift.Repository; + +import gift.Model.Product; +import gift.Model.WishListItem; +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.BeanPropertyRowMapper; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public class WishListRepository { + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public WishListRepository(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @PostConstruct + public void createTable() { + String sql = """ + CREATE TABLE if not exists Wishlist ( + user_id INT NOT NULL, + product_id INT NOT NULL, + count INT NOT NULL, + price INT, + FOREIGN KEY (user_id) REFERENCES Users(id), -- users 테이블의 id 칼럼과 외래키 관계 + FOREIGN KEY (product_id) REFERENCES products(id) -- products 테이블의 id 칼럼과 외래키 관계 + ); + """; + jdbcTemplate.execute(sql); + } + + public List getWishlist(int userId) { + String sql = """ + SELECT w.user_id, w.product_id, p.name AS productName, w.count, w.price + FROM Wishlist w + JOIN products p ON w.product_id = p.id + WHERE w.user_id = ?; + """; + return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(WishListItem.class), userId); + } + + public void addWishlistItem(WishListItem wishlistItem) { + String sql = "INSERT INTO Wishlist (user_id, product_id, count, price) VALUES (?, ?, ?, ?)"; + jdbcTemplate.update(sql, wishlistItem.getUserId(), wishlistItem.getProductId(), wishlistItem.getCount(), wishlistItem.getPrice() * wishlistItem.getCount()); + } + + public void reduceWishlistItem(WishListItem wishListItem) { + int userId = wishListItem.getUserId(); + int productId = wishListItem.getProductId(); + int count = wishListItem.getCount() - wishListItem.getQuantity(); // 기존 수량 - 뺄 수량 + + System.out.println(count); //4 + + String updateSql = "UPDATE Wishlist SET count = ? WHERE user_id = ? AND product_id = ?"; // 상품의 수량을 업데이트하기 위한 쿼리 + jdbcTemplate.update(updateSql, count, userId, productId); + updateSql = "SELECT price FROM products WHERE id = ?"; // 상품 하나의 가격을 가져오기 위한 쿼리 + List list = jdbcTemplate.query(updateSql, new BeanPropertyRowMapper<>(Product.class), productId); + updateSql = "UPDATE Wishlist SET price = ? WHERE user_id = ? AND product_id = ?"; + jdbcTemplate.update(updateSql, list.get(0).getPrice() * count, userId, productId); // 상품 + } + + public void removeWishlistItem(WishListItem wishListItem) { + int userId = wishListItem.getUserId(); + int productId = wishListItem.getProductId(); + + String deleteSql = "DELETE FROM Wishlist WHERE user_id = ? AND product_id = ?"; + jdbcTemplate.update(deleteSql, userId, productId); + } + +} diff --git a/src/main/java/gift/Service/ProductService.java b/src/main/java/gift/Service/ProductService.java index e9454c58c..bc920f85a 100644 --- a/src/main/java/gift/Service/ProductService.java +++ b/src/main/java/gift/Service/ProductService.java @@ -7,6 +7,7 @@ import org.springframework.stereotype.Service; import java.util.*; + @Service @Qualifier("productService") public class ProductService { diff --git a/src/main/java/gift/Service/UserService.java b/src/main/java/gift/Service/UserService.java index ab195eae0..b8660aac3 100644 --- a/src/main/java/gift/Service/UserService.java +++ b/src/main/java/gift/Service/UserService.java @@ -2,33 +2,38 @@ import gift.Model.User; import gift.Repository.UserRepository; +import gift.Utils.JwtUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; -import java.util.List; - @Service @Qualifier("userService") public class UserService { - private UserRepository userRepository; + private final UserRepository userRepository; @Autowired - public UserService(UserRepository userRepository ) { + public UserService(UserRepository userRepository) { this.userRepository = userRepository; } - public List login(User user) { - return userRepository.login(user); - } - public void register(User user) { userRepository.save(user); } + public User findByEmail(String email) { + return userRepository.findByEmail(email); + } + public boolean isAdmin(String email) { User user = userRepository.findByEmail(email); return user.isAdmin(); } + + public boolean authenticate(String email, String password) { + User user = userRepository.findByEmail(email); + return user != null && user.getPassword().equals(password); + } + } diff --git a/src/main/java/gift/Service/WishListService.java b/src/main/java/gift/Service/WishListService.java new file mode 100644 index 000000000..900b9d4d5 --- /dev/null +++ b/src/main/java/gift/Service/WishListService.java @@ -0,0 +1,37 @@ +package gift.Service; + +import gift.Model.WishListItem; +import gift.Repository.WishListRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class WishListService { + + private final WishListRepository wishListRepository; + + @Autowired + public WishListService(WishListRepository wishListRepository) { + this.wishListRepository = wishListRepository; + } + + public List getWishlist(int userId) { + return wishListRepository.getWishlist(userId); + } + + public void addWishlistItem(WishListItem wishlistItem) { + wishListRepository.addWishlistItem(wishlistItem); + } + + public void removeWishlistItem(WishListItem wishListItem) { + //값만 줄일건지 + if(wishListItem.getCount() - wishListItem.getQuantity() > 0) { + wishListRepository.reduceWishlistItem(wishListItem); + return; + } + wishListRepository.removeWishlistItem(wishListItem); + } + +} diff --git a/src/main/java/gift/Controller/JwtTokenProvider.java b/src/main/java/gift/Utils/JwtUtil.java similarity index 56% rename from src/main/java/gift/Controller/JwtTokenProvider.java rename to src/main/java/gift/Utils/JwtUtil.java index 56f93be7b..bc4fb4e48 100644 --- a/src/main/java/gift/Controller/JwtTokenProvider.java +++ b/src/main/java/gift/Utils/JwtUtil.java @@ -1,9 +1,11 @@ -package gift.Controller; +package gift.Utils; import gift.Model.User; -import io.jsonwebtoken.*; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; -import org.springframework.context.annotation.Bean; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.nio.charset.StandardCharsets; @@ -12,12 +14,16 @@ import java.util.Map; @Component -public class JwtTokenProvider { - private final String secretKey = "Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E="; - private final long validityInMilliseconds = 3600000; // 1 hour +public class JwtUtil { + + @Value("${jwt.secretKey}") + private String secretKey; + @Value("${jwt.expiredMs}") + private long validityInMilliseconds; // 1 hour public String generateToken(User user, boolean isAdmin) { Map claims = new HashMap<>(); + claims.put("userId", user.getId()); claims.put("email", user.getEmail()); claims.put("name", user.getName()); claims.put("password", user.getPassword()); @@ -34,4 +40,16 @@ public String generateToken(User user, boolean isAdmin) { .signWith(Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8))) .compact(); } -} \ No newline at end of file + + public Claims decodeToken(String token) { + try { + return Jwts.parserBuilder() + .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8))) + .build() + .parseClaimsJws(token) + .getBody(); + } catch (JwtException e) { + return null; + } + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index ace5260a7..4663f0737 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -3,4 +3,6 @@ spring.application.name=spring-gift spring.h2.console.enabled=true spring.h2.console.path=/h2-console spring.datasource.url=jdbc:h2:mem:product -spring.datasource.username=sa \ No newline at end of file +spring.datasource.username=sa +jwt.secretKey=Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E= +jwt.expiredMs=3600000 diff --git a/src/main/resources/templates/product_form.html b/src/main/resources/templates/product_form.html index a666db041..22c908dc2 100644 --- a/src/main/resources/templates/product_form.html +++ b/src/main/resources/templates/product_form.html @@ -1,29 +1,30 @@ - - Product Form - + + Product Form +

-
- - - -
- - -
- - -
- -
-

-
- - Cancel + + + + +
+ + +
+ + +
+ +
+

+
+ + Cancel
diff --git a/src/main/resources/templates/products.html b/src/main/resources/templates/products.html index 59e4aca6a..4cd8e5e16 100644 --- a/src/main/resources/templates/products.html +++ b/src/main/resources/templates/products.html @@ -1,38 +1,38 @@ - - 상품 관리 - + + 상품 관리 +

Product Management

Add New Product - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + +
IDNamePriceImageActions
1NamePriceProduct Image - Update -
- - -
-
IDNamePriceImageActions
1NamePriceProduct Image + Update +
+ + +
+
diff --git a/src/main/resources/templates/user_products.html b/src/main/resources/templates/user_products.html index 57d4c0418..a1b1a98eb 100644 --- a/src/main/resources/templates/user_products.html +++ b/src/main/resources/templates/user_products.html @@ -3,10 +3,16 @@ 상품 관리 - +

Product Management

+ + + + @@ -22,14 +28,67 @@

Product Management

- +
1 Name PriceProduct ImageProduct Image -
- -
+
+ + +
+ + + +
+ + +
+ + diff --git a/src/main/resources/templates/wishlist.html b/src/main/resources/templates/wishlist.html new file mode 100644 index 000000000..fe2358724 --- /dev/null +++ b/src/main/resources/templates/wishlist.html @@ -0,0 +1,72 @@ + + + + + 위시 리스트 + + + +

위시 리스트

+ + + + + + + + + + + + + + + + + + + +
ID상품 이름수량가격Actions
+
+ + + + + +
+
+ + + +