Files
wxserver/server/src/wx.js
T
yuming 3965e542fc
部署到群晖 / deploy (push) Failing after 6m22s
接入自建后端 + Gitea CI/CD
- 新增 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>
2026-06-01 15:44:09 +08:00

58 lines
1.9 KiB
JavaScript

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 }