Files
kra-new/internal/biz/business/ledger.go
T

244 lines
7.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package business
import (
"context"
"strconv"
"strings"
"time"
"github.com/go-kratos/kratos/v3/errors"
)
var (
ErrLedgerInput = errors.BadRequest("SW_LEDGER_INPUT", "明细查询参数不正确")
ErrLedgerUnavailable = errors.BadRequest("SW_LEDGER_UNAVAILABLE", "明细数据尚未接入,无法导出")
ErrLedgerExportLimit = errors.BadRequest("SW_LEDGER_EXPORT_LIMIT", "导出数据超过10000条,请缩小时间范围或筛选条件")
ErrGameOptionsUnavailable = errors.BadRequest("SW_GAME_OPTIONS_UNAVAILABLE", "游戏数据尚未接入")
)
type LedgerFilter struct {
Sources, GameNames []string
UserID *int64
PromoteAccount, BusierAccount, PlayerName, OrderNumber string
PayStatus, PayWay *int
StartDate, EndDate *time.Time
BeginUnix, EndUnix int64
SortField string
Ascending bool
}
type LedgerScope struct {
Viewer UserScope
IncludeSub, Unrestricted bool
}
type LedgerListOptions struct {
Offset, Limit int
RejectAbove int64
}
type LedgerNames struct {
PromoteID *int64
PromoteAccount, BindingAccount, BindingUserName string
BusinessPath []string
}
func (n *LedgerNames) DisplayPromote() string {
if strings.TrimSpace(n.PromoteAccount) != "" {
return n.PromoteAccount
}
if n.PromoteID == nil {
return "未知渠道"
}
if *n.PromoteID == 0 {
return "官方渠道"
}
return "未知渠道(ID:" + strconv.FormatInt(*n.PromoteID, 10) + ")"
}
func (n *LedgerNames) DisplayBusiness() string {
if len(n.BusinessPath) > 0 {
return strings.Join(n.BusinessPath, "→")
}
return (&Channel{BindingAccount: n.BindingAccount, BindingUserName: n.BindingUserName}).BusinessDisplayName()
}
type Registration struct {
ChannelKey
LedgerNames
RegisterTime *int64
FirstGameID *int64
FirstGameName, RegisterIP, DeviceName string
TotalPayAmount *string
}
type Spend struct {
ChannelKey
LedgerNames
UserID, GameID *int64
OrderNumber, GameName, ServerID, ServerName, PlayerName, IP string
PayAmount *string
PayTime *int64
PayWay, PayStatus *int
}
type RegistrationPage struct {
Rows []*Registration
Total int64
Available bool
}
type SpendPage struct {
Rows []*Spend
Total int64
TotalPayAmount *string
Available bool
}
type GameOption struct {
SourceDB string
ID uint64
Name string
}
type LedgerRepo interface {
Registrations(context.Context, LedgerScope, LedgerFilter, LedgerListOptions) (*RegistrationPage, error)
Spends(context.Context, LedgerScope, LedgerFilter, LedgerListOptions) (*SpendPage, error)
Games(context.Context, UserScope, []string, string, int) ([]*GameOption, error)
}
type LedgerUsecase struct {
repo LedgerRepo
accounts *AccountUsecase
preferences *UserPreferenceUsecase
now func() time.Time
}
func NewLedgerUsecase(repo LedgerRepo, accounts *AccountUsecase, preferences *UserPreferenceUsecase) *LedgerUsecase {
return &LedgerUsecase{repo: repo, accounts: accounts, preferences: preferences, now: time.Now}
}
func (uc *LedgerUsecase) prepare(ctx context.Context, filter LedgerFilter) (LedgerScope, LedgerFilter, error) {
actor, a, err := uc.accounts.actor(ctx)
if err != nil {
return LedgerScope{}, filter, err
}
pref, err := uc.preferences.Current(ctx)
if err != nil {
return LedgerScope{}, filter, err
}
viewer := portraitUserScope(a)
viewer.All = actor.AuthorityID == 888
scope := LedgerScope{Viewer: viewer, IncludeSub: pref.IncludeSubPromote, Unrestricted: viewer.All && strings.TrimSpace(filter.BusierAccount) == ""}
begin, end := reportDateDefaults(filter.StartDate, filter.EndDate, 30, uc.now())
if end.Before(*begin) {
return scope, filter, ErrLedgerInput
}
filter.BeginUnix = begin.Unix()
filter.EndUnix = end.AddDate(0, 0, 1).Unix() - 1
filter.StartDate, filter.EndDate = begin, end
return scope, filter, nil
}
func registrationSort(field string) string {
switch field {
case "totalPayAmount", "total_pay_amount", "totalPay", "payAmount":
return "totalPayAmount"
}
return "registerTime"
}
func spendSort(field string) string {
switch field {
case "id":
return "id"
case "payAmount", "pay_amount", "amount":
return "payAmount"
}
return "payTime"
}
func (uc *LedgerUsecase) Registrations(ctx context.Context, filter LedgerFilter, options LedgerListOptions) (*RegistrationPage, error) {
scope, filter, err := uc.prepare(ctx, filter)
if err != nil {
return nil, err
}
filter.SortField = registrationSort(filter.SortField)
page, err := uc.repo.Registrations(ctx, scope, filter, options)
if err != nil {
return nil, err
}
for _, row := range page.Rows {
row.PromoteAccount = row.DisplayPromote()
if row.TotalPayAmount != nil {
n, err := portraitNumber(*row.TotalPayAmount)
if err != nil {
return nil, err
}
row.TotalPayAmount = stringPointer(portraitDecimal(n, 2))
}
}
return page, nil
}
func (uc *LedgerUsecase) Spends(ctx context.Context, filter LedgerFilter, options LedgerListOptions) (*SpendPage, error) {
scope, filter, err := uc.prepare(ctx, filter)
if err != nil {
return nil, err
}
filter.SortField = spendSort(filter.SortField)
page, err := uc.repo.Spends(ctx, scope, filter, options)
if err != nil {
return nil, err
}
for _, row := range page.Rows {
row.PromoteAccount = row.DisplayPromote()
if row.PayAmount != nil {
n, err := portraitNumber(*row.PayAmount)
if err != nil {
return nil, err
}
row.PayAmount = stringPointer(portraitDecimal(n, 2))
}
}
if page.TotalPayAmount != nil {
n, err := portraitNumber(*page.TotalPayAmount)
if err != nil {
return nil, err
}
page.TotalPayAmount = stringPointer(portraitDecimal(n, 2))
}
return page, nil
}
func (uc *LedgerUsecase) ExportRegistrations(ctx context.Context, filter LedgerFilter) ([]*Registration, error) {
page, err := uc.Registrations(ctx, filter, LedgerListOptions{Limit: 10000, RejectAbove: 10000})
if err != nil {
return nil, err
}
if !page.Available {
return nil, ErrLedgerUnavailable
}
if page.Total > 10000 {
return nil, ErrLedgerExportLimit
}
for _, row := range page.Rows {
if row.TotalPayAmount == nil {
return nil, ErrLedgerUnavailable
}
}
return page.Rows, nil
}
func (uc *LedgerUsecase) ExportSpends(ctx context.Context, filter LedgerFilter) ([]*Spend, error) {
page, err := uc.Spends(ctx, filter, LedgerListOptions{Limit: 10000, RejectAbove: 10000})
if err != nil {
return nil, err
}
if !page.Available {
return nil, ErrLedgerUnavailable
}
if page.Total > 10000 {
return nil, ErrLedgerExportLimit
}
return page.Rows, nil
}
func (uc *LedgerUsecase) Games(ctx context.Context, sources []string, keyword string, limit int) ([]*GameOption, error) {
actor, account, err := uc.accounts.actor(ctx)
if err != nil {
return nil, err
}
viewer := portraitUserScope(account)
viewer.All = actor.AuthorityID == 888
if limit <= 0 {
limit = 20
}
if limit > 50 {
limit = 10000
}
return uc.repo.Games(ctx, viewer, sources, strings.TrimSpace(keyword), limit)
}