更新支付和推荐系统逻辑,新增根据推荐配置计算支付金额的功能,确保用户享受优惠。调整绑定推荐关系的有效期读取方式,支持从配置中获取。优化提现流程,增加最低提现金额的配置读取,提升系统灵活性和用户体验。同时,更新管理界面中的支付设置链接,确保一致性。
This commit is contained in:
@@ -47,7 +47,7 @@ export default function AdminLayout({ children }: { children: React.ReactNode })
|
||||
{ icon: BookOpen, label: "内容管理", href: "/admin/content" },
|
||||
{ icon: Users, label: "用户管理", href: "/admin/users" },
|
||||
{ icon: Wallet, label: "交易中心", href: "/admin/distribution" }, // 合并:分销+订单+提现
|
||||
{ icon: CreditCard, label: "支付设置", href: "/admin/payment" },
|
||||
{ icon: CreditCard, label: "推广设置", href: "/admin/referral-settings" }, // 单独入口,集中管理分销配置
|
||||
{ icon: Settings, label: "系统设置", href: "/admin/settings" },
|
||||
]
|
||||
|
||||
|
||||
272
app/admin/referral-settings/page.tsx
Normal file
272
app/admin/referral-settings/page.tsx
Normal file
@@ -0,0 +1,272 @@
|
||||
"use client"
|
||||
|
||||
import { useEffect, useState } from "react"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Switch } from "@/components/ui/switch"
|
||||
import { Slider } from "@/components/ui/slider"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Save, Percent, Users, Wallet, Info } from "lucide-react"
|
||||
|
||||
type ReferralConfig = {
|
||||
distributorShare: number
|
||||
minWithdrawAmount: number
|
||||
bindingDays: number
|
||||
userDiscount: number
|
||||
enableAutoWithdraw: boolean
|
||||
}
|
||||
|
||||
const DEFAULT_REFERRAL_CONFIG: ReferralConfig = {
|
||||
distributorShare: 90,
|
||||
minWithdrawAmount: 10,
|
||||
bindingDays: 30,
|
||||
userDiscount: 5,
|
||||
enableAutoWithdraw: false,
|
||||
}
|
||||
|
||||
export default function ReferralSettingsPage() {
|
||||
const [config, setConfig] = useState<ReferralConfig>(DEFAULT_REFERRAL_CONFIG)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [saving, setSaving] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const loadConfig = async () => {
|
||||
try {
|
||||
const res = await fetch("/api/db/config?key=referral_config")
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
if (data?.success && data.config) {
|
||||
setConfig({
|
||||
distributorShare: data.config.distributorShare ?? 90,
|
||||
minWithdrawAmount: data.config.minWithdrawAmount ?? 10,
|
||||
bindingDays: data.config.bindingDays ?? 30,
|
||||
userDiscount: data.config.userDiscount ?? 5,
|
||||
enableAutoWithdraw: data.config.enableAutoWithdraw ?? false,
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("加载 referral_config 失败:", e)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
loadConfig()
|
||||
}, [])
|
||||
|
||||
const handleSave = async () => {
|
||||
setSaving(true)
|
||||
try {
|
||||
// 确保所有字段都是正确类型(防止字符串导致计算错误)
|
||||
const safeConfig = {
|
||||
distributorShare: Number(config.distributorShare) || 0,
|
||||
minWithdrawAmount: Number(config.minWithdrawAmount) || 0,
|
||||
bindingDays: Number(config.bindingDays) || 0,
|
||||
userDiscount: Number(config.userDiscount) || 0,
|
||||
enableAutoWithdraw: Boolean(config.enableAutoWithdraw),
|
||||
}
|
||||
|
||||
const body = {
|
||||
key: "referral_config",
|
||||
config: safeConfig,
|
||||
description: "分销 / 推广规则配置",
|
||||
}
|
||||
const res = await fetch("/api/db/config", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
const data = await res.json()
|
||||
if (!res.ok || !data?.success) {
|
||||
alert("保存失败: " + (data?.error || res.statusText))
|
||||
return
|
||||
}
|
||||
|
||||
alert("✅ 分销配置已保存成功!\n\n• 小程序与网站的推广规则会一起生效\n• 绑定关系会使用新的天数配置\n• 佣金比例会立即应用到新订单\n\n如有缓存,请刷新前台/小程序页面。")
|
||||
} catch (e: any) {
|
||||
console.error("保存 referral_config 失败:", e)
|
||||
alert("保存失败: " + (e?.message || String(e)))
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleNumberChange = (field: keyof ReferralConfig) => (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = parseFloat(e.target.value || "0")
|
||||
setConfig((prev) => ({ ...prev, [field]: isNaN(value) ? 0 : value }))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-8 max-w-4xl mx-auto">
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-white flex items-center gap-2">
|
||||
<Wallet className="w-5 h-5 text-[#38bdac]" />
|
||||
推广 / 分销设置
|
||||
</h2>
|
||||
<p className="text-gray-400 mt-1">
|
||||
统一管理「好友优惠」「你得 90% 收益」「绑定期 30 天」「提现门槛」等规则,小程序和 Web 共用这套配置。
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
disabled={saving || loading}
|
||||
className="bg-[#38bdac] hover:bg-[#2da396] text-white"
|
||||
>
|
||||
<Save className="w-4 h-4 mr-2" />
|
||||
{saving ? "保存中..." : "保存配置"}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
{/* 核心规则卡片 */}
|
||||
<Card className="bg-[#0f2137] border-gray-700/50 shadow-xl">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-white">
|
||||
<Percent className="w-4 h-4 text-[#38bdac]" />
|
||||
推广规则
|
||||
</CardTitle>
|
||||
<CardDescription className="text-gray-400">
|
||||
这三项会直接体现在小程序「推广规则」卡片上,同时影响实收佣金计算。
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<div className="grid grid-cols-3 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-300 flex items-center gap-2">
|
||||
<Info className="w-3 h-3 text-[#38bdac]" />
|
||||
好友优惠(%)
|
||||
</Label>
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
max={100}
|
||||
className="bg-[#0a1628] border-gray-700 text-white"
|
||||
value={config.userDiscount}
|
||||
onChange={handleNumberChange("userDiscount")}
|
||||
/>
|
||||
<p className="text-xs text-gray-500">例如 5 表示好友立减 5%(在价格配置基础上生效)。</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-300 flex items-center gap-2">
|
||||
<Users className="w-3 h-3 text-[#38bdac]" />
|
||||
推广者分成(%)
|
||||
</Label>
|
||||
<div className="flex items-center gap-4">
|
||||
<Slider
|
||||
className="flex-1"
|
||||
min={10}
|
||||
max={100}
|
||||
step={1}
|
||||
value={[config.distributorShare]}
|
||||
onValueChange={([val]) => setConfig((prev) => ({ ...prev, distributorShare: val }))}
|
||||
/>
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
max={100}
|
||||
className="w-20 bg-[#0a1628] border-gray-700 text-white text-center"
|
||||
value={config.distributorShare}
|
||||
onChange={handleNumberChange("distributorShare")}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500">
|
||||
实际佣金 = 订单金额 × {" "}
|
||||
<span className="text-[#38bdac] font-mono">{config.distributorShare}%</span>,支付回调和分销统计都会用这个值。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-300 flex items-center gap-2">
|
||||
<Users className="w-3 h-3 text-[#38bdac]" />
|
||||
绑定有效期(天)
|
||||
</Label>
|
||||
<Input
|
||||
type="number"
|
||||
min={1}
|
||||
max={365}
|
||||
className="bg-[#0a1628] border-gray-700 text-white"
|
||||
value={config.bindingDays}
|
||||
onChange={handleNumberChange("bindingDays")}
|
||||
/>
|
||||
<p className="text-xs text-gray-500">好友通过你的链接进来并登录后,绑定在你名下的天数。</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 提现与自动提现 */}
|
||||
<Card className="bg-[#0f2137] border-gray-700/50 shadow-xl">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-white">
|
||||
<Wallet className="w-4 h-4 text-[#38bdac]" />
|
||||
提现规则
|
||||
</CardTitle>
|
||||
<CardDescription className="text-gray-400">
|
||||
与「提现中心」「自动提现」相关的参数,影响推广者看到的可提现金额和最低门槛。
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-300">最低提现金额(元)</Label>
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
step={1}
|
||||
className="bg-[#0a1628] border-gray-700 text-white"
|
||||
value={config.minWithdrawAmount}
|
||||
onChange={handleNumberChange("minWithdrawAmount")}
|
||||
/>
|
||||
<p className="text-xs text-gray-500">小程序「满 X 元可提现」展示的门槛,同时用于后端接口校验。</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-300 flex items-center gap-2">
|
||||
自动提现开关
|
||||
<Badge variant="outline" className="border-[#38bdac]/40 text-[#38bdac] text-[10px]">
|
||||
预留
|
||||
</Badge>
|
||||
</Label>
|
||||
<div className="flex items-center gap-3 mt-1">
|
||||
<Switch
|
||||
checked={config.enableAutoWithdraw}
|
||||
onCheckedChange={(checked) => setConfig((prev) => ({ ...prev, enableAutoWithdraw: checked }))}
|
||||
/>
|
||||
<span className="text-sm text-gray-400">
|
||||
开启后,可结合定时任务实现「收益自动打款到微信零钱」。
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 提示卡片 */}
|
||||
<Card className="bg-[#0f2137] border-gray-700/50">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-gray-200 text-sm">
|
||||
<Info className="w-4 h-4 text-[#38bdac]" />
|
||||
使用说明
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-2 text-xs text-gray-400 leading-relaxed">
|
||||
<p>
|
||||
1. 以上配置会写入 <code className="font-mono text-[11px] text-[#38bdac]">system_config.referral_config</code>,小程序「推广中心」、
|
||||
Web 推广页以及支付回调都会读取同一份配置。
|
||||
</p>
|
||||
<p>
|
||||
2. 修改后新订单立即生效;旧订单的历史佣金不会自动重算,只影响之后产生的订单。
|
||||
</p>
|
||||
<p>
|
||||
3. 如遇前端展示与实际结算不一致,优先以此处配置为准,再排查缓存和小程序版本。
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user