Ver código fonte

增加API版本控制过滤器;SQL分类型存放;APIv2重新整理及SDK

Sand 8 anos atrás
pai
commit
b88232fcfd

+ 4 - 25
src/server/app.js

@ -13,25 +13,12 @@ let log = require('./util/log');
let redis = require('redis');
// 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);
// pages
let index = require('./controllers/index');
let socket = require('./controllers/socket');
// endpoints
let application = require('./endpoints/application.endpoint');
let users = require('./endpoints/users.endpoint');
let groups = require('./endpoints/groups.endpoint');
let chats = require('./endpoints/chats.endpoint');
let sessions = require('./endpoints/session.endpoint');
let management = require('./endpoints/management.endpoint');
// handlers
let SocketHandler = require('./handlers/socket.handler');
let UrlInitializer = require('./endpoints/url.initializer');
// initialize express application
let app = express();
@ -49,17 +36,9 @@ app.use(bodyParser.urlencoded({extended: true}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// pages
app.use(PAGES.Home.Index, index);
app.use(PAGES.Socket.Index, socket);
// setup rest endpoints
app.use(APIv1.Application.Base, application);
app.use(APIv1.Chats.Base, chats);
app.use(APIv1.Users.Base, users);
app.use(APIv1.Groups.Base, groups);
app.use(APIv1.Management.Base, management);
app.use(APIv1.Sessions.Base,sessions);
// web pages and endpoint
UrlInitializer.initWebPages(app);
UrlInitializer.initRestApi(app);
// error handler, only handle the sync call exception
app.use(function (err, req, res, next) {

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

@ -0,0 +1,47 @@
/**
 * URL初始化。
 *
 * author: Sand
 * since: 12/23/2016
 */
"use strict";
let PAGES = require('../include/endpoints').PAGES;
let APIv2 = require('../include/endpoints').APIv2;
// pages
let index = require('../controllers/index');
let socket = require('../controllers/socket');
// endpoints
let application = require('../endpoints/v2/application.endpoint');
let users = require('../endpoints/v2/user.endpoint');
let sessions = require('../endpoints/v2/session.endpoint');
let topics = require('../endpoints/v2/topic.endpoint');
let management = require('../endpoints/v2/management.endpoint');
class UrlInitializer{
    constructor(){
    }
    static initRestApi(app){
        app.use(APIv2.Application.Base, application);
        app.use(APIv2.Management.Base, management);
        app.use(APIv2.Users.Base, users);
        app.use(APIv2.Sessions.Base,sessions);
        app.use(APIv2.Topics.Base,topics);
        // TODO: 过滤APIv1接口调用
        app.user(function (url) {
        });
    }
    static initWebPages(app){
        app.use(PAGES.Home.Index, index);
        app.use(PAGES.Socket.Index, socket);
    }
}
module.exports = UrlInitializer;

+ 71 - 0
src/server/endpoints/v2/topic.endpoint.js

@ -0,0 +1,71 @@
/**
 * 会话接口。
 *
 * author: Sand
 * since: 12/15/2016
 */
"use strict";
let express = require('express');
let router = express.Router();
let http = require('http');
let log = require('../util/log.js');
let ObjectUtil = require("../util/object.util.js");
let ControllerUtil = require('../util/controller.util');
let Topics = require('../models/sessions/topics');
const APIv1 = require('../include/endpoints').APIv1;
/**
 * 获取用户的聊天列表
 */
router.get(APIv1.Topics.Topics,function(req,res){
    let topicId = req.query.topicId;
    let topic = new Topics();
    ControllerUtil.regModelEventHandler(topic, res);
    topic.getTopicMessages(topicId);
});
router.post(APIv1.Topics.StartTopic,function(req,res){
    let data = req.body;
    //    data.topicName ="topicstest1";
    //    data.topicId ="12132312";
    //    data.healthDoctor ="1";
    //    data.doctor ="2";
    //    data.message ={description:"咨询:猥琐大叔大",title:"测试咨询",img:"img/sada.jpg",patient:"topicpatient",patientName:"甘宁"};
    //if (!ObjectUtil.isJsonObject(data)) {
    //    throw {httpStatus: 406, message: 'Problems parsing JSON.'};
    //}
    //if (!ObjectUtil.isJsonObject(data)) {
    //    throw {httpStatus: 406, message: 'Problems parsing JSON.'};
    //}
    //let testing = ObjectUtil.fieldsCheck(data.message, "description", "title", "img", "patient","patientName");
    //if (!testing.description) {
    //    throw {httpStatus: 406, message: "miss message.description"};
    //}
    //if (!testing.title) {
    //    throw {httpStatus: 406, message:"miss message.title"};
    //}
    //if (!testing.patient) {
    //    throw {httpStatus: 406, message:"miss message.patient"};
    //}
    //if (!testing.patientName) {
    //    throw {httpStatus: 406, message:"miss message.patientName"};
    //}
    let topic = new Topics();
    ControllerUtil.regModelEventHandler(topic, res);
    topic.createTopics(data.topicName,data.topicId,data.message.patient,data.healthDoctor,data.doctor,data.message);
});
router.get(APIv1.Topics.EndTopic,function(req,res){
    let data = req.body;
    let endUser = data.endUser;
    let endUserName = data.endUserName;
    let topicId = data.topicId
    let topic = new Topics();
    ControllerUtil.regModelEventHandler(topic, res);
    topic.endTopic(topicId,endUser,endUserName);
});
module.exports = router;

+ 5 - 0
src/server/include/commons.js

@ -97,6 +97,11 @@ const REDIS_KEY_REPLACER = "{id}";
exports.REDIS_KEY_REPLACER = REDIS_KEY_REPLACER;
exports.SESSION_USER_STATUS={
    "ONLINE":"0",
    "OTHER":"1"
}
exports.REDIS_KEYS = {
    Users: "users:",

+ 0 - 68
src/server/include/endpoints.js

@ -1,71 +1,3 @@
/**
 * REST API v1,以端点的形式提供。
 */
const APIv1 = {
    Application: {
        Base: '/api/v1/application',
        BadgeNo: '/badge_no'
    },
    Chats: {
        Base: "/api/v1/chats",
        List: "/list",
        ListWithPatient: "/list/patient",
        ListWithDoctor: "/list/doctor",
        MsgAmount: "/msg/amount",
        Recent: '/recent',
        SearchAboutPatient: '/search/patient',
        SearchAboutPatientList: '/search/patient/list',
        SearchAbountPatientMore: '/search/patient/all',
        SearchAboutDoctor: '/search/doctor',
        SearchAboutDoctorList: '/search/doctor/list',
        SearchAbountDoctorContentDetail: '/search/doctor/content/list',
        // 所有未读消息数
        UnreadMsgCount: '/unread_count',
        Message: '/message',                    // 单条消息
        // 私信
        PM: '/pm',
        PMUnread: '/pm/unread',
        PMUnreadCount: '/pm/unread/count',
        PMStats: '/pm/statistic',
        PMFinished: '/pm/finished',             // 当前会话是否已经结束
        // 组信
        GM: '/gm',
        GMUnread: '/gm/unread',
        GMUnreadCount: '/gm/unread/count',
        GMStats: '/gm/statistic',
        //系统消息
        SM: '/sm',
        TEST: '/test'
    },
    Users: {
        Base: '/api/v1/users',
        Login: '/login',
        Logout: '/logout',
        User: '/:user_id',
        UserStatus: '/:user_id/status'
    },
    Groups: {
        Base: '/api/v1/groups',
        Members: '/:group_id/members',
        MembersAvatar: '/member/avatars'
    },
    Management: {
        Base: '/api/v1/management',
        Health: '/health',
        DbStatus: '/db'
    }
};
/**
 * REST API v2,以端点的形式提供。
 */

+ 0 - 7
src/server/models/sessions/participants.js

@ -115,13 +115,6 @@ class Participants extends RedisModel {
        return ParticipantRepo.saveParticipantsToMysql(session_id, users);
    }
    /**
     * MUC成员创建
     * @param users
     */
    createMUCParticipants(users) {
        return true;
    }
    /**
     * 移除成员

+ 51 - 4
src/server/models/sessions/sessions.js

@ -36,11 +36,11 @@ class Sessions extends RedisModel {
     * @param type 会话类型
     * @param users 会话成员
     */
    createSession(sessionId, name, type, users) {
    createSession(sessionId, name, type, users,handler) {
        let self = this;
        let _super = super.makeRedisKey;
        users = eval("["+users+"]")[0];
        if (type == 2) {//P2P消息用hash校验
        if (type == config.sessionConfig.P2P) {//P2P消息用hash校验
            var userArray=[];
            for(var key in users){
                userArray.push(key);
@ -73,7 +73,11 @@ class Sessions extends RedisModel {
                    messages.content = "";
                    messages.contentType = "1";
                    self.updateLastContent(session_key, type, name, messages);
                    ModelUtil.emitData(self.eventEmitter, {"status": 200, "msg": "session create success!"});
                    if(config.sessionConfig.MUC==type){
                        handler(true);
                    }else {
                        modelUtil.emitData(self.eventEmitter, {"status": 200, "msg": "session create success!"});
                    }
                    self.saveSessionToMysql(sessionId, name, type, createDate);
                    participants.saveParticipantsToMysql(sessionId, users); //创建session成员到数据库
                }
@ -322,7 +326,50 @@ class Sessions extends RedisModel {
            }
        })
    }
    /**
     * 保存消息
     *
     * @param message
     * @param sessionId
     */
    saveMessageByTopic(message, sessionId,handler) {
        let self = this;
        let messages = new Messages();
        let participants = new Participants();
        let session_key = super.makeRedisKey(RedisKeys.Session, sessionId);
        let message_id = mongoose.Types.ObjectId().toString();
        let session_type = 0;
        let name = "";
        participants.existsParticipant(sessionId, message.senderId, function (err,res) {
            //校验发送成员是都在讨论组
            if (res) {
                redis.hmgetAsync(session_key, ["type", "name"]).then(function (res) {
                    session_type = res[0];
                    name = res[1];
                    if (!session_type || !name) {
                        log.error("session is error for key " + session_key);
                        throw "session is not found";
                    }
                }).then(function (res) {
                    //更新消息相关
                    return messages.saveMessageForRedis(message_id, sessionId, message);
                }).then(function (res) {
                    //更新session的最后一条聊天记录
                    return self.updateLastContent(session_key, session_type, name, message);
                }).then(function (res) {
                    //操作mysql数据库
                    messages.saveMessageToMysql(message, session_type, message_id, sessionId);
                    //返回数据给前端。
                    handler(null,message_id)
                    //消息推送
                }).catch(function (res) {
                    handler(res,message_id)
                })
            } else {
                handler( "用户不在此会话当中!",message_id);
            }
        })
    }
    /**
     * 置顶操作
     */

+ 143 - 37
src/server/models/sessions/topics.js

@ -5,11 +5,17 @@
let RedisClient = require('../../repository/redis/redis.client.js');
let redisClient = RedisClient.redisClient();
let redis = redisClient.connection;
let RedisModel = require('./../redis.model.js');
let modelUtil = require('../../util/modelUtil');
let modelUtil = require('../../util/model.util');
let Participants = require("./participants");
let Sessions = require("./sessions");
const RedisKey = require('../../include/commons').RedisKey;
let log = require('../../util/log.js');
let TopicsRepo = require('../../repository/mysql/topics.repo');
let configFile = require('../../include/commons').CONFIG_FILE;
let config = require('../../resources/config/' + configFile);
const RedisKey = require('../../include/commons').REDIS_KEYS;
const UserStatus = require('../../include/commons').SESSION_USER_STATUS;
class Topics extends RedisModel {
@ -17,59 +23,159 @@ class Topics extends RedisModel {
        super();
    }
    /**
     * 根据topicId获取对应的Topics
     * @param topicId
     */
    getTopicsById(topicId){
        
    }
    /**
     * 根据用户ID获取用户的Topics列表
     * @param UserId
     */
    getUserTopics(UserId){
    }
    /**
     * 根据topicId获取对应的消息
     * @param topicId
     */
    getTopicMessages(topicId){
    getTopicMessages(topicId,page,pagesize) {
        let self = this;
        let topic_key = super.makeRedisKey(RedisKey.Topic, topicId);
        let _super = super.makeRedisKey;
        redis.hgetallAsync(topic_key).then(function (topic) {
            let message_time_key = _super(RedisKey.MessagesByTimestamp, topic.session_id);
            let message_key = _super(RedisKey.Messages, topic.session_id);
            //倒序取出所有的消息ID
            let create_time =topic.create_time;
            let end_time =topic.end_time;
            if(!end_time){
                end_time = new Date().getTime();
            }
            redis.zrevrangebyscoreAsync(message_time_key,end_time,create_time).then(function (messages) {
                //取出消息实例
                redis.hmgetAsync(message_key,messages).then(function(res){
                    modelUtil.emitData(self.eventEmitter,res);
                })
            })
        })
    }
    /**
     *
     * @param topicName 发起议题的名称
     * @param patient 发起议题的患者
     * @param doctor 参与的医生
     * @param messages 发送的消息对象{quesion:"",img:""}图片多个用逗号隔开
     * @param messages 发送的消息对象{description:"",title:"",img:"",patient:"",patientName:""}图片多个用逗号隔开
     */
    createTopics(topicName,patient,healthDoctor,doctor,messages){
    createTopics(topicName,topicId,patient,healthDoctor,doctor,messages){
        let self = this;
        let participants = new Participants();
        //MUC模式中sessionID就是患者ID
        let topics_key  = super.makeRedisKey(RedisKey.Topics,patient);
        let topic_key  = super.makeRedisKey(RedisKey.Topic,topicId);
        let sessions = new Sessions();
        let participants  = new Participants();
        //从数据库中获取sessionId
        participants.getSessionIdByParticipants(patient,healthDoctor,function(err,res){
            if (err) {
                modelUtil.emitDbError(self.eventEmitter, "Get group member's avatar list failed", err);
                return;
        let date = new Date();
        redis.zaddAsync(topics_key, date.getTime(), topicId).then(function(res){
            redis.hmsetAsync(topic_key,"name",topicName,"end_by","","session_id",patient,"create_time",date.getTime(),"end_time","","description",messages.description).then(function(res){
                sessions.getSessions(patient,function(err,res){
                    //已经存在对应的会话更新全科为旁听
                    if(res&&res.length>0){
                        participants.updateUser(patient,doctor,UserStatus.OTHER);
                        callbegin();
                    }else{//不存在创建SESSION
                        var users={};
                        users[patient]=UserStatus.ONLINE;
                        users[healthDoctor]=UserStatus.ONLINE;
                        users[doctor]=UserStatus.OTHER;
                        sessions.createSession(patient,messages.patientName,config.sessionConfig.MUC,JSON.stringify(users),function(res){
                            if(res){
                                callbegin();
                            }
                        });
                    }
                })
            })
        })
        /**
         * 开始消息发送
         */
        function callbegin(){
            let msg ={};
            msg.senderId = messages.patient;
            msg.senderName = messages.patientName;
            msg.contentType = 6;
            msg.content ="开始咨询"
            msg.timestamp=date;
            sessions.saveMessageByTopic(msg,patient,function(err,msgId){
                if(err){
                    modelUtil.emitData(self.eventEmitter,err);
                }else{
                    self.saveTopicsToSql(topicName,topicId,patient,msgId,date);
                    callBeginMsg();
                }
            })
        }
        /**
         * 发送求助内容
         */
        function callBeginMsg(){
            let msg ={};
            msg.senderId = messages.patient;
            msg.senderName = messages.patientName;
            msg.contentType = 1;
            msg.content =messages.description;
            msg.timestamp = new Date();
            sessions.saveMessageByTopic(msg,patient,function(err,msgId){
                log.info("begin send"+messages.description);
            })
            if(messages.img){
                let imgs = messages.img.split(",");
                for(var j in imgs){
                    let msgimg ={};
                    msgimg.senderId = messages.patient;
                    msgimg.senderName = messages.patientName;
                    msgimg.contentType =2;
                    msgimg.content =imgs[j];
                    msgimg.timestamp = new Date();
                    sessions.saveMessageByTopic(msgimg,patient,function(err,msgId){
                        log.info("begin send"+imgs[j]);
                    })
                }
            }
            //如果存在的情况直接返回
            if(res){
                return res;
            }else{
            //不存在则去创建一个session
            modelUtil.emitData(self.eventEmitter,"创建成功!");
        }
    }
            }
    saveTopicsToSql(topicName,topicId,sessionId,messageId,date){
        TopicsRepo.saveTopic(topicName,topicId,sessionId,messageId,date);
    }
    /**
     * 结束议题
     * @param topicId
     * @param endUser
     */
    endTopic(topicId,endUser,endUserName){
        let endDate = new Date();
        let self = this;
        let topic_key  = super.makeRedisKey(RedisKey.Topic,topicId);
        redis.hmsetAsync(topic_key,"end_time",endDate.getTime(),"end_by",endUser).then(function (res) {
            redis.hgetallAsync(topic_key).then(function(topic){
                callEnd(topic.session_id);
            })
        })
        /**
         * 结束消息发送
         */
        function callEnd(sessionId){
            let msg ={};
            msg.senderId = endUser;
            msg.senderName = endUserName;
            msg.contentType = 7;
            msg.content =endUserName+"结束了咨询"
            msg.timestamp = new Date();
            let sessions = new Sessions();
            sessions.saveMessageByTopic(msg,sessionId,function(err,msgId){
                if(err){
                    modelUtil.emitData(self.eventEmitter,err);
                }else{
                    modelUtil.emitData(self.eventEmitter,"结束成功!");
                    TopicsRepo.endTopic(topicId,endUser,msg.date,msgId);
                }
            })
        }
    }
}

+ 10 - 0
src/server/repository/mysql/participant.repo.js

@ -149,6 +149,16 @@ class ParticipantRepo {
            }
        });
    }
    static getPatientOpenid(code, handler) {
        var sql = "select openid from patients where code = ? ";
        ImDb.execQuery({
            "sql": sql,
            "args": [code],
            "handler": handler
        });
    }
}
module.exports = ParticipantRepo;

+ 57 - 0
src/server/repository/mysql/topics.repo.js

@ -0,0 +1,57 @@
/**
 * 搜索功能。
 */
"use strict";
let ImDb = require('../mysql/db/im.db');
let DbUtil = require('../../util/db.util');
let log = require('../../util/log.js');
const DB_TABLES = require('../../include/commons').DB_TABLES;
class TopicRepo {
    constructor() {
    }
    /**
     *
     * 保存议题
     * @param sessionId
     */
    static saveTopic(topicName,topicId,sessionId,messageId,date) {
        let sql = "insert into topics (id,session_id,name,create_time,start_message_id) VALUES (?,?,?,?,?)";
        ImDb.execQuery({
            "sql": sql,
            "args": [topicId,sessionId,topicName,date,messageId],
            "handler": function (err, res) {
                if (err) {
                    log.error("saveTopic is fail error: " + err+"messageId:"+messageId);
                }else{
                    log.info("saveTopic is success" );
                }
            }
        });
    }
    /**
     * 结束议题
     *
     * @param sessionId
     * @param handler
     */
    static endTopic(topicId,endUser,date,messageId) {
        let sql = "update topics set end_by = ?,end_time=?,end_message_id=? where  id = ?";
        ImDb.execQuery({
            "sql": sql,
            "args": [endUser,date,messageId,topicId],
            "handler": function (err, res) {
                if (err) {
                    log.error("endTopic is fail error: " + err);
                }else{
                    log.info("endTopic is success" );
                }
            }
        });
    }
}
module.exports = TopicRepo;

+ 5 - 1
src/server/resources/config/config.dev.js

@ -72,7 +72,11 @@ let sessionConfig = {
    maxMessageTimespan: 7 * 24 * 3600,      // 会话缓存的最大时间跨度
    expireTime: 3 * 60 * 60 * 1000,         // 会话过期时间,以毫秒计
    expireSessionCleanCount: 10             // 每次清理多少个过期会话
    expireSessionCleanCount: 10,             // 每次清理多少个过期会话
    MUC:"1", //session模式配置
    P2P:"2",
    TEAM:"3"
};
exports.app = 'IM.Server';

src/server/resources/schema/ichat_schema.1.2.5.sql → src/server/resources/schema/ichat_1.2.5_schema.sql


+ 1 - 0
src/server/resources/schema/ichat_1.2.8_data_migration.sql

@ -0,0 +1 @@
/* 1.2.5->1.2.8数据迁移语句 */

src/server/resources/schema/ichat_schema.1.2.8.sql → src/server/resources/schema/ichat_1.2.8_table_schema.sql


src/server/resources/schema/ichat_schema.1.2.8-views.sql → src/server/resources/schema/ichat_1.2.8_view_schema.sql