Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
37898 ranu 1
package com.spice.profitmandi.web.service;
2
 
3
import com.fasterxml.jackson.databind.ObjectMapper;
4
import com.spice.profitmandi.dao.entity.catalog.Item;
5
import com.spice.profitmandi.dao.entity.dtr.WebOffer;
6
import com.spice.profitmandi.dao.entity.dtr.WebOfferProduct;
7
import com.spice.profitmandi.dao.repository.catalog.ItemRepository;
8
import com.spice.profitmandi.dao.repository.dtr.WebOfferProductRepository;
9
import com.spice.profitmandi.dao.repository.dtr.WebOfferRepository;
10
import com.spice.profitmandi.web.model.EmiOfferImportDTO;
11
import org.apache.logging.log4j.LogManager;
12
import org.apache.logging.log4j.Logger;
13
import org.springframework.beans.factory.annotation.Autowired;
14
import org.springframework.stereotype.Service;
15
 
16
import java.io.InputStream;
17
import java.time.LocalDateTime;
18
import java.util.*;
19
import java.util.stream.Collectors;
20
 
21
/**
22
 * Imports EMI scraper JSON (one JSON per scrape run) and creates one
23
 * dtr.web_offer per (product × bank), with a dtr.web_offer_product row linking
24
 * the catalog_id. Plan details are summarised into detailedText.
25
 * <p>
26
 * SKU resolution: catalog.item.brand_identifier -> catalog.item.catalog_item_id
27
 * which is the WebOfferProduct.entityId.
28
 */
29
@Service
30
public class EmiOfferImportService {
31
 
32
    private static final Logger LOGGER = LogManager.getLogger(EmiOfferImportService.class);
33
 
34
    @Autowired
35
    private ItemRepository itemRepository;
36
 
37
    @Autowired
38
    private WebOfferRepository webOfferRepository;
39
 
40
    @Autowired
41
    private WebOfferProductRepository webOfferProductRepository;
42
 
43
    public ImportResult importFromJson(InputStream in, LocalDateTime startDate, LocalDateTime endDate, boolean dryRun)
44
            throws Exception {
45
        ObjectMapper mapper = new ObjectMapper();
46
        EmiOfferImportDTO dto = mapper.readValue(in, EmiOfferImportDTO.class);
47
 
48
        ImportResult result = new ImportResult();
49
        result.dryRun = dryRun;
50
        result.generatedAt = dto.getGeneratedAt();
51
 
52
        if (dto.getProducts() == null || dto.getProducts().isEmpty()) {
53
            return result;
54
        }
55
 
56
        List<String> skus = dto.getProducts().stream()
57
                .map(EmiOfferImportDTO.Product::getSku)
58
                .filter(s -> s != null && !s.isEmpty())
59
                .distinct()
60
                .collect(Collectors.toList());
61
 
62
        Map<String, Integer> skuToCatalogId = resolveSkus(skus, result);
63
 
64
        for (EmiOfferImportDTO.Product product : dto.getProducts()) {
65
            Integer catalogId = skuToCatalogId.get(product.getSku());
66
            if (catalogId == null) {
67
                continue; // already recorded in missingSkus
68
            }
69
            if (product.getCardTypes() == null) continue;
70
 
71
            for (EmiOfferImportDTO.CardType cardType : product.getCardTypes()) {
72
                if (cardType.getBanks() == null) continue;
73
                for (EmiOfferImportDTO.Bank bank : cardType.getBanks()) {
74
                    if (bank.getPlans() == null || bank.getPlans().isEmpty()) {
75
                        result.banksSkippedNoPlans++;
76
                        continue;
77
                    }
78
                    createOffer(product, cardType, bank, catalogId, startDate, endDate, dryRun, result);
79
                }
80
            }
81
        }
82
 
83
        return result;
84
    }
85
 
86
    private Map<String, Integer> resolveSkus(List<String> skus, ImportResult result) {
87
        Map<String, Integer> out = new HashMap<>();
88
        if (skus.isEmpty()) return out;
89
 
90
        LOGGER.info("EmiImport: resolving {} sku(s): {}", skus.size(), skus);
91
        List<Item> items = itemRepository.selectAllByBrandIdentifiers(skus);
92
        LOGGER.info("EmiImport: itemRepository returned {} row(s) for {} sku(s)", items.size(), skus.size());
93
 
94
        Set<String> matched = new HashSet<>();
95
        for (Item item : items) {
96
            String sku = item.getBrandIdentifier();
97
            LOGGER.info("EmiImport:   matched item id={} brandIdentifier='{}' catalogItemId={}",
98
                    item.getId(), sku, item.getCatalogItemId());
99
            if (sku == null) continue;
100
            matched.add(sku);
101
            // keep first occurrence; catalog_item_id should be identical across colors of same catalog
102
            out.putIfAbsent(sku, item.getCatalogItemId());
103
        }
104
        for (String sku : skus) {
105
            if (!matched.contains(sku)) {
106
                LOGGER.warn("EmiImport: NO match for sku '{}' (len={})", sku, sku == null ? -1 : sku.length());
107
                result.missingSkus.add(sku);
108
            }
109
        }
110
        return out;
111
    }
112
 
113
    private void createOffer(EmiOfferImportDTO.Product product, EmiOfferImportDTO.CardType cardType,
114
                             EmiOfferImportDTO.Bank bank, int catalogId, LocalDateTime startDate, LocalDateTime endDate,
115
                             boolean dryRun, ImportResult result) {
116
 
117
        String title = bank.getBankName();
118
        String smallText = inferOfferTypeSummary(bank.getPlans());
119
        String detailedText = buildDetailedText(product, bank);
120
 
121
        OfferPreview preview = new OfferPreview();
122
        preview.sku = product.getSku();
123
        preview.catalogId = catalogId;
124
        preview.title = title;
125
        preview.smallText = smallText;
126
        preview.detailedText = detailedText;
127
        preview.planCount = bank.getPlans().size();
128
        result.previews.add(preview);
129
 
130
        if (dryRun) {
131
            result.offersWouldCreate++;
132
            return;
133
        }
134
 
135
        WebOffer offer = new WebOffer();
136
        offer.setTitle(title);
137
        offer.setSmallText(smallText);
138
        offer.setDetailedText(detailedText);
139
        offer.setStartDate(startDate);
140
        offer.setEndDate(endDate);
141
        offer.setActive(true);
142
        offer.setCreatedDate(LocalDateTime.now());
143
        webOfferRepository.persist(offer);
144
 
145
        WebOfferProduct wop = new WebOfferProduct();
146
        wop.setWebOfferId(offer.getId());
147
        wop.setEntityId(catalogId);
148
        wop.setRank(0);
149
        boolean exists = webOfferProductRepository.isExists(wop.getWebOfferId(), wop.getEntityId());
150
        if (!exists) {
151
            webOfferProductRepository.persist(wop);
152
        }
153
 
154
        preview.webOfferId = offer.getId();
155
        result.offersCreated++;
156
        result.productsLinked++;
157
        LOGGER.info("EmiImport: created web_offer id={} title='{}' catalogId={}", offer.getId(), title, catalogId);
158
    }
159
 
160
    private String inferOfferTypeSummary(List<EmiOfferImportDTO.Plan> plans) {
161
        Map<String, Integer> counts = new LinkedHashMap<>();
162
        for (EmiOfferImportDTO.Plan p : plans) {
163
            String ot = p.getOfferType();
164
            if (ot == null || ot.isEmpty()) continue;
165
            counts.merge(ot, 1, Integer::sum);
166
        }
167
        if (counts.isEmpty()) return "EMI";
168
        return counts.keySet().iterator().next(); // dominant/first offer type
169
    }
170
 
171
    private String buildDetailedText(EmiOfferImportDTO.Product product, EmiOfferImportDTO.Bank bank) {
172
        StringBuilder sb = new StringBuilder();
173
        sb.append(product.getProductName()).append(" · ").append(product.getPrice()).append("\n");
174
        for (EmiOfferImportDTO.Plan p : bank.getPlans()) {
175
            sb.append(nz(p.getTenure())).append("  ");
176
            sb.append(nz(p.getEmiAmount()));
177
            if (p.getOfferType() != null && !p.getOfferType().isEmpty()) {
178
                sb.append("  ").append(p.getOfferType());
179
            }
180
            if (p.getTotalCost() != null && !p.getTotalCost().isEmpty()) {
181
                sb.append("  total ").append(p.getTotalCost());
182
            }
183
            if (p.getInterestRate() != null && !p.getInterestRate().isEmpty()) {
184
                sb.append(" @").append(p.getInterestRate());
185
            }
186
            if (p.getDiscount() != null && !p.getDiscount().isEmpty()) {
187
                sb.append("  disc ").append(p.getDiscount());
188
            }
189
            sb.append("\n");
190
        }
191
        return sb.toString().trim();
192
    }
193
 
194
    private String nz(String s) {
195
        return s == null ? "" : s;
196
    }
197
 
198
    public static class ImportResult {
199
        public boolean dryRun;
200
        public String generatedAt;
201
        public int offersCreated;
202
        public int offersWouldCreate;
203
        public int productsLinked;
204
        public int banksSkippedNoPlans;
205
        public List<String> missingSkus = new ArrayList<>();
206
        public List<OfferPreview> previews = new ArrayList<>();
207
    }
208
 
209
    public static class OfferPreview {
210
        public String sku;
211
        public int catalogId;
212
        public int webOfferId;      // 0 when dry-run
213
        public String title;
214
        public String smallText;
215
        public String detailedText;
216
        public int planCount;
217
    }
218
}