前言
最近接了个活, 大概就是开发一个企业的周年庆的活动, 内容有抽奖, 游戏等, 自信满满的接了下来, 结果过程一言难尽, 今天特意来总结一下
开发准备
首先要确定客户的公众号是服务号还是订阅号, 你想要获取用户更多的信息, 比如头像, 地区, 昵称等, 只有服务号才能完成。 很多客户域名和服务器都是没有的, 从购买域名 -> 个人/企业认证备案 -> 购买服务器 -> 服务器域名备案, 没有一个月是下不来的
frp
公众号开发最头疼的一点就是你需要在本地开发, 但是你要调用微信api需要指定域名, 比如Oauth2网页授权, 你总不可能每次修改一点一次次打包到服务器, 所以这时候内网穿透的优势就体现出来了, 常见的有ngrok/frp/natapp, 这里我建议是frp, 因为配置比较简单而且是免费的, 但前提是你有域名和服务器,安装过程我就不赘述了,服务器需要配置frps.ini
# frps.ini
[common]
bind_port = 7000
vhost_http_port = 80 // 这个是对应的frpc中custom_domain的端口号,最好是80, 其他端口的话, 展示形式就是custom_domain:vhost_http_port
# frpc.ini
[common]
server_addr = 180.76.169.181
server_port = 7000
[web]
type = http // 前端静态页面类型就是http
local_ip = 0.0.0.0
local_port = 5419 // 脚手架热更新服务器端口
custom_domains = you.domain.com // 在线上暴露的域名
[api]
type = tcp // 后端接口就是tcp
local_ip = 0.0.0.0
local_port = 3000 // node 服务端口3000
custom_domains = you1.domain.com // 这个域名一定要和上一个不同, 因为vhost_http_port只能指定一个,
假如你的后端接口和前端页面都是部署在本地, 后端端口映射的type是tcp, 前端页面映射的type是http
获取微信用户头像
这次的需求是获取微信用户头像, 然后外面是一个企业周年庆的边框, 相当于DIY头像, 然后可以供用户下载, 这里我使用了html2canvas, 但是生成头像图片模糊不清, 同样的尺寸比原头像还要模糊, 我做了很多尝试,后来终于发现, 只是将周年庆边框从background-image转换为img, 整个图片清晰度一下子上来了, 可能html2canvas对img边框重绘更加友好, 这还不是最优的, 微信后台传过来的用户头像url最后会有一个/数字, 大多数就是132, 就是132px * 132px, 把路径/132去掉, 整个头像图片大小可以达到720 * 720, 这下可以达到高清标准了, 在开发的时候还遇到另外一个问题,就是你刚换成DIY头像, 立马又替换成自己的头像, 但微信后台传过来的头像还是DIY头像, 论坛上有说明,大概有4个小时的延迟,后台不会立即保存用户头像。
获取accoken_token
微信里有两种accoken_token, 在此我说的就是网页授权获取用户信息的accoken_token
const Koa = require("koa");
const Router = require("@koa/router");
const bodyParser = require("koa-bodyparser");
const axios = require("axios");
const app = new Koa();
const router = new Router();
app.use(bodyParser());
const appID = "xxxxxxxxxxx"; // 微信测试号的appid
const appSecret = "yyyyyyyyyyy"; // 微信测试号的appsecret
router.post("/getUserInfo", async (ctx, next) => {
const { code } = ctx.request.body;
try {
const { data: resp } = await axios.get(
`https://api.weixin.qq.com/sns/oauth2/access_token`,
{
params: {
appid: appID,
secret: appSecret,
grant_type: "authorization_code",
code,
},
}
);
const { access_token, openid } = resp;
// data的JSON数据结构
// {
// "openid": "OPENID",
// "nickname": NICKNAME,
// "sex": 1,
// "province":"PROVINCE",
// "city":"CITY",
// "country":"COUNTRY",
// "headimgurl":"https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46",
// "privilege":[ "PRIVILEGE1" "PRIVILEGE2"],
// "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
// }
const { data } = await axios.get("https://api.weixin.qq.com/sns/userinfo", {
params: {
access_token,
openid,
lang: "zh_CN",
},
});
ctx.body = data;
} catch (error) {
console.log(error);
}
});
再谈access_token
再谈另一种access_token, 这种token是用来获取填充初始化jssdk参数的, 参数如下 前端代码
wx.config({
debug: true, // 开启调试模式,调用的所有 api 的返回值会在客户端 alert 出来,若要查看传入的参数,可以在 pc 端打开,参数信息会通过 log 打出,仅在 pc 端时才会打印。
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名
jsApiList: [] // 必填,需要使用的 JS 接口列表
});
后端代码
const Koa = require("koa");
const Router = require("@koa/router");
const bodyParser = require("koa-bodyparser");
const sha1 = require("sha1");
const axios = require("axios");
const redis = require("redis");
app.use(bodyParser());
const client = redis.createClient({
url: 'redis://alice:foobared@awesome.redis.server:6380'
});
const app = new Koa();
const router = new Router();
const appID = "xxxxxxxxxxx"; // 微信测试号的appid
const appSecret = "yyyyyyyyyyy"; // 微信测试号的appsecret
client.on("error", function (err) {
console.log("Error " + err);
});
router.get("/getTicket", async (ctx, next) => {
/**
* 这里的access_token需要缓存, 7200秒后才会更新
* 假如这段时期再次请求, 是不会在返回access_token参数
* 所以用redis缓存access_token
*/
let access_token = await client.get("access_token");
if(!access_token){
const { access_token: token } = await axios.get(
`https://api.weixin.qq.com/cgi-bin/token`,
{
params: {
appid: appID,
secret: appSecret,
grant_type: "client_credential",
},
}
);
access_token = token;
// EX 过期时间7200秒 NX 只有键不存在时,才会操作
await client.set("access_token", token, { EX: 7200, NX: true, });
}
const { ticket } = await axios.get(
`https://api.weixin.qq.com/cgi-bin/ticket/getticket`,
{
params: {
access_token,
type: "jsapi",
},
}
);
const timestamp = new Date().getTime();
const noncestr = "jacklove";
const str = `jsapi_ticket=${ticket}&noncestr=${noncestr}×tamp=${timestamp}&url=http://you.domain.cn`;
const signature = sha1(str);
ctx.body = { signature, noncestr, signature, timestamp, appId: appID };
});
Termux
人不可能时刻守在电脑面前, 但手机是随身携带的, 对, 假如有个很急但又很小的需求或者你想监控一下服务器的状态, termux是个不错的选择, 相当于andriod的终端, 只需要输入下面代码, 就可以愉快的输入linux命令了,这块我用的是PM2来管理项目, 比如查看项目状态pm2 status, 重启服务pm2 reload all, 开启集群pm2 start app.js -i 4
1.pkg install openssh
2.sshd