fix: 菜单固定为全局
This commit is contained in:
@@ -60,11 +60,8 @@ public sealed class MenusController(IMediator mediator) : BaseApiController
|
|||||||
[ProducesResponseType(typeof(ApiResponse<MenuDefinitionDto>), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(ApiResponse<MenuDefinitionDto>), StatusCodes.Status200OK)]
|
||||||
public async Task<ApiResponse<MenuDefinitionDto>> Create([FromBody, Required] CreateMenuCommand command, CancellationToken cancellationToken)
|
public async Task<ApiResponse<MenuDefinitionDto>> Create([FromBody, Required] CreateMenuCommand command, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 1. 创建菜单
|
// 1. 菜单已固定,禁止新增
|
||||||
var result = await mediator.Send(command, cancellationToken);
|
return await Task.FromResult(ApiResponse<MenuDefinitionDto>.Error(StatusCodes.Status403Forbidden, "菜单已固定,禁止新增"));
|
||||||
|
|
||||||
// 2. 返回创建结果
|
|
||||||
return ApiResponse<MenuDefinitionDto>.Ok(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -79,16 +76,8 @@ public sealed class MenusController(IMediator mediator) : BaseApiController
|
|||||||
[FromBody, Required] UpdateMenuCommand command,
|
[FromBody, Required] UpdateMenuCommand command,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 1. 绑定菜单 ID
|
// 1. 菜单已固定,禁止修改
|
||||||
command = command with { Id = menuId };
|
return await Task.FromResult(ApiResponse<MenuDefinitionDto>.Error(StatusCodes.Status403Forbidden, "菜单已固定,禁止修改"));
|
||||||
|
|
||||||
// 2. 执行更新
|
|
||||||
var result = await mediator.Send(command, cancellationToken);
|
|
||||||
|
|
||||||
// 3. 返回或 404
|
|
||||||
return result is null
|
|
||||||
? ApiResponse<MenuDefinitionDto>.Error(StatusCodes.Status404NotFound, "菜单不存在")
|
|
||||||
: ApiResponse<MenuDefinitionDto>.Ok(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -99,10 +88,7 @@ public sealed class MenusController(IMediator mediator) : BaseApiController
|
|||||||
[ProducesResponseType(typeof(ApiResponse<bool>), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(ApiResponse<bool>), StatusCodes.Status200OK)]
|
||||||
public async Task<ApiResponse<bool>> Delete(long menuId, CancellationToken cancellationToken)
|
public async Task<ApiResponse<bool>> Delete(long menuId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 1. 删除菜单
|
// 1. 菜单已固定,禁止删除
|
||||||
var result = await mediator.Send(new DeleteMenuCommand { Id = menuId }, cancellationToken);
|
return await Task.FromResult(ApiResponse<bool>.Error(StatusCodes.Status403Forbidden, "菜单已固定,禁止删除"));
|
||||||
|
|
||||||
// 2. 返回执行结果
|
|
||||||
return ApiResponse<bool>.Ok(result);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
using TakeoutSaaS.Application.Identity.Commands;
|
using TakeoutSaaS.Application.Identity.Commands;
|
||||||
using TakeoutSaaS.Application.Identity.Contracts;
|
using TakeoutSaaS.Application.Identity.Contracts;
|
||||||
|
using TakeoutSaaS.Application.Identity;
|
||||||
using TakeoutSaaS.Domain.Identity.Entities;
|
using TakeoutSaaS.Domain.Identity.Entities;
|
||||||
using TakeoutSaaS.Domain.Identity.Repositories;
|
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||||
|
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||||
|
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||||
|
|
||||||
namespace TakeoutSaaS.Application.Identity.Handlers;
|
namespace TakeoutSaaS.Application.Identity.Handlers;
|
||||||
@@ -18,7 +21,13 @@ public sealed class CreateMenuCommandHandler(
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<MenuDefinitionDto> Handle(CreateMenuCommand request, CancellationToken cancellationToken)
|
public async Task<MenuDefinitionDto> Handle(CreateMenuCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 1. 构造实体
|
// 1. 菜单固定时禁止新增
|
||||||
|
if (!MenuPolicy.CanMaintainMenus)
|
||||||
|
{
|
||||||
|
throw new BusinessException(ErrorCodes.Forbidden, "菜单已固定,禁止新增");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 构造实体
|
||||||
var tenantId = tenantProvider.GetCurrentTenantId();
|
var tenantId = tenantProvider.GetCurrentTenantId();
|
||||||
var entity = new MenuDefinition
|
var entity = new MenuDefinition
|
||||||
{
|
{
|
||||||
@@ -41,11 +50,11 @@ public sealed class CreateMenuCommandHandler(
|
|||||||
: System.Text.Json.JsonSerializer.Serialize(request.AuthList)
|
: System.Text.Json.JsonSerializer.Serialize(request.AuthList)
|
||||||
};
|
};
|
||||||
|
|
||||||
// 2. 持久化
|
// 3. 持久化
|
||||||
await menuRepository.AddAsync(entity, cancellationToken);
|
await menuRepository.AddAsync(entity, cancellationToken);
|
||||||
await menuRepository.SaveChangesAsync(cancellationToken);
|
await menuRepository.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
// 3. 返回 DTO
|
// 4. 返回 DTO
|
||||||
return MenuMapper.ToDto(entity);
|
return MenuMapper.ToDto(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
|
using TakeoutSaaS.Application.Identity;
|
||||||
using TakeoutSaaS.Application.Identity.Commands;
|
using TakeoutSaaS.Application.Identity.Commands;
|
||||||
using TakeoutSaaS.Domain.Identity.Repositories;
|
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||||
|
using TakeoutSaaS.Shared.Abstractions.Constants;
|
||||||
|
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
||||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||||
|
|
||||||
namespace TakeoutSaaS.Application.Identity.Handlers;
|
namespace TakeoutSaaS.Application.Identity.Handlers;
|
||||||
@@ -16,14 +19,20 @@ public sealed class DeleteMenuCommandHandler(
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<bool> Handle(DeleteMenuCommand request, CancellationToken cancellationToken)
|
public async Task<bool> Handle(DeleteMenuCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 1. 删除目标及可能的孤儿由外层保证
|
// 1. 菜单固定时禁止删除
|
||||||
|
if (!MenuPolicy.CanMaintainMenus)
|
||||||
|
{
|
||||||
|
throw new BusinessException(ErrorCodes.Forbidden, "菜单已固定,禁止删除");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 删除目标及可能的孤儿由外层保证
|
||||||
var tenantId = tenantProvider.GetCurrentTenantId();
|
var tenantId = tenantProvider.GetCurrentTenantId();
|
||||||
await menuRepository.DeleteAsync(request.Id, tenantId, cancellationToken);
|
await menuRepository.DeleteAsync(request.Id, tenantId, cancellationToken);
|
||||||
|
|
||||||
// 2. 持久化
|
// 3. 持久化
|
||||||
await menuRepository.SaveChangesAsync(cancellationToken);
|
await menuRepository.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
// 3. 返回执行结果
|
// 4. 返回执行结果
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
|
using TakeoutSaaS.Application.Identity;
|
||||||
using TakeoutSaaS.Application.Identity.Commands;
|
using TakeoutSaaS.Application.Identity.Commands;
|
||||||
using TakeoutSaaS.Application.Identity.Contracts;
|
using TakeoutSaaS.Application.Identity.Contracts;
|
||||||
using TakeoutSaaS.Domain.Identity.Repositories;
|
using TakeoutSaaS.Domain.Identity.Repositories;
|
||||||
@@ -19,12 +20,18 @@ public sealed class UpdateMenuCommandHandler(
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<MenuDefinitionDto?> Handle(UpdateMenuCommand request, CancellationToken cancellationToken)
|
public async Task<MenuDefinitionDto?> Handle(UpdateMenuCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 1. 校验存在
|
// 1. 菜单固定时禁止修改
|
||||||
|
if (!MenuPolicy.CanMaintainMenus)
|
||||||
|
{
|
||||||
|
throw new BusinessException(ErrorCodes.Forbidden, "菜单已固定,禁止修改");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 校验存在
|
||||||
var tenantId = tenantProvider.GetCurrentTenantId();
|
var tenantId = tenantProvider.GetCurrentTenantId();
|
||||||
var entity = await menuRepository.FindByIdAsync(request.Id, tenantId, cancellationToken)
|
var entity = await menuRepository.FindByIdAsync(request.Id, tenantId, cancellationToken)
|
||||||
?? throw new BusinessException(ErrorCodes.NotFound, "菜单不存在");
|
?? throw new BusinessException(ErrorCodes.NotFound, "菜单不存在");
|
||||||
|
|
||||||
// 2. 更新字段
|
// 3. 更新字段
|
||||||
entity.ParentId = request.ParentId;
|
entity.ParentId = request.ParentId;
|
||||||
entity.Name = request.Name.Trim();
|
entity.Name = request.Name.Trim();
|
||||||
entity.Path = request.Path.Trim();
|
entity.Path = request.Path.Trim();
|
||||||
@@ -42,11 +49,11 @@ public sealed class UpdateMenuCommandHandler(
|
|||||||
? null
|
? null
|
||||||
: System.Text.Json.JsonSerializer.Serialize(request.AuthList);
|
: System.Text.Json.JsonSerializer.Serialize(request.AuthList);
|
||||||
|
|
||||||
// 3. 持久化
|
// 4. 持久化
|
||||||
await menuRepository.UpdateAsync(entity, cancellationToken);
|
await menuRepository.UpdateAsync(entity, cancellationToken);
|
||||||
await menuRepository.SaveChangesAsync(cancellationToken);
|
await menuRepository.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
// 4. 返回 DTO
|
// 5. 返回 DTO
|
||||||
return MenuMapper.ToDto(entity);
|
return MenuMapper.ToDto(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace TakeoutSaaS.Application.Identity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 菜单维护策略(固定菜单时禁用增删改)。
|
||||||
|
/// </summary>
|
||||||
|
public static class MenuPolicy
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 是否允许维护菜单(创建/更新/删除)。
|
||||||
|
/// </summary>
|
||||||
|
public const bool CanMaintainMenus = false;
|
||||||
|
}
|
||||||
@@ -10,12 +10,15 @@ namespace TakeoutSaaS.Infrastructure.Identity.Repositories;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class EfMenuRepository(IdentityDbContext dbContext) : IMenuRepository
|
public sealed class EfMenuRepository(IdentityDbContext dbContext) : IMenuRepository
|
||||||
{
|
{
|
||||||
|
private const long PlatformRootTenantId = 1000000000001;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<IReadOnlyList<MenuDefinition>> GetByTenantAsync(long tenantId, CancellationToken cancellationToken = default)
|
public Task<IReadOnlyList<MenuDefinition>> GetByTenantAsync(long tenantId, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return dbContext.MenuDefinitions
|
return dbContext.MenuDefinitions
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(x => x.TenantId == tenantId)
|
.IgnoreQueryFilters()
|
||||||
|
.Where(x => x.TenantId == PlatformRootTenantId && x.DeletedAt == null)
|
||||||
.OrderBy(x => x.ParentId)
|
.OrderBy(x => x.ParentId)
|
||||||
.ThenBy(x => x.SortOrder)
|
.ThenBy(x => x.SortOrder)
|
||||||
.ToListAsync(cancellationToken)
|
.ToListAsync(cancellationToken)
|
||||||
@@ -27,7 +30,10 @@ public sealed class EfMenuRepository(IdentityDbContext dbContext) : IMenuReposit
|
|||||||
{
|
{
|
||||||
return dbContext.MenuDefinitions
|
return dbContext.MenuDefinitions
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.FirstOrDefaultAsync(x => x.Id == id && x.TenantId == tenantId, cancellationToken);
|
.IgnoreQueryFilters()
|
||||||
|
.FirstOrDefaultAsync(
|
||||||
|
x => x.Id == id && x.TenantId == PlatformRootTenantId && x.DeletedAt == null,
|
||||||
|
cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
Reference in New Issue
Block a user