feat(split): 添加分账手动指定接收方功能

- 实现SplitManualService服务类,支持手动覆盖分账明细
- 新增SplitManual和SplitManualLog数据模型
- 修改SplitCalculator优先使用手动覆盖数据进行分账计算
- 添加分账记录手动指定接收方的数据库表结构
- 集成手动分账到汇付分账流程中,支持失败重试机制
- 添加商品详情内容更新SQL脚本用于汇付审核
```
这个提交包含在:
可乐加冰
2026-10-22 22:38:35 +08:00
父节点 b5142ff780
当前提交 af08af8d47
共有 11 个文件被更改,包括 1301 次插入 和 11 次删除

查看文件

@@ -6,6 +6,7 @@ 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\SplitExecutor;
use addons\shopro\library\split\SplitManualService;
use addons\shopro\library\split\ProfitSharingService;
use app\admin\model\shopro\order\Order as ShopOrder;
use app\admin\model\shopro\user\User as ShopUser;
@@ -183,12 +184,18 @@ class Order extends Base
$data['fail_reason_full'] = $this->enrichFailReasonWithDiagnose($data['fail_reason_full'], $status, $diag);
$data['fail_reason'] = $this->summarizeFailReason($data['fail_reason_full'], $diag, $status);
$data['split_source'] = $diag['final_source'];
$data['split_source_text'] = $this->formatSplitSourceText($diag);
$data['calc_source'] = $diag['final_source'];
$data['calc_source_text'] = $this->formatSplitSourceText($diag);
$data['split_diagnose'] = $diag['issues'];
$data['commission_total'] = $diag['commission_total'];
$data['rule_total'] = $diag['rule_total'];
$orderSplitSource = $data['split_source'] ?? 'auto';
$data['split_source'] = $orderSplitSource;
$data['split_source_text'] = $orderSplitSource === 'manual' ? '手动指定' : $data['calc_source_text'];
$data['has_manual_override'] = (new SplitManualService())->hasOverride($orderId);
$data['can_edit_manual'] = ($status === 4 && $channel === 'huifu');
unset($data['order'], $data['receivers']);
$rows[] = $data;
}
@@ -468,6 +475,143 @@ class Order extends Base
return $map[$type] ?? ($type ?: '接收方');
}
/**
* 加载指定接收方弹窗数据
*/
public function manualReceivers()
{
$splitOrderId = intval($this->request->get('split_order_id', 0));
if ($splitOrderId <= 0) {
$this->error('参数错误');
}
try {
$context = (new SplitManualService())->getManualContext($splitOrderId);
} catch (\Throwable $e) {
$this->error($e->getMessage());
}
$this->success('操作成功', null, $context);
}
/**
* 保存手动覆盖(不重试)
*/
public function saveManual()
{
$splitOrderId = intval($this->request->post('split_order_id', 0));
$items = $this->request->post('items/a', []);
$remark = trim((string) $this->request->post('remark', ''));
$row = $this->model->get($splitOrderId);
if (!$row) {
$this->error('分账记录不存在');
}
try {
(new SplitManualService())->save(
intval($row['order_id']),
$items,
intval($this->auth->id),
(string) ($this->auth->nickname ?? $this->auth->username ?? ''),
$remark
);
} catch (\Throwable $e) {
$this->error($e->getMessage());
}
$this->success('保存成功');
}
/**
* 保存手动覆盖并重新分账
*/
public function saveManualAndRetry()
{
$splitOrderId = intval($this->request->post('split_order_id', 0));
$items = $this->request->post('items/a', []);
$remark = trim((string) $this->request->post('remark', ''));
$row = $this->model->get($splitOrderId);
if (!$row) {
$this->error('分账记录不存在');
}
$orderId = intval($row['order_id']);
$adminId = intval($this->auth->id);
$adminName = (string) ($this->auth->nickname ?? $this->auth->username ?? '');
$service = new SplitManualService();
try {
$service->save($orderId, $items, $adminId, $adminName, $remark);
$service->markPendingRetryLog($orderId, $adminId, $adminName, $remark);
} catch (\Throwable $e) {
$this->error($e->getMessage());
}
try {
if (class_exists('\\addons\\shopro_huifu\\library\\HuifuSplitService')) {
$result = (new \addons\shopro_huifu\library\HuifuSplitService())->executeByOrderId($orderId);
} else {
$result = (new SplitExecutor())->retry($splitOrderId);
}
} catch (\Throwable $e) {
$service->finishPendingRetryLog($orderId, false, $e->getMessage());
$this->error('重试失败:' . $e->getMessage());
}
if ($result) {
$this->success('保存并重试成功');
}
$hint = $this->formatSplitWxResultHint($orderId, 'huifu');
$this->error('保存成功但分账失败' . ($hint ?: ''));
}
/**
* 清除手动覆盖
*/
public function clearManual()
{
$splitOrderId = intval($this->request->post('split_order_id', 0));
$remark = trim((string) $this->request->post('remark', ''));
$row = $this->model->get($splitOrderId);
if (!$row) {
$this->error('分账记录不存在');
}
try {
(new SplitManualService())->clear(
intval($row['order_id']),
intval($this->auth->id),
(string) ($this->auth->nickname ?? $this->auth->username ?? ''),
$remark
);
} catch (\Throwable $e) {
$this->error($e->getMessage());
}
$this->success('已清除手动指定');
}
/**
* 变更历史
*/
public function manualHistory()
{
$orderId = intval($this->request->get('order_id', 0));
if ($orderId <= 0) {
$splitOrderId = intval($this->request->get('split_order_id', 0));
if ($splitOrderId > 0) {
$row = $this->model->get($splitOrderId);
$orderId = $row ? intval($row['order_id']) : 0;
}
}
if ($orderId <= 0) {
$this->error('参数错误');
}
$offset = intval($this->request->get('offset', 0));
$limit = intval($this->request->get('limit', 20));
$limit = max(1, min(100, $limit));
$result = (new SplitManualService())->getHistory($orderId, $offset, $limit);
$this->success('操作成功', null, $result);
}
public function retry($ids = null)
{
$id = $ids ?: $this->request->post('id');