Files
soul-yongping/next-project/scripts/auto-unbind-expired.js
2026-02-09 14:43:35 +08:00

171 lines
5.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* 自动解绑定时任务
*
* 功能:定时检查并解绑过期的推荐关系
*
* 解绑条件:
* 1. 绑定状态为 active
* 2. 过期时间已到expiry_date < NOW
* 3. 期间没有任何购买purchase_count = 0
*
* 执行方式:
* - 手动执行node scripts/auto-unbind-expired.js
* - 定时任务:配置 cron 每天凌晨2点执行
*
* 宝塔面板配置:
* 计划任务 -> Shell脚本
* 执行周期:每天 02:00
* 脚本内容cd /www/wwwroot/soul && node scripts/auto-unbind-expired.js
*/
const path = require('path')
const fs = require('fs')
// 动态加载数据库模块
async function loadDB() {
const dbPath = path.join(__dirname, '../lib/db.ts')
// 如果是 TypeScript 文件,需要使用 ts-node 或编译后的版本
if (fs.existsSync(dbPath)) {
// 尝试导入编译后的 JS 文件
const compiledPath = path.join(__dirname, '../.next/server/lib/db.js')
if (fs.existsSync(compiledPath)) {
return require(compiledPath)
}
// 如果没有编译版本,尝试使用 ts-node
try {
require('ts-node/register')
return require(dbPath)
} catch (e) {
console.error('❌ 无法加载数据库模块,请确保已编译或安装 ts-node')
process.exit(1)
}
} else {
console.error('❌ 找不到数据库模块文件')
process.exit(1)
}
}
async function autoUnbind() {
console.log('=' .repeat(60))
console.log('自动解绑定时任务')
console.log('执行时间:', new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }))
console.log('=' .repeat(60))
console.log()
try {
// 加载数据库模块
const { query } = await loadDB()
// 1. 查询需要解绑的记录
console.log('步骤 1: 查询需要解绑的记录...')
console.log('-' .repeat(60))
const expiredBindings = await query(`
SELECT
id,
referee_id,
referrer_id,
binding_date,
expiry_date,
purchase_count,
total_commission
FROM referral_bindings
WHERE status = 'active'
AND expiry_date < NOW()
AND purchase_count = 0
ORDER BY expiry_date ASC
`)
if (expiredBindings.length === 0) {
console.log('✅ 无需解绑的记录')
console.log()
console.log('=' .repeat(60))
console.log('任务完成')
console.log('=' .repeat(60))
return
}
console.log(`找到 ${expiredBindings.length} 条需要解绑的记录`)
console.log()
// 2. 输出明细
console.log('步骤 2: 解绑明细')
console.log('-' .repeat(60))
expiredBindings.forEach((binding, index) => {
const bindingDate = new Date(binding.binding_date).toLocaleDateString('zh-CN')
const expiryDate = new Date(binding.expiry_date).toLocaleDateString('zh-CN')
const daysExpired = Math.floor((Date.now() - new Date(binding.expiry_date).getTime()) / (1000 * 60 * 60 * 24))
console.log(`${index + 1}. 用户 ${binding.referee_id}`)
console.log(` 推荐人: ${binding.referrer_id}`)
console.log(` 绑定时间: ${bindingDate}`)
console.log(` 过期时间: ${expiryDate} (已过期 ${daysExpired} 天)`)
console.log(` 购买次数: ${binding.purchase_count}`)
console.log(` 累计佣金: ¥${(binding.total_commission || 0).toFixed(2)}`)
console.log()
})
// 3. 批量更新为 expired
console.log('步骤 3: 执行解绑操作...')
console.log('-' .repeat(60))
const ids = expiredBindings.map(b => b.id)
const result = await query(`
UPDATE referral_bindings
SET status = 'expired'
WHERE id IN (${ids.map(() => '?').join(',')})
`, ids)
console.log(`✅ 已成功解绑 ${result.affectedRows || expiredBindings.length} 条记录`)
console.log()
// 4. 更新推荐人的推广数量
console.log('步骤 4: 更新推荐人统计...')
console.log('-' .repeat(60))
const referrerIds = [...new Set(expiredBindings.map(b => b.referrer_id))]
for (const referrerId of referrerIds) {
const count = expiredBindings.filter(b => b.referrer_id === referrerId).length
await query(`
UPDATE users
SET referral_count = GREATEST(referral_count - ?, 0)
WHERE id = ?
`, [count, referrerId])
console.log(` - ${referrerId}: -${count} 个绑定`)
}
console.log(`✅ 已更新 ${referrerIds.length} 个推荐人的统计数据`)
console.log()
// 5. 总结
console.log('=' .repeat(60))
console.log('✅ 任务完成')
console.log(` - 解绑记录数: ${expiredBindings.length}`)
console.log(` - 受影响推荐人: ${referrerIds.length}`)
console.log('=' .repeat(60))
} catch (error) {
console.error('❌ 任务执行失败:', error)
console.error(error.stack)
process.exit(1)
}
}
// 如果直接运行此脚本
if (require.main === module) {
autoUnbind().then(() => {
process.exit(0)
}).catch((err) => {
console.error('❌ 脚本执行异常:', err)
process.exit(1)
})
}
module.exports = { autoUnbind }