适应场景:Sails框架、微信公众号数据配置在数据库中,支持多个公众号。
1、Model定义(Waterline)
文件路径:/api/models/wx/Wx_config.js
/**
* Created by root on 10/25/15.
*/
var moment = require('moment');
module.exports = {
schema: true,
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
id: {
type: 'integer',
autoIncrement: true,
primaryKey: true
},
appname: {
type: 'string',
required: true
},
ghid: {//原始ID
type: 'string',
unique: true,
required: true
},
appid: {
type: 'string',
unique: true,
required: true
},
appsecret: {
type: 'string',
unique: true,
required: true
},
encodingAESKey:{
type: 'string',
unique: true,
required: true
},
token: {
type: 'string',
unique: true,
required: true
},
access_token: {
type: 'string'
},
expire_time: {
type:'integer',
defaultsTo:function(){
return 0;
}
},
createdBy:{
type: 'integer'
},
createdAt:{
type:'integer',
defaultsTo:function(){
return moment().format('X');
}
}
}
};
2、Sails默认不支持微信xml post,改造之
文件路径:/config/http.js
rawParser: function(req, res, next) {
switch(req.headers['content-type']) {
case 'text/xml':
require('body-parser').raw({ type: 'text/xml' })(req, res, next);
break;
default :
next();
}
},
order: [
'startRequestTimer',
'cookieParser',
'session',
'myRequestLogger',
'rawParser',
'bodyParser',
'handleBodyParserError',
'compress',
'methodOverride',
'poweredBy',
'$custom',
'router',
'www',
'favicon',
'404',
'500'
],
3、控制类
文件路径:/api/controllers/open/WeixinController.js
微信公众号后台配置路径:http://127.0.0.1/open/weixin/api?wxid=1
/**
* Created by root on 10/25/15.
*/
module.exports = {
api: function (req, res) {
var id = req.query.wxid;
Wx_config.findOne(id).exec(function (err, conf) {
if (err)return res.send(200, 'fail');
if (req.body) {
WeixinService.loop(req, function (data) {
if (data.type = 'text') {//用户发送纯文本
var msg = {toUserName: data.openid, fromUserName: conf.ghid, content: 'Node.js Test...'};
WeixinService.sendTextMsg(res, msg);//向用户回复消息
}
});
}
//签名
if (WeixinService.checkSignature(req, conf.token)) {
res.send(200, req.query.echostr);
} else {
res.send(200, 'fail');
}
});
}
};
4、微信服务工具类
文件路径:/api/services/WeixinService.js
/**
* Created by root on 10/26/15.
*/
var sha1 = require('sha1'), xml2js = require('xml2js');
module.exports = {
/**
* 签名验证
* @param req
* @param token
* @returns {boolean}
*/
checkSignature: function (req, token) {
var signature = req.query.signature,
timestamp = req.query.timestamp,
nonce = req.query.nonce,
echostr = req.query.echostr;
// 按照字典排序
var array = [token, timestamp, nonce];
array.sort();
// 连接
var str = sha1(array.join(""));
// 对比签名
return str == signature;
},
/**
* 监听用户消息
* @param req
* @param callback
*/
loop: function (req, callback) {
var body = req.body.toString('utf-8');
var data = {};
xml2js.parseString(body, function (err, json) {
if (err) {
} else {
console.log('json::' + JSON.stringify(json));
data.type = json.xml.MsgType;
data.openid = json.xml.FromUserName;
if (data.type == 'text') {
data.txt = json.xml.Content;
} else if (data.type == 'image') {
data.pic = json.xml.PicUrl;
}
return callback(data);
}
});
},
/**
* 发送文本消息
* @param res
* @param msg
*/
sendTextMsg: function (res, msg) {
var time = Math.round(new Date().getTime() / 1000);
var funcFlag = msg.funcFlag ? msg.funcFlag : 0;
var output = "" +
"<xml>" +
"<ToUserName><![CDATA[" + msg.toUserName + "]]></ToUserName>" +
"<FromUserName><![CDATA[" + msg.fromUserName + "]]></FromUserName>" +
"<CreateTime>" + time + "</CreateTime>" +
"<MsgType><![CDATA[text]]></MsgType>" +
"<Content><![CDATA[" + msg.content + "]]></Content>" +
"<FuncFlag>" + funcFlag + "</FuncFlag>" +
"</xml>";
res.type('xml');
res.send(output);
},
/**
* 发送图文消息
* @param res
* @param msg
*/
sendNewsMsg: function (res, msg) {
var time = Math.round(new Date().getTime() / 1000);
var articlesStr = "";
for (var i = 0; i < msg.articles.length; i++) {
articlesStr += "<item>" +
"<Title><![CDATA[" + msg.articles[i].title + "]]></Title>" +
"<Description><![CDATA[" + msg.articles[i].description + "]]></Description>" +
"<PicUrl><![CDATA[" + msg.articles[i].picUrl + "]]></PicUrl>" +
"<Url><![CDATA[" + msg.articles[i].url + "]]></Url>" +
"</item>";
}
var funcFlag = msg.funcFlag ? msg.funcFlag : 0;
var output = "" +
"<xml>" +
"<ToUserName><![CDATA[" + msg.toUserName + "]]></ToUserName>" +
"<FromUserName><![CDATA[" + msg.fromUserName + "]]></FromUserName>" +
"<CreateTime>" + time + "</CreateTime>" +
"<MsgType><![CDATA[news]]></MsgType>" +
"<ArticleCount>" + msg.articles.length + "</ArticleCount>" +
"<Articles>" + articlesStr + "</Articles>" +
"<FuncFlag>" + funcFlag + "</FuncFlag>" +
"</xml>";
res.type('xml');
res.send(output);
}
};
Nodejs版本:0.12.7
Sails版本:0.11.0
require相关组件,记得安装。
参考项目1:https://github.com/node-webot/wechat-api
参考项目2:https://github.com/JeremyWei/weixin_api