mirror of
https://gitea.gofwd.group/Forward_Group/ballistic-builder-spring.git
synced 2025-12-05 18:46:44 -05:00
slug handling changes
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
package group.goforward.ballistic.imports;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Optional;
|
||||
|
||||
import group.goforward.ballistic.model.Brand;
|
||||
import group.goforward.ballistic.model.Merchant;
|
||||
@@ -9,7 +11,6 @@ import group.goforward.ballistic.repos.ProductRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class MerchantFeedImportServiceImpl implements MerchantFeedImportService {
|
||||
@@ -33,25 +34,48 @@ public class MerchantFeedImportServiceImpl implements MerchantFeedImportService
|
||||
Merchant merchant = merchantRepository.findById(merchantId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Merchant not found: " + merchantId));
|
||||
|
||||
// For now, just pick a brand to prove inserts work.
|
||||
// For now, just pick a brand to prove inserts work (Aero Precision for merchant 4).
|
||||
// Later we can switch to row.brandName() + auto-create brands.
|
||||
Brand brand = brandRepository.findByNameIgnoreCase("Aero Precision")
|
||||
.orElseThrow(() -> new IllegalStateException("Brand 'Aero Precision' not found"));
|
||||
|
||||
// Fake a single row – we’ll swap this for real CSV parsing once the plumbing works
|
||||
// TODO: replace this with real feed parsing:
|
||||
// List<MerchantFeedRow> rows = feedClient.fetch(merchant);
|
||||
// rows.forEach(row -> upsertProduct(merchant, row));
|
||||
MerchantFeedRow row = new MerchantFeedRow(
|
||||
"TEST-SKU-001",
|
||||
"APPG100002",
|
||||
brand.getName(),
|
||||
"Test Product From Import",
|
||||
null, null, null, null, null,
|
||||
null, null, null, null, null,
|
||||
null, null,
|
||||
null, null, null, null, null, null, null, null
|
||||
"This is a long description from AvantLink.",
|
||||
"Short description from AvantLink.",
|
||||
"Rifles",
|
||||
"AR-15 Parts",
|
||||
"Handguards & Rails",
|
||||
"https://example.com/thumb.jpg",
|
||||
"https://example.com/image.jpg",
|
||||
"https://example.com/buy-link",
|
||||
"ar-15, handguard, aero",
|
||||
null,
|
||||
new BigDecimal("199.99"), // retailPrice
|
||||
new BigDecimal("149.99"), // salePrice
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"https://example.com/medium.jpg",
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
Product p = createProduct(brand, row);
|
||||
|
||||
System.out.println("IMPORT >>> created product id=" + p.getId()
|
||||
+ ", name=" + p.getName()
|
||||
+ ", slug=" + p.getSlug()
|
||||
+ ", platform=" + p.getPlatform()
|
||||
+ ", partRole=" + p.getPartRole()
|
||||
+ ", merchant=" + merchant.getName());
|
||||
}
|
||||
|
||||
@@ -63,23 +87,24 @@ public class MerchantFeedImportServiceImpl implements MerchantFeedImportService
|
||||
Product p = new Product();
|
||||
p.setBrand(brand);
|
||||
|
||||
String name = row.productName();
|
||||
if (name == null || name.isBlank()) {
|
||||
name = row.sku();
|
||||
}
|
||||
if (name == null || name.isBlank()) {
|
||||
// ---------- NAME ----------
|
||||
String name = coalesce(
|
||||
trimOrNull(row.productName()),
|
||||
trimOrNull(row.shortDescription()),
|
||||
trimOrNull(row.longDescription()),
|
||||
trimOrNull(row.sku())
|
||||
);
|
||||
if (name == null) {
|
||||
name = "Unknown Product";
|
||||
}
|
||||
|
||||
// Set required fields: name and slug
|
||||
p.setName(name);
|
||||
|
||||
// Generate a simple slug from the name (fallback to SKU if needed)
|
||||
String baseForSlug = name;
|
||||
if (baseForSlug == null || baseForSlug.isBlank()) {
|
||||
baseForSlug = row.sku();
|
||||
}
|
||||
if (baseForSlug == null || baseForSlug.isBlank()) {
|
||||
// ---------- SLUG ----------
|
||||
String baseForSlug = coalesce(
|
||||
trimOrNull(name),
|
||||
trimOrNull(row.sku())
|
||||
);
|
||||
if (baseForSlug == null) {
|
||||
baseForSlug = "product-" + System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@@ -87,22 +112,124 @@ public class MerchantFeedImportServiceImpl implements MerchantFeedImportService
|
||||
.toLowerCase()
|
||||
.replaceAll("[^a-z0-9]+", "-")
|
||||
.replaceAll("(^-|-$)", "");
|
||||
|
||||
if (slug.isBlank()) {
|
||||
slug = "product-" + System.currentTimeMillis();
|
||||
}
|
||||
|
||||
p.setSlug(slug);
|
||||
// Ensure slug is unique by appending a numeric suffix if needed
|
||||
String uniqueSlug = generateUniqueSlug(slug);
|
||||
p.setSlug(uniqueSlug);
|
||||
|
||||
if (p.getPlatform() == null || p.getPlatform().isBlank()) {
|
||||
p.setPlatform("AR-15");
|
||||
// ---------- DESCRIPTIONS ----------
|
||||
p.setShortDescription(trimOrNull(row.shortDescription()));
|
||||
p.setDescription(trimOrNull(row.longDescription()));
|
||||
|
||||
// ---------- IMAGE ----------
|
||||
String mainImage = coalesce(
|
||||
trimOrNull(row.imageUrl()),
|
||||
trimOrNull(row.mediumImageUrl()),
|
||||
trimOrNull(row.thumbUrl())
|
||||
);
|
||||
p.setMainImageUrl(mainImage);
|
||||
|
||||
// ---------- IDENTIFIERS ----------
|
||||
// AvantLink "Manufacturer Id" is a good fit for MPN.
|
||||
String mpn = coalesce(
|
||||
trimOrNull(row.manufacturerId()),
|
||||
trimOrNull(row.sku())
|
||||
);
|
||||
p.setMpn(mpn);
|
||||
|
||||
// Feed doesn’t give us UPC in the header you showed.
|
||||
// We’ll leave UPC null for now.
|
||||
p.setUpc(null);
|
||||
|
||||
// ---------- PLATFORM ----------
|
||||
// For now, hard-code to AR-15 to satisfy not-null constraint.
|
||||
// Later we can infer from row.category()/row.department().
|
||||
String platform = inferPlatform(row);
|
||||
p.setPlatform(platform != null ? platform : "AR-15");
|
||||
|
||||
// ---------- PART ROLE ----------
|
||||
// We can do a tiny heuristic off category/subcategory.
|
||||
String partRole = inferPartRole(row);
|
||||
if (partRole == null || partRole.isBlank()) {
|
||||
partRole = "unknown";
|
||||
}
|
||||
|
||||
if (p.getPartRole() == null || p.getPartRole().isBlank()) {
|
||||
p.setPartRole("unknown");
|
||||
}
|
||||
|
||||
p.setPartRole(partRole);
|
||||
|
||||
return productRepository.save(p);
|
||||
}
|
||||
|
||||
// --- Helpers ----------------------------------------------------------
|
||||
|
||||
private String trimOrNull(String value) {
|
||||
if (value == null) return null;
|
||||
String trimmed = value.trim();
|
||||
return trimmed.isEmpty() ? null : trimmed;
|
||||
}
|
||||
|
||||
private String coalesce(String... values) {
|
||||
if (values == null) return null;
|
||||
for (String v : values) {
|
||||
if (v != null && !v.isBlank()) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String generateUniqueSlug(String baseSlug) {
|
||||
String candidate = baseSlug;
|
||||
int suffix = 1;
|
||||
while (productRepository.existsBySlug(candidate)) {
|
||||
candidate = baseSlug + "-" + suffix;
|
||||
suffix++;
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
|
||||
private String inferPlatform(MerchantFeedRow row) {
|
||||
String department = coalesce(trimOrNull(row.department()), trimOrNull(row.category()));
|
||||
if (department == null) return null;
|
||||
|
||||
String lower = department.toLowerCase();
|
||||
if (lower.contains("ar-15") || lower.contains("ar15")) return "AR-15";
|
||||
if (lower.contains("ar-10") || lower.contains("ar10")) return "AR-10";
|
||||
if (lower.contains("ak-47") || lower.contains("ak47")) return "AK-47";
|
||||
|
||||
// Default: treat Aero as AR-15 universe for now
|
||||
return "AR-15";
|
||||
}
|
||||
|
||||
private String inferPartRole(MerchantFeedRow row) {
|
||||
String cat = coalesce(
|
||||
trimOrNull(row.subCategory()),
|
||||
trimOrNull(row.category())
|
||||
);
|
||||
if (cat == null) return null;
|
||||
|
||||
String lower = cat.toLowerCase();
|
||||
|
||||
if (lower.contains("handguard") || lower.contains("rail")) {
|
||||
return "handguard";
|
||||
}
|
||||
if (lower.contains("barrel")) {
|
||||
return "barrel";
|
||||
}
|
||||
if (lower.contains("upper")) {
|
||||
return "upper-receiver";
|
||||
}
|
||||
if (lower.contains("lower")) {
|
||||
return "lower-receiver";
|
||||
}
|
||||
if (lower.contains("stock") || lower.contains("buttstock")) {
|
||||
return "stock";
|
||||
}
|
||||
if (lower.contains("grip")) {
|
||||
return "grip";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
@@ -14,4 +14,7 @@ public interface ProductRepository extends JpaRepository<Product, Integer> {
|
||||
Optional<Product> findByBrandAndMpn(Brand brand, String mpn);
|
||||
|
||||
Optional<Product> findByBrandAndUpc(Brand brand, String upc);
|
||||
|
||||
boolean existsBySlug(String slug);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user