优化小程序阅读页面,新增好友优惠展示逻辑,支持通过推荐人获取折扣。调整价格计算方式,确保用户在购买章节时能看到实际优惠。更新相关样式以提升用户体验。

This commit is contained in:
乘风
2026-02-12 17:08:46 +08:00
parent f1dad89434
commit 543a465682
20 changed files with 352 additions and 139 deletions

View File

@@ -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',

View File

@@ -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
})
// 先拉取章节获取 idmid 时必需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(

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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>

View File

@@ -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; }

View File

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

View File

@@ -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

View File

@@ -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>

View File

@@ -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">

View File

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

View File

@@ -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

View File

@@ -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{

View File

@@ -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)

View File

@@ -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

View File

@@ -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),

View File

@@ -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.