using Model;
using Model.Dto.System;
using Model.Entity.System;
using Model.Mapper;
using ORM;
using Service.Interface;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Service.Implement
{
///
/// 角色权限管理 服务实现
///
public class RoleService : IRoleService
{
public async Task>> GetPagedAsync(int pageIndex, int pageSize, RefAsync total, string? keyword = null)
{
try
{
var list = await SqlSugarContext.DbContext.Queryable()
.Where(x => x.IsDel == 0)
.WhereIF(!string.IsNullOrWhiteSpace(keyword), x => x.Code.Contains(keyword!) || x.Name.Contains(keyword!))
.OrderBy(x => x.Sort).OrderBy(x => x.CreateTime, OrderByType.Desc)
.ToPageListAsync(pageIndex, pageSize, total);
var dtos = list.ToDtoList();
await FillPermissionsAsync(dtos);
return Result>.Success(dtos);
}
catch (Exception ex)
{
return Result>.Error("查询角色列表失败", ex);
}
}
public async Task> GetByIdAsync(long id)
{
try
{
var entity = await SqlSugarContext.DbContext.Queryable()
.Where(x => x.Id == id && x.IsDel == 0).FirstAsync();
if (entity == null) return Result.Error("角色不存在或已被删除");
var dto = entity.ToDto();
await FillPermissionsAsync(new List { dto });
return Result.Success(dto);
}
catch (Exception ex)
{
return Result.Error("查询角色详情失败", ex);
}
}
public async Task> AddAsync(RoleDto dto)
{
if (dto == null || string.IsNullOrWhiteSpace(dto.Code) || string.IsNullOrWhiteSpace(dto.Name))
return Result.Error("角色编码和名称不能为空");
try
{
var exists = await SqlSugarContext.DbContext.Queryable()
.Where(x => x.Code == dto.Code && x.IsDel == 0).AnyAsync();
if (exists) return Result.Error($"角色编码「{dto.Code}」已存在");
var entity = dto.ToEntity();
entity.Id = 0;
entity.IsSystem = 0; // 新建角色一律非系统内置
entity.CreateTime = DateTime.Now;
var id = await SqlSugarContext.DbContext.Insertable(entity).ExecuteReturnSnowflakeIdAsync();
entity.Id = id;
if (dto.PermissionIds != null && dto.PermissionIds.Count > 0)
{
var permIds = dto.PermissionIds.Select(long.Parse).Where(p => p > 0).ToList();
await ReplaceRolePermissionsAsync(id, permIds);
}
var result = entity.ToDto();
await FillPermissionsAsync(new List { result });
return Result.Success(result);
}
catch (Exception ex)
{
return Result.Error("新增角色失败", ex);
}
}
public async Task> UpdateAsync(RoleDto dto)
{
if (dto == null || !long.TryParse(dto.Id, out var id) || id <= 0)
return Result.Error("角色Id无效");
try
{
var entity = await SqlSugarContext.DbContext.Queryable()
.Where(x => x.Id == id && x.IsDel == 0).FirstAsync();
if (entity == null) return Result.Error("角色不存在或已被删除");
// 系统内置角色不允许改编码
if (entity.IsSystem == 1 && dto.Code != entity.Code)
return Result.Error($"系统内置角色「{entity.Name}」不允许修改编码");
var codeExists = await SqlSugarContext.DbContext.Queryable()
.Where(x => x.Code == dto.Code && x.Id != id && x.IsDel == 0).AnyAsync();
if (codeExists) return Result.Error($"角色编码「{dto.Code}」已存在");
var newEntity = dto.ToEntity();
newEntity.Id = id;
newEntity.IsSystem = entity.IsSystem; // 内置标记不可被前端覆盖
await SqlSugarContext.DbContext.Updateable(newEntity)
.IgnoreColumns(x => new { x.CreateTime, x.IsDel, x.IsSystem })
.ExecuteCommandAsync();
// 权限变化时同步更新关联(传入 null 表示不改权限)
if (dto.PermissionIds != null)
{
var permIds = dto.PermissionIds.Select(long.Parse).Where(p => p > 0).ToList();
var guard = await EnsureAdminRemainsAfterRoleChangeAsync(id, permIds);
if (guard != null) return Result.Error(guard.Msg);
await ReplaceRolePermissionsAsync(id, permIds);
}
var result = newEntity.ToDto();
await FillPermissionsAsync(new List { result });
return Result.Success(result);
}
catch (Exception ex)
{
return Result.Error("修改角色失败", ex);
}
}
public async Task DeleteAsync(long id)
{
if (id <= 0) return Result.Error("角色Id无效");
try
{
var entity = await SqlSugarContext.DbContext.Queryable()
.Where(x => x.Id == id && x.IsDel == 0).FirstAsync();
if (entity == null) return Result.Error("角色不存在或已被删除");
if (entity.IsSystem == 1)
return Result.Error($"系统内置角色「{entity.Name}」不允许删除");
var usedByUser = await SqlSugarContext.DbContext.Queryable()
.Where(x => x.RoleId == id).AnyAsync();
if (usedByUser)
return Result.Error("该角色下存在用户,请先移除角色下的用户再删除");
await SqlSugarContext.DbContext.Updateable()
.SetColumns(x => x.IsDel == 1)
.Where(x => x.Id == id).ExecuteCommandAsync();
await SqlSugarContext.DbContext.Deleteable()
.Where(x => x.RoleId == id).ExecuteCommandAsync();
return Result.Success();
}
catch (Exception ex)
{
return Result.Error("删除角色失败", ex);
}
}
public async Task AssignPermissionsAsync(long roleId, List permissionIds)
{
if (roleId <= 0) return Result.Error("角色Id无效");
try
{
var exists = await SqlSugarContext.DbContext.Queryable()
.Where(x => x.Id == roleId && x.IsDel == 0).AnyAsync();
if (!exists) return Result.Error("角色不存在或已被删除");
// 防锁死:移除角色的用户管理权限后,系统必须仍有其它可管理用户的管理员
var newIds = permissionIds ?? new List();
var guard = await EnsureAdminRemainsAfterRoleChangeAsync(roleId, newIds);
if (guard != null) return guard;
await ReplaceRolePermissionsAsync(roleId, newIds);
return Result.Success();
}
catch (Exception ex)
{
return Result.Error("分配权限失败", ex);
}
}
public async Task>> GetOptionsAsync()
{
try
{
var list = await SqlSugarContext.DbContext.Queryable()
.Where(x => x.IsDel == 0)
.OrderBy(x => x.Sort).OrderBy(x => x.Code)
.ToListAsync();
return Result>.Success(list.ToOptionDtoList());
}
catch (Exception ex)
{
return Result>.Error("查询角色选项失败", ex);
}
}
public async Task>> GetPermissionsAsync()
{
try
{
var list = await SqlSugarContext.DbContext.Queryable()
.Where(x => x.IsDel == 0)
.OrderBy(x => x.Sort).OrderBy(x => x.Code)
.ToListAsync();
return Result>.Success(list.ToDtoList());
}
catch (Exception ex)
{
return Result>.Error("查询权限列表失败", ex);
}
}
/// 全量覆盖角色-权限关联
private static async Task ReplaceRolePermissionsAsync(long roleId, List permissionIds)
{
var db = SqlSugarContext.DbContext;
await db.Deleteable().Where(x => x.RoleId == roleId).ExecuteCommandAsync();
if (permissionIds.Count > 0)
{
var now = DateTime.Now;
var links = permissionIds.Distinct().Select(p => new RolePermissionEntity { RoleId = roleId, PermissionId = p, CreateTime = now }).ToList();
await db.Insertable(links).ExecuteCommandAsync();
}
}
///
/// 防锁死校验:当 roleId 将失去 user:manage 权限时,系统必须仍存在
/// 「已启用且通过其它角色拥有 user:manage 权限」的用户,否则返回 Error
///
private async Task EnsureAdminRemainsAfterRoleChangeAsync(long roleId, List newPermissionIds)
{
var db = SqlSugarContext.DbContext;
var managePermId = await db.Queryable()
.Where(x => x.Code == "user:manage" && x.IsDel == 0)
.Select(x => x.Id).FirstAsync();
if (managePermId == 0) return null;
bool currentlyHas = await db.Queryable()
.AnyAsync(x => x.RoleId == roleId && x.PermissionId == managePermId);
bool willHave = newPermissionIds.Contains(managePermId);
if (currentlyHas && !willHave)
{
var otherRoleIds = await db.Queryable()
.Where(x => x.PermissionId == managePermId && x.RoleId != roleId)
.Select(x => x.RoleId).ToListAsync();
var otherUserIds = otherRoleIds.Count > 0
? await db.Queryable().Where(x => otherRoleIds.Contains(x.RoleId)).Select(x => x.UserId).ToListAsync()
: new List();
var exists = otherUserIds.Count > 0 && await db.Queryable()
.AnyAsync(x => otherUserIds.Contains(x.Id) && x.IsDel == 0 && x.IsEnabled == 1);
if (!exists)
return Result.Error("操作被拒绝:移除该角色的用户管理权限后,系统将没有任何可管理用户的管理员");
}
return null;
}
/// 给角色 DTO 填充权限Id列表与权限编码列表
private static async Task FillPermissionsAsync(List dtos)
{
if (dtos == null || dtos.Count == 0) return;
var db = SqlSugarContext.DbContext;
var roleIds = dtos.Select(d => long.Parse(d.Id)).ToList();
var links = await db.Queryable()
.Where(x => roleIds.Contains(x.RoleId)).ToListAsync();
var permIds = links.Select(l => l.PermissionId).Distinct().ToList();
var perms = permIds.Count > 0
? await db.Queryable().Where(x => permIds.Contains(x.Id) && x.IsDel == 0).ToListAsync()
: new List();
var permMap = perms.ToDictionary(p => p.Id, p => p.Code);
foreach (var d in dtos)
{
var rid = long.Parse(d.Id);
var myPermIds = links.Where(l => l.RoleId == rid).Select(l => l.PermissionId).ToList();
d.PermissionIds = myPermIds.Select(p => p.ToString()).ToList();
d.PermissionCodes = myPermIds.Where(p => permMap.ContainsKey(p)).Select(p => permMap[p]).ToList();
}
}
}
}