From 3f421f5ca794a40a021c8cd5058282dc98f4d6ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E6=9C=A8?= <594326199@qq.com> Date: Wed, 28 Oct 2026 17:00:26 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=94=AE=E5=90=8E?= =?UTF-8?q?=E9=80=80=E8=B4=A7=E9=80=80=E6=AC=BE=E9=A2=84=E8=AD=A6=E5=AD=90?= =?UTF-8?q?=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/Business/Pages.php | 1 + app/Utilities/SidebarPermissions.php | 6 +- config/business_navigation.php | 53 ++++---- .../other/after-sales-return-refund.blade.php | 13 ++ tests/Feature/Business/ManagementCrudTest.php | 15 ++- tests/Feature/Business/WarningPageTest.php | 118 ++++++++++++++++++ 6 files changed, 183 insertions(+), 23 deletions(-) create mode 100644 resources/views/business/pages/other/after-sales-return-refund.blade.php create mode 100644 tests/Feature/Business/WarningPageTest.php diff --git a/app/Http/Controllers/Business/Pages.php b/app/Http/Controllers/Business/Pages.php index e9f76b34..6c10ff3e 100644 --- a/app/Http/Controllers/Business/Pages.php +++ b/app/Http/Controllers/Business/Pages.php @@ -4705,6 +4705,7 @@ class Pages extends Controller 'withdrawal-management' => $this->contentForWithdrawalManagement(), 'systemization-todos' => app(\App\Http\Controllers\Business\SystemizationTodos::class)->page(), 'todos' => $this->contentForTodos(), + 'after-sales-return-refund' => ['view' => 'business.pages.other.after-sales-return-refund'], 'bill-offset' => app(\Modules\EcommerceFinance\View\BillOffsetPageData::class)->page(), 'account-mapping' => $this->contentForAccountMapping(), 'ledger' => app(\Modules\EcommerceFinance\View\FinanceLedgerPageData::class)->page($tab), diff --git a/app/Utilities/SidebarPermissions.php b/app/Utilities/SidebarPermissions.php index 3365a54e..435c8001 100644 --- a/app/Utilities/SidebarPermissions.php +++ b/app/Utilities/SidebarPermissions.php @@ -127,8 +127,12 @@ class SidebarPermissions $pageKey = $sectionKey . '/' . $page['slug']; if (in_array($pageKey, $allowed, true)) { - // 若有 children,保留 children 的精确权限过滤 + // 页面拆分为子页时,可显式沿用父页授权,避免旧权限配置丢失入口。 if (! empty($page['children'])) { + if (($page['children_inherit_parent_access'] ?? false) === true) { + return $page; + } + $page['children'] = array_values(array_filter($page['children'], function (array $child) use ($allowed, $sectionKey) { return in_array($sectionKey . '/' . $child['slug'], $allowed, true); })); diff --git a/config/business_navigation.php b/config/business_navigation.php index ff584459..5782f182 100644 --- a/config/business_navigation.php +++ b/config/business_navigation.php @@ -1071,27 +1071,38 @@ $navigation = [ [ 'slug' => 'warnings', 'title' => '预警页面', - 'directory' => [ - 'icon' => 'warning', - 'status' => '尚未同步', - 'description' => '集中展示销量、税务、主体、人员、商标和续签风险,并可关联待办编号追踪处理结果。', - 'empty' => '预警数据尚未同步。完成规则引擎和预警来源接入后,这里会显示风险记录。', - 'fields' => [ - ['name' => 'warning_number', 'label' => '预警编号'], - ['name' => 'warning_type', 'label' => '预警类型'], - ['name' => 'risk_level', 'label' => '风险等级'], - ['name' => 'related_object', 'label' => '关联对象'], - ['name' => 'risk_description', 'label' => '风险描述'], - ['name' => 'impact_amount', 'label' => '影响金额', 'kind' => 'number'], - ['name' => 'trigger_rule', 'label' => '触发规则'], - ['name' => 'triggered_at', 'label' => '触发时间', 'kind' => 'date'], - ['name' => 'owner', 'label' => '责任人'], - ['name' => 'collaborator', 'label' => '协同人'], - ['name' => 'due_at', 'label' => '截止时间', 'kind' => 'date'], - ['name' => 'todo_number', 'label' => '待办编号'], - ['name' => 'status', 'label' => '处理状态'], - ['name' => 'evidence', 'label' => '处理证据'], - ['name' => 'closed_by_date', 'label' => '关闭人/日期'], + 'children_inherit_parent_access' => true, + 'children' => [ + [ + 'slug' => 'warning-overview', + 'title' => '预警总览', + 'directory' => [ + 'icon' => 'warning', + 'status' => '尚未同步', + 'description' => '集中展示销量、税务、主体、人员、商标和续签风险,并可关联待办编号追踪处理结果。', + 'empty' => '预警数据尚未同步。完成规则引擎和预警来源接入后,这里会显示风险记录。', + 'fields' => [ + ['name' => 'warning_number', 'label' => '预警编号'], + ['name' => 'warning_type', 'label' => '预警类型'], + ['name' => 'risk_level', 'label' => '风险等级'], + ['name' => 'related_object', 'label' => '关联对象'], + ['name' => 'risk_description', 'label' => '风险描述'], + ['name' => 'impact_amount', 'label' => '影响金额', 'kind' => 'number'], + ['name' => 'trigger_rule', 'label' => '触发规则'], + ['name' => 'triggered_at', 'label' => '触发时间', 'kind' => 'date'], + ['name' => 'owner', 'label' => '责任人'], + ['name' => 'collaborator', 'label' => '协同人'], + ['name' => 'due_at', 'label' => '截止时间', 'kind' => 'date'], + ['name' => 'todo_number', 'label' => '待办编号'], + ['name' => 'status', 'label' => '处理状态'], + ['name' => 'evidence', 'label' => '处理证据'], + ['name' => 'closed_by_date', 'label' => '关闭人/日期'], + ], + ], + ], + [ + 'slug' => 'after-sales-return-refund', + 'title' => '售后退货退款', ], ], ], diff --git a/resources/views/business/pages/other/after-sales-return-refund.blade.php b/resources/views/business/pages/other/after-sales-return-refund.blade.php new file mode 100644 index 00000000..f7fbece1 --- /dev/null +++ b/resources/views/business/pages/other/after-sales-return-refund.blade.php @@ -0,0 +1,13 @@ +@include('business.pages.negative-alerts._placeholder', [ + 'icon' => 'assignment_return', + 'scopeLabel' => '售后退货退款', + 'statusTitle' => '售后退货退款预警规则待接入', + 'description' => '页面入口已建立。待确认售后退货、退款及售后差异的判定口径和取数来源后,这里将集中展示需要财务跟进的预警记录。', + 'scopeItems' => [ + ['label' => '预警范围', 'value' => '退货、退款及售后差异'], + ['label' => '判定规则', 'value' => '待确认'], + ['label' => '数据来源', 'value' => '待接入'], + ], + 'emptyTitle' => '接入后在这里查看售后退货退款预警', + 'emptyDescription' => '预警将保留订单、店铺、金额、原因、责任人和处理结果,便于财务核对与追踪。', +]) diff --git a/tests/Feature/Business/ManagementCrudTest.php b/tests/Feature/Business/ManagementCrudTest.php index 2f2b5c9a..704d59f9 100644 --- a/tests/Feature/Business/ManagementCrudTest.php +++ b/tests/Feature/Business/ManagementCrudTest.php @@ -777,9 +777,22 @@ class ManagementCrudTest extends FeatureTestCase ['systemization-todos', 'todos', 'warnings'], array_column($sections['other']['pages'], 'slug') ); + $warnings = collect($sections['other']['pages'])->firstWhere('slug', 'warnings'); + $this->assertSame( + ['warning-overview', 'after-sales-return-refund'], + array_column($warnings['children'], 'slug') + ); + $this->assertSame( + ['预警总览', '售后退货退款'], + array_column($warnings['children'], 'title') + ); foreach (['company-management', 'trademark-management', 'store-management', 'other'] as $sectionSlug) { foreach ($sections[$sectionSlug]['pages'] as $page) { + if (! empty($page['children']) || $page['slug'] === 'after-sales-return-refund') { + continue; + } + $this->assertArrayHasKey('directory', $page, "{$sectionSlug}/{$page['slug']} 缺少目录字段契约"); foreach ($page['directory']['fields'] as $field) { @@ -851,7 +864,7 @@ class ManagementCrudTest extends FeatureTestCase ['section' => 'store-management', 'page' => 'change-progress', 'headers' => ['接替公司', '完成时间', '办理人']], ['section' => 'other', 'page' => 'systemization-todos', 'headers' => ['类型', '内容', '待办事项', '总负责人', '责任人', '状态', '完成截图']], ['section' => 'other', 'page' => 'todos', 'headers' => ['待办编号', '是否逾期', '关闭日期']], - ['section' => 'other', 'page' => 'warnings', 'headers' => ['风险等级', '影响金额', '处理证据']], + ['section' => 'other', 'page' => 'warning-overview', 'headers' => ['风险等级', '影响金额', '处理证据']], ]; foreach ($expectedHeaders as $page) { diff --git a/tests/Feature/Business/WarningPageTest.php b/tests/Feature/Business/WarningPageTest.php new file mode 100644 index 00000000..2d75937a --- /dev/null +++ b/tests/Feature/Business/WarningPageTest.php @@ -0,0 +1,118 @@ +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(); + + $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('退货、退款及售后差异') + ->assertSeeText('接入后在这里查看售后退货退款预警'); + } + + 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->loginAs($regular) + ->get(route('business.pages.show', [ + 'section' => 'other', + 'page' => 'after-sales-return-refund', + ])) + ->assertOk() + ->assertSeeText('售后退货退款预警规则待接入'); + } + + 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; + } +} -- 2.47.2