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

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

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

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

236 lines
7.8 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 - 增强版
*
* 可见数据:
* - 绑定用户数(当前有效绑定)
* - 通过链接进的人数(总访问量)
* - 带来的付款人数(已转化购买)
* - 收益统计90%归分发者)
*/
import { NextRequest, NextResponse } from 'next/server'
import { query, getConfig } from '@/lib/db'
// 分成比例默认90%给推广者)
const DISTRIBUTOR_SHARE = 0.9
/**
* GET - 获取分销数据
*/
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url)
const userId = searchParams.get('userId')
if (!userId) {
return NextResponse.json({
success: false,
error: '用户ID不能为空'
}, { status: 400 })
}
try {
// 获取分销配置
let distributorShare = DISTRIBUTOR_SHARE
try {
const config = await getConfig('referral_config')
if (config?.distributorShare) {
distributorShare = config.distributorShare / 100
}
} catch (e) { /* 使用默认配置 */ }
// 1. 获取用户基本信息
const users = await query(`
SELECT id, nickname, referral_code, earnings, pending_earnings,
withdrawn_earnings, referral_count
FROM users WHERE id = ?
`, [userId]) as any[]
if (users.length === 0) {
return NextResponse.json({
success: false,
error: '用户不存在'
}, { status: 404 })
}
const user = users[0]
// 2. 获取绑定关系统计从referral_bindings表
let bindingStats = { total: 0, active: 0, converted: 0, expired: 0 }
try {
const bindings = await query(`
SELECT
COUNT(*) as total,
SUM(CASE WHEN status = 'active' AND expiry_date > NOW() THEN 1 ELSE 0 END) as active,
SUM(CASE WHEN status = 'converted' THEN 1 ELSE 0 END) as converted,
SUM(CASE WHEN status = 'expired' OR (status = 'active' AND expiry_date <= NOW()) THEN 1 ELSE 0 END) as expired
FROM referral_bindings
WHERE referrer_id = ?
`, [userId]) as any[]
if (bindings.length > 0) {
bindingStats = {
total: parseInt(bindings[0].total) || 0,
active: parseInt(bindings[0].active) || 0,
converted: parseInt(bindings[0].converted) || 0,
expired: parseInt(bindings[0].expired) || 0
}
}
} catch (e) { /* 忽略 */ }
// 3. 获取通过链接进入的总人数(访问日志)
let totalVisits = 0
try {
const visits = await query(`
SELECT COUNT(DISTINCT visitor_id) as count
FROM referral_visits
WHERE referrer_id = ?
`, [userId]) as any[]
totalVisits = parseInt(visits[0]?.count) || 0
} catch (e) { /* 访问记录表可能不存在 */ }
// 如果没有访问记录表,用绑定总数替代
if (totalVisits === 0) {
totalVisits = bindingStats.total
}
// 4. 获取带来的付款人数和金额
let paymentStats = { paidCount: 0, totalAmount: 0 }
try {
const payments = await query(`
SELECT
COUNT(DISTINCT o.user_id) as paid_count,
COALESCE(SUM(o.amount), 0) as total_amount
FROM orders o
JOIN referral_bindings rb ON o.user_id = rb.referee_id
WHERE rb.referrer_id = ? AND o.status = 'paid'
`, [userId]) as any[]
if (payments.length > 0) {
paymentStats = {
paidCount: parseInt(payments[0].paid_count) || 0,
totalAmount: parseFloat(payments[0].total_amount) || 0
}
}
} catch (e) { /* 忽略 */ }
// 5. 获取活跃绑定用户列表
const activeBindings = await query(`
SELECT rb.id, rb.referee_id, rb.expiry_date, rb.binding_date,
u.nickname, u.avatar, u.has_full_book,
DATEDIFF(rb.expiry_date, NOW()) as days_remaining
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
LIMIT 50
`, [userId]) as any[]
// 6. 获取已转化用户列表
const convertedBindings = await query(`
SELECT rb.id, rb.referee_id, rb.conversion_date, rb.commission_amount,
u.nickname, u.avatar
FROM referral_bindings rb
JOIN users u ON rb.referee_id = u.id
WHERE rb.referrer_id = ? AND rb.status = 'converted'
ORDER BY rb.conversion_date DESC
LIMIT 50
`, [userId]) as any[]
// 7. 获取收益明细
let earningsDetails: any[] = []
try {
earningsDetails = await query(`
SELECT o.id, o.order_sn, o.amount, o.product_type, o.pay_time,
u.nickname as buyer_nickname,
rb.commission_amount
FROM orders o
JOIN users u ON o.user_id = u.id
JOIN referral_bindings rb ON o.user_id = rb.referee_id AND rb.referrer_id = ?
WHERE o.status = 'paid'
ORDER BY o.pay_time DESC
LIMIT 30
`, [userId]) as any[]
} catch (e) { /* 忽略 */ }
// 8. 计算预估收益
const estimatedEarnings = paymentStats.totalAmount * distributorShare
return NextResponse.json({
success: true,
data: {
// === 核心可见数据 ===
// 绑定用户数(当前有效绑定)
bindingCount: bindingStats.active,
// 通过链接进的人数
visitCount: totalVisits,
// 带来的付款人数
paidCount: paymentStats.paidCount,
// === 收益数据 ===
// 已结算收益
earnings: parseFloat(user.earnings) || 0,
// 待结算收益
pendingEarnings: parseFloat(user.pending_earnings) || 0,
// 已提现金额
withdrawnEarnings: parseFloat(user.withdrawn_earnings) || 0,
// 预估总收益
estimatedEarnings: Math.round(estimatedEarnings * 100) / 100,
// 分成比例
shareRate: Math.round(distributorShare * 100),
// === 推荐码 ===
referralCode: user.referral_code,
referralCount: user.referral_count || bindingStats.total,
// === 详细统计 ===
stats: {
totalBindings: bindingStats.total,
activeBindings: bindingStats.active,
convertedBindings: bindingStats.converted,
expiredBindings: bindingStats.expired,
// 即将过期7天内
expiringCount: activeBindings.filter((b: any) => b.days_remaining <= 7 && b.days_remaining > 0).length,
// 总支付金额
totalPaymentAmount: paymentStats.totalAmount
},
// === 用户列表 ===
activeUsers: activeBindings.map((b: any) => ({
id: b.referee_id,
nickname: b.nickname || '用户' + b.referee_id.slice(-4),
avatar: b.avatar,
daysRemaining: Math.max(0, b.days_remaining),
hasFullBook: b.has_full_book,
bindingDate: b.binding_date
})),
convertedUsers: convertedBindings.map((b: any) => ({
id: b.referee_id,
nickname: b.nickname || '用户' + b.referee_id.slice(-4),
avatar: b.avatar,
commission: parseFloat(b.commission_amount) || 0,
conversionDate: b.conversion_date
})),
// === 收益明细 ===
earningsDetails: earningsDetails.map((e: any) => ({
id: e.id,
orderSn: e.order_sn,
amount: parseFloat(e.amount),
commission: parseFloat(e.commission_amount) || parseFloat(e.amount) * distributorShare,
productType: e.product_type,
buyerNickname: e.buyer_nickname,
payTime: e.pay_time
}))
}
})
} catch (error) {
console.error('[ReferralData] 错误:', error)
return NextResponse.json({
success: false,
error: '获取分销数据失败: ' + (error as Error).message
}, { status: 500 })
}
}