Files
soul-yongping/app/api/referral/bind/route.ts

322 lines
9.6 KiB
TypeScript
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.

/**
* 推荐码绑定API - 增强版
*
* 核心规则:
* 1. 链接带ID谁发的链接进的人就绑谁
* 2. 一级、一月:只有一级分销;绑定有效期一个月
* 3. 长期不发:别人发得多,客户会被「抢走」
* 4. 每天发:持续发的人绑定一直有效,收益越来越高
* 5. 约90%给分发:谁发得多谁拿得多
*/
import { NextRequest, NextResponse } from 'next/server'
import { query, getConfig } from '@/lib/db'
// 绑定有效期(天)- 默认值,优先从配置读取
const DEFAULT_BINDING_DAYS = 30
/**
* POST - 绑定推荐关系(支持抢夺机制)
*/
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { userId, referralCode, openId, source } = body
// 验证参数
const effectiveUserId = userId || (openId ? `user_${openId.slice(-8)}` : null)
if (!effectiveUserId || !referralCode) {
return NextResponse.json({
success: false,
error: '用户ID和推荐码不能为空'
}, { status: 400 })
}
// 获取绑定天数配置
let bindingDays = DEFAULT_BINDING_DAYS
try {
const config = await getConfig('referral_config')
if (config?.bindingDays) {
bindingDays = Number(config.bindingDays)
}
} catch (e) {
console.warn('[Referral Bind] 读取配置失败,使用默认值', DEFAULT_BINDING_DAYS)
}
// 查找推荐人
const referrers = await query(
'SELECT id, nickname, referral_code FROM users WHERE referral_code = ?',
[referralCode]
) as any[]
if (referrers.length === 0) {
return NextResponse.json({
success: false,
error: '推荐码无效'
}, { status: 400 })
}
const referrer = referrers[0]
// 不能自己推荐自己
if (referrer.id === effectiveUserId) {
return NextResponse.json({
success: false,
error: '不能使用自己的推荐码'
}, { status: 400 })
}
// 检查用户是否存在
const users = await query(
'SELECT id FROM users WHERE id = ? OR open_id = ?',
[effectiveUserId, openId || effectiveUserId]
) as any[]
if (users.length === 0) {
return NextResponse.json({
success: false,
error: '用户不存在'
}, { status: 400 })
}
const user = users[0]
const now = new Date()
// 检查现有绑定关系
const existingBindings = await query(`
SELECT id, referrer_id, expiry_date, status
FROM referral_bindings
WHERE referee_id = ? AND status = 'active'
ORDER BY binding_date DESC LIMIT 1
`, [user.id]) as any[]
let action = 'new' // new=新绑定, renew=续期, switch=立即切换
let oldReferrerId = null
if (existingBindings.length > 0) {
const existing = existingBindings[0]
// 同一个推荐人 - 续期刷新30天
if (existing.referrer_id === referrer.id) {
action = 'renew'
}
// 不同推荐人 - 立即切换(新逻辑:无条件切换)
else {
action = 'switch'
oldReferrerId = existing.referrer_id
// 将旧绑定标记为 cancelled被切换
await query(
"UPDATE referral_bindings SET status = 'cancelled' WHERE id = ?",
[existing.id]
)
console.log(`[Referral Bind] 立即切换: ${user.id}: ${oldReferrerId} -> ${referrer.id}`)
}
}
// 计算新的过期时间(从配置读取天数)
const expiryDate = new Date()
expiryDate.setDate(expiryDate.getDate() + bindingDays)
// 创建或更新绑定记录
const bindingId = 'bind_' + Date.now().toString(36) + Math.random().toString(36).substr(2, 6)
if (action === 'renew') {
// 续期:更新过期时间
await query(`
UPDATE referral_bindings
SET expiry_date = ?, binding_date = CURRENT_TIMESTAMP
WHERE referee_id = ? AND referrer_id = ? AND status = 'active'
`, [expiryDate, user.id, referrer.id])
console.log(`[Referral Bind] 续期: ${user.id} -> ${referrer.id},新过期时间: ${expiryDate.toISOString()}`)
} else {
// 新绑定或切换
await query(`
INSERT INTO referral_bindings (
id, referrer_id, referee_id, referral_code, status, expiry_date, binding_date
) VALUES (?, ?, ?, ?, 'active', ?, CURRENT_TIMESTAMP)
ON DUPLICATE KEY UPDATE
referrer_id = VALUES(referrer_id),
referral_code = VALUES(referral_code),
expiry_date = VALUES(expiry_date),
binding_date = CURRENT_TIMESTAMP,
status = 'active'
`, [bindingId, referrer.id, user.id, referralCode, expiryDate])
// 注意:不再更新 users.referred_by已弃用只使用 referral_bindings
// 更新推荐人的推广数量(仅新绑定时)
if (action === 'new') {
await query(
'UPDATE users SET referral_count = referral_count + 1 WHERE id = ?',
[referrer.id]
)
console.log(`[Referral Bind] 新绑定: ${user.id} -> ${referrer.id}`)
}
// 如果是立即切换,更新双方的推广数量
if (action === 'switch' && oldReferrerId) {
// 减少旧推荐人的数量
await query(
'UPDATE users SET referral_count = GREATEST(referral_count - 1, 0) WHERE id = ?',
[oldReferrerId]
)
// 增加新推荐人的数量
await query(
'UPDATE users SET referral_count = referral_count + 1 WHERE id = ?',
[referrer.id]
)
console.log(`[Referral Bind] 立即切换完成: ${user.id}: ${oldReferrerId} -> ${referrer.id}`)
}
}
// 记录访问日志(用于统计「通过链接进的人数」)
try {
await query(`
INSERT INTO referral_visits (referrer_id, visitor_id, source, created_at)
VALUES (?, ?, ?, CURRENT_TIMESTAMP)
`, [referrer.id, user.id, source || 'miniprogram'])
} catch (e) {
// 访问日志表可能不存在,忽略错误
}
const messages = {
new: '绑定成功',
renew: '绑定已续期',
switch: '已切换推荐人'
}
return NextResponse.json({
success: true,
message: messages[action] || '绑定成功',
action,
expiryDate: expiryDate.toISOString(),
bindingDays,
referrer: {
id: referrer.id,
nickname: referrer.nickname
},
...(oldReferrerId && { oldReferrerId })
})
} catch (error) {
console.error('[Referral Bind] 错误:', error)
return NextResponse.json({
success: false,
error: '绑定失败: ' + (error as Error).message
}, { status: 500 })
}
}
/**
* GET - 查询推荐关系
*/
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url)
const userId = searchParams.get('userId')
const referralCode = searchParams.get('referralCode')
try {
if (referralCode) {
// 查询推荐码对应的用户
const users = await query(
'SELECT id, nickname, avatar FROM users WHERE referral_code = ?',
[referralCode]
) as any[]
if (users.length === 0) {
return NextResponse.json({
success: false,
error: '推荐码无效'
}, { status: 404 })
}
return NextResponse.json({
success: true,
referrer: users[0]
})
}
if (userId) {
// 查询用户是否存在
const users = await query(
'SELECT id FROM users WHERE id = ?',
[userId]
) as any[]
if (users.length === 0) {
return NextResponse.json({
success: false,
error: '用户不存在'
}, { status: 404 })
}
// 从 referral_bindings 查询当前有效的推荐人
let referrer = null
const activeBinding = await query(`
SELECT
rb.referrer_id,
u.nickname,
u.avatar,
rb.expiry_date,
rb.purchase_count
FROM referral_bindings rb
JOIN users u ON rb.referrer_id = u.id
WHERE rb.referee_id = ?
AND rb.status = 'active'
AND rb.expiry_date > NOW()
ORDER BY rb.binding_date DESC
LIMIT 1
`, [userId]) as any[]
if (activeBinding.length > 0) {
referrer = {
id: activeBinding[0].referrer_id,
nickname: activeBinding[0].nickname,
avatar: activeBinding[0].avatar,
expiryDate: activeBinding[0].expiry_date,
purchaseCount: activeBinding[0].purchase_count
}
}
// 获取该用户推荐的人(所有活跃绑定)
const referees = await query(`
SELECT
u.id,
u.nickname,
u.avatar,
rb.binding_date as created_at,
rb.purchase_count,
rb.total_commission
FROM referral_bindings rb
JOIN users u ON rb.referee_id = u.id
WHERE rb.referrer_id = ?
AND rb.status = 'active'
AND rb.expiry_date > NOW()
ORDER BY rb.binding_date DESC
`, [userId]) as any[]
return NextResponse.json({
success: true,
referrer,
referees,
referralCount: referees.length
})
}
return NextResponse.json({
success: false,
error: '请提供userId或referralCode参数'
}, { status: 400 })
} catch (error) {
console.error('[Referral Bind] GET错误:', error)
return NextResponse.json({
success: false,
error: '查询失败: ' + (error as Error).message
}, { status: 500 })
}
}