Rev 36337 | Blame | Compare with Previous | Last modification | View Log | RSS feed
package com.spice.profitmandi.service.cron;import com.spice.profitmandi.dao.entity.transaction.CronBatch;import com.spice.profitmandi.dao.entity.transaction.CronBatchItem;import com.spice.profitmandi.dao.enumuration.transaction.CronBatchItemStatus;import com.spice.profitmandi.dao.enumuration.transaction.CronBatchStatus;import com.spice.profitmandi.dao.repository.transaction.CronBatchItemRepository;import com.spice.profitmandi.dao.repository.transaction.CronBatchRepository;import com.spice.profitmandi.service.mail.MailOutboxService;import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;import java.time.LocalDateTime;import java.time.format.DateTimeFormatter;import java.util.List;import java.util.Map;@Servicepublic class CronBatchService {private static final Logger LOGGER = LogManager.getLogger(CronBatchService.class);private static final String[] TECHNOLOGY_EMAIL = {"amit.gupta@smartdukaan.com"};private static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");@Autowiredprivate CronBatchRepository cronBatchRepository;@Autowiredprivate CronBatchItemRepository cronBatchItemRepository;@Autowiredprivate MailOutboxService mailOutboxService;/*** Read-only guard used by OfferBatchService before creating a new batch.* Returns any RUNNING batch for this offer (sellin or activation job name),* or null if none. Wrapped in a read-only tx so callers running outside an* existing transaction (e.g. background worker thread) can query safely.*/@Transactional(readOnly = true)public CronBatch findRunningForOffer(int offerId) {List<CronBatch> running = cronBatchRepository.selectRunningForOffer(offerId);return running.isEmpty() ? null : running.get(0);}@Transactional(propagation = Propagation.REQUIRES_NEW)public CronBatch createBatch(String jobName, Map<Integer, String> fofoIdPartnerNameMap) {CronBatch batch = new CronBatch(jobName);batch.setTotalCount(fofoIdPartnerNameMap.size());cronBatchRepository.persist(batch);for (Map.Entry<Integer, String> entry : fofoIdPartnerNameMap.entrySet()) {CronBatchItem item = new CronBatchItem(batch.getId(), entry.getKey(), entry.getValue());cronBatchItemRepository.persist(item);}LOGGER.info("Created batch {} for job {} with {} items", batch.getId(), jobName, fofoIdPartnerNameMap.size());return batch;}@Transactional(propagation = Propagation.REQUIRES_NEW)public void markItemSuccess(int batchId, int fofoId) {List<CronBatchItem> items = cronBatchItemRepository.selectByBatchIdAndStatus(batchId, CronBatchItemStatus.PENDING);for (CronBatchItem item : items) {if (item.getFofoId() == fofoId) {item.markSuccess();break;}}}@Transactional(propagation = Propagation.REQUIRES_NEW)public void markItemFailed(int batchId, int fofoId, String errorMessage) {List<CronBatchItem> items = cronBatchItemRepository.selectByBatchIdAndStatus(batchId, CronBatchItemStatus.PENDING);for (CronBatchItem item : items) {if (item.getFofoId() == fofoId) {item.markFailed(errorMessage);break;}}}@Transactional(propagation = Propagation.REQUIRES_NEW)public void finalizeBatch(int batchId) {CronBatch batch = cronBatchRepository.selectById(batchId);List<CronBatchItem> failedItems = cronBatchItemRepository.selectFailedByBatchId(batchId);int failureCount = failedItems.size();int successCount = batch.getTotalCount() - failureCount;batch.setSuccessCount(successCount);batch.setFailureCount(failureCount);batch.setCompletedAt(LocalDateTime.now());batch.setStatus(failureCount == 0 ? CronBatchStatus.COMPLETED : CronBatchStatus.PARTIAL_FAILURE);LOGGER.info("Batch {} finalized: {} success, {} failed", batchId, successCount, failureCount);if (failureCount > 0) {sendFailureEmail(batch, failedItems);}}private void sendFailureEmail(CronBatch batch, List<CronBatchItem> failedItems) {StringBuilder body = new StringBuilder();body.append("Cron job: ").append(batch.getJobName()).append("\n");body.append("Run time: ").append(batch.getStartedAt().format(DTF)).append("\n");body.append("Total processed: ").append(batch.getTotalCount()).append("\n");body.append("Success: ").append(batch.getSuccessCount()).append("\n");body.append("Failures: ").append(batch.getFailureCount()).append("\n\n");body.append("Partner failures:\n");for (CronBatchItem item : failedItems) {body.append("- ").append(item.getPartnerName()).append(" (fofoId: ").append(item.getFofoId()).append(")").append(" — ").append(item.getErrorMessage()).append("\n");}String subject = String.format("[CRON ALERT] %s — %d of %d partners failed",batch.getJobName(), batch.getFailureCount(), batch.getTotalCount());try {mailOutboxService.queueMailViaGoogle(TECHNOLOGY_EMAIL, null, subject, body.toString(),"CronBatchService." + batch.getJobName());} catch (Exception e) {LOGGER.error("Failed to send batch failure email for batch {}: {}", batch.getId(), e.getMessage());}}}