mirror of
https://gitea.gofwd.group/Forward_Group/ballistic-builder-spring.git
synced 2025-12-06 02:56:44 -05:00
no idea?
This commit is contained in:
@@ -1,6 +1,17 @@
|
|||||||
package group.goforward.ballistic.imports;
|
package group.goforward.ballistic.imports;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import org.apache.commons.csv.CSVFormat;
|
||||||
|
import org.apache.commons.csv.CSVParser;
|
||||||
|
import org.apache.commons.csv.CSVRecord;
|
||||||
|
|
||||||
import group.goforward.ballistic.model.Brand;
|
import group.goforward.ballistic.model.Brand;
|
||||||
import group.goforward.ballistic.model.Merchant;
|
import group.goforward.ballistic.model.Merchant;
|
||||||
@@ -34,66 +45,37 @@ public class MerchantFeedImportServiceImpl implements MerchantFeedImportService
|
|||||||
Merchant merchant = merchantRepository.findById(merchantId)
|
Merchant merchant = merchantRepository.findById(merchantId)
|
||||||
.orElseThrow(() -> new IllegalArgumentException("Merchant not found: " + merchantId));
|
.orElseThrow(() -> new IllegalArgumentException("Merchant not found: " + merchantId));
|
||||||
|
|
||||||
// For now, just pick a brand to prove inserts work (Aero Precision for merchant 4).
|
// Read all rows from the merchant feed
|
||||||
// Later we can switch to row.brandName() + auto-create brands.
|
List<MerchantFeedRow> rows = readFeedRowsForMerchant(merchant);
|
||||||
Brand brand = brandRepository.findByNameIgnoreCase("Aero Precision")
|
System.out.println("IMPORT >>> read " + rows.size() + " rows for merchant=" + merchant.getName());
|
||||||
.orElseThrow(() -> new IllegalStateException("Brand 'Aero Precision' not found"));
|
|
||||||
|
|
||||||
// TODO: replace this with real feed parsing:
|
for (MerchantFeedRow row : rows) {
|
||||||
// List<MerchantFeedRow> rows = feedClient.fetch(merchant);
|
// Resolve brand from the row (fallback to "Aero Precision" or whatever you want as default)
|
||||||
// rows.forEach(row -> upsertProduct(merchant, brand, row));
|
Brand brand = resolveBrand(row);
|
||||||
MerchantFeedRow row = new MerchantFeedRow(
|
Product p = upsertProduct(merchant, brand, row);
|
||||||
"TEST-SKU-001",
|
|
||||||
"APPG100002",
|
|
||||||
brand.getName(),
|
|
||||||
"Test Product From Import",
|
|
||||||
"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 = upsertProduct(merchant, brand, row);
|
System.out.println("IMPORT >>> upserted product id=" + p.getId()
|
||||||
|
+ ", name=" + p.getName()
|
||||||
System.out.println("IMPORT >>> upserted product id=" + p.getId()
|
+ ", slug=" + p.getSlug()
|
||||||
+ ", name=" + p.getName()
|
+ ", platform=" + p.getPlatform()
|
||||||
+ ", slug=" + p.getSlug()
|
+ ", partRole=" + p.getPartRole()
|
||||||
+ ", platform=" + p.getPlatform()
|
+ ", merchant=" + merchant.getName());
|
||||||
+ ", partRole=" + p.getPartRole()
|
}
|
||||||
+ ", merchant=" + merchant.getName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// ---------------------------------------------------------------------
|
||||||
* Upsert logic:
|
// Upsert logic
|
||||||
* - Try Brand+MPN, then Brand+UPC (for now using sku as a stand-in)
|
// ---------------------------------------------------------------------
|
||||||
* - If found, update fields but keep existing slug
|
|
||||||
* - If not found, create a new Product and generate a unique slug
|
|
||||||
*/
|
|
||||||
private Product upsertProduct(Merchant merchant, Brand brand, MerchantFeedRow row) {
|
private Product upsertProduct(Merchant merchant, Brand brand, MerchantFeedRow row) {
|
||||||
System.out.println("IMPORT >>> upsertProduct brand=" + brand.getName()
|
System.out.println("IMPORT >>> upsertProduct brand=" + brand.getName()
|
||||||
+ ", sku=" + row.sku()
|
+ ", sku=" + row.sku()
|
||||||
+ ", productName=" + row.productName());
|
+ ", productName=" + row.productName());
|
||||||
|
|
||||||
String mpn = trimOrNull(row.manufacturerId());
|
String mpn = trimOrNull(row.manufacturerId());
|
||||||
String upc = trimOrNull(row.sku()); // later: real UPC column
|
String upc = trimOrNull(row.sku()); // placeholder until real UPC field
|
||||||
|
|
||||||
java.util.List<Product> candidates = java.util.Collections.emptyList();
|
List<Product> candidates = Collections.emptyList();
|
||||||
|
|
||||||
if (mpn != null) {
|
if (mpn != null) {
|
||||||
candidates = productRepository.findAllByBrandAndMpn(brand, mpn);
|
candidates = productRepository.findAllByBrandAndMpn(brand, mpn);
|
||||||
@@ -118,16 +100,10 @@ public class MerchantFeedImportServiceImpl implements MerchantFeedImportService
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateProductFromRow(p, row, isNew);
|
updateProductFromRow(p, row, isNew);
|
||||||
|
|
||||||
return productRepository.save(p);
|
return productRepository.save(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Shared mapping logic from feed row -> Product entity.
|
|
||||||
* If isNew = true, we generate a slug. Otherwise we leave the slug alone.
|
|
||||||
*/
|
|
||||||
private void updateProductFromRow(Product p, MerchantFeedRow row, boolean isNew) {
|
private void updateProductFromRow(Product p, MerchantFeedRow row, boolean isNew) {
|
||||||
|
|
||||||
// ---------- NAME ----------
|
// ---------- NAME ----------
|
||||||
String name = coalesce(
|
String name = coalesce(
|
||||||
trimOrNull(row.productName()),
|
trimOrNull(row.productName()),
|
||||||
@@ -158,7 +134,6 @@ public class MerchantFeedImportServiceImpl implements MerchantFeedImportService
|
|||||||
slug = "product-" + System.currentTimeMillis();
|
slug = "product-" + System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure slug is unique by appending a numeric suffix if needed
|
|
||||||
String uniqueSlug = generateUniqueSlug(slug);
|
String uniqueSlug = generateUniqueSlug(slug);
|
||||||
p.setSlug(uniqueSlug);
|
p.setSlug(uniqueSlug);
|
||||||
}
|
}
|
||||||
@@ -176,15 +151,13 @@ public class MerchantFeedImportServiceImpl implements MerchantFeedImportService
|
|||||||
p.setMainImageUrl(mainImage);
|
p.setMainImageUrl(mainImage);
|
||||||
|
|
||||||
// ---------- IDENTIFIERS ----------
|
// ---------- IDENTIFIERS ----------
|
||||||
// AvantLink "Manufacturer Id" is a good fit for MPN.
|
|
||||||
String mpn = coalesce(
|
String mpn = coalesce(
|
||||||
trimOrNull(row.manufacturerId()),
|
trimOrNull(row.manufacturerId()),
|
||||||
trimOrNull(row.sku())
|
trimOrNull(row.sku())
|
||||||
);
|
);
|
||||||
p.setMpn(mpn);
|
p.setMpn(mpn);
|
||||||
|
|
||||||
// Feed doesn’t give us UPC in the header you showed.
|
// UPC placeholder
|
||||||
// We’ll leave UPC null for now (or map later).
|
|
||||||
p.setUpc(null);
|
p.setUpc(null);
|
||||||
|
|
||||||
// ---------- PLATFORM ----------
|
// ---------- PLATFORM ----------
|
||||||
@@ -199,7 +172,100 @@ public class MerchantFeedImportServiceImpl implements MerchantFeedImportService
|
|||||||
p.setPartRole(partRole);
|
p.setPartRole(partRole);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Helpers ----------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
|
// Feed reading + brand resolution
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
private List<MerchantFeedRow> readFeedRowsForMerchant(Merchant merchant) {
|
||||||
|
String rawFeedUrl = merchant.getFeedUrl();
|
||||||
|
if (rawFeedUrl == null || rawFeedUrl.isBlank()) {
|
||||||
|
throw new IllegalStateException("Merchant " + merchant.getName() + " has no feed_url configured");
|
||||||
|
}
|
||||||
|
|
||||||
|
String feedUrl = rawFeedUrl.trim();
|
||||||
|
System.out.println("IMPORT >>> reading feed for merchant=" + merchant.getName() + " from: " + feedUrl);
|
||||||
|
|
||||||
|
List<MerchantFeedRow> rows = new ArrayList<>();
|
||||||
|
|
||||||
|
try (Reader reader = (feedUrl.startsWith("http://") || feedUrl.startsWith("https://"))
|
||||||
|
? new InputStreamReader(new URL(feedUrl).openStream(), StandardCharsets.UTF_8)
|
||||||
|
: java.nio.file.Files.newBufferedReader(java.nio.file.Paths.get(feedUrl), StandardCharsets.UTF_8);
|
||||||
|
CSVParser parser = CSVFormat.DEFAULT
|
||||||
|
.withFirstRecordAsHeader()
|
||||||
|
.withIgnoreSurroundingSpaces()
|
||||||
|
.withTrim()
|
||||||
|
.parse(reader)) {
|
||||||
|
|
||||||
|
for (CSVRecord rec : parser) {
|
||||||
|
MerchantFeedRow row = new MerchantFeedRow(
|
||||||
|
rec.get("SKU"),
|
||||||
|
rec.get("Manufacturer Id"),
|
||||||
|
rec.get("Brand Name"),
|
||||||
|
rec.get("Product Name"),
|
||||||
|
rec.get("Long Description"),
|
||||||
|
rec.get("Short Description"),
|
||||||
|
rec.get("Department"),
|
||||||
|
rec.get("Category"),
|
||||||
|
rec.get("SubCategory"),
|
||||||
|
rec.get("Thumb URL"),
|
||||||
|
rec.get("Image URL"),
|
||||||
|
rec.get("Buy Link"),
|
||||||
|
rec.get("Keywords"),
|
||||||
|
rec.get("Reviews"),
|
||||||
|
parseBigDecimal(rec.get("Retail Price")),
|
||||||
|
parseBigDecimal(rec.get("Sale Price")),
|
||||||
|
rec.get("Brand Page Link"),
|
||||||
|
rec.get("Brand Logo Image"),
|
||||||
|
rec.get("Product Page View Tracking"),
|
||||||
|
rec.get("Variants XML"),
|
||||||
|
rec.get("Medium Image URL"),
|
||||||
|
rec.get("Product Content Widget"),
|
||||||
|
rec.get("Google Categorization"),
|
||||||
|
rec.get("Item Based Commission")
|
||||||
|
);
|
||||||
|
|
||||||
|
rows.add(row);
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new RuntimeException("Failed to read feed for merchant "
|
||||||
|
+ merchant.getName() + " from " + feedUrl, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("IMPORT >>> parsed " + rows.size() + " rows for merchant=" + merchant.getName());
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Brand resolveBrand(MerchantFeedRow row) {
|
||||||
|
String rawBrand = trimOrNull(row.brandName());
|
||||||
|
final String brandName = (rawBrand != null) ? rawBrand : "Aero Precision";
|
||||||
|
|
||||||
|
return brandRepository.findByNameIgnoreCase(brandName)
|
||||||
|
.orElseGet(() -> {
|
||||||
|
Brand b = new Brand();
|
||||||
|
b.setName(brandName);
|
||||||
|
return brandRepository.save(b);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCol(String[] cols, int index) {
|
||||||
|
return (index >= 0 && index < cols.length) ? cols[index] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal parseBigDecimal(String raw) {
|
||||||
|
if (raw == null) return null;
|
||||||
|
String trimmed = raw.trim();
|
||||||
|
if (trimmed.isEmpty()) return null;
|
||||||
|
try {
|
||||||
|
return new BigDecimal(trimmed);
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
System.out.println("IMPORT !!! bad BigDecimal value: '" + raw + "', skipping");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Misc helpers
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
private String trimOrNull(String value) {
|
private String trimOrNull(String value) {
|
||||||
if (value == null) return null;
|
if (value == null) return null;
|
||||||
@@ -236,7 +302,6 @@ public class MerchantFeedImportServiceImpl implements MerchantFeedImportService
|
|||||||
if (lower.contains("ar-10") || lower.contains("ar10")) return "AR-10";
|
if (lower.contains("ar-10") || lower.contains("ar10")) return "AR-10";
|
||||||
if (lower.contains("ak-47") || lower.contains("ak47")) return "AK-47";
|
if (lower.contains("ak-47") || lower.contains("ak47")) return "AK-47";
|
||||||
|
|
||||||
// Default: treat Aero as AR-15 universe for now
|
|
||||||
return "AR-15";
|
return "AR-15";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,6 +326,9 @@ public class MerchantFeedImportServiceImpl implements MerchantFeedImportService
|
|||||||
if (lower.contains("lower")) {
|
if (lower.contains("lower")) {
|
||||||
return "lower-receiver";
|
return "lower-receiver";
|
||||||
}
|
}
|
||||||
|
if (lower.contains("magazine") || lower.contains("mag")) {
|
||||||
|
return "magazine";
|
||||||
|
}
|
||||||
if (lower.contains("stock") || lower.contains("buttstock")) {
|
if (lower.contains("stock") || lower.contains("buttstock")) {
|
||||||
return "stock";
|
return "stock";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user