144 lines
4.9 KiB
Go
144 lines
4.9 KiB
Go
package model
|
||
|
||
import (
|
||
"errors"
|
||
"fmt"
|
||
"strings"
|
||
|
||
"github.com/QuantumNous/new-api/common"
|
||
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// AffInviteProfitShareLog 利润分成模式下,单次请求结算产生的分销入账流水。
|
||
type AffInviteProfitShareLog struct {
|
||
Id int `json:"id" gorm:"primaryKey;autoIncrement"`
|
||
InviterId int `json:"inviter_id" gorm:"not null;index:idx_aff_ps_inv_inv,priority:1"`
|
||
InviteeUserId int `json:"invitee_user_id" gorm:"not null;index:idx_aff_ps_inv_inv,priority:2;index"`
|
||
ChannelId int `json:"channel_id" gorm:"not null;column:channel_id"`
|
||
RouteSlug string `json:"route_slug,omitempty" gorm:"-"`
|
||
ModelName string `json:"model_name" gorm:"type:varchar(255);not null;default:'';column:model_name"`
|
||
UserQuotaCharged int `json:"user_quota_charged" gorm:"not null;column:user_quota_charged"`
|
||
MarkupSliceQuota int `json:"markup_slice_quota" gorm:"not null;column:markup_slice_quota"`
|
||
RewardQuota int `json:"reward_quota" gorm:"not null;column:reward_quota"`
|
||
// CommissionBps 本条结算适用的代理分润比例(万分之一,与 AffiliateDefaultCommissionBps / EffectiveAffiliateCommissionBps 同单位)。
|
||
CommissionBps int `json:"commission_bps" gorm:"not null;default:0;column:commission_bps"`
|
||
CreatedAt int64 `json:"created_at" gorm:"bigint;index"`
|
||
}
|
||
|
||
func (AffInviteProfitShareLog) TableName() string {
|
||
return "aff_invite_profit_share_logs"
|
||
}
|
||
|
||
// CreditDistributorProfitShare 将利润分成「入账额度」记入邀请人 aff_quota,并累计关系表 profit_share_earned_quota。
|
||
// markupSliceQuota:用户扣费中的加价切片(分润基数);rewardQuota:基数 × 生效分润比例后的入账额度(rewardQuota <= markupSliceQuota)。
|
||
// commissionBps:本条入账采用的万分之一比例(记入流水供明细展示)。
|
||
func CreditDistributorProfitShare(inviterId, inviteeUserId, channelId int, modelName string, userQuotaCharged, markupSliceQuota, rewardQuota, commissionBps int) error {
|
||
if inviterId <= 0 || inviteeUserId <= 0 || rewardQuota <= 0 {
|
||
return nil
|
||
}
|
||
if markupSliceQuota < 0 {
|
||
markupSliceQuota = 0
|
||
}
|
||
reward := rewardQuota
|
||
modelName = strings.TrimSpace(modelName)
|
||
ts := common.GetTimestamp()
|
||
|
||
return DB.Transaction(func(tx *gorm.DB) error {
|
||
up := tx.Model(&User{}).Where("id = ?", inviterId).Updates(map[string]interface{}{
|
||
"aff_quota": gorm.Expr("aff_quota + ?", reward),
|
||
"aff_history": gorm.Expr("aff_history + ?", reward),
|
||
})
|
||
if up.Error != nil {
|
||
return up.Error
|
||
}
|
||
if up.RowsAffected == 0 {
|
||
return fmt.Errorf("inviter user not found: %d", inviterId)
|
||
}
|
||
res := tx.Model(&AffInviteRelation{}).
|
||
Where("inviter_id = ? AND invitee_user_id = ?", inviterId, inviteeUserId).
|
||
UpdateColumn("profit_share_earned_quota", gorm.Expr("profit_share_earned_quota + ?", reward))
|
||
if res.Error != nil {
|
||
return res.Error
|
||
}
|
||
if commissionBps < 0 {
|
||
commissionBps = 0
|
||
}
|
||
if commissionBps > maxAffiliateCommissionBps {
|
||
commissionBps = maxAffiliateCommissionBps
|
||
}
|
||
row := AffInviteProfitShareLog{
|
||
InviterId: inviterId,
|
||
InviteeUserId: inviteeUserId,
|
||
ChannelId: channelId,
|
||
ModelName: modelName,
|
||
UserQuotaCharged: userQuotaCharged,
|
||
MarkupSliceQuota: markupSliceQuota,
|
||
RewardQuota: reward,
|
||
CommissionBps: commissionBps,
|
||
CreatedAt: ts,
|
||
}
|
||
return tx.Create(&row).Error
|
||
})
|
||
}
|
||
|
||
// ListAffInviteProfitShareLogs 分页返回某邀请人对某一被邀请人的利润分成明细。
|
||
func ListAffInviteProfitShareLogs(inviterId, inviteeUserId int, pageInfo *common.PageInfo) ([]AffInviteProfitShareLog, int64, error) {
|
||
if inviterId <= 0 || inviteeUserId <= 0 {
|
||
return nil, 0, errors.New("invalid id")
|
||
}
|
||
var total int64
|
||
base := DB.Model(&AffInviteProfitShareLog{}).Where("inviter_id = ? AND invitee_user_id = ?", inviterId, inviteeUserId)
|
||
if err := base.Count(&total).Error; err != nil {
|
||
return nil, 0, err
|
||
}
|
||
var rows []AffInviteProfitShareLog
|
||
err := DB.Where("inviter_id = ? AND invitee_user_id = ?", inviterId, inviteeUserId).
|
||
Order("created_at DESC").
|
||
Limit(pageInfo.GetPageSize()).
|
||
Offset(pageInfo.GetStartIdx()).
|
||
Find(&rows).Error
|
||
if err != nil {
|
||
return nil, 0, err
|
||
}
|
||
if rows == nil {
|
||
rows = []AffInviteProfitShareLog{}
|
||
}
|
||
attachProfitShareLogRouteSlugs(rows)
|
||
return rows, total, nil
|
||
}
|
||
|
||
func attachProfitShareLogRouteSlugs(rows []AffInviteProfitShareLog) {
|
||
if len(rows) == 0 {
|
||
return
|
||
}
|
||
ids := make([]int, 0, len(rows))
|
||
seen := make(map[int]struct{}, len(rows))
|
||
for i := range rows {
|
||
cid := rows[i].ChannelId
|
||
if cid <= 0 {
|
||
continue
|
||
}
|
||
if _, ok := seen[cid]; ok {
|
||
continue
|
||
}
|
||
seen[cid] = struct{}{}
|
||
ids = append(ids, cid)
|
||
}
|
||
slugMap := GetRouteSlugsByChannelIDs(ids)
|
||
for i := range rows {
|
||
cid := rows[i].ChannelId
|
||
if cid <= 0 {
|
||
continue
|
||
}
|
||
slug := ""
|
||
if slugMap != nil {
|
||
slug = strings.TrimSpace(slugMap[cid])
|
||
}
|
||
if slug == "" {
|
||
slug = DefaultRouteSlugFromChannelID(int64(cid))
|
||
}
|
||
rows[i].RouteSlug = slug
|
||
}
|
||
}
|