first commit
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
using AuthHex.Infra.UoW;
|
||||
using AuthHex.Interfaces;
|
||||
using AuthHex.Models;
|
||||
using Core_Archi.Helpers;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace AuthHex.Services.RoleManager;
|
||||
|
||||
/// <summary>
|
||||
/// Role use-cases for AuthHex. AuthHex only owns the <see cref="Role"/> lookup table
|
||||
/// (identity source for the JWT RoleId/RoleCode claims). Roles are authored in ERP-core
|
||||
/// and cloned here via <c>createRole</c> using the SAME RoleId so the two stores stay in
|
||||
/// sync. See docs/11-BACKEND-PHASE1.md (AuthHex proxy section).
|
||||
/// </summary>
|
||||
public class RoleManagerService
|
||||
{
|
||||
private readonly IRoleRepository _repository;
|
||||
private readonly IUnitOfWork _uow;
|
||||
|
||||
public RoleManagerService(IRoleRepository repository, IUnitOfWork uow)
|
||||
{
|
||||
_repository = repository;
|
||||
_uow = uow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clone a role created in the main system. Accepts the caller-supplied RoleId so the
|
||||
/// AuthHex row matches ERP-core's. Idempotent-ish: a duplicate RoleId or Code is rejected.
|
||||
/// </summary>
|
||||
public async Task<object> CreateRole(object payload, HttpContext httpContext)
|
||||
{
|
||||
await _uow.BeginAsync();
|
||||
try
|
||||
{
|
||||
var data = DeserializePayload.Deserialize(payload);
|
||||
if (data == null || !data.Any())
|
||||
throw new ArgumentException("Invalid payload data");
|
||||
|
||||
if (!data.ContainsKey("roleId") || !Guid.TryParse(data["roleId"].GetString(), out var roleId))
|
||||
throw new ArgumentException("Invalid roleId");
|
||||
|
||||
var code = data.ContainsKey("code") ? data["code"].GetString() : null;
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
throw new ArgumentException("Role code is required");
|
||||
code = code.Trim();
|
||||
|
||||
var name = data.ContainsKey("name") ? data["name"].GetString() : null;
|
||||
|
||||
bool? isSystemRole = null;
|
||||
if (data.ContainsKey("isSystemRole"))
|
||||
{
|
||||
var el = data["isSystemRole"];
|
||||
isSystemRole = el.ValueKind == System.Text.Json.JsonValueKind.True
|
||||
|| el.ValueKind == System.Text.Json.JsonValueKind.False
|
||||
? el.GetBoolean()
|
||||
: (bool?)null;
|
||||
}
|
||||
|
||||
if (await _repository.GetByIdAsync(roleId) != null)
|
||||
throw new InvalidOperationException($"Role '{roleId}' already exists.");
|
||||
|
||||
if (await _repository.GetByCodeAsync(code) != null)
|
||||
throw new InvalidOperationException($"Role code '{code}' already exists.");
|
||||
|
||||
var role = new Role
|
||||
{
|
||||
RoleId = roleId,
|
||||
Code = code,
|
||||
Name = name,
|
||||
IsSystemRole = isSystemRole,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
await _repository.AddRoleAsync(role);
|
||||
await _uow.CommitAsync();
|
||||
|
||||
return ToDto(role);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await _uow.RollbackAsync();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<object> ListRoles(object payload, HttpContext httpContext)
|
||||
{
|
||||
var roles = await _repository.ListAsync();
|
||||
return roles.Select(ToDto).ToList();
|
||||
}
|
||||
|
||||
public async Task<object> GetRole(object payload, HttpContext httpContext)
|
||||
{
|
||||
var data = DeserializePayload.Deserialize(payload);
|
||||
if (data == null || !data.ContainsKey("roleId") || !Guid.TryParse(data["roleId"].GetString(), out var roleId))
|
||||
throw new ArgumentException("Invalid roleId");
|
||||
|
||||
var role = await _repository.GetByIdAsync(roleId);
|
||||
if (role == null)
|
||||
throw new KeyNotFoundException($"Role '{roleId}' not found.");
|
||||
|
||||
return ToDto(role);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete a role. The User.RoleId FK is Restrict, so if any user is still assigned
|
||||
/// the DB rejects the delete and we surface a conflict — keeping ERP-core (which calls
|
||||
/// this before deleting its own copy) from ever orphaning an in-use role.
|
||||
/// </summary>
|
||||
public async Task<object> DeleteRole(object payload, HttpContext httpContext)
|
||||
{
|
||||
await _uow.BeginAsync();
|
||||
try
|
||||
{
|
||||
var data = DeserializePayload.Deserialize(payload);
|
||||
if (data == null || !data.ContainsKey("roleId") || !Guid.TryParse(data["roleId"].GetString(), out var roleId))
|
||||
throw new ArgumentException("Invalid roleId");
|
||||
|
||||
var role = await _repository.GetByIdAsync(roleId)
|
||||
?? throw new KeyNotFoundException($"Role '{roleId}' not found.");
|
||||
|
||||
var inUse = await _repository.IsAssignedToAnyUserAsync(roleId);
|
||||
if (inUse)
|
||||
throw new InvalidOperationException($"Role '{role.Code}' is assigned to one or more users and cannot be deleted.");
|
||||
|
||||
await _repository.DeleteRoleAsync(role);
|
||||
await _uow.CommitAsync();
|
||||
|
||||
return new { roleId = role.RoleId, deleted = true };
|
||||
}
|
||||
catch
|
||||
{
|
||||
await _uow.RollbackAsync();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static object ToDto(Role role) => new
|
||||
{
|
||||
roleId = role.RoleId,
|
||||
code = role.Code,
|
||||
name = role.Name,
|
||||
isSystemRole = role.IsSystemRole,
|
||||
createdAt = role.CreatedAt
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user