diff --git a/backend/internal/modules/order/pricing.go b/backend/internal/modules/order/pricing.go index 1e19461..844715c 100644 --- a/backend/internal/modules/order/pricing.go +++ b/backend/internal/modules/order/pricing.go @@ -63,10 +63,12 @@ func estimateOrderDurationHours(snapshot datatypes.JSON) int { if days <= 0 { return internalOrderHours } - hours := int(math.Round(days * 24)) - if hours <= 0 { - return internalOrderHours + // 不足整天按整天向上取整,例如 1.3 / 1.7 天均按 2 天计 + daysCeil := int(math.Ceil(days)) + if daysCeil < 1 { + daysCeil = 1 } + hours := daysCeil * 24 return hours } diff --git a/backend/internal/modules/order/repository_integration_test.go b/backend/internal/modules/order/repository_integration_test.go index 959eaaa..4556b23 100644 --- a/backend/internal/modules/order/repository_integration_test.go +++ b/backend/internal/modules/order/repository_integration_test.go @@ -373,9 +373,10 @@ func TestEstimateOrderDurationHoursFromSnapshot(t *testing.T) { } }`)) + // 165M / 10M = 16.5 天 → 向上取整 17 天 = 408 小时 hours := estimateOrderDurationHours(snapshot) - if hours != 396 { - t.Fatalf("hours = %d, want 396", hours) + if hours != 408 { + t.Fatalf("hours = %d, want 408", hours) } } @@ -387,9 +388,39 @@ func TestEstimateOrderDurationHoursFromServerSnapshot(t *testing.T) { } }`)) + // 105M / 10M = 10.5 天 → 向上取整 11 天 = 264 小时 hours := estimateOrderDurationHours(snapshot) - if hours != 252 { - t.Fatalf("hours = %d, want 252", hours) + if hours != 264 { + t.Fatalf("hours = %d, want 264", hours) + } +} + +func TestEstimateOrderDurationHoursCeilsFractionalDays(t *testing.T) { + // 13M / 10M = 1.3 天 → 2 天 = 48 小时 + snapshot13 := datatypes.JSON([]byte(`{ + "haf_coin_amount": 13000000, + "asset_summary": {"daily_loss_m": 10} + }`)) + if hours := estimateOrderDurationHours(snapshot13); hours != 48 { + t.Fatalf("1.3 days hours = %d, want 48", hours) + } + + // 17M / 10M = 1.7 天 → 2 天 = 48 小时 + snapshot17 := datatypes.JSON([]byte(`{ + "haf_coin_amount": 17000000, + "asset_summary": {"daily_loss_m": 10} + }`)) + if hours := estimateOrderDurationHours(snapshot17); hours != 48 { + t.Fatalf("1.7 days hours = %d, want 48", hours) + } + + // 整天天数不放大:20M / 10M = 2 天 = 48 小时 + snapshot20 := datatypes.JSON([]byte(`{ + "haf_coin_amount": 20000000, + "asset_summary": {"daily_loss_m": 10} + }`)) + if hours := estimateOrderDurationHours(snapshot20); hours != 48 { + t.Fatalf("2.0 days hours = %d, want 48", hours) } } @@ -472,8 +503,8 @@ func TestConfirmReceiveRecalculatesEstimatedDuration(t *testing.T) { if err := db.First(&saved, order.ID).Error; err != nil { t.Fatalf("load order failed: %v", err) } - if saved.EstimatedDurationHours != 396 { - t.Fatalf("EstimatedDurationHours = %d, want 396", saved.EstimatedDurationHours) + if saved.EstimatedDurationHours != 408 { + t.Fatalf("EstimatedDurationHours = %d, want 408", saved.EstimatedDurationHours) } if saved.RentedAt == nil || saved.RentedAt.After(time.Now()) { t.Fatalf("RentedAt not set correctly: %#v", saved.RentedAt) @@ -650,8 +681,8 @@ func TestAdminResetReturnOverdueRecalculatesDuration(t *testing.T) { if saved.HandoffStatus != handoffStatusReceived { t.Fatalf("handoff status = %q, want %q", saved.HandoffStatus, handoffStatusReceived) } - if saved.EstimatedDurationHours != 252 { - t.Fatalf("EstimatedDurationHours = %d, want 252", saved.EstimatedDurationHours) + if saved.EstimatedDurationHours != 264 { + t.Fatalf("EstimatedDurationHours = %d, want 264", saved.EstimatedDurationHours) } if saved.RentedAt == nil || !saved.RentedAt.Equal(rentedAt) { t.Fatalf("RentedAt = %#v, want original %v", saved.RentedAt, rentedAt) diff --git a/frontend/src/shared/utils/listingDisplay.ts b/frontend/src/shared/utils/listingDisplay.ts index 8ef6b35..94e368f 100644 --- a/frontend/src/shared/utils/listingDisplay.ts +++ b/frontend/src/shared/utils/listingDisplay.ts @@ -307,7 +307,8 @@ export function getEstimatedRentalDays(item: Listing) { export function formatEstimatedRentalDuration(item: Listing) { const days = getEstimatedRentalDays(item) if (days <= 0) return '--' - return `${Math.max(1, Math.round(days))}天` + // 不足整天按整天向上取整,与后端 estimateOrderDurationHours 一致 + return `${Math.max(1, Math.ceil(days))}天` } export function readAssetString(item: Listing, key: string) {