feat(split): 新增分账接收方明细功能 新增 SplitOrderHelper 类用于同步分账接收方明细数据,包括接收方角色、 姓名、账户和金额等信息。优化管理后台分账订单列表页面,显示详细的 接收方信息和失败原因,提升分账流程的可视化和可追溯性。 ```
231 行
8.5 KiB
PHP
231 行
8.5 KiB
PHP
<?php
|
|
|
|
namespace addons\shopro\library\split;
|
|
|
|
use addons\shopro\model\Order;
|
|
use addons\shopro\library\split\SplitOrderHelper;
|
|
use addons\shopro\model\split\SplitOrder;
|
|
use addons\shopro\model\split\SplitOrderReceiver;
|
|
use think\Db;
|
|
|
|
class SplitExecutor
|
|
{
|
|
protected $config;
|
|
protected $service;
|
|
protected $calculator;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->config = new Config();
|
|
$this->service = new ProfitSharingService();
|
|
$this->calculator = new SplitCalculator();
|
|
}
|
|
|
|
public function executeByOrderId($orderId, $event = 'payed')
|
|
{
|
|
$order = Order::get($orderId);
|
|
$deferWallet = $order && SplitWalletSettlement::shouldDeferForGatewaySplit($order, $event);
|
|
$ok = false;
|
|
|
|
try {
|
|
if (!$this->config->isEnable()) {
|
|
return false;
|
|
}
|
|
|
|
if (class_exists('\\addons\\shopro_huifu\\library\\Bridge')) {
|
|
$hookParams = [
|
|
'order_id' => $orderId,
|
|
'event' => $event,
|
|
];
|
|
$handled = \think\Hook::listen('shopro_split_execute', $hookParams, null, true);
|
|
if ($handled === true) {
|
|
$ok = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
$orderCheck = Order::get($orderId);
|
|
if ($orderCheck && $orderCheck['pay_type'] === 'huifu_wechat') {
|
|
return false;
|
|
}
|
|
|
|
$exists = SplitOrder::where('order_id', $orderId)->where('status', 'in', [1, 2])->find();
|
|
if ($exists) {
|
|
$ok = true;
|
|
return true;
|
|
}
|
|
|
|
$order = Order::get($orderId);
|
|
if (!$order || empty($order['transaction_id'])) {
|
|
return false;
|
|
}
|
|
|
|
$items = $this->calculator->calculate($orderId);
|
|
if (empty($items)) {
|
|
return false;
|
|
}
|
|
|
|
$outOrderNo = 'PS' . date('YmdHis') . $orderId;
|
|
$splitAmount = array_sum(array_column($items, 'amount'));
|
|
|
|
Db::startTrans();
|
|
try {
|
|
$splitOrder = SplitOrder::create([
|
|
'order_id' => $orderId,
|
|
'order_sn' => $order['order_sn'],
|
|
'transaction_id' => $order['transaction_id'],
|
|
'out_order_no' => $outOrderNo,
|
|
'total_amount' => $order['pay_fee'],
|
|
'split_amount' => $splitAmount,
|
|
'status' => 1,
|
|
]);
|
|
|
|
$receivers = [];
|
|
foreach ($items as $item) {
|
|
SplitOrderReceiver::create([
|
|
'split_order_id' => $splitOrder->id,
|
|
'receiver_id' => $item['receiver_id'],
|
|
'role_type' => $item['role_type'],
|
|
'amount' => $item['amount'],
|
|
'description' => $item['description'],
|
|
'status' => 0,
|
|
]);
|
|
$receivers[] = [
|
|
'type' => $item['receiver_type'],
|
|
'account' => $item['receiver_account'],
|
|
'name' => $item['receiver_name'] ?? '',
|
|
'amount' => intval(round($item['amount'] * 100)),
|
|
'description' => $item['description'],
|
|
];
|
|
}
|
|
|
|
$result = $this->service->createOrder([
|
|
'transaction_id' => $order['transaction_id'],
|
|
'out_order_no' => $outOrderNo,
|
|
'receivers' => $receivers,
|
|
'unfreeze_unsplit' => true,
|
|
]);
|
|
|
|
$splitOrder->wx_result = is_array($result) ? json_encode($result, JSON_UNESCAPED_UNICODE) : json_encode($result);
|
|
$splitOrder->status = 2;
|
|
$splitOrder->save();
|
|
|
|
SplitOrderReceiver::where('split_order_id', $splitOrder->id)->update(['status' => 1]);
|
|
Db::commit();
|
|
$ok = true;
|
|
return true;
|
|
} catch (\Exception $e) {
|
|
Db::rollback();
|
|
$this->markFailed($orderId, $outOrderNo ?? ('PS' . $orderId), $order, $items ?? [], $e->getMessage());
|
|
return false;
|
|
}
|
|
} finally {
|
|
if ($deferWallet) {
|
|
SplitWalletSettlement::finalizeAfterSplitAttempt($orderId, $event, $ok);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function retry($splitOrderId)
|
|
{
|
|
$splitOrder = SplitOrder::get($splitOrderId, ['receivers']);
|
|
if (!$splitOrder) {
|
|
return false;
|
|
}
|
|
|
|
if (($splitOrder['channel'] ?? 'wechat') === 'huifu' && class_exists('\\addons\\shopro_huifu\\library\\HuifuSplitService')) {
|
|
return (new \addons\shopro_huifu\library\HuifuSplitService())->retry($splitOrderId);
|
|
}
|
|
|
|
try {
|
|
$receivers = [];
|
|
foreach ($splitOrder['receivers'] as $row) {
|
|
$receiver = \addons\shopro\model\split\Receiver::get($row['receiver_id']);
|
|
if (!$receiver) {
|
|
continue;
|
|
}
|
|
$receivers[] = [
|
|
'type' => $receiver['receiver_type'],
|
|
'account' => $receiver['receiver_account'],
|
|
'name' => $receiver['receiver_name'] ?? '',
|
|
'amount' => intval(round($row['amount'] * 100)),
|
|
'description' => $row['description'],
|
|
];
|
|
}
|
|
|
|
$result = $this->service->createOrder([
|
|
'transaction_id' => $splitOrder['transaction_id'],
|
|
'out_order_no' => $splitOrder['out_order_no'] . 'R' . ($splitOrder['retry_count'] + 1),
|
|
'receivers' => $receivers,
|
|
'unfreeze_unsplit' => true,
|
|
]);
|
|
|
|
$splitOrder->status = 2;
|
|
$splitOrder->wx_result = json_encode($result, JSON_UNESCAPED_UNICODE);
|
|
$splitOrder->retry_count = $splitOrder['retry_count'] + 1;
|
|
$splitOrder->next_retry_time = null;
|
|
$splitOrder->save();
|
|
return true;
|
|
} catch (\Exception $e) {
|
|
$splitOrder->retry_count = $splitOrder['retry_count'] + 1;
|
|
$splitOrder->status = 4;
|
|
$splitOrder->next_retry_time = time() + $this->config->getRetryInterval();
|
|
$splitOrder->wx_result = $e->getMessage();
|
|
$splitOrder->save();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function returnByOrderId($orderId)
|
|
{
|
|
$splitOrder = SplitOrder::where('order_id', $orderId)->where('status', 2)->find();
|
|
if (!$splitOrder) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
foreach ($splitOrder->receivers as $row) {
|
|
$this->service->returnOrder([
|
|
'out_order_no' => $splitOrder['out_order_no'],
|
|
'out_return_no' => 'RT' . $splitOrder['id'] . $row['id'],
|
|
'return_mchid' => '',
|
|
'amount' => intval(round($row['amount'] * 100)),
|
|
'description' => '订单退款分账回退',
|
|
]);
|
|
}
|
|
$splitOrder->status = 4;
|
|
$splitOrder->save();
|
|
return true;
|
|
} catch (\Exception $e) {
|
|
\think\Log::error('split-return-error:' . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected function markFailed($orderId, $outOrderNo, $order, $items, $message)
|
|
{
|
|
$splitOrder = SplitOrder::where('order_id', $orderId)->find();
|
|
if (!$splitOrder) {
|
|
$splitOrder = SplitOrder::create([
|
|
'order_id' => $orderId,
|
|
'order_sn' => $order['order_sn'],
|
|
'transaction_id' => $order['transaction_id'] ?? '',
|
|
'out_order_no' => $outOrderNo,
|
|
'total_amount' => $order['pay_fee'],
|
|
'split_amount' => array_sum(array_column($items, 'amount')),
|
|
'status' => 4,
|
|
'wx_result' => $message,
|
|
'retry_count' => 1,
|
|
'next_retry_time' => time() + $this->config->getRetryInterval(),
|
|
]);
|
|
} else {
|
|
$splitOrder->status = 4;
|
|
$splitOrder->wx_result = $message;
|
|
$splitOrder->retry_count = $splitOrder['retry_count'] + 1;
|
|
$splitOrder->next_retry_time = time() + $this->config->getRetryInterval();
|
|
$splitOrder->save();
|
|
}
|
|
SplitOrderHelper::syncReceivers($splitOrder->id, $items);
|
|
}
|
|
}
|