Ver código fonte

Merge remote-tracking branch 'remotes/origin/feature-refactor' into dev

# Conflicts:
#	src/server/include/commons.js
#	src/server/resources/config/config.local.js
#	src/server/resources/config/config.test.js
shenzaixin 6 anos atrás
pai
commit
d168791521
36 arquivos alterados com 1869 adições e 319 exclusões
  1. 102 14
      readme.md
  2. 3 3
      src/server/app.js
  3. 2 0
      src/server/endpoints/url.initializer.js
  4. 50 0
      src/server/endpoints/v2/message.endpoint.js
  5. 87 4
      src/server/endpoints/v2/session.endpoint.js
  6. 19 1
      src/server/endpoints/v2/topic.endpoint.js
  7. 1 1
      src/server/endpoints/v2/user.endpoint.js
  8. 17 5
      src/server/handlers/socket.handler.js
  9. 33 8
      src/server/include/commons.js
  10. 35 22
      src/server/include/endpoints.js
  11. 100 36
      src/server/models/client/app.client.js
  12. 133 49
      src/server/models/client/wechat.client.js
  13. 111 1
      src/server/models/messages/messages.js
  14. 21 4
      src/server/models/redis/pubSub.js
  15. 3 3
      src/server/models/redis/redisPubClient.js
  16. 3 3
      src/server/models/redis/redisSubClient.js
  17. 10 1
      src/server/models/sessions/participants.js
  18. 508 43
      src/server/models/sessions/sessions.js
  19. 30 5
      src/server/models/sessions/topics.js
  20. 19 0
      src/server/models/socket.io/pcDoctor.client.js
  21. 10 2
      src/server/models/user/users.js
  22. 56 0
      src/server/repository/mysql/message.noticeSetting.repo.js
  23. 15 1
      src/server/repository/mysql/message.repo.js
  24. 28 5
      src/server/repository/mysql/participant.repo.js
  25. 1 1
      src/server/repository/mysql/patient.repo.js
  26. 8 8
      src/server/repository/mysql/search.repo.js
  27. 135 14
      src/server/repository/mysql/session.repo.js
  28. 51 0
      src/server/repository/mysql/sign.family.repo.js
  29. 56 3
      src/server/repository/mysql/topics.repo.js
  30. 22 2
      src/server/repository/mysql/wechat.token.repo.js
  31. 42 55
      src/server/resources/config/config.dev.js
  32. 22 6
      src/server/resources/config/config.prodPC.js
  33. 30 7
      src/server/resources/config/config.prod.js
  34. 31 12
      src/server/resources/config/config.test.js
  35. 20 0
      src/server/util/wechat.sdk.js
  36. 55 0
      src/server/util/wlyyAssistant.sdk.js

+ 102 - 14
readme.md

@ -120,29 +120,38 @@ REST API遵循REST最佳实践,规范命名URL中的每个部分。注意POST
    Sessions: {
        Base: '/api/v2/sessions',
        Session: '/:session_id/session',
        Session: '/:session_id/session',                                // 获取会话
        SessionListByType: '/sessionListByType',                        // 按会话类型获取会话
        SessionCountByType: '/sessionCountByType',                      // 按会话类型获取会话数量
        SessionSticky: '/:session_id/sticky',                           // 会话置顶,置顶使用PUT,取消置顶使用DELETE
        SessionStatus: '/:session_id/status',                           //更新状态
        SessionStatus: '/:session_id/status',                           // 更新状态
        SessionName: '/:session_id/name',                               // 更新会话名称
        RecentSessions: '/recent',                                      // 最近会话,使用类型过滤出'患者'或'医生'会话
        IsExist: '/isExist',                                            // 判断会话是否存在(i健康发送muc的im消息会话前会先调用创建会话的方法,这个方法会更新redis中每个成员的最后获取时间,导致未读消息不准)
        Topics: '/:topic_id/topics',
        Topic: '/topics/:topic_id',                          // 议题,指定ID的议题将返回其信息
        Topics: '/:topic_id/topics',                                    // 获取单个议题
        Topic: '/topics/:topic_id',                                     // 议题,指定ID的议题将返回其信息
        TopicEnded: '/:session_id/topics/:topic_id/ended',              // 议题是否已结束,若top_id为current,则检查最后一个议题的状态
        TopicList:'/topics',
        TopicReplyCount:"/topics/count/reply",
        TopicMessages:'/topic/:topic_id/messages',
        Messages: '/:session_id/messages',                             // 会话消息
        TopicInto: '/:session_id/topics/:topic_id/into',                // 居民进入议题
        TopicList:'/topics',                                            //获取议题列表
        HealthTopicList:'/healthTopics',                                //健康咨询
        TopicListByType:'/topicListByType',                             //按类型查找咨询(未回复,进行中,已回复)
        TopicListCountByType:'/topicListCountByType',                   //按类型查找咨询(未回复,进行中,已回复)的数量
        HealthTeamTopicList:'/healthTeamTopics',                        //健康咨询(区分团队)
        TopicReplyCount:"/topics/count/reply",                          //议题回复数统计
        TopicMessages:'/topic/:topic_id/messages',                      //议题消息
        Messages: '/:session_id/messages',                              // 会话消息
        MessagesByTopic: '/:session_id/topics/:topic_id/messages',      // 议题消息
        Message: '/:session_id/messages/:message_id',                   // 单条消息
        SessionsUnreadMessageCount: '/unread_message_count',            // 所有会话的未读消息数
        SessionUnreadMessageCount: '/:session_id/unread_message_count', // 指定会话的未读消息数
        SessionUnreadMessages: '/:session_id/messages/unread',           // 会话未读消息
        SessionUnreadMessages: '/:session_id/messages/unread',          // 会话未读消息
        ParticipantUpdate:'/:session_id/participant/update',//更新成员=删除旧成员,新增新成员
        ParticipantUpdate:'/:session_id/participant/update',            //更新成员=删除旧成员,新增新成员
        Participants: '/:session_id/participants',                      // 会话所有成员
        ParticipantsAvatar: '/:session_id/participants/avatars',        // 会话所有成员头像
        Participant: '/:session_id/participants/:participant_id',       // 会话单个成员
        Participant: '/:session_id/participants/:participant_id',       // 会话单个成员(多个participant_id 用英文逗号间隔)
        ParticipantAvatar: '/:session_id/participants/:participant_id/avatars'  // 会话单个成员头像
    }
### 业务分析
@ -164,18 +173,40 @@ REST API遵循REST最佳实践,规范命名URL中的每个部分。注意POST
            11、预留
            12、小视频消息
            13、预留
            
            14、进入议题 系统发送的会话消息
            15、续方审核消息消息
            16、续方咨询血糖血压咨询消息
            17、续方咨询随访问卷消息
            18、个人名片
            19、消息转发
            20、康复计划发送
            21、转诊待预约发送
            22、已知悉
    **会话类型** 
    
        sessions的type字段
            a、type =1 消息保存muc_messsges、患者的三师、家庭咨询会话
            a、type =1 消息保存muc_messsges、患者的三师、家庭咨询会话,
            sessionId:patient + "_" + ct.getTeam() + "_" + ct.getType()
            居民code+"_"+服务团队code+"_"+咨询类型
            
            b、type=2 消息保存p2p_messages、患者的名医咨询,医生间的p2p、名医咨询会话
            sessionId: 参与者的code的哈希值
            
            c、type =3 消息保存group_messages、行政团队聊天会话
            sessionId:新增团队id
            
            d、type =0 消息保存system_messages 系统聊天会话
            
            sessionId: 参与者的code的哈希值
            e、type =4 临时讨论组(已经废弃 被type=5的类型代替了)
            sessionId: 第一条消息的消息id
            f、type =5 专科医生和家庭医生的固定组
            sessionId: 居民code+"_"+专科医生行政团队code
            g、type =8 续方咨询
            sessionId: 居民code+"_"+咨询code+"_"+咨询类型
        sessions的business_type字段
        
            a、business_type =1 此会话不包含患者
@ -255,6 +286,63 @@ select w.* from  muc_messages w ,(
select m.`timestamp` as begin_date,m2.`timestamp` as end_date,t.session_id from topics t,muc_messages m, muc_messages m2 where m.id = t.start_message_id and m2.id = t.end_message_id and  t.session_id = '4d5be29f8ba0413d8658441902b958ff_c7a121954a3a4fae84852fd5668b590d_2'
) s where w.session_id = s.session_id and w.`timestamp` >= s.begin_date and w.`timestamp`<=s.end_date;
-- 系统不稳定的时候会出现wlyy和im库其中一个数据库的数据没有新增,目前是手动处理,后续可以考虑写job定时处理脏数据
SELECT * from wlyy.wlyy_patient WHERE mobile = '';-- 查找居民
SELECT * from wlyy.wlyy_consult WHERE patient = '';-- 查找居民咨询
SELECT * from wlyy.wlyy_consult_team WHERE patient = '';-- 查找居民咨询
SELECT * from im.topics WHERE id = ''; -- 查找议题
SELECT * from im.sessions WHERE id = ''; -- 查找会话
SELECT * from im.muc_messages WHERE session_id = '' ORDER BY `timestamp` desc;-- 查找聊天记录
(ps:大部分是im库有数据,wlyy库没有数据,此时只需要吧wlyy_consult和wlyy_consult_team的数据手动插入即可
如果是wlyy库有数据,im库没有数据则吧im的相关topic表插入数据)
------------------------------------------------------------------------------------------
    SELECT * from dm_hospital WHERE name like '%嵩屿%';
   -- 嵩屿 3502050100
   -- 统计团队咨询量
   SELECT a.name,b.c from wlyy_admin_team a,
   (SELECT admin_team_code,COUNT(*) c from wlyy_consult_team WHERE type = 2 and czrq>'2017-07-01' GROUP BY admin_team_code) b
   WHERE a.org_code = '3502050100' and a.id = b.admin_team_code;
   -- 统计团队健管师咨询量
   SELECT a.name,d.name,sum(b.c) from wlyy_admin_team a, wlyy_doctor d,
   (SELECT admin_team_code,doctor,COUNT(*) c from wlyy_consult_team WHERE type = 2 and czrq>'2017-07-01' GROUP BY admin_team_code,doctor) b
   WHERE a.org_code = '3502050300' and a.id = b.admin_team_code and d.code = b.doctor  GROUP BY a.name,d.name
   -- 统计团队长咨询量
   SELECT a.name,d.name,b.c from wlyy_admin_team a, wlyy_doctor d,
   (SELECT admin_team_code,COUNT(*) c from wlyy_consult_team WHERE type = 2 and czrq>'2017-07-01' GROUP BY admin_team_code) b
   WHERE a.org_code = '3502050300' and a.id = b.admin_team_code and a.leader_code = d.code;
###更改NODE插件数据源在安装完成NODE及配置完成环境变量后执行
    npm config set registry https://registry.npm.taobao.org
    设置完成后执行npm install 即可安装相对于的插件。
##专科医生的业务
1、专科医生和家庭医生聊天
/api/v2/sessions
 session_id : 居民code+"_"+专科医生行政团队code
 session_name : 某居民-共管对话
 session_type : 5
 participants: 此会话的成员列表,格式:["userId1:role", "userId2:role"],用户的ID及角色。
2.修改会话名称
/api/v2/sessions/:session_id/name
post请求
入参:sessionId:会话id
     name:新的会话名称
3、临时聊天组(由医生单聊在拉其他医生进来聊天后生成的新的会话)
/api/v2/sessions
 session_id : 为空(后台自动生成)
 session_name : 显示群组人名,用顿号隔开。超过10字省略显示
 session_type : 4
 participants: 此会话的成员列表,格式:["userId1:role", "userId2:role"],用户的ID及角色。
 
 ###备注
 topic的id=wlyy_consult的code
 

+ 3 - 3
src/server/app.js

@ -36,8 +36,8 @@ app.set('view engine', 'jade');
// logger, body parser, cookie parser and view path
app.use(favicon(__dirname + '/public/favicon.ico', null));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json({limit:'50mb'}));
app.use(bodyParser.urlencoded({limit:'50mb',extended: true}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(methodOverride(null, {methods: ['GET', 'POST', 'PUT', 'DELETE']}));
@ -115,7 +115,7 @@ if(!server.address()){
    log.info('Configuration profile: ' + configFile.split('.')[1]);
}
JobInitializer.init();
//JobInitializer.init();
pubSub.registerHandlers(config.subChannel,msg=> console.log(msg));
pubSub.subscribe(config.subChannel);

+ 2 - 0
src/server/endpoints/url.initializer.js

@ -16,6 +16,7 @@ let socket = require('../controllers/socket');
// endpoints
let application = require('./v2/application.endpoint');
let message = require('./v2/message.endpoint');
let users = require('./v2/user.endpoint');
let sessions = require('./v2/session.endpoint');
let topics = require('./v2/topic.endpoint');
@ -51,6 +52,7 @@ class UrlInitializer {
        });
        app.use(APIv2.Application.Base, application);
        app.use(APIv2.Message.Base, message);
        app.use(APIv2.Management.Base, management);
        app.use(APIv2.Users.Base, users);
        app.use(APIv2.Sessions.Base, sessions);

+ 50 - 0
src/server/endpoints/v2/message.endpoint.js

@ -0,0 +1,50 @@
/**
 * 独立的发送消息接口
 */
"use strict";
let express = require('express');
let router = express.Router();
let ObjectUtil = require('../../util/object.util');
let ControllerUtil = require('../../util/controller.util');
let APIv2 = require('../../include/endpoints').APIv2;
let MODEL_EVENTS = require('../../include/commons').MODEL_EVENTS;
let Messages = require('../../models/messages/messages');
/**
 * 发送消息
 */
router.post(APIv2.Message.Send, function (req, res) {
    let payload = req.body;
    let testing = ObjectUtil.fieldsCheck(payload, "sender_id", "sender_name", "content_type", "content");
    if (!testing.pass) {
        throw {httpStatus: 406, message: testing.message}
    }
    // 消息的发送时间由服务端决定
    payload.timestamp = new Date((new Date().getTime()));
    let messages = new Messages();
    ControllerUtil.regModelEventHandler(messages, res);
    messages.sendMessage(payload);
});
router.get(APIv2.Message.DataMessage, function (req, res) {
    let sessionId = req.query.sessionId;
    let messages = new Messages();
    ControllerUtil.regModelEventHandler(messages, res);
    messages.dataMessage(sessionId);
});
router.get(APIv2.Message.CleanMessageLastFetchTimeToRedis, function (req, res) {
    let sessionId = req.query.sessionId;
    let userId = req.query.userId;
    let messages = new Messages();
    ControllerUtil.regModelEventHandler(messages, res);
    messages.cleanMessageLastFetchTime(sessionId,userId);
});
module.exports = router;

+ 87 - 4
src/server/endpoints/v2/session.endpoint.js

@ -105,6 +105,53 @@ router.get("/", function (req, res) {
});
/**
 * 按会话类型获取会话列表
 * 请求URL /sessions/sessionListByType?user_id=3121&page=0&size=10&type=4
 */
router.get(APIv2.Sessions.SessionListByType, function (req, res) {
    let page = req.query.page;
    let size = req.query.size;
    let userId = req.query.user_id;
    let type = req.query.type;
    if (!page) {
        throw {httpStatus: 406, message: 'Missing page.'};
    }
    if (!size) {
        throw {httpStatus: 406, message: 'Missing size.'};
    }
    if (!type) {
        throw {httpStatus: 406, message: 'Missing type.'};
    }
    if (!userId) {
        throw {httpStatus: 406, message: 'Missing user.'};
    }
    let sessions = new Sessions();
    ControllerUtil.regModelEventHandler(sessions, res);
    sessions.getUserSessionsByType(userId,type,page,size);
});
/**
 * 按会话类型获取会话总数
 * 请求URL /sessions/sessionCountByType?user_id=3121&type=4
 */
router.get(APIv2.Sessions.SessionCountByType, function (req, res) {
    let userId = req.query.user_id;
    let type = req.query.type;
    if (!type) {
        throw {httpStatus: 406, message: 'Missing type.'};
    }
    if (!userId) {
        throw {httpStatus: 406, message: 'Missing user.'};
    }
    let sessions = new Sessions();
    ControllerUtil.regModelEventHandler(sessions, res);
    sessions.getSessionCountByType(userId,type);
});
router.get(APIv2.Sessions.Session,function(req,res){
    let sessionId = req.params.session_id;
    let userId = req.query.user_id;
@ -117,7 +164,7 @@ router.get(APIv2.Sessions.Session,function(req,res){
    let sessions = new Sessions();
    ControllerUtil.regModelEventHandler(sessions, res);
    sessions.getSession(sessionId,userId);
})
});
/**
 * 最近会话列表。
@ -137,6 +184,23 @@ router.get(APIv2.Sessions.RecentSessions, function (req, res) {
    sessions.getRecentSessions(userId, dateSpan);
});
/**
 * 判断会话是否存在。
 *
 * URL:
 *  /sessions/isExist?session_id=abc
 */
router.get(APIv2.Sessions.IsExist, function (req, res) {
    let sessionId = req.query.session_id;
    if (!sessionId) {
        throw {httpStatus: 406, message: 'Missing sessionId.'}
    }
    let sessions = new Sessions();
    ControllerUtil.regModelEventHandler(sessions, res);
    sessions.isExist(sessionId);
});
/**
 * 某个聊天记录置顶操作
 *
@ -176,6 +240,25 @@ router.post(APIv2.Sessions.SessionStatus, function (req, res) {
    sessions.updateSessionStatus(sessionId, status);
});
/**
 * 修改会话名称(修改redis和数据库)
 *
 * 请求URL /name?name=哈哈&sessionId=testsessionmsg1
 */
router.post(APIv2.Sessions.SessionName, function (req, res) {
    let sessionId = req.body.sessionId;
    let name = req.body.name;
    if (!sessionId) {
        throw {httpStatus: 406, message: 'Missing sessionId.'};
    }
    if (!name) {
        throw {httpStatus: 406, message: 'Missing name.'};
    }
    let sessions = new Sessions();
    ControllerUtil.regModelEventHandler(sessions, res);
    sessions.updateSessionName(sessionId, name);
});
/**
 * 取消置顶
@ -267,18 +350,18 @@ router.post(APIv2.Sessions.ParticipantUpdate, function (req, res) {
 * sessionId 会话ID
 */
router.delete(APIv2.Sessions.Participant, function (req, res) {
    let user = req.query.user;
    let user = req.params.participant_id;
    if (!user) {
        throw {httpStatus: 406, message: 'Missing user.'};
    }
    let sessionId = req.query.sessionId;
    let sessionId = req.params.session_id;
    if (!sessionId) {
        throw {httpStatus: 406, message: 'Missing sessionId.'};
    }
    let participants = new Participants();
    ControllerUtil.regModelEventHandler(sessions, res);
    ControllerUtil.regModelEventHandler(participants, res);
    participants.removeUser(sessionId, user);
});

+ 19 - 1
src/server/endpoints/v2/topic.endpoint.js

@ -74,10 +74,28 @@ router.get(APIv2.Sessions.TopicListByType,function (req,res) {
    let reply = req.query.reply;//SESSION_TYPES
    let page = req.query.page;
    let pagesize = req.query.pagesize;
    let patientName = req.query.patientName;
    let startTime = req.query.startTime;
    let endTime = req.query.endTime;
    let topic = new Topics();
    ControllerUtil.regModelEventHandler(topic, res);
    let users = user.split(",");
    topic.findAllTopicByType(users,reply,status,type,page,pagesize);
    topic.findAllTopicByType(users,reply,status,type,patientName,startTime,endTime,page,pagesize);
});
router.get(APIv2.Sessions.TopicListCountByType,function (req,res) {
    let user = req.query.user;
    let status = req.query.status;
    let type = req.query.type;
    let reply = req.query.reply;//SESSION_TYPES
    let patientName = req.query.patientName;
    let startTime = req.query.startTime;
    let endTime = req.query.endTime;
    let topic = new Topics();
    ControllerUtil.regModelEventHandler(topic, res);
    let users = user.split(",");
    topic.topicListCountByType(users,reply,status,type,patientName,startTime,endTime);
});

+ 1 - 1
src/server/endpoints/v2/user.endpoint.js

@ -53,7 +53,7 @@ router.post(APIv2.Users.Login, function (req, res) {
 *  /users/:user_id/status
 *
 * POST参数格式:
 *  {status: 1}, app状态,0在后台,1在前台
 *  {status: 1}, app状态,1在后台,0在前台(1的时候个推推送app消息)
 */
router.put(APIv2.Users.UserStatus, function (req, res) {
    let userId = req.params.user_id;

+ 17 - 5
src/server/handlers/socket.handler.js

@ -8,6 +8,7 @@ let log = require("../util/log.js");
let clientCache = require('../models/socket.io/client.cache').clientCache();
let PatientClient = require('./../models/socket.io/patient.client');
let PcDoctorClient  = require('./../models/socket.io/pcDoctor.client');
let DoctorClient  = require('./../models/socket.io/doctor.client');
let Sessions = require('../models/sessions/sessions');
let Users = require('../models/user/users');
@ -34,12 +35,14 @@ class SocketHandler {
                } else {
                    if("pc_doctor"===data.clientType){//新增pc端医生登录
                        data.userId = "pc_"+data.userId;
                    }else if("pcim_doctor"===data.clientType){//用于pcim 消息通知
                        data.userId = "pcim_"+data.userId;
                    }
                    if(clientCache.removeByUserId(data.userId)){
                        log.info("User " + data.userId + " already login");
                        return;
                    }
                    clientCache.removeByUserId(data.userId);
                    // if(clientCache.removeByUserId(data.userId)){
                    //     log.info("User " + data.userId + " already login");
                    //     return;
                    // }
                    log.info('User ' + data.userId + ' login');
                    if(!data.clientType||data.clientType=="patient"){
@ -51,6 +54,15 @@ class SocketHandler {
                        clientCache.addClient(patientClient);
                        users.login(data.userId, 10, '', '');
                        socket.emit('ack', {});
                    }else if("pcim_doctor"===data.clientType){
                        //用于pcim 消息通知
                        let pcdoctorClient = new PcDoctorClient(socket, socketServer);
                        pcdoctorClient.userId = data.userId;
                        pcdoctorClient.password = data.password;
                        pcdoctorClient.clientType = data.clientType;
                        pcdoctorClient.sessionId =  "";
                        clientCache.addClient(pcdoctorClient);
                        socket.emit('ack', {});
                    }else{
                        let doctorClient = new DoctorClient(socket, socketServer);
                        doctorClient.userId = data.userId;

+ 33 - 8
src/server/include/commons.js

@ -13,12 +13,12 @@ let configFile = "config.";
if (process.env.IM_PROFILE === "prod") {
    configFile += "prod";
} else if (process.env.IM_PROFILE === "prodPC") {
    configFile += "prodPC";
} else if (process.env.IM_PROFILE === "local") {
    configFile += "local";
} else if (process.env.IM_PROFILE === "test") {
    configFile += "test";
} else {
    configFile += "dev";
    configFile += "test";
}
exports.CONFIG_FILE = configFile;
@ -32,7 +32,7 @@ const SESSION_TYPES = {
    P2P: 2,             // P2P
    GROUP: 3,           // 固定组
    DISCUSSION: 4,       // 临时讨论组
    PRESCRIPTION: 8,       // 续方
    SPECIALISTGROUP: 5,       // 专科医生和家庭医生的固定组
    PRESCRIPTION_HOSPITAL:9,//医院在线复诊
    ONDOOR_NURSING:11   //上门护理
};
@ -85,10 +85,15 @@ const CONTENT_TYPES = {
    GoTo: 5,        // 跳转信息,求组其他医生或者邀请其他医生发送的推送消息
    TopicBegin: 6,  // 议题开始
    TopicEnd: 7,    // 议题结束 10 11 系统发送的会话消息
    PersonalCard: 18,    // 个人名片
    MessageForward: 19,    // 消息转发
    TopicInto: 14,    // 进入议题 系统发送的会话消息
    Video:12,//视频
    System:13,//系统消息
    PrescriptionCheck:15,//续方审核消息消息
    PrescriptionBloodStatus:16,//续方咨询血糖血压咨询消息
    PrescriptionFollowupContent:17,//续方咨询随访问卷消息
    Rehabilitation: 20, //康复计划发送
    typeToDescription: function (type, defaultDescription) {
        if (CONTENT_TYPES.Image == type) {
            return '[图片]';
@ -112,6 +117,7 @@ exports.SOCKET_TYPES={
    PATIENT:"patient",
    DOCTOR:"doctor",
    PC_DOCTOR:"pc_doctor",
    PCIM_DOCTOR:"pcim_doctor",
    DEMO:"demo"
}
@ -121,7 +127,7 @@ exports.SOCKET_TYPES={
exports.PLATFORM = {
    iOS: 0,
    Android: 1,
    PC: 3,
    PC: 4,
    Wechat: 10
};
@ -200,7 +206,6 @@ const DB_TABLES = {
    P2pMessages: "p2p_messages",
    MucMessages: "muc_messages",
    GroupMessages: "group_messages",
    DiscussionMessages: "discussion_messages",
    SystemMessages: "system_messages",
    Participants: "participants",
    Sessions: "sessions",
@ -209,6 +214,9 @@ const DB_TABLES = {
    WlyyConsult:"wlyy_consults",
    WlyyConsultTeam:"wlyy.wlyy_consult_team",
    WlyyConsultS:"wlyy.wlyy_consult",
    WlyyMessage:"wlyy.wlyy_message",
    MessageNoticeSetting:"wlyy.wlyy_message_notice_setting",
    SignFamily:"wlyy.wlyy_sign_family",
    sessionTypeToTableName: function (sessionType) {
        if (sessionType == SESSION_TYPES.SYSTEM)
@ -220,15 +228,32 @@ const DB_TABLES = {
        else if (sessionType == SESSION_TYPES.GROUP)
            return DB_TABLES.GroupMessages;
        else if (sessionType == SESSION_TYPES.DISCUSSION)
            return DB_TABLES.DiscussionMessages;
            return DB_TABLES.GroupMessages;
        else if (sessionType == SESSION_TYPES.PRESCRIPTION)
            return DB_TABLES.MucMessages;
<<<<<<< .mine
        else if (sessionType == SESSION_TYPES.PRESCRIPTION_HOSPITAL)
            return DB_TABLES.MucMessages;
        else if (sessionType == SESSION_TYPES.ONDOOR_NURSING)
            return DB_TABLES.MucMessages;
=======
        else if (sessionType == SESSION_TYPES.SPECIALISTGROUP)
            return DB_TABLES.GroupMessages;
>>>>>>> .theirs
        else throw {message: "Unknown session type"};
    }
};
exports.DB_TABLES = DB_TABLES;
exports.DB_TABLES = DB_TABLES;
const MESSAGE_BUSSINESS_TYPE = {
    D_CT_01: "D_CT_01",//指定咨询, 您有新的指定咨询
    D_CT_02: "D_CT_02",//指定咨询, 您有新的消息
    D_CT_03: "D_CT_03",//名医咨询, 您有新的名医咨询
    D_CT_04: "D_CT_04",//名医咨询, 您有新的名医咨询
    D_CT_05: "D_CT_05"//续方咨询, 您有新的续方咨询
};
exports.MESSAGE_BUSSINESS_TYPE = MESSAGE_BUSSINESS_TYPE;

+ 35 - 22
src/server/include/endpoints.js

@ -5,7 +5,7 @@ const APIv2 = {
    Application: {
        Base: '/api/v2/application',
        BadgeNo: '/badge_no'
        BadgeNo: '/badge_no'                                            //获取角标
    },
    Management: {
        Base: '/api/v2/management',
@ -13,50 +13,63 @@ const APIv2 = {
        Health: '/health',
        DbStatus: '/db'
    },
    Message: {
        Base: '/api/v2/message',
        Send: '/send',       //发送消息,和会话无关,内外网通信
        DataMessage: '/dataMessage',
        CleanMessageToRedis: '/cleanMessageToRedis',
        CleanMessageLastFetchTimeToRedis: '/cleanMessageLastFetchTimeToRedis'
    },
    Users: {
        Base: '/api/v2/users',
        Login: '/login',
        Logout: '/logout',
        Login: '/login',                                                //用户登录
        Logout: '/logout',                                              //用户退出
        User: '/:user_id',
        UserStatus: '/:user_id/status',
        UserConsultTime:'/:user_id/consult/time'
        User: '/:user_id',                                              //用户信息
        UserStatus: '/:user_id/status',                                 //用户状态
        UserConsultTime:'/:user_id/consult/time'                        //用户咨询时间
    },
    Sessions: {
        Base: '/api/v2/sessions',
        Session: '/:session_id/session',
        Session: '/:session_id/session',                                // 获取会话
        SessionListByType: '/sessionListByType',                        // 按会话类型获取会话
        SessionCountByType: '/sessionCountByType',                      // 按会话类型获取会话数量
        SessionSticky: '/:session_id/sticky',                           // 会话置顶,置顶使用PUT,取消置顶使用DELETE
        SessionStatus: '/:session_id/status',//更新状态
        SessionStatus: '/:session_id/status',                           // 更新状态
        SessionName: '/:session_id/name',                               // 更新会话名称
        RecentSessions: '/recent',                                      // 最近会话,使用类型过滤出'患者'或'医生'会话
        IsExist: '/isExist',                                            // 判断会话是否存在(i健康发送muc的im消息会话前会先调用创建会话的方法,这个方法会更新redis中每个成员的最后获取时间,导致未读消息不准)
        Topics: '/:topic_id/topics',
        Topic: '/topics/:topic_id',                          // 议题,指定ID的议题将返回其信息
        Topics: '/:topic_id/topics',                                    // 获取单个议题
        Topic: '/topics/:topic_id',                                     // 议题,指定ID的议题将返回其信息
        TopicEnded: '/:session_id/topics/:topic_id/ended',              // 议题是否已结束,若top_id为current,则检查最后一个议题的状态
        TopicInto: '/:session_id/topics/:topic_id/into',              // 居民进入议题
        TopicList:'/topics',
        HealthTopicList:'/healthTopics', //健康咨询
        TopicListByType:'/topicListByType', //按类型查找咨询(未回复,进行中,已回复)
        HealthTeamTopicList:'/healthTeamTopics', //健康咨询(区分团队)
        TopicReplyCount:"/topics/count/reply",
        TopicMessages:'/topic/:topic_id/messages',
        Messages: '/:session_id/messages',                             // 会话消息
        TopicInto: '/:session_id/topics/:topic_id/into',                // 居民进入议题
        TopicList:'/topics',                                            //获取议题列表
        HealthTopicList:'/healthTopics',                                //健康咨询
        TopicListByType:'/topicListByType',                             //按类型查找咨询(未回复,进行中,已回复)
        TopicListCountByType:'/topicListCountByType',                   //按类型查找咨询(未回复,进行中,已回复)的数量
        HealthTeamTopicList:'/healthTeamTopics',                        //健康咨询(区分团队)
        TopicReplyCount:"/topics/count/reply",                          //议题回复数统计
        TopicMessages:'/topic/:topic_id/messages',                      //议题消息
        Messages: '/:session_id/messages',                              // 会话消息
        MessagesByTopic: '/:session_id/topics/:topic_id/messages',      // 议题消息
        Message: '/:session_id/messages/:message_id',                   // 单条消息
        SessionsUnreadMessageCount: '/unread_message_count',            // 所有会话的未读消息数
        SessionUnreadMessageCount: '/:session_id/unread_message_count', // 指定会话的未读消息数
        SessionUnreadMessages: '/:session_id/messages/unread',           // 会话未读消息
        SessionUnreadMessages: '/:session_id/messages/unread',          // 会话未读消息
        ParticipantUpdate:'/:session_id/participant/update',//更新成员=删除旧成员,新增新成员
        ParticipantUpdate:'/:session_id/participant/update',            //更新成员=删除旧成员,新增新成员
        Participants: '/:session_id/participants',                      // 会话所有成员
        ParticipantsAvatar: '/:session_id/participants/avatars',        // 会话所有成员头像
        Participant: '/:session_id/participants/:participant_id',       // 会话单个成员
        Participant: '/:session_id/participants/:participant_id',       // 会话单个成员(多个participant_id 用英文逗号间隔)
        ParticipantAvatar: '/:session_id/participants/:participant_id/avatars'  // 会话单个成员头像
    },
    Search: {
        Base: '/api/v2/search'                                         // 搜索,下一版本的语法使用ElasticSearch
        Base: '/api/v2/search'                                          // 搜索,下一版本的语法使用ElasticSearch
    },
    Demo:{
        Base:'/api/v2/demo'

+ 100 - 36
src/server/models/client/app.client.js

@ -6,8 +6,12 @@
let RedisClient = require('../../repository/redis/redis.client');
let RedisModel = require('./../redis.model');
let AppStatusRepo = require('../../repository/mysql/app.status.repo');
let MessageNoticeSettingRepo = require('../../repository/mysql/message.noticeSetting.repo');
let SignFamilyRepo = require('../../repository/mysql/sign.family.repo');
let ModelUtil = require('../../util/model.util');
let clientCache = require('../socket.io/client.cache').clientCache();
let WlyyAssistantSDK = require("../../util/wlyyAssistant.sdk");
let WlyySDK = require("../../util/wlyy.sdk");
let log = require("../../util/log.js");
let pusher = require('../push/pusher');
@ -15,6 +19,7 @@ let redisConn = RedisClient.redisClient().connection;
const CONTENT_TYPES = require('../../include/commons').CONTENT_TYPES;
const SESSION_TYPES = require('../../include/commons').SESSION_TYPES;
const MESSAGE_BUSSINESS_TYPE = require('../../include/commons').MESSAGE_BUSSINESS_TYPE;
const REDIS_KEYS = require('../../include/commons').REDIS_KEYS;
const PLATFORMS = require('../../include/commons').PLATFORM;
@ -35,7 +40,8 @@ class AppClient extends RedisModel {
        let userStatusKey = RedisModel.makeRedisKey(REDIS_KEYS.UserAppStatus, userId);
        redisConn.hgetAsync(userStatusKey, 'app_in_bg').then(function (res) {
            if (res !== null) {
                redisConn.hsetAsync(userStatusKey, 'app_in_bg', appInBg ? 1 : 0).then(function (res) {
                // redisConn.hsetAsync(userStatusKey, 'app_in_bg', appInBg ? 1 : 0).then(function (res) {
                redisConn.hsetAsync(userStatusKey, 'app_in_bg', appInBg).then(function (res) {
                    if (handler) {
                        handler(null, true);
                    } else {
@ -88,6 +94,62 @@ class AppClient extends RedisModel {
            });
    }
    static sendNotice(targetId, message, sessionType,badge,userStatus){
        //调用接口推送医生助手微信模板
        let param = {
            sessionId:message.session_id||'',
            sessionType:sessionType,
            from:message.sender_id|| '',
            content:message.content,
            contentType:message.content_type,
            businessType:message.businessType || 1,
            doctorCode:targetId
        }
        // let params = 'sessionId='+(message.session_id||'')+'&sessionType='+sessionType+"&from="+(message.sender_id|| '')+'&content='+message.content+'&businessType='+(message.business_type || 1)+'&doctorCode='+targetId;
        // 医生助手有自己的模板消息,不需要通过im发送
        // WlyyAssistantSDK.request('admin', '0a5c5258-8863-4b07-a3f9-88c768528ab4', '', 'admin_imei', '/doctor/feldsher/sendDoctorTemplate', param, function (err, res) {
        //     // WlyySDK.request('admin', '0a5c5258-8863-4b07-a3f9-88c768528ab4', '', 'admin_imei', '/doctor/feldsher/sendDoctorTemplate?' + params, 'GET', function (err, res) {
        //     if(err){
        //         log.error(err);
        //     }else {
        //         log.info(res);
        //     }
        // });
        if (!userStatus) {
            log.warn("User's app status is not found, user id: " + targetId + ", maybe user never login yet or logout?");
            return;
        }
        let tipMessage = CONTENT_TYPES.typeToDescription(parseInt(message.content_type), "您有一条新消息") || message.content;
        let customData = {
            session_id: message.session_id||'',
            session_type: sessionType,
            from: message.sender_id|| '',
            data: message.content,
            business_type: message.business_type || 1
        };
        if (userStatus.platform == PLATFORMS.iOS) {
            pusher.pushToSingleViaAPN(tipMessage, customData, message.content_type, userStatus.device_token,badge, function (err, res) {
                if (err) {
                    ModelUtil.logError("Send notification via APN failed:"+userStatus.device_token, err);
                } else {
                    log.info("Send notification via APN succeed: ", JSON.stringify(res));
                }
            });
        } else if (userStatus.platform == PLATFORMS.Android) {
            let title = '新消息';
            pusher.pushToSingleViaAndroid(title, tipMessage, customData, userStatus.client_id, userStatus.app_in_bg, function (err, res) {
                if (err) {
                    ModelUtil.logError("Send notification via Android failed", err);
                } else {
                    log.info("Send notification via Android succeed: ", JSON.stringify(res));
                }
            });
        }
    }
    /**
     * 向App端推送消息。指令性消息不推。
     *
@ -109,47 +171,49 @@ class AppClient extends RedisModel {
                    return;
                }
                // let pc_doctorClient = clientCache.findByIdAndType("pc_"+targetId,SOCKET_TYPES.PC_DOCTOR);
                // if(pc_doctorClient){
                //     log.warn("User's pc is online, user id: " + targetId + ", we cannot send getui.");
                //     return;
                // }
                if (!userStatus) {
                    log.warn("User's app status is not found, user id: " + targetId + ", maybe user never login yet or logout?");
                    return;
                }
                let tipMessage = CONTENT_TYPES.typeToDescription(parseInt(message.content_type), "您有一条新消息") || message.content;
                let customData = {
                    session_id: message.session_id||'',
                    session_type: sessionType,
                    from: message.sender_id|| '',
                    data: message.content,
                    business_type: message.business_type || 1
                };
                if (userStatus.platform == PLATFORMS.iOS) {
                    pusher.pushToSingleViaAPN(tipMessage, customData, message.content_type, userStatus.device_token,badge, function (err, res) {
                        if (err) {
                            ModelUtil.logError("Send notification via APN failed:"+userStatus.device_token, err);
                        } else {
                            log.info("Send notification via APN succeed: ", JSON.stringify(res));
                        }
                    });
                } else if (userStatus.platform == PLATFORMS.Android) {
                    let title = '新消息';
                    pusher.pushToSingleViaAndroid(title, tipMessage, customData, userStatus.client_id, userStatus.app_in_bg, function (err, res) {
                        if (err) {
                            ModelUtil.logError("Send notification via Android failed", err);
                        } else {
                            log.info("Send notification via Android succeed: ", JSON.stringify(res));
                var session_type = isNaN(Number(sessionType))?sessionType:Number(sessionType);
                if( session_type==SESSION_TYPES.MUC||
                    session_type==SESSION_TYPES.P2P||
                    session_type==SESSION_TYPES.GROUP||
                    session_type==SESSION_TYPES.DISCUSSION||
                    session_type==SESSION_TYPES.SPECIALISTGROUP||
                    session_type==SESSION_TYPES.PRESCRIPTION||
                    message.business_type == MESSAGE_BUSSINESS_TYPE.D_CT_01||
                    message.business_type == MESSAGE_BUSSINESS_TYPE.D_CT_02||
                    message.business_type == MESSAGE_BUSSINESS_TYPE.D_CT_03||
                    message.business_type == MESSAGE_BUSSINESS_TYPE.D_CT_04){
                    //是否发送消息
                    MessageNoticeSettingRepo.findOne(targetId,'1',function (err,res) {
                        if(res&&res.length>0){
                            let master_switch = res[0].master_switch;
                            let im_switch = res[0].im_switch;
                            let family_topic_switch = res[0].family_topic_switch;
                            if(master_switch==0||im_switch==0){
                                ModelUtil.logError(targetId+"-关闭im消息,消息id", message.id);
                            }
                            // else if(session_type==SESSION_TYPES.MUC&&family_topic_switch==1){
                            //     SignFamilyRepo.isHealthDoctor(message.session_id,targetId,function (err,res) {
                            //         if(res&&res.length==0){
                            //             ModelUtil.logError("全科医生:"+targetId+"-关闭im消息,消息id", message.id);
                            //         }else{
                            //             AppClient.sendNotice(targetId, message, sessionType,badge,userStatus)
                            //         }
                            //     })
                            // }
                            else{
                                AppClient.sendNotice(targetId, message, sessionType,badge,userStatus)
                            }
                        }
                    });
                }else{
                    AppClient.sendNotice(targetId, message, sessionType,badge,userStatus)
                }
            });
        }
    }
}
// Expose class

+ 133 - 49
src/server/models/client/wechat.client.js

@ -20,6 +20,7 @@ let https = require('https');
let async = require('async');
const CONTENT_TYPES = require('../../include/commons').CONTENT_TYPES;
const SESSION_TYPES = require('../../include/commons').SESSION_TYPES;
const SOCKET_TYPES = require('../../include/commons').SOCKET_TYPES;
const REDIS_KEYS = require('../../include/commons').REDIS_KEYS;
@ -59,9 +60,13 @@ class WechatClient extends RedisModel {
    static sendMessage(targetUserId, targetUserName, message) {
        if (message&&(message.content_type == CONTENT_TYPES.PlainText ||
            message.content_type == CONTENT_TYPES.Image ||
            message.content_type == CONTENT_TYPES.Audio)) {
            message.content_type == CONTENT_TYPES.Audio||
            message.content_type == CONTENT_TYPES.PrescriptionBloodStatus ||
            message.content_type == CONTENT_TYPES.PrescriptionFollowupContent)) {
            let patientClient = clientCache.findById(targetUserId);
            let doctorClient = clientCache.findByIdAndType(message.sender_id,SOCKET_TYPES.DOCTOR);
            let pc_doctorClient = clientCache.findByIdAndType("pc_"+message.sender_id,SOCKET_TYPES.PC_DOCTOR);
            var count = 0;
            if (patientClient) {
                log.warn("User's wechat endpoint is online, sending via web socket. User id: ", targetUserId);
                WechatClient.sendViaWebSocket(patientClient.socket, message);
@ -75,11 +80,40 @@ class WechatClient extends RedisModel {
                        WechatClient.sendReadDoctor(doctorClient.socket, message);
                    }
                }else{
                    count++;
                }
                if(pc_doctorClient){
                    log.error("doctor sessionid "+pc_doctorClient.sessionId);
                    log.error("patient sessionid "+patientClient.sessionId);
                    if(patientClient.sessionId==pc_doctorClient.sessionId){
                        //用户socket在线,推送给用户后,告知医生此消息已读
                        WechatClient.updateParticipantLastFetchTime(pc_doctorClient.sessionId, targetUserId, ObjectUtil.timestampToLong(message.timestamp));
                        WechatClient.sendReadDoctor(pc_doctorClient.socket, message);
                    }
                }else{
                    count++;
                }
                if(count==0){
                    log.error("doctor client not found");
                }
            } else {
                log.info("User's wechat endpoint is not online, sending via wechat template message. User id: ", targetUserId);
                WechatClient.sendViaMessageTemplate(targetUserId, targetUserName, message);
                var isSendWXTem = true;//是否发送微信模板
                if(message.content_type == CONTENT_TYPES.PrescriptionBloodStatus||message.content_type == CONTENT_TYPES.PrescriptionFollowupContent){
                    var content = JSON.parse(message.content);
                    if(content.isSendWxTemplate){
                        message.content = content.text;
                    }else {
                        isSendWXTem = false;
                    }
                }
                if(isSendWXTem){
                    WechatClient.sendViaMessageTemplate(targetUserId, targetUserName, message);
                }
            }
        } else if(message.content_type == CONTENT_TYPES.TopicEnd){
            let patientClient = clientCache.findById(targetUserId);
@ -155,14 +189,17 @@ class WechatClient extends RedisModel {
    static sendReadDoctorByDoctorId(doctorId, message) {
        let doctorClient = clientCache.findByIdAndType(doctorId,SOCKET_TYPES.DOCTOR);
        // let pc_doctorClient = clientCache.findByIdAndType("pc_"+doctorId,SOCKET_TYPES.PC_DOCTOR);
        if(!doctorClient){
        let pc_doctorClient = clientCache.findByIdAndType("pc_"+doctorId,SOCKET_TYPES.PC_DOCTOR);
        if(!doctorClient&&!pc_doctorClient){
            log.warn("target doctor is not online!");
            return;
        }
        let sendDoctorClient = clientCache.findByIdAndType(message.sender_id,SOCKET_TYPES.DOCTOR);
        // let pc_sendDoctorClient = clientCache.findByIdAndType("pc_"+message.sender_id,SOCKET_TYPES.PC_DOCTOR);
        if(sendDoctorClient&&sendDoctorClient.sessionId==doctorClient.sessionId){
        if(!sendDoctorClient){
            sendDoctorClient = clientCache.findByIdAndType("pc_"+message.sender_id,SOCKET_TYPES.PC_DOCTOR);
        }
        var count = 0;
        if(doctorClient&&sendDoctorClient&&sendDoctorClient.sessionId==doctorClient.sessionId){
            WechatClient.updateParticipantLastFetchTime(doctorClient.sessionId, doctorId, ObjectUtil.timestampToLong(message.timestamp));
            sendDoctorClient.socket.emit('message', {
                id: message.id,
@ -177,45 +214,49 @@ class WechatClient extends RedisModel {
                read:"one"
            });
        }else{
            log.warn("doctor is not in the same session or not online");
            count++;
        }
        //发送pc版医生端
        // if(pc_doctorClient&&pc_sendDoctorClient&&pc_sendDoctorClient.sessionId==pc_doctorClient.sessionId){
        //     WechatClient.updateParticipantLastFetchTime(pc_doctorClient.sessionId, doctorId, ObjectUtil.timestampToLong(message.timestamp));
        //     pc_sendDoctorClient.socket.emit('message', {
        //         id: message.id,
        //         session_id: message.session_id,
        //         sender_id: message.sender_id,
        //         sender_name: message.sender_name,
        //         content_type: message.content_type,
        //         content: message.content,
        //         timestamp: ObjectUtil.timestampToLong(message.timestamp),
        //         type: message.content_type,          // legacy support
        //         name: message.sender_name,
        //         read:"one"
        //     });
        // }else{
        //     log.warn("doctor is not in the same session or not online");
        // }
        if(pc_doctorClient&&sendDoctorClient&&sendDoctorClient.sessionId==pc_doctorClient.sessionId){
            WechatClient.updateParticipantLastFetchTime(pc_doctorClient.sessionId, doctorId, ObjectUtil.timestampToLong(message.timestamp));
            pc_doctorClient.socket.emit('message', {
                id: message.id,
                session_id: message.session_id,
                sender_id: message.sender_id,
                sender_name: message.sender_name,
                content_type: message.content_type,
                content: message.content,
                timestamp: ObjectUtil.timestampToLong(message.timestamp),
                type: message.content_type,          // legacy support
                name: message.sender_name,
                read:"one"
            });
        }else{
            count++;
        }
        if(count==2){
            log.warn("doctor is not in the same session or is not online");
        }
    }
    static sendSocketMessageToDoctor(doctorId, message) {
        let doctorClient = clientCache.findByIdAndType(doctorId,SOCKET_TYPES.DOCTOR);
        // let pc_doctorClient = clientCache.findByIdAndType("pc_"+doctorId,SOCKET_TYPES.PC_DOCTOR);
        if(!doctorClient){
        let pc_doctorClient = clientCache.findByIdAndType("pc_"+doctorId,SOCKET_TYPES.PC_DOCTOR);
        if(!doctorClient&&!pc_doctorClient){
            log.warn("target doctor is not online!");
            return;
        }
        let sendClient = clientCache.findByIdAndType(message.sender_id,SOCKET_TYPES.DOCTOR);//app医生发送的消息
        // let sendClient = clientCache.findByIdAndType(message.sender_id,SOCKET_TYPES.DOCTOR);//app医生发送的消息
        // if(!sendClient){//pc医生发送的消息
        //     sendClient = clientCache.findByIdAndType("pc_"+message.sender_id,SOCKET_TYPES.PC_DOCTOR);
        // }
        if(!sendClient){//居民发送的消息
            sendClient = clientCache.findByIdAndType(message.sender_id,SOCKET_TYPES.PATIENT);
        }
        // if(!sendClient){//居民发送的消息
        //     sendClient = clientCache.findByIdAndType(message.sender_id,SOCKET_TYPES.PATIENT);
        // }
        if(sendClient&&sendClient.sessionId==doctorClient.sessionId){
        var count = 0;
        if(doctorClient&&message.session_id==doctorClient.sessionId){
            WechatClient.updateParticipantLastFetchTime(doctorClient.sessionId, doctorId, ObjectUtil.timestampToLong(message.timestamp));
            doctorClient.socket.emit('message', {
                id: message.id,
@ -229,27 +270,62 @@ class WechatClient extends RedisModel {
                name: message.sender_name,
            });
        }else{
            log.warn("doctor is not in the same session or is not online");
            count++;
        }
        //发送pc端
        // if(pc_doctorClient&&sendClient&&sendClient.sessionId==pc_doctorClient.sessionId){
        //     WechatClient.updateParticipantLastFetchTime(pc_doctorClient.sessionId, doctorId, ObjectUtil.timestampToLong(message.timestamp));
        //     pc_doctorClient.socket.emit('message', {
        //         id: message.id,
        //         session_id: message.session_id,
        //         sender_id: message.sender_id,
        //         sender_name: message.sender_name,
        //         content_type: message.content_type,
        //         content: message.content,
        //         timestamp: ObjectUtil.timestampToLong(message.timestamp),
        //         type: message.content_type,          // legacy support
        //         name: message.sender_name,
        //     });
        // }else{
        //     log.warn("doctor is not in the same session or is not online");
        // }
        if(pc_doctorClient&&message.session_id==pc_doctorClient.sessionId){
            WechatClient.updateParticipantLastFetchTime(pc_doctorClient.sessionId, doctorId, ObjectUtil.timestampToLong(message.timestamp));
            pc_doctorClient.socket.emit('message', {
                id: message.id,
                session_id: message.session_id,
                sender_id: message.sender_id,
                sender_name: message.sender_name,
                content_type: message.content_type,
                content: message.content,
                timestamp: ObjectUtil.timestampToLong(message.timestamp),
                type: message.content_type,          // legacy support
                name: message.sender_name,
            });
        }else{
            count++;
        }
        if(count==2){
            log.warn("doctor is not in the same session or is not online");
        }
    }
    static sendPcImSocket(targetId, message, sessionType) {
        if (message.content_type == CONTENT_TYPES.PlainText ||
            message.content_type == CONTENT_TYPES.Image ||
            message.content_type == CONTENT_TYPES.Audio||
            message.content_type == CONTENT_TYPES.Video||
            message.content_type == CONTENT_TYPES.GoTo||
            sessionType==SESSION_TYPES.SYSTEM) {
            let pcim_doctorClient = clientCache.findByIdAndType("pcim_"+targetId,SOCKET_TYPES.PCIM_DOCTOR);
            if(pcim_doctorClient) {
                 let customData = {
                    session_id: message.session_id||'',
                    session_type: sessionType,
                    from: message.sender_id|| '',
                    data: message.content,
                    business_type: message.business_type || 1
                };
                pcim_doctorClient.socket.emit('message', {
                    session_id: message.session_id||'',
                    session_type: sessionType,
                    from: message.sender_id|| '',
                    data: message.content,
                    business_type: message.business_type || 1
                });
            }
        }
    }
    /**
     * 发送微信模板消息给居民
     *
@ -339,6 +415,8 @@ class WechatClient extends RedisModel {
                                    // 发送模板消息
                                    WechatSDK.sendTemplateMessage({
                                        touser: openid,
                                        name: member.name,
                                        patient: member.code,
                                        template_id: config.wechatConfig.template.consultTemplate,
                                        url: url + "?openid=" + openid + "&type="+topic.type+"&doctor="+message.sender_id+
                                        "&consult=" + topic.id + "&toUser=" + member.code + "&toName=" + member.name+"&represented="+patient.code,
@ -360,6 +438,8 @@ class WechatClient extends RedisModel {
                            // 发送模板消息
                            WechatSDK.sendTemplateMessage({
                                touser: patient.openid,
                                name: targetUserName,
                                patient: targetUserId,
                                template_id: config.wechatConfig.template.consultTemplate,
                                url: url + "?openid=" + patient.openid +"&type="+topic.type+"&doctor="+message.sender_id+
                                "&consult=" + topic.id + "&toUser=" + targetUserId + "&toName=" + targetUserName+"&represented="+patient.code,
@ -380,6 +460,8 @@ class WechatClient extends RedisModel {
                        if(patient.openid){
                            WechatSDK.sendTemplateMessage({
                                touser: patient.openid,
                                name: targetUserName,
                                patient: targetUserId,
                                template_id: config.wechatConfig.template.consultTemplate,
                                url: url + "?openid=" + patient.openid +"&type="+topic.type+"&doctor="+message.sender_id+
                                "&consult=" + topic.id + "&toUser=" + targetUserId + "&toName=" + targetUserName+"&represented="+patient.code,
@ -403,6 +485,8 @@ class WechatClient extends RedisModel {
                                    // 发送模板消息
                                    WechatSDK.sendTemplateMessage({
                                        touser: openid,
                                        name: member.name,
                                        patient: member.code,
                                        template_id: config.wechatConfig.template.consultTemplate,
                                        url: url + "?openid=" + openid +"&type="+topic.type+"&doctor="+message.sender_id+
                                        "&consult=" + topic.id + "&toUser=" + member.code + "&toName=" + member.name+"&represented="+patient.code,

+ 111 - 1
src/server/models/messages/messages.js

@ -6,15 +6,20 @@
let MessageRepo = require('../../repository/mysql/message.repo');
let RedisModel = require('./../redis.model.js');
let SessionRepo = require('../../repository/mysql/session.repo');
let ParticipantRepo = require('../../repository/mysql/participant.repo');
let RedisClient = require('../../repository/redis/redis.client.js');
let ModelUtil = require('../../util/model.util');
var ObjectUtil = require("../../util/object.util.js");
let WechatClient = require("../client/wechat.client.js");
let AppClient = require("../client/app.client.js");
let mongoose = require('mongoose');
let redis = RedisClient.redisClient().connection;
let log = require('../../util/log.js');
let configFile = require('../../include/commons').CONFIG_FILE;
let config = require('../../resources/config/' + configFile);
let logger = require('../../util/log');
let pubSub = require("../redis/pubSub.js");
const REDIS_KEYS = require('../../include/commons').REDIS_KEYS;
@ -152,6 +157,43 @@ class Messages extends RedisModel {
    /**
     * 内外网通信。
     * 消息内容 存系统表
     * @param message
     */
    sendMessage(message) {
        let self = this;
        let messages = new Messages();
        let messageId = mongoose.Types.ObjectId().toString();
        let sessionType =0;
        message.id = messageId;
        message.session_id = "system";
        messages.saveMessageToMysql("system", sessionType, messageId, message, function (err, res) {
            if (err) {
                ModelUtil.emitError(self.eventEmitter, {message: "Failed to save message to mysql: " + err});
            } else {
                message.timestamp = message.timestamp.getTime();
                let targetUserId = message.sender_name;
                //告知医生新消息
                WechatClient.sendSocketMessageToDoctor(targetUserId,message);
                if(config.environment!='local'){//pc版不推送个推,通过redis的publish
                    AppClient.sendNotification(targetUserId, message,sessionType,1);
                }
                //外网pcim通过socket推送
                WechatClient.sendPcImSocket(targetUserId,message,sessionType);
                //redis发布消息
                if(config.pubSubSwitch) {//接收订阅消息处理开关,本地运行和测试库单独运行时防止用户接收消息2次
                    pubSub.publish(config.pubChannel,JSON.stringify(message));
                }
                ModelUtil.emitOK(self.eventEmitter, {status: 200});
            }
        });
    }
    /**
     * 保存Message 到mysql
     * @param message 消息对象
@ -213,7 +255,7 @@ class Messages extends RedisModel {
        let messagesByTimestampKey = this.makeRedisKey(REDIS_KEYS.MessagesByTimestamp, sessionId);
        redis.zcardAsync(messagesByTimestampKey).then(function (count) {
            if (count > maxMessageCacheCount) {
                redis.zrevrangeAsync(messagesByTimestampKey, 0, count - maxMessageCacheCount).then(function (idList) {
                redis.zrevrangeAsync(messagesByTimestampKey, maxMessageCacheCount, count - maxMessageCacheCount).then(function (idList) {
                    redis.zremAsync(messagesByTimestampKey, idList).then(function (res) {
                        redis.hdel(messageById, idList);
                    })
@ -221,6 +263,74 @@ class Messages extends RedisModel {
            }
        });
    }
    /**
     *
     * 清洗数据
     * @param message
     */
    dataMessage(sessionId) {
        let self = this;
        let messagesKey = RedisModel.makeRedisKey(REDIS_KEYS.Messages, sessionId);
        let message_timestamp_key = RedisModel.makeRedisKey(REDIS_KEYS.MessagesByTimestamp, sessionId);
        MessageRepo.findBySessionId(sessionId, 0, config.sessionConfig.maxMessageCount, null, function (err, messages) {
            if (err) {
                ModelUtil.emitError(self.eventEmitter, {status: -1, message: "没有找到对应sessionId的聊天记录----"+sessionId});
                return;
            }
            let multi = redis.multi();
            if(messages){
                messages.forEach(function (message) {
                    let msgJson = {
                        id: message.id,
                        sender_id: message.sender_id,
                        sender_name: message.sender_name,
                        timestamp: ObjectUtil.timestampToLong(message.timestamp),
                        content_type: message.content_type,
                        content: message.content
                    };
                    multi = multi.hset(messagesKey, message.id, JSON.stringify(msgJson))
                        .zadd(message_timestamp_key, ObjectUtil.timestampToLong(message.timestamp), message.id);
                });
            }
            multi.execAsync()
                .then(function (res) {
                    ModelUtil.emitOK(self.eventEmitter, {status:200,message:"存入redis成功!"});
                })
                .catch(function (ex) {
                    log.error("Login failed while caching messages: ", ex);
                    ModelUtil.emitOK(self.eventEmitter, res);
                    return;
                });
        });
    }
    cleanMessageLastFetchTime(sessionId,userId) {
        let self = this;
        ParticipantRepo.findLastFetchTime(sessionId,userId,function(err,res){
            if (err) {
                logger.error(err);
                return;
            } else {
                let last_fetch_time = new Date(res[0].last_fetch_time).getTime();
                last_fetch_time = last_fetch_time + 1;
                let participantsKey = RedisModel.makeRedisKey(REDIS_KEYS.SessionParticipants, sessionId);
                redis.zaddAsync(participantsKey, last_fetch_time, userId)
                    .then(function (res) {
                        ModelUtil.emitOK(self.eventEmitter, {status:200,message:"存入redis成功!"});
                    })
                    .catch(function (err) {
                        logger.error("Update participant last fetch time failed: ", err);
                        ModelUtil.emitOK(self.eventEmitter, res);
                        return;
                    });
            }
        });
    }
}
// Expose class

+ 21 - 4
src/server/models/redis/pubSub.js

@ -15,27 +15,41 @@ let config = require('../../resources/config/' + configFile);
let RedisModel = require('./../redis.model');
let RedisSubClient = require('./redisSubClient');
let RedisPubClient = require('./redisPubClient');
let RedisClient = require('../../repository/redis/redis.client.js');
let redisPubConn = RedisPubClient.redisClient().connection;
let redisSubConn = RedisSubClient.redisClient().connection;
let Sessions = require('../sessions/sessions.js');
let redis = RedisClient.redisClient().connection;
let Sessions = require('../../models/sessions/sessions');
let WechatClient = require("../client/wechat.client.js");
let WlyySDK = require("../../util/wlyy.sdk");
let AppClient = require("../client/app.client.js");
let Participants = require('../sessions/participants');
let Messages = require('../messages/messages');
const REDIS_KEYS = require('../../include/commons').REDIS_KEYS;
let ObjectUtil = require("../../util/object.util.js");
let logger = require('../../util/log.js');
let SessionRepo = require('../../repository/mysql/session.repo');
let ParticipantRepo = require('../../repository/mysql/participant.repo');
const SESSION_TYPES = require('../../include/commons').SESSION_TYPES;
const SESSION_BUSINESS_TYPE = require('../../include/commons').SESSION_BUSINESS_TYPE;
class PubSub{
    constructor(){
        this.sub=redisSubConn;
        this.handlers=new Map();
        this.subAction=(channle,message)=>{
            let actions= this.handlers.get(channle)||new Set();
            for(let action of actions)
            {
                console.log("接收消息:"+message);
                message = JSON.parse(message);
                if(config.pubSubSwitch){//接收订阅消息处理开关,本地运行和测试库单独运行时防止用户接收消息2次
                    message = JSON.parse(message);
                    //Sessions.getRedisPushNotification(message);这里不知为什么无法调用这个方法,提示getRedisPushNotification不是方法
                    if (message.targetType=='patient') {
                        if(config.environment!='prodPC'){//pc版接收要发给居民的消息不做处理
                        if(config.environment!='local'){//pc版接收要发给居民的消息不做处理
                            WechatClient.sendMessage(message.targetUserId, message.targetUserName, message);
                        }
                    } else {
@ -44,7 +58,7 @@ class PubSub{
                        }
                        //告知医生新消息
                        WechatClient.sendSocketMessageToDoctor(message.targetUserId,message);
                        if(config.environment!='prodPC'){//pc版不推送个推
                        if(config.environment!='local'){//pc版不推送个推
                            WlyySDK.request(message.targetUserId, '', '', '', '/im/common/message/messages', 'POST', function (err, res) {
                                let count = 0;
                                res =  JSON.parse(res)
@ -52,9 +66,12 @@ class PubSub{
                                    let data = res.data;
                                    count = parseInt(JSON.parse(data.imMsgCount).count) + parseInt(data.system.amount) + parseInt(data.healthIndex.amount) + parseInt(data.sign.amount);
                                }
                                AppClient.sendNotification(message.targetUserId, message,message.sessionType,count);
                            });
                        }
                        //外网pcim通过socket推送
                        WechatClient.sendPcImSocket(message.targetUserId,message,message.sessionType);
                    }
                }
                //action(message);

+ 3 - 3
src/server/models/redis/redisPubClient.js

@ -19,7 +19,7 @@ let redisPubClient = null;
class RedisPubClient {
    constructor() {
        var redisConfig = config.redisConfig;
        var redisConfig = config.innerRedisConfig;
            redisConfig.retry_strategy = function(options){
                log.info("pub Redis重新连接次数:"+options.times_connected);
                if (options.error.code === 'ECONNREFUSED') {
@ -31,9 +31,9 @@ class RedisPubClient {
                return Math.max(options.attempt * 100, 3000);
            }
        this._connection = redis.createClient(
            config.redisConfig
            config.innerRedisConfig
        );
        this._connection.auth(config.redisConfig.password||"",function(){
        this._connection.auth(config.innerRedisConfig.password||"",function(){
            console.log('pub Redis通过认证');
        });

+ 3 - 3
src/server/models/redis/redisSubClient.js

@ -19,7 +19,7 @@ let redisSubClient = null;
class RedisSubClient {
    constructor() {
        var redisConfig = config.redisConfig;
        var redisConfig = config.innerRedisConfig;
            redisConfig.retry_strategy = function(options){
                log.info("sub Redis重新连接次数:"+options.times_connected);
                if (options.error.code === 'ECONNREFUSED') {
@ -31,9 +31,9 @@ class RedisSubClient {
                return Math.max(options.attempt * 100, 3000);
            }
        this._connection = redis.createClient(
            config.redisConfig
            config.innerRedisConfig
        );
        this._connection.auth(config.redisConfig.password||"",function(){
        this._connection.auth(config.innerRedisConfig.password||"",function(){
            console.log('sub Redis通过认证');
        });

+ 10 - 1
src/server/models/sessions/participants.js

@ -210,7 +210,16 @@ class Participants extends RedisModel {
    addUser(sessionId, user,role,handler) {
        let self = this;
        if(!role)role = 0;
        let users = [user+":"+role];
        let users = [];
        if(user.split(",").length==1){
            users = [user+":"+role];
        }else {
            //添加多个成员
            let participants = user.split(",");
            for (let j in participants) {
                users.push( participants[j] +":0");
            }
        }
        Participants.saveParticipantsToRedis(sessionId, users, new Date(), function (res) {
            if (res) {
                Participants.saveParticipantsToMysql(sessionId, users,function(err,res){

+ 508 - 43
src/server/models/sessions/sessions.js

@ -15,6 +15,7 @@ let MessageRepo = require('../../repository/mysql/message.repo');
let ParticipantRepo = require('../../repository/mysql/participant.repo');
let ImDb = require('../../repository/mysql/db/im.db');
let WlyySDK = require("../../util/wlyy.sdk");
let ObjectUtil = require("../../util/object.util.js");
let WechatClient = require("../client/wechat.client.js");
let AppClient = require("../client/app.client.js");
@ -33,6 +34,7 @@ const STICKY_SESSION_BASE_SCORE = require('../../include/commons').STICKY_SESSIO
const PARTICIPANT_ROLES = require('../../include/commons').PARTICIPANT_ROLES;
const CONTENT_TYPES = require('../../include/commons').CONTENT_TYPES;
const SESSION_BUSINESS_TYPE = require('../../include/commons').SESSION_BUSINESS_TYPE;
const SESSION_STATUS = require('../../include/commons').SESSION_STATUS;
class Sessions extends RedisModel {
@ -64,6 +66,7 @@ class Sessions extends RedisModel {
                }
                ModelUtil.emitError(self.eventEmitter, {message: err, status: -1}, null);
            } else {
                name = res.name;
                //创建session到redis
                self.createSessionToRedis(sessionId, name, type, participantArray, messageId, function (err, res) {
                    if (err) {
@ -118,8 +121,12 @@ class Sessions extends RedisModel {
            }
        } else {
            if (!sessionId) {
                handler("MUC OR GROUP session sessionId is not  allow null .", null);
                return;
                if(type == SESSION_TYPES.DISCUSSION){
                    sessionId = messageId;
                }else {
                    handler("MUC OR GROUP session sessionId is not  allow null .", null);
                    return;
                }
            }
            callBusinessType(sessionId);
        }
@ -127,6 +134,8 @@ class Sessions extends RedisModel {
        function callBusinessType(sessionId) {
            if(type == SESSION_TYPES.MUC||type == SESSION_TYPES.PRESCRIPTION || SESSION_TYPES.PRESCRIPTION_HOSPITAL || SESSION_TYPES.ONDOOR_NURSING){
                callCreate(sessionId, SESSION_BUSINESS_TYPE.PATIENT);
            }else if(type==SESSION_TYPES.DISCUSSION||type==SESSION_TYPES.GROUP){
                callCreate(sessionId, SESSION_BUSINESS_TYPE.DOCTOR);
            }else {
                ParticipantRepo.getBusinessType(participantIdArray, function (err, businessType) {
                    callCreate(sessionId, businessType);
@ -183,6 +192,9 @@ class Sessions extends RedisModel {
                    sessionId = res;
                    callBusinessType();
                });
            } else if(type == SESSION_TYPES.DISCUSSION){
                sessionId = messageId;
                callBusinessType();
            } else {
                return handler("MUC模式和团队模式,不允许sessionId为空!", null);
            }
@ -194,6 +206,8 @@ class Sessions extends RedisModel {
        function callBusinessType() {
            if(type==SESSION_TYPES.MUC||type==SESSION_TYPES.PRESCRIPTION || SESSION_TYPES.PRESCRIPTION_HOSPITAL || SESSION_TYPES.ONDOOR_NURSING){
                callCreateSession(SESSION_BUSINESS_TYPE.PATIENT);
            }else if(type==SESSION_TYPES.SPECIALISTGROUP||type==SESSION_TYPES.DISCUSSION){
                callCreateSession(SESSION_BUSINESS_TYPE.DOCTOR);
            }else{
                ParticipantRepo.getBusinessType(participantIdArray, function (err, businessType) {
                    if (err) {
@ -210,6 +224,8 @@ class Sessions extends RedisModel {
            //查找该sessionId是否存在存在则直接返回实例
            SessionRepo.findOne(sessionId, function (err, res) {
                if (res.length > 0) {//已经存在
                    //已存在的会话不修改名称
                    name = res[0].name;
                    //更新成员
                    Participants.saveParticipantsToMysql(sessionId, participantArray, function (err, update) {
                        handler(null, res[0]);
@ -319,6 +335,25 @@ class Sessions extends RedisModel {
        SessionRepo.findOne(sessionId, handler);
    }
    /**
     * 判断会话是否存在
     * @param sessionId
     * @param handler
     */
    isExist(sessionId) {
        let self = this;
        SessionRepo.findOne(sessionId, function (err, res) {
            if(err){
                log.error(err);
                ModelUtil.emitError(self.eventEmitter, {message: err, status: -1}, null);
            } else if(res&&res.length>0){
                ModelUtil.emitOK(self.eventEmitter, {sessionId: res[0].id, status: 200});
            }else {
                ModelUtil.emitOK(self.eventEmitter, {sessionId: '', status: 200});
            }
        });
    }
    getSession(sessionId,userId,handler){
        let self = this;
        let sessionKey = RedisModel.makeRedisKey(REDIS_KEYS.Session, sessionId);
@ -367,15 +402,28 @@ class Sessions extends RedisModel {
        }
        async.waterfall([
            // 获取会话ID列表
            // function (callback) {
            //     redis.zrevrangeAsync(userSessionKey, page, size)
            //         .then(function (sessionIds) {
            //             if (sessionIds.length == 0) {
            //                 ModelUtil.emitOK(self.eventEmitter, []);
            //                 return;
            //             }
            //             callback(null, sessionIds);
            //         })
            // },
            function (callback) {
                redis.zrevrangeAsync(userSessionKey, page, size)
                    .then(function (sessionIds) {
                        if (sessionIds.length == 0) {
                            ModelUtil.emitOK(self.eventEmitter, []);
                            return;
                        }
                        callback(null, sessionIds);
                    })
                SessionRepo.findAllByType(userId,businessType,page,size,function(err,res){
                    if (res.length == 0) {
                        ModelUtil.emitOK(self.eventEmitter, []);
                        return;
                    }
                    var sessionIds=[];
                    for(var j in res){
                        sessionIds.push(res[j].id);
                    }
                    callback(null,sessionIds);
                })
            },
            // 遍历会话
            function (sessionIds) {
@ -407,6 +455,137 @@ class Sessions extends RedisModel {
                                let userRoles = res[5];
                                let participantsTime = [];
                                let isInvite = true;
                                //处理session未加入redis的bug
                                if(session==null){
                                    let lastLoginTime = new Date();
                                    SessionRepo.findOne(sessionId, function (err, res) {
                                        if(res){
                                            session = res;
                                            let redisSession = [
                                                "id", session.id,
                                                "name", session.name,
                                                "type", session.type,
                                                "business_type", session.business_type,
                                                "last_sender_id", session.last_sender_id||"",
                                                "last_sender_name", session.last_sender_name||"",
                                                "last_content_type", session.last_content_type||"",
                                                "last_content", session.last_content||"",
                                                "last_message_time", session.last_message_time||"",
                                                "create_date", ObjectUtil.timestampToLong(session.create_date),
                                                "status",session.status==null?0:session.status
                                            ];
                                            // cache sessions
                                            redis.multi()
                                                .zadd(REDIS_KEYS.Sessions, lastLoginTime.getTime(), sessionId)                                           // 会话的最后活动时间设置为此用户的登录时间
                                                .zadd(RedisModel.makeRedisKey(REDIS_KEYS.UserSessions, userId), lastLoginTime.getTime(), sessionId)      // 会话的最后活动时间设置为此用户的登录时间
                                                .hmset(RedisModel.makeRedisKey(REDIS_KEYS.Session, sessionId), redisSession)
                                                .execAsync()
                                                .then(function (res) {
                                                    // cache participants
                                                    let sessionParticipantsKey = RedisModel.makeRedisKey(REDIS_KEYS.SessionParticipants, sessionId);
                                                    let sessionParticipantsRoleKey = RedisModel.makeRedisKey(REDIS_KEYS.SessionParticipantsRole, sessionId);
                                                    ParticipantRepo.findAll(sessionId, function (err, participants) {
                                                        if (err) {
                                                            ModelUtil.emitError(self.eventEmitter, err.message);
                                                            return;
                                                        }
                                                        let multi = redis.multi();
                                                        participants.forEach(function (participant) {
                                                            let participantId = participant.id;
                                                            let participantRole = participant.role;
                                                            let score = ObjectUtil.timestampToLong(participant.last_fetch_time||(new Date()));
                                                            multi = multi.zadd(sessionParticipantsKey, score, participantId)
                                                                .hset(sessionParticipantsRoleKey, participantId, participantRole);
                                                        });
                                                        multi.execAsync()
                                                            .then(function (res) {
                                                            })
                                                            .catch(function (ex) {
                                                                log.error("Login failed while caching participants: ", ex);
                                                            });
                                                    });
                                                    // cache messages
                                                    let messagesKey = RedisModel.makeRedisKey(REDIS_KEYS.Messages, sessionId);
                                                    let messagesByTimestampKey = RedisModel.makeRedisKey(REDIS_KEYS.MessagesByTimestamp, sessionId);
                                                    MessageRepo.findBySessionId(sessionId, 0, config.sessionConfig.maxMessageCount, null, function (err, messages) {
                                                        if (err) {
                                                            ModelUtil.emitError(self.eventEmitter, err.message);
                                                            return;
                                                        }
                                                        let multi = redis.multi();
                                                        messages.forEach(function (message) {
                                                            let msgJson = {
                                                                id: message.id,
                                                                sender_id: message.sender_id,
                                                                sender_name: message.sender_name,
                                                                timestamp: ObjectUtil.timestampToLong(message.timestamp),
                                                                content_type: message.content_type,
                                                                content: message.content
                                                            };
                                                            multi = multi.hset(messagesKey, message.id, JSON.stringify(msgJson))
                                                                .zadd(messagesByTimestampKey, ObjectUtil.timestampToLong(message.timestamp), message.id);
                                                        });
                                                        multi.execAsync()
                                                            .then(function (res) {
                                                            })
                                                            .catch(function (ex) {
                                                                log.error("Login failed while caching messages: ", ex);
                                                            });
                                                    });
                                                    // cache topics for MUC
                                                    let topicsKey = RedisModel.makeRedisKey(REDIS_KEYS.Topics, sessionId);
                                                    TopicRepo.findAllBySessionId(sessionId, function (err, topics) {
                                                        if (err) {
                                                            ModelUtil.emitError(self.eventEmitter, err.message);
                                                            return;
                                                        }
                                                        topics.forEach(function (topic) {
                                                            let topicKey = RedisModel.makeRedisKey(REDIS_KEYS.Topic, topic.id);
                                                            let topicId = topic.id;
                                                            let name = topic.name == null ? "" : topic.name;
                                                            let createTime = ObjectUtil.timestampToLong(topic.create_time);
                                                            let endBy = topic.end_by == null ? "" : topic.end_by;
                                                            let endTime = topic.end_time == null ? 0 : ObjectUtil.timestampToLong(topic.end_time);
                                                            let startMessageId = topic.start_message_id == null ? "" : topic.start_message_id;
                                                            let endMessageId = topic.end_message_id == null ? "" : topic.end_message_id;
                                                            let description = topic.description == null ? "" : topic.description;
                                                            let status = topic.status == null ? 0 : topic.status;
                                                            redisConn.multi()
                                                                .zadd(topicsKey, createTime, topicId)
                                                                .hmset(topicKey,
                                                                    'name', name,
                                                                    'session_id', sessionId,
                                                                    'create_time', createTime,
                                                                    'end_by', endBy,
                                                                    'end_time', endTime,
                                                                    'start_message_id', startMessageId,
                                                                    'end_message_id', endMessageId,
                                                                    'description', description,
                                                                    'status', status)
                                                                .execAsync()
                                                                .catch(function (ex) {
                                                                    log.error("Login failed while caching topics: ", ex);
                                                                });
                                                        });
                                                    });
                                                })
                                                .catch(function (ex) {
                                                    log.error("Login failed while caching sessions: ", ex);
                                                });
                                        }
                                    });
                                }
                                for(var j in userRoles){
                                    if(userRoles[j]==1){
                                        isInvite = false;
@ -491,6 +670,149 @@ class Sessions extends RedisModel {
        ]);
    }
    /**
     * 查找会话数
     * @param userId
     * @param type
     * @param handler
     */
    getSessionCountByType(userId,type,handler){
        let self = this;
        SessionRepo.findSessionCountByType(userId,type,function (err,res) {
            if(res){
                ModelUtil.emitOK(self.eventEmitter,{status:200,count:res[0].count});
                return;
            }else {
                ModelUtil.emitOK(self.eventEmitter,{status:200,count:0});
                return;
            }
        })
    }
    /**
     * 根据用户类型获取用户的session列表
     * @param userId
     * @param page
     * @param size
     * @param businessType
     */
    getUserSessionsByType(userId,type,page, size) {
        let userSessionKey = RedisModel.makeRedisKey(REDIS_KEYS.UserSessions, userId);
        let self = this;
        async.waterfall([
            // 获取会话ID列表
            function (callback) {
                SessionRepo.findListByType(userId,type,page,size,function(err,res){
                    if (res.length == 0) {
                        ModelUtil.emitOK(self.eventEmitter, []);
                        return;
                    }
                    callback(null,res);
                })
            },
            // 遍历会话
            function (sessionIds) {
                let sessionList = [];
                let functionList = [];
                for (let j = 0; j < sessionIds.length; j++) {
                    let fun = function (index, callback) {
                        if (!callback) {
                            callback = index, index = 0
                        }
                        let mysqlSession = sessionIds[index];
                        let sessionId = mysqlSession.id;
                        let sessionKey = RedisModel.makeRedisKey(REDIS_KEYS.Session, sessionId);
                        let sessionParticipantsKey = RedisModel.makeRedisKey(REDIS_KEYS.SessionParticipants, sessionId);
                        redis.multi()
                            .hgetall(sessionKey)                       // 会话实体
                            .zscore(sessionParticipantsKey, userId)    // 用户在此会话中最后一次获取未读消息的时间
                            .zrange(sessionParticipantsKey, 0, -1)
                            .zrange(sessionParticipantsKey, 0,-1,'withscores')  // 所有用户在此会话中最后一次获取未读消息的时间
                            .execAsync()
                            .then(function (res) {
                                let session = res[0];
                                let lastFetchTime = res[1];
                                let users = res[2];
                                let participantsTimeArray = res[3];
                                let participantsTime = [];
                                for(var j = 0 ;j<participantsTimeArray.length;j++){
                                    if(j%2!=0)continue;
                                    let participantsTimeJson = {};
                                    participantsTimeJson[participantsTimeArray[j]] = participantsTimeArray[j+1];
                                    participantsTime.push(participantsTimeJson);
                                }
                                let sessionName = "";
                                let otherUserId = "";
                                if (session.type == SESSION_TYPES.P2P) {
                                    for (let j in users) {
                                        if (users[j] != userId) {
                                            otherUserId = users[j];
                                        }
                                    }
                                }
                                if (!lastFetchTime) lastFetchTime = new Date().getTime();
                                // 计算未读消息数
                                let messagesByTimestampKey = RedisModel.makeRedisKey(REDIS_KEYS.MessagesByTimestamp, sessionId);
                                redis.zcountAsync(messagesByTimestampKey, parseInt(lastFetchTime)+1, new Date().getTime())
                                    .then(function (count) {
                                        if (!otherUserId) otherUserId = userId;
                                        ParticipantRepo.findNameById(otherUserId, function (err, res) {
                                            if ((res && res.length == 0) || session.type != SESSION_TYPES.P2P) {
                                                sessionName = session.name;
                                            } else {
                                                sessionName = res[0].name;
                                            }
                                            var bir = new Date().getTime();
                                            if (res.length != 0 && res[0].birthdate) {
                                                bir = res[0].birthdate.getTime();
                                            }
                                            var sex = 1;
                                            if (res.length != 0 && res[0].sex) {
                                                sex = res[0].sex;
                                            }
                                            sessionList.push({
                                                id: sessionId,
                                                name: sessionName,
                                                create_date: new Date(mysqlSession.last_message_time).getTime(),
                                                last_content_type: session.last_content_type,
                                                last_content: session.last_content,
                                                sender_id: session.last_sender_id,
                                                type: session.type,
                                                sender_name: session.last_sender_name,
                                                unread_count: count,
                                                business_type: session.business_type,
                                                sender_sex: sex,
                                                sender_birthday: bir,
                                                participantsTimeArray:participantsTime,
                                                status:session.status,
                                            });
                                            index = (parseInt(index) + 1);
                                            if (index == sessionIds.length) {
                                                ModelUtil.emitOK(self.eventEmitter, sessionList);
                                            } else {
                                                callback(null, index);
                                            }
                                        })
                                    })
                            })
                            .catch(function (err) {
                                logger.error("Get sessions:"+sessionId+" failed: ", err);
                            });
                    };
                    functionList.push(fun);
                }
                async.waterfall(functionList);
            }
        ]);
    }
    /**
     * 根据用户ID获取用户已经结束咨询的session列表
     * @param userId
@ -509,11 +831,11 @@ class Sessions extends RedisModel {
                        ModelUtil.emitOK(self.eventEmitter, []);
                        return;
                    }
                    var sessionIds=[];
                    for(var j in res){
                        sessionIds.push(res[j].id);
                    }
                    callback(null,sessionIds);
                    // var sessionIds=[];
                    // for(var j in res){
                    //     sessionIds.push(res[j].id);
                    // }
                    callback(null,res);
                })
            },
            // 遍历会话
@ -526,7 +848,8 @@ class Sessions extends RedisModel {
                            callback = index, index = 0
                        }
                        let sessionId = sessionIds[index];
                        let mysqlSession = sessionIds[index];
                        let sessionId = mysqlSession.id;
                        let sessionKey = RedisModel.makeRedisKey(REDIS_KEYS.Session, sessionId);
                        let participantsRoleKey = RedisModel.makeRedisKey(REDIS_KEYS.SessionParticipantsRole, sessionId);
                        let sessionParticipantsKey = RedisModel.makeRedisKey(REDIS_KEYS.SessionParticipants, sessionId);
@ -595,7 +918,7 @@ class Sessions extends RedisModel {
                                            sessionList.push({
                                                id: sessionId,
                                                name: sessionName,
                                                create_date: session.create_date,
                                                create_date: new Date(mysqlSession.last_message_time).getTime(),
                                                last_content_type: session.last_content_type,
                                                last_content: session.last_content,
                                                sender_id: session.last_sender_id,
@ -621,7 +944,7 @@ class Sessions extends RedisModel {
                                    })
                            })
                            .catch(function (err) {
                                logger.error("Get sessions failed: ", err);
                                logger.error("Get sessions:"+sessionId+" failed: ", err);
                            });
                    };
@ -653,13 +976,48 @@ class Sessions extends RedisModel {
        let message_timestamp_key = RedisModel.makeRedisKey(REDIS_KEYS.MessagesByTimestamp, sessionId);
        if (!start_msg_id && !end_msg_id) {
            redis.zrevrangeAsync(message_timestamp_key, 0, 0).then(function (res) {
                logger.info("redis return res-----"+res);
                if (res.length == 0) {
                    if (handler) {
                        handler(null, res);
                        return;
                    }
                    ModelUtil.emitOK(self.eventEmitter, res);
                    return;
                    //修复应redis没有缓冲聊天记录导致会话列表加载不出来
                    // cache messages
                    let messagesKey = RedisModel.makeRedisKey(REDIS_KEYS.Messages, sessionId);
                    MessageRepo.findBySessionId(sessionId, 0, config.sessionConfig.maxMessageCount, null, function (err, messages) {
                        if (err) {
                            ModelUtil.emitError(self.eventEmitter, err.message);
                            return;
                        }
                        let multi = redis.multi();
                        messages.forEach(function (message) {
                            let msgJson = {
                                id: message.id,
                                sender_id: message.sender_id,
                                sender_name: message.sender_name,
                                timestamp: ObjectUtil.timestampToLong(message.timestamp),
                                content_type: message.content_type,
                                content: message.content
                            };
                            multi = multi.hset(messagesKey, message.id, JSON.stringify(msgJson))
                                .zadd(message_timestamp_key, ObjectUtil.timestampToLong(message.timestamp), message.id);
                        });
                        multi.execAsync()
                            .then(function (res) {
                            })
                            .catch(function (ex) {
                                log.error("Login failed while caching messages: ", ex);
                                ModelUtil.emitOK(self.eventEmitter, res);
                                return;
                            });
                    });
                    // if (handler) {
                    //     handler(null, res);
                    //     return;
                    // }
                    // ModelUtil.emitOK(self.eventEmitter, res);
                    // return;
                }
                start_msg_id = res[0];
                redis.zrangeAsync(message_timestamp_key, 0, 0).then(function (res) {
@ -884,7 +1242,9 @@ class Sessions extends RedisModel {
        let count = 0;
        let patientCount = 0;
        let doctorCount = 0;
        let patientEndCount = 0;
        SessionRepo.findAll(userId, function (err, res) {
        // SessionRepo.findUnEndAll(userId, function (err, res) {
            if (err) {
                if(handler)
                {
@ -933,7 +1293,11 @@ class Sessions extends RedisModel {
                count = count + con;
                if (session.business_type == SESSION_BUSINESS_TYPE.PATIENT) {
                    patientCount = patientCount + con;
                    if(session.status == SESSION_STATUS.ENDED){//新增判断是否咨询结束
                        patientEndCount = patientEndCount + con;
                    }else{
                        patientCount = patientCount + con;
                    }
                } else {
                    doctorCount = doctorCount + con;
                }
@ -943,7 +1307,7 @@ class Sessions extends RedisModel {
                        handler(err,count)
                        return;
                    }
                    ModelUtil.emitOK(self.eventEmitter, {count: count, patient: patientCount, doctor: doctorCount});
                    ModelUtil.emitOK(self.eventEmitter, {count: count, patient: patientCount,patientEnd: patientEndCount, doctor: doctorCount});
                }
            })
        }
@ -972,7 +1336,7 @@ class Sessions extends RedisModel {
            function (lastFetchTime, callback) {
                if (!lastFetchTime) lastFetchTime = 0;
                let now = new Date().getTime();
                redis.zcountAsync(messagesByTimestampKey, lastFetchTime, now)
                redis.zcountAsync(messagesByTimestampKey, parseInt(lastFetchTime)+1, now)
                    .then(function (count) {
                        if (handler) {
                            handler(null, count);
@ -1059,6 +1423,7 @@ class Sessions extends RedisModel {
        let sessionKey = RedisModel.makeRedisKey(REDIS_KEYS.Session, sessionId);
        let messageId = mongoose.Types.ObjectId().toString();
        let sessionType =0;
        let sessionName;
        message.id = messageId;
        // 检查会话中是否存在此成员
@ -1073,8 +1438,49 @@ class Sessions extends RedisModel {
                    sessionType = res[0];
                    let sessionName = res[1];
                    if (sessionType == null) {
                        ModelUtil.emitError(self.eventEmitter, "Session " + sessionId + " is not found.");
                        return;
                        self.getSessions(sessionId,function(err,res){
                            if (err){
                                logger.error("session data is error");
                            } else {
                                sessionName=res[0].name;
                                sessionType = res[0].type;
                            }
                        });
                        let participantArray = [];
                        let participantsStr="{";
                        ParticipantRepo.findAll(sessionId, function (err, participants) {
                            if (err) {
                                ModelUtil.emitError(self.eventEmitter, err.message);
                                return;
                            }
                            participants.forEach(function (participant) {
                                let participantId = participant.id;
                                let participantRole = participant.role;
                                let score = ObjectUtil.timestampToLong(participant.last_fetch_time||(new Date()));
                                participantsStr +="\""+participantId+"\":\""+participantRole+"\",";
                            });
                            participantsStr = participantsStr.substring(0,participantsStr.length-1)+'}';
                            participantsStr = JSON.parse(participantsStr);
                            for (let j in participantsStr) {
                                participantArray.push(j + ":" + participantsStr[j]);
                            }
                            //创建session到redis
                            self.createSessionToRedis(sessionId, sessionName, sessionType, participantArray, messageId, function (err, res) {
                                if (err) {
                                    if (handler) {
                                        handler(err, null);
                                    }
                                    ModelUtil.emitError(self.eventEmitter, {message: err, status: -1}, null);
                                } else {
                                    if (handler) {
                                        handler(null, res);
                                    }
                                    ModelUtil.emitOK(self.eventEmitter, {status: 200, data: res});
                                }
                            });
                        });
                    }
                    if(sessionType == SESSION_TYPES.MUC || sessionType == SESSION_TYPES.PRESCRIPTION || SESSION_TYPES.PRESCRIPTION_HOSPITAL || SESSION_TYPES.ONDOOR_NURSING){
                        if(message.content_type == CONTENT_TYPES.PlainText ||
@ -1094,6 +1500,7 @@ class Sessions extends RedisModel {
                            })
                        }
                    }
                    // 消息保存到Redis,并更新会话最后状态、用户最后消息获取时间
                    messages.saveMessageToRedis(sessionId, sessionType, messageId, message);
                    Messages.updateLastContent(sessionKey, sessionType, sessionName, message);
@ -1140,6 +1547,7 @@ class Sessions extends RedisModel {
        }
    }
    sendTopicMessages(topicId, message) {
        let self = this;
        TopicRepo.findAllByTopicId(topicId, function (err, res) {
@ -1227,7 +1635,8 @@ class Sessions extends RedisModel {
        });
        function callPush(participants,message){
            participants.forEach(function (participant) {
                if (participant.id !== message.sender_id &&
                if ((participant.id !== message.sender_id||message.content_type == CONTENT_TYPES.PrescriptionBloodStatus ||
                    message.content_type == CONTENT_TYPES.PrescriptionFollowupContent) &&
                    participant.participant_role == PARTICIPANT_ROLES.HOST) {
                    Sessions.pushNotification(participant.id, participant.name, message,sessionType);
                }
@ -1403,7 +1812,7 @@ class Sessions extends RedisModel {
        let self = this;
        Users.isPatientId(targetUserId, function (err, isPatient) {
            if (isPatient) {
                if(config.environment!='prodPC'){//pc版不直接发送给居民,通过redis的publish
                if(config.environment!='local'){//pc版不直接发送给居民,通过redis的publish
                    WechatClient.sendMessage(targetUserId, targetUserName, message);
                }
                message.targetUserId = targetUserId;
@ -1416,17 +1825,47 @@ class Sessions extends RedisModel {
                }
                //告知医生新消息
                WechatClient.sendSocketMessageToDoctor(targetUserId,message);
                if(config.environment!='prodPC'){//pc版不推送个推,通过redis的publish
                    WlyySDK.request(targetUserId, '', '', '', '/im/common/message/messages', 'POST', function (err, res) {
                        let count = 0;
                        res =  JSON.parse(res)
                        if (res.status == 200) {
                            let data = res.data;
                            count = parseInt(JSON.parse(data.imMsgCount).count) + parseInt(data.system.amount) + parseInt(data.healthIndex.amount) + parseInt(data.sign.amount);
                        }
                        AppClient.sendNotification(targetUserId, message,sessionType,count);
                    });
                let count = 0;
                //系统消息
                MessageRepo.getWlyyMessageCount(targetUserId,function (err,res) {
                    if(res){
                        count = res[0].count;
                    }
                });
                //im消息
                let sessions = new Sessions();
                sessions.getAllSessionsUnreadMessageCount(targetUserId,function (err,res) {
                    if(res){
                        count += res;
                    }
                });
                if(config.environment!='local'){//pc版不推送个推,通过redis的publish
                    AppClient.sendNotification(targetUserId, message,sessionType,count);
                }
                //外网pcim通过socket推送
                WechatClient.sendPcImSocket(targetUserId,message,sessionType);
                // WlyySDK.request(targetUserId, '', '', '', '/im/common/message/messages', 'POST', function (err, res) {
                //     let count = 0;
                //     if(err){
                //         logger.error(err);
                //     }else {
                //         logger.error(res);
                //         res =  JSON.parse(res)
                //         if (res.status == 200) {
                //             let data = res.data;
                //             count = parseInt(JSON.parse(data.imMsgCount).count) + parseInt(data.system.amount) + parseInt(data.healthIndex.amount) + parseInt(data.sign.amount);
                //         }
                //     }
                //     if(config.environment!='local'){//pc版不推送个推,通过redis的publish
                //         AppClient.sendNotification(targetUserId, message,sessionType,count);
                //     }
                //     //外网pcim通过socket推送
                //     WechatClient.sendPcImSocket(targetUserId,message,sessionType);
                // });
                message.targetUserId = targetUserId;
                message.targetUserName = targetUserName;
                message.sessionType = sessionType;
@ -1448,7 +1887,7 @@ class Sessions extends RedisModel {
     */
    static getRedisPushNotification(message) {
        if (message.targetType=='patient') {
            if(config.environment!='prodPC'){//pc版接收要发给居民的消息不做处理
            if(config.environment!='local'){//pc版接收要发给居民的消息不做处理
                WechatClient.sendMessage(message.targetUserId, message.targetUserName, message);
            }
        } else {
@ -1457,7 +1896,7 @@ class Sessions extends RedisModel {
            }
            //告知医生新消息
            WechatClient.sendSocketMessageToDoctor(message.targetUserId,message);
            if(config.environment!='prodPC'){//pc版不推送个推
            if(config.environment!='local'){//pc版不推送个推
                WlyySDK.request(message.targetUserId, '', '', '', '/im/common/message/messages', 'POST', function (err, res) {
                    let count = 0;
                    res =  JSON.parse(res)
@ -1510,6 +1949,32 @@ class Sessions extends RedisModel {
    }
    /**
     * 针对专科医生和家医模式更新会话的名称
     * @param sessionId
     * @param name
     */
    updateSessionName(sessionId,name,handler){
        let self = this;
        let sessionKey  =  RedisModel.makeRedisKey(REDIS_KEYS.Session,sessionId);
        redis.hsetAsync(sessionKey,"name",name).then(function(res){
            SessionRepo.updateSessionName(sessionId,name,function(err,sqlResult){
                if(handler){
                    handler(err,sqlResult);
                    return;
                }
                if(err){
                    logger.error("set session name to mysql is error !");
                    ModelUtil.emitError(self.eventEmitter,"set session name to mysql is error !",err)
                }else{
                    logger.info("set session name is success");
                    ModelUtil.emitOK(self.eventEmitter, []);
                }
            });
        });
    }
}
// Expose class

+ 30 - 5
src/server/models/sessions/topics.js

@ -171,12 +171,12 @@ class Topics extends RedisModel {
     * @param page
     * @param pagesize
     */
    findAllTopicByType(users,reply, status,type, page, pagesize) {
    findAllTopicByType(users,reply, status,type,patientName,startTime,endTime, page, pagesize) {
        let self = this;
        page = (page - 1 < 0 ? 0 : page - 1) * pagesize;
        if (!pagesize) pagesize = 10;
        pagesize = parseInt(pagesize);
        TopicsRepo.findAllTopicByType(users,reply,status,type, page, pagesize, function (err, res) {
        TopicsRepo.findAllTopicByType(users,reply,status,type,patientName,startTime,endTime, page, pagesize, function (err, res) {
            if (err) {
                ModelUtil.emitError(self.eventEmitter, "获取列表失败" + err);
            }
@ -184,6 +184,23 @@ class Topics extends RedisModel {
        })
    }
    /**
     * 按类型查找医生的未回复,进行中,已完成的咨询 总数
     * @param users
     * @param reply
     * @param status
     * @param adminTeamCode
     */
    topicListCountByType(users,reply, status,type,patientName,startTime,endTime) {
        let self = this;
        TopicsRepo.topicListCountByType(users,reply,status,type,patientName,startTime,endTime, function (err, res) {
            if (err) {
                ModelUtil.emitError(self.eventEmitter, "获取总数失败" + err);
            }
            ModelUtil.emitOK(self.eventEmitter, {count:res[0].count});
        })
    }
    findReplyCount(users,reply, status,adminTeamCode) {
        let self = this;
@ -418,9 +435,17 @@ class Topics extends RedisModel {
        let self = this;
        let topic_key = RedisModel.makeRedisKey(REDIS_KEYS.Topic, topicId);
        redis.hmsetAsync(topic_key, "end_time", endDate.getTime(), "end_by", endUser, "status", TOPIC_STATUS.ENDED).then(function (res) {
            redis.hgetallAsync(topic_key).then(function (topic) {
                callEnd(topic.session_id);
            })
            // redis.hgetallAsync(topic_key).then(function (topic) {
            //     callEnd(topic.session_id);
            // })
            // redis 出错的时候 根据topic_key无法取出正确的sessionId,改成从数据库取
            TopicsRepo.findOne(topicId,function (err, res){
                if(err){
                    ModelUtil.emitOK(self.eventEmitter, {status: -1, "message": err});
                }else {
                    callEnd(res[0].session_id);
                }
            });
        });
        /**

+ 19 - 0
src/server/models/socket.io/pcDoctor.client.js

@ -0,0 +1,19 @@
/**
 * pc端im socket.
 *
 * author: ysj
 * since: 2017/10/26
 */
"use strict";
var util = require('util');
var Client = require('./client');
class PcDoctorClient extends Client{
    constructor(socket, socketServer){
        super(socket, socketServer);
    }
}
module.exports = PcDoctorClient;

+ 10 - 2
src/server/models/user/users.js

@ -86,8 +86,11 @@ class Users extends RedisModel {
     */
    login(userId, platform, deviceToken, clientId) {
        let self = this;
        let loginFromApp = platform !== PLATFORMS.Wechat;
        let loginFromPc = platform !== PLATFORMS.PC;
        if(platform){
            platform = parseInt(platform);
        }
        let loginFromApp = (platform === PLATFORMS.Android)||(platform === PLATFORMS.iOS);
        let loginFromPc = platform === PLATFORMS.PC;
        log.error(userId+"  "+ platform+"  "+deviceToken+"  "+clientId);
        let usersKey = REDIS_KEYS.Users;
        let userKey = RedisModel.makeRedisKey(REDIS_KEYS.User, userId);
@ -159,6 +162,11 @@ class Users extends RedisModel {
                        sessions.forEach(function (session) {
                            redisConn.zscore(REDIS_KEYS.Sessions, session.id, function (err, res) {
                                if (res != null) return;    // 已经缓存过的会话不再缓存
                                // if(session.id =="915ce5ab-5b1d-11e6-8344-fa163e8aee56_828c1a62000d4838a0c8bab1acdfadff_8"){
                                //     log.error("1111");
                                // }else if(res != null){
                                //     return;
                                // }
                                (function (sessionId, userId) {
                                    let redisSession = [
                                        "id", session.id,

+ 56 - 0
src/server/repository/mysql/message.noticeSetting.repo.js

@ -0,0 +1,56 @@
/**
 * 消息提醒设置
 * Created by ysj on 2017/12/4.
 */
"use strict";
let log = require('../../util/log');
let ImDb = require('../mysql/db/im.db');
const DB_TABLES = require('../../include/commons').DB_TABLES;
class MessageNoticeSettingRepo{
    constructor(){
    }
    /**
     * 获取单个MessageNoticeSetting对象
     *
     * @param user
     * @param type
     * @param handler
     * return  master_switch 总开关(1开,0关)',im_switch im消息开关(1开,0关)',family_topic_switch 健管师邀请后推送开关(1开,0关)',
     */
    static findOne(user,type, handler) {
        let sql = "select master_switch,im_switch,family_topic_switch,sound_switch,vibration_switch from " + DB_TABLES.MessageNoticeSetting + " s where s.user = ? and s.type=? ";
        ImDb.execQuery({
            "sql": sql,
            "args": [user,type],
            "handler": handler || function (err, res) {
                if(err) log.error(err);
            }
        });
    }
    /**
     * 保存MessageNoticeSetting
     *
     * @param user 用户id
     * @param type 用户类型 1doctor 2patient
     * @param createTime
     * @param handler 回调函数
     */
    static save(user, type, createTime, handler) {
        let sql = "insert into " + DB_TABLES.MessageNoticeSetting + " (user,type,master_switch,im_switch,family_topic_switch," +
            "sign_switch,health_sign_switch,system_switch,prescription_switch,sound_switch,vibration_switch,coordination_switch,create_time)" +
            " values (?,?,1,1,1,1,1,1,1,1,1,1,?)";
        ImDb.execQuery({
            "sql": sql,
            "args": [user, type, createTime.getTime()],
            "handler": handler
        });
    }
}
module.exports = MessageNoticeSettingRepo;

+ 15 - 1
src/server/repository/mysql/message.repo.js

@ -37,7 +37,7 @@ class MessageRepo {
                let params = [];
                let type = res[0].type;
                let MessageTable = "";
                if (type == 1) {
                if (type == 1 || type == 8) {
                    MessageTable = DB_TABLES.MucMessages;
                } else if (type == 2) {
                    MessageTable = DB_TABLES.P2pMessages;
@ -117,6 +117,20 @@ class MessageRepo {
        });
    }
    /**
     * i健康未读消息数
     * @param user
     * @param handler
     */
    static getWlyyMessageCount(user, handler) {
        let sql = "SELECT count(1) as count FROM "+DB_TABLES.WlyyMessage+" a WHERE a.has_read= 1 and a.receiver = ?";
        ImDb.execQuery({
            "sql": sql,
            "args": [user],
            "handler": handler
        });
    }
}
module.exports = MessageRepo;

+ 28 - 5
src/server/repository/mysql/participant.repo.js

@ -23,9 +23,9 @@ class ParticipantRepo {
     * @param handler
     */
    static findAll(sessionId, handler) {
        let sql = "SELECT u.id, u.name, u.sex, u.birthdate, u.avatar, p.participant_role role, false is_patient,p.last_fetch_time,u.level FROM sessions s, participants p, doctors u " +
        let sql = "SELECT u.id, u.name, u.sex, u.birthdate, u.avatar,u.hospital_name, p.participant_role role, false is_patient,p.last_fetch_time,u.level FROM sessions s, participants p, doctors u " +
            "WHERE s.id = ? AND s.id = p.session_id AND p.participant_id = u.id union " +
            "SELECT u.id, u.name, u.sex, u.birthdate, u.avatar, p.participant_role role, true is_patient,p.last_fetch_time,0 as level FROM sessions s, participants p, patients u " +
            "SELECT u.id, u.name, u.sex, u.birthdate, u.avatar,u.hospital_name, p.participant_role role, true is_patient,p.last_fetch_time,0 as level FROM sessions s, participants p, patients u " +
            "WHERE s.id = ? AND s.id = p.session_id AND p.participant_id = u.id";
        ImDb.execQuery({
@ -65,6 +65,20 @@ class ParticipantRepo {
        });
    }
    /**
     * 根据会话id查找会话成员
     * @param sessionId
     * @param handler
     */
    static findParricipantBySessionId(sessionId,handler){
        let sql = "select participant_id,participant_role from participants p where p.session_id = ?";
        ImDb.execQuery({
            "sql": sql,
            "args": [sessionId],
            "handler": handler
        });
    }
    /**
     * 获取会话医生的id
     * @param sessionId
@ -108,9 +122,9 @@ class ParticipantRepo {
     * @param handler
     */
    static findAllAvatars(sessionId, handler) {
        let sql = "SELECT u.id, u.avatar,'0' as ispatient FROM sessions s, participants p, doctors u " +
        let sql = "SELECT u.id, u.avatar,'0' as ispatient,u.name as name FROM sessions s, participants p, doctors u " +
            "WHERE s.id = ? AND s.id = p.session_id AND p.participant_id = u.id union " +
            "SELECT u.id, u.avatar,'1' as ispatient FROM sessions s, participants p, patients u " +
            "SELECT u.id, u.avatar,'1' as ispatient,u.name as name FROM sessions s, participants p, patients u " +
            "WHERE s.id = ? AND s.id = p.session_id AND p.participant_id = u.id";
        ImDb.execQuery({
@ -248,7 +262,7 @@ class ParticipantRepo {
    static saveParticipantsToMysql(sessionId, users, handler) {
        let sql = "INSERT INTO " + DB_TABLES.Participants + " (session_id,participant_id,participant_role,last_fetch_time) VALUES ";
        let args = [];
        let nowDate = new Date();
        let nowDate = null;
        for (let j in users) {
            let tokens = users[j].split(":");
            sql += "(?,?,?,?)";
@ -287,6 +301,15 @@ class ParticipantRepo {
            }
        });
    }
    static findLastFetchTime(sessionId,userId,handler){
        let sql = "select last_fetch_time from participants p where p.session_id = ? and p.participant_id=?";
        ImDb.execQuery({
            "sql": sql,
            "args": [sessionId,userId],
            "handler": handler
        });
    }
}
module.exports = ParticipantRepo;

+ 1 - 1
src/server/repository/mysql/patient.repo.js

@ -39,7 +39,7 @@ class PatientRepo {
     */
    static findWechatOpenIds(code, handler){
        var sql = "SELECT m.family_member code,p.name,p.openid from wlyy.wlyy_patient p,wlyy.wlyy_patient_family_member m" +
            "  WHERE m.patient = ? and m.family_member=p.code and p.openid is not null" +
            "  WHERE m.patient = ? and m.family_member=p.code and p.openid is not null and m.is_authorize = 1 " +
            " UNION ALL" +
            "  SELECT p.code,p.name,p.openid  from wlyy.wlyy_patient p WHERE p.code = ?";
        ImDb.execQuery({

+ 8 - 8
src/server/repository/mysql/search.repo.js

@ -20,11 +20,11 @@ class SearchRepo {
    static findTopicEndedSessionIdList(userId, handler) {
        let sql = "SELECT s.id " +
            "FROM sessions s, topics t, participants p " +
            "WHERE p.participant_id = ? AND p.session_id = s.id AND s.id = t.session_id AND t.end_message_id IS NOT NULL AND s.`type` IN (1,2) " +
            "WHERE p.participant_id = ? AND p.session_id = s.id AND s.id = t.session_id AND t.end_message_id IS NOT NULL AND s.`type` IN (1,2,8) " +
            " UNION " +
            "SELECT s.id " +
            "FROM sessions s, participants p " +
            "WHERE p.participant_id = ? AND p.session_id = s.id AND s.`type` IN (2, 3)";
            "WHERE p.participant_id = ? AND p.session_id = s.id AND s.`type` IN (2, 3, 4)";
        ImDb.execQuery({
            sql: sql,
@ -42,11 +42,11 @@ class SearchRepo {
    static findTopicActiveSessionIdList(userId, handler) {
        let sql = "SELECT s.id " +
            "FROM sessions s, topics t, participants p " +
            "WHERE p.participant_id = ? AND p.session_id = s.id AND s.id = t.session_id AND t.end_message_id IS NULL  AND s.`type` IN (1,2) " +
            "WHERE p.participant_id = ? AND p.session_id = s.id AND s.id = t.session_id AND t.end_message_id IS NULL  AND s.`type` IN (1,2,8) " +
            " UNION " +
            "SELECT s.id " +
            "FROM sessions s, participants p " +
            "WHERE p.participant_id = ? AND p.session_id = s.id  AND s.`type` IN (2,3)  and s.id not in(" +
            "WHERE p.participant_id = ? AND p.session_id = s.id  AND s.`type` IN (2,3,4)  and s.id not in(" +
            " select DISTINCT p1.session_id from  participants p1 ,topics t where p1.participant_id = ? and  t.session_id = p1.session_id  " +
            ") ";
        ImDb.execQuery({
@ -115,9 +115,9 @@ class SearchRepo {
            " u,sessions s WHERE s.id in (?) AND s.id = p.session_id AND p.participant_id = u.id and p.participant_id<>? AND (u.name like ? or u.idcard like ?) ";
        if (userTable === DB_TABLES.Doctors) {
            sql += " AND s.type in (2) and s.business_type = 1 ";
            sql += " AND s.type = 2 and s.business_type = 1 ";
        }else{
            sql += " AND s.type in (1,2)  and s.business_type = 2 ";
            sql += " AND s.type in (1,2,8)  and s.business_type = 2 ";
        }
        sql += " limit ?, ? ";
        sql = vsprintf(sql, [userTable == DB_TABLES.Doctors ? ', hospital_name' : '']);
@ -141,7 +141,7 @@ class SearchRepo {
        let sql = "SELECT * FROM(" +
            "SELECT s.id, s.name, s.type, s.create_date, s.business_type,GROUP_CONCAT(u. NAME) as participant_name " +
            "FROM sessions s,  doctors u ,participants p " +
            "WHERE s.id IN (?) and s.type = 3 AND s.id = p.session_id AND p.participant_id = u.id and p.participant_id<>? AND (u.name like ? or s.name like ?) group by s.id " +
            "WHERE s.id IN (?) and s.type in (3,4) AND s.id = p.session_id AND p.participant_id = u.id and p.participant_id<>? AND (u.name like ? or s.name like ?) group by s.id " +
            ") X LIMIT ?, ?";
        keyword = '%' + keyword + '%';
@ -167,7 +167,7 @@ class SearchRepo {
            " UNION " +
            "SELECT s.id session_id, s.name session_name, s.type session_type, s.business_type session_business_type, m.id message_id, m.sender_id, m.sender_name, m.timestamp, m.content " +
            "FROM sessions s, group_messages m " +
            "WHERE s.id IN (?) AND s.id = m.session_id AND s.`type` = 3 AND m.content_type = 1 AND m.content LIKE ? ) X " +
            "WHERE s.id IN (?) AND s.id = m.session_id AND s.`type` in (3,5) AND m.content_type = 1 AND m.content LIKE ? ) X " +
            "ORDER BY X.session_id, X.message_id LIMIT ?, ?";
        keyword = '%' + keyword + '%';

+ 135 - 14
src/server/repository/mysql/session.repo.js

@ -9,6 +9,7 @@ let log = require('../../util/log.js');
const DB_TABLES = require('../../include/commons').DB_TABLES;
const PARTICIPANT_ROLES = require('../../include/commons').PARTICIPANT_ROLES;
const SESSION_STATUS = require('../../include/commons').SESSION_STATUS;
const SESSION_BUSINESS_TYPE = require('../../include/commons').SESSION_BUSINESS_TYPE;
class SessionRepo {
    constructor() {
    }
@ -20,7 +21,7 @@ class SessionRepo {
     * @param handler
     */
    static findOne(sessionId, handler) {
        let sessionSQL = "select id,name,type,create_date from " + DB_TABLES.Sessions + " s where s.id = ?";
        let sessionSQL = "select id,name,type,create_date,business_type from " + DB_TABLES.Sessions + " s where s.id = ?";
        ImDb.execQuery({
            "sql": sessionSQL,
            "args": [sessionId],
@ -49,6 +50,27 @@ class SessionRepo {
            }
        });
    }
    /**
     * 获取用户全部(未结束的)会话不包含角色未1的会话
     * @param userId
     * @param handler
     */
    static findUnEndAll(userId, handler) {
        let sql = "select session_id from " + DB_TABLES.Participants + " w where w.participant_id = ? and participant_role =0  group by w.session_id";
        let sys_session = "select session_id from " + DB_TABLES.Participants + " w where w.participant_id = 'system' and participant_role =0  group by w.session_id";
        let sessionSQL = "select id, name, type, create_date,business_type, last_sender_id, last_sender_name, last_content_type, last_content, last_message_time,status from "
            + DB_TABLES.Sessions + " s where s.id in(" + sql + ") and s.id not in ("+sys_session+") and s.`status` = 0";
        ImDb.execQuery({
            "sql": sessionSQL,
            "args": [userId],
            "handler": handler || function (err, res) {
                if(err) log.error(err);
            }
        });
    }
    /**
     * 获取用户全部会话忽略角色
     *
@ -89,6 +111,51 @@ class SessionRepo {
        });
    }
    /**
     * 查找某类型的用户的会话数量
     * @param userId
     * @param type
     * @param handler
     */
    static findSessionCountByType(userId,type,handler){
        let sql = "select session_id count from " + DB_TABLES.Participants + " w where w.participant_id = ? group by w.session_id";
        let sessionSQL = "select count(id) count from " + DB_TABLES.Sessions + " s where s.id in(" + sql + ") and s.type=?";
        ImDb.execQuery({
            "sql": sessionSQL,
            "args": [userId, type],
            "handler": handler || function (err, res) {
                if(err) log.error(err);
            }
        });
    }
    /**
     * 分页获取用户会话列表
     * @param userId
     * @param type
     * @param handler
     */
    static findListByType(userId, type,page,pagesize, handler) {
        if (page > 0) {
            if (page == 1) {
                page = 0;
            }else{
                page = (parseInt(page)-1) * parseInt(pagesize);
            }
        }
        let sql = "select session_id from " + DB_TABLES.Participants + " w where w.participant_id = ? group by w.session_id";
        let sessionSQL = "select id, name, type, create_date, last_sender_id, last_sender_name, last_content_type, last_content, last_message_time from "
            + DB_TABLES.Sessions + " s where s.id in(" + sql + ") and s.type=? order by s.last_message_time desc limit "+page+","+pagesize;
        ImDb.execQuery({
            "sql": sessionSQL,
            "args": [userId, type],
            "handler": handler || function (err, res) {
                if(err) log.error(err);
            }
        });
    }
    static findAllByTypeAndStatus(userId, businessType,status,page,pagesize, handler) {
        if (page > 0) {
            if (page == 1) {
@ -100,19 +167,38 @@ class SessionRepo {
        let sessionSQL ="";
        let sql ="";
        if(status == SESSION_STATUS.ENDED){
            //找出已经结束的咨询
            sql = "select session_id from " + DB_TABLES.Participants + " w where w.participant_id = ?  group by w.session_id";
            //找出角色讨论组中为旁听且未结束的咨询
            let sql1 = ("select session_id from " + DB_TABLES.Participants + " w where w.participant_id = ? and participant_role ="+PARTICIPANT_ROLES.REGULAR+" group by w.session_id")
            sessionSQL =  "select * from "
                + DB_TABLES.Sessions + " s where (s.id in(" + sql + ") and s.business_type = ? and s.status = 1) or (s.id in(" + sql1 + ") and s.business_type = ? and s.status = 0) limit "+page+","+pagesize;
            ImDb.execQuery({
                "sql": sessionSQL,
                "args": [userId, businessType,userId,businessType],
                "handler": handler || function (err, res) {
                    if(err) log.error(err);
                }
            });
            if(businessType == SESSION_BUSINESS_TYPE.PATIENT){//区分居民,有未读消息的置顶排列
                //找出已经结束的咨询
                sql = "select session_id from " + DB_TABLES.Participants + " w where w.participant_id = ?  group by w.session_id";
                //找出角色讨论组中为旁听且未结束的咨询
                let sql1 = ("select session_id from " + DB_TABLES.Participants + " w where w.participant_id = ? and participant_role ="+PARTICIPANT_ROLES.REGULAR+" group by w.session_id")
                sessionSQL =  "select s.* from " + DB_TABLES.Sessions + " s, " + DB_TABLES.Participants + " p " +
                    " where ((s.id in(" + sql + ") and s.business_type = ? and s.status = 1) or (s.id in(" + sql1 + ") and s.business_type = ? and s.status = 0)) " +
                    // " and s.id = p.session_id and p.participant_id = ? ORDER BY (p.last_fetch_time - s.last_message_time+1)>0,s.create_date desc limit "+page+","+pagesize;
                    " and s.id = p.session_id and p.participant_id = ? ORDER BY s.last_message_time desc limit "+page+","+pagesize;
                ImDb.execQuery({
                    "sql": sessionSQL,
                    "args": [userId, businessType,userId,businessType,userId],
                    "handler": handler || function (err, res) {
                        if(err) log.error(err);
                    }
                });
            }else{
                //找出已经结束的咨询
                sql = "select session_id from " + DB_TABLES.Participants + " w where w.participant_id = ?  group by w.session_id";
                //找出角色讨论组中为旁听且未结束的咨询
                let sql1 = ("select session_id from " + DB_TABLES.Participants + " w where w.participant_id = ? and participant_role ="+PARTICIPANT_ROLES.REGULAR+" group by w.session_id")
                sessionSQL =  "select * from "
                    + DB_TABLES.Sessions + " s where (s.id in(" + sql + ") and s.business_type = ? and s.status = 1) or (s.id in(" + sql1 + ") and s.business_type = ? and s.status = 0) limit "+page+","+pagesize;
                ImDb.execQuery({
                    "sql": sessionSQL,
                    "args": [userId, businessType,userId,businessType],
                    "handler": handler || function (err, res) {
                        if(err) log.error(err);
                    }
                });
            }
        }else{
            sql = "select session_id from " + DB_TABLES.Participants + " w where w.participant_id = ? and participant_role ="+PARTICIPANT_ROLES.HOST+" group by w.session_id";
@ -129,6 +215,30 @@ class SessionRepo {
    }
    static findAllByType(userId, businessType,page,pagesize, handler) {
        if (page > 0) {
            if (page == 1) {
                page = 0;
            }else{
                page = (parseInt(page)-1) * parseInt(pagesize);
            }
        }
        let sessionSQL ="";
        let sql ="";
        sql = "select session_id from " + DB_TABLES.Participants + " w where w.participant_id = ? and participant_role ="+PARTICIPANT_ROLES.HOST+" group by w.session_id";
        sessionSQL =  "select * from "
            + DB_TABLES.Sessions + " s where s.id in(" + sql + ") and s.business_type = ? and s.type!=0 limit "+page+","+pagesize;
        ImDb.execQuery({
            "sql": sessionSQL,
            "args": [userId, businessType],
            "handler": handler || function (err, res) {
                if(err) log.error(err);
            }
        });
    }
    /**
     * 按时间跨度查询会话。
     *
@ -268,6 +378,17 @@ class SessionRepo {
            }
        });
    }
    static updateSessionName(sessionId,name,handler){
        let sql = "update " + DB_TABLES.Sessions + " set name=? where id = ?";
        ImDb.execQuery({
            "sql": sql,
            "args": [name, sessionId],
            "handler": handler || function (err, res) {
                if(err) log.error(err);
            }
        });
    }
}
module.exports = SessionRepo;

+ 51 - 0
src/server/repository/mysql/sign.family.repo.js

@ -0,0 +1,51 @@
/**
 * Created by ysj on 2017/12/4.
 */
"use strict";
let log = require('../../util/log');
let ImDb = require('../mysql/db/im.db');
const DB_TABLES = require('../../include/commons').DB_TABLES;
class SignFamilyRepo{
    constructor(){
    }
    /**
     * 获取单个MessageNoticeSetting对象
     *
     * @param session_id
     * @param handler
     */
    static findDoctor(session_id, handler) {
        let sql = "SELECT s.doctor,s.doctor_health from "+DB_TABLES.Participants+" p,"+DB_TABLES.SignFamily+" s WHERE p.session_id = ? and p.participant_id = s.patient and s.`status`>0";
        ImDb.execQuery({
            "sql": sql,
            "args": [session_id],
            "handler": handler || function (err, res) {
                if(err) log.error(err);
            }
        });
    }
    /**
     * 判断医生在会话中是否是健管师
     * @param session_id
     * @param doctor
     * @param handler
     */
    static isHealthDoctor(session_id,doctor, handler) {
        let sql = "SELECT s.doctor_health from "+DB_TABLES.Participants+" p,"+DB_TABLES.SignFamily+" s WHERE p.session_id = ? and p.participant_id = s.patient and s.doctor_health=? and s.`status`>0";
        ImDb.execQuery({
            "sql": sql,
            "args": [session_id,doctor],
            "handler": handler || function (err, res) {
                if(err) log.error(err);
            }
        });
    }
}
module.exports = SignFamilyRepo;

+ 56 - 3
src/server/repository/mysql/topics.repo.js

@ -127,21 +127,74 @@ class TopicRepo {
     * @param size
     * @param handler
     */
    static findAllTopicByType(userId,reply,status,type,page,size,handler){
    static findAllTopicByType(userId,reply,status,type,patientName,startTime,endTime,page,size,handler){
        let sql = "";
        var args =[];
        var tempParms = "";
        if(patientName){
            tempParms += " and s.name like '%"+patientName+"%' ";
        }
        if(startTime){
            tempParms += " and t.create_time >= '"+startTime+"' ";
        }
        if(endTime){
            tempParms += " and t.create_time <= '"+endTime+"' ";
        }
        if(status==10){
            args.push(userId,status,type,page,size);
            sql =  "SELECT t.*, s.avatar,s.sex,s.birthdate, s.`name` AS patient_name,c.doctor FROM "+
                DB_TABLES.Topics+" t,"+DB_TABLES.Participants+" p,"+DB_TABLES.Doctors+" d,"+DB_TABLES.WlyyConsult+" c,"+DB_TABLES.Patients+" s "+
                "WHERE d.id = p.participant_id AND c.id = t.id AND c.patient = s.id AND p.session_id = t.session_id "+
                "AND d.id in (?) AND t. STATUS = ? AND c.type =? ORDER BY create_time DESC limit ?,?";
                "AND d.id in (?) AND t. STATUS = ? AND c.type =? "+tempParms+" ORDER BY create_time DESC limit ?,?";
        }else{
            args.push(userId,status,reply,type,page,size);
            sql =  "SELECT t.*, s.avatar,s.sex,s.birthdate, s.`name` AS patient_name,c.doctor FROM "+
                DB_TABLES.Topics+" t,"+DB_TABLES.Participants+" p,"+DB_TABLES.Doctors+" d,"+DB_TABLES.WlyyConsult+" c,"+DB_TABLES.Patients+" s "+
                "WHERE d.id = p.participant_id AND c.id = t.id AND c.patient = s.id AND p.session_id = t.session_id "+
                "AND d.id in (?) AND t. STATUS = ? and t.reply=? AND c.type = ? ORDER BY create_time DESC limit ?,?";
                "AND d.id in (?) AND t. STATUS = ? and t.reply=? AND c.type = ? "+tempParms+" ORDER BY create_time DESC limit ?,?";
        }
        ImDb.execQuery({
            sql: sql,
            args: args,
            handler: function (err, res) {
                handler(err, res);
            }
        });
    }
    /**
     * 按类型查找医生的未回复,进行中,已完成的咨询 总数
     * @param userId
     * @param reply
     * @param status
     * @param type
     * @param handler
     */
    static topicListCountByType(userId,reply,status,type,patientName,startTime,endTime,handler){
        let sql = "";
        var args =[];
        var tempParms = "";
        if(patientName){
            tempParms += " and s.name like '%"+patientName+"%' ";
        }
        if(startTime){
            tempParms += " and t.create_time >= '"+startTime+"' ";
        }
        if(endTime){
            tempParms += " and t.create_time <= '"+endTime+"' ";
        }
        if(status==10){
            args.push(userId,status,type);
            sql =  "SELECT count(t.id) count FROM "+
                DB_TABLES.Topics+" t,"+DB_TABLES.Participants+" p,"+DB_TABLES.Doctors+" d,"+DB_TABLES.WlyyConsult+" c,"+DB_TABLES.Patients+" s "+
                "WHERE d.id = p.participant_id AND c.id = t.id AND c.patient = s.id AND p.session_id = t.session_id "+
                "AND d.id in (?) AND t. STATUS = ? AND c.type =? "+tempParms;
        }else{
            args.push(userId,status,reply,type);
            sql =  "SELECT count(t.id) count FROM "+
                DB_TABLES.Topics+" t,"+DB_TABLES.Participants+" p,"+DB_TABLES.Doctors+" d,"+DB_TABLES.WlyyConsult+" c,"+DB_TABLES.Patients+" s "+
                "WHERE d.id = p.participant_id AND c.id = t.id AND c.patient = s.id AND p.session_id = t.session_id "+
                "AND d.id in (?) AND t. STATUS = ? and t.reply=? AND c.type = ? "+tempParms;
        }
        ImDb.execQuery({
            sql: sql,

+ 22 - 2
src/server/repository/mysql/wechat.token.repo.js

@ -8,6 +8,8 @@
let log = require('../../util/log');
let ImDb = require('../mysql/db/im.db');
let configFile = require('../../include/commons').CONFIG_FILE;
let config = require('../../resources/config/' + configFile);
class WeChatTokenRepo {
    constructor() {
@ -20,7 +22,7 @@ class WeChatTokenRepo {
     */
    static findOne(handler) {
        ImDb.execQuery({
            "sql": "select access_token, expires_in, add_timestamp from wechat_access_tokens order by add_timestamp desc limit 0, 1"
            "sql": "select access_token, expires_in, add_timestamp from wlyy.wx_access_token where acc_id = '"+config.wechatConfig.accId+"' order by add_timestamp desc limit 0, 1"
            , "handler": handler
        });
    };
@ -35,11 +37,29 @@ class WeChatTokenRepo {
     */
    static save(accessToken, expireIn, createTime, handler) {
        ImDb.execQuery({
            "sql": "insert into wechat_access_tokens (access_token, expires_in, add_timestamp) values (?,?,?)"
            "sql": "insert into wlyy.wx_access_token (acc_id,access_token, expires_in, add_timestamp) values ('"+config.wechatConfig.accId+"',?,?,?)"
            , "args": [accessToken, expireIn, createTime.getTime()]
            , "handler": handler
        });
    }
    /**
     * 微信推送日志
     * @param openid
     * @param patient
     * @param name
     * @param request
     * @param response
     * @param status
     * @param createTime
     */
    static saveLog(openid,patient,name,request,response,status,createTime,handler){
        ImDb.execQuery({
            "sql": "insert into wlyy.wlyy_wx_push_log (type,openid, patient,name,request,response,status, create_time) values (3,?,?,?,?,?,?,?)"
            , "args": [openid,patient,name,request,response,status, createTime]
            , "handler": handler
        });
    }
}
module.exports = WeChatTokenRepo;

+ 42 - 55
src/server/resources/config/config.dev.js

@ -1,76 +1,62 @@
"use strict";
// IM数据库配置
let imDbConfig = {
    host: '172.19.103.85',
    user: 'linzhou',
    password: 'linzhou',
    host: '172.19.103.77',
    user: 'root',
    password: '123456',
    database: 'im_new',
    connectionLimit: '50',
    charset: 'utf8mb4'
};
// let imDbConfig = {
//     host: '172.19.103.77',
//     user: 'root',
//     password: '123456',
//     database: 'im_new',
//     connectionLimit: '50',
//     charset: 'utf8mb4'
// };
// Redis
let redisConfig = {
    host: '192.168.1.220',
    host: '192.168.131.172',
    port: 6379,
    db: 1
    db: 6
};
// 三师后台
// let wlyyServerConfig = {
//     host: '172.19.103.88',
//     port: 9092,
//     model:"/wlyy"
// let redisConfig = {
//     host: '192.168.1.220',
//     port: 6379,
//     db: 1
// };
// 内网Redis
let innerRedisConfig = {
    host: '192.168.131.172',
    port: 6379,
    db: 6
};
// 三师后台
let wlyyServerConfig = {
    host: '192.168.131.24',
    port: 8080,
    model:"/"
    host: '172.19.103.72',
    port: 9096,
    model:"/csijk"
};
//医生助手配置
let wlyyDAServerConfig = {
    host: '192.168.131.172',
    port: 9092,
    model:"/wlyy"
};
// 个推AppStore版参数
let getTuiConfig = {
    HOST: 'https://api.getui.com/apiex.htm',
    APPID: 'qWmRh2X88l7HuE36z3qBe8',
    APPKEY: 'EzERfV8c849lBkZqHWzQG1',
    MASTERSECRET: 'veXiajQrId6iojy7Qv8kZ2'
    APPID: 'H6FYbDejks6VjMmW3uH7V6',
    APPKEY: '0PFWlKmLBN9YzhCfFWVgYA',
    MASTERSECRET: 'pvjCGtRZJx9SRVODkxc816'
};
/*let getTuiConfig = {
    HOST: 'https://api.getui.com/apiex.htm',
    APPID: 'DKgbGvbacm74nJJzen5ilA',
    APPSECRET: '4kGcL7e7kU6mbSqfEGZFW7',
    APPKEY: 'ArZfS2qvoA7N3hawOAGVC5',
    MASTERSECRET: '9lpy5vEss46tVzP1RCJiC4'
};*/
// 微信配置
// let wechatConfig = {
//     appId: 'wxd03f859efdf0873d',
//     appSecret: '2935b54b53a957d9516c920a544f2537',
//     token: '27eb3bb24f149a7760cf1bb154b08040',
//     baseUrl: 'weixin.xmtyw.cn/wlyy',
//     template: {
//         consultTemplate: 'qSOW0DBxO3qEBm4ucG0Ial0jxsOyD7_f2TFK5e-mQEc'  // 咨询回复模板
//     }
// };
// 微信配置
let wechatConfig = {
    appId: 'wx1f129f7b51701428',
    appSecret: '988f005d8309ed1795939e0f042431fb',
    appId: 'wxddece6347fe7fe87',
    appSecret: '41ade403de3d0e2e450f54a324ec013a',
    token: '27eb3bb24f149a7760cf1bb154b08040',
    accId: 'gh_710bf0b315a1',
    baseUrl: 'ehr.yihu.com/wlyy',
    template: {
        consultTemplate: '-dr4QNyFoRvVsf8uWxXMC1dRyjwnbUuJwJ21vBLhf18'  // 咨询回复模板
        consultTemplate: 'oGFfTWPEYvwwfmcIK1hquZ4-cVXdNomV3bCMLfj97Jc'  // 咨询回复模板
    }
};
@ -79,33 +65,34 @@ let sessionConfig = {
    maxMessageCount: 1000,                  // 会话缓存的消息数量
    maxMessageTimespan: 7 * 24 * 3600,      // 会话缓存的最大时间跨度
    expireTime: 3 * 60 * 60 * 1000,         // 会话过期时间,以毫秒计
    expireSessionCleanCount: 10              // 每次清理多少个过期会话
    expireSessionCleanCount: 10             // 每次清理多少个过期会话
};
// 议题配置
let topicConfig = {
    TTL: 24,                                 // 议题的存活时间,TTL = Time To Live
    TERMINATING_CRON: "* 30 * * * *"     // 议题自动关闭的任务执行时间间隔
    TTL: 24,                                // 议题的存活时间,TTL = Time To Live
    TERMINATING_CRON: "* 30 * * * *"        // 议题自动关闭的任务执行时间间隔
};
exports.environment = 'dev';
exports.pubChannel = 'dev';
exports.environment = 'test';
exports.pubChannel = 'test';
exports.subChannel = 'dev';
exports.pubSubSwitch = false;
exports.app = 'IM.Server';
exports.app = 'im.server';
exports.version = '2.0.0';
exports.debug = true;
exports.serverPort = 3000;
exports.sessionExpire = 1800;
exports.showSQL = true;
exports.showSQL = false;
exports.imDbConfig = imDbConfig;
exports.redisConfig = redisConfig;
exports.innerRedisConfig = innerRedisConfig;
exports.getTuiConfig = getTuiConfig;
exports.wlyyServerConfig = wlyyServerConfig;
exports.wlyyDAServerConfig = wlyyDAServerConfig;
exports.wechatConfig = wechatConfig;
exports.sessionConfig = sessionConfig;
exports.topicConfig = topicConfig;

+ 22 - 6
src/server/resources/config/config.prodPC.js

@ -1,7 +1,7 @@
"use strict";
let imDbConfig = {
    host: '10.59.22.7',
    host: '10.95.22.143',
    user: 'im',
    port:3306,
    password: 'im!)123',
@ -12,16 +12,29 @@ let imDbConfig = {
// Redis
let redisConfig = {
    host: '10.59.22.142',
    host: '10.95.22.142',
    port: 6380,
    db: 1,
    password:'jkzlehr'
};
// 内网Redis
let innerRedisConfig = {
    host: '10.95.22.142',
    port: 6380,
    db: 1,
    password:'jkzlehr'
};
// 三师后台
let wlyyServerConfig = {
    host: '10.95.22.10',
    port: 8011,
    host: '10.95.22.137',
    port: 9660,
    model:"/wlyy"
};
//医生助手配置
let wlyyDAServerConfig = {
    host: '27.155.100.191',
    port: 9660,
    model:"/wlyy"
};
@ -38,6 +51,7 @@ let wechatConfig = {
    appId: 'wxad04e9c4c5255acf',
    appSecret: 'ae77c48ccf1af5d07069f5153d1ac8d3',
    token: '27eb3bb24f149a7760cf1bb154b08040',
    accId: 'gh_ffd64560fb21',
    baseUrl: 'www.xmtyw.cn/wlyy',
    template: {
        consultTemplate: '0mF_vHj-ILx8EH8DwzmAi7LqzjqYiU9IrSRRmziTZyc'  // 咨询回复模板
@ -59,7 +73,7 @@ let topicConfig = {
    TERMINATING_CRON: "* 59 * * * *"        // 议题自动关闭的任务执行时间间隔
};
exports.environment = 'prodPC';
exports.environment = 'local';
exports.pubChannel = 'pc_to_phone';
exports.subChannel = 'phone_to_pc';
exports.pubSubSwitch = true;
@ -73,9 +87,11 @@ exports.sessionExpire = 1800;
exports.showSQL = false;
exports.imDbConfig = imDbConfig;
exports.redisConfig = redisConfig;
exports.innerRedisConfig = innerRedisConfig;
exports.getTuiConfig = getTuiConfig;
exports.wlyyServerConfig = wlyyServerConfig;
exports.wlyyDAServerConfig = wlyyDAServerConfig;
exports.wechatConfig = wechatConfig;
exports.sessionConfig = sessionConfig;
exports.topicConfig = topicConfig;

+ 30 - 7
src/server/resources/config/config.prod.js

@ -3,7 +3,7 @@
let imDbConfig = {
    host: '59.61.92.90',
    user: 'im',
    port:8079,
    port:9069,
    password: 'im!)123',
    database: 'im',
    connectionLimit: '100',
@ -12,19 +12,39 @@ let imDbConfig = {
// Redis
let redisConfig = {
    host: '27.155.101.77',
    port: 6380,
    host: '59.61.92.90',
    port: 9054,
    db: 1,
    password:'jkzlehr'
};
// let redisConfig = {
//     host: '192.168.120.14',
//     port: 6380,
//     db: 1,
//     password:'jkzl_ehr'
// };
// 内网Redis
let innerRedisConfig = {
    host: '59.61.92.90',
    port: 9054,
    db: 1,
    password:'jkzl_ehr'
    password:'jkzlehr'
};
// 三师后台
let wlyyServerConfig = {
    host: '27.155.100.191',
    port: 9660,
    host: 'www.xmtyw.cn',
    port: 80,
    model:"/wlyy"
};
//医生助手配置
let wlyyDAServerConfig = {
    host: '192.168.120.167',
    port: 5550,
    model:"/assistant"
};
// 个推AppStore版参数
let getTuiConfig = {
    HOST: 'https://api.getui.com/apiex.htm',
@ -38,6 +58,7 @@ let wechatConfig = {
    appId: 'wxad04e9c4c5255acf',
    appSecret: 'ae77c48ccf1af5d07069f5153d1ac8d3',
    token: '27eb3bb24f149a7760cf1bb154b08040',
    accId: 'gh_ffd64560fb21',
    baseUrl: 'www.xmtyw.cn/wlyy',
    template: {
        consultTemplate: '0mF_vHj-ILx8EH8DwzmAi7LqzjqYiU9IrSRRmziTZyc'  // 咨询回复模板
@ -62,7 +83,7 @@ let topicConfig = {
exports.environment = 'prod';
exports.pubChannel = 'phone_to_pc';
exports.subChannel = 'pc_to_phone';
exports.pubSubSwitch = false;
exports.pubSubSwitch = true;
exports.app = 'im.server';
exports.version = '2.0.0';
@ -73,9 +94,11 @@ exports.sessionExpire = 1800;
exports.showSQL = false;
exports.imDbConfig = imDbConfig;
exports.redisConfig = redisConfig;
exports.innerRedisConfig = innerRedisConfig;
exports.getTuiConfig = getTuiConfig;
exports.wlyyServerConfig = wlyyServerConfig;
exports.wlyyDAServerConfig = wlyyDAServerConfig;
exports.wechatConfig = wechatConfig;
exports.sessionConfig = sessionConfig;
exports.topicConfig = topicConfig;

+ 31 - 12
src/server/resources/config/config.test.js

@ -1,25 +1,41 @@
"use strict";
let imDbConfig = {
    //172.17.110.160
    host: '172.19.103.85',
    user: 'ssgg',
    password: 'ssgg',
    user: 'linzhou',
    password: 'linzhou',
    database: 'im_new',
    connectionLimit: '100',
    connectionLimit: '50',
    charset: 'utf8mb4'
};
// Redis
let redisConfig = {
    host: '192.168.1.220',
    host: '192.168.131.172',
    port: 6379,
    db: 1
};
// let redisConfig = {
//     host: '192.168.1.220',
//     port: 6379,
//     db: 1
// };
// 内网Redis
let innerRedisConfig = {
    host: '192.168.131.172',
    port: 6379,
    db: 1
};
// 三师后台
let wlyyServerConfig = {
    host: '172.19.103.88',
    host: '192.168.131.172',
    port: 9092,
    model:"/wlyy"
};
//医生助手配置
let wlyyDAServerConfig = {
    host: '192.168.131.172',
    port: 9092,
    model:"/wlyy"
};
@ -34,12 +50,13 @@ let getTuiConfig = {
// 微信配置
let wechatConfig = {
    appId: 'wx1f129f7b51701428',
    appSecret: '988f005d8309ed1795939e0f042431fb',
    appId: 'wxddece6347fe7fe87',
    appSecret: '41ade403de3d0e2e450f54a324ec013a',
    token: '27eb3bb24f149a7760cf1bb154b08040',
    accId: 'gh_710bf0b315a1',
    baseUrl: 'ehr.yihu.com/wlyy',
    template: {
        consultTemplate: '-dr4QNyFoRvVsf8uWxXMC1dRyjwnbUuJwJ21vBLhf18'  // 咨询回复模板
        consultTemplate: 'oGFfTWPEYvwwfmcIK1hquZ4-cVXdNomV3bCMLfj97Jc'  // 咨询回复模板
    }
};
@ -59,7 +76,7 @@ let topicConfig = {
exports.environment = 'test';
exports.pubChannel = 'test';
exports.subChannel = 'test';
exports.subChannel = 'dev';
exports.pubSubSwitch = false;
exports.app = 'im.server';
@ -68,12 +85,14 @@ exports.debug = true;
exports.serverPort = 3000;
exports.sessionExpire = 1800;
exports.showSQL = true;
exports.showSQL = false;
exports.imDbConfig = imDbConfig;
exports.redisConfig = redisConfig;
exports.innerRedisConfig = innerRedisConfig;
exports.getTuiConfig = getTuiConfig;
exports.wlyyServerConfig = wlyyServerConfig;
exports.wlyyDAServerConfig = wlyyDAServerConfig;
exports.wechatConfig = wechatConfig;
exports.sessionConfig = sessionConfig;
exports.topicConfig = topicConfig;

+ 20 - 0
src/server/util/wechat.sdk.js

@ -50,19 +50,39 @@ class WechatSDK {
                            let wechatEcho = JSON.parse(data);
                            if (wechatEcho && wechatEcho.errcode === 0) {
                                log.info("Send wechat template message successfully: " + messageJSON);
                                WechatTokenRepo.saveLog(message.touser,message.patient, message.name,messageJSON,data,1, new Date(), function (err, result) {
                                    if (err) {
                                        log.error("save push log failed: " + err.message);
                                    }
                                });
                                handler(null, wechatEcho);
                            } else {
                                WechatTokenRepo.saveLog(message.touser,message.patient, message.name,messageJSON,data,0, new Date(), function (err, result) {
                                    if (err) {
                                        log.error("save push log failed: " + err.message);
                                    }
                                });
                                log.error("Send wechat template message failed: " + messageJSON);
                                handler(wechatEcho, null);
                            }
                        });
                        res.on('error', function (err) {
                            WechatTokenRepo.saveLog(message.touser,message.patient, message.name,messageJSON,err.message,1, new Date(), function (err, result) {
                                if (err) {
                                    log.error("save push log failed: " + err.message);
                                }
                            });
                            log.error('Send wechat template message failed: ' + err.message);
                            handler(err, null);
                        });
                    })
                    .on('error', (e) => {
                        WechatTokenRepo.saveLog(message.touser,message.patient, message.name,messageJSON,e.message,1, new Date(), function (err, result) {
                            if (err) {
                                log.error("save push log failed: " + err.message);
                            }
                        });
                        log.error("Send wechat template message failed:" + e.message);
                        handler(e, null);
                    });

+ 55 - 0
src/server/util/wlyyAssistant.sdk.js

@ -0,0 +1,55 @@
/**
 * 简易WLYY SDK,提供WLYY接口调用。
 */
'use strict';
let http = require('http');
let querystring = require('querystring');
let configFile = require('../include/commons').CONFIG_FILE;
let config = require('../resources/config/' + configFile);
let log = require('./log.js');
class WlyyAssistantSDK {
    constructor(){}
    static request(userId, adminToken, token, imei, endpoint, param, handler){
        let userAgent = {
            admin_token: adminToken,
            token: token,
            uid: userId,
            imei: imei
        };
        var postData=querystring.stringify(param);
        let options = {
            hostname: config.wlyyDAServerConfig.host,
            port: config.wlyyDAServerConfig.port,
            path: config.wlyyDAServerConfig.model+endpoint,
            method: 'POST',
            headers: {
                'userAgent': JSON.stringify(userAgent),
                'Content-Type': 'application/x-www-form-urlencoded;text/html;charset=utf-8'
            }
        };
        let req = http.request(options, function (res) {
            res.setEncoding('utf-8');
            res.on('data', function (chunk) {
                log.info('家庭医生助手平台->请求成功:', chunk);
                handler(null, chunk);
            });
        });
        req.on('error', function (err) {
            log.error('家庭医生助手平台->请求失败: ', err.message);
            handler(err, null);
        });
        req.write(postData);
        req.end();
    }
}
module.exports = WlyyAssistantSDK;