wechat.client.js 24 KB

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