From 38f4134d241601d07c3c95adb0bc0bb374d14828 Mon Sep 17 00:00:00 2001 From: chenxi Date: Tue, 1 Sep 2026 16:08:36 +0800 Subject: [PATCH] feat: add installation details export --- app/Http/Controllers/Business/Pages.php | 44 +++++++++++++++++++ resources/views/business/pages/show.blade.php | 3 +- routes/admin.php | 3 ++ tests/Feature/Business/ManagementCrudTest.php | 23 +++++++++- 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Business/Pages.php b/app/Http/Controllers/Business/Pages.php index fab8a8fe..8c64d641 100644 --- a/app/Http/Controllers/Business/Pages.php +++ b/app/Http/Controllers/Business/Pages.php @@ -10,6 +10,7 @@ use App\Business\StoreReceivingAccountWorkbookImporter; use App\Models\Banking\MarketingAccountBill; use App\Models\Business\CourierAgreementSignature; use App\Models\Business\CourierSigningSubject; +use App\Models\Business\InstallationDetail; use App\Models\StoreReceivables\StoreZhongcaiReconciliation; use App\Utilities\SidebarPermissions; use Illuminate\Http\Request; @@ -184,6 +185,49 @@ class Pages extends Controller return response()->json(['data' => $payload['data'], 'has_more' => $payload['has_more'] ?? false]); } + public function exportInstallationDetails(Request $request): StreamedResponse + { + $section = collect(SidebarPermissions::navigationFor(user()))->firstWhere('slug', 'financial-operations'); + abort_unless($section && collect($section['pages'] ?? [])->contains('slug', 'installation-details'), 403, '当前账号没有该页面权限。'); + + $filters = InstallationDetailMirrorRepository::requestFilters(); + $firstPage = $this->installationDetails->paginate(1, 100, $filters); + abort_if(! $firstPage['ready'], 503, '安装明细镜像尚未完成首次同步'); + $labels = InstallationDetail::columnLabels(); + + return response()->streamDownload(function () use ($filters, $firstPage, $labels): void { + $csv = fopen('php://output', 'w'); + if ($csv === false) { + throw new \RuntimeException('无法创建安装明细导出流。'); + } + + fputs($csv, chr(0xEF) . chr(0xBB) . chr(0xBF)); + fputcsv($csv, array_values($labels)); + + $page = 1; + $result = $firstPage; + do { + foreach ($result['data'] as $row) { + fputcsv($csv, array_map( + fn (string $name): string => $this->csvText($row[$name] ?? ''), + array_keys($labels), + )); + } + + $page++; + $hasMore = ($page - 1) * 100 < $result['total']; + if ($hasMore) { + $result = $this->installationDetails->paginate($page, 100, $filters); + } + } while ($hasMore); + + fclose($csv); + }, '安装明细_' . now()->format('Ymd_His') . '.csv', [ + 'Content-Type' => 'text/csv; charset=UTF-8', + 'Cache-Control' => 'private, no-store', + ]); + } + protected function viewData( array $section, ?array $page = null, diff --git a/resources/views/business/pages/show.blade.php b/resources/views/business/pages/show.blade.php index 364c2002..416d3a0f 100644 --- a/resources/views/business/pages/show.blade.php +++ b/resources/views/business/pages/show.blade.php @@ -636,7 +636,7 @@
@php $filterFields = [ - ['name' => 'created_from', 'label' => '提交日期:', 'type' => 'date'], ['name' => 'created_to', 'label' => '至:', 'type' => 'date'], + ['name' => 'created_from', 'label' => '创建日期:', 'type' => 'date'], ['name' => 'created_to', 'label' => '至:', 'type' => 'date'], ['name' => 'customer_review_from', 'label' => '客服审核时间:', 'type' => 'date'], ['name' => 'customer_review_to', 'label' => '至:', 'type' => 'date'], ['name' => 'finance_review_from', 'label' => '财务审核时间:', 'type' => 'date'], ['name' => 'finance_review_to', 'label' => '至:', 'type' => 'date'], ['name' => 'push_status', 'label' => '推送状态:'], ['name' => 'order_no', 'label' => '订单编号:'], @@ -661,6 +661,7 @@
清除 + 导出
@endif diff --git a/routes/admin.php b/routes/admin.php index a30991a3..dcaf8838 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -124,6 +124,9 @@ Route::get('workspace/financial-operations/installation-details/details/{guid}', ->where('guid', '[A-Za-z0-9-]+') ->name('business.installation-details.details'); +Route::get('workspace/financial-operations/installation-details/export', 'Business\Pages@exportInstallationDetails') + ->name('business.installation-details.export'); + Route::get('workspace/{section}/{page?}/{tab?}', 'Business\Pages@show') ->name('business.pages.show'); diff --git a/tests/Feature/Business/ManagementCrudTest.php b/tests/Feature/Business/ManagementCrudTest.php index c8aa0277..8777090b 100644 --- a/tests/Feature/Business/ManagementCrudTest.php +++ b/tests/Feature/Business/ManagementCrudTest.php @@ -79,7 +79,7 @@ class ManagementCrudTest extends FeatureTestCase ->assertSeeText('安装单号') ->assertSeeText('申请总金额') ->assertSeeText('商品名称(1)') - ->assertSeeText('提交日期:') + ->assertSeeText('创建日期:') ->assertSeeText('客服审核时间:') ->assertSeeText('财务审核时间:') ->assertSeeText('推送状态:') @@ -87,12 +87,33 @@ class ManagementCrudTest extends FeatureTestCase ->assertSeeText('选择删除:') ->assertSeeText('订单类型:') ->assertSeeText('商品ID:') + ->assertSeeText('导出') ->assertSeeText('INSTALL-TEST-001') ->assertSeeText('测试安装公司') ->assertSeeText('120.50') ->assertSeeText('共 1 条'); } + public function testInstallationDetailsExportUsesFiltersAndMirrorRows(): void + { + $row = array_fill_keys(array_keys(InstallationDetail::columnLabels()), null); + $row['installation_no'] = 'INSTALL-EXPORT-001'; + $mirror = Mockery::mock(InstallationDetailMirrorRepository::class); + $mirror->shouldReceive('paginate')->once()->with(1, 100, ['created_from' => '2026-08-01'])->andReturn([ + 'ready' => true, + 'total' => 1, + 'data' => [$row], + ]); + $this->app->instance(InstallationDetailMirrorRepository::class, $mirror); + + $response = $this->loginAs()->get(route('business.installation-details.export', [ + 'created_from' => '2026-08-01', + ])); + + $response->assertOk()->assertHeader('content-type', 'text/csv; charset=UTF-8'); + $this->assertStringContainsString('INSTALL-EXPORT-001', $response->streamedContent()); + } + public function testInstallationDetailsEndpointReturnsChildItemsForMainRecord(): void { $mirror = Mockery::mock(InstallationDetailMirrorRepository::class); -- 2.47.2