| Line 1... |
Line 1... |
| 1 |
package com.smartdukaan.cron.scheduled;
|
1 |
package com.smartdukaan.cron.scheduled;
|
| 2 |
|
2 |
|
| 3 |
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
|
- |
|
| 4 |
import com.spice.profitmandi.dao.entity.transaction.CronBatch;
|
3 |
import com.spice.profitmandi.dao.entity.transaction.CronBatch;
|
| 5 |
import com.spice.profitmandi.dao.enumuration.catalog.OfferSchemeType;
|
- |
|
| 6 |
import com.spice.profitmandi.dao.model.CreateOfferRequest;
|
- |
|
| 7 |
import com.spice.profitmandi.service.cron.CronBatchService;
|
4 |
import com.spice.profitmandi.service.cron.CronBatchService;
|
| 8 |
import com.spice.profitmandi.service.offers.OfferPartnerPayoutData;
|
- |
|
| 9 |
import com.spice.profitmandi.service.offers.OfferProcessingHelper;
|
- |
|
| 10 |
import com.spice.profitmandi.service.offers.OfferService;
|
5 |
import com.spice.profitmandi.service.offers.OfferBatchService;
|
| 11 |
import com.spice.profitmandi.service.offers.SellinPartnerPayoutData;
|
- |
|
| 12 |
import com.spice.profitmandi.service.transaction.PartnerLimitUpdateData;
|
6 |
import com.spice.profitmandi.service.transaction.PartnerLimitUpdateData;
|
| 13 |
import org.apache.logging.log4j.LogManager;
|
7 |
import org.apache.logging.log4j.LogManager;
|
| 14 |
import org.apache.logging.log4j.Logger;
|
8 |
import org.apache.logging.log4j.Logger;
|
| 15 |
import org.springframework.beans.factory.annotation.Autowired;
|
9 |
import org.springframework.beans.factory.annotation.Autowired;
|
| 16 |
import org.springframework.stereotype.Component;
|
10 |
import org.springframework.stereotype.Component;
|
| Line 22... |
Line 16... |
| 22 |
* Batch-aware scheduled tasks with per-partner transaction isolation.
|
16 |
* Batch-aware scheduled tasks with per-partner transaction isolation.
|
| 23 |
* NO @Transactional at class level — each partner gets its own transaction
|
17 |
* NO @Transactional at class level — each partner gets its own transaction
|
| 24 |
* via REQUIRES_NEW in the helper beans.
|
18 |
* via REQUIRES_NEW in the helper beans.
|
| 25 |
*
|
19 |
*
|
| 26 |
* Tracks every run in cron_batch / cron_batch_item tables.
|
20 |
* Tracks every run in cron_batch / cron_batch_item tables.
|
| 27 |
* Sends failure summary email to technology on partial failures.
|
21 |
* Sends failure summary email on partial failures.
|
| 28 |
*/
|
22 |
*/
|
| 29 |
@Component
|
23 |
@Component
|
| 30 |
public class BatchScheduledTasks {
|
24 |
public class BatchScheduledTasks {
|
| 31 |
|
25 |
|
| 32 |
private static final Logger LOGGER = LogManager.getLogger(BatchScheduledTasks.class);
|
26 |
private static final Logger LOGGER = LogManager.getLogger(BatchScheduledTasks.class);
|
| 33 |
|
27 |
|
| 34 |
@Autowired
|
28 |
@Autowired
|
| 35 |
private CronBatchService cronBatchService;
|
29 |
private CronBatchService cronBatchService;
|
| 36 |
|
30 |
|
| 37 |
@Autowired
|
31 |
@Autowired
|
| 38 |
private OfferProcessingHelper offerProcessingHelper;
|
- |
|
| 39 |
|
- |
|
| 40 |
@Autowired
|
- |
|
| 41 |
private OfferService offerService;
|
32 |
private OfferBatchService offerBatchService;
|
| 42 |
|
33 |
|
| 43 |
@Autowired
|
34 |
@Autowired
|
| 44 |
private PartnerLimitHelper partnerLimitHelper;
|
35 |
private PartnerLimitHelper partnerLimitHelper;
|
| 45 |
|
36 |
|
| 46 |
/**
|
37 |
/**
|
| 47 |
* Processes a single offer with per-partner transaction isolation and batch tracking.
|
38 |
* CLI entrypoint for cron: delegates to shared OfferBatchService (also used by web/fofo controllers).
|
| 48 |
* Routes to activation or sellin based on scheme type.
|
- |
|
| 49 |
*
|
- |
|
| 50 |
* Flow:
|
- |
|
| 51 |
* 1. Load offer (read-only transaction)
|
- |
|
| 52 |
* 2. Calculate payouts per partner (read-only transaction)
|
- |
|
| 53 |
* 3. Create batch record with all eligible partners
|
- |
|
| 54 |
* 4. Per-partner: call helper with REQUIRES_NEW — commits/rolls back independently
|
- |
|
| 55 |
* 5. Finalize batch: update counts, send failure email if any
|
- |
|
| 56 |
*/
|
39 |
*/
|
| 57 |
public void processOfferWithBatch(int offerId) throws Exception {
|
40 |
public void processOfferWithBatch(int offerId) throws Exception {
|
| 58 |
CreateOfferRequest createOfferRequest = offerProcessingHelper.loadOfferRequest(offerId);
|
- |
|
| 59 |
if (createOfferRequest == null) {
|
- |
|
| 60 |
return;
|
- |
|
| 61 |
}
|
- |
|
| 62 |
LOGGER.info("Processing offer {} (type={}) with batch tracking", offerId, createOfferRequest.getSchemeType());
|
- |
|
| 63 |
|
- |
|
| 64 |
if (createOfferRequest.getSchemeType().equals(OfferSchemeType.ACTIVATION)) {
|
- |
|
| 65 |
processActivationOfferWithBatch(offerId, createOfferRequest);
|
- |
|
| 66 |
} else if (createOfferRequest.getSchemeType().equals(OfferSchemeType.SELLIN)) {
|
- |
|
| 67 |
processSellinOfferWithBatch(offerId, createOfferRequest);
|
- |
|
| 68 |
} else {
|
- |
|
| 69 |
LOGGER.warn("Offer {} has unsupported scheme type: {}", offerId, createOfferRequest.getSchemeType());
|
- |
|
| 70 |
}
|
- |
|
| 71 |
}
|
- |
|
| 72 |
|
- |
|
| 73 |
private void processActivationOfferWithBatch(int offerId, CreateOfferRequest createOfferRequest) throws Exception {
|
- |
|
| 74 |
List<OfferPartnerPayoutData> partnerPayouts;
|
- |
|
| 75 |
try {
|
- |
|
| 76 |
partnerPayouts = offerService.calculateOfferPayouts(createOfferRequest);
|
- |
|
| 77 |
} catch (Exception e) {
|
- |
|
| 78 |
LOGGER.error("Failed to calculate activation payouts for offer {}: {}", offerId, e.getMessage());
|
- |
|
| 79 |
return;
|
- |
|
| 80 |
}
|
- |
|
| 81 |
|
- |
|
| 82 |
if (partnerPayouts.isEmpty()) {
|
- |
|
| 83 |
LOGGER.info("No eligible partners for activation offer {}", offerId);
|
- |
|
| 84 |
return;
|
- |
|
| 85 |
}
|
- |
|
| 86 |
|
- |
|
| 87 |
LinkedHashMap<Integer, String> fofoIdPartnerNameMap = new LinkedHashMap<>();
|
- |
|
| 88 |
for (OfferPartnerPayoutData data : partnerPayouts) {
|
- |
|
| 89 |
fofoIdPartnerNameMap.put(data.getFofoId(), "fofo-" + data.getFofoId());
|
- |
|
| 90 |
}
|
- |
|
| 91 |
|
- |
|
| 92 |
CronBatch batch = cronBatchService.createBatch("processActivationOffer-" + offerId, fofoIdPartnerNameMap);
|
- |
|
| 93 |
|
- |
|
| 94 |
for (OfferPartnerPayoutData data : partnerPayouts) {
|
- |
|
| 95 |
try {
|
- |
|
| 96 |
offerProcessingHelper.processPartnerOfferPayout(
|
- |
|
| 97 |
batch.getId(), data.getFofoId(), offerId, createOfferRequest.getDescription(),
|
- |
|
| 98 |
data.getLineItemValueMap(), data.getItemCriteriaPayout(), data.getCriteriaId(),
|
- |
|
| 99 |
data.getSerialNumberPaid(), data.getAgeingSummaryModelsMap(),
|
- |
|
| 100 |
data.getEligiblePayoutValue(), data.isDiscount());
|
- |
|
| 101 |
} catch (Exception e) {
|
- |
|
| 102 |
LOGGER.error("Activation offer {} failed for fofoId={}: {}", offerId, data.getFofoId(), e.getMessage());
|
- |
|
| 103 |
cronBatchService.markItemFailed(batch.getId(), data.getFofoId(), e.getMessage());
|
- |
|
| 104 |
}
|
- |
|
| 105 |
}
|
- |
|
| 106 |
|
- |
|
| 107 |
cronBatchService.finalizeBatch(batch.getId());
|
- |
|
| 108 |
}
|
- |
|
| 109 |
|
- |
|
| 110 |
private void processSellinOfferWithBatch(int offerId, CreateOfferRequest createOfferRequest) throws Exception {
|
- |
|
| 111 |
List<SellinPartnerPayoutData> partnerPayouts;
|
- |
|
| 112 |
try {
|
- |
|
| 113 |
partnerPayouts = offerService.calculateSellinPayouts(createOfferRequest);
|
- |
|
| 114 |
} catch (Exception e) {
|
- |
|
| 115 |
LOGGER.error("Failed to calculate sellin payouts for offer {}: {}", offerId, e.getMessage());
|
- |
|
| 116 |
return;
|
- |
|
| 117 |
}
|
- |
|
| 118 |
|
- |
|
| 119 |
if (partnerPayouts.isEmpty()) {
|
- |
|
| 120 |
LOGGER.info("No eligible partners for sellin offer {}", offerId);
|
- |
|
| 121 |
return;
|
- |
|
| 122 |
}
|
- |
|
| 123 |
|
- |
|
| 124 |
LinkedHashMap<Integer, String> fofoIdPartnerNameMap = new LinkedHashMap<>();
|
- |
|
| 125 |
for (SellinPartnerPayoutData data : partnerPayouts) {
|
- |
|
| 126 |
fofoIdPartnerNameMap.put(data.getFofoId(), "fofo-" + data.getFofoId());
|
- |
|
| 127 |
}
|
- |
|
| 128 |
|
- |
|
| 129 |
CronBatch batch = cronBatchService.createBatch("processSellinOffer-" + offerId, fofoIdPartnerNameMap);
|
- |
|
| 130 |
|
- |
|
| 131 |
for (SellinPartnerPayoutData data : partnerPayouts) {
|
- |
|
| 132 |
try {
|
- |
|
| 133 |
offerProcessingHelper.processPartnerSellinPayout(
|
- |
|
| 134 |
batch.getId(), data.getFofoId(), offerId, createOfferRequest.getDescription(),
|
- |
|
| 135 |
data.getSerialNumberInventoryItemMap(), data.getItemCriteriaPayout(), data.getCriteriaId(),
|
- |
|
| 136 |
data.getSerialNumberPaid(), data.getEligiblePayoutValue(), data.isDiscount());
|
- |
|
| 137 |
} catch (Exception e) {
|
- |
|
| 138 |
LOGGER.error("Sellin offer {} failed for fofoId={}: {}", offerId, data.getFofoId(), e.getMessage());
|
- |
|
| 139 |
cronBatchService.markItemFailed(batch.getId(), data.getFofoId(), e.getMessage());
|
- |
|
| 140 |
}
|
- |
|
| 141 |
}
|
- |
|
| 142 |
|
- |
|
| 143 |
cronBatchService.finalizeBatch(batch.getId());
|
41 |
offerBatchService.processOfferWithBatch(offerId);
|
| 144 |
}
|
42 |
}
|
| 145 |
|
43 |
|
| 146 |
/**
|
44 |
/**
|
| 147 |
* Recalculates partner credit limits. Only writes to partners where values actually changed.
|
45 |
* Recalculates partner credit limits. Only writes to partners where values actually changed.
|
| 148 |
*
|
46 |
*
|