wechat.client.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  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.PrescriptionCheck ||
  60. message.content_type == CONTENT_TYPES.PrescriptionBloodStatus ||
  61. message.content_type == CONTENT_TYPES.PrescriptionFollowupContent)) {
  62. let patientClient = clientCache.findById(targetUserId);
  63. let doctorClient = clientCache.findByIdAndType(message.sender_id,SOCKET_TYPES.DOCTOR);
  64. if (patientClient) {
  65. log.warn("User's wechat endpoint is online, sending via web socket. User id: ", targetUserId);
  66. WechatClient.sendViaWebSocket(patientClient.socket, message);
  67. if(doctorClient){
  68. log.error("doctor sessionid "+doctorClient.sessionId);
  69. log.error("patient sessionid "+patientClient.sessionId);
  70. if(patientClient.sessionId==doctorClient.sessionId){
  71. //用户socket在线,推送给用户后,告知医生此消息已读
  72. WechatClient.updateParticipantLastFetchTime(doctorClient.sessionId, targetUserId, ObjectUtil.timestampToLong(message.timestamp));
  73. WechatClient.sendReadDoctor(doctorClient.socket, message);
  74. }
  75. }else{
  76. log.error("doctor client not found");
  77. }
  78. } else {
  79. log.info("User's wechat endpoint is not online, sending via wechat template message. User id: ", targetUserId);
  80. WechatClient.sendViaMessageTemplate(targetUserId, targetUserName, message);
  81. }
  82. } else if(message.content_type == CONTENT_TYPES.TopicEnd){
  83. let patientClient = clientCache.findById(targetUserId);
  84. if(patientClient)//结束咨询的告知患者
  85. WechatClient.sendViaWebSocket(patientClient.socket, message);
  86. }
  87. };
  88. static sendViaWebSocket(socket, message) {
  89. socket.emit('message', {
  90. id: message.id,
  91. session_id: message.session_id,
  92. sender_id: message.sender_id,
  93. sender_name: message.sender_name,
  94. content_type: message.content_type,
  95. content: message.content,
  96. timestamp: ObjectUtil.timestampToLong(message.timestamp),
  97. type: message.content_type, // legacy support
  98. name: message.sender_name,
  99. sender_img : message.sender_img
  100. });
  101. }
  102. static sendAllRead(doctorId,sessionId){
  103. let doctorClient = clientCache.findByIdAndType(doctorId,SOCKET_TYPES.DOCTOR);
  104. if(doctorClient){
  105. if(doctorClient.sessionId==sessionId){
  106. doctorClient.socket.emit('message',{ read:"all"});
  107. }else{
  108. log.warn(" doctor not in the same session ");
  109. }
  110. }else{
  111. log.warn(doctorId+" target doctor is not online!");
  112. }
  113. }
  114. static sendMucAllRead(doctorId,loginUserId,sessionId){
  115. let loginClinet = clientCache.findByIdAndType(loginUserId,SOCKET_TYPES.DOCTOR);
  116. if(loginClinet){
  117. //muc是医生来获取数据不能更新成已读
  118. log.warn("type is muc login is doctor not send all read to other doctor!")
  119. return;
  120. }
  121. let doctorClient = clientCache.findByIdAndType(doctorId,SOCKET_TYPES.DOCTOR);
  122. if(doctorClient){
  123. if(doctorClient.sessionId==sessionId){
  124. doctorClient.socket.emit('message',{ read:"all"});
  125. }else{
  126. log.warn(" doctor not in the same session ");
  127. }
  128. }else{
  129. log.warn(doctorId+" target doctor is not online!");
  130. }
  131. }
  132. static sendReadDoctor(socket, message) {
  133. socket.emit('message', {
  134. id: message.id,
  135. session_id: message.session_id,
  136. sender_id: message.sender_id,
  137. sender_name: message.sender_name,
  138. content_type: message.content_type,
  139. content: message.content,
  140. timestamp: ObjectUtil.timestampToLong(message.timestamp),
  141. type: message.content_type, // legacy support
  142. name: message.sender_name,
  143. read:"one"
  144. });
  145. }
  146. static sendReadDoctorByDoctorId(doctorId, message) {
  147. let doctorClient = clientCache.findByIdAndType(doctorId,SOCKET_TYPES.DOCTOR);
  148. let pc_doctorClient = clientCache.findByIdAndType("pc_"+doctorId,SOCKET_TYPES.PC_DOCTOR);
  149. if(!doctorClient&&!pc_doctorClient){
  150. log.warn("target doctor is not online!");
  151. return;
  152. }
  153. let sendDoctorClient = clientCache.findByIdAndType(message.sender_id,SOCKET_TYPES.DOCTOR);
  154. if(!sendDoctorClient){
  155. sendDoctorClient = clientCache.findByIdAndType("pc_"+message.sender_id,SOCKET_TYPES.PC_DOCTOR);
  156. }
  157. var count = 0;
  158. if(doctorClient&&sendDoctorClient&&sendDoctorClient.sessionId==doctorClient.sessionId){
  159. WechatClient.updateParticipantLastFetchTime(doctorClient.sessionId, doctorId, ObjectUtil.timestampToLong(message.timestamp));
  160. sendDoctorClient.socket.emit('message', {
  161. id: message.id,
  162. session_id: message.session_id,
  163. sender_id: message.sender_id,
  164. sender_name: message.sender_name,
  165. content_type: message.content_type,
  166. content: message.content,
  167. timestamp: ObjectUtil.timestampToLong(message.timestamp),
  168. type: message.content_type, // legacy support
  169. name: message.sender_name,
  170. read:"one"
  171. });
  172. }else{
  173. count++;
  174. }
  175. //发送pc版医生端
  176. if(pc_doctorClient&&sendDoctorClient&&sendDoctorClient.sessionId==pc_doctorClient.sessionId){
  177. WechatClient.updateParticipantLastFetchTime(pc_doctorClient.sessionId, doctorId, ObjectUtil.timestampToLong(message.timestamp));
  178. pc_doctorClient.socket.emit('message', {
  179. id: message.id,
  180. session_id: message.session_id,
  181. sender_id: message.sender_id,
  182. sender_name: message.sender_name,
  183. content_type: message.content_type,
  184. content: message.content,
  185. timestamp: ObjectUtil.timestampToLong(message.timestamp),
  186. type: message.content_type, // legacy support
  187. name: message.sender_name,
  188. read:"one"
  189. });
  190. }else{
  191. count++;
  192. }
  193. if(count==2){
  194. log.warn("doctor is not in the same session or is not online");
  195. }
  196. }
  197. static sendSocketMessageToDoctor(doctorId, message) {
  198. let doctorClient = clientCache.findByIdAndType(doctorId,SOCKET_TYPES.DOCTOR);
  199. let pc_doctorClient = clientCache.findByIdAndType("pc_"+doctorId,SOCKET_TYPES.PC_DOCTOR);
  200. if(!doctorClient&&!pc_doctorClient){
  201. log.warn("target doctor is not online!");
  202. return;
  203. }
  204. let sendClient = clientCache.findByIdAndType(message.sender_id,SOCKET_TYPES.DOCTOR);//app医生发送的消息
  205. if(!sendClient){//pc医生发送的消息
  206. sendClient = clientCache.findByIdAndType("pc_"+message.sender_id,SOCKET_TYPES.PC_DOCTOR);
  207. }
  208. if(!sendClient){//居民发送的消息
  209. sendClient = clientCache.findByIdAndType(message.sender_id,SOCKET_TYPES.PATIENT);
  210. }
  211. var count = 0;
  212. if(doctorClient&&sendClient&&sendClient.sessionId==doctorClient.sessionId){
  213. WechatClient.updateParticipantLastFetchTime(doctorClient.sessionId, doctorId, ObjectUtil.timestampToLong(message.timestamp));
  214. doctorClient.socket.emit('message', {
  215. id: message.id,
  216. session_id: message.session_id,
  217. sender_id: message.sender_id,
  218. sender_name: message.sender_name,
  219. content_type: message.content_type,
  220. content: message.content,
  221. timestamp: ObjectUtil.timestampToLong(message.timestamp),
  222. type: message.content_type, // legacy support
  223. name: message.sender_name,
  224. });
  225. }else{
  226. count++;
  227. }
  228. //发送pc端
  229. if(pc_doctorClient&&sendClient&&sendClient.sessionId==pc_doctorClient.sessionId){
  230. WechatClient.updateParticipantLastFetchTime(pc_doctorClient.sessionId, doctorId, ObjectUtil.timestampToLong(message.timestamp));
  231. pc_doctorClient.socket.emit('message', {
  232. id: message.id,
  233. session_id: message.session_id,
  234. sender_id: message.sender_id,
  235. sender_name: message.sender_name,
  236. content_type: message.content_type,
  237. content: message.content,
  238. timestamp: ObjectUtil.timestampToLong(message.timestamp),
  239. type: message.content_type, // legacy support
  240. name: message.sender_name,
  241. });
  242. }else{
  243. count++;
  244. }
  245. if(count==2){
  246. log.warn("doctor is not in the same session or is not online");
  247. }
  248. }
  249. static sendPcImSocket(targetId, message, sessionType) {
  250. if (message.content_type == CONTENT_TYPES.PlainText ||
  251. message.content_type == CONTENT_TYPES.Image ||
  252. message.content_type == CONTENT_TYPES.Audio||
  253. message.content_type == CONTENT_TYPES.Video||
  254. message.content_type == CONTENT_TYPES.GoTo||
  255. sessionType==SESSION_TYPES.SYSTEM) {
  256. let pcim_doctorClient = clientCache.findByIdAndType("pcim_"+targetId,SOCKET_TYPES.PCIM_DOCTOR);
  257. if(pcim_doctorClient) {
  258. let customData = {
  259. session_id: message.session_id||'',
  260. session_type: sessionType,
  261. from: message.sender_id|| '',
  262. data: message.content,
  263. business_type: message.business_type || 1
  264. };
  265. pcim_doctorClient.socket.emit('message', {
  266. session_id: message.session_id||'',
  267. session_type: sessionType,
  268. from: message.sender_id|| '',
  269. data: message.content,
  270. business_type: message.business_type || 1
  271. });
  272. }
  273. }
  274. }
  275. /**
  276. * 发送微信模板消息给居民
  277. *
  278. * @param targetUserId
  279. * @param message
  280. */
  281. static sendViaMessageTemplate(targetUserId, targetUserName, message) {
  282. async.waterfall([
  283. // 获取微信openid
  284. function (callback) {
  285. PatientRepo.findWechatOpenIds(targetUserId, function (err, res) {
  286. if (err) {
  287. ModelUtil.logError("Get wechat openid failed", err);
  288. return;
  289. }
  290. var map = new Map();
  291. res.forEach(function (participant) {
  292. let openid = participant.openid;
  293. if (targetUserId==participant.code) {
  294. if (!openid) {
  295. ModelUtil.logError("User haven't bound with wechat, user id: " + targetUserId);
  296. }
  297. map.set("openid",participant);
  298. }else {
  299. if(!map.has(openid)){
  300. map.set(openid,participant);
  301. }
  302. }
  303. })
  304. //
  305. // let openid = result && result.length > 0 ? result[0].openid : null;
  306. // if (!openid) {
  307. // ModelUtil.logError("User haven't bound with wechat, user id: " + targetUserId);
  308. // return;
  309. // }
  310. //
  311. // log.warn("Send via wechat message template, user id: " + targetUserId + ", openid: " + openid);
  312. callback(null, map);
  313. });
  314. },
  315. // 获取议题信息
  316. function (map, callback) {
  317. TopicRepo.findLastTopicStatusAndType(message.session_id, function (err, res) {
  318. if (err) {
  319. ModelUtil.logError("Get topic failed", err);
  320. return;
  321. }
  322. if (!res || res.length == 0) {
  323. ModelUtil.logError("Unable to find session last topic");
  324. return;
  325. }
  326. callback(null, map, message.sender_name, res[0]);
  327. });
  328. },
  329. // 发送消息
  330. function (map, senderName, topic,callback) {
  331. let replyContent = message.content;
  332. switch (Number.parseInt(message.content_type)) {
  333. case CONTENT_TYPES.Image:
  334. replyContent = "[图片]";
  335. break;
  336. case CONTENT_TYPES.Audio:
  337. replyContent = "[语音]";
  338. break;
  339. default:
  340. break;
  341. }
  342. var patient = map.get("openid");
  343. map.delete("openid");
  344. let agent = topic.agent;
  345. let consultTitle = topic.type==8?"续方":"健康";
  346. let description = topic.type==8?"续方咨询":topic.description;
  347. let url = config.wechatConfig.baseUrl + (topic.type==8?"/wx/html/yszx/html/prescription-consulting.html":"/wx/html/yszx/html/consulting-doctor.html");
  348. if(agent){//代理人发起的议题
  349. var agentOpenid = "";
  350. if(map.size>0){
  351. for(var key of map.keys()){
  352. var member = map.get(key);
  353. if(agent == member.code){
  354. agentOpenid = key;
  355. var openid = key;
  356. var first = "您的家人("+patient.name+")的"+consultTitle+"咨询有新的回复";
  357. // 发送模板消息
  358. WechatSDK.sendTemplateMessage({
  359. touser: openid,
  360. name: member.name,
  361. patient: member.code,
  362. template_id: config.wechatConfig.template.consultTemplate,
  363. url: url + "?openid=" + openid + "&type="+topic.type+"&doctor="+message.sender_id+
  364. "&consult=" + topic.id + "&toUser=" + member.code + "&toName=" + member.name+"&represented="+patient.code,
  365. data: {
  366. first: {value: first, color: "#000000"}
  367. , remark: {value: "", color: "#000000"}
  368. , keyword1: {value: description, color: "#000000"}
  369. , keyword2: {value: replyContent, color: "#000000"}
  370. , keyword3: {value: senderName, color: "#000000"}
  371. }
  372. }, function (err, res) {
  373. err ? log.error(err) : log.info(res);
  374. });
  375. }
  376. }
  377. }
  378. if(patient.openid&&patient.openid!=agentOpenid){
  379. var first = "您的"+consultTitle+"咨询有新的回复";
  380. // 发送模板消息
  381. WechatSDK.sendTemplateMessage({
  382. touser: patient.openid,
  383. name: targetUserName,
  384. patient: targetUserId,
  385. template_id: config.wechatConfig.template.consultTemplate,
  386. url: url + "?openid=" + patient.openid +"&type="+topic.type+"&doctor="+message.sender_id+
  387. "&consult=" + topic.id + "&toUser=" + targetUserId + "&toName=" + targetUserName+"&represented="+patient.code,
  388. data: {
  389. first: {value: first, color: "#000000"}
  390. , remark: {value: "", color: "#000000"}
  391. , keyword1: {value: description, color: "#000000"}
  392. , keyword2: {value: replyContent, color: "#000000"}
  393. , keyword3: {value: senderName, color: "#000000"}
  394. }
  395. }, function (err, res) {
  396. err ? log.error(err) : log.info(res);
  397. });
  398. }
  399. }else {//自己发起的议题
  400. // 发送模板消息
  401. if(patient.openid){
  402. WechatSDK.sendTemplateMessage({
  403. touser: patient.openid,
  404. name: targetUserName,
  405. patient: targetUserId,
  406. template_id: config.wechatConfig.template.consultTemplate,
  407. url: url + "?openid=" + patient.openid +"&type="+topic.type+"&doctor="+message.sender_id+
  408. "&consult=" + topic.id + "&toUser=" + targetUserId + "&toName=" + targetUserName+"&represented="+patient.code,
  409. data: {
  410. first: {value: "您的"+consultTitle+"咨询有新的回复", color: "#000000"}
  411. , remark: {value: "", color: "#000000"}
  412. , keyword1: {value: description, color: "#000000"}
  413. , keyword2: {value: replyContent, color: "#000000"}
  414. , keyword3: {value: senderName, color: "#000000"}
  415. }
  416. }, function (err, res) {
  417. err ? log.error(err) : log.info(res);
  418. });
  419. }
  420. if(map.size>0){
  421. for(var key of map.keys()){
  422. if(!patient.openid||key!=patient.openid){
  423. var member = map.get(key);
  424. var openid = key;
  425. var first = "您的家人("+patient.name+")的"+consultTitle+"咨询有新的回复";
  426. // 发送模板消息
  427. WechatSDK.sendTemplateMessage({
  428. touser: openid,
  429. name: member.name,
  430. patient: member.code,
  431. template_id: config.wechatConfig.template.consultTemplate,
  432. url: url + "?openid=" + openid +"&type="+topic.type+"&doctor="+message.sender_id+
  433. "&consult=" + topic.id + "&toUser=" + member.code + "&toName=" + member.name+"&represented="+patient.code,
  434. data: {
  435. first: {value: first, color: "#000000"}
  436. , remark: {value: "", color: "#000000"}
  437. , keyword1: {value: description, color: "#000000"}
  438. , keyword2: {value: replyContent, color: "#000000"}
  439. , keyword3: {value: senderName, color: "#000000"}
  440. }
  441. }, function (err, res) {
  442. err ? log.error(err) : log.info(res);
  443. });
  444. }
  445. }
  446. }
  447. }
  448. callback(null, null);
  449. }
  450. ],
  451. function (err, res) {
  452. if (!err) {
  453. log.info("Send via wechat template message, DONE!");
  454. }
  455. });
  456. };
  457. static updateParticipantLastFetchTime(sessionId, userId, score) {
  458. score = score + 1000;
  459. let participantsKey = RedisModel.makeRedisKey(REDIS_KEYS.SessionParticipants, sessionId);
  460. redisConn.zaddAsync(participantsKey, score, userId)
  461. .then(function (res) {
  462. ParticipantRepo.updateLastFetchTime(new Date(score), sessionId, userId, function (err, res) {
  463. if (err) {
  464. log.error("Update participant last fetch time failed: ", err);
  465. }
  466. });
  467. })
  468. .catch(function (err) {
  469. log.error("Update participant last fetch time failed: ", err);
  470. });
  471. }
  472. }
  473. module.exports = WechatClient;