2026-06-23 11:53:00 +08:00

84 lines
2.5 KiB
Go

package mysql
import (
"context"
"sort"
"strings"
"hyapp/pkg/appcode"
"hyapp/pkg/xerr"
"hyapp/services/wallet-service/internal/domain/ledger"
)
// GetBalances 读取用户多资产余额;指定资产不存在时返回 0 投影,便于客户端稳定渲染。
func (r *Repository) GetBalances(ctx context.Context, userID int64, assetTypes []string) ([]ledger.AssetBalance, error) {
if r == nil || r.db == nil {
return nil, xerr.New(xerr.Unavailable, "mysql repository is not configured")
}
assetTypes = normalizeAssetTypes(assetTypes)
appCode := appcode.FromContext(ctx)
query := `SELECT app_code, user_id, asset_type, available_amount, frozen_amount, version, updated_at_ms
FROM wallet_accounts WHERE app_code = ? AND user_id = ?`
args := []any{appCode, userID}
if len(assetTypes) > 0 {
placeholders := strings.TrimRight(strings.Repeat("?,", len(assetTypes)), ",")
query += " AND asset_type IN (" + placeholders + ")"
for _, assetType := range assetTypes {
args = append(args, assetType)
}
}
query += " ORDER BY asset_type"
rows, err := r.db.QueryContext(ctx, query, args...)
if err != nil {
return nil, err
}
defer rows.Close()
byAsset := make(map[string]ledger.AssetBalance)
for rows.Next() {
var balance ledger.AssetBalance
if err := rows.Scan(&balance.AppCode, &balance.UserID, &balance.AssetType, &balance.AvailableAmount, &balance.FrozenAmount, &balance.Version, &balance.UpdatedAtMs); err != nil {
return nil, err
}
byAsset[balance.AssetType] = balance
}
if err := rows.Err(); err != nil {
return nil, err
}
if len(assetTypes) == 0 {
balances := make([]ledger.AssetBalance, 0, len(byAsset))
for _, balance := range byAsset {
balances = append(balances, balance)
}
sort.Slice(balances, func(i, j int) bool { return balances[i].AssetType < balances[j].AssetType })
return balances, nil
}
balances := make([]ledger.AssetBalance, 0, len(assetTypes))
for _, assetType := range assetTypes {
if balance, ok := byAsset[assetType]; ok {
balances = append(balances, balance)
continue
}
balances = append(balances, ledger.AssetBalance{AppCode: appCode, UserID: userID, AssetType: assetType})
}
return balances, nil
}
func normalizeAssetTypes(assetTypes []string) []string {
seen := make(map[string]bool, len(assetTypes))
result := make([]string, 0, len(assetTypes))
for _, assetType := range assetTypes {
assetType = strings.ToUpper(strings.TrimSpace(assetType))
if assetType == "" || seen[assetType] {
continue
}
seen[assetType] = true
result = append(result, assetType)
}
return result
}