// 每月1号运行:给活跃用户发放断签保护卡 // Grant freeze cards to active users on the 1st of each month import { PrismaClient } from "@prisma/client"; import { PrismaMariaDb } from "@prisma/adapter-mariadb"; import "dotenv/config"; const base = (process.env.DATABASE_URL || "mysql://localhost:3306/zhuiguang_ai?charset=utf8mb4").replace("mysql://", "mariadb://"); const sep = base.includes("?") ? "&" : "?"; const connectionString = `${base}${sep}connection_limit=5&pool_timeout=15`; const adapter = new PrismaMariaDb(connectionString); const prisma = new PrismaClient({ adapter }); function getCurrentMonth() { const d = new Date(); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`; } async function main() { const currentMonth = getCurrentMonth(); console.log(`[grant-freeze-cards] 开始发放 ${currentMonth} 月断签保护卡...`); // 活跃判定:近 30 天有 签到 / 发帖 / 回复 任一即可(真人用户)。 // 原实现只统计签到(forumCheckIn 常为空),导致断签卡永远发不出。 const thirtyDaysAgo = new Date(); thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); const [checkinIds, topicIds, postIds] = await Promise.all([ prisma.forumCheckIn.findMany({ where: { checkInDate: { gte: thirtyDaysAgo } }, distinct: ["userId"], select: { userId: true }, }), prisma.forumTopic.findMany({ where: { createdAt: { gte: thirtyDaysAgo }, user: { isBot: false } }, distinct: ["userId"], select: { userId: true }, }), prisma.forumPost.findMany({ where: { createdAt: { gte: thirtyDaysAgo }, user: { isBot: false } }, distinct: ["userId"], select: { userId: true }, }), ]); const activeUserIds = [...new Set([...checkinIds, ...topicIds, ...postIds].map((r) => r.userId))]; console.log(`[grant-freeze-cards] 找到 ${activeUserIds.length} 位近30天活跃用户`); let granted = 0; let skipped = 0; let newUsers = 0; for (const userId of activeUserIds) { const key = `streak_freeze_${userId}`; const existing = await prisma.systemConfig.findUnique({ where: { key } }); if (existing) { const data = existing.value; if (data.lastGrantedMonth === currentMonth) { skipped++; continue; // Already granted this month } // Grant 2 cards, cap at 2 data.freezeCards = Math.min((data.freezeCards || 0) + 2, 2); data.lastGrantedMonth = currentMonth; await prisma.systemConfig.update({ where: { key }, data: { value: data }, }); granted++; } else { // New user: grant initial 2 cards await prisma.systemConfig.create({ data: { key, value: { freezeCards: 2, lastGrantedMonth: currentMonth }, }, }); newUsers++; } } console.log(`[grant-freeze-cards] 完成: 发放 ${granted} 人, 新用户 ${newUsers} 人, 跳过 ${skipped} 人 (本月已发放) `); await prisma.$disconnect(); } main().catch(async (e) => { console.error("[grant-freeze-cards] 执行失败:", e.message); if (prisma) await prisma.$disconnect(); process.exit(1); });