添加订单押金暂扣与归还
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user