Merge remote-tracking branch 'origin/main' into feature/renshen-courier-freight-reconciliation

This commit is contained in:
renshen 2026-09-08 12:14:30 +08:00
commit 4e075fd3db
3 changed files with 18 additions and 10 deletions

View File

@ -35,9 +35,6 @@ class SidebarPermissions
$subject = self::subjectKey($user);
if (! in_array($subject, $settings['configured'] ?? [], true)) {
if ($user->created_from === 'dingtalk_roster' && SystemAccess::roleName($user) === SystemAccess::USER) {
return [];
}
return null;
}

View File

@ -15,7 +15,7 @@
新账号使用钉钉接口返回的姓名、可用邮箱和手机号;邮箱缺失或已被其他账号使用时,生成稳定的 `@dingtalk.invalid` 内部占位地址。系统生成不可预知的随机本地密码,不展示或发送密码,不发送注册邀请。花名册显示名仅作为姓名缺失时的后备。
新账号赋予现有 `user` 角色及进入应用外壳所需的 `read-admin-panel` 权限,不添加管理权限。业务访问继承管理员明确配置的普通用户权限;尚无配置时默认无业务权限,进入“你已登录,等待分配业务权限”的工作台。管理员可在“侧边栏管理”中为普通用户角色或指定账号配置业务范围。
新账号赋予现有 `user` 角色及进入应用外壳所需的 `read-admin-panel` 权限,不添加管理权限。按负责人最新要求,尚无页面权限配置时默认显示完整业务导航;主管理员随后可在“侧边栏管理”中设置指定用户的业务范围。配置优先级为指定用户、角色、默认完整导航;显式配置为空时没有业务页面,不回退到完整导航。管理员专属操作(包括店铺密码读取)继续单独鉴权。
## 只读数据契约

View File

@ -111,7 +111,8 @@ class DingTalkLoginTest extends FeatureTestCase
$this->assertNotSame($this->user->id, $created->id);
$this->assertTrue($created->hasRole('user'));
$this->assertFalse($created->hasRole('super_admin'));
$this->assertSame([], SidebarPermissions::allowedKeys($created));
$this->assertNull(SidebarPermissions::allowedKeys($created));
$this->assertTrue(collect(SidebarPermissions::navigationFor($created))->contains('slug', 'store-management'));
$this->assertDatabaseHas('dingtalk_identities', [
'user_id' => $created->id,
'union_id' => 'union-auto',
@ -127,8 +128,7 @@ class DingTalkLoginTest extends FeatureTestCase
'outcome' => 'success',
'user_id' => $created->id,
]);
$this->get(route('dashboard', ['company_id' => $this->company->id]))->assertOk()
->assertSeeText('你已登录')->assertSeeText('无需再次注册');
$this->get(route('dashboard', ['company_id' => $this->company->id]))->assertRedirectContains('/workspace/');
}
public function test_shared_mobile_does_not_claim_an_existing_account(): void
@ -430,14 +430,13 @@ class DingTalkLoginTest extends FeatureTestCase
public function test_admin_can_assign_business_access_to_the_automatically_created_user(): void
{
app('url')->defaults(['company_id' => $this->company->id]);
Http::preventStrayRequests();
Http::fake($this->dingtalkResponses('union-grant', 'open-grant'));
$state = $this->startAuthorization();
$this->get(route('login.dingtalk.callback', ['code' => 'fake-code', 'state' => $state]));
$created = auth()->user();
$this->withExceptionHandling()->get(route('business.pages.show', [
'company_id' => $this->company->id, 'section' => 'store-management', 'page' => 'basic-information',
]))->assertForbidden();
$this->assertNull(SidebarPermissions::allowedKeys($created));
auth()->logout(); session()->invalidate();
$this->actingAs($this->user)->get(route('system-settings.sidebar.edit', ['subject' => 'user_'.$created->id]))
->assertOk()->assertSee('value="user_'.$created->id.'"', false);
@ -447,6 +446,18 @@ class DingTalkLoginTest extends FeatureTestCase
$this->assertSame(['store-management/basic-information'], SidebarPermissions::allowedKeys($created));
$this->actingAs($created)->withSession(['dingtalk_identity_id' => $created->dingtalkIdentity->id, 'dingtalk_roster_checked_at' => now()->timestamp])
->get(route('dashboard'))->assertRedirectContains('/workspace/store-management/basic-information');
$this->withExceptionHandling()->get(route('business.pages.show', [
'company_id' => $this->company->id, 'section' => 'company-management', 'page' => 'basic-information',
]))->assertForbidden();
}
public function test_explicit_empty_user_scope_does_not_fall_back_to_all_pages(): void
{
setting()->set('system.sidebar_permissions', [
'configured' => ['user_'.$this->user->id], 'subjects' => ['user_'.$this->user->id => []],
]);
$this->assertSame([], SidebarPermissions::navigationFor($this->user));
$this->loginAs()->get(route('dashboard', ['company_id' => $this->company->id]))->assertOk()->assertSeeText('尚未分配业务权限');
}
protected function startAuthorization(): string