using System.ComponentModel.DataAnnotations; using GB5Shared.DaprCache; using GB5Shared.DTO.Framework.AutoNumber; using GB5Shared.DTO.Framework.Criteria; using GB5Shared.DTO.Framework.Login; using GB5Shared.EntityHandler; using GB5Shared.GenerateAutoNumber; using GB5Shared.Resource.Response; using GB5Shared.Telemetry; using GB5Shared.Validation; using MarketingDAL.CustomCode.ContentRef; using MarketingDAL.DTO.ContentRef; using Microsoft.Extensions.Logging; using static GB5Shared.GB5Constant.Constant; namespace MarketingBLL.ContentRef; public class ContentRefBLL : IContentRefBLL { private readonly IContentRefDAL _ContentRefDAL; private readonly AutoNumber _AutoNumber; private readonly IValidation _Validation; private readonly KeyInvalidate _KeyInvalidate; private readonly BaseEntityAppService _BaseEntityAppService; private readonly ILogger _Logger; public ContentRefBLL( IContentRefDAL contentRefDAL, AutoNumber autoNumber, IValidation validation, KeyInvalidate keyInvalidate, BaseEntityAppService baseEntityAppService, ILogger logger) { _ContentRefDAL = contentRefDAL; _AutoNumber = autoNumber; _Validation = validation; _KeyInvalidate = keyInvalidate; _BaseEntityAppService = baseEntityAppService; _Logger = logger; } public async Task GetContentRef(int contentRefId, LoginDTO loginDTO, CancellationToken ct) { try { return await _ContentRefDAL.GetContentRef(contentRefId, loginDTO, ct).ConfigureAwait(false); } catch (Exception ex) { GB5Trace.MarkFailed("get-contentref-failed", ex); _Logger.LogError(ex, "GetContentRef failed for ContentRefId {ContentRefId}", contentRefId); throw; } } public async Task SaveContentRef(ContentRefDTO contentRefDTO, LoginDTO loginDTO, CancellationToken ct) { if (contentRefDTO == null) throw new ArgumentNullException(nameof(contentRefDTO)); AutoNumberDTO? autoNumberDTO = null; try { GB5Trace.Step("validate-contentref", new { contentRefDTO.ContentRefId }); await _Validation.NotEmpty(contentRefDTO.ContentRefCode, nameof(contentRefDTO.ContentRefCode)); await _Validation.NotEmpty(contentRefDTO.ContentRefTitle, nameof(contentRefDTO.ContentRefTitle)); await _Validation.Range((byte)1, (byte)5, contentRefDTO.JourneyStage, nameof(contentRefDTO.JourneyStage)); await _Validation.Range((byte)1, (byte)3, contentRefDTO.RRRTag, nameof(contentRefDTO.RRRTag)); if (contentRefDTO.PersonaId == 0) contentRefDTO.PersonaId = -1; // SourceContentId is a real FK to CMS's IDENTITY-keyed TCONTENT (starts at 1) — 0 is // never a valid CONTENTID, so normalize it to NULL ("no CMS content linked") the same // way 0 is normalized to the -1 sentinel for AutoNumber-keyed optional FKs elsewhere. if (contentRefDTO.SourceContentId == 0) contentRefDTO.SourceContentId = null; contentRefDTO.ModifiedById = loginDTO.UserId; contentRefDTO.ModifiedOn = DateTime.UtcNow; bool isNew = contentRefDTO.ContentRefId == 0; if (isNew) { autoNumberDTO = await _AutoNumber.GetNumberAsync(1, "CONTENTREF", loginDTO).ConfigureAwait(false); contentRefDTO.ContentRefId = autoNumberDTO.StartNumber; contentRefDTO.CreatedById = loginDTO.UserId; contentRefDTO.CreatedOn = DateTime.UtcNow; } GB5Trace.Step("save-contentref", new { contentRefDTO.ContentRefId, isNew }); await _BaseEntityAppService.ExecuteSaveAsync( EntityConstant.OBJECTMARKETINGCONTENTREF, isNew ? EventTypeConstant.SAVEMARKETINGCONTENTREFEVENTTYPEID : EventTypeConstant.UPDATEMARKETINGCONTENTREFEVENTTYPEID, contentRefDTO, loginDTO, async tx => { _ = isNew ? await _ContentRefDAL.SaveContentRef(contentRefDTO, loginDTO, tx, ct).ConfigureAwait(false) : await _ContentRefDAL.UpdateContentRef(contentRefDTO, loginDTO, tx, ct).ConfigureAwait(false); return contentRefDTO.ContentRefId; }, null, -1, -1, null, isNew).ConfigureAwait(false); GB5Trace.Step("event-publish", new { EventTypeId = isNew ? EventTypeConstant.SAVEMARKETINGCONTENTREFEVENTTYPEID : EventTypeConstant.UPDATEMARKETINGCONTENTREFEVENTTYPEID }); var cacheKey = new CacheKeyGeneration().KeyGeneration( contentRefDTO.ContentRefId, EntityConstant.OBJECTMARKETINGCONTENTREF, CacheKeyLevel.CLIENT_LEVEL, loginDTO); await _KeyInvalidate.AllInvalidateCache(cacheKey).ConfigureAwait(false); _Logger.LogInformation("ContentRef {ContentRefId} {Action} by user {UserId}", contentRefDTO.ContentRefId, isNew ? "saved" : "updated", loginDTO.UserId); return isNew ? $"{SuccessResponse.SaveSuccessMessage} {contentRefDTO.ContentRefId}" : $"{SuccessResponse.UpdateSuccessMessage} {contentRefDTO.ContentRefId}"; } catch (ValidationException vex) { GB5Trace.MarkFailed("save-contentref-failed", vex); if (autoNumberDTO != null && contentRefDTO.ContentRefId > 0) await _AutoNumber.RollbackAutoNumber("CONTENTREF", contentRefDTO.ContentRefId, loginDTO).ConfigureAwait(false); throw new Exception(vex.Message); } catch (Exception ex) { GB5Trace.MarkFailed("save-contentref-failed", ex); _Logger.LogError(ex, "SaveContentRef failed for ContentRefId {ContentRefId}", contentRefDTO.ContentRefId); if (autoNumberDTO != null && contentRefDTO.ContentRefId > 0) await _AutoNumber.RollbackAutoNumber("CONTENTREF", contentRefDTO.ContentRefId, loginDTO).ConfigureAwait(false); throw; } } public async Task GetSelectListContentRef(CriteriaDTO criteriaDTO, LoginDTO loginDTO, CancellationToken ct) { try { return await _ContentRefDAL.GetSelectListContentRef(criteriaDTO, loginDTO, ct).ConfigureAwait(false); } catch (Exception ex) { GB5Trace.MarkFailed("get-selectlist-contentref-failed", ex); _Logger.LogError(ex, "GetSelectListContentRef failed"); throw; } } public async Task DeleteContentRef(int contentRefId, LoginDTO loginDTO, CancellationToken ct) { try { GB5Trace.Step("delete-contentref", new { contentRefId }); var result = await _ContentRefDAL.DeleteContentRef(contentRefId, loginDTO, ct).ConfigureAwait(false); var cacheKey = new CacheKeyGeneration().KeyGeneration( contentRefId, EntityConstant.OBJECTMARKETINGCONTENTREF, CacheKeyLevel.CLIENT_LEVEL, loginDTO); await _KeyInvalidate.AllInvalidateCache(cacheKey).ConfigureAwait(false); _Logger.LogInformation("ContentRef {ContentRefId} deleted by user {UserId}", contentRefId, loginDTO.UserId); return result; } catch (Exception ex) { GB5Trace.MarkFailed("delete-contentref-failed", ex); _Logger.LogError(ex, "DeleteContentRef failed for ContentRefId {ContentRefId}", contentRefId); throw; } } }