ProductServiceImpl.java

package com.flogin.backend.service.product;

import com.flogin.backend.dto.ProductRequest;
import com.flogin.backend.dto.ProductResponse;
import com.flogin.backend.entity.Product;
import com.flogin.backend.exception.EntityNotFoundException;
import com.flogin.backend.repository.ProductRepository;
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;

@Service
@RequiredArgsConstructor
public class ProductServiceImpl implements IProductService {

    private final ProductRepository productRepository;

    // --- Create product ---
    @Transactional
    public ProductResponse createProduct(ProductRequest request) {
        if (productRepository.existsByName(request.getName())) {
            throw new IllegalArgumentException("Product with this name already exists");
        }

        Product product = Product.builder()
            .name(request.getName())
            .price(request.getPrice())
            .quantity(request.getQuantity())
            .description(request.getDescription())
            .category(request.getCategory())
            .build();

        productRepository.save(product);

        return mapToResponse(product);
    }

    // --- Update product ---
    @Transactional
    public ProductResponse updateProduct(Long id, ProductRequest request) {
        Product product = productRepository.findById(id)
            .orElseThrow(() -> EntityNotFoundException.forId(Product.class, id));
        // Nếu đổi tên, kiểm tra trùng
        if (!product.getName().equalsIgnoreCase(request.getName())
            && productRepository.existsByName(request.getName())) {
            throw new IllegalArgumentException("Product with this name already exists");
        }

        product.setName(request.getName());
        product.setPrice(request.getPrice());
        product.setQuantity(request.getQuantity());
        product.setDescription(request.getDescription());
        product.setCategory(request.getCategory());

        productRepository.save(product);

        return mapToResponse(product);
    }

    // --- Product by id ---
    @Transactional(readOnly = true)
    public ProductResponse getProductById(Long id) {
        Product product = productRepository.findById(id)
            .orElseThrow(() -> EntityNotFoundException.forId(Product.class, id));
        return mapToResponse(product);
    }

    // --- List products with filtering and paging ---
    public Page<ProductResponse> getProducts(String nameKeyword, String categoryKeyword, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("name").ascending());

        Page<Product> products = productRepository
            .findByNameContainingIgnoreCaseAndCategoryContainingIgnoreCase(
                nameKeyword == null ? "" : nameKeyword,
                categoryKeyword == null ? "" : categoryKeyword,
                pageable);

        return products.map(this::mapToResponse);
    }

    // --- Delete product ---
    @Transactional
    public void deleteProduct(Long id) {
        Product product = productRepository.findById(id)
            .orElseThrow(() -> EntityNotFoundException.forId(Product.class, id));

        productRepository.delete(product);
    }

    private ProductResponse mapToResponse(Product product) {
        return ProductResponse.builder()
            .id(product.getId())
            .name(product.getName())
            .price(product.getPrice())
            .quantity(product.getQuantity())
            .description(product.getDescription())
            .category(product.getCategory())
            .build();
    }
}