241 lines
8.9 KiB
PHP
241 lines
8.9 KiB
PHP
<?php
|
||
|
||
namespace App\Utilities;
|
||
|
||
use App\Models\Auth\User;
|
||
use App\Models\Auth\UserAccessGroup;
|
||
|
||
class SidebarPermissions
|
||
{
|
||
private const LEGACY_PAGE_KEYS = [
|
||
'refunds/small-payment-work-orders' => 'financial-operations/small-payment-work-orders',
|
||
];
|
||
|
||
private const FINAL_SECTION_KEYS = [
|
||
'supplier-payables' => 'supplier-payables-final',
|
||
'courier-payables' => 'courier-payables-final',
|
||
];
|
||
|
||
public static function settings(): array
|
||
{
|
||
$value = setting('system.sidebar_permissions', []);
|
||
|
||
if (is_string($value)) {
|
||
$value = json_decode($value, true) ?: [];
|
||
}
|
||
|
||
return is_array($value) ? $value : [];
|
||
}
|
||
|
||
public static function subjectKey(User $user): string
|
||
{
|
||
$userKey = 'user_' . $user->id;
|
||
$settings = self::settings();
|
||
|
||
if (in_array($userKey, $settings['configured'] ?? [], true)) {
|
||
return $userKey;
|
||
}
|
||
|
||
return 'role_' . SystemAccess::roleName($user);
|
||
}
|
||
|
||
public static function allowedKeys(User $user): ?array
|
||
{
|
||
if (SystemAccess::isSuperAdmin($user)) {
|
||
return null;
|
||
}
|
||
|
||
$settings = self::settings();
|
||
$userSubject = 'user_' . $user->id;
|
||
if (in_array($userSubject, $settings['configured'] ?? [], true)) {
|
||
return self::normalizeAllowedKeys((array) ($settings['subjects'][$userSubject] ?? []));
|
||
}
|
||
|
||
$groups = UserAccessGroup::forUser($user);
|
||
if ($groups->isNotEmpty()) {
|
||
return self::normalizeAllowedKeys(
|
||
$groups->flatMap(fn ($group) => $group->page_permissions ?? [])->all()
|
||
);
|
||
}
|
||
|
||
$subject = 'role_' . SystemAccess::roleName($user);
|
||
|
||
if (! in_array($subject, $settings['configured'] ?? [], true)) {
|
||
return null;
|
||
}
|
||
|
||
return self::normalizeAllowedKeys((array) ($settings['subjects'][$subject] ?? []));
|
||
}
|
||
|
||
public static function navigationFor(User $user): array
|
||
{
|
||
$navigation = BusinessNavigation::all();
|
||
$allowed = self::allowedKeys($user);
|
||
|
||
if ($allowed === null) {
|
||
return $navigation;
|
||
}
|
||
|
||
$navigation = array_values(array_filter(array_map(function (array $section) use ($allowed) {
|
||
$sectionKey = $section['slug'];
|
||
|
||
if (in_array($sectionKey, $allowed, true)) {
|
||
return $section;
|
||
}
|
||
|
||
$pages = array_values(array_filter(array_map(function (array $page) use ($allowed, $sectionKey) {
|
||
$pageKey = $sectionKey . '/' . $page['slug'];
|
||
|
||
if (in_array($pageKey, $allowed, true)) {
|
||
// 若有 children,保留 children 的精确权限过滤
|
||
if (! empty($page['children'])) {
|
||
$page['children'] = array_values(array_filter($page['children'], function (array $child) use ($allowed, $sectionKey) {
|
||
return in_array($sectionKey . '/' . $child['slug'], $allowed, true);
|
||
}));
|
||
}
|
||
return $page;
|
||
}
|
||
|
||
// 子页面可能在 allowed 列表中(独立 URL),父级分组也允许展开
|
||
if (! empty($page['children'])) {
|
||
$visibleChildren = array_values(array_filter($page['children'], function (array $child) use ($allowed, $sectionKey) {
|
||
return in_array($sectionKey . '/' . $child['slug'], $allowed, true);
|
||
}));
|
||
if (! empty($visibleChildren)) {
|
||
$page['children'] = $visibleChildren;
|
||
return $page;
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}, $section['pages'] ?? [])));
|
||
|
||
if (! empty($pages)) {
|
||
$section['pages'] = $pages;
|
||
|
||
return $section;
|
||
}
|
||
|
||
return null;
|
||
}, $navigation)));
|
||
|
||
// 新增的公司成本安排、税金预测表与主体经营报表共用公司经营报表权限。
|
||
// 对已经保存过精确权限的账号兼容旧配置,避免新增页面被旧权限集合隐藏。
|
||
foreach ($navigation as &$section) {
|
||
if (($section['slug'] ?? null) !== 'company-management') {
|
||
continue;
|
||
}
|
||
|
||
$hasSubjectReport = collect($section['pages'] ?? [])->contains('slug', 'subject-operating-report');
|
||
|
||
if ($hasSubjectReport) {
|
||
$configuredPages = collect(BusinessNavigation::all())
|
||
->firstWhere('slug', 'company-management')['pages'] ?? [];
|
||
|
||
foreach (['company-cost-arrangement', 'tax-forecast'] as $sharedReportSlug) {
|
||
$alreadyVisible = collect($section['pages'] ?? [])->contains('slug', $sharedReportSlug);
|
||
|
||
if ($alreadyVisible) {
|
||
continue;
|
||
}
|
||
|
||
$sharedReportPage = collect($configuredPages)->firstWhere('slug', $sharedReportSlug);
|
||
|
||
if ($sharedReportPage) {
|
||
$section['pages'][] = $sharedReportPage;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
unset($section);
|
||
|
||
// 「财务经营风险管控」新增「预览统计 / 流程统计」子页面:按页面键保存过精确权限的
|
||
// 账号只会有一条或两条,会看不到新页面;这里按「能看店铺违规预警 就能看新增页」
|
||
// 兼容旧配置(同属财务经营风险管控权限)。
|
||
foreach ($navigation as &$riskSection) {
|
||
if (($riskSection['slug'] ?? null) !== 'financial-risk-control') {
|
||
continue;
|
||
}
|
||
|
||
$pageSlugs = array_column($riskSection['pages'] ?? [], 'slug');
|
||
if (! in_array('store-violation-alert', $pageSlugs, true)) {
|
||
continue;
|
||
}
|
||
|
||
$configuredPages = collect(BusinessNavigation::all())
|
||
->firstWhere('slug', 'financial-risk-control')['pages'] ?? [];
|
||
|
||
foreach ($configuredPages as $configuredPage) {
|
||
$configuredSlug = (string) ($configuredPage['slug'] ?? '');
|
||
if (! in_array($configuredSlug, ['preview-statistics', 'process-statistics'], true)) {
|
||
continue;
|
||
}
|
||
if (! in_array($configuredSlug, $pageSlugs, true)) {
|
||
$riskSection['pages'][] = $configuredPage;
|
||
}
|
||
}
|
||
}
|
||
unset($riskSection);
|
||
|
||
// 「种菜明细表 / 聚水潭订单标签明细」原先挂在「账单流水数据」下,后来移入新的一级菜单
|
||
// 「种菜数据汇总」。按页面键保存过精确权限的账号(访问组/角色)里不会有新的 planting-data,
|
||
// 会因此看不到这两个被移动的页面;这里按「能看账单流水数据 就能看这两个页面」兼容旧配置。
|
||
$slugs = array_column($navigation, 'slug');
|
||
if (! in_array('planting-data', $slugs, true) && in_array('billing-transactions', $slugs, true)) {
|
||
$plantingSection = collect(BusinessNavigation::all())->firstWhere('slug', 'planting-data');
|
||
|
||
if (! empty($plantingSection)) {
|
||
$billingIndex = array_search('billing-transactions', $slugs, true);
|
||
array_splice($navigation, $billingIndex + 1, 0, [$plantingSection]);
|
||
}
|
||
}
|
||
|
||
return $navigation;
|
||
}
|
||
|
||
public static function allKeys(): array
|
||
{
|
||
$keys = [];
|
||
|
||
foreach (BusinessNavigation::all() as $section) {
|
||
$keys[] = $section['slug'];
|
||
|
||
foreach ($section['pages'] ?? [] as $page) {
|
||
$keys[] = $section['slug'] . '/' . $page['slug'];
|
||
|
||
foreach ($page['children'] ?? [] as $child) {
|
||
$keys[] = $section['slug'] . '/' . $child['slug'];
|
||
}
|
||
}
|
||
}
|
||
|
||
return $keys;
|
||
}
|
||
|
||
public static function normalizeAllowedKeys(array $keys): array
|
||
{
|
||
$normalized = [];
|
||
|
||
foreach ($keys as $key) {
|
||
$key = (string) $key;
|
||
$normalizedKey = self::LEGACY_PAGE_KEYS[$key] ?? $key;
|
||
$normalized[] = $normalizedKey;
|
||
|
||
foreach (self::FINAL_SECTION_KEYS as $sectionKey => $finalSectionKey) {
|
||
if ($normalizedKey === $sectionKey) {
|
||
$normalized[] = $finalSectionKey;
|
||
} elseif (str_starts_with($normalizedKey, $sectionKey . '/')) {
|
||
// 「终版」目录已是独立目录,按同样的子页面粒度跟着授权。
|
||
$normalized[] = $finalSectionKey . substr($normalizedKey, strlen($sectionKey));
|
||
}
|
||
}
|
||
|
||
if ($key === 'refunds') {
|
||
$normalized[] = self::LEGACY_PAGE_KEYS['refunds/small-payment-work-orders'];
|
||
}
|
||
}
|
||
|
||
return array_values(array_unique($normalized));
|
||
}
|
||
}
|