更新支付和推荐系统逻辑,新增根据推荐配置计算支付金额的功能,确保用户享受优惠。调整绑定推荐关系的有效期读取方式,支持从配置中获取。优化提现流程,增加最低提现金额的配置读取,提升系统灵活性和用户体验。同时,更新管理界面中的支付设置链接,确保一致性。

This commit is contained in:
乘风
2026-02-05 18:45:28 +08:00
parent 19d0e625db
commit 1a95aee112
9 changed files with 858 additions and 24 deletions

View File

@@ -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" },
]

View 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>
)
}