From 8b4c698b151e75ed3d5adfa5df72294843e3bab4 Mon Sep 17 00:00:00 2001 From: yueyang <2495544247@qq.com> Date: Mon, 26 Oct 2026 18:24:46 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=E6=B5=81=E7=A8=8B=E6=98=8E=E7=BB=86?= =?UTF-8?q?=E7=B2=BE=E7=A1=AE=E8=B7=AF=E7=94=B1=E9=81=BF=E5=85=8D=E7=A9=BA?= =?UTF-8?q?=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/EcommerceFinance/Routes/admin.php | 18 --------------- routes/admin.php | 13 +++++++++++ .../Business/ProcessDetailsPageTest.php | 23 +++++++++++++++++++ 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/modules/EcommerceFinance/Routes/admin.php b/modules/EcommerceFinance/Routes/admin.php index d128dda9..5a08c09f 100644 --- a/modules/EcommerceFinance/Routes/admin.php +++ b/modules/EcommerceFinance/Routes/admin.php @@ -1,25 +1,7 @@ name('index'); }); - -// 通用 workspace/{section}/{page} 路由在 App\Providers\Route::boot() 中注册; -// 模块路由在 register 阶段先注册,因此这里的精确路由会优先命中。沿用 -// business.pages.show 名称,侧边栏仍能生成与通用页面一致的可访问 URL。 -Route::admin('ecommerce-finance', function (): void { - Route::get('workspace/financial-operations/process-details', [ProcessDetails::class, 'show']) - ->middleware('business.page.viewer') - ->defaults('section', 'financial-operations') - ->defaults('page', 'process-details') - ->name('business.pages.show'); - - Route::get('workspace/financial-operations/process-details/export', [ProcessDetails::class, 'export']) - ->middleware('business.page.viewer') - ->defaults('section', 'financial-operations') - ->defaults('page', 'process-details') - ->name('business.process-details.export'); -}, ['prefix' => '', 'as' => '']); diff --git a/routes/admin.php b/routes/admin.php index b4899f31..ae8bed54 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -22,6 +22,7 @@ use Modules\EcommerceFinance\Http\Controllers\AccountBills; use Modules\EcommerceFinance\Http\Controllers\AlipayBills; use Modules\EcommerceFinance\Http\Controllers\DewuAccountBills; use Modules\EcommerceFinance\Http\Controllers\PinduoduoAccountBills; +use Modules\EcommerceFinance\Http\Controllers\ProcessDetails; use Modules\EcommerceFinance\Http\Controllers\TaobaoWechatAccountBills; use Modules\EcommerceFinance\Http\Controllers\Api\GoodsReconciliations; use Modules\EcommerceFinance\Http\Controllers\Api\SupplierPayables; @@ -819,7 +820,19 @@ Route::get('workspace/financial-ledger/{page?}/{tab?}', function (Request $reque })->name('business.financial-ledger.legacy'); Route::middleware('business.page.viewer')->group(function (): void { + Route::get('workspace/financial-operations/process-details', [ProcessDetails::class, 'show']) + ->defaults('section', 'financial-operations') + ->defaults('page', 'process-details') + ->name('business.pages.show'); + + Route::get('workspace/financial-operations/process-details/export', [ProcessDetails::class, 'export']) + ->defaults('section', 'financial-operations') + ->defaults('page', 'process-details') + ->name('business.process-details.export'); + Route::get('workspace/{section}/{page?}/{tab?}', 'Business\Pages@show') + // 流程明细使用上面的精确 GET 路由;通用路由不能再次抢占并渲染空表。 + ->where('page', '^(?!process-details$).*') ->name('business.pages.show'); Route::post('workspace/{section}/{page}/import/{tab?}', 'Business\Pages@import') diff --git a/tests/Feature/Business/ProcessDetailsPageTest.php b/tests/Feature/Business/ProcessDetailsPageTest.php index 3877a7a7..fa013cbb 100644 --- a/tests/Feature/Business/ProcessDetailsPageTest.php +++ b/tests/Feature/Business/ProcessDetailsPageTest.php @@ -3,13 +3,36 @@ namespace Tests\Feature\Business; use App\Models\Auth\Permission; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Route; use Mockery; +use Modules\EcommerceFinance\Http\Controllers\ProcessDetails; use Modules\EcommerceFinance\Repositories\DingProcessDetailMirrorRepository; use RuntimeException; use Tests\Feature\FeatureTestCase; class ProcessDetailsPageTest extends FeatureTestCase { + public function test_process_details_route_is_not_shadowed_by_generic_workspace_page(): void + { + $genericRoute = collect(Route::getRoutes()->getRoutes())->first( + static fn ($route): bool => $route->uri() === '{company_id}/workspace/{section}/{page?}/{tab?}', + ); + + $this->assertNotNull($genericRoute); + $this->assertArrayHasKey('page', $genericRoute->wheres); + $this->assertSame( + 0, + preg_match('#' . $genericRoute->wheres['page'] . '#', 'process-details'), + ); + + $matched = Route::getRoutes()->match(Request::create( + '/1/workspace/financial-operations/process-details', + )); + + $this->assertSame(ProcessDetails::class . '@show', $matched->getActionName()); + } + public function test_page_renders_process_detail_rows_and_filters(): void { $this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail()); -- 2.47.2 From 9b15f8f10edae1a43a65abcc7e3719900b9d4b40 Mon Sep 17 00:00:00 2001 From: yueyang <2495544247@qq.com> Date: Mon, 26 Oct 2026 18:38:55 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E6=B5=81=E7=A8=8B=E6=98=8E=E7=BB=86?= =?UTF-8?q?=E9=80=9A=E7=94=A8=E5=85=A5=E5=8F=A3=E5=9B=9E=E6=9F=A5=E9=95=9C?= =?UTF-8?q?=E5=83=8F=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/EcommerceFinance/Routes/admin.php | 18 ++++ .../View/ProcessDetailsPageData.php | 86 +++++++++++++++++++ .../process-details.blade.php | 15 +++- routes/admin.php | 13 --- .../Business/ProcessDetailsPageTest.php | 23 ----- .../ProcessDetailsPageDataTest.php | 62 +++++++++++++ 6 files changed, 177 insertions(+), 40 deletions(-) create mode 100644 modules/EcommerceFinance/View/ProcessDetailsPageData.php create mode 100644 tests/Unit/EcommerceFinance/ProcessDetailsPageDataTest.php diff --git a/modules/EcommerceFinance/Routes/admin.php b/modules/EcommerceFinance/Routes/admin.php index 5a08c09f..d128dda9 100644 --- a/modules/EcommerceFinance/Routes/admin.php +++ b/modules/EcommerceFinance/Routes/admin.php @@ -1,7 +1,25 @@ name('index'); }); + +// 通用 workspace/{section}/{page} 路由在 App\Providers\Route::boot() 中注册; +// 模块路由在 register 阶段先注册,因此这里的精确路由会优先命中。沿用 +// business.pages.show 名称,侧边栏仍能生成与通用页面一致的可访问 URL。 +Route::admin('ecommerce-finance', function (): void { + Route::get('workspace/financial-operations/process-details', [ProcessDetails::class, 'show']) + ->middleware('business.page.viewer') + ->defaults('section', 'financial-operations') + ->defaults('page', 'process-details') + ->name('business.pages.show'); + + Route::get('workspace/financial-operations/process-details/export', [ProcessDetails::class, 'export']) + ->middleware('business.page.viewer') + ->defaults('section', 'financial-operations') + ->defaults('page', 'process-details') + ->name('business.process-details.export'); +}, ['prefix' => '', 'as' => '']); diff --git a/modules/EcommerceFinance/View/ProcessDetailsPageData.php b/modules/EcommerceFinance/View/ProcessDetailsPageData.php new file mode 100644 index 00000000..4e644d5e --- /dev/null +++ b/modules/EcommerceFinance/View/ProcessDetailsPageData.php @@ -0,0 +1,86 @@ + */ + public function page(Request $request): array + { + $filters = $this->filters($request); + $page = max((int) $request->query('page', 1), 1); + $perPage = 50; + + try { + $result = $this->repository->paginate($filters, $page, $perPage); + $options = $this->repository->filterOptions(); + + return [ + 'processRows' => new LengthAwarePaginator( + $result['data'], + $result['total'], + $perPage, + $page, + ['path' => $request->url(), 'query' => $request->except('page')], + ), + 'filters' => $filters, + 'summary' => $result['summary'], + 'months' => $options['months'], + 'documentTypes' => $options['document_types'], + 'statuses' => $options['statuses'], + 'mirrorStatus' => $result['status'], + 'mirrorSyncedAt' => $result['last_synced_at'], + 'scope' => $result['scope'], + 'error' => null, + ]; + } catch (Throwable $exception) { + report($exception); + + return [ + 'processRows' => new LengthAwarePaginator([], 0, $perPage, $page, [ + 'path' => $request->url(), + 'query' => $request->except('page'), + ]), + 'filters' => $filters, + 'summary' => [ + 'rows' => 0, + 'approvals' => 0, + 'amount_total' => 0.0, + 'bank_amount_total' => 0.0, + ], + 'months' => [], + 'documentTypes' => [], + 'statuses' => [], + 'mirrorStatus' => 'unavailable', + 'mirrorSyncedAt' => null, + 'scope' => $this->repository->scope(), + 'error' => '流程明细镜像尚未完成导入,请稍后重试。', + ]; + } + } + + /** @return array */ + private function filters(Request $request): array + { + $month = trim((string) $request->query('month', '')); + $status = trim((string) $request->query('status', '')); + + return [ + 'month' => preg_match('/^\d{4}-\d{2}$/', $month) === 1 ? $month : '', + 'document_type' => mb_substr(trim((string) $request->query('document_type', '')), 0, 100), + 'status' => mb_substr($status, 0, 20), + 'business_id' => mb_substr(trim((string) $request->query('business_id', '')), 0, 32), + 'originator_name' => mb_substr(trim((string) $request->query('originator_name', '')), 0, 100), + 'keyword' => mb_substr(trim((string) $request->query('keyword', '')), 0, 100), + ]; + } +} diff --git a/resources/views/business/pages/financial-operations/process-details.blade.php b/resources/views/business/pages/financial-operations/process-details.blade.php index 2064d35f..7fda65f4 100644 --- a/resources/views/business/pages/financial-operations/process-details.blade.php +++ b/resources/views/business/pages/financial-operations/process-details.blade.php @@ -1,10 +1,17 @@ @php $processRows = $processRows ?? null; if (! $processRows instanceof \Illuminate\Contracts\Pagination\LengthAwarePaginator) { - $fallbackRows = $rows ?? null; - $processRows = $fallbackRows instanceof \Illuminate\Contracts\Pagination\LengthAwarePaginator - ? $fallbackRows - : new \Illuminate\Pagination\LengthAwarePaginator([], 0, 50); + $pageData = app(\Modules\EcommerceFinance\View\ProcessDetailsPageData::class)->page(request()); + $processRows = $pageData['processRows']; + $filters = $pageData['filters']; + $summary = $pageData['summary']; + $months = $pageData['months']; + $documentTypes = $pageData['documentTypes']; + $statuses = $pageData['statuses']; + $mirrorStatus = $pageData['mirrorStatus']; + $mirrorSyncedAt = $pageData['mirrorSyncedAt']; + $scope = $pageData['scope']; + $error = $pageData['error']; } $filters = $filters ?? []; $summary = $summary ?? []; diff --git a/routes/admin.php b/routes/admin.php index ae8bed54..b4899f31 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -22,7 +22,6 @@ use Modules\EcommerceFinance\Http\Controllers\AccountBills; use Modules\EcommerceFinance\Http\Controllers\AlipayBills; use Modules\EcommerceFinance\Http\Controllers\DewuAccountBills; use Modules\EcommerceFinance\Http\Controllers\PinduoduoAccountBills; -use Modules\EcommerceFinance\Http\Controllers\ProcessDetails; use Modules\EcommerceFinance\Http\Controllers\TaobaoWechatAccountBills; use Modules\EcommerceFinance\Http\Controllers\Api\GoodsReconciliations; use Modules\EcommerceFinance\Http\Controllers\Api\SupplierPayables; @@ -820,19 +819,7 @@ Route::get('workspace/financial-ledger/{page?}/{tab?}', function (Request $reque })->name('business.financial-ledger.legacy'); Route::middleware('business.page.viewer')->group(function (): void { - Route::get('workspace/financial-operations/process-details', [ProcessDetails::class, 'show']) - ->defaults('section', 'financial-operations') - ->defaults('page', 'process-details') - ->name('business.pages.show'); - - Route::get('workspace/financial-operations/process-details/export', [ProcessDetails::class, 'export']) - ->defaults('section', 'financial-operations') - ->defaults('page', 'process-details') - ->name('business.process-details.export'); - Route::get('workspace/{section}/{page?}/{tab?}', 'Business\Pages@show') - // 流程明细使用上面的精确 GET 路由;通用路由不能再次抢占并渲染空表。 - ->where('page', '^(?!process-details$).*') ->name('business.pages.show'); Route::post('workspace/{section}/{page}/import/{tab?}', 'Business\Pages@import') diff --git a/tests/Feature/Business/ProcessDetailsPageTest.php b/tests/Feature/Business/ProcessDetailsPageTest.php index fa013cbb..3877a7a7 100644 --- a/tests/Feature/Business/ProcessDetailsPageTest.php +++ b/tests/Feature/Business/ProcessDetailsPageTest.php @@ -3,36 +3,13 @@ namespace Tests\Feature\Business; use App\Models\Auth\Permission; -use Illuminate\Http\Request; -use Illuminate\Support\Facades\Route; use Mockery; -use Modules\EcommerceFinance\Http\Controllers\ProcessDetails; use Modules\EcommerceFinance\Repositories\DingProcessDetailMirrorRepository; use RuntimeException; use Tests\Feature\FeatureTestCase; class ProcessDetailsPageTest extends FeatureTestCase { - public function test_process_details_route_is_not_shadowed_by_generic_workspace_page(): void - { - $genericRoute = collect(Route::getRoutes()->getRoutes())->first( - static fn ($route): bool => $route->uri() === '{company_id}/workspace/{section}/{page?}/{tab?}', - ); - - $this->assertNotNull($genericRoute); - $this->assertArrayHasKey('page', $genericRoute->wheres); - $this->assertSame( - 0, - preg_match('#' . $genericRoute->wheres['page'] . '#', 'process-details'), - ); - - $matched = Route::getRoutes()->match(Request::create( - '/1/workspace/financial-operations/process-details', - )); - - $this->assertSame(ProcessDetails::class . '@show', $matched->getActionName()); - } - public function test_page_renders_process_detail_rows_and_filters(): void { $this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail()); diff --git a/tests/Unit/EcommerceFinance/ProcessDetailsPageDataTest.php b/tests/Unit/EcommerceFinance/ProcessDetailsPageDataTest.php new file mode 100644 index 00000000..c5adf0a6 --- /dev/null +++ b/tests/Unit/EcommerceFinance/ProcessDetailsPageDataTest.php @@ -0,0 +1,62 @@ + '202610241052000241951', + 'line_no' => 1, + 'status' => '审批中', + ]; + + $repository = $this->createMock(DingProcessDetailMirrorRepository::class); + $repository->expects($this->once()) + ->method('paginate') + ->with($this->callback(static fn (array $filters): bool => $filters['month'] === '2026-10'), 2, 50) + ->willReturn([ + 'data' => [$row], + 'total' => 11331, + 'summary' => [ + 'rows' => 11331, + 'approvals' => 4775, + 'amount_total' => 225953332.98, + 'bank_amount_total' => 225862410.66, + ], + 'status' => 'succeeded', + 'last_synced_at' => '2026-10-26 16:55:11', + 'scope' => 'personal_permission', + ]); + $repository->expects($this->once()) + ->method('filterOptions') + ->willReturn([ + 'months' => ['2026-10'], + 'document_types' => ['种菜核销/种菜报销'], + 'statuses' => ['审批中'], + ]); + + $data = (new ProcessDetailsPageData($repository))->page(Request::create( + '/1/workspace/financial-operations/process-details', + 'GET', + ['month' => '2026-10', 'page' => 2], + )); + + $this->assertInstanceOf(LengthAwarePaginator::class, $data['processRows']); + $this->assertSame(11331, $data['processRows']->total()); + $this->assertSame(50, $data['processRows']->perPage()); + $this->assertSame(2, $data['processRows']->currentPage()); + $this->assertSame($row, $data['processRows']->items()[0]); + $this->assertSame('succeeded', $data['mirrorStatus']); + $this->assertSame(11331, $data['summary']['rows']); + $this->assertSame(['2026-10'], $data['months']); + $this->assertNull($data['error']); + } +} -- 2.47.2