// 云函数:dailyPush // 每日定时推送运势通知 // 定时触发器:cron: 0 0 7 * * * *(每日 07:00) // 或每分钟扫描:cron: 0 * * * * * *(支持用户自定义时间) const cloud = require('wx-server-sdk') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db = cloud.database() // 引入共享算法包(需在云函数部署时安装) // 由于云函数环境限制,算法代码会内联到本文件 // 参见 _algorithm.js 文件 const _ = require('./_algorithm') exports.main = async (event, context) => { const now = new Date() const todayStr = now.toISOString().slice(0, 10) const currentHour = now.getHours() const currentMinute = now.getMinutes() console.log(`[dailyPush] Triggered at ${now.toISOString()}`) try { // 查询所有已开启推送的订阅 const { data: subscriptions } = await db.collection('subscriptions') .where({ pushEnabled: true, }) .get() console.log(`[dailyPush] Found ${subscriptions.length} active subscriptions`) let pushCount = 0 let skipCount = 0 let errorCount = 0 for (const sub of subscriptions) { try { // 检查是否已达到推送时间 const [pushHour, pushMinute] = (sub.pushTime || '07:00').split(':').map(Number) if (currentHour !== pushHour || currentMinute !== pushMinute) { skipCount++ continue } // 检查是否已推送过(防重复) if (sub.lastPushDate === todayStr) { skipCount++ continue } // 生成运势内容 const fortune = _.generatePushContent(sub.birthInfo, now) // 格式化推送内容 const pushData = { touser: sub.openid, templateId: sub.templateId, page: '/pages/fortune/index', // 点击跳转到运势页面 data: { date1: { value: formatDate(now) }, thing2: { value: `综合运势评分${fortune.overallScore}分,等级:${fortune.overallLuck}` }, thing3: { value: formatFortuneSummary(fortune) }, thing4: { value: `幸运色:${fortune.luckyColor} 幸运数字:${fortune.luckyNumber}` }, }, } // 发送微信订阅消息 await cloud.openapi.subscribeMessage.send(pushData) // 更新最后推送日期 await db.collection('subscriptions').doc(sub._id).update({ data: { lastPushDate: todayStr }, }) pushCount++ console.log(`[dailyPush] Pushed to ${sub.openid}`) } catch (err) { errorCount++ console.error(`[dailyPush] Failed for ${sub.openid}:`, err.message) } } return { success: true, summary: { total: subscriptions.length, pushed: pushCount, skipped: skipCount, errors: errorCount, }, } } catch (err) { console.error('[dailyPush] Fatal error:', err) return { success: false, error: err.message } } } /** * 格式化日期为中文显示 */ function formatDate(date) { const year = date.getFullYear() const month = date.getMonth() + 1 const day = date.getDate() const weekdays = ['日', '一', '二', '三', '四', '五', '六'] const weekday = weekdays[date.getDay()] return `${year}年${month}月${day}日 星期${weekday}` } /** * 格式化运势摘要 */ function formatFortuneSummary(fortune) { const parts = [] if (fortune.careerAdvice) { const brief = fortune.careerAdvice.length > 10 ? fortune.careerAdvice.slice(0, 10) + '…' : fortune.careerAdvice parts.push(`事业:${brief}`) } if (fortune.wealthAdvice) { const brief = fortune.wealthAdvice.length > 10 ? fortune.wealthAdvice.slice(0, 10) + '…' : fortune.wealthAdvice parts.push(`财运:${brief}`) } if (fortune.relationshipAdvice) { const brief = fortune.relationshipAdvice.length > 10 ? fortune.relationshipAdvice.slice(0, 10) + '…' : fortune.relationshipAdvice parts.push(`感情:${brief}`) } if (fortune.healthAdvice) { const brief = fortune.healthAdvice.length > 10 ? fortune.healthAdvice.slice(0, 10) + '…' : fortune.healthAdvice parts.push(`健康:${brief}`) } return parts.join(' ') }