Subversion Repositories SmartDukaan

Rev

Rev 36306 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

package com.smartdukaan.cron.scheduled;

import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
import com.spice.profitmandi.dao.entity.transaction.CronBatch;
import com.spice.profitmandi.dao.enumuration.catalog.OfferSchemeType;
import com.spice.profitmandi.dao.model.CreateOfferRequest;
import com.spice.profitmandi.service.cron.CronBatchService;
import com.spice.profitmandi.service.offers.OfferPartnerPayoutData;
import com.spice.profitmandi.service.offers.OfferProcessingHelper;
import com.spice.profitmandi.service.offers.OfferService;
import com.spice.profitmandi.service.offers.SellinPartnerPayoutData;
import com.spice.profitmandi.service.transaction.PartnerLimitUpdateData;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.LinkedHashMap;
import java.util.List;

/**
 * Batch-aware scheduled tasks with per-partner transaction isolation.
 * NO @Transactional at class level — each partner gets its own transaction
 * via REQUIRES_NEW in the helper beans.
 *
 * Tracks every run in cron_batch / cron_batch_item tables.
 * Sends failure summary email to technology on partial failures.
 */
@Component
public class BatchScheduledTasks {

    private static final Logger LOGGER = LogManager.getLogger(BatchScheduledTasks.class);

    @Autowired
    private CronBatchService cronBatchService;

    @Autowired
    private OfferProcessingHelper offerProcessingHelper;

    @Autowired
    private OfferService offerService;

    @Autowired
    private PartnerLimitHelper partnerLimitHelper;

    /**
     * Processes a single offer with per-partner transaction isolation and batch tracking.
     * Routes to activation or sellin based on scheme type.
     *
     * Flow:
     * 1. Load offer (read-only transaction)
     * 2. Calculate payouts per partner (read-only transaction)
     * 3. Create batch record with all eligible partners
     * 4. Per-partner: call helper with REQUIRES_NEW — commits/rolls back independently
     * 5. Finalize batch: update counts, send failure email if any
     */
    public void processOfferWithBatch(int offerId) throws Exception {
        CreateOfferRequest createOfferRequest = offerProcessingHelper.loadOfferRequest(offerId);
        if (createOfferRequest == null) {
            return;
        }
        LOGGER.info("Processing offer {} (type={}) with batch tracking", offerId, createOfferRequest.getSchemeType());

        if (createOfferRequest.getSchemeType().equals(OfferSchemeType.ACTIVATION)) {
            processActivationOfferWithBatch(offerId, createOfferRequest);
        } else if (createOfferRequest.getSchemeType().equals(OfferSchemeType.SELLIN)) {
            processSellinOfferWithBatch(offerId, createOfferRequest);
        } else {
            LOGGER.warn("Offer {} has unsupported scheme type: {}", offerId, createOfferRequest.getSchemeType());
        }
    }

    private void processActivationOfferWithBatch(int offerId, CreateOfferRequest createOfferRequest) throws Exception {
        List<OfferPartnerPayoutData> partnerPayouts;
        try {
            partnerPayouts = offerService.calculateOfferPayouts(createOfferRequest);
        } catch (Exception e) {
            LOGGER.error("Failed to calculate activation payouts for offer {}: {}", offerId, e.getMessage());
            return;
        }

        if (partnerPayouts.isEmpty()) {
            LOGGER.info("No eligible partners for activation offer {}", offerId);
            return;
        }

        LinkedHashMap<Integer, String> fofoIdPartnerNameMap = new LinkedHashMap<>();
        for (OfferPartnerPayoutData data : partnerPayouts) {
            fofoIdPartnerNameMap.put(data.getFofoId(), "fofo-" + data.getFofoId());
        }

        CronBatch batch = cronBatchService.createBatch("processActivationOffer-" + offerId, fofoIdPartnerNameMap);

        for (OfferPartnerPayoutData data : partnerPayouts) {
            try {
                offerProcessingHelper.processPartnerOfferPayout(
                        batch.getId(), data.getFofoId(), offerId, createOfferRequest.getDescription(),
                        data.getLineItemValueMap(), data.getItemCriteriaPayout(), data.getCriteriaId(),
                        data.getSerialNumberPaid(), data.getAgeingSummaryModelsMap(),
                        data.getEligiblePayoutValue(), data.isDiscount());
            } catch (Exception e) {
                LOGGER.error("Activation offer {} failed for fofoId={}: {}", offerId, data.getFofoId(), e.getMessage());
                cronBatchService.markItemFailed(batch.getId(), data.getFofoId(), e.getMessage());
            }
        }

        cronBatchService.finalizeBatch(batch.getId());
    }

    private void processSellinOfferWithBatch(int offerId, CreateOfferRequest createOfferRequest) throws Exception {
        List<SellinPartnerPayoutData> partnerPayouts;
        try {
            partnerPayouts = offerService.calculateSellinPayouts(createOfferRequest);
        } catch (Exception e) {
            LOGGER.error("Failed to calculate sellin payouts for offer {}: {}", offerId, e.getMessage());
            return;
        }

        if (partnerPayouts.isEmpty()) {
            LOGGER.info("No eligible partners for sellin offer {}", offerId);
            return;
        }

        LinkedHashMap<Integer, String> fofoIdPartnerNameMap = new LinkedHashMap<>();
        for (SellinPartnerPayoutData data : partnerPayouts) {
            fofoIdPartnerNameMap.put(data.getFofoId(), "fofo-" + data.getFofoId());
        }

        CronBatch batch = cronBatchService.createBatch("processSellinOffer-" + offerId, fofoIdPartnerNameMap);

        for (SellinPartnerPayoutData data : partnerPayouts) {
            try {
                offerProcessingHelper.processPartnerSellinPayout(
                        batch.getId(), data.getFofoId(), offerId, createOfferRequest.getDescription(),
                        data.getSerialNumberInventoryItemMap(), data.getItemCriteriaPayout(), data.getCriteriaId(),
                        data.getSerialNumberPaid(), data.getEligiblePayoutValue(), data.isDiscount());
            } catch (Exception e) {
                LOGGER.error("Sellin offer {} failed for fofoId={}: {}", offerId, data.getFofoId(), e.getMessage());
                cronBatchService.markItemFailed(batch.getId(), data.getFofoId(), e.getMessage());
            }
        }

        cronBatchService.finalizeBatch(batch.getId());
    }

    /**
     * Recalculates partner credit limits. Only writes to partners where values actually changed.
     *
     * Flow:
     * 1. Read phase: calculate limits for all 1,500 partners, compare with current values
     * 2. Create batch with only changed partners (~50-100 typically)
     * 3. Per-partner: update in REQUIRES_NEW transaction
     * 4. Finalize: counts + failure email
     */
    public void updatePartnerLimitWithBatch() throws Exception {
        List<PartnerLimitUpdateData> changedPartners;
        try {
            changedPartners = partnerLimitHelper.calculateChangedPartnerLimits();
        } catch (Exception e) {
            LOGGER.error("Failed to calculate partner limits: {}", e.getMessage());
            return;
        }

        if (changedPartners.isEmpty()) {
            LOGGER.info("No partner limits changed, skipping");
            return;
        }

        LinkedHashMap<Integer, String> fofoIdPartnerNameMap = new LinkedHashMap<>();
        for (PartnerLimitUpdateData data : changedPartners) {
            fofoIdPartnerNameMap.put(data.getFofoId(), "fofo-" + data.getFofoId());
        }

        CronBatch batch = cronBatchService.createBatch("updatePartnerLimit", fofoIdPartnerNameMap);

        for (PartnerLimitUpdateData data : changedPartners) {
            try {
                partnerLimitHelper.updateSinglePartnerLimit(batch.getId(), data);
            } catch (Exception e) {
                LOGGER.error("updatePartnerLimit failed for fofoId={}: {}", data.getFofoId(), e.getMessage());
                cronBatchService.markItemFailed(batch.getId(), data.getFofoId(), e.getMessage());
            }
        }

        cronBatchService.finalizeBatch(batch.getId());
    }
}