- 新增 server/:Node + Express + SQLite + node-cron 实现登录、纪念日 CRUD 和定时订阅消息推送 - 新增 .gitea/workflows/deploy.yml:推送即触发群晖 Docker 部署,监听 15002 - utils/api.js:自动按 envVersion 切换本地/线上 BASE_URL - app.js 与 add-anniversary.js 移除 wx.cloud 调用,改走自建后端 - cloudfunctions/ 暂保留以便回滚 - 一并提交此前未入库的首页 / 设置页 / 日历 / 万年历等改造 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
const axios = require('axios')
|
||||
|
||||
const APPID = process.env.WX_APPID
|
||||
const APPSECRET = process.env.WX_APPSECRET
|
||||
|
||||
// access_token 内存缓存(微信全局唯一,2 小时有效)
|
||||
let _token = { value: null, expireAt: 0 }
|
||||
|
||||
async function getAccessToken() {
|
||||
const now = Date.now()
|
||||
// 提前 5 分钟刷新,避免临界过期
|
||||
if (_token.value && _token.expireAt - now > 5 * 60 * 1000) {
|
||||
return _token.value
|
||||
}
|
||||
|
||||
const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${APPSECRET}`
|
||||
const { data } = await axios.get(url, { timeout: 8000 })
|
||||
if (!data.access_token) {
|
||||
throw new Error(`获取 access_token 失败: ${JSON.stringify(data)}`)
|
||||
}
|
||||
_token = {
|
||||
value: data.access_token,
|
||||
expireAt: now + (data.expires_in - 300) * 1000
|
||||
}
|
||||
return _token.value
|
||||
}
|
||||
|
||||
// code2session:拿 openid(小程序登录)
|
||||
async function code2session(code) {
|
||||
const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${APPID}&secret=${APPSECRET}&js_code=${code}&grant_type=authorization_code`
|
||||
const { data } = await axios.get(url, { timeout: 8000 })
|
||||
if (data.errcode) {
|
||||
throw new Error(`code2session 失败: ${JSON.stringify(data)}`)
|
||||
}
|
||||
return { openid: data.openid, unionid: data.unionid || null, session_key: data.session_key }
|
||||
}
|
||||
|
||||
// 发送订阅消息
|
||||
async function sendSubscribeMessage({ touser, page, data, templateId, miniprogramState = 'formal' }) {
|
||||
const token = await getAccessToken()
|
||||
const url = `https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=${token}`
|
||||
const body = {
|
||||
touser,
|
||||
template_id: templateId,
|
||||
page,
|
||||
miniprogram_state: miniprogramState,
|
||||
lang: 'zh_CN',
|
||||
data
|
||||
}
|
||||
const { data: res } = await axios.post(url, body, { timeout: 8000 })
|
||||
if (res.errcode !== 0) {
|
||||
throw new Error(`发送订阅消息失败: ${JSON.stringify(res)}`)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
module.exports = { getAccessToken, code2session, sendSubscribeMessage }
|
||||
Reference in New Issue
Block a user