diff --git a/MiGu.Server/Auth/PageCatalog.cs b/MiGu.Server/Auth/PageCatalog.cs index d6582ad..8ceebab 100644 --- a/MiGu.Server/Auth/PageCatalog.cs +++ b/MiGu.Server/Auth/PageCatalog.cs @@ -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 _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", }; + /// + /// 管理端页面 → 运营端对应页面。给运营角色勾页时,只能从「当前管理员已有的管理端页」映射出来。 + /// 无对应运营页的管理端入口(如任务/报警)不出现在映射里。 + /// + public static readonly IReadOnlyDictionary PlatformToMonitor = + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + ["admin-map-monitor"] = "monitor-map", + ["admin-vehicle-hub"] = "monitor-vehicle-hub", + ["admin-ops-log"] = "monitor-ops", + ["admin-notes"] = "monitor-notes", + }; + + /// 仅运营端有的页面:不依赖管理端对应页,始终可授给运营角色。 + public static readonly IReadOnlyList MonitorOnlyPages = + new[] { "monitor-ops", "monitor-notes" }; + + /// + /// 把角色里勾选的页面展开到指定登录域:勾了运营监控的「运维记录」时, + /// 管理账号/通用角色进管理壳也能拿到对应的 admin-ops-log。 + /// + public static HashSet ExpandKeysForScope(IEnumerable keys, string scope) + { + var scopeKeys = KeysForScope(scope).ToHashSet(StringComparer.OrdinalIgnoreCase); + var set = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var raw in keys ?? Array.Empty()) + { + 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; + } + + /// 管理页 ↔ 运营页互认:monitor-ops → admin-ops-log(当前域是 Platform 时)。 + 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; + } + /// 判断页面 Key 是否合法(用于角色保存时过滤掉脏数据 / 已下线页面)。 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(); + + /// 当前管理员在管理端拥有的页面 → 可授给运营端角色的页面集合。 + public static HashSet GrantableMonitorPages(IEnumerable? actorPlatformPages) + { + var set = new HashSet(MonitorOnlyPages, StringComparer.OrdinalIgnoreCase); + var list = (actorPlatformPages ?? Array.Empty()).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; + } + + /// 当前管理员可授给管理端角色的页面(不能超权:只能授自己已有的)。 + public static HashSet GrantablePlatformPages(IEnumerable? actorPlatformPages) + { + var list = (actorPlatformPages ?? Array.Empty()).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); + } + + /// 按角色归属域,算出当前管理员可勾选的页面 Key。 + public static HashSet GrantablePagesForRoleScope( + string roleScope, IEnumerable? 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 have, IEnumerable all) + { + var set = have.ToHashSet(StringComparer.OrdinalIgnoreCase); + return all.All(k => set.Contains(k)); + } } diff --git a/MiGu.Server/Auth/RbacStore.cs b/MiGu.Server/Auth/RbacStore.cs index 1b3ca46..e79c832 100644 --- a/MiGu.Server/Auth/RbacStore.cs +++ b/MiGu.Server/Auth/RbacStore.cs @@ -16,7 +16,7 @@ namespace MiGu.Server.Auth; /// /// 首次启动(rbac.json 不存在)时 seed 两个内置账号: /// admin(超级管理员,scope=*,全部页面 / 操作) -/// ops (运营人员,scope=RCSMonitor,运营四页 + 运维操作码) + /// ops (运营人员,scope=RCSMonitor,运营监控页 + 运维操作码) /// 初始密码取 appsettings Auth:Users:{name}:Password,缺省 admin/ops(开发弱口令,生产须改)。 /// public sealed class RbacStore @@ -58,9 +58,14 @@ public sealed class RbacStore var snap = JsonSerializer.Deserialize(json, _jsonOpts); if (snap is { Users.Count: > 0 }) { - _snapshot = Normalize(snap); + _snapshot = Normalize(snap, out var migrated); _logger.LogInformation("RBAC 从 {File} 载入:{Users} 用户 / {Roles} 角色。", _file, _snapshot.Users.Count, _snapshot.Roles.Count); + if (migrated) + { + Persist(); + _logger.LogInformation("RBAC 已将下线页面 Key 迁移并写回 {File}。", _file); + } return; } _logger.LogWarning("RBAC 文件 {File} 内容为空或无用户,回退到默认 seed。", _file); @@ -82,21 +87,30 @@ public sealed class RbacStore } /// 清洗加载结果:补默认、去重、过滤非法页面 Key,保证内置角色存在。 - private static RbacSnapshot Normalize(RbacSnapshot snap) + /// 任一角色页面 Key 被别名替换 / 丢弃 / 补齐时为 true,调用方应落盘。 + private static RbacSnapshot Normalize(RbacSnapshot snap, out bool migrated) { + migrated = false; snap.Roles ??= new(); snap.Users ??= new(); foreach (var r in snap.Roles) { - r.Pages = (r.Pages ?? new()) + var before = r.Pages ?? new(); + var hadSystemHub = before.Any(p => + string.Equals(p, "admin-config-system-center", StringComparison.OrdinalIgnoreCase)); + var after = before .Select(p => p == PageCatalog.Wildcard ? p : PageCatalog.NormalizeKey(p)) .Where(p => p == PageCatalog.Wildcard || PageCatalog.IsValidKey(p)) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); + if (!SamePages(before, after)) migrated = true; + r.Pages = after; r.Ops = (r.Ops ?? new()).Distinct().ToList(); r.WidgetGrants ??= new(); if (string.IsNullOrWhiteSpace(r.Scope)) r.Scope = PageCatalog.ScopePlatform; - BackfillKnownPageMigrations(r); + var countBeforeBackfill = r.Pages.Count; + BackfillKnownPageMigrations(r, hadSystemHub); + if (r.Pages.Count != countBeforeBackfill) migrated = true; } foreach (var u in snap.Users) { @@ -105,7 +119,12 @@ public sealed class RbacStore return snap; } - private static void BackfillKnownPageMigrations(RbacRole r) + private static bool SamePages(IReadOnlyList a, IReadOnlyList b) => + a.Count == b.Count + && !a.Except(b, StringComparer.OrdinalIgnoreCase).Any() + && !b.Except(a, StringComparer.OrdinalIgnoreCase).Any(); + + private static void BackfillKnownPageMigrations(RbacRole r, bool hadSystemHub) { if (!string.Equals(r.Scope, PageCatalog.ScopePlatform, StringComparison.OrdinalIgnoreCase) && r.Scope != PageCatalog.Wildcard) return; @@ -132,6 +151,25 @@ public sealed class RbacStore && r.Pages.Contains("admin-cars", StringComparer.OrdinalIgnoreCase) && r.Pages.Contains("admin-maps", StringComparer.OrdinalIgnoreCase)) r.Pages.Add("admin-setup"); + + // 日志管理、调度回放从监控配置聚合页拆出:原先有聚合页权限的角色补上新入口。 + if (!r.Pages.Contains(PageCatalog.Wildcard) + && r.Pages.Contains("admin-config-ops-center", StringComparer.OrdinalIgnoreCase)) + { + if (!r.Pages.Contains("admin-logs", StringComparer.OrdinalIgnoreCase)) + r.Pages.Add("admin-logs"); + if (!r.Pages.Contains("admin-playback", StringComparer.OrdinalIgnoreCase)) + r.Pages.Add("admin-playback"); + } + + // 「系统与权限」拆成系统配置 / 权限与角色。 + if (!r.Pages.Contains(PageCatalog.Wildcard) && hadSystemHub) + { + if (!r.Pages.Contains("admin-config-system", StringComparer.OrdinalIgnoreCase)) + r.Pages.Add("admin-config-system"); + if (!r.Pages.Contains("admin-config-auth", StringComparer.OrdinalIgnoreCase)) + r.Pages.Add("admin-config-auth"); + } } private RbacSnapshot SeedDefault(IConfiguration config) @@ -159,7 +197,7 @@ public sealed class RbacStore { Id = RoleOpsId, Name = "运营人员", Description = "运营监控端默认角色:可执行运维操作、查看监控", Scope = PageCatalog.ScopeMonitor, - Pages = new() { "monitor-dashboard", "monitor-vehicle-hub", "monitor-map", "monitor-ops", "monitor-notes" }, + Pages = new() { "monitor-vehicle-hub", "monitor-map", "monitor-ops", "monitor-notes" }, Ops = new() { "ops.car.pause", "ops.car.resume", "ops.car.gohome", "ops.car.resetSession", @@ -250,6 +288,20 @@ public sealed class RbacStore public bool CanUseScope(RbacUser user, string scope) => UsableScopes(user).Contains(scope, StringComparer.OrdinalIgnoreCase); + /// + /// 登录按账号角色自动选域:能进管理端则进管理端,否则进运营权限页。 + /// 不再接受客户端挑选「运营端 / 管理端」。 + /// + public string? ResolveLoginScope(RbacUser user) + { + var scopes = UsableScopes(user); + if (scopes.Contains(PageCatalog.ScopePlatform, StringComparer.OrdinalIgnoreCase)) + return PageCatalog.ScopePlatform; + if (scopes.Contains(PageCatalog.ScopeMonitor, StringComparer.OrdinalIgnoreCase)) + return PageCatalog.ScopeMonitor; + return null; + } + /// 角色名(展示用,写入 AuthUserDto.Roles / JWT role claim)。 public List RoleNamesOf(RbacUser user) { @@ -276,7 +328,7 @@ public sealed class RbacStore foreach (var r in roles) { if (r.Pages.Contains(PageCatalog.Wildcard)) pages.UnionWith(scopeKeys); - else foreach (var p in r.Pages) if (scopeKeys.Contains(p)) pages.Add(p); + else pages.UnionWith(PageCatalog.ExpandKeysForScope(r.Pages, scope)); foreach (var o in r.Ops) { @@ -486,7 +538,11 @@ public sealed class RbacStore { if (pages is null) return new(); if (pages.Contains(PageCatalog.Wildcard)) return new() { PageCatalog.Wildcard }; - return pages.Where(PageCatalog.IsValidKey).Distinct().ToList(); + return pages + .Select(PageCatalog.NormalizeKey) + .Where(p => p == PageCatalog.Wildcard || PageCatalog.IsValidKey(p)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); } private static string NormalizeScope(string? scope) => scope switch diff --git a/MiGu.Server/Controllers/RbacController.cs b/MiGu.Server/Controllers/RbacController.cs index 4ac4ac7..1246177 100644 --- a/MiGu.Server/Controllers/RbacController.cs +++ b/MiGu.Server/Controllers/RbacController.cs @@ -29,6 +29,7 @@ public class RbacController : ControllerBase new("ops.car.gohome", "车辆 · 回库"), new("ops.car.resetSession", "车辆 · 重置会话"), new("ops.car.manualCharge", "车辆 · 手动充电"), + new("ops.car.execute", "车辆 · 地图监控动作(按管理端配置)"), new("ops.task.pause", "任务 · 暂停"), new("ops.task.cancel", "任务 · 取消"), new("ops.task.reassign", "任务 · 改派"), @@ -61,18 +62,35 @@ public class RbacController : ControllerBase /// 权限「字典」:页面清单 + 可选操作码 + 可选控件 + scope 选项。前端角色编辑器据此渲染勾选项。 [HttpGet("catalog")] - public IActionResult Catalog() => Ok(new + public IActionResult Catalog() { - pages = PageCatalog.All, - ops = KnownOps, - widgets = KnownWidgets, - scopes = new[] + var actorPages = ActorPlatformPages(); + var grantablePlatform = PageCatalog.GrantablePlatformPages(actorPages).OrderBy(x => x).ToList(); + var grantableMonitor = PageCatalog.GrantableMonitorPages(actorPages).OrderBy(x => x).ToList(); + return Ok(new { - new { value = PageCatalog.ScopePlatform, label = "管理端 (Platform)" }, - new { value = PageCatalog.ScopeMonitor, label = "运营端 (RCSMonitor)" }, - new { value = PageCatalog.Wildcard, label = "通用 (全部域)" }, - } - }); + pages = PageCatalog.All, + ops = KnownOps, + widgets = KnownWidgets, + scopes = new[] + { + new { value = PageCatalog.ScopePlatform, label = "管理权限 (Platform)" }, + new { value = PageCatalog.ScopeMonitor, label = "运营权限 (RCSMonitor)" }, + new { value = PageCatalog.Wildcard, label = "通用 (全部域)" }, + }, + // 当前登录管理员可勾选的页面(运营端由管理端已有页映射而来)。 + grantablePages = new Dictionary>(StringComparer.OrdinalIgnoreCase) + { + [PageCatalog.ScopePlatform] = grantablePlatform, + [PageCatalog.ScopeMonitor] = grantableMonitor, + [PageCatalog.Wildcard] = grantablePlatform.Concat(grantableMonitor).Distinct(StringComparer.OrdinalIgnoreCase).OrderBy(x => x).ToList(), + }, + platformToMonitor = PageCatalog.PlatformToMonitor + .Select(kv => new { platform = kv.Key, monitor = kv.Value }) + .ToList(), + monitorOnlyPages = PageCatalog.MonitorOnlyPages, + }); + } // ───────────────────────── 角色 ───────────────────────── @@ -80,10 +98,12 @@ public class RbacController : ControllerBase public IActionResult ListRoles() => Ok(_store.ListRoles()); [HttpPost("roles")] - public IActionResult CreateRole([FromBody] SaveRoleRequest req) => Guard(() => Ok(_store.CreateRole(req))); + public IActionResult CreateRole([FromBody] SaveRoleRequest req) => Guard(() => + Ok(_store.CreateRole(ClampPages(req)))); [HttpPut("roles/{id}")] - public IActionResult UpdateRole(string id, [FromBody] SaveRoleRequest req) => Guard(() => Ok(_store.UpdateRole(id, req))); + public IActionResult UpdateRole(string id, [FromBody] SaveRoleRequest req) => Guard(() => + Ok(_store.UpdateRole(id, ClampPages(req)))); [HttpDelete("roles/{id}")] public IActionResult DeleteRole(string id) => Guard(() => @@ -136,4 +156,43 @@ public class RbacController : ControllerBase private string? CurrentUserId() => User.FindFirstValue("sub") ?? User.FindFirstValue(ClaimTypes.NameIdentifier); + + /// 当前登录管理员在管理端的有效页面集合。 + private List ActorPlatformPages() + { + var id = CurrentUserId(); + if (string.IsNullOrEmpty(id)) return new(); + var user = _store.FindUserById(id); + if (user is null) return new(); + return _store.ComputeEffective(user, PageCatalog.ScopePlatform).Pages; + } + + /// + /// 保存角色时按「当前管理员可授页面」裁剪:运营端页只能选自管理端已有页的映射 + 运营专属页。 + /// 通配 * 仅当可授集合已覆盖该域全部页面时才保留。 + /// + private SaveRoleRequest ClampPages(SaveRoleRequest req) + { + var scope = string.IsNullOrWhiteSpace(req.Scope) ? PageCatalog.ScopePlatform : req.Scope.Trim(); + var grantable = PageCatalog.GrantablePagesForRoleScope(scope, ActorPlatformPages()); + var pages = req.Pages ?? new List(); + if (pages.Contains(PageCatalog.Wildcard, StringComparer.OrdinalIgnoreCase)) + { + var scopeKeys = scope == PageCatalog.Wildcard + ? PageCatalog.All.Select(p => p.Key).ToList() + : PageCatalog.KeysForScope(scope).ToList(); + if (scopeKeys.All(k => grantable.Contains(k))) + return req with { Pages = new List { PageCatalog.Wildcard } }; + return req with + { + Pages = scopeKeys.Where(k => grantable.Contains(k)).Distinct(StringComparer.OrdinalIgnoreCase).ToList() + }; + } + var clamped = pages + .Select(PageCatalog.NormalizeKey) + .Where(p => p == PageCatalog.Wildcard || (PageCatalog.IsValidKey(p) && grantable.Contains(p))) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + return req with { Pages = clamped }; + } } diff --git a/MiGu.Server/Dashboard/DashboardShortcutCatalog.cs b/MiGu.Server/Dashboard/DashboardShortcutCatalog.cs index eddd773..da4a9d7 100644 --- a/MiGu.Server/Dashboard/DashboardShortcutCatalog.cs +++ b/MiGu.Server/Dashboard/DashboardShortcutCatalog.cs @@ -17,10 +17,12 @@ public static class DashboardShortcutCatalog ["platform-config"] = "admin-map-editor", ["mission"] = "admin-task-templates", ["cars"] = "admin-cars", - ["auth"] = "admin-config-system-center", - ["system"] = "admin-config-system-center", + ["auth"] = "admin-config-auth", + ["system"] = "admin-config-system", + ["admin-config-system-center"] = "admin-config-auth", ["ops"] = "admin-config-ops-center", - ["tasks"] = "admin-config-strategy", + ["tasks"] = "admin-task-templates", + ["admin-config-strategy"] = "admin-task-templates", }; private static readonly ShortcutDef[] PlatformShortcuts = @@ -28,6 +30,10 @@ public static class DashboardShortcutCatalog new("admin-dashboard", "admin-dashboard", PageCatalog.ScopePlatform), new("admin-setup", "admin-setup", PageCatalog.ScopePlatform), new("admin-map-monitor", "admin-map-monitor", PageCatalog.ScopePlatform), + new("admin-playback", "admin-playback", PageCatalog.ScopePlatform), + new("admin-ops-log", "admin-ops-log", PageCatalog.ScopePlatform), + new("admin-notes", "admin-notes", PageCatalog.ScopePlatform), + new("admin-logs", "admin-logs", PageCatalog.ScopePlatform), new("admin-maps", "admin-maps", PageCatalog.ScopePlatform), new("admin-map-editor", "admin-map-editor", PageCatalog.ScopePlatform), new("admin-project-properties", "admin-project-properties", PageCatalog.ScopePlatform), @@ -38,17 +44,17 @@ public static class DashboardShortcutCatalog new("admin-task-templates", "admin-task-templates", PageCatalog.ScopePlatform), new("admin-simple-fields", "admin-simple-fields", PageCatalog.ScopePlatform), new("admin-data-center", "admin-data-center", PageCatalog.ScopePlatform), - new("admin-config-strategy", "admin-config-strategy", PageCatalog.ScopePlatform), + new("admin-setup", "admin-setup", PageCatalog.ScopePlatform), + new("admin-wcs-template-proto", "admin-wcs-template-proto", PageCatalog.ScopePlatform), new("admin-vehicle-hub", "admin-vehicle-hub", PageCatalog.ScopePlatform), new("admin-config-facility", "admin-config-facility", PageCatalog.ScopePlatform), - new("admin-config-business", "admin-config-business", PageCatalog.ScopePlatform), new("admin-config-ops-center", "admin-config-ops-center", PageCatalog.ScopePlatform), - new("admin-config-system-center", "admin-config-system-center", PageCatalog.ScopePlatform), + new("admin-config-system", "admin-config-system", PageCatalog.ScopePlatform), + new("admin-config-auth", "admin-config-auth", PageCatalog.ScopePlatform), ]; private static readonly ShortcutDef[] MonitorShortcuts = [ - new("monitor-dashboard", "monitor-dashboard", PageCatalog.ScopeMonitor), new("monitor-vehicle-hub", "monitor-vehicle-hub", PageCatalog.ScopeMonitor), new("monitor-map", "monitor-map", PageCatalog.ScopeMonitor), new("monitor-ops", "monitor-ops", PageCatalog.ScopeMonitor), @@ -80,16 +86,16 @@ public static class DashboardShortcutCatalog "admin-map-monitor", "admin-vehicle-hub", "admin-task-templates", - "admin-config-system-center", + "admin-config-auth", "admin-config-ops-center", - "admin-config-strategy" + "admin-config-facility" ]; public static readonly IReadOnlyList DefaultMonitorKeys = ["monitor-vehicle-hub", "monitor-map", "monitor-ops"]; private static readonly HashSet ExcludedKeys = - new(StringComparer.OrdinalIgnoreCase) { "admin-dashboard", "monitor-dashboard" }; + new(StringComparer.OrdinalIgnoreCase) { "admin-dashboard" }; public static bool IsValidKey(string key) => !ExcludedKeys.Contains(key) && ByKey.ContainsKey(key); diff --git a/MiGu.Server/data/rbac.json b/MiGu.Server/data/rbac.json index 26f5066..7fd7b73 100644 --- a/MiGu.Server/data/rbac.json +++ b/MiGu.Server/data/rbac.json @@ -19,12 +19,18 @@ "id": "role-ops", "name": "\u8FD0\u8425\u4EBA\u5458", "description": "\u8FD0\u8425\u76D1\u63A7\u7AEF\u9ED8\u8BA4\u89D2\u8272\uFF1A\u53EF\u6267\u884C\u8FD0\u7EF4\u64CD\u4F5C\u3001\u67E5\u770B\u76D1\u63A7", - "scope": "RCSMonitor", + "scope": "*", "pages": [ - "monitor-dashboard", - "monitor-map", + "monitor-notes", + "admin-dashboard", + "admin-map-monitor", + "admin-tasks", + "admin-alarms", + "admin-config-warehouse", "monitor-ops", - "monitor-notes" + "admin-config-ops-center", + "admin-logs", + "admin-playback" ], "ops": [ "ops.car.pause", @@ -87,8 +93,8 @@ "roleIds": [ "role-ops" ], - "salt": "G6c5\u002BnLEjf3fEor1LgNjyg==", - "passwordHash": "Zb1QiDzHctduFbZGKniTEy4OLJIvIOeoyLfc\u002Bo0y\u002BVg=" + "salt": "F6wxGwnf/nvdbbAKp6hYoQ==", + "passwordHash": "\u002BFvJCZgUYReNSr4T6xIpZU7FoajVHe067JVaHf/Evb8=" } ] } \ No newline at end of file diff --git a/frontends/apps/simple-platform-vue/src/api/delivery.ts b/frontends/apps/simple-platform-vue/src/api/delivery.ts index 5aa37a2..15353c8 100644 --- a/frontends/apps/simple-platform-vue/src/api/delivery.ts +++ b/frontends/apps/simple-platform-vue/src/api/delivery.ts @@ -20,7 +20,7 @@ export async function listDeliveries(opts?: { return Array.isArray(data) ? data : [] } -/** CDM 任务快照订阅结果:来自平台库 cdm_tasks(SimpleLite 关闭时仍可读,含完整历史)。 */ +/** CDM 任务快照订阅结果:来自平台库 cdm_tasks(Simple3 关闭时仍可读,含完整历史)。 */ export interface CdmTaskFeed { online: boolean lastSyncAt: string | null diff --git a/frontends/apps/simple-platform-vue/src/components/PermissionGuard.vue b/frontends/apps/simple-platform-vue/src/components/PermissionGuard.vue index 230099c..992161c 100644 --- a/frontends/apps/simple-platform-vue/src/components/PermissionGuard.vue +++ b/frontends/apps/simple-platform-vue/src/components/PermissionGuard.vue @@ -9,18 +9,26 @@ diff --git a/frontends/apps/simple-platform-vue/src/views/admin/config/DeviceHubView.vue b/frontends/apps/simple-platform-vue/src/views/admin/config/DeviceHubView.vue index cbc63e0..b6a813c 100644 --- a/frontends/apps/simple-platform-vue/src/views/admin/config/DeviceHubView.vue +++ b/frontends/apps/simple-platform-vue/src/views/admin/config/DeviceHubView.vue @@ -5,7 +5,7 @@ description="电梯/卷帘门/安全门/充电桩/AP/交换机/摄像头/读码器/PLC(DeviceManagementConfig)" :defaults="DEFAULT_DEVICE"> diff --git a/frontends/apps/simple-platform-vue/src/views/admin/config/OpsCenterView.vue b/frontends/apps/simple-platform-vue/src/views/admin/config/OpsCenterView.vue index 680b209..1a8c930 100644 --- a/frontends/apps/simple-platform-vue/src/views/admin/config/OpsCenterView.vue +++ b/frontends/apps/simple-platform-vue/src/views/admin/config/OpsCenterView.vue @@ -1,15 +1,9 @@