SessionManager.java 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. package com.telerobot.fs.wshandle;
  2. import com.telerobot.fs.config.AppContextProvider;
  3. import com.telerobot.fs.config.SystemConfig;
  4. import com.telerobot.fs.config.UuidGenerator;
  5. import com.telerobot.fs.entity.po.AgentEntity;
  6. import com.telerobot.fs.entity.pojo.AgentStatus;
  7. import com.telerobot.fs.mybatis.dao.SysDao;
  8. import com.telerobot.fs.service.SysService;
  9. import com.telerobot.fs.utils.CommonUtils;
  10. import com.telerobot.fs.utils.StringUtils;
  11. import com.telerobot.fs.utils.ThreadUtil;
  12. import org.slf4j.Logger;
  13. import org.slf4j.LoggerFactory;
  14. import java.util.ArrayList;
  15. import java.util.Iterator;
  16. import java.util.List;
  17. import java.util.Map;
  18. import java.util.concurrent.ConcurrentHashMap;
  19. public class SessionManager {
  20. private static SessionManager instance;
  21. private static final Object syncRoot = new Object();
  22. private static final Logger logger = LoggerFactory.getLogger(MessageHandlerEngine.class);
  23. /**
  24. * 保存所有客户端的Session会话信息的容器
  25. */
  26. private Map<String, SessionEntity> sessionContainer = new ConcurrentHashMap<String, SessionEntity>();
  27. private SessionManager() {
  28. new Thread(new Runnable() {
  29. @Override
  30. public void run() {
  31. resetAgentBusyLockTime();
  32. }
  33. }).start();
  34. new Thread(new Runnable() {
  35. @Override
  36. public void run() {
  37. logger.info("websocket sessionManager thread has been started ...");
  38. while (true) {
  39. deleteAndGetInvalidSession();
  40. int timeout = Integer.parseInt(AppContextProvider.getEnvConfig("ws-server.ws-session-timeout").trim());
  41. ThreadUtil.sleep(timeout * 1000);
  42. }
  43. }
  44. }, "deleteInvalidSession").start();
  45. }
  46. private int lastSessionCount = 0;
  47. /**
  48. * 删除过期无效的会话
  49. *
  50. * @return
  51. */
  52. private List<String> deleteAndGetInvalidSession() {
  53. List<String> invalidOptList = new ArrayList<String>(10);
  54. Iterator<Map.Entry<String, SessionEntity>> it = sessionContainer.entrySet().iterator();
  55. int sessionCount = 0;
  56. while(it.hasNext()){
  57. Map.Entry<String, SessionEntity> entry = it.next();
  58. SessionEntity tmpObj = entry.getValue();
  59. if (!tmpObj.IsValid()) {
  60. invalidOptList.add(tmpObj.getOpNum());
  61. MessageHandlerEngineList.getInstance().delete(tmpObj.getSessionId(), false);
  62. it.remove();
  63. }
  64. sessionCount ++;
  65. }
  66. if(lastSessionCount != sessionCount) {
  67. logger.info("current session count: {}", sessionCount);
  68. lastSessionCount = sessionCount;
  69. }
  70. if (invalidOptList.size() != 0) {
  71. logger.info("Session {} has been Expired, delete from SessionContainer.", CommonUtils.ListToString(invalidOptList, true));
  72. }
  73. removeOnlineUser(invalidOptList);
  74. return invalidOptList;
  75. }
  76. /**
  77. * 根据工号删除在线用户;
  78. * @param optList
  79. * @return
  80. */
  81. private int removeOnlineUser(List<String> optList){
  82. if(optList.size() == 0){
  83. return 0;
  84. }
  85. int affectRow = 0;
  86. try {
  87. affectRow = AppContextProvider.getBean(SysDao.class).removeOnlineUser(optList);
  88. logger.info("delete expired user from database: {}", affectRow);
  89. }
  90. catch (Exception e){
  91. logger.error("error occurs while deleting expired user from database: {} , {}", e.toString(),
  92. CommonUtils.getStackTraceString(e.getStackTrace())
  93. );
  94. }
  95. return affectRow;
  96. }
  97. /**
  98. * 根据工号删除在线用户;
  99. * @param opnum 工号
  100. * @return
  101. */
  102. private int removeOnlineUser(String opnum){
  103. ArrayList<String> optList = new ArrayList<>();
  104. optList.add(opnum);
  105. int affectRow = 0;
  106. try {
  107. affectRow = AppContextProvider.getBean(SysDao.class).removeOnlineUser(optList);
  108. logger.info("Delete the number of expired session users from the database: {}", affectRow);
  109. }
  110. catch (Exception e){
  111. logger.error("database error: {}", e.toString());
  112. }
  113. return affectRow;
  114. }
  115. private int addOnlineUser(SessionEntity session){
  116. AgentEntity entity = new AgentEntity();
  117. entity.setId(UuidGenerator.GetOneUuid());
  118. entity.setClientIp(session.getClientIp());
  119. entity.setExtnum(session.getExtNum());
  120. entity.setOpnum(session.getOpNum());
  121. entity.setGroupId(session.getGroupId());
  122. entity.setLoginTime(session.getLoginTime());
  123. entity.setSessionId(session.getSessionId());
  124. entity.setSkillLevel(session.getSkillLevel());
  125. entity.setAgentStatus(AgentStatus.justLogin);
  126. entity.setSessionId(session.getSessionId());
  127. int affectRow = 0;
  128. try {
  129. affectRow = AppContextProvider.getBean(SysDao.class).addOnlineUser(entity);
  130. logger.info("addOnlineUser affectRow: {}", affectRow);
  131. }
  132. catch (Exception e){
  133. logger.error("addOnlineUser failed: {}", e.toString());
  134. }
  135. return affectRow;
  136. }
  137. /**
  138. * 通过单体模式实现,返回一个SessionManager的实例
  139. *
  140. * @return
  141. */
  142. public static SessionManager getInstance() {
  143. if (instance == null) {
  144. synchronized (syncRoot) {
  145. if (instance == null) {
  146. instance = new SessionManager();
  147. }
  148. }
  149. }
  150. return instance;
  151. }
  152. private static int transferAgentTimeOut = Integer.parseInt(
  153. SystemConfig.getValue("inbound-transfer-agent-timeout", "30")
  154. );
  155. /**
  156. * 定时批量重置 cc_online_user表 座席锁定状态 busy_lock_time;
  157. * 如果座席的锁定状态超过 transferAgentTimeOut 仍然没有解除,说明电话转接可能出现异常或者其他原因导致,
  158. * 此时如果不解除锁定状态,会导致座席永远处于忙碌状态而无法接到电话。
  159. */
  160. private void resetAgentBusyLockTime() {
  161. logger.info("start resetAgentBusyLockTime thread.");
  162. int maxLooper = 6000;
  163. int counter = 0;
  164. while (true) {
  165. long timeout = System.currentTimeMillis() - (transferAgentTimeOut + 5) * 1000;
  166. List<SessionEntity> sessionList = AppContextProvider.getBean(SysService.class).
  167. selectAgentBusyLockTimeout(timeout);
  168. if(null != sessionList && sessionList.size() > 0) {
  169. for (SessionEntity session : sessionList) {
  170. int affectRow = AppContextProvider.getBean(SysService.class).resetAgentBusyLockTimeEx(
  171. session.getOpNum(), timeout
  172. );
  173. if (affectRow > 0) {
  174. logger.info(" resetAgentBusyLockTimeEx affectRow:{}, opnum={}",
  175. affectRow, session.getOpNum());
  176. }
  177. MessageHandlerEngine engine = MessageHandlerEngineList.getInstance().
  178. getMsgHandlerEngineByOpNum(session.getOpNum());
  179. if (null != engine) {
  180. if (engine.getSessionInfo() != null) {
  181. logger.info("unLock acd agent extNum={}, opNum={}.", session.getExtNum(), session.getOpNum());
  182. engine.getSessionInfo().unLock();
  183. }
  184. }
  185. }
  186. }
  187. counter++;
  188. if(counter > maxLooper){
  189. counter = 0;
  190. logger.info("定时批量重置座席锁定状态 的线程运行中...");
  191. }
  192. ThreadUtil.sleep(1500);
  193. }
  194. }
  195. /**
  196. * 根据SessionId更新会话的活跃时间
  197. * @param sessionId
  198. * @return
  199. */
  200. public boolean updateSessionActiveTime(String sessionId) {
  201. SessionEntity sessionEntity = this.sessionContainer.get(sessionId);
  202. if(sessionEntity != null){
  203. sessionEntity.setLastActiveTime(System.currentTimeMillis());
  204. return true;
  205. }
  206. return false;
  207. }
  208. /**
  209. * 保存当前会话信息;(用户身份认证通过后才进行保存会话状态) 把当前客户端会话信息存入系统
  210. *
  211. * @param clientSession
  212. * @return 如果会话保存成功则返回空字符串,否则返回错误原因
  213. */
  214. public boolean add(SessionEntity clientSession) {
  215. if (clientSession == null) {
  216. return false;
  217. }
  218. boolean found = false;
  219. String deleteClientId = "";
  220. Iterator<Map.Entry<String, SessionEntity>> it = sessionContainer.entrySet().iterator();
  221. while(it.hasNext()){
  222. Map.Entry<String, SessionEntity> entry = it.next();
  223. SessionEntity tmpObj = entry.getValue();
  224. if (tmpObj.getOpNum().equalsIgnoreCase(clientSession.getOpNum())) {
  225. found = true;
  226. deleteClientId = tmpObj.getSessionId();
  227. it.remove();
  228. break;
  229. }
  230. }
  231. //先从数据库中删除可能存在的指定工号的登录信息;
  232. removeOnlineUser(clientSession.getOpNum());
  233. this.sessionContainer.put(clientSession.getSessionId(), clientSession);
  234. addOnlineUser(clientSession);
  235. logger.info("Current Session count is {}, successfully add a session object to SessionContainer. Details: {}",
  236. sessionContainer.size(),
  237. clientSession.toString());
  238. if (found) {
  239. MessageHandlerEngine engine = MessageHandlerEngineList.getInstance().getMsgHandlerEngine(deleteClientId);
  240. if (engine != null && !engine.getDisposeStatus()) {
  241. try {
  242. MessageResponse response = new MessageResponse();
  243. response.setMsg("user_logined_on_other_device");
  244. response.setStatus(201);
  245. engine.sendReplyToAgent(response);
  246. } catch (Exception e) {
  247. logger.info("send websocket msg error: {}", e.toString());
  248. }
  249. MessageHandlerEngineList.getInstance().delete(deleteClientId, false);
  250. }
  251. }
  252. return true;
  253. }
  254. /**
  255. * 把当前客户端会话信息从系统中删除
  256. *
  257. * @param sessionId
  258. * 客户端会话的SessionId
  259. * @return
  260. */
  261. public boolean delete(String sessionId) {
  262. if (StringUtils.isNullOrEmpty(sessionId)) {
  263. return true;
  264. }
  265. boolean found = false;
  266. SessionEntity tmpObj = sessionContainer.get(sessionId);
  267. if (tmpObj != null) {
  268. removeOnlineUser(tmpObj.getOpNum());
  269. this.sessionContainer.remove(sessionId);
  270. found = true;
  271. }
  272. return found;
  273. }
  274. }