hyapp-server/server/admin/internal/modules/dashboard/aslan_coin_seller_bills.go
2026-07-06 12:43:40 +08:00

184 lines
6.5 KiB
Go

package dashboard
import (
"context"
"fmt"
"strings"
"time"
)
// CoinSellerRechargeBillQuery 是 finance legacy 列表需要的币商进货明细筛选。
// 它只服务只读展示;金额口径必须与 StatisticsOverview 的 coin_seller_recharge_usd_minor 保持一致。
type CoinSellerRechargeBillQuery struct {
Keyword string
RegionID int64
StatTZ string
StartMS int64
EndMS int64
Page int
PageSize int
}
type CoinSellerRechargeBill struct {
TransactionID string
Source string
UserID int64
CountryCode string
USDMinorAmount int64
CreatedAtMS int64
}
type CoinSellerRechargeBillSource interface {
ListCoinSellerRechargeBills(ctx context.Context, query CoinSellerRechargeBillQuery) ([]CoinSellerRechargeBill, int64, error)
}
func (s *AslanMongoDashboardSource) ListCoinSellerRechargeBills(ctx context.Context, query CoinSellerRechargeBillQuery) ([]CoinSellerRechargeBill, int64, error) {
if s == nil || s.legacyDB == nil {
return nil, 0, fmt.Errorf("aslan legacy mysql is not configured")
}
countryFilter, err := s.countryFilter(ctx, StatisticsQuery{RegionID: query.RegionID})
if err != nil {
return nil, 0, err
}
if countryFilter.Applied && len(countryFilter.Codes) == 0 {
return []CoinSellerRechargeBill{}, 0, nil
}
page := query.Page
if page < 1 {
page = 1
}
pageSize := query.PageSize
if pageSize < 1 {
pageSize = 10
}
unionSQL, args := s.coinSellerRechargeBillUnionSQL(query, countryFilter)
queryCtx, cancel := context.WithTimeout(ctx, s.requestTimeout)
defer cancel()
var total int64
if err := s.legacyDB.QueryRowContext(queryCtx, `SELECT COUNT(*) FROM (`+unionSQL+`) bills`, args...).Scan(&total); err != nil {
return nil, 0, fmt.Errorf("count aslan coin seller recharge bills: %w", err)
}
if total == 0 {
return []CoinSellerRechargeBill{}, 0, nil
}
rows, err := s.legacyDB.QueryContext(queryCtx, `
SELECT transaction_id, source, user_id, country_code, amount, created_at
FROM (`+unionSQL+`) bills
ORDER BY created_at DESC, transaction_id DESC
LIMIT ? OFFSET ?`, append(args, pageSize, (page-1)*pageSize)...)
if err != nil {
return nil, 0, fmt.Errorf("query aslan coin seller recharge bills: %w", err)
}
defer rows.Close()
items := make([]CoinSellerRechargeBill, 0, pageSize)
for rows.Next() {
var item CoinSellerRechargeBill
var amountText string
var createdAt time.Time
if err := rows.Scan(&item.TransactionID, &item.Source, &item.UserID, &item.CountryCode, &amountText, &createdAt); err != nil {
return nil, 0, fmt.Errorf("scan aslan coin seller recharge bill: %w", err)
}
amount, err := decimalTextToMinor(amountText)
if err != nil {
return nil, 0, fmt.Errorf("parse aslan coin seller recharge bill %s amount: %w", item.TransactionID, err)
}
item.CountryCode = aslanMongoCountryCode(item.CountryCode)
item.USDMinorAmount = amount
item.CreatedAtMS = createdAt.UnixMilli()
items = append(items, item)
}
if err := rows.Err(); err != nil {
return nil, 0, fmt.Errorf("iterate aslan coin seller recharge bills: %w", err)
}
return items, total, nil
}
func (s *AslanMongoDashboardSource) coinSellerRechargeBillUnionSQL(query CoinSellerRechargeBillQuery, countryFilter externalDashboardCountryFilter) (string, []any) {
goldWhere := []string{
"ugcr.sys_origin = ?",
"ugcr.gold_coin_source = 2",
"ugcr.recharge_amount > 0",
"ubi.country_code IS NOT NULL",
"ubi.country_code != ''",
}
goldArgs := []any{s.sysOrigin}
freightWhere := []string{
"fbrw.sys_origin = ?",
"fbrw.origin = 'PURCHASE'",
"fbrw.type = 0",
"(COALESCE(fbrw.amount, 0) <> 0 OR COALESCE(fbrw.usd_quantity, 0) <> 0)",
"ubi.country_code IS NOT NULL",
"ubi.country_code != ''",
}
freightArgs := []any{s.sysOrigin}
if query.StartMS > 0 {
start := time.UnixMilli(query.StartMS)
goldWhere = append(goldWhere, "ugcr.create_time >= ?")
goldArgs = append(goldArgs, start)
freightWhere = append(freightWhere, "fbrw.create_time >= ?")
freightArgs = append(freightArgs, start)
}
if query.EndMS > 0 {
end := time.UnixMilli(query.EndMS)
goldWhere = append(goldWhere, "ugcr.create_time < ?")
goldArgs = append(goldArgs, end)
freightWhere = append(freightWhere, "fbrw.create_time < ?")
freightArgs = append(freightArgs, end)
}
if keyword := strings.TrimSpace(query.Keyword); keyword != "" {
goldWhere = append(goldWhere, "(CAST(ugcr.id AS CHAR) = ? OR CONCAT('aslan-gold-coin-', ugcr.id) = ? OR CAST(ugcr.user_id AS CHAR) = ?)")
goldArgs = append(goldArgs, keyword, keyword, keyword)
freightWhere = append(freightWhere, "(CAST(fbrw.id AS CHAR) = ? OR CONCAT('aslan-freight-purchase-', fbrw.id) = ? OR CAST(fbrw.user_id AS CHAR) = ?)")
freightArgs = append(freightArgs, keyword, keyword, keyword)
}
if countryFilter.Applied {
placeholders := make([]string, 0, len(countryFilter.Codes))
for _, code := range countryFilter.Codes {
normalized := aslanMongoCountryCode(code)
if normalized == "" {
continue
}
placeholders = append(placeholders, "?")
goldArgs = append(goldArgs, normalized)
freightArgs = append(freightArgs, normalized)
}
if len(placeholders) == 0 {
placeholders = append(placeholders, "?")
goldArgs = append(goldArgs, "__NO_COUNTRY__")
freightArgs = append(freightArgs, "__NO_COUNTRY__")
}
condition := "UPPER(ubi.country_code) IN (" + strings.Join(placeholders, ",") + ")"
goldWhere = append(goldWhere, condition)
freightWhere = append(freightWhere, condition)
}
walletDatabase := strings.TrimSpace(s.legacyWalletDatabase)
if walletDatabase == "" {
walletDatabase = "atyou_wallet"
}
unionSQL := `
SELECT CONCAT('aslan-gold-coin-', ugcr.id) AS transaction_id,
'gold_coin_recharge' AS source,
ugcr.user_id,
UPPER(ubi.country_code) AS country_code,
CAST(ugcr.recharge_amount AS CHAR) AS amount,
ugcr.create_time AS created_at
FROM user_gold_coin_recharge ugcr
INNER JOIN user_base_info ubi ON ubi.id = ugcr.user_id
WHERE ` + strings.Join(goldWhere, " AND ") + `
UNION ALL
SELECT CONCAT('aslan-freight-purchase-', fbrw.id) AS transaction_id,
'freight_purchase' AS source,
fbrw.user_id,
UPPER(ubi.country_code) AS country_code,
CAST(CASE WHEN fbrw.amount IS NOT NULL AND fbrw.amount <> 0 THEN fbrw.amount ELSE COALESCE(fbrw.usd_quantity, 0) END AS CHAR) AS amount,
fbrw.create_time AS created_at
FROM ` + quoteMySQLIdentifier(walletDatabase) + `.user_freight_balance_running_water fbrw
INNER JOIN user_base_info ubi ON ubi.id = fbrw.user_id
WHERE ` + strings.Join(freightWhere, " AND ")
return unionSQL, append(goldArgs, freightArgs...)
}