更新小程序环境变量以切换至开发 API 地址,调整分销页面以支持新字段展示,包括推荐人和消费者的头像及昵称,优化绑定记录的状态显示和佣金计算逻辑,提升用户体验和代码可读性。
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
# 开发环境:对接当前 Next 后端(与现网 API 路径完全一致,无缝切换)
|
||||
VITE_API_BASE_URL=https://soulapi.quwanzhi.com
|
||||
# VITE_API_BASE_URL=https://soulapi.quwanzhi.com
|
||||
VITE_API_BASE_URL=https://souldev.quwanzhi.com
|
||||
1
soul-admin/dist/assets/index-2chBMZjx.css
vendored
1
soul-admin/dist/assets/index-2chBMZjx.css
vendored
File diff suppressed because one or more lines are too long
454
soul-admin/dist/assets/index-CbOmKBRd.js
vendored
Normal file
454
soul-admin/dist/assets/index-CbOmKBRd.js
vendored
Normal file
File diff suppressed because one or more lines are too long
454
soul-admin/dist/assets/index-CgjaRP73.js
vendored
454
soul-admin/dist/assets/index-CgjaRP73.js
vendored
File diff suppressed because one or more lines are too long
1
soul-admin/dist/assets/index-DBQ1UORI.css
vendored
Normal file
1
soul-admin/dist/assets/index-DBQ1UORI.css
vendored
Normal file
File diff suppressed because one or more lines are too long
4
soul-admin/dist/index.html
vendored
4
soul-admin/dist/index.html
vendored
@@ -4,8 +4,8 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>管理后台 - Soul创业派对</title>
|
||||
<script type="module" crossorigin src="/assets/index-CgjaRP73.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-2chBMZjx.css">
|
||||
<script type="module" crossorigin src="/assets/index-CbOmKBRd.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DBQ1UORI.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -44,14 +44,17 @@ interface Binding {
|
||||
id: string
|
||||
referrerId: string
|
||||
referrerName?: string
|
||||
referrerCode: string
|
||||
referrerCode?: string
|
||||
referrerAvatar?: string | null
|
||||
refereeId: string
|
||||
refereePhone?: string
|
||||
refereeNickname?: string
|
||||
refereeAvatar?: string | null
|
||||
boundAt: string
|
||||
expiresAt: string
|
||||
status: 'active' | 'converted' | 'expired' | 'cancelled'
|
||||
commission?: number
|
||||
totalCommission?: number
|
||||
}
|
||||
|
||||
interface Withdrawal {
|
||||
@@ -771,8 +774,12 @@ export function DistributionPage() {
|
||||
<option value="active">有效</option>
|
||||
<option value="converted">已转化</option>
|
||||
<option value="expired">已过期</option>
|
||||
<option value="cancelled">已取消</option>
|
||||
</select>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500">
|
||||
规则(见《新分销逻辑设计方案》):绑定有有效期(默认 30 天,可在推广设置中配置 bindingDays)。有效 = 未到期;已转化 = 该消费者有购买;已过期 = 到期且未购买被自动解绑;已取消 = 消费者点击了他人链接,绑定已切换。
|
||||
</p>
|
||||
<Card className="bg-[#0f2137] border-gray-700/50">
|
||||
<CardContent className="p-0">
|
||||
{filteredBindings.length === 0 ? (
|
||||
@@ -782,8 +789,8 @@ export function DistributionPage() {
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="bg-[#0a1628] text-gray-400">
|
||||
<th className="p-4 text-left font-medium">访客</th>
|
||||
<th className="p-4 text-left font-medium">分销商</th>
|
||||
<th className="p-4 text-left font-medium">消费者</th>
|
||||
<th className="p-4 text-left font-medium">绑定时间</th>
|
||||
<th className="p-4 text-left font-medium">到期时间</th>
|
||||
<th className="p-4 text-left font-medium">状态</th>
|
||||
@@ -794,36 +801,55 @@ export function DistributionPage() {
|
||||
{filteredBindings.map((binding) => (
|
||||
<tr key={binding.id} className="hover:bg-[#0a1628] transition-colors">
|
||||
<td className="p-4">
|
||||
<div>
|
||||
<p className="text-white font-medium">
|
||||
{binding.refereeNickname || '匿名用户'}
|
||||
</p>
|
||||
<p className="text-gray-500 text-xs">{binding.refereePhone}</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-8 h-8 rounded-full bg-[#38bdac]/20 flex items-center justify-center text-sm text-[#38bdac] overflow-hidden shrink-0">
|
||||
{binding.referrerAvatar ? (
|
||||
<img src={binding.referrerAvatar} className="w-full h-full object-cover" alt="" />
|
||||
) : (
|
||||
(binding.referrerName || binding.referrerCode || '?').charAt(0)
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-white font-medium">{binding.referrerName || '-'}</p>
|
||||
<p className="text-gray-500 text-xs font-mono">
|
||||
{binding.referrerCode || '-'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="p-4">
|
||||
<div>
|
||||
<p className="text-white">{binding.referrerName || '-'}</p>
|
||||
<p className="text-gray-500 text-xs font-mono">
|
||||
{binding.referrerCode}
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-8 h-8 rounded-full bg-[#38bdac]/20 flex items-center justify-center text-sm text-[#38bdac] overflow-hidden shrink-0">
|
||||
{binding.refereeAvatar ? (
|
||||
<img src={binding.refereeAvatar} className="w-full h-full object-cover" alt="" />
|
||||
) : (
|
||||
(binding.refereeNickname || '?').charAt(0)
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-white">
|
||||
{binding.refereeNickname || '微信用户'}
|
||||
</p>
|
||||
<p className="text-gray-500 text-xs">{binding.refereePhone || '-'}</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="p-4 text-gray-400">
|
||||
{binding.boundAt
|
||||
? new Date(binding.boundAt).toLocaleDateString('zh-CN')
|
||||
? new Date(binding.boundAt).toLocaleString('zh-CN', { dateStyle: 'short', timeStyle: 'short' })
|
||||
: '-'}
|
||||
</td>
|
||||
<td className="p-4 text-gray-400">
|
||||
{binding.expiresAt
|
||||
? new Date(binding.expiresAt).toLocaleDateString('zh-CN')
|
||||
? new Date(binding.expiresAt).toLocaleString('zh-CN', { dateStyle: 'short', timeStyle: 'short' })
|
||||
: '-'}
|
||||
</td>
|
||||
<td className="p-4">{getStatusBadge(binding.status)}</td>
|
||||
<td className="p-4">
|
||||
{binding.commission ? (
|
||||
{(binding.totalCommission ?? binding.commission) != null &&
|
||||
(binding.totalCommission ?? binding.commission) !== undefined ? (
|
||||
<span className="text-[#38bdac] font-medium">
|
||||
¥{binding.commission.toFixed(2)}
|
||||
¥{Number(binding.totalCommission ?? binding.commission).toFixed(2)}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-gray-500">-</span>
|
||||
|
||||
@@ -34,7 +34,7 @@ import {
|
||||
ChevronRight,
|
||||
} from 'lucide-react'
|
||||
import { UserDetailModal } from '@/components/modules/user/UserDetailModal'
|
||||
import { get, del, post, put } from '@/api/client'
|
||||
import { get, del, put } from '@/api/client'
|
||||
|
||||
interface User {
|
||||
id: string
|
||||
|
||||
@@ -781,31 +781,48 @@ func DBDistribution(c *gin.Context) {
|
||||
for i := range users {
|
||||
userMap[users[i].ID] = &users[i]
|
||||
}
|
||||
getStr := func(s *string) string {
|
||||
if s == nil || *s == "" {
|
||||
return ""
|
||||
}
|
||||
return *s
|
||||
}
|
||||
out := make([]gin.H, 0, len(bindings))
|
||||
for _, b := range bindings {
|
||||
refNick := "用户"
|
||||
if u := userMap[b.RefereeID]; u != nil && u.Nickname != nil {
|
||||
refNick = *u.Nickname
|
||||
} else {
|
||||
refNick = refNick + b.RefereeID
|
||||
refNick := "微信用户"
|
||||
var refereePhone, refereeAvatar *string
|
||||
if u := userMap[b.RefereeID]; u != nil {
|
||||
if u.Nickname != nil && *u.Nickname != "" {
|
||||
refNick = *u.Nickname
|
||||
} else {
|
||||
refNick = "微信用户"
|
||||
}
|
||||
refereePhone = u.Phone
|
||||
refereeAvatar = u.Avatar
|
||||
}
|
||||
var referrerName *string
|
||||
var referrerName, referrerAvatar *string
|
||||
if u := userMap[b.ReferrerID]; u != nil {
|
||||
referrerName = u.Nickname
|
||||
referrerAvatar = u.Avatar
|
||||
}
|
||||
days := 0
|
||||
if b.ExpiryDate.After(time.Now()) {
|
||||
days = int(b.ExpiryDate.Sub(time.Now()).Hours() / 24)
|
||||
}
|
||||
var refereePhone *string
|
||||
if u := userMap[b.RefereeID]; u != nil {
|
||||
refereePhone = u.Phone
|
||||
// 佣金展示用累计佣金 total_commission(支付回调累加),无则用 commission_amount
|
||||
commissionVal := b.TotalCommission
|
||||
if commissionVal == nil {
|
||||
commissionVal = b.CommissionAmount
|
||||
}
|
||||
statusVal := ""
|
||||
if b.Status != nil {
|
||||
statusVal = *b.Status
|
||||
}
|
||||
out = append(out, gin.H{
|
||||
"id": b.ID, "referrer_id": b.ReferrerID, "referrer_name": referrerName, "referrer_code": b.ReferralCode,
|
||||
"referee_id": b.RefereeID, "referee_nickname": refNick, "referee_phone": refereePhone,
|
||||
"bound_at": b.BindingDate, "expires_at": b.ExpiryDate, "status": b.Status,
|
||||
"days_remaining": days, "commission": b.CommissionAmount, "source": "miniprogram",
|
||||
"id": b.ID, "referrerId": b.ReferrerID, "referrerName": getStr(referrerName), "referrerCode": b.ReferralCode, "referrerAvatar": getStr(referrerAvatar),
|
||||
"refereeId": b.RefereeID, "refereeNickname": refNick, "refereePhone": getStr(refereePhone), "refereeAvatar": getStr(refereeAvatar),
|
||||
"boundAt": b.BindingDate, "expiresAt": b.ExpiryDate, "status": statusVal,
|
||||
"daysRemaining": days, "commission": commissionVal, "totalCommission": commissionVal, "source": "miniprogram",
|
||||
})
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "bindings": out, "total": len(out)})
|
||||
|
||||
@@ -555,12 +555,12 @@ func processReferralCommission(db *gorm.DB, buyerUserID string, amount float64,
|
||||
db.Model(&model.User{}).Where("id = ?", binding.ReferrerID).
|
||||
Update("pending_earnings", db.Raw("pending_earnings + ?", commission))
|
||||
|
||||
// 更新绑定记录
|
||||
// 更新绑定记录(COALESCE 避免 total_commission 为 NULL 时 NULL+?=NULL)
|
||||
db.Exec(`
|
||||
UPDATE referral_bindings
|
||||
SET last_purchase_date = NOW(),
|
||||
purchase_count = purchase_count + 1,
|
||||
total_commission = total_commission + ?
|
||||
purchase_count = COALESCE(purchase_count, 0) + 1,
|
||||
total_commission = COALESCE(total_commission, 0) + ?
|
||||
WHERE id = ?
|
||||
`, commission, binding.ID)
|
||||
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user