wishlist
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
package com.example.project_it207_server.controller;
|
||||
|
||||
import com.example.project_it207_server.model.dto.request.AddWishlistRequest;
|
||||
import com.example.project_it207_server.model.dto.response.WishlistPaginatedResponse;
|
||||
import com.example.project_it207_server.model.dto.response.WishlistResponse;
|
||||
import com.example.project_it207_server.service.AuthService;
|
||||
import com.example.project_it207_server.service.WishlistService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/wishlists")
|
||||
@RequiredArgsConstructor
|
||||
public class WishlistController {
|
||||
|
||||
private final WishlistService wishlistService;
|
||||
private final AuthService authService;
|
||||
|
||||
@PostMapping("/add")
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
public ResponseEntity<?> addToWishlist(
|
||||
@RequestBody AddWishlistRequest request,
|
||||
Authentication authentication) {
|
||||
try {
|
||||
// Lấy email từ token
|
||||
String email = authentication.getName();
|
||||
|
||||
// Lấy userId từ email
|
||||
Long userId = authService.getUserIdByEmail(email);
|
||||
WishlistResponse response = wishlistService.addToWishlist(userId, request);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", true);
|
||||
result.put("message", "Thêm vào danh sách ưu thích thành công");
|
||||
result.put("data", response);
|
||||
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(result);
|
||||
} catch (Exception e) {
|
||||
Map<String, Object> error = new HashMap<>();
|
||||
error.put("success", false);
|
||||
error.put("message", e.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/remove/{productId}")
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
public ResponseEntity<?> removeFromWishlist(
|
||||
@PathVariable Long productId,
|
||||
Authentication authentication) {
|
||||
try {
|
||||
// Lấy email từ token
|
||||
String email = authentication.getName();
|
||||
|
||||
// Lấy userId từ email
|
||||
Long userId = authService.getUserIdByEmail(email);
|
||||
wishlistService.removeFromWishlist(userId, productId);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", true);
|
||||
result.put("message", "Xóa khỏi danh sách ưu thích thành công");
|
||||
|
||||
return ResponseEntity.ok(result);
|
||||
} catch (Exception e) {
|
||||
Map<String, Object> error = new HashMap<>();
|
||||
error.put("success", false);
|
||||
error.put("message", e.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
public ResponseEntity<?> getWishlist(
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(defaultValue = "createdAt") String sortBy,
|
||||
Authentication authentication) {
|
||||
try {
|
||||
// Lấy email từ token
|
||||
String email = authentication.getName();
|
||||
|
||||
// Lấy userId từ email
|
||||
Long userId = authService.getUserIdByEmail(email);
|
||||
WishlistPaginatedResponse response = wishlistService.getWishlist(userId, page, size, sortBy);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", true);
|
||||
result.put("message", "Lấy danh sách ưu thích thành công");
|
||||
result.put("data", response);
|
||||
|
||||
return ResponseEntity.ok(result);
|
||||
} catch (Exception e) {
|
||||
Map<String, Object> error = new HashMap<>();
|
||||
error.put("success", false);
|
||||
error.put("message", e.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/count")
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
public ResponseEntity<?> getWishlistCount(Authentication authentication) {
|
||||
try {
|
||||
// Lấy email từ token
|
||||
String email = authentication.getName();
|
||||
|
||||
// Lấy userId từ email
|
||||
Long userId = authService.getUserIdByEmail(email);
|
||||
long count = wishlistService.getWishlistCount(userId);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", true);
|
||||
result.put("message", "Lấy số lượng danh sách ưu thích thành công");
|
||||
result.put("data", count);
|
||||
|
||||
return ResponseEntity.ok(result);
|
||||
} catch (Exception e) {
|
||||
Map<String, Object> error = new HashMap<>();
|
||||
error.put("success", false);
|
||||
error.put("message", e.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.example.project_it207_server.model.dto.request;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AddWishlistRequest {
|
||||
private Long productId;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.example.project_it207_server.model.dto.response;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class WishlistPaginatedResponse {
|
||||
private java.util.List<WishlistResponse> wishlists;
|
||||
private int totalPages;
|
||||
private long totalElements;
|
||||
private int currentPage;
|
||||
private int pageSize;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.example.project_it207_server.model.dto.response;
|
||||
|
||||
import com.example.project_it207_server.model.entity.Product;
|
||||
import com.example.project_it207_server.model.entity.Wishlist;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class WishlistResponse {
|
||||
private Long wishlistId;
|
||||
private Long productId;
|
||||
private String productName;
|
||||
private String productImage;
|
||||
private BigDecimal price;
|
||||
private String description;
|
||||
private LocalDateTime addedAt;
|
||||
|
||||
public static WishlistResponse fromEntity(Wishlist wishlist) {
|
||||
Product product = wishlist.getProduct();
|
||||
return WishlistResponse.builder()
|
||||
.wishlistId(wishlist.getWishlistId())
|
||||
.productId(product.getProductId())
|
||||
.productName(product.getProductName())
|
||||
.productImage(product.getImageUrl())
|
||||
.price(product.getPrice())
|
||||
.description(product.getDescription())
|
||||
.addedAt(wishlist.getCreatedAt())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.example.project_it207_server.model.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "wishlist", uniqueConstraints = {
|
||||
@UniqueConstraint(columnNames = {"user_id", "product_id"})
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class Wishlist {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
private Long wishlistId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
private User user;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "product_id", nullable = false)
|
||||
private Product product;
|
||||
|
||||
@Column(name = "created_at", nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
createdAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.example.project_it207_server.repository;
|
||||
|
||||
import com.example.project_it207_server.model.entity.Wishlist;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface WishlistRepository extends JpaRepository<Wishlist, Long> {
|
||||
|
||||
@Query("SELECT w FROM Wishlist w WHERE w.user.userId = :userId ORDER BY w.createdAt DESC")
|
||||
Page<Wishlist> findByUserId(@Param("userId") Long userId, Pageable pageable);
|
||||
|
||||
@Query("SELECT w FROM Wishlist w WHERE w.user.userId = :userId AND w.product.productId = :productId")
|
||||
Optional<Wishlist> findByUserIdAndProductId(@Param("userId") Long userId, @Param("productId") Long productId);
|
||||
|
||||
@Query("SELECT CASE WHEN COUNT(w) > 0 THEN true ELSE false END " +
|
||||
"FROM Wishlist w WHERE w.user.userId = :userId AND w.product.productId = :productId")
|
||||
boolean existsByUserIdAndProductId(@Param("userId") Long userId, @Param("productId") Long productId);
|
||||
|
||||
@Query("DELETE FROM Wishlist w WHERE w.user.userId = :userId AND w.product.productId = :productId")
|
||||
void deleteByUserIdAndProductId(@Param("userId") Long userId, @Param("productId") Long productId);
|
||||
|
||||
@Query("SELECT COUNT(w) FROM Wishlist w WHERE w.user.userId = :userId")
|
||||
long countByUserId(@Param("userId") Long userId);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.example.project_it207_server.service;
|
||||
|
||||
import com.example.project_it207_server.model.dto.request.AddWishlistRequest;
|
||||
import com.example.project_it207_server.model.dto.response.WishlistPaginatedResponse;
|
||||
import com.example.project_it207_server.model.dto.response.WishlistResponse;
|
||||
import com.example.project_it207_server.model.entity.Product;
|
||||
import com.example.project_it207_server.model.entity.User;
|
||||
import com.example.project_it207_server.model.entity.Wishlist;
|
||||
import com.example.project_it207_server.repository.ProductRepository;
|
||||
import com.example.project_it207_server.repository.UserRepository;
|
||||
import com.example.project_it207_server.repository.WishlistRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class WishlistService {
|
||||
|
||||
private final WishlistRepository wishlistRepository;
|
||||
private final UserRepository userRepository;
|
||||
private final ProductRepository productRepository;
|
||||
|
||||
@Transactional
|
||||
public WishlistResponse addToWishlist(Long userId, AddWishlistRequest request) {
|
||||
User user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> new RuntimeException("Người dùng không tồn tại"));
|
||||
|
||||
Product product = productRepository.findById(request.getProductId())
|
||||
.orElseThrow(() -> new RuntimeException("Sản phẩm không tồn tại"));
|
||||
|
||||
boolean exists = wishlistRepository.existsByUserIdAndProductId(userId, request.getProductId());
|
||||
if (exists) {
|
||||
throw new RuntimeException("Sản phẩm đã có trong danh sách ưu thích");
|
||||
}
|
||||
|
||||
Wishlist wishlist = Wishlist.builder()
|
||||
.user(user)
|
||||
.product(product)
|
||||
.build();
|
||||
|
||||
Wishlist saved = wishlistRepository.save(wishlist);
|
||||
return WishlistResponse.fromEntity(saved);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void removeFromWishlist(Long userId, Long productId) {
|
||||
Wishlist wishlist = wishlistRepository.findByUserIdAndProductId(userId, productId)
|
||||
.orElseThrow(() -> new RuntimeException("Sản phẩm không trong danh sách ưu thích"));
|
||||
|
||||
wishlistRepository.delete(wishlist);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public WishlistPaginatedResponse getWishlist(Long userId, int page, int size, String sortBy) {
|
||||
Sort.Direction direction = Sort.Direction.DESC;
|
||||
if ("name".equalsIgnoreCase(sortBy)) {
|
||||
sortBy = "product.name";
|
||||
} else if ("price".equalsIgnoreCase(sortBy)) {
|
||||
sortBy = "product.price";
|
||||
} else {
|
||||
sortBy = "createdAt";
|
||||
}
|
||||
|
||||
Pageable pageable = PageRequest.of(page, size, Sort.by(direction, sortBy));
|
||||
Page<Wishlist> wishlistPage = wishlistRepository.findByUserId(userId, pageable);
|
||||
|
||||
return WishlistPaginatedResponse.builder()
|
||||
.wishlists(wishlistPage.getContent().stream()
|
||||
.map(WishlistResponse::fromEntity)
|
||||
.collect(Collectors.toList()))
|
||||
.totalPages(wishlistPage.getTotalPages())
|
||||
.totalElements(wishlistPage.getTotalElements())
|
||||
.currentPage(page)
|
||||
.pageSize(size)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public long getWishlistCount(Long userId) {
|
||||
return wishlistRepository.countByUserId(userId);
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,16 @@ spring.datasource.username=root
|
||||
spring.datasource.password=
|
||||
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
|
||||
# JPA & Hibernate Configuration
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.jpa.show-sql=true
|
||||
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
|
||||
spring.jpa.properties.hibernate.format_sql=true
|
||||
|
||||
# Redis Configuration
|
||||
spring.redis.host=localhost
|
||||
spring.redis.port=6379
|
||||
|
||||
# JWT Configuration
|
||||
jwt.secret-key=404E635266556A586E3272357538782F413F4428472B4B6250645367566B5970
|
||||
jwt.expiration=86400000
|
||||
Reference in New Issue
Block a user