```
feat(split): 优化手工分账服务日志记录和历史展示功能 - 添加CommissionReward和RegionalReward模型引用,用于获取原有分账数据 - 实现snapshotPlannedAutoItems方法,首次改人时保存原系统计划快照 - 新增itemsEqual方法,比较分账项目是否发生变化,避免重复记录日志 - 重构历史记录展示界面,添加change_summary摘要显示变更内容 - 优化前端格式化显示,统一账户显示格式为"未进件"或实际账户 - 修复重试操作时的日志记录问题,改进备注显示逻辑 ```
这个提交包含在:
@@ -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;
|
||||
|
||||
在新工单中引用
屏蔽一个用户