Ai config
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
async function paginate(queryFn, sql, params, page = 1, pageSize = 20) {
|
||||
const p = Math.max(1, parseInt(page) || 1)
|
||||
const ps = Math.min(100, Math.max(1, parseInt(pageSize) || 20))
|
||||
|
||||
const countResult = await queryFn(
|
||||
`SELECT COUNT(*) as total FROM (${sql}) AS _paged`,
|
||||
params
|
||||
)
|
||||
const total = countResult[0].total
|
||||
const totalPages = Math.ceil(total / ps)
|
||||
|
||||
const data = await queryFn(
|
||||
`${sql} LIMIT ? OFFSET ?`,
|
||||
[...params, ps, (p - 1) * ps]
|
||||
)
|
||||
|
||||
return { data, total, page: p, pageSize: ps, totalPages }
|
||||
}
|
||||
|
||||
module.exports = { paginate }
|
||||
@@ -0,0 +1,87 @@
|
||||
const fetch = require('node-fetch')
|
||||
|
||||
const APPID = process.env.WECHAT_APPID
|
||||
const SECRET = process.env.WECHAT_SECRET
|
||||
|
||||
let accessToken = null
|
||||
let tokenExpiresAt = 0
|
||||
|
||||
async function getAccessToken() {
|
||||
if (!APPID || !SECRET) {
|
||||
throw new Error('WECHAT_APPID and WECHAT_SECRET must be configured in .env')
|
||||
}
|
||||
|
||||
if (accessToken && Date.now() < tokenExpiresAt) {
|
||||
return accessToken
|
||||
}
|
||||
|
||||
const res = await fetch(
|
||||
`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${SECRET}`
|
||||
)
|
||||
const data = await res.json()
|
||||
|
||||
if (data.errcode) {
|
||||
throw new Error(`微信API错误: ${data.errmsg}`)
|
||||
}
|
||||
|
||||
accessToken = data.access_token
|
||||
tokenExpiresAt = Date.now() + (data.expires_in - 300) * 1000
|
||||
return accessToken
|
||||
}
|
||||
|
||||
async function sendSubscribeMessage(openid, templateId, data, page = '') {
|
||||
const token = await getAccessToken()
|
||||
|
||||
const body = {
|
||||
touser: openid,
|
||||
template_id: templateId,
|
||||
page,
|
||||
data,
|
||||
miniprogram_state: 'formal'
|
||||
}
|
||||
|
||||
const res = await fetch(
|
||||
`https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=${token}`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
}
|
||||
)
|
||||
const result = await res.json()
|
||||
|
||||
if (result.errcode && result.errcode !== 0) {
|
||||
console.error('发送订阅消息失败:', result)
|
||||
return { success: false, error: result.errmsg }
|
||||
}
|
||||
|
||||
return { success: true }
|
||||
}
|
||||
|
||||
async function sendOrderStatusNotification(openid, orderId, status, totalPrice) {
|
||||
const statusText = {
|
||||
paid: '已付款',
|
||||
completed: '已完成',
|
||||
cancelled: '已取消'
|
||||
}
|
||||
|
||||
const templateId = process.env.WECHAT_ORDER_TEMPLATE_ID
|
||||
if (!templateId) {
|
||||
console.warn('WECHAT_ORDER_TEMPLATE_ID not configured, skipping notification')
|
||||
return { success: false, error: 'template not configured' }
|
||||
}
|
||||
|
||||
return sendSubscribeMessage(openid, templateId, {
|
||||
thing1: { value: `订单 ${orderId}` },
|
||||
phrase2: { value: statusText[status] || status },
|
||||
amount3: { value: `¥${totalPrice}` },
|
||||
date4: { value: new Date().toLocaleString('zh-CN') },
|
||||
thing5: { value: '点击查看订单详情' }
|
||||
}, `/pages/customer/order-detail/order-detail?id=${orderId}`)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getAccessToken,
|
||||
sendSubscribeMessage,
|
||||
sendOrderStatusNotification
|
||||
}
|
||||
Reference in New Issue
Block a user