Rev 36017 | Blame | Compare with Previous | Last modification | View Log | RSS feed
package com.spice.profitmandi.web.service;import com.spice.profitmandi.dao.entity.cs.RbmBreakLog;import com.spice.profitmandi.dao.repository.cs.RbmBreakLogRepository;import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.PlatformTransactionManager;import org.springframework.transaction.TransactionStatus;import org.springframework.transaction.support.TransactionCallbackWithoutResult;import org.springframework.transaction.support.TransactionTemplate;import javax.annotation.PostConstruct;import java.time.LocalDate;import java.time.LocalDateTime;import java.util.ArrayList;import java.util.List;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;/*** DEPRECATED: This service is no longer actively used.* <p>* Live agent status is now retrieved from Redis via KnowlarityCallMonitorService in profitmandi-dao.* Break logs are now persisted by KnowlarityCallMonitorService via WebSocket in profitmandi-cron.* <p>* Use the following for live status:* - REST API: /api/agent-live-status endpoints in profitmandi-web (AgentLiveStatusController)* - DAO Service: KnowlarityCallMonitorService.getAgentStatusesFromRedis()* <p>* This service is kept for backward compatibility but should not be used for new features.*/@Deprecated@Servicepublic class AgentLiveStatusService {private static final Logger LOGGER = LogManager.getLogger(AgentLiveStatusService.class);// In-memory map to store agent status by authIdprivate final Map<Integer, AgentStatus> agentStatusMap = new ConcurrentHashMap<>();// Caller ID (Knowlarity SR Number) to Auth ID mapping (loaded from sip_master)private final Map<String, Integer> callerIdToAuthIdMap = new ConcurrentHashMap<>();// SR Number to Auth ID mapping (loaded from sip_master)private final Map<String, Integer> srNumberToAuthIdMap = new ConcurrentHashMap<>();// Agent name to Auth ID mapping (fallback)private final Map<String, Integer> agentNameToAuthIdMap = new ConcurrentHashMap<>();@Autowiredprivate SessionFactory sessionFactory;@Autowiredprivate RbmBreakLogRepository rbmBreakLogRepository;@Autowiredprivate PlatformTransactionManager transactionManager;private TransactionTemplate transactionTemplate;private String lastError = null;private int lastQueryCount = 0;/*** Load mapping from sip_master table on startup* caller_id in sip_master = SR Number on Knowlarity dashboard* sip_master has auth_id directly*/@PostConstructpublic void loadMappingFromDatabase() {this.transactionTemplate = new TransactionTemplate(transactionManager);try {Session session = sessionFactory.openSession();// Query to get caller_id, auth_user.id, first_name from sip_master// Join with auth_user to get correct auth_id used by rbm_call_target pageString sql = "SELECT sm.caller_id, au.id, sm.first_name " +"FROM auth.sip_master sm " +"JOIN auth.auth_user au ON sm.email_id = au.email_id " +"WHERE au.id IS NOT NULL";List<Object[]> results = session.createNativeQuery(sql).getResultList();lastQueryCount = results.size();for (Object[] row : results) {String callerId = row[0] != null ? row[0].toString().trim() : "";int authId = row[1] != null ? ((Number) row[1]).intValue() : 0;String firstName = row[2] != null ? row[2].toString().trim() : "";if (authId > 0) {// Map by caller_id if availableif (!callerId.isEmpty()) {callerIdToAuthIdMap.put(callerId, authId);}// Map by first_name for name-based matchingif (!firstName.isEmpty()) {agentNameToAuthIdMap.put(firstName.toLowerCase(), authId);System.out.println("Mapping: '" + firstName + "' -> authId=" + authId + " (caller_id=" + callerId + ")");}}}session.close();System.out.println("Loaded " + callerIdToAuthIdMap.size() + " caller_id -> auth_id mappings from sip_master");System.out.println("Loaded " + agentNameToAuthIdMap.size() + " name -> auth_id mappings");} catch (Exception e) {lastError = e.getClass().getSimpleName() + ": " + e.getMessage();System.err.println("Error loading sip_master mapping: " + e.getMessage());e.printStackTrace();}}public String getLastError() {return lastError;}public int getLastQueryCount() {return lastQueryCount;}/*** Reload mapping from database (can be called via API)*/public void reloadMapping() {callerIdToAuthIdMap.clear();srNumberToAuthIdMap.clear();agentNameToAuthIdMap.clear();lastError = null;lastQueryCount = 0;loadMappingFromDatabase();}public Integer getAuthIdByCallerId(String callerId) {return callerIdToAuthIdMap.get(callerId);}public void setSrNumberMapping(String srNumber, int authId) {srNumberToAuthIdMap.put(srNumber, authId);}public void setAgentNameMapping(String agentName, int authId) {agentNameToAuthIdMap.put(agentName.toLowerCase(), authId);}public Integer getAuthIdBySrNumber(String srNumber) {return srNumberToAuthIdMap.get(srNumber);}public Integer getAuthIdByAgentName(String agentName) {// Try exact matchInteger authId = agentNameToAuthIdMap.get(agentName.toLowerCase());if (authId != null) return authId;// Try partial matchfor (Map.Entry<String, Integer> entry : agentNameToAuthIdMap.entrySet()) {if (agentName.toLowerCase().contains(entry.getKey()) || entry.getKey().contains(agentName.toLowerCase())) {return entry.getValue();}}return null;}public Map<String, Integer> getSrNumberMapping() {return srNumberToAuthIdMap;}public Map<String, Integer> getAgentNameMapping() {return agentNameToAuthIdMap;}public Map<String, Integer> getCallerIdMapping() {return callerIdToAuthIdMap;}public void clearMappings() {srNumberToAuthIdMap.clear();agentNameToAuthIdMap.clear();}public void updateStatus(int authId, String srNumber, String agentName, String agentStatus, String timeInStatus, String customerNumber) {// DISABLED: Break log persistence is now handled by profitmandi-cron via WebSocket// This method only updates in-memory status map (also deprecated - use Redis via KnowlarityCallMonitorService)// Update current status (in-memory only, no persistence)AgentStatus status = new AgentStatus(authId, srNumber, agentName, agentStatus, timeInStatus, customerNumber);agentStatusMap.put(authId, status);}/*** Check if status should be tracked (Only Available and Break)*/private boolean isTrackableStatus(String status) {if (status == null || status.isEmpty()) return false;String lower = status.toLowerCase();return lower.contains("available") || lower.contains("break");}/*** Parse timeInStatus string (e.g., "00:05:30" or "5m 30s") to seconds*/private int parseTimeInStatusToSeconds(String timeInStatus) {if (timeInStatus == null || timeInStatus.isEmpty()) return 0;try {// Format: HH:mm:ss or H:mm:ssif (timeInStatus.contains(":")) {String[] parts = timeInStatus.split(":");if (parts.length == 3) {int hours = Integer.parseInt(parts[0].trim());int minutes = Integer.parseInt(parts[1].trim());int seconds = Integer.parseInt(parts[2].trim());return hours * 3600 + minutes * 60 + seconds;} else if (parts.length == 2) {int minutes = Integer.parseInt(parts[0].trim());int seconds = Integer.parseInt(parts[1].trim());return minutes * 60 + seconds;}}// Format: "5m 30s" or "1h 5m 30s"int totalSeconds = 0;String lower = timeInStatus.toLowerCase();if (lower.contains("h")) {String hourPart = lower.split("h")[0].trim();totalSeconds += Integer.parseInt(hourPart.replaceAll("[^0-9]", "")) * 3600;}if (lower.contains("m")) {String minPart = lower.contains("h") ? lower.split("h")[1].split("m")[0] : lower.split("m")[0];totalSeconds += Integer.parseInt(minPart.trim().replaceAll("[^0-9]", "")) * 60;}if (lower.contains("s") && !lower.contains("h") && !lower.contains("m")) {totalSeconds += Integer.parseInt(lower.replaceAll("[^0-9]", ""));} else if (lower.contains("s")) {String secPart = lower.split("m")[1].split("s")[0];totalSeconds += Integer.parseInt(secPart.trim().replaceAll("[^0-9]", ""));}return totalSeconds;} catch (Exception e) {LOGGER.warn("Could not parse timeInStatus: {}", timeInStatus);return 0;}}/*** Save status duration to RbmBreakLog* Uses UPSERT logic: If record exists within time window, update duration; otherwise create new** @param statusStartTime - the time when agent ENTERED this status (start time)*/private void saveStatusDuration(int authId, String agentName, String srNumber, String status,String durationDisplay, int durationSeconds, LocalDateTime statusStartTime) {try {transactionTemplate.execute(new TransactionCallbackWithoutResult() {@Overrideprotected void doInTransactionWithoutResult(TransactionStatus txStatus) {LocalDate logDate = statusStartTime.toLocalDate();// Check if existing record within 5 minute windowRbmBreakLog existing = rbmBreakLogRepository.findByAuthIdStatusAndLogTimeWindow(authId, status, statusStartTime, 5);if (existing != null) {// Update existing record with latest durationLOGGER.info("Updating existing break log id={}: agent={}, status={}, duration {}s -> {}s",existing.getId(), agentName, status, existing.getDurationSeconds(), durationSeconds);rbmBreakLogRepository.updateDuration(existing.getId(), durationSeconds, durationDisplay);} else {// Create new recordLOGGER.info("Creating new break log: agent={}, status={}, startTime={}, duration={}s ({})",agentName, status, statusStartTime, durationSeconds, durationDisplay);RbmBreakLog breakLog = new RbmBreakLog(authId,agentName,srNumber,status,statusStartTime,durationSeconds,durationDisplay,logDate);rbmBreakLogRepository.persist(breakLog);}}});} catch (Exception e) {LOGGER.error("Error saving status duration for {}: {}", agentName, e.getMessage());}}public AgentStatus getStatusByAuthId(int authId) {return agentStatusMap.get(authId);}public Map<Integer, AgentStatus> getAllStatusMap() {return agentStatusMap;}public List<AgentStatus> getAllStatuses() {return new ArrayList<>(agentStatusMap.values());}public void clearAll() {agentStatusMap.clear();}// Inner class to hold agent statuspublic static class AgentStatus {private int authId;private String srNumber;private String agentName;private String agentStatus;private String timeInStatus;private String customerNumber;private LocalDateTime lastUpdated;public AgentStatus() {}public AgentStatus(int authId, String srNumber, String agentName, String agentStatus, String timeInStatus, String customerNumber) {this.authId = authId;this.srNumber = srNumber;this.agentName = agentName;this.agentStatus = agentStatus;this.timeInStatus = timeInStatus;this.customerNumber = customerNumber;this.lastUpdated = LocalDateTime.now();}public int getAuthId() {return authId;}public void setAuthId(int authId) {this.authId = authId;}public String getSrNumber() {return srNumber;}public void setSrNumber(String srNumber) {this.srNumber = srNumber;}public String getAgentName() {return agentName;}public void setAgentName(String agentName) {this.agentName = agentName;}public String getAgentStatus() {return agentStatus;}public void setAgentStatus(String agentStatus) {this.agentStatus = agentStatus;}public String getTimeInStatus() {return timeInStatus;}public void setTimeInStatus(String timeInStatus) {this.timeInStatus = timeInStatus;}public String getCustomerNumber() {return customerNumber;}public void setCustomerNumber(String customerNumber) {this.customerNumber = customerNumber;}public LocalDateTime getLastUpdated() {return lastUpdated;}public void setLastUpdated(LocalDateTime lastUpdated) {this.lastUpdated = lastUpdated;}}}