刘永才 發表於 2019-12-20 12:40:00

Koa微信公众号开发

<blockquote>
<p>微信开发者模式开启需要服务器域名合法并且把接口配置好,这个接口是接通的关键,接通后微信后台的菜单设置功能,客服功能会失效,需要开发者自定义菜单和智能客服界面,并且接通后可以调用微信网页内部的定位分享等功能</p>
</blockquote>
<p><strong>前提条件</strong></p>
<ul>
<li>先去看koa和mongoose那篇笔记</li>
<li>安装xml2js中间件</li>
<li>安装request中间件【flyio或者axios也行】</li>
<li>创建一个wx文件夹单独使用</li>
</ul>
<p><strong>借用xml2js封装出方法</strong></p>
<pre><code class="language-js">// utils/xml.js

const xml2js = require('xml2js')

exports.xmlToJson = (str) =&gt; {
   return new Promise((resolve, reject) =&gt; {
      const parseString = xml2js.parseString
      parseString(str, (err, result) =&gt; {
            if (err) {
                reject(err)
            } else {
                resolve(result)
            }
      })
   })
}

exports.jsonToXml = (obj) =&gt; {
    const builder = new xml2js.Builder()
    return builder.buildObject(obj)
}
</code></pre>
<p><strong>自定义一个xml的中间件</strong><br>
如果数据是xml格式先处理成json格式</p>
<pre><code class="language-js">// middleware/xmlParse.js

const xml = require('../util/xml')

module.exports = () =&gt; {
    return async (ctx, next) =&gt; {
      if (ctx.method == 'POST' &amp;&amp; ctx.is('text/xml')) {
            let promise = new Promise(function (resolve, reject) {
                let buf = ''
                ctx.req.setEncoding('utf8')
                ctx.req.on('data', (chunk) =&gt; {
                  buf += chunk
                })
                ctx.req.on('end', () =&gt; {
                  xml.xmlToJson(buf)
                        .then(resolve)
                        .catch(reject)
                })
            })
            await promise.then((result) =&gt; {
                  ctx.req.body = result
                })
                .catch((e) =&gt; {
                  e.status = 400
                })
            await next()
      } else {
            await next()
      }
    }
}
</code></pre>
<p><strong>配置文件</strong></p>
<pre><code class="language-js">// config.js
...
wx: {
// 这几个都是微信给的
appid: 'AppSecret',
AppSecret:'AppSecret',
token:'token',
encodingAESKey: 'encodingAESKey',
},
...
</code></pre>
<p><strong>微信的路由</strong></p>
<pre><code class="language-js">// router/index.js

const wxSignature = require('../wx/wxSignature')
const wxHandle = require('../wx/wxHandle')

router.get('/wxHandle', wxHandle.getHandle)
router.post('/wxHandle', wxHandle.postHandle)
</code></pre>
<p><strong>sha1加密</strong></p>
<pre><code class="language-js">// utils/encode.js

const crypto = require('crypto')

exports.sha1 = (str) =&gt; {
    const sha1 = crypto.createHash('sha1')
    sha1.update(str)
    return sha1.digest('hex')
}
</code></pre>
<p><strong>微信核心的构造函数</strong><br>
把几个值闭包在构造函数里,过期时间也是,等到token过期再请求一次</p>
<pre><code class="language-js">// wx/index.js

const config = require('../config')
const request = require('request');

function wxchat(config) {
    this.appid =config.appid;
    this.AppSecret =config.AppSecret;
    this.token =config.token;
    this.encodingAESKey =config.encodingAESKey;
    this.accessToken ="";
    this.accessTokenTime ="";
    this.jsapiTicket ="";
    this.jsapiTicketTime ="";
}
wxchat.prototype.getAccessToken = async function(){
    if(this.accessToken==""){
      await this.accessTokenRequest();
    }else{
      var now = new Date().getTime()
      if(now&gt;Number(this.accessTokenTime)){
            // 过期了
            await this.accessTokenRequest();
      }
    }
    return this.accessToken
}

wxchat.prototype.accessTokenRequest = async function(){
    var url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential';
    url += '&amp;appid=' + this.appid;
    url += '&amp;secret=' + this.AppSecret;
    return new Promise((resolve,reject) =&gt; {
      request(url,(error, response, body) =&gt; {
            if(response.statusCode == 200){
                var res = JSON.parse(body);
                if(res.access_token){
                  console.log("accessToken成功:"+ res.access_token)
                  this.accessToken = res.access_token;
                  this.accessTokenTime = new Date().getTime() + Number(res.expires_in) - 200;
                }else{
                  console.log("获取accessToken接口出错:"+res.errcode)
                  this.accessToken = "";
                  this.accessTokenTime = "";
                }
            }else{
                console.log("获取accessToken接口出错")
                this.accessToken = "";
                this.accessTokenTime = "";
            }
            resolve()
      })
    })
}

wxchat.prototype.init=async function () {
   await this.getAccessToken()
}

const wx = new wxchat(config.wx);
wx.init()

module.exports = wx;
</code></pre>
<p><strong>入口文件</strong></p>
<pre><code class="language-js">// app.js

const xmlParse = require('./src/middleware/xmlParse')
// 这个js是自启动服务,不需要use,每次启动都会自动获取access_token
const wx = require('./src/wx/index')

app.use(xmlParse());
</code></pre>
<p><strong>接通服务和智能客服</strong><br>
上面的所有代码都是为了这个js服务</p>
<pre><code class="language-js">// wx/wxHandle.js

const encode = require('../util/encode')
const config = require('../config')
const xml = require('../util/xml')

//公众号接通服务
exports.getHandle = async (ctx, next) =&gt; {
    const token = config.wx.token,
      signature = ctx.query.signature,
      timestamp = ctx.query.timestamp,
      nonce = ctx.query.nonce;
    // 字典排序
    const arr = .sort()
    const result = encode.sha1(arr.join(''))

    if (result === signature) {
      ctx.body = ctx.query.echostr
    } else {
      ctx.body = { code: -1, msg: "你不是微信!"}
    }
}

//微信客服,回复
exports.postHandle = (ctx, next) =&gt; {
    let msg,MsgType,result;
    msg = ctx.req.body ? ctx.req.body.xml : ''

    if (!msg) {
      ctx.body = 'error request.'
      return;
    }
    MsgType = msg.MsgType
    if(MsgType=='text'){
      // 文字回复
      result = xml.jsonToXml({
                xml: {
                  ToUserName: msg.FromUserName,
                  FromUserName: msg.ToUserName,
                  CreateTime: Date.now(),
                  MsgType: msg.MsgType,
                  Content: msg.Content
                }
      })
    }else if(MsgType=='event'){
      // 关注回复
      result = xml.jsonToXml({
            xml: {
                ToUserName: msg.FromUserName,
                FromUserName: msg.ToUserName,
                CreateTime: Date.now(),
                MsgType: 'text',
                Content: "感谢关注"
            }
      })
    }else{
      // 其他回复
      result = 'success'
    }
    ctx.res.setHeader('Content-Type', 'application/xml')
    ctx.res.end(result)
}
</code></pre>
<p>上面的代码设置好后,去到外网域名启动服务,如果服务启动成功,去到微信服务器后台点击接通,会需要填接通请求地址,就是服务器域名【/xxx/wxHandle】,点击完成后,微信会向这个地址发出请求,是一个xml请求,请求到达我们刚开启的Koa服务器,被中间件解析成Json,然后通过config.js里的appId的加密计算后返回数据给微信,不出问题就接通了,接通成功后,去到公众号关注,就能收到刚才配置的【欢迎关注】,你再发文字上去,他会把你发的重新发回来,完结撒花</p>
<p>智能客服不能用代码去写,需要做个页面去配置到数据库,服务器要根据用户的请求查数据库回复,还有菜单的配置也是,菜单的配置是需要公众号认证的,每年300,我没搞,还有一个微信网页签名的接口,可以实现获取地址,分享,微信网页里的分享和获取地址怎么写,在Js笔记里有,这个接口怎么写,查看github,有帮助的给个星星谢谢!</p>
<blockquote>
<p>微信用户登陆获取信息,查看下一篇</p>
</blockquote><br><br>
来源:https://www.cnblogs.com/pengdt/p/12072512.html
頁: [1]
查看完整版本: Koa微信公众号开发