Browse Source

修改微信客户端

Sand 8 năm trước cách đây
mục cha
commit
314010059c

+ 24 - 0
src/server/endpoints/session.endpoint.js

@ -0,0 +1,24 @@
/**
 * 会话接口。
 *
 * author: Sand
 * since: 12/15/2016
 */
"use strict";
let express = require('express');
let router = express.Router();
let http = require('http');
let configFile = require('../include/commons').CONFIG_FILE;
let config = require('../resources/config/' + configFile);
let log = require("../util/log.js");
let objectUtil = require('../util/objectUtil');
let UserStatus = require('../models/user.status');
const APIv1 = require('../include/endpoints').APIv1;
const MODEL_EVENTS = require('../include/commons').MODEL_EVENTS;
router.get(APIv1.Chats)
module.exports = router;

+ 7 - 8
src/server/endpoints/users.endpoint.js

@ -12,7 +12,7 @@ let config = require('../resources/config/' + configFile);
let log = require("../util/log.js");
let log = require("../util/log.js");
let objectUtil = require('../util/objectUtil');
let objectUtil = require('../util/objectUtil');
let UserStatus = require('../models/user.status');
let Users = require('../models/user/users');
const APIv1 = require('../include/endpoints').APIv1;
const APIv1 = require('../include/endpoints').APIv1;
const MODEL_EVENTS = require('../include/commons').MODEL_EVENTS;
const MODEL_EVENTS = require('../include/commons').MODEL_EVENTS;
@ -50,17 +50,16 @@ router.get(APIv1.Users.Login, function (req, res) {
        throw {httpStatus: 406, message: 'Missing field: platform.'};
        throw {httpStatus: 406, message: 'Missing field: platform.'};
    }
    }
    let userStatus = new UserStatus();
    userStatus.on(MODEL_EVENTS.OK, function (message) {
    let users = new Users();
    users.on(MODEL_EVENTS.OK, function (message) {
        res.status(200).send(message);
        res.status(200).send(message);
    });
    });
    userStatus.on(MODEL_EVENTS.Error, function (message) {
    users.on(MODEL_EVENTS.Error, function (message) {
        res.status(500).send(message);
        res.status(500).send(message);
    });
    });
    userStatus.login(userId, token, clientId, platform);
    users.login(userId, token, clientId, platform);
});
});
/**
/**
@ -78,7 +77,7 @@ router.get(APIv1.Users.Logout, function (req, res) {
        throw {httpStatus: 406, message: 'Logout Failed. Missing field: user_id.'};
        throw {httpStatus: 406, message: 'Logout Failed. Missing field: user_id.'};
    }
    }
    let userStatus = new UserStatus();
    let userStatus = new Users();
    userStatus.on(MODEL_EVENTS.OK, function (message) {
    userStatus.on(MODEL_EVENTS.OK, function (message) {
        res.status(200).send(message);
        res.status(200).send(message);
@ -111,7 +110,7 @@ router.post(APIv1.Users.UserStatus, function (req, res) {
        throw {httpStatus: 406, message: 'Validation Failed. Missing fields.'};
        throw {httpStatus: 406, message: 'Validation Failed. Missing fields.'};
    }
    }
    let userStatus = new UserStatus();
    let userStatus = new Users();
    userStatus.on(MODEL_EVENTS.OK, function (message) {
    userStatus.on(MODEL_EVENTS.OK, function (message) {
        res.status(200).send(message);
        res.status(200).send(message);

+ 0 - 79
src/server/models/user.status.js

@ -1,79 +0,0 @@
/**
 * 用户状态模型。此处状态是指用户移动应用App状态,如用户是否已登录,App是否已打开。
 *
 * 用户状态区分患者与医生。**当前**仅是与医生相关,若需要增加患者相关内容,
 * 需要增加在用户判断。
 *
 * author: Sand
 * since: 2016.11.20
 */
"use strict";
let BaseModel = require('./base.model');
let doctorRepo = require('../repository/mysql/doctor.repo');
let modelUtil = require("../util/modelUtil.js");
let Token = require('./auth/token');
class UserStatus extends BaseModel {
    constructor() {
        super();
    }
    /**
     * 登录。
     */
    login(userId, token, clientId, platform) {
        let self = this;
        doctorRepo.deleteToken(token, function (err, result) {
            if (err) {
                modelUtil.emitDbError(self.eventEmitter, 'Error occurs when user login and delete token', err);
            } else {
                doctorRepo.login(userId, token, clientId, platform,
                    function (err, result) {
                        if (err) {
                            modelUtil.emitDbError(self.eventEmitter, 'Error occurs when user login and delete token', err);
                        }
                        let token = new Token(userId, clientId, platform);
                        modelUtil.emitData(self.eventEmitter, {token: token.value});
                    });
            }
        });
    }
    /**
     * 退出。
     */
    logout(userId) {
        let self = this;
        doctorRepo.logout(userId,
            function (err, result) {
                if (err) {
                    modelUtil.emitDbError(self.eventEmitter, 'Logout failed', err);
                    return;
                }
                modelUtil.emitData(self.eventEmitter, {});
            });
    }
    /**
     * 更新用户状态。
     */
    updateStatus(userId, status) {
        let self = this;
        doctorRepo.updateStatus(userId, status,
            function (err, result) {
                if (err) {
                    modelUtil.emitDbError(self.eventEmitter, 'Update user status failed', err);
                    return;
                }
                modelUtil.emitData(self.eventEmitter, {});
            });
    }
}
module.exports = UserStatus;

+ 2 - 2
src/server/models/user/doctor.js

@ -14,8 +14,8 @@ let pmRepo = require('../../repository/mysql/private.msg.repo');
let nmRepo = require("../../repository/mysql/notify.msg.repo");
let nmRepo = require("../../repository/mysql/notify.msg.repo");
let smRepo = require("../../repository/mysql/system.msg.repo.js");
let smRepo = require("../../repository/mysql/system.msg.repo.js");
let statsRepo = require("../../repository/mysql/stats.msg.repo");
let statsRepo = require("../../repository/mysql/stats.msg.repo");
let objectUtil = require("../../util/objectUtil.js");
let modelUtil = require('../../util/modelUtil');
let objectUtil = require("../../util/object.util.js");
let modelUtil = require('../../util/model.util');
const CONTENT_TYPES = require('../../include/commons').CONTENT_TYPE;
const CONTENT_TYPES = require('../../include/commons').CONTENT_TYPE;
const PLATFORMS = require('../../include/commons').PLATFORM;
const PLATFORMS = require('../../include/commons').PLATFORM;

+ 77 - 9
src/server/models/user/users.js

@ -9,21 +9,23 @@
const RedisKeys = require('../../include/commons').REDIS_KEYS;
const RedisKeys = require('../../include/commons').REDIS_KEYS;
let RedisModel = require('../redis.model');
let RedisModel = require('../redis.model');
let DoctorRepo = require('../../repository/mysql/doctor.repo');
let PatientRepo = require('../../repository/mysql/patient.repo');
let UserStatusRepo = require('../../repository/mysql/user.status.repo');
let Doctor = require('./doctor');
let Doctor = require('./doctor');
let Patient = require('./patient');
let Patient = require('./patient');
let DoctorRepo = require('../../repository/mysql/doctor.repo');
let PatientRepo = require('../../repository/mysql/patient.repo');
let AppStatusRepo = require('../../repository/mysql/app.status.repo');
let RedisClient = require('../../repository/redis/redis.client');
let RedisClient = require('../../repository/redis/redis.client');
let redisConn = RedisClient.redisClient().connection;
let redisConn = RedisClient.redisClient().connection;
let async = require('async');
let async = require('async');
let bearcat = require('bearcat');
let bearcat = require('bearcat');
let log = require('../../util/log');
let log = require('../../util/log');
let objectUtil = require('../../util/objectUtil');
var imDb = require('../../repository/mysql/db/im.db');
var imDb = require('../../repository/mysql/db/im.db');
let users = null;
class Users extends RedisModel {
class Users extends RedisModel {
    constructor() {
    constructor() {
        super();
        super();
@ -32,7 +34,7 @@ class Users extends RedisModel {
    }
    }
    /**
    /**
     * 获取用户。
     * 获取用户,直接从MYSQL获取,缓存是否有在不能确定。
     *
     *
     * @param userId
     * @param userId
     * @param outCallback
     * @param outCallback
@ -65,12 +67,12 @@ class Users extends RedisModel {
    }
    }
    /**
    /**
     * 获取用户状态。
     * 获取客户端App状态。
     *
     *
     * @param userId
     * @param userId
     * @param outCallback
     * @param outCallback
     */
     */
    getUserStatus(userId, outCallback){
    getAppStatus(userId, outCallback){
        let self = this;
        let self = this;
        async.waterfall([
        async.waterfall([
            // get from redis
            // get from redis
@ -78,7 +80,7 @@ class Users extends RedisModel {
                let userStatusKey = self.makeRedisKey(RedisKeys.UserStatus, userId);
                let userStatusKey = self.makeRedisKey(RedisKeys.UserStatus, userId);
                redisConn.hgetallAsync(userStatusKey).then(function (res) {
                redisConn.hgetallAsync(userStatusKey).then(function (res) {
                   if(res === null){
                   if(res === null){
                       callback(null);
                       callback(null);  // get from mysql
                   } else {
                   } else {
                       outCallback(null, res);
                       outCallback(null, res);
                   }
                   }
@ -86,7 +88,7 @@ class Users extends RedisModel {
            },
            },
            // get from MySQL
            // get from MySQL
            function () {
            function () {
                UserStatusRepo.getUserStatus(userId, function (err, res) {
                AppStatusRepo.findOne(userId, function (err, res) {
                    let userStatus = null;
                    let userStatus = null;
                    if(res.length > 0){
                    if(res.length > 0){
                        userStatus = {};
                        userStatus = {};
@ -103,6 +105,64 @@ class Users extends RedisModel {
        ]);
        ]);
    }
    }
    /**
     * 更新客户端App状态。
     *
     * @param userId
     * @param appInBg
     * @param outCallback
     */
    updateAppStatus(userId, appInBg, outCallback){
        let self = this;
        DoctorRepo.updateStatus(userId, status,
            function (err, result) {
                if (err) {
                    ModelUtil.emitDbError(self.eventEmitter, 'Update user status failed', err);
                    return;
                }
                ModelUtil.emitData(self.eventEmitter, {});
            });
    }
    /**
     * 用户登录。
     *
     * @param userId
     * @param outCallback
     */
    login(userId, outCallback){
        let self = this;
        DoctorRepo.deleteToken(token, function (err, result) {
            if (err) {
                ModelUtil.emitDbError(self.eventEmitter, 'Error occurs when user login and delete token', err);
            } else {
                doctorRepo.login(userId, token, clientId, platform,
                    function (err, result) {
                        if (err) {
                            ModelUtil.emitDbError(self.eventEmitter, 'Error occurs when user login and delete token', err);
                        }
                        let token = new Token(userId, clientId, platform);
                        ModelUtil.emitData(self.eventEmitter, {token: token.value});
                    });
            }
        });
    }
    logout(userId, outCallback){
        let self = this;
        doctorRepo.logout(userId,
            function (err, result) {
                if (err) {
                    ModelUtil.emitDbError(self.eventEmitter, 'Logout failed', err);
                    return;
                }
                ModelUtil.emitData(self.eventEmitter, {});
            });
    }
    /**
    /**
     * 用户ID是否属于患者。
     * 用户ID是否属于患者。
     *
     *
@ -140,6 +200,14 @@ class Users extends RedisModel {
                callback(null, res !== 0);
                callback(null, res !== 0);
            });
            });
    }
    }
    static instance(){
        if(users === null){
            users = new Users();
        }
        return users;
    }
}
}
let Promises = require('bluebird');
let Promises = require('bluebird');

+ 148 - 0
src/server/models/wechat.client/wechat.client.js

@ -0,0 +1,148 @@
/**
 * 微信客户端。
 *
 * author: lyr
 * author: sand
 * since: 2016/11/25
 */
"use strict"
let https = require('https');
let configFile = require('../../include/commons').CONFIG_FILE;
let config = require('../resources/config/' + configFile);
let log = require('../../util/log');
let WechatTokenRepo = require('../../repository/mysql/wechat.token.repo.js');
class WeChatClient {
    constructor() {
    }
    /**
     * 发送微信模板消息
     *
     * @param message {touser:"", template_id:"", url:"", data:{firts: {value:"", color:""}}}
     */
    static sendWxTemplateMessage(message, handler) {
        WehcatClient.getAccessToken(function (err, token) {
            if (err) {
                log.error("get access_token failed:" + err);
                return;
            }
            var opt = {
                host: 'api.weixin.qq.com',
                path: '/cgi-bin/message/template/send?access_token=' + token,
                method: 'POST'
            };
            var msg = JSON.stringify(message);
            log.info("sending wechat template message:" + msg);
            // 发送模板消息
            var req = https.request(opt, function (res) {
                res.setEncoding('utf8');
                var data = "";
                res.on('data', (d) => {
                    data += d;
                });
                res.on('end', () => {
                    var result = JSON.parse(data);
                    if (result && result.errcode === 0) {
                        log.info("send wechat template message success:" + msg);
                        if (handler) {
                            handler(null, result);
                        }
                    } else {
                        log.error("send wechat template message failed:" + msg);
                        if (handler) {
                            handler(result, null);
                        }
                    }
                });
                res.on('error', function (err) {
                    log.error('send wechat template message failed: ' + err.message);
                    if (handler) {
                        handler(err, null);
                    }
                });
            }).on('error', (e) => {
                log.error("send wechat template message failed:" + e.message);
                if (handler) {
                    handler(e, null);
                }
            });
            req.end(msg);
        });
    };
    /**
     * 获取微信access_token
     *
     * @param handler 回调函数
     */
    static getAccessToken(handler) {
        WechatTokenRepo.findOne(function (err, result) {
            if (err) {
                log.error("get wechat accessToken failed", err);
                return;
            }
            var data = result && result.length > 0 ? result[0] : null;
            var accessToken = "";
            if (data) {
                // 判断access_token是否有效
                if ((new Date().getTime() - data.add_timestamp) < (data.expires_in * 1000)) {
                    accessToken = data.access_token;
                }
            }
            // access_token为空时从微信新获取并执行回调,否则直接执行回调
            if (!accessToken) {
                var token_url = "https://api.weixin.qq.com/cgi-bin/token?";
                var params = "grant_type=client_credential&appid=" + config.wechatConfig.appId
                    + "&secret=" + config.wechatConfig.appSecret;
                // 从微信获取access_token
                https.get(token_url + params, function (res) {
                    var data = '';
                    res.on('data', (d) => {
                        data += d;
                    });
                    res.on('end', () => {
                        data = data ? JSON.parse(data) : {};
                        if (data.access_token) {
                            accessToken = data.access_token;
                            var expiresIn = data.expires_in;
                            WechatTokenRepo.save(accessToken, expiresIn, new Date(), function (err, result) {
                                if (err) {
                                    log.error("insert wechat access_token failed:" + err.message);
                                }
                            });
                            if (handler) handler(null, accessToken);
                        } else {
                            log.error("get wechat access_token failed:" + data);
                            if (handler) handler(data, null);
                        }
                    });
                }).on('error', (e) => {
                    log.error("get wechat access_token from wechat failed:" + e.message);
                    if (handler) handler(data, null);
                });
            } else {
                if (handler) handler(data, null);
            }
        });
    };
}
module.exports = WeChatClient;

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

@ -0,0 +1,77 @@
/**
 * 客户端App状态库。
 *
 * 客户端App状态对每个用户来讲只保存一个,不区分设备。
 * 因此当用户从iOS切换至Android平台时,先前的状态将被新的替换。
 *
 * author: Sand
 * since: 12/14/2016
 */
"use strict";
var imDb = require("./db/im.db.js");
class AppStatusRepo {
    constructor() {
    }
    /**
     * 查找客户端App状态。
     *
     * @param userId
     * @param handler
     */
    static findOne(userId, handler) {
        let sql = "SELECT platform, token, client_id, app_in_bg, last_login_time " +
            "FROM app_status WHERE user_id = ?";
        imDb.execQuery({
            "sql": sql,
            "args": [userId],
            "handler": handler
        });
    };
    /**
     * 保存App的最新状态。
     *
     * @param userId
     * @param token
     * @param client_id
     * @param platform
     * @param handler
     */
    static save(userId, token, client_id, platform, handler) {
        imDb.execQuery({
            "sql": "INSERT INTO app_status (user_id, platform, token, client_id, app_in_bg, last_login_time) VALUES (?,?,?,?,1,1) ON" +
            " DUPLICATE KEY UPDATE token=?,client_id=?,platform=?,is_online=1,status=1",
            "args": [userId, token, client_id, platform, token, client_id, platform],
            "handler": handler
        });
    };
    /**
     * 更新App前后台状态。
     *
     * @param userId
     * @param appInBg
     * @param handler
     */
    static updateStatus(userId, appInBg, handler) {
        imDb.execQuery({
            "sql": "UPDATE user SET app_in_bg = ? WHERE user_id = ",
            "args": [appInBg, userId],
            "handler": handler
        });
    };
    static delete(token, handler) {
        imDb.execQuery({
            "sql": "DELETE FROM user WHERE token=?",
            "args": [token],
            "handler": handler
        });
    };
}
module.exports = AppStatusRepo;

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

@ -11,9 +11,6 @@ class DoctorRepo {
    constructor() {
    constructor() {
    }
    }
    static update() {
    }
    static findOne(doctorId, handler) {
    static findOne(doctorId, handler) {
        imDb.execQuery({
        imDb.execQuery({
            "sql": "select id, name, sex, birthdate, avatar from doctors where id = ? ",
            "sql": "select id, name, sex, birthdate, avatar from doctors where id = ? ",
@ -21,39 +18,6 @@ class DoctorRepo {
            "handler": handler
            "handler": handler
        });
        });
    };
    };
    static login(doctorId, token, client_id, platform, handler) {
        imDb.execQuery({
            "sql": "INSERT INTO user (user_id,token,client_id,platform,is_online,status) VALUES (?,?,?,?,1,1) ON" +
            " DUPLICATE KEY UPDATE token=?,client_id=?,platform=?,is_online=1,status=1",
            "args": [doctorId, token, client_id, platform, token, client_id, platform],
            "handler": handler
        });
    };
    static logout(userId, handler) {
        imDb.execQuery({
            "sql": "UPDATE user SET is_online='0',status='0' WHERE user_id=?",
            "args": [userId],
            "handler": handler
        });
    };
    static deleteToken(token, handler) {
        imDb.execQuery({
            "sql": "DELETE FROM user WHERE token=?",
            "args": [token],
            "handler": handler
        });
    };
    static updateStatus(userId, status, handler) {
        imDb.execQuery({
            "sql": "UPDATE user SET status=? WHERE user_id=?",
            "args": [status, userId],
            "handler": handler
        });
    };
}
}
module.exports = DoctorRepo;
module.exports = DoctorRepo;

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

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

+ 35 - 26
src/server/repository/mysql/wechat.token.repo.js

@ -1,34 +1,43 @@
/**
/**
 * 微信AccessToken
 * 微信AccessToken库。
 *
 *
 * Created by lyr-pc on 2016/11/25.
 * author:lyr
 * since: 2016/11/25.
 */
 */
"use strict";
"use strict";
var log = require('../../util/log');
var log = require('../../util/log');
var wlyyDb = require('../mysql/db/im.db');
var wlyyDb = require('../mysql/db/im.db');
/**
 * 获取微信AccessToken
 *
 * @param handler 回调函数
 */
exports.getAccessToken = function(handler) {
    wlyyDb.execQuery({
        "sql":"select p.* from wx_access_token p order by p.add_timestamp desc limit 0,1"
        ,"handler":handler
    });
};
class WeChatTokenRepo {
    constructor() {
    }
/**
 * 保存AccessToken
 *
 * @param accessToken 微信AccessToken
 * @param hanlder 回调函数
 */
exports.saveAccessToken = function(accessToken,handler) {
    wlyyDb.execQuery({
        "sql":"insert into wx_access_token (access_token,add_timestamp,expires_in,czrq) values (?,?,?,?)"
        ,"args":accessToken
        ,"handler":handler
    });
}
    /**
     * 获取微信AccessToken
     *
     * @param handler 回调函数
     */
    static findOne(handler) {
        wlyyDb.execQuery({
            "sql": "select access_token, expiry_date, create_time from wechat_access_tokens order by create_time desc limit 0, 1"
            , "handler": handler
        });
    };
    /**
     * 保存AccessToken
     *
     * @param accessToken 微信AccessToken
     * @param handler 回调函数
     */
    static save(accessToken, expiryDate, createTime, handler) {
        wlyyDb.execQuery({
            "sql": "insert into wechat_access_tokens (access_token, expiry_date, create_time) values (?,?,?)"
            , "args": [accessToken, expiryDate, createTime]
            , "handler": handler
        });
    }
}
module.exports = WeChatTokenRepo;

+ 9 - 11
src/server/resources/config/config.dev.js

@ -7,7 +7,7 @@ let wlyyDbConfig = {
    password: '123456',
    password: '123456',
    database: 'wlyy',
    database: 'wlyy',
    connectionLimit: '50',
    connectionLimit: '50',
    charset : 'utf8mb4'
    charset: 'utf8mb4'
};
};
// IM数据库配置
// IM数据库配置
@ -17,7 +17,7 @@ let imDbConfig = {
    password: '123456',
    password: '123456',
    database: 'ichat',
    database: 'ichat',
    connectionLimit: '50',
    connectionLimit: '50',
    charset : 'utf8mb4'
    charset: 'utf8mb4'
};
};
// Redis
// Redis
@ -57,13 +57,12 @@ let geTuiAppStoreCfg = {
// 微信配置
// 微信配置
let wechatConfig = {
let wechatConfig = {
    appId: 'wxd03f859efdf0873d'
    , appSecret: '2935b54b53a957d9516c920a544f2537'
    , token: '27eb3bb24f149a7760cf1bb154b08040'
    , baseUrl: 'weixin.xmtyw.cn/wlyy'
    , template: {
        // 咨询回复模板
        consultTemplate: 'qSOW0DBxO3qEBm4ucG0Ial0jxsOyD7_f2TFK5e-mQEc'
    appId: 'wxd03f859efdf0873d',
    appSecret: '2935b54b53a957d9516c920a544f2537',
    token: '27eb3bb24f149a7760cf1bb154b08040',
    baseUrl: 'weixin.xmtyw.cn/wlyy',
    template: {
        consultTemplate: 'qSOW0DBxO3qEBm4ucG0Ial0jxsOyD7_f2TFK5e-mQEc'  // 咨询回复模板
    }
    }
};
};
@ -82,5 +81,4 @@ exports.geTuiConfig = geTuiConfig;
exports.geTuiAppStoreCfg = geTuiAppStoreCfg;
exports.geTuiAppStoreCfg = geTuiAppStoreCfg;
exports.wlyyServerConfig = wlyyServerConfig;
exports.wlyyServerConfig = wlyyServerConfig;
exports.transServerConfig = transServerConfig;
exports.transServerConfig = transServerConfig;
exports.wechatConfig = wechatConfig;
exports.wechatConfig = wechatConfig;

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

@ -55,13 +55,12 @@ let transServerConfig = {
// 微信配置
// 微信配置
let wechatConfig = {
let wechatConfig = {
    appId: 'wxad04e9c4c5255acf'
    , appSecret: 'ae77c48ccf1af5d07069f5153d1ac8d3'
    , token: '27eb3bb24f149a7760cf1bb154b08040'
    , baseUrl: 'www.xmtyw.cn/wlyy'
    , template: {
        // 咨询回复模板
        consultTemplate: '0mF_vHj-ILx8EH8DwzmAi7LqzjqYiU9IrSRRmziTZyc'
    appId: 'wxad04e9c4c5255acf',
    appSecret: 'ae77c48ccf1af5d07069f5153d1ac8d3',
    token: '27eb3bb24f149a7760cf1bb154b08040',
    baseUrl: 'www.xmtyw.cn/wlyy',
    template: {
        consultTemplate: '0mF_vHj-ILx8EH8DwzmAi7LqzjqYiU9IrSRRmziTZyc'  // 咨询回复模板
    }
    }
};
};
@ -79,4 +78,5 @@ exports.redisConfig = redisConfig;
exports.geTuiConfig = geTuiConfig;
exports.geTuiConfig = geTuiConfig;
exports.geTuiAppStoreCfg = geTuiAppStoreCfg;
exports.geTuiAppStoreCfg = geTuiAppStoreCfg;
exports.wlyyServerConfig = wlyyServerConfig;
exports.wlyyServerConfig = wlyyServerConfig;
exports.transServerConfig = transServerConfig;
exports.transServerConfig = transServerConfig;
exports.wechatConfig = wechatConfig;

+ 23 - 11
src/server/resources/config/config.test.js

@ -25,6 +25,18 @@ let redisConfig = {
    db: 1
    db: 1
};
};
// 三师后台
var wlyyServerConfig = {
    host: '172.19.103.88',
    port: 9092
};
// 透传服务
var transServerConfig = {
    host: '172.19.103.76',
    port: 8000
};
// 企业版的推送配置
// 企业版的推送配置
var geTuiConfig = {
var geTuiConfig = {
    HOST: 'https://api.getui.com/apiex.htm',
    HOST: 'https://api.getui.com/apiex.htm',
@ -41,16 +53,15 @@ var geTuiAppStoreCfg = {
    MASTERSECRET : 'pvjCGtRZJx9SRVODkxc816'
    MASTERSECRET : 'pvjCGtRZJx9SRVODkxc816'
};
};
// 三师后台
var wlyyServerConfig = {
    host: '172.19.103.88',
    port: 9092
};
// 透传服务
var transServerConfig = {
    host: '172.19.103.76',
    port: 8000
// 微信配置
let wechatConfig = {
    appId: 'wxd03f859efdf0873d',
    appSecret: '2935b54b53a957d9516c920a544f2537',
    token: '27eb3bb24f149a7760cf1bb154b08040',
    baseUrl: 'weixin.xmtyw.cn/wlyy',
    template: {
        consultTemplate: 'qSOW0DBxO3qEBm4ucG0Ial0jxsOyD7_f2TFK5e-mQEc'  // 咨询回复模板
    }
};
};
exports.app = 'im.server';
exports.app = 'im.server';
@ -67,4 +78,5 @@ exports.redisConfig = redisConfig;
exports.geTuiConfig = geTuiConfig;
exports.geTuiConfig = geTuiConfig;
exports.geTuiAppStoreCfg = geTuiAppStoreCfg;
exports.geTuiAppStoreCfg = geTuiAppStoreCfg;
exports.wlyyServerConfig = wlyyServerConfig;
exports.wlyyServerConfig = wlyyServerConfig;
exports.transServerConfig = transServerConfig;
exports.transServerConfig = transServerConfig;
exports.wechatConfig = wechatConfig;

+ 22 - 5
src/server/resources/schema/ichat_schema.1.2.8.sql

@ -26,7 +26,10 @@ DROP TABLE IF EXISTS `participants` CASCADE
DROP TABLE IF EXISTS `sessions` CASCADE
DROP TABLE IF EXISTS `sessions` CASCADE
;
;
DROP TABLE IF EXISTS `user_status` CASCADE
DROP TABLE IF EXISTS `app_status` CASCADE
;
DROP TABLE IF EXISTS `wechat_access_tokens` CASCADE
;
;
/* Create Tables */
/* Create Tables */
@ -103,7 +106,7 @@ CREATE TABLE `sessions`
) COMMENT='会话'
) COMMENT='会话'
;
;
CREATE TABLE `user_status`
CREATE TABLE `app_status`
(
(
	`user_id` VARCHAR(50) NOT NULL COMMENT '用户ID',
	`user_id` VARCHAR(50) NOT NULL COMMENT '用户ID',
	`platform` TINYINT COMMENT '平台,0为iOS,1为安卓',
	`platform` TINYINT COMMENT '平台,0为iOS,1为安卓',
@ -112,7 +115,16 @@ CREATE TABLE `user_status`
	`app_in_bg` TINYINT COMMENT 'App是否处于后台状态',
	`app_in_bg` TINYINT COMMENT 'App是否处于后台状态',
	`last_login_time` TIMESTAMP(0) COMMENT '最后登录 时间',
	`last_login_time` TIMESTAMP(0) COMMENT '最后登录 时间',
	CONSTRAINT `PK_user_status` PRIMARY KEY (`user_id`)
	CONSTRAINT `PK_user_status` PRIMARY KEY (`user_id`)
) COMMENT='用户状态表'
) COMMENT='app端状态'
;
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 Primary Keys, Indexes, Uniques, Checks */
/* Create Primary Keys, Indexes, Uniques, Checks */
@ -147,11 +159,16 @@ ALTER TABLE `muc_messages`
SET FOREIGN_KEY_CHECKS=1;
SET FOREIGN_KEY_CHECKS=1;
/* 用户视图:医生与患者 */
/* 用户视图:医生、患者、用户微信状态*/
create or replace view doctors as 
create or replace view doctors as 
select code id, name, sex, birthday birthdate, photo avatar, level
select code id, name, sex, birthday birthdate, photo avatar, level
from wlyy.wlyy_doctor;
from wlyy.wlyy_doctor;
create or replace view patients as 
create or replace view patients as 
select code id, name, sex, birthday birthdate, photo avatar, openid
select code id, name, sex, birthday birthdate, photo avatar, openid
from wlyy.wlyy_patient;
from wlyy.wlyy_patient;
create or replace view wechat_status as
select code user_id, openid open_id
from wlyy.wlyy_patient
;

+ 25 - 0
src/server/resources/schema/temp.sql

@ -0,0 +1,25 @@
/* ---------------------------------------------------- */
/*  Generated by Enterprise Architect Version 12.0 		*/
/*  Created On : 15-Dec-2016 10:44:54 AM 				*/
/*  DBMS       : MySql 						*/
/* ---------------------------------------------------- */
SET FOREIGN_KEY_CHECKS=0
/* Drop Tables */
DROP TABLE IF EXISTS `wechat_access_tokens` CASCADE
;
/* Create Tables */
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'
;
SET FOREIGN_KEY_CHECKS=1

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

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

src/server/util/controllerUtil.js → src/server/util/controller.util.js


src/server/util/dbUtil.js → src/server/util/db.util.js


+ 15 - 0
src/server/util/endpoint.validator.js

@ -0,0 +1,15 @@
/**
 * REST接口参数校验器。
 *
 * author: Sand
 * since: 12/15/2016
 */
"use strict";
class EndPointValidator{
    constructor(){
    }
}
module.exports = EndPointValidator;

src/server/util/modelUtil.js → src/server/util/model.util.js


+ 67 - 0
src/server/util/object.util.js

@ -0,0 +1,67 @@
/**
 * 对象工具。简化常见的对象检查,转换操作。
 *
 * author: Sand
 * since: 12/15/2016
 */
"use strict";
class ObjectUtil {
    constructor() {
    }
    /**
     * 检查对象是否为JSON。
     *
     * @see http://stackoverflow.com/questions/11182924/how-to-check-if-javascript-object-is-json
     *
     * @param value
     */
    static isJsonObject(value) {
        return {}.constructor === value.constructor;
    };
    /**
     * 检查对象是否具有指定的属性列表。
     *
     * @param arguments 可变参数列表
     * @returns {{pass: boolean, message: string}}
     */
    static fieldsCheck() {
        var ret = {pass: false, message: ''};
        if (arguments.length <= 1) {
            ret.message = 'Function arguments not enough.';
            return ret;
        }
        var obj = arguments[0];
        if (!this.isJsonObject(obj)) {
            ret.message = 'Function first argument must be a dict.';
            return ret;
        }
        ret.pass = true;
        ret.message = 'Missing field(s): ';
        for (var i = 1; i < arguments.length; ++i) {
            if (!obj.hasOwnProperty(arguments[i])) {
                ret.pass = false;
                ret.message += arguments[i];
                if (i !== arguments.length - 1) {
                    ret.message += ", ";
                }
            }
        }
        if (ret.pass) ret.message = null;
        return ret;
    };
    static timestampToLong(tm) {
        return Date.parse(new Date(tm));
    };
}
module.exports = ObjectUtil;

+ 0 - 54
src/server/util/objectUtil.js

@ -1,54 +0,0 @@
"use strict";
/**
 * 检查对象是否为JSON。
 *
 * @see http://stackoverflow.com/questions/11182924/how-to-check-if-javascript-object-is-json
 *
 * @param object
 */
exports.isJsonObject = function(value){
    return {}.constructor === value.constructor;
};
/**
 * 检查对象是否具有指定的属性列表。
 *
 * @param arguments
 * @returns {{pass: boolean, message: string}}
 */
exports.fieldsCheck = function(){
    var ret = {pass: false, message: ''};
    if(arguments.length <= 1){
        ret.message = 'Function arguments not enough.';
        return ret;
    }
    var obj = arguments[0];
    if(!this.isJsonObject(obj)){
        ret.message = 'Function first argument must be a dict.';
        return ret;
    }
    ret.pass = true;
    ret.message = 'Missing field(s): ';
    for(var i = 1; i < arguments.length; ++i){
        if(!obj.hasOwnProperty(arguments[i])){
            ret.pass = false;
            ret.message += arguments[i];
            if(i !== arguments.length - 1){
                ret.message += ", ";
            }
        }
    }
    if(ret.pass) ret.message = null;
    return ret;
};
exports.timestampToLong = function(tm){
    return Date.parse(new Date(tm));
};

+ 0 - 149
src/server/util/wechatUtil.js

@ -1,149 +0,0 @@
/**
 * 微信工具类
 *
 * Created by lyr-pc on 2016/11/25.
 */
"use strict"
var log = require('./log');
var configFile = require('../include/commons').CONFIG_FILE;
var config = require('../resources/config/' + configFile);
var wxTokenRepo = require('../repository/mysql/wechat.token.repo.js');
var https = require('https');
/**
 * 发送微信模板消息
 *
 * @param message {"touser":"","template_id":"","url":"","data":{"firts":{"value":"","color":""}}}
 */
function sendWxTemplateMessage(message, handler) {
    getWxAccessToken(function (err, token) {
        if (err) {
            log.error("get access_token failed:" + err);
            return;
        }
        var opt = {
            host: 'api.weixin.qq.com',
            path: '/cgi-bin/message/template/send?access_token=' + token,
            method: 'POST'
        };
        var msg = JSON.stringify(message);
        log.info("sending wechat template message:" + msg);
        // 发送模板消息
        var req = https.request(opt, function (res) {
            res.setEncoding('utf8');
            var data = "";
            res.on('data', (d) => {
                data += d;
            });
            res.on('end', () => {
                var result = JSON.parse(data);
                if (result && result.errcode === 0) {
                    log.info("send wechat template message success:" + msg);
                    if (handler) {
                        handler(null, result);
                    }
                } else {
                    log.error("send wechat template message failed:" + msg);
                    if (handler) {
                        handler(result, null);
                    }
                }
            });
            res.on('error', function (err) {
                log.error('send wechat template message failed: ' + err.message);
                if (handler) {
                    handler(err, null);
                }
            });
        }).on('error', (e) => {
            log.error("send wechat template message failed:" + e.message);
            if (handler) {
                handler(e, null);
            }
        });
        req.end(msg);
    });
};
/**
 * 获取微信access_token
 *
 * @param handler 回调函数
 */
function getWxAccessToken(handler) {
    wxTokenRepo.getAccessToken(function (err, result) {
        if (err) {
            log.error("get wechat accessToken failed", err);
            return;
        }
        var data = result && result.length > 0 ? result[0] : null;
        var accessToken = "";
        if (data) {
            // 判断access_token是否有效
            if ((new Date().getTime() - data.add_timestamp) < (data.expires_in * 1000)) {
                accessToken = data.access_token;
            }
        }
        // access_token为空时从微信重新获取并执行回调,否则直接执行回调
        if (!accessToken) {
            var token_url = "https://api.weixin.qq.com/cgi-bin/token?";
            var params = "grant_type=client_credential&appid=" + config.wechatConfig.appId
                + "&secret=" + config.wechatConfig.appSecret;
            // 从微信获取access_token
            https.get(token_url + params, function (res) {
                var data = '';
                res.on('data', (d) => {
                    data += d;
                });
                res.on('end', () => {
                    data = data ? JSON.parse(data) : {};
                    if (data.access_token) {
                        accessToken = data.access_token;
                        var expiresIn = data.expires_in;
                        if (handler) {
                            // 执行回调
                            handler(null, accessToken);
                        }
                        var tokenData = [accessToken, new Date().getTime(), expiresIn, new Date()];
                        // 保存access_token
                        wxTokenRepo.saveAccessToken(tokenData, function (err, result) {
                            if (err) {
                                log.error("insert wechat access_token failed:" + err.message);
                            }
                        })
                    } else {
                        log.error("get wechat access_token failed:" + data);
                        if (handler) {
                            // 执行回调
                            handler(data, null);
                        }
                    }
                });
            }).on('error', (e) => {
                log.error("get wechat access_token from wechat failed:" + e.message);
                if (handler) {
                    // 执行回调
                    handler(e, null);
                }
            });
        } else {
            if (handler) {
                // 执行回调
                handler(null, accessToken);
            }
        }
    });
};
exports.sendWxTemplateMessage = sendWxTemplateMessage;
exports.getWxAccessToken = getWxAccessToken;