wechat.client.js 25 KB

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