重构页面目录与 RBAC:扁平配置入口并对齐管理/运营页映射。

下线运营总览幽灵页,补 ExpandKeysForScope / PlatformToMonitor,同步导航与角色配置 UI。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
黄兆尉
2026-08-26 17:50:02 +08:00
co-authored by Cursor
parent 59ccd7934a
commit c5d5f7a726
20 changed files with 653 additions and 179 deletions
+124 -18
View File
@@ -31,8 +31,12 @@ public static class PageCatalog
new("admin-dashboard", "总览", "概览", ScopePlatform),
new("admin-setup", "初始配置", "概览", ScopePlatform),
new("admin-map-monitor", "地图监控", "概览", ScopePlatform),
new("admin-playback", "调度回放", "概览", ScopePlatform),
new("admin-tasks", "任务管理", "概览", ScopePlatform),
new("admin-alarms", "报警管理", "概览", ScopePlatform),
new("admin-ops-log", "运维记录", "概览", ScopePlatform),
new("admin-notes", "运维备注", "概览", ScopePlatform),
new("admin-logs", "日志管理", "概览", ScopePlatform),
// ── 管理端 / Platform:设计与编排 ──
new("admin-maps", "地图管理", "设计与编排", ScopePlatform),
@@ -50,20 +54,18 @@ public static class PageCatalog
new("admin-data-center", "数据中心", "数据中心", ScopePlatform),
// ── 管理端 / Platform:平台配置中心(聚合页,每个 Key 对齐前端聚合路由 route.name) ──
new("admin-config-strategy", "调度策略", "平台配置中心", ScopePlatform),
new("admin-vehicle-hub", "车辆运维", "平台配置中心", ScopePlatform),
new("admin-config-facility", "设备接入", "平台配置中心", ScopePlatform),
new("admin-config-warehouse", "库位管理", "平台配置中心", ScopePlatform),
new("admin-config-business", "业务与集成", "平台配置中心", ScopePlatform),
new("admin-config-ops-center", "运维与回放", "平台配置中心", ScopePlatform),
new("admin-config-system-center", "系统与权限", "平台配置中心", ScopePlatform),
new("admin-config-ops-center", "监控配置", "平台配置中心", ScopePlatform),
new("admin-config-system", "系统配置", "平台配置中心", ScopePlatform),
new("admin-config-auth", "权限与角色", "平台配置中心", ScopePlatform),
// ── 运营端 / RCSMonitor ──
new("monitor-dashboard", "运营总览", "运营监控", ScopeMonitor),
new("monitor-vehicle-hub", "车辆运维", "运营监控", ScopeMonitor),
new("monitor-map", "地图监控", "运营监控", ScopeMonitor),
new("monitor-ops", "运维操作", "运营监控", ScopeMonitor),
new("monitor-notes", "运备注", "运营监控", ScopeMonitor),
new("monitor-ops", "运维记录", "运营监控", ScopeMonitor),
new("monitor-notes", "运备注", "运营监控", ScopeMonitor),
};
private static readonly HashSet<string> _keys =
@@ -76,21 +78,14 @@ public static class PageCatalog
["admin-config-vehicle"] = "admin-vehicle-hub",
["admin-config-fleet"] = "admin-vehicle-hub",
// 会话 16:平台配置中心入口按业务收敛为 6 个聚合页,旧 Key 迁移到对应聚合页 Key。
["admin-config-routing"] = "admin-config-strategy",
["admin-config-task"] = "admin-config-strategy",
["admin-config-traffic"] = "admin-config-strategy",
["admin-config-charge"] = "admin-config-strategy",
["admin-config-device"] = "admin-config-facility",
["admin-config-location"] = "admin-config-warehouse",
["admin-config-integrations"] = "admin-config-business",
["admin-config-scenario"] = "admin-config-business",
["admin-config-widget"] = "admin-config-business",
["admin-playback"] = "admin-config-ops-center",
["admin-config-ops"] = "admin-config-ops-center",
["admin-config-logs"] = "admin-config-ops-center",
["admin-config-logs"] = "admin-logs",
["admin-config-map-monitor"] = "admin-config-ops-center",
["admin-config-system"] = "admin-config-system-center",
["admin-config-auth"] = "admin-config-system-center",
["admin-config-system-center"] = "admin-config-system",
// 运营总览页已下线:旧角色勾选迁到地图监控,避免权限清单里出现幽灵页面。
["monitor-dashboard"] = "monitor-map",
["admin-data-center-stations"] = "admin-data-center",
["admin-data-center-docks"] = "admin-data-center",
["admin-data-center-handshake"] = "admin-data-center",
@@ -98,6 +93,64 @@ public static class PageCatalog
["admin-data-center-mag-control"] = "admin-data-center",
};
/// <summary>
/// 管理端页面 → 运营端对应页面。给运营角色勾页时,只能从「当前管理员已有的管理端页」映射出来。
/// 无对应运营页的管理端入口(如任务/报警)不出现在映射里。
/// </summary>
public static readonly IReadOnlyDictionary<string, string> PlatformToMonitor =
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
["admin-map-monitor"] = "monitor-map",
["admin-vehicle-hub"] = "monitor-vehicle-hub",
["admin-ops-log"] = "monitor-ops",
["admin-notes"] = "monitor-notes",
};
/// <summary>仅运营端有的页面:不依赖管理端对应页,始终可授给运营角色。</summary>
public static readonly IReadOnlyList<string> MonitorOnlyPages =
new[] { "monitor-ops", "monitor-notes" };
/// <summary>
/// 把角色里勾选的页面展开到指定登录域:勾了运营监控的「运维记录」时,
/// 管理账号/通用角色进管理壳也能拿到对应的 admin-ops-log。
/// </summary>
public static HashSet<string> ExpandKeysForScope(IEnumerable<string> keys, string scope)
{
var scopeKeys = KeysForScope(scope).ToHashSet(StringComparer.OrdinalIgnoreCase);
var set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var raw in keys ?? Array.Empty<string>())
{
if (string.Equals(raw, Wildcard, StringComparison.OrdinalIgnoreCase))
{
foreach (var k in scopeKeys) set.Add(k);
return set;
}
var key = NormalizeKey(raw);
if (scopeKeys.Contains(key)) set.Add(key);
var counterpart = CounterpartForScope(key, scope);
if (counterpart != null && scopeKeys.Contains(counterpart)) set.Add(counterpart);
}
return set;
}
/// <summary>管理页 ↔ 运营页互认:monitor-ops → admin-ops-log(当前域是 Platform 时)。</summary>
public static string? CounterpartForScope(string key, string targetScope)
{
if (string.Equals(targetScope, ScopePlatform, StringComparison.OrdinalIgnoreCase))
{
foreach (var kv in PlatformToMonitor)
{
if (string.Equals(kv.Value, key, StringComparison.OrdinalIgnoreCase))
return kv.Key;
}
return null;
}
if (string.Equals(targetScope, ScopeMonitor, StringComparison.OrdinalIgnoreCase)
&& PlatformToMonitor.TryGetValue(key, out var monitorKey))
return monitorKey;
return null;
}
/// <summary>判断页面 Key 是否合法(用于角色保存时过滤掉脏数据 / 已下线页面)。</summary>
public static bool IsValidKey(string key) => _keys.Contains(key);
@@ -110,4 +163,57 @@ public static class PageCatalog
All.Where(p => string.Equals(p.Scope, scope, StringComparison.OrdinalIgnoreCase))
.Select(p => p.Key)
.ToList();
/// <summary>当前管理员在管理端拥有的页面 → 可授给运营端角色的页面集合。</summary>
public static HashSet<string> GrantableMonitorPages(IEnumerable<string>? actorPlatformPages)
{
var set = new HashSet<string>(MonitorOnlyPages, StringComparer.OrdinalIgnoreCase);
var list = (actorPlatformPages ?? Array.Empty<string>()).ToList();
if (list.Contains(Wildcard, StringComparer.OrdinalIgnoreCase)
|| HasAllKeys(list, KeysForScope(ScopePlatform)))
{
foreach (var k in KeysForScope(ScopeMonitor)) set.Add(k);
return set;
}
foreach (var p in list)
{
var key = NormalizeKey(p);
if (PlatformToMonitor.TryGetValue(key, out var monitorKey) && IsValidKey(monitorKey))
set.Add(monitorKey);
}
return set;
}
/// <summary>当前管理员可授给管理端角色的页面(不能超权:只能授自己已有的)。</summary>
public static HashSet<string> GrantablePlatformPages(IEnumerable<string>? actorPlatformPages)
{
var list = (actorPlatformPages ?? Array.Empty<string>()).ToList();
var all = KeysForScope(ScopePlatform);
if (list.Contains(Wildcard, StringComparer.OrdinalIgnoreCase) || HasAllKeys(list, all))
return all.ToHashSet(StringComparer.OrdinalIgnoreCase);
return list
.Select(NormalizeKey)
.Where(k => IsValidKey(k) && all.Contains(k, StringComparer.OrdinalIgnoreCase))
.ToHashSet(StringComparer.OrdinalIgnoreCase);
}
/// <summary>按角色归属域,算出当前管理员可勾选的页面 Key。</summary>
public static HashSet<string> GrantablePagesForRoleScope(
string roleScope, IEnumerable<string>? actorPlatformPages)
{
if (string.Equals(roleScope, ScopeMonitor, StringComparison.OrdinalIgnoreCase))
return GrantableMonitorPages(actorPlatformPages);
if (string.Equals(roleScope, ScopePlatform, StringComparison.OrdinalIgnoreCase))
return GrantablePlatformPages(actorPlatformPages);
// 通用域:管理端可授 + 运营端可授
var set = GrantablePlatformPages(actorPlatformPages);
set.UnionWith(GrantableMonitorPages(actorPlatformPages));
return set;
}
private static bool HasAllKeys(IEnumerable<string> have, IEnumerable<string> all)
{
var set = have.ToHashSet(StringComparer.OrdinalIgnoreCase);
return all.All(k => set.Contains(k));
}
}