mirror of
https://gitea.gofwd.group/Forward_Group/ballistic-builder-spring.git
synced 2025-12-06 02:56:44 -05:00
dynamic category mapping and updating from admin.
This commit is contained in:
@@ -20,21 +20,21 @@ public class AdminCategoryController {
|
|||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public List<PartCategoryDto> listCategories() {
|
public List<PartCategoryDto> listCategories() {
|
||||||
return partCategories.findAllByOrderByGroupNameAscSortOrderAscNameAsc()
|
return partCategories
|
||||||
|
.findAllByOrderByGroupNameAscSortOrderAscNameAsc()
|
||||||
.stream()
|
.stream()
|
||||||
.map(this::toDto)
|
.map(this::toDto)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private PartCategoryDto toDto(PartCategory entity) {
|
private PartCategoryDto toDto(PartCategory entity) {
|
||||||
PartCategoryDto dto = new PartCategoryDto();
|
return new PartCategoryDto(
|
||||||
dto.setId(entity.getId());
|
entity.getId(),
|
||||||
dto.setSlug(entity.getSlug());
|
entity.getSlug(),
|
||||||
dto.setName(entity.getName());
|
entity.getName(),
|
||||||
dto.setDescription(entity.getDescription());
|
entity.getDescription(),
|
||||||
dto.setGroupName(entity.getGroupName());
|
entity.getGroupName(),
|
||||||
dto.setSortOrder(entity.getSortOrder());
|
entity.getSortOrder()
|
||||||
dto.setUuid(entity.getUuid());
|
);
|
||||||
return dto;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
package group.goforward.ballistic.controllers.admin;
|
||||||
|
|
||||||
|
import group.goforward.ballistic.model.AffiliateCategoryMap;
|
||||||
|
import group.goforward.ballistic.model.PartCategory;
|
||||||
|
import group.goforward.ballistic.repos.CategoryMappingRepository;
|
||||||
|
import group.goforward.ballistic.repos.PartCategoryRepository;
|
||||||
|
import group.goforward.ballistic.web.dto.admin.PartRoleMappingDto;
|
||||||
|
import group.goforward.ballistic.web.dto.admin.PartRoleMappingRequest;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/admin/category-mappings")
|
||||||
|
@CrossOrigin
|
||||||
|
public class AdminCategoryMappingController {
|
||||||
|
|
||||||
|
private final CategoryMappingRepository categoryMappingRepository;
|
||||||
|
private final PartCategoryRepository partCategoryRepository;
|
||||||
|
|
||||||
|
public AdminCategoryMappingController(
|
||||||
|
CategoryMappingRepository categoryMappingRepository,
|
||||||
|
PartCategoryRepository partCategoryRepository
|
||||||
|
) {
|
||||||
|
this.categoryMappingRepository = categoryMappingRepository;
|
||||||
|
this.partCategoryRepository = partCategoryRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/admin/category-mappings?platform=AR-15
|
||||||
|
@GetMapping
|
||||||
|
public List<PartRoleMappingDto> list(
|
||||||
|
@RequestParam(name = "platform", defaultValue = "AR-15") String platform
|
||||||
|
) {
|
||||||
|
List<AffiliateCategoryMap> mappings =
|
||||||
|
categoryMappingRepository.findBySourceTypeAndPlatformOrderById("PART_ROLE", platform);
|
||||||
|
|
||||||
|
return mappings.stream()
|
||||||
|
.map(this::toDto)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /api/admin/category-mappings
|
||||||
|
@PostMapping
|
||||||
|
public ResponseEntity<PartRoleMappingDto> create(
|
||||||
|
@RequestBody PartRoleMappingRequest request
|
||||||
|
) {
|
||||||
|
if (request.platform() == null || request.partRole() == null || request.categorySlug() == null) {
|
||||||
|
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "platform, partRole, and categorySlug are required");
|
||||||
|
}
|
||||||
|
|
||||||
|
PartCategory category = partCategoryRepository.findBySlug(request.categorySlug())
|
||||||
|
.orElseThrow(() -> new ResponseStatusException(
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
"Unknown category slug: " + request.categorySlug()
|
||||||
|
));
|
||||||
|
|
||||||
|
AffiliateCategoryMap mapping = new AffiliateCategoryMap();
|
||||||
|
mapping.setSourceType("PART_ROLE");
|
||||||
|
mapping.setSourceValue(request.partRole());
|
||||||
|
mapping.setPlatform(request.platform());
|
||||||
|
mapping.setPartCategory(category);
|
||||||
|
mapping.setNotes(request.notes());
|
||||||
|
|
||||||
|
AffiliateCategoryMap saved = categoryMappingRepository.save(mapping);
|
||||||
|
|
||||||
|
return ResponseEntity.status(HttpStatus.CREATED).body(toDto(saved));
|
||||||
|
}
|
||||||
|
|
||||||
|
// PUT /api/admin/category-mappings/{id}
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public PartRoleMappingDto update(
|
||||||
|
@PathVariable Integer id,
|
||||||
|
@RequestBody PartRoleMappingRequest request
|
||||||
|
) {
|
||||||
|
AffiliateCategoryMap mapping = categoryMappingRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Mapping not found"));
|
||||||
|
|
||||||
|
if (request.platform() != null) {
|
||||||
|
mapping.setPlatform(request.platform());
|
||||||
|
}
|
||||||
|
if (request.partRole() != null) {
|
||||||
|
mapping.setSourceValue(request.partRole());
|
||||||
|
}
|
||||||
|
if (request.categorySlug() != null) {
|
||||||
|
PartCategory category = partCategoryRepository.findBySlug(request.categorySlug())
|
||||||
|
.orElseThrow(() -> new ResponseStatusException(
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
"Unknown category slug: " + request.categorySlug()
|
||||||
|
));
|
||||||
|
mapping.setPartCategory(category);
|
||||||
|
}
|
||||||
|
if (request.notes() != null) {
|
||||||
|
mapping.setNotes(request.notes());
|
||||||
|
}
|
||||||
|
|
||||||
|
AffiliateCategoryMap saved = categoryMappingRepository.save(mapping);
|
||||||
|
return toDto(saved);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE /api/admin/category-mappings/{id}
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
|
public void delete(@PathVariable Integer id) {
|
||||||
|
if (!categoryMappingRepository.existsById(id)) {
|
||||||
|
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Mapping not found");
|
||||||
|
}
|
||||||
|
categoryMappingRepository.deleteById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PartRoleMappingDto toDto(AffiliateCategoryMap map) {
|
||||||
|
PartCategory cat = map.getPartCategory();
|
||||||
|
|
||||||
|
return new PartRoleMappingDto(
|
||||||
|
map.getId(),
|
||||||
|
map.getPlatform(),
|
||||||
|
map.getSourceValue(), // partRole
|
||||||
|
cat != null ? cat.getSlug() : null, // categorySlug
|
||||||
|
cat != null ? cat.getGroupName() : null,
|
||||||
|
map.getNotes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package group.goforward.ballistic.controllers.admin;
|
||||||
|
|
||||||
|
import group.goforward.ballistic.model.PartCategory;
|
||||||
|
import group.goforward.ballistic.repos.PartCategoryRepository;
|
||||||
|
import group.goforward.ballistic.web.dto.admin.PartCategoryDto;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/admin/part-categories")
|
||||||
|
@CrossOrigin // keep it loose for now, you can tighten origins later
|
||||||
|
public class PartCategoryAdminController {
|
||||||
|
|
||||||
|
private final PartCategoryRepository partCategories;
|
||||||
|
|
||||||
|
public PartCategoryAdminController(PartCategoryRepository partCategories) {
|
||||||
|
this.partCategories = partCategories;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public List<PartCategoryDto> list() {
|
||||||
|
return partCategories.findAllByOrderByGroupNameAscSortOrderAscNameAsc()
|
||||||
|
.stream()
|
||||||
|
.map(pc -> new PartCategoryDto(
|
||||||
|
pc.getId(),
|
||||||
|
pc.getSlug(),
|
||||||
|
pc.getName(),
|
||||||
|
pc.getDescription(),
|
||||||
|
pc.getGroupName(),
|
||||||
|
pc.getSortOrder()
|
||||||
|
))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,18 +8,17 @@ public class AffiliateCategoryMap {
|
|||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
@Column(name = "id", nullable = false)
|
|
||||||
private Integer id;
|
private Integer id;
|
||||||
|
|
||||||
// e.g. "PART_ROLE"
|
// e.g. "PART_ROLE", "RAW_CATEGORY", etc.
|
||||||
@Column(name = "source_type", nullable = false)
|
@Column(name = "source_type", nullable = false)
|
||||||
private String sourceType;
|
private String sourceType;
|
||||||
|
|
||||||
// e.g. "suppressor"
|
// the value we’re mapping from (e.g. "suppressor", "TRIGGER")
|
||||||
@Column(name = "source_value", nullable = false)
|
@Column(name = "source_value", nullable = false)
|
||||||
private String sourceValue;
|
private String sourceValue;
|
||||||
|
|
||||||
// e.g. "AR-15", nullable
|
// optional platform ("AR-15", "PRECISION", etc.)
|
||||||
@Column(name = "platform")
|
@Column(name = "platform")
|
||||||
private String platform;
|
private String platform;
|
||||||
|
|
||||||
@@ -30,6 +29,8 @@ public class AffiliateCategoryMap {
|
|||||||
@Column(name = "notes")
|
@Column(name = "notes")
|
||||||
private String notes;
|
private String notes;
|
||||||
|
|
||||||
|
// --- getters / setters ---
|
||||||
|
|
||||||
public Integer getId() {
|
public Integer getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,27 @@ package group.goforward.ballistic.repos;
|
|||||||
import group.goforward.ballistic.model.AffiliateCategoryMap;
|
import group.goforward.ballistic.model.AffiliateCategoryMap;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface CategoryMappingRepository extends JpaRepository<AffiliateCategoryMap, Integer> {
|
public interface CategoryMappingRepository extends JpaRepository<AffiliateCategoryMap, Integer> {
|
||||||
|
|
||||||
|
// Match by source_type + source_value + platform (case-insensitive)
|
||||||
Optional<AffiliateCategoryMap> findBySourceTypeAndSourceValueAndPlatformIgnoreCase(
|
Optional<AffiliateCategoryMap> findBySourceTypeAndSourceValueAndPlatformIgnoreCase(
|
||||||
String sourceType,
|
String sourceType,
|
||||||
String sourceValue,
|
String sourceValue,
|
||||||
String platform
|
String platform
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Fallback: match by source_type + source_value when platform is null/ignored
|
||||||
Optional<AffiliateCategoryMap> findBySourceTypeAndSourceValueIgnoreCase(
|
Optional<AffiliateCategoryMap> findBySourceTypeAndSourceValueIgnoreCase(
|
||||||
String sourceType,
|
String sourceType,
|
||||||
String sourceValue
|
String sourceValue
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Used by AdminCategoryMappingController: list mappings for a given source_type + platform
|
||||||
|
List<AffiliateCategoryMap> findBySourceTypeAndPlatformOrderById(
|
||||||
|
String sourceType,
|
||||||
|
String platform
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -1,35 +1,10 @@
|
|||||||
package group.goforward.ballistic.web.dto.admin;
|
package group.goforward.ballistic.web.dto.admin;
|
||||||
|
|
||||||
import java.util.UUID;
|
public record PartCategoryDto(
|
||||||
|
Integer id,
|
||||||
public class PartCategoryDto {
|
String slug,
|
||||||
private Integer id;
|
String name,
|
||||||
private String slug;
|
String description,
|
||||||
private String name;
|
String groupName,
|
||||||
private String description;
|
Integer sortOrder
|
||||||
private String groupName;
|
) {}
|
||||||
private Integer sortOrder;
|
|
||||||
private UUID uuid;
|
|
||||||
|
|
||||||
// getters + setters
|
|
||||||
public Integer getId() { return id; }
|
|
||||||
public void setId(Integer id) { this.id = id; }
|
|
||||||
|
|
||||||
public String getSlug() { return slug; }
|
|
||||||
public void setSlug(String slug) { this.slug = slug; }
|
|
||||||
|
|
||||||
public String getName() { return name; }
|
|
||||||
public void setName(String name) { this.name = name; }
|
|
||||||
|
|
||||||
public String getDescription() { return description; }
|
|
||||||
public void setDescription(String description) { this.description = description; }
|
|
||||||
|
|
||||||
public String getGroupName() { return groupName; }
|
|
||||||
public void setGroupName(String groupName) { this.groupName = groupName; }
|
|
||||||
|
|
||||||
public Integer getSortOrder() { return sortOrder; }
|
|
||||||
public void setSortOrder(Integer sortOrder) { this.sortOrder = sortOrder; }
|
|
||||||
|
|
||||||
public UUID getUuid() { return uuid; }
|
|
||||||
public void setUuid(UUID uuid) { this.uuid = uuid; }
|
|
||||||
}
|
|
||||||
@@ -1,69 +1,10 @@
|
|||||||
package group.goforward.ballistic.web.dto.admin;
|
package group.goforward.ballistic.web.dto.admin;
|
||||||
|
|
||||||
public class PartRoleMappingDto {
|
public record PartRoleMappingDto(
|
||||||
private Integer id;
|
Integer id,
|
||||||
private String platform;
|
String platform,
|
||||||
private String partRole;
|
String partRole,
|
||||||
private String categorySlug;
|
String categorySlug,
|
||||||
private String categoryName;
|
String groupName,
|
||||||
private String groupName;
|
String notes
|
||||||
private String notes;
|
) {}
|
||||||
|
|
||||||
// getters + setters...
|
|
||||||
|
|
||||||
public Integer getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(Integer id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPlatform() {
|
|
||||||
return platform;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPlatform(String platform) {
|
|
||||||
this.platform = platform;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPartRole() {
|
|
||||||
return partRole;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPartRole(String partRole) {
|
|
||||||
this.partRole = partRole;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCategorySlug() {
|
|
||||||
return categorySlug;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCategorySlug(String categorySlug) {
|
|
||||||
this.categorySlug = categorySlug;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCategoryName() {
|
|
||||||
return categoryName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCategoryName(String categoryName) {
|
|
||||||
this.categoryName = categoryName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getGroupName() {
|
|
||||||
return groupName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGroupName(String groupName) {
|
|
||||||
this.groupName = groupName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getNotes() {
|
|
||||||
return notes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNotes(String notes) {
|
|
||||||
this.notes = notes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package group.goforward.ballistic.web.dto.admin;
|
||||||
|
|
||||||
|
public record PartRoleMappingRequest(
|
||||||
|
String platform,
|
||||||
|
String partRole,
|
||||||
|
String categorySlug,
|
||||||
|
String notes
|
||||||
|
) {}
|
||||||
Reference in New Issue
Block a user