- Permission 和 MenuDefinition 改为继承 AuditableEntityBase(移除 TenantId) - 添加 PortalType 枚举区分平台端/租户端 - Repository 使用 IgnoreQueryFilters() 查询系统级数据 - 更新所有相关 Handler 和 DTO,移除 TenantId 引用 - 与 AdminApi 保持一致的设计 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
130 lines
4.3 KiB
C#
130 lines
4.3 KiB
C#
using System.Text.Json;
|
|
using TakeoutSaaS.Application.Identity.Contracts;
|
|
using TakeoutSaaS.Domain.Identity.Entities;
|
|
using TakeoutSaaS.Domain.Identity.Enums;
|
|
|
|
namespace TakeoutSaaS.Application.Identity.Handlers;
|
|
|
|
/// <summary>
|
|
/// 菜单映射辅助。
|
|
/// </summary>
|
|
internal static class MenuMapper
|
|
{
|
|
/// <summary>
|
|
/// 将菜单实体映射为 DTO。
|
|
/// </summary>
|
|
/// <param name="entity">菜单实体。</param>
|
|
/// <returns>菜单定义 DTO。</returns>
|
|
public static MenuDefinitionDto ToDto(MenuDefinition entity)
|
|
{
|
|
// 1. 解析权限字段
|
|
var requiredPermissions = SplitCodes(entity.RequiredPermissions);
|
|
var metaPermissions = SplitCodes(entity.MetaPermissions);
|
|
var metaRoles = SplitCodes(entity.MetaRoles);
|
|
|
|
// 2. 解析按钮权限
|
|
var authList = string.IsNullOrWhiteSpace(entity.AuthListJson)
|
|
? []
|
|
: JsonSerializer.Deserialize<List<MenuAuthItemDto>>(entity.AuthListJson) ?? [];
|
|
|
|
// 3. 输出 DTO
|
|
return new MenuDefinitionDto
|
|
{
|
|
Id = entity.Id,
|
|
ParentId = entity.ParentId,
|
|
Name = entity.Name,
|
|
Path = entity.Path,
|
|
Component = entity.Component,
|
|
Title = entity.Title,
|
|
Icon = entity.Icon,
|
|
IsIframe = entity.IsIframe,
|
|
Link = entity.Link,
|
|
KeepAlive = entity.KeepAlive,
|
|
SortOrder = entity.SortOrder,
|
|
RequiredPermissions = requiredPermissions,
|
|
MetaPermissions = metaPermissions,
|
|
MetaRoles = metaRoles,
|
|
AuthList = authList
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将 DTO 字段填充到实体。
|
|
/// </summary>
|
|
/// <param name="entity">菜单实体。</param>
|
|
/// <param name="dto">菜单 DTO。</param>
|
|
public static void FillEntity(MenuDefinition entity, MenuDefinitionDto dto)
|
|
{
|
|
// 1. 赋值基础字段
|
|
entity.ParentId = dto.ParentId;
|
|
entity.Name = dto.Name;
|
|
entity.Path = dto.Path;
|
|
entity.Component = dto.Component;
|
|
entity.Title = dto.Title;
|
|
entity.Icon = dto.Icon;
|
|
entity.IsIframe = dto.IsIframe;
|
|
entity.Link = dto.Link;
|
|
entity.KeepAlive = dto.KeepAlive;
|
|
entity.SortOrder = dto.SortOrder;
|
|
|
|
// 2. 权限与按钮
|
|
entity.RequiredPermissions = JoinCodes(dto.RequiredPermissions);
|
|
entity.MetaPermissions = JoinCodes(dto.MetaPermissions);
|
|
entity.MetaRoles = JoinCodes(dto.MetaRoles);
|
|
entity.AuthListJson = dto.AuthList.Count == 0
|
|
? null
|
|
: JsonSerializer.Serialize(dto.AuthList);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 构建或更新菜单实体并返回 DTO。
|
|
/// </summary>
|
|
/// <param name="existing">已存在的菜单实体。</param>
|
|
/// <param name="portal">门户类型。</param>
|
|
/// <param name="payload">菜单 DTO 载荷。</param>
|
|
/// <returns>菜单定义 DTO。</returns>
|
|
public static MenuDefinitionDto FromCommand(MenuDefinition? existing, PortalType portal, MenuDefinitionDto payload)
|
|
{
|
|
// 1. 构造实体
|
|
var entity = existing ?? new MenuDefinition
|
|
{
|
|
Portal = portal,
|
|
CreatedAt = DateTime.UtcNow
|
|
};
|
|
|
|
// 2. 填充字段
|
|
FillEntity(entity, payload);
|
|
|
|
// 3. 返回 DTO 映射
|
|
return ToDto(entity);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将权限/角色集合合并为存储字符串。
|
|
/// </summary>
|
|
/// <param name="codes">编码集合。</param>
|
|
/// <returns>逗号分隔字符串。</returns>
|
|
public static string JoinCodes(IEnumerable<string> codes)
|
|
{
|
|
return string.Join(',', codes.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).Distinct(StringComparer.OrdinalIgnoreCase));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将逗号分隔编码拆分为集合。
|
|
/// </summary>
|
|
/// <param name="codes">编码字符串。</param>
|
|
/// <returns>编码数组。</returns>
|
|
public static string[] SplitCodes(string? codes)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(codes))
|
|
{
|
|
return Array.Empty<string>();
|
|
}
|
|
|
|
return codes
|
|
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
|
.ToArray();
|
|
}
|
|
}
|