Subversion Repositories SmartDukaan

Rev

Rev 36017 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
35867 ranu 1
package com.spice.profitmandi.web.service;
2
 
36000 ranu 3
import com.spice.profitmandi.dao.entity.cs.RbmBreakLog;
4
import com.spice.profitmandi.dao.repository.cs.RbmBreakLogRepository;
5
import org.apache.logging.log4j.LogManager;
6
import org.apache.logging.log4j.Logger;
35879 ranu 7
import org.hibernate.Session;
8
import org.hibernate.SessionFactory;
9
import org.springframework.beans.factory.annotation.Autowired;
35867 ranu 10
import org.springframework.stereotype.Service;
36000 ranu 11
import org.springframework.transaction.PlatformTransactionManager;
12
import org.springframework.transaction.TransactionStatus;
13
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
14
import org.springframework.transaction.support.TransactionTemplate;
35867 ranu 15
 
35879 ranu 16
import javax.annotation.PostConstruct;
36000 ranu 17
import java.time.LocalDate;
35879 ranu 18
import java.time.LocalDateTime;
19
import java.util.ArrayList;
20
import java.util.List;
21
import java.util.Map;
22
import java.util.concurrent.ConcurrentHashMap;
23
 
36057 ranu 24
/**
25
 * DEPRECATED: This service is no longer actively used.
26
 * <p>
27
 * Live agent status is now retrieved from Redis via KnowlarityCallMonitorService in profitmandi-dao.
28
 * Break logs are now persisted by KnowlarityCallMonitorService via WebSocket in profitmandi-cron.
29
 * <p>
30
 * Use the following for live status:
31
 * - REST API: /api/agent-live-status endpoints in profitmandi-web (AgentLiveStatusController)
32
 * - DAO Service: KnowlarityCallMonitorService.getAgentStatusesFromRedis()
33
 * <p>
34
 * This service is kept for backward compatibility but should not be used for new features.
35
 */
36
@Deprecated
35867 ranu 37
@Service
38
public class AgentLiveStatusService {
35879 ranu 39
 
36000 ranu 40
    private static final Logger LOGGER = LogManager.getLogger(AgentLiveStatusService.class);
41
 
35867 ranu 42
    // In-memory map to store agent status by authId
43
    private final Map<Integer, AgentStatus> agentStatusMap = new ConcurrentHashMap<>();
44
    // Caller ID (Knowlarity SR Number) to Auth ID mapping (loaded from sip_master)
45
    private final Map<String, Integer> callerIdToAuthIdMap = new ConcurrentHashMap<>();
46
    // SR Number to Auth ID mapping (loaded from sip_master)
47
    private final Map<String, Integer> srNumberToAuthIdMap = new ConcurrentHashMap<>();
48
    // Agent name to Auth ID mapping (fallback)
49
    private final Map<String, Integer> agentNameToAuthIdMap = new ConcurrentHashMap<>();
36000 ranu 50
 
35867 ranu 51
    @Autowired
52
    private SessionFactory sessionFactory;
36000 ranu 53
 
54
    @Autowired
55
    private RbmBreakLogRepository rbmBreakLogRepository;
56
 
57
    @Autowired
58
    private PlatformTransactionManager transactionManager;
59
 
60
    private TransactionTemplate transactionTemplate;
61
 
35867 ranu 62
    private String lastError = null;
63
    private int lastQueryCount = 0;
64
 
35879 ranu 65
    /**
35867 ranu 66
     * Load mapping from sip_master table on startup
67
     * caller_id in sip_master = SR Number on Knowlarity dashboard
68
     * sip_master has auth_id directly
35879 ranu 69
     */
35867 ranu 70
    @PostConstruct
71
    public void loadMappingFromDatabase() {
36000 ranu 72
        this.transactionTemplate = new TransactionTemplate(transactionManager);
35867 ranu 73
        try {
74
            Session session = sessionFactory.openSession();
75
 
76
            // Query to get caller_id, auth_user.id, first_name from sip_master
77
            // Join with auth_user to get correct auth_id used by rbm_call_target page
78
            String sql = "SELECT sm.caller_id, au.id, sm.first_name " +
79
                    "FROM auth.sip_master sm " +
80
                    "JOIN auth.auth_user au ON sm.email_id = au.email_id " +
81
                    "WHERE au.id IS NOT NULL";
82
 
83
            List<Object[]> results = session.createNativeQuery(sql).getResultList();
84
            lastQueryCount = results.size();
85
 
86
            for (Object[] row : results) {
87
                String callerId = row[0] != null ? row[0].toString().trim() : "";
88
                int authId = row[1] != null ? ((Number) row[1]).intValue() : 0;
89
                String firstName = row[2] != null ? row[2].toString().trim() : "";
90
 
91
                if (authId > 0) {
92
                    // Map by caller_id if available
93
                    if (!callerId.isEmpty()) {
94
                        callerIdToAuthIdMap.put(callerId, authId);
95
                    }
96
                    // Map by first_name for name-based matching
97
                    if (!firstName.isEmpty()) {
98
                        agentNameToAuthIdMap.put(firstName.toLowerCase(), authId);
99
                        System.out.println("Mapping: '" + firstName + "' -> authId=" + authId + " (caller_id=" + callerId + ")");
100
                    }
101
                }
102
            }
103
 
104
            session.close();
105
            System.out.println("Loaded " + callerIdToAuthIdMap.size() + " caller_id -> auth_id mappings from sip_master");
106
            System.out.println("Loaded " + agentNameToAuthIdMap.size() + " name -> auth_id mappings");
107
 
108
        } catch (Exception e) {
109
            lastError = e.getClass().getSimpleName() + ": " + e.getMessage();
110
            System.err.println("Error loading sip_master mapping: " + e.getMessage());
111
            e.printStackTrace();
112
        }
113
    }
114
 
115
    public String getLastError() {
116
        return lastError;
117
    }
118
 
119
    public int getLastQueryCount() {
120
        return lastQueryCount;
121
    }
122
 
35879 ranu 123
    /**
35867 ranu 124
     * Reload mapping from database (can be called via API)
35879 ranu 125
     */
35867 ranu 126
    public void reloadMapping() {
127
        callerIdToAuthIdMap.clear();
128
        srNumberToAuthIdMap.clear();
129
        agentNameToAuthIdMap.clear();
130
        lastError = null;
131
        lastQueryCount = 0;
132
        loadMappingFromDatabase();
133
    }
134
 
135
    public Integer getAuthIdByCallerId(String callerId) {
136
        return callerIdToAuthIdMap.get(callerId);
137
    }
138
 
139
    public void setSrNumberMapping(String srNumber, int authId) {
140
        srNumberToAuthIdMap.put(srNumber, authId);
141
    }
142
 
143
    public void setAgentNameMapping(String agentName, int authId) {
144
        agentNameToAuthIdMap.put(agentName.toLowerCase(), authId);
145
    }
146
 
147
    public Integer getAuthIdBySrNumber(String srNumber) {
148
        return srNumberToAuthIdMap.get(srNumber);
149
    }
150
 
151
    public Integer getAuthIdByAgentName(String agentName) {
152
        // Try exact match
153
        Integer authId = agentNameToAuthIdMap.get(agentName.toLowerCase());
154
        if (authId != null) return authId;
155
 
156
        // Try partial match
157
        for (Map.Entry<String, Integer> entry : agentNameToAuthIdMap.entrySet()) {
158
            if (agentName.toLowerCase().contains(entry.getKey()) || entry.getKey().contains(agentName.toLowerCase())) {
159
                return entry.getValue();
160
            }
161
        }
162
        return null;
163
    }
164
 
165
    public Map<String, Integer> getSrNumberMapping() {
166
        return srNumberToAuthIdMap;
167
    }
168
 
169
    public Map<String, Integer> getAgentNameMapping() {
170
        return agentNameToAuthIdMap;
171
    }
172
 
173
    public Map<String, Integer> getCallerIdMapping() {
174
        return callerIdToAuthIdMap;
175
    }
176
 
177
    public void clearMappings() {
178
        srNumberToAuthIdMap.clear();
179
        agentNameToAuthIdMap.clear();
180
    }
181
 
182
    public void updateStatus(int authId, String srNumber, String agentName, String agentStatus, String timeInStatus, String customerNumber) {
36057 ranu 183
        // DISABLED: Break log persistence is now handled by profitmandi-cron via WebSocket
184
        // This method only updates in-memory status map (also deprecated - use Redis via KnowlarityCallMonitorService)
36000 ranu 185
 
36057 ranu 186
        // Update current status (in-memory only, no persistence)
35867 ranu 187
        AgentStatus status = new AgentStatus(authId, srNumber, agentName, agentStatus, timeInStatus, customerNumber);
188
        agentStatusMap.put(authId, status);
189
    }
190
 
36000 ranu 191
    /**
192
     * Check if status should be tracked (Only Available and Break)
193
     */
194
    private boolean isTrackableStatus(String status) {
195
        if (status == null || status.isEmpty()) return false;
196
        String lower = status.toLowerCase();
197
        return lower.contains("available") || lower.contains("break");
198
    }
199
 
200
    /**
201
     * Parse timeInStatus string (e.g., "00:05:30" or "5m 30s") to seconds
202
     */
203
    private int parseTimeInStatusToSeconds(String timeInStatus) {
204
        if (timeInStatus == null || timeInStatus.isEmpty()) return 0;
205
 
206
        try {
207
            // Format: HH:mm:ss or H:mm:ss
208
            if (timeInStatus.contains(":")) {
209
                String[] parts = timeInStatus.split(":");
210
                if (parts.length == 3) {
211
                    int hours = Integer.parseInt(parts[0].trim());
212
                    int minutes = Integer.parseInt(parts[1].trim());
213
                    int seconds = Integer.parseInt(parts[2].trim());
214
                    return hours * 3600 + minutes * 60 + seconds;
215
                } else if (parts.length == 2) {
216
                    int minutes = Integer.parseInt(parts[0].trim());
217
                    int seconds = Integer.parseInt(parts[1].trim());
218
                    return minutes * 60 + seconds;
219
                }
220
            }
221
 
222
            // Format: "5m 30s" or "1h 5m 30s"
223
            int totalSeconds = 0;
224
            String lower = timeInStatus.toLowerCase();
225
            if (lower.contains("h")) {
226
                String hourPart = lower.split("h")[0].trim();
227
                totalSeconds += Integer.parseInt(hourPart.replaceAll("[^0-9]", "")) * 3600;
228
            }
229
            if (lower.contains("m")) {
230
                String minPart = lower.contains("h") ? lower.split("h")[1].split("m")[0] : lower.split("m")[0];
231
                totalSeconds += Integer.parseInt(minPart.trim().replaceAll("[^0-9]", "")) * 60;
232
            }
233
            if (lower.contains("s") && !lower.contains("h") && !lower.contains("m")) {
234
                totalSeconds += Integer.parseInt(lower.replaceAll("[^0-9]", ""));
235
            } else if (lower.contains("s")) {
236
                String secPart = lower.split("m")[1].split("s")[0];
237
                totalSeconds += Integer.parseInt(secPart.trim().replaceAll("[^0-9]", ""));
238
            }
239
            return totalSeconds;
240
 
241
        } catch (Exception e) {
242
            LOGGER.warn("Could not parse timeInStatus: {}", timeInStatus);
243
            return 0;
244
        }
245
    }
246
 
247
    /**
248
     * Save status duration to RbmBreakLog
36017 ranu 249
     * Uses UPSERT logic: If record exists within time window, update duration; otherwise create new
36000 ranu 250
     *
251
     * @param statusStartTime - the time when agent ENTERED this status (start time)
252
     */
253
    private void saveStatusDuration(int authId, String agentName, String srNumber, String status,
254
                                    String durationDisplay, int durationSeconds, LocalDateTime statusStartTime) {
255
        try {
256
            transactionTemplate.execute(new TransactionCallbackWithoutResult() {
257
                @Override
258
                protected void doInTransactionWithoutResult(TransactionStatus txStatus) {
259
                    LocalDate logDate = statusStartTime.toLocalDate();
260
 
36017 ranu 261
                    // Check if existing record within 5 minute window
262
                    RbmBreakLog existing = rbmBreakLogRepository.findByAuthIdStatusAndLogTimeWindow(
263
                            authId, status, statusStartTime, 5);
36000 ranu 264
 
36017 ranu 265
                    if (existing != null) {
266
                        // Update existing record with latest duration
267
                        LOGGER.info("Updating existing break log id={}: agent={}, status={}, duration {}s -> {}s",
268
                                existing.getId(), agentName, status, existing.getDurationSeconds(), durationSeconds);
269
                        rbmBreakLogRepository.updateDuration(existing.getId(), durationSeconds, durationDisplay);
270
                    } else {
271
                        // Create new record
272
                        LOGGER.info("Creating new break log: agent={}, status={}, startTime={}, duration={}s ({})",
273
                                agentName, status, statusStartTime, durationSeconds, durationDisplay);
274
 
275
                        RbmBreakLog breakLog = new RbmBreakLog(
276
                                authId,
277
                                agentName,
278
                                srNumber,
279
                                status,
280
                                statusStartTime,
281
                                durationSeconds,
282
                                durationDisplay,
283
                                logDate
284
                        );
285
                        rbmBreakLogRepository.persist(breakLog);
286
                    }
36000 ranu 287
                }
288
            });
289
        } catch (Exception e) {
290
            LOGGER.error("Error saving status duration for {}: {}", agentName, e.getMessage());
291
        }
292
    }
293
 
35867 ranu 294
    public AgentStatus getStatusByAuthId(int authId) {
295
        return agentStatusMap.get(authId);
296
    }
297
 
298
    public Map<Integer, AgentStatus> getAllStatusMap() {
299
        return agentStatusMap;
300
    }
301
 
302
    public List<AgentStatus> getAllStatuses() {
303
        return new ArrayList<>(agentStatusMap.values());
304
    }
305
 
306
    public void clearAll() {
307
        agentStatusMap.clear();
308
    }
309
 
310
    // Inner class to hold agent status
311
    public static class AgentStatus {
312
        private int authId;
313
        private String srNumber;
314
        private String agentName;
315
        private String agentStatus;
316
        private String timeInStatus;
317
        private String customerNumber;
318
        private LocalDateTime lastUpdated;
319
 
320
        public AgentStatus() {
321
        }
322
 
323
        public AgentStatus(int authId, String srNumber, String agentName, String agentStatus, String timeInStatus, String customerNumber) {
324
            this.authId = authId;
325
            this.srNumber = srNumber;
326
            this.agentName = agentName;
327
            this.agentStatus = agentStatus;
328
            this.timeInStatus = timeInStatus;
329
            this.customerNumber = customerNumber;
330
            this.lastUpdated = LocalDateTime.now();
331
        }
332
 
333
        public int getAuthId() {
334
            return authId;
335
        }
336
 
337
        public void setAuthId(int authId) {
338
            this.authId = authId;
339
        }
340
 
341
        public String getSrNumber() {
342
            return srNumber;
343
        }
344
 
345
        public void setSrNumber(String srNumber) {
346
            this.srNumber = srNumber;
347
        }
348
 
349
        public String getAgentName() {
350
            return agentName;
351
        }
352
 
353
        public void setAgentName(String agentName) {
354
            this.agentName = agentName;
355
        }
356
 
357
        public String getAgentStatus() {
358
            return agentStatus;
359
        }
360
 
361
        public void setAgentStatus(String agentStatus) {
362
            this.agentStatus = agentStatus;
363
        }
364
 
365
        public String getTimeInStatus() {
366
            return timeInStatus;
367
        }
368
 
369
        public void setTimeInStatus(String timeInStatus) {
370
            this.timeInStatus = timeInStatus;
371
        }
372
 
373
        public String getCustomerNumber() {
374
            return customerNumber;
375
        }
376
 
377
        public void setCustomerNumber(String customerNumber) {
378
            this.customerNumber = customerNumber;
379
        }
380
 
381
        public LocalDateTime getLastUpdated() {
382
            return lastUpdated;
383
        }
384
 
385
        public void setLastUpdated(LocalDateTime lastUpdated) {
386
            this.lastUpdated = lastUpdated;
387
        }
35879 ranu 388
    }
35867 ranu 389
}