微信公众号 - 实际例子

通过一个完整的"智能客服公众号"例子,展示公众号开发的完整流程:服务器搭建、消息处理、菜单创建、网页授权。

项目结构

├── server │ ├── index.js // 入口文件(Express服务) │ ├── wechat.js // 微信消息处理 │ ├── menu.js // 自定义菜单管理 │ ├── auth.js // OAuth网页授权 │ └── token.js // access_token管理 ├── public │ └── user.html // 用户中心网页 ├── package.json └── .env // 配置文件(AppID等)

server/index.js - 入口文件

const express = require('express') const crypto = require('crypto') const { handleMessage } = require('./wechat') const { handleAuth } = require('./auth') const getRawBody = require('raw-body') const app = express() const TOKEN = process.env.WX_TOKEN app.get('/wechat', (req, res) => { const { signature, timestamp, nonce, echostr } = req.query const hash = crypto.createHash('sha1') .update([TOKEN, timestamp, nonce].sort().join('')) .digest('hex') res.send(hash === signature ? echostr : 'failed') }) app.post('/wechat', async (req, res) => { const xml = await getRawBody(req, { encoding: 'utf-8' }) const reply = await handleMessage(xml) res.type('application/xml').send(reply) }) app.get('/auth', handleAuth) app.use(express.static('public')) app.listen(3000, () => console.log('服务启动于端口 3000'))

server/wechat.js - 消息处理

const xml2js = require('xml2js') const KEYWORDS = { '帮助': '可用命令:\n1. 天气+城市名 查询天气\n2. 签到 每日签到\n3. 帮助 查看说明', '签到': '签到成功!今日积分+10' } function buildReply(to, from, content) { return `<xml> <ToUserName><![CDATA[${to}]]></ToUserName> <FromUserName><![CDATA[${from}]]></FromUserName> <CreateTime>${Math.floor(Date.now() / 1000)}</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[${content}]]></Content> </xml>` } async function handleMessage(xmlData) { const { xml } = await xml2js.parseStringPromise(xmlData) const from = xml.FromUserName[0] const to = xml.ToUserName[0] const type = xml.MsgType[0] if (type === 'event') { const event = xml.Event[0] if (event === 'subscribe') { return buildReply(from, to, '欢迎关注智能客服!\n回复"帮助"查看功能列表') } if (event === 'CLICK' && xml.EventKey[0] === 'SIGN_IN') { return buildReply(from, to, '签到成功!积分+10') } } if (type === 'text') { const content = xml.Content[0].trim() if (KEYWORDS[content]) { return buildReply(from, to, KEYWORDS[content]) } if (content.startsWith('天气')) { const city = content.replace('天气', '').trim() return buildReply(from, to, `${city}今日:晴 25°C ~ 32°C`) } return buildReply(from, to, '未识别的指令,回复"帮助"查看功能列表') } return buildReply(from, to, '暂不支持该消息类型') } module.exports = { handleMessage }

server/auth.js - 网页授权

const axios = require('axios') const APP_ID = process.env.WX_APPID const APP_SECRET = process.env.WX_SECRET async function handleAuth(req, res) { const { code } = req.query if (!code) { const redirectUri = encodeURIComponent('https://example.com/auth') const url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${APP_ID}&redirect_uri=${redirectUri}&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect` return res.redirect(url) } const tokenUrl = `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${APP_ID}&secret=${APP_SECRET}&code=${code}&grant_type=authorization_code` const { data: tokenData } = await axios.get(tokenUrl) const userUrl = `https://api.weixin.qq.com/sns/userinfo?access_token=${tokenData.access_token}&openid=${tokenData.openid}&lang=zh_CN` const { data: userInfo } = await axios.get(userUrl) res.redirect(`/user.html?nickname=${encodeURIComponent(userInfo.nickname)}&avatar=${encodeURIComponent(userInfo.headimgurl)}`) } module.exports = { handleAuth }

public/user.html - 用户中心页面

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>用户中心</title> <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script> </head> <body> <div id="app"> <img id="avatar" style="width:80px;border-radius:50%"> <h2 id="nickname"></h2> <button onclick="shareToFriend()">分享给朋友</button> <button onclick="scanCode()">扫码签到</button> </div> <script> const params = new URLSearchParams(location.search) document.getElementById('avatar').src = params.get('avatar') document.getElementById('nickname').textContent = params.get('nickname') function scanCode() { wx.scanQRCode({ needResult: 1, scanType: ['qrCode', 'barCode'], success(res) { alert('扫码结果:' + res.resultStr) } }) } </script> </body> </html>

效果预览

智能客服公众号
欢迎关注智能客服!
回复"帮助"查看功能列表
帮助
可用命令:
1. 天气+城市名 查询天气
2. 签到 每日签到
3. 帮助 查看说明
天气北京
北京今日:晴 25°C ~ 32°C
今日签到
服务中心
官方网站
← Mp Webpage Sa Overview →