Browse Source

增加用户状态

Sand 8 years ago
parent
commit
dcc9fff296

+ 27 - 21
src/server/include/commons.js

@ -7,8 +7,8 @@
 *
 * @type {string}
 */
 "use strict";
 
"use strict";
let configFile = "config.";
if (process.env.IM_PROFILE === "prod") {
@ -79,25 +79,31 @@ exports.MAX_INT = 9007199254740992;
exports.DEFAULT_PAGE_SIZE = 100;
/**
 * Redis Key列表。
 * Redis Key列表与占位符。
 */
let redis_model_key ="{key}";
const REDIS_KEY_REPLACER = "{id}";
exports.REDIS_KEY_REPLACER = REDIS_KEY_REPLACER;
exports.REDIS_KEYS = {
  Users: "users:",
  Sessions:"sessions:",
  Session:"sessions:"+redis_model_key,
  Messages:"sessions:"+redis_model_key+":messages",
  MessagesTimestamp:"sessions:"+redis_model_key+":messages_by_timestamp",
  Topics:"sessions:"+redis_model_key+":topics",
  Topic:"topics:"+redis_model_key
    Users: "users:",
    User: "users:" + REDIS_KEY_REPLACER,
    UserStatus: "users:" + REDIS_KEY_REPLACER + ":status",
    Sessions: "sessions:",
    Session: "sessions:" + REDIS_KEY_REPLACER,
    Messages: "sessions:" + REDIS_KEY_REPLACER + ":messages",
    MessagesTimestamp: "sessions:" + REDIS_KEY_REPLACER + ":messages_by_timestamp",
    Topics: "sessions:" + REDIS_KEY_REPLACER + ":topics",
    Topic: "topics:" + REDIS_KEY_REPLACER
};
exports.REDIS_MODEL_KEY =redis_model_key;
exports.IM_DB={
    "P2PMSG":"p2p_messages",
    "MUCMSG":"muc_messages",
    "GROUPMSG":"group_messages",
    "PARTICIPANTS":"participants",
    "SESSIONS":"sessions",
    "TOPICS":"topics"
}
exports.IM_DB = {
    "P2PMSG": "p2p_messages",
    "MUCMSG": "muc_messages",
    "GROUPMSG": "group_messages",
    "PARTICIPANTS": "participants",
    "SESSIONS": "sessions",
    "TOPICS": "topics"
};

+ 13 - 3
src/server/models/base.model.js

@ -9,17 +9,27 @@
"use strict";
let EventEmitter = require('events').EventEmitter;
let RedisModel = require('./redis.model.js');
class BaseModel extends  RedisModel{
class BaseModel{
    constructor() {
        super();
        this._eventEmitter = new EventEmitter();
    }
    /**
     * 获取模型的事件触发器。
     *
     * @returns {EventEmitter|*}
     */
    get eventEmitter(){
        return this._eventEmitter;
    }
    /**
     * 监听事件。
     *
     * @param event
     * @param handler
     */
    on(event, handler){
        this._eventEmitter.on(event, handler);
    }

+ 19 - 11
src/server/models/redis.model.js

@ -1,23 +1,31 @@
/**
 * REIDS键值对模型基类。
 *
 * 模型,提供事件抽象。
 * Redis模型基类,提供基础模型操作。
 *
 * author:linz
 * since: 2016.12.13
 */
"use strict";
let BaseModel = require('./base.model');
let log = require("../util/log.js");
const RedisKeys = require('../include/commons').REDIS_KEYS;
const RedisModelKey = require('../include/commons').REDIS_MODEL_KEY;
class RedisModel {
    constructor() {
const RedisKeyReplacer = require('../include/commons').REDIS_KEY_REPLACER;
class RedisModel extends BaseModel {
    constructor() {
        super();
    }
    _getKey(redisKey, keyValue) {
        if (redisKey.indexOf(RedisModelKey) >= 0) {
            return redisKey.replace(RedisModelKey, keyValue);
        }else{
    /**
     * 替换Redis Key模板中的占位符,生成有效的Key。
     *
     * @param redisKey
     * @param keyValue
     */
    makeRedisKey(redisKey, keyValue) {
        if (redisKey.indexOf(RedisKeyReplacer) >= 0) {
            return redisKey.replace(RedisKeyReplacer, keyValue);
        } else {
            log.warn("redisModelKey is not found");
            return redisKey;
        }

+ 3 - 3
src/server/models/sessions/sessions.js

@ -80,9 +80,9 @@ class Sessions extends BaseModel {
    saveMessageBySession(message,sessionId) {
        let self = this;
        let message_key = super._getKey(this._msg_key,sessionId);
        let message_timestamp_key = super._getKey(this._ms_key_timestamp,sessionId);
        let session_key = super._getKey(this._session_key,sessionId);
        let message_key = super.makeRedisKey(this._msg_key,sessionId);
        let message_timestamp_key = super.makeRedisKey(this._ms_key_timestamp,sessionId);
        let session_key = super.makeRedisKey(this._session_key,sessionId);
        let message_id = mongoose.Types.ObjectId().toString();
        let session_type = 0;
        let name = 0;

+ 8 - 4
src/server/models/user/doctor.js

@ -6,7 +6,7 @@
let log = require("../../util/log.js");
let getui = require('getui');
let BaseModel = require('./../base.model');
let RedisModel = require('./../redis.model');
let Schedule = require("./../schedule/schedule.js");
let doctorRepo = require('../../repository/mysql/doctor.repo.js');
let gmRepo = require('../../repository/mysql/group.msg.repo');
@ -21,11 +21,15 @@ const CONTENT_TYPES = require('../../include/commons').CONTENT_TYPE;
const PLATFORMS = require('../../include/commons').PLATFORM;
const MAX_INT = require('../../include/commons').MAX_INT;
class Doctor extends BaseModel {
    constructor() {
class Doctor extends RedisModel {
    constructor(doctorId) {
        super();
        this.$id = 'doctor';
        this._id = doctorId;
    }
    get id(){
        return this._id;
    }
    /**

+ 22 - 42
src/server/models/user/patient.js

@ -7,51 +7,31 @@ let configFile = require('../../include/commons').CONFIG_FILE;
let config = require('../../resources/config/' + configFile);
let log = require("../../util/log.js");
let BaseModel = require('../base.model');
let patientRepo = require('../../repository/mysql/patient.repo');
let statsRepo = require("../../repository/mysql/stats.msg.repo.js");
let pmRepo = require('../../repository/mysql/private.msg.repo');
let RedisModel = require('../redis.model');
let objectUtil = require("../../util/objectUtil.js");
let modelUtil = require('../../util/modelUtil');
let wechatUtil = require('../../util/wechatUtil');
let clientCache = require('../socket.io/client.cache').clientCache();
let Doctor = require('../../models/user/doctor');
let DoctorRepo = require('../../repository/mysql/doctor.repo');
let GroupRepo = require('../../repository/mysql/group.repo');
let PatientRepo = require('../../repository/mysql/patient.repo');
let StatsRepo = require("../../repository/mysql/stats.msg.repo");
let PmRepo = require('../../repository/mysql/private.msg.repo');
const CONTENT_TYPES = require('../../include/commons').CONTENT_TYPE;
let clientCache = require('../socket.io/client.cache').clientCache();
let DoctorRepo = require('../../repository/mysql/doctor.repo');
let groupRepo = require('../../repository/mysql/group.repo');
let wechatUtil = require('../../util/wechatUtil');
class Patient extends BaseModel {
    constructor() {
class Patient extends RedisModel {
    constructor(patientId) {
        super();
    }
    /**
     * 是否为患者代码。
     *
     * @param code
     * @param passedCallback 测试通过回调
     * @param failedCallback 测试失败回调
     */
    static isPatientCode(code, passedCallback, failedCallback) {
        patientRepo.isPatientCode(code, function (err, result) {
            if (err) {
                log.error('Send message to patient failed: ', err);
                return;
            }
        this._id = patientId;
    }
            if (result[0].c > 0) {
                passedCallback();
            } else {
                failedCallback();
            }
        });
    get id(){
        return this._id;
    }
    /**
@ -66,14 +46,14 @@ class Patient extends BaseModel {
        let self = this;
        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) {
        PmRepo.save(message.to, message.from, message.contentType, tempContent, function (err, result) {
            if (err) {
                modelUtil.emitDbError(self.eventEmitter, 'Save private message failed', err);
                return;
            }
            // 结束网络连接,后续操作继续执行
            pmRepo.findOnePatientMessage(result.insertId, function (err, msg) {
            PmRepo.findOnePatientMessage(result.insertId, function (err, msg) {
                if (err) {
                    modelUtil.emitDbError(self.eventEmitter, 'Save private message success, but return last message failed', err);
                    return;
@ -95,12 +75,12 @@ class Patient extends BaseModel {
            });
            // 更新自身的聊天统计信息
            statsRepo.updatePrivateChatSummary(message.from, message.to, message.from, message.contentType, message.content, function (err, result) {
            StatsRepo.updatePrivateChatSummary(message.from, message.to, message.from, message.contentType, message.content, function (err, result) {
                if (err) log.error(err);
            });
            // 更新对端的聊天统计信息
            statsRepo.updatePrivateChatSummary(message.to, message.from, message.from, message.contentType, message.content, function (err, result) {
            StatsRepo.updatePrivateChatSummary(message.to, message.from, message.from, message.contentType, message.content, function (err, result) {
                if (err) log.error(err);
            });
        });
@ -121,7 +101,7 @@ class Patient extends BaseModel {
            self.sendConsultWechatReplyTempMsg(message);
            return;
        }
        groupRepo.getOnGroupMsg(message.msgId, function (err, result) {
        GroupRepo.getOnGroupMsg(message.msgId, function (err, result) {
            if (err) {
                modelUtil.emitDbError(self.eventEmitter, "get group msg info failed", err);
            }
@ -184,7 +164,7 @@ class Patient extends BaseModel {
        }
        // 查询居民openid
        patientRepo.getPatientOpenid(message.to, function (err, result) {
        PatientRepo.getPatientOpenid(message.to, function (err, result) {
            if (err) {
                modelUtil.emitDbError(self.eventEmitter, "get patient openid failed", err);
                return;
@ -203,7 +183,7 @@ class Patient extends BaseModel {
                        var name = result[0].name;
                        if (message.group) {
                            groupRepo.getGroupConsultInfo(message.group, function (err, result) {
                            GroupRepo.getGroupConsultInfo(message.group, function (err, result) {
                                if (err) {
                                    modelUtil.emitDbError(self.eventEmitter, "get patient and doctor consult info failed", err);
                                    return;
@ -217,7 +197,7 @@ class Patient extends BaseModel {
                            });
                        } else {
                            // 查询医生与居民对应的咨询信息
                            patientRepo.getPatientDoctorConsult(message.to, message.from, function (err, result) {
                            PatientRepo.getPatientDoctorConsult(message.to, message.from, function (err, result) {
                                if (err) {
                                    modelUtil.emitDbError(self.eventEmitter, "get patient and doctor consult info failed", err);
                                    return;

+ 41 - 17
src/server/models/user/users.js

@ -6,21 +6,25 @@
 */
"use strict";
let BaseModel = require('../base.model');
let RedisKeys = require('../../include/commons').REDIS_KEYS;
const RedisKeys = require('../../include/commons').REDIS_KEYS;
let RedisModel = require('../redis.model');
let DoctorRepo = require('../../repository/mysql/doctor.repo');
let PatientRepo = require('../../repository/mysql/patient.repo');
let UserStatusRepo = require('../../repository/mysql/user.status.repo');
let Doctor = require('./doctor');
let Patient = require('./patient');
let RedisClient = require('../../repository/redis/redis.client');
let redisConn = RedisClient.redisClient().connection;
let redisConn = RedisClient.redisClient().connection;
let async = require('async');
let bearcat = require('bearcat');
let log = require('../../util/log');
let objectUtil = require('../../util/objectUtil');
var imDb = require('../../repository/mysql/db/im.db');
class Users extends BaseModel {
class Users extends RedisModel {
    constructor() {
        super();
@ -43,10 +47,10 @@ class Users extends BaseModel {
                });
            },
            // get from mysql
            function (isPatientId, callback) {
            function (isPatientId) {
                let repoProto = isPatientId ? PatientRepo : DoctorRepo;
                repoProto.findOne(userId, function (err, res) {
                    let user = {role: isPatientId ? 'patient' : 'doctor'};
                    let user = isPatientId ? new Doctor() : new Patient();
                    if(res.length > 0){
                        user.name = res[0].name;
                        user.sex = res[0].sex;
@ -64,19 +68,39 @@ class Users extends BaseModel {
     * 获取用户状态。
     *
     * @param userId
     * @param callback
     * @param outCallback
     */
    getUserStatus(userId, callback){
    }
    getUserStatus(userId, outCallback){
        let self = this;
        async.waterfall([
            // get from redis
            function (callback) {
                let userStatusKey = self.makeRedisKey(RedisKeys.UserStatus, userId);
                redisConn.hgetallAsync(userStatusKey).then(function (res) {
                   if(res === null){
                       callback(null);
                   } else {
                       outCallback(null, res);
                   }
                });
            },
            // get from MySQL
            function () {
                UserStatusRepo.getUserStatus(userId, function (err, res) {
                    let userStatus = null;
                    if(res.length > 0){
                        userStatus = {};
                        userStatus.platform = res[0].platform;
                        userStatus.token = res[0].token;
                        userStatus.client_id = res[0].client_id;
                        userStatus.app_in_bg = res[0].app_in_bg;
                        userStatus.last_login_time = res[0].last_login_time;
                    }
    /**
     * 用户是否在线。
     *
     * @param userId
     * @param callback
     */
    isUserOnline(userId, callback) {
                    outCallback(null, userStatus);
                });
            }
        ]);
    }
    /**

+ 0 - 18
src/server/repository/mysql/doctor.repo.js

@ -22,24 +22,6 @@ class DoctorRepo {
        });
    };
    /**
     * 判断用户是否存在。
     *
     * @param user
     * @param handler
     */
    static isExist(doctorId, handler) {
    };
    static getUserStatus(doctorId, handler) {
        imDb.execQuery({
            "sql": "SELECT platform,token,client_id,is_online,status from user WHERE user_id = ?",
            "args": [userId],
            "handler": handler
        });
    };
    static login(doctorId, token, client_id, platform, handler) {
        imDb.execQuery({
            "sql": "INSERT INTO user (user_id,token,client_id,platform,is_online,status) VALUES (?,?,?,?,1,1) ON" +

+ 28 - 0
src/server/repository/mysql/user.status.repo.js

@ -0,0 +1,28 @@
/**
 * 用户状态库。
 *
 * author: Sand
 * since: 12/14/2016
 */
"use strict";
var imDb = require("./db/im.db.js");
var log = require('../../util/log');
class UserStatusRepo {
    constructor() {
    }
    static getUserStatus(userId, handler) {
        let sql = "SELECT platform, token, client_id, app_in_bg, last_login_time " +
            "FROM user_status WHERE user_id = ?";
        imDb.execQuery({
            "sql": sql,
            "args": [userId],
            "handler": handler
        });
    };
}
module.exports = UserStatusRepo;

+ 17 - 2
src/server/resources/schema/ichat_schema.1.2.8.sql

@ -26,6 +26,9 @@ DROP TABLE IF EXISTS `participants` CASCADE
DROP TABLE IF EXISTS `sessions` CASCADE
;
DROP TABLE IF EXISTS `user_status` CASCADE
;
/* Create Tables */
CREATE TABLE `topics`
@ -82,9 +85,9 @@ CREATE TABLE `muc_messages`
CREATE TABLE `participants`
(
	`session_id` VARCHAR(50) NOT NULL COMMENT '会话ID',
	`session_id` VARCHAR(50) NOT NULL COMMENT '会话ID。ID结构:以患者ID+最大次数',
	`participaint_id` VARCHAR(50) NOT NULL COMMENT '参与者ID',
	`member_type` INTEGER NOT NULL COMMENT '成员类型,1医生,2患者',
	`participaint_role` INTEGER COMMENT '参与者角色,MUC模式中的主持人/普通参与者',
	`receiving` TINYINT COMMENT '当前是否正在接收',
	CONSTRAINT `PK_participants` PRIMARY KEY (`session_id`,`participaint_id`)
) COMMENT='会话参与者'
@ -100,6 +103,18 @@ CREATE TABLE `sessions`
) COMMENT='会话'
;
CREATE TABLE `user_status`
(
	`user_id` VARCHAR(50) NOT NULL COMMENT '用户ID',
	`platform` TINYINT COMMENT '平台,0为iOS,1为安卓',
	`token` VARCHAR(100) COMMENT '个推Token',
	`client_id` VARCHAR(100) COMMENT '客户端ID',
	`app_in_bg` TINYINT COMMENT 'App是否处于后台状态',
	`last_login_time` TIMESTAMP(0) COMMENT '最后登录 时间',
	CONSTRAINT `PK_user_status` PRIMARY KEY (`user_id`)
) COMMENT='用户状态表'
;
/* Create Primary Keys, Indexes, Uniques, Checks */
ALTER TABLE `topics` 

+ 29 - 0
src/server/resources/schema/user_status.sql

@ -0,0 +1,29 @@
/* ---------------------------------------------------- */
/*  Generated by Enterprise Architect Version 12.0 		*/
/*  Created On : 14-Dec-2016 4:12:07 PM 				*/
/*  DBMS       : MySql 						*/
/* ---------------------------------------------------- */
SET FOREIGN_KEY_CHECKS=0
/* Drop Tables */
DROP TABLE IF EXISTS `user_status` CASCADE
;
/* Create Tables */
CREATE TABLE `user_status`
(
	`user_id` VARCHAR(50) NOT NULL COMMENT '用户ID',
	`platform` TINYINT COMMENT '平台,0为iOS,1为安卓',
	`token` VARCHAR(100) COMMENT '个推Token',
	`client_id` VARCHAR(100) COMMENT '客户端ID',
	`is_online` TINYINT COMMENT '是否在线',
	`app_in_bg` TINYINT COMMENT 'App是否处于后台状态',
	`last_login_time` TIMESTAMP(0) COMMENT '最后登录 时间',
	CONSTRAINT `PK_user_status` PRIMARY KEY (`user_id`)
) COMMENT='用户状态表'
;
SET FOREIGN_KEY_CHECKS=1

+ 13 - 1
test/server/models/user/user.Test.js

@ -32,12 +32,24 @@ describe('Users class', function () {
    });
    describe('getUser', function () {
        it('should return user with really id', function (done) {
        it('should return user info', function (done) {
            let users = new Users();
            users.getUserAsync('test00000000006').then(function (res) {
                assert.notStrictEqual(res, null);
                if(res !== null) console.log(res);
                done();
            });
        });
    });
    describe('getUserStatus', function () {
        it('should return user status', function (done) {
            let users = new Users();
            users.getUserStatusAsync('test00000000006').then(function (res) {
                assert.notStrictEqual(res, null);
                if(res !== null) console.log(res);
                done();
            });
        });