package mysql import ( "context" "database/sql" "encoding/json" "strings" "hyapp/pkg/appcode" "hyapp/services/wallet-service/internal/domain/ledger" resourcedomain "hyapp/services/wallet-service/internal/domain/resource" ) func (r *Repository) receiptForGiftTransaction(ctx context.Context, tx *sql.Tx, transactionID string) (ledger.Receipt, error) { var metadataJSON string if err := tx.QueryRowContext(ctx, `SELECT COALESCE(CAST(metadata_json AS CHAR), '{}') FROM wallet_transactions WHERE app_code = ? AND transaction_id = ?`, appcode.FromContext(ctx), transactionID, ).Scan(&metadataJSON); err != nil { return ledger.Receipt{}, err } var metadata giftMetadata if err := json.Unmarshal([]byte(metadataJSON), &metadata); err != nil { return ledger.Receipt{}, err } return receiptFromGiftMetadata(transactionID, metadata), nil } func receiptFromGiftMetadata(transactionID string, metadata giftMetadata) ledger.Receipt { chargeAssetType := ledger.NormalizeGiftChargeAssetType(metadata.ChargeAssetType) if metadata.RobotGift { chargeAssetType = ledger.AssetRobotCoin } chargeAmount := metadata.ChargeAmount if chargeAmount == 0 { chargeAmount = metadata.CoinSpent } return ledger.Receipt{ BillingReceiptID: metadata.BillingReceipt, TransactionID: transactionID, CoinSpent: metadata.CoinSpent, ChargeAssetType: chargeAssetType, ChargeAmount: chargeAmount, GiftPointAdded: 0, HeatValue: metadata.HeatValue, GiftTypeCode: metadata.GiftTypeCode, CPRelationType: metadata.CPRelationType, GiftName: metadata.GiftName, GiftIconURL: metadata.GiftIconURL, GiftAnimationURL: metadata.GiftAnimationURL, GiftEffectTypes: normalizeGiftEffectTypes(metadata.GiftEffectTypes), PriceVersion: metadata.PriceVersion, BalanceAfter: metadata.BalanceAfter, HostPeriodDiamondAdded: metadata.HostPeriodDiamondAdded, HostPeriodCycleKey: metadata.HostPeriodCycleKey, EntitlementID: strings.TrimSpace(metadata.EntitlementID), ChargeSource: normalizeGiftChargeSource(metadata.ChargeSource, metadata.EntitlementID), GiftIncomeCoinAmount: metadata.GiftIncomeCoinAmount, GiftIncomeBalanceAfter: metadata.GiftIncomeBalanceAfter, } } func giftDisplayIconURL(resource resourcedomain.Resource) string { if value := strings.TrimSpace(resource.PreviewURL); value != "" { return value } return strings.TrimSpace(resource.AssetURL) } func (r *Repository) receiptForTaskRewardTransaction(ctx context.Context, tx *sql.Tx, transactionID string) (ledger.TaskRewardReceipt, error) { var metadataJSON string if err := tx.QueryRowContext(ctx, `SELECT COALESCE(CAST(metadata_json AS CHAR), '{}') FROM wallet_transactions WHERE app_code = ? AND transaction_id = ?`, appcode.FromContext(ctx), transactionID, ).Scan(&metadataJSON); err != nil { return ledger.TaskRewardReceipt{}, err } var metadata taskRewardMetadata if err := json.Unmarshal([]byte(metadataJSON), &metadata); err != nil { return ledger.TaskRewardReceipt{}, err } return receiptFromTaskRewardMetadata(transactionID, metadata), nil } func receiptFromTaskRewardMetadata(transactionID string, metadata taskRewardMetadata) ledger.TaskRewardReceipt { return ledger.TaskRewardReceipt{ TransactionID: transactionID, Balance: ledger.AssetBalance{ AppCode: metadata.AppCode, UserID: metadata.TargetUserID, AssetType: metadata.AssetType, AvailableAmount: metadata.BalanceAfter, }, Amount: metadata.Amount, GrantedAtMS: metadata.GrantedAtMS, } } func (r *Repository) receiptForLuckyGiftRewardTransaction(ctx context.Context, tx *sql.Tx, transactionID string) (ledger.LuckyGiftRewardReceipt, error) { var metadataJSON string if err := tx.QueryRowContext(ctx, `SELECT COALESCE(CAST(metadata_json AS CHAR), '{}') FROM wallet_transactions WHERE app_code = ? AND transaction_id = ?`, appcode.FromContext(ctx), transactionID, ).Scan(&metadataJSON); err != nil { return ledger.LuckyGiftRewardReceipt{}, err } var metadata luckyGiftRewardMetadata if err := json.Unmarshal([]byte(metadataJSON), &metadata); err != nil { return ledger.LuckyGiftRewardReceipt{}, err } return receiptFromLuckyGiftRewardMetadata(transactionID, metadata), nil } func receiptFromLuckyGiftRewardMetadata(transactionID string, metadata luckyGiftRewardMetadata) ledger.LuckyGiftRewardReceipt { return ledger.LuckyGiftRewardReceipt{ TransactionID: transactionID, Balance: ledger.AssetBalance{ AppCode: metadata.AppCode, UserID: metadata.TargetUserID, AssetType: metadata.AssetType, AvailableAmount: metadata.BalanceAfter, }, Amount: metadata.Amount, GrantedAtMS: metadata.GrantedAtMS, } } func (r *Repository) receiptForWheelRewardTransaction(ctx context.Context, tx *sql.Tx, transactionID string) (ledger.WheelRewardReceipt, error) { var metadataJSON string if err := tx.QueryRowContext(ctx, `SELECT COALESCE(CAST(metadata_json AS CHAR), '{}') FROM wallet_transactions WHERE app_code = ? AND transaction_id = ?`, appcode.FromContext(ctx), transactionID, ).Scan(&metadataJSON); err != nil { return ledger.WheelRewardReceipt{}, err } var metadata wheelRewardMetadata if err := json.Unmarshal([]byte(metadataJSON), &metadata); err != nil { return ledger.WheelRewardReceipt{}, err } return receiptFromWheelRewardMetadata(transactionID, metadata), nil } func receiptFromWheelRewardMetadata(transactionID string, metadata wheelRewardMetadata) ledger.WheelRewardReceipt { return ledger.WheelRewardReceipt{ TransactionID: transactionID, Balance: ledger.AssetBalance{ AppCode: metadata.AppCode, UserID: metadata.TargetUserID, AssetType: metadata.AssetType, AvailableAmount: metadata.BalanceAfter, }, Amount: metadata.Amount, GrantedAtMS: metadata.GrantedAtMS, } } func (r *Repository) receiptForRoomTurnoverRewardTransaction(ctx context.Context, tx *sql.Tx, transactionID string) (ledger.RoomTurnoverRewardReceipt, error) { var metadataJSON string if err := tx.QueryRowContext(ctx, `SELECT COALESCE(CAST(metadata_json AS CHAR), '{}') FROM wallet_transactions WHERE app_code = ? AND transaction_id = ?`, appcode.FromContext(ctx), transactionID, ).Scan(&metadataJSON); err != nil { return ledger.RoomTurnoverRewardReceipt{}, err } var metadata roomTurnoverRewardMetadata if err := json.Unmarshal([]byte(metadataJSON), &metadata); err != nil { return ledger.RoomTurnoverRewardReceipt{}, err } return receiptFromRoomTurnoverRewardMetadata(transactionID, metadata), nil } func receiptFromRoomTurnoverRewardMetadata(transactionID string, metadata roomTurnoverRewardMetadata) ledger.RoomTurnoverRewardReceipt { return ledger.RoomTurnoverRewardReceipt{ TransactionID: transactionID, Balance: ledger.AssetBalance{ AppCode: metadata.AppCode, UserID: metadata.TargetUserID, AssetType: metadata.AssetType, AvailableAmount: metadata.BalanceAfter, }, Amount: metadata.Amount, GrantedAtMS: metadata.GrantedAtMS, } } func (r *Repository) receiptForInviteActivityRewardTransaction(ctx context.Context, tx *sql.Tx, transactionID string) (ledger.InviteActivityRewardReceipt, error) { var metadataJSON string if err := tx.QueryRowContext(ctx, `SELECT COALESCE(CAST(metadata_json AS CHAR), '{}') FROM wallet_transactions WHERE app_code = ? AND transaction_id = ?`, appcode.FromContext(ctx), transactionID, ).Scan(&metadataJSON); err != nil { return ledger.InviteActivityRewardReceipt{}, err } var metadata inviteActivityRewardMetadata if err := json.Unmarshal([]byte(metadataJSON), &metadata); err != nil { return ledger.InviteActivityRewardReceipt{}, err } return receiptFromInviteActivityRewardMetadata(transactionID, metadata), nil } func receiptFromInviteActivityRewardMetadata(transactionID string, metadata inviteActivityRewardMetadata) ledger.InviteActivityRewardReceipt { return ledger.InviteActivityRewardReceipt{ TransactionID: transactionID, Balance: ledger.AssetBalance{ AppCode: metadata.AppCode, UserID: metadata.TargetUserID, AssetType: metadata.AssetType, AvailableAmount: metadata.BalanceAfter, }, Amount: metadata.Amount, GrantedAtMS: metadata.GrantedAtMS, } } func (r *Repository) receiptForAgencyOpeningRewardTransaction(ctx context.Context, tx *sql.Tx, transactionID string) (ledger.AgencyOpeningRewardReceipt, error) { var metadataJSON string if err := tx.QueryRowContext(ctx, `SELECT COALESCE(CAST(metadata_json AS CHAR), '{}') FROM wallet_transactions WHERE app_code = ? AND transaction_id = ?`, appcode.FromContext(ctx), transactionID, ).Scan(&metadataJSON); err != nil { return ledger.AgencyOpeningRewardReceipt{}, err } var metadata agencyOpeningRewardMetadata if err := json.Unmarshal([]byte(metadataJSON), &metadata); err != nil { return ledger.AgencyOpeningRewardReceipt{}, err } return receiptFromAgencyOpeningRewardMetadata(transactionID, metadata), nil } func receiptFromAgencyOpeningRewardMetadata(transactionID string, metadata agencyOpeningRewardMetadata) ledger.AgencyOpeningRewardReceipt { return ledger.AgencyOpeningRewardReceipt{ TransactionID: transactionID, Balance: ledger.AssetBalance{ AppCode: metadata.AppCode, UserID: metadata.TargetUserID, AssetType: metadata.AssetType, AvailableAmount: metadata.BalanceAfter, }, Amount: metadata.Amount, GrantedAtMS: metadata.GrantedAtMS, } } func (r *Repository) receiptForGameTransaction(ctx context.Context, tx *sql.Tx, transactionID string) (ledger.GameCoinChangeReceipt, error) { var metadataJSON string if err := tx.QueryRowContext(ctx, `SELECT COALESCE(CAST(metadata_json AS CHAR), '{}') FROM wallet_transactions WHERE app_code = ? AND transaction_id = ?`, appcode.FromContext(ctx), transactionID, ).Scan(&metadataJSON); err != nil { return ledger.GameCoinChangeReceipt{}, err } var metadata gameCoinMetadata if err := json.Unmarshal([]byte(metadataJSON), &metadata); err != nil { return ledger.GameCoinChangeReceipt{}, err } return receiptFromGameMetadata(transactionID, metadata, false), nil } func receiptFromGameMetadata(transactionID string, metadata gameCoinMetadata, idempotentReplay bool) ledger.GameCoinChangeReceipt { return ledger.GameCoinChangeReceipt{ TransactionID: transactionID, BalanceAfter: metadata.BalanceAfter, IdempotentReplay: idempotentReplay, } } func (r *Repository) receiptForCoinSellerStockTransaction(ctx context.Context, tx *sql.Tx, transactionID string) (ledger.CoinSellerStockCreditReceipt, error) { row := tx.QueryRowContext(ctx, `SELECT seller_user_id, stock_type, coin_amount, paid_currency_code, paid_amount_micro, counts_as_seller_recharge, balance_after, metadata_json, created_at_ms FROM coin_seller_stock_records WHERE app_code = ? AND transaction_id = ?`, appcode.FromContext(ctx), transactionID, ) var receipt ledger.CoinSellerStockCreditReceipt var metadataJSON string if err := row.Scan( &receipt.SellerUserID, &receipt.StockType, &receipt.CoinAmount, &receipt.PaidCurrencyCode, &receipt.PaidAmountMicro, &receipt.CountsAsSellerRecharge, &receipt.BalanceAfter, &metadataJSON, &receipt.CreatedAtMS, ); err != nil { return ledger.CoinSellerStockCreditReceipt{}, err } if metadataJSON != "" { var metadata coinSellerStockMetadata if err := json.Unmarshal([]byte(metadataJSON), &metadata); err == nil { receipt.SellerCountryID = metadata.SellerCountryID receipt.SellerRegionID = metadata.SellerRegionID } } receipt.TransactionID = transactionID return receipt, nil } func receiptFromCoinSellerStockMetadata(transactionID string, metadata coinSellerStockMetadata) ledger.CoinSellerStockCreditReceipt { return ledger.CoinSellerStockCreditReceipt{ TransactionID: transactionID, SellerUserID: metadata.SellerUserID, SellerCountryID: metadata.SellerCountryID, SellerRegionID: metadata.SellerRegionID, StockType: metadata.StockType, CoinAmount: metadata.CoinAmount, PaidCurrencyCode: metadata.PaidCurrencyCode, PaidAmountMicro: metadata.PaidAmountMicro, CountsAsSellerRecharge: metadata.CountsAsSellerRecharge, BalanceAfter: metadata.BalanceAfter, CreatedAtMS: metadata.CreatedAtMS, } } func (r *Repository) receiptForCoinSellerTransferTransaction(ctx context.Context, tx *sql.Tx, transactionID string) (ledger.CoinSellerTransferReceipt, error) { var metadataJSON string if err := tx.QueryRowContext(ctx, `SELECT COALESCE(CAST(metadata_json AS CHAR), '{}') FROM wallet_transactions WHERE app_code = ? AND transaction_id = ?`, appcode.FromContext(ctx), transactionID, ).Scan(&metadataJSON); err != nil { return ledger.CoinSellerTransferReceipt{}, err } var metadata coinSellerTransferMetadata if err := json.Unmarshal([]byte(metadataJSON), &metadata); err != nil { return ledger.CoinSellerTransferReceipt{}, err } return receiptFromCoinSellerTransferMetadata(transactionID, metadata), nil } func receiptFromCoinSellerTransferMetadata(transactionID string, metadata coinSellerTransferMetadata) ledger.CoinSellerTransferReceipt { return ledger.CoinSellerTransferReceipt{ TransactionID: transactionID, SellerBalanceAfter: metadata.SellerBalanceAfter, TargetBalanceAfter: metadata.TargetBalanceAfter, Amount: metadata.Amount, RechargeSequence: metadata.RechargeSequence, RechargeUSDMinor: metadata.RechargeUSDMinor, RechargeCurrencyCode: metadata.RechargeCurrencyCode, RechargePolicyID: metadata.RechargePolicyID, RechargePolicyVersion: metadata.RechargePolicyVersion, RechargePolicyCoinAmount: metadata.RechargePolicyCoinAmount, RechargePolicyUSDMinorUnit: metadata.RechargePolicyUSDMinorAmount, } } func (r *Repository) receiptForSalaryExchangeTransaction(ctx context.Context, tx *sql.Tx, transactionID string) (ledger.SalaryExchangeReceipt, error) { var metadataJSON string if err := tx.QueryRowContext(ctx, `SELECT COALESCE(CAST(metadata_json AS CHAR), '{}') FROM wallet_transactions WHERE app_code = ? AND transaction_id = ?`, appcode.FromContext(ctx), transactionID, ).Scan(&metadataJSON); err != nil { return ledger.SalaryExchangeReceipt{}, err } var metadata salaryExchangeMetadata if err := json.Unmarshal([]byte(metadataJSON), &metadata); err != nil { return ledger.SalaryExchangeReceipt{}, err } return receiptFromSalaryExchangeMetadata(transactionID, metadata), nil } func receiptFromSalaryExchangeMetadata(transactionID string, metadata salaryExchangeMetadata) ledger.SalaryExchangeReceipt { return ledger.SalaryExchangeReceipt{ TransactionID: transactionID, UserID: metadata.UserID, SalaryAssetType: metadata.SalaryAssetType, SalaryBalanceAfter: metadata.SalaryBalanceAfter, CoinBalanceAfter: metadata.CoinBalanceAfter, SalaryUSDMinor: metadata.SalaryUSDMinor, CoinAmount: metadata.CoinAmount, CoinPerUSD: metadata.CoinPerUSD, CreatedAtMS: metadata.CreatedAtMS, } } func (r *Repository) receiptForSalaryTransferToCoinSellerTransaction(ctx context.Context, tx *sql.Tx, transactionID string) (ledger.SalaryTransferToCoinSellerReceipt, error) { var metadataJSON string if err := tx.QueryRowContext(ctx, `SELECT COALESCE(CAST(metadata_json AS CHAR), '{}') FROM wallet_transactions WHERE app_code = ? AND transaction_id = ?`, appcode.FromContext(ctx), transactionID, ).Scan(&metadataJSON); err != nil { return ledger.SalaryTransferToCoinSellerReceipt{}, err } var metadata salaryTransferToCoinSellerMetadata if err := json.Unmarshal([]byte(metadataJSON), &metadata); err != nil { return ledger.SalaryTransferToCoinSellerReceipt{}, err } return receiptFromSalaryTransferToCoinSellerMetadata(transactionID, metadata), nil } func receiptFromSalaryTransferToCoinSellerMetadata(transactionID string, metadata salaryTransferToCoinSellerMetadata) ledger.SalaryTransferToCoinSellerReceipt { return ledger.SalaryTransferToCoinSellerReceipt{ TransactionID: transactionID, SourceUserID: metadata.SourceUserID, SellerUserID: metadata.SellerUserID, SalaryAssetType: metadata.SalaryAssetType, SourceSalaryBalanceAfter: metadata.SourceSalaryBalanceAfter, SellerBalanceAfter: metadata.SellerBalanceAfter, SalaryUSDMinor: metadata.SalaryUSDMinor, CoinAmount: metadata.CoinAmount, CoinPerUSD: metadata.CoinPerUSD, RateMinUSDMinor: metadata.RateMinUSDMinor, RateMaxUSDMinor: metadata.RateMaxUSDMinor, CreatedAtMS: metadata.CreatedAtMS, } }