Sfoglia il codice sorgente

合并代码,当消息发送者为系统时,不校验是否是会话成员

LiTaohong 6 anni fa
parent
commit
02d6b32ebf

+ 91 - 20
readme.md

@ -120,34 +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,则检查最后一个议题的状态
        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',                             // 会话消息
        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'  // 会话单个成员头像
    }
### 业务分析
@ -169,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 此会话不包含患者
@ -260,18 +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:新的会话名称
     name:新的会话名称
3、临时聊天组(由医生单聊在拉其他医生进来聊天后生成的新的会话)
/api/v2/sessions
 session_id : 为空(后台自动生成)
 session_name : 显示群组人名,用顿号隔开。超过10字省略显示
 session_type : 4
 participants: 此会话的成员列表,格式:["userId1:role", "userId2:role"],用户的ID及角色。
 
 ###备注
 topic的id=wlyy_consult的code
 

+ 2 - 2
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']}));

+ 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;

+ 64 - 1
src/server/endpoints/v2/session.endpoint.js

@ -21,7 +21,8 @@ const APIv2 = require('../../include/endpoints').APIv2;
/**
 * 创建会话。对MUC会话而言,需要创建的是议题,如果是第一次创建议题,那应该附带创建会话,而不是直接创建会话。
 *
 * session_type:1表示MUC会话,2表示P2P,3表示群会话,4表示临时讨论组
 * session_type:1表示MUC会话,2表示P2P,3表示群会话,4表示临时讨论组  
 * (咨询类型:1三师咨询,2视频咨询,3图文咨询,4公共咨询,5病友圈 6、患者名医咨询 7医生名医咨询 8续方咨询 9居民直接咨询专科  10医生发起的求助  11上门服务咨询)
 * users 讨论组包含的用户标示
 * sessionId 会话ID
 *
@ -48,6 +49,9 @@ router.post("/", function (req, res) {
    let sessions = new Sessions();
    let participantArray = [];
    ///是否视频会议的会话
    let videoconferencing = false;
    participants = JSON.parse(participants);
    for (let j in participants) {
        participantArray.push(j + ":" + participants[j]);
@ -55,6 +59,18 @@ router.post("/", function (req, res) {
    ControllerUtil.regModelEventHandler(sessions, res);
    sessions.createSession(sessionId, sessionName, sessionType, participantArray);
    //视频会议的会话 szx add 20181121
    if (!payload.hasOwnProperty('videoconferencing') && payload.videoconferencing == 1){
        videoconferencing = true;
        //发送广播,给相关的与会人员
        for (let j in participants) {
            let participant = participants[j];
            let socketClient = ClientCache.findById(participant.userId);
            socketClient.emt("startVideoconference",{"session_id":sessionId});
        }
        
    }
});
/**
@ -89,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;

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

@ -84,7 +84,7 @@ router.get(APIv2.Sessions.TopicListByType,function (req,res) {
});
router.get(APIv2.Sessions.topicListCountByType,function (req,res) {
router.get(APIv2.Sessions.TopicListCountByType,function (req,res) {
    let user = req.query.user;
    let status = req.query.status;
    let type = req.query.type;

+ 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;

+ 2 - 2
src/server/handlers/socket.handler.js

@ -35,7 +35,7 @@ class SocketHandler {
    start() {
        let socketServer = this._socketServer;
        socketServer.sockets.on('connection', function (socket) {
            log.info('one user connection...');
            // 客户端注册
            socket.on('login', function (data) {
                if (!data.userId) {
@ -52,7 +52,7 @@ class SocketHandler {
                        return;
                    }
                    log.error('User ' + data.userId + ' login');
                    log.info('User ' + data.userId + ' login');
                    if(!data.clientType||data.clientType=="patient"){
                        let patientClient = new PatientClient(socket, socketServer);
                        patientClient.userId = data.userId;

+ 9 - 2
src/server/include/commons.js

@ -86,6 +86,8 @@ const CONTENT_TYPES = {
    GoTo: 5,        // 跳转信息,求组其他医生或者邀请其他医生发送的推送消息
    TopicBegin: 6,  // 议题开始
    TopicEnd: 7,    // 议题结束 10 11 系统发送的会话消息
    PersonalCard: 18,    // 个人名片
    MessageForward: 19,    // 消息转发
    TopicInto: 14,    // 进入议题 系统发送的会话消息
    Video:12,//视频
    System:13,//系统消息
@ -94,6 +96,7 @@ const CONTENT_TYPES = {
    PrescriptionFollowupContent:17,//续方咨询随访问卷消息
    MapMsg:18,//地图消息
    DoorOrderBriefMsg:19,//服务工单格式化消息
    Rehabilitation: 20, //康复计划发送
    typeToDescription: function (type, defaultDescription) {
        if (CONTENT_TYPES.Image == type) {
            return '[图片]';
@ -206,7 +209,6 @@ const DB_TABLES = {
    P2pMessages: "p2p_messages",
    MucMessages: "muc_messages",
    GroupMessages: "group_messages",
    DiscussionMessages: "discussion_messages",
    SystemMessages: "system_messages",
    Participants: "participants",
    Sessions: "sessions",
@ -215,6 +217,7 @@ 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",
@ -228,9 +231,13 @@ 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;
        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;
        else if (sessionType == SESSION_TYPES.PRESCRIPTION_HOSPITAL)

+ 11 - 1
src/server/include/endpoints.js

@ -13,6 +13,14 @@ const APIv2 = {
        Health: '/health',
        DbStatus: '/db'
    },
    Message: {
        Base: '/api/v2/message',
        Send: '/send',       //发送消息,和会话无关,内外网通信
        DataMessage: '/dataMessage',
        CleanMessageToRedis: '/cleanMessageToRedis',
        CleanMessageLastFetchTimeToRedis: '/cleanMessageLastFetchTimeToRedis'
    },
    Users: {
        Base: '/api/v2/users',
@ -27,6 +35,8 @@ const APIv2 = {
        Base: '/api/v2/sessions',
        Session: '/:session_id/session',                                // 获取会话
        SessionListByType: '/sessionListByType',                        // 按会话类型获取会话
        SessionCountByType: '/sessionCountByType',                      // 按会话类型获取会话数量
        SessionSticky: '/:session_id/sticky',                           // 会话置顶,置顶使用PUT,取消置顶使用DELETE
        SessionStatus: '/:session_id/status',                           // 更新状态
        SessionName: '/:session_id/name',                               // 更新会话名称
@ -40,7 +50,7 @@ const APIv2 = {
        TopicList:'/topics',                                            //获取议题列表
        HealthTopicList:'/healthTopics',                                //健康咨询
        TopicListByType:'/topicListByType',                             //按类型查找咨询(未回复,进行中,已回复)
        topicListCountByType:'/topicListCountByType',                   //按类型查找咨询(未回复,进行中,已回复)的数量
        TopicListCountByType:'/topicListCountByType',                   //按类型查找咨询(未回复,进行中,已回复)的数量
        HealthTeamTopicList:'/healthTeamTopics',                        //健康咨询(区分团队)
        TopicReplyCount:"/topics/count/reply",                          //议题回复数统计
        TopicMessages:'/topic/:topic_id/messages',                      //议题消息

+ 110 - 0
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 消息对象
@ -241,6 +283,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

+ 102 - 18
src/server/models/sessions/sessions.js

@ -121,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);
        }
@ -188,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);
            }
@ -197,9 +204,9 @@ class Sessions extends RedisModel {
        //流程2-判断session的业务类型;
        function callBusinessType() {
            if(type==SESSION_TYPES.MUC||type==SESSION_TYPES.PRESCRIPTION || type==SESSION_TYPES.ONDOOR_NURSING){
            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){
            }else if(type==SESSION_TYPES.SPECIALISTGROUP||type==SESSION_TYPES.DISCUSSION){
                callCreateSession(SESSION_BUSINESS_TYPE.DOCTOR);
            }else{
                ParticipantRepo.getBusinessType(participantIdArray, function (err, businessType) {
@ -1141,7 +1148,10 @@ class Sessions extends RedisModel {
        }
        participants.existsParticipant(sessionId, userId, function (err, res) {
            if (! res[0].exist || userId != "system") {
            log.info("1151-userId=" + userId);
            log.info(" res[0].exist=" + res[0].exist);
            log.info(!res[0].exist && userId != "system");
            if (! res[0].exist && userId != "system") {
                handler(Error("User not found in session " + sessionId), null);
            } else {
                //将消息ID转换成分值
@ -1420,6 +1430,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;
        // 检查会话中是否存在此成员
@ -1428,14 +1439,56 @@ class Sessions extends RedisModel {
                ModelUtil.emitError(self.eventEmitter, "Check session participant failed: ", err);
                return;
            }
            logger.info("session.js--1285--res[0].exist" + res[0].exist);
            logger.info("session.js--1443--res[0].exist" + res[0].exist);
            log.info("message.sender_id=" + message.sender_id);
            if ( res[0].exist || message.sender_id == "system") {
                redis.hmgetAsync(sessionKey, ["type", "name"]).then(function (res) {
                    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 ||
@ -1455,6 +1508,7 @@ class Sessions extends RedisModel {
                            })
                        }
                    }
                    // 消息保存到Redis,并更新会话最后状态、用户最后消息获取时间
                    messages.saveMessageToRedis(sessionId, sessionType, messageId, message);
                    Messages.updateLastContent(sessionKey, sessionType, sessionName, message);
@ -1501,6 +1555,7 @@ class Sessions extends RedisModel {
        }
    }
    sendTopicMessages(topicId, message) {
        let self = this;
        TopicRepo.findAllByTopicId(topicId, function (err, res) {
@ -1541,6 +1596,8 @@ class Sessions extends RedisModel {
        }
        // 发送成员必须处于会话中
        participants.existsParticipant(sessionId, message.sender_id, function (err, res) {
            log.info("1599-message.sender_id=" + message.sender_id);
            log.info("res[0].exist=" + res[0].exist);
            if ( res[0].exist || message.sender_id == "system") {
                redis.hmgetAsync(session_key, ["type", "name"]).then(function (res) {
                    sessionType = res[0];
@ -1621,6 +1678,7 @@ class Sessions extends RedisModel {
        // 发送成员必须处于会话中
        participants.existsParticipant(sessionId, message.sender_id, function (err, res) {
            log.info("session.js--1477--res[0].exist" + res[0].exist);
            log.info("message.sender_id=" + message.sender_id);
            if (res[0].exist || message.sender_id == "system") {
                redis.hmgetAsync(session_key, ["type", "name"]).then(function (res) {
                    sessionType = res[0];
@ -1782,20 +1840,46 @@ class Sessions extends RedisModel {
                //告知医生新消息
                WechatClient.sendSocketMessageToDoctor(targetUserId,message);
                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);
                let count = 0;
                //系统消息
                MessageRepo.getWlyyMessageCount(targetUserId,function (err,res) {
                    if(res){
                        count = res[0].count;
                    }
                    if(config.environment!='local'){//pc版不推送个推,通过redis的publish
                        AppClient.sendNotification(targetUserId, message,sessionType,count);
                });
                //im消息
                let sessions = new Sessions();
                sessions.getAllSessionsUnreadMessageCount(targetUserId,function (err,res) {
                    if(res){
                        count += res;
                    }
                    //外网pcim通过socket推送
                    WechatClient.sendPcImSocket(targetUserId,message,sessionType);
                });
                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;

+ 1 - 1
src/server/models/sessions/topics.js

@ -265,7 +265,7 @@ class Topics extends RedisModel {
     */
    createTopic(topicName, topicId, sessionId, users, messages, sessionType) {
        let self = this;
        if (!sessionId && (sessionType == SESSION_TYPES.MUC|| sessionType == SESSION_TYPES.PRESCRIPTION)) {
        if (!sessionId && (sessionType == SESSION_TYPES.MUC|| sessionType == SESSION_TYPES.PRESCRIPTION || SESSION_TYPES.PRESCRIPTION_HOSPITAL || SESSION_TYPES.ONDOOR_NURSING)) {
            ModelUtil.emitOK(self.eventEmitter, {status: -1, message: "会话ID为空,请先生成会话ID"});
            return;
        }

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

@ -140,6 +140,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;

+ 11 - 2
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({
@ -306,6 +306,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({

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

@ -24,7 +24,7 @@ class SearchRepo {
            " 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,
@ -46,7 +46,7 @@ class SearchRepo {
            " 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({
@ -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 in (3,5) 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 + '%';

+ 45 - 0
src/server/repository/mysql/session.repo.js

@ -113,6 +113,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) {

+ 4 - 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 wlyy.wx_access_token where acc_id = 'gh_ffd64560fb21' 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,7 +37,7 @@ class WeChatTokenRepo {
     */
    static save(accessToken, expireIn, createTime, handler) {
        ImDb.execQuery({
            "sql": "insert into wlyy.wx_access_token (acc_id,access_token, expires_in, add_timestamp) values ('gh_ffd64560fb21',?,?,?)"
            "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
        });

+ 7 - 7
src/server/resources/config/config.dev.js

@ -11,7 +11,7 @@ let imDbConfig = {
// Redis
let redisConfig = {
    host: '172.19.103.88',
    host: '192.168.131.172',
    port: 6379,
    db: 6
};
@ -22,7 +22,7 @@ let redisConfig = {
// };
// 内网Redis
let innerRedisConfig = {
    host: '172.19.103.88',
    host: '192.168.131.172',
    port: 6379,
    db: 6
};
@ -35,7 +35,7 @@ let wlyyServerConfig = {
//医生助手配置
let wlyyDAServerConfig = {
    host: '172.19.103.88',
    host: '192.168.131.172',
    port: 9092,
    model:"/wlyy"
};
@ -50,13 +50,13 @@ let getTuiConfig = {
// 微信配置
let wechatConfig = {
    appId: 'wx1f129f7b51701428',
    appSecret: '988f005d8309ed1795939e0f042431fb',
    appId: 'wxddece6347fe7fe87',
    appSecret: '41ade403de3d0e2e450f54a324ec013a',
    token: '27eb3bb24f149a7760cf1bb154b08040',
    accId: 'gh_ffd64560fb21',
    accId: 'gh_710bf0b315a1',
    baseUrl: 'ehr.yihu.com/wlyy',
    template: {
        consultTemplate: '-dr4QNyFoRvVsf8uWxXMC1dRyjwnbUuJwJ21vBLhf18'  // 咨询回复模板
        consultTemplate: 'oGFfTWPEYvwwfmcIK1hquZ4-cVXdNomV3bCMLfj97Jc'  // 咨询回复模板
    }
};

+ 1 - 0
src/server/resources/config/config.local.js

@ -51,6 +51,7 @@ let wechatConfig = {
    appId: 'wxad04e9c4c5255acf',
    appSecret: 'ae77c48ccf1af5d07069f5153d1ac8d3',
    token: '27eb3bb24f149a7760cf1bb154b08040',
    accId: 'gh_ffd64560fb21',
    baseUrl: 'www.xmtyw.cn/wlyy',
    template: {
        consultTemplate: '0mF_vHj-ILx8EH8DwzmAi7LqzjqYiU9IrSRRmziTZyc'  // 咨询回复模板