mirror of
https://gitea.gofwd.group/Forward_Group/ballistic-builder-spring.git
synced 2026-01-20 16:51:03 -05:00
new classifier and resolver for product mappings. also cleaned up /product endpoints
This commit is contained in:
@@ -1,83 +0,0 @@
|
||||
package group.goforward.battlbuilder.controllers;
|
||||
|
||||
import group.goforward.battlbuilder.services.GunbuilderProductService;
|
||||
import group.goforward.battlbuilder.web.dto.GunbuilderProductDto;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/gunbuilder")
|
||||
public class GunbuilderProductController {
|
||||
|
||||
private final GunbuilderProductService gunbuilderProductService;
|
||||
|
||||
public GunbuilderProductController(GunbuilderProductService gunbuilderProductService) {
|
||||
this.gunbuilderProductService = gunbuilderProductService;
|
||||
System.out.println(">>> GunbuilderProductController initialized");
|
||||
}
|
||||
|
||||
// 🔹 sanity check: is this controller even mapped?
|
||||
@GetMapping("/ping")
|
||||
public Map<String, String> ping() {
|
||||
return Map.of("status", "ok", "source", "GunbuilderProductController");
|
||||
}
|
||||
|
||||
// 🔹 super-dumb test: no DB, no service, just prove the route works
|
||||
@GetMapping("/test-products")
|
||||
public Map<String, Object> testProducts(@RequestParam String platform) {
|
||||
System.out.println(">>> /api/gunbuilder/test-products hit for platform=" + platform);
|
||||
|
||||
Map<String, Object> m = new java.util.HashMap<>();
|
||||
m.put("platform", platform);
|
||||
m.put("note", "test endpoint only");
|
||||
m.put("ok", true);
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* List products for the builder UI.
|
||||
*
|
||||
* Examples:
|
||||
* GET /api/gunbuilder/products?platform=AR-15
|
||||
* GET /api/gunbuilder/products?platform=AR-15&partRoles=LOWER_RECEIVER_STRIPPED&partRoles=LOWER_RECEIVER_COMPLETE
|
||||
*/
|
||||
@GetMapping("/products")
|
||||
public List<GunbuilderProductDto> listProducts(
|
||||
@RequestParam String platform,
|
||||
@RequestParam(required = false) List<String> partRoles
|
||||
) {
|
||||
return gunbuilderProductService.listGunbuilderProducts(platform, partRoles);
|
||||
}
|
||||
|
||||
@GetMapping("/products/by-category")
|
||||
public List<GunbuilderProductDto> listProductsByCategory(
|
||||
@RequestParam String platform,
|
||||
@RequestParam String categorySlug
|
||||
) {
|
||||
return gunbuilderProductService.listGunbuilderProductsByCategory(platform, categorySlug);
|
||||
}
|
||||
|
||||
// 🔹 DB test: hit repo via service and return a tiny view of products
|
||||
@GetMapping("/test-products-db")
|
||||
public List<Map<String, Object>> testProductsDb(@RequestParam String platform) {
|
||||
System.out.println(">>> /api/gunbuilder/test-products-db hit for platform=" + platform);
|
||||
|
||||
var products = gunbuilderProductService.getSampleProducts(platform);
|
||||
|
||||
return products.stream()
|
||||
.map(p -> {
|
||||
Map<String, Object> m = new java.util.HashMap<>();
|
||||
m.put("id", p.getId());
|
||||
m.put("name", p.getName());
|
||||
m.put("brand", p.getBrand() != null ? p.getBrand().getName() : null);
|
||||
m.put("partRole", p.getPartRole());
|
||||
return m;
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
package group.goforward.battlbuilder.services;
|
||||
|
||||
import group.goforward.battlbuilder.model.PartCategory;
|
||||
import group.goforward.battlbuilder.model.Product;
|
||||
import group.goforward.battlbuilder.repos.ProductRepository;
|
||||
import group.goforward.battlbuilder.web.dto.GunbuilderProductDto;
|
||||
import org.springframework.stereotype.Service;
|
||||
import group.goforward.battlbuilder.model.PartRoleMapping;
|
||||
import group.goforward.battlbuilder.repos.PartRoleMappingRepository;
|
||||
import group.goforward.battlbuilder.model.ImportStatus;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
@Service
|
||||
public class GunbuilderProductService {
|
||||
|
||||
private final ProductRepository productRepository;
|
||||
private final PartCategoryResolverService partCategoryResolverService;
|
||||
private final PartRoleMappingRepository partRoleMappingRepository;
|
||||
|
||||
public GunbuilderProductService(
|
||||
ProductRepository productRepository,
|
||||
PartCategoryResolverService partCategoryResolverService,
|
||||
PartRoleMappingRepository partRoleMappingRepository
|
||||
) {
|
||||
this.productRepository = productRepository;
|
||||
this.partCategoryResolverService = partCategoryResolverService;
|
||||
this.partRoleMappingRepository = partRoleMappingRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main builder endpoint.
|
||||
* For now we ONLY support calls that provide partRoles,
|
||||
* to avoid pulling the entire catalog into memory.
|
||||
*/
|
||||
public List<GunbuilderProductDto> listGunbuilderProducts(String platform, List<String> partRoles) {
|
||||
|
||||
System.out.println(">>> GB: listGunbuilderProducts platform=" + platform
|
||||
+ ", partRoles=" + partRoles);
|
||||
|
||||
if (partRoles == null || partRoles.isEmpty()) {
|
||||
System.out.println(">>> GB: no partRoles provided, returning empty list");
|
||||
return List.of();
|
||||
}
|
||||
|
||||
List<Product> products =
|
||||
productRepository.findForGunbuilderByPlatformAndPartRoles(
|
||||
platform,
|
||||
partRoles,
|
||||
ImportStatus.MAPPED
|
||||
);
|
||||
|
||||
System.out.println(">>> GB: repo returned " + products.size() + " products");
|
||||
|
||||
Map<String, PartCategory> categoryCache = new HashMap<>();
|
||||
|
||||
return products.stream()
|
||||
.map(p -> {
|
||||
PartCategory cat = categoryCache.computeIfAbsent(
|
||||
p.getPartRole(),
|
||||
role -> partCategoryResolverService
|
||||
.resolveForPlatformAndPartRole(platform, role)
|
||||
.orElse(null)
|
||||
);
|
||||
|
||||
if (cat == null) {
|
||||
System.out.println(">>> GB: NO CATEGORY for platform=" + platform
|
||||
+ ", partRole=" + p.getPartRole()
|
||||
+ ", productId=" + p.getId());
|
||||
} else {
|
||||
System.out.println(">>> GB: CATEGORY for productId=" + p.getId()
|
||||
+ " -> slug=" + cat.getSlug()
|
||||
+ ", group=" + cat.getGroupName());
|
||||
}
|
||||
|
||||
// TEMP: do NOT drop products if category is null.
|
||||
// Just mark them as "unmapped" so we can see them in the JSON.
|
||||
String categorySlug = (cat != null) ? cat.getSlug() : "unmapped";
|
||||
String categoryGroup = (cat != null) ? cat.getGroupName() : "Unmapped";
|
||||
|
||||
return new GunbuilderProductDto(
|
||||
p.getId(),
|
||||
p.getName(),
|
||||
p.getBrand().getName(),
|
||||
platform,
|
||||
p.getPartRole(),
|
||||
p.getBestOfferPrice(),
|
||||
p.getMainImageUrl(),
|
||||
p.getBestOfferBuyUrl(),
|
||||
categorySlug,
|
||||
categoryGroup
|
||||
);
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
|
||||
public List<GunbuilderProductDto> listGunbuilderProductsByCategory(
|
||||
String platform,
|
||||
String categorySlug
|
||||
) {
|
||||
System.out.println(">>> GB: listGunbuilderProductsByCategory platform=" + platform
|
||||
+ ", categorySlug=" + categorySlug);
|
||||
|
||||
if (platform == null || platform.isBlank()
|
||||
|| categorySlug == null || categorySlug.isBlank()) {
|
||||
System.out.println(">>> GB: missing platform or categorySlug, returning empty list");
|
||||
return List.of();
|
||||
}
|
||||
|
||||
List<PartRoleMapping> mappings =
|
||||
partRoleMappingRepository.findByPlatformAndPartCategory_SlugAndDeletedAtIsNull(
|
||||
platform,
|
||||
categorySlug
|
||||
);
|
||||
|
||||
List<String> partRoles = mappings.stream()
|
||||
.map(PartRoleMapping::getPartRole)
|
||||
.distinct()
|
||||
.toList();
|
||||
|
||||
System.out.println(">>> GB: resolved " + partRoles.size()
|
||||
+ " partRoles for categorySlug=" + categorySlug + " -> " + partRoles);
|
||||
|
||||
if (partRoles.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
// Reuse the existing method that already does all the DTO + category resolution logic
|
||||
return listGunbuilderProducts(platform, partRoles);
|
||||
}
|
||||
|
||||
public Optional<GunbuilderProductDto> getGunbuilderProductById(String id) {
|
||||
if (id == null || id.isBlank()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Integer numericId;
|
||||
try {
|
||||
numericId = Integer.parseInt(id);
|
||||
} catch (NumberFormatException ex) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return productRepository.findById(numericId)
|
||||
.map(p -> {
|
||||
String platform = p.getPlatform();
|
||||
|
||||
PartCategory cat = partCategoryResolverService
|
||||
.resolveForPlatformAndPartRole(platform, p.getPartRole())
|
||||
.orElse(null);
|
||||
|
||||
String categorySlug = (cat != null) ? cat.getSlug() : "unmapped";
|
||||
String categoryGroup = (cat != null) ? cat.getGroupName() : "Unmapped";
|
||||
|
||||
return new GunbuilderProductDto(
|
||||
p.getId(),
|
||||
p.getName(),
|
||||
p.getBrand().getName(),
|
||||
platform,
|
||||
p.getPartRole(),
|
||||
p.getBestOfferPrice(),
|
||||
p.getMainImageUrl(),
|
||||
p.getBestOfferBuyUrl(),
|
||||
categorySlug,
|
||||
categoryGroup
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tiny helper used ONLY by /test-products-db to prove DB wiring.
|
||||
*/
|
||||
public List<Product> getSampleProducts(String platform) {
|
||||
// You already have this wired via ProductRepository.findTop5ByPlatformWithBrand
|
||||
// If that method exists, keep using it; if not, you can stub a tiny query here.
|
||||
return productRepository.findTop5ByPlatformWithBrand(platform);
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package group.goforward.battlbuilder.web.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class GunbuilderProductDto {
|
||||
|
||||
private Integer id;
|
||||
private String name;
|
||||
private String brand;
|
||||
private String platform;
|
||||
private String partRole;
|
||||
private BigDecimal price;
|
||||
private String imageUrl;
|
||||
private String buyUrl;
|
||||
private String categorySlug;
|
||||
private String categoryGroup;
|
||||
|
||||
public GunbuilderProductDto(
|
||||
Integer id,
|
||||
String name,
|
||||
String brand,
|
||||
String platform,
|
||||
String partRole,
|
||||
BigDecimal price,
|
||||
String imageUrl,
|
||||
String buyUrl,
|
||||
String categorySlug,
|
||||
String categoryGroup
|
||||
) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.brand = brand;
|
||||
this.platform = platform;
|
||||
this.partRole = partRole;
|
||||
this.price = price;
|
||||
this.imageUrl = imageUrl;
|
||||
this.buyUrl = buyUrl;
|
||||
this.categorySlug = categorySlug;
|
||||
this.categoryGroup = categoryGroup;
|
||||
}
|
||||
|
||||
// --- Getters only (DTOs are read-only in most cases) ---
|
||||
public Integer getId() { return id; }
|
||||
public String getName() { return name; }
|
||||
public String getBrand() { return brand; }
|
||||
public String getPlatform() { return platform; }
|
||||
public String getPartRole() { return partRole; }
|
||||
public BigDecimal getPrice() { return price; }
|
||||
public String getImageUrl() { return imageUrl; }
|
||||
public String getBuyUrl() { return buyUrl; }
|
||||
public String getCategorySlug() { return categorySlug; }
|
||||
public String getCategoryGroup() { return categoryGroup; }
|
||||
}
|
||||
Reference in New Issue
Block a user