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();
}
}