mirror of
https://gitee.com/wanwujie/sub2api
synced 2026-05-06 06:00:44 +08:00
完善返利转入余额历史显示
This commit is contained in:
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -973,16 +974,213 @@ func (s *adminServiceImpl) GetUserUsageStats(ctx context.Context, userID int64,
|
||||
// GetUserBalanceHistory returns paginated balance/concurrency change records for a user.
|
||||
func (s *adminServiceImpl) GetUserBalanceHistory(ctx context.Context, userID int64, page, pageSize int, codeType string) ([]RedeemCode, int64, float64, error) {
|
||||
params := pagination.PaginationParams{Page: page, PageSize: pageSize}
|
||||
if codeType == RedeemTypeAffiliateBalance {
|
||||
codes, total, err := s.listAffiliateBalanceHistory(ctx, userID, params)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
totalRecharged, err := s.redeemCodeRepo.SumPositiveBalanceByUser(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
return codes, total, totalRecharged, nil
|
||||
}
|
||||
|
||||
if codeType == "" {
|
||||
return s.getAllUserBalanceHistory(ctx, userID, params)
|
||||
}
|
||||
|
||||
codes, result, err := s.redeemCodeRepo.ListByUserPaginated(ctx, userID, params, codeType)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
total := result.Total
|
||||
// Aggregate total recharged amount (only once, regardless of type filter)
|
||||
totalRecharged, err := s.redeemCodeRepo.SumPositiveBalanceByUser(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
return codes, result.Total, totalRecharged, nil
|
||||
return codes, total, totalRecharged, nil
|
||||
}
|
||||
|
||||
func (s *adminServiceImpl) getAllUserBalanceHistory(ctx context.Context, userID int64, params pagination.PaginationParams) ([]RedeemCode, int64, float64, error) {
|
||||
needed := params.Offset() + params.Limit()
|
||||
if needed < params.Limit() {
|
||||
needed = params.Limit()
|
||||
}
|
||||
|
||||
redeemCodes, redeemTotal, err := s.listRedeemBalanceHistoryForMerge(ctx, userID, needed)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
affiliateCodes, affiliateTotal, err := s.listAffiliateBalanceHistoryForMerge(ctx, userID, needed)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
codes := mergeBalanceHistoryCodes(redeemCodes, affiliateCodes, params)
|
||||
|
||||
totalRecharged, err := s.redeemCodeRepo.SumPositiveBalanceByUser(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
return codes, redeemTotal + affiliateTotal, totalRecharged, nil
|
||||
}
|
||||
|
||||
func (s *adminServiceImpl) listRedeemBalanceHistoryForMerge(ctx context.Context, userID int64, needed int) ([]RedeemCode, int64, error) {
|
||||
if needed <= 0 {
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
||||
var (
|
||||
out []RedeemCode
|
||||
total int64
|
||||
)
|
||||
for page := 1; len(out) < needed; page++ {
|
||||
params := pagination.PaginationParams{Page: page, PageSize: 1000}
|
||||
codes, result, err := s.redeemCodeRepo.ListByUserPaginated(ctx, userID, params, "")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if result != nil {
|
||||
total = result.Total
|
||||
}
|
||||
out = append(out, codes...)
|
||||
if len(codes) < params.Limit() || int64(len(out)) >= total {
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(out) > needed {
|
||||
out = out[:needed]
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
|
||||
func (s *adminServiceImpl) listAffiliateBalanceHistoryForMerge(ctx context.Context, userID int64, needed int) ([]RedeemCode, int64, error) {
|
||||
if needed <= 0 {
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
||||
var (
|
||||
out []RedeemCode
|
||||
total int64
|
||||
)
|
||||
for page := 1; len(out) < needed; page++ {
|
||||
params := pagination.PaginationParams{Page: page, PageSize: 1000}
|
||||
codes, currentTotal, err := s.listAffiliateBalanceHistory(ctx, userID, params)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total = currentTotal
|
||||
out = append(out, codes...)
|
||||
if len(codes) < params.Limit() || int64(len(out)) >= total {
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(out) > needed {
|
||||
out = out[:needed]
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
|
||||
func (s *adminServiceImpl) listAffiliateBalanceHistory(ctx context.Context, userID int64, params pagination.PaginationParams) ([]RedeemCode, int64, error) {
|
||||
if s == nil || s.entClient == nil || userID <= 0 {
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
||||
rows, err := s.entClient.QueryContext(ctx, `
|
||||
SELECT id,
|
||||
amount::double precision,
|
||||
created_at
|
||||
FROM user_affiliate_ledger
|
||||
WHERE user_id = $1
|
||||
AND action = 'transfer'
|
||||
ORDER BY created_at DESC, id DESC
|
||||
OFFSET $2
|
||||
LIMIT $3`, userID, params.Offset(), params.Limit())
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
codes := make([]RedeemCode, 0, params.Limit())
|
||||
for rows.Next() {
|
||||
var id int64
|
||||
var amount float64
|
||||
var createdAt time.Time
|
||||
if err := rows.Scan(&id, &amount, &createdAt); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
usedBy := userID
|
||||
usedAt := createdAt
|
||||
codes = append(codes, RedeemCode{
|
||||
ID: -id,
|
||||
Code: fmt.Sprintf("AFF-%d", id),
|
||||
Type: RedeemTypeAffiliateBalance,
|
||||
Value: amount,
|
||||
Status: StatusUsed,
|
||||
UsedBy: &usedBy,
|
||||
UsedAt: &usedAt,
|
||||
CreatedAt: createdAt,
|
||||
})
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
total, err := countAffiliateBalanceHistory(ctx, s.entClient, userID)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return codes, total, nil
|
||||
}
|
||||
|
||||
func countAffiliateBalanceHistory(ctx context.Context, client *dbent.Client, userID int64) (int64, error) {
|
||||
rows, err := client.QueryContext(ctx, `
|
||||
SELECT COUNT(*)
|
||||
FROM user_affiliate_ledger
|
||||
WHERE user_id = $1
|
||||
AND action = 'transfer'`, userID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
var total sql.NullInt64
|
||||
if rows.Next() {
|
||||
if err := rows.Scan(&total); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !total.Valid {
|
||||
return 0, nil
|
||||
}
|
||||
return total.Int64, nil
|
||||
}
|
||||
|
||||
func mergeBalanceHistoryCodes(redeemCodes, affiliateCodes []RedeemCode, params pagination.PaginationParams) []RedeemCode {
|
||||
combined := append(append([]RedeemCode{}, redeemCodes...), affiliateCodes...)
|
||||
sort.SliceStable(combined, func(i, j int) bool {
|
||||
return redeemCodeHistoryTime(combined[i]).After(redeemCodeHistoryTime(combined[j]))
|
||||
})
|
||||
offset := params.Offset()
|
||||
if offset >= len(combined) {
|
||||
return []RedeemCode{}
|
||||
}
|
||||
end := offset + params.Limit()
|
||||
if end > len(combined) {
|
||||
end = len(combined)
|
||||
}
|
||||
return combined[offset:end]
|
||||
}
|
||||
|
||||
func redeemCodeHistoryTime(code RedeemCode) time.Time {
|
||||
if code.UsedAt != nil {
|
||||
return *code.UsedAt
|
||||
}
|
||||
return code.CreatedAt
|
||||
}
|
||||
|
||||
func (s *adminServiceImpl) BindUserAuthIdentity(ctx context.Context, userID int64, input AdminBindAuthIdentityInput) (*AdminBoundAuthIdentity, error) {
|
||||
|
||||
Reference in New Issue
Block a user