Files
soul-yongping/app/api/referral/bind/route.ts
卡若 6989ade3e2 feat: 分销规则完善 + 微信支付修复
1. 分销规则:
   - 链接带ID绑定推荐关系
   - 一级分销 + 30天有效期
   - 客户抢夺机制(过期可被抢走)
   - 90%收益归分发者

2. 新增统计数据:
   - 绑定用户数
   - 链接进入人数
   - 带来付款人数

3. 微信支付:
   - 添加点击反馈
   - 优化支付流程日志
   - 改善错误提示

4. 分销中心UI优化
2026-01-29 09:47:04 +08:00

284 lines
8.4 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 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 })
}
// 查找推荐人
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, referred_by 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=续期, takeover=抢夺
let oldReferrerId = null
if (existingBindings.length > 0) {
const existing = existingBindings[0]
const expiryDate = new Date(existing.expiry_date)
// 同一个推荐人 - 续期
if (existing.referrer_id === referrer.id) {
action = 'renew'
}
// 不同推荐人 - 检查是否可以抢夺
else if (expiryDate < now) {
// 已过期,可以被抢夺
action = 'takeover'
oldReferrerId = existing.referrer_id
// 将旧绑定标记为过期
await query(
"UPDATE referral_bindings SET status = 'expired' WHERE id = ?",
[existing.id]
)
} else {
// 未过期,不能被抢夺
return NextResponse.json({
success: false,
error: '用户已绑定其他推荐人,绑定有效期内无法更换',
expiryDate: expiryDate.toISOString()
}, { status: 400 })
}
}
// 计算新的过期时间30天
const expiryDate = new Date()
expiryDate.setDate(expiryDate.getDate() + BINDING_DAYS)
// 创建或更新绑定记录
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}`)
} 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])
// 更新用户的推荐人
await query(
'UPDATE users SET referred_by = ? WHERE id = ?',
[referrer.id, user.id]
)
// 更新推荐人的推广数量(仅新绑定时)
if (action === 'new') {
await query(
'UPDATE users SET referral_count = referral_count + 1 WHERE id = ?',
[referrer.id]
)
}
// 如果是抢夺,减少原推荐人的推广数量
if (action === 'takeover' && oldReferrerId) {
await query(
'UPDATE users SET referral_count = GREATEST(referral_count - 1, 0) WHERE id = ?',
[oldReferrerId]
)
console.log(`[Referral Bind] 抢夺: ${user.id}: ${oldReferrerId} -> ${referrer.id}`)
} else {
console.log(`[Referral Bind] 新绑定: ${user.id} -> ${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) {
// 访问日志表可能不存在,忽略错误
}
return NextResponse.json({
success: true,
message: action === 'renew' ? '绑定已续期' : (action === 'takeover' ? '绑定已更新' : '绑定成功'),
action,
expiryDate: expiryDate.toISOString(),
referrer: {
id: referrer.id,
nickname: referrer.nickname
}
})
} 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, referred_by FROM users WHERE id = ?',
[userId]
) as any[]
if (users.length === 0) {
return NextResponse.json({
success: false,
error: '用户不存在'
}, { status: 404 })
}
const user = users[0]
// 如果有推荐人,获取推荐人信息
let referrer = null
if (user.referred_by) {
const referrers = await query(
'SELECT id, nickname, avatar FROM users WHERE id = ?',
[user.referred_by]
) as any[]
if (referrers.length > 0) {
referrer = referrers[0]
}
}
// 获取该用户推荐的人
const referees = await query(
'SELECT id, nickname, avatar, created_at FROM users WHERE referred_by = ?',
[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 })
}
}