From f1e2be8dc45e1a7edb5d9cec14ec0969becdc312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=AF=E4=B9=90=E5=8A=A0=E5=86=B0?= <11869599+addie66@user.noreply.gitee.com> Date: Fri, 23 Oct 2026 00:31:47 +0800 Subject: [PATCH] =?UTF-8?q?```=20feat(split):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=89=8B=E5=B7=A5=E5=88=86=E8=B4=A6=E6=9C=8D=E5=8A=A1=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E8=AE=B0=E5=BD=95=E5=92=8C=E5=8E=86=E5=8F=B2=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加CommissionReward和RegionalReward模型引用,用于获取原有分账数据 - 实现snapshotPlannedAutoItems方法,首次改人时保存原系统计划快照 - 新增itemsEqual方法,比较分账项目是否发生变化,避免重复记录日志 - 重构历史记录展示界面,添加change_summary摘要显示变更内容 - 优化前端格式化显示,统一账户显示格式为"未进件"或实际账户 - 修复重试操作时的日志记录问题,改进备注显示逻辑 ``` --- .../library/split/SplitManualService.php | 165 +++++++++++++++++- .../admin/view/shopro/split/order/index.html | 31 ++-- .../assets/js/backend/shopro/split/order.js | 3 +- 3 files changed, 180 insertions(+), 19 deletions(-) diff --git a/addons/shopro/library/split/SplitManualService.php b/addons/shopro/library/split/SplitManualService.php index 2e000aa..e523a86 100644 --- a/addons/shopro/library/split/SplitManualService.php +++ b/addons/shopro/library/split/SplitManualService.php @@ -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); + } } diff --git a/application/admin/view/shopro/split/order/index.html b/application/admin/view/shopro/split/order/index.html index 590360f..9baa461 100644 --- a/application/admin/view/shopro/split/order/index.html +++ b/application/admin/view/shopro/split/order/index.html @@ -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 @@ {{ h.createtime_text }}
- -