Browse Source

修改微信客户端

Sand 8 years ago
parent
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 objectUtil = require('../util/objectUtil');
let UserStatus = require('../models/user.status');
let Users = require('../models/user/users');
const APIv1 = require('../include/endpoints').APIv1;
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.'};
    }
    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);
    });
    userStatus.on(MODEL_EVENTS.Error, function (message) {
    users.on(MODEL_EVENTS.Error, function (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.'};
    }
    let userStatus = new UserStatus();
    let userStatus = new Users();
    userStatus.on(MODEL_EVENTS.OK, function (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.'};
    }
    let userStatus = new UserStatus();
    let userStatus = new Users();
    userStatus.on(MODEL_EVENTS.OK, function (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 smRepo = require("../../repository/mysql/system.msg.repo.js");
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 PLATFORMS = require('../../include/commons').PLATFORM;

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

@ -9,21 +9,23 @@
const RedisKeys = require('../../include/commons').REDIS_KEYS;
let RedisModel = require('../redis.model');
let DoctorRepo = require('../../repository/mysql/doctor.repo');
let PatientRepo = require('../../repository/mysql/patient.repo');
let UserStatusRepo = require('../../repository/mysql/user.status.repo');
let Doctor = require('./doctor');
let Patient = require('./patient');
let 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 redisConn = RedisClient.redisClient().connection;
let async = require('async');
let bearcat = require('bearcat');
let log = require('../../util/log');
let objectUtil = require('../../util/objectUtil');
var imDb = require('../../repository/mysql/db/im.db');
let users = null;
class Users extends RedisModel {
    constructor() {
        super();
@ -32,7 +34,7 @@ class Users extends RedisModel {
    }
    /**
     * 获取用户。
     * 获取用户,直接从MYSQL获取,缓存是否有在不能确定。
     *
     * @param userId
     * @param outCallback
@ -65,12 +67,12 @@ class Users extends RedisModel {
    }
    /**
     * 获取用户状态。
     * 获取客户端App状态。
     *
     * @param userId
     * @param outCallback
     */
    getUserStatus(userId, outCallback){
    getAppStatus(userId, outCallback){
        let self = this;
        async.waterfall([
            // get from redis
@ -78,7 +80,7 @@ class Users extends RedisModel {
                let userStatusKey = self.makeRedisKey(RedisKeys.UserStatus, userId);
                redisConn.hgetallAsync(userStatusKey).then(function (res) {
                   if(res === null){
                       callback(null);
                       callback(null);  // get from mysql
                   } else {
                       outCallback(null, res);
                   }
@ -86,7 +88,7 @@ class Users extends RedisModel {
            },
            // get from MySQL
            function () {
                UserStatusRepo.getUserStatus(userId, function (err, res) {
                AppStatusRepo.findOne(userId, function (err, res) {
                    let userStatus = null;
                    if(res.length > 0){
                        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是否属于患者。
     *
@ -140,6 +200,14 @@ class Users extends RedisModel {
                callback(null, res !== 0);
            });
    }
    static instance(){
        if(users === null){
            users = new Users();
        }
        return users;
    }
}
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() {
    }
    static update() {
    }
    static findOne(doctorId, handler) {
        imDb.execQuery({
            "sql": "select id, name, sex, birthdate, avatar from doctors where id = ? ",
@ -21,39 +18,6 @@ class DoctorRepo {
            "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;

+ 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";
var log = require('../../util/log');
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',
    database: 'wlyy',
    connectionLimit: '50',
    charset : 'utf8mb4'
    charset: 'utf8mb4'
};
// IM数据库配置
@ -17,7 +17,7 @@ let imDbConfig = {
    password: '123456',
    database: 'ichat',
    connectionLimit: '50',
    charset : 'utf8mb4'
    charset: 'utf8mb4'
};
// Redis
@ -57,13 +57,12 @@ let geTuiAppStoreCfg = {
// 微信配置
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.wlyyServerConfig = wlyyServerConfig;
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 = {
    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.geTuiAppStoreCfg = geTuiAppStoreCfg;
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
};
// 三师后台
var wlyyServerConfig = {
    host: '172.19.103.88',
    port: 9092
};
// 透传服务
var transServerConfig = {
    host: '172.19.103.76',
    port: 8000
};
// 企业版的推送配置
var geTuiConfig = {
    HOST: 'https://api.getui.com/apiex.htm',
@ -41,16 +53,15 @@ var geTuiAppStoreCfg = {
    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';
@ -67,4 +78,5 @@ exports.redisConfig = redisConfig;
exports.geTuiConfig = geTuiConfig;
exports.geTuiAppStoreCfg = geTuiAppStoreCfg;
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 `user_status` CASCADE
DROP TABLE IF EXISTS `app_status` CASCADE
;
DROP TABLE IF EXISTS `wechat_access_tokens` CASCADE
;
/* Create Tables */
@ -103,7 +106,7 @@ CREATE TABLE `sessions`
) COMMENT='会话'
;
CREATE TABLE `user_status`
CREATE TABLE `app_status`
(
	`user_id` VARCHAR(50) NOT NULL COMMENT '用户ID',
	`platform` TINYINT COMMENT '平台,0为iOS,1为安卓',
@ -112,7 +115,16 @@ CREATE TABLE `user_status`
	`app_in_bg` TINYINT COMMENT 'App是否处于后台状态',
	`last_login_time` TIMESTAMP(0) COMMENT '最后登录 时间',
	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 */
@ -147,11 +159,16 @@ ALTER TABLE `muc_messages`
SET FOREIGN_KEY_CHECKS=1;
/* 用户视图:医生与患者 */
/* 用户视图:医生、患者、用户微信状态*/
create or replace view doctors as 
select code id, name, sex, birthday birthdate, photo avatar, level
from wlyy.wlyy_doctor;
create or replace view patients as 
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;