소스 검색

Merge branch 'feature-refactor' of http://192.168.1.220:10080/Amoy/im.doctor into feature-refactor

8 년 전
부모
커밋
7eda96d558

+ 67 - 30
src/client/im.client.js

@ -77,7 +77,7 @@ var ENDPOINTS = {
        ParticipantAvatar: '/session/:session_id/participants/:participant_id/avatars'
    },
    Search: {
        Search: "/search"
        Search: '/search'
    }
};
@ -399,39 +399,76 @@ var imClient = {
                {user_id: userId, date_span: 7},
                success,
                failure);
        }
    },
    Search: {
        // 搜索所有对象:医生,会话及消息
        searchAll: function (userId, keyword, success, failure) {
            httpClient.get(ENDPOINTS.Search.Search,
                {user_id: userId, target: 'all', keyword: keyword},
                success,
                failure);
        },
        // 搜索医生
        searchDoctors: function (userId, keyword, page, size, success, failure) {
            httpClient.get(ENDPOINTS.Search.Search,
                {user_id: userId, target: 'doctor', keyword: keyword, page: page, size: size},
                success,
                failure);
        },
        Search: {
            Doctor: {
                // 搜索所有对象:医生,会话及消息
                searchAll: function (userId, keyword, excludeTopicEndedSessions, success, failure) {
                    httpClient.get(ENDPOINTS.Search.Search,
                        {user_id: userId, target_role: 'doctor', entity_type: 'all', keyword: keyword, exclude_topic_ended_sessions: excludeTopicEndedSessions},
                        success,
                        failure);
                },
        // 搜索会话
        searchSessions: function (userId, keyword, page, size, success, failure) {
            httpClient.get(ENDPOINTS.Search.Search,
                {user_id: userId, target: 'session', keyword: keyword, page: page, size: size},
                success,
                failure);
        },
                // 搜索医生
                searchDoctors: function (userId, keyword, page, size, success, failure) {
                    httpClient.get(ENDPOINTS.Search.Search,
                        {user_id: userId, target_role: 'doctor', entity_type: 'user', keyword: keyword, exclude_topic_ended_sessions: false, page: page, size: size},
                        success,
                        failure);
                },
        // 搜索会话消息
        searchMessages: function (userId, keyword, page, size, success, failure) {
            httpClient.get(ENDPOINTS.Search.Search,
                {user_id: userId, target: 'message', keyword: keyword, page: page, size: size},
                success,
                failure);
                // 搜索会话
                searchSessions: function (userId, keyword, excludeTopicEndedSessions, page, size, success, failure) {
                    httpClient.get(ENDPOINTS.Search.Search,
                        {user_id: userId, target_role: 'doctor', entity_type: 'session', keyword: keyword, exclude_topic_ended_sessions: excludeTopicEndedSessions, page: page, size: size},
                        success,
                        failure);
                },
                // 搜索会话消息
                searchMessages: function (userId, keyword, page, size, success, failure) {
                    httpClient.get(ENDPOINTS.Search.Search,
                        {user_id: userId, target_role: 'doctor', entity_type: 'message', keyword: keyword, exclude_topic_ended_sessions: false, page: page, size: size},
                        success,
                        failure);
                }
            },
            Patient: {
                // 搜索所有对象:患者,会话及消息
                searchAll: function (userId, keyword, excludeTopicEndedSessions, success, failure) {
                    httpClient.get(ENDPOINTS.Search.Search,
                        {user_id: userId, target_role: 'patient', entity_type: 'all', keyword: keyword, exclude_topic_ended_sessions: excludeTopicEndedSessions},
                        success,
                        failure);
                },
                // 搜索患者
                searchPatient: function (userId, keyword, page, size, success, failure) {
                    httpClient.get(ENDPOINTS.Search.Search,
                        {user_id: userId, target_role: 'patient', entity_type: 'user', keyword: keyword, exclude_topic_ended_sessions: false, page: page, size: size},
                        success,
                        failure);
                },
                // 搜索会话
                searchSessions: function (userId, keyword, excludeTopicEndedSessions, page, size, success, failure) {
                    httpClient.get(ENDPOINTS.Search.Search,
                        {user_id: userId, target_role: 'patient', entity_type: 'session', keyword: keyword, exclude_topic_ended_sessions: excludeTopicEndedSessions, page: page, size: size},
                        success,
                        failure);
                },
                // 搜索会话消息
                searchMessages: function (userId, keyword, page, size, success, failure) {
                    httpClient.get(ENDPOINTS.Search.Search,
                        {user_id: userId, target_role: 'patient', entity_type: 'message', keyword: keyword, exclude_topic_ended_sessions: false, page: page, size: size},
                        success,
                        failure);
                }
            }
        }
    }
};

+ 16 - 19
src/server/endpoints/v2/search.endpoint.js

@ -10,46 +10,43 @@ let express = require('express');
let router = express.Router();
let ControllerUtil = require("../../util/controller.util.js");
let MySqlSearcher = require('../../models/search/mysql.searcher');
let ObjectSearcher = require('../../models/search/object.searcher');
const APIv2 = require('../../include/endpoints').APIv2;
/**
 * 搜索API。
 * 搜索。
 *
 * 参数:
 *  target: all/doctor/session/message
 *  user_id: 用户ID
 *  target_role: 目标角色,patient为患者相关,doctor为医生相关
 *  entity_type: 实体类型,user为用户,session为会话,message为消息,all为全部都搜索
 *  keyword: 关键字
 *  exclude_topic_ended_sessions: 排除议题已结束的会话
 *  page: 第几页,从1开始,当target为all时无效
 *  size: 页大小,当target为all时无效
 *
 * URL:
 *  /search?target=all&keyword=张&page
 *  /search?user_id=e51ac&target_role=doctor&entity_type=all&keyword=张&exclude_topic_ended_sessions=true&page=1&size=3
 */
router.get("/", function (req, res) {
    ControllerUtil.checkRequestQueryParams(req, ['user_id', 'target', 'keyword']);
router.get('/', function (req, res) {
    ControllerUtil.checkRequestQueryParams(req, ['user_id', 'target_role', 'entity_type', 'keyword']);
    let userId = req.query.user_id;
    let target = req.query.target;
    let targetRole = req.query.target_role;
    let entityType = req.query.entity_type;
    let keyword = req.query.keyword;
    let excludeTopicEndedSessions = req.query.exclude_topic_ended_sessions === 'true';
    let page = req.query.page;
    let size = req.query.size;
    page = page ? parseInt(page) - 1 : 0;
    size = size ? parseInt(size) : 10;
    let searcher = new MySqlSearcher();
    let searcher = new ObjectSearcher();
    ControllerUtil.regModelEventHandler(searcher, res);
    if (target == 'all') {
        searcher.searchAll(userId, keyword);
    } else if (target == 'doctor') {
        searcher.searchDoctors(keyword, page, size)
    } else if (target == 'session') {
        searcher.searchSessions(userId, keyword, page, size);
    } else if (target == 'message') {
        searcher.searchMessages(userId, keyword, page, size);
    } else {
        throw {message: "Unknown target type: " + target};
    }
    searcher.search(userId, keyword, targetRole, entityType, excludeTopicEndedSessions, page, size);
});
module.exports = router;

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

@ -170,6 +170,8 @@ exports.REDIS_KEYS = {
};
const DB_TABLES = {
    Doctors: "doctors",
    Patients: "patients",
    P2pMessages: "p2p_messages",
    MucMessages: "muc_messages",
    GroupMessages: "group_messages",

+ 1 - 1
src/server/include/endpoints.js

@ -50,7 +50,7 @@ const APIv2 = {
        ParticipantAvatar: '/:session_id/participants/:participant_id/avatars'  // 会话单个成员头像
    },
    Search: {
        Base: '/api/v2/search'                                          // 搜索,下一版本的语法使用ElasticSearch
        Base: '/api/v2/search'                                         // 搜索,下一版本的语法使用ElasticSearch
    }
};

+ 0 - 171
src/server/models/search/mysql.searcher.js

@ -1,171 +0,0 @@
/**
 * 用户、会话及消息搜索。
 *
 * 注意:此模型效率堪忧,但为了实现先这样做。更稳妥的方案是使用Solr或Elastic Search
 * 为数据提供索引功能,JS使用搜索接口搜索之后再取得对象的ID进行获取,提高效率。
 * 后续开发都希望看到这段注释,实现此方案。
 *
 * author: Sand
 * since: 2016.11.20
 */
"use strict";
let BaseModel = require('../base.model');
let SearchRepo = require('../../repository/mysql/search.repo');
let ModelUtil = require("../../util/model.util");
let ObjectUtil = require("../../util/object.util.js");
let async = require("async");
class MySqlSearcher extends BaseModel {
    constructor() {
        super();
    }
    /**
     * 一次性搜索所有对象。
     *
     * @param userId
     * @param keyword
     */
    searchAll(userId, keyword) {
        let self = this;
        async.waterfall([
                function (callback) {
                    self.searchDoctors(keyword, 0, 4, function (err, doctors) {
                        if (err) return callback(err, null);
                        let data = {};
                        let buffer = [];
                        doctors.forEach(function (doctor) {
                            buffer.push(doctor);
                        });
                        data.doctor = buffer;
                        callback(null, data);
                    });
                },
                function (data, callback) {
                    self.searchSessions(userId, keyword, 0, 4, function (err, sessions) {
                        if (err) return callback(err, null);
                        let buffer = [];
                        sessions.forEach(function (session) {
                            buffer.push(session);
                        });
                        data.groups = buffer;
                        callback(null, data);
                    });
                },
                function (data, callback) {
                    self.searchMessages(userId, keyword, 0, 4, function (err, sessions) {
                        if (err) return callback(err, null);
                        let buffer = [];
                        sessions.forEach(function (session) {
                            buffer.push(session);
                        });
                        data.content = buffer;
                        callback(null, data);
                    });
                }
            ],
            function (err, res) {
                if (err) {
                    ModelUtil.emitError(self.eventEmitter, "Search all object types failed", err);
                } else {
                    ModelUtil.emitOK(self.eventEmitter, res);
                }
            });
    }
    /**
     * 医生搜索。
     *
     * @param keyword
     * @param page
     * @param size
     * @param handler
     */
    searchDoctors(keyword, page, size, handler) {
        let self = this;
        SearchRepo.searchDoctors(keyword, page, size, function (err, doctors) {
            if (handler) {
                handler(err, doctors);
            } else {
                if (err) {
                    ModelUtil.emitError(self.eventEmitter, "Search doctors failed", err);
                } else {
                    doctors.forEach(function (doctor) {
                        doctor.avatar = doctor.avatar ? doctor.avatar : "";
                        doctor.birthdate = doctor.birthdate ? doctor.birthdate : "";
                    });
                    ModelUtil.emitOK(self.eventEmitter, doctors);
                }
            }
        });
    }
    /**
     * 会话搜索。
     *
     * @param userId
     * @param keyword
     * @param page
     * @param size
     * @param handler
     */
    searchSessions(userId, keyword, page, size, handler) {
        let self = this;
        SearchRepo.searchSessions(userId, keyword, page, size, function (err, sessions) {
            if (handler) {
                handler(err, sessions);
            } else {
                if (err) {
                    ModelUtil.emitError(self.eventEmitter, "Search sessions failed", err);
                } else {
                    sessions.forEach(function (session) {
                        session.create_date = ObjectUtil.timestampToLong(session.create_date);
                    });
                    ModelUtil.emitOK(self.eventEmitter, sessions);
                }
            }
        });
    }
    /**
     * 搜索会话消息。
     *
     * @param userId
     * @param keyword
     * @param page
     * @param size
     * @param handler
     */
    searchMessages(userId, keyword, page, size, handler) {
        let self = this;
        SearchRepo.searchMessages(userId, keyword, page, size, function (err, messages) {
            if (handler) {
                handler(err, messages);
            } else {
                if (err) {
                    ModelUtil.emitError(self.eventEmitter, "Search sessions failed", err);
                } else {
                    messages.forEach(function (message) {
                        message.create_time = ObjectUtil.timestampToLong(message.create_time);
                    });
                    ModelUtil.emitOK(self.eventEmitter, messages);
                }
            }
        });
    }
}
module.exports = MySqlSearcher;

+ 98 - 0
src/server/models/search/object.searcher.js

@ -0,0 +1,98 @@
"use strict";
let RedisModel = require('./../redis.model.js');
let ModelUtil = require('../../util/model.util');
let RedisClient = require('../../repository/redis/redis.client.js');
let SearchRepo = require('../../repository/mysql/search.repo');
let log = require('../../util/log.js');
let async = require("async");
let ObjectUtil = require("../../util/object.util.js");
let redis = RedisClient.redisClient().connection;
const REDIS_KEYS = require('../../include/commons').REDIS_KEYS;
const DB_TABLES = require('../../include/commons').DB_TABLES;
class ObjectSearcher extends RedisModel {
    constructor() {
        super();
    }
    search(userId, keyword, targetRole, entityType, excludeTopicEndedSessions, page, size) {
        let self = this;
        async.waterfall([
                // 获取会话ID列表,根据参数过滤应该议题已结束的会话
                function (callback) {
                    if (excludeTopicEndedSessions) {
                        SearchRepo.findTopicEndedSessionIdList(userId, function (err, sessionIdList) {
                            if (err) return callback(err, null);
                            callback(null, sessionIdList, keyword, targetRole, entityType);
                        })
                    } else {
                        redis.zrangeAsync(RedisModel.makeRedisKey(REDIS_KEYS.UserSessions, userId), 0, -1)
                            .then(function (sessionIdList) {
                                callback(null, sessionIdList, keyword, targetRole, entityType);
                            })
                            .catch(function (err) {
                                return callback(err, null);
                            });
                    }
                },
                // 搜索
                function (sessionIdList, keyword, targetRole, entityType, callback) {
                    if (targetRole !== 'doctor' && targetRole !== 'patient') {
                        ModelUtil.emitError(self.eventEmitter, "Unknown target role: " + targetRole);
                    }
                    let userTable = targetRole === 'doctor' ? DB_TABLES.Doctors : DB_TABLES.Patients;
                    if (entityType === 'all') {
                        SearchRepo.searchAll(sessionIdList, keyword, userTable, function (err, res) {
                            if (err) return callback(err, null);
                            callback(null, res);
                        });
                    } else if (entityType === 'user') {
                        SearchRepo.searchUser(sessionIdList, keyword, userTable, page, size, function (err, res) {
                            if (err) return callback(err, null);
                            if (userTable == DB_TABLES.Patients) {
                                callback(null, {patients: res});
                            } else {
                                callback(null, {doctors: res});
                            }
                        });
                    } else if (entityType === 'session') {
                        SearchRepo.searchSessions(sessionIdList, keyword, page, size, function (err, res) {
                            if (err) return callback(err, null);
                            callback(null, {sessions: res});
                        });
                    } else if (entityType === 'message') {
                        SearchRepo.searchMessages(sessionIdList, keyword, page, size, function (err, res) {
                            if (err) return callback(err, null);
                            res.forEach(function (message) {
                                message.timestamp = ObjectUtil.timestampToLong(message.timestamp);
                            });
                            callback(null, {messages: res});
                        });
                    } else {
                        callback(new Error("Unknown entity type: " + entityType), null);
                    }
                }
            ],
            function (err, res) {
                if (err) {
                    return ModelUtil.emitError(self.eventEmitter, "Search failed", err);
                }
                ModelUtil.emitOK(self.eventEmitter, res);
            });
    }
}
module.exports = ObjectSearcher;

+ 19 - 14
src/server/models/sessions/sessions.js

@ -342,6 +342,7 @@ class Sessions extends RedisModel {
     * @param userId
     * @param page
     * @param size
     * @param businessType
     */
    getUserSessions(userId, page, size, businessType) {
        let userSessionKey = RedisModel.makeRedisKey(REDIS_KEYS.UserSessions, userId);
@ -369,12 +370,12 @@ class Sessions extends RedisModel {
            function (sessionIds) {
                let sessionList = [];
                let functionList = [];
                for (var j = 0; j < sessionIds.length; j++) {
                for (let j = 0; j < sessionIds.length; j++) {
                    let fun = function (index, callback) {
                        if (!callback) {
                            callback = index, index = 0
                        }
                        ;
                        let sessionId = sessionIds[index]
                        let sessionKey = RedisModel.makeRedisKey(REDIS_KEYS.Session, sessionId);
                        let participantsRoleKey = RedisModel.makeRedisKey(REDIS_KEYS.SessionParticipantsRole, sessionId);
@ -394,7 +395,7 @@ class Sessions extends RedisModel {
                                let sessionName = "";
                                let otheruserId = "";
                                if (session.type == SESSION_TYPES.P2P) {
                                    for (var j in users) {
                                    for (let j in users) {
                                        if (users[j] != userId) {
                                            otheruserId = users[j];
                                        }
@ -402,6 +403,7 @@ class Sessions extends RedisModel {
                                }
                                if (!role) role = 0;
                                if (!lastFetchTime) lastFetchTime = new Date().getTime();
                                // 计算未读消息数
                                let messagesByTimestampKey = RedisModel.makeRedisKey(REDIS_KEYS.MessagesByTimestamp, sessionId);
                                redis.zcountAsync(messagesByTimestampKey, lastFetchTime, new Date().getTime())
@ -413,6 +415,7 @@ class Sessions extends RedisModel {
                                            } else {
                                                sessionName = res[0].name;
                                            }
                                            sessionList.push({
                                                id: sessionId,
                                                name: sessionName,
@ -426,22 +429,24 @@ class Sessions extends RedisModel {
                                                business_type: session.business_type,
                                                my_role: role
                                            });
                                            index = (parseInt(index) + 1);
                                            if (index == sessionIds.length) {
                                                ModelUtil.emitOK(self.eventEmitter, sessionList);
                                            } else {
                                                callback(null, index);
                                            }
                                        })
                                    })
                            }).catch(function (err) {
                            logger.error("Get sessions failed: ", err);
                        });
                    }
                            })
                            .catch(function (err) {
                                logger.error("Get sessions failed: ", err);
                            });
                    };
                    functionList.push(fun);
                }
                ;
                async.waterfall(functionList);
            }
        ]);
@ -887,7 +892,7 @@ class Sessions extends RedisModel {
                    sessionName = res[1];
                    if (!sessionType || !sessionName) {
                        logger.error("Unknown session key " + session_key);
                        if(handler) return handler(new Error("Unknown session key " + session_key));
                        if (handler) return handler(new Error("Unknown session key " + session_key));
                    }
                }).then(function (res) {
                    // 更新消息存储REDIS
@ -901,12 +906,12 @@ class Sessions extends RedisModel {
                    //更新session实体的最后一条消息
                    SessionRepo.updateSessionLastStatus(message.sender_id, message.sender_name, message.timestamp, message.content, message.content_type, sessionId);
                    if(handler) handler(null, messageId);
                    if (handler) handler(null, messageId);
                }).then(function (res) {
                    // 推送消息
                    ParticipantRepo.findIds(sessionId, function (err, res) {
                        if (err) {
                            if(handler) handler(err, messageId)
                            if (handler) handler(err, messageId)
                        } else {
                            message.session_id = sessionId;
                            res.forEach(function (participant) {
@ -917,10 +922,10 @@ class Sessions extends RedisModel {
                        }
                    })
                }).catch(function (err) {
                    if(handler) handler(err, messageId)
                    if (handler) handler(err, messageId)
                })
            } else {
                if(handler) handler("用户不在此会话当中!", messageId);
                if (handler) handler("用户不在此会话当中!", messageId);
            }
        })
    }

+ 1 - 1
src/server/repository/mysql/app.status.repo.js

@ -9,7 +9,7 @@
 */
"use strict";
var ImDb = require("./db/im.db.js");
let ImDb = require("./db/im.db.js");
class AppStatusRepo {
    constructor() {

+ 6 - 2
src/server/repository/mysql/message.repo.js

@ -59,7 +59,9 @@ class MessageRepo {
                ImDb.execQuery({
                    "sql": sql,
                    "args": params,
                    "handler": handler
                    "handler": handler || function (err, res) {
                        if(err) log.error(err);
                    }
                });
            }
        });
@ -80,7 +82,9 @@ class MessageRepo {
        ImDb.execQuery({
            "sql": sql,
            "args": [messageId, sessionId, message.sender_id, message.sender_name, message.content_type, message.content, message.timestamp, message.business_type || 1],
            "handler": handler
            "handler": handler || function (err, res) {
                if(err) log.error(err);
            }
        });
    }

+ 112 - 44
src/server/repository/mysql/search.repo.js

@ -1,81 +1,149 @@
/**
 * 搜索。
 */
"use strict";
'use strict';
let ImDb = require('../mysql/db/im.db');
let async = require("async");
var ObjectUtil = require("../../util/object.util.js");
const DB_TABLES = require('../../include/commons').DB_TABLES;
class SearchRepo {
    constructor() {
    }
    static searchDoctors(keyword, page, size, handler) {
        if(page<=0)page = 1;
        page = (page-1)*size;
        let sql = "select d.id, d.name, d.sex, d.birthdate, d.avatar, d.level from doctors d where d.name like ? limit ?, ?";
    /**
     * 查询正常会话及议题已结束的会话(P2P, MUC)
     *
     * @param userId
     * @param handler
     */
    static findTopicEndedSessionIdList(userId, handler) {
        let sql = "SELECT s.id " +
            "FROM sessions s, topics t, participants p " +
            "WHERE p.participant_id = ? AND p.session_id = s.id AND s.id = t.session_id AND t.end_message_id IS NOT NULL AND s.`type` IN (1) " +
            " UNION " +
            "SELECT s.id " +
            "FROM sessions s, participants p " +
            "WHERE p.participant_id = ? AND p.session_id = s.id AND s.`type` IN (2, 3)";
        ImDb.execQuery({
            sql: sql,
            args: ['%' + keyword + '%',page , size],
            args: [userId, userId],
            handler: handler
        });
    }
    /**
     * 搜索会话。
     * 全部搜索
     *
     * TODO: 还需要联合搜索包含特定用户的会话。当前只实现了按会话名称搜索。
     * @param sessionIdList
     * @param keyword
     * @param userTable
     * @param handler
     */
    static searchAll(sessionIdList, keyword, userTable, handler) {
        async.waterfall([
            function (callback) {
                SearchRepo.searchUser(sessionIdList, keyword, userTable, 0, 3, function (err, res) {
                    if (err) return handler(err, null);
                    let data = {};
                    if (userTable == DB_TABLES.Doctors) {
                        data.doctors = res;
                    } else {
                        data.patients = res;
                    }
                    callback(null, data);
                });
            },
            function (data, callback) {
                SearchRepo.searchSessions(sessionIdList, keyword, 0, 3, function (err, res) {
                    if (err) return handler(err, null);
                    data.sessions = res;
                    callback(null, data);
                })
            },
            function (data, callback) {
                SearchRepo.searchMessages(sessionIdList, keyword, 0, 3, function (err, res) {
                    if (err) return handler(err, null);
                    res.forEach(function (message) {
                        message.timestamp = ObjectUtil.timestampToLong(message.timestamp);
                    });
                    data.messages = res;
                    handler(null, data);
                })
            }
        ]);
    }
    /**
     * 用户搜索
     *
     * @param userId
     * @param sessionIdList
     * @param keyword
     * @param userTable
     * @param page
     * @param size
     * @param handler
     */
    static searchSessions(userId, keyword, page, size, handler) {
        let sql = "select s.id, s.name, s.type, s.create_date, s.business_type," +
            "(SELECT group_concat(d.name) FROM doctors d,participants p1 WHERE p1.participant_id = d.id and p1.session_id = s.id and d.name like ?) as members," +
            "(SELECT group_concat(d.avatar) FROM doctors d,participants p1 WHERE p1.participant_id = d.id and p1.session_id = s.id) as images from sessions s" +
            " where s.id in (select id from sessions s, participants p where s.id = p.session_id and p.participant_id = ?) " +
            "and s.type <> 0 and s.name like ? limit ?, ?";
    static searchUser(sessionIdList, keyword, userTable, page, size, handler) {
        let sql = "SELECT u.id, u.name, u.sex, u.avatar FROM sessions s, participants p, " + userTable +
            " u WHERE s.id in (?) AND s.id = p.session_id AND p.participant_id = u.id AND u.name like ? limit ?, ?";
        keyword = '%' + keyword + '%';
        ImDb.execQuery({
            sql: sql,
            args: [ '%' + keyword + '%',userId, '%' + keyword + '%', page * size, size],
            args: [sessionIdList, keyword, page * size, size],
            handler: handler
        });
    }
    /**
     * 搜索会话消息。
     *
     * @param userId
     * @param keyword
     * @param page
     * @param size
     * @param handler
     * 会话搜索
     */
    static searchSessions(sessionIdList, keyword, page, size, handler) {
        if(sessionIdList.length == 0){
            return handler(null, []);
        }
        let sql = "SELECT s.id, s.name, s.type, s.create_date, s.business_type FROM sessions s WHERE s.id in (?) AND s.name LIKE ? LIMIT ?, ? ";
        keyword = '%' + keyword + '%';
        ImDb.execQuery({
            sql: sql,
            args: [sessionIdList, keyword, page * size, size],
            handler: handler
        });
    }
    /**
     * 消息搜索
     */
    static searchMessages(userId, keyword, page, size, handler) {
        let sql = "SELECT * FROM( " +
        "SELECT s.id session_id, s.name session_name, s.type session_type, s.create_date, s.business_type, count(m.id) count, max(m.content) message_content " +
        "FROM sessions s, participants p, muc_messages m " +
        "WHERE p.participant_id = ? and p.session_id = s.id and s.id = m.session_id and m.content_type = 1 AND m.content LIKE ? " +
        "GROUP BY s.id, s.name, s.type, s.create_date, s.business_type "+
         "UNION " +
        "SELECT s.id session_id, s.name session_name, s.type session_type, s.create_date, s.business_type, count(m.id) count, max(m.content) message_content " +
        "FROM sessions s, participants p, group_messages m " +
        "WHERE p.participant_id = ? and p.session_id = s.id and s.id = m.session_id and m.content_type = 1 AND m.content LIKE ? " +
        "GROUP BY s.id, s.name, s.type, s.create_date, s.business_type "+
        "UNION " +
        "SELECT s.id session_id, s.name session_name, s.type session_type, s.create_date, s.business_type, count(m.id) count, max(m.content) message_content " +
        "FROM sessions s, participants p, p2p_messages m " +
        "WHERE p.participant_id = ? and p.session_id = s.id and s.id = m.session_id and m.content_type = 1 AND m.content LIKE ? " +
         "GROUP BY s.id, s.name, s.type, s.create_date, s.business_type "+
         ") X LIMIT ?, ?";
    static searchMessages(sessionIdList, keyword, page, size, handler) {
        let sql = "SELECT * FROM(" +
            "SELECT s.id session_id, s.name session_name, s.type session_type, s.business_type session_business_type, m.id message_id, m.sender_id, m.sender_name, m.timestamp, m.content " +
            "FROM sessions s, muc_messages m " +
            "WHERE s.id IN (?) AND s.id = m.session_id AND s.`type` = 1 AND m.content_type = 1 AND m.content LIKE ? GROUP BY s.id" +
            " UNION " +
            "SELECT s.id session_id, s.name session_name, s.type session_type, s.business_type session_business_type, m.id message_id, m.sender_id, m.sender_name, m.timestamp, m.content " +
            "FROM sessions s, p2p_messages m " +
            "WHERE s.id IN (?) AND s.id = m.session_id AND s.`type` = 2 AND m.content_type = 1 AND m.content LIKE ? GROUP BY s.id" +
            " UNION " +
            "SELECT s.id session_id, s.name session_name, s.type session_type, s.business_type session_business_type, m.id message_id, m.sender_id, m.sender_name, m.timestamp, m.content " +
            "FROM sessions s, group_messages m " +
            "WHERE s.id IN (?) AND s.id = m.session_id AND s.`type` = 3 AND m.content_type = 1 AND m.content LIKE ? GROUP BY s.id) X " +
            "ORDER BY X.timestamp";
        keyword = '%' + keyword + '%';
        ImDb.execQuery({
            sql: sql,
            args: [userId, keyword, userId, keyword, userId, keyword, page * size, size],
            args: [sessionIdList, keyword, sessionIdList, keyword, sessionIdList, keyword, page * size, size],
            handler: handler
        });
    }

+ 18 - 12
src/server/repository/mysql/session.repo.js

@ -23,7 +23,9 @@ class SessionRepo {
        ImDb.execQuery({
            "sql": sessionSQL,
            "args": [sessionId],
            "handler": handler
            "handler": handler || function (err, res) {
                if(err) log.error(err);
            }
        });
    }
@ -40,7 +42,9 @@ class SessionRepo {
        ImDb.execQuery({
            "sql": sessionSQL,
            "args": [userId],
            "handler": handler
            "handler": handler || function (err, res) {
                if(err) log.error(err);
            }
        });
    }
@ -58,7 +62,9 @@ class SessionRepo {
        ImDb.execQuery({
            "sql": sessionSQL,
            "args": [userId, type],
            "handler": handler
            "handler": handler || function (err, res) {
                if(err) log.error(err);
            }
        });
    }
@ -82,7 +88,9 @@ class SessionRepo {
        ImDb.execQuery({
            sql: sql,
            args: [dateSpan * 3600 * 24, userId, userId],
            handler: handler
            handler: handler || function (err, res) {
                if(err) log.error(err);
            }
        });
    }
@ -98,12 +106,8 @@ class SessionRepo {
        ImDb.execQuery({
            "sql": sessionSQL,
            "args": [userId],
            "handler": function (err, res) {
                if (err) {
                    log.error("sql:" + sessionSQL + "data:userId:" + userId);
                }
                handler(err, res);
            "handler": handler || function (err, res) {
                if(err) log.error(err);
            }
        });
    }
@ -124,7 +128,9 @@ class SessionRepo {
        ImDb.execQuery({
            "sql": sql,
            "args": [sessionId, name, type, createDate, businessType, name],
            "handler": handler
            "handler": handler || function (err, res) {
                if(err) log.error(err);
            }
        });
    }
@ -145,7 +151,7 @@ class SessionRepo {
            "sql": sql,
            "args": [lastSenderId, lastSenderName, lastMessageTime, lastContent, lastContentType, sessionId],
            "handler": handler || function (err, res) {
                log.info("updateSessionLastStatus");
                if(err) log.error(err);
            }
        });
    }

+ 15 - 5
src/server/repository/mysql/topics.repo.js

@ -25,7 +25,9 @@ class TopicRepo {
        ImDb.execQuery({
            sql: sql,
            args: [topicId],
            handler: handler
            handler: handler || function (err, res) {
                if(err) log.error(err);
            }
        });
    }
@ -50,7 +52,9 @@ class TopicRepo {
        ImDb.execQuery({
            sql: sql,
            args: [topicId],
            handler: handler
            handler: handler || function (err, res) {
                if(err) log.error(err);
            }
        });
    }
@ -67,7 +71,9 @@ class TopicRepo {
        ImDb.execQuery({
            sql: sql,
            args: [sessionId],
            handler: handler
            handler: handler || function (err, res) {
                if(err) log.error(err);
            }
        });
    }
@ -84,7 +90,9 @@ class TopicRepo {
        ImDb.execQuery({
            sql: sql,
            args: [id],
            handler: handler
            handler: handler || function (err, res) {
                if(err) log.error(err);
            }
        });
    }
@ -94,7 +102,9 @@ class TopicRepo {
        ImDb.execQuery({
            sql: sql,
            args: [page,pagesize],
            handler: handler
            handler: handler || function (err, res) {
                if(err) log.error(err);
            }
        });

+ 7 - 7
src/server/resources/config/config.dev.js

@ -24,20 +24,20 @@ let wlyyServerConfig = {
};
// 个推AppStore版参数
/*let getTuiConfig = {
let getTuiConfig = {
    HOST: 'https://api.getui.com/apiex.htm',
    APPID: 'H6FYbDejks6VjMmW3uH7V6',
    APPKEY: '0PFWlKmLBN9YzhCfFWVgYA',
    MASTERSECRET: 'pvjCGtRZJx9SRVODkxc816'
};*/
    APPID: 'qWmRh2X88l7HuE36z3qBe8',
    APPKEY: 'EzERfV8c849lBkZqHWzQG1',
    MASTERSECRET: 'veXiajQrId6iojy7Qv8kZ2'
};
let getTuiConfig = {
/*let getTuiConfig = {
    HOST: 'https://api.getui.com/apiex.htm',
    APPID: 'DKgbGvbacm74nJJzen5ilA',
    APPSECRET: '4kGcL7e7kU6mbSqfEGZFW7',
    APPKEY: 'ArZfS2qvoA7N3hawOAGVC5',
    MASTERSECRET: '9lpy5vEss46tVzP1RCJiC4'
};
};*/
// 微信配置
let wechatConfig = {

+ 3 - 3
src/server/resources/config/config.prod.js

@ -25,9 +25,9 @@ let wlyyServerConfig = {
// 个推AppStore版参数
let getTuiConfig = {
    HOST: 'https://api.getui.com/apiex.htm',
    APPID: 'H6FYbDejks6VjMmW3uH7V6',
    APPKEY: '0PFWlKmLBN9YzhCfFWVgYA',
    MASTERSECRET: 'pvjCGtRZJx9SRVODkxc816'
    APPID: 'qWmRh2X88l7HuE36z3qBe8',
    APPKEY: 'EzERfV8c849lBkZqHWzQG1',
    MASTERSECRET: 'veXiajQrId6iojy7Qv8kZ2'
};
// 微信配置

+ 4 - 4
src/server/resources/config/config.test.js

@ -24,10 +24,10 @@ let wlyyServerConfig = {
// 个推AppStore版参数
let getTuiConfig = {
    HOST : 'https://api.getui.com/apiex.htm',
    APPID : 'H6FYbDejks6VjMmW3uH7V6',
    APPKEY : '0PFWlKmLBN9YzhCfFWVgYA',
    MASTERSECRET : 'pvjCGtRZJx9SRVODkxc816'
    HOST: 'https://api.getui.com/apiex.htm',
    APPID: 'qWmRh2X88l7HuE36z3qBe8',
    APPKEY: 'EzERfV8c849lBkZqHWzQG1',
    MASTERSECRET: 'veXiajQrId6iojy7Qv8kZ2'
};
// 微信配置

+ 9 - 18
src/server/util/db.util.js

@ -17,43 +17,34 @@ class DbUtil {
        if (config.showSQL) log.info(options.sql);
        pool.getConnection(function (err, connection) {
            // 查询参数
            let sql = options['sql'];
            let args = options['args'];
            let handler = options['handler'];
            if (err) {
                //log.error('Database - get connection failed, ' + err);
                handler(err, 'db-getConnection');
                return;
                return handler(err, null);
            }
            // 执行查询
            if (args) {
                connection.query(sql, args, function (err, results) {
                    if (err) {
                        //log.error('Database - execute query failed, ' + err);
                        handler(err, results);
                        return;
                        log.error("Execute SQL failed: " + sql);
                        return handler(err, results);
                    }
                    if(!handler)log.info("错误sql:"+options.sql);
                    // 处理结果
                    handler(err, results);
                    handler(null, results);
                });
            } else {
                connection.query(sql, function (err, results) {
                    if (err) {
                        //log.error('Database - execute query failed, ' + err);
                        handler(err, results);
                        log.error("Execute SQL failed: " + sql);
                        return;
                        return handler(err, results);
                    }
                    // 处理结果
                    handler(err, results);
                    handler(null, results);
                });
            }
@ -71,7 +62,7 @@ class DbUtil {
     *
     * @param stringArray
     */
    static stringArrayHash(stringArray){
    static stringArrayHash(stringArray) {
        let sortedArr = stringArray.sort();
        return crypto.createHash("sha1").update(sortedArr.join(",")).digest('hex');
    }

+ 1 - 1
src/server/util/model.util.js

@ -44,7 +44,7 @@ class ModelUtil {
    static emitError(eventEmitter, description, err) {
        ModelUtil.logError(description, err);
        eventEmitter.emit(MODEL_EVENTS.Error, {message: description});
        eventEmitter.emit(MODEL_EVENTS.Error, {message: description + ": " + err});
    };
    /**

+ 10 - 10
test/client/im.client.search.Test.js

@ -5,13 +5,13 @@ let imClient = require('../../src/client/im.client');
let userId = 'D2016008240002';
describe('API: Search', function () {
describe('API: Doctor Search', function () {
    // 搜索所有
    describe('search all object types', function () {
        it('should return 200', function (done) {
            imClient.Search.searchAll(userId, '哦',
            imClient.Sessions.Search.Doctor.searchAll(userId, '哦', false,
            function (data) {
                assert(data.length > 0, "Search must return at least one data");
                assert(data.messages.length > 0, "Search must return at least one data");
                console.log(data);
@ -25,11 +25,11 @@ describe('API: Search', function () {
    });
    // 搜索医生
    describe('search doctors', function () {
    describe('search doctor', function () {
        it('should return 200', function (done) {
            imClient.Search.searchDoctors(userId, '张', 1, 10,
            imClient.Sessions.Search.Doctor.searchDoctors(userId, '张', 1, 10,
                function (data) {
                    assert(data.length > 0, "Search must return at least one data");
                    assert(data.doctors.length > 0, "Search must return at least one data");
                    console.log(data);
@ -45,9 +45,9 @@ describe('API: Search', function () {
    // 搜索会话
    describe('search sessions', function () {
        it('should return 200', function (done) {
            imClient.Search.searchSessions('D2016008240003', '1', 1, 10,
            imClient.Sessions.Search.Doctor.searchSessions('D2016008240003', '丽', false, 1, 10,
                function (data) {
                    assert(data.length > 0, "Search must return at least one data");
                    assert(data.sessions.length > 0, "Search must return at least one data");
                    console.log(data);
@ -63,9 +63,9 @@ describe('API: Search', function () {
    // 搜索会话消息
    describe('search messages', function () {
        it('should return 200', function (done) {
            imClient.Search.searchMessages(userId, '哦', 1, 10,
            imClient.Sessions.Search.Doctor.searchMessages(userId, '哦', 1, 10,
                function (data) {
                    assert(data.length > 0, "Search must return at least one data");
                    assert(data.messages.length > 0, "Search must return at least one data");
                    console.log(data);

+ 81 - 0
test/client/im.client.patient.session.search.Test.js

@ -0,0 +1,81 @@
"use strict";
let assert = require('assert');
let imClient = require('../../src/client/im.client');
let userId = 'D2016008240003';
describe('API: Patient Search', function () {
    // 搜索所有
    describe('search all object types', function () {
        it('return a message', function (done) {
            imClient.Sessions.Search.Patient.searchAll(userId, '哦', false,
            function (data) {
                assert(data.messages.length > 0, "Search must return at least one data");
                console.log(data);
                done();
            },
            function (xhr, status, error) {
                assert.ok(false, xhr.responseJSON.message);
                done();
            });
        });
    });
    // 搜索患者
    describe('search patients', function () {
        it('return a patient', function (done) {
            imClient.Sessions.Search.Patient.searchPatient(userId, '丽', 1, 10,
                function (data) {
                    assert(data.patients.length > 0, "Search must return at least one data");
                    console.log(data);
                    done();
                },
                function (xhr, status, error) {
                    assert.ok(false, xhr.responseJSON.message);
                    done();
                });
        });
    });
    // 搜索会话
    describe('search sessions', function () {
        it('return a session', function (done) {
            imClient.Sessions.Search.Patient.searchSessions(userId, '廖小', false, 1, 10,
                function (data) {
                    assert(data.sessions.length > 0, "Search must return at least one data");
                    console.log(data);
                    done();
                },
                function (xhr, status, error) {
                    assert.ok(false, xhr.responseJSON.message);
                    done();
                });
        });
    });
    // 搜索会话消息
    describe('search messages', function () {
        it('return at least one message', function (done) {
            imClient.Sessions.Search.Patient.searchMessages(userId, '哦', 1, 10,
                function (data) {
                    assert(data.messages.length > 0, "Search must return at least one data");
                    console.log(data);
                    done();
                },
                function (xhr, status, error) {
                    assert.ok(false, xhr.responseJSON.message);
                    done();
                });
        });
    });
});