feat(split): 优化手工分账服务日志记录和历史展示功能

- 添加CommissionReward和RegionalReward模型引用,用于获取原有分账数据
- 实现snapshotPlannedAutoItems方法,首次改人时保存原系统计划快照
- 新增itemsEqual方法,比较分账项目是否发生变化,避免重复记录日志
- 重构历史记录展示界面,添加change_summary摘要显示变更内容
- 优化前端格式化显示,统一账户显示格式为"未进件"或实际账户
- 修复重试操作时的日志记录问题,改进备注显示逻辑
```
这个提交包含在:
可乐加冰
2026-10-23 00:31:47 +08:00
父节点 d2f22d7ecf
当前提交 f1e2be8dc4
共有 3 个文件被更改,包括 180 次插入 和 19 次删除

查看文件

@@ -3,6 +3,9 @@
namespace addons\shopro\library\split;
use addons\shopro\model\Order;
use addons\shopro\model\User;
use addons\shopro\model\commission\Reward as CommissionReward;
use addons\shopro\model\regional_agent\Reward as RegionalReward;
use addons\shopro\model\split\Receiver;
use addons\shopro\model\split\SplitManual;
use addons\shopro\model\split\SplitManualLog;
@@ -92,14 +95,14 @@ class SplitManualService
$originalItems = [];
if (!$existing) {
$calculator = new SplitCalculator();
$originalItems = $calculator->calculateAuto($orderId);
$originalItems = $this->enrichItemsForStorage($originalItems);
$originalItems = $this->snapshotPlannedAutoItems($orderId);
} else {
$originalItems = $existing['original_items'] ?? [];
}
$storageItems = $this->enrichItemsForStorage($normalized);
$itemsChanged = !$existing || !$this->itemsEqual($beforeItems, $storageItems);
$logBefore = $existing ? $beforeItems : $originalItems;
Db::startTrans();
try {
@@ -119,7 +122,9 @@ class SplitManualService
]);
}
$this->appendLog($orderId, $action, $beforeItems, $storageItems, $adminId, $adminName, $remark);
if ($itemsChanged) {
$this->appendLog($orderId, $action, $logBefore, $storageItems, $adminId, $adminName, $remark);
}
Db::commit();
return $manual;
} catch (\Exception $e) {
@@ -174,11 +179,11 @@ class SplitManualService
'order_id' => $orderId,
'order_sn' => $orderSn,
'action' => 'retry',
'before_items' => $manual['items'] ?? [],
'before_items' => [],
'after_items' => $manual['items'] ?? [],
'admin_id' => intval($adminId),
'admin_name' => $adminName,
'remark' => $remark,
'remark' => $remark ?: '保存并重试',
'retry_result' => 'pending',
'createtime' => time(),
]);
@@ -236,6 +241,11 @@ class SplitManualService
$data['createtime_text'] = !empty($data['createtime'])
? date('Y-m-d H:i:s', $data['createtime'])
: '-';
$before = is_array($data['before_items'] ?? null) ? $data['before_items'] : [];
$after = is_array($data['after_items'] ?? null) ? $data['after_items'] : [];
$data['before_summary'] = $this->formatItemsBrief($before);
$data['after_summary'] = $this->formatItemsBrief($after);
$data['change_summary'] = $this->buildChangeSummary($data['action'] ?? '', $before, $after);
$list[] = $data;
}
@@ -578,4 +588,147 @@ class SplitManualService
];
return $map[$result] ?? $result;
}
/**
* 首次改人时的「原系统计划」快照(含未进件推广员)
*/
protected function snapshotPlannedAutoItems($orderId)
{
$calculator = new SplitCalculator();
$auto = $calculator->calculateAuto($orderId);
if (!empty($auto)) {
return $this->enrichItemsForStorage($auto);
}
$items = [];
$commissionRewards = CommissionReward::where('order_id', $orderId)->where('status', '>=', 0)->select();
foreach ($commissionRewards as $reward) {
$userId = intval($reward['agent_id']);
$amount = round(floatval($reward['commission']), 2);
if ($userId <= 0 || $amount <= 0) {
continue;
}
$items[] = $this->buildPlannedItem('promoter', $userId, $amount, '原推广员佣金');
}
foreach ($this->getRegionalRewardsByOrderId($orderId) as $reward) {
$userId = intval($reward['agent_user_id']);
$amount = round(floatval($reward['commission']), 2);
if ($userId <= 0 || $amount <= 0) {
continue;
}
$items[] = $this->buildPlannedItem('regional_agent', $userId, $amount, '原区域公司佣金');
}
return $items;
}
protected function getRegionalRewardsByOrderId($orderId)
{
try {
return RegionalReward::where('order_id', $orderId)->where('status', '>=', 0)->select();
} catch (\Exception $e) {
return RegionalReward::alias('rr')
->join('shopro_regional_agent_order ro', 'ro.id = rr.regional_order_id')
->where('ro.order_id', $orderId)
->where('rr.status', '>=', 0)
->field('rr.*')
->select();
}
}
protected function buildPlannedItem($roleType, $userId, $amount, $desc)
{
$receiver = Receiver::where('role_type', $roleType)
->where('role_id', $userId)
->where('status', 'normal')
->find();
$user = User::get($userId);
$name = $receiver ? trim((string) ($receiver['receiver_name'] ?? '')) : '';
if ($name === '' && $user) {
$name = trim((string) ($user['nickname'] ?? '')) ?: ('用户#' . $userId);
}
return [
'receiver_id' => $receiver ? intval($receiver['id']) : 0,
'role_type' => $roleType,
'role_id' => $userId,
'receiver_account' => $receiver ? trim((string) ($receiver['receiver_account'] ?? '')) : '',
'receiver_name' => $name,
'amount' => $amount,
'description' => $desc,
];
}
protected function buildChangeSummary($action, array $before, array $after)
{
if ($action === 'retry') {
$afterText = $this->formatItemsBrief($after);
return $afterText ? ('按以下接收方重新发起分账:' . $afterText) : '重新发起分账重试';
}
if ($action === 'clear') {
$beforeText = $this->formatItemsBrief($before);
return $beforeText
? ('清除手动指定,恢复自动计算(原手动指定:' . $beforeText . ')')
: '清除手动指定,恢复自动计算';
}
$beforeText = $this->formatItemsBrief($before);
$afterText = $this->formatItemsBrief($after);
if ($beforeText === '' && $afterText !== '') {
return '指定分账接收方为:' . $afterText;
}
if ($beforeText === $afterText) {
return '分账接收方未变化:' . $afterText;
}
return '分账接收方:' . ($beforeText ?: '(无有效自动接收方)') . ' → ' . $afterText;
}
protected function formatItemsBrief(array $items)
{
if (empty($items)) {
return '';
}
$parts = [];
foreach ($items as $item) {
$parts[] = $this->formatOneItemBrief($item);
}
return implode(';', $parts);
}
protected function formatOneItemBrief(array $item)
{
$roleMap = [
'platform' => '平台',
'promoter' => '推广员',
'regional_agent' => '区域公司',
];
$role = $roleMap[$item['role_type'] ?? ''] ?? ($item['role_type'] ?? '接收方');
$name = trim((string) ($item['receiver_name'] ?? ''));
if ($name === '' && !empty($item['role_id'])) {
$name = '用户#' . intval($item['role_id']);
}
$account = trim((string) ($item['receiver_account'] ?? ''));
$accountPart = $account !== '' ? $account : '未进件';
$amt = number_format(floatval($item['amount'] ?? 0), 2, '.', '');
return $role . ' ' . $name . '(' . $accountPart . ')¥' . $amt;
}
protected function itemsEqual(array $before, array $after)
{
$norm = function (array $items) {
$rows = [];
foreach ($items as $item) {
$rows[] = [
intval($item['receiver_id'] ?? 0),
number_format(floatval($item['amount'] ?? 0), 2, '.', ''),
trim((string) ($item['receiver_account'] ?? '')),
];
}
usort($rows, function ($a, $b) {
return ($a[0] <=> $b[0]) ?: strcmp($a[1], $b[1]);
});
return $rows;
};
return $norm($before) === $norm($after);
}
}

查看文件

@@ -244,7 +244,17 @@
}
.history-drawer .history-time { font-size: 12px; color: #909399; }
.history-drawer .history-meta { font-size: 12px; color: #606266; margin-bottom: 6px; }
.history-drawer .items-diff { font-size: 12px; line-height: 1.65; color: #606266; }
.history-drawer .change-summary {
font-size: 13px;
line-height: 1.7;
color: #303133;
padding: 8px 10px;
background: #f5f7fa;
border-radius: 6px;
margin-bottom: 6px;
}
.history-drawer .change-summary .arrow-hl { color: #7438D5; font-weight: 600; margin: 0 4px; }
.history-drawer .items-diff { font-size: 12px; line-height: 1.65; color: #909399; margin-top: 4px; }
.history-drawer .items-diff .arrow { color: #909399; margin: 0 4px; }
.history-drawer .retry-result { margin-top: 6px; font-size: 12px; }
.history-drawer .retry-result.success { color: #67c23a; }
@@ -469,17 +479,14 @@
<span class="history-time">{{ h.createtime_text }}</span>
</div>
<div class="history-meta" v-if="h.admin_name">操作人:{{ h.admin_name }}</div>
<div class="history-meta" v-if="h.remark">备注:{{ h.remark }}</div>
<div class="items-diff">
<div v-if="h.before_items && h.before_items.length">
<div v-for="(it, i) in h.before_items" :key="'b'+i">{{ formatManualItemLine(it) }}</div>
</div>
<span v-else style="color:#c0c4cc;">(空)</span>
<span class="arrow">→</span>
<div v-if="h.after_items && h.after_items.length">
<div v-for="(it, i) in h.after_items" :key="'a'+i">{{ formatManualItemLine(it) }}</div>
</div>
<span v-else style="color:#c0c4cc;">(空)</span>
<div class="history-meta" v-if="h.remark && h.action !== 'retry'">备注:{{ h.remark }}</div>
<div class="change-summary" v-if="h.change_summary">{{ h.change_summary }}</div>
<div class="items-diff" v-if="h.action === 'create' || h.action === 'update' || h.action === 'clear'">
<span v-if="h.before_summary">{{ h.before_summary }}</span>
<span v-else style="color:#c0c4cc;">(变更前无有效接收方)</span>
<span class="arrow-hl">→</span>
<span v-if="h.after_summary">{{ h.after_summary }}</span>
<span v-else style="color:#c0c4cc;">(已清除)</span>
</div>
<div v-if="h.retry_result" :class="'retry-result ' + h.retry_result">
重试结果:{{ h.retry_result_text }}

查看文件

@@ -110,7 +110,8 @@ define(['jquery', 'bootstrap', 'backend', 'form'], function ($, undefined, Backe
var account = it.receiver_account || '';
var amt = this.formatMoney(it.amount);
var role = this.roleTypeLabel(it.role_type);
return role + ' ' + name + (account ? ' (' + account + ')' : '') + ' ¥' + amt;
var acctLabel = account ? account : '未进件';
return role + ' ' + name + '(' + acctLabel + ')¥' + amt;
},
scrollCellWheel(e) {
var el = e.currentTarget;