فهرست منبع

重构WS,使之可以收发消息

Sand Wen 8 سال پیش
والد
کامیت
568cacbd16

+ 5 - 3
src/doctor/app.js

@ -29,7 +29,7 @@ var chats = require('./endpoints/chats.endpoint');
var management = require('./endpoints/management.endpoint');
// handlers
var socketHandler = require('./handlers/socket.handler');
var SocketHandler = require('./handlers/socket.handler');
// initialize express application
var app = express();
@ -90,7 +90,7 @@ var io = require('socket.io').listen(server);
server.listen(config.serverPort);
// event listener for HTTP server "error" event
server.on('error', function(error) {
server.on('error', function (error) {
    if (error.syscall !== 'listen') throw error;
    var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
@ -116,7 +116,9 @@ server.on('listening', function onListening() {
    var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
});
io.sockets.on('connection', socketHandler.onConnection);
//io.sockets.on('connection', socketHandler.onConnection);
var socketHandler = new SocketHandler(io);
socketHandler.start();
log.info('Starting IM server, version ' + config.version + ', running on port ' + server.address().port + ', start at ' + new Date().toLocaleString());
log.info('Configuration profile: ' + configFile.split('.')[1]);

+ 73 - 69
src/doctor/endpoints/chats.endpoint.js

@ -3,34 +3,34 @@
 *
 * 此控制器处理点对点,组及消息消息。为三类消息提供发送及查询功能。
 */
var express = require('express');
var router = express.Router();
let express = require('express');
let router = express.Router();
var http = require('http');
var getui = require('getui');
var log = require('../util/log.js');
var objectUtil = require("../util/objectUtil.js");
let http = require('http');
let getui = require('getui');
let log = require('../util/log.js');
let objectUtil = require("../util/objectUtil.js");
var Patient = new require("../models/patient");
var patient = new Patient();
let Patient = new require("../models/patient");
let patient = new Patient();
var smRepo = require("../repository/system.msg.repo.js");
var pmRepo = require('../repository/private.msg.repo.js');
var gmRepo = require("../repository/group.msg.repo.js");
var nmRepo = require("../repository/notify.msg.repo.js");
var statsRepo = require("../repository/stats.msg.repo.js");
var doctorRepo = require("../repository/doctor.repo.js");
var groupRepo = require("../repository/group.repo.js");
var searchRepo = require('../repository/search.repo.js');
let smRepo = require("../repository/system.msg.repo.js");
let pmRepo = require('../repository/private.msg.repo.js');
let gmRepo = require("../repository/group.msg.repo.js");
let nmRepo = require("../repository/notify.msg.repo.js");
let statsRepo = require("../repository/stats.msg.repo.js");
let doctorRepo = require("../repository/doctor.repo.js");
let groupRepo = require("../repository/group.repo.js");
let searchRepo = require('../repository/search.repo.js');
var APIv1 = require('../include/endpoints').APIv1;
var CONTENT_TYPES = require('../include/commons').CONTENT_TYPE;
var GROUP_TYPE = require('../include/commons').GROUP_TYPE;
var PLATFORMS = require('../include/commons').PLATFORM;
let APIv1 = require('../include/endpoints').APIv1;
let CONTENT_TYPES = require('../include/commons').CONTENT_TYPE;
let GROUP_TYPE = require('../include/commons').GROUP_TYPE;
let PLATFORMS = require('../include/commons').PLATFORM;
var MAX_INT = 9007199254740992;
let MAX_INT = 9007199254740992;
var DEFAULT_PAGE_SIZE = 100;
let DEFAULT_PAGE_SIZE = 100;
//--------------------------------------------------------------//
//----------------------------消息发送----------------------------//
@ -55,13 +55,13 @@ var DEFAULT_PAGE_SIZE = 100;
 */
router.post(APIv1.Chats.SM, function (req, res) {
    // 检查消息体及消息格式是否正确
    var message = req.body;
    let message = req.body;
    if (!objectUtil.isJsonObject(message)) {
        throw {httpStatus: 406, message: 'Problems parsing JSON.'}
    }
    // 字段判断
    var testing = objectUtil.fieldsCheck(message, "to", "title", "summary", "contentType", "content");
    let testing = objectUtil.fieldsCheck(message, "to", "title", "summary", "contentType", "content");
    if (!testing.pass) {
        throw {httpStatus: 406, message: testing.message}
    }
@ -80,9 +80,9 @@ router.post(APIv1.Chats.SM, function (req, res) {
            return;
        }
        var pushable = rows.length > 0 && rows[0].is_online;
        var notifyMessage = JSON.stringify({type: 'system_msg', data: message.content});
        var userStatus = rows[0];
        let pushable = rows.length > 0 && rows[0].is_online;
        let notifyMessage = JSON.stringify({type: 'system_msg', data: message.content});
        let userStatus = rows[0];
        // 保存该条推送信息
        smRepo.save(message.to,
@ -153,19 +153,23 @@ router.post(APIv1.Chats.SM, function (req, res) {
 */
router.post(APIv1.Chats.PM, function (req, res) {
    // 检查消息体及消息格式是否正确
    var message = req.body;
    let message = req.body;
    if (!objectUtil.isJsonObject(message)) {
        throw {httpStatus: 406, message: 'Problems parsing JSON.'}
    }
    // 字段判断
    var testing = objectUtil.fieldsCheck(message, "from", "to", "contentType", "content");
    let testing = objectUtil.fieldsCheck(message, "from", "to", "contentType", "content");
    if (!testing.pass) {
        throw {httpStatus: 406, message: testing.message};
    }
    patient.sendMessage(message.to, message);
    res.status(200).send({});
    return;
    // 保存消息
    var tempContent = message.contentType === CONTENT_TYPES.Article ? JSON.stringify(message.content) : message.content;
    let tempContent = message.contentType === CONTENT_TYPES.Article ? JSON.stringify(message.content) : message.content;
    pmRepo.save(message.to, message.from, message.contentType, tempContent, function (err, result) {
        if (err) {
            res.status(500).send({message: 'Save private message failed.'});
@ -179,7 +183,7 @@ router.post(APIv1.Chats.PM, function (req, res) {
                return;
            }
            var feedback = fillMessages(msg);
            let feedback = fillMessages(msg);
            res.status(200).send(feedback);
            patient.isPatientCode(
@ -203,8 +207,8 @@ router.post(APIv1.Chats.PM, function (req, res) {
                            }
                            // 构建通知消息
                            var title = '新消息';
                            var content = '';
                            let title = '新消息';
                            let content = '';
                            if (message.contentType === CONTENT_TYPES.PlainText) {
                                content = message.content;
                            } else if (message.contentType === CONTENT_TYPES.Image) {
@ -215,9 +219,9 @@ router.post(APIv1.Chats.PM, function (req, res) {
                                content = '接收到一条新消息';
                            }
                            var target = result[0];
                            var isOnline = result.length > 0 && target.is_online === 1;
                            var notifyMessage = JSON.stringify({type: 'p2p_msg', from_uid: message.from});
                            let target = result[0];
                            let isOnline = result.length > 0 && target.is_online === 1;
                            let notifyMessage = JSON.stringify({type: 'p2p_msg', from_uid: message.from});
                            // 保存通知消息到数据库中并根据用户在线状态推送此消息
                            nmRepo.save(message.to, message.contentType, title, content, notifyMessage, isOnline, function (err, result) {
@ -295,14 +299,14 @@ router.post(APIv1.Chats.PM, function (req, res) {
 */
router.post(APIv1.Chats.GM, function (req, res) {
    // 检查消息体及消息格式是否正确
    var message = req.body;
    let message = req.body;
    if (!objectUtil.isJsonObject(message)) {
        throw {httpStatus: 406, message: 'Problems parsing JSON.'};
    }
    // 字段判断
    var testing = objectUtil.fieldsCheck(message, 'from', 'at', 'group', 'groupType', 'contentType', 'content');
    let testing = objectUtil.fieldsCheck(message, 'from', 'at', 'group', 'groupType', 'contentType', 'content');
    if (!testing.pass) {
        throw {httpStatus: 406, message: testing.message}
    }
@ -337,7 +341,7 @@ router.post(APIv1.Chats.GM, function (req, res) {
                    res.status(500).send({message: "Save group message success, but return this message failed."});
                }
                // 关闭网络连接后执行后续操作
                var feedback = fillMessages(groupMsg);
                let feedback = fillMessages(groupMsg);
                res.status(200).send(feedback);
                // 推送通知消息给群组成员
@ -355,8 +359,8 @@ router.post(APIv1.Chats.GM, function (req, res) {
                    }
                    // 逐个推送通知,患者与医生推送方式不一样
                    for (var i = 0; i < members.length; i++) {
                        var member = members[i];
                    for (let i = 0; i < members.length; i++) {
                        let member = members[i];
                        if (member.user_id === message.from) continue;
                        (function (user_id) {
@ -371,8 +375,8 @@ router.post(APIv1.Chats.GM, function (req, res) {
                                            return;
                                        }
                                        var title = '';
                                        var content = '';
                                        let title = '';
                                        let content = '';
                                        if (message.contentType === CONTENT_TYPES.PlainText) {
                                            title = '群组消息';
                                            content = message.content;
@ -387,12 +391,12 @@ router.post(APIv1.Chats.GM, function (req, res) {
                                            content = '接收到一条新消息';
                                        }
                                        var pushable = result.length > 0 && result[0].is_online === 1;
                                        var notifyMessage = JSON.stringify({type: 'group_msg', gid: message.group});
                                        let pushable = result.length > 0 && result[0].is_online === 1;
                                        let notifyMessage = JSON.stringify({type: 'group_msg', gid: message.group});
                                        // 发送并保存通知到数据库中
                                        if (pushable) {
                                            var userStatus = result[0];
                                            let userStatus = result[0];
                                            if (userStatus.platform === PLATFORMS.iOS) {
                                                getui.pushAPN(user_id,
@ -464,7 +468,7 @@ router.post(APIv1.Chats.GM, function (req, res) {
                                    });
                                    // 统计'@'信息
                                    var at = message.at == user_id ? 1 : 0;
                                    let at = message.at == user_id ? 1 : 0;
                                    statsRepo.updateGroupChatSummary(user_id,
                                        message.group,
@ -501,7 +505,7 @@ router.post(APIv1.Chats.GM, function (req, res) {
 *  /chats/list?user_id=sand
 */
router.get(APIv1.Chats.List, function (req, res) {
    var userId = req.query.user_id;
    let userId = req.query.user_id;
    if (userId === null) {
        throw {httpStatus: 406, message: 'Missing fields.'};
    }
@ -515,9 +519,9 @@ router.get(APIv1.Chats.List, function (req, res) {
            return;
        }
        var chats = {patients: [], doctors: [], groups: []};
        for (var i = 0; i < patients.length; i++) {
            var patient = patients[i];
        let chats = {patients: [], doctors: [], groups: []};
        for (let i = 0; i < patients.length; i++) {
            let patient = patients[i];
            chats.patients.push({
                code: patient.code,
                name: patient.name,
@ -540,8 +544,8 @@ router.get(APIv1.Chats.List, function (req, res) {
                return;
            }
            for (var i = 0; i < groups.length; i++) {
                var group = groups[i];
            for (let i = 0; i < groups.length; i++) {
                let group = groups[i];
                // 过滤掉医生间的求助团队
                if (group.group_type === 2) continue;
@ -568,8 +572,8 @@ router.get(APIv1.Chats.List, function (req, res) {
                    return;
                }
                for (var i = 0; i < doctors.length; i++) {
                    var doctor = doctors[i];
                for (let i = 0; i < doctors.length; i++) {
                    let doctor = doctors[i];
                    chats.doctors.push({
                        code: doctor.code,
                        name: doctor.name,
@ -591,8 +595,8 @@ router.get(APIv1.Chats.List, function (req, res) {
                        return;
                    }
                    for (var i = 0; i < groups.length; i++) {
                        var group = groups[i];
                    for (let i = 0; i < groups.length; i++) {
                        let group = groups[i];
                        chats.groups.push({
                            code: group.code,
                            name: group.name,
@ -618,7 +622,7 @@ router.get(APIv1.Chats.List, function (req, res) {
 *  /chats/list/patient
 */
router.get(APIv1.Chats.ListWithPatient, function (req, res) {
    var userId = req.query.user_id;
    let userId = req.query.user_id;
    if (userId === null) {
        throw {httpStatus: 406, message: 'Missing fields.'};
    }
@ -631,9 +635,9 @@ router.get(APIv1.Chats.ListWithPatient, function (req, res) {
            return;
        }
        var chats = {patients: [], groups: []};
        for (var i = 0; i < patients.length; i++) {
            var patient = patients[i];
        let chats = {patients: [], groups: []};
        for (let i = 0; i < patients.length; i++) {
            let patient = patients[i];
            chats.patients.push({
                code: patient.code,
                name: patient.name,
@ -655,8 +659,8 @@ router.get(APIv1.Chats.ListWithPatient, function (req, res) {
                return;
            }
            for (var i = 0; i < groups.length; i++) {
                var group = groups[i];
            for (let i = 0; i < groups.length; i++) {
                let group = groups[i];
                // 过滤掉医生间的求助团队
                if (group.group_type === 2) continue;
@ -684,7 +688,7 @@ router.get(APIv1.Chats.ListWithPatient, function (req, res) {
 *  /chats/list/doctor
 */
router.get(APIv1.Chats.ListWithDoctor, function (req, res) {
    var userId = req.query.user_id;
    let userId = req.query.user_id;
    if (userId === null) {
        throw {httpStatus: 406, message: 'Missing fields.'};
    }
@ -698,9 +702,9 @@ router.get(APIv1.Chats.ListWithDoctor, function (req, res) {
            return;
        }
        var chats = {doctors: [], groups: []};
        for (var i = 0; i < doctors.length; i++) {
            var doctor = doctors[i];
        let chats = {doctors: [], groups: []};
        for (let i = 0; i < doctors.length; i++) {
            let doctor = doctors[i];
            chats.doctors.push({
                code: doctor.code,
                name: doctor.name,
@ -722,8 +726,8 @@ router.get(APIv1.Chats.ListWithDoctor, function (req, res) {
                return;
            }
            for (var i = 0; i < groups.length; i++) {
                var group = groups[i];
            for (let i = 0; i < groups.length; i++) {
                let group = groups[i];
                chats.groups.push({
                    code: group.code,
                    name: group.name,

+ 72 - 22
src/doctor/handlers/socket.handler.js

@ -4,31 +4,81 @@
 */
"use strict";
var log = require("../util/log.js");
let log = require("../util/log.js");
var clientCache = require('../models/socket.io/client.cache').clientCache();
var PatientClient = require('./../models/socket.io/patient.client');
let clientCache = require('../models/socket.io/client.cache').clientCache();
let PatientClient = require('./../models/socket.io/patient.client');
/**
 * 建立WS连接,发送welcome消息要求登录。之后响应login消息保存用户通信凭据。
 *
 * @param socket
 */
module.exports.onConnection = function (socket) {
    socket.on('login', function (data) {
        log.info('User login: ' + data.userId);
let Doctor = require('../models/doctor');
let doctor = new Doctor();
let Group = require('../models/group');
let group = new Group();
class SocketHandler{
    constructor(socketServer){
        this._socketServer = socketServer;
    }
    /**
     * 启用事件监听。
     *
     * @param socket
     */
    start(){
        let socketServer = this._socketServer;
        socketServer.sockets.on('connection', function (socket) {
            // 客户端注册
            socket.on('login', function (data) {
                if(!data.userId){
                    socketServer.sockets.emit('error', {message: 'Missing fields(s): userId.'});
                } else{
                    log.info('User login: ' + data.userId);
                    let patientClient = new PatientClient(socket, socketServer);
                    patientClient.userId = data.userId;
                    patientClient.password = data.password;
                    clientCache.addClient(patientClient);
                    socketServer.sockets.emit('ack', {});
                }
            });
            // 接收客户端消息
            socket.on('message', function (data) {
                log.info('Got message from ' + clientCache.findBySocket(socket).userId);
                let targetType = data.targetType;
                let message = data.message;
                if(targetType == 1){
                    doctor.sendMessage(message);
                } else {
                    group.sendMessage(message);
                }
            });
            // 客户端退出
            socket.on('logout', function (data) {
                let userId = clientCache.findBySocket(socket).userId;
                log.info('User logout: ' + userId);
                clientCache.removeByUserId(userId);
            });
        var patientClient = new PatientClient(socket);
        patientClient.userId = data.userId;
        patientClient.password = data.password;
            // 客户端断开
            socket.on('disconnect', function () {
                let patientClient = clientCache.findBySocket(socket);
                if(patientClient){log.info("User disconnect: ", patientClient.userId);
                    clientCache.removeByUserSocket(socket);
                }
            });
        clientCache.addClient(patientClient);
    });
            socket.emit('welcome', {message: 'Welcome to connect IM server, please login.'});
        });
    }
}
    socket.on('disconnect', function () {
        log.info("User disconnect: ", clientCache.findBySocket(socket).userId);
        clientCache.removeByUserSocket(socket);
    });
module.exports = SocketHandler;
    socket.emit('welcome', {message: 'Welcome to connect IM server, please login.'});
};

+ 345 - 341
src/doctor/models/doctor.js

@ -4,7 +4,6 @@
"use strict";
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var log = require("../util/log.js");
var modelUtil = require('../util/modelUtil');
@ -21,385 +20,372 @@ var MODEL_EVENTS = require('../include/commons').MODEL_EVENTS;
var CONTENT_TYPES = require('../include/commons').CONTENT_TYPE;
var PLATFORMS = require('../include/commons').PLATFORM;
function Doctor(){
    EventEmitter.call(this);
}
/**
 * 向医生发送消息。
 *
 * @param message
 */
Doctor.prototype.sendMessage = function(message){
    var self = this;
    userRepo.isExist(message.to, function (err, rows) {
        if (err) {
            modelUtil.emitDbError(self, 'Lookup receiving users failed', err);
            return;
        }
        if (rows.length == 0) {
            self.emit(MODEL_EVENTS.DataNotFound, {});
            return;
        }
class Doctor{
    constructor(){
        this._eventEmitter = new EventEmitter();
    }
        // 保存消息
        var tempContent = message.contentType === CONTENT_TYPES.Article ? JSON.stringify(message.content) : message.content;
        pmRepo.save(message.to, message.from, message.contentType, tempContent, function (err, result) {
    /**
     * 向医生发送消息。
     *
     * @param message
     */
    sendMessage(message){
        var self = this;
        userRepo.isExist(message.to, function (err, rows) {
            if (err) {
                modelUtil.emitDbError(self, 'Save private message failed.', err);
                modelUtil.emitDbError(self, 'Lookup receiving users failed', err);
                return;
            }
            // 返回发送成功后的消息记录,控制器可选择结束网络连接
            pmRepo.findOneMessage(result.insertId, function (err, msg) {
                if (err) {
                    modelUtil.emitDbError(self, 'Save private message success, but return last message failed.', err);
                } else {
                    self.emit(MODEL_EVENTS.OK, fillMessages(msg));
                }
            });
            // 更新自身的聊天统计信息
            statsRepo.updatePrivateChatSummary(message.from, message.to, message.from, message.contentType, message.content, function (err, result) {
                if (err) modelUtil.logDbError(null, err);
            });
            // 更新对端的聊天统计信息
            statsRepo.updatePrivateChatSummary(message.to, message.from, message.from, message.contentType, message.content, function (err, result) {
                if (err) modelUtil.logDbError(null, err);
            });
            if (rows.length == 0) {
                self.emit(MODEL_EVENTS.DataNotFound, {});
                return;
            }
            // 获取对方状态,即对端的系统平台,token等信息,并推送通知消息给对端
            userRepo.getUserStatus(message.to, function (err, result) {
            // 保存消息
            var tempContent = message.contentType === CONTENT_TYPES.Article ? JSON.stringify(message.content) : message.content;
            pmRepo.save(message.to, message.from, message.contentType, tempContent, function (err, result) {
                if (err) {
                    modelUtil.logDbError('Get target user status for private message failed: ' + message.to, err);
                    modelUtil.emitDbError(self, 'Save private message failed.', err);
                    return;
                }
                // 构建通知消息
                var title = '新消息';
                var content = '';
                if (message.contentType === CONTENT_TYPES.PlainText) {
                    content = message.content;
                } else if (message.contentType === CONTENT_TYPES.Image) {
                    content = '[图片]';
                } else if (message.contentType === CONTENT_TYPES.Audio) {
                    content = '[语音]';
                } else {
                    content = '接收到一条新消息';
                }
                var isOnline = false;
                var target;
                if (result.length > 0) {
                    target = result[0];
                    if (target.is_online) {
                        isOnline = true;
                // 返回发送成功后的消息记录,控制器可选择结束网络连接
                pmRepo.findOneMessage(result.insertId, function (err, msg) {
                    if (err) {
                        modelUtil.emitDbError(self, 'Save private message success, but return last message failed.', err);
                    } else {
                        self.emit(MODEL_EVENTS.OK, fillMessages(msg));
                    }
                }
                });
                var notifyMessage = JSON.stringify({type: 'p2p_msg', from_uid: message.from});
                // 更新自身的聊天统计信息
                statsRepo.updatePrivateChatSummary(message.from, message.to, message.from, message.contentType, message.content, function (err, result) {
                    if (err) modelUtil.logDbError(null, err);
                });
                // 保存通知消息到数据库中并根据用户在线状态推送此消息
                nmRepo.save(message.to, message.contentType, title, content, notifyMessage, isOnline, function (err, result) {
                // 更新对端的聊天统计信息
                statsRepo.updatePrivateChatSummary(message.to, message.from, message.from, message.contentType, message.content, function (err, result) {
                    if (err) modelUtil.logDbError(null, err);
                });
                // 获取对方状态,即对端的系统平台,token等信息,并推送通知消息给对端
                userRepo.getUserStatus(message.to, function (err, result) {
                    if (err) {
                        log.error('Save private notify message failed, ', err);
                        modelUtil.logDbError('Get target user status for private message failed: ' + message.to, err);
                        return;
                    }
                    // 构建通知消息
                    var title = '新消息';
                    var content = '';
                    if (message.contentType === CONTENT_TYPES.PlainText) {
                        content = message.content;
                    } else if (message.contentType === CONTENT_TYPES.Image) {
                        content = '[图片]';
                    } else if (message.contentType === CONTENT_TYPES.Audio) {
                        content = '[语音]';
                    } else {
                        if (isOnline === true) {
                            if (target.platform === PLATFORMS.iOS) {
                                getui.pushAPN(message.to,
                                    target.token,
                                    message.contentType,
                                    title,
                                    content,
                                    notifyMessage,
                                    function (err, result) {
                                        if (err != null) {
                                            console.log(err);
                                        } else {
                                            console.log(result);
                                        }
                                    });
                            } else if (target.platform === PLATFORMS.Android) {
                                getui.pushAndroid(target.client_id,
                                    message.contentType,
                                    title,
                                    content,
                                    notifyMessage,
                                    target.status,
                                    function (err, result) {
                                        if (err != null) {
                                            console.log(err);
                                        } else {
                                            console.log(result);
                                        }
                                    });
                            }
                        content = '接收到一条新消息';
                    }
                    var isOnline = false;
                    var target;
                    if (result.length > 0) {
                        target = result[0];
                        if (target.is_online) {
                            isOnline = true;
                        }
                    }
                    var notifyMessage = JSON.stringify({type: 'p2p_msg', from_uid: message.from});
                    // 保存通知消息到数据库中并根据用户在线状态推送此消息
                    nmRepo.save(message.to, message.contentType, title, content, notifyMessage, isOnline, function (err, result) {
                        if (err) {
                            log.error('Save private notify message failed, ', err);
                        } else {
                            if (isOnline === true) {
                                if (target.platform === PLATFORMS.iOS) {
                                    getui.pushAPN(message.to,
                                        target.token,
                                        message.contentType,
                                        title,
                                        content,
                                        notifyMessage,
                                        function (err, result) {
                                            if (err != null) {
                                                console.log(err);
                                            } else {
                                                console.log(result);
                                            }
                                        });
                                } else if (target.platform === PLATFORMS.Android) {
                                    getui.pushAndroid(target.client_id,
                                        message.contentType,
                                        title,
                                        content,
                                        notifyMessage,
                                        target.status,
                                        function (err, result) {
                                            if (err != null) {
                                                console.log(err);
                                            } else {
                                                console.log(result);
                                            }
                                        });
                                }
                            }
                        }
                    });
                });
            });
        });
    });
};
    }
/**
 * 获取最近聊天的用户,组。
 */
Doctor.prototype.getRecentChats = function(){
    statsRepo.getRecentChats(userId, days, function (err, result) {
        if (err) {
            log.error('Get recent chat objects failed: ', err);
    /**
     * 获取最近聊天的用户,组。
     */
    getRecentChats(){
        statsRepo.getRecentChats(userId, days, function (err, result) {
            if (err) {
                log.error('Get recent chat objects failed: ', err);
            res.status(500).send({message: 'Get recent chat objects failed.'});
            return;
        }
                res.status(500).send({message: 'Get recent chat objects failed.'});
                return;
            }
        var data = {patients: [], doctors: [], groups: []};
        if (result.length === 0) {
            res.status(200).send(data);
            return;
        }
            var data = {patients: [], doctors: [], groups: []};
            if (result.length === 0) {
                res.status(200).send(data);
                return;
            }
        for (var i = 0; i < result.length; ++i) {
            var row = result[i];
            if (row.type.indexOf('patient') > -1) {
                data.patients.push({
                    code: row.code,
                    name: row.name,
                    birthday: row.birthday === null ? "" : row.birthday,
                    sex: row.sex,
                    avatar: row.photo === null ? "" : row.photo
                });
            } else if (row.type.indexOf('doctor') > -1) {
                data.doctors.push({
                    code: row.code,
                    name: row.name,
                    birthday: row.birthday === null ? "" : row.birthday,
                    sex: row.sex,
                    avatar: row.photo === null ? "" : row.photo
                });
            } else if (row.type.indexOf('group') > -1) {
                data.groups.push({
                    code: row.code,
                    name: row.name
                });
            for (var i = 0; i < result.length; ++i) {
                var row = result[i];
                if (row.type.indexOf('patient') > -1) {
                    data.patients.push({
                        code: row.code,
                        name: row.name,
                        birthday: row.birthday === null ? "" : row.birthday,
                        sex: row.sex,
                        avatar: row.photo === null ? "" : row.photo
                    });
                } else if (row.type.indexOf('doctor') > -1) {
                    data.doctors.push({
                        code: row.code,
                        name: row.name,
                        birthday: row.birthday === null ? "" : row.birthday,
                        sex: row.sex,
                        avatar: row.photo === null ? "" : row.photo
                    });
                } else if (row.type.indexOf('group') > -1) {
                    data.groups.push({
                        code: row.code,
                        name: row.name
                    });
                }
            }
        }
        res.status(200).send(data);
    });
};
            res.status(200).send(data);
        });
    }
/**
 * 获取参与的聊天列表,包括:点对点,参与的讨论组,系统消息等。
 *
 * @param userId
 */
Doctor.prototype.getChatList = function (userId) {
    var self = this;
    // 与患者的私信
    pmRepo.findAllP2PWithPatient(userId, function (err, patients) {
        if (err) {
            modelUtil.emitDbError(self, 'Get chat list with patient failed', err);
            return;
        }
    /**
     * 获取参与的聊天列表,包括:点对点,参与的讨论组,系统消息等。
     *
     * @param userId
     */
    getChatList(userId) {
        var self = this;
        var chats = {patients: [], doctors: [], groups: []};
        for (var i = 0; i < patients.length; i++) {
            var patient = patients[i];
            chats.patients.push({
                code: patient.code,
                name: patient.name,
                birthday: patient.birthday,
                sex: patient.sex,
                avatar: patient.photo == null ? "" : patient.photo,
                newMessageCount: patient.new_msg_count,
                lastContentType: patient.last_content_type,
                lastContent: patient.last_content,
                timestamp: objectUtil.timestampToLong(patient.timestamp)
            });
        }
        // 含有患者的群
        gmRepo.findAllGroupsWithPatient(userId, function (err, groups) {
        // 与患者的私信
        pmRepo.findAllP2PWithPatient(userId, function (err, patients) {
            if (err) {
                modelUtil.emitDbError(self, 'Get group list with patient failed', err);
                modelUtil.emitDbError(self, 'Get chat list with patient failed', err);
                return;
            }
            for (var i = 0; i < groups.length; i++) {
                var group = groups[i];
                // 过滤掉医生间的求助团队
                if(group.group_type === 2) continue;
                chats.groups.push({
                    code: group.code,
                    name: group.name,
                    groupType: group.msg_type,
                    newMessageCount: group.new_msg_count,
                    lastContentType: group.last_content_type,
                    lastContent: group.last_content,
                    timestamp: objectUtil.timestampToLong(group.timestamp)
            var chats = {patients: [], doctors: [], groups: []};
            for (var i = 0; i < patients.length; i++) {
                var patient = patients[i];
                chats.patients.push({
                    code: patient.code,
                    name: patient.name,
                    birthday: patient.birthday,
                    sex: patient.sex,
                    avatar: patient.photo == null ? "" : patient.photo,
                    newMessageCount: patient.new_msg_count,
                    lastContentType: patient.last_content_type,
                    lastContent: patient.last_content,
                    timestamp: objectUtil.timestampToLong(patient.timestamp)
                });
            }
            // 医生间的私聊
            pmRepo.findAllP2PWithDoctor(userId, function (err, doctors) {
            // 含有患者的群
            gmRepo.findAllGroupsWithPatient(userId, function (err, groups) {
                if (err) {
                    modelUtil.emitDbError(self, 'Get chat list with doctor failed', err);
                    modelUtil.emitDbError(self, 'Get group list with patient failed', err);
                    return;
                }
                for (var i = 0; i < doctors.length; i++) {
                    var doctor = doctors[i];
                    chats.doctors.push({
                        code: doctor.code,
                        name: doctor.name,
                        sex: doctor.sex,
                        avatar: doctor.photo === null ? "" : doctor.photo,
                        newMessageCount: doctor.new_msg_count,
                        lastContentType: doctor.last_content_type,
                        lastContent: doctor.last_content,
                        timestamp: objectUtil.timestampToLong(doctor.timestamp)
                for (var i = 0; i < groups.length; i++) {
                    var group = groups[i];
                    // 过滤掉医生间的求助团队
                    if(group.group_type === 2) continue;
                    chats.groups.push({
                        code: group.code,
                        name: group.name,
                        groupType: group.msg_type,
                        newMessageCount: group.new_msg_count,
                        lastContentType: group.last_content_type,
                        lastContent: group.last_content,
                        timestamp: objectUtil.timestampToLong(group.timestamp)
                    });
                }
                // 获取医生间的组
                gmRepo.findAllGroupsWithDoctor(userId, function (err, groups) {
                // 医生间的私聊
                pmRepo.findAllP2PWithDoctor(userId, function (err, doctors) {
                    if (err) {
                        modelUtil.emitDbError(self, 'Get group list with doctor failed', err);
                        modelUtil.emitDbError(self, 'Get chat list with doctor failed', err);
                        return;
                    }
                    for (var i = 0; i < groups.length; i++) {
                        var group = groups[i];
                        chats.groups.push({
                            code: group.code,
                            name: group.name,
                            groupType: group.group_type, // 行政团队 or 求助
                            newMessageCount: group.new_msg_count,
                            lastContentType: group.last_content_type,
                            lastContent: group.last_content,
                            timestamp: objectUtil.timestampToLong(group.timestamp)
                    for (var i = 0; i < doctors.length; i++) {
                        var doctor = doctors[i];
                        chats.doctors.push({
                            code: doctor.code,
                            name: doctor.name,
                            sex: doctor.sex,
                            avatar: doctor.photo === null ? "" : doctor.photo,
                            newMessageCount: doctor.new_msg_count,
                            lastContentType: doctor.last_content_type,
                            lastContent: doctor.last_content,
                            timestamp: objectUtil.timestampToLong(doctor.timestamp)
                        });
                    }
                    self.emit(MODEL_EVENTS.OK, chats);
                });
            });
        })
    });
};
                    // 获取医生间的组
                    gmRepo.findAllGroupsWithDoctor(userId, function (err, groups) {
                        if (err) {
                            modelUtil.emitDbError(self, 'Get group list with doctor failed', err);
                            return;
                        }
/**
 * 获取与医生的聊天列表,包括:点对点,参与的讨论组。
 *
 * @param userId
 */
Doctor.prototype.getChatListWithDoctor = function(userId){
    // 先获取医生间的私聊
    pmRepo.findAllP2PWithDoctor(userId, function (err, doctors) {
        if (err) {
            log.error('Get chat list with doctor failed: ', err);
            res.status(500).send({message: 'Get chat list with doctor failed.'});
            return;
        }
                        for (var i = 0; i < groups.length; i++) {
                            var group = groups[i];
                            chats.groups.push({
                                code: group.code,
                                name: group.name,
                                groupType: group.group_type, // 行政团队 or 求助
                                newMessageCount: group.new_msg_count,
                                lastContentType: group.last_content_type,
                                lastContent: group.last_content,
                                timestamp: objectUtil.timestampToLong(group.timestamp)
                            });
                        }
        var chats = {doctors: [], groups: []};
        for (var i = 0; i < doctors.length; i++) {
            var doctor = doctors[i];
            chats.doctors.push({
                code: doctor.code,
                name: doctor.name,
                sex: doctor.sex,
                avatar: doctor.photo === null ? "" : doctor.photo,
                newMessageCount: doctor.new_msg_count,
                lastContentType: doctor.last_content_type,
                lastContent: doctor.last_content,
                timestamp: objectUtil.timestampToLong(doctor.timestamp)
            });
        }
                        self.emit(MODEL_EVENTS.OK, chats);
                    });
                });
            })
        });
    }
        // 再获取医生间的组
        gmRepo.findAllGroupsWithDoctor(userId, function (err, groups) {
    /**
     * 获取与医生的聊天列表,包括:点对点,参与的讨论组。
     *
     * @param userId
     */
    getChatListWithDoctor(userId){
        // 先获取医生间的私聊
        pmRepo.findAllP2PWithDoctor(userId, function (err, doctors) {
            if (err) {
                log.error('Get group list with doctor failed: ', err);
                log.error('Get chat list with doctor failed: ', err);
                res.status(500).send({message: 'Get group list with doctor failed.'});
                res.status(500).send({message: 'Get chat list with doctor failed.'});
                return;
            }
            for (var i = 0; i < groups.length; i++) {
                var group = groups[i];
                chats.groups.push({
                    code: group.code,
                    name: group.name,
                    groupType: group.group_type, // 行政团队 or 求助
                    newMessageCount: group.new_msg_count,
                    lastContentType: group.last_content_type,
                    lastContent: group.last_content,
                    timestamp: objectUtil.timestampToLong(group.timestamp)
            var chats = {doctors: [], groups: []};
            for (var i = 0; i < doctors.length; i++) {
                var doctor = doctors[i];
                chats.doctors.push({
                    code: doctor.code,
                    name: doctor.name,
                    sex: doctor.sex,
                    avatar: doctor.photo === null ? "" : doctor.photo,
                    newMessageCount: doctor.new_msg_count,
                    lastContentType: doctor.last_content_type,
                    lastContent: doctor.last_content,
                    timestamp: objectUtil.timestampToLong(doctor.timestamp)
                });
            }
            res.status(200).send(chats);
        });
    });
};
/**
 * 获取与指定用户的聊天记录。
 *
 * @param userId
 * @param peerId
 */
Doctor.prototype.getPrivateMessages = function(userId, peerId){
    pmRepo.findAllMessages(userId, peerId, contentType === undefined ? "1,2,3,5,6" : contentType, msgStartId, msgEndId, count, closedInterval, function (err, rows) {
        if (err) {
            log.error("Get private message failed, ", err);
            // 再获取医生间的组
            gmRepo.findAllGroupsWithDoctor(userId, function (err, groups) {
                if (err) {
                    log.error('Get group list with doctor failed: ', err);
            res.status(500).send({message: "Get private messages failed."});
            return;
        }
                    res.status(500).send({message: 'Get group list with doctor failed.'});
                    return;
                }
        var messages = fillMessages(rows);
                for (var i = 0; i < groups.length; i++) {
                    var group = groups[i];
                    chats.groups.push({
                        code: group.code,
                        name: group.name,
                        groupType: group.group_type, // 行政团队 or 求助
                        newMessageCount: group.new_msg_count,
                        lastContentType: group.last_content_type,
                        lastContent: group.last_content,
                        timestamp: objectUtil.timestampToLong(group.timestamp)
                    });
                }
        // 清空统计信息
        statsRepo.clearPrivateChatSummary(userId, peerId, function (err, result) {
            if (err) console.log(err);
                res.status(200).send(chats);
            });
        });
    }
        res.status(200).send(messages);
    });
};
    /**
     * 获取与指定用户的聊天记录。
     *
     * @param userId
     * @param peerId
     */
    getPrivateMessages(userId, peerId){
        pmRepo.findAllMessages(userId, peerId, contentType === undefined ? "1,2,3,5,6" : contentType, msgStartId, msgEndId, count, closedInterval, function (err, rows) {
            if (err) {
                log.error("Get private message failed, ", err);
/**
 * 获取与指定用户的未读聊天记录。
 *
 * @param userId
 * @param peerId
 */
Doctor.prototype.getUnreadPrivateMessages = function (userId, peerId) {
    statsRepo.getPrivateChatSummary(userId, peerId, function (err, summary) {
        if (err) {
            log.error("Get unread private messages failed: ", err);
                res.status(500).send({message: "Get private messages failed."});
                return;
            }
            res.status(500).send({message: "Get unread private messages failed."});
            return;
        }
            var messages = fillMessages(rows);
        // 没有未读消息,直接返回
        if (summary.length == 0 || summary[0].new_msg_count === 0) {
            res.status(200).send({startId: 0, count: 0, records: []});
            return;
        }
            // 清空统计信息
            statsRepo.clearPrivateChatSummary(userId, peerId, function (err, result) {
                if (err) console.log(err);
            });
        pmRepo.findUnread(peerId, userId, MAX_INT, summary[0].new_msg_count, function (err, rows) {
            res.status(200).send(messages);
        });
    }
    /**
     * 获取与指定用户的未读聊天记录。
     *
     * @param userId
     * @param peerId
     */
    getUnreadPrivateMessages(userId, peerId) {
        statsRepo.getPrivateChatSummary(userId, peerId, function (err, summary) {
            if (err) {
                log.error("Get unread private messages failed: ", err);
@ -407,41 +393,59 @@ Doctor.prototype.getUnreadPrivateMessages = function (userId, peerId) {
                return;
            }
            var messages = fillMessages(rows);
            // 没有未读消息,直接返回
            if (summary.length == 0 || summary[0].new_msg_count === 0) {
                res.status(200).send({startId: 0, count: 0, records: []});
                return;
            }
            res.status(200).send(messages);
            pmRepo.findUnread(peerId, userId, MAX_INT, summary[0].new_msg_count, function (err, rows) {
                if (err) {
                    log.error("Get unread private messages failed: ", err);
                    res.status(500).send({message: "Get unread private messages failed."});
                    return;
                }
                var messages = fillMessages(rows);
                res.status(200).send(messages);
            });
        });
    });
};
    }
/**
 * 将消息的返回结果合并成JSON。
 *
 * @param rows
 *
 * @returns {startId: 0, count: 0, records: []}
 */
function fillMessages(rows) {
    var messages = {startId: rows.length > 0 ? rows[0].msg_id : '', count: rows.length, records: []};
    for (var i = 0; i < rows.length; i++) {
        var row = rows[i];
        var record = {
            id: row.msg_id,
            from: row.from_uid,
            contentType: row.type,
            content: row.content,
            timestamp: objectUtil.timestampToLong(row.timestamp)
        };
        if (row.to_uid !== undefined) record.to = row.to_uid;
        if (row.at_uid !== undefined) record.at = row.at_uid;
        messages.records.push(record);
    on(message, handler){
        this._eventEmitter.on(message, handler);
    }
    return messages;
    /**
     * 将消息的返回结果合并成JSON。
     *
     * @param rows
     *
     * @returns {startId: 0, count: 0, records: []}
     */
    static fillMessages(rows) {
        var messages = {startId: rows.length > 0 ? rows[0].msg_id : '', count: rows.length, records: []};
        for (var i = 0; i < rows.length; i++) {
            var row = rows[i];
            var record = {
                id: row.msg_id,
                from: row.from_uid,
                contentType: row.type,
                content: row.content,
                timestamp: objectUtil.timestampToLong(row.timestamp)
            };
            if (row.to_uid !== undefined) record.to = row.to_uid;
            if (row.at_uid !== undefined) record.at = row.at_uid;
            messages.records.push(record);
        }
        return messages;
    }
}
// This class inherits from EventEmitter and expose class
util.inherits(Doctor, EventEmitter);
// Expose class
module.exports = Doctor;

+ 224 - 222
src/doctor/models/group.js

@ -4,13 +4,11 @@
"use strict";
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var MODEL_EVENTS = require('../include/commons').MODEL_EVENTS;
var CONTENT_TYPES = require('../include/commons').CONTENT_TYPE;
var PLATFORMS = require('../include/commons').PLATFORM;
var log = require("../util/log.js");
var modelUtil = require('../util/modelUtil');
var getui = require('getui');
@ -22,228 +20,214 @@ var nmRepo = require("../repository/notify.msg.repo");
var statsRepo = require("../repository/stats.msg.repo");
var objectUtil = require("../util/objectUtil.js");
function GroupMessage(){
    EventEmitter.call(this);
}
/**
 * 发送消息。
 *
 * @param message
 */
GroupMessage.prototype.sendMessage = function(message){
    var self = this;
    groupRepo.isGroupMember(message.group, message.groupType, message.from, function (err, result) {
        if (err) {
            modelUtil.emitDbError(self, 'Check group member failed', err);
            return;
        }
        if (result.length == 0) {
            self.emit(MODEL_EVENTS.DataNotFound, {message: 'Member with id "' + message.from + '" is not in group "' + message.group + '"'});
            return;
        }
        gmRepo.save(message.from, message.group, message.at, message.contentType, message.content, function (err, insertedRow) {
class GroupMessage {
    constructor() {
        this._eventEmitter = new EventEmitter();
    }
    
    /**
     * 发送消息。
     *
     * @param message
     */
    sendMessage(message) {
        var self = this;
        groupRepo.isGroupMember(message.group, message.groupType, message.from, function (err, result) {
            if (err) {
                modelUtil.emitDbError(self, 'Save group message failed', err);
                modelUtil.emitDbError(self._eventEmitter, 'Check group member failed', err);
                return;
            }
            if (result.length == 0) {
                self._eventEmitter.emit(MODEL_EVENTS.DataNotFound, {message: 'Member with id "' + message.from + '" is not in group "' + message.group + '"'});
                return;
            }
            gmRepo.findOneMessage(insertedRow.insertId, function (err, groupMsg) {
            gmRepo.save(message.from, message.group, message.at, message.contentType, message.content, function (err, insertedRow) {
                if (err) {
                    modelUtil.emitDbError(self, "Save group message success, but return this message failed", err);
                    modelUtil.emitDbError(self._eventEmitter, 'Save group message failed', err);
                    return;
                }
                // 返回新增加的消息,控制器可结束网络连接
                self.emit(MODEL_EVENTS.OK, fillMessages(groupMsg));
            });
                gmRepo.findOneMessage(insertedRow.insertId, function (err, groupMsg) {
                    if (err) {
                        modelUtil.emitDbError(self._eventEmitter, "Save group message success, but return this message failed", err);
                        return;
                    }
            // 更新组内统计信息, 推送通知消息给群组成员
            statsRepo.updateGroupChatSummary(message.from, message.group, message.from, 0, message.contentType, message.content, false, function (err, result) {
                if (err) modelUtil.emitDbError(null, err);
            });
                    // 返回新增加的消息,控制器可结束网络连接
                    self._eventEmitter.emit(MODEL_EVENTS.OK, GroupMessage.fillMessages(groupMsg));
                });
            groupRepo.getMembers(message.group, message.groupType, function (err, members) {
                if (err) {
                    modelUtil.emitDbError(self, 'Get group members failed: ', err);
                    return;
                }
                // 更新组内统计信息, 推送通知消息给群组成员
                statsRepo.updateGroupChatSummary(message.from, message.group, message.from, 0, message.contentType, message.content, false, function (err, result) {
                    if (err) modelUtil.emitDbError(null, err);
                });
                if (members.length == 0) {
                    log.warn('No members in group ', message.group,
                        message.groupType === GROUP_TYPE.AdminTeam ? " of admin team." : "of discussion group.");
                groupRepo.getMembers(message.group, message.groupType, function (err, members) {
                    if (err) {
                        modelUtil.emitDbError(self._eventEmitter, 'Get group members failed: ', err);
                        return;
                    }
                    return;
                }
                    if (members.length == 0) {
                        log.warn('No members in group ', message.group,
                            message.groupType === GROUP_TYPE.AdminTeam ? " of admin team." : "of discussion group.");
                // 逐个推送通知
                for (var i = 0; i < members.length; i++) {
                    var member = members[i];
                    if (member.user_id === message.from){
                        continue;
                        return;
                    }
                    (function (user_id) {
                        userRepo.getUserStatus(user_id, function (err, result) {
                            if (err) {
                                console.error('Get group member status failed: ', err);
                                return;
                            }
                            var title = '';
                            var content = '';
                            if (message.contentType === CONTENT_TYPES.PlainText) {
                                title = '群组消息';
                                content = message.content;
                            } else if (message.contentType === CONTENT_TYPES.Image) {
                                title = '群组消息';
                                content = '[图片]';
                            } else if (message.contentType === CONTENT_TYPES.Audio) {
                                title = '群组消息';
                                content = '[语音]';
                            } else {
                                title = '群组消息';
                                content = '接收到一条新消息';
                            }
                            var isOnline = result.length > 0 && result[0].is_online === 1;
                            var notifyMessage = JSON.stringify({type: 'group_msg', gid: message.group});
                            // 发送并保存通知到数据库中
                            if (isOnline) {
                                var userStatus = result[0];
                                if (userStatus.platform === PLATFORMS.iOS) {
                                    getui.pushAPN(user_id,
                                        userStatus.token,
                                        message.contentType,
                                        title,
                                        content,
                                        notifyMessage,
                                        function (err, result) {
                                            err != null ? console.error(err) : console.log(result);
                                            // 这段代码重复
                                            nmRepo.save(user_id,
                                                message.contentType,
                                                title,
                                                content,
                                                notifyMessage,
                                                err != null ? 0 : 1,
                                                function (err, result) {
                                                    if (err) {
                                                        console.log('Save group notify message failed: ', err);
                                                    } else {
                                                    }
                                                });
                                        });
                                } else if (userStatus.platform === PLATFORMS.Android) {
                                    getui.pushAndroid(data.client_id,
                    // 逐个推送通知
                    for (var i = 0; i < members.length; i++) {
                        var member = members[i];
                        if (member.user_id === message.from) {
                            continue;
                        }
                        (function (user_id) {
                            userRepo.getUserStatus(user_id, function (err, result) {
                                if (err) {
                                    console.error('Get group member status failed: ', err);
                                    return;
                                }
                                var title = '';
                                var content = '';
                                if (message.contentType === CONTENT_TYPES.PlainText) {
                                    title = '群组消息';
                                    content = message.content;
                                } else if (message.contentType === CONTENT_TYPES.Image) {
                                    title = '群组消息';
                                    content = '[图片]';
                                } else if (message.contentType === CONTENT_TYPES.Audio) {
                                    title = '群组消息';
                                    content = '[语音]';
                                } else {
                                    title = '群组消息';
                                    content = '接收到一条新消息';
                                }
                                var isOnline = result.length > 0 && result[0].is_online === 1;
                                var notifyMessage = JSON.stringify({type: 'group_msg', gid: message.group});
                                // 发送并保存通知到数据库中
                                if (isOnline) {
                                    var userStatus = result[0];
                                    if (userStatus.platform === PLATFORMS.iOS) {
                                        getui.pushAPN(user_id,
                                            userStatus.token,
                                            message.contentType,
                                            title,
                                            content,
                                            notifyMessage,
                                            function (err, result) {
                                                err != null ? console.error(err) : console.log(result);
                                                // 这段代码重复
                                                nmRepo.save(user_id,
                                                    message.contentType,
                                                    title,
                                                    content,
                                                    notifyMessage,
                                                    err != null ? 0 : 1,
                                                    function (err, result) {
                                                        if (err) {
                                                            console.log('Save group notify message failed: ', err);
                                                        } else {
                                                        }
                                                    });
                                            });
                                    } else if (userStatus.platform === PLATFORMS.Android) {
                                        getui.pushAndroid(data.client_id,
                                            message.contentType,
                                            title,
                                            content,
                                            notifyMessage,
                                            userStatus.status,
                                            function (err, result) {
                                                err != null ? console.error(err) : console.log(result);
                                                // 这段代码重复
                                                nmRepo.save(user_id,
                                                    message.contentType,
                                                    title,
                                                    content,
                                                    notifyMessage,
                                                    err != null ? 0 : 1,
                                                    function (err, result) {
                                                        if (err) {
                                                            modelUtil.logDbError('Save group notify message failed', err);
                                                        }
                                                    });
                                            });
                                    }
                                } else {
                                    // 这段代码重复
                                    nmRepo.save(user_id,
                                        message.contentType,
                                        title,
                                        content,
                                        notifyMessage,
                                        userStatus.status,
                                        0,
                                        function (err, result) {
                                            err != null ? console.error(err) : console.log(result);
                                            // 这段代码重复
                                            nmRepo.save(user_id,
                                                message.contentType,
                                                title,
                                                content,
                                                notifyMessage,
                                                err != null ? 0 : 1,
                                                function (err, result) {
                                                    if (err) {
                                                        modelUtil.logDbError('Save group notify message failed', err);
                                                    }
                                                });
                                            if (err) {
                                                modelUtil.logDbError('Save group notify message failed', err);
                                            }
                                        });
                                }
                            } else {
                                // 这段代码重复
                                nmRepo.save(user_id,
                                    message.contentType,
                                    title,
                                    content,
                                    notifyMessage,
                                    0,
                                    function (err, result) {
                                        if (err) {
                                            modelUtil.logDbError('Save group notify message failed', err);
                                        }
                                    });
                            }
                        });
                        // 统计'@'信息
                        var at = message.at == user_id ? 1 : 0;
                        statsRepo.updateGroupChatSummary(user_id,
                            message.group,
                            message.from,
                            at,
                            message.contentType,
                            message.content,
                            true,
                            function (err, result) {
                                if (err) modelUtil.logDbError('Update group chat summary failed', err);
                            });
                    })(member.user_id);
                }
                            // 统计'@'信息
                            var at = message.at == user_id ? 1 : 0;
                            statsRepo.updateGroupChatSummary(user_id,
                                message.group,
                                message.from,
                                at,
                                message.contentType,
                                message.content,
                                true,
                                function (err, result) {
                                    if (err) modelUtil.logDbError('Update group chat summary failed', err);
                                });
                        })(member.user_id);
                    }
                });
            });
        });
    });
};
/**
 * 获取组消息。
 */
GroupMessage.prototype.getMessages = function (groupId, memberId) {
    gmRepo.findAllMessages(groupId, !contentType ? "1,2,3,5,6" : contentType, msgStartId, msgEndId, count, function (err, rows) {
        if (err) {
            console.log('Get group message failed: ', err);
            res.status(500).send({message: 'Get group message failed.'});
            return;
        }
        var messages = fillMessages(rows);
    }
        // 清空统计信息
        statsRepo.clearGroupChatSummary(userId, groupId, function (err, result) {
            if (err) console.log(err);
        });
    /**
     * 获取组消息。
     */
    getMessages(groupId, memberId) {
        gmRepo.findAllMessages(groupId, !contentType ? "1,2,3,5,6" : contentType, msgStartId, msgEndId, count, function (err, rows) {
            if (err) {
                console.log('Get group message failed: ', err);
        res.status(200).send(messages);
    });
};
                res.status(500).send({message: 'Get group message failed.'});
                return;
            }
/**
 * 获取组内未读消息。
 * @param groupId
 */
GroupMessage.prototype.getUnreadMessages = function (groupId, memberId) {
    statsRepo.getGroupChatSummary(userId, groupId, function (err, summary) {
        if (err) {
            log.error("Get unread group messages failed: ", err);
            var messages = GroupMessage.fillMessages(rows);
            res.status(500).send({message: "Get unread group messages failed."});
            return;
        }
            // 清空统计信息
            statsRepo.clearGroupChatSummary(userId, groupId, function (err, result) {
                if (err) console.log(err);
            });
        var messages = {startId: 0, count: 0, records: []};
        if (summary.length == 0 || summary[0].new_msg_count === 0) {
            res.status(200).send(messages);
            return;
        }
        });
    }
        messages.count = summary[0].new_msg_count;
        gmRepo.findUnread(groupId, MAX_INT, messages.count, function (err, rows) {
    /**
     * 获取组内未读消息。
     * @param groupId
     */
    getUnreadMessages(groupId, memberId) {
        statsRepo.getGroupChatSummary(userId, groupId, function (err, summary) {
            if (err) {
                log.error("Get unread group messages failed: ", err);
@ -251,41 +235,59 @@ GroupMessage.prototype.getUnreadMessages = function (groupId, memberId) {
                return;
            }
            var messages = fillMessages(rows);
            var messages = {startId: 0, count: 0, records: []};
            if (summary.length == 0 || summary[0].new_msg_count === 0) {
                res.status(200).send(messages);
                return;
            }
            res.status(200).send(messages);
            messages.count = summary[0].new_msg_count;
            gmRepo.findUnread(groupId, MAX_INT, messages.count, function (err, rows) {
                if (err) {
                    log.error("Get unread group messages failed: ", err);
                    res.status(500).send({message: "Get unread group messages failed."});
                    return;
                }
                var messages = fillMessages(rows);
                res.status(200).send(messages);
            });
        });
    });
};
    }
/**
 * 将消息的返回结果合并成JSON。
 *
 * @param rows
 *
 * @returns {startId: 0, count: 0, records: []}
 */
function fillMessages(rows) {
    var messages = {startId: rows.length > 0 ? rows[0].msg_id : '', count: rows.length, records: []};
    for (var i = 0; i < rows.length; i++) {
        var row = rows[i];
        var record = {
            id: row.msg_id,
            from: row.from_uid,
            contentType: row.type,
            content: row.content,
            timestamp: objectUtil.timestampToLong(row.timestamp)
        };
        if (row.to_uid !== undefined) record.to = row.to_uid;
        if (row.at_uid !== undefined) record.at = row.at_uid;
        messages.records.push(record);
    on(event, handler) {
        this._eventEmitter.on(event, handler);
    }
    return messages;
    /**
     * 将消息的返回结果合并成JSON。
     *
     * @param rows
     *
     * @returns {startId: 0, count: 0, records: []}
     */
    static fillMessages(rows) {
        var messages = {startId: rows.length > 0 ? rows[0].msg_id : '', count: rows.length, records: []};
        for (var i = 0; i < rows.length; i++) {
            var row = rows[i];
            var record = {
                id: row.msg_id,
                from: row.from_uid,
                contentType: row.type,
                content: row.content,
                timestamp: objectUtil.timestampToLong(row.timestamp)
            };
            if (row.to_uid !== undefined) record.to = row.to_uid;
            if (row.at_uid !== undefined) record.at = row.at_uid;
            messages.records.push(record);
        }
        return messages;
    }
}
// This class inherits from EventEmitter and expose class
util.inherits(GroupMessage, EventEmitter);
module.exports = GroupMessage;

+ 43 - 98
src/doctor/models/patient.js

@ -3,111 +3,56 @@
 */
"use strict";
var http = require('http');
var EventEmitter = require('events').EventEmitter;
var util = require('util');
let configFile = require('../include/commons').CONFIG_FILE;
let config = require('../resources/config/' + configFile);
let log = require("../util/log.js");
var configFile = require('../include/commons').CONFIG_FILE;
var config = require('../resources/config/' + configFile);
var log = require("../util/log.js");
let patientRepo = require('../repository/patient.repo');
let objectUtil = require("../util/objectUtil.js");
var patientRepo = require('../repository/patient.repo');
var pmRepo = require('../repository/private.msg.repo.js');
var statsRepo = require("../repository/stats.msg.repo.js");
var objectUtil = require("../util/objectUtil.js");
let clientCache = require('./socket.io/client.cache').clientCache();
var TRANS_ENDPOINTS = require('../include/transparent.endpoints').TRAN_ENDPOINTS;
// inherit and expose class
util.inherits(Patient, EventEmitter);
module.exports = Patient;
class Patient {
    constructor() {
    }
function Patient() {
    EventEmitter.call(this);
}
    /**
     * 是否为患者代码。
     *
     * @param code
     * @param passedCallback 测试通过回调
     * @param failedCallback 测试失败回调
     */
    isPatientCode(code, passedCallback, failedCallback) {
        patientRepo.isPatientCode(code, function (err, result) {
            if (err) {
                log.error('Send message to patient failed: ', err);
                return;
            }
            if (result[0].c > 0) {
                passedCallback();
            } else {
                failedCallback();
            }
        });
    }
Patient.prototype.isPatientCode = function (code, trueCallback, falseCallback) {
    patientRepo.isPatientCode(code, function (err, result) {
        if (err) {
            log.error('Send message to patient failed: ', err);
    /**
     * 向患者发送消息。
     *
     * @param patientId 患者ID
     * @param message 消息
     */
    sendMessage(patientId, message) {
        let patientClient = clientCache.findById(patientId);
        if (!patientClient) {
            log.warn("User is not online, user id: ", patientId);
            return;
        }
        if (result[0].c > 0) {
            trueCallback();
        } else {
            falseCallback();
        }
    });
};
/**
 * 向患者发送消息。
 *
 * @param userId
 * @param messageType
 * @param feedback
 */
Patient.prototype.sendMessage = function (userId, messageType, feedback) {
    var msg = {
        busiType: 'qianyuezixun',
        msgid: feedback.startId,
        msgType: messageType
    };
    // 通知患者
    var path = TRANS_ENDPOINTS.User.Message.Path +
        "?userid=" + userId + "&data=" + JSON.stringify(msg);
    var options = {
        hostname: config.transServerConfig.host,
        port: config.transServerConfig.port,
        path: path,
        method: TRANS_ENDPOINTS.User.Message.Method,
        headers: {
            "Content-Type": 'application/json'
        }
        patientClient.socketServer.sockets.emit('message', message);
    };
}
    log.info('患者消息透传: http://' + options.hostname + ":" + options.port + options.path);
    var req = http.request(options, function (res) {
        res.setEncoding('utf8');
        res.on('data', function (chunk) {
            log.info('透传服务器返回: ', chunk);
        });
    });
    req.on('error', function (e) {
        log.error('患者消息透传出错: ', e.message);
    });
    req.end();
};
/**
 * 将消息的返回结果合并成JSON。
 *
 * @param rows
 *
 * @returns {startId: 0, count: 0, records: []}
 */
function fillMessages(rows) {
    var messages = {startId: rows.length > 0 ? rows[0].msg_id : '', count: rows.length, records: []};
    for (var i = 0; i < rows.length; i++) {
        var row = rows[i];
        var record = {
            id: row.msg_id,
            from: row.from_uid,
            contentType: row.type,
            content: row.content,
            timestamp: objectUtil.timestampToLong(row.timestamp)
        };
        if (row.to_uid !== undefined) record.to = row.to_uid;
        if (row.at_uid !== undefined) record.at = row.at_uid;
        messages.records.push(record);
    }
    return messages;
}
module.exports = Patient;

+ 1 - 1
src/doctor/models/socket.io/client.cache.js

@ -24,7 +24,7 @@ class ClientCache {
        this._clientsByUserId.set(client.userId, client);
        this._clientsBySocket.set(client.socket, client);
        log.info('Current clients: ', this.clients);
        //log.info('Current clients: ', this.clients);
    }
    removeByUserId(userId){

+ 7 - 2
src/doctor/models/socket.io/client.js

@ -7,8 +7,9 @@
"use strict";
class Client{
    constructor(socket) {
    constructor(socket, socketServer) {
        this._socket = socket;
        this._socketServer = socketServer;
        this._userId = '';
        this._password = '';
    }
@ -17,6 +18,10 @@ class Client{
        return this._socket;
    }
    get socketServer(){
        return this._socketServer;
    }
    get userId(){
        return this._userId;
    }
@ -33,7 +38,7 @@ class Client{
        this._password = password;
    }
    tellus(){
    tellme(){
        return "Socket: " + this.socket + ", user: " + this.userId + ", password: " + this.password;
    }
}

+ 2 - 2
src/doctor/models/socket.io/patient.client.js

@ -11,8 +11,8 @@ var util = require('util');
var Client = require('./client');
class PatientClient extends Client{
    constructor(socket){
        super(socket);
    constructor(socket, socketServer){
        super(socket, socketServer);
    }
}

+ 27 - 25
src/doctor/models/stats.js

@ -4,7 +4,6 @@
"use strict";
var EventEmitter = require("events").EventEmitter;
var util = require("util");
var statsRepo = require("../repository/stats.msg.repo.js");
var log = require("../util/log.js");
@ -12,30 +11,33 @@ var modelUtil = require('../util/modelUtil');
var MODEL_EVENTS = require('../include/commons').MODEL_EVENTS;
// This class inherits from EventEmitter and expose class
util.inherits(StatsMessage, EventEmitter);
module.exports = StatsMessage;
class StatsMessage{
    constructor(){
        this._eventEmitter = new EventEmitter();
    }
    /**
     * 获取应用角标数,基于消息数量。
     * @param userId
     */
    getBadgeNumber(userId){
        var self = this;
        statsRepo.getBadgeNumber(userId, function (err, result) {
            if (err) {
                modelUtil.emitDbError(this, "Get badge number failed: ", err);
                return;
            }
            var data = {userId: userId, badge: result};
            this._evenEmitter.emit(MODEL_EVENTS.OK, data);
        });
    }
    on(event, handler) {
        this._eventEmitter.on(event, handler);
    }
}
function StatsMessage() {
    // call the super constructor to initialize `this`
    EventEmitter.call(this);
    log.info("Construct StatsMessage.");
}
/**
 * 获取应用角标数,基于消息数量。
 * @param userId
 */
StatsMessage.prototype.getBadgeNumber = function(userId){
    var self = this;
    statsRepo.getBadgeNumber(userId, function (err, result) {
        if (err) {
            modelUtil.emitDbError(this, "Get badge number failed: ", err);
            return;
        }
        var data = {userId: userId, badge: result};
        self.emit(MODEL_EVENTS.OK, data);
    });
};
module.exports = StatsMessage;

+ 68 - 65
src/doctor/models/system.js

@ -4,7 +4,6 @@
"use strict";
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var MODEL_EVENTS = require('../include/commons').MODEL_EVENTS;
var PLATFORMS = require('../include/commons').PLATFORM;
@ -17,63 +16,65 @@ var userRepo = require('../repository/doctor.repo.js');
var smRepo = require("../repository/system.msg.repo.js");
var nmRepo = require("../repository/notify.msg.repo.js");
function SystemMessage() {
    EventEmitter.call(this);
}
class SystemMessage {
    constructor() {
        this._eventEmitter = new EventEmitter();
    }
    /**
     * 发送消息。
     *
     * @param message
     */
    send(message) {
        var self = this;
        userRepo.getUserStatus(message.to, function (err, rows) {
            if (err) {
                console.log("Lookup system message receiver failed: ", err);
                this.emit(MODEL_EVENTS.Error, {message: "Lookup system message receiver failed."});
                return;
            }
            if (rows.length == 0) {
                this.emit(MODEL_EVENTS.DataNotFound, {message: "User not found: " + message.to});
                return;
            }
            var userStatus = rows[0];
            var isOnline = userStatus.is_online;
            var notifyMessage = JSON.stringify({type: 'system_msg', data: message.content});
            // 保存该条推送信息
            smRepo.save(message.to,
                message.contentType,
                message.title,
                message.summary,
                message.content,
                function (err, result) {
                    if (err) {
                        modelUtil.emitDbError(self._eventEmitter, "Save system notify message failed.", err);
                        return;
                    }
                    // 先通知外层操作已经完成,再处理后续操作。客户可先结束网络连接,减少客户端等待。
                    self._eventEmitter.emit(MODEL_EVENTS.OK, {});
                    // 保存通知到数据库中
                    nmRepo.save(message.to,
                        message.contentType,
                        message.title,
                        message.summary,
                        notifyMessage,
                        isOnline,
                        function (err, result) {
                            if (err) {
                                modelUtil.logDbError("Save system notify message failed", err);
                                return;
                            }
                            if (!isOnline) return;
/**
 * 发送消息。
 *
 * @param message
 */
SystemMessage.prototype.send = function (message) {
    var self = this;
    userRepo.getUserStatus(message.to, function (err, rows) {
        if (err) {
            console.log("Lookup system message receiver failed: ", err);
            this.emit(MODEL_EVENTS.Error, {message: "Lookup system message receiver failed."});
            return;
        }
        if (rows.length == 0) {
            this.emit(MODEL_EVENTS.DataNotFound, {message: "User not found: " + message.to});
            return;
        }
        var userStatus = rows[0];
        var isOnline = userStatus.is_online;
        var notifyMessage = JSON.stringify({type: 'system_msg', data: message.content});
        // 保存该条推送信息
        smRepo.save(message.to,
            message.contentType,
            message.title,
            message.summary,
            message.content,
            function (err, result) {
                if (err) {
                    modelUtil.emitDbError(self, "Save system notify message failed.", err);
                    return;
                }
                // 先通知外层操作已经完成,再处理后续操作。客户可先结束网络连接,减少客户端等待。
                self.emit(MODEL_EVENTS.OK, {});
                // 保存通知到数据库中
                nmRepo.save(message.to,
                    message.contentType,
                    message.title,
                    message.summary,
                    notifyMessage,
                    isOnline,
                    function (err, result) {
                        if (err) {
                            modelUtil.logDbError("Save system notify message failed", err);
                            return;
                        }
                        if (isOnline) {
                            if (userStatus.platform === PLATFORMS.iOS) {
                                getui.pushAPN(message.to, userStatus.token, message.contentType, message.title, message.content, notifyMessage,
                                    function (err, result) {
@ -85,12 +86,14 @@ SystemMessage.prototype.send = function (message) {
                                        err != null ? console.log(err) : console.log(result);
                                    });
                            }
                        }
                    });
            });
    });
};
// This class inherits from EventEmitter and expose class
util.inherits(SystemMessage, EventEmitter);
                        });
                });
        });
    }
    on(event, handler) {
        this._eventEmitter.on(event, handler);
    }
}
module.exports = SystemMessage;

+ 58 - 4
src/doctor/public/html/socket/test.html

@ -4,20 +4,74 @@
    <meta charset="utf-8">
    <title>Socket.IO Utility Page</title>
</head>
<body>
<h1>Socket.IO Utility Page</h1>
<h2>Patient Login</h2>
<form id="patient_register">
    <label for="patient_id">Patient Id: </label>
    <input type="text" id="patient_id"/>
    <p></p>
    <label for="password">Password: </label>
    <input type="text" id="password"/>
    <p></p>
    <input id="login" type="button" value="Login"/>
    <input id="logout" type="button" value="Logout" hidden/>
</form>
<h2>Chat with doctors</h2>
<form>
    <label for="doctor_id">Doctor Id: </label>
    <input type="text" id="doctor_id"/>
    <p><label for="message">Message: </label></p>
    <p><textarea cols="25" id="message"></textarea></p>
    <input id="send" type="submit" value="Send"/>
    <p><label for="history">History: </label></p>
    <p><textarea cols="25" id="history"></textarea></p>
</form>
<script src="//cdn.bootcss.com/jquery/1.9.0/jquery.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
    var socket = io.connect();
    socket.on('welcome', function (data) {
        console.log(data.message);
        socket.emit('login', {userId: 'sand', password: 'hello'});
    socket.on('message', function (data) {
        console.log(data.message);
    });
    socket.on('message', function (data) {
    socket.on('error', function (data) {
        console.log(data.message);
    });
    socket.on('ack', function (data) {
        $('#patient_id').prop("disabled",true);
        $('#password').prop("disabled",true);
        $('#login').hide();
        $('#logout').show();
    });
    $('#login').click(function () {
        socket.emit('login', {userId: $('#patient_id').val(), password: $('#password').val()});
    });
    $('#logout').click(function () {
        socket.emit('logout', {userId: $('#patient_id').val()});
        $('#patient_id').prop("disabled",false);
        $('#password').prop("disabled",false);
        $('#login').show();
        $('#logout').hide();
    });
</script>
</body>
</html>

+ 2 - 2
src/doctor/util/modelUtil.js

@ -16,8 +16,8 @@ function logDbError(description, err) {
 * @param err
 * @param description
 */
module.exports.emitDbError = function (object, description, err) {
    logDbError(description, MODEL_EVENTS.Error);
module.exports.emitDbError = function (object, description) {
    logDbError(description, err);
    object.emit(MODEL_EVENTS.Error, {message: description});
};