From 69cb5fd322263debb1f8e446833615d9de7c21aa Mon Sep 17 00:00:00 2001 From: Duy Le Van Date: Mon, 30 Sep 2024 15:19:41 +0700 Subject: [PATCH 1/9] #1102 - Create recommendation project, product vector ddl --- .../service/ProductRecommendationService.java | 156 ++++++++++++++++++ .../yas/recommendation/dto/CategoryDTO.java | 19 +++ .../com/yas/recommendation/dto/ImageDto.java | 17 ++ .../dto/ProductAttributeValueDTO.java | 14 ++ .../recommendation/dto/ProductDetailDTO.java | 38 +++++ .../dto/ProductVariationDTO.java | 20 +++ .../recommendation/dto/RelatedProductDto.java | 40 +++++ .../service/ProductSyncDataService.java | 15 ++ .../db/changelog/data/changelog-0001.sql | 1 + .../db/changelog/db.changelog-master.yaml | 5 + .../db/changelog/ddl/changelog-0001.sql | 10 ++ 11 files changed, 335 insertions(+) create mode 100644 product/src/main/java/com/yas/product/service/ProductRecommendationService.java create mode 100644 recommendation/src/main/java/com/yas/recommendation/dto/CategoryDTO.java create mode 100644 recommendation/src/main/java/com/yas/recommendation/dto/ImageDto.java create mode 100644 recommendation/src/main/java/com/yas/recommendation/dto/ProductAttributeValueDTO.java create mode 100644 recommendation/src/main/java/com/yas/recommendation/dto/ProductDetailDTO.java create mode 100644 recommendation/src/main/java/com/yas/recommendation/dto/ProductVariationDTO.java create mode 100644 recommendation/src/main/java/com/yas/recommendation/dto/RelatedProductDto.java create mode 100644 recommendation/src/main/java/com/yas/recommendation/service/ProductSyncDataService.java create mode 100644 recommendation/src/main/resources/db/changelog/data/changelog-0001.sql create mode 100644 recommendation/src/main/resources/db/changelog/db.changelog-master.yaml create mode 100644 recommendation/src/main/resources/db/changelog/ddl/changelog-0001.sql diff --git a/product/src/main/java/com/yas/product/service/ProductRecommendationService.java b/product/src/main/java/com/yas/product/service/ProductRecommendationService.java new file mode 100644 index 0000000000..502bad4dc6 --- /dev/null +++ b/product/src/main/java/com/yas/product/service/ProductRecommendationService.java @@ -0,0 +1,156 @@ +package com.yas.product.service; + +import com.yas.commonlibrary.exception.NotFoundException; +import com.yas.product.model.Brand; +import com.yas.product.model.Category; +import com.yas.product.model.Product; +import com.yas.product.model.ProductCategory; +import com.yas.product.model.ProductOptionCombination; +import com.yas.product.repository.ProductOptionCombinationRepository; +import com.yas.product.repository.ProductRepository; +import com.yas.product.utils.Constants; +import com.yas.product.viewmodel.ImageVm; +import com.yas.product.viewmodel.product.ProductDetailInfoVm; +import com.yas.product.viewmodel.product.ProductVariationGetVm; +import com.yas.product.viewmodel.productattribute.ProductAttributeValueGetVm; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * Service class responsible for managing product recommendations. + */ + +@Service +@Transactional +@Slf4j +public class ProductRecommendationService { + private final ProductRepository productRepository; + private final MediaService mediaService; + private final ProductOptionCombinationRepository productOptionCombinationRepository; + + /** + * Constructor for {@code ProductRecommendationService} that initializes the service with necessary dependencies. + * + * @param productRepository the repository responsible for handling product data + * @param mediaService the service responsible for managing media assets associated + * with products + * @param productOptionCombinationRepository the repository for managing product option combinations + */ + public ProductRecommendationService(ProductRepository productRepository, MediaService mediaService, + ProductOptionCombinationRepository productOptionCombinationRepository) { + this.productRepository = productRepository; + this.mediaService = mediaService; + this.productOptionCombinationRepository = productOptionCombinationRepository; + } + + /** + * Retrieves detailed information about a product by its unique identifier. + * + * @param productId the unique identifier of the product to be retrieved + * @return a {@link ProductDetailInfoVm} containing detailed information about the product + * @throws NotFoundException if no product is found with the given {@code productId} + */ + public ProductDetailInfoVm getProductDetailById(long productId) { + List variations = new ArrayList<>(); + Product product = productRepository + .findById(productId) + .filter(Product::isPublished) + .orElseThrow(() -> + new NotFoundException(Constants.ErrorCode.PRODUCT_NOT_FOUND, productId) + ); + + List categories = Optional.ofNullable(product.getProductCategories()) + .orElse(Collections.emptyList()) // Handle null case + .stream() + .map(ProductCategory::getCategory) + .toList(); + + Long brandId = Optional.ofNullable(product.getBrand()) + .map(Brand::getId) + .orElse(null); + String brandName = Optional.ofNullable(product.getBrand()) + .map(Brand::getName) + .orElse(null); + + List productAttributes = product.getAttributeValues() + .stream() + .map(ProductAttributeValueGetVm::fromModel) + .toList(); + + if (Boolean.TRUE.equals(product.isHasOptions())) { + List productVariations = product.getProducts() + .stream() + .toList(); + variations = productVariations.stream() + .filter(Product::isPublished) + .map(pro -> { + List productOptionCombinations = + productOptionCombinationRepository.findAllByProduct(pro); + Map options = productOptionCombinations.stream().collect(Collectors.toMap( + productOptionCombination -> productOptionCombination.getProductOption().getId(), + ProductOptionCombination::getValue + )); + + return new ProductVariationGetVm( + pro.getId(), + pro.getName(), + pro.getSlug(), + pro.getSku(), + pro.getGtin(), + pro.getPrice(), + getThumbnailFromProduct(pro), + getImagesFromProduct(pro), + options + ); + }).toList(); + } + return new ProductDetailInfoVm(product.getId(), + product.getName(), + product.getShortDescription(), + product.getDescription(), + product.getSpecification(), + product.getSku(), + product.getGtin(), + product.getSlug(), + product.isAllowedToOrder(), + product.isPublished(), + product.isFeatured(), + product.isVisibleIndividually(), + product.isStockTrackingEnabled(), + product.getPrice(), + brandId, + categories, + product.getMetaTitle(), + product.getMetaKeyword(), + product.getMetaDescription(), + product.getTaxClassId(), + brandName, + productAttributes, + variations, + getThumbnailFromProduct(product), + getImagesFromProduct(product) + ); + } + + private ImageVm getThumbnailFromProduct(Product product) { + return Optional.ofNullable(product.getThumbnailMediaId()) + .map(thumbnailId -> new ImageVm(thumbnailId, mediaService.getMedia(thumbnailId).url())) + .orElse(null); + + } + + private List getImagesFromProduct(Product product) { + return Optional.ofNullable(product.getProductImages()) + .orElse(Collections.emptyList()) + .stream() + .map(image -> new ImageVm(image.getImageId(), mediaService.getMedia(image.getImageId()).url())) + .toList(); + } +} diff --git a/recommendation/src/main/java/com/yas/recommendation/dto/CategoryDTO.java b/recommendation/src/main/java/com/yas/recommendation/dto/CategoryDTO.java new file mode 100644 index 0000000000..26a29089d3 --- /dev/null +++ b/recommendation/src/main/java/com/yas/recommendation/dto/CategoryDTO.java @@ -0,0 +1,19 @@ +package com.yas.recommendation.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +@JsonInclude(Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record CategoryDTO( + Long id, + String name, + String description, + String slug, + String metaKeyword, + String metaDescription, + Short displayOrder, + Boolean isPublished +) { +} diff --git a/recommendation/src/main/java/com/yas/recommendation/dto/ImageDto.java b/recommendation/src/main/java/com/yas/recommendation/dto/ImageDto.java new file mode 100644 index 0000000000..e323e41d21 --- /dev/null +++ b/recommendation/src/main/java/com/yas/recommendation/dto/ImageDto.java @@ -0,0 +1,17 @@ +package com.yas.recommendation.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@JsonIgnoreProperties(ignoreUnknown = true) +public class ImageDto { + + public ImageDto() { + } + + private Long id; + private String url; +} diff --git a/recommendation/src/main/java/com/yas/recommendation/dto/ProductAttributeValueDTO.java b/recommendation/src/main/java/com/yas/recommendation/dto/ProductAttributeValueDTO.java new file mode 100644 index 0000000000..3ee5a54674 --- /dev/null +++ b/recommendation/src/main/java/com/yas/recommendation/dto/ProductAttributeValueDTO.java @@ -0,0 +1,14 @@ +package com.yas.recommendation.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +@JsonInclude(Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ProductAttributeValueDTO( + long id, + String nameProductAttribute, + String value +) { +} diff --git a/recommendation/src/main/java/com/yas/recommendation/dto/ProductDetailDTO.java b/recommendation/src/main/java/com/yas/recommendation/dto/ProductDetailDTO.java new file mode 100644 index 0000000000..2654cf3b85 --- /dev/null +++ b/recommendation/src/main/java/com/yas/recommendation/dto/ProductDetailDTO.java @@ -0,0 +1,38 @@ +package com.yas.recommendation.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +import java.util.List; + +@JsonInclude(Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ProductDetailDTO( + long id, + String name, + String shortDescription, + String description, + String specification, + String sku, + String gtin, + String slug, + Boolean isAllowedToOrder, + Boolean isPublished, + Boolean isFeatured, + Boolean isVisible, + Boolean stockTrackingEnabled, + Double price, + Long brandId, + List categories, + String metaTitle, + String metaKeyword, + String metaDescription, + Long taxClassId, + String brandName, + List attributeValues, + List variations, + ImageDto thumbnail, + List productImages +) { +} diff --git a/recommendation/src/main/java/com/yas/recommendation/dto/ProductVariationDTO.java b/recommendation/src/main/java/com/yas/recommendation/dto/ProductVariationDTO.java new file mode 100644 index 0000000000..afe9bbac5e --- /dev/null +++ b/recommendation/src/main/java/com/yas/recommendation/dto/ProductVariationDTO.java @@ -0,0 +1,20 @@ +package com.yas.recommendation.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +import java.util.Map; + +@JsonInclude(Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ProductVariationDTO( + Long id, + String name, + String slug, + String sku, + String gtin, + Double price, + Map options +) { +} diff --git a/recommendation/src/main/java/com/yas/recommendation/dto/RelatedProductDto.java b/recommendation/src/main/java/com/yas/recommendation/dto/RelatedProductDto.java new file mode 100644 index 0000000000..8c7cea1d74 --- /dev/null +++ b/recommendation/src/main/java/com/yas/recommendation/dto/RelatedProductDto.java @@ -0,0 +1,40 @@ +package com.yas.recommendation.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.util.List; +import lombok.Getter; + +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +public class RelatedProductDto { + + @JsonProperty("id") + private Integer productId; + + private String name; + + private BigDecimal price; + + @JsonProperty("brandName") + private String brand; + + @JsonProperty("metaTitle") + private String title; + + private String description; + + private String metaDescription; + + @JsonProperty("specification") + private String specification; + + private ImageDto thumbnail; + + private List productImages; + + public RelatedProductDto() { + } +} + diff --git a/recommendation/src/main/java/com/yas/recommendation/service/ProductSyncDataService.java b/recommendation/src/main/java/com/yas/recommendation/service/ProductSyncDataService.java new file mode 100644 index 0000000000..2d8c2ace93 --- /dev/null +++ b/recommendation/src/main/java/com/yas/recommendation/service/ProductSyncDataService.java @@ -0,0 +1,15 @@ +package com.yas.recommendation.service; + +import org.springframework.stereotype.Component; + +@Component +public class ProductSyncDataService { + public void createProduct(Long id) { + } + + public void updateProduct(Long id) { + } + + public void deleteProduct(Long id) { + } +} diff --git a/recommendation/src/main/resources/db/changelog/data/changelog-0001.sql b/recommendation/src/main/resources/db/changelog/data/changelog-0001.sql new file mode 100644 index 0000000000..b6fd147401 --- /dev/null +++ b/recommendation/src/main/resources/db/changelog/data/changelog-0001.sql @@ -0,0 +1 @@ +--liquibase formatted sql \ No newline at end of file diff --git a/recommendation/src/main/resources/db/changelog/db.changelog-master.yaml b/recommendation/src/main/resources/db/changelog/db.changelog-master.yaml new file mode 100644 index 0000000000..419ad29d64 --- /dev/null +++ b/recommendation/src/main/resources/db/changelog/db.changelog-master.yaml @@ -0,0 +1,5 @@ +databaseChangeLog: + - includeAll: + path: db/changelog/ddl/ + - includeAll: + path: db/changelog/data/ \ No newline at end of file diff --git a/recommendation/src/main/resources/db/changelog/ddl/changelog-0001.sql b/recommendation/src/main/resources/db/changelog/ddl/changelog-0001.sql new file mode 100644 index 0000000000..b5a8f9561c --- /dev/null +++ b/recommendation/src/main/resources/db/changelog/ddl/changelog-0001.sql @@ -0,0 +1,10 @@ +--liquibase formatted sql +CREATE EXTENSION IF NOT EXISTS vector; +CREATE TABLE IF NOT EXISTS vector_store ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + item_id BIGINT NOT NULL, + type TEXT, + content TEXT, + metadata JSON, + content_embedding VECTOR(1536) +); \ No newline at end of file From d5c14b53d2fa11c50916c7d858cf3f7d8c6ed1ef Mon Sep 17 00:00:00 2001 From: Duy Le Van Date: Tue, 8 Oct 2024 16:49:13 +0700 Subject: [PATCH 2/9] #1102 - Fix checkstyle --- .../dto/{CategoryDTO.java => CategoryDto.java} | 2 +- .../java/com/yas/recommendation/dto/ImageDto.java | 13 +------------ ...eValueDTO.java => ProductAttributeValueDto.java} | 2 +- ...{ProductDetailDTO.java => ProductDetailDto.java} | 8 ++++---- ...ctVariationDTO.java => ProductVariationDto.java} | 2 +- 5 files changed, 8 insertions(+), 19 deletions(-) rename recommendation/src/main/java/com/yas/recommendation/dto/{CategoryDTO.java => CategoryDto.java} (94%) rename recommendation/src/main/java/com/yas/recommendation/dto/{ProductAttributeValueDTO.java => ProductAttributeValueDto.java} (90%) rename recommendation/src/main/java/com/yas/recommendation/dto/{ProductDetailDTO.java => ProductDetailDto.java} (84%) rename recommendation/src/main/java/com/yas/recommendation/dto/{ProductVariationDTO.java => ProductVariationDto.java} (93%) diff --git a/recommendation/src/main/java/com/yas/recommendation/dto/CategoryDTO.java b/recommendation/src/main/java/com/yas/recommendation/dto/CategoryDto.java similarity index 94% rename from recommendation/src/main/java/com/yas/recommendation/dto/CategoryDTO.java rename to recommendation/src/main/java/com/yas/recommendation/dto/CategoryDto.java index 26a29089d3..9181ffe5fd 100644 --- a/recommendation/src/main/java/com/yas/recommendation/dto/CategoryDTO.java +++ b/recommendation/src/main/java/com/yas/recommendation/dto/CategoryDto.java @@ -6,7 +6,7 @@ @JsonInclude(Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record CategoryDTO( +public record CategoryDto( Long id, String name, String description, diff --git a/recommendation/src/main/java/com/yas/recommendation/dto/ImageDto.java b/recommendation/src/main/java/com/yas/recommendation/dto/ImageDto.java index e323e41d21..02d311482b 100644 --- a/recommendation/src/main/java/com/yas/recommendation/dto/ImageDto.java +++ b/recommendation/src/main/java/com/yas/recommendation/dto/ImageDto.java @@ -1,17 +1,6 @@ package com.yas.recommendation.dto; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Getter; -import lombok.Setter; -@Getter -@Setter @JsonIgnoreProperties(ignoreUnknown = true) -public class ImageDto { - - public ImageDto() { - } - - private Long id; - private String url; -} +public record ImageDto(Long id, String url) {} diff --git a/recommendation/src/main/java/com/yas/recommendation/dto/ProductAttributeValueDTO.java b/recommendation/src/main/java/com/yas/recommendation/dto/ProductAttributeValueDto.java similarity index 90% rename from recommendation/src/main/java/com/yas/recommendation/dto/ProductAttributeValueDTO.java rename to recommendation/src/main/java/com/yas/recommendation/dto/ProductAttributeValueDto.java index 3ee5a54674..b513cc9601 100644 --- a/recommendation/src/main/java/com/yas/recommendation/dto/ProductAttributeValueDTO.java +++ b/recommendation/src/main/java/com/yas/recommendation/dto/ProductAttributeValueDto.java @@ -6,7 +6,7 @@ @JsonInclude(Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record ProductAttributeValueDTO( +public record ProductAttributeValueDto( long id, String nameProductAttribute, String value diff --git a/recommendation/src/main/java/com/yas/recommendation/dto/ProductDetailDTO.java b/recommendation/src/main/java/com/yas/recommendation/dto/ProductDetailDto.java similarity index 84% rename from recommendation/src/main/java/com/yas/recommendation/dto/ProductDetailDTO.java rename to recommendation/src/main/java/com/yas/recommendation/dto/ProductDetailDto.java index 2654cf3b85..674d166e55 100644 --- a/recommendation/src/main/java/com/yas/recommendation/dto/ProductDetailDTO.java +++ b/recommendation/src/main/java/com/yas/recommendation/dto/ProductDetailDto.java @@ -8,7 +8,7 @@ @JsonInclude(Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record ProductDetailDTO( +public record ProductDetailDto( long id, String name, String shortDescription, @@ -24,14 +24,14 @@ public record ProductDetailDTO( Boolean stockTrackingEnabled, Double price, Long brandId, - List categories, + List categories, String metaTitle, String metaKeyword, String metaDescription, Long taxClassId, String brandName, - List attributeValues, - List variations, + List attributeValues, + List variations, ImageDto thumbnail, List productImages ) { diff --git a/recommendation/src/main/java/com/yas/recommendation/dto/ProductVariationDTO.java b/recommendation/src/main/java/com/yas/recommendation/dto/ProductVariationDto.java similarity index 93% rename from recommendation/src/main/java/com/yas/recommendation/dto/ProductVariationDTO.java rename to recommendation/src/main/java/com/yas/recommendation/dto/ProductVariationDto.java index afe9bbac5e..64a8d74066 100644 --- a/recommendation/src/main/java/com/yas/recommendation/dto/ProductVariationDTO.java +++ b/recommendation/src/main/java/com/yas/recommendation/dto/ProductVariationDto.java @@ -8,7 +8,7 @@ @JsonInclude(Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record ProductVariationDTO( +public record ProductVariationDto( Long id, String name, String slug, From 455d11fa083ab72b7cd7b23d0e7e5b300e808b94 Mon Sep 17 00:00:00 2001 From: Duy Le Van Date: Tue, 8 Oct 2024 17:03:32 +0700 Subject: [PATCH 3/9] #1102 - Clean up --- .../resources/db/changelog/data/changelog-0001.sql | 1 - .../resources/db/changelog/db.changelog-master.yaml | 5 ----- .../main/resources/db/changelog/ddl/changelog-0001.sql | 10 ---------- 3 files changed, 16 deletions(-) delete mode 100644 recommendation/src/main/resources/db/changelog/data/changelog-0001.sql delete mode 100644 recommendation/src/main/resources/db/changelog/db.changelog-master.yaml delete mode 100644 recommendation/src/main/resources/db/changelog/ddl/changelog-0001.sql diff --git a/recommendation/src/main/resources/db/changelog/data/changelog-0001.sql b/recommendation/src/main/resources/db/changelog/data/changelog-0001.sql deleted file mode 100644 index b6fd147401..0000000000 --- a/recommendation/src/main/resources/db/changelog/data/changelog-0001.sql +++ /dev/null @@ -1 +0,0 @@ ---liquibase formatted sql \ No newline at end of file diff --git a/recommendation/src/main/resources/db/changelog/db.changelog-master.yaml b/recommendation/src/main/resources/db/changelog/db.changelog-master.yaml deleted file mode 100644 index 419ad29d64..0000000000 --- a/recommendation/src/main/resources/db/changelog/db.changelog-master.yaml +++ /dev/null @@ -1,5 +0,0 @@ -databaseChangeLog: - - includeAll: - path: db/changelog/ddl/ - - includeAll: - path: db/changelog/data/ \ No newline at end of file diff --git a/recommendation/src/main/resources/db/changelog/ddl/changelog-0001.sql b/recommendation/src/main/resources/db/changelog/ddl/changelog-0001.sql deleted file mode 100644 index b5a8f9561c..0000000000 --- a/recommendation/src/main/resources/db/changelog/ddl/changelog-0001.sql +++ /dev/null @@ -1,10 +0,0 @@ ---liquibase formatted sql -CREATE EXTENSION IF NOT EXISTS vector; -CREATE TABLE IF NOT EXISTS vector_store ( - id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - item_id BIGINT NOT NULL, - type TEXT, - content TEXT, - metadata JSON, - content_embedding VECTOR(1536) -); \ No newline at end of file From 6c1f4ad525ae5ee700d5444827350ac934775043 Mon Sep 17 00:00:00 2001 From: Duy Le Van Date: Tue, 8 Oct 2024 17:45:04 +0700 Subject: [PATCH 4/9] #1102 - Fix checkstyle & remove duplicated class --- .../service/ProductSyncDataService.java | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 recommendation/src/main/java/com/yas/recommendation/service/ProductSyncDataService.java diff --git a/recommendation/src/main/java/com/yas/recommendation/service/ProductSyncDataService.java b/recommendation/src/main/java/com/yas/recommendation/service/ProductSyncDataService.java deleted file mode 100644 index 2d8c2ace93..0000000000 --- a/recommendation/src/main/java/com/yas/recommendation/service/ProductSyncDataService.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.yas.recommendation.service; - -import org.springframework.stereotype.Component; - -@Component -public class ProductSyncDataService { - public void createProduct(Long id) { - } - - public void updateProduct(Long id) { - } - - public void deleteProduct(Long id) { - } -} From 86f6bce8027c2442c0359b9726db5f371109e041 Mon Sep 17 00:00:00 2001 From: Duy Le Van Date: Tue, 8 Oct 2024 17:47:55 +0700 Subject: [PATCH 5/9] #1102 - Fix checkstyle --- .../com/yas/product/service/ProductRecommendationService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/product/src/main/java/com/yas/product/service/ProductRecommendationService.java b/product/src/main/java/com/yas/product/service/ProductRecommendationService.java index 502bad4dc6..2a1aef530c 100644 --- a/product/src/main/java/com/yas/product/service/ProductRecommendationService.java +++ b/product/src/main/java/com/yas/product/service/ProductRecommendationService.java @@ -26,10 +26,9 @@ /** * Service class responsible for managing product recommendations. */ - +@Slf4j @Service @Transactional -@Slf4j public class ProductRecommendationService { private final ProductRepository productRepository; private final MediaService mediaService; From 981c404387294ba79d402f7c006fd5424c20daea Mon Sep 17 00:00:00 2001 From: Chinh Duong Date: Tue, 8 Oct 2024 23:18:50 +0700 Subject: [PATCH 6/9] #1116-[Recommendation Service]-Create service to get product detail for storing pgVector - Fix unit test --- .../service/ProductRecommendationService.java | 155 ------------------ 1 file changed, 155 deletions(-) delete mode 100644 product/src/main/java/com/yas/product/service/ProductRecommendationService.java diff --git a/product/src/main/java/com/yas/product/service/ProductRecommendationService.java b/product/src/main/java/com/yas/product/service/ProductRecommendationService.java deleted file mode 100644 index 2a1aef530c..0000000000 --- a/product/src/main/java/com/yas/product/service/ProductRecommendationService.java +++ /dev/null @@ -1,155 +0,0 @@ -package com.yas.product.service; - -import com.yas.commonlibrary.exception.NotFoundException; -import com.yas.product.model.Brand; -import com.yas.product.model.Category; -import com.yas.product.model.Product; -import com.yas.product.model.ProductCategory; -import com.yas.product.model.ProductOptionCombination; -import com.yas.product.repository.ProductOptionCombinationRepository; -import com.yas.product.repository.ProductRepository; -import com.yas.product.utils.Constants; -import com.yas.product.viewmodel.ImageVm; -import com.yas.product.viewmodel.product.ProductDetailInfoVm; -import com.yas.product.viewmodel.product.ProductVariationGetVm; -import com.yas.product.viewmodel.productattribute.ProductAttributeValueGetVm; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -/** - * Service class responsible for managing product recommendations. - */ -@Slf4j -@Service -@Transactional -public class ProductRecommendationService { - private final ProductRepository productRepository; - private final MediaService mediaService; - private final ProductOptionCombinationRepository productOptionCombinationRepository; - - /** - * Constructor for {@code ProductRecommendationService} that initializes the service with necessary dependencies. - * - * @param productRepository the repository responsible for handling product data - * @param mediaService the service responsible for managing media assets associated - * with products - * @param productOptionCombinationRepository the repository for managing product option combinations - */ - public ProductRecommendationService(ProductRepository productRepository, MediaService mediaService, - ProductOptionCombinationRepository productOptionCombinationRepository) { - this.productRepository = productRepository; - this.mediaService = mediaService; - this.productOptionCombinationRepository = productOptionCombinationRepository; - } - - /** - * Retrieves detailed information about a product by its unique identifier. - * - * @param productId the unique identifier of the product to be retrieved - * @return a {@link ProductDetailInfoVm} containing detailed information about the product - * @throws NotFoundException if no product is found with the given {@code productId} - */ - public ProductDetailInfoVm getProductDetailById(long productId) { - List variations = new ArrayList<>(); - Product product = productRepository - .findById(productId) - .filter(Product::isPublished) - .orElseThrow(() -> - new NotFoundException(Constants.ErrorCode.PRODUCT_NOT_FOUND, productId) - ); - - List categories = Optional.ofNullable(product.getProductCategories()) - .orElse(Collections.emptyList()) // Handle null case - .stream() - .map(ProductCategory::getCategory) - .toList(); - - Long brandId = Optional.ofNullable(product.getBrand()) - .map(Brand::getId) - .orElse(null); - String brandName = Optional.ofNullable(product.getBrand()) - .map(Brand::getName) - .orElse(null); - - List productAttributes = product.getAttributeValues() - .stream() - .map(ProductAttributeValueGetVm::fromModel) - .toList(); - - if (Boolean.TRUE.equals(product.isHasOptions())) { - List productVariations = product.getProducts() - .stream() - .toList(); - variations = productVariations.stream() - .filter(Product::isPublished) - .map(pro -> { - List productOptionCombinations = - productOptionCombinationRepository.findAllByProduct(pro); - Map options = productOptionCombinations.stream().collect(Collectors.toMap( - productOptionCombination -> productOptionCombination.getProductOption().getId(), - ProductOptionCombination::getValue - )); - - return new ProductVariationGetVm( - pro.getId(), - pro.getName(), - pro.getSlug(), - pro.getSku(), - pro.getGtin(), - pro.getPrice(), - getThumbnailFromProduct(pro), - getImagesFromProduct(pro), - options - ); - }).toList(); - } - return new ProductDetailInfoVm(product.getId(), - product.getName(), - product.getShortDescription(), - product.getDescription(), - product.getSpecification(), - product.getSku(), - product.getGtin(), - product.getSlug(), - product.isAllowedToOrder(), - product.isPublished(), - product.isFeatured(), - product.isVisibleIndividually(), - product.isStockTrackingEnabled(), - product.getPrice(), - brandId, - categories, - product.getMetaTitle(), - product.getMetaKeyword(), - product.getMetaDescription(), - product.getTaxClassId(), - brandName, - productAttributes, - variations, - getThumbnailFromProduct(product), - getImagesFromProduct(product) - ); - } - - private ImageVm getThumbnailFromProduct(Product product) { - return Optional.ofNullable(product.getThumbnailMediaId()) - .map(thumbnailId -> new ImageVm(thumbnailId, mediaService.getMedia(thumbnailId).url())) - .orElse(null); - - } - - private List getImagesFromProduct(Product product) { - return Optional.ofNullable(product.getProductImages()) - .orElse(Collections.emptyList()) - .stream() - .map(image -> new ImageVm(image.getImageId(), mediaService.getMedia(image.getImageId()).url())) - .toList(); - } -} From af7b29f5fad5c66dd6920b5878d26ee48cac6b03 Mon Sep 17 00:00:00 2001 From: Chinh Duong Date: Tue, 8 Oct 2024 23:27:18 +0700 Subject: [PATCH 7/9] #1116-[Recommendation Service]-Create service to get product detail for storing pgVector - Remane DTO --- .../yas/recommendation/dto/CategoryDto.java | 19 --------- .../com/yas/recommendation/dto/ImageDto.java | 6 --- .../dto/ProductAttributeValueDto.java | 14 ------- .../recommendation/dto/ProductDetailDto.java | 38 ------------------ .../dto/ProductVariationDto.java | 20 ---------- .../recommendation/dto/RelatedProductDto.java | 40 ------------------- 6 files changed, 137 deletions(-) delete mode 100644 recommendation/src/main/java/com/yas/recommendation/dto/CategoryDto.java delete mode 100644 recommendation/src/main/java/com/yas/recommendation/dto/ImageDto.java delete mode 100644 recommendation/src/main/java/com/yas/recommendation/dto/ProductAttributeValueDto.java delete mode 100644 recommendation/src/main/java/com/yas/recommendation/dto/ProductDetailDto.java delete mode 100644 recommendation/src/main/java/com/yas/recommendation/dto/ProductVariationDto.java delete mode 100644 recommendation/src/main/java/com/yas/recommendation/dto/RelatedProductDto.java diff --git a/recommendation/src/main/java/com/yas/recommendation/dto/CategoryDto.java b/recommendation/src/main/java/com/yas/recommendation/dto/CategoryDto.java deleted file mode 100644 index 9181ffe5fd..0000000000 --- a/recommendation/src/main/java/com/yas/recommendation/dto/CategoryDto.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.yas.recommendation.dto; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonInclude.Include; - -@JsonInclude(Include.NON_NULL) -@JsonIgnoreProperties(ignoreUnknown = true) -public record CategoryDto( - Long id, - String name, - String description, - String slug, - String metaKeyword, - String metaDescription, - Short displayOrder, - Boolean isPublished -) { -} diff --git a/recommendation/src/main/java/com/yas/recommendation/dto/ImageDto.java b/recommendation/src/main/java/com/yas/recommendation/dto/ImageDto.java deleted file mode 100644 index 02d311482b..0000000000 --- a/recommendation/src/main/java/com/yas/recommendation/dto/ImageDto.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.yas.recommendation.dto; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -@JsonIgnoreProperties(ignoreUnknown = true) -public record ImageDto(Long id, String url) {} diff --git a/recommendation/src/main/java/com/yas/recommendation/dto/ProductAttributeValueDto.java b/recommendation/src/main/java/com/yas/recommendation/dto/ProductAttributeValueDto.java deleted file mode 100644 index b513cc9601..0000000000 --- a/recommendation/src/main/java/com/yas/recommendation/dto/ProductAttributeValueDto.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.yas.recommendation.dto; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonInclude.Include; - -@JsonInclude(Include.NON_NULL) -@JsonIgnoreProperties(ignoreUnknown = true) -public record ProductAttributeValueDto( - long id, - String nameProductAttribute, - String value -) { -} diff --git a/recommendation/src/main/java/com/yas/recommendation/dto/ProductDetailDto.java b/recommendation/src/main/java/com/yas/recommendation/dto/ProductDetailDto.java deleted file mode 100644 index 674d166e55..0000000000 --- a/recommendation/src/main/java/com/yas/recommendation/dto/ProductDetailDto.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.yas.recommendation.dto; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonInclude.Include; - -import java.util.List; - -@JsonInclude(Include.NON_NULL) -@JsonIgnoreProperties(ignoreUnknown = true) -public record ProductDetailDto( - long id, - String name, - String shortDescription, - String description, - String specification, - String sku, - String gtin, - String slug, - Boolean isAllowedToOrder, - Boolean isPublished, - Boolean isFeatured, - Boolean isVisible, - Boolean stockTrackingEnabled, - Double price, - Long brandId, - List categories, - String metaTitle, - String metaKeyword, - String metaDescription, - Long taxClassId, - String brandName, - List attributeValues, - List variations, - ImageDto thumbnail, - List productImages -) { -} diff --git a/recommendation/src/main/java/com/yas/recommendation/dto/ProductVariationDto.java b/recommendation/src/main/java/com/yas/recommendation/dto/ProductVariationDto.java deleted file mode 100644 index 64a8d74066..0000000000 --- a/recommendation/src/main/java/com/yas/recommendation/dto/ProductVariationDto.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.yas.recommendation.dto; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonInclude.Include; - -import java.util.Map; - -@JsonInclude(Include.NON_NULL) -@JsonIgnoreProperties(ignoreUnknown = true) -public record ProductVariationDto( - Long id, - String name, - String slug, - String sku, - String gtin, - Double price, - Map options -) { -} diff --git a/recommendation/src/main/java/com/yas/recommendation/dto/RelatedProductDto.java b/recommendation/src/main/java/com/yas/recommendation/dto/RelatedProductDto.java deleted file mode 100644 index 8c7cea1d74..0000000000 --- a/recommendation/src/main/java/com/yas/recommendation/dto/RelatedProductDto.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.yas.recommendation.dto; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.math.BigDecimal; -import java.util.List; -import lombok.Getter; - -@Getter -@JsonIgnoreProperties(ignoreUnknown = true) -public class RelatedProductDto { - - @JsonProperty("id") - private Integer productId; - - private String name; - - private BigDecimal price; - - @JsonProperty("brandName") - private String brand; - - @JsonProperty("metaTitle") - private String title; - - private String description; - - private String metaDescription; - - @JsonProperty("specification") - private String specification; - - private ImageDto thumbnail; - - private List productImages; - - public RelatedProductDto() { - } -} - From d8a8c290aa03d3d4a8f08eca957d508a3af99d88 Mon Sep 17 00:00:00 2001 From: Duy Le Van Date: Wed, 9 Oct 2024 11:34:49 +0700 Subject: [PATCH 8/9] #1102 - Add recommendation-ci.yaml & setup deployment for recommendation service --- .env | 1 + .github/workflows/recommendation-ci.yaml | 91 ++++++++++++++++++++++++ k8s/charts/yas-configuration/values.yaml | 1 + nginx/templates/default.conf.template | 3 + 4 files changed, 96 insertions(+) create mode 100644 .github/workflows/recommendation-ci.yaml diff --git a/.env b/.env index 39009bbfe9..2f34d16058 100644 --- a/.env +++ b/.env @@ -78,6 +78,7 @@ YAS_SERVICES_PROMOTION=http://promotion/promotion YAS_SERVICES_INVENTORY=http://inventory/inventory YAS_SERVICES_RATING=http://rating/rating YAS_SERVICES_SAMPLE_DATA=http://sampledata/sampledata +YAS_SERVICES_RECOMMENDATION=http://recommendation/recommendation SERVER_PORT=80 # Swagger UI diff --git a/.github/workflows/recommendation-ci.yaml b/.github/workflows/recommendation-ci.yaml new file mode 100644 index 0000000000..01cc91f60c --- /dev/null +++ b/.github/workflows/recommendation-ci.yaml @@ -0,0 +1,91 @@ +name: recommendation service ci + +on: + push: + branches: ["main"] + paths: + - "recommendation/**" + - ".github/workflows/actions/action.yaml" + - ".github/workflows/recommendation-ci.yaml" + - "pom.xml" + pull_request: + branches: ["main"] + paths: + - "recommendation/**" + - ".github/workflows/actions/action.yaml" + - ".github/workflows/recommendation-ci.yaml" + - "pom.xml" + workflow_dispatch: + +jobs: + Build: + runs-on: ubuntu-latest + env: + FROM_ORIGINAL_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name == github.repository || github.ref == 'refs/heads/main' }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - uses: ./.github/workflows/actions + - name: Run Maven Build Command + run: mvn clean install -pl recommendation -am + - name: Run Maven Checkstyle + if: ${{ env.FROM_ORIGINAL_REPOSITORY == 'true' }} + run: mvn checkstyle:checkstyle -pl recommendation -am -Dcheckstyle.output.file=recommendation-checkstyle-result.xml + - name: Upload Checkstyle Result + if: ${{ env.FROM_ORIGINAL_REPOSITORY == 'true' }} + uses: jwgmeligmeyling/checkstyle-github-action@master + with: + path: '**/recommendation-checkstyle-result.xml' + - name: Test Results + uses: dorny/test-reporter@v1 + if: ${{ env.FROM_ORIGINAL_REPOSITORY == 'true' && (success() || failure()) }} + continue-on-error: true # TODO: remove once defining UT + with: + name: Recommendation-Service-Unit-Test-Results + path: "recommendation/**/*-reports/TEST*.xml" + reporter: java-junit + - name: Analyze with sonar cloud + if: ${{ env.FROM_ORIGINAL_REPOSITORY == 'true' }} + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: mvn org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -f recommendation + - name: OWASP Dependency Check + if: ${{ env.FROM_ORIGINAL_REPOSITORY == 'true' }} + uses: dependency-check/Dependency-Check_Action@main + env: + JAVA_HOME: /opt/jdk + with: + project: 'yas' + path: '.' + format: 'HTML' + - name: Upload OWASP Dependency Check results + if: ${{ env.FROM_ORIGINAL_REPOSITORY == 'true' }} + uses: actions/upload-artifact@master + with: + name: OWASP Dependency Check Report + path: ${{github.workspace}}/reports + - name: Add coverage report to PR + uses: madrapps/jacoco-report@v1.6.1 + if: ${{ env.FROM_ORIGINAL_REPOSITORY == 'true' }} + with: + paths: ${{github.workspace}}/recommendation/target/site/jacoco/jacoco.xml + token: ${{secrets.GITHUB_TOKEN}} + min-coverage-overall: 80 + min-coverage-changed-files: 60 + title: 'Recommendation Coverage Report' + update-comment: true + - name: Log in to the Container registry + if: ${{ github.ref == 'refs/heads/main' }} + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push Docker images + if: ${{ github.ref == 'refs/heads/main' }} + uses: docker/build-push-action@v6 + with: + context: ./recommendation + push: true + tags: ghcr.io/nashtech-garage/yas-recommendation:latest \ No newline at end of file diff --git a/k8s/charts/yas-configuration/values.yaml b/k8s/charts/yas-configuration/values.yaml index d153dd387b..acc67e3b27 100644 --- a/k8s/charts/yas-configuration/values.yaml +++ b/k8s/charts/yas-configuration/values.yaml @@ -84,6 +84,7 @@ applicationConfig: search: http://search/search tax: http://tax/tax sampledata: http://sampledata/sampledata + recommendation: http://recommendation/recommendation # Gateway config for bff microservices gatewayRoutesConfig: diff --git a/nginx/templates/default.conf.template b/nginx/templates/default.conf.template index efac44bc64..71fca5ce3e 100644 --- a/nginx/templates/default.conf.template +++ b/nginx/templates/default.conf.template @@ -54,6 +54,9 @@ server { location /sampledata/ { proxy_pass http://sampledata; } + location /recommendation/ { + proxy_pass http://recommendation; + } } server { From 2b61b34f8eb1cbea2d2cea5d83e005a462f647a8 Mon Sep 17 00:00:00 2001 From: Duy Le Van Date: Thu, 10 Oct 2024 10:28:47 +0700 Subject: [PATCH 9/9] #1102 - Add recommendation service to container --- .env | 7 ++++- docker-compose.yml | 29 +++++++++++++++++++ recommendation/Dockerfile | 3 ++ .../src/main/resources/application.properties | 6 ++-- 4 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 recommendation/Dockerfile diff --git a/.env b/.env index 2f34d16058..f67fddce55 100644 --- a/.env +++ b/.env @@ -89,4 +89,9 @@ COMPOSE_FILE=docker-compose.yml:docker-compose.search.yml:docker-compose.o11y.ym # Additional configuration YAS_PRICE_INCLUDES_TAX=false -YAS_CURRENCY_UNIT=VND \ No newline at end of file +YAS_CURRENCY_UNIT=VND + +# OpenAI Config +SPRING_AI_AZURE_OPENAI_API_KEY= +SPRING_AI_AZURE_OPENAI_ENDPOINT= +SPRING_AI_AZURE_OPENAI_EMBEDDING_OPTIONS_MODEL= \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 538fa1dab6..79a5ac90b6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -497,6 +497,35 @@ services: - ./deployment/app-config:/app-config networks: - yas-network + recommendation: + build: ./recommendation + image: ghcr.io/nashtech-garage/yas-recommendation:latest + ports: + - '9791:9791' + environment: + - SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/recommendation + - SERVER_SERVLET_CONTEXT_PATH=/recommendation + - YAS_PUBLIC_URL=${YAS_PUBLIC_API_URL}/recommendation + - YAS_SERVICES_PRODUCT + - SERVER_PORT + - LOGGING_CONFIG + - JAVA_TOOL_OPTIONS + - OTEL_EXPORTER_OTLP_ENDPOINT + - OTEL_EXPORTER_OTLP_PROTOCOL + - OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE + - OTEL_RESOURCE_ATTRIBUTES + - OTEL_SERVICE_NAME=recommendation-service + - OTEL_LOGS_EXPORTER + - OTEL_TRACES_EXPORTER + - OTEL_METRICS_EXPORTER + - OTEL_INSTRUMENTATION_LOGBACK-MDC_ADD-BAGGAGE + - OTEL_JAVAAGENT_LOGGING + - OTEL_JAVAAGENT_ENABLED + - OTEL_JAVAAGENT_DEBUG + # OpenAI Config + - SPRING_AI_AZURE_OPENAI_API_KEY + - SPRING_AI_AZURE_OPENAI_ENDPOINT + - SPRING_AI_AZURE_OPENAI_EMBEDDING_OPTIONS_MODEL swagger-ui: image: swaggerapi/swagger-ui:v4.16.0 environment: diff --git a/recommendation/Dockerfile b/recommendation/Dockerfile new file mode 100644 index 0000000000..e41eaeea6f --- /dev/null +++ b/recommendation/Dockerfile @@ -0,0 +1,3 @@ +FROM eclipse-temurin:21-jre-alpine +COPY target/recommendation*.jar app.jar +ENTRYPOINT ["java", "-jar", "/app.jar"] \ No newline at end of file diff --git a/recommendation/src/main/resources/application.properties b/recommendation/src/main/resources/application.properties index 9ed2599991..405a4390fb 100644 --- a/recommendation/src/main/resources/application.properties +++ b/recommendation/src/main/resources/application.properties @@ -27,9 +27,9 @@ spring.ai.vectorstore.pgvector.index-type=HNSW spring.ai.vectorstore.pgvector.distance-type=COSINE_DISTANCE # Azure AI configuration -spring.ai.azure.openai.api-key=xxx -spring.ai.azure.openai.endpoint=https://product-recommendation.openai.azure.com -spring.ai.azure.openai.embedding.options.model=text-embedding-3-small +spring.ai.azure.openai.api-key= +spring.ai.azure.openai.endpoint= +spring.ai.azure.openai.embedding.options.model= # swagger-ui custom path springdoc.swagger-ui.path=/swagger-ui