wechat.client.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  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 SOCKET_TYPES = require('../../include/commons').SOCKET_TYPES;
  22. const REDIS_KEYS = require('../../include/commons').REDIS_KEYS;
  23. class WechatClient extends RedisModel {
  24. constructor() {
  25. super();
  26. }
  27. /**
  28. * 取得用户微信端状态。若Redis中找不到,则从MySQL中查找。
  29. *
  30. * @param userId
  31. * @param handler
  32. */
  33. static getWechatStatus(userId, handler) {
  34. redisConn.hgetallAsync(RedisModel.makeRedisKey(REDIS_KEYS.UserWechatStatus, userId))
  35. .then(function (status) {
  36. if (status == null) {
  37. PatientRepo.findWechatOpenId(userId, handler);
  38. } else {
  39. handler(null, {openid: status.openid});
  40. }
  41. })
  42. .catch(function (err) {
  43. handler(err, null);
  44. });
  45. }
  46. /**
  47. * 向微信端用户发送消息。若用户微信端在线,通过Web Socket推送给患者,如果不在线则通过微信的模板消息。
  48. *
  49. * 只推送文本、图片及语音消息
  50. *
  51. * @param targetUserId
  52. * @param message 消息体
  53. */
  54. static sendMessage(targetUserId, targetUserName, message) {
  55. if (message&&(message.content_type == CONTENT_TYPES.PlainText ||
  56. message.content_type == CONTENT_TYPES.Image ||
  57. message.content_type == CONTENT_TYPES.Audio)) {
  58. let patientClient = clientCache.findById(targetUserId);
  59. let doctorClient = clientCache.findByIdAndType(message.sender_id,SOCKET_TYPES.DOCTOR);
  60. if (patientClient) {
  61. log.warn("User's wechat endpoint is online, sending via web socket. User id: ", targetUserId);
  62. WechatClient.sendViaWebSocket(patientClient.socket, message);
  63. if(doctorClient){
  64. log.error("doctor sessionid "+doctorClient.sessionId);
  65. log.error("patient sessionid "+patientClient.sessionId);
  66. if(patientClient.sessionId==doctorClient.sessionId){
  67. //用户socket在线,推送给用户后,告知医生此消息已读
  68. //WechatClient.updateParticipantLastFetchTime(doctorClient.sessionId, targetUserId, ObjectUtil.timestampToLong(message.timestamp));
  69. WechatClient.updateParticipantLastFetchTime(doctorClient.sessionId, message.sender_id, ObjectUtil.timestampToLong(message.timestamp));
  70. WechatClient.sendReadDoctor(doctorClient.socket, message);
  71. }
  72. }else{
  73. log.error("doctor client not found");
  74. }
  75. } else {
  76. log.info("User's wechat endpoint is not online, sending via wechat template message. User id: ", targetUserId);
  77. WechatClient.sendViaMessageTemplate(targetUserId, targetUserName, message);
  78. }
  79. } else if(message.content_type == CONTENT_TYPES.TopicEnd){
  80. let patientClient = clientCache.findById(targetUserId);
  81. if(patientClient)//结束咨询的告知患者
  82. WechatClient.sendViaWebSocket(patientClient.socket, message);
  83. }
  84. };
  85. static sendViaWebSocket(socket, message) {
  86. socket.emit('message', {
  87. id: message.id,
  88. session_id: message.session_id,
  89. sender_id: message.sender_id,
  90. sender_name: message.sender_name,
  91. content_type: message.content_type,
  92. content: message.content,
  93. timestamp: ObjectUtil.timestampToLong(message.timestamp),
  94. type: message.content_type, // legacy support
  95. name: message.sender_name,
  96. sender_img : message.sender_img
  97. });
  98. }
  99. static sendAllRead(doctorId,sessionId){
  100. let doctorClient = clientCache.findByIdAndType(doctorId,SOCKET_TYPES.DOCTOR);
  101. if(doctorClient){
  102. if(doctorClient.sessionId==sessionId){
  103. doctorClient.socket.emit('message',{ read:"all"});
  104. }else{
  105. log.warn(" doctor not in the same session ");
  106. }
  107. }else{
  108. log.warn(doctorId+" target doctor is not online!");
  109. }
  110. }
  111. static sendMucAllRead(doctorId,loginUserId,sessionId){
  112. let loginClinet = clientCache.findByIdAndType(loginUserId,SOCKET_TYPES.DOCTOR);
  113. if(loginClinet){
  114. //muc是医生来获取数据不能更新成已读
  115. log.warn("type is muc login is doctor not send all read to other doctor!")
  116. return;
  117. }
  118. let doctorClient = clientCache.findByIdAndType(doctorId,SOCKET_TYPES.DOCTOR);
  119. if(doctorClient){
  120. if(doctorClient.sessionId==sessionId){
  121. doctorClient.socket.emit('message',{ read:"all"});
  122. }else{
  123. log.warn(" doctor not in the same session ");
  124. }
  125. }else{
  126. log.warn(doctorId+" target doctor is not online!");
  127. }
  128. }
  129. static sendReadDoctor(socket, message) {
  130. socket.emit('message', {
  131. id: message.id,
  132. session_id: message.session_id,
  133. sender_id: message.sender_id,
  134. sender_name: message.sender_name,
  135. content_type: message.content_type,
  136. content: message.content,
  137. timestamp: ObjectUtil.timestampToLong(message.timestamp),
  138. type: message.content_type, // legacy support
  139. name: message.sender_name,
  140. read:"one"
  141. });
  142. }
  143. static sendReadDoctorByDoctorId(doctorId, message) {
  144. let doctorClient = clientCache.findByIdAndType(doctorId,SOCKET_TYPES.DOCTOR);
  145. // let pc_doctorClient = clientCache.findByIdAndType("pc_"+doctorId,SOCKET_TYPES.PC_DOCTOR);
  146. if(!doctorClient){
  147. log.warn("target doctor is not online!");
  148. return;
  149. }
  150. let sendDoctorClient = clientCache.findByIdAndType(message.sender_id,SOCKET_TYPES.DOCTOR);
  151. // let pc_sendDoctorClient = clientCache.findByIdAndType("pc_"+message.sender_id,SOCKET_TYPES.PC_DOCTOR);
  152. if(sendDoctorClient&&sendDoctorClient.sessionId==doctorClient.sessionId){
  153. WechatClient.updateParticipantLastFetchTime(doctorClient.sessionId, doctorId, ObjectUtil.timestampToLong(message.timestamp));
  154. sendDoctorClient.socket.emit('message', {
  155. id: message.id,
  156. session_id: message.session_id,
  157. sender_id: message.sender_id,
  158. sender_name: message.sender_name,
  159. content_type: message.content_type,
  160. content: message.content,
  161. timestamp: ObjectUtil.timestampToLong(message.timestamp),
  162. type: message.content_type, // legacy support
  163. name: message.sender_name,
  164. read:"one"
  165. });
  166. }else{
  167. log.warn("doctor is not in the same session or not online");
  168. }
  169. //发送pc版医生端
  170. // if(pc_doctorClient&&pc_sendDoctorClient&&pc_sendDoctorClient.sessionId==pc_doctorClient.sessionId){
  171. // WechatClient.updateParticipantLastFetchTime(pc_doctorClient.sessionId, doctorId, ObjectUtil.timestampToLong(message.timestamp));
  172. // pc_sendDoctorClient.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. // }else{
  185. // log.warn("doctor is not in the same session or not online");
  186. // }
  187. }
  188. static sendSocketMessageToDoctor(doctorId, message) {
  189. let doctorClient = clientCache.findByIdAndType(doctorId,SOCKET_TYPES.DOCTOR);
  190. // let pc_doctorClient = clientCache.findByIdAndType("pc_"+doctorId,SOCKET_TYPES.PC_DOCTOR);
  191. if(!doctorClient){
  192. log.warn("target doctor is not online!");
  193. return;
  194. }
  195. let sendClient = clientCache.findByIdAndType(message.sender_id,SOCKET_TYPES.DOCTOR);//app医生发送的消息
  196. // if(!sendClient){//pc医生发送的消息
  197. // sendClient = clientCache.findByIdAndType("pc_"+message.sender_id,SOCKET_TYPES.PC_DOCTOR);
  198. // }
  199. if(!sendClient){//居民发送的消息
  200. sendClient = clientCache.findByIdAndType(message.sender_id,SOCKET_TYPES.PATIENT);
  201. }
  202. if(sendClient&&sendClient.sessionId==doctorClient.sessionId){
  203. WechatClient.updateParticipantLastFetchTime(doctorClient.sessionId, doctorId, ObjectUtil.timestampToLong(message.timestamp));
  204. 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. });
  215. }else{
  216. log.warn("doctor is not in the same session or is not online");
  217. }
  218. //发送pc端
  219. // if(pc_doctorClient&&sendClient&&sendClient.sessionId==pc_doctorClient.sessionId){
  220. // WechatClient.updateParticipantLastFetchTime(pc_doctorClient.sessionId, doctorId, ObjectUtil.timestampToLong(message.timestamp));
  221. // pc_doctorClient.socket.emit('message', {
  222. // id: message.id,
  223. // session_id: message.session_id,
  224. // sender_id: message.sender_id,
  225. // sender_name: message.sender_name,
  226. // content_type: message.content_type,
  227. // content: message.content,
  228. // timestamp: ObjectUtil.timestampToLong(message.timestamp),
  229. // type: message.content_type, // legacy support
  230. // name: message.sender_name,
  231. // });
  232. // }else{
  233. // log.warn("doctor is not in the same session or is not online");
  234. // }
  235. }
  236. /**
  237. * 发送微信模板消息给居民
  238. *
  239. * @param targetUserId
  240. * @param message
  241. */
  242. static sendViaMessageTemplate(targetUserId, targetUserName, message) {
  243. async.waterfall([
  244. // 获取微信openid
  245. function (callback) {
  246. PatientRepo.findWechatOpenIds(targetUserId, function (err, res) {
  247. if (err) {
  248. ModelUtil.logError("Get wechat openid failed", err);
  249. return;
  250. }
  251. var map = new Map();
  252. res.forEach(function (participant) {
  253. let openid = participant.openid;
  254. if (targetUserId==participant.code) {
  255. if (!openid) {
  256. ModelUtil.logError("User haven't bound with wechat, user id: " + targetUserId);
  257. }
  258. map.set("openid",participant);
  259. }else {
  260. if(!map.has(openid)){
  261. map.set(openid,participant);
  262. }
  263. }
  264. })
  265. //
  266. // let openid = result && result.length > 0 ? result[0].openid : null;
  267. // if (!openid) {
  268. // ModelUtil.logError("User haven't bound with wechat, user id: " + targetUserId);
  269. // return;
  270. // }
  271. //
  272. // log.warn("Send via wechat message template, user id: " + targetUserId + ", openid: " + openid);
  273. callback(null, map);
  274. });
  275. },
  276. // 获取议题信息
  277. function (map, callback) {
  278. TopicRepo.findLastTopicStatusAndType(message.session_id, function (err, res) {
  279. if (err) {
  280. ModelUtil.logError("Get topic failed", err);
  281. return;
  282. }
  283. if (!res || res.length == 0) {
  284. ModelUtil.logError("Unable to find session last topic");
  285. return;
  286. }
  287. callback(null, map, message.sender_name, res[0]);
  288. });
  289. },
  290. // 发送消息
  291. function (map, senderName, topic,callback) {
  292. let replyContent = message.content;
  293. switch (Number.parseInt(message.content_type)) {
  294. case CONTENT_TYPES.Image:
  295. replyContent = "[图片]";
  296. break;
  297. case CONTENT_TYPES.Audio:
  298. replyContent = "[语音]";
  299. break;
  300. default:
  301. break;
  302. }
  303. var patient = map.get("openid");
  304. map.delete("openid");
  305. let agent = topic.agent;
  306. let url = config.wechatConfig.baseUrl + "/wx/html/yszx/html/consulting-doctor.html";
  307. if(agent){//代理人发起的议题
  308. var agentOpenid = "";
  309. if(map.size>0){
  310. for(var key of map.keys()){
  311. var member = map.get(key);
  312. if(agent == member.code){
  313. agentOpenid = key;
  314. var openid = key;
  315. var first = "您的家人("+patient.name+")的健康咨询有新的回复";
  316. // 发送模板消息
  317. WechatSDK.sendTemplateMessage({
  318. touser: openid,
  319. template_id: config.wechatConfig.template.consultTemplate,
  320. url: url + "?openid=" + openid + "&type="+topic.type+"&doctor="+message.sender_id+
  321. "&consult=" + topic.id + "&toUser=" + member.code + "&toName=" + member.name+"&represented="+patient.code,
  322. data: {
  323. first: {value: first, color: "#000000"}
  324. , remark: {value: "", color: "#000000"}
  325. , keyword1: {value: topic.description, color: "#000000"}
  326. , keyword2: {value: replyContent, color: "#000000"}
  327. , keyword3: {value: senderName, color: "#000000"}
  328. }
  329. }, function (err, res) {
  330. err ? log.error(err) : log.info(res);
  331. });
  332. }
  333. }
  334. }
  335. if(patient.openid&&patient.openid!=agentOpenid){
  336. var first = "您的健康咨询有新的回复";
  337. // 发送模板消息
  338. WechatSDK.sendTemplateMessage({
  339. touser: patient.openid,
  340. template_id: config.wechatConfig.template.consultTemplate,
  341. url: url + "?openid=" + patient.openid +"&type="+topic.type+"&doctor="+message.sender_id+
  342. "&consult=" + topic.id + "&toUser=" + targetUserId + "&toName=" + targetUserName+"&represented="+patient.code,
  343. data: {
  344. first: {value: first, color: "#000000"}
  345. , remark: {value: "", color: "#000000"}
  346. , keyword1: {value: topic.description, color: "#000000"}
  347. , keyword2: {value: replyContent, color: "#000000"}
  348. , keyword3: {value: senderName, color: "#000000"}
  349. }
  350. }, function (err, res) {
  351. err ? log.error(err) : log.info(res);
  352. });
  353. }
  354. }else {//自己发起的议题
  355. // 发送模板消息
  356. if(patient.openid){
  357. WechatSDK.sendTemplateMessage({
  358. touser: patient.openid,
  359. template_id: config.wechatConfig.template.consultTemplate,
  360. url: url + "?openid=" + patient.openid +"&type="+topic.type+"&doctor="+message.sender_id+
  361. "&consult=" + topic.id + "&toUser=" + targetUserId + "&toName=" + targetUserName+"&represented="+patient.code,
  362. data: {
  363. first: {value: "您的健康咨询有新的回复", color: "#000000"}
  364. , remark: {value: "", color: "#000000"}
  365. , keyword1: {value: topic.description, color: "#000000"}
  366. , keyword2: {value: replyContent, color: "#000000"}
  367. , keyword3: {value: senderName, color: "#000000"}
  368. }
  369. }, function (err, res) {
  370. err ? log.error(err) : log.info(res);
  371. });
  372. }
  373. if(map.size>0){
  374. for(var key of map.keys()){
  375. if(!patient.openid||key!=patient.openid){
  376. var member = map.get(key);
  377. var openid = key;
  378. var first = "您的家人("+patient.name+")的健康咨询有新的回复";
  379. // 发送模板消息
  380. WechatSDK.sendTemplateMessage({
  381. touser: openid,
  382. template_id: config.wechatConfig.template.consultTemplate,
  383. url: url + "?openid=" + openid +"&type="+topic.type+"&doctor="+message.sender_id+
  384. "&consult=" + topic.id + "&toUser=" + member.code + "&toName=" + member.name+"&represented="+patient.code,
  385. data: {
  386. first: {value: first, color: "#000000"}
  387. , remark: {value: "", color: "#000000"}
  388. , keyword1: {value: topic.description, color: "#000000"}
  389. , keyword2: {value: replyContent, color: "#000000"}
  390. , keyword3: {value: senderName, color: "#000000"}
  391. }
  392. }, function (err, res) {
  393. err ? log.error(err) : log.info(res);
  394. });
  395. }
  396. }
  397. }
  398. }
  399. callback(null, null);
  400. }
  401. ],
  402. function (err, res) {
  403. if (!err) {
  404. log.info("Send via wechat template message, DONE!");
  405. }
  406. });
  407. };
  408. static updateParticipantLastFetchTime(sessionId, userId, score) {
  409. score = score + 1000;
  410. let participantsKey = RedisModel.makeRedisKey(REDIS_KEYS.SessionParticipants, sessionId);
  411. redisConn.zaddAsync(participantsKey, score, userId)
  412. .then(function (res) {
  413. ParticipantRepo.updateLastFetchTime(new Date(score), sessionId, userId, function (err, res) {
  414. if (err) {
  415. log.error("Update participant last fetch time failed: ", err);
  416. }
  417. });
  418. })
  419. .catch(function (err) {
  420. log.error("Update participant last fetch time failed: ", err);
  421. });
  422. }
  423. }
  424. module.exports = WechatClient;