package order import ( "context" "time" "hfb_sys/backend/internal/model" "gorm.io/gorm" "gorm.io/gorm/clause" ) // AdminHoldDeposit 客服对进行中订单的押金进行暂扣。 // 暂扣本身不移动任何资金:租客押金全程在支付渠道,系统只记录“暂扣中”标记; // 待订单结算/取消/关闭/仲裁时,本应原路退还租客的押金部分会被拦截挂起, // 后续由客服通过 AdminReleaseDeposit 手动原路归还。 func (r *Repository) AdminHoldDeposit(ctx context.Context, adminID uint64, orderID uint64, req AdminActionRequest, meta AuditMeta) error { return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { var order model.RentalOrder if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, orderID).Error; err != nil { return err } if !canHoldDeposit(order) { return ErrDepositCannotHold } beforeStatus := order.DepositHoldStatus now := time.Now() order.DepositHoldStatus = depositHoldStatusHeld order.DepositHoldReason = req.Reason order.DepositHeldBy = &adminID order.DepositHeldAt = &now order.DepositHoldReleasedAt = nil if err := appendAuditLog(tx, adminID, "order.deposit_hold", "order", order.ID, meta, map[string]any{ "order_id": order.ID, "order_no": order.OrderNo, "renter_id": order.RenterID, "reason": req.Reason, "before_hold_status": beforeStatus, "after_hold_status": order.DepositHoldStatus, "deposit_amount_cent": order.DepositAmountCent, }); err != nil { return err } return tx.Save(&order).Error }) } // AdminReleaseDeposit 客服手动归还此前暂扣的押金,通过支付渠道原路退回。 // 仅支持归还,不支持扣款(系统内无平台资金池承接扣款)。 func (r *Repository) AdminReleaseDeposit(ctx context.Context, adminID uint64, orderID uint64, req AdminActionRequest, meta AuditMeta) error { var refund *refundAction err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { var order model.RentalOrder if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, orderID).Error; err != nil { return err } if order.DepositHoldStatus != depositHoldStatusHeld { return ErrDepositNotHeld } holdAmountCent := order.DepositHoldAmountCent if holdAmountCent <= 0 { return ErrDepositHoldAmountEmpty } now := time.Now() order.DepositHoldStatus = depositHoldStatusReleased order.DepositHoldReleasedAt = &now action, err := r.prepareRefund(&order, holdAmountCent, refundBizDeposit, "暂扣押金归还原路退回") if err != nil { return err } refund = action if err := appendAuditLog(tx, adminID, "order.deposit_release", "order", order.ID, meta, map[string]any{ "order_id": order.ID, "order_no": order.OrderNo, "renter_id": order.RenterID, "reason": req.Reason, "deposit_hold_amount_cent": holdAmountCent, }); err != nil { return err } return tx.Save(&order).Error }) if err != nil { return err } r.startRefundBestEffort(ctx, refund) return nil } // canHoldDeposit 判断订单当前是否允许暂扣押金: // 仅进行中(非终态、押金退款尚未发生)且有实付押金、且未处于暂扣态的订单。 func canHoldDeposit(order model.RentalOrder) bool { if order.DepositHoldStatus != depositHoldStatusNone { return false } if order.DepositAmountCent <= 0 { return false } switch order.Status { case orderStatusPendingHandoff, orderStatusRenting, orderStatusOverdue, orderStatusPendingCheckoutConfirm, orderStatusPendingCheckoutAccept, orderStatusCheckoutDisputing: return true default: return false } }