优化小程序阅读页面,新增好友优惠展示逻辑,支持通过推荐人获取折扣。调整价格计算方式,确保用户在购买章节时能看到实际优惠。更新相关样式以提升用户体验。
This commit is contained in:
@@ -7,9 +7,9 @@ App({
|
||||
globalData: {
|
||||
// API基础地址 - 连接真实后端
|
||||
// baseUrl: 'https://soulapi.quwanzhi.com',
|
||||
// baseUrl: 'https://souldev.quwanzhi.com',
|
||||
baseUrl: 'https://souldev.quwanzhi.com',
|
||||
// baseUrl: 'http://localhost:3006',
|
||||
baseUrl: 'http://localhost:8080',
|
||||
// baseUrl: 'http://localhost:8080',
|
||||
|
||||
// 小程序配置 - 真实AppID
|
||||
appId: 'wxb8bbb2b10dec74aa',
|
||||
|
||||
@@ -56,6 +56,12 @@ Page({
|
||||
sectionPrice: 1,
|
||||
fullBookPrice: 9.9,
|
||||
totalSections: 62,
|
||||
// 好友优惠展示
|
||||
userDiscount: 5,
|
||||
hasReferralDiscount: false,
|
||||
showDiscountHint: false,
|
||||
displaySectionPrice: 1,
|
||||
displayFullBookPrice: 9.9,
|
||||
|
||||
// 弹窗
|
||||
showShareModal: false,
|
||||
@@ -131,11 +137,34 @@ Page({
|
||||
}
|
||||
|
||||
try {
|
||||
const config = await accessManager.fetchLatestConfig()
|
||||
const userId = app.globalData.userInfo?.id
|
||||
const [config, purchaseRes] = await Promise.all([
|
||||
accessManager.fetchLatestConfig(),
|
||||
userId ? app.request(`/api/miniprogram/user/purchase-status?userId=${userId}`) : Promise.resolve(null)
|
||||
])
|
||||
const sectionPrice = config.prices?.section ?? 1
|
||||
const fullBookPrice = config.prices?.fullbook ?? 9.9
|
||||
const userDiscount = config.userDiscount ?? 5
|
||||
// 有推荐人 = ref/ referral_code 或 用户信息中有推荐人绑定
|
||||
const hasReferral = !!(wx.getStorageSync('referral_code') || ref || purchaseRes?.data?.hasReferrer)
|
||||
const hasReferralDiscount = hasReferral && userDiscount > 0
|
||||
const showDiscountHint = userDiscount > 0
|
||||
const displaySectionPrice = hasReferralDiscount
|
||||
? Math.round(sectionPrice * (1 - userDiscount / 100) * 100) / 100
|
||||
: sectionPrice
|
||||
const displayFullBookPrice = hasReferralDiscount
|
||||
? Math.round(fullBookPrice * (1 - userDiscount / 100) * 100) / 100
|
||||
: fullBookPrice
|
||||
this.setData({
|
||||
freeIds: config.freeChapters,
|
||||
sectionPrice: config.prices?.section ?? 1,
|
||||
fullBookPrice: config.prices?.fullbook ?? 9.9
|
||||
sectionPrice,
|
||||
fullBookPrice,
|
||||
userDiscount,
|
||||
hasReferralDiscount,
|
||||
showDiscountHint: userDiscount > 0,
|
||||
displaySectionPrice,
|
||||
displayFullBookPrice,
|
||||
purchasedCount: purchaseRes?.data?.purchasedSections?.length ?? this.data.purchasedCount ?? 0
|
||||
})
|
||||
|
||||
// 先拉取章节获取 id(mid 时必需;id 时可直接用)
|
||||
@@ -157,7 +186,8 @@ Page({
|
||||
accessState,
|
||||
canAccess,
|
||||
isLoggedIn: !!app.globalData.userInfo?.id,
|
||||
showPaywall: !canAccess
|
||||
showPaywall: !canAccess,
|
||||
purchasedCount: purchaseRes?.data?.purchasedSections?.length ?? 0
|
||||
})
|
||||
|
||||
await this.loadContent(mid, resolvedId, accessState, prefetchedChapter)
|
||||
@@ -621,9 +651,34 @@ Page({
|
||||
// 1. 刷新用户购买状态(从 orders 表拉取最新)
|
||||
await accessManager.refreshUserPurchaseStatus()
|
||||
|
||||
// 2. 重新拉取免费列表(极端情况:刚登录时当前章节可能改免费了)
|
||||
const config = await accessManager.fetchLatestConfig()
|
||||
this.setData({ freeIds: config.freeChapters })
|
||||
// 2. 重新拉取免费列表、价格与用户推荐人状态
|
||||
const userId = app.globalData.userInfo?.id
|
||||
const [config, purchaseRes] = await Promise.all([
|
||||
accessManager.fetchLatestConfig(),
|
||||
userId ? app.request(`/api/miniprogram/user/purchase-status?userId=${userId}`) : Promise.resolve(null)
|
||||
])
|
||||
const sectionPrice = config.prices?.section ?? this.data.sectionPrice ?? 1
|
||||
const fullBookPrice = config.prices?.fullbook ?? this.data.fullBookPrice ?? 9.9
|
||||
const userDiscount = config.userDiscount ?? 5
|
||||
const hasReferral = !!(wx.getStorageSync('referral_code') || purchaseRes?.data?.hasReferrer)
|
||||
const hasReferralDiscount = hasReferral && userDiscount > 0
|
||||
const displaySectionPrice = hasReferralDiscount
|
||||
? Math.round(sectionPrice * (1 - userDiscount / 100) * 100) / 100
|
||||
: sectionPrice
|
||||
const displayFullBookPrice = hasReferralDiscount
|
||||
? Math.round(fullBookPrice * (1 - userDiscount / 100) * 100) / 100
|
||||
: fullBookPrice
|
||||
this.setData({
|
||||
freeIds: config.freeChapters,
|
||||
sectionPrice,
|
||||
fullBookPrice,
|
||||
userDiscount,
|
||||
hasReferralDiscount,
|
||||
showDiscountHint: userDiscount > 0,
|
||||
displaySectionPrice,
|
||||
displayFullBookPrice,
|
||||
purchasedCount: purchaseRes?.data?.purchasedSections?.length ?? this.data.purchasedCount ?? 0
|
||||
})
|
||||
|
||||
// 3. 重新判断当前章节权限
|
||||
const newAccessState = await accessManager.determineAccessState(
|
||||
@@ -1235,9 +1290,33 @@ Page({
|
||||
wx.showLoading({ title: '重试中...', mask: true })
|
||||
|
||||
try {
|
||||
// 重新拉取配置
|
||||
const config = await accessManager.fetchLatestConfig()
|
||||
this.setData({ freeIds: config.freeChapters })
|
||||
const userId = app.globalData.userInfo?.id
|
||||
const [config, purchaseRes] = await Promise.all([
|
||||
accessManager.fetchLatestConfig(),
|
||||
userId ? app.request(`/api/miniprogram/user/purchase-status?userId=${userId}`) : Promise.resolve(null)
|
||||
])
|
||||
const sectionPrice = config.prices?.section ?? this.data.sectionPrice ?? 1
|
||||
const fullBookPrice = config.prices?.fullbook ?? this.data.fullBookPrice ?? 9.9
|
||||
const userDiscount = config.userDiscount ?? 5
|
||||
const hasReferral = !!(wx.getStorageSync('referral_code') || purchaseRes?.data?.hasReferrer)
|
||||
const hasReferralDiscount = hasReferral && userDiscount > 0
|
||||
const displaySectionPrice = hasReferralDiscount
|
||||
? Math.round(sectionPrice * (1 - userDiscount / 100) * 100) / 100
|
||||
: sectionPrice
|
||||
const displayFullBookPrice = hasReferralDiscount
|
||||
? Math.round(fullBookPrice * (1 - userDiscount / 100) * 100) / 100
|
||||
: fullBookPrice
|
||||
this.setData({
|
||||
freeIds: config.freeChapters,
|
||||
sectionPrice,
|
||||
fullBookPrice,
|
||||
userDiscount,
|
||||
hasReferralDiscount,
|
||||
showDiscountHint: userDiscount > 0,
|
||||
displaySectionPrice,
|
||||
displayFullBookPrice,
|
||||
purchasedCount: purchaseRes?.data?.purchasedSections?.length ?? this.data.purchasedCount ?? 0
|
||||
})
|
||||
|
||||
// 重新判断权限
|
||||
const newAccessState = await accessManager.determineAccessState(
|
||||
|
||||
@@ -166,7 +166,16 @@
|
||||
<!-- 购买本章 - 直接调起支付 -->
|
||||
<view class="purchase-btn purchase-section" bindtap="handlePurchaseSection">
|
||||
<text class="btn-label">购买本章</text>
|
||||
<view class="btn-price-row" wx:if="{{hasReferralDiscount}}">
|
||||
<text class="btn-original-price">¥{{section && section.price != null ? section.price : sectionPrice}}</text>
|
||||
<text class="btn-price brand-color">¥{{displaySectionPrice}}</text>
|
||||
<text class="btn-discount-tag">省{{userDiscount}}%</text>
|
||||
</view>
|
||||
<view class="btn-price-row" wx:elif="{{showDiscountHint}}">
|
||||
<text class="btn-price brand-color">¥{{section && section.price != null ? section.price : sectionPrice}}</text>
|
||||
<text class="btn-discount-tag">好友链接立省{{userDiscount}}%</text>
|
||||
</view>
|
||||
<text wx:else class="btn-price brand-color">¥{{section && section.price != null ? section.price : sectionPrice}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 解锁全书 - 只有购买超过3章才显示 -->
|
||||
@@ -176,8 +185,9 @@
|
||||
<text class="btn-label">解锁全部 {{totalSections}} 章</text>
|
||||
</view>
|
||||
<view class="btn-right">
|
||||
<text class="btn-price">¥{{fullBookPrice || 9.9}}</text>
|
||||
<text class="btn-discount">省82%</text>
|
||||
<text class="btn-original-price" wx:if="{{hasReferralDiscount}}">¥{{fullBookPrice || 9.9}}</text>
|
||||
<text class="btn-price">¥{{hasReferralDiscount ? displayFullBookPrice : (fullBookPrice || 9.9)}}</text>
|
||||
<text class="btn-discount">{{hasReferralDiscount ? '省' + userDiscount + '%' : '省82%'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -273,6 +273,23 @@
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.purchase-section .btn-price-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.btn-original-price {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.55);
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.btn-discount-tag {
|
||||
font-size: 22rpx;
|
||||
color: #00CED1;
|
||||
}
|
||||
|
||||
.brand-color {
|
||||
color: #00CED1;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ Page({
|
||||
pendingEarnings: 0, // 待结算收益(保留兼容)
|
||||
shareRate: 90, // 分成比例(90%),从 referral/data 或 config 获取
|
||||
minWithdrawAmount: 10, // 最低提现金额,从 referral/data 获取
|
||||
withdrawFee: 5, // 提现手续费%,从 referral/data 获取
|
||||
bindingDays: 30, // 绑定期天数,从 referral/data 获取
|
||||
userDiscount: 5, // 好友购买优惠%,从 referral/data 获取
|
||||
hasWechatId: false, // 是否已绑定微信号(未绑定时需引导去设置)
|
||||
@@ -199,6 +200,7 @@ Page({
|
||||
pendingEarnings: formatMoney(realData?.pendingEarnings || 0),
|
||||
shareRate: realData?.shareRate ?? 90,
|
||||
minWithdrawAmount: minWithdrawAmount,
|
||||
withdrawFee: realData?.withdrawFee ?? 5,
|
||||
bindingDays: realData?.bindingDays ?? 30,
|
||||
userDiscount: realData?.userDiscount ?? 5,
|
||||
|
||||
@@ -565,9 +567,16 @@ Page({
|
||||
return
|
||||
}
|
||||
|
||||
const withdrawFee = this.data.withdrawFee ?? 5
|
||||
const actualAmount = withdrawFee > 0
|
||||
? Math.round(availableEarnings * (1 - withdrawFee / 100) * 100) / 100
|
||||
: availableEarnings
|
||||
const feeText = withdrawFee > 0
|
||||
? `\n扣除 ${withdrawFee}% 手续费后,实际到账 ¥${actualAmount.toFixed(2)}`
|
||||
: ''
|
||||
wx.showModal({
|
||||
title: '确认提现',
|
||||
content: `将提现 ¥${availableEarnings.toFixed(2)} 到您的微信零钱`,
|
||||
content: `申请提现 ¥${availableEarnings.toFixed(2)} 到您的微信零钱${feeText}`,
|
||||
confirmText: '立即提现',
|
||||
success: async (res) => {
|
||||
if (!res.confirm) return
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
<view class="withdraw-btn {{availableEarningsNum < minWithdrawAmount || !hasWechatId ? 'btn-disabled' : ''}}" bindtap="handleWithdraw">
|
||||
{{availableEarningsNum < minWithdrawAmount ? '满' + minWithdrawAmount + '元可提现' : !hasWechatId ? '请先绑定微信号' : '申请提现 ¥' + availableEarnings}}
|
||||
</view>
|
||||
<text class="withdraw-fee-tip" wx:if="{{withdrawFee > 0}}">提现将扣除 {{withdrawFee}}% 手续费</text>
|
||||
<text class="wechat-tip" wx:if="{{availableEarningsNum > 0 && !hasWechatId}}">为便于提现到账,请先到「设置」中绑定微信号</text>
|
||||
<view class="withdraw-records-link" bindtap="goToWithdrawRecords">查看提现记录</view>
|
||||
</view>
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
|
||||
.withdraw-btn { padding: 28rpx; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); color: #fff; font-size: 32rpx; font-weight: 600; text-align: center; border-radius: 24rpx; box-shadow: 0 8rpx 24rpx rgba(0,206,209,0.3); }
|
||||
.withdraw-btn.btn-disabled { background: rgba(0,206,209,0.2); color: rgba(255,255,255,0.3); box-shadow: none; }
|
||||
.withdraw-fee-tip { display: block; font-size: 24rpx; color: rgba(255,255,255,0.5); margin-top: 12rpx; text-align: center; }
|
||||
.wechat-tip { display: block; font-size: 24rpx; color: rgba(255,165,0,0.9); margin-top: 16rpx; text-align: center; }
|
||||
.withdraw-records-link { display: block; margin-top: 16rpx; text-align: center; font-size: 26rpx; color: #00CED1; }
|
||||
|
||||
|
||||
@@ -23,12 +23,33 @@
|
||||
"condition": {
|
||||
"miniprogram": {
|
||||
"list": [
|
||||
{
|
||||
"name": "pages/referral/referral",
|
||||
"pathName": "pages/referral/referral",
|
||||
"query": "",
|
||||
"scene": null,
|
||||
"launchMode": "default"
|
||||
},
|
||||
{
|
||||
"name": "pages/read/read",
|
||||
"pathName": "pages/read/read",
|
||||
"query": "mid=11",
|
||||
"launchMode": "default",
|
||||
"scene": null
|
||||
},
|
||||
{
|
||||
"name": "pages/referral/referral",
|
||||
"pathName": "pages/referral/referral",
|
||||
"query": "",
|
||||
"launchMode": "default",
|
||||
"scene": null
|
||||
},
|
||||
{
|
||||
"name": "ces",
|
||||
"pathName": "pages/read/read",
|
||||
"query": "scene=mid%3D3&ref%3DogpTW5a9ex",
|
||||
"scene": null,
|
||||
"launchMode": "default"
|
||||
"launchMode": "default",
|
||||
"scene": null
|
||||
},
|
||||
{
|
||||
"name": "pages/chapters/chapters",
|
||||
|
||||
@@ -26,17 +26,18 @@ class ChapterAccessManager {
|
||||
if (res.success && res.freeChapters) {
|
||||
return {
|
||||
freeChapters: res.freeChapters,
|
||||
prices: res.prices || { section: 1, fullbook: 9.9 }
|
||||
prices: res.prices || { section: 1, fullbook: 9.9 },
|
||||
userDiscount: (typeof res.userDiscount === 'number' ? res.userDiscount : 5)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[AccessManager] 获取配置失败,使用默认配置:', e)
|
||||
}
|
||||
|
||||
// 默认配置
|
||||
return {
|
||||
freeChapters: ['preface', 'epilogue', '1.1', 'appendix-1', 'appendix-2', 'appendix-3'],
|
||||
prices: { section: 1, fullbook: 9.9 }
|
||||
prices: { section: 1, fullbook: 9.9 },
|
||||
userDiscount: 5
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
2
soul-admin/dist/index.html
vendored
2
soul-admin/dist/index.html
vendored
@@ -4,7 +4,7 @@
|
||||
<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-BKk79j3K.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-CgjaRP73.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-2chBMZjx.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -14,6 +14,7 @@ interface ReferralConfig {
|
||||
minWithdrawAmount: number
|
||||
bindingDays: number
|
||||
userDiscount: number
|
||||
withdrawFee: number
|
||||
enableAutoWithdraw: boolean
|
||||
}
|
||||
|
||||
@@ -22,6 +23,7 @@ const DEFAULT: ReferralConfig = {
|
||||
minWithdrawAmount: 10,
|
||||
bindingDays: 30,
|
||||
userDiscount: 5,
|
||||
withdrawFee: 5,
|
||||
enableAutoWithdraw: false,
|
||||
}
|
||||
|
||||
@@ -40,6 +42,7 @@ export function ReferralSettingsPage() {
|
||||
minWithdrawAmount: c.minWithdrawAmount ?? 10,
|
||||
bindingDays: c.bindingDays ?? 30,
|
||||
userDiscount: c.userDiscount ?? 5,
|
||||
withdrawFee: c.withdrawFee ?? 5,
|
||||
enableAutoWithdraw: c.enableAutoWithdraw ?? false,
|
||||
})
|
||||
}
|
||||
@@ -56,6 +59,7 @@ export function ReferralSettingsPage() {
|
||||
minWithdrawAmount: Number(config.minWithdrawAmount) || 0,
|
||||
bindingDays: Number(config.bindingDays) || 0,
|
||||
userDiscount: Number(config.userDiscount) || 0,
|
||||
withdrawFee: Number(config.withdrawFee) ?? 5,
|
||||
enableAutoWithdraw: Boolean(config.enableAutoWithdraw),
|
||||
}
|
||||
const res = await post<{ success?: boolean; error?: string }>('/api/admin/referral-settings', body)
|
||||
@@ -216,6 +220,24 @@ export function ReferralSettingsPage() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-300 flex items-center gap-2">
|
||||
<Percent 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.withdrawFee}
|
||||
onChange={handleNumberChange('withdrawFee')}
|
||||
/>
|
||||
<p className="text-xs text-gray-500">
|
||||
提现时扣除该比例手续费,例如 5 表示申请 100 元实际到账 95 元。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="text-gray-300 flex items-center gap-2">
|
||||
自动提现开关
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"root":["./src/app.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/auth.ts","./src/api/client.ts","./src/components/modules/user/userdetailmodal.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/dialog.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/select.tsx","./src/components/ui/slider.tsx","./src/components/ui/switch.tsx","./src/components/ui/table.tsx","./src/components/ui/tabs.tsx","./src/components/ui/textarea.tsx","./src/layouts/adminlayout.tsx","./src/lib/utils.ts","./src/pages/chapters/chapterspage.tsx","./src/pages/content/contentpage.tsx","./src/pages/dashboard/dashboardpage.tsx","./src/pages/distribution/distributionpage.tsx","./src/pages/login/loginpage.tsx","./src/pages/match/matchpage.tsx","./src/pages/not-found/notfoundpage.tsx","./src/pages/orders/orderspage.tsx","./src/pages/payment/paymentpage.tsx","./src/pages/qrcodes/qrcodespage.tsx","./src/pages/referral-settings/referralsettingspage.tsx","./src/pages/settings/settingspage.tsx","./src/pages/site/sitepage.tsx","./src/pages/users/userspage.tsx","./src/pages/withdrawals/withdrawalspage.tsx"],"errors":true,"version":"5.6.3"}
|
||||
{"root":["./src/app.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/auth.ts","./src/api/client.ts","./src/components/modules/user/userdetailmodal.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/dialog.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/select.tsx","./src/components/ui/slider.tsx","./src/components/ui/switch.tsx","./src/components/ui/table.tsx","./src/components/ui/tabs.tsx","./src/components/ui/textarea.tsx","./src/layouts/adminlayout.tsx","./src/lib/utils.ts","./src/pages/chapters/chapterspage.tsx","./src/pages/content/contentpage.tsx","./src/pages/dashboard/dashboardpage.tsx","./src/pages/distribution/distributionpage.tsx","./src/pages/login/loginpage.tsx","./src/pages/match/matchpage.tsx","./src/pages/not-found/notfoundpage.tsx","./src/pages/orders/orderspage.tsx","./src/pages/payment/paymentpage.tsx","./src/pages/qrcodes/qrcodespage.tsx","./src/pages/referral-settings/referralsettingspage.tsx","./src/pages/settings/settingspage.tsx","./src/pages/site/sitepage.tsx","./src/pages/users/userspage.tsx","./src/pages/withdrawals/withdrawalspage.tsx"],"version":"5.6.3"}
|
||||
@@ -2,6 +2,7 @@ package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
@@ -155,12 +156,26 @@ func AdminWithdrawalsAction(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 调用微信转账接口(FundApp 单笔发起转账,与 商家转账.md 示例一致)
|
||||
// 调用微信转账接口:按提现手续费扣除后打款,例如申请100元、手续费5%则实际打款95元
|
||||
remark := "提现"
|
||||
if w.Remark != nil && *w.Remark != "" {
|
||||
remark = *w.Remark
|
||||
}
|
||||
amountFen := int(w.Amount * 100)
|
||||
withdrawFee := 0.0
|
||||
var refCfg model.SystemConfig
|
||||
if err := db.Where("config_key = ?", "referral_config").First(&refCfg).Error; err == nil {
|
||||
var refVal map[string]interface{}
|
||||
if err := json.Unmarshal(refCfg.ConfigValue, &refVal); err == nil {
|
||||
if v, ok := refVal["withdrawFee"].(float64); ok {
|
||||
withdrawFee = v / 100
|
||||
}
|
||||
}
|
||||
}
|
||||
actualAmount := w.Amount * (1 - withdrawFee)
|
||||
if actualAmount < 0.01 {
|
||||
actualAmount = 0.01
|
||||
}
|
||||
amountFen := int(actualAmount * 100)
|
||||
if amountFen < 1 {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "提现金额异常"})
|
||||
return
|
||||
|
||||
@@ -59,7 +59,7 @@ func CronSyncOrders(c *gin.Context) {
|
||||
if pt == "fullbook" {
|
||||
db.Model(&model.User{}).Where("id = ?", o.UserID).Update("has_full_book", true)
|
||||
}
|
||||
processReferralCommission(db, o.UserID, totalAmount, o.OrderSN)
|
||||
processReferralCommission(db, o.UserID, totalAmount, o.OrderSN, &o)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
|
||||
@@ -91,6 +91,19 @@ func GetPublicDBConfig(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 好友优惠(用于 read 页展示优惠价)
|
||||
var refRow model.SystemConfig
|
||||
if err := db.Where("config_key = ?", "referral_config").First(&refRow).Error; err == nil {
|
||||
var refVal map[string]interface{}
|
||||
if err := json.Unmarshal(refRow.ConfigValue, &refVal); err == nil {
|
||||
if v, ok := refVal["userDiscount"].(float64); ok {
|
||||
out["userDiscount"] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, has := out["userDiscount"]; !has {
|
||||
out["userDiscount"] = float64(5)
|
||||
}
|
||||
c.JSON(http.StatusOK, out)
|
||||
}
|
||||
|
||||
@@ -225,6 +238,7 @@ func AdminReferralSettingsGet(c *gin.Context) {
|
||||
"minWithdrawAmount": float64(10),
|
||||
"bindingDays": float64(30),
|
||||
"userDiscount": float64(5),
|
||||
"withdrawFee": float64(5),
|
||||
"enableAutoWithdraw": false,
|
||||
}
|
||||
var row model.SystemConfig
|
||||
@@ -247,6 +261,7 @@ func AdminReferralSettingsPost(c *gin.Context) {
|
||||
MinWithdrawAmount float64 `json:"minWithdrawAmount"`
|
||||
BindingDays float64 `json:"bindingDays"`
|
||||
UserDiscount float64 `json:"userDiscount"`
|
||||
WithdrawFee float64 `json:"withdrawFee"`
|
||||
EnableAutoWithdraw bool `json:"enableAutoWithdraw"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
@@ -258,6 +273,7 @@ func AdminReferralSettingsPost(c *gin.Context) {
|
||||
"minWithdrawAmount": body.MinWithdrawAmount,
|
||||
"bindingDays": body.BindingDays,
|
||||
"userDiscount": body.UserDiscount,
|
||||
"withdrawFee": body.WithdrawFee,
|
||||
"enableAutoWithdraw": body.EnableAutoWithdraw,
|
||||
}
|
||||
valBytes, err := json.Marshal(val)
|
||||
|
||||
@@ -195,9 +195,33 @@ func miniprogramPayPost(c *gin.Context) {
|
||||
|
||||
db := database.DB()
|
||||
|
||||
// 获取推广配置计算好友优惠
|
||||
// 查询用户的有效推荐人(先查 binding,再查 referralCode)
|
||||
var referrerID *string
|
||||
if req.UserID != "" {
|
||||
var binding struct {
|
||||
ReferrerID string `gorm:"column:referrer_id"`
|
||||
}
|
||||
err := db.Raw(`
|
||||
SELECT referrer_id
|
||||
FROM referral_bindings
|
||||
WHERE referee_id = ? AND status = 'active' AND expiry_date > NOW()
|
||||
ORDER BY binding_date DESC
|
||||
LIMIT 1
|
||||
`, req.UserID).Scan(&binding).Error
|
||||
if err == nil && binding.ReferrerID != "" {
|
||||
referrerID = &binding.ReferrerID
|
||||
}
|
||||
}
|
||||
if referrerID == nil && req.ReferralCode != "" {
|
||||
var refUser model.User
|
||||
if err := db.Where("referral_code = ?", req.ReferralCode).First(&refUser).Error; err == nil {
|
||||
referrerID = &refUser.ID
|
||||
}
|
||||
}
|
||||
|
||||
// 有推荐人时应用好友优惠(无论是 binding 还是 referralCode)
|
||||
finalAmount := req.Amount
|
||||
if req.ReferralCode != "" {
|
||||
if referrerID != nil {
|
||||
var cfg model.SystemConfig
|
||||
if err := db.Where("config_key = ?", "referral_config").First(&cfg).Error; err == nil {
|
||||
var config map[string]interface{}
|
||||
@@ -233,33 +257,6 @@ func miniprogramPayPost(c *gin.Context) {
|
||||
clientIP = "127.0.0.1"
|
||||
}
|
||||
|
||||
// 查询用户的有效推荐人
|
||||
var referrerID *string
|
||||
if req.UserID != "" {
|
||||
var binding struct {
|
||||
ReferrerID string `gorm:"column:referrer_id"`
|
||||
}
|
||||
err := db.Raw(`
|
||||
SELECT referrer_id
|
||||
FROM referral_bindings
|
||||
WHERE referee_id = ? AND status = 'active' AND expiry_date > NOW()
|
||||
ORDER BY binding_date DESC
|
||||
LIMIT 1
|
||||
`, req.UserID).Scan(&binding).Error
|
||||
|
||||
if err == nil && binding.ReferrerID != "" {
|
||||
referrerID = &binding.ReferrerID
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有绑定,尝试从邀请码解析推荐人
|
||||
if referrerID == nil && req.ReferralCode != "" {
|
||||
var refUser model.User
|
||||
if err := db.Where("referral_code = ?", req.ReferralCode).First(&refUser).Error; err == nil {
|
||||
referrerID = &refUser.ID
|
||||
}
|
||||
}
|
||||
|
||||
// 插入订单到数据库
|
||||
userID := req.UserID
|
||||
if userID == "" {
|
||||
@@ -472,7 +469,7 @@ func MiniprogramPayNotify(c *gin.Context) {
|
||||
"user_id = ? AND product_type = ? AND product_id = ? AND status = 'created' AND order_sn != ?",
|
||||
buyerUserID, attach.ProductType, productID, orderSn,
|
||||
).Delete(&model.Order{})
|
||||
processReferralCommission(db, buyerUserID, totalAmount, orderSn)
|
||||
processReferralCommission(db, buyerUserID, totalAmount, orderSn, &order)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -492,9 +489,11 @@ func MiniprogramPayNotify(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 处理分销佣金
|
||||
func processReferralCommission(db *gorm.DB, buyerUserID string, amount float64, orderSn string) {
|
||||
// 获取分成配置,默认 90%
|
||||
// amount 为实付金额(若有好友优惠则已打折);order 用于判断是否有推荐人从而反推原价
|
||||
func processReferralCommission(db *gorm.DB, buyerUserID string, amount float64, orderSn string, order *model.Order) {
|
||||
// 获取分成配置,默认 90%;好友优惠用于反推原价(佣金按原价计算)
|
||||
distributorShare := 0.9
|
||||
userDiscount := 0.0
|
||||
var cfg model.SystemConfig
|
||||
if err := db.Where("config_key = ?", "referral_config").First(&cfg).Error; err == nil {
|
||||
var config map[string]interface{}
|
||||
@@ -502,6 +501,16 @@ func processReferralCommission(db *gorm.DB, buyerUserID string, amount float64,
|
||||
if share, ok := config["distributorShare"].(float64); ok {
|
||||
distributorShare = share / 100
|
||||
}
|
||||
if disc, ok := config["userDiscount"].(float64); ok {
|
||||
userDiscount = disc / 100
|
||||
}
|
||||
}
|
||||
}
|
||||
// 佣金按原价计算:若有推荐人则实付已打折,反推原价 = amount / (1 - userDiscount)
|
||||
commissionBase := amount
|
||||
if order != nil && userDiscount > 0 && (order.ReferrerID != nil && *order.ReferrerID != "" || order.ReferralCode != nil && *order.ReferralCode != "") {
|
||||
if (1 - userDiscount) > 0 {
|
||||
commissionBase = amount / (1 - userDiscount)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,8 +543,8 @@ func processReferralCommission(db *gorm.DB, buyerUserID string, amount float64,
|
||||
return
|
||||
}
|
||||
|
||||
// 计算佣金
|
||||
commission := amount * distributorShare
|
||||
// 计算佣金(按原价)
|
||||
commission := commissionBase * distributorShare
|
||||
newPurchaseCount := binding.PurchaseCount + 1
|
||||
newTotalCommission := binding.TotalCommission + commission
|
||||
|
||||
|
||||
@@ -158,6 +158,7 @@ func ReferralData(c *gin.Context) {
|
||||
minWithdrawAmount := 10.0
|
||||
bindingDays := defaultBindingDays
|
||||
userDiscount := 5
|
||||
withdrawFee := 5.0
|
||||
|
||||
var cfg model.SystemConfig
|
||||
if err := db.Where("config_key = ?", "referral_config").First(&cfg).Error; err == nil {
|
||||
@@ -175,6 +176,9 @@ func ReferralData(c *gin.Context) {
|
||||
if discount, ok := config["userDiscount"].(float64); ok {
|
||||
userDiscount = int(discount)
|
||||
}
|
||||
if fee, ok := config["withdrawFee"].(float64); ok {
|
||||
withdrawFee = fee
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,6 +401,7 @@ func ReferralData(c *gin.Context) {
|
||||
"minWithdrawAmount": minWithdrawAmount,
|
||||
"bindingDays": bindingDays,
|
||||
"userDiscount": userDiscount,
|
||||
"withdrawFee": withdrawFee,
|
||||
|
||||
// 推荐码
|
||||
"referralCode": getStringValue(user.ReferralCode),
|
||||
|
||||
@@ -285,6 +285,12 @@ func UserPurchaseStatus(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 是否有推荐人(被推荐绑定,可享好友优惠)
|
||||
var refCount int64
|
||||
db.Model(&model.ReferralBinding{}).Where("referee_id = ? AND status = ?", userId, "active").
|
||||
Where("expiry_date > ?", time.Now()).Count(&refCount)
|
||||
hasReferrer := refCount > 0
|
||||
|
||||
// 匹配次数配额:纯计算(订单 + match_records)
|
||||
freeLimit := getFreeMatchLimit(db)
|
||||
matchQuota := GetMatchQuota(db, userId, freeLimit)
|
||||
@@ -301,6 +307,7 @@ func UserPurchaseStatus(c *gin.Context) {
|
||||
"purchasedSections": purchasedSections,
|
||||
"sectionMidMap": sectionMidMap,
|
||||
"purchasedCount": len(purchasedSections),
|
||||
"hasReferrer": hasReferrer,
|
||||
"matchCount": matchQuota.PurchasedTotal,
|
||||
"matchQuota": gin.H{
|
||||
"purchasedTotal": matchQuota.PurchasedTotal,
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user