Files
soul/app/api/referral/data/route.ts
卡若 a228911170 推广中心优化 + 用户资料功能
1. 推广中心优化:
   - 增加"待购买"、"已过期"统计
   - 访问量单独显示
   - 移除10元提现门槛,有收益即可提现
   - 复制文案去掉"专属邀请码"

2. 我的页面:
   - 支持获取微信头像(原生能力)
   - 支持获取微信昵称
   - 新增用户更新API

3. 后台API:
   - referral/data返回expiredCount和expiredUsers
   - 新增user/update接口同步用户信息
2026-01-29 11:35:56 +08:00

263 lines
8.9 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,
(SELECT COALESCE(SUM(amount), 0) FROM orders WHERE user_id = rb.referee_id AND status = 'paid') as order_amount
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[]
// 6.5 获取已过期用户列表
const expiredBindings = await query(`
SELECT rb.id, rb.referee_id, rb.expiry_date, rb.binding_date,
u.nickname, u.avatar
FROM referral_bindings rb
JOIN users u ON rb.referee_id = u.id
WHERE rb.referrer_id = ? AND (rb.status = 'expired' OR (rb.status = 'active' AND rb.expiry_date <= NOW()))
ORDER BY rb.expiry_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,
// 已过期用户数
expiredCount: bindingStats.expired,
// === 收益数据 ===
// 已结算收益
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,
status: 'active'
})),
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,
orderAmount: parseFloat(b.order_amount) || 0,
conversionDate: b.conversion_date,
status: 'converted'
})),
// 已过期用户列表
expiredUsers: expiredBindings.map((b: any) => ({
id: b.referee_id,
nickname: b.nickname || '用户' + b.referee_id.slice(-4),
avatar: b.avatar,
bindingDate: b.binding_date,
expiryDate: b.expiry_date,
status: 'expired'
})),
// === 收益明细 ===
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 })
}
}