using AnalyticsDAL.CustomCode.RoleKPI; using AnalyticsDAL.DTO.RoleKPI; using GB5Shared.DaprCache; using GB5Shared.DTO.Framework.Login; using GB5Shared.Resource.Response; using GB5Shared.Telemetry; using Microsoft.Extensions.Logging; using static GB5Shared.GB5Constant.Constant; namespace AnalyticsBLL.RoleKPI { public class RoleKPIBLL : IRoleKPIBLL { private readonly IRoleKPIDAL _dal; private readonly KeyInvalidate _keyInvalidate; private readonly ILogger _logger; public RoleKPIBLL( IRoleKPIDAL dal, KeyInvalidate keyInvalidate, ILogger logger) { _dal = dal; _keyInvalidate = keyInvalidate; _logger = logger; } public async Task GetRoleKPI(int roleKPIId, LoginDTO login, CancellationToken ct) => await _dal.GetRoleKPI(roleKPIId, login, ct).ConfigureAwait(false); public async Task GetSelectListRoleKPI(LoginDTO login, CancellationToken ct) => await _dal.GetSelectListRoleKPI(login, ct).ConfigureAwait(false); public async Task SaveRoleKPI(RoleKPIDTO dto, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("validate-rolekpi", new { dto.RoleKPIId }); // Real MROLE/MKPI ids are negative (this codebase's own master-data ID convention // — confirmed live, e.g. RoleId -1899999998 "Admin", KPIId -1799999776 "Working // Capital") — a "<= 0" required-field check rejects every real row and only ever // accepts the unused positive-id space. "== 0" is the correct "not selected" check. if (dto.RoleId == 0) throw new ArgumentException("RoleId is required."); if (dto.RoleKPIDetailArray == null || dto.RoleKPIDetailArray.Count == 0) throw new ArgumentException("At least one KPI must be assigned to the role."); foreach (var row in dto.RoleKPIDetailArray) { if (row.KPIId == 0) throw new ArgumentException("Each detail row must have a valid KPI selected."); } if (await _dal.IsDuplicateRole(dto.RoleId, dto.RoleKPIId, login, ct).ConfigureAwait(false)) throw new InvalidOperationException($"A KPI assignment for RoleId {dto.RoleId} already exists."); var isNew = dto.RoleKPIId == 0; GB5Trace.Step("save-rolekpi", new { dto.RoleKPIId, isNew }); int savedId; if (isNew) savedId = await _dal.SaveRoleKPI(dto, login, ct).ConfigureAwait(false); else { savedId = dto.RoleKPIId; await _dal.UpdateRoleKPI(dto, login, ct).ConfigureAwait(false); } var cacheKey = new CacheKeyGeneration().KeyGeneration( savedId, EntityConstant.OBJECTROLEKPI, CacheKeyLevel.CLIENT_LEVEL, login); await _keyInvalidate.AllInvalidateCache(cacheKey).ConfigureAwait(false); return isNew ? $"{SuccessResponse.SaveSuccessMessage} {savedId}" : SuccessResponse.UpdateSuccess; } catch (Exception ex) { GB5Trace.MarkFailed("save-rolekpi-failed", ex); _logger.LogError(ex, "SaveRoleKPI failed for RoleKPIId {RoleKPIId}", dto.RoleKPIId); throw; } } public async Task DeleteRoleKPI(int roleKPIId, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("delete-rolekpi", new { roleKPIId }); await _dal.DeleteRoleKPI(roleKPIId, login, ct).ConfigureAwait(false); var cacheKey = new CacheKeyGeneration().KeyGeneration( roleKPIId, EntityConstant.OBJECTROLEKPI, CacheKeyLevel.CLIENT_LEVEL, login); await _keyInvalidate.AllInvalidateCache(cacheKey).ConfigureAwait(false); return SuccessResponse.DeleteSuccessMessage; } catch (Exception ex) { GB5Trace.MarkFailed("delete-rolekpi-failed", ex); _logger.LogError(ex, "DeleteRoleKPI failed for RoleKPIId {RoleKPIId}", roleKPIId); throw; } } } }