mirror of
https://gitea.gofwd.group/Forward_Group/ballistic-builder-spring.git
synced 2026-01-20 16:51:03 -05:00
fixing part_role mapping
This commit is contained in:
@@ -37,7 +37,8 @@ public class AdminPartRoleMappingController {
|
|||||||
List<PartRoleMapping> mappings;
|
List<PartRoleMapping> mappings;
|
||||||
|
|
||||||
if (platform != null && !platform.isBlank()) {
|
if (platform != null && !platform.isBlank()) {
|
||||||
mappings = partRoleMappingRepository.findByPlatformOrderByPartRoleAsc(platform);
|
mappings = partRoleMappingRepository
|
||||||
|
.findByPlatformAndDeletedAtIsNullOrderByPartRoleAsc(platform);
|
||||||
} else {
|
} else {
|
||||||
mappings = partRoleMappingRepository.findAll();
|
mappings = partRoleMappingRepository.findAll();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package group.goforward.ballistic.model;
|
package group.goforward.ballistic.model;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "part_role_mappings")
|
@Table(name = "part_role_mappings")
|
||||||
@@ -14,15 +15,38 @@ public class PartRoleMapping {
|
|||||||
private String platform; // e.g. "AR-15"
|
private String platform; // e.g. "AR-15"
|
||||||
|
|
||||||
@Column(name = "part_role", nullable = false)
|
@Column(name = "part_role", nullable = false)
|
||||||
private String partRole; // e.g. "UPPER", "BARREL", etc.
|
private String partRole; // e.g. "LOWER_RECEIVER_STRIPPED"
|
||||||
|
|
||||||
@ManyToOne(optional = false)
|
@ManyToOne(optional = false, fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "part_category_id")
|
@JoinColumn(name = "part_category_id")
|
||||||
private PartCategory partCategory;
|
private PartCategory partCategory;
|
||||||
|
|
||||||
@Column(columnDefinition = "text")
|
@Column(columnDefinition = "text")
|
||||||
private String notes;
|
private String notes;
|
||||||
|
|
||||||
|
@Column(name = "created_at", updatable = false)
|
||||||
|
private OffsetDateTime createdAt;
|
||||||
|
|
||||||
|
@Column(name = "updated_at")
|
||||||
|
private OffsetDateTime updatedAt;
|
||||||
|
|
||||||
|
@Column(name = "deleted_at")
|
||||||
|
private OffsetDateTime deletedAt;
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
protected void onCreate() {
|
||||||
|
OffsetDateTime now = OffsetDateTime.now();
|
||||||
|
this.createdAt = now;
|
||||||
|
this.updatedAt = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreUpdate
|
||||||
|
protected void onUpdate() {
|
||||||
|
this.updatedAt = OffsetDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
// getters/setters
|
||||||
|
|
||||||
public Integer getId() {
|
public Integer getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@@ -62,4 +86,20 @@ public class PartRoleMapping {
|
|||||||
public void setNotes(String notes) {
|
public void setNotes(String notes) {
|
||||||
this.notes = notes;
|
this.notes = notes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OffsetDateTime getCreatedAt() {
|
||||||
|
return createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OffsetDateTime getUpdatedAt() {
|
||||||
|
return updatedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OffsetDateTime getDeletedAt() {
|
||||||
|
return deletedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeletedAt(OffsetDateTime deletedAt) {
|
||||||
|
this.deletedAt = deletedAt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,9 +4,29 @@ import group.goforward.ballistic.model.PartRoleMapping;
|
|||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface PartRoleMappingRepository extends JpaRepository<PartRoleMapping, Integer> {
|
public interface PartRoleMappingRepository extends JpaRepository<PartRoleMapping, Integer> {
|
||||||
|
|
||||||
// List mappings for a platform, ordered nicely for the UI
|
// For resolver: one mapping per platform + partRole
|
||||||
List<PartRoleMapping> findByPlatformOrderByPartRoleAsc(String platform);
|
Optional<PartRoleMapping> findFirstByPlatformAndPartRoleAndDeletedAtIsNull(
|
||||||
|
String platform,
|
||||||
|
String partRole
|
||||||
|
);
|
||||||
|
|
||||||
|
// Optional: debug / inspection
|
||||||
|
List<PartRoleMapping> findAllByPlatformAndPartRoleAndDeletedAtIsNull(
|
||||||
|
String platform,
|
||||||
|
String partRole
|
||||||
|
);
|
||||||
|
|
||||||
|
// This is the one PartRoleMappingService needs
|
||||||
|
List<PartRoleMapping> findByPlatformAndDeletedAtIsNullOrderByPartRoleAsc(
|
||||||
|
String platform
|
||||||
|
);
|
||||||
|
|
||||||
|
List<PartRoleMapping> findByPlatformAndPartCategory_SlugAndDeletedAtIsNull(
|
||||||
|
String platform,
|
||||||
|
String slug
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
|||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface ProductRepository extends JpaRepository<Product, Integer> {
|
public interface ProductRepository extends JpaRepository<Product, Integer> {
|
||||||
@@ -33,8 +34,8 @@ public interface ProductRepository extends JpaRepository<Product, Integer> {
|
|||||||
""")
|
""")
|
||||||
List<Product> findByPlatformWithBrand(@Param("platform") String platform);
|
List<Product> findByPlatformWithBrand(@Param("platform") String platform);
|
||||||
|
|
||||||
@Query(name="Products.findByPlatformWithBrand")
|
@Query(name = "Products.findByPlatformWithBrand")
|
||||||
List<Product> findByPlatformWithBrandNQ(@Param("platform") String platform);
|
List<Product> findByPlatformWithBrandNQ(@Param("platform") String platform);
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT p
|
SELECT p
|
||||||
@@ -50,7 +51,21 @@ List<Product> findByPlatformWithBrandNQ(@Param("platform") String platform);
|
|||||||
);
|
);
|
||||||
|
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
// Used by Gunbuilder service (if you wired this)
|
// Used by /api/gunbuilder/test-products-db
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT p
|
||||||
|
FROM Product p
|
||||||
|
JOIN FETCH p.brand b
|
||||||
|
WHERE p.platform = :platform
|
||||||
|
AND p.deletedAt IS NULL
|
||||||
|
ORDER BY p.id
|
||||||
|
""")
|
||||||
|
List<Product> findTop5ByPlatformWithBrand(@Param("platform") String platform);
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
// Used by GunbuilderProductService
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
@@ -59,7 +74,11 @@ List<Product> findByPlatformWithBrandNQ(@Param("platform") String platform);
|
|||||||
LEFT JOIN FETCH p.brand b
|
LEFT JOIN FETCH p.brand b
|
||||||
LEFT JOIN FETCH p.offers o
|
LEFT JOIN FETCH p.offers o
|
||||||
WHERE p.platform = :platform
|
WHERE p.platform = :platform
|
||||||
|
AND p.partRole IN :partRoles
|
||||||
AND p.deletedAt IS NULL
|
AND p.deletedAt IS NULL
|
||||||
""")
|
""")
|
||||||
List<Product> findSomethingForGunbuilder(@Param("platform") String platform);
|
List<Product> findForGunbuilderByPlatformAndPartRoles(
|
||||||
|
@Param("platform") String platform,
|
||||||
|
@Param("partRoles") Collection<String> partRoles
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -5,38 +5,77 @@ import group.goforward.ballistic.model.Product;
|
|||||||
import group.goforward.ballistic.repos.ProductRepository;
|
import group.goforward.ballistic.repos.ProductRepository;
|
||||||
import group.goforward.ballistic.web.dto.GunbuilderProductDto;
|
import group.goforward.ballistic.web.dto.GunbuilderProductDto;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import group.goforward.ballistic.model.PartRoleMapping;
|
||||||
|
import group.goforward.ballistic.repos.PartRoleMappingRepository;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class GunbuilderProductService {
|
public class GunbuilderProductService {
|
||||||
|
|
||||||
private final ProductRepository productRepository;
|
private final ProductRepository productRepository;
|
||||||
private final PartCategoryResolverService partCategoryResolverService;
|
private final PartCategoryResolverService partCategoryResolverService;
|
||||||
|
private final PartRoleMappingRepository partRoleMappingRepository;
|
||||||
|
|
||||||
public GunbuilderProductService(
|
public GunbuilderProductService(
|
||||||
ProductRepository productRepository,
|
ProductRepository productRepository,
|
||||||
PartCategoryResolverService partCategoryResolverService
|
PartCategoryResolverService partCategoryResolverService,
|
||||||
|
PartRoleMappingRepository partRoleMappingRepository
|
||||||
) {
|
) {
|
||||||
this.productRepository = productRepository;
|
this.productRepository = productRepository;
|
||||||
this.partCategoryResolverService = partCategoryResolverService;
|
this.partCategoryResolverService = partCategoryResolverService;
|
||||||
|
this.partRoleMappingRepository = partRoleMappingRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<GunbuilderProductDto> listGunbuilderProducts(String platform) {
|
/**
|
||||||
|
* 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) {
|
||||||
|
|
||||||
List<Product> products = productRepository.findSomethingForGunbuilder(platform);
|
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);
|
||||||
|
|
||||||
|
System.out.println(">>> GB: repo returned " + products.size() + " products");
|
||||||
|
|
||||||
|
Map<String, PartCategory> categoryCache = new HashMap<>();
|
||||||
|
|
||||||
return products.stream()
|
return products.stream()
|
||||||
.map(p -> {
|
.map(p -> {
|
||||||
var maybeCategory = partCategoryResolverService
|
PartCategory cat = categoryCache.computeIfAbsent(
|
||||||
.resolveForPlatformAndPartRole(platform, p.getPartRole());
|
p.getPartRole(),
|
||||||
|
role -> partCategoryResolverService
|
||||||
|
.resolveForPlatformAndPartRole(platform, role)
|
||||||
|
.orElse(null)
|
||||||
|
);
|
||||||
|
|
||||||
if (maybeCategory.isEmpty()) {
|
if (cat == null) {
|
||||||
// you can also log here
|
System.out.println(">>> GB: NO CATEGORY for platform=" + platform
|
||||||
return null;
|
+ ", partRole=" + p.getPartRole()
|
||||||
|
+ ", productId=" + p.getId());
|
||||||
|
} else {
|
||||||
|
System.out.println(">>> GB: CATEGORY for productId=" + p.getId()
|
||||||
|
+ " -> slug=" + cat.getSlug()
|
||||||
|
+ ", group=" + cat.getGroupName());
|
||||||
}
|
}
|
||||||
|
|
||||||
PartCategory cat = maybeCategory.get();
|
// 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(
|
return new GunbuilderProductDto(
|
||||||
p.getId(),
|
p.getId(),
|
||||||
@@ -47,11 +86,54 @@ public class GunbuilderProductService {
|
|||||||
p.getBestOfferPrice(),
|
p.getBestOfferPrice(),
|
||||||
p.getMainImageUrl(),
|
p.getMainImageUrl(),
|
||||||
p.getBestOfferBuyUrl(),
|
p.getBestOfferBuyUrl(),
|
||||||
cat.getSlug(),
|
categorySlug,
|
||||||
cat.getGroupName()
|
categoryGroup
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.filter(dto -> dto != null)
|
|
||||||
.toList();
|
.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,7 +1,8 @@
|
|||||||
package group.goforward.ballistic.services;
|
package group.goforward.ballistic.services;
|
||||||
|
|
||||||
import group.goforward.ballistic.model.PartCategory;
|
import group.goforward.ballistic.model.PartCategory;
|
||||||
import group.goforward.ballistic.repos.PartCategoryRepository;
|
import group.goforward.ballistic.model.PartRoleMapping;
|
||||||
|
import group.goforward.ballistic.repos.PartRoleMappingRepository;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -9,33 +10,29 @@ import java.util.Optional;
|
|||||||
@Service
|
@Service
|
||||||
public class PartCategoryResolverService {
|
public class PartCategoryResolverService {
|
||||||
|
|
||||||
private final PartCategoryRepository partCategoryRepository;
|
private final PartRoleMappingRepository partRoleMappingRepository;
|
||||||
|
|
||||||
public PartCategoryResolverService(PartCategoryRepository partCategoryRepository) {
|
public PartCategoryResolverService(PartRoleMappingRepository partRoleMappingRepository) {
|
||||||
this.partCategoryRepository = partCategoryRepository;
|
this.partRoleMappingRepository = partRoleMappingRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the canonical PartCategory for a given platform + partRole.
|
* Resolve a PartCategory for a given platform + partRole.
|
||||||
*
|
* Example: ("AR-15", "LOWER_RECEIVER_STRIPPED") -> category "lower"
|
||||||
* For now we keep it simple:
|
|
||||||
* - We treat partRole as the slug (e.g. "barrel", "upper", "trigger").
|
|
||||||
* - Normalize to lower-kebab (spaces -> dashes, lowercased).
|
|
||||||
* - Look up by slug in part_categories.
|
|
||||||
*
|
|
||||||
* Later, if we want per-merchant / per-platform overrides using category_mappings,
|
|
||||||
* we can extend this method without changing callers.
|
|
||||||
*/
|
*/
|
||||||
public Optional<PartCategory> resolveForPlatformAndPartRole(String platform, String partRole) {
|
public Optional<PartCategory> resolveForPlatformAndPartRole(String platform, String partRole) {
|
||||||
if (partRole == null || partRole.isBlank()) {
|
|
||||||
|
if (platform == null || partRole == null) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
String normalizedSlug = partRole
|
// Keep things case-sensitive since your DB values are already uppercase.
|
||||||
.trim()
|
Optional<PartRoleMapping> mappingOpt =
|
||||||
.toLowerCase()
|
partRoleMappingRepository.findFirstByPlatformAndPartRoleAndDeletedAtIsNull(
|
||||||
.replace(" ", "-");
|
platform,
|
||||||
|
partRole
|
||||||
|
);
|
||||||
|
|
||||||
return partCategoryRepository.findBySlug(normalizedSlug);
|
return mappingOpt.map(PartRoleMapping::getPartCategory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package group.goforward.ballistic.services;
|
||||||
|
|
||||||
|
import group.goforward.ballistic.repos.PartRoleMappingRepository;
|
||||||
|
import group.goforward.ballistic.web.dto.admin.PartRoleMappingDto;
|
||||||
|
import group.goforward.ballistic.web.dto.PartRoleToCategoryDto;
|
||||||
|
import group.goforward.ballistic.web.mapper.PartRoleMappingMapper;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class PartRoleMappingService {
|
||||||
|
|
||||||
|
private final PartRoleMappingRepository repository;
|
||||||
|
|
||||||
|
public PartRoleMappingService(PartRoleMappingRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PartRoleMappingDto> getMappingsForPlatform(String platform) {
|
||||||
|
return repository
|
||||||
|
.findByPlatformAndDeletedAtIsNullOrderByPartRoleAsc(platform)
|
||||||
|
.stream()
|
||||||
|
.map(PartRoleMappingMapper::toDto)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PartRoleToCategoryDto> getRoleToCategoryMap(String platform) {
|
||||||
|
return repository
|
||||||
|
.findByPlatformAndDeletedAtIsNullOrderByPartRoleAsc(platform)
|
||||||
|
.stream()
|
||||||
|
.map(PartRoleMappingMapper::toRoleMapDto)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package group.goforward.ballistic.web;
|
||||||
|
|
||||||
|
import group.goforward.ballistic.services.GunbuilderProductService;
|
||||||
|
import group.goforward.ballistic.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.io.Serializable;
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package group.goforward.ballistic.web;
|
||||||
|
|
||||||
|
import group.goforward.ballistic.services.PartRoleMappingService;
|
||||||
|
import group.goforward.ballistic.web.dto.admin.PartRoleMappingDto;
|
||||||
|
import group.goforward.ballistic.web.dto.PartRoleToCategoryDto;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/part-role-mappings")
|
||||||
|
public class PartRoleMappingController {
|
||||||
|
|
||||||
|
private final PartRoleMappingService service;
|
||||||
|
|
||||||
|
public PartRoleMappingController(PartRoleMappingService service) {
|
||||||
|
this.service = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Full view for admin UI
|
||||||
|
@GetMapping("/{platform}")
|
||||||
|
public List<PartRoleMappingDto> getMappings(@PathVariable String platform) {
|
||||||
|
return service.getMappingsForPlatform(platform);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thin mapping for the builder
|
||||||
|
@GetMapping("/{platform}/map")
|
||||||
|
public List<PartRoleToCategoryDto> getRoleMap(@PathVariable String platform) {
|
||||||
|
return service.getRoleToCategoryMap(platform);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package group.goforward.ballistic.web.dto;
|
||||||
|
|
||||||
|
public record PartRoleToCategoryDto(
|
||||||
|
String platform,
|
||||||
|
String partRole,
|
||||||
|
String categorySlug // e.g. "lower", "barrel", "optic"
|
||||||
|
) {}
|
||||||
@@ -4,7 +4,8 @@ public record PartRoleMappingDto(
|
|||||||
Integer id,
|
Integer id,
|
||||||
String platform,
|
String platform,
|
||||||
String partRole,
|
String partRole,
|
||||||
String categorySlug,
|
Integer partCategoryId,
|
||||||
String groupName,
|
String partCategorySlug,
|
||||||
|
String partCategoryName,
|
||||||
String notes
|
String notes
|
||||||
) {}
|
) {}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package group.goforward.ballistic.web.mapper;
|
||||||
|
|
||||||
|
import group.goforward.ballistic.model.PartCategory;
|
||||||
|
import group.goforward.ballistic.model.PartRoleMapping;
|
||||||
|
import group.goforward.ballistic.web.dto.admin.PartRoleMappingDto;
|
||||||
|
import group.goforward.ballistic.web.dto.PartRoleToCategoryDto;
|
||||||
|
|
||||||
|
public final class PartRoleMappingMapper {
|
||||||
|
|
||||||
|
private PartRoleMappingMapper() {
|
||||||
|
// utility class
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PartRoleMappingDto toDto(PartRoleMapping entity) {
|
||||||
|
PartCategory cat = entity.getPartCategory();
|
||||||
|
|
||||||
|
return new PartRoleMappingDto(
|
||||||
|
entity.getId(),
|
||||||
|
entity.getPlatform(),
|
||||||
|
entity.getPartRole(),
|
||||||
|
cat != null ? cat.getId() : null,
|
||||||
|
cat != null ? cat.getSlug() : null,
|
||||||
|
cat != null ? cat.getName() : null,
|
||||||
|
entity.getNotes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PartRoleToCategoryDto toRoleMapDto(PartRoleMapping entity) {
|
||||||
|
PartCategory cat = entity.getPartCategory();
|
||||||
|
|
||||||
|
return new PartRoleToCategoryDto(
|
||||||
|
entity.getPlatform(),
|
||||||
|
entity.getPartRole(),
|
||||||
|
cat != null ? cat.getSlug() : null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,8 +7,13 @@ spring.datasource.driver-class-name=org.postgresql.Driver
|
|||||||
|
|
||||||
# Hibernate properties
|
# Hibernate properties
|
||||||
#spring.jpa.hibernate.ddl-auto=update
|
#spring.jpa.hibernate.ddl-auto=update
|
||||||
spring.jpa.show-sql=true
|
#spring.jpa.show-sql=true
|
||||||
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
|
#spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
|
||||||
|
|
||||||
security.jwt.secret=ballistic-test-secret-key-1234567890-ABCDEFGHIJKLNMOPQRST
|
security.jwt.secret=ballistic-test-secret-key-1234567890-ABCDEFGHIJKLNMOPQRST
|
||||||
security.jwt.access-token-minutes=2880
|
security.jwt.access-token-minutes=2880
|
||||||
|
|
||||||
|
# Temp disabling logging to find what I fucked up
|
||||||
|
spring.jpa.show-sql=false
|
||||||
|
logging.level.org.hibernate.SQL=warn
|
||||||
|
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=warn
|
||||||
Reference in New Issue
Block a user