user->attachPermission( Permission::where('name', 'read-admin-panel')->firstOrFail() ); } public function test_warning_page_contains_after_sales_return_refund_child(): void { $warnings = collect(BusinessNavigation::all()) ->firstWhere('slug', 'other')['pages']; $warnings = collect($warnings)->firstWhere('slug', 'warnings'); $this->assertSame( ['warning-overview', 'after-sales-return-refund'], array_column($warnings['children'], 'slug'), ); $this->assertSame( ['预警总览', '售后退货退款'], array_column($warnings['children'], 'title'), ); } public function test_warning_children_render_and_are_linked_from_the_sidebar(): void { $this->loginAs(); $this->bindReadyRefundMirror(); $overviewUrl = route('business.pages.show', [ 'section' => 'other', 'page' => 'warning-overview', ]); $afterSalesRefundUrl = route('business.pages.show', [ 'section' => 'other', 'page' => 'after-sales-return-refund', ]); $this->get(route('business.pages.show', [ 'section' => 'other', 'page' => 'warnings', ])) ->assertRedirect($overviewUrl); $this->get($overviewUrl) ->assertOk() ->assertSeeText('预警总览') ->assertSeeText('售后退货退款') ->assertSeeText('风险等级') ->assertSee($afterSalesRefundUrl, false); $this->get($afterSalesRefundUrl) ->assertOk() ->assertSeeText('售后退货退款') ->assertSeeText('聚水潭售后数据已接入') ->assertSeeText('AS-WARNING-001') ->assertSeeText('WB-WARNING-001') ->assertSeeText('售后测试店') ->assertSeeText('退款总金额') ->assertSeeText('运单号') ->assertSee('data-refund-page="after-sales-warning"', false) ->assertSee('action="' . $afterSalesRefundUrl . '"', false) ->assertSee('name="waybill_no"', false) ->assertSee(route('business.other.after-sales-return-refund.export'), false) ->assertDontSee(route('business.refunds.non-jingxi-refund-data.export'), false) ->assertDontSeeText('售后退货退款预警规则待接入'); } public function test_existing_warning_page_permission_keeps_both_child_entries(): void { $regular = $this->regularAccount(); $subject = 'user_' . $regular->id; setting()->set('system.sidebar_permissions', [ 'configured' => [$subject], 'subjects' => [$subject => ['other/warnings']], ]); $other = collect(SidebarPermissions::navigationFor($regular)) ->firstWhere('slug', 'other'); $warnings = collect($other['pages'])->firstWhere('slug', 'warnings'); $this->assertSame( ['warning-overview', 'after-sales-return-refund'], array_column($warnings['children'], 'slug'), ); $this->bindReadyRefundMirror(); $this->loginAs($regular) ->get(route('business.pages.show', [ 'section' => 'other', 'page' => 'after-sales-return-refund', ])) ->assertOk() ->assertSeeText('聚水潭售后数据已接入') ->assertSeeText('AS-WARNING-001'); } public function test_warning_page_exports_filtered_rows_without_refund_page_permission(): void { $regular = $this->regularAccount(); $subject = 'user_' . $regular->id; setting()->set('system.sidebar_permissions', [ 'configured' => [$subject], 'subjects' => [$subject => ['other/warnings']], ]); $filters = [ 'from' => '2026-10-23', 'to' => '2026-10-29', 'order_no' => 'ORDER-WARNING-001', 'waybill_no' => 'WB-WARNING-001', 'shop_name' => '售后测试店', 'sku_id' => 'SKU-WARNING-001', ]; $repository = \Mockery::mock(NonJingxiRefundMirrorRepository::class); $repository->shouldReceive('available', 'ready', 'queryReady')->andReturnTrue(); $repository->shouldReceive('exportRows')->once()->with($filters)->andReturn([(object) [ 'Id' => 9001, 'refundDate' => '2026-10-28 10:00:00.123456', 'waybillNo' => 'WB-WARNING-001', 'outer_as_id' => 'AS-WARNING-001', 'shop_name' => '售后测试店', 'sku_id' => 'SKU-WARNING-001', ]]); $this->app->instance(NonJingxiRefundMirrorRepository::class, $repository); $response = $this->loginAs($regular)->get(route( 'business.other.after-sales-return-refund.export', $filters + ['columns' => 'refundDate,waybillNo,outer_as_id,shop_name,sku_id'], )); $response->assertOk()->assertDownload( 'after_sales_refund_warning_2026-10-23_2026-10-29.csv' ); $content = $response->streamedContent(); $lines = preg_split('/\r?\n/', trim($content)); $this->assertSame( ['退款日期', '运单号', '外部售后单号', '店铺名称', 'SKU编码'], str_getcsv(ltrim($lines[0], "\xEF\xBB\xBF")), ); $this->assertSame( ['2026-10-28 10:00:00', "'WB-WARNING-001", "'AS-WARNING-001", '售后测试店', "'SKU-WARNING-001"], str_getcsv($lines[1]), ); } private function regularAccount(): User { $name = 'fixture-warning-page-' . User::count(); $user = User::create([ 'name' => $name, 'email' => $name . '@example.test', 'password' => 'fixture-unused-password', 'enabled' => true, 'locale' => 'zh-CN', 'landing_page' => 'dashboard', ]); $user->roles()->attach(Role::where('name', 'user')->firstOrFail()->id); $user->permissions()->attach(Permission::where('name', 'read-admin-panel')->firstOrFail()->id); $user->companies()->attach($this->company->id); return $user; } private function bindReadyRefundMirror(): void { $states = [ NonJingxiRefundMirrorRepository::INFO_DATASET => [ 'status' => 'succeeded', 'initial_completed_at' => '2026-10-01 00:00:00', ], NonJingxiRefundMirrorRepository::ITEM_DATASET => [ 'status' => 'succeeded', 'initial_completed_at' => '2026-10-01 00:00:00', ], ]; $repository = \Mockery::mock(NonJingxiRefundMirrorRepository::class); $repository->shouldReceive('available')->once()->andReturnTrue(); $repository->shouldReceive('states')->once()->andReturn($states); $repository->shouldReceive('syncStatus')->once()->with($states)->andReturn([ 'code' => 'healthy', 'last_succeeded_at' => '2026-10-29 09:00:00', ]); $repository->shouldReceive('ready')->once()->andReturnTrue(); $repository->shouldReceive('queryReady')->once()->andReturnTrue(); $repository->shouldReceive('paginate')->once()->andReturn([ 'data' => [(object) [ 'Id' => 9001, 'refundDate' => '2026-10-28 10:00:00', 'dataSource' => '售后资料', 'detailStatus' => '已关联', 'as_id' => 8001, 'outer_as_id' => 'AS-WARNING-001', 'waybillNo' => 'WB-WARNING-001', 'o_id' => 'ORDER-WARNING-001', 'so_id' => 'SUB-WARNING-001', 'type' => '退货退款', 'shop_name' => '售后测试店', 'platform' => '天猫', 'totalRefundMoney' => 128.5, 'sku_id' => 'SKU-WARNING-001', 'detailRefundMoney' => 128.5, 'refundQty' => 1, 'status' => '退款完成', ]], 'total' => 1, ]); $this->app->instance(NonJingxiRefundMirrorRepository::class, $repository); } }