Przeglądaj źródła

增加MYSQL会话状态缓存

Sand 8 lat temu
rodzic
commit
250453bb60

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

@ -217,6 +217,7 @@ router.get(APIv2.Sessions.Messages, function (req, res) {
    }
    let sessions = new Sessions();
    ControllerUtil.regModelEventHandler(sessions, res);
    sessions.getMessages(sessionId, user, startMsgId,endMsgId,page,pagesize);
});

+ 65 - 87
src/server/models/sessions/sessions.js

@ -79,7 +79,7 @@ class Sessions extends RedisModel {
                // 保存会话及成员至MySQL中
                self.saveSessionToMysql(sessionId, name, type, createDate, function (err, res) {
                    Participants.saveParticipantsToMysql(sessionId, participantArray, function (err, res) {
                        if(err){
                        if (err) {
                            ModelUtil.emitError(self.eventEmitter, err.message);
                            return;
                        }
@ -152,86 +152,64 @@ class Sessions extends RedisModel {
            size = size + page;
        }
        // 倒序获取
        redis.zrevrangeAsync(userSessionKey, page, size).then(function (res) {
            let sessionList = [];
            if (res.length == 0) {
                ModelUtil.emitOK(self.eventEmitter, []);
                return;
            }
            for (let i in res) {
                callGetSessions(res[i], i == res.length - 1);
            }
            function callGetSessions(sessionId, lastOne) {
                let sessionKey = RedisModel.makeRedisKey(REDIS_KEYS.Session, sessionId);
                redis.hgetallAsync(sessionKey).then(function (session) {
                    let sessionParticipantsKey = RedisModel.makeRedisKey(REDIS_KEYS.SessionParticipants, sessionId);
                    // 对比当前用户最后一次此会话消息的时间与会话中最新的消息时间,以此判断未读消息数量
                    redis.zscoreAsync(sessionParticipantsKey, userId).then(function (lastFetchTime) {
                        callGetUnreadCount(session, sessionId, lastFetchTime, lastOne);
                    })
                }).catch(function (err) {
                    throw err;
                })
            }
        async.waterfall([
            // 获取会话ID列表
            function (callback) {
                redis.zrevrangeAsync(userSessionKey, page, size)
                    .then(function (sessionIds) {
                        if (sessionIds.length == 0) {
                            ModelUtil.emitOK(self.eventEmitter, []);
                            return;
                        }
            /**
             * 统计未读消息数。以当前时间为准。
             *
             * @param session 返回的会话列表
             * @param sessionId 当前会话ID
             * @param lastFetchTime 当前会话当前用户的最后一次时间搓
             * @param lastOne
             */
            function callGetUnreadCount(session, sessionId, lastFetchTime, lastOne) {
                let messagesByTimestampKey = RedisModel.makeRedisKey(REDIS_KEYS.MessagesByTimestamp, sessionId);
                redis.zrangebyscoreAsync(messagesByTimestampKey, lastFetchTime, (new Date().getTime()))
                    .then(function (messagetimelist) {
                        session.id = sessionId;
                        session.unread_count = messagetimelist.length;
                        callGetMyRole(session, sessionId, lastOne);
                        callback(null, sessionIds);
                    })
                    .catch(function (err) {
                        throw err;
                    });
            }
            /**
             * 获取用户在此会话中的角色。
             *
             * @param session 要返回的JSON
             * @param sessionId
             * @param lastOne
             */
            function callGetMyRole(session, sessionId, lastOne) {
                let participantsRoleKey = RedisModel.makeRedisKey(REDIS_KEYS.SessionParticipantsRole, sessionId);
                redis.hgetAsync(participantsRoleKey, userId).then(function (role) {
                    session.my_role = role;
                    callback(session, lastOne);
                })
            }
            },
            // 遍历会话
            function (sessionIds, callback) {
                let sessionList = [];
                sessionIds.forEach(function (sessionId) {
                    let sessionKey = RedisModel.makeRedisKey(REDIS_KEYS.Session, sessionId);
                    let participantsRoleKey = RedisModel.makeRedisKey(REDIS_KEYS.SessionParticipantsRole, sessionId);
                    let sessionParticipantsKey = RedisModel.makeRedisKey(REDIS_KEYS.SessionParticipants, sessionId);
            /**
             * 列表封装完毕后由此回调返回数据界面
             *
             * @param session
             * @param lastOne
             */
            function callback(session, lastOne) {
                sessionList.push(session);
                if (lastOne) {
                    ModelUtil.emitOK(self.eventEmitter, sessionList);
                }
                    redis.multi()
                        .hgetall(sessionKey)                       // 会话实体
                        .hget(participantsRoleKey, userId)         // 用户在此会话中的角色
                        .zscore(sessionParticipantsKey, userId)    // 用户在此会话中最后一次获取未读消息的时间
                        .execAsync()
                        .then(function (res) {
                            let session = res[0];
                            let role = res[1];
                            let lastFetchTime = res[2];
                            // 计算未读消息数
                            let messagesByTimestampKey = RedisModel.makeRedisKey(REDIS_KEYS.MessagesByTimestamp, sessionId);
                            redis.zcountAsync(messagesByTimestampKey, lastFetchTime, new Date().getTime())
                                .then(function (count) {
                                    sessionList.push({
                                        id: sessionId,
                                        name: session.name,
                                        create_date: session.create_date,
                                        last_content_type: session.last_content_type,
                                        last_content: session.last_content,
                                        sender_id: session.sender_id,
                                        sender_name: session.sender_name,
                                        unread_count: count,
                                        my_role: role
                                    });
                                    if (sessionId === sessionIds[sessionIds.length - 1]) {
                                        ModelUtil.emitOK(self.eventEmitter, sessionList);
                                    }
                                });
                        })
                        .catch(function (err) {
                            ModelUtil.emitError(self.eventEmitter, "Get sessions failed: " + err);
                        });
                });
            }
        }).catch(function (err) {
            ModelUtil.emitError(self.eventEmitter, {message: "Get sessions failed: " + err});
        })
        ]);
    }
    /**
@ -247,13 +225,13 @@ class Sessions extends RedisModel {
        let message_timestamp_key = RedisModel.makeRedisKey(REDIS_KEYS.MessagesByTimestamp, sessionId);
        if (!start_msg_id && !end_msg_id) {
            redis.zrevrangeAsync(message_timestamp_key, 0, 0).then(function (res) {
                if(res.length==0){
                if (res.length == 0) {
                    ModelUtil.emitOK(self.eventEmitter, res);
                    return;
                }
                start_msg_id = res[0];
                redis.zrangeAsync(message_timestamp_key, 0, 0).then(function (res) {
                    if(res.length==0){
                    if (res.length == 0) {
                        ModelUtil.emitOK(self.eventEmitter, res);
                        return;
                    }
@ -261,7 +239,7 @@ class Sessions extends RedisModel {
                    self.getMessagesByPage(sessionId, user, start_msg_id, end_msg_id, page, pagesize, function (err, res) {
                        if (err) {
                            logger.error("getMessagesByPage error" + err);
                            ModelUtil.emitError(self.eventEmitter,err,err);
                            ModelUtil.emitError(self.eventEmitter, err, err);
                        } else {
                            ModelUtil.emitOK(self.eventEmitter, res);
                        }
@ -270,7 +248,7 @@ class Sessions extends RedisModel {
            })
        } else if (!start_msg_id) {
            redis.zrevrangeAsync(message_timestamp_key, 0, 0).then(function (res) {
                if(res.length==0){
                if (res.length == 0) {
                    ModelUtil.emitOK(self.eventEmitter, res);
                    return;
                }
@ -278,7 +256,7 @@ class Sessions extends RedisModel {
                self.getMessagesByPage(sessionId, user, startMsgId, endMsgId, page, pagesize, function (err, res) {
                    if (err) {
                        logger.error("getMessagesByPage error" + err);
                        ModelUtil.emitError(self.eventEmitter,err,err);
                        ModelUtil.emitError(self.eventEmitter, err, err);
                    } else {
                        ModelUtil.emitOK(self.eventEmitter, res);
                    }
@ -286,7 +264,7 @@ class Sessions extends RedisModel {
            })
        } else if (!end_msg_id) {
            redis.zrangeAsync(message_timestamp_key, 0, 0).then(function (res) {
                if(res.length==0){
                if (res.length == 0) {
                    ModelUtil.emitOK(self.eventEmitter, res);
                    return;
                }
@ -294,7 +272,7 @@ class Sessions extends RedisModel {
                self.getMessagesByPage(sessionId, user, start_msg_id, end_msg_id, page, pagesize, function (err, res) {
                    if (err) {
                        logger.error("getMessagesByPage error" + err);
                        ModelUtil.emitError(self.eventEmitter,err,err);
                        ModelUtil.emitError(self.eventEmitter, err, err);
                    } else {
                        ModelUtil.emitOK(self.eventEmitter, res);
                    }
@ -304,7 +282,7 @@ class Sessions extends RedisModel {
            self.getMessagesByPage(sessionId, user, start_msg_id, end_msg_id, page, pagesize, function (err, res) {
                if (err) {
                    logger.error("getMessagesByPage error" + err);
                    ModelUtil.emitError(self.eventEmitter,err,err);
                    ModelUtil.emitError(self.eventEmitter, err, err);
                } else {
                    ModelUtil.emitOK(self.eventEmitter, res);
                }
@ -333,7 +311,7 @@ class Sessions extends RedisModel {
        let offset = (page - 1 < 0 ? 0 : page - 1) * size;
        let count = 20;
        participants.existsParticipant(sessionId, user, function (err,res) {
        participants.existsParticipant(sessionId, user, function (err, res) {
            if (!res) {
                handler("用户不在此会话中!", false);
            } else {
@ -491,7 +469,7 @@ class Sessions extends RedisModel {
            if (res) {
                redis.hmgetAsync(sessionKey, ["type", "name"]).then(function (res) {
                    let sessionType = res[0];
                    if(sessionType == null){
                    if (sessionType == null) {
                        ModelUtil.emitError(self.eventEmitter, "Session with id " + sessionId + " not found.");
                        return;
                    }

+ 22 - 11
src/server/models/user/users.js

@ -204,25 +204,36 @@ class Users extends RedisModel {
                // cache sessions, participants, topics, messages
                function (callback) {
                    SessionRepo.findAll(userId, function (err, sessions) {
                        sessions.forEach(function (session) {
                            let sessionId = session.id;
                            let name = session.name;
                            let type = session.type;
                            let createDate = session.create_date;
                        if(err){
                            ModelUtil.emitError(self.eventEmitter, err.message);
                            return;
                        }
                        sessions.forEach(function (session) {
                            let redisSession = [
                                "id", session.id,
                                "name", session.name,
                                "type", session.type,
                                "last_sender_id", session.last_sender_id == null ? "" : session.last_sender_id,
                                "last_sender_name", session.last_sender_name == null ? "" : session.last_sender_name,
                                "last_content_type", session.last_content_type == null ? "" : session.last_content_type,
                                "last_content", session.last_content == null ? "" : session.last_content,
                                "last_message_time", session.last_message_time == null ? "" : session.last_message_time,
                                "createDate", ObjectUtil.timestampToLong(session.create_date),
                            ];
                            (function (sessionId, userId) {
                                // cache sessions
                                redisConn.multi()
                                    .zadd(REDIS_KEYS.Sessions, lastLoginTime.getTime(), sessionId)                                           // 会话的最后活动时间设置为此用户的登录时间
                                    .zadd(RedisModel.makeRedisKey(REDIS_KEYS.UserSessions, userId), lastLoginTime.getTime(), sessionId)      // 会话的最后活动时间设置为此用户的登录时间
                                    .hmset(RedisModel.makeRedisKey(REDIS_KEYS.Session, sessionId), 'name', name, 'type', type, 'create_date', createDate.getTime())
                                    .hmset(RedisModel.makeRedisKey(REDIS_KEYS.Session, sessionId), redisSession)
                                    .execAsync()
                                    .then(function (res) {
                                        // cache participants
                                        let sessionParticipantsKey = RedisModel.makeRedisKey(REDIS_KEYS.SessionParticipants, sessionId);
                                        let sessionParticipantsRoleKey = RedisModel.makeRedisKey(REDIS_KEYS.SessionParticipantsRole, sessionId);
                                        ParticipantRepo.findParticipants(sessionId, function (err, participants) {
                                            if(err){
                                            if (err) {
                                                ModelUtil.emitError(self.eventEmitter, err.message);
                                                return;
                                            }
@ -230,7 +241,7 @@ class Users extends RedisModel {
                                            participants.forEach(function (participant) {
                                                let participantId = participant.participant_id;
                                                let participantRole = participant.participant_role;
                                                let score = new Date().getTime();
                                                let score = ObjectUtil.timestampToLong(participant.last_fetch_time);
                                                redisConn.multi()
                                                    .zadd(sessionParticipantsKey, score, participantId)
@ -244,7 +255,7 @@ class Users extends RedisModel {
                                        let messagesKey = RedisModel.makeRedisKey(REDIS_KEYS.Messages, sessionId);
                                        let messagesByTimestampKey = RedisModel.makeRedisKey(REDIS_KEYS.MessagesByTimestamp, sessionId);
                                        MessageRepo.findBySessionId(sessionId, 0, config.sessionConfig.maxMessageCount, function (err, messages) {
                                            if(err){
                                            if (err) {
                                                ModelUtil.emitError(self.eventEmitter, err.message);
                                                return;
                                            }
@ -271,7 +282,7 @@ class Users extends RedisModel {
                                        // cache topics for MUC
                                        let topicsKey = RedisModel.makeRedisKey(REDIS_KEYS.Topics, sessionId);
                                        TopicRepo.findAll(sessionId, function (err, topics) {
                                            if(err){
                                            if (err) {
                                                ModelUtil.emitError(self.eventEmitter, err.message);
                                                return;
                                            }
@ -295,7 +306,7 @@ class Users extends RedisModel {
                                            });
                                        });
                                    });
                            })(sessionId, userId);
                            })(session.id, userId);
                        });
                    });

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

@ -28,7 +28,6 @@ class MessageRepo {
            page = 1;
        }
        let session = new Sessions();
        SessionRepo.findOne(sessionId, function (err, res) {
            if (!err) {
                if (res.length == 0) {

+ 1 - 1
src/server/repository/mysql/participant.repo.js

@ -19,7 +19,7 @@ class ParticipantRepo {
     * @param handler
     */
    static findParticipants(sessionId, handler) {
        let sql = "select participant_id, participant_role from participants where session_id = ? ";
        let sql = "select participant_id, participant_role, last_fetch_time from participants where session_id = ? ";
        ImDb.execQuery({
            "sql": sql,

+ 2 - 1
src/server/repository/mysql/session.repo.js

@ -35,7 +35,8 @@ class SessionRepo {
     */
    static findAll(userId, handler) {
        let sql = "select session_id from " + DB_TABLES.Participants + " w where w.participant_id = ? group by w.session_id";
        let sessionSQL = "select id,name,type,create_date from " + DB_TABLES.Sessions + " s where s.id in(" + sql + ")";
        let sessionSQL = "select id, name, type, create_date, last_sender_id, last_sender_name, last_content_type, last_content, last_message_time from "
            + DB_TABLES.Sessions + " s where s.id in(" + sql + ")";
        ImDb.execQuery({
            "sql": sessionSQL,
            "args": [userId],

+ 8 - 2
src/server/resources/schema/ichat_1.2.8_table_schema.sql

@ -139,7 +139,8 @@ CREATE TABLE `participants`
(
	`session_id` VARCHAR(50) NOT NULL COMMENT '会话ID。ID结构:以患者ID+最大次数',
	`participant_id` VARCHAR(50) NOT NULL COMMENT '参与者ID',
	`participant_role` INTEGER COMMENT '参与者角色,MUC模式中的主持人/普通参与者',
	`participant_role` INTEGER COMMENT 'MUC中的参与者角色。0为主持人, 1为普通参与者, 10为旁听者',
	`last_fetch_time` TIMESTAMP(0) COMMENT '最后获取此会话消息的时间',
	CONSTRAINT `PK_participants` PRIMARY KEY (`session_id`,`participant_id`)
) COMMENT='会话参与者'
;
@ -149,7 +150,12 @@ CREATE TABLE `sessions`
	`id` VARCHAR(50) NOT NULL COMMENT '会话标识。会话标识来源根据业务场景:1 医生间P2P会话使用随机生成的ID;2 医生间的群会话使用行政团队的ID;3 医生与患者间的咨询以患者的ID+当前咨询次数为ID',
	`name` VARCHAR(50) NOT NULL COMMENT '会话名称',
	`type` INTEGER NOT NULL COMMENT '会话类型,1表示MUC会话,2表示P2P,3表示群会话,4表示临时讨论组',
	`create_date` DATETIME NOT NULL COMMENT '创建时间',
	`create_date` DATETIME(0) NOT NULL COMMENT '创建时间',
	`last_sender_id` VARCHAR(50) COMMENT '消息最后发送人ID',
	`last_sender_name` VARCHAR(50) COMMENT '消息最后发送人姓名',
	`last_content_type` VARCHAR(50) COMMENT '消息最后内容类型',
	`last_content` VARCHAR(1024) COMMENT '消息最后内容',
	`last_message_time` TIMESTAMP(0) COMMENT '消息最后时间',
	CONSTRAINT `PK_sessions` PRIMARY KEY (`id`)
) COMMENT='会话'
;

+ 7 - 172
src/server/resources/schema/temp.sql

@ -1,6 +1,6 @@
/* ---------------------------------------------------- */
/*  Generated by Enterprise Architect Version 12.0 		*/
/*  Created On : 23-Dec-2016 3:13:54 PM 				*/
/*  Created On : 04-Jan-2017 5:41:28 PM 				*/
/*  DBMS       : MySql 						*/
/* ---------------------------------------------------- */
@ -8,189 +8,24 @@ SET FOREIGN_KEY_CHECKS=0
/* Drop Tables */
DROP TABLE IF EXISTS `system_messages` CASCADE
;
DROP TABLE IF EXISTS `sticky_sessions` CASCADE
;
DROP TABLE IF EXISTS `wechat_access_tokens` CASCADE
;
DROP TABLE IF EXISTS `app_status` CASCADE
;
DROP TABLE IF EXISTS `topics` CASCADE
;
DROP TABLE IF EXISTS `p2p_messages` CASCADE
;
DROP TABLE IF EXISTS `group_messages` CASCADE
;
DROP TABLE IF EXISTS `muc_messages` CASCADE
;
DROP TABLE IF EXISTS `participants` CASCADE
;
DROP TABLE IF EXISTS `sessions` CASCADE
;
/* Create Tables */
CREATE TABLE `system_messages`
(
	`id` VARCHAR(32) NOT NULL COMMENT '消息ID',
	`session_id` VARCHAR(50) NOT NULL COMMENT '所属会话',
	`sender_id` VARCHAR(50) NOT NULL COMMENT '消息发送者',
	`sender_name` VARCHAR(50),
	`content_type` INTEGER NOT NULL COMMENT '消息类型,1文本,2图片,3语音,4文章,5跳转,6咨询开始,7咨询结束',
	`content` VARCHAR(1024) COMMENT '消息内容',
	`timestamp` TIMESTAMP(0) COMMENT '发送时间',
	CONSTRAINT `PK_messages` PRIMARY KEY (`id`)
) COMMENT='P2P会话消息'
;
CREATE TABLE `sticky_sessions`
(
	`user_id` VARCHAR(50) NOT NULL COMMENT '用户ID',
	`session_id` VARCHAR(50) NOT NULL COMMENT '会话ID',
	`score` NUMERIC(15,0) COMMENT '置顶分值',
	CONSTRAINT `PK_sticky_sessions` PRIMARY KEY (`user_id`,`session_id`)
) COMMENT='置顶会话'
;
CREATE TABLE `wechat_access_tokens`
(
	`access_token` VARCHAR(50) NOT NULL COMMENT '访问token',
	`expiry_date` TIMESTAMP(0) COMMENT '过期时间',
	`create_time` TIMESTAMP(0) COMMENT '数据创建时间',
	CONSTRAINT `PK_wechat_access_tokens` PRIMARY KEY (`access_token`)
) COMMENT='微信接口调用所需要token'
;
CREATE TABLE `app_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是否处于后台状态',
	CONSTRAINT `PK_user_status` PRIMARY KEY (`user_id`)
) COMMENT='app端状态'
;
CREATE TABLE `topics`
(
	`id` VARCHAR(32) NOT NULL COMMENT 'ID',
	`session_id` VARCHAR(50) NOT NULL COMMENT 'MUC会话ID',
	`name` VARCHAR(50) COMMENT '议题名称',
	`create_time` TIMESTAMP(0) COMMENT '创建时间',
	`end_by` VARCHAR(50) COMMENT '结束人ID',
	`end_time` TIMESTAMP(0) COMMENT '结束时间',
	`start_message_id` INTEGER COMMENT '消息起始ID',
	`end_message_id` INTEGER COMMENT '消息结束ID',
	CONSTRAINT `PK_topics` PRIMARY KEY (`id`)
) COMMENT='议题,仅MUC模式使用。'
;
CREATE TABLE `p2p_messages`
(
	`id` VARCHAR(32) NOT NULL COMMENT '消息ID',
	`session_id` VARCHAR(50) NOT NULL COMMENT '所属会话',
	`sender_id` VARCHAR(50) NOT NULL COMMENT '消息发送者',
	`sender_name` VARCHAR(50),
	`content_type` INTEGER NOT NULL COMMENT '消息类型,1文本,2图片,3语音,4文章,5跳转,6咨询开始,7咨询结束',
	`content` VARCHAR(1024) COMMENT '消息内容',
	`timestamp` TIMESTAMP(0) COMMENT '发送时间',
	CONSTRAINT `PK_messages` PRIMARY KEY (`id`)
) COMMENT='P2P会话消息'
;
CREATE TABLE `group_messages`
(
	`id` VARCHAR(32) NOT NULL COMMENT '消息ID',
	`session_id` VARCHAR(50) NOT NULL COMMENT '所属会话',
	`sender_id` VARCHAR(50) NOT NULL COMMENT '消息发送者',
	`sender_name` VARCHAR(50),
	`content_type` INTEGER NOT NULL COMMENT '消息类型,1文本,2图片,3语音,4文章,5跳转,6咨询开始,7咨询结束',
	`content` VARCHAR(1024) COMMENT '消息内容',
	`timestamp` TIMESTAMP(0) COMMENT '发送时间',
	CONSTRAINT `PK_messages` PRIMARY KEY (`id`)
) COMMENT='群会话消息'
;
CREATE TABLE `muc_messages`
(
	`id` VARCHAR(32) NOT NULL COMMENT '消息ID',
	`session_id` VARCHAR(50) NOT NULL COMMENT '所属会话',
	`sender_id` VARCHAR(50) NOT NULL COMMENT '消息发送者',
	`sender_name` VARCHAR(50),
	`content_type` INTEGER NOT NULL COMMENT '消息类型,1文本,2图片,3语音,4文章,5跳转,6咨询开始,7咨询结束',
	`content` VARCHAR(1024) COMMENT '消息内容',
	`timestamp` TIMESTAMP(0) COMMENT '发送时间',
	CONSTRAINT `PK_messages` PRIMARY KEY (`id`)
) COMMENT='MUC会话消息'
;
CREATE TABLE `participants`
(
	`session_id` VARCHAR(50) NOT NULL COMMENT '会话ID。ID结构:以患者ID+最大次数',
	`participant_id` VARCHAR(50) NOT NULL COMMENT '参与者ID',
	`participant_role` INTEGER COMMENT '参与者角色,MUC模式中的主持人/普通参与者',
	`receiving` TINYINT COMMENT '暂未使用',
	CONSTRAINT `PK_participants` PRIMARY KEY (`session_id`,`participant_id`)
) COMMENT='会话参与者'
;
CREATE TABLE `sessions`
(
	`id` VARCHAR(50) NOT NULL COMMENT '会话标识。会话标识来源根据业务场景:1 医生间P2P会话使用随机生成的ID;2 医生间的群会话使用行政团队的ID;3 医生与患者间的咨询以患者的ID+当前咨询次数为ID',
	`name` VARCHAR(50) NOT NULL COMMENT '会话名称',
	`type` INTEGER NOT NULL COMMENT '会话类型,1表示MUC会话,2表示P2P,3表示群会话,4表示临时讨论组',
	`create_date` DATE NOT NULL COMMENT '创建时间',
	`create_date` DATETIME(0) NOT NULL COMMENT '创建时间',
	`last_sender_id` VARCHAR(50) COMMENT '消息最后发送人ID',
	`last_sender_name` VARCHAR(50) COMMENT '消息最后发送人姓名',
	`last_content_type` VARCHAR(50) COMMENT '消息最后内容类型',
	`last_content` VARCHAR(1024) COMMENT '消息最后内容',
	`last_message_time` TIMESTAMP(0) COMMENT '消息最后时间',
	CONSTRAINT `PK_sessions` PRIMARY KEY (`id`)
) COMMENT='会话'
;
/* Create Primary Keys, Indexes, Uniques, Checks */
ALTER TABLE `system_messages` 
 ADD INDEX `IXFK_messages_participants` (`session_id` ASC,`sender_id` ASC)
;
ALTER TABLE `system_messages` 
 ADD INDEX `IXFK_messages_sessions` (`session_id` ASC)
;
ALTER TABLE `topics` 
 ADD INDEX `IXFK_topics_sessions` (`session_id` ASC)
;
ALTER TABLE `p2p_messages` 
 ADD INDEX `IXFK_messages_participants` (`session_id` ASC,`sender_id` ASC)
;
ALTER TABLE `p2p_messages` 
 ADD INDEX `IXFK_messages_sessions` (`session_id` ASC)
;
ALTER TABLE `group_messages` 
 ADD INDEX `IXFK_messages_participants` (`session_id` ASC,`sender_id` ASC)
;
ALTER TABLE `group_messages` 
 ADD INDEX `IXFK_messages_sessions` (`session_id` ASC)
;
ALTER TABLE `muc_messages` 
 ADD INDEX `IXFK_messages_participants` (`session_id` ASC,`sender_id` ASC)
;
ALTER TABLE `muc_messages` 
 ADD INDEX `IXFK_messages_sessions` (`session_id` ASC)
;
SET FOREIGN_KEY_CHECKS=1