From 8ea82f54a1eed86890be782a0f44cc7d8f6c07ef Mon Sep 17 00:00:00 2001 From: 92451 <924518135@qq.com> Date: Wed, 23 Sep 2026 21:46:58 +0800 Subject: [PATCH] feat: add withdrawal table controls --- app/Business/WithdrawalManagementReport.php | 124 ++++++- app/Http/Controllers/Business/Pages.php | 1 + .../withdrawal-management.blade.php | 321 +++++++++++++++--- tests/Feature/Business/ManagementCrudTest.php | 27 +- .../WithdrawalManagementImportExportTest.php | 43 +++ 5 files changed, 450 insertions(+), 66 deletions(-) diff --git a/app/Business/WithdrawalManagementReport.php b/app/Business/WithdrawalManagementReport.php index 4519e6a4..0c0a595f 100644 --- a/app/Business/WithdrawalManagementReport.php +++ b/app/Business/WithdrawalManagementReport.php @@ -9,6 +9,20 @@ use Modules\EcommerceFinance\Repositories\JushuitanShopBalanceRepository; class WithdrawalManagementReport { + private const SORT_FIELDS = [ + 'platform', + 'erp_store_name', + 'store_id', + 'store_entity', + 'withdrawal_path', + 'withdrawal_account', + 'bank_card', + 'balance', + 'reserve_standard', + 'withdrawal_amount', + 'compliance', + ]; + public function __construct( private readonly JushuitanShopBalanceRepository $shopBalances, ) { @@ -20,12 +34,13 @@ class WithdrawalManagementReport * platforms:list,paths:list,storeEntities:list,rows:Collection, * balances:array, * reserveStandards:array,summary:array{source:int,matched:int,ambiguous:int,unmatched:int,not_ready:int}, - * balanceColumnLabel:string,balanceDate:string + * balanceColumnLabel:string,balanceDate:string,sort:array{field:string,direction:string} * } */ public function results(Request $request): array { $filters = $this->filters($request); + $sort = $this->sorting($request); $platforms = $this->options('store_platform'); $paths = $this->options('receiving_method'); $storeEntities = $this->options('store_entity_name'); @@ -58,18 +73,6 @@ class WithdrawalManagementReport ); } - $statusOrder = ['matched' => 0, 'ambiguous' => 1, 'unmatched' => 2, 'not_ready' => 3]; - $rows = $rows->sort( - static function (StoreReceivingAccount $left, StoreReceivingAccount $right) use ($balances, $statusOrder): int { - $leftStatus = $balances[(int) $left->id]['status'] ?? 'not_ready'; - $rightStatus = $balances[(int) $right->id]['status'] ?? 'not_ready'; - $statusComparison = ($statusOrder[$leftStatus] ?? 99) <=> ($statusOrder[$rightStatus] ?? 99); - - return $statusComparison !== 0 - ? $statusComparison - : ((int) $right->id <=> (int) $left->id); - }, - )->values(); $reserveStandards = $rows->mapWithKeys( static fn (StoreReceivingAccount $row): array => [ (int) $row->id => WithdrawalReserveStandard::amountFor( @@ -78,6 +81,9 @@ class WithdrawalManagementReport ), ], )->all(); + $rows = $sort['field'] === '' + ? $this->sortByBalanceStatus($rows, $balances) + : $this->sortRows($rows, $sort, $balances, $reserveStandards); return [ 'filters' => $filters, @@ -96,6 +102,7 @@ class WithdrawalManagementReport ], 'balanceColumnLabel' => $today->format('n月j日') . '店铺余额', 'balanceDate' => $balanceDate, + 'sort' => $sort, ]; } @@ -131,4 +138,95 @@ class WithdrawalManagementReport ->values() ->all(); } + + /** @return array{field:string,direction:string} */ + private function sorting(Request $request): array + { + $field = trim((string) $request->query('sort')); + $direction = strtolower(trim((string) $request->query('direction'))); + + return [ + 'field' => in_array($field, self::SORT_FIELDS, true) ? $field : '', + 'direction' => in_array($direction, ['asc', 'desc'], true) ? $direction : 'asc', + ]; + } + + /** + * @param Collection $rows + * @param array $balances + * @return Collection + */ + private function sortByBalanceStatus(Collection $rows, array $balances): Collection + { + $statusOrder = ['matched' => 0, 'ambiguous' => 1, 'unmatched' => 2, 'not_ready' => 3]; + + return $rows->sort( + static function (StoreReceivingAccount $left, StoreReceivingAccount $right) use ($balances, $statusOrder): int { + $leftStatus = $balances[(int) $left->id]['status'] ?? 'not_ready'; + $rightStatus = $balances[(int) $right->id]['status'] ?? 'not_ready'; + $statusComparison = ($statusOrder[$leftStatus] ?? 99) <=> ($statusOrder[$rightStatus] ?? 99); + + return $statusComparison !== 0 + ? $statusComparison + : ((int) $right->id <=> (int) $left->id); + }, + )->values(); + } + + /** + * @param Collection $rows + * @param array{field:string,direction:string} $sort + * @param array $balances + * @param array $reserveStandards + * @return Collection + */ + private function sortRows(Collection $rows, array $sort, array $balances, array $reserveStandards): Collection + { + return $rows->sort(function (StoreReceivingAccount $left, StoreReceivingAccount $right) use ($sort, $balances, $reserveStandards): int { + $leftValue = $this->sortValue($left, $sort['field'], $balances, $reserveStandards); + $rightValue = $this->sortValue($right, $sort['field'], $balances, $reserveStandards); + $comparison = $this->compareSortValues($leftValue, $rightValue, $sort['direction']); + + return $comparison !== 0 + ? $comparison + : ((int) $right->id <=> (int) $left->id); + })->values(); + } + + /** + * @param array $balances + * @param array $reserveStandards + */ + private function sortValue(StoreReceivingAccount $row, string $field, array $balances, array $reserveStandards): mixed + { + return match ($field) { + 'platform' => $row->store_platform, + 'erp_store_name' => $row->erp_store_name, + 'store_id' => $row->store_id, + 'store_entity' => $row->store_entity_name, + 'withdrawal_path' => $row->withdrawal_path, + 'withdrawal_account' => $row->withdrawal_account_name, + 'bank_card' => $row->bank_card_last_four, + 'balance' => ($balances[(int) $row->id]['status'] ?? null) === 'matched' + ? $balances[(int) $row->id]['balance'] + : null, + 'reserve_standard' => $reserveStandards[(int) $row->id] ?? null, + default => null, + }; + } + + private function compareSortValues(mixed $left, mixed $right, string $direction): int + { + $leftEmpty = $left === null || $left === ''; + $rightEmpty = $right === null || $right === ''; + if ($leftEmpty || $rightEmpty) { + return $leftEmpty === $rightEmpty ? 0 : ($leftEmpty ? 1 : -1); + } + + $comparison = is_numeric($left) && is_numeric($right) + ? ((float) $left <=> (float) $right) + : strnatcasecmp((string) $left, (string) $right); + + return $direction === 'desc' ? -$comparison : $comparison; + } } diff --git a/app/Http/Controllers/Business/Pages.php b/app/Http/Controllers/Business/Pages.php index 35d97e88..c870e7cd 100644 --- a/app/Http/Controllers/Business/Pages.php +++ b/app/Http/Controllers/Business/Pages.php @@ -894,6 +894,7 @@ class Pages extends Controller 'withdrawalBalanceSummary' => $report['summary'], 'withdrawalBalanceColumnLabel' => $report['balanceColumnLabel'], 'withdrawalBalanceDate' => $report['balanceDate'], + 'withdrawalSort' => $report['sort'], ]; } diff --git a/resources/views/business/pages/fund-management/withdrawal-management.blade.php b/resources/views/business/pages/fund-management/withdrawal-management.blade.php index a18138f4..8fb22873 100644 --- a/resources/views/business/pages/fund-management/withdrawal-management.blade.php +++ b/resources/views/business/pages/fund-management/withdrawal-management.blade.php @@ -1,28 +1,69 @@ @php $withdrawalImportPreview = session('withdrawalImportPreview'); $withdrawalOpenImport = is_array($withdrawalImportPreview) || $errors->hasAny(['import', 'preview_token']); - $withdrawalQuery = array_filter( + $withdrawalFilterQuery = array_filter( request()->only(['platform', 'withdrawal_path', 'store_entity', 'keyword', 'balance_status']), static fn ($value): bool => filled($value), ); + $withdrawalQuery = array_filter( + request()->only(['platform', 'withdrawal_path', 'store_entity', 'keyword', 'balance_status', 'sort', 'direction']), + static fn ($value): bool => filled($value), + ); + $withdrawalSortUrl = static function (string $field, string $direction) use ($withdrawalFilterQuery): string { + $query = http_build_query(array_merge($withdrawalFilterQuery, [ + 'sort' => $field, + 'direction' => $direction, + ])); + + return request()->url() . '?' . $query; + }; + $withdrawalColumns = [ + ['key' => 'platform', 'label' => '店铺平台', 'numeric' => false, 'title' => null], + ['key' => 'erp_store_name', 'label' => 'ERP店铺名称', 'numeric' => false, 'title' => null], + ['key' => 'store_id', 'label' => '平台店铺ID', 'numeric' => false, 'title' => null], + ['key' => 'store_entity', 'label' => '店铺主体', 'numeric' => false, 'title' => null], + ['key' => 'withdrawal_path', 'label' => '提现路径', 'numeric' => false, 'title' => null], + ['key' => 'withdrawal_account', 'label' => '提现账户名称', 'numeric' => false, 'title' => null], + ['key' => 'bank_card', 'label' => '银行卡号', 'numeric' => false, 'title' => null], + ['key' => 'balance', 'label' => $withdrawalBalanceColumnLabel, 'numeric' => true, 'title' => '余额数据日期:' . $withdrawalBalanceDate], + ['key' => 'reserve_standard', 'label' => '预留标准', 'numeric' => true, 'title' => null], + ['key' => 'withdrawal_amount', 'label' => '提现金额', 'numeric' => true, 'title' => null], + ['key' => 'compliance', 'label' => '是否达标', 'numeric' => false, 'title' => null], + ]; @endphp
-
- - 提现配置 +
+
+ + 提现配置 +
+
+ 余额源 {{ number_format($withdrawalBalanceSummary['source']) }} 条 + 已显示余额 {{ number_format($withdrawalBalanceSummary['matched']) }} 条 + @if ($withdrawalBalanceSummary['ambiguous'] > 0) + 多条匹配 {{ number_format($withdrawalBalanceSummary['ambiguous']) }} 条 + @endif +
-
- 余额源 {{ number_format($withdrawalBalanceSummary['source']) }} 条 - 已显示余额 {{ number_format($withdrawalBalanceSummary['matched']) }} 条 - @if ($withdrawalBalanceSummary['ambiguous'] > 0) - 多条匹配 {{ number_format($withdrawalBalanceSummary['ambiguous']) }} 条 - @endif +
+ + + + 导出 +
+ @if ($withdrawalSort['field'] !== '') + + + @endif
- - - - 导出 - @if (request()->hasAny(['platform', 'withdrawal_path', 'store_entity', 'keyword', 'balance_status'])) 重置 @endif
- +
- - - - - - - - - - - + @foreach ($withdrawalColumns as $column) + + @endforeach @@ -114,17 +170,17 @@ $reserveStandard = $withdrawalReserveStandards[(int) $row->id] ?? null; @endphp - - - - - - - - - - - + + + + + + + + + + + @empty @@ -138,6 +194,42 @@ @endforelse
店铺平台ERP店铺名称平台店铺ID店铺主体提现路径提现账户名称银行卡号{{ $withdrawalBalanceColumnLabel }}预留标准提现金额是否达标 $column['numeric']]) + data-withdrawal-column="{{ $column['key'] }}" + @if ($column['title']) title="{{ $column['title'] }}" @endif + @if ($withdrawalSort['field'] === $column['key']) aria-sort="{{ $withdrawalSort['direction'] === 'asc' ? 'ascending' : 'descending' }}" @endif + > +
+ {{ $column['label'] }} + +
+
{{ $row->store_platform ?: '未配置' }}{{ $row->erp_store_name ?: '-' }}{{ $row->store_id ?: '-' }}{{ $row->store_entity_name ?: '-' }}{{ $row->withdrawal_path ?: '未配置' }}{{ $row->withdrawal_account_name ?: '未配置' }}{{ $bankCard }}{{ $balanceText }}{{ $reserveStandard === null ? '待配置' : number_format($reserveStandard, 2) }}待接入待核验{{ $row->store_platform ?: '未配置' }}{{ $row->erp_store_name ?: '-' }}{{ $row->store_id ?: '-' }}{{ $row->store_entity_name ?: '-' }}{{ $row->withdrawal_path ?: '未配置' }}{{ $row->withdrawal_account_name ?: '未配置' }}{{ $bankCard }}{{ $balanceText }}{{ $reserveStandard === null ? '待配置' : number_format($reserveStandard, 2) }}待接入待核验
+ +
@@ -242,9 +334,36 @@