fix: 优化退款防重与防多退机制,增加防重锁、事务排他行锁及汇付幂等对齐
这个提交包含在:
@@ -252,8 +252,8 @@ class Aftersale extends Backend
|
||||
if ($this->request->isAjax()) {
|
||||
$refund_money = round($this->request->post('refund_money', 0), 2);
|
||||
|
||||
if ($refund_money < 0) {
|
||||
$this->error('退款金额不能小于 0');
|
||||
if ($refund_money <= 0) {
|
||||
$this->error('退款金额必须大于 0');
|
||||
}
|
||||
|
||||
$aftersale = $this->model->withTrashed()->canOper()->where('id', $id)->find();
|
||||
@@ -268,37 +268,34 @@ class Aftersale extends Backend
|
||||
$this->error('订单不存在');
|
||||
}
|
||||
|
||||
$items = $order->item;
|
||||
$items = array_column($items, null, 'id');
|
||||
|
||||
// 旧的退款逻辑
|
||||
// 当前订单已退款总金额
|
||||
// $refunded_money = array_sum(array_column($items, 'refund_fee'));
|
||||
// 剩余可退款金额
|
||||
// $refund_surplus_money = $order->pay_fee - $refunded_money;
|
||||
// 如果退款金额大于订单支付总金额
|
||||
// if ($refund_money > $refund_surplus_money) {
|
||||
// $this->error('退款总金额不能大于实际支付金额');
|
||||
// }
|
||||
|
||||
// 新的退款逻辑
|
||||
$orderItem = $items[$aftersale['order_item_id']];
|
||||
// 剩余可退款金额
|
||||
$refund_surplus_money = bcsub($orderItem->pay_price, $orderItem->refund_fee, 2);
|
||||
|
||||
if ($refund_money > $refund_surplus_money) {
|
||||
$this->error('退款金额不能大于' . $refund_surplus_money . '元');
|
||||
}
|
||||
|
||||
if (!$orderItem || in_array($orderItem['refund_status'], [
|
||||
\app\admin\model\shopro\order\OrderItem::REFUND_STATUS_OK,
|
||||
\app\admin\model\shopro\order\OrderItem::REFUND_STATUS_FINISH,
|
||||
])) {
|
||||
$this->error('订单商品已退款,不能重复退款');
|
||||
$lockKey = 'lock:refund:aftersale:' . $id;
|
||||
if (\think\Cache::get($lockKey)) {
|
||||
$this->error('退款正在处理中,请勿重复操作');
|
||||
}
|
||||
\think\Cache::set($lockKey, 1, 10);
|
||||
|
||||
try {
|
||||
Db::transaction(function () use ($aftersale, $order, $orderItem, $refund_money, $refund_surplus_money) {
|
||||
Db::transaction(function () use ($id, $refund_money, $order) {
|
||||
// 行级排他锁锁定售后单与订单商品,防止并发重复退款
|
||||
$aftersale = $this->model->withTrashed()->where('id', $id)->lock(true)->find();
|
||||
if (!$aftersale || $aftersale['refund_status'] == \app\admin\model\shopro\order\Aftersale::REFUND_STATUS_FINISH) {
|
||||
throw new \Exception('售后单已退款,不能重复退款');
|
||||
}
|
||||
|
||||
$orderItem = OrderItem::where('id', $aftersale['order_item_id'])->lock(true)->find();
|
||||
if (!$orderItem || in_array($orderItem['refund_status'], [
|
||||
\app\admin\model\shopro\order\OrderItem::REFUND_STATUS_OK,
|
||||
\app\admin\model\shopro\order\OrderItem::REFUND_STATUS_FINISH,
|
||||
])) {
|
||||
throw new \Exception('订单商品已退款,不能重复退款');
|
||||
}
|
||||
|
||||
// 实时剩余可退金额硬校验(防多退)
|
||||
$refund_surplus_money = bcsub($orderItem->pay_price, $orderItem->refund_fee, 2);
|
||||
if (bccomp($refund_money, $refund_surplus_money, 2) === 1) {
|
||||
throw new \Exception('退款金额不能大于' . $refund_surplus_money . '元');
|
||||
}
|
||||
|
||||
$data = ['aftersale' => $aftersale, 'order' => $order, 'item' => $orderItem];
|
||||
\think\Hook::listen('aftersale_finish_before', $data);
|
||||
|
||||
@@ -325,9 +322,11 @@ class Aftersale extends Backend
|
||||
\think\Hook::listen('aftersale_finish_after', $data);
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
\think\Cache::rm($lockKey);
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
|
||||
\think\Cache::rm($lockKey);
|
||||
return $this->success('操作成功');
|
||||
}
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户