mirror of
https://gitea.gofwd.group/Forward_Group/ballistic-builder-spring.git
synced 2025-12-06 02:56:44 -05:00
lots of changes that I don't think I made, must of been the pull
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
package group.goforward.ballistic.configuration;
|
||||
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class CacheConfig {
|
||||
|
||||
@Bean
|
||||
public CacheManager cacheManager() {
|
||||
// Simple in-memory cache for dev/local
|
||||
return new ConcurrentMapCacheManager("gunbuilderProducts");
|
||||
}
|
||||
package group.goforward.ballistic.configuration;
|
||||
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class CacheConfig {
|
||||
|
||||
@Bean
|
||||
public CacheManager cacheManager() {
|
||||
// Simple in-memory cache for dev/local
|
||||
return new ConcurrentMapCacheManager("gunbuilderProducts");
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
/**
|
||||
* Provides the classes necessary for the Spring Configurations for the ballistic -Builder application.
|
||||
* This package includes Configurations for Spring-Boot application
|
||||
*
|
||||
*
|
||||
* <p>The main entry point for managing the inventory is the
|
||||
* {@link group.goforward.ballistic.BallisticApplication} class.</p>
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Don Strawsburg
|
||||
* @version 1.1
|
||||
*/
|
||||
/**
|
||||
* Provides the classes necessary for the Spring Configurations for the ballistic -Builder application.
|
||||
* This package includes Configurations for Spring-Boot application
|
||||
*
|
||||
*
|
||||
* <p>The main entry point for managing the inventory is the
|
||||
* {@link group.goforward.ballistic.BallisticApplication} class.</p>
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Don Strawsburg
|
||||
* @version 1.1
|
||||
*/
|
||||
package group.goforward.ballistic.configuration;
|
||||
@@ -1,17 +1,17 @@
|
||||
# Stage 1: Build the application
|
||||
FROM openjdk:17-jdk-slim as build
|
||||
WORKDIR /app
|
||||
COPY gradlew .
|
||||
COPY settings.gradle .
|
||||
COPY build.gradle .
|
||||
COPY src ./src
|
||||
# Adjust the build command for Maven: ./mvnw package -DskipTests
|
||||
RUN ./gradlew bootJar
|
||||
|
||||
# Stage 2: Create the final lightweight image
|
||||
FROM openjdk:17-jre-slim
|
||||
WORKDIR /app
|
||||
# Get the built JAR from the build stage
|
||||
COPY --from=build /app/build/libs/*.jar app.jar
|
||||
EXPOSE 8080
|
||||
# Stage 1: Build the application
|
||||
FROM openjdk:17-jdk-slim as build
|
||||
WORKDIR /app
|
||||
COPY gradlew .
|
||||
COPY settings.gradle .
|
||||
COPY build.gradle .
|
||||
COPY src ./src
|
||||
# Adjust the build command for Maven: ./mvnw package -DskipTests
|
||||
RUN ./gradlew bootJar
|
||||
|
||||
# Stage 2: Create the final lightweight image
|
||||
FROM openjdk:17-jre-slim
|
||||
WORKDIR /app
|
||||
# Get the built JAR from the build stage
|
||||
COPY --from=build /app/build/libs/*.jar app.jar
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["java", "-jar", "app.jar"]
|
||||
@@ -1,39 +1,39 @@
|
||||
package group.goforward.ballistic.controllers;
|
||||
|
||||
import group.goforward.ballistic.services.MerchantFeedImportService;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/admin/imports")
|
||||
@CrossOrigin(origins = "http://localhost:3000")
|
||||
public class ImportController {
|
||||
|
||||
private final MerchantFeedImportService merchantFeedImportService;
|
||||
|
||||
public ImportController(MerchantFeedImportService merchantFeedImportService) {
|
||||
this.merchantFeedImportService = merchantFeedImportService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Full product + offer import for a merchant.
|
||||
*
|
||||
* POST /admin/imports/{merchantId}
|
||||
*/
|
||||
@PostMapping("/{merchantId}")
|
||||
public ResponseEntity<Void> importMerchant(@PathVariable Integer merchantId) {
|
||||
merchantFeedImportService.importMerchantFeed(merchantId);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Offers-only sync (price/stock) for a merchant.
|
||||
*
|
||||
* POST /admin/imports/{merchantId}/offers-only
|
||||
*/
|
||||
@PostMapping("/{merchantId}/offers-only")
|
||||
public ResponseEntity<Void> syncOffersOnly(@PathVariable Integer merchantId) {
|
||||
merchantFeedImportService.syncOffersOnly(merchantId);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
package group.goforward.ballistic.controllers;
|
||||
|
||||
import group.goforward.ballistic.services.MerchantFeedImportService;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/admin/imports")
|
||||
@CrossOrigin(origins = "http://localhost:3000")
|
||||
public class ImportController {
|
||||
|
||||
private final MerchantFeedImportService merchantFeedImportService;
|
||||
|
||||
public ImportController(MerchantFeedImportService merchantFeedImportService) {
|
||||
this.merchantFeedImportService = merchantFeedImportService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Full product + offer import for a merchant.
|
||||
*
|
||||
* POST /admin/imports/{merchantId}
|
||||
*/
|
||||
@PostMapping("/{merchantId}")
|
||||
public ResponseEntity<Void> importMerchant(@PathVariable Integer merchantId) {
|
||||
merchantFeedImportService.importMerchantFeed(merchantId);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Offers-only sync (price/stock) for a merchant.
|
||||
*
|
||||
* POST /admin/imports/{merchantId}/offers-only
|
||||
*/
|
||||
@PostMapping("/{merchantId}/offers-only")
|
||||
public ResponseEntity<Void> syncOffersOnly(@PathVariable Integer merchantId) {
|
||||
merchantFeedImportService.syncOffersOnly(merchantId);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
}
|
||||
@@ -1,63 +1,63 @@
|
||||
// MerchantAdminController.java
|
||||
package group.goforward.ballistic.controllers;
|
||||
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import group.goforward.ballistic.model.Merchant;
|
||||
import group.goforward.ballistic.repos.MerchantRepository;
|
||||
import group.goforward.ballistic.web.dto.MerchantAdminDto;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/admin/merchants")
|
||||
@CrossOrigin(origins = "http://localhost:3000") // TEMP for Cross-Origin Bug
|
||||
public class MerchantAdminController {
|
||||
|
||||
private final MerchantRepository merchantRepository;
|
||||
|
||||
public MerchantAdminController(MerchantRepository merchantRepository) {
|
||||
this.merchantRepository = merchantRepository;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<MerchantAdminDto> listMerchants() {
|
||||
return merchantRepository.findAll().stream().map(this::toDto).toList();
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public MerchantAdminDto updateMerchant(
|
||||
@PathVariable Integer id,
|
||||
@RequestBody MerchantAdminDto payload
|
||||
) {
|
||||
Merchant merchant = merchantRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("Merchant not found"));
|
||||
|
||||
merchant.setFeedUrl(payload.getFeedUrl());
|
||||
merchant.setOfferFeedUrl(payload.getOfferFeedUrl());
|
||||
merchant.setIsActive(payload.getIsActive() != null ? payload.getIsActive() : true);
|
||||
// don’t touch last* here; those are set by import jobs
|
||||
|
||||
merchant = merchantRepository.save(merchant);
|
||||
return toDto(merchant);
|
||||
}
|
||||
|
||||
private MerchantAdminDto toDto(Merchant m) {
|
||||
MerchantAdminDto dto = new MerchantAdminDto();
|
||||
dto.setId(m.getId());
|
||||
dto.setName(m.getName());
|
||||
dto.setFeedUrl(m.getFeedUrl());
|
||||
dto.setOfferFeedUrl(m.getOfferFeedUrl());
|
||||
dto.setIsActive(m.getIsActive());
|
||||
dto.setLastFullImportAt(m.getLastFullImportAt());
|
||||
dto.setLastOfferSyncAt(m.getLastOfferSyncAt());
|
||||
return dto;
|
||||
}
|
||||
// MerchantAdminController.java
|
||||
package group.goforward.ballistic.controllers;
|
||||
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import group.goforward.ballistic.model.Merchant;
|
||||
import group.goforward.ballistic.repos.MerchantRepository;
|
||||
import group.goforward.ballistic.web.dto.MerchantAdminDto;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/admin/merchants")
|
||||
@CrossOrigin(origins = "http://localhost:3000") // TEMP for Cross-Origin Bug
|
||||
public class MerchantAdminController {
|
||||
|
||||
private final MerchantRepository merchantRepository;
|
||||
|
||||
public MerchantAdminController(MerchantRepository merchantRepository) {
|
||||
this.merchantRepository = merchantRepository;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<MerchantAdminDto> listMerchants() {
|
||||
return merchantRepository.findAll().stream().map(this::toDto).toList();
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public MerchantAdminDto updateMerchant(
|
||||
@PathVariable Integer id,
|
||||
@RequestBody MerchantAdminDto payload
|
||||
) {
|
||||
Merchant merchant = merchantRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("Merchant not found"));
|
||||
|
||||
merchant.setFeedUrl(payload.getFeedUrl());
|
||||
merchant.setOfferFeedUrl(payload.getOfferFeedUrl());
|
||||
merchant.setIsActive(payload.getIsActive() != null ? payload.getIsActive() : true);
|
||||
// don’t touch last* here; those are set by import jobs
|
||||
|
||||
merchant = merchantRepository.save(merchant);
|
||||
return toDto(merchant);
|
||||
}
|
||||
|
||||
private MerchantAdminDto toDto(Merchant m) {
|
||||
MerchantAdminDto dto = new MerchantAdminDto();
|
||||
dto.setId(m.getId());
|
||||
dto.setName(m.getName());
|
||||
dto.setFeedUrl(m.getFeedUrl());
|
||||
dto.setOfferFeedUrl(m.getOfferFeedUrl());
|
||||
dto.setIsActive(m.getIsActive());
|
||||
dto.setLastFullImportAt(m.getLastFullImportAt());
|
||||
dto.setLastOfferSyncAt(m.getLastOfferSyncAt());
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
@@ -1,65 +1,65 @@
|
||||
package group.goforward.ballistic.controllers;
|
||||
|
||||
import group.goforward.ballistic.model.Merchant;
|
||||
import group.goforward.ballistic.model.MerchantCategoryMapping;
|
||||
import group.goforward.ballistic.repos.MerchantRepository;
|
||||
import group.goforward.ballistic.services.MerchantCategoryMappingService;
|
||||
import group.goforward.ballistic.web.dto.MerchantCategoryMappingDto;
|
||||
import group.goforward.ballistic.web.dto.UpsertMerchantCategoryMappingRequest;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/admin/merchant-category-mappings")
|
||||
@CrossOrigin
|
||||
public class MerchantCategoryMappingController {
|
||||
|
||||
private final MerchantCategoryMappingService mappingService;
|
||||
private final MerchantRepository merchantRepository;
|
||||
|
||||
public MerchantCategoryMappingController(
|
||||
MerchantCategoryMappingService mappingService,
|
||||
MerchantRepository merchantRepository
|
||||
) {
|
||||
this.mappingService = mappingService;
|
||||
this.merchantRepository = merchantRepository;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<MerchantCategoryMappingDto> listMappings(
|
||||
@RequestParam("merchantId") Integer merchantId
|
||||
) {
|
||||
List<MerchantCategoryMapping> mappings = mappingService.findByMerchant(merchantId);
|
||||
return mappings.stream()
|
||||
.map(this::toDto)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public MerchantCategoryMappingDto upsertMapping(
|
||||
@RequestBody UpsertMerchantCategoryMappingRequest request
|
||||
) {
|
||||
Merchant merchant = merchantRepository
|
||||
.findById(request.getMerchantId())
|
||||
.orElseThrow(() -> new IllegalArgumentException("Merchant not found: " + request.getMerchantId()));
|
||||
|
||||
MerchantCategoryMapping mapping = mappingService.upsertMapping(
|
||||
merchant,
|
||||
request.getRawCategory(),
|
||||
request.getMappedPartRole()
|
||||
);
|
||||
|
||||
return toDto(mapping);
|
||||
}
|
||||
|
||||
private MerchantCategoryMappingDto toDto(MerchantCategoryMapping mapping) {
|
||||
MerchantCategoryMappingDto dto = new MerchantCategoryMappingDto();
|
||||
dto.setId(mapping.getId());
|
||||
dto.setMerchantId(mapping.getMerchant().getId());
|
||||
dto.setMerchantName(mapping.getMerchant().getName());
|
||||
dto.setRawCategory(mapping.getRawCategory());
|
||||
dto.setMappedPartRole(mapping.getMappedPartRole());
|
||||
return dto;
|
||||
}
|
||||
package group.goforward.ballistic.controllers;
|
||||
|
||||
import group.goforward.ballistic.model.Merchant;
|
||||
import group.goforward.ballistic.model.MerchantCategoryMapping;
|
||||
import group.goforward.ballistic.repos.MerchantRepository;
|
||||
import group.goforward.ballistic.services.MerchantCategoryMappingService;
|
||||
import group.goforward.ballistic.web.dto.MerchantCategoryMappingDto;
|
||||
import group.goforward.ballistic.web.dto.UpsertMerchantCategoryMappingRequest;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/admin/merchant-category-mappings")
|
||||
@CrossOrigin
|
||||
public class MerchantCategoryMappingController {
|
||||
|
||||
private final MerchantCategoryMappingService mappingService;
|
||||
private final MerchantRepository merchantRepository;
|
||||
|
||||
public MerchantCategoryMappingController(
|
||||
MerchantCategoryMappingService mappingService,
|
||||
MerchantRepository merchantRepository
|
||||
) {
|
||||
this.mappingService = mappingService;
|
||||
this.merchantRepository = merchantRepository;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<MerchantCategoryMappingDto> listMappings(
|
||||
@RequestParam("merchantId") Integer merchantId
|
||||
) {
|
||||
List<MerchantCategoryMapping> mappings = mappingService.findByMerchant(merchantId);
|
||||
return mappings.stream()
|
||||
.map(this::toDto)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public MerchantCategoryMappingDto upsertMapping(
|
||||
@RequestBody UpsertMerchantCategoryMappingRequest request
|
||||
) {
|
||||
Merchant merchant = merchantRepository
|
||||
.findById(request.getMerchantId())
|
||||
.orElseThrow(() -> new IllegalArgumentException("Merchant not found: " + request.getMerchantId()));
|
||||
|
||||
MerchantCategoryMapping mapping = mappingService.upsertMapping(
|
||||
merchant,
|
||||
request.getRawCategory(),
|
||||
request.getMappedPartRole()
|
||||
);
|
||||
|
||||
return toDto(mapping);
|
||||
}
|
||||
|
||||
private MerchantCategoryMappingDto toDto(MerchantCategoryMapping mapping) {
|
||||
MerchantCategoryMappingDto dto = new MerchantCategoryMappingDto();
|
||||
dto.setId(mapping.getId());
|
||||
dto.setMerchantId(mapping.getMerchant().getId());
|
||||
dto.setMerchantName(mapping.getMerchant().getName());
|
||||
dto.setRawCategory(mapping.getRawCategory());
|
||||
dto.setMappedPartRole(mapping.getMappedPartRole());
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,23 @@
|
||||
package group.goforward.ballistic.controllers;
|
||||
|
||||
import group.goforward.ballistic.model.Merchant;
|
||||
import group.goforward.ballistic.repos.MerchantRepository;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
public class MerchantDebugController {
|
||||
|
||||
private final MerchantRepository merchantRepository;
|
||||
|
||||
public MerchantDebugController(MerchantRepository merchantRepository) {
|
||||
this.merchantRepository = merchantRepository;
|
||||
}
|
||||
|
||||
@GetMapping("/admin/debug/merchants")
|
||||
public List<Merchant> listMerchants() {
|
||||
return merchantRepository.findAll();
|
||||
}
|
||||
package group.goforward.ballistic.controllers;
|
||||
|
||||
import group.goforward.ballistic.model.Merchant;
|
||||
import group.goforward.ballistic.repos.MerchantRepository;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
public class MerchantDebugController {
|
||||
|
||||
private final MerchantRepository merchantRepository;
|
||||
|
||||
public MerchantDebugController(MerchantRepository merchantRepository) {
|
||||
this.merchantRepository = merchantRepository;
|
||||
}
|
||||
|
||||
@GetMapping("/admin/debug/merchants")
|
||||
public List<Merchant> listMerchants() {
|
||||
return merchantRepository.findAll();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package group.goforward.ballistic.controllers;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class PingController {
|
||||
|
||||
@GetMapping("/ping")
|
||||
public String ping() {
|
||||
return "pong";
|
||||
}
|
||||
package group.goforward.ballistic.controllers;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class PingController {
|
||||
|
||||
@GetMapping("/ping")
|
||||
public String ping() {
|
||||
return "pong";
|
||||
}
|
||||
}
|
||||
@@ -1,137 +1,137 @@
|
||||
package group.goforward.ballistic.controllers;
|
||||
|
||||
import group.goforward.ballistic.model.Product;
|
||||
import group.goforward.ballistic.model.ProductOffer;
|
||||
import group.goforward.ballistic.repos.ProductOfferRepository;
|
||||
import group.goforward.ballistic.web.dto.ProductOfferDto;
|
||||
import group.goforward.ballistic.repos.ProductRepository;
|
||||
import group.goforward.ballistic.web.dto.ProductSummaryDto;
|
||||
import group.goforward.ballistic.web.mapper.ProductMapper;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/products")
|
||||
@CrossOrigin
|
||||
public class ProductController {
|
||||
|
||||
private final ProductRepository productRepository;
|
||||
private final ProductOfferRepository productOfferRepository;
|
||||
|
||||
public ProductController(
|
||||
ProductRepository productRepository,
|
||||
ProductOfferRepository productOfferRepository
|
||||
) {
|
||||
this.productRepository = productRepository;
|
||||
this.productOfferRepository = productOfferRepository;
|
||||
}
|
||||
|
||||
@GetMapping("/gunbuilder")
|
||||
@Cacheable(
|
||||
value = "gunbuilderProducts",
|
||||
key = "#platform + '::' + (#partRoles == null ? 'ALL' : #partRoles.toString())"
|
||||
)
|
||||
public List<ProductSummaryDto> getGunbuilderProducts(
|
||||
@RequestParam(defaultValue = "AR-15") String platform,
|
||||
@RequestParam(required = false, name = "partRoles") List<String> partRoles
|
||||
) {
|
||||
long started = System.currentTimeMillis();
|
||||
System.out.println("getGunbuilderProducts: start, platform=" + platform +
|
||||
", partRoles=" + (partRoles == null ? "null" : partRoles));
|
||||
|
||||
// 1) Load products (with brand pre-fetched)
|
||||
long tProductsStart = System.currentTimeMillis();
|
||||
List<Product> products;
|
||||
if (partRoles == null || partRoles.isEmpty()) {
|
||||
products = productRepository.findByPlatformWithBrand(platform);
|
||||
} else {
|
||||
products = productRepository.findByPlatformAndPartRoleInWithBrand(platform, partRoles);
|
||||
}
|
||||
long tProductsEnd = System.currentTimeMillis();
|
||||
System.out.println("getGunbuilderProducts: loaded products: " +
|
||||
products.size() + " in " + (tProductsEnd - tProductsStart) + " ms");
|
||||
|
||||
if (products.isEmpty()) {
|
||||
long took = System.currentTimeMillis() - started;
|
||||
System.out.println("getGunbuilderProducts: 0 products in " + took + " ms");
|
||||
return List.of();
|
||||
}
|
||||
|
||||
// 2) Load offers for these product IDs
|
||||
long tOffersStart = System.currentTimeMillis();
|
||||
List<Integer> productIds = products.stream()
|
||||
.map(Product::getId)
|
||||
.toList();
|
||||
|
||||
List<ProductOffer> allOffers =
|
||||
productOfferRepository.findByProductIdIn(productIds);
|
||||
long tOffersEnd = System.currentTimeMillis();
|
||||
System.out.println("getGunbuilderProducts: loaded offers: " +
|
||||
allOffers.size() + " in " + (tOffersEnd - tOffersStart) + " ms");
|
||||
|
||||
Map<Integer, List<ProductOffer>> offersByProductId = allOffers.stream()
|
||||
.collect(Collectors.groupingBy(o -> o.getProduct().getId()));
|
||||
|
||||
// 3) Map to DTOs with price and buyUrl
|
||||
long tMapStart = System.currentTimeMillis();
|
||||
List<ProductSummaryDto> result = products.stream()
|
||||
.map(p -> {
|
||||
List<ProductOffer> offersForProduct =
|
||||
offersByProductId.getOrDefault(p.getId(), Collections.emptyList());
|
||||
|
||||
ProductOffer bestOffer = pickBestOffer(offersForProduct);
|
||||
|
||||
BigDecimal price = bestOffer != null ? bestOffer.getEffectivePrice() : null;
|
||||
String buyUrl = bestOffer != null ? bestOffer.getBuyUrl() : null;
|
||||
|
||||
return ProductMapper.toSummary(p, price, buyUrl);
|
||||
})
|
||||
.toList();
|
||||
long tMapEnd = System.currentTimeMillis();
|
||||
long took = System.currentTimeMillis() - started;
|
||||
|
||||
System.out.println("getGunbuilderProducts: mapping to DTOs took " +
|
||||
(tMapEnd - tMapStart) + " ms");
|
||||
System.out.println("getGunbuilderProducts: TOTAL " + took + " ms (" +
|
||||
"products=" + (tProductsEnd - tProductsStart) + " ms, " +
|
||||
"offers=" + (tOffersEnd - tOffersStart) + " ms, " +
|
||||
"map=" + (tMapEnd - tMapStart) + " ms)");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/offers")
|
||||
public List<ProductOfferDto> getOffersForProduct(@PathVariable("id") Integer productId) {
|
||||
List<ProductOffer> offers = productOfferRepository.findByProductId(productId);
|
||||
|
||||
return offers.stream()
|
||||
.map(offer -> {
|
||||
ProductOfferDto dto = new ProductOfferDto();
|
||||
dto.setId(offer.getId().toString());
|
||||
dto.setMerchantName(offer.getMerchant().getName());
|
||||
dto.setPrice(offer.getEffectivePrice());
|
||||
dto.setOriginalPrice(offer.getOriginalPrice());
|
||||
dto.setInStock(Boolean.TRUE.equals(offer.getInStock()));
|
||||
dto.setBuyUrl(offer.getBuyUrl());
|
||||
dto.setLastUpdated(offer.getLastSeenAt());
|
||||
return dto;
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
|
||||
private ProductOffer pickBestOffer(List<ProductOffer> offers) {
|
||||
if (offers == null || offers.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Right now: lowest price wins, regardless of stock (we set inStock=true on import anyway)
|
||||
return offers.stream()
|
||||
.filter(o -> o.getEffectivePrice() != null)
|
||||
.min(Comparator.comparing(ProductOffer::getEffectivePrice))
|
||||
.orElse(null);
|
||||
}
|
||||
package group.goforward.ballistic.controllers;
|
||||
|
||||
import group.goforward.ballistic.model.Product;
|
||||
import group.goforward.ballistic.model.ProductOffer;
|
||||
import group.goforward.ballistic.repos.ProductOfferRepository;
|
||||
import group.goforward.ballistic.web.dto.ProductOfferDto;
|
||||
import group.goforward.ballistic.repos.ProductRepository;
|
||||
import group.goforward.ballistic.web.dto.ProductSummaryDto;
|
||||
import group.goforward.ballistic.web.mapper.ProductMapper;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/products")
|
||||
@CrossOrigin
|
||||
public class ProductController {
|
||||
|
||||
private final ProductRepository productRepository;
|
||||
private final ProductOfferRepository productOfferRepository;
|
||||
|
||||
public ProductController(
|
||||
ProductRepository productRepository,
|
||||
ProductOfferRepository productOfferRepository
|
||||
) {
|
||||
this.productRepository = productRepository;
|
||||
this.productOfferRepository = productOfferRepository;
|
||||
}
|
||||
|
||||
@GetMapping("/gunbuilder")
|
||||
@Cacheable(
|
||||
value = "gunbuilderProducts",
|
||||
key = "#platform + '::' + (#partRoles == null ? 'ALL' : #partRoles.toString())"
|
||||
)
|
||||
public List<ProductSummaryDto> getGunbuilderProducts(
|
||||
@RequestParam(defaultValue = "AR-15") String platform,
|
||||
@RequestParam(required = false, name = "partRoles") List<String> partRoles
|
||||
) {
|
||||
long started = System.currentTimeMillis();
|
||||
System.out.println("getGunbuilderProducts: start, platform=" + platform +
|
||||
", partRoles=" + (partRoles == null ? "null" : partRoles));
|
||||
|
||||
// 1) Load products (with brand pre-fetched)
|
||||
long tProductsStart = System.currentTimeMillis();
|
||||
List<Product> products;
|
||||
if (partRoles == null || partRoles.isEmpty()) {
|
||||
products = productRepository.findByPlatformWithBrand(platform);
|
||||
} else {
|
||||
products = productRepository.findByPlatformAndPartRoleInWithBrand(platform, partRoles);
|
||||
}
|
||||
long tProductsEnd = System.currentTimeMillis();
|
||||
System.out.println("getGunbuilderProducts: loaded products: " +
|
||||
products.size() + " in " + (tProductsEnd - tProductsStart) + " ms");
|
||||
|
||||
if (products.isEmpty()) {
|
||||
long took = System.currentTimeMillis() - started;
|
||||
System.out.println("getGunbuilderProducts: 0 products in " + took + " ms");
|
||||
return List.of();
|
||||
}
|
||||
|
||||
// 2) Load offers for these product IDs
|
||||
long tOffersStart = System.currentTimeMillis();
|
||||
List<Integer> productIds = products.stream()
|
||||
.map(Product::getId)
|
||||
.toList();
|
||||
|
||||
List<ProductOffer> allOffers =
|
||||
productOfferRepository.findByProductIdIn(productIds);
|
||||
long tOffersEnd = System.currentTimeMillis();
|
||||
System.out.println("getGunbuilderProducts: loaded offers: " +
|
||||
allOffers.size() + " in " + (tOffersEnd - tOffersStart) + " ms");
|
||||
|
||||
Map<Integer, List<ProductOffer>> offersByProductId = allOffers.stream()
|
||||
.collect(Collectors.groupingBy(o -> o.getProduct().getId()));
|
||||
|
||||
// 3) Map to DTOs with price and buyUrl
|
||||
long tMapStart = System.currentTimeMillis();
|
||||
List<ProductSummaryDto> result = products.stream()
|
||||
.map(p -> {
|
||||
List<ProductOffer> offersForProduct =
|
||||
offersByProductId.getOrDefault(p.getId(), Collections.emptyList());
|
||||
|
||||
ProductOffer bestOffer = pickBestOffer(offersForProduct);
|
||||
|
||||
BigDecimal price = bestOffer != null ? bestOffer.getEffectivePrice() : null;
|
||||
String buyUrl = bestOffer != null ? bestOffer.getBuyUrl() : null;
|
||||
|
||||
return ProductMapper.toSummary(p, price, buyUrl);
|
||||
})
|
||||
.toList();
|
||||
long tMapEnd = System.currentTimeMillis();
|
||||
long took = System.currentTimeMillis() - started;
|
||||
|
||||
System.out.println("getGunbuilderProducts: mapping to DTOs took " +
|
||||
(tMapEnd - tMapStart) + " ms");
|
||||
System.out.println("getGunbuilderProducts: TOTAL " + took + " ms (" +
|
||||
"products=" + (tProductsEnd - tProductsStart) + " ms, " +
|
||||
"offers=" + (tOffersEnd - tOffersStart) + " ms, " +
|
||||
"map=" + (tMapEnd - tMapStart) + " ms)");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/offers")
|
||||
public List<ProductOfferDto> getOffersForProduct(@PathVariable("id") Integer productId) {
|
||||
List<ProductOffer> offers = productOfferRepository.findByProductId(productId);
|
||||
|
||||
return offers.stream()
|
||||
.map(offer -> {
|
||||
ProductOfferDto dto = new ProductOfferDto();
|
||||
dto.setId(offer.getId().toString());
|
||||
dto.setMerchantName(offer.getMerchant().getName());
|
||||
dto.setPrice(offer.getEffectivePrice());
|
||||
dto.setOriginalPrice(offer.getOriginalPrice());
|
||||
dto.setInStock(Boolean.TRUE.equals(offer.getInStock()));
|
||||
dto.setBuyUrl(offer.getBuyUrl());
|
||||
dto.setLastUpdated(offer.getLastSeenAt());
|
||||
return dto;
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
|
||||
private ProductOffer pickBestOffer(List<ProductOffer> offers) {
|
||||
if (offers == null || offers.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Right now: lowest price wins, regardless of stock (we set inStock=true on import anyway)
|
||||
return offers.stream()
|
||||
.filter(o -> o.getEffectivePrice() != null)
|
||||
.min(Comparator.comparing(ProductOffer::getEffectivePrice))
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +1,53 @@
|
||||
package group.goforward.ballistic.controllers;
|
||||
|
||||
import group.goforward.ballistic.model.User;
|
||||
import group.goforward.ballistic.repos.UserRepository;
|
||||
import group.goforward.ballistic.services.UsersService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping()
|
||||
public class UserController {
|
||||
@Autowired
|
||||
private UserRepository repo;
|
||||
@Autowired
|
||||
private UsersService usersService;
|
||||
|
||||
@GetMapping("/api/getAllUsers")
|
||||
public ResponseEntity<List<User>> getAllUsers() {
|
||||
List<User> data = repo.findAll();
|
||||
return ResponseEntity.ok(data);
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/api/getAllUsersById/{id}")
|
||||
public ResponseEntity<User> getAllStatesById(@PathVariable Integer id) {
|
||||
return repo.findById(id)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
@PostMapping("/api/addUser")
|
||||
public ResponseEntity<User> createUser(@RequestBody User item) {
|
||||
User created = usersService.save(item);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(created);
|
||||
}
|
||||
|
||||
@DeleteMapping("/api/deleteUser/{id}")
|
||||
public ResponseEntity<Void> deleteItem(@PathVariable Integer id) {
|
||||
return usersService.findById(id)
|
||||
.map(item -> {
|
||||
usersService.deleteById(id);
|
||||
return ResponseEntity.noContent().<Void>build();
|
||||
})
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
}
|
||||
package group.goforward.ballistic.controllers;
|
||||
|
||||
import group.goforward.ballistic.model.User;
|
||||
import group.goforward.ballistic.repos.UserRepository;
|
||||
import group.goforward.ballistic.services.UsersService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping()
|
||||
public class UserController {
|
||||
private final UserRepository repo;
|
||||
private final UsersService usersService;
|
||||
|
||||
public UserController(UserRepository repo, UsersService usersService) {
|
||||
this.repo = repo;
|
||||
this.usersService = usersService;
|
||||
}
|
||||
|
||||
@GetMapping("/api/getAllUsers")
|
||||
public ResponseEntity<List<User>> getAllUsers() {
|
||||
List<User> data = repo.findAll();
|
||||
return ResponseEntity.ok(data);
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/api/getAllUsersById/{id}")
|
||||
public ResponseEntity<User> getAllStatesById(@PathVariable Integer id) {
|
||||
return repo.findById(id)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
@PostMapping("/api/addUser")
|
||||
public ResponseEntity<User> createUser(@RequestBody User item) {
|
||||
User created = usersService.save(item);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(created);
|
||||
}
|
||||
|
||||
@DeleteMapping("/api/deleteUser/{id}")
|
||||
public ResponseEntity<Void> deleteItem(@PathVariable Integer id) {
|
||||
return usersService.findById(id)
|
||||
.map(item -> {
|
||||
usersService.deleteById(id);
|
||||
return ResponseEntity.noContent().<Void>build();
|
||||
})
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/**
|
||||
* Provides the classes necessary for the Spring Controllers for the ballistic -Builder application.
|
||||
* This package includes Controllers for Spring-Boot application
|
||||
*
|
||||
*
|
||||
* <p>The main entry point for managing the inventory is the
|
||||
* {@link group.goforward.ballistic.BallisticApplication} class.</p>
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Don Strawsburg
|
||||
* @version 1.1
|
||||
*/
|
||||
/**
|
||||
* Provides the classes necessary for the Spring Controllers for the ballistic -Builder application.
|
||||
* This package includes Controllers for Spring-Boot application
|
||||
*
|
||||
*
|
||||
* <p>The main entry point for managing the inventory is the
|
||||
* {@link group.goforward.ballistic.BallisticApplication} class.</p>
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Don Strawsburg
|
||||
* @version 1.1
|
||||
*/
|
||||
package group.goforward.ballistic.controllers;
|
||||
@@ -1,30 +1,30 @@
|
||||
package group.goforward.ballistic.imports;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public record MerchantFeedRow(
|
||||
String sku,
|
||||
String manufacturerId,
|
||||
String brandName,
|
||||
String productName,
|
||||
String longDescription,
|
||||
String shortDescription,
|
||||
String department,
|
||||
String category,
|
||||
String subCategory,
|
||||
String thumbUrl,
|
||||
String imageUrl,
|
||||
String buyLink,
|
||||
String keywords,
|
||||
String reviews,
|
||||
BigDecimal retailPrice,
|
||||
BigDecimal salePrice,
|
||||
String brandPageLink,
|
||||
String brandLogoImage,
|
||||
String productPageViewTracking,
|
||||
String variantsXml,
|
||||
String mediumImageUrl,
|
||||
String productContentWidget,
|
||||
String googleCategorization,
|
||||
String itemBasedCommission
|
||||
package group.goforward.ballistic.imports;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public record MerchantFeedRow(
|
||||
String sku,
|
||||
String manufacturerId,
|
||||
String brandName,
|
||||
String productName,
|
||||
String longDescription,
|
||||
String shortDescription,
|
||||
String department,
|
||||
String category,
|
||||
String subCategory,
|
||||
String thumbUrl,
|
||||
String imageUrl,
|
||||
String buyLink,
|
||||
String keywords,
|
||||
String reviews,
|
||||
BigDecimal retailPrice,
|
||||
BigDecimal salePrice,
|
||||
String brandPageLink,
|
||||
String brandLogoImage,
|
||||
String productPageViewTracking,
|
||||
String variantsXml,
|
||||
String mediumImageUrl,
|
||||
String productContentWidget,
|
||||
String googleCategorization,
|
||||
String itemBasedCommission
|
||||
) {}
|
||||
@@ -1,17 +1,17 @@
|
||||
package group.goforward.ballistic.imports.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public record MerchantFeedRow(
|
||||
String brandName,
|
||||
String productName,
|
||||
String mpn,
|
||||
String upc,
|
||||
String avantlinkProductId,
|
||||
String sku,
|
||||
String categoryPath,
|
||||
String buyUrl,
|
||||
BigDecimal price,
|
||||
BigDecimal originalPrice,
|
||||
boolean inStock
|
||||
package group.goforward.ballistic.imports.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public record MerchantFeedRow(
|
||||
String brandName,
|
||||
String productName,
|
||||
String mpn,
|
||||
String upc,
|
||||
String avantlinkProductId,
|
||||
String sku,
|
||||
String categoryPath,
|
||||
String buyUrl,
|
||||
BigDecimal price,
|
||||
BigDecimal originalPrice,
|
||||
boolean inStock
|
||||
) {}
|
||||
@@ -1,13 +1,13 @@
|
||||
/**
|
||||
* Provides the classes necessary for the Spring Data Transfer Objects for the ballistic -Builder application.
|
||||
* This package includes DTO for Spring-Boot application
|
||||
*
|
||||
*
|
||||
* <p>The main entry point for managing the inventory is the
|
||||
* {@link group.goforward.ballistic.BallisticApplication} class.</p>
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Sean Strawsburg
|
||||
* @version 1.1
|
||||
*/
|
||||
/**
|
||||
* Provides the classes necessary for the Spring Data Transfer Objects for the ballistic -Builder application.
|
||||
* This package includes DTO for Spring-Boot application
|
||||
*
|
||||
*
|
||||
* <p>The main entry point for managing the inventory is the
|
||||
* {@link group.goforward.ballistic.BallisticApplication} class.</p>
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Sean Strawsburg
|
||||
* @version 1.1
|
||||
*/
|
||||
package group.goforward.ballistic.imports.dto;
|
||||
@@ -1,105 +1,105 @@
|
||||
package group.goforward.ballistic.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
import group.goforward.ballistic.model.ProductConfiguration;
|
||||
|
||||
@Entity
|
||||
@Table(
|
||||
name = "merchant_category_mappings",
|
||||
uniqueConstraints = @UniqueConstraint(
|
||||
name = "uq_merchant_category",
|
||||
columnNames = { "merchant_id", "raw_category" }
|
||||
)
|
||||
)
|
||||
public class MerchantCategoryMapping {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY) // SERIAL
|
||||
@Column(name = "id", nullable = false)
|
||||
private Integer id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "merchant_id", nullable = false)
|
||||
private Merchant merchant;
|
||||
|
||||
@Column(name = "raw_category", nullable = false, length = 512)
|
||||
private String rawCategory;
|
||||
|
||||
@Column(name = "mapped_part_role", length = 128)
|
||||
private String mappedPartRole; // e.g. "upper-receiver", "barrel"
|
||||
|
||||
@Column(name = "mapped_configuration")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private ProductConfiguration mappedConfiguration;
|
||||
|
||||
@Column(name = "created_at", nullable = false)
|
||||
private OffsetDateTime createdAt = OffsetDateTime.now();
|
||||
|
||||
@Column(name = "updated_at", nullable = false)
|
||||
private OffsetDateTime updatedAt = OffsetDateTime.now();
|
||||
|
||||
@PreUpdate
|
||||
public void onUpdate() {
|
||||
this.updatedAt = OffsetDateTime.now();
|
||||
}
|
||||
|
||||
// getters & setters
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Merchant getMerchant() {
|
||||
return merchant;
|
||||
}
|
||||
|
||||
public void setMerchant(Merchant merchant) {
|
||||
this.merchant = merchant;
|
||||
}
|
||||
|
||||
public String getRawCategory() {
|
||||
return rawCategory;
|
||||
}
|
||||
|
||||
public void setRawCategory(String rawCategory) {
|
||||
this.rawCategory = rawCategory;
|
||||
}
|
||||
|
||||
public String getMappedPartRole() {
|
||||
return mappedPartRole;
|
||||
}
|
||||
|
||||
public void setMappedPartRole(String mappedPartRole) {
|
||||
this.mappedPartRole = mappedPartRole;
|
||||
}
|
||||
|
||||
public ProductConfiguration getMappedConfiguration() {
|
||||
return mappedConfiguration;
|
||||
}
|
||||
|
||||
public void setMappedConfiguration(ProductConfiguration mappedConfiguration) {
|
||||
this.mappedConfiguration = mappedConfiguration;
|
||||
}
|
||||
|
||||
public OffsetDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(OffsetDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public OffsetDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(OffsetDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
package group.goforward.ballistic.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
import group.goforward.ballistic.model.ProductConfiguration;
|
||||
|
||||
@Entity
|
||||
@Table(
|
||||
name = "merchant_category_mappings",
|
||||
uniqueConstraints = @UniqueConstraint(
|
||||
name = "uq_merchant_category",
|
||||
columnNames = { "merchant_id", "raw_category" }
|
||||
)
|
||||
)
|
||||
public class MerchantCategoryMapping {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY) // SERIAL
|
||||
@Column(name = "id", nullable = false)
|
||||
private Integer id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "merchant_id", nullable = false)
|
||||
private Merchant merchant;
|
||||
|
||||
@Column(name = "raw_category", nullable = false, length = 512)
|
||||
private String rawCategory;
|
||||
|
||||
@Column(name = "mapped_part_role", length = 128)
|
||||
private String mappedPartRole; // e.g. "upper-receiver", "barrel"
|
||||
|
||||
@Column(name = "mapped_configuration")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private ProductConfiguration mappedConfiguration;
|
||||
|
||||
@Column(name = "created_at", nullable = false)
|
||||
private OffsetDateTime createdAt = OffsetDateTime.now();
|
||||
|
||||
@Column(name = "updated_at", nullable = false)
|
||||
private OffsetDateTime updatedAt = OffsetDateTime.now();
|
||||
|
||||
@PreUpdate
|
||||
public void onUpdate() {
|
||||
this.updatedAt = OffsetDateTime.now();
|
||||
}
|
||||
|
||||
// getters & setters
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Merchant getMerchant() {
|
||||
return merchant;
|
||||
}
|
||||
|
||||
public void setMerchant(Merchant merchant) {
|
||||
this.merchant = merchant;
|
||||
}
|
||||
|
||||
public String getRawCategory() {
|
||||
return rawCategory;
|
||||
}
|
||||
|
||||
public void setRawCategory(String rawCategory) {
|
||||
this.rawCategory = rawCategory;
|
||||
}
|
||||
|
||||
public String getMappedPartRole() {
|
||||
return mappedPartRole;
|
||||
}
|
||||
|
||||
public void setMappedPartRole(String mappedPartRole) {
|
||||
this.mappedPartRole = mappedPartRole;
|
||||
}
|
||||
|
||||
public ProductConfiguration getMappedConfiguration() {
|
||||
return mappedConfiguration;
|
||||
}
|
||||
|
||||
public void setMappedConfiguration(ProductConfiguration mappedConfiguration) {
|
||||
this.mappedConfiguration = mappedConfiguration;
|
||||
}
|
||||
|
||||
public OffsetDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(OffsetDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public OffsetDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(OffsetDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package group.goforward.ballistic.model;
|
||||
|
||||
public enum ProductConfiguration {
|
||||
STRIPPED, // bare receiver / component
|
||||
ASSEMBLED, // built up but not fully complete
|
||||
BARRELED, // upper + barrel + gas system, no BCG/CH
|
||||
COMPLETE, // full assembly ready to run
|
||||
KIT, // collection of parts (LPK, trigger kits, etc.)
|
||||
OTHER // fallback / unknown
|
||||
package group.goforward.ballistic.model;
|
||||
|
||||
public enum ProductConfiguration {
|
||||
STRIPPED, // bare receiver / component
|
||||
ASSEMBLED, // built up but not fully complete
|
||||
BARRELED, // upper + barrel + gas system, no BCG/CH
|
||||
COMPLETE, // full assembly ready to run
|
||||
KIT, // collection of parts (LPK, trigger kits, etc.)
|
||||
OTHER // fallback / unknown
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.Account;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface AccountRepository extends JpaRepository<Account, UUID> {
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.Account;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface AccountRepository extends JpaRepository<Account, UUID> {
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package group.goforward.ballistic.repos;
|
||||
import group.goforward.ballistic.model.Brand;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface BrandRepository extends JpaRepository<Brand, Integer> {
|
||||
Optional<Brand> findByNameIgnoreCase(String name);
|
||||
package group.goforward.ballistic.repos;
|
||||
import group.goforward.ballistic.model.Brand;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface BrandRepository extends JpaRepository<Brand, Integer> {
|
||||
Optional<Brand> findByNameIgnoreCase(String name);
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.BuildsComponent;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface BuildItemRepository extends JpaRepository<BuildsComponent, Integer> {
|
||||
List<BuildsComponent> findByBuildId(Integer buildId);
|
||||
Optional<BuildsComponent> findByUuid(UUID uuid);
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.BuildsComponent;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface BuildItemRepository extends JpaRepository<BuildsComponent, Integer> {
|
||||
List<BuildsComponent> findByBuildId(Integer buildId);
|
||||
Optional<BuildsComponent> findByUuid(UUID uuid);
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.Build;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface BuildRepository extends JpaRepository<Build, Integer> {
|
||||
Optional<Build> findByUuid(UUID uuid);
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.Build;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface BuildRepository extends JpaRepository<Build, Integer> {
|
||||
Optional<Build> findByUuid(UUID uuid);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.AffiliateCategoryMap;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface CategoryMappingRepository extends JpaRepository<AffiliateCategoryMap, Integer> {
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.AffiliateCategoryMap;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface CategoryMappingRepository extends JpaRepository<AffiliateCategoryMap, Integer> {
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.FeedImport;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface FeedImportRepository extends JpaRepository<FeedImport, Long> {
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.FeedImport;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface FeedImportRepository extends JpaRepository<FeedImport, Long> {
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.MerchantCategoryMapping;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface MerchantCategoryMappingRepository
|
||||
extends JpaRepository<MerchantCategoryMapping, Integer> {
|
||||
|
||||
Optional<MerchantCategoryMapping> findByMerchantIdAndRawCategoryIgnoreCase(
|
||||
Integer merchantId,
|
||||
String rawCategory
|
||||
);
|
||||
|
||||
List<MerchantCategoryMapping> findByMerchantIdOrderByRawCategoryAsc(Integer merchantId);
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.MerchantCategoryMapping;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface MerchantCategoryMappingRepository
|
||||
extends JpaRepository<MerchantCategoryMapping, Integer> {
|
||||
|
||||
Optional<MerchantCategoryMapping> findByMerchantIdAndRawCategoryIgnoreCase(
|
||||
Integer merchantId,
|
||||
String rawCategory
|
||||
);
|
||||
|
||||
List<MerchantCategoryMapping> findByMerchantIdOrderByRawCategoryAsc(Integer merchantId);
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.Merchant;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface MerchantRepository extends JpaRepository<Merchant, Integer> {
|
||||
|
||||
Optional<Merchant> findByNameIgnoreCase(String name);
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.Merchant;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface MerchantRepository extends JpaRepository<Merchant, Integer> {
|
||||
|
||||
Optional<Merchant> findByNameIgnoreCase(String name);
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.PartCategory;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface PartCategoryRepository extends JpaRepository<PartCategory, Integer> {
|
||||
Optional<PartCategory> findBySlug(String slug);
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.PartCategory;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface PartCategoryRepository extends JpaRepository<PartCategory, Integer> {
|
||||
Optional<PartCategory> findBySlug(String slug);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.PriceHistory;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface PriceHistoryRepository extends JpaRepository<PriceHistory, Long> {
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.PriceHistory;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface PriceHistoryRepository extends JpaRepository<PriceHistory, Long> {
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.ProductOffer;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface ProductOfferRepository extends JpaRepository<ProductOffer, Integer> {
|
||||
|
||||
List<ProductOffer> findByProductId(Integer productId);
|
||||
|
||||
// Used by the /api/products/gunbuilder endpoint
|
||||
List<ProductOffer> findByProductIdIn(Collection<Integer> productIds);
|
||||
|
||||
// Unique offer lookup for importer upsert
|
||||
Optional<ProductOffer> findByMerchantIdAndAvantlinkProductId(
|
||||
Integer merchantId,
|
||||
String avantlinkProductId
|
||||
);
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.ProductOffer;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface ProductOfferRepository extends JpaRepository<ProductOffer, Integer> {
|
||||
|
||||
List<ProductOffer> findByProductId(Integer productId);
|
||||
|
||||
// Used by the /api/products/gunbuilder endpoint
|
||||
List<ProductOffer> findByProductIdIn(Collection<Integer> productIds);
|
||||
|
||||
// Unique offer lookup for importer upsert
|
||||
Optional<ProductOffer> findByMerchantIdAndAvantlinkProductId(
|
||||
Integer merchantId,
|
||||
String avantlinkProductId
|
||||
);
|
||||
}
|
||||
@@ -1,53 +1,53 @@
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.Product;
|
||||
import group.goforward.ballistic.model.Brand;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
|
||||
public interface ProductRepository extends JpaRepository<Product, Integer> {
|
||||
|
||||
Optional<Product> findByUuid(UUID uuid);
|
||||
|
||||
boolean existsBySlug(String slug);
|
||||
|
||||
List<Product> findAllByBrandAndMpn(Brand brand, String mpn);
|
||||
|
||||
List<Product> findAllByBrandAndUpc(Brand brand, String upc);
|
||||
|
||||
// All products for a given platform (e.g. "AR-15")
|
||||
List<Product> findByPlatform(String platform);
|
||||
|
||||
// Products filtered by platform + part roles (e.g. upper-receiver, barrel, etc.)
|
||||
List<Product> findByPlatformAndPartRoleIn(String platform, Collection<String> partRoles);
|
||||
|
||||
// ---------- Optimized variants for Gunbuilder (fetch brand to avoid N+1) ----------
|
||||
|
||||
@Query("""
|
||||
SELECT p
|
||||
FROM Product p
|
||||
JOIN FETCH p.brand b
|
||||
WHERE p.platform = :platform
|
||||
AND p.deletedAt IS NULL
|
||||
""")
|
||||
List<Product> findByPlatformWithBrand(@Param("platform") String platform);
|
||||
|
||||
@Query("""
|
||||
SELECT p
|
||||
FROM Product p
|
||||
JOIN FETCH p.brand b
|
||||
WHERE p.platform = :platform
|
||||
AND p.partRole IN :partRoles
|
||||
AND p.deletedAt IS NULL
|
||||
""")
|
||||
List<Product> findByPlatformAndPartRoleInWithBrand(
|
||||
@Param("platform") String platform,
|
||||
@Param("partRoles") Collection<String> partRoles
|
||||
);
|
||||
package group.goforward.ballistic.repos;
|
||||
|
||||
import group.goforward.ballistic.model.Product;
|
||||
import group.goforward.ballistic.model.Brand;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
|
||||
public interface ProductRepository extends JpaRepository<Product, Integer> {
|
||||
|
||||
Optional<Product> findByUuid(UUID uuid);
|
||||
|
||||
boolean existsBySlug(String slug);
|
||||
|
||||
List<Product> findAllByBrandAndMpn(Brand brand, String mpn);
|
||||
|
||||
List<Product> findAllByBrandAndUpc(Brand brand, String upc);
|
||||
|
||||
// All products for a given platform (e.g. "AR-15")
|
||||
List<Product> findByPlatform(String platform);
|
||||
|
||||
// Products filtered by platform + part roles (e.g. upper-receiver, barrel, etc.)
|
||||
List<Product> findByPlatformAndPartRoleIn(String platform, Collection<String> partRoles);
|
||||
|
||||
// ---------- Optimized variants for Gunbuilder (fetch brand to avoid N+1) ----------
|
||||
|
||||
@Query("""
|
||||
SELECT p
|
||||
FROM Product p
|
||||
JOIN FETCH p.brand b
|
||||
WHERE p.platform = :platform
|
||||
AND p.deletedAt IS NULL
|
||||
""")
|
||||
List<Product> findByPlatformWithBrand(@Param("platform") String platform);
|
||||
|
||||
@Query("""
|
||||
SELECT p
|
||||
FROM Product p
|
||||
JOIN FETCH p.brand b
|
||||
WHERE p.platform = :platform
|
||||
AND p.partRole IN :partRoles
|
||||
AND p.deletedAt IS NULL
|
||||
""")
|
||||
List<Product> findByPlatformAndPartRoleInWithBrand(
|
||||
@Param("platform") String platform,
|
||||
@Param("partRoles") Collection<String> partRoles
|
||||
);
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
/**
|
||||
* Provides the classes necessary for the Spring Repository for the ballistic -Builder application.
|
||||
* This package includes Repository for Spring-Boot application
|
||||
*
|
||||
*
|
||||
* <p>The main entry point for managing the inventory is the
|
||||
* {@link group.goforward.ballistic.BallisticApplication} class.</p>
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Sean Strawsburg
|
||||
* @version 1.1
|
||||
*/
|
||||
/**
|
||||
* Provides the classes necessary for the Spring Repository for the ballistic -Builder application.
|
||||
* This package includes Repository for Spring-Boot application
|
||||
*
|
||||
*
|
||||
* <p>The main entry point for managing the inventory is the
|
||||
* {@link group.goforward.ballistic.BallisticApplication} class.</p>
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Sean Strawsburg
|
||||
* @version 1.1
|
||||
*/
|
||||
package group.goforward.ballistic.repos;
|
||||
@@ -1,96 +1,96 @@
|
||||
package group.goforward.ballistic.services;
|
||||
|
||||
import group.goforward.ballistic.model.Merchant;
|
||||
import group.goforward.ballistic.model.MerchantCategoryMapping;
|
||||
import group.goforward.ballistic.model.ProductConfiguration;
|
||||
import group.goforward.ballistic.repos.MerchantCategoryMappingRepository;
|
||||
import jakarta.transaction.Transactional;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class MerchantCategoryMappingService {
|
||||
|
||||
private final MerchantCategoryMappingRepository mappingRepository;
|
||||
|
||||
public MerchantCategoryMappingService(MerchantCategoryMappingRepository mappingRepository) {
|
||||
this.mappingRepository = mappingRepository;
|
||||
}
|
||||
|
||||
public List<MerchantCategoryMapping> findByMerchant(Integer merchantId) {
|
||||
return mappingRepository.findByMerchantIdOrderByRawCategoryAsc(merchantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve (or create) a mapping row for this merchant + raw category.
|
||||
* - If it exists, returns it (with whatever mappedPartRole / mappedConfiguration are set).
|
||||
* - If it doesn't exist, creates a placeholder row with null mappings and returns it.
|
||||
*
|
||||
* The importer can then:
|
||||
* - skip rows where mappedPartRole is still null
|
||||
* - use mappedConfiguration if present
|
||||
*/
|
||||
@Transactional
|
||||
public MerchantCategoryMapping resolveMapping(Merchant merchant, String rawCategory) {
|
||||
if (rawCategory == null || rawCategory.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String trimmed = rawCategory.trim();
|
||||
|
||||
return mappingRepository
|
||||
.findByMerchantIdAndRawCategoryIgnoreCase(merchant.getId(), trimmed)
|
||||
.orElseGet(() -> {
|
||||
MerchantCategoryMapping mapping = new MerchantCategoryMapping();
|
||||
mapping.setMerchant(merchant);
|
||||
mapping.setRawCategory(trimmed);
|
||||
mapping.setMappedPartRole(null);
|
||||
mapping.setMappedConfiguration(null);
|
||||
return mappingRepository.save(mapping);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Upsert mapping (admin UI).
|
||||
*/
|
||||
@Transactional
|
||||
public MerchantCategoryMapping upsertMapping(
|
||||
Merchant merchant,
|
||||
String rawCategory,
|
||||
String mappedPartRole,
|
||||
ProductConfiguration mappedConfiguration
|
||||
) {
|
||||
String trimmed = rawCategory.trim();
|
||||
|
||||
MerchantCategoryMapping mapping = mappingRepository
|
||||
.findByMerchantIdAndRawCategoryIgnoreCase(merchant.getId(), trimmed)
|
||||
.orElseGet(() -> {
|
||||
MerchantCategoryMapping m = new MerchantCategoryMapping();
|
||||
m.setMerchant(merchant);
|
||||
m.setRawCategory(trimmed);
|
||||
return m;
|
||||
});
|
||||
|
||||
mapping.setMappedPartRole(
|
||||
(mappedPartRole == null || mappedPartRole.isBlank()) ? null : mappedPartRole.trim()
|
||||
);
|
||||
|
||||
mapping.setMappedConfiguration(mappedConfiguration);
|
||||
|
||||
return mappingRepository.save(mapping);
|
||||
}
|
||||
/**
|
||||
* Backwards-compatible overload for existing callers (e.g. controller)
|
||||
* that don’t care about productConfiguration yet.
|
||||
*/
|
||||
@Transactional
|
||||
public MerchantCategoryMapping upsertMapping(
|
||||
Merchant merchant,
|
||||
String rawCategory,
|
||||
String mappedPartRole
|
||||
) {
|
||||
// Delegate to the new method with `null` configuration
|
||||
return upsertMapping(merchant, rawCategory, mappedPartRole, null);
|
||||
}
|
||||
package group.goforward.ballistic.services;
|
||||
|
||||
import group.goforward.ballistic.model.Merchant;
|
||||
import group.goforward.ballistic.model.MerchantCategoryMapping;
|
||||
import group.goforward.ballistic.model.ProductConfiguration;
|
||||
import group.goforward.ballistic.repos.MerchantCategoryMappingRepository;
|
||||
import jakarta.transaction.Transactional;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class MerchantCategoryMappingService {
|
||||
|
||||
private final MerchantCategoryMappingRepository mappingRepository;
|
||||
|
||||
public MerchantCategoryMappingService(MerchantCategoryMappingRepository mappingRepository) {
|
||||
this.mappingRepository = mappingRepository;
|
||||
}
|
||||
|
||||
public List<MerchantCategoryMapping> findByMerchant(Integer merchantId) {
|
||||
return mappingRepository.findByMerchantIdOrderByRawCategoryAsc(merchantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve (or create) a mapping row for this merchant + raw category.
|
||||
* - If it exists, returns it (with whatever mappedPartRole / mappedConfiguration are set).
|
||||
* - If it doesn't exist, creates a placeholder row with null mappings and returns it.
|
||||
*
|
||||
* The importer can then:
|
||||
* - skip rows where mappedPartRole is still null
|
||||
* - use mappedConfiguration if present
|
||||
*/
|
||||
@Transactional
|
||||
public MerchantCategoryMapping resolveMapping(Merchant merchant, String rawCategory) {
|
||||
if (rawCategory == null || rawCategory.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String trimmed = rawCategory.trim();
|
||||
|
||||
return mappingRepository
|
||||
.findByMerchantIdAndRawCategoryIgnoreCase(merchant.getId(), trimmed)
|
||||
.orElseGet(() -> {
|
||||
MerchantCategoryMapping mapping = new MerchantCategoryMapping();
|
||||
mapping.setMerchant(merchant);
|
||||
mapping.setRawCategory(trimmed);
|
||||
mapping.setMappedPartRole(null);
|
||||
mapping.setMappedConfiguration(null);
|
||||
return mappingRepository.save(mapping);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Upsert mapping (admin UI).
|
||||
*/
|
||||
@Transactional
|
||||
public MerchantCategoryMapping upsertMapping(
|
||||
Merchant merchant,
|
||||
String rawCategory,
|
||||
String mappedPartRole,
|
||||
ProductConfiguration mappedConfiguration
|
||||
) {
|
||||
String trimmed = rawCategory.trim();
|
||||
|
||||
MerchantCategoryMapping mapping = mappingRepository
|
||||
.findByMerchantIdAndRawCategoryIgnoreCase(merchant.getId(), trimmed)
|
||||
.orElseGet(() -> {
|
||||
MerchantCategoryMapping m = new MerchantCategoryMapping();
|
||||
m.setMerchant(merchant);
|
||||
m.setRawCategory(trimmed);
|
||||
return m;
|
||||
});
|
||||
|
||||
mapping.setMappedPartRole(
|
||||
(mappedPartRole == null || mappedPartRole.isBlank()) ? null : mappedPartRole.trim()
|
||||
);
|
||||
|
||||
mapping.setMappedConfiguration(mappedConfiguration);
|
||||
|
||||
return mappingRepository.save(mapping);
|
||||
}
|
||||
/**
|
||||
* Backwards-compatible overload for existing callers (e.g. controller)
|
||||
* that don’t care about productConfiguration yet.
|
||||
*/
|
||||
@Transactional
|
||||
public MerchantCategoryMapping upsertMapping(
|
||||
Merchant merchant,
|
||||
String rawCategory,
|
||||
String mappedPartRole
|
||||
) {
|
||||
// Delegate to the new method with `null` configuration
|
||||
return upsertMapping(merchant, rawCategory, mappedPartRole, null);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
package group.goforward.ballistic.services;
|
||||
|
||||
public interface MerchantFeedImportService {
|
||||
|
||||
/**
|
||||
* Full product + offer import for a given merchant.
|
||||
*/
|
||||
void importMerchantFeed(Integer merchantId);
|
||||
|
||||
/**
|
||||
* Offers-only sync (price / stock) for a given merchant.
|
||||
*/
|
||||
void syncOffersOnly(Integer merchantId);
|
||||
package group.goforward.ballistic.services;
|
||||
|
||||
public interface MerchantFeedImportService {
|
||||
|
||||
/**
|
||||
* Full product + offer import for a given merchant.
|
||||
*/
|
||||
void importMerchantFeed(Integer merchantId);
|
||||
|
||||
/**
|
||||
* Offers-only sync (price / stock) for a given merchant.
|
||||
*/
|
||||
void syncOffersOnly(Integer merchantId);
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
package group.goforward.ballistic.services;
|
||||
|
||||
import group.goforward.ballistic.model.Psa;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface PsaService {
|
||||
List<Psa> findAll();
|
||||
|
||||
Optional<Psa> findById(UUID id);
|
||||
|
||||
Psa save(Psa psa);
|
||||
|
||||
void deleteById(UUID id);
|
||||
}
|
||||
package group.goforward.ballistic.services;
|
||||
|
||||
import group.goforward.ballistic.model.Psa;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface PsaService {
|
||||
List<Psa> findAll();
|
||||
|
||||
Optional<Psa> findById(UUID id);
|
||||
|
||||
Psa save(Psa psa);
|
||||
|
||||
void deleteById(UUID id);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package group.goforward.ballistic.services;
|
||||
|
||||
import group.goforward.ballistic.model.State;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface StatesService {
|
||||
|
||||
List<State> findAll();
|
||||
|
||||
Optional<State> findById(Integer id);
|
||||
|
||||
State save(State item);
|
||||
void deleteById(Integer id);
|
||||
}
|
||||
package group.goforward.ballistic.services;
|
||||
|
||||
import group.goforward.ballistic.model.State;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface StatesService {
|
||||
|
||||
List<State> findAll();
|
||||
|
||||
Optional<State> findById(Integer id);
|
||||
|
||||
State save(State item);
|
||||
void deleteById(Integer id);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package group.goforward.ballistic.services;
|
||||
|
||||
import group.goforward.ballistic.model.User;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface UsersService {
|
||||
|
||||
List<User> findAll();
|
||||
|
||||
Optional<User> findById(Integer id);
|
||||
|
||||
User save(User item);
|
||||
void deleteById(Integer id);
|
||||
}
|
||||
package group.goforward.ballistic.services;
|
||||
|
||||
import group.goforward.ballistic.model.User;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface UsersService {
|
||||
|
||||
List<User> findAll();
|
||||
|
||||
Optional<User> findById(Integer id);
|
||||
|
||||
User save(User item);
|
||||
void deleteById(Integer id);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,41 +1,41 @@
|
||||
package group.goforward.ballistic.services.impl;
|
||||
import group.goforward.ballistic.model.Psa;
|
||||
import group.goforward.ballistic.repos.PsaRepository;
|
||||
import group.goforward.ballistic.services.PsaService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class PsaServiceImpl implements PsaService {
|
||||
|
||||
private final PsaRepository psaRepository;
|
||||
|
||||
@Autowired
|
||||
public PsaServiceImpl(PsaRepository psaRepository) {
|
||||
this.psaRepository = psaRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Psa> findAll() {
|
||||
return psaRepository.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Psa> findById(UUID id) {
|
||||
return psaRepository.findById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Psa save(Psa psa) {
|
||||
return psaRepository.save(psa);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(UUID id) {
|
||||
psaRepository.deleteById(id);
|
||||
}
|
||||
}
|
||||
package group.goforward.ballistic.services.impl;
|
||||
import group.goforward.ballistic.model.Psa;
|
||||
import group.goforward.ballistic.repos.PsaRepository;
|
||||
import group.goforward.ballistic.services.PsaService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class PsaServiceImpl implements PsaService {
|
||||
|
||||
private final PsaRepository psaRepository;
|
||||
|
||||
@Autowired
|
||||
public PsaServiceImpl(PsaRepository psaRepository) {
|
||||
this.psaRepository = psaRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Psa> findAll() {
|
||||
return psaRepository.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Psa> findById(UUID id) {
|
||||
return psaRepository.findById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Psa save(Psa psa) {
|
||||
return psaRepository.save(psa);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(UUID id) {
|
||||
psaRepository.deleteById(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
package group.goforward.ballistic.services.impl;
|
||||
|
||||
|
||||
import group.goforward.ballistic.model.State;
|
||||
import group.goforward.ballistic.repos.StateRepository;
|
||||
import group.goforward.ballistic.services.StatesService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class StatesServiceImpl implements StatesService {
|
||||
|
||||
@Autowired
|
||||
private StateRepository repo;
|
||||
|
||||
@Override
|
||||
public List<State> findAll() {
|
||||
return repo.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<State> findById(Integer id) {
|
||||
return repo.findById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public State save(State item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(Integer id) {
|
||||
deleteById(id);
|
||||
}
|
||||
}
|
||||
package group.goforward.ballistic.services.impl;
|
||||
|
||||
|
||||
import group.goforward.ballistic.model.State;
|
||||
import group.goforward.ballistic.repos.StateRepository;
|
||||
import group.goforward.ballistic.services.StatesService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class StatesServiceImpl implements StatesService {
|
||||
|
||||
@Autowired
|
||||
private StateRepository repo;
|
||||
|
||||
@Override
|
||||
public List<State> findAll() {
|
||||
return repo.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<State> findById(Integer id) {
|
||||
return repo.findById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public State save(State item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(Integer id) {
|
||||
deleteById(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
package group.goforward.ballistic.services.impl;
|
||||
|
||||
import group.goforward.ballistic.model.User;
|
||||
import group.goforward.ballistic.repos.UserRepository;
|
||||
import group.goforward.ballistic.services.UsersService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class UsersServiceImpl implements UsersService {
|
||||
|
||||
@Autowired
|
||||
private UserRepository repo;
|
||||
|
||||
@Override
|
||||
public List<User> findAll() {
|
||||
return repo.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<User> findById(Integer id) {
|
||||
return repo.findById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User save(User item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(Integer id) {
|
||||
deleteById(id);
|
||||
}
|
||||
}
|
||||
package group.goforward.ballistic.services.impl;
|
||||
|
||||
import group.goforward.ballistic.model.User;
|
||||
import group.goforward.ballistic.repos.UserRepository;
|
||||
import group.goforward.ballistic.services.UsersService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class UsersServiceImpl implements UsersService {
|
||||
|
||||
@Autowired
|
||||
private UserRepository repo;
|
||||
|
||||
@Override
|
||||
public List<User> findAll() {
|
||||
return repo.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<User> findById(Integer id) {
|
||||
return repo.findById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User save(User item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(Integer id) {
|
||||
deleteById(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/**
|
||||
* Provides the classes necessary for the Spring Services implementations for the ballistic -Builder application.
|
||||
* This package includes Services implementations for Spring-Boot application
|
||||
*
|
||||
*
|
||||
* <p>The main entry point for managing the inventory is the
|
||||
* {@link group.goforward.ballistic.BallisticApplication} class.</p>
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Don Strawsburg
|
||||
* @version 1.1
|
||||
*/
|
||||
/**
|
||||
* Provides the classes necessary for the Spring Services implementations for the ballistic -Builder application.
|
||||
* This package includes Services implementations for Spring-Boot application
|
||||
*
|
||||
*
|
||||
* <p>The main entry point for managing the inventory is the
|
||||
* {@link group.goforward.ballistic.BallisticApplication} class.</p>
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Don Strawsburg
|
||||
* @version 1.1
|
||||
*/
|
||||
package group.goforward.ballistic.services.impl;
|
||||
@@ -1,70 +1,70 @@
|
||||
// MerchantAdminDto.java
|
||||
package group.goforward.ballistic.web.dto;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
public class MerchantAdminDto {
|
||||
private Integer id;
|
||||
private String name;
|
||||
private String feedUrl;
|
||||
private String offerFeedUrl;
|
||||
private Boolean isActive;
|
||||
private OffsetDateTime lastFullImportAt;
|
||||
private OffsetDateTime lastOfferSyncAt;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getFeedUrl() {
|
||||
return feedUrl;
|
||||
}
|
||||
|
||||
public void setFeedUrl(String feedUrl) {
|
||||
this.feedUrl = feedUrl;
|
||||
}
|
||||
|
||||
public String getOfferFeedUrl() {
|
||||
return offerFeedUrl;
|
||||
}
|
||||
|
||||
public void setOfferFeedUrl(String offerFeedUrl) {
|
||||
this.offerFeedUrl = offerFeedUrl;
|
||||
}
|
||||
|
||||
public Boolean getIsActive() {
|
||||
return isActive;
|
||||
}
|
||||
|
||||
public void setIsActive(Boolean isActive) {
|
||||
this.isActive = isActive;
|
||||
}
|
||||
|
||||
public OffsetDateTime getLastFullImportAt() {
|
||||
return lastFullImportAt;
|
||||
}
|
||||
|
||||
public void setLastFullImportAt(OffsetDateTime lastFullImportAt) {
|
||||
this.lastFullImportAt = lastFullImportAt;
|
||||
}
|
||||
|
||||
public OffsetDateTime getLastOfferSyncAt() {
|
||||
return lastOfferSyncAt;
|
||||
}
|
||||
|
||||
public void setLastOfferSyncAt(OffsetDateTime lastOfferSyncAt) {
|
||||
this.lastOfferSyncAt = lastOfferSyncAt;
|
||||
}
|
||||
// MerchantAdminDto.java
|
||||
package group.goforward.ballistic.web.dto;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
public class MerchantAdminDto {
|
||||
private Integer id;
|
||||
private String name;
|
||||
private String feedUrl;
|
||||
private String offerFeedUrl;
|
||||
private Boolean isActive;
|
||||
private OffsetDateTime lastFullImportAt;
|
||||
private OffsetDateTime lastOfferSyncAt;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getFeedUrl() {
|
||||
return feedUrl;
|
||||
}
|
||||
|
||||
public void setFeedUrl(String feedUrl) {
|
||||
this.feedUrl = feedUrl;
|
||||
}
|
||||
|
||||
public String getOfferFeedUrl() {
|
||||
return offerFeedUrl;
|
||||
}
|
||||
|
||||
public void setOfferFeedUrl(String offerFeedUrl) {
|
||||
this.offerFeedUrl = offerFeedUrl;
|
||||
}
|
||||
|
||||
public Boolean getIsActive() {
|
||||
return isActive;
|
||||
}
|
||||
|
||||
public void setIsActive(Boolean isActive) {
|
||||
this.isActive = isActive;
|
||||
}
|
||||
|
||||
public OffsetDateTime getLastFullImportAt() {
|
||||
return lastFullImportAt;
|
||||
}
|
||||
|
||||
public void setLastFullImportAt(OffsetDateTime lastFullImportAt) {
|
||||
this.lastFullImportAt = lastFullImportAt;
|
||||
}
|
||||
|
||||
public OffsetDateTime getLastOfferSyncAt() {
|
||||
return lastOfferSyncAt;
|
||||
}
|
||||
|
||||
public void setLastOfferSyncAt(OffsetDateTime lastOfferSyncAt) {
|
||||
this.lastOfferSyncAt = lastOfferSyncAt;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +1,50 @@
|
||||
package group.goforward.ballistic.web.dto;
|
||||
|
||||
public class MerchantCategoryMappingDto {
|
||||
|
||||
private Integer id;
|
||||
private Integer merchantId;
|
||||
private String merchantName;
|
||||
private String rawCategory;
|
||||
private String mappedPartRole;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Integer getMerchantId() {
|
||||
return merchantId;
|
||||
}
|
||||
|
||||
public void setMerchantId(Integer merchantId) {
|
||||
this.merchantId = merchantId;
|
||||
}
|
||||
|
||||
public String getMerchantName() {
|
||||
return merchantName;
|
||||
}
|
||||
|
||||
public void setMerchantName(String merchantName) {
|
||||
this.merchantName = merchantName;
|
||||
}
|
||||
|
||||
public String getRawCategory() {
|
||||
return rawCategory;
|
||||
}
|
||||
|
||||
public void setRawCategory(String rawCategory) {
|
||||
this.rawCategory = rawCategory;
|
||||
}
|
||||
|
||||
public String getMappedPartRole() {
|
||||
return mappedPartRole;
|
||||
}
|
||||
|
||||
public void setMappedPartRole(String mappedPartRole) {
|
||||
this.mappedPartRole = mappedPartRole;
|
||||
}
|
||||
package group.goforward.ballistic.web.dto;
|
||||
|
||||
public class MerchantCategoryMappingDto {
|
||||
|
||||
private Integer id;
|
||||
private Integer merchantId;
|
||||
private String merchantName;
|
||||
private String rawCategory;
|
||||
private String mappedPartRole;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Integer getMerchantId() {
|
||||
return merchantId;
|
||||
}
|
||||
|
||||
public void setMerchantId(Integer merchantId) {
|
||||
this.merchantId = merchantId;
|
||||
}
|
||||
|
||||
public String getMerchantName() {
|
||||
return merchantName;
|
||||
}
|
||||
|
||||
public void setMerchantName(String merchantName) {
|
||||
this.merchantName = merchantName;
|
||||
}
|
||||
|
||||
public String getRawCategory() {
|
||||
return rawCategory;
|
||||
}
|
||||
|
||||
public void setRawCategory(String rawCategory) {
|
||||
this.rawCategory = rawCategory;
|
||||
}
|
||||
|
||||
public String getMappedPartRole() {
|
||||
return mappedPartRole;
|
||||
}
|
||||
|
||||
public void setMappedPartRole(String mappedPartRole) {
|
||||
this.mappedPartRole = mappedPartRole;
|
||||
}
|
||||
}
|
||||
@@ -1,70 +1,70 @@
|
||||
package group.goforward.ballistic.web.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
public class ProductOfferDto {
|
||||
private String id;
|
||||
private String merchantName;
|
||||
private BigDecimal price;
|
||||
private BigDecimal originalPrice;
|
||||
private boolean inStock;
|
||||
private String buyUrl;
|
||||
private OffsetDateTime lastUpdated;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getMerchantName() {
|
||||
return merchantName;
|
||||
}
|
||||
|
||||
public void setMerchantName(String merchantName) {
|
||||
this.merchantName = merchantName;
|
||||
}
|
||||
|
||||
public BigDecimal getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public void setPrice(BigDecimal price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public BigDecimal getOriginalPrice() {
|
||||
return originalPrice;
|
||||
}
|
||||
|
||||
public void setOriginalPrice(BigDecimal originalPrice) {
|
||||
this.originalPrice = originalPrice;
|
||||
}
|
||||
|
||||
public boolean isInStock() {
|
||||
return inStock;
|
||||
}
|
||||
|
||||
public void setInStock(boolean inStock) {
|
||||
this.inStock = inStock;
|
||||
}
|
||||
|
||||
public String getBuyUrl() {
|
||||
return buyUrl;
|
||||
}
|
||||
|
||||
public void setBuyUrl(String buyUrl) {
|
||||
this.buyUrl = buyUrl;
|
||||
}
|
||||
|
||||
public OffsetDateTime getLastUpdated() {
|
||||
return lastUpdated;
|
||||
}
|
||||
|
||||
public void setLastUpdated(OffsetDateTime lastUpdated) {
|
||||
this.lastUpdated = lastUpdated;
|
||||
}
|
||||
package group.goforward.ballistic.web.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
public class ProductOfferDto {
|
||||
private String id;
|
||||
private String merchantName;
|
||||
private BigDecimal price;
|
||||
private BigDecimal originalPrice;
|
||||
private boolean inStock;
|
||||
private String buyUrl;
|
||||
private OffsetDateTime lastUpdated;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getMerchantName() {
|
||||
return merchantName;
|
||||
}
|
||||
|
||||
public void setMerchantName(String merchantName) {
|
||||
this.merchantName = merchantName;
|
||||
}
|
||||
|
||||
public BigDecimal getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public void setPrice(BigDecimal price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public BigDecimal getOriginalPrice() {
|
||||
return originalPrice;
|
||||
}
|
||||
|
||||
public void setOriginalPrice(BigDecimal originalPrice) {
|
||||
this.originalPrice = originalPrice;
|
||||
}
|
||||
|
||||
public boolean isInStock() {
|
||||
return inStock;
|
||||
}
|
||||
|
||||
public void setInStock(boolean inStock) {
|
||||
this.inStock = inStock;
|
||||
}
|
||||
|
||||
public String getBuyUrl() {
|
||||
return buyUrl;
|
||||
}
|
||||
|
||||
public void setBuyUrl(String buyUrl) {
|
||||
this.buyUrl = buyUrl;
|
||||
}
|
||||
|
||||
public OffsetDateTime getLastUpdated() {
|
||||
return lastUpdated;
|
||||
}
|
||||
|
||||
public void setLastUpdated(OffsetDateTime lastUpdated) {
|
||||
this.lastUpdated = lastUpdated;
|
||||
}
|
||||
}
|
||||
@@ -1,79 +1,79 @@
|
||||
package group.goforward.ballistic.web.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class ProductSummaryDto {
|
||||
|
||||
private String id; // product UUID as string
|
||||
private String name;
|
||||
private String brand;
|
||||
private String platform;
|
||||
private String partRole;
|
||||
private String categoryKey;
|
||||
private BigDecimal price;
|
||||
private String buyUrl;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getBrand() {
|
||||
return brand;
|
||||
}
|
||||
|
||||
public void setBrand(String brand) {
|
||||
this.brand = brand;
|
||||
}
|
||||
|
||||
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 getCategoryKey() {
|
||||
return categoryKey;
|
||||
}
|
||||
|
||||
public void setCategoryKey(String categoryKey) {
|
||||
this.categoryKey = categoryKey;
|
||||
}
|
||||
|
||||
public BigDecimal getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public void setPrice(BigDecimal price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public String getBuyUrl() {
|
||||
return buyUrl;
|
||||
}
|
||||
|
||||
public void setBuyUrl(String buyUrl) {
|
||||
this.buyUrl = buyUrl;
|
||||
}
|
||||
package group.goforward.ballistic.web.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class ProductSummaryDto {
|
||||
|
||||
private String id; // product UUID as string
|
||||
private String name;
|
||||
private String brand;
|
||||
private String platform;
|
||||
private String partRole;
|
||||
private String categoryKey;
|
||||
private BigDecimal price;
|
||||
private String buyUrl;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getBrand() {
|
||||
return brand;
|
||||
}
|
||||
|
||||
public void setBrand(String brand) {
|
||||
this.brand = brand;
|
||||
}
|
||||
|
||||
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 getCategoryKey() {
|
||||
return categoryKey;
|
||||
}
|
||||
|
||||
public void setCategoryKey(String categoryKey) {
|
||||
this.categoryKey = categoryKey;
|
||||
}
|
||||
|
||||
public BigDecimal getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public void setPrice(BigDecimal price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public String getBuyUrl() {
|
||||
return buyUrl;
|
||||
}
|
||||
|
||||
public void setBuyUrl(String buyUrl) {
|
||||
this.buyUrl = buyUrl;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,32 @@
|
||||
package group.goforward.ballistic.web.dto;
|
||||
|
||||
public class UpsertMerchantCategoryMappingRequest {
|
||||
|
||||
private Integer merchantId;
|
||||
private String rawCategory;
|
||||
private String mappedPartRole; // can be null to "unmap"
|
||||
|
||||
public Integer getMerchantId() {
|
||||
return merchantId;
|
||||
}
|
||||
|
||||
public void setMerchantId(Integer merchantId) {
|
||||
this.merchantId = merchantId;
|
||||
}
|
||||
|
||||
public String getRawCategory() {
|
||||
return rawCategory;
|
||||
}
|
||||
|
||||
public void setRawCategory(String rawCategory) {
|
||||
this.rawCategory = rawCategory;
|
||||
}
|
||||
|
||||
public String getMappedPartRole() {
|
||||
return mappedPartRole;
|
||||
}
|
||||
|
||||
public void setMappedPartRole(String mappedPartRole) {
|
||||
this.mappedPartRole = mappedPartRole;
|
||||
}
|
||||
package group.goforward.ballistic.web.dto;
|
||||
|
||||
public class UpsertMerchantCategoryMappingRequest {
|
||||
|
||||
private Integer merchantId;
|
||||
private String rawCategory;
|
||||
private String mappedPartRole; // can be null to "unmap"
|
||||
|
||||
public Integer getMerchantId() {
|
||||
return merchantId;
|
||||
}
|
||||
|
||||
public void setMerchantId(Integer merchantId) {
|
||||
this.merchantId = merchantId;
|
||||
}
|
||||
|
||||
public String getRawCategory() {
|
||||
return rawCategory;
|
||||
}
|
||||
|
||||
public void setRawCategory(String rawCategory) {
|
||||
this.rawCategory = rawCategory;
|
||||
}
|
||||
|
||||
public String getMappedPartRole() {
|
||||
return mappedPartRole;
|
||||
}
|
||||
|
||||
public void setMappedPartRole(String mappedPartRole) {
|
||||
this.mappedPartRole = mappedPartRole;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,30 @@
|
||||
package group.goforward.ballistic.web.mapper;
|
||||
|
||||
import group.goforward.ballistic.model.Product;
|
||||
import group.goforward.ballistic.web.dto.ProductSummaryDto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class ProductMapper {
|
||||
|
||||
public static ProductSummaryDto toSummary(Product product, BigDecimal price, String buyUrl) {
|
||||
ProductSummaryDto dto = new ProductSummaryDto();
|
||||
|
||||
// Product ID -> String
|
||||
dto.setId(String.valueOf(product.getId()));
|
||||
|
||||
dto.setName(product.getName());
|
||||
dto.setBrand(product.getBrand() != null ? product.getBrand().getName() : null);
|
||||
dto.setPlatform(product.getPlatform());
|
||||
dto.setPartRole(product.getPartRole());
|
||||
|
||||
// Use rawCategoryKey from the Product entity
|
||||
dto.setCategoryKey(product.getRawCategoryKey());
|
||||
|
||||
// Price + buy URL from offers
|
||||
dto.setPrice(price);
|
||||
dto.setBuyUrl(buyUrl);
|
||||
|
||||
return dto;
|
||||
}
|
||||
package group.goforward.ballistic.web.mapper;
|
||||
|
||||
import group.goforward.ballistic.model.Product;
|
||||
import group.goforward.ballistic.web.dto.ProductSummaryDto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class ProductMapper {
|
||||
|
||||
public static ProductSummaryDto toSummary(Product product, BigDecimal price, String buyUrl) {
|
||||
ProductSummaryDto dto = new ProductSummaryDto();
|
||||
|
||||
// Product ID -> String
|
||||
dto.setId(String.valueOf(product.getId()));
|
||||
|
||||
dto.setName(product.getName());
|
||||
dto.setBrand(product.getBrand() != null ? product.getBrand().getName() : null);
|
||||
dto.setPlatform(product.getPlatform());
|
||||
dto.setPartRole(product.getPartRole());
|
||||
|
||||
// Use rawCategoryKey from the Product entity
|
||||
dto.setCategoryKey(product.getRawCategoryKey());
|
||||
|
||||
// Price + buy URL from offers
|
||||
dto.setPrice(price);
|
||||
dto.setBuyUrl(buyUrl);
|
||||
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user