Blame | Last modification | View Log | RSS feed
package com.spice.profitmandi.service;import com.spice.profitmandi.common.enumuration.ActivationType;import com.spice.profitmandi.dao.entity.fofo.FofoStore;import com.spice.profitmandi.dao.entity.fofo.PartnerDailyInvestment;import com.spice.profitmandi.dao.enumuration.dtr.AgendaType;import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;import org.hibernate.SessionFactory;import org.hibernate.query.NativeQuery;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import java.time.LocalDateTime;import java.util.HashMap;import java.util.List;import java.util.Map;@Servicepublic class AgendaAutoRuleServiceImpl implements AgendaAutoRuleService {private static final Logger LOGGER = LogManager.getLogger(AgendaAutoRuleServiceImpl.class);@AutowiredFofoStoreRepository fofoStoreRepository;@AutowiredPartnerInvestmentService partnerInvestmentService;@AutowiredAgendaConfigService agendaConfigService;@AutowiredAgendaAutoRuleApplier agendaAutoRuleApplier;@AutowiredSessionFactory sessionFactory;@Override@Transactionalpublic AgendaSyncResult syncAllPartners() {AgendaSyncResult result = new AgendaSyncResult();try {// Global-default params. Sales window params (baselineDays/windowDays)// are honored globally only — the batched SQL below cannot vary the// time window per partner. flagPct/clearPct etc. resolve per partner.Map<String, Double> salesDefaults = agendaConfigService.getParams(AgendaType.LOW_SALES.name(), 0);int windowDays = (int) doubleOr(salesDefaults.get("windowDays"), 30);int baselineDays = (int) doubleOr(salesDefaults.get("baselineDays"), 90);int lookbackDays = windowDays + baselineDays;List<FofoStore> candidates = fofoStoreRepository.selectAllFranchiseStores();candidates.removeIf(s -> !s.isActive() || s.isInternal() || s.isClosed());Map<Integer, double[]> salesByFofo = gatherSales(windowDays, lookbackDays);Map<Integer, Integer> billGapByFofo = gatherPurchaseGaps();Map<Integer, double[]> loanByFofo = gatherLoanAges();LocalDateTime activationCutoff = LocalDateTime.now().minusDays(lookbackDays);for (FofoStore store : candidates) {int fofoId = store.getId();result.setScanned(result.getScanned() + 1);try {AgendaRuleDecider.Inputs in = new AgendaRuleDecider.Inputs();in.fofoId = fofoId;in.revival = store.getActivationType() == ActivationType.REVIVAL;in.storeActivatedRecently = store.getActiveTimeStamp() != null&& store.getActiveTimeStamp().isAfter(activationCutoff);in.minInvestment = store.getMinimumInvestment();if (in.minInvestment > 0) {try {PartnerDailyInvestment inv = partnerInvestmentService.getInvestment(fofoId, 0);in.shortPercentage = (double) inv.getShortPercentage();} catch (Exception e) {in.shortPercentage = null; // decider LEAVEs LOW_INVESTMENTLOGGER.warn("Investment eval failed for fofoId={}: {}", fofoId, e.getMessage());}}double[] sales = salesByFofo.get(fofoId);in.baselineAmt = sales != null ? sales[0] : 0;in.currentAmt = sales != null ? sales[1] : 0;in.lastBillDaysAgo = billGapByFofo.get(fofoId);double[] loan = loanByFofo.get(fofoId);in.oldestLoanAgeDays = loan != null ? (int) loan[0] : null;in.pendingLoanAmt = loan != null ? loan[1] : 0;// Per-partner params, EXCEPT the sales time windows: those were// baked into the batched SQL above, so the global values must// win here even if a partner row overrides them.Map<String, Double> salesParams = new HashMap<>(agendaConfigService.getParams(AgendaType.LOW_SALES.name(), fofoId));salesParams.put("windowDays", (double) windowDays);salesParams.put("baselineDays", (double) baselineDays);Map<AgendaType, AgendaRuleDecider.Decision> decisions = AgendaRuleDecider.decide(in,agendaConfigService.getParams(AgendaType.LOW_INVESTMENT.name(), fofoId),salesParams,agendaConfigService.getParams(AgendaType.LOW_PURCHASE.name(), fofoId),agendaConfigService.getParams(AgendaType.CREDIT_DUES.name(), fofoId));int[] counts = agendaAutoRuleApplier.applyForStore(fofoId, decisions);result.addOpened(counts[0]);result.addClosed(counts[1]);} catch (Exception e) {result.addError("fofoId " + fofoId + ": " + e.getMessage());LOGGER.error("Agenda sync failed for fofoId={}", fofoId, e);}}} catch (Exception e) {result.addError("sync aborted: " + e.getMessage());LOGGER.error("Agenda sync aborted", e);}LOGGER.info("PJP agenda sync done: {}", result);return result;}// fofo_id -> [baselineAmt, currentAmt]private Map<Integer, double[]> gatherSales(int windowDays, int lookbackDays) {String sql = "SELECT fofo_id, " +" SUM(CASE WHEN create_timestamp < DATE_SUB(NOW(), INTERVAL :windowDays DAY) THEN total_amount ELSE 0 END), " +" SUM(CASE WHEN create_timestamp >= DATE_SUB(NOW(), INTERVAL :windowDays DAY) THEN total_amount ELSE 0 END) " +"FROM fofo.fofo_order " +"WHERE cancelled_timestamp IS NULL " +" AND create_timestamp >= DATE_SUB(NOW(), INTERVAL :lookbackDays DAY) " +"GROUP BY fofo_id";NativeQuery<?> q = sessionFactory.getCurrentSession().createNativeQuery(sql);q.setParameter("windowDays", windowDays);q.setParameter("lookbackDays", lookbackDays);Map<Integer, double[]> out = new HashMap<>();for (Object rowObj : q.getResultList()) {Object[] r = (Object[]) rowObj;out.put(asInt(r[0]), new double[]{asDouble(r[1]), asDouble(r[2])});}return out;}// fofo_id -> days since last billed purchase (absent = never billed)private Map<Integer, Integer> gatherPurchaseGaps() {String sql = "SELECT t.customer_id, DATEDIFF(NOW(), MAX(o.billing_timestamp)) " +"FROM transaction.`order` o " +"JOIN transaction.transaction t ON t.id = o.transaction_id " +"WHERE o.billing_timestamp IS NOT NULL " +"GROUP BY t.customer_id";NativeQuery<?> q = sessionFactory.getCurrentSession().createNativeQuery(sql);Map<Integer, Integer> out = new HashMap<>();for (Object rowObj : q.getResultList()) {Object[] r = (Object[]) rowObj;out.put(asInt(r[0]), asInt(r[1]));}return out;}// fofo_id -> [oldest open-loan age in days from created_on, total pending]private Map<Integer, double[]> gatherLoanAges() {String sql = "SELECT l.fofo_id, MAX(DATEDIFF(NOW(), l.created_on)), SUM(l.pending_amount) " +"FROM transaction.loan l " +"WHERE l.settled_on IS NULL AND l.pending_amount > 1 " +"GROUP BY l.fofo_id";NativeQuery<?> q = sessionFactory.getCurrentSession().createNativeQuery(sql);Map<Integer, double[]> out = new HashMap<>();for (Object rowObj : q.getResultList()) {Object[] r = (Object[]) rowObj;out.put(asInt(r[0]), new double[]{asDouble(r[1]), asDouble(r[2])});}return out;}private static int asInt(Object o) {return o == null ? 0 : ((Number) o).intValue();}private static double asDouble(Object o) {return o == null ? 0 : ((Number) o).doubleValue();}private static double doubleOr(Double v, double fallback) {return v != null ? v : fallback;}}