feat(invitation): 新增返利统计汇总与时间筛选
This commit is contained in:
@@ -998,17 +998,8 @@ func (r *Repository) ListAdminInvitations(ctx context.Context, query AdminInvita
|
||||
return list, total, nil
|
||||
}
|
||||
|
||||
// ListAdminRebateLedgers 管理后台查询返利流水
|
||||
func (r *Repository) ListAdminRebateLedgers(ctx context.Context, query AdminRebateLedgerQuery) ([]map[string]any, int64, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return nil, 0, ErrDependencyUnavailable
|
||||
}
|
||||
if query.Page < 1 {
|
||||
query.Page = 1
|
||||
}
|
||||
if query.PageSize < 1 || query.PageSize > 100 {
|
||||
query.PageSize = 20
|
||||
}
|
||||
// adminRebateLedgerBase 构造后台返利流水/统计数据共用的筛选范围。
|
||||
func (r *Repository) adminRebateLedgerBase(ctx context.Context, query AdminRebateLedgerQuery) *gorm.DB {
|
||||
db := r.db.WithContext(ctx).Table("invitation_rebate_ledgers irl").
|
||||
Joins("LEFT JOIN users inviter ON inviter.id = irl.inviter_id").
|
||||
Joins("LEFT JOIN users invitee ON invitee.id = irl.invitee_id")
|
||||
@@ -1025,6 +1016,15 @@ func (r *Repository) ListAdminRebateLedgers(ctx context.Context, query AdminReba
|
||||
if t := strings.TrimSpace(query.TradeRole); t != "" {
|
||||
db = db.Where("irl.trade_role = ?", t)
|
||||
}
|
||||
if s := strings.TrimSpace(query.Source); s != "" {
|
||||
db = db.Where("irl.source = ?", s)
|
||||
}
|
||||
if start, ok := parseAdminRebateDate(query.StartDate); ok {
|
||||
db = db.Where("irl.settled_at >= ?", start)
|
||||
}
|
||||
if end, ok := parseAdminRebateDate(query.EndDate); ok {
|
||||
db = db.Where("irl.settled_at < ?", end.AddDate(0, 0, 1))
|
||||
}
|
||||
if query.DistributionSiteID != nil {
|
||||
db = db.Where("irl.distribution_site_id = ?", *query.DistributionSiteID)
|
||||
}
|
||||
@@ -1034,6 +1034,34 @@ func (r *Repository) ListAdminRebateLedgers(ctx context.Context, query AdminReba
|
||||
if query.InviteeID > 0 {
|
||||
db = db.Where("irl.invitee_id = ?", query.InviteeID)
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func parseAdminRebateDate(value string) (time.Time, bool) {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return time.Time{}, false
|
||||
}
|
||||
for _, layout := range []string{"2006-01-02", "2006-01-02 15:04:05", time.RFC3339} {
|
||||
if parsed, err := time.ParseInLocation(layout, value, time.Local); err == nil {
|
||||
return parsed, true
|
||||
}
|
||||
}
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
// ListAdminRebateLedgers 管理后台查询返利流水
|
||||
func (r *Repository) ListAdminRebateLedgers(ctx context.Context, query AdminRebateLedgerQuery) ([]map[string]any, int64, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return nil, 0, ErrDependencyUnavailable
|
||||
}
|
||||
if query.Page < 1 {
|
||||
query.Page = 1
|
||||
}
|
||||
if query.PageSize < 1 || query.PageSize > 100 {
|
||||
query.PageSize = 20
|
||||
}
|
||||
db := r.adminRebateLedgerBase(ctx, query)
|
||||
|
||||
var total int64
|
||||
if err := db.Count(&total).Error; err != nil {
|
||||
@@ -1095,6 +1123,141 @@ func (r *Repository) ListAdminRebateLedgers(ctx context.Context, query AdminReba
|
||||
return list, total, nil
|
||||
}
|
||||
|
||||
// AdminRebateSummary 管理后台返利统计分析,沿用流水页的筛选条件。
|
||||
func (r *Repository) AdminRebateSummary(ctx context.Context, query AdminRebateLedgerQuery) (*AdminRebateSummaryDTO, error) {
|
||||
if r == nil || r.db == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
|
||||
type aggregateRow struct {
|
||||
TotalRebateCent int64 `gorm:"column:total_rebate_cent"`
|
||||
LedgerCount int64 `gorm:"column:ledger_count"`
|
||||
InviterCount int64 `gorm:"column:inviter_count"`
|
||||
InviteeCount int64 `gorm:"column:invitee_count"`
|
||||
OrderRebateCent int64 `gorm:"column:order_rebate_cent"`
|
||||
OrderCount int64 `gorm:"column:order_count"`
|
||||
PickupRebateCent int64 `gorm:"column:pickup_rebate_cent"`
|
||||
PickupCount int64 `gorm:"column:pickup_count"`
|
||||
BuyerRebateCent int64 `gorm:"column:buyer_rebate_cent"`
|
||||
BuyerCount int64 `gorm:"column:buyer_count"`
|
||||
SellerRebateCent int64 `gorm:"column:seller_rebate_cent"`
|
||||
SellerCount int64 `gorm:"column:seller_count"`
|
||||
FirstMonthCent int64 `gorm:"column:first_month_cent"`
|
||||
SecondMonthCent int64 `gorm:"column:second_month_cent"`
|
||||
SubsequentCent int64 `gorm:"column:subsequent_cent"`
|
||||
}
|
||||
var aggregate aggregateRow
|
||||
err := r.adminRebateLedgerBase(ctx, query).Select(`
|
||||
COALESCE(SUM(irl.rebate_amount_cent), 0) AS total_rebate_cent,
|
||||
COUNT(*) AS ledger_count,
|
||||
COUNT(DISTINCT irl.inviter_id) AS inviter_count,
|
||||
COUNT(DISTINCT irl.invitee_id) AS invitee_count,
|
||||
COALESCE(SUM(CASE WHEN irl.source = 'rental_order' THEN irl.rebate_amount_cent ELSE 0 END), 0) AS order_rebate_cent,
|
||||
COALESCE(SUM(CASE WHEN irl.source = 'rental_order' THEN 1 ELSE 0 END), 0) AS order_count,
|
||||
COALESCE(SUM(CASE WHEN irl.source = 'pickup' THEN irl.rebate_amount_cent ELSE 0 END), 0) AS pickup_rebate_cent,
|
||||
COALESCE(SUM(CASE WHEN irl.source = 'pickup' THEN 1 ELSE 0 END), 0) AS pickup_count,
|
||||
COALESCE(SUM(CASE WHEN irl.trade_role = 'buyer' THEN irl.rebate_amount_cent ELSE 0 END), 0) AS buyer_rebate_cent,
|
||||
COALESCE(SUM(CASE WHEN irl.trade_role = 'buyer' THEN 1 ELSE 0 END), 0) AS buyer_count,
|
||||
COALESCE(SUM(CASE WHEN irl.trade_role = 'seller' THEN irl.rebate_amount_cent ELSE 0 END), 0) AS seller_rebate_cent,
|
||||
COALESCE(SUM(CASE WHEN irl.trade_role = 'seller' THEN 1 ELSE 0 END), 0) AS seller_count,
|
||||
COALESCE(SUM(CASE WHEN irl.cycle_stage = ? THEN irl.rebate_amount_cent ELSE 0 END), 0) AS first_month_cent,
|
||||
COALESCE(SUM(CASE WHEN irl.cycle_stage = ? THEN irl.rebate_amount_cent ELSE 0 END), 0) AS second_month_cent,
|
||||
COALESCE(SUM(CASE WHEN irl.cycle_stage = ? THEN irl.rebate_amount_cent ELSE 0 END), 0) AS subsequent_cent
|
||||
`, CycleFirstMonth, CycleSecondMonth, CycleSubsequent).Scan(&aggregate).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := &AdminRebateSummaryDTO{
|
||||
TotalRebateCent: aggregate.TotalRebateCent,
|
||||
LedgerCount: aggregate.LedgerCount,
|
||||
InviterCount: aggregate.InviterCount,
|
||||
InviteeCount: aggregate.InviteeCount,
|
||||
OrderRebateCent: aggregate.OrderRebateCent,
|
||||
OrderCount: aggregate.OrderCount,
|
||||
PickupRebateCent: aggregate.PickupRebateCent,
|
||||
PickupCount: aggregate.PickupCount,
|
||||
BuyerRebateCent: aggregate.BuyerRebateCent,
|
||||
BuyerCount: aggregate.BuyerCount,
|
||||
SellerRebateCent: aggregate.SellerRebateCent,
|
||||
SellerCount: aggregate.SellerCount,
|
||||
FirstMonthCent: aggregate.FirstMonthCent,
|
||||
SecondMonthCent: aggregate.SecondMonthCent,
|
||||
SubsequentCent: aggregate.SubsequentCent,
|
||||
Daily: make([]AdminRebateDailyStat, 0),
|
||||
TopInviters: make([]AdminRebateInviterStat, 0),
|
||||
}
|
||||
if result.LedgerCount > 0 {
|
||||
result.AvgRebateCent = result.TotalRebateCent / result.LedgerCount
|
||||
}
|
||||
|
||||
type dailyRow struct {
|
||||
Date string `gorm:"column:date"`
|
||||
LedgerCount int64 `gorm:"column:ledger_count"`
|
||||
RebateAmountCent int64 `gorm:"column:rebate_amount_cent"`
|
||||
OrderAmountCent int64 `gorm:"column:order_amount_cent"`
|
||||
PickupAmountCent int64 `gorm:"column:pickup_amount_cent"`
|
||||
}
|
||||
var daily []dailyRow
|
||||
err = r.adminRebateLedgerBase(ctx, query).
|
||||
Select(`
|
||||
DATE_FORMAT(irl.settled_at, '%Y-%m-%d') AS date,
|
||||
COUNT(*) AS ledger_count,
|
||||
COALESCE(SUM(irl.rebate_amount_cent), 0) AS rebate_amount_cent,
|
||||
COALESCE(SUM(CASE WHEN irl.source = 'rental_order' THEN irl.rebate_amount_cent ELSE 0 END), 0) AS order_amount_cent,
|
||||
COALESCE(SUM(CASE WHEN irl.source = 'pickup' THEN irl.rebate_amount_cent ELSE 0 END), 0) AS pickup_amount_cent
|
||||
`).
|
||||
Group("DATE_FORMAT(irl.settled_at, '%Y-%m-%d')").
|
||||
Order("DATE_FORMAT(irl.settled_at, '%Y-%m-%d') DESC").
|
||||
Scan(&daily).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, row := range daily {
|
||||
result.Daily = append(result.Daily, AdminRebateDailyStat{
|
||||
Date: row.Date,
|
||||
LedgerCount: row.LedgerCount,
|
||||
RebateAmountCent: row.RebateAmountCent,
|
||||
OrderAmountCent: row.OrderAmountCent,
|
||||
PickupAmountCent: row.PickupAmountCent,
|
||||
})
|
||||
}
|
||||
|
||||
type topInviterRow struct {
|
||||
InviterID uint64 `gorm:"column:inviter_id"`
|
||||
InviterPhone string `gorm:"column:inviter_phone"`
|
||||
InviterNickname string `gorm:"column:inviter_nickname"`
|
||||
LedgerCount int64 `gorm:"column:ledger_count"`
|
||||
RebateAmountCent int64 `gorm:"column:rebate_amount_cent"`
|
||||
}
|
||||
var top []topInviterRow
|
||||
err = r.adminRebateLedgerBase(ctx, query).
|
||||
Select(`
|
||||
irl.inviter_id,
|
||||
COALESCE(inviter.phone, '') AS inviter_phone,
|
||||
COALESCE(inviter.nickname, '') AS inviter_nickname,
|
||||
COUNT(*) AS ledger_count,
|
||||
COALESCE(SUM(irl.rebate_amount_cent), 0) AS rebate_amount_cent
|
||||
`).
|
||||
Group("irl.inviter_id, inviter.phone, inviter.nickname").
|
||||
Order("rebate_amount_cent DESC").
|
||||
Limit(10).
|
||||
Scan(&top).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, row := range top {
|
||||
result.TopInviters = append(result.TopInviters, AdminRebateInviterStat{
|
||||
InviterID: row.InviterID,
|
||||
InviterPhone: row.InviterPhone,
|
||||
InviterNickname: row.InviterNickname,
|
||||
LedgerCount: row.LedgerCount,
|
||||
RebateAmountCent: row.RebateAmountCent,
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ProcessOrderInvitationRebateTx 便捷事务入口,供订单结账完成终态时调用
|
||||
func ProcessOrderInvitationRebateTx(tx *gorm.DB, orderID uint64) error {
|
||||
return ProcessOrderInvitationRebateWithOptionsTx(tx, orderID, OrderRebateOptions{})
|
||||
|
||||
Reference in New Issue
Block a user