wechat.client.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. /**
  2. * 用户微信客户端。
  3. */
  4. "use strict";
  5. let RedisClient = require('../../repository/redis/redis.client');
  6. let RedisModel = require('../redis.model');
  7. let ObjectUtil = require("../../util/object.util.js");
  8. let ModelUtil = require('../../util/model.util');
  9. let WechatSDK = require('../../util/wechat.sdk');
  10. let PatientRepo = require('../../repository/mysql/patient.repo');
  11. let TopicRepo = require("../../repository/mysql/topics.repo.js");
  12. let ParticipantRepo = require("../../repository/mysql/participant.repo");
  13. let redisConn = RedisClient.redisClient().connection;
  14. let clientCache = require('../socket.io/client.cache').clientCache();
  15. let configFile = require('../../include/commons').CONFIG_FILE;
  16. let config = require('../../resources/config/' + configFile);
  17. let log = require("../../util/log.js");
  18. let https = require('https');
  19. let async = require('async');
  20. const CONTENT_TYPES = require('../../include/commons').CONTENT_TYPES;
  21. const SESSION_TYPES = require('../../include/commons').SESSION_TYPES;
  22. const SOCKET_TYPES = require('../../include/commons').SOCKET_TYPES;
  23. const REDIS_KEYS = require('../../include/commons').REDIS_KEYS;
  24. class WechatClient extends RedisModel {
  25. constructor() {
  26. super();
  27. }
  28. /**
  29. * 取得用户微信端状态。若Redis中找不到,则从MySQL中查找。
  30. *
  31. * @param userId
  32. * @param handler
  33. */
  34. static getWechatStatus(userId, handler) {
  35. redisConn.hgetallAsync(RedisModel.makeRedisKey(REDIS_KEYS.UserWechatStatus, userId))
  36. .then(function (status) {
  37. if (status == null) {
  38. PatientRepo.findWechatOpenId(userId, handler);
  39. } else {
  40. handler(null, {openid: status.openid});
  41. }
  42. })
  43. .catch(function (err) {
  44. handler(err, null);
  45. });
  46. }
  47. /**
  48. * 向微信端用户发送消息。若用户微信端在线,通过Web Socket推送给患者,如果不在线则通过微信的模板消息。
  49. *
  50. * 只推送文本、图片及语音消息
  51. *
  52. * @param targetUserId
  53. * @param message 消息体
  54. */
  55. static sendMessage(targetUserId, targetUserName, message) {
  56. if (message&&(message.content_type == CONTENT_TYPES.PlainText ||
  57. message.content_type == CONTENT_TYPES.Image ||
  58. message.content_type == CONTENT_TYPES.Audio||
  59. message.content_type == CONTENT_TYPES.PrescriptionBloodStatus ||
  60. message.content_type == CONTENT_TYPES.PrescriptionFollowupContent ||
  61. message.content_type == CONTENT_TYPES.ChangeDoorCardInfo ||
  62. message.content_type == CONTENT_TYPES.ChangeDoorDoctor ||
  63. message.content_type == CONTENT_TYPES.ChangeDoorPackageItems)) {
  64. let patientClient = clientCache.findById(targetUserId);
  65. let doctorClient = clientCache.findByIdAndType(message.sender_id,SOCKET_TYPES.DOCTOR);
  66. let pc_doctorClient = clientCache.findByIdAndType("pc_"+message.sender_id,SOCKET_TYPES.PC_DOCTOR);
  67. var count = 0;
  68. if (patientClient) {
  69. log.warn("User's wechat endpoint is online, sending via web socket. User id: ", targetUserId);
  70. WechatClient.sendViaWebSocket(patientClient.socket, message);
  71. if(doctorClient){
  72. log.error("doctor sessionid "+doctorClient.sessionId);
  73. log.error("patient sessionid "+patientClient.sessionId);
  74. if(patientClient.sessionId==doctorClient.sessionId){
  75. //用户socket在线,推送给用户后,告知医生此消息已读
  76. WechatClient.updateParticipantLastFetchTime(doctorClient.sessionId, targetUserId, ObjectUtil.timestampToLong(message.timestamp));
  77. WechatClient.sendReadDoctor(doctorClient.socket, message);
  78. }
  79. }else{
  80. count++;
  81. }
  82. if(pc_doctorClient){
  83. log.error("doctor sessionid "+pc_doctorClient.sessionId);
  84. log.error("patient sessionid "+patientClient.sessionId);
  85. if(patientClient.sessionId==pc_doctorClient.sessionId){
  86. //用户socket在线,推送给用户后,告知医生此消息已读
  87. WechatClient.updateParticipantLastFetchTime(pc_doctorClient.sessionId, targetUserId, ObjectUtil.timestampToLong(message.timestamp));
  88. WechatClient.sendReadDoctor(pc_doctorClient.socket, message);
  89. }
  90. }else{
  91. count++;
  92. }
  93. if(count==0){
  94. log.error("doctor client not found");
  95. }
  96. } else {
  97. log.info("User's wechat endpoint is not online, sending via wechat template message. User id: ", targetUserId);
  98. var isSendWXTem = true;//是否发送微信模板
  99. if(message.content_type == CONTENT_TYPES.PrescriptionBloodStatus||message.content_type == CONTENT_TYPES.PrescriptionFollowupContent){
  100. var content = JSON.parse(message.content);
  101. if(content.isSendWxTemplate){
  102. message.content = content.text;
  103. }else {
  104. isSendWXTem = false;
  105. }
  106. }
  107. if(isSendWXTem){
  108. WechatClient.sendViaMessageTemplate(targetUserId, targetUserName, message);
  109. }
  110. }
  111. } else if(message.content_type == CONTENT_TYPES.TopicEnd){
  112. let patientClient = clientCache.findById(targetUserId);
  113. if(patientClient)//结束咨询的告知患者
  114. WechatClient.sendViaWebSocket(patientClient.socket, message);
  115. }
  116. };
  117. static sendViaWebSocket(socket, message) {
  118. socket.emit('message', {
  119. id: message.id,
  120. session_id: message.session_id,
  121. sender_id: message.sender_id,
  122. sender_name: message.sender_name,
  123. content_type: message.content_type,
  124. content: message.content,
  125. timestamp: ObjectUtil.timestampToLong(message.timestamp),
  126. type: message.content_type, // legacy support
  127. name: message.sender_name,
  128. sender_img : message.sender_img
  129. });
  130. }
  131. static sendAllRead(doctorId,sessionId){
  132. let doctorClient = clientCache.findByIdAndType(doctorId,SOCKET_TYPES.DOCTOR);
  133. let pc_doctorClient = clientCache.findByIdAndType("pc_"+doctorId,SOCKET_TYPES.PC_DOCTOR);
  134. if(doctorClient){
  135. if(doctorClient.sessionId==sessionId){
  136. doctorClient.socket.emit('message',{ read:"all"});
  137. }else{
  138. log.warn(" doctor not in the same session ");
  139. }
  140. }else{
  141. if(pc_doctorClient && pc_doctorClient.sessionId == sessionId){
  142. pc_doctorClient.socket.emit('message',{ read:"all"});
  143. }else{
  144. log.warn(doctorId+" target doctor is not online!");
  145. }
  146. }
  147. }
  148. static sendMucAllRead(doctorId,loginUserId,sessionId){
  149. let loginClinet = clientCache.findByIdAndType(loginUserId,SOCKET_TYPES.DOCTOR);
  150. if(loginClinet){
  151. //muc是医生来获取数据不能更新成已读
  152. log.warn("type is muc login is doctor not send all read to other doctor!")
  153. return;
  154. }
  155. let doctorClient = clientCache.findByIdAndType(doctorId,SOCKET_TYPES.DOCTOR);
  156. let pc_doctorClient = clientCache.findByIdAndType("pc_"+doctorId,SOCKET_TYPES.PC_DOCTOR);
  157. if(doctorClient){
  158. if(doctorClient.sessionId==sessionId){
  159. doctorClient.socket.emit('message',{ read:"all"});
  160. }else{
  161. log.warn(" doctor not in the same session ");
  162. }
  163. }else{
  164. if(pc_doctorClient && pc_doctorClient.sessionId == sessionId){
  165. pc_doctorClient.socket.emit('message',{ read:"all"});
  166. }else{
  167. log.warn(doctorId+" target doctor is not online!");
  168. }
  169. }
  170. }
  171. static sendReadDoctor(socket, message) {
  172. socket.emit('message', {
  173. id: message.id,
  174. session_id: message.session_id,
  175. sender_id: message.sender_id,
  176. sender_name: message.sender_name,
  177. content_type: message.content_type,
  178. content: message.content,
  179. timestamp: ObjectUtil.timestampToLong(message.timestamp),
  180. type: message.content_type, // legacy support
  181. name: message.sender_name,
  182. read:"one"
  183. });
  184. }
  185. static sendReadDoctorByDoctorId(doctorId, message) {
  186. let doctorClient = clientCache.findByIdAndType(doctorId,SOCKET_TYPES.DOCTOR);
  187. let pc_doctorClient = clientCache.findByIdAndType("pc_"+doctorId,SOCKET_TYPES.PC_DOCTOR);
  188. if(!doctorClient&&!pc_doctorClient){
  189. log.warn("target doctor is not online!");
  190. return;
  191. }
  192. let sendDoctorClient = clientCache.findByIdAndType(message.sender_id,SOCKET_TYPES.DOCTOR);
  193. if(!sendDoctorClient){
  194. sendDoctorClient = clientCache.findByIdAndType("pc_"+message.sender_id,SOCKET_TYPES.PC_DOCTOR);
  195. }
  196. var count = 0;
  197. if(doctorClient&&sendDoctorClient&&sendDoctorClient.sessionId==doctorClient.sessionId){
  198. WechatClient.updateParticipantLastFetchTime(doctorClient.sessionId, doctorId, ObjectUtil.timestampToLong(message.timestamp));
  199. sendDoctorClient.socket.emit('message', {
  200. id: message.id,
  201. session_id: message.session_id,
  202. sender_id: message.sender_id,
  203. sender_name: message.sender_name,
  204. content_type: message.content_type,
  205. content: message.content,
  206. timestamp: ObjectUtil.timestampToLong(message.timestamp),
  207. type: message.content_type, // legacy support
  208. name: message.sender_name,
  209. read:"one"
  210. });
  211. }else{
  212. count++;
  213. }
  214. //发送pc版医生端
  215. if(pc_doctorClient&&sendDoctorClient&&sendDoctorClient.sessionId==pc_doctorClient.sessionId){
  216. WechatClient.updateParticipantLastFetchTime(pc_doctorClient.sessionId, doctorId, ObjectUtil.timestampToLong(message.timestamp));
  217. pc_doctorClient.socket.emit('message', {
  218. id: message.id,
  219. session_id: message.session_id,
  220. sender_id: message.sender_id,
  221. sender_name: message.sender_name,
  222. content_type: message.content_type,
  223. content: message.content,
  224. timestamp: ObjectUtil.timestampToLong(message.timestamp),
  225. type: message.content_type, // legacy support
  226. name: message.sender_name,
  227. read:"one"
  228. });
  229. }else{
  230. count++;
  231. }
  232. if(count==2){
  233. log.warn("doctor is not in the same session or is not online");
  234. }
  235. }
  236. static sendSocketMessageToDoctor(doctorId, message) {
  237. let doctorClient = clientCache.findByIdAndType(doctorId,SOCKET_TYPES.DOCTOR);
  238. let pc_doctorClient = clientCache.findByIdAndType("pc_"+doctorId,SOCKET_TYPES.PC_DOCTOR);
  239. if(!doctorClient&&!pc_doctorClient){
  240. log.warn("target doctor is not online!");
  241. return;
  242. }
  243. // let sendClient = clientCache.findByIdAndType(message.sender_id,SOCKET_TYPES.DOCTOR);//app医生发送的消息
  244. // if(!sendClient){//pc医生发送的消息
  245. // sendClient = clientCache.findByIdAndType("pc_"+message.sender_id,SOCKET_TYPES.PC_DOCTOR);
  246. // }
  247. // if(!sendClient){//居民发送的消息
  248. // sendClient = clientCache.findByIdAndType(message.sender_id,SOCKET_TYPES.PATIENT);
  249. // }
  250. var count = 0;
  251. if(doctorClient&&message.session_id==doctorClient.sessionId){
  252. WechatClient.updateParticipantLastFetchTime(doctorClient.sessionId, doctorId, ObjectUtil.timestampToLong(message.timestamp));
  253. doctorClient.socket.emit('message', {
  254. id: message.id,
  255. session_id: message.session_id,
  256. sender_id: message.sender_id,
  257. sender_name: message.sender_name,
  258. content_type: message.content_type,
  259. content: message.content,
  260. timestamp: ObjectUtil.timestampToLong(message.timestamp),
  261. type: message.content_type, // legacy support
  262. name: message.sender_name,
  263. });
  264. }else{
  265. count++;
  266. }
  267. //发送pc端
  268. if(pc_doctorClient&&message.session_id==pc_doctorClient.sessionId){
  269. WechatClient.updateParticipantLastFetchTime(pc_doctorClient.sessionId, doctorId, ObjectUtil.timestampToLong(message.timestamp));
  270. pc_doctorClient.socket.emit('message', {
  271. id: message.id,
  272. session_id: message.session_id,
  273. sender_id: message.sender_id,
  274. sender_name: message.sender_name,
  275. content_type: message.content_type,
  276. content: message.content,
  277. timestamp: ObjectUtil.timestampToLong(message.timestamp),
  278. type: message.content_type, // legacy support
  279. name: message.sender_name,
  280. });
  281. }else{
  282. count++;
  283. }
  284. if(count==2){
  285. log.warn("doctor is not in the same session or is not online");
  286. }
  287. }
  288. static sendPcImSocket(targetId, message, sessionType) {
  289. if (message.content_type == CONTENT_TYPES.PlainText ||
  290. message.content_type == CONTENT_TYPES.Image ||
  291. message.content_type == CONTENT_TYPES.Audio||
  292. message.content_type == CONTENT_TYPES.Video||
  293. message.content_type == CONTENT_TYPES.GoTo||
  294. sessionType==SESSION_TYPES.SYSTEM) {
  295. let pcim_doctorClient = clientCache.findByIdAndType("pcim_"+targetId,SOCKET_TYPES.PCIM_DOCTOR);
  296. if(pcim_doctorClient) {
  297. let customData = {
  298. session_id: message.session_id||'',
  299. session_type: sessionType,
  300. from: message.sender_id|| '',
  301. data: message.content,
  302. business_type: message.business_type || 1
  303. };
  304. pcim_doctorClient.socket.emit('message', {
  305. session_id: message.session_id||'',
  306. session_type: sessionType,
  307. from: message.sender_id|| '',
  308. data: message.content,
  309. business_type: message.business_type || 1
  310. });
  311. }
  312. }
  313. }
  314. /**
  315. *
  316. * 发送微信模板消息给居民
  317. *
  318. * @param targetUserId
  319. * @param message
  320. */
  321. static sendViaMessageTemplate(targetUserId, targetUserName, message) {
  322. async.waterfall([
  323. // 获取微信openid
  324. function (callback) {
  325. PatientRepo.findWechatOpenIds(targetUserId, function (err, res) {
  326. if (err) {
  327. ModelUtil.logError("Get wechat openid failed", err);
  328. return;
  329. }
  330. var map = new Map();
  331. res.forEach(function (participant) {
  332. let openid = participant.openid;
  333. if (targetUserId==participant.code) {
  334. if (!openid) {
  335. ModelUtil.logError("User haven't bound with wechat, user id: " + targetUserId);
  336. }
  337. map.set("openid",participant);
  338. }else {
  339. if(!map.has(openid)){
  340. map.set(openid,participant);
  341. }
  342. }
  343. })
  344. //
  345. // let openid = result && result.length > 0 ? result[0].openid : null;
  346. // if (!openid) {
  347. // ModelUtil.logError("User haven't bound with wechat, user id: " + targetUserId);
  348. // return;
  349. // }
  350. //
  351. // log.warn("Send via wechat message template, user id: " + targetUserId + ", openid: " + openid);
  352. callback(null, map);
  353. });
  354. },
  355. // 获取议题信息
  356. function (map, callback) {
  357. TopicRepo.findLastTopicStatusAndType(message.session_id, function (err, res) {
  358. if (err) {
  359. ModelUtil.logError("Get topic failed", err);
  360. return;
  361. }
  362. if (!res || res.length == 0) {
  363. ModelUtil.logError("Unable to find session last topic");
  364. return;
  365. }
  366. callback(null, map, message.sender_name, res[0]);
  367. });
  368. },
  369. // 发送消息
  370. function (map, senderName, topic,callback) {
  371. let replyContent = message.content;
  372. switch (Number.parseInt(message.content_type)) {
  373. case CONTENT_TYPES.Image:
  374. replyContent = "[图片]";
  375. break;
  376. case CONTENT_TYPES.Audio:
  377. replyContent = "[语音]";
  378. break;
  379. default:
  380. break;
  381. }
  382. var patient = map.get("openid");
  383. map.delete("openid");
  384. let agent = topic.agent;
  385. let consultTitle = null;
  386. let description = null;
  387. let url = config.wechatConfig.baseUrl;
  388. switch (topic.type) {
  389. case 8:
  390. consultTitle = "续方";
  391. description = "续方咨询";
  392. url = url + "/wx/html/yszx/html/prescription-consulting.html";
  393. case 11:
  394. consultTitle = "上门服务";
  395. description = "上门服务咨询";
  396. url = url + "wx/html/appoint_service/html/appoint-serviceDetail.html";
  397. default:
  398. consultTitle = "健康";
  399. description = topic.description;
  400. url = url + "/wx/html/yszx/html/consulting-doctor.html";
  401. }
  402. if(agent){//代理人发起的议题
  403. var agentOpenid = "";
  404. if(map.size>0){
  405. for(var key of map.keys()){
  406. var member = map.get(key);
  407. if(agent == member.code){
  408. agentOpenid = key;
  409. var openid = key;
  410. var first = "您的家人("+patient.name+")的"+consultTitle+"咨询有新的回复";
  411. // 发送模板消息
  412. WechatSDK.sendTemplateMessage({
  413. touser: openid,
  414. name: member.name,
  415. patient: member.code,
  416. template_id: config.wechatConfig.template.consultTemplate,
  417. url: url + "?openid=" + openid + "&type="+topic.type+"&doctor="+message.sender_id+
  418. "&consult=" + topic.id + "&toUser=" + member.code + "&toName=" + member.name+"&represented="+patient.code,
  419. data: {
  420. first: {value: first, color: "#000000"}
  421. , remark: {value: "", color: "#000000"}
  422. , keyword1: {value: description, color: "#000000"}
  423. , keyword2: {value: replyContent, color: "#000000"}
  424. , keyword3: {value: senderName, color: "#000000"}
  425. }
  426. }, function (err, res) {
  427. err ? log.error(err) : log.info(res);
  428. });
  429. }
  430. }
  431. }
  432. if(patient.openid&&patient.openid!=agentOpenid){
  433. var first = "您的"+consultTitle+"咨询有新的回复";
  434. // 发送模板消息
  435. WechatSDK.sendTemplateMessage({
  436. touser: patient.openid,
  437. name: targetUserName,
  438. patient: targetUserId,
  439. template_id: config.wechatConfig.template.consultTemplate,
  440. url: url + "?openid=" + patient.openid +"&type="+topic.type+"&doctor="+message.sender_id+
  441. "&consult=" + topic.id + "&toUser=" + targetUserId + "&toName=" + targetUserName+"&represented="+patient.code,
  442. data: {
  443. first: {value: first, color: "#000000"}
  444. , remark: {value: "", color: "#000000"}
  445. , keyword1: {value: description, color: "#000000"}
  446. , keyword2: {value: replyContent, color: "#000000"}
  447. , keyword3: {value: senderName, color: "#000000"}
  448. }
  449. }, function (err, res) {
  450. err ? log.error(err) : log.info(res);
  451. });
  452. }
  453. }else {//自己发起的议题
  454. // 发送模板消息
  455. if(patient.openid){
  456. WechatSDK.sendTemplateMessage({
  457. touser: patient.openid,
  458. name: targetUserName,
  459. patient: targetUserId,
  460. template_id: config.wechatConfig.template.consultTemplate,
  461. url: url + "?openid=" + patient.openid +"&type="+topic.type+"&doctor="+message.sender_id+
  462. "&consult=" + topic.id + "&toUser=" + targetUserId + "&toName=" + targetUserName+"&represented="+patient.code,
  463. data: {
  464. first: {value: "您的"+consultTitle+"咨询有新的回复", color: "#000000"}
  465. , remark: {value: "", color: "#000000"}
  466. , keyword1: {value: description, color: "#000000"}
  467. , keyword2: {value: replyContent, color: "#000000"}
  468. , keyword3: {value: senderName, color: "#000000"}
  469. }
  470. }, function (err, res) {
  471. err ? log.error(err) : log.info(res);
  472. });
  473. }
  474. if(map.size>0){
  475. for(var key of map.keys()){
  476. if(!patient.openid||key!=patient.openid){
  477. var member = map.get(key);
  478. var openid = key;
  479. var first = "您的家人("+patient.name+")的"+consultTitle+"咨询有新的回复";
  480. // 发送模板消息
  481. WechatSDK.sendTemplateMessage({
  482. touser: openid,
  483. name: member.name,
  484. patient: member.code,
  485. template_id: config.wechatConfig.template.consultTemplate,
  486. url: url + "?openid=" + openid +"&type="+topic.type+"&doctor="+message.sender_id+
  487. "&consult=" + topic.id + "&toUser=" + member.code + "&toName=" + member.name+"&represented="+patient.code,
  488. data: {
  489. first: {value: first, color: "#000000"}
  490. , remark: {value: "", color: "#000000"}
  491. , keyword1: {value: description, color: "#000000"}
  492. , keyword2: {value: replyContent, color: "#000000"}
  493. , keyword3: {value: senderName, color: "#000000"}
  494. }
  495. }, function (err, res) {
  496. err ? log.error(err) : log.info(res);
  497. });
  498. }
  499. }
  500. }
  501. }
  502. callback(null, null);
  503. }
  504. ],
  505. function (err, res) {
  506. if (!err) {
  507. log.info("Send via wechat template message, DONE!");
  508. }
  509. });
  510. };
  511. static updateParticipantLastFetchTime(sessionId, userId, score) {
  512. score = score + 1000;
  513. let participantsKey = RedisModel.makeRedisKey(REDIS_KEYS.SessionParticipants, sessionId);
  514. redisConn.zaddAsync(participantsKey, score, userId)
  515. .then(function (res) {
  516. ParticipantRepo.updateLastFetchTime(new Date(score), sessionId, userId, function (err, res) {
  517. if (err) {
  518. log.error("Update participant last fetch time failed: ", err);
  519. }
  520. });
  521. })
  522. .catch(function (err) {
  523. log.error("Update participant last fetch time failed: ", err);
  524. });
  525. }
  526. }
  527. module.exports = WechatClient;