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 }