feat: 新增RBAC角色模板复制与初始化
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
using TakeoutSaaS.Application.Identity.Templates;
|
||||
|
||||
namespace TakeoutSaaS.Application.Identity.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// 角色模板提供者,用于获取预置模板定义。
|
||||
/// </summary>
|
||||
public interface IRoleTemplateProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取全部角色模板定义。
|
||||
/// </summary>
|
||||
/// <returns>模板定义集合。</returns>
|
||||
IReadOnlyList<RoleTemplateDefinition> GetTemplates();
|
||||
|
||||
/// <summary>
|
||||
/// 根据模板编码查找模板。
|
||||
/// </summary>
|
||||
/// <param name="templateCode">模板编码。</param>
|
||||
/// <returns>模板定义;不存在时返回 null。</returns>
|
||||
RoleTemplateDefinition? FindByCode(string templateCode);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using MediatR;
|
||||
using TakeoutSaaS.Application.Identity.Contracts;
|
||||
|
||||
namespace TakeoutSaaS.Application.Identity.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// 从预置模板复制角色并绑定权限。
|
||||
/// </summary>
|
||||
public sealed record CopyRoleTemplateCommand : IRequest<RoleDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// 模板编码。
|
||||
/// </summary>
|
||||
public string TemplateCode { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 复制后角色名称(为空则使用模板名称)。
|
||||
/// </summary>
|
||||
public string? RoleName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 复制后角色编码(为空则使用模板编码)。
|
||||
/// </summary>
|
||||
public string? RoleCode { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 角色描述(为空则沿用模板描述)。
|
||||
/// </summary>
|
||||
public string? Description { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
using MediatR;
|
||||
using TakeoutSaaS.Application.Identity.Contracts;
|
||||
|
||||
namespace TakeoutSaaS.Application.Identity.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// 批量为当前租户初始化角色模板。
|
||||
/// </summary>
|
||||
public sealed record InitializeRoleTemplatesCommand : IRequest<IReadOnlyList<RoleDto>>
|
||||
{
|
||||
/// <summary>
|
||||
/// 需要初始化的模板编码列表(为空则全部)。
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<string>? TemplateCodes { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace TakeoutSaaS.Application.Identity.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// 权限模板 DTO。
|
||||
/// </summary>
|
||||
public sealed record PermissionTemplateDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 权限编码。
|
||||
/// </summary>
|
||||
public string Code { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 权限名称。
|
||||
/// </summary>
|
||||
public string Name { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 权限描述。
|
||||
/// </summary>
|
||||
public string? Description { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TakeoutSaaS.Application.Identity.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// 角色模板 DTO。
|
||||
/// </summary>
|
||||
public sealed record RoleTemplateDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 模板编码。
|
||||
/// </summary>
|
||||
public string TemplateCode { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 模板名称。
|
||||
/// </summary>
|
||||
public string Name { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 模板描述。
|
||||
/// </summary>
|
||||
public string? Description { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 包含的权限定义。
|
||||
/// </summary>
|
||||
public IReadOnlyList<PermissionTemplateDto> Permissions { get; init; } = [];
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using TakeoutSaaS.Application.Identity.Abstractions;
|
||||
using TakeoutSaaS.Application.Identity.Services;
|
||||
using TakeoutSaaS.Application.Identity.Templates;
|
||||
|
||||
namespace TakeoutSaaS.Application.Identity.Extensions;
|
||||
|
||||
@@ -17,6 +18,7 @@ public static class IdentityServiceCollectionExtensions
|
||||
public static IServiceCollection AddIdentityApplication(this IServiceCollection services, bool enableMiniSupport = false)
|
||||
{
|
||||
services.AddScoped<IAdminAuthService, AdminAuthService>();
|
||||
services.AddSingleton<IRoleTemplateProvider, RoleTemplateProvider>();
|
||||
|
||||
if (enableMiniSupport)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MediatR;
|
||||
using TakeoutSaaS.Application.Identity.Abstractions;
|
||||
using TakeoutSaaS.Application.Identity.Commands;
|
||||
using TakeoutSaaS.Application.Identity.Contracts;
|
||||
using TakeoutSaaS.Domain.Identity.Entities;
|
||||
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
|
||||
namespace TakeoutSaaS.Application.Identity.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// 角色模板复制处理器。
|
||||
/// </summary>
|
||||
public sealed class CopyRoleTemplateCommandHandler(
|
||||
IRoleTemplateProvider roleTemplateProvider,
|
||||
IRoleRepository roleRepository,
|
||||
IPermissionRepository permissionRepository,
|
||||
IRolePermissionRepository rolePermissionRepository,
|
||||
ITenantProvider tenantProvider)
|
||||
: IRequestHandler<CopyRoleTemplateCommand, RoleDto>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<RoleDto> Handle(CopyRoleTemplateCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var template = roleTemplateProvider.FindByCode(request.TemplateCode)
|
||||
?? throw new BusinessException(ErrorCodes.NotFound, $"角色模板 {request.TemplateCode} 不存在");
|
||||
|
||||
var tenantId = tenantProvider.GetCurrentTenantId();
|
||||
var roleCode = string.IsNullOrWhiteSpace(request.RoleCode) ? template.TemplateCode : request.RoleCode.Trim();
|
||||
var roleName = string.IsNullOrWhiteSpace(request.RoleName) ? template.Name : request.RoleName.Trim();
|
||||
var roleDescription = request.Description ?? template.Description;
|
||||
|
||||
// 1. 准备或更新角色主体(幂等创建)。
|
||||
var role = await roleRepository.FindByCodeAsync(roleCode, tenantId, cancellationToken);
|
||||
if (role is null)
|
||||
{
|
||||
role = new Role
|
||||
{
|
||||
TenantId = tenantId,
|
||||
Name = roleName,
|
||||
Code = roleCode,
|
||||
Description = roleDescription
|
||||
};
|
||||
await roleRepository.AddAsync(role, cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(request.RoleName))
|
||||
{
|
||||
role.Name = roleName;
|
||||
}
|
||||
|
||||
if (request.Description is not null)
|
||||
{
|
||||
role.Description = roleDescription;
|
||||
}
|
||||
|
||||
await roleRepository.UpdateAsync(role, cancellationToken);
|
||||
}
|
||||
|
||||
// 2. 确保模板权限全部存在,不存在则按模板定义创建。
|
||||
var targetPermissionCodes = template.Permissions
|
||||
.Select(permission => permission.Code)
|
||||
.Where(code => !string.IsNullOrWhiteSpace(code))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray();
|
||||
|
||||
var existingPermissions = await permissionRepository.GetByCodesAsync(tenantId, targetPermissionCodes, cancellationToken);
|
||||
var permissionMap = existingPermissions.ToDictionary(x => x.Code, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var permissionDefinition in template.Permissions)
|
||||
{
|
||||
if (permissionMap.ContainsKey(permissionDefinition.Code))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var permission = new Permission
|
||||
{
|
||||
TenantId = tenantId,
|
||||
Name = permissionDefinition.Name,
|
||||
Code = permissionDefinition.Code,
|
||||
Description = permissionDefinition.Description
|
||||
};
|
||||
|
||||
await permissionRepository.AddAsync(permission, cancellationToken);
|
||||
permissionMap[permissionDefinition.Code] = permission;
|
||||
}
|
||||
|
||||
await roleRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 3. 绑定缺失的权限,保留租户自定义的已有授权。
|
||||
var rolePermissions = await rolePermissionRepository.GetByRoleIdsAsync(tenantId, new[] { role.Id }, cancellationToken);
|
||||
var existingPermissionIds = rolePermissions
|
||||
.Select(x => x.PermissionId)
|
||||
.ToHashSet();
|
||||
|
||||
var targetPermissionIds = targetPermissionCodes
|
||||
.Select(code => permissionMap[code].Id)
|
||||
.ToHashSet();
|
||||
|
||||
var toAdd = targetPermissionIds.Except(existingPermissionIds).ToArray();
|
||||
if (toAdd.Length > 0)
|
||||
{
|
||||
var relations = toAdd.Select(permissionId => new RolePermission
|
||||
{
|
||||
TenantId = tenantId,
|
||||
RoleId = role.Id,
|
||||
PermissionId = permissionId
|
||||
});
|
||||
|
||||
await rolePermissionRepository.AddRangeAsync(relations, cancellationToken);
|
||||
}
|
||||
|
||||
await rolePermissionRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return new RoleDto
|
||||
{
|
||||
Id = role.Id,
|
||||
TenantId = role.TenantId,
|
||||
Name = role.Name,
|
||||
Code = role.Code,
|
||||
Description = role.Description
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using MediatR;
|
||||
using TakeoutSaaS.Application.Identity.Abstractions;
|
||||
using TakeoutSaaS.Application.Identity.Contracts;
|
||||
using TakeoutSaaS.Application.Identity.Queries;
|
||||
|
||||
namespace TakeoutSaaS.Application.Identity.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// 角色模板详情查询处理器。
|
||||
/// </summary>
|
||||
public sealed class GetRoleTemplateQueryHandler(IRoleTemplateProvider roleTemplateProvider)
|
||||
: IRequestHandler<GetRoleTemplateQuery, RoleTemplateDto?>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Task<RoleTemplateDto?> Handle(GetRoleTemplateQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var template = roleTemplateProvider.FindByCode(request.TemplateCode);
|
||||
return Task.FromResult(template is null ? null : TemplateMapper.ToDto(template));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MediatR;
|
||||
using TakeoutSaaS.Application.Identity.Abstractions;
|
||||
using TakeoutSaaS.Application.Identity.Commands;
|
||||
using TakeoutSaaS.Application.Identity.Contracts;
|
||||
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||
|
||||
namespace TakeoutSaaS.Application.Identity.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// 租户角色模板批量初始化处理器。
|
||||
/// </summary>
|
||||
public sealed class InitializeRoleTemplatesCommandHandler(
|
||||
IRoleTemplateProvider roleTemplateProvider,
|
||||
IMediator mediator)
|
||||
: IRequestHandler<InitializeRoleTemplatesCommand, IReadOnlyList<RoleDto>>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<RoleDto>> Handle(InitializeRoleTemplatesCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// 1. 解析需要初始化的模板编码,默认取全部预置模板。
|
||||
var requestedCodes = request.TemplateCodes?
|
||||
.Where(code => !string.IsNullOrWhiteSpace(code))
|
||||
.Select(code => code.Trim())
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray();
|
||||
|
||||
var targetCodes = requestedCodes?.Length > 0
|
||||
? requestedCodes
|
||||
: roleTemplateProvider.GetTemplates().Select(template => template.TemplateCode).ToArray();
|
||||
|
||||
if (targetCodes.Length == 0)
|
||||
{
|
||||
return Array.Empty<RoleDto>();
|
||||
}
|
||||
|
||||
// 2. 逐个复制模板,幂等写入角色与权限。
|
||||
var roles = new List<RoleDto>(targetCodes.Length);
|
||||
foreach (var templateCode in targetCodes)
|
||||
{
|
||||
var template = roleTemplateProvider.FindByCode(templateCode)
|
||||
?? throw new BusinessException(ErrorCodes.NotFound, $"角色模板 {templateCode} 不存在");
|
||||
|
||||
var role = await mediator.Send(new CopyRoleTemplateCommand
|
||||
{
|
||||
TemplateCode = template.TemplateCode
|
||||
}, cancellationToken);
|
||||
|
||||
roles.Add(role);
|
||||
}
|
||||
|
||||
return roles;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using MediatR;
|
||||
using TakeoutSaaS.Application.Identity.Abstractions;
|
||||
using TakeoutSaaS.Application.Identity.Contracts;
|
||||
using TakeoutSaaS.Application.Identity.Queries;
|
||||
|
||||
namespace TakeoutSaaS.Application.Identity.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// 角色模板列表查询处理器。
|
||||
/// </summary>
|
||||
public sealed class ListRoleTemplatesQueryHandler(IRoleTemplateProvider roleTemplateProvider)
|
||||
: IRequestHandler<ListRoleTemplatesQuery, IReadOnlyList<RoleTemplateDto>>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<RoleTemplateDto>> Handle(ListRoleTemplatesQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var templates = roleTemplateProvider.GetTemplates()
|
||||
.OrderBy(template => template.TemplateCode, StringComparer.OrdinalIgnoreCase)
|
||||
.Select(TemplateMapper.ToDto)
|
||||
.ToArray();
|
||||
|
||||
return Task.FromResult<IReadOnlyList<RoleTemplateDto>>(templates);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System.Linq;
|
||||
using TakeoutSaaS.Application.Identity.Contracts;
|
||||
using TakeoutSaaS.Application.Identity.Templates;
|
||||
|
||||
namespace TakeoutSaaS.Application.Identity.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// 模板 DTO 映射工具。
|
||||
/// </summary>
|
||||
internal static class TemplateMapper
|
||||
{
|
||||
/// <summary>
|
||||
/// 将角色模板定义映射为 DTO。
|
||||
/// </summary>
|
||||
/// <param name="definition">角色模板定义。</param>
|
||||
/// <returns>模板 DTO。</returns>
|
||||
public static RoleTemplateDto ToDto(RoleTemplateDefinition definition)
|
||||
{
|
||||
return new RoleTemplateDto
|
||||
{
|
||||
TemplateCode = definition.TemplateCode,
|
||||
Name = definition.Name,
|
||||
Description = definition.Description,
|
||||
Permissions = definition.Permissions.Select(ToDto).ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
private static PermissionTemplateDto ToDto(PermissionTemplateDefinition definition)
|
||||
{
|
||||
return new PermissionTemplateDto
|
||||
{
|
||||
Code = definition.Code,
|
||||
Name = definition.Name,
|
||||
Description = definition.Description
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using MediatR;
|
||||
using TakeoutSaaS.Application.Identity.Contracts;
|
||||
|
||||
namespace TakeoutSaaS.Application.Identity.Queries;
|
||||
|
||||
/// <summary>
|
||||
/// 获取单个角色模板详情。
|
||||
/// </summary>
|
||||
public sealed record GetRoleTemplateQuery : IRequest<RoleTemplateDto?>
|
||||
{
|
||||
/// <summary>
|
||||
/// 模板编码。
|
||||
/// </summary>
|
||||
public string TemplateCode { get; init; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using MediatR;
|
||||
using TakeoutSaaS.Application.Identity.Contracts;
|
||||
|
||||
namespace TakeoutSaaS.Application.Identity.Queries;
|
||||
|
||||
/// <summary>
|
||||
/// 查询角色模板列表。
|
||||
/// </summary>
|
||||
public sealed record ListRoleTemplatesQuery : IRequest<IReadOnlyList<RoleTemplateDto>>;
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace TakeoutSaaS.Application.Identity.Templates;
|
||||
|
||||
/// <summary>
|
||||
/// 权限模板定义。
|
||||
/// </summary>
|
||||
public sealed record PermissionTemplateDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// 权限编码(唯一键)。
|
||||
/// </summary>
|
||||
public required string Code { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 权限名称。
|
||||
/// </summary>
|
||||
public required string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 权限描述。
|
||||
/// </summary>
|
||||
public string? Description { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TakeoutSaaS.Application.Identity.Templates;
|
||||
|
||||
/// <summary>
|
||||
/// 角色模板定义。
|
||||
/// </summary>
|
||||
public sealed record RoleTemplateDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// 模板编码(唯一键)。
|
||||
/// </summary>
|
||||
public required string TemplateCode { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 角色名称。
|
||||
/// </summary>
|
||||
public required string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 角色描述。
|
||||
/// </summary>
|
||||
public string? Description { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 模板绑定的权限集合。
|
||||
/// </summary>
|
||||
public IReadOnlyList<PermissionTemplateDefinition> Permissions { get; init; } = [];
|
||||
}
|
||||
@@ -0,0 +1,480 @@
|
||||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TakeoutSaaS.Application.Identity.Abstractions;
|
||||
|
||||
namespace TakeoutSaaS.Application.Identity.Templates;
|
||||
|
||||
/// <summary>
|
||||
/// 预置角色模板提供者。
|
||||
/// </summary>
|
||||
public sealed class RoleTemplateProvider : IRoleTemplateProvider
|
||||
{
|
||||
private static readonly FrozenDictionary<string, PermissionTemplateDefinition> PermissionDefinitions =
|
||||
new Dictionary<string, PermissionTemplateDefinition>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["identity:role:read"] = new()
|
||||
{
|
||||
Code = "identity:role:read",
|
||||
Name = "角色查询",
|
||||
Description = "查看当前租户的角色列表与详情。"
|
||||
},
|
||||
["identity:role:create"] = new()
|
||||
{
|
||||
Code = "identity:role:create",
|
||||
Name = "角色创建",
|
||||
Description = "在当前租户内创建新角色。"
|
||||
},
|
||||
["identity:role:update"] = new()
|
||||
{
|
||||
Code = "identity:role:update",
|
||||
Name = "角色更新",
|
||||
Description = "修改角色名称与描述。"
|
||||
},
|
||||
["identity:role:delete"] = new()
|
||||
{
|
||||
Code = "identity:role:delete",
|
||||
Name = "角色删除",
|
||||
Description = "删除租户内的角色记录。"
|
||||
},
|
||||
["identity:role:bind-permission"] = new()
|
||||
{
|
||||
Code = "identity:role:bind-permission",
|
||||
Name = "角色权限绑定",
|
||||
Description = "为角色绑定或调整权限集合。"
|
||||
},
|
||||
["identity:permission:read"] = new()
|
||||
{
|
||||
Code = "identity:permission:read",
|
||||
Name = "权限查询",
|
||||
Description = "查看权限列表与详情。"
|
||||
},
|
||||
["identity:permission:create"] = new()
|
||||
{
|
||||
Code = "identity:permission:create",
|
||||
Name = "权限创建",
|
||||
Description = "创建新的权限定义。"
|
||||
},
|
||||
["identity:permission:update"] = new()
|
||||
{
|
||||
Code = "identity:permission:update",
|
||||
Name = "权限更新",
|
||||
Description = "修改权限名称或描述。"
|
||||
},
|
||||
["identity:permission:delete"] = new()
|
||||
{
|
||||
Code = "identity:permission:delete",
|
||||
Name = "权限删除",
|
||||
Description = "删除租户内的权限记录。"
|
||||
},
|
||||
["identity:profile:read"] = new()
|
||||
{
|
||||
Code = "identity:profile:read",
|
||||
Name = "个人信息查看",
|
||||
Description = "查看当前登录人的账号与权限信息。"
|
||||
},
|
||||
["tenant:create"] = new()
|
||||
{
|
||||
Code = "tenant:create",
|
||||
Name = "租户创建",
|
||||
Description = "创建新的租户记录。"
|
||||
},
|
||||
["tenant:read"] = new()
|
||||
{
|
||||
Code = "tenant:read",
|
||||
Name = "租户查询",
|
||||
Description = "查看租户列表与详情。"
|
||||
},
|
||||
["tenant:review"] = new()
|
||||
{
|
||||
Code = "tenant:review",
|
||||
Name = "租户审核",
|
||||
Description = "审核租户实名与资质信息。"
|
||||
},
|
||||
["tenant:subscription"] = new()
|
||||
{
|
||||
Code = "tenant:subscription",
|
||||
Name = "租户订阅管理",
|
||||
Description = "创建、续费或调整租户套餐订阅。"
|
||||
},
|
||||
["merchant:create"] = new()
|
||||
{
|
||||
Code = "merchant:create",
|
||||
Name = "商户创建",
|
||||
Description = "创建或提交商户入驻信息。"
|
||||
},
|
||||
["merchant:read"] = new()
|
||||
{
|
||||
Code = "merchant:read",
|
||||
Name = "商户查看",
|
||||
Description = "查看商户资料与审核状态。"
|
||||
},
|
||||
["merchant:update"] = new()
|
||||
{
|
||||
Code = "merchant:update",
|
||||
Name = "商户更新",
|
||||
Description = "更新商户资料或合同等信息。"
|
||||
},
|
||||
["merchant:delete"] = new()
|
||||
{
|
||||
Code = "merchant:delete",
|
||||
Name = "商户删除",
|
||||
Description = "删除或作废商户记录。"
|
||||
},
|
||||
["merchant:review"] = new()
|
||||
{
|
||||
Code = "merchant:review",
|
||||
Name = "商户审核",
|
||||
Description = "审核商户入驻、合同与证照。"
|
||||
},
|
||||
["merchant_category:read"] = new()
|
||||
{
|
||||
Code = "merchant_category:read",
|
||||
Name = "类目查询",
|
||||
Description = "查看经营类目列表。"
|
||||
},
|
||||
["merchant_category:create"] = new()
|
||||
{
|
||||
Code = "merchant_category:create",
|
||||
Name = "类目创建",
|
||||
Description = "新增经营类目。"
|
||||
},
|
||||
["merchant_category:update"] = new()
|
||||
{
|
||||
Code = "merchant_category:update",
|
||||
Name = "类目更新",
|
||||
Description = "调整经营类目名称或顺序。"
|
||||
},
|
||||
["merchant_category:delete"] = new()
|
||||
{
|
||||
Code = "merchant_category:delete",
|
||||
Name = "类目删除",
|
||||
Description = "删除经营类目。"
|
||||
},
|
||||
["store:create"] = new()
|
||||
{
|
||||
Code = "store:create",
|
||||
Name = "门店创建",
|
||||
Description = "创建新的门店记录。"
|
||||
},
|
||||
["store:read"] = new()
|
||||
{
|
||||
Code = "store:read",
|
||||
Name = "门店查看",
|
||||
Description = "查看门店列表与详情。"
|
||||
},
|
||||
["store:update"] = new()
|
||||
{
|
||||
Code = "store:update",
|
||||
Name = "门店更新",
|
||||
Description = "更新门店资料与配置。"
|
||||
},
|
||||
["store:delete"] = new()
|
||||
{
|
||||
Code = "store:delete",
|
||||
Name = "门店删除",
|
||||
Description = "删除或停用门店。"
|
||||
},
|
||||
["product:create"] = new()
|
||||
{
|
||||
Code = "product:create",
|
||||
Name = "商品创建",
|
||||
Description = "创建商品、菜品或规格。"
|
||||
},
|
||||
["product:read"] = new()
|
||||
{
|
||||
Code = "product:read",
|
||||
Name = "商品查看",
|
||||
Description = "查看商品/菜品列表与详情。"
|
||||
},
|
||||
["product:update"] = new()
|
||||
{
|
||||
Code = "product:update",
|
||||
Name = "商品更新",
|
||||
Description = "更新商品、菜品或上下架状态。"
|
||||
},
|
||||
["product:delete"] = new()
|
||||
{
|
||||
Code = "product:delete",
|
||||
Name = "商品删除",
|
||||
Description = "删除商品或菜品。"
|
||||
},
|
||||
["order:create"] = new()
|
||||
{
|
||||
Code = "order:create",
|
||||
Name = "订单创建",
|
||||
Description = "创建订单或手工录单。"
|
||||
},
|
||||
["order:read"] = new()
|
||||
{
|
||||
Code = "order:read",
|
||||
Name = "订单查看",
|
||||
Description = "查看订单列表与详情。"
|
||||
},
|
||||
["order:update"] = new()
|
||||
{
|
||||
Code = "order:update",
|
||||
Name = "订单更新",
|
||||
Description = "修改订单状态或履约信息。"
|
||||
},
|
||||
["order:delete"] = new()
|
||||
{
|
||||
Code = "order:delete",
|
||||
Name = "订单删除",
|
||||
Description = "删除或作废订单。"
|
||||
},
|
||||
["delivery:create"] = new()
|
||||
{
|
||||
Code = "delivery:create",
|
||||
Name = "配送创建",
|
||||
Description = "创建或发起配送单。"
|
||||
},
|
||||
["delivery:read"] = new()
|
||||
{
|
||||
Code = "delivery:read",
|
||||
Name = "配送查看",
|
||||
Description = "查看配送订单与轨迹。"
|
||||
},
|
||||
["delivery:update"] = new()
|
||||
{
|
||||
Code = "delivery:update",
|
||||
Name = "配送更新",
|
||||
Description = "更新配送状态或骑手信息。"
|
||||
},
|
||||
["delivery:delete"] = new()
|
||||
{
|
||||
Code = "delivery:delete",
|
||||
Name = "配送删除",
|
||||
Description = "取消或删除配送单。"
|
||||
},
|
||||
["payment:create"] = new()
|
||||
{
|
||||
Code = "payment:create",
|
||||
Name = "支付创建",
|
||||
Description = "创建收款或支付记录。"
|
||||
},
|
||||
["payment:read"] = new()
|
||||
{
|
||||
Code = "payment:read",
|
||||
Name = "支付查看",
|
||||
Description = "查看支付、退款记录。"
|
||||
},
|
||||
["payment:update"] = new()
|
||||
{
|
||||
Code = "payment:update",
|
||||
Name = "支付更新",
|
||||
Description = "更新支付状态或补充信息。"
|
||||
},
|
||||
["payment:delete"] = new()
|
||||
{
|
||||
Code = "payment:delete",
|
||||
Name = "支付删除",
|
||||
Description = "删除或作废支付记录。"
|
||||
},
|
||||
["dictionary:group:read"] = new()
|
||||
{
|
||||
Code = "dictionary:group:read",
|
||||
Name = "字典分组查询",
|
||||
Description = "查看字典分组与明细。"
|
||||
},
|
||||
["dictionary:group:create"] = new()
|
||||
{
|
||||
Code = "dictionary:group:create",
|
||||
Name = "字典分组创建",
|
||||
Description = "新增字典分组。"
|
||||
},
|
||||
["dictionary:group:update"] = new()
|
||||
{
|
||||
Code = "dictionary:group:update",
|
||||
Name = "字典分组更新",
|
||||
Description = "修改字典分组信息。"
|
||||
},
|
||||
["dictionary:group:delete"] = new()
|
||||
{
|
||||
Code = "dictionary:group:delete",
|
||||
Name = "字典分组删除",
|
||||
Description = "删除字典分组。"
|
||||
},
|
||||
["dictionary:item:create"] = new()
|
||||
{
|
||||
Code = "dictionary:item:create",
|
||||
Name = "字典项创建",
|
||||
Description = "新增字典项。"
|
||||
},
|
||||
["dictionary:item:update"] = new()
|
||||
{
|
||||
Code = "dictionary:item:update",
|
||||
Name = "字典项更新",
|
||||
Description = "调整字典项内容。"
|
||||
},
|
||||
["dictionary:item:delete"] = new()
|
||||
{
|
||||
Code = "dictionary:item:delete",
|
||||
Name = "字典项删除",
|
||||
Description = "删除字典项。"
|
||||
},
|
||||
["system-parameter:create"] = new()
|
||||
{
|
||||
Code = "system-parameter:create",
|
||||
Name = "系统参数创建",
|
||||
Description = "新增系统参数配置。"
|
||||
},
|
||||
["system-parameter:read"] = new()
|
||||
{
|
||||
Code = "system-parameter:read",
|
||||
Name = "系统参数查询",
|
||||
Description = "查看系统参数列表与详情。"
|
||||
},
|
||||
["system-parameter:update"] = new()
|
||||
{
|
||||
Code = "system-parameter:update",
|
||||
Name = "系统参数更新",
|
||||
Description = "更新系统参数配置。"
|
||||
},
|
||||
["system-parameter:delete"] = new()
|
||||
{
|
||||
Code = "system-parameter:delete",
|
||||
Name = "系统参数删除",
|
||||
Description = "删除系统参数配置。"
|
||||
}
|
||||
}.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
private static readonly FrozenDictionary<string, RoleTemplateDefinition> Templates =
|
||||
new Dictionary<string, RoleTemplateDefinition>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["platform-admin"] = new()
|
||||
{
|
||||
TemplateCode = "platform-admin",
|
||||
Name = "平台管理员",
|
||||
Description = "平台全量权限,负责租户、商户、配置与运维。",
|
||||
Permissions = BuildPermissions(PermissionDefinitions.Keys)
|
||||
},
|
||||
["tenant-admin"] = new()
|
||||
{
|
||||
TemplateCode = "tenant-admin",
|
||||
Name = "租户管理员",
|
||||
Description = "管理本租户的门店、商品、订单与团队权限。",
|
||||
Permissions = BuildPermissions(new[]
|
||||
{
|
||||
"identity:profile:read",
|
||||
"identity:role:read",
|
||||
"identity:role:create",
|
||||
"identity:role:update",
|
||||
"identity:role:delete",
|
||||
"identity:role:bind-permission",
|
||||
"identity:permission:read",
|
||||
"identity:permission:create",
|
||||
"identity:permission:update",
|
||||
"identity:permission:delete",
|
||||
"tenant:read",
|
||||
"tenant:subscription",
|
||||
"merchant:read",
|
||||
"merchant:update",
|
||||
"merchant_category:read",
|
||||
"merchant_category:create",
|
||||
"merchant_category:update",
|
||||
"merchant_category:delete",
|
||||
"store:create",
|
||||
"store:read",
|
||||
"store:update",
|
||||
"store:delete",
|
||||
"product:create",
|
||||
"product:read",
|
||||
"product:update",
|
||||
"product:delete",
|
||||
"order:create",
|
||||
"order:read",
|
||||
"order:update",
|
||||
"delivery:create",
|
||||
"delivery:read",
|
||||
"delivery:update",
|
||||
"payment:create",
|
||||
"payment:read",
|
||||
"payment:update",
|
||||
"dictionary:group:read",
|
||||
"dictionary:group:create",
|
||||
"dictionary:group:update",
|
||||
"dictionary:group:delete",
|
||||
"dictionary:item:create",
|
||||
"dictionary:item:update",
|
||||
"dictionary:item:delete",
|
||||
"system-parameter:read"
|
||||
})
|
||||
},
|
||||
["store-manager"] = new()
|
||||
{
|
||||
TemplateCode = "store-manager",
|
||||
Name = "店长",
|
||||
Description = "负责门店日常运营、商品与订单管理。",
|
||||
Permissions = BuildPermissions(new[]
|
||||
{
|
||||
"identity:profile:read",
|
||||
"store:read",
|
||||
"store:update",
|
||||
"product:create",
|
||||
"product:read",
|
||||
"product:update",
|
||||
"order:create",
|
||||
"order:read",
|
||||
"order:update",
|
||||
"delivery:read",
|
||||
"delivery:update",
|
||||
"payment:read",
|
||||
"payment:update",
|
||||
"dictionary:group:read",
|
||||
"dictionary:item:create",
|
||||
"dictionary:item:update",
|
||||
"dictionary:item:delete"
|
||||
})
|
||||
},
|
||||
["store-staff"] = new()
|
||||
{
|
||||
TemplateCode = "store-staff",
|
||||
Name = "店员",
|
||||
Description = "处理订单履约、配送跟踪与收款查询。",
|
||||
Permissions = BuildPermissions(new[]
|
||||
{
|
||||
"identity:profile:read",
|
||||
"store:read",
|
||||
"product:read",
|
||||
"order:read",
|
||||
"order:update",
|
||||
"delivery:read",
|
||||
"payment:read"
|
||||
})
|
||||
}
|
||||
}.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<RoleTemplateDefinition> GetTemplates()
|
||||
=> Templates.Values.ToArray();
|
||||
|
||||
/// <inheritdoc />
|
||||
public RoleTemplateDefinition? FindByCode(string templateCode)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(templateCode))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Templates.GetValueOrDefault(templateCode);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<PermissionTemplateDefinition> BuildPermissions(IEnumerable<string> codes)
|
||||
{
|
||||
return codes
|
||||
.Where(code => !string.IsNullOrWhiteSpace(code))
|
||||
.Select(code => code.Trim())
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.Select(code => PermissionDefinitions.TryGetValue(code, out var definition)
|
||||
? definition
|
||||
: new PermissionTemplateDefinition
|
||||
{
|
||||
Code = code,
|
||||
Name = code,
|
||||
Description = "未在预置表中定义的权限,请补充描述。"
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user