fix(back-end): fix hex color
This commit is contained in:
@@ -9,10 +9,12 @@ import com.printcalculator.dto.ShopProductDetailDto;
|
|||||||
import com.printcalculator.dto.ShopProductModelDto;
|
import com.printcalculator.dto.ShopProductModelDto;
|
||||||
import com.printcalculator.dto.ShopProductSummaryDto;
|
import com.printcalculator.dto.ShopProductSummaryDto;
|
||||||
import com.printcalculator.dto.ShopProductVariantOptionDto;
|
import com.printcalculator.dto.ShopProductVariantOptionDto;
|
||||||
|
import com.printcalculator.entity.FilamentVariant;
|
||||||
import com.printcalculator.entity.ShopCategory;
|
import com.printcalculator.entity.ShopCategory;
|
||||||
import com.printcalculator.entity.ShopProduct;
|
import com.printcalculator.entity.ShopProduct;
|
||||||
import com.printcalculator.entity.ShopProductModelAsset;
|
import com.printcalculator.entity.ShopProductModelAsset;
|
||||||
import com.printcalculator.entity.ShopProductVariant;
|
import com.printcalculator.entity.ShopProductVariant;
|
||||||
|
import com.printcalculator.repository.FilamentVariantRepository;
|
||||||
import com.printcalculator.repository.ShopCategoryRepository;
|
import com.printcalculator.repository.ShopCategoryRepository;
|
||||||
import com.printcalculator.repository.ShopProductModelAssetRepository;
|
import com.printcalculator.repository.ShopProductModelAssetRepository;
|
||||||
import com.printcalculator.repository.ShopProductRepository;
|
import com.printcalculator.repository.ShopProductRepository;
|
||||||
@@ -31,9 +33,11 @@ import java.util.Collection;
|
|||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@@ -41,11 +45,13 @@ import java.util.stream.Collectors;
|
|||||||
public class PublicShopCatalogService {
|
public class PublicShopCatalogService {
|
||||||
private static final String SHOP_CATEGORY_MEDIA_USAGE_TYPE = "SHOP_CATEGORY";
|
private static final String SHOP_CATEGORY_MEDIA_USAGE_TYPE = "SHOP_CATEGORY";
|
||||||
private static final String SHOP_PRODUCT_MEDIA_USAGE_TYPE = "SHOP_PRODUCT";
|
private static final String SHOP_PRODUCT_MEDIA_USAGE_TYPE = "SHOP_PRODUCT";
|
||||||
|
private static final Pattern HEX_COLOR_PATTERN = Pattern.compile("^#?[A-Fa-f0-9]{6}$");
|
||||||
|
|
||||||
private final ShopCategoryRepository shopCategoryRepository;
|
private final ShopCategoryRepository shopCategoryRepository;
|
||||||
private final ShopProductRepository shopProductRepository;
|
private final ShopProductRepository shopProductRepository;
|
||||||
private final ShopProductVariantRepository shopProductVariantRepository;
|
private final ShopProductVariantRepository shopProductVariantRepository;
|
||||||
private final ShopProductModelAssetRepository shopProductModelAssetRepository;
|
private final ShopProductModelAssetRepository shopProductModelAssetRepository;
|
||||||
|
private final FilamentVariantRepository filamentVariantRepository;
|
||||||
private final PublicMediaQueryService publicMediaQueryService;
|
private final PublicMediaQueryService publicMediaQueryService;
|
||||||
private final ShopStorageService shopStorageService;
|
private final ShopStorageService shopStorageService;
|
||||||
|
|
||||||
@@ -53,12 +59,14 @@ public class PublicShopCatalogService {
|
|||||||
ShopProductRepository shopProductRepository,
|
ShopProductRepository shopProductRepository,
|
||||||
ShopProductVariantRepository shopProductVariantRepository,
|
ShopProductVariantRepository shopProductVariantRepository,
|
||||||
ShopProductModelAssetRepository shopProductModelAssetRepository,
|
ShopProductModelAssetRepository shopProductModelAssetRepository,
|
||||||
|
FilamentVariantRepository filamentVariantRepository,
|
||||||
PublicMediaQueryService publicMediaQueryService,
|
PublicMediaQueryService publicMediaQueryService,
|
||||||
ShopStorageService shopStorageService) {
|
ShopStorageService shopStorageService) {
|
||||||
this.shopCategoryRepository = shopCategoryRepository;
|
this.shopCategoryRepository = shopCategoryRepository;
|
||||||
this.shopProductRepository = shopProductRepository;
|
this.shopProductRepository = shopProductRepository;
|
||||||
this.shopProductVariantRepository = shopProductVariantRepository;
|
this.shopProductVariantRepository = shopProductVariantRepository;
|
||||||
this.shopProductModelAssetRepository = shopProductModelAssetRepository;
|
this.shopProductModelAssetRepository = shopProductModelAssetRepository;
|
||||||
|
this.filamentVariantRepository = filamentVariantRepository;
|
||||||
this.publicMediaQueryService = publicMediaQueryService;
|
this.publicMediaQueryService = publicMediaQueryService;
|
||||||
this.shopStorageService = shopStorageService;
|
this.shopStorageService = shopStorageService;
|
||||||
}
|
}
|
||||||
@@ -99,7 +107,12 @@ public class PublicShopCatalogService {
|
|||||||
List<ShopProductSummaryDto> products = productContext.entries().stream()
|
List<ShopProductSummaryDto> products = productContext.entries().stream()
|
||||||
.filter(entry -> allowedCategoryIds.contains(entry.product().getCategory().getId()))
|
.filter(entry -> allowedCategoryIds.contains(entry.product().getCategory().getId()))
|
||||||
.filter(entry -> !Boolean.TRUE.equals(featuredOnly) || Boolean.TRUE.equals(entry.product().getIsFeatured()))
|
.filter(entry -> !Boolean.TRUE.equals(featuredOnly) || Boolean.TRUE.equals(entry.product().getIsFeatured()))
|
||||||
.map(entry -> toProductSummaryDto(entry, productContext.productMediaBySlug(), language))
|
.map(entry -> toProductSummaryDto(
|
||||||
|
entry,
|
||||||
|
productContext.productMediaBySlug(),
|
||||||
|
productContext.variantColorHexByMaterialAndColor(),
|
||||||
|
language
|
||||||
|
))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
ShopCategoryDetailDto selectedCategoryDetail = selectedCategory != null
|
ShopCategoryDetailDto selectedCategoryDetail = selectedCategory != null
|
||||||
@@ -128,7 +141,12 @@ public class PublicShopCatalogService {
|
|||||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Product not found");
|
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Product not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
return toProductDetailDto(entry, productContext.productMediaBySlug(), language);
|
return toProductDetailDto(
|
||||||
|
entry,
|
||||||
|
productContext.productMediaBySlug(),
|
||||||
|
productContext.variantColorHexByMaterialAndColor(),
|
||||||
|
language
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProductModelDownload getProductModelDownload(String slug) {
|
public ProductModelDownload getProductModelDownload(String slug) {
|
||||||
@@ -187,11 +205,32 @@ public class PublicShopCatalogService {
|
|||||||
entries.stream().map(entry -> productMediaUsageKey(entry.product())).toList(),
|
entries.stream().map(entry -> productMediaUsageKey(entry.product())).toList(),
|
||||||
language
|
language
|
||||||
);
|
);
|
||||||
|
Map<String, String> variantColorHexByMaterialAndColor = buildFilamentVariantColorHexMap();
|
||||||
|
|
||||||
Map<String, ProductEntry> entriesBySlug = entries.stream()
|
Map<String, ProductEntry> entriesBySlug = entries.stream()
|
||||||
.collect(Collectors.toMap(entry -> entry.product().getSlug(), entry -> entry, (left, right) -> left, LinkedHashMap::new));
|
.collect(Collectors.toMap(entry -> entry.product().getSlug(), entry -> entry, (left, right) -> left, LinkedHashMap::new));
|
||||||
|
|
||||||
return new PublicProductContext(entries, entriesBySlug, productMediaBySlug);
|
return new PublicProductContext(entries, entriesBySlug, productMediaBySlug, variantColorHexByMaterialAndColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> buildFilamentVariantColorHexMap() {
|
||||||
|
Map<String, String> colorsByMaterialAndColor = new LinkedHashMap<>();
|
||||||
|
for (FilamentVariant variant : filamentVariantRepository.findByIsActiveTrue()) {
|
||||||
|
String materialCode = variant.getFilamentMaterialType() != null
|
||||||
|
? variant.getFilamentMaterialType().getMaterialCode()
|
||||||
|
: null;
|
||||||
|
String key = toMaterialAndColorKey(materialCode, variant.getColorName());
|
||||||
|
if (key == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String normalizedHex = normalizeHexColor(variant.getColorHex());
|
||||||
|
if (normalizedHex == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
colorsByMaterialAndColor.putIfAbsent(key, normalizedHex);
|
||||||
|
}
|
||||||
|
return colorsByMaterialAndColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ProductEntry> loadPublicProducts(Collection<UUID> activeCategoryIds) {
|
private List<ProductEntry> loadPublicProducts(Collection<UUID> activeCategoryIds) {
|
||||||
@@ -349,6 +388,7 @@ public class PublicShopCatalogService {
|
|||||||
|
|
||||||
private ShopProductSummaryDto toProductSummaryDto(ProductEntry entry,
|
private ShopProductSummaryDto toProductSummaryDto(ProductEntry entry,
|
||||||
Map<String, List<PublicMediaUsageDto>> productMediaBySlug,
|
Map<String, List<PublicMediaUsageDto>> productMediaBySlug,
|
||||||
|
Map<String, String> variantColorHexByMaterialAndColor,
|
||||||
String language) {
|
String language) {
|
||||||
List<PublicMediaUsageDto> images = productMediaBySlug.getOrDefault(productMediaUsageKey(entry.product()), List.of());
|
List<PublicMediaUsageDto> images = productMediaBySlug.getOrDefault(productMediaUsageKey(entry.product()), List.of());
|
||||||
return new ShopProductSummaryDto(
|
return new ShopProductSummaryDto(
|
||||||
@@ -365,7 +405,7 @@ public class PublicShopCatalogService {
|
|||||||
),
|
),
|
||||||
resolvePriceFrom(entry.variants()),
|
resolvePriceFrom(entry.variants()),
|
||||||
resolvePriceTo(entry.variants()),
|
resolvePriceTo(entry.variants()),
|
||||||
toVariantDto(entry.defaultVariant(), entry.defaultVariant()),
|
toVariantDto(entry.defaultVariant(), entry.defaultVariant(), variantColorHexByMaterialAndColor),
|
||||||
selectPrimaryMedia(images),
|
selectPrimaryMedia(images),
|
||||||
toProductModelDto(entry)
|
toProductModelDto(entry)
|
||||||
);
|
);
|
||||||
@@ -373,6 +413,7 @@ public class PublicShopCatalogService {
|
|||||||
|
|
||||||
private ShopProductDetailDto toProductDetailDto(ProductEntry entry,
|
private ShopProductDetailDto toProductDetailDto(ProductEntry entry,
|
||||||
Map<String, List<PublicMediaUsageDto>> productMediaBySlug,
|
Map<String, List<PublicMediaUsageDto>> productMediaBySlug,
|
||||||
|
Map<String, String> variantColorHexByMaterialAndColor,
|
||||||
String language) {
|
String language) {
|
||||||
List<PublicMediaUsageDto> images = productMediaBySlug.getOrDefault(productMediaUsageKey(entry.product()), List.of());
|
List<PublicMediaUsageDto> images = productMediaBySlug.getOrDefault(productMediaUsageKey(entry.product()), List.of());
|
||||||
String localizedSeoTitle = entry.product().getSeoTitleForLanguage(language);
|
String localizedSeoTitle = entry.product().getSeoTitleForLanguage(language);
|
||||||
@@ -398,9 +439,9 @@ public class PublicShopCatalogService {
|
|||||||
buildCategoryBreadcrumbs(entry.product().getCategory()),
|
buildCategoryBreadcrumbs(entry.product().getCategory()),
|
||||||
resolvePriceFrom(entry.variants()),
|
resolvePriceFrom(entry.variants()),
|
||||||
resolvePriceTo(entry.variants()),
|
resolvePriceTo(entry.variants()),
|
||||||
toVariantDto(entry.defaultVariant(), entry.defaultVariant()),
|
toVariantDto(entry.defaultVariant(), entry.defaultVariant(), variantColorHexByMaterialAndColor),
|
||||||
entry.variants().stream()
|
entry.variants().stream()
|
||||||
.map(variant -> toVariantDto(variant, entry.defaultVariant()))
|
.map(variant -> toVariantDto(variant, entry.defaultVariant(), variantColorHexByMaterialAndColor))
|
||||||
.toList(),
|
.toList(),
|
||||||
selectPrimaryMedia(images),
|
selectPrimaryMedia(images),
|
||||||
images,
|
images,
|
||||||
@@ -408,21 +449,65 @@ public class PublicShopCatalogService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ShopProductVariantOptionDto toVariantDto(ShopProductVariant variant, ShopProductVariant defaultVariant) {
|
private ShopProductVariantOptionDto toVariantDto(ShopProductVariant variant,
|
||||||
|
ShopProductVariant defaultVariant,
|
||||||
|
Map<String, String> variantColorHexByMaterialAndColor) {
|
||||||
if (variant == null) {
|
if (variant == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
String colorHex = normalizeHexColor(variant.getColorHex());
|
||||||
|
if (colorHex == null) {
|
||||||
|
String key = toMaterialAndColorKey(variant.getInternalMaterialCode(), variant.getColorName());
|
||||||
|
colorHex = key != null ? variantColorHexByMaterialAndColor.get(key) : null;
|
||||||
|
}
|
||||||
return new ShopProductVariantOptionDto(
|
return new ShopProductVariantOptionDto(
|
||||||
variant.getId(),
|
variant.getId(),
|
||||||
variant.getSku(),
|
variant.getSku(),
|
||||||
variant.getVariantLabel(),
|
variant.getVariantLabel(),
|
||||||
variant.getColorName(),
|
variant.getColorName(),
|
||||||
variant.getColorHex(),
|
colorHex,
|
||||||
variant.getPriceChf(),
|
variant.getPriceChf(),
|
||||||
defaultVariant != null && Objects.equals(defaultVariant.getId(), variant.getId())
|
defaultVariant != null && Objects.equals(defaultVariant.getId(), variant.getId())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String toMaterialAndColorKey(String materialCode, String colorName) {
|
||||||
|
String normalizedMaterialCode = normalizeMaterialCode(materialCode);
|
||||||
|
String normalizedColorName = normalizeColorName(colorName);
|
||||||
|
if (normalizedMaterialCode == null || normalizedColorName == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return normalizedMaterialCode + "|" + normalizedColorName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeMaterialCode(String materialCode) {
|
||||||
|
String raw = String.valueOf(materialCode == null ? "" : materialCode).trim();
|
||||||
|
if (raw.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return raw.toUpperCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeColorName(String colorName) {
|
||||||
|
String raw = String.valueOf(colorName == null ? "" : colorName).trim();
|
||||||
|
if (raw.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return raw.toLowerCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeHexColor(String value) {
|
||||||
|
String raw = String.valueOf(value == null ? "" : value).trim();
|
||||||
|
if (raw.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!HEX_COLOR_PATTERN.matcher(raw).matches()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String withHash = raw.startsWith("#") ? raw : "#" + raw;
|
||||||
|
return withHash.toUpperCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
private ShopProductModelDto toProductModelDto(ProductEntry entry) {
|
private ShopProductModelDto toProductModelDto(ProductEntry entry) {
|
||||||
if (entry.modelAsset() == null) {
|
if (entry.modelAsset() == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -494,7 +579,8 @@ public class PublicShopCatalogService {
|
|||||||
private record PublicProductContext(
|
private record PublicProductContext(
|
||||||
List<ProductEntry> entries,
|
List<ProductEntry> entries,
|
||||||
Map<String, ProductEntry> entriesBySlug,
|
Map<String, ProductEntry> entriesBySlug,
|
||||||
Map<String, List<PublicMediaUsageDto>> productMediaBySlug
|
Map<String, List<PublicMediaUsageDto>> productMediaBySlug,
|
||||||
|
Map<String, String> variantColorHexByMaterialAndColor
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user