using System.Text.Json; using FBCKBLL.Interfaces; using FBCKDAL.DTOs; using FBCKDAL.Interfaces; using GB5Shared.DaprCache; using GB5Shared.DTO.Framework.Login; using GB5Shared.EventLogPublish; using static GB5Shared.GB5Constant.Constant; using GB5Shared.Resource.Response; using GB5Shared.Telemetry; using GB5Shared.Validation; using Microsoft.Extensions.Logging; namespace FBCKBLL.Implementations; public class MemoBLL : IMemoBLL { private readonly IMemoDAL _dal; private readonly IValidation _validation; private readonly KeyInvalidate _keyInvalidate; private readonly EventLogPublish _eventLog; private readonly IFBCKNotificationService _notification; private readonly ILogger _logger; public MemoBLL(IMemoDAL dal, IValidation validation, KeyInvalidate keyInvalidate, EventLogPublish eventLog, IFBCKNotificationService notification, ILogger logger) { _dal = dal; _validation = validation; _keyInvalidate = keyInvalidate; _eventLog = eventLog; _notification = notification; _logger = logger; } public async Task GetMemoAsync(int memoId, LoginDTO login, CancellationToken ct) { var dto = await _dal.GetMemoAsync(memoId, login, ct).ConfigureAwait(false); return JsonSerializer.Serialize(dto); } public async Task GetMemoListAsync(int toEmployeeId, int fromEmployeeId, short memoType, short status, int page, int pageSize, LoginDTO login, CancellationToken ct) { var list = await _dal.GetMemoListAsync(toEmployeeId, fromEmployeeId, memoType, status, page, pageSize, login, ct).ConfigureAwait(false); return JsonSerializer.Serialize(list); } public async Task SaveMemoAsync(MemoDTO dto, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("validate-memo", new { dto.MemoId }); await _validation.NotEmpty(dto.Subject, nameof(dto.Subject)).ConfigureAwait(false); await _validation.NotEmpty(dto.ContentHtml, nameof(dto.ContentHtml)).ConfigureAwait(false); bool isNew = dto.MemoId == 0; GB5Trace.Step("save-memo", new { dto.MemoId, isNew }); var savedId = await _dal.SaveMemoAsync(dto, login, ct).ConfigureAwait(false); GB5Trace.Step("event-publish", new { EventTypeId = EventTypeConstant.SAVEFBCKMEMOEVENTTYPEID }); await _eventLog.PublishEventLogAsync("Memo Saved", nameof(MemoDTO), EventTypeConstant.SAVEFBCKMEMOEVENTTYPEID, savedId, login, ct: ct).ConfigureAwait(false); var cacheKey = new CacheKeyGeneration().KeyGeneration(0, EntityConstant.OBJECTMEMO, CacheKeyLevel.USER_LEVEL, login); await _keyInvalidate.AllInvalidateCache(cacheKey).ConfigureAwait(false); return isNew ? $"{SuccessResponse.SaveSuccessMessage} {savedId}" : SuccessResponse.UpdateSuccess; } catch (Exception ex) { GB5Trace.MarkFailed("memo-save-failed", ex); _logger.LogError(ex, "SaveMemo failed for MemoId {MemoId}", dto.MemoId); throw; } } public async Task SendMemoAsync(int memoId, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("validate-memo-send", new { memoId }); var status = await _dal.GetMemoStatusAsync(memoId, login, ct).ConfigureAwait(false); if (status != 0) return "Only Draft memos can be sent."; GB5Trace.Step("send-memo", new { memoId }); await _dal.SendMemoAsync(memoId, login, ct).ConfigureAwait(false); GB5Trace.Step("event-publish", new { EventTypeId = EventTypeConstant.SENDFBCKMEMOEVENTTYPEID }); await _eventLog.PublishEventLogAsync("Memo Sent", nameof(MemoDTO), EventTypeConstant.SENDFBCKMEMOEVENTTYPEID, memoId, login, ct: ct).ConfigureAwait(false); var memo = await _dal.GetMemoAsync(memoId, login, ct).ConfigureAwait(false); if (memo is not null) { try { await _notification.NotifyMemoSentAsync(memo, ct).ConfigureAwait(false); } catch (Exception notifyEx) { _logger.LogWarning(notifyEx, "SignalR notification failed for sent memo {MemoId}", memoId); } } var cacheKey = new CacheKeyGeneration().KeyGeneration(0, EntityConstant.OBJECTMEMO, CacheKeyLevel.USER_LEVEL, login); await _keyInvalidate.AllInvalidateCache(cacheKey).ConfigureAwait(false); return SuccessResponse.UpdateSuccess; } catch (Exception ex) { GB5Trace.MarkFailed("memo-send-failed", ex); _logger.LogError(ex, "SendMemo failed for MemoId {MemoId}", memoId); throw; } } public async Task AcknowledgeMemoAsync(int memoId, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("validate-memo-acknowledge", new { memoId }); GB5Trace.Step("acknowledge-memo", new { memoId }); await _dal.AcknowledgeMemoAsync(memoId, login, ct).ConfigureAwait(false); GB5Trace.Step("event-publish", new { EventTypeId = EventTypeConstant.ACKNOWLEDGEFBCKMEMOEVENTTYPEID }); await _eventLog.PublishEventLogAsync("Memo Acknowledged", nameof(MemoDTO), EventTypeConstant.ACKNOWLEDGEFBCKMEMOEVENTTYPEID, memoId, login, ct: ct).ConfigureAwait(false); var cacheKey = new CacheKeyGeneration().KeyGeneration(0, EntityConstant.OBJECTMEMO, CacheKeyLevel.USER_LEVEL, login); await _keyInvalidate.AllInvalidateCache(cacheKey).ConfigureAwait(false); return SuccessResponse.UpdateSuccess; } catch (Exception ex) { GB5Trace.MarkFailed("memo-acknowledge-failed", ex); _logger.LogError(ex, "AcknowledgeMemo failed for MemoId {MemoId}", memoId); throw; } } public async Task RecallMemoAsync(int memoId, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("validate-memo-recall", new { memoId }); var status = await _dal.GetMemoStatusAsync(memoId, login, ct).ConfigureAwait(false); if (status == 3) return "Memo has already been recalled."; GB5Trace.Step("recall-memo", new { memoId }); await _dal.RecallMemoAsync(memoId, login, ct).ConfigureAwait(false); GB5Trace.Step("event-publish", new { EventTypeId = EventTypeConstant.RECALLFBCKMEMOEVENTTYPEID }); await _eventLog.PublishEventLogAsync("Memo Recalled", nameof(MemoDTO), EventTypeConstant.RECALLFBCKMEMOEVENTTYPEID, memoId, login, ct: ct).ConfigureAwait(false); var cacheKey = new CacheKeyGeneration().KeyGeneration(0, EntityConstant.OBJECTMEMO, CacheKeyLevel.USER_LEVEL, login); await _keyInvalidate.AllInvalidateCache(cacheKey).ConfigureAwait(false); return SuccessResponse.UpdateSuccess; } catch (Exception ex) { GB5Trace.MarkFailed("memo-recall-failed", ex); _logger.LogError(ex, "RecallMemo failed for MemoId {MemoId}", memoId); throw; } } }