```
feat(split): 新增分账订单线下结案功能 - 添加 SplitCloseService 类用于处理订单结案逻辑 - 实现订单结案状态检查和标记功能 - 在汇付分账重试逻辑中加入结案状态验证,防止已结案订单继续重试 - 更新分账手动服务,添加结案操作类型 - 扩展钱包结算类,增加结案相关诊断方法 - 修改测试脚本,支持批量标记订单为已结案状态 - 添加数据库升级脚本,增加结案相关字段和菜单权限 ```
这个提交包含在:
@@ -5,8 +5,10 @@ namespace app\admin\controller\shopro\split;
|
||||
use app\admin\controller\shopro\Base;
|
||||
use addons\shopro\library\split\Config as SplitConfig;
|
||||
use addons\shopro\library\split\SplitCalculator;
|
||||
use addons\shopro\library\split\SplitCloseService;
|
||||
use addons\shopro\library\split\SplitExecutor;
|
||||
use addons\shopro\library\split\SplitManualService;
|
||||
use addons\shopro\library\split\SplitWalletSettlement;
|
||||
use addons\shopro\library\split\ProfitSharingService;
|
||||
use app\admin\model\shopro\order\Order as ShopOrder;
|
||||
use app\admin\model\shopro\user\User as ShopUser;
|
||||
@@ -22,6 +24,7 @@ class Order extends Base
|
||||
2 => '成功',
|
||||
3 => '部分成功',
|
||||
4 => '失败',
|
||||
5 => '已结案',
|
||||
];
|
||||
|
||||
public function _initialize()
|
||||
@@ -136,6 +139,7 @@ class Order extends Base
|
||||
{
|
||||
$rows = [];
|
||||
$manualService = new SplitManualService();
|
||||
$closeService = new SplitCloseService();
|
||||
$orderIds = [];
|
||||
foreach ($list as $item) {
|
||||
$data = is_array($item) ? $item : $item->toArray();
|
||||
@@ -146,6 +150,7 @@ class Order extends Base
|
||||
}
|
||||
$overrideOrderIds = array_flip($manualService->orderIdsWithOverride($orderIds));
|
||||
$logOrderIds = array_flip($manualService->orderIdsWithLog($orderIds));
|
||||
$closedLogOrderIds = array_flip($closeService->orderIdsWithCloseLog($orderIds));
|
||||
|
||||
foreach ($list as $item) {
|
||||
$data = is_array($item) ? $item : $item->toArray();
|
||||
@@ -209,10 +214,42 @@ class Order extends Base
|
||||
$hasManualLog = isset($logOrderIds[$orderId]);
|
||||
$data['has_manual_override'] = $hasOverride;
|
||||
$data['has_manual_log'] = $hasManualLog;
|
||||
$data['can_edit_manual'] = ($status === 4 && $channel === 'huifu');
|
||||
$settleDiag = SplitWalletSettlement::diagnoseOrderSettlement($orderId);
|
||||
$data['settlement_source'] = $settleDiag['settlement_source'];
|
||||
$data['settlement_source_text'] = $settleDiag['settlement_source_text'];
|
||||
$data['wallet_settlement_sum'] = $settleDiag['wallet_sum'];
|
||||
$data['retry_blocked'] = $settleDiag['retry_blocked'];
|
||||
$data['retry_block_reason'] = $settleDiag['retry_block_reason'];
|
||||
|
||||
$data['can_edit_manual'] = ($status === 4 && $channel === 'huifu')
|
||||
&& !$settleDiag['retry_blocked'];
|
||||
$data['can_retry_split'] = ($status === 4 && $channel === 'huifu')
|
||||
&& !$settleDiag['retry_blocked'];
|
||||
// 变更历史:成功/失败后均可查看,不限于 status=4
|
||||
$data['can_view_manual_history'] = ($channel === 'huifu')
|
||||
&& ($hasOverride || $hasManualLog || $orderSplitSource === 'manual');
|
||||
&& ($hasOverride || $hasManualLog || $orderSplitSource === 'manual' || isset($closedLogOrderIds[$orderId]));
|
||||
|
||||
$isClosed = ($status === SplitCloseService::STATUS_CLOSED) || isset($closedLogOrderIds[$orderId]);
|
||||
$data['is_closed'] = $isClosed;
|
||||
$data['close_reason'] = trim((string) ($data['close_reason'] ?? ''));
|
||||
$data['closetime_text'] = !empty($data['closetime'])
|
||||
? date('Y-m-d H:i:s', $data['closetime'])
|
||||
: '';
|
||||
$data['can_mark_closed'] = ($channel === 'huifu')
|
||||
&& in_array($status, [2, 3, 4], true)
|
||||
&& !$isClosed;
|
||||
if ($settleDiag['retry_blocked'] && $status === 4 && $data['fail_reason_full'] === '') {
|
||||
$data['fail_reason_full'] = $settleDiag['retry_block_reason'];
|
||||
$data['fail_reason'] = mb_strlen($settleDiag['retry_block_reason']) > 72
|
||||
? (mb_substr($settleDiag['retry_block_reason'], 0, 72) . '…')
|
||||
: $settleDiag['retry_block_reason'];
|
||||
}
|
||||
if ($status === SplitCloseService::STATUS_CLOSED && $data['close_reason'] !== '') {
|
||||
$data['fail_reason_full'] = '线下结案:' . $data['close_reason'];
|
||||
$data['fail_reason'] = mb_strlen($data['close_reason']) > 72
|
||||
? (mb_substr($data['close_reason'], 0, 72) . '…')
|
||||
: $data['close_reason'];
|
||||
}
|
||||
|
||||
unset($data['order'], $data['receivers']);
|
||||
$rows[] = $data;
|
||||
@@ -630,6 +667,37 @@ class Order extends Base
|
||||
$this->success('操作成功', null, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记已线下处理(停止重试,status=5)
|
||||
*/
|
||||
public function markClosed()
|
||||
{
|
||||
$splitOrderId = intval($this->request->post('split_order_id', 0));
|
||||
$reason = trim((string) $this->request->post('reason', ''));
|
||||
|
||||
$row = $this->model->get($splitOrderId);
|
||||
if (!$row) {
|
||||
$this->error('分账记录不存在');
|
||||
}
|
||||
if (($row['channel'] ?? '') !== 'huifu') {
|
||||
$this->error('仅汇付分账记录可标记结案');
|
||||
}
|
||||
try {
|
||||
$result = (new SplitCloseService())->closeByOrderId(
|
||||
intval($row['order_id']),
|
||||
$reason,
|
||||
intval($this->auth->id),
|
||||
(string) ($this->auth->nickname ?? $this->auth->username ?? '')
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
if (!empty($result['already'])) {
|
||||
$this->success('该订单此前已标记结案');
|
||||
}
|
||||
$this->success('已标记线下结案,将不再自动重试');
|
||||
}
|
||||
|
||||
public function retry($ids = null)
|
||||
{
|
||||
$id = $ids ?: $this->request->post('id');
|
||||
@@ -637,6 +705,14 @@ class Order extends Base
|
||||
if (!$row) {
|
||||
$this->error('分账记录不存在,可能已被上次重试替换。请刷新列表后,对最新一条失败记录重试');
|
||||
}
|
||||
if (intval($row['status']) === SplitCloseService::STATUS_CLOSED
|
||||
|| (new SplitCloseService())->isClosed((int) $row['order_id'])) {
|
||||
$this->error('该订单已标记线下结案,不可重试');
|
||||
}
|
||||
$block = SplitWalletSettlement::getHuifuRetryBlockReason((int) $row['order_id']);
|
||||
if ($block !== null) {
|
||||
$this->error($block);
|
||||
}
|
||||
$orderId = (int) $row['order_id'];
|
||||
$channel = $row['channel'] ?? 'wechat';
|
||||
try {
|
||||
|
||||
在新工单中引用
屏蔽一个用户