Rev 35791 | Blame | Compare with Previous | Last modification | View Log | RSS feed
package com.spice.profitmandi.dao.service.shopify;import com.fasterxml.jackson.databind.JsonNode;import com.fasterxml.jackson.databind.ObjectMapper;import com.spice.profitmandi.common.enumuration.SchemeType;import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;import com.spice.profitmandi.common.web.client.RestClient;import com.spice.profitmandi.dao.entity.dtr.WebOffer;import com.spice.profitmandi.dao.entity.fofo.FofoStore;import com.spice.profitmandi.dao.repository.catalog.ItemRepository;import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;import com.spice.profitmandi.dao.repository.dtr.WebOfferRepository;import com.spice.profitmandi.dao.repository.fofo.CurrentInventorySnapshotRepository;import com.spice.profitmandi.service.inventory.FofoAvailabilityInfo;import com.spice.profitmandi.service.inventory.FofoCatalogResponse;import com.spice.profitmandi.service.inventory.SaholicInventoryService;import org.apache.commons.lang.StringUtils;import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;import org.json.JSONArray;import org.json.JSONObject;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;import java.util.*;import java.util.stream.Collectors;@Service@Transactional(propagation = Propagation.REQUIRES_NEW)public class ShopifyService {private static final Logger logger = LogManager.getLogger(ShopifyService.class);@Value("${new.solr.url}")private String solrUrl;@AutowiredObjectMapper objectMapper = new ObjectMapper();@AutowiredShopifyGraphQLService shopifyGraphQLService;@AutowiredRestClient restClient;@AutowiredFofoStoreRepository fofoStoreRepository;@AutowiredItemRepository itemRepository;@AutowiredWebOfferRepository webOfferRepository;@AutowiredCurrentInventorySnapshotRepository currentInventorySnapshotRepository;@AutowiredSaholicInventoryService saholicInventoryService;// -------------------------------// ✅ 1. Create Product (with duplicate check)// -------------------------------public JsonNode createProduct(Map<String, Object> productInput) {try {return shopifyGraphQLService.productCreate(productInput);} catch (Exception e) {logger.error("createProduct Error: {}", e.getMessage());return objectMapper.createObjectNode();}}// -------------------------------// ✅ 2. Get All Products// -------------------------------public JsonNode getAllProducts() {try {String query = "query { products(first: 50) { edges { node { id title handle } } } }";return shopifyGraphQLService.execute(query, null);} catch (Exception e) {logger.error("getAllProducts Error: {}", e.getMessage());return objectMapper.createObjectNode();}}// -------------------------------// ✅ 3. Get Product by ID// -------------------------------public JsonNode getProductById(String productId) {try {String query = "query($id: ID!) { product(id: $id) { id title handle } }";Map<String, Object> vars = new HashMap<String, Object>();vars.put("id", productId);return shopifyGraphQLService.execute(query, vars);} catch (Exception e) {logger.error("getProductById Error: {}", e.getMessage());return objectMapper.createObjectNode();}}// -------------------------------// ✅ 4. Update Product by ID// -------------------------------public JsonNode updateProductById(String productId, Map<String, Object> params) {try {params.put("id", productId);String mutation = "mutation($input: ProductInput!) { productUpdate(input: $input) { product { id title } userErrors { field message } } }";Map<String, Object> vars = new HashMap<String, Object>();vars.put("input", params);return shopifyGraphQLService.execute(mutation, vars);} catch (Exception e) {logger.error("updateProductById Error: {}", e.getMessage());return objectMapper.createObjectNode();}}// -------------------------------// ✅ 5. Delete Product by ID// -------------------------------public JsonNode deleteProductById(String productId) {try {String mutation = "mutation($input: ProductDeleteInput!) { productDelete(input: $input) { deletedProductId userErrors { field message } } }";Map<String, Object> vars = new HashMap<String, Object>();Map<String, Object> input = new HashMap<String, Object>();input.put("id", productId);vars.put("input", input);return shopifyGraphQLService.execute(mutation, vars);} catch (Exception e) {logger.error("deleteProductById Error: {}", e.getMessage());return objectMapper.createObjectNode();}}public JsonNode getAllLocations() {try {String query = "query { locations(first: 10) { edges { node { id name } } } }";return shopifyGraphQLService.execute(query, null);} catch (Exception e) {logger.error("getAllLocations Error: {}", e.getMessage());return objectMapper.createObjectNode();}}public String getPrimaryLocationId() {try {JsonNode locationsResponse = getAllLocations();JsonNode edges = locationsResponse.path("locations").path("edges");if (edges.isArray() && edges.size() > 0) {String locationId = edges.get(0).path("node").path("id").asText();logger.info("✅ Using Shopify Location ID: {}", locationId);return locationId;} else {logger.warn("⚠️ No Shopify locations found.");return null;}} catch (Exception e) {logger.error("getPrimaryLocationId Error: {}", e.getMessage());return null;}}public List<Map<String, Object>> buildAllVariantsBulk(List<FofoAvailabilityInfo> items, List<Map<String, String>> variantAttrs) {List<Map<String, Object>> list = new ArrayList<>();for (FofoAvailabilityInfo item : items) {String color = item.getColor().trim();String price = String.valueOf(item.getSellingPrice());String compareAt = String.valueOf(item.getMrp());for (Map<String, String> attr : variantAttrs) {String storageValue = attr.get("variantName").trim();String sku = String.valueOf(attr.get("catalogId"));list.add(buildVariant(color, storageValue, price, compareAt, sku));}}return list;}public Map<String, JsonNode> fetchVariantMapByOptions(String productId) throws Exception {String query ="query getVariants($id: ID!) { " +" product(id: $id) { " +" variants(first: 100) { " +" edges { " +" node { " +" id " +" title " +" sku " +" inventoryItem { id } " +" selectedOptions { name value } " +" image { originalSrc } " +" } " +" } " +" } " +" } " +"}";Map<String,Object> vars = new HashMap<>();vars.put("id", productId);JsonNode data = shopifyGraphQLService.execute(query, vars);JsonNode edges = data.path("product").path("variants").path("edges");Map<String, JsonNode> map = new HashMap<>();for (JsonNode edge : edges) {JsonNode node = edge.path("node");String color = "", variant = "", insurance = "";for (JsonNode opt : node.path("selectedOptions")) {String name = opt.path("name").asText();String value = opt.path("value").asText();if ("Color".equalsIgnoreCase(name)) color = value;if ("Variant".equalsIgnoreCase(name)) variant = value;if ("Insurance".equalsIgnoreCase(name)) insurance = value;}String key = color + "|" + variant + "|" + insurance;map.put(key.trim(), node);}return map;}public List<JsonNode> getAllOrders() {List<JsonNode> allOrders = new ArrayList<>();try {String cursor = null;while (true) {JsonNode page = shopifyGraphQLService.getOrders(cursor);JsonNode ordersNode = page.path("orders");JsonNode edges = ordersNode.path("edges");for (JsonNode edge : edges) {allOrders.add(edge.path("node"));}boolean hasNext = ordersNode.path("pageInfo").path("hasNextPage").asBoolean();if (!hasNext) break;cursor = ordersNode.path("pageInfo").path("endCursor").asText();}} catch (Exception e) {logger.error("getAllOrders Error: {}", e.getMessage());}return allOrders;}public JsonNode getOrderDetailById(String id) {String graphQLId = "gid://shopify/Order/" + id;JsonNode order = null;try {JsonNode orderNode = shopifyGraphQLService.getOrderById(graphQLId);order = orderNode.path("order");} catch (Exception e) {logger.error("getAllOrders Error: {}", e.getMessage());}return order;}public List<FofoCatalogResponse> getAllDocs(FofoStore fofoStore) throws Exception {Map<String, String> params = new HashMap<String, String>();List<String> mandatoryQ = new ArrayList<String>();mandatoryQ.add("+active_b:true AND show_default_b:true");params.put("q", StringUtils.join(mandatoryQ, " "));params.put("fl", "*, [child parentFilter=id:catalog* childFilter=active_b:true ]");params.put("wt", "json");params.put("rows", "10");String response = restClient.get(SchemeType.HTTP, solrUrl, 8984, "solr/demo/select", params);JSONObject solrResponseJSONObj = new JSONObject(response).getJSONObject("response");JSONArray docs = solrResponseJSONObj.getJSONArray("docs");return getCatalogResponse(docs, fofoStore.getId());}public List<FofoCatalogResponse> getAllDocsPaginated(FofoStore fofoStore, int batchSize) throws Exception {List<FofoCatalogResponse> allProducts = new ArrayList<>();int start = 0;while (true) {Map<String, String> params = new HashMap<>();params.put("q", "+active_b:true AND show_default_b:true");params.put("fl", "*, [child parentFilter=id:catalog* childFilter=active_b:true ]");params.put("wt", "json");params.put("rows", String.valueOf(batchSize));params.put("start", String.valueOf(start));String response = restClient.get(SchemeType.HTTP, solrUrl, 8984, "solr/demo/select", params);JSONObject solrResponseJSONObj = new JSONObject(response).getJSONObject("response");int numFound = solrResponseJSONObj.getInt("numFound");JSONArray docs = solrResponseJSONObj.getJSONArray("docs");if (docs.length() == 0) break;List<FofoCatalogResponse> batch = getCatalogResponse(docs, fofoStore.getId());allProducts.addAll(batch);start += batchSize;if (start >= numFound) break;logger.info("Fetched {} / {} products from Solr", Math.min(start, numFound), numFound);}logger.info("Total products fetched from Solr: {}", allProducts.size());return allProducts;}public List<FofoCatalogResponse> getAllDocsPaginatedForWarehouse(int batchSize) throws Exception {List<FofoCatalogResponse> allProducts = new ArrayList<>();int start = 0;while (true) {Map<String, String> params = new HashMap<>();params.put("q", "+active_b:true AND show_default_b:true");params.put("fl", "*, [child parentFilter=id:catalog* childFilter=active_b:true ]");params.put("wt", "json");params.put("rows", String.valueOf(batchSize));params.put("start", String.valueOf(start));String response = restClient.get(SchemeType.HTTP, solrUrl, 8984, "solr/demo/select", params);JSONObject solrResponseJSONObj = new JSONObject(response).getJSONObject("response");int numFound = solrResponseJSONObj.getInt("numFound");JSONArray docs = solrResponseJSONObj.getJSONArray("docs");if (docs.length() == 0) break;List<FofoCatalogResponse> batch = getCatalogResponse(docs, 0);allProducts.addAll(batch);start += batchSize;if (start >= numFound) break;logger.info("Fetched {} / {} products from Solr (warehouse)", Math.min(start, numFound), numFound);}logger.info("Total products fetched from Solr (warehouse): {}", allProducts.size());return allProducts;}private List<FofoCatalogResponse> getCatalogResponse(JSONArray docs, int fofoId) throws ProfitMandiBusinessException {Map<Integer, Integer> ourItemAvailabilityMap = null;Map<Integer, Integer> partnerStockAvailabilityMap = new HashMap<>();List<FofoCatalogResponse> dealResponse = new ArrayList<>();if (docs.length() > 0) {HashSet<Integer> itemsSet = new HashSet<>();for (int i = 0; i < docs.length(); i++) {JSONObject doc = docs.getJSONObject(i);if (doc.has("_childDocuments_")) {for (int j = 0; j < doc.getJSONArray("_childDocuments_").length(); j++) {JSONObject childItem = doc.getJSONArray("_childDocuments_").getJSONObject(j);int itemId = childItem.getInt("itemId_i");itemsSet.add(itemId);}}}if (itemsSet.isEmpty()) {return dealResponse;}if (fofoId > 0) {partnerStockAvailabilityMap = currentInventorySnapshotRepository.selectItemsStock(fofoId).stream().collect(Collectors.toMap(x -> x.getItemId(), x -> x.getAvailability()));}ourItemAvailabilityMap = saholicInventoryService.getTotalAvailabilityByItemIds(new ArrayList<>(itemsSet));}for (int i = 0; i < docs.length(); i++) {Map<Integer, FofoAvailabilityInfo> fofoAvailabilityInfoMap = new HashMap<>();JSONObject doc = docs.getJSONObject(i);FofoCatalogResponse ffdr = new FofoCatalogResponse();ffdr.setCatalogId(doc.getInt("catalogId_i"));ffdr.setImageUrl(doc.getString("imageUrl_s"));ffdr.setTitle(doc.getString("title_s"));ffdr.setSuperCatalogTitle(doc.getString("superCatalog_s"));ffdr.setSuperCatalogId(doc.getInt("superCatalog_i"));ffdr.setSuperCatalogVariants(doc.getString("superCatalogVariants_s"));List<WebOffer> webOffers = webOfferRepository.selectAllActiveOffers().get(ffdr.getCatalogId());if (webOffers != null && webOffers.size() > 0) {ffdr.setOffers(webOffers.stream().map(x -> x.getTitle()).collect(Collectors.toList()));}try {ffdr.setFeature(doc.getString("feature_s"));} catch (Exception e) {ffdr.setFeature(null);}ffdr.setBrand(doc.getJSONArray("brand_ss").getString(0));if (doc.has("_childDocuments_")) {for (int j = 0; j < doc.getJSONArray("_childDocuments_").length(); j++) {try {JSONObject childItem = doc.getJSONArray("_childDocuments_").getJSONObject(j);int itemId = childItem.getInt("itemId_i");ffdr.setIsSmartPhone(itemRepository.selectById(itemId).isSmartPhone());float sellingPrice = (float) childItem.getDouble("sellingPrice_f");if (fofoAvailabilityInfoMap.containsKey(itemId)) {if (fofoAvailabilityInfoMap.get(itemId).getSellingPrice() > sellingPrice) {fofoAvailabilityInfoMap.get(itemId).setSellingPrice(sellingPrice);fofoAvailabilityInfoMap.get(itemId).setMop((float) childItem.getDouble("mop_f"));}} else {FofoAvailabilityInfo fdi = new FofoAvailabilityInfo();fdi.setSellingPrice(sellingPrice);fdi.setMrp(childItem.getDouble("mrp_f"));fdi.setMop((float) childItem.getDouble("mop_f"));fdi.setColor(childItem.has("color_s") ? childItem.getString("color_s") : "");fdi.setTagId(childItem.getInt("tagId_i"));fdi.setItem_id(itemId);fdi.setCatalog_id(doc.getInt("catalogId_i"));fdi.setCashback(0);fdi.setMinBuyQuantity(1);int partnerAvailability = partnerStockAvailabilityMap.get(itemId) == null ? 0 : partnerStockAvailabilityMap.get(itemId);int ourStockAvailability = ourItemAvailabilityMap.get(itemId) == null ? 0 : Math.max(0, ourItemAvailabilityMap.get(itemId));fdi.setActive(partnerAvailability > 0);fdi.setPartnerAvailability(partnerAvailability);fdi.setAvailability(Math.min(5, ourStockAvailability + partnerAvailability));fdi.setQuantityStep(1);fdi.setMaxQuantity(fdi.getAvailability());fofoAvailabilityInfoMap.put(itemId, fdi);}} catch (Exception e) {logger.warn("Skipping stale item in Solr doc catalogId={}: {}", doc.getInt("catalogId_i"), e.getMessage());}}}if (fofoAvailabilityInfoMap.values().size() > 0) {ffdr.setItems(fofoAvailabilityInfoMap.values().stream().sorted(Comparator.comparing(FofoAvailabilityInfo::isActive, Comparator.reverseOrder()).thenComparingInt(y -> -y.getAvailability())).collect(Collectors.toList()));dealResponse.add(ffdr);}}return dealResponse.stream().sorted(Comparator.comparing(FofoCatalogResponse::getItems, (s1, s2) -> (s2.get(0).isActive() ? 1 : 0) - (s1.get(0).isActive() ? 1 : 0)).thenComparing(FofoCatalogResponse::getItems, (x, y) -> y.get(0).getAvailability() - x.get(0).getAvailability())).collect(Collectors.toList());}private List<Map<String, String>> buildSelectedOptions(String color, String storage) {List<Map<String, String>> selectedOptions = new ArrayList<>();Map<String, String> c = new HashMap<>();c.put("name", "Colour");c.put("value", color);selectedOptions.add(c);Map<String, String> s = new HashMap<>();s.put("name", "Storage");s.put("value", storage);selectedOptions.add(s);return selectedOptions;}private Map<String, Object> buildVariant(String color, String storage, String price, String compareAt, String sku) {Map<String, Object> v = new HashMap<>();v.put("selectedOptions", this.buildSelectedOptions(color, storage));v.put("price", price);v.put("compareAtPrice", compareAt);v.put("sku", sku);return v;}}