HandlerThread.java 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. package com.dashitech.callcenter.socket;
  2. import com.dashitech.businessdata.dao.*;
  3. import com.dashitech.businessdata.entity.CallLogEntity;
  4. import com.dashitech.businessdata.entity.CallRecordEntity;
  5. import com.dashitech.businessdata.entity.HospitalConfig;
  6. import com.dashitech.callcenter.webSocket.service.PhoneWebSocket;
  7. import com.dashitech.utils.DateUtil2;
  8. import com.dashitech.utils.JsonUtil;
  9. import com.dashitech.utils.PropertiesUtil;
  10. import com.fasterxml.jackson.databind.ObjectMapper;
  11. import net.sf.json.JSONObject;
  12. import org.apache.commons.lang.StringUtils;
  13. import org.apache.commons.logging.Log;
  14. import org.apache.commons.logging.LogFactory;
  15. import org.springframework.beans.factory.annotation.Autowired;
  16. import org.springframework.stereotype.Service;
  17. import java.io.DataInputStream;
  18. import java.io.DataOutputStream;
  19. import java.net.Socket;
  20. import java.text.ParseException;
  21. import java.text.SimpleDateFormat;
  22. import java.util.*;
  23. /**
  24. * Created by chi on 2020/8/6.
  25. */
  26. @Service
  27. public class HandlerThread {
  28. private static Log log = LogFactory.getLog(HandlerThread.class);
  29. // 这里使用静态,让 service 属于类
  30. private static ICallLogDAO callLogDAO;
  31. private static ICallRecordDAO callRecordDAO;
  32. @Autowired
  33. public void setCallRecordDAO(ICallRecordDAO callRecordDAO) {
  34. HandlerThread.callRecordDAO = callRecordDAO;
  35. }
  36. @Autowired
  37. public void setCallLogDAO(ICallLogDAO callLogDAO) {
  38. HandlerThread.callLogDAO = callLogDAO;
  39. }
  40. private static IDictionaryDAO dictionaryDAO;
  41. @Autowired
  42. public void setDictionaryDAO(IDictionaryDAO dictionaryDAO) {
  43. HandlerThread.dictionaryDAO = dictionaryDAO;
  44. }
  45. private static IDepartmentDAO departmentDAO;
  46. @Autowired
  47. public void setDepartmentDAO(IDepartmentDAO departmentDAO) {
  48. HandlerThread.departmentDAO = departmentDAO;
  49. }
  50. private static IHospitalConfigDAO hospitalConfigDAO;
  51. @Autowired
  52. public void setHospitalConfigDAO(IHospitalConfigDAO hospitalConfigDAO) {
  53. HandlerThread.hospitalConfigDAO = hospitalConfigDAO;
  54. }
  55. private static String project;
  56. private static String recordNginxPath;
  57. private static String recordRegex;
  58. private Socket sc;
  59. private static Map<String,String> phoneMap = new HashMap<>();
  60. private static Map<String,CallLogEntity> phoneLog = new HashMap<>();
  61. private String his = "";
  62. public HandlerThread() {
  63. }
  64. public HandlerThread(Socket client) {
  65. sc = client;
  66. }
  67. public void run() {
  68. DataInputStream input = null;
  69. DataOutputStream output = null;
  70. try {
  71. // sc.setSoTimeout(10000);
  72. input = new DataInputStream(sc.getInputStream());
  73. output = new DataOutputStream(sc.getOutputStream());
  74. String reset = "";
  75. //持续读取内部消息
  76. while (true) {
  77. // System.out.println("处理客户端数据:"+input.readUTF());
  78. // System.out.println("3333333333333333333333");
  79. //获取信息流长度
  80. byte[] b = new byte[4];
  81. reset = "keepalive";
  82. input.read(b);
  83. int count = bytes2Int(b);
  84. // System.out.println("count::"+count);
  85. //根据信息流长度获取信息流
  86. byte[] b1 = new byte[count];
  87. input.read(b1);
  88. //将信息流转换为String
  89. String data = new String(b1);
  90. System.out.println("data:"+data+"his"+his);
  91. if(project == null) {
  92. project = PropertiesUtil.getProperty("project");
  93. recordNginxPath = PropertiesUtil.getProperty("db.hjzx.record.nginx");
  94. recordRegex = PropertiesUtil.getProperty("db.hjzx.record.path.regex");
  95. }
  96. if (data != null || !data.equals("")&&data.substring(0,1).equals("{")) {
  97. if (JsonUtil.toJSONObject(data).containsKey("channelStatusList")){
  98. List llist = JsonUtil.toJSONObject(data).optJSONArray("channelStatusList");
  99. JSONObject jsonObject = JsonUtil.toJSONObject(llist.get(0).toString());
  100. sendSocket(jsonObject,his);
  101. handleCallLog(jsonObject,his);
  102. reset = "channelstatus";
  103. }else if(JsonUtil.toJSONObject(data).containsKey("recordList")){
  104. saveCallLog(data,his);
  105. reset = "addrec";
  106. }
  107. }
  108. if (data != null && data.length() >= 10){
  109. String dataStr = "";
  110. if (reset.equals("addrec")){
  111. dataStr = "{\"cmd\":\""+reset+"\",\"error\":\"0\", \"timestamp\":\""+(System.currentTimeMillis() / 1000)+"\"}";
  112. }else{
  113. dataStr = "{\"cmd\":\""+reset+"\", \"timestamp\":\""+(System.currentTimeMillis() / 1000)+"\"}";
  114. }
  115. if (data.indexOf("loginreq") >= 0){
  116. //{"cmd":"loginreq","ipaddr":"192.168.25.37","serialNo":"21380501019804","recordNumber":4,"webport":80,"filestoretype":0}his
  117. if(JsonUtil.toJSONObject(data).containsKey("serialNo")){
  118. his = JsonUtil.toJSONObject(data).getString("serialNo");
  119. }else{
  120. his = JsonUtil.toJSONObject(data).getString("sernelNo");
  121. }
  122. // 设备 21040501017523 写死支持 院区1
  123. if(his.equals("21040501017523")){
  124. his = "1";
  125. }else{
  126. his = callLogDAO.selectSernelNo("sernelNo",his);
  127. }
  128. dataStr = "{\"cmd\":\"loginres\", \"error\":\"0\", \"timestamp\":\"" + (System.currentTimeMillis() / 1000) + "\"}";
  129. }
  130. System.out.println("redate>>"+dataStr+"his"+his);
  131. output.write(dataToBytes(dataStr));
  132. }
  133. }
  134. } catch (Exception e) {
  135. e.printStackTrace();
  136. System.out.println("服务器EOFException异常");
  137. } finally {
  138. if (output != null) {
  139. try {
  140. output.close();
  141. } catch (Exception e) {
  142. output = null;
  143. }
  144. }
  145. if (input != null) {
  146. try {
  147. input.close();
  148. } catch (Exception e) {
  149. input = null;
  150. }
  151. }
  152. if (sc != null) {
  153. try {
  154. sc.close();
  155. } catch (Exception e) {
  156. sc = null;
  157. }
  158. }
  159. }
  160. }
  161. /**
  162. * 将发送内容转换为byte数组
  163. * 需要注意:byte前面四位需要描述整个发送内容的长度(dataStr)
  164. *
  165. * @param dataStr
  166. */
  167. public byte[] dataToBytes(String dataStr) {
  168. //将int转换为byte数组
  169. byte[] lengthBytes = int2Bytes(dataStr.length());
  170. //将string转换为byte数组
  171. byte[] dataBytes = dataStr.getBytes();
  172. //最终发送的byte数组,int byte数组长度和string byte数组长度相加得到最终长度
  173. byte[] lengthAndDataBytes = new byte[(lengthBytes.length + dataBytes.length)];
  174. //将int byte数组四位加入到新byte数组中
  175. lengthAndDataBytes[0] = lengthBytes[0];
  176. lengthAndDataBytes[1] = lengthBytes[1];
  177. lengthAndDataBytes[2] = lengthBytes[2];
  178. lengthAndDataBytes[3] = lengthBytes[3];
  179. //将string byte数组四位加入到新byte数组中
  180. for (int index = 4; index < lengthAndDataBytes.length; index++) {
  181. lengthAndDataBytes[index] = dataBytes[index - 4];
  182. }
  183. return lengthAndDataBytes;
  184. }
  185. /**
  186. * byte数组转换为int
  187. *
  188. * @param bytes
  189. * @return
  190. */
  191. public static int bytes2Int(byte[] bytes) {
  192. //如果不与0xff进行按位与操作,转换结果将出错,有兴趣的同学可以试一下。
  193. int int1 = bytes[0] & 0xff;
  194. int int2 = (bytes[1] & 0xff) << 8;
  195. int int3 = (bytes[2] & 0xff) << 16;
  196. int int4 = (bytes[3] & 0xff) << 24;
  197. return int1 | int2 | int3 | int4;
  198. }
  199. /**
  200. * int 转换为 byte数组
  201. *
  202. * @param integer
  203. * @return
  204. */
  205. public static byte[] int2Bytes(int integer) {
  206. byte[] bytes = new byte[4];
  207. bytes[3] = (byte) ((byte) integer >> 24);
  208. bytes[2] = (byte) ((byte) integer >> 16);
  209. bytes[1] = (byte) ((byte) integer >> 8);
  210. bytes[0] = (byte) integer;
  211. return bytes;
  212. }
  213. /**
  214. * 发送弹屏socket,将电话号码返回过去
  215. * 201为通话中
  216. * 202为振铃
  217. *
  218. */
  219. public void sendSocket(JSONObject jsonObject,String his) {
  220. try {
  221. putPhoneMap("phone_num_port",his);
  222. if (jsonObject != null && jsonObject.containsKey("calldirection") && jsonObject.get("calldirection").toString().equals("1") || jsonObject.get("calldirection").toString() == "1") {
  223. if (jsonObject.containsKey("phonestate") && jsonObject.get("phonestate").toString().equals("3") || jsonObject.get("phonestate").toString() == "3") {
  224. if (jsonObject.containsKey("channel")&&jsonObject.get("channel").toString()!=null) {
  225. Map rMap = new HashMap();
  226. //TODO 增加返回 uuid 并且保存通话记录数据
  227. if (jsonObject.containsKey("dtmfa") && jsonObject.get("dtmfa").toString() != null && !jsonObject.optString("dtmfa").toString().equals("")){
  228. rMap.put("phone", jsonObject.opt("dtmfa"));
  229. rMap.put("status", 201);
  230. //TODO 先保存通话记录
  231. CallLogEntity callLogEntity = new CallLogEntity();
  232. callLogEntity.setdTMFA(jsonObject.optString("dtmfa"));
  233. callLogEntity.setdTMFB(jsonObject.optString("dtmfb"));
  234. if(StringUtils.isNotEmpty(jsonObject.optString("rstime"))) {
  235. callLogEntity.setResponseTime(DateUtil2.parseDateTime(jsonObject.optString("rstime")));
  236. }
  237. saveItsmCallRecord(callLogEntity,jsonObject.optString("uuid"));
  238. rMap.put("callId",jsonObject.optString("uuid"));
  239. }else {
  240. rMap.put("phone","");
  241. rMap.put("status", 201);
  242. }
  243. ObjectMapper objectMapper = new ObjectMapper();
  244. String json = objectMapper.writeValueAsString(rMap);
  245. PhoneWebSocket.sendMessage(phoneMap.get(his+jsonObject.get("channel").toString()),json);
  246. }
  247. } else if (jsonObject.containsKey("phonestate") && jsonObject.get("phonestate").toString().equals("4") || jsonObject.get("phonestate").toString() == "4") {
  248. Map rMap = new HashMap();
  249. rMap.put("msg","来电话啦!" );
  250. rMap.put("status", 202);
  251. ObjectMapper objectMapper = new ObjectMapper();
  252. String json = objectMapper.writeValueAsString(rMap);
  253. if (jsonObject.containsKey("channel")&&jsonObject.get("channel").toString()!=null){
  254. PhoneWebSocket.sendMessage(phoneMap.get(his+jsonObject.get("channel").toString()),json);
  255. }
  256. CallLogEntity callLog = new CallLogEntity();
  257. callLog.setCallState(0);
  258. }
  259. }
  260. } catch (Exception e) {
  261. e.printStackTrace();
  262. }
  263. }
  264. private void handleCallLog(JSONObject jsonObject,String his){
  265. if(jsonObject.get("phonestate").toString().equals("4")){
  266. // 主叫号码不等于空字符串,并且不等于座机号(通道号获得),即可判断为来电号码
  267. if(!jsonObject.get("dtmfa").toString().equals("") && !jsonObject.get("dtmfa").toString().equals(phoneMap.get(his+jsonObject.get("channel").toString()))){
  268. phoneLog.put(his+jsonObject.get("channel").toString(),getCallLog(jsonObject));
  269. }
  270. //如果phonestate 为零则表示话机为空闲状态,则保存通话记录
  271. }else if(jsonObject.get("phonestate").toString().equals("0")){
  272. CallLogEntity callLog = phoneLog.get(his+jsonObject.get("channel").toString());
  273. if (callLog!=null){
  274. if (jsonObject.containsKey("dtmfa")&& StringUtils.isNotEmpty(jsonObject.get("dtmfa").toString())&&!jsonObject.get("dtmfa").toString().equals("")&&!jsonObject.get("dtmfa").toString().equals(" ")){
  275. // 查询科室信息 并赋值
  276. callLog.setCallDept(getDeptByDTMFA(jsonObject.get("dtmfa").toString()));
  277. }else {
  278. callLog.setCallDept("暂无");
  279. }
  280. callLog.setHosId(Integer.parseInt(his));
  281. callLogDAO.save(callLog);
  282. }
  283. }else if(jsonObject.get("phonestate").toString().equals("1")){
  284. phoneLog.put(his+jsonObject.get("channel").toString(),null);
  285. }else if(jsonObject.get("phonestate").toString().equals("3")){
  286. phoneLog.put(his+jsonObject.get("channel").toString(),null);
  287. }
  288. }
  289. /**
  290. * 将json转换为实体
  291. * 默认 已接未接为 未接
  292. * 默认 通话类型为 呼入
  293. * 默认 时间为 当前时间。
  294. * @param jsonObject
  295. * @return
  296. */
  297. private CallLogEntity getCallLog(JSONObject jsonObject){
  298. CallLogEntity callLog = new CallLogEntity();
  299. if (jsonObject.containsKey("channel")){
  300. callLog.setPhyIDA(jsonObject.get("channel").toString());
  301. }
  302. if (jsonObject.containsKey("dtmfa")) {
  303. callLog.setdTMFA(jsonObject.get("dtmfa").toString());
  304. callLog.setCallDept(getDeptByDTMFA(jsonObject.get("dtmfa").toString()));
  305. }
  306. if (jsonObject.containsKey("dtmfb")){
  307. callLog.setdTMFB(jsonObject.get("dtmfb").toString());
  308. }
  309. callLog.setResponseTime(new Date());
  310. callLog.setCallState(0);
  311. callLog.setCallType(1);
  312. return callLog;
  313. }
  314. public void putPhoneMap(String key,String his){
  315. // List<DictionaryEntity> dictionaryEntityList = dictionaryDAO.selectDictionaryByKey(key);
  316. List<HospitalConfig> hospitalConfigList = hospitalConfigDAO.getHospitalConfigList(key,his);
  317. if (hospitalConfigList!=null&&hospitalConfigList.size()>0){
  318. for (HospitalConfig hospitalConfig:hospitalConfigList){
  319. phoneMap.put(his+hospitalConfig.getValue2(),hospitalConfig.getValue());
  320. }
  321. }
  322. }
  323. /**
  324. * 保存通话记录
  325. * @param message
  326. */
  327. public void saveCallLog(String message,String his){
  328. //TODO 修改通话记录数据;DTMFA DTMFB rstime
  329. if (JsonUtil.toJSONObject(message).containsKey("recordList")) {
  330. List list = JsonUtil.toJSONObject(message).optJSONArray("recordList");
  331. if (list!=null&&list.size()>0){
  332. JSONObject jsonObject = JsonUtil.toJSONObject(list.get(0).toString());
  333. CallLogEntity callLog = new CallLogEntity();
  334. if (jsonObject.containsKey("phyIDA")){
  335. callLog.setPhyIDA(jsonObject.get("phyIDA").toString());
  336. }
  337. if (jsonObject.containsKey("DTMFA")){
  338. callLog.setdTMFA(jsonObject.get("DTMFA").toString());
  339. callLog.setCallDept(getDeptByDTMFA(jsonObject.get("DTMFA").toString()));
  340. }
  341. if (jsonObject.containsKey("DTMFB")){
  342. callLog.setdTMFB(jsonObject.get("DTMFB").toString());
  343. }
  344. if (jsonObject.containsKey("rstime")){
  345. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  346. try {
  347. callLog.setResponseTime(sdf.parse(jsonObject.get("rstime").toString()));
  348. } catch (ParseException e) {
  349. e.printStackTrace();
  350. }
  351. }
  352. if (jsonObject.containsKey("longtime")){
  353. callLog.setLongTime(jsonObject.get("longtime").toString());
  354. }
  355. if (jsonObject.containsKey("path")){
  356. callLog.setPath(jsonObject.get("path").toString());
  357. }
  358. if(callLog.getPhyIDA() != null){
  359. // 判断为呼出(主叫号码等于分机号(通道号)为呼出)
  360. if(callLog.getdTMFA() != null &&phoneMap!=null&&phoneMap.size()>0&& phoneMap.get(his+callLog.getPhyIDA()).equals(callLog.getdTMFA())){
  361. callLog.setCallType(0);
  362. // callLog.setCallState(1);
  363. // 判断为呼入(被叫号码等于分机号(通道号)为呼入)
  364. }else if(callLog.getdTMFB()!= null &&phoneMap!=null&&phoneMap.size()>0&& phoneMap.get(his+callLog.getPhyIDA()).equals(callLog.getdTMFB())){
  365. callLog.setCallType(1);
  366. // callLog.setCallState(1);
  367. }else{//未知
  368. callLog.setCallType(2);
  369. }
  370. callLog.setCallState(1);
  371. }
  372. callLog.setHosId(Integer.parseInt(his));
  373. callLogDAO.save(callLog);
  374. //TODO 根据配置文件开关,是否保存到ITSM的通话记录数据
  375. saveItsmCallRecord(callLog,null);
  376. }
  377. }
  378. }
  379. private String getDeptByDTMFA(String phone){
  380. try {
  381. if (phone!=null){
  382. phone = phone.trim();
  383. if (phone.equals("")){
  384. return "暂无";
  385. }
  386. //给来电科室赋值
  387. String deptName = departmentDAO.getDeptNameByPhone(phone);
  388. if (deptName!=null&&!deptName.equals("")){
  389. return deptName;
  390. }else {
  391. /* String deptName2 = departmentDAO.getDeptNameByWorkOrderPhone(phone);
  392. if (deptName2!=null&&!deptName2.equals("")){
  393. return deptName2;
  394. }else {
  395. return "暂无";
  396. }*/
  397. return "暂无";
  398. }
  399. }
  400. }catch (Exception e){
  401. e.printStackTrace();
  402. }
  403. return "暂无";
  404. }
  405. /**
  406. * 根据配置文件开关,是否保存到ITSM的通话记录数据
  407. */
  408. private void saveItsmCallRecord(CallLogEntity callLog,String uuid) {
  409. try {
  410. if("ITSM".equals(project)) {
  411. CallRecordEntity recordEntity = null;
  412. log.info("开始保存通话记录");
  413. //TODO 根据uuid区分是:新增通话记录,还是修改附件的通话记录
  414. if(StringUtils.isNotEmpty(uuid)) { //新增
  415. Long id = callRecordDAO.selectIdByUuid(uuid);
  416. if(id == null) {
  417. recordEntity = new CallRecordEntity();
  418. recordEntity.setVersion(0);
  419. recordEntity.setCallAccept(uuid);
  420. recordEntity.setCallerIdNumber(callLog.getdTMFA());
  421. recordEntity.setDestinationNumber(callLog.getdTMFB());
  422. recordEntity.setCreatedTime(callLog.getResponseTime());
  423. callRecordDAO.save(recordEntity);
  424. log.info("新增临时通话记录完成");
  425. }else {
  426. log.info("已有临时通话记录,忽略此条uuid:"+uuid);
  427. }
  428. }else { //修改
  429. log.info("开始匹配临时通话记录");
  430. recordEntity = callRecordDAO.selectByParams(callLog.getdTMFA(), callLog.getdTMFB(), callLog.getResponseTime());
  431. //TODO 根据【主叫、被叫、起始时间】判断是否查询到对应数据,来绑定附件,如果未匹配到,直接新增
  432. String newUUID = null;
  433. if(recordEntity != null) {
  434. log.info("匹配到临时通话记录!开始修改");
  435. newUUID = recordEntity.getCallAccept();
  436. }else {
  437. log.info("未匹配到临时通话记录!开始新增");
  438. recordEntity = new CallRecordEntity();
  439. newUUID = UUID.randomUUID().toString();
  440. }
  441. recordEntity.setCallAccept(newUUID);
  442. recordEntity.setVersion(0);
  443. recordEntity.setCallerIdNumber(callLog.getdTMFA());
  444. recordEntity.setDestinationNumber(callLog.getdTMFB());
  445. recordEntity.setCreatedTime(callLog.getResponseTime());
  446. recordEntity.setCallTime(callLog.getLongTime());
  447. recordEntity.setRecordingFileName(callLog.getPath());
  448. recordEntity.setIsAnswered(callLog.getCallState());
  449. //处理结束时间
  450. if(callLog.getResponseTime() != null && callLog.getLongTime() != null) {
  451. Date overTime = DateUtil2.addSeconds(callLog.getResponseTime(), Integer.valueOf(callLog.getLongTime()));
  452. recordEntity.setOverTime(overTime);
  453. }
  454. //院区
  455. if(callLog.getHosId() != null) {
  456. recordEntity.setBranch(callLog.getHosId().longValue());
  457. }
  458. //呼入呼出,其它
  459. if(callLog.getCallType() != null) {
  460. if("1".equals(callLog.getCallType().toString())) {
  461. recordEntity.setCallType("2");
  462. }else if("2".equals(callLog.getCallType().toString())) {
  463. recordEntity.setCallType("1");
  464. }else {
  465. recordEntity.setCallType("3");
  466. }
  467. }
  468. //处理录音地址
  469. if(callLog.getPath() != null) {
  470. String newPath = callLog.getPath();
  471. String[] arr = callLog.getPath().split(recordRegex);
  472. if (arr.length > 1) {
  473. newPath = recordNginxPath + arr[1];
  474. }
  475. recordEntity.setRecordingFileName(newPath);
  476. }
  477. callRecordDAO.save(recordEntity);
  478. log.info("附件通话记录保存完成");
  479. }
  480. log.info("ITSM保存通话记录完成:"+recordEntity.getCallAccept());
  481. System.out.println("ITSM保存通话记录完成:"+recordEntity.getCallAccept());
  482. }
  483. } catch (Exception ex) {
  484. ex.printStackTrace();
  485. }
  486. }
  487. public static void main(String[] args) {
  488. System.out.println(System.currentTimeMillis() / 1000);
  489. }
  490. }