Subversion Repositories SmartDukaan

Rev

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