RbacRoleAppService.cs 9.55 KB
using System.Text.Json;
using FoodLabeling.Application.Contracts.Constants;
using FoodLabeling.Application.Contracts.Dtos.RbacRole;
using FoodLabeling.Application.Helpers;
using FoodLabeling.Application.Contracts.Dtos.Common;
using FoodLabeling.Application.Contracts.IServices;
using FoodLabeling.Application.Services.DbModels;
using Microsoft.AspNetCore.Mvc;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Uow;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;

namespace FoodLabeling.Application.Services;

/// <summary>
/// 角色管理(食品标签-美国版对外)
/// </summary>
public class RbacRoleAppService : ApplicationService, IRbacRoleAppService
{
    private readonly ISqlSugarDbContext _dbContext;
    private readonly ISqlSugarRepository<RoleAggregateRoot, Guid> _roleRepository;
    private readonly ISqlSugarRepository<RoleMenuEntity> _roleMenuRepository;
    private readonly ISqlSugarRepository<RoleDeptEntity> _roleDeptRepository;
    private readonly ISqlSugarRepository<UserRoleEntity> _userRoleRepository;

    public RbacRoleAppService(
        ISqlSugarDbContext dbContext,
        ISqlSugarRepository<RoleAggregateRoot, Guid> roleRepository,
        ISqlSugarRepository<RoleMenuEntity> roleMenuRepository,
        ISqlSugarRepository<RoleDeptEntity> roleDeptRepository,
        ISqlSugarRepository<UserRoleEntity> userRoleRepository)
    {
        _dbContext = dbContext;
        _roleRepository = roleRepository;
        _roleMenuRepository = roleMenuRepository;
        _roleDeptRepository = roleDeptRepository;
        _userRoleRepository = userRoleRepository;
    }

    /// <inheritdoc />
    public async Task<PagedResultWithPageDto<RbacRoleGetListOutputDto>> GetListAsync([FromQuery] RbacRoleGetListInputVo input)
    {
        RefAsync<int> total = 0;

        var query = _roleRepository._DbQueryable
            .Where(x => x.IsDeleted == false)
            .WhereIF(!string.IsNullOrWhiteSpace(input.RoleCode), x => x.RoleCode.Contains(input.RoleCode!.Trim()))
            .WhereIF(!string.IsNullOrWhiteSpace(input.RoleName), x => x.RoleName.Contains(input.RoleName!.Trim()))
            .WhereIF(input.State is not null, x => x.State == input.State);

        if (!string.IsNullOrWhiteSpace(input.Sorting))
        {
            query = query.OrderBy(input.Sorting);
        }
        else
        {
            query = query.OrderBy(x => x.OrderNum, OrderByType.Desc);
        }

        var entities = await query.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);

        var items = entities.Select(x => new RbacRoleGetListOutputDto
        {
            Id = x.Id,
            RoleName = x.RoleName ?? string.Empty,
            RoleCode = x.RoleCode ?? string.Empty,
            Remark = x.Remark,
            DataScope = (int)x.DataScope,
            State = x.State,
            OrderNum = x.OrderNum,
            AccessPermissionCodes = DeserializeAccessPermissionCodes(x.AccessPermissionCodesJson)
        }).ToList();

        var pageSize = input.MaxResultCount <= 0 ? items.Count : input.MaxResultCount;
        var pageIndex = pageSize <= 0 ? 1 : PagedQueryConvention.PageIndexFromSkipCount(input.SkipCount);
        var totalPages = pageSize <= 0 ? 0 : (int)Math.Ceiling(total / (double)pageSize);

        return new PagedResultWithPageDto<RbacRoleGetListOutputDto>
        {
            PageIndex = pageIndex,
            PageSize = pageSize,
            TotalCount = total,
            TotalPages = totalPages,
            Items = items
        };
    }

    /// <inheritdoc />
    public async Task<RbacRoleGetOutputDto> GetAsync(Guid id)
    {
        var entity = await _roleRepository.GetSingleAsync(x => x.Id == id && x.IsDeleted == false);
        if (entity is null)
        {
            throw new UserFriendlyException("角色不存在");
        }

        var menuIds = await _dbContext.SqlSugarClient.Queryable<RoleMenuDbEntity>()
            .Where(x => x.RoleId == id.ToString())
            .Select(x => x.MenuId)
            .ToListAsync();

        return new RbacRoleGetOutputDto
        {
            Id = entity.Id,
            RoleName = entity.RoleName ?? string.Empty,
            RoleCode = entity.RoleCode ?? string.Empty,
            Remark = entity.Remark,
            DataScope = (int)entity.DataScope,
            State = entity.State,
            OrderNum = entity.OrderNum,
            AccessPermissionCodes = DeserializeAccessPermissionCodes(entity.AccessPermissionCodesJson),
            MenuIds = menuIds
        };
    }

    /// <inheritdoc />
    public async Task<RbacRoleGetOutputDto> CreateAsync([FromBody] RbacRoleCreateInputVo input)
    {
        var roleName = input.RoleName?.Trim();
        var roleCode = input.RoleCode?.Trim();
        if (string.IsNullOrWhiteSpace(roleName))
        {
            throw new UserFriendlyException("角色名称不能为空");
        }

        if (string.IsNullOrWhiteSpace(roleCode))
        {
            throw new UserFriendlyException("角色编码不能为空");
        }

        var isExist = await _roleRepository.IsAnyAsync(x => x.RoleCode == roleCode || x.RoleName == roleName);
        if (isExist)
        {
            throw new UserFriendlyException("角色名称或编码已存在");
        }

        var entity = new RoleAggregateRoot
        {
            RoleName = roleName,
            RoleCode = roleCode,
            Remark = input.Remark?.Trim(),
            DataScope = (Yi.Framework.Rbac.Domain.Shared.Enums.DataScopeEnum)input.DataScope,
            State = input.State,
            OrderNum = input.OrderNum ?? 0,
            AccessPermissionCodesJson = SerializeAccessPermissionCodes(ResolveAccessPermissions(input))
        };
        EntityHelper.TrySetId(entity, () => GuidGenerator.Create());

        await _roleRepository.InsertAsync(entity);

        return await GetAsync(entity.Id);
    }

    /// <inheritdoc />
    public async Task<RbacRoleGetOutputDto> UpdateAsync(Guid id, [FromBody] RbacRoleUpdateInputVo input)
    {
        var entity = await _roleRepository.GetSingleAsync(x => x.Id == id && x.IsDeleted == false);
        if (entity is null)
        {
            throw new UserFriendlyException("角色不存在");
        }

        var roleName = input.RoleName?.Trim();
        var roleCode = input.RoleCode?.Trim();
        if (string.IsNullOrWhiteSpace(roleName))
        {
            throw new UserFriendlyException("角色名称不能为空");
        }

        if (string.IsNullOrWhiteSpace(roleCode))
        {
            throw new UserFriendlyException("角色编码不能为空");
        }

        var isExist = await _roleRepository._DbQueryable
            .Where(x => x.Id != entity.Id && x.IsDeleted == false)
            .AnyAsync(x => x.RoleCode == roleCode || x.RoleName == roleName);
        if (isExist)
        {
            throw new UserFriendlyException("角色名称或编码已存在");
        }

        entity.RoleName = roleName;
        entity.RoleCode = roleCode;
        entity.Remark = input.Remark?.Trim();
        entity.DataScope = (Yi.Framework.Rbac.Domain.Shared.Enums.DataScopeEnum)input.DataScope;
        entity.State = input.State;
        entity.OrderNum = input.OrderNum ?? 0;
        entity.AccessPermissionCodesJson =
            SerializeAccessPermissionCodes(ResolveAccessPermissions(input));

        await _roleRepository.UpdateAsync(entity);

        return await GetAsync(entity.Id);
    }

    /// <inheritdoc />
    [UnitOfWork]
    public async Task DeleteAsync([FromBody] List<Guid> ids)
    {
        var idList = ids?.Distinct().ToList() ?? new List<Guid>();
        if (idList.Count == 0)
        {
            return;
        }

        await _roleMenuRepository.DeleteAsync(x => idList.Contains(x.RoleId));
        await _roleDeptRepository.DeleteAsync(x => idList.Contains(x.RoleId));
        await _userRoleRepository.DeleteAsync(x => idList.Contains(x.RoleId));

        // 角色表为软删(ISoftDelete)
        await _roleRepository.DeleteAsync(x => idList.Contains(x.Id));
    }

    private static List<string> DeserializeAccessPermissionCodes(string? json)
    {
        if (string.IsNullOrWhiteSpace(json))
        {
            return new List<string>();
        }

        try
        {
            var raw = JsonSerializer.Deserialize<List<string>>(json);
            return NormalizeAccessPermissionCodes(raw);
        }
        catch
        {
            return new List<string>();
        }
    }

    private static string? SerializeAccessPermissionCodes(List<string> codes)
    {
        if (codes == null || codes.Count == 0)
        {
            return null;
        }

        return JsonSerializer.Serialize(codes);
    }

    private static List<string> ResolveAccessPermissions(RbacRoleCreateInputVo input)
    {
        var merged = new List<string>();
        if (!string.IsNullOrWhiteSpace(input.AccessPermissions))
        {
            merged.AddRange(DeserializeAccessPermissionCodes(input.AccessPermissions));
        }

        if (input.AccessPermissionCodes is { Count: > 0 })
        {
            merged.AddRange(input.AccessPermissionCodes);
        }

        return NormalizeAccessPermissionCodes(merged.Count > 0 ? merged : null);
    }

    private static List<string> NormalizeAccessPermissionCodes(List<string>? input)
    {
        if (input == null || input.Count == 0)
        {
            return new List<string>();
        }

        return input
            .Select(x => x?.Trim() ?? "")
            .Where(x => x.Length > 0 && RoleAccessPermissionCodes.IsKnown(x))
            .Distinct(StringComparer.Ordinal)
            .ToList();
    }
}