wechat.client.js 26 KB

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