using FoodLabeling.Application.Contracts.Constants; namespace FoodLabeling.Application.Helpers; /// /// 角色表单勾选项(manage_labels 等)与 Menu.Router / PermissionCode 的映射。 /// public static class RoleAccessPermissionMenuMapping { /// /// UI 权限码 → 可绑定的菜单 Router(与库中 Menu.Router 一致,含父级目录)。 /// private static readonly IReadOnlyDictionary RoleCodeToRouters = new Dictionary(StringComparer.OrdinalIgnoreCase) { [RoleAccessPermissionCodes.ManageLabels] = [ "/labeling", "/labels", "/label-categories", "/label-types", "/label-templates" ], [RoleAccessPermissionCodes.ManagePeople] = ["/account-management"], [RoleAccessPermissionCodes.EditSettings] = ["/menu-management", "/multiple-options"], [RoleAccessPermissionCodes.ViewReports] = ["/reports"], // 当前 Menu 种子暂无独立 Products / Batches 页面,保留空映射(不阻断其它权限绑定) [RoleAccessPermissionCodes.ManageProducts] = [], [RoleAccessPermissionCodes.ApproveBatches] = [] }; /// /// 将入参权限码展开为可与菜单匹配的 token(含 menu.xxx 与 /router)。 /// public static HashSet ExpandMatchTokens(IEnumerable inputCodes) { var tokens = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (var raw in inputCodes) { if (string.IsNullOrWhiteSpace(raw)) { continue; } var code = raw.Trim(); tokens.Add(code); if (!RoleAccessPermissionCodes.IsKnown(code)) { continue; } if (!RoleCodeToRouters.TryGetValue(code, out var routers)) { continue; } foreach (var router in routers) { AddRouterTokens(tokens, router); } } return tokens; } /// /// 判断菜单是否命中任一 token(PermissionCode 或 Router)。 /// public static bool MenuMatchesAnyToken(string? permissionCode, string? router, IEnumerable tokens) { var effective = RbacAccessPermissionHelper.GetEffectivePermissionCode(permissionCode, router); var routerNorm = NormalizeRouterPath(router); foreach (var token in tokens) { if (string.IsNullOrWhiteSpace(token)) { continue; } var t = token.Trim(); if (effective is not null && string.Equals(effective, t, StringComparison.OrdinalIgnoreCase)) { return true; } if (routerNorm is null) { continue; } if (t.StartsWith("menu.", StringComparison.OrdinalIgnoreCase)) { continue; } var tokenPath = t.StartsWith('/') ? NormalizeRouterPath(t) : NormalizeRouterPath("/" + t); if (tokenPath is not null && string.Equals(routerNorm, tokenPath, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private static void AddRouterTokens(HashSet tokens, string router) { var path = NormalizeRouterPath(router); if (string.IsNullOrEmpty(path)) { return; } tokens.Add(path); var derived = RbacAccessPermissionHelper.DerivePermissionCodeFromRouter(path); if (!string.IsNullOrWhiteSpace(derived)) { tokens.Add(derived); } } private static string? NormalizeRouterPath(string? router) { var r = router?.Trim(); if (string.IsNullOrEmpty(r)) { return null; } r = r.TrimEnd('/'); if (!r.StartsWith('/')) { r = "/" + r; } return r.ToLowerInvariant(); } }