package order import ( "testing" "time" "hfb_sys/backend/internal/model" "hfb_sys/backend/internal/timeutil" "gorm.io/datatypes" ) func TestCalculateCheckoutSettlementRefundsUnusedRent(t *testing.T) { order := model.RentalOrder{ RentAmountCent: 38300, OwnerRentAmountCent: 35300, DepositAmountCent: 15000, AccountSnapshot: datatypes.JSON([]byte(`{ "haf_coin_amount": 100000000, "asset_summary": { "price_breakdown": { "buyer_coin_base_price": 263, "seller_coin_base_price": 233, "consumable_price": 120 } } }`)), } settlement := calculateCheckoutSettlement(order, 700, 90, 0) // 角精度:243.7 = roundMoney(236.7 + 7) if settlement.ActualRentAmountCent != 24370 { t.Fatalf("ActualRentAmountCent = %d, want 24370", settlement.ActualRentAmountCent) } // 角精度:216.7 = roundMoney(209.7 + 7) 卖家币价+消耗品 if settlement.OwnerRentIncomeCent != 21670 { t.Fatalf("OwnerRentIncomeCent = %d, want 21670", settlement.OwnerRentIncomeCent) } // 角精度:27.0 = roundMoney(27) 平台费 if settlement.PlatformFeeCent != 2700 { t.Fatalf("PlatformFeeCent = %d, want 2700", settlement.PlatformFeeCent) } // 角精度:139.3 = roundMoney(383 - 243.7) if settlement.RentRefundCent != 13930 { t.Fatalf("RentRefundCent = %d, want 13930", settlement.RentRefundCent) } if settlement.DepositRefundCent != 15000 { t.Fatalf("DepositRefundCent = %d, want 15000", settlement.DepositRefundCent) } // 角精度:289.3 = roundMoney(139.3 + 150) if settlement.RenterRefundCent != 28930 { t.Fatalf("RenterRefundCent = %d, want 28930", settlement.RenterRefundCent) } } func TestCalculateCheckoutSettlementUsesBuyerAndSellerRatiosSeparately(t *testing.T) { order := model.RentalOrder{ RentAmountCent: 38300, OwnerRentAmountCent: 35300, DepositAmountCent: 15000, AccountSnapshot: datatypes.JSON([]byte(`{ "haf_coin_amount": 100000000, "asset_summary": { "price_breakdown": { "buyer_coin_base_price": 263, "seller_coin_base_price": 233, "consumable_price": 120 } } }`)), } settlement := calculateCheckoutSettlement(order, 0, 50, 0) // 角精度:131.5 = roundMoney(131.5) if settlement.ActualRentAmountCent != 13150 { t.Fatalf("租客侧实际租金 = %d, want 13150", settlement.ActualRentAmountCent) } // 角精度:116.5 = roundMoney(116.5) if settlement.OwnerRentIncomeCent != 11650 { t.Fatalf("卖家侧租金收入 = %d, want 11650", settlement.OwnerRentIncomeCent) } // 角精度:15.0 = roundMoney(15) if settlement.PlatformFeeCent != 1500 { t.Fatalf("平台差价 = %d, want 1500", settlement.PlatformFeeCent) } } func TestCalculateCheckoutSettlementAddsDepositCompensation(t *testing.T) { order := model.RentalOrder{ RentAmountCent: 38300, OwnerRentAmountCent: 35300, DepositAmountCent: 15000, AccountSnapshot: datatypes.JSON([]byte(`{ "haf_coin_amount": 100000000, "asset_summary": { "price_breakdown": { "buyer_coin_base_price": 263, "seller_coin_base_price": 233, "consumable_price": 120 } } }`)), } settlement := calculateCheckoutSettlement(order, 12000, 100, 3000) if settlement.ActualRentAmountCent != 38300 { t.Fatalf("ActualRentAmountCent = %d, want 38300", settlement.ActualRentAmountCent) } if settlement.OwnerRentIncomeCent != 35300 { t.Fatalf("OwnerRentIncomeCent = %d, want 35300", settlement.OwnerRentIncomeCent) } if settlement.DepositCompensationCent != 3000 { t.Fatalf("DepositCompensationCent = %d, want 3000", settlement.DepositCompensationCent) } if settlement.OwnerIncomeCent != 38300 { t.Fatalf("OwnerIncomeCent = %d, want 38300", settlement.OwnerIncomeCent) } if settlement.RenterRefundCent != 12000 { t.Fatalf("RenterRefundCent = %d, want 12000", settlement.RenterRefundCent) } } func TestCalculateDepositWaiverUsesSharedRemainingQuota(t *testing.T) { paid, waived := calculateDepositWaiver(500, 300, 0) if paid != 200 || waived != 300 { t.Fatalf("paid = %d, waived = %d, want 200, 300", paid, waived) } paid, waived = calculateDepositWaiver(500, 300, 200) if paid != 400 || waived != 100 { t.Fatalf("paid = %d, waived = %d, want 400, 100", paid, waived) } paid, waived = calculateDepositWaiver(500, 300, 300) if paid != 500 || waived != 0 { t.Fatalf("paid = %d, waived = %d, want 500, 0", paid, waived) } } func TestArchiveAssetsMovesListingOffline(t *testing.T) { now := time.Now() listing := model.RentalListing{ Status: listingStatusRented, InTransaction: true, PublishedAt: &now, } account := model.GameAccount{Status: accountStatusRented} archiveAssets(&listing, &account) if listing.Status != listingStatusOffline { t.Fatalf("listing.Status = %q, want %q", listing.Status, listingStatusOffline) } if listing.InTransaction { t.Fatal("listing.InTransaction = true, want false") } if listing.PublishedAt != nil { t.Fatal("listing.PublishedAt is not nil") } if account.Status != accountStatusOffline { t.Fatalf("account.Status = %q, want %q", account.Status, accountStatusOffline) } } func TestNewOrderNoUsesShanghaiTimeWhenLocalIsUTC(t *testing.T) { oldLocal := time.Local time.Local = time.UTC defer func() { time.Local = oldLocal }() loc := timeutil.ShanghaiLocation() before := time.Now().In(loc) orderNo, err := newOrderNo() after := time.Now().In(loc) if err != nil { t.Fatalf("newOrderNo() error = %v", err) } if len(orderNo) != len("RO20260603123456789") { t.Fatalf("orderNo length = %d, want %d: %s", len(orderNo), len("RO20260603123456789"), orderNo) } parsed, err := time.ParseInLocation("20060102150405", orderNo[2:16], loc) if err != nil { t.Fatalf("parse order no time: %v", err) } if parsed.Before(before.Add(-2*time.Second)) || parsed.After(after.Add(2*time.Second)) { t.Fatalf("order no time = %s, want between %s and %s", parsed, before, after) } }