| Line 813... |
Line 813... |
| 813 |
Map<Integer, Long> outOfSequenceCountByAuthId = outOfSequenceLogs.stream()
|
813 |
Map<Integer, Long> outOfSequenceCountByAuthId = outOfSequenceLogs.stream()
|
| 814 |
.collect(Collectors.groupingBy(RbmCallSequenceLog::getAuthId,
|
814 |
.collect(Collectors.groupingBy(RbmCallSequenceLog::getAuthId,
|
| 815 |
Collectors.mapping(RbmCallSequenceLog::getFofoId, Collectors.collectingAndThen(Collectors.toSet(), s -> (long) s.size()))));
|
815 |
Collectors.mapping(RbmCallSequenceLog::getFofoId, Collectors.collectingAndThen(Collectors.toSet(), s -> (long) s.size()))));
|
| 816 |
LOGGER.info("RBM Call Target - Out of Sequence fetch: {}ms", System.currentTimeMillis() - start);
|
816 |
LOGGER.info("RBM Call Target - Out of Sequence fetch: {}ms", System.currentTimeMillis() - start);
|
| 817 |
|
817 |
|
| - |
|
818 |
// BATCH FETCH: All call logs for all RBMs (L1 + L2 + L3) in a single query.
|
| - |
|
819 |
// Replaces the previous N+1 pattern where getCallStats() was called per RBM.
|
| - |
|
820 |
start = System.currentTimeMillis();
|
| - |
|
821 |
List<AgentCallLog> allCallLogs = agentCallLogRepository.findByAuthIdsAndDate(rbmPositionsAuthIds, queryDate);
|
| - |
|
822 |
Map<Long, List<AgentCallLog>> callLogsByAuthId = allCallLogs.stream()
|
| - |
|
823 |
.collect(Collectors.groupingBy(AgentCallLog::getAuthId));
|
| - |
|
824 |
LOGGER.info("RBM Call Target - Call logs batch fetch: {}ms ({} logs across {} RBMs)",
|
| - |
|
825 |
System.currentTimeMillis() - start, allCallLogs.size(), callLogsByAuthId.size());
|
| - |
|
826 |
|
| - |
|
827 |
// BATCH FETCH: Build a single mobile -> fofoId map from all unique customer numbers across all call logs.
|
| - |
|
828 |
// Replaces the previous N+1 pattern where findFofoIdByMobile() was called per call log entry.
|
| - |
|
829 |
start = System.currentTimeMillis();
|
| - |
|
830 |
Set<String> allNormalizedMobiles = new HashSet<>();
|
| - |
|
831 |
for (AgentCallLog callLog : allCallLogs) {
|
| - |
|
832 |
String customerNumber = callLog.getCustomerNumber();
|
| - |
|
833 |
if (customerNumber != null) {
|
| - |
|
834 |
String normalized = customerNumber.startsWith("+91") ? customerNumber.substring(3) : customerNumber;
|
| - |
|
835 |
allNormalizedMobiles.add(normalized);
|
| - |
|
836 |
}
|
| - |
|
837 |
}
|
| - |
|
838 |
Map<String, Integer> mobileToFofoIdMap = buildMobileToFofoIdMap(allNormalizedMobiles);
|
| - |
|
839 |
LOGGER.info("RBM Call Target - Mobile→FofoId batch fetch: {}ms ({} unique mobiles, {} mapped)",
|
| - |
|
840 |
System.currentTimeMillis() - start, allNormalizedMobiles.size(), mobileToFofoIdMap.size());
|
| - |
|
841 |
|
| 818 |
// Identify users who are both L1 and L2 — they will be shown only as L2
|
842 |
// Identify users who are both L1 and L2 — they will be shown only as L2
|
| 819 |
Set<Integer> l2AuthIdSet = new HashSet<>(l2AuthIds);
|
843 |
Set<Integer> l2AuthIdSet = new HashSet<>(l2AuthIds);
|
| 820 |
|
844 |
|
| 821 |
// Process L1 RBMs (skip users who are also L2 — their data will be merged into L2 model)
|
845 |
// Process L1 RBMs (skip users who are also L2 — their data will be merged into L2 model)
|
| 822 |
for (int rbmAuthId : l1AuthIds) {
|
846 |
for (int rbmAuthId : l1AuthIds) {
|
| Line 943... |
Line 967... |
| 943 |
todayTargetPartners.addAll(planTodayPartners);
|
967 |
todayTargetPartners.addAll(planTodayPartners);
|
| 944 |
todayTargetPartners.addAll(carryForwardPartners);
|
968 |
todayTargetPartners.addAll(carryForwardPartners);
|
| 945 |
todayTargetPartners.addAll(zeroBillingPartners);
|
969 |
todayTargetPartners.addAll(zeroBillingPartners);
|
| 946 |
todayTargetPartners.addAll(untouchedPartners);
|
970 |
todayTargetPartners.addAll(untouchedPartners);
|
| 947 |
|
971 |
|
| 948 |
// Value Achieved = All distinct partners called today (from call logs)
|
972 |
// Value Achieved = All distinct partners called today (from pre-fetched call logs)
|
| 949 |
long[] callStats = getCallStats(rbmAuthId, queryDate);
|
973 |
long[] callStats = getCallStatsFromLogs(callLogsByAuthId.get((long) rbmAuthId), mobileToFofoIdMap);
|
| 950 |
targetModel.setValueTargetAchieved(callStats[0]);
|
974 |
targetModel.setValueTargetAchieved(callStats[0]);
|
| 951 |
targetModel.setTotalRecordingCalls(callStats[1]);
|
975 |
targetModel.setTotalRecordingCalls(callStats[1]);
|
| 952 |
targetModel.setUniqueRecordingCalls(callStats[2]);
|
976 |
targetModel.setUniqueRecordingCalls(callStats[2]);
|
| 953 |
|
977 |
|
| 954 |
// Keep todayRemarks for movedToFuture calculation
|
978 |
// Keep todayRemarks for movedToFuture calculation
|
| Line 1098... |
Line 1122... |
| 1098 |
} else {
|
1122 |
} else {
|
| 1099 |
// Pure L2 (not also L1) — target is only L2 escalation
|
1123 |
// Pure L2 (not also L1) — target is only L2 escalation
|
| 1100 |
l2Model.setTodayTargetOfCall(l2TargetFofoIds.size());
|
1124 |
l2Model.setTodayTargetOfCall(l2TargetFofoIds.size());
|
| 1101 |
}
|
1125 |
}
|
| 1102 |
|
1126 |
|
| 1103 |
// Value Achieved = All distinct partners called today (from call logs)
|
1127 |
// Value Achieved = All distinct partners called today (from pre-fetched call logs)
|
| 1104 |
long[] l2CallStats = getCallStats(l2AuthId, queryDate);
|
1128 |
long[] l2CallStats = getCallStatsFromLogs(callLogsByAuthId.get((long) l2AuthId), mobileToFofoIdMap);
|
| 1105 |
l2Model.setValueTargetAchieved(l2CallStats[0]);
|
1129 |
l2Model.setValueTargetAchieved(l2CallStats[0]);
|
| 1106 |
l2Model.setTotalRecordingCalls(l2CallStats[1]);
|
1130 |
l2Model.setTotalRecordingCalls(l2CallStats[1]);
|
| 1107 |
l2Model.setUniqueRecordingCalls(l2CallStats[2]);
|
1131 |
l2Model.setUniqueRecordingCalls(l2CallStats[2]);
|
| 1108 |
|
1132 |
|
| 1109 |
l2Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l2AuthId, 0L));
|
1133 |
l2Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l2AuthId, 0L));
|
| Line 1132... |
Line 1156... |
| 1132 |
l3Model.setPartnerCount(l3AssignedFofoIds.size());
|
1156 |
l3Model.setPartnerCount(l3AssignedFofoIds.size());
|
| 1133 |
|
1157 |
|
| 1134 |
// L3 Target = partners with RBM_L3_ESCALATION as latest remark
|
1158 |
// L3 Target = partners with RBM_L3_ESCALATION as latest remark
|
| 1135 |
l3Model.setTodayTargetOfCall(l3TargetFofoIds.size());
|
1159 |
l3Model.setTodayTargetOfCall(l3TargetFofoIds.size());
|
| 1136 |
|
1160 |
|
| 1137 |
// Value Achieved = All distinct partners called today (from call logs)
|
1161 |
// Value Achieved = All distinct partners called today (from pre-fetched call logs)
|
| 1138 |
long[] l3CallStats = getCallStats(l3AuthId, queryDate);
|
1162 |
long[] l3CallStats = getCallStatsFromLogs(callLogsByAuthId.get((long) l3AuthId), mobileToFofoIdMap);
|
| 1139 |
l3Model.setValueTargetAchieved(l3CallStats[0]);
|
1163 |
l3Model.setValueTargetAchieved(l3CallStats[0]);
|
| 1140 |
l3Model.setTotalRecordingCalls(l3CallStats[1]);
|
1164 |
l3Model.setTotalRecordingCalls(l3CallStats[1]);
|
| 1141 |
l3Model.setUniqueRecordingCalls(l3CallStats[2]);
|
1165 |
l3Model.setUniqueRecordingCalls(l3CallStats[2]);
|
| 1142 |
|
1166 |
|
| 1143 |
l3Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l3AuthId, 0L));
|
1167 |
l3Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l3AuthId, 0L));
|
| Line 1721... |
Line 1745... |
| 1721 |
public long getCalledCountFromCallLogs(long authId, LocalDate date) {
|
1745 |
public long getCalledCountFromCallLogs(long authId, LocalDate date) {
|
| 1722 |
return getCallStats(authId, date)[0];
|
1746 |
return getCallStats(authId, date)[0];
|
| 1723 |
}
|
1747 |
}
|
| 1724 |
|
1748 |
|
| 1725 |
/**
|
1749 |
/**
|
| - |
|
1750 |
* Batch-builds a mobile (normalized, +91 stripped) -> fofoId mapping for all the given mobiles
|
| - |
|
1751 |
* in just two queries (retailer_contact, then address fallback for unmapped numbers).
|
| - |
|
1752 |
* Replaces the previous per-call-log findFofoIdByMobile() N+1 lookups.
|
| - |
|
1753 |
*/
|
| - |
|
1754 |
private Map<String, Integer> buildMobileToFofoIdMap(Set<String> normalizedMobiles) {
|
| - |
|
1755 |
Map<String, Integer> result = new HashMap<>();
|
| - |
|
1756 |
if (normalizedMobiles == null || normalizedMobiles.isEmpty()) {
|
| - |
|
1757 |
return result;
|
| - |
|
1758 |
}
|
| - |
|
1759 |
List<String> mobilesList = new ArrayList<>(normalizedMobiles);
|
| - |
|
1760 |
|
| - |
|
1761 |
// First pass: retailer_contact
|
| - |
|
1762 |
List<RetailerContact> contacts = retailerContactRepository.selectByMobiles(mobilesList);
|
| - |
|
1763 |
for (RetailerContact rc : contacts) {
|
| - |
|
1764 |
// Keep the first fofoId we see per mobile (matches old single-mobile behavior of get(0))
|
| - |
|
1765 |
result.putIfAbsent(rc.getMobile(), rc.getFofoId());
|
| - |
|
1766 |
}
|
| - |
|
1767 |
|
| - |
|
1768 |
// Second pass: address fallback for mobiles not yet mapped
|
| - |
|
1769 |
List<String> unmapped = mobilesList.stream()
|
| - |
|
1770 |
.filter(m -> !result.containsKey(m))
|
| - |
|
1771 |
.collect(Collectors.toList());
|
| - |
|
1772 |
if (!unmapped.isEmpty()) {
|
| - |
|
1773 |
List<Address> addresses = addressRepository.selectAllByPhoneNumbers(unmapped);
|
| - |
|
1774 |
for (Address addr : addresses) {
|
| - |
|
1775 |
result.putIfAbsent(addr.getPhoneNumber(), addr.getRetaierId());
|
| - |
|
1776 |
}
|
| - |
|
1777 |
}
|
| - |
|
1778 |
return result;
|
| - |
|
1779 |
}
|
| - |
|
1780 |
|
| - |
|
1781 |
/**
|
| - |
|
1782 |
* In-memory variant of getCallStats() that uses pre-fetched call logs and mobile→fofoId mapping.
|
| - |
|
1783 |
* Used by the batch-fetched RBM Call Target loop to avoid N+1 query patterns.
|
| - |
|
1784 |
*/
|
| - |
|
1785 |
private long[] getCallStatsFromLogs(List<AgentCallLog> callLogs, Map<String, Integer> mobileToFofoIdMap) {
|
| - |
|
1786 |
if (callLogs == null || callLogs.isEmpty()) {
|
| - |
|
1787 |
return new long[]{0, 0, 0};
|
| - |
|
1788 |
}
|
| - |
|
1789 |
|
| - |
|
1790 |
Set<Integer> calledFofoIds = new HashSet<>();
|
| - |
|
1791 |
Set<String> calledNumbersWithoutFofoId = new HashSet<>();
|
| - |
|
1792 |
long totalRecordingCalls = 0;
|
| - |
|
1793 |
Set<String> uniqueRecordingNumbers = new HashSet<>();
|
| - |
|
1794 |
|
| - |
|
1795 |
for (AgentCallLog callLog : callLogs) {
|
| - |
|
1796 |
String customerNumber = callLog.getCustomerNumber();
|
| - |
|
1797 |
if (customerNumber != null) {
|
| - |
|
1798 |
String normalized = customerNumber.startsWith("+91") ? customerNumber.substring(3) : customerNumber;
|
| - |
|
1799 |
Integer fofoId = mobileToFofoIdMap.get(normalized);
|
| - |
|
1800 |
if (fofoId != null) {
|
| - |
|
1801 |
calledFofoIds.add(fofoId);
|
| - |
|
1802 |
} else {
|
| - |
|
1803 |
calledNumbersWithoutFofoId.add(normalized);
|
| - |
|
1804 |
}
|
| - |
|
1805 |
|
| - |
|
1806 |
if (callLog.getRecordingUrl() != null && !callLog.getRecordingUrl().isEmpty()
|
| - |
|
1807 |
&& !"None".equalsIgnoreCase(callLog.getRecordingUrl())) {
|
| - |
|
1808 |
totalRecordingCalls++;
|
| - |
|
1809 |
uniqueRecordingNumbers.add(normalized);
|
| - |
|
1810 |
}
|
| - |
|
1811 |
}
|
| - |
|
1812 |
}
|
| - |
|
1813 |
|
| - |
|
1814 |
long calledCount = calledFofoIds.size() + calledNumbersWithoutFofoId.size();
|
| - |
|
1815 |
return new long[]{calledCount, totalRecordingCalls, uniqueRecordingNumbers.size()};
|
| - |
|
1816 |
}
|
| - |
|
1817 |
|
| - |
|
1818 |
/**
|
| 1726 |
* Returns call stats: [0] = called count, [1] = total recording calls, [2] = unique recording calls
|
1819 |
* Returns call stats: [0] = called count, [1] = total recording calls, [2] = unique recording calls
|
| 1727 |
*/
|
1820 |
*/
|
| 1728 |
public long[] getCallStats(long authId, LocalDate date) {
|
1821 |
public long[] getCallStats(long authId, LocalDate date) {
|
| 1729 |
List<AgentCallLog> callLogs = agentCallLogRepository.findByAuthIdAndDate(authId, date);
|
1822 |
List<AgentCallLog> callLogs = agentCallLogRepository.findByAuthIdAndDate(authId, date);
|
| 1730 |
|
1823 |
|