package model import ( "time" "gorm.io/datatypes" ) type RentalOrder struct { ID uint64 `gorm:"primaryKey" json:"id"` OrderNo string `gorm:"size:64;not null;uniqueIndex" json:"order_no"` ListingID uint64 `gorm:"not null;index" json:"listing_id"` AccountID uint64 `gorm:"not null;index" json:"account_id"` OwnerID uint64 `gorm:"not null;index" json:"owner_id"` RenterID uint64 `gorm:"not null;index" json:"renter_id"` RentedAt *time.Time `json:"rented_at"` HandoffStartedAt *time.Time `json:"handoff_started_at"` EstimatedDurationHours int `gorm:"not null;default:24" json:"estimated_duration_hours"` RentAmountCent int64 `gorm:"not null;default:0" json:"-"` OwnerRentAmountCent int64 `gorm:"not null;default:0" json:"-"` DepositAmountCent int64 `gorm:"not null;default:0" json:"-"` DepositOriginalAmountCent int64 `gorm:"not null;default:0" json:"-"` DepositWaivedAmountCent int64 `gorm:"not null;default:0" json:"-"` DepositFreeLevelQuotaCent int64 `gorm:"not null;default:0" json:"deposit_free_level_quota_cent"` DepositFreeManualQuotaCent int64 `gorm:"not null;default:0" json:"deposit_free_manual_quota_cent"` DepositFreeUsedBeforeCent int64 `gorm:"not null;default:0" json:"deposit_free_used_before_cent"` PlatformFeeCent int64 `gorm:"not null;default:0" json:"-"` RentOriginalAmountCent int64 `gorm:"not null;default:0" json:"-"` RentDiscountAmountCent int64 `gorm:"not null;default:0" json:"-"` PureCoinOriginalAmountCent int64 `gorm:"not null;default:0" json:"-"` ExtraItemOriginalAmountCent int64 `gorm:"not null;default:0" json:"-"` ActualCoinConsumedM float64 `gorm:"type:decimal(12,2);not null;default:0" json:"actual_coin_consumed_m"` ActualPureCoinAmountCent int64 `gorm:"not null;default:0" json:"-"` ActualPureCoinDiscountCent int64 `gorm:"not null;default:0" json:"-"` RenterGrowthLevel string `gorm:"size:32;not null;default:'normal'" json:"renter_growth_level"` RenterGrowthLevelName string `gorm:"size:32;not null;default:'普通'" json:"renter_growth_level_name"` RenterDiscountBps int `gorm:"not null;default:10000" json:"renter_discount_bps"` GrowthPointsBasisCent int64 `gorm:"not null;default:0" json:"growth_points_basis_cent"` GrowthPointsPerYuan int64 `gorm:"not null;default:0" json:"growth_points_per_yuan"` GrowthPointsAwarded int64 `gorm:"not null;default:0" json:"growth_points_awarded"` GrowthPointsAwardedAt *time.Time `json:"growth_points_awarded_at"` AccountSnapshot datatypes.JSON `json:"account_snapshot"` Status string `gorm:"size:32;not null;default:'pending_payment'" json:"status"` HandoffStatus string `gorm:"size:32;not null;default:'none'" json:"handoff_status"` HandoffMode string `gorm:"size:16;not null;default:'owner';index" json:"handoff_mode"` SettlementMode string `gorm:"size:32;not null;default:'owner_wallet'" json:"settlement_mode"` ManagedAdminID *uint64 `gorm:"index" json:"managed_admin_id"` SettlementStatus string `gorm:"size:32;not null;default:'unsettled'" json:"settlement_status"` OfflineSettlementStatus string `gorm:"size:16;not null;default:'none';index" json:"offline_settlement_status"` OfflineSettlementAmountCent int64 `gorm:"not null;default:0" json:"offline_settlement_amount_cent"` OfflineSettlementRemark string `gorm:"size:255;not null;default:''" json:"offline_settlement_remark"` OfflineSettledBy *uint64 `json:"offline_settled_by"` OfflineSettledAt *time.Time `json:"offline_settled_at"` RefundStatus string `gorm:"size:32;not null;default:'none';index" json:"refund_status"` RefundAmountCent int64 `gorm:"not null;default:0" json:"refund_amount_cent"` RefundedAt *time.Time `json:"refunded_at"` DepositHoldStatus string `gorm:"size:16;not null;default:'none';index" json:"deposit_hold_status"` DepositHoldAmountCent int64 `gorm:"not null;default:0" json:"deposit_hold_amount_cent"` DepositHoldReason string `gorm:"size:255;not null;default:''" json:"deposit_hold_reason"` DepositHeldBy *uint64 `json:"deposit_held_by"` DepositHeldAt *time.Time `json:"deposit_held_at"` DepositHoldReleasedAt *time.Time `json:"deposit_hold_released_at"` OwnerSettledAt *time.Time `json:"owner_settled_at"` SettledAt *time.Time `json:"settled_at"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } func (RentalOrder) TableName() string { return "rental_orders" } // 押金暂扣状态:客服可对进行中订单的押金退款进行暂扣,订单照常结算, // 但本应原路退给租客的押金部分挂起不退,后续由客服手动归还。 const ( DepositHoldStatusNone = "none" // 未暂扣 DepositHoldStatusHeld = "held" // 已暂扣(押金退款被拦截/挂起) DepositHoldStatusReleased = "released" // 已归还 ) // IsDepositHeld 判断订单当前是否处于押金暂扣状态。 func (o *RentalOrder) IsDepositHeld() bool { return o.DepositHoldStatus == DepositHoldStatusHeld } // ApplyDepositHold 在任一结算退款路径中拦截押金退款。 // totalRefundCent 是本次本应原路退还给租客的总金额(含租金退款和押金退款), // depositPortionCent 是其中属于押金退款的部分。 // 若订单已暂扣,则把押金部分(不超过本次退款总额)从退款中扣除, // 并把尚未记录过的部分累加到暂扣金额(累计不超过实付押金),返回扣除后实际发起原路退款的金额; // 未暂扣时原样返回 totalRefundCent。 // 注意:本函数只做“少退、挂起”,绝不多退,因此在任何路径下都不会造成租客损失或平台多付。 func (o *RentalOrder) ApplyDepositHold(totalRefundCent, depositPortionCent int64) int64 { if !o.IsDepositHeld() { return totalRefundCent } withheld := depositPortionCent if withheld > totalRefundCent { withheld = totalRefundCent } if withheld <= 0 { return totalRefundCent } remainingDepositHoldCent := o.DepositAmountCent - o.DepositHoldAmountCent if remainingDepositHoldCent > 0 { newHold := withheld if newHold > remainingDepositHoldCent { newHold = remainingDepositHoldCent } o.DepositHoldAmountCent += newHold } return totalRefundCent - withheld }