Преглед на файлове

修订技术结构图;完成消息推送功能

Sand преди 8 години
родител
ревизия
d4e8f7680b

BIN
doc/images/activity.png


+ 4 - 0
readme.md

@ -79,6 +79,10 @@ IM提供了开发SDK,一个JS脚本。客户端可以通过引用此脚本或
- REST API相关的放在endpoints
- 与页面相关的放在controllers
### 消息发送
	实时与延时
## 错误处理
Node.js支持同步与异步调用,也导致了异常错误处理与众不同。一个给定的函数,它处理异常的方式要么是同步(用 throw方式)要么是异步的(用 callback 或者 EventEmitter),不会两者兼具。

+ 6 - 2
src/doctor/app.js

@ -15,7 +15,6 @@ let log = require('./util/log');
// server configurations
let APIv1 = require('./include/endpoints').APIv1;
let PAGES = require('./include/endpoints').PAGES;
let configFile = require('./include/commons').CONFIG_FILE;
let config = require('./resources/config/' + configFile);
@ -33,6 +32,9 @@ let management = require('./endpoints/management.endpoint');
// handlers
let SocketHandler = require('./handlers/socket.handler');
// schedule
let PushJobLoader = require('./models/schedule/push.job.loader')
// initialize express application
let app = express();
app.set('port', config.serverPort);
@ -118,9 +120,11 @@ server.on('listening', function onListening() {
    let bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
});
//io.sockets.on('connection', socketHandler.onConnection);
let socketHandler = new SocketHandler(io);
socketHandler.start();
log.info('Starting IM server, version ' + config.version + ', running on port ' + server.address().port + ', ' + new Date().toLocaleString());
log.info('Configuration profile: ' + configFile.split('.')[1]);
// load the notify messages that have not pushed
//PushJobLoader.load();

+ 2 - 1
src/doctor/endpoints/chats.endpoint.js

@ -39,7 +39,8 @@ const DEFAULT_PAGE_SIZE = require('../include/commons').DEFAULT_PAGE_SIZE;
 *      title: "System Message",
 *      summary: "You have new job",
 *      contentType: "1",
 *      content: "The patient has been followed in the scheduler, please make new follow plan as soon as possible."
 *      content: "The patient has been followed in the scheduler, please make new follow plan as soon as possible.",
 *      delay: 986123465
 *  }
 *
 * @param message

+ 31 - 16
src/doctor/models/doctor.js

@ -7,6 +7,8 @@ let log = require("../util/log.js");
let getui = require('getui');
let BaseModel = require('./base.model');
let Schedule = require("./schedule/schedule.js");
let doctorRepo = require('../repository/doctor.repo.js');
let gmRepo = require('../repository/group.msg.repo');
let pmRepo = require('../repository/private.msg.repo');
@ -96,7 +98,7 @@ class Doctor extends BaseModel {
     * @param message
     * @param channel
     */
    static pushMessage(message, channel){
    static pushMessage(message, channel) {
        doctorRepo.getUserStatus(message.to, function (err, result) {
            if (err) {
                log.error('Lookup notify message receiver failed: ' + message.to);
@ -110,11 +112,13 @@ class Doctor extends BaseModel {
            let userStatus = result[0];
            let isOnline = result.length > 0 && userStatus.is_online === 1;
            let delay = null;
            // 构建通知消息
            let notifyMessage = {type: channel, data: message.content};
            if(message.from) notifyMessage.from_uid = message.from;
            if(message.gid) notifyMessage.gid = message.gid;
            if (message.from) notifyMessage.from_uid = message.from;
            if (message.gid) notifyMessage.gid = message.gid;
            if (message.delay && message.delay !== "null") delay = new Date(Date.parse(message.delay));
            let title = '新消息';
            let content = message.content;
@ -122,27 +126,38 @@ class Doctor extends BaseModel {
                content = '[图片]';
            } else if (message.contentType === CONTENT_TYPES.Audio) {
                content = '[语音]';
            } else if(message.contentType > 3) {
            } else if (message.contentType > 3) {
                content = '您有一条新消息';
            }
            // 保存通知消息到数据库中,并根据用户在线状态推送此消息
            nmRepo.save(message.to, message.contentType, title, content, JSON.stringify(notifyMessage), isOnline, function (err, result) {
            nmRepo.save(message.to, message.contentType, title, content, JSON.stringify(notifyMessage), isOnline, delay, function (err, result) {
                if (err) {
                    log.error('Save notify message failed, ', err);
                    return;
                }
                if (!isOnline) return;
                Doctor.pushToClient(message.to, userStatus.client_id, userStatus.status, userStatus.token, message.contentType,
                    title, content, notifyMessage, userStatus.platform, function (err, result) {
                        if (err != null) {
                            console.log(err);
                        } else {
                            console.log(result);
                        }
                    });
                if (delay) {
                    Schedule.dateSchedule(delay, function (message, client_id, status, token, title, content, notifyMessage, platform) {
                        Doctor.pushToClient(message.to, client_id, status, token, message.contentType,
                            title, content, notifyMessage, platform, function (err, result) {
                                if (err != null) {
                                    log.error(err);
                                } else {
                                    log.info(result);
                                }
                            });
                    }.bind(null, message, userStatus.client_id, userStatus.status, userStatus.token, title, content, notifyMessage, userStatus.platform));
                } else if (isOnline) {
                    Doctor.pushToClient(message.to, userStatus.client_id, userStatus.status, userStatus.token, message.contentType,
                        title, content, notifyMessage, userStatus.platform, function (err, result) {
                            if (err != null) {
                                log.error(err);
                            } else {
                                log.info(result);
                            }
                        });
                }
            });
        });
    }
@ -452,7 +467,7 @@ class Doctor extends BaseModel {
            // 清空统计信息
            statsRepo.clearPrivateChatSummary(userId, peerId, function (err, result) {
                if (err) console.log(err);
                if (err) log.error(err);
            });
        });
    }

+ 1 - 1
src/doctor/models/group.js

@ -96,7 +96,7 @@ class GroupMessage extends BaseModel {
                                            message.content,
                                            true,
                                            function (err, result) {
                                                if (err) console.log(err);
                                                if (err) log.error(err);
                                            });
                                    });
                            })(member.user_id);

+ 62 - 0
src/doctor/models/schedule/push.job.loader.js

@ -0,0 +1,62 @@
/**
 * 推送消息加载器。应用启动时使用此加载器将将来需要推送的消息加入消息调度中。
 *
 * author: Sand
 * since: 2016/11/28
 */
"use strict";
let Schedule = require('./schedule');
let Doctor = require("../doctor.js");
const nmRepo = require('../../repository/notify.msg.repo');
const doctorRepo = require('../../repository/doctor.repo');
const log = require("../../util/log.js");
class PushJobLoader{
    constructor(){}
    static load(){
        nmRepo.findUnpushedMessages(function (err, rows) {
            if(err){
                log.error('Load schedule jobs failed: ', err);
                return;
            }
            for(let i = 0; i < rows.length; ++i){
                let row = rows[i];
                let delay = new Date(Date.parse(row.delay));
                let userId = row.to_uid;
                let title = row.title;
                let contentType = row.type;
                let content = row.content;
                let notifyMessage = row.data;
                Schedule.dateSchedule(delay, function (userId, title, contentType, content, notifyMessage) {
                    doctorRepo.getUserStatus(userId, function (err, result) {
                        if(err) {
                            log.error('Get user status failed in schedule: ', err);
                            return;
                        }
                        if(result.length > 0){
                            let userStatus = result[0];
                            Doctor.pushToClient(userId, userStatus.client_id, userStatus.status, userStatus.token, contentType,
                                title, content, notifyMessage, userStatus.platform, function (err, result) {
                                    if (err != null) {
                                        log.error(err);
                                    } else {
                                        log.info(result);
                                    }
                                });
                        } else {
                            log.warn('User is not online, scheduled pushing job omitted.');
                        }
                    })
                }.bind(null, userId, title, contentType, content, notifyMessage));
            }
        });
    }
}
module.exports = PushJobLoader;

+ 31 - 0
src/doctor/models/schedule/schedule.js

@ -0,0 +1,31 @@
/**
 * 计划任务调度器。
 *
 * https://github.com/node-schedule/node-schedule
 *
 * author: Sand
 * since: 2016/11/28
 */
"use strict";
let nodeSchedule = require('node-schedule');
let log = require("../../util/log.js");
class Schedule {
    constructor(){
    }
    static cronSchedule(cronExp, handler){
        log.info('Job scheduled by cron express: ' + cronExp);
        return nodeSchedule.scheduleJob(cronExp, handler);
    }
    static dateSchedule(date, handler){
        log.info('Job scheduled at ' + date.toUTCString());
        return nodeSchedule.scheduleJob(date, handler);
    }
}
module.exports = Schedule;

+ 2 - 1
src/doctor/models/search.js

@ -14,6 +14,7 @@ let BaseModel = require('./base.model');
let searchRepo = require('../repository/search.repo');
let modelUtil = require("../util/modelUtil");
let objectUtil = require('../util/objectUtil');
let log = require("../util/log.js");
const GROUP_TYPE = require('../include/commons').GROUP_TYPE;
@ -59,7 +60,7 @@ class Search extends BaseModel{
                for (var i = 0; i < chats.length; ++i) {
                    var lastPatient = {code: '', name: '', sex: '', avatar: '',amount:'',content:'',chat:'',type:''};
                    var chat = chats[i];
                    console.log(JSON.stringify(chat));
                    log.info(JSON.stringify(chat));
                    lastPatient.code = chat.code;
                    lastPatient.name = chat.name;
                    lastPatient.sex = chat.sex;

+ 0 - 93
src/doctor/models/system.js

@ -1,93 +0,0 @@
/**
 * 系统消息。
 */
"use strict";
let PLATFORMS = require('../include/commons').PLATFORM;
let BaseModel = require('./base.model');
let log = require("../util/log.js");
let modelUtil = require('../util/modelUtil');
let getui = require('getui');
let userRepo = require('../repository/doctor.repo.js');
let smRepo = require("../repository/system.msg.repo.js");
let nmRepo = require("../repository/notify.msg.repo.js");
class SystemMessage extends BaseModel{
    constructor() {
        super();
    }
    /**
     * 发送消息。
     *
     * @param message
     */
    send(message) {
        let self = this;
        userRepo.getUserStatus(message.to, function (err, rows) {
            if (err) {
                console.log("Lookup system message receiver failed: ", err);
                modelUtil.emitDbError(self.eventEmitter, "Lookup system message receiver failed.", err);
                return;
            }
            if (rows.length == 0) {
                modelUtil.emitDataNotFound(self.eventEmitter, "User not found: " + message.to);
                return;
            }
            let userStatus = rows[0];
            let isOnline = userStatus.is_online;
            let 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;
                    }
                    // 先通知外层操作已经完成,再处理后续操作。客户可先结束网络连接,减少客户端等待。
                    modelUtil.emitData(self.eventEmitter, {});
                    // 保存通知到数据库中
                    nmRepo.save(message.to,
                        message.contentType,
                        message.title,
                        message.summary,
                        notifyMessage,
                        isOnline,
                        function (err, result) {
                            if (err) {
                                modelUtil.logError("Save system notify message failed", err);
                                return;
                            }
                            if (!isOnline) return;
                            if (userStatus.platform === PLATFORMS.iOS) {
                                getui.pushAPN(message.to, userStatus.token, message.contentType, message.title, message.content, notifyMessage,
                                    function (err, result) {
                                        err != null ? console.log(err) : console.log(result);
                                    });
                            } else if (userStatus.platform === PLATFORMS.Android) {
                                getui.pushAndroid(userStatus.client_id, message.contentType, message.title, message.content, notifyMessage, userStatus.status,
                                    function (err, result) {
                                        err != null ? console.log(err) : console.log(result);
                                    });
                            }
                        });
                });
        });
    }
}
module.exports = SystemMessage;

+ 31 - 4
src/doctor/repository/notify.msg.repo.js

@ -4,15 +4,42 @@
 */
"use strict";
var log = require('../util/log');
var imRepo = require("./database/im.db.js");
let log = require('../util/log');
let imRepo = require("./database/im.db.js");
exports.save = function(to, contentType, title, content, message, has_pushed, handler) {
/**
 * 保存推送消息。
 *
 * @param to
 * @param contentType
 * @param title
 * @param content
 * @param message
 * @param has_pushed
 * @param delay
 * @param handler
 */
exports.save = function(to, contentType, title, content, message, has_pushed, delay, handler) {
    imRepo.execQuery({
        "sql": "INSERT INTO push_notify (to_uid,type,title,content,data,has_pushed) VALUES (?,?,?,?,?,?)",
        "sql": "INSERT INTO push_notify (to_uid, type, title, content, data, delay, has_pushed) VALUES (?,?,?,?,?,?)",
        "args": [to, contentType, title, content, message, has_pushed],
        "handler": handler
    });
};
/**
 * 查找未推送的消息。
 */
exports.findUnpushedMessages = function (handler) {
    let sql = "select to_uid, title, type, content, data, delay " +
        "FROM push_notify " +
        "WHERE delay IS NOT NULL AND delay > now()";
    imRepo.execQuery({
        "sql": sql,
        "args": [],
        "handler": handler
    });
};
//exports.save = save;

+ 1 - 1
src/doctor/resources/config/config.dev.js

@ -47,7 +47,7 @@ let geTuiAppStoreCfg = {
};
exports.app = 'IM.Server';
exports.version = '1.2.5.20161107';
exports.version = '1.2.7';
exports.debug = true;
exports.serverPort = 3000;
exports.sessionExpire = 1800;

+ 7 - 7
src/doctor/resources/config/config.prod.js

@ -9,10 +9,10 @@ let wlyyDbConfig = {
};
let imDbConfig = {
    host: 'localhost',
    user: 'root',
    password: '19991120',
    database: 'im_new',
    host: '59.61.92.94',
    user: 'wlyy',
    password: 'jkzlehr@123',
    database: 'wlyy',
    connectionLimit: '100'
};
@ -34,8 +34,8 @@ let geTuiAppStoreCfg = {
// 三师后台
let wlyyServerConfig = {
    host: '172.19.103.87',
    port: 9090
    host: '120.41.252.108',
    port: 9660
};
// 透传服务
@ -45,7 +45,7 @@ let transServerConfig = {
};
exports.app = 'im.server';
exports.version = '1.2.5.20161107';
exports.version = '1.2.7';
exports.debug = true;
exports.serverPort = 3000;
exports.sessionExpire = 1800;

+ 1 - 1
src/doctor/resources/config/config.test.js

@ -45,7 +45,7 @@ let transServerConfig = {
};
exports.app = 'im.server';
exports.version = '1.2.5.20161107';
exports.version = '1.2.7';
exports.debug = true;
exports.serverPort = 3000;
exports.sessionExpire = 1800;

+ 8 - 2
test/doctor/endpoints/chats.endpoint.Test.js

@ -19,13 +19,19 @@ describe('API: Chat', function () {
    describe('when send SYSTEM message correctly', function () {
        it('should return 200', function (done) {
            var path = APIv1.Chats.Base + APIv1.Chats.SM;
            let now = new Date();
            now.setSeconds(now.getSeconds() + 2);
            let delay = now.toUTCString();
            restTemplate.post(path)
                .send({
                    to: userId,
                    to: 'x1',
                    title: "系统消息",
                    summary: "您有一条新的工作记录",
                    contentType: "1",
                    content: "患者已经签约,可以制定随访计划。"
                    content: "患者已经签约,可以制定随访计划。",
                    delay: delay
                })
                .end(function (err, response) {
                    if(response.status === 200){

+ 21 - 0
test/doctor/models/schedule/schedule.Test.js

@ -0,0 +1,21 @@
/**
 *
 * author: Sand
 * since: 2016/11/28
 */
"use strict";
let assert = require('assert');
let Schedule =  require('../../../../src/doctor/models/schedule/schedule');
describe('Schedule  API', function () {
    describe('When schedule job with 5 seconds', function () {
        it('should execute every 5 seconds', function () {
            let schedule = new Schedule();
            schedule.cronSchedule('5 * * * * *', function () {
                console.log('execute at: ' + new Date());
            });
        });
    });
});