package order import ( "time" "hfb_sys/backend/internal/model" "hfb_sys/backend/internal/modules/notification" "hfb_sys/backend/internal/modules/rentergrowth" "hfb_sys/backend/internal/modules/wallet" "gorm.io/gorm" ) func (r *Repository) finalizeCheckout(tx *gorm.DB, order *model.RentalOrder, checkout *model.OrderCheckout, renterContent string) (*refundAction, error) { listing, account, err := r.lockListingAccountForOrder(tx, order) if err != nil { return nil, err } now := time.Now() order.Status = orderStatusCompleted order.HandoffStatus = handoffStatusReturned order.SettlementStatus = settlementStatusSettled order.SettledAt = &now if !isPlatformSettlementOrder(*order) { order.OwnerSettledAt = &now } if err := completeAssets(tx, listing, account); err != nil { return nil, err } settlement := buildCheckoutSettlement(*order, checkout) if settlement.ShortfallCent > 0 { return nil, ErrCheckoutDepositShortfall } if err := appendCheckoutOwnerIncome(tx, order, settlement); err != nil { return nil, err } refund, err := r.prepareCheckoutRefund(order, settlement) if err != nil { return nil, err } applyCheckoutSettlement(checkout, settlement) order.ActualCoinConsumedM = settlement.CoinConsumedM order.ActualPureCoinAmountCent = settlement.PureCoinAmountCent order.ActualPureCoinDiscountCent = settlement.PureCoinDiscountCent order.ActualExtraItemAmountCent = settlement.ExtraItemAmountCent if err := appendCheckoutCompletedNotifications(tx, order, renterContent); err != nil { return nil, err } if err := rentergrowth.AwardOrderCompleted(tx, order, settlement.PureCoinAmountCent, rentergrowth.SourceOrderCompleted); err != nil { return nil, err } if err := saveFinalizedCheckout(tx, order, checkout, listing, account); err != nil { return nil, err } return refund, nil } func appendCheckoutOwnerIncome(tx *gorm.DB, order *model.RentalOrder, settlement checkoutSettlement) error { if isPlatformSettlementOrder(*order) { amountCent := settlement.OwnerRentIncomeCent + settlement.DepositCompensationCent order.OfflineSettlementAmountCent = amountCent if amountCent > 0 { order.OfflineSettlementStatus = offlineSettlementStatusPending } else { order.OfflineSettlementStatus = offlineSettlementStatusNone } return nil } orderID := order.ID var ownerEntries []wallet.Entry if settlement.OwnerRentIncomeCent > 0 { ownerEntries = append(ownerEntries, wallet.Entry{ UserID: order.OwnerID, OrderID: &orderID, Direction: "in", AmountCent: settlement.OwnerRentIncomeCent, BalanceType: "available", BizType: walletBizOwnerIncome, BizNo: order.OrderNo, Remark: "订单结账租金收入", }) } if settlement.DepositCompensationCent > 0 { ownerEntries = append(ownerEntries, wallet.Entry{ UserID: order.OwnerID, OrderID: &orderID, Direction: "in", AmountCent: settlement.DepositCompensationCent, BalanceType: "available", BizType: walletBizDepositCompensation, BizNo: order.OrderNo, Remark: "订单结账押金赔付", }) } if len(ownerEntries) == 0 { return nil } return wallet.AppendEntries(tx, ownerEntries...) } func (r *Repository) prepareCheckoutRefund(order *model.RentalOrder, settlement checkoutSettlement) (*refundAction, error) { renterRefundTotalCent := settlement.RentRefundCent + settlement.DepositRefundCent // 押金暂扣:若订单已暂扣,拦截本次结账中应退还租客的押金部分挂起不退,其余(租金退款)照常原路退。 actualRefundCent := order.ApplyDepositHold(renterRefundTotalCent, settlement.DepositRefundCent) if actualRefundCent <= 0 { return nil, nil } return r.prepareRefund(order, actualRefundCent, refundBizCheckout, "结账退款原路退还") } func applyCheckoutSettlement(checkout *model.OrderCheckout, settlement checkoutSettlement) { checkout.RentAmountCent = settlement.ActualRentAmountCent checkout.OwnerRentAmountCent = settlement.OwnerRentIncomeCent checkout.PlatformFeeCent = settlement.PlatformFeeCent checkout.PureCoinAmountCent = settlement.PureCoinAmountCent checkout.PureCoinDiscountCent = settlement.PureCoinDiscountCent checkout.PureCoinPayableCent = settlement.PureCoinPayableCent checkout.RenterRefundAmountCent = settlement.RenterRefundCent checkout.OwnerIncomeAmountCent = settlement.OwnerIncomeCent checkout.ShortfallCent = settlement.ShortfallCent checkout.OvershootAmountCent = settlement.OvershootAmountCent } func appendCheckoutCompletedNotifications(tx *gorm.DB, order *model.RentalOrder, renterContent string) error { orderID := order.ID if isPlatformSettlementOrder(*order) { if err := notification.Append(tx, notification.Entry{ UserID: order.RenterID, Type: "settlement", Title: "订单已完成", Content: renterContent, BizType: "order", BizID: &orderID, }); err != nil { return err } if order.OfflineSettlementAmountCent > 0 { return appendManagedAdminNotification(tx, *order, "settlement", "代管订单待线下结算", "订单已完成,请按线下流程向卖家转出结算金额并回填状态。") } return nil } return notification.Append(tx, notification.Entry{ UserID: order.RenterID, Type: "settlement", Title: "订单已完成", Content: renterContent, BizType: "order", BizID: &orderID, }, notification.Entry{ UserID: order.OwnerID, Type: "settlement", Title: "订单已完成", Content: "订单已完成,结账金额已入账。", BizType: "order", BizID: &orderID, }, ) } func saveFinalizedCheckout(tx *gorm.DB, order *model.RentalOrder, checkout *model.OrderCheckout, listing *model.RentalListing, account *model.GameAccount) error { if err := tx.Save(order).Error; err != nil { return err } if err := tx.Save(checkout).Error; err != nil { return err } if err := tx.Save(listing).Error; err != nil { return err } return tx.Save(account).Error } func hasOpenCheckout(tx *gorm.DB, orderID uint64) (bool, error) { var count int64 err := tx.Model(&model.OrderCheckout{}). Where("order_id = ? AND status IN ?", orderID, []string{checkoutStatusSubmitted, checkoutStatusCountered, checkoutStatusAccepted, checkoutStatusDisputed}). Count(&count).Error return count > 0, err }