using GB5Shared.DaprCache; using GB5Shared.DTO.Framework.AutoNumber; using GB5Shared.DTO.Framework.Criteria; using GB5Shared.DTO.Framework.Login; using GB5Shared.DTO.Qualifier; using GB5Shared.EntityHandler; using GB5Shared.GB5Library.Qualifier; using GB5Shared.GenerateAutoNumber; using GB5Shared.ListQuery; using GB5Shared.QueryExecutor; using GB5Shared.Resource.Response; using Newtonsoft.Json; using SwDAL.CustomCode.ObjectGroup; using SwDAL.DTO.ObjectGroup; using static GB5Shared.GB5Constant.Constant; namespace SwBLL.ObjectGroup; public class ObjectGroupBLL : IObjectGroupBLL { private readonly IObjectGroupDAL _ObjectGroupDAL; private readonly AutoNumber _AutoNumber; private readonly IQueryExecutor _QueryExecutor; private readonly KeyInvalidate _KeyInvalidate; private readonly IListHandler _ListHandler; private readonly BaseEntityAppService _BaseEntityAppService; public ObjectGroupBLL( IObjectGroupDAL objectGroupDAL, AutoNumber autoNumber, IQueryExecutor queryExecutor, KeyInvalidate keyInvalidate, IListHandler listHandler, BaseEntityAppService baseEntityAppService) { _ObjectGroupDAL = objectGroupDAL; _AutoNumber = autoNumber; _QueryExecutor = queryExecutor; _KeyInvalidate = keyInvalidate; _ListHandler = listHandler; _BaseEntityAppService = baseEntityAppService; } // ── Read — typed pass-through ───────────────────────────────────────── public async Task GetById(int objectGroupId, LoginDTO loginDTO, CancellationToken ct) => await _ObjectGroupDAL.GetById(objectGroupId, loginDTO, ct).ConfigureAwait(false); public async Task GetList( CriteriaDTO criteria, string? searchText, int pageOffset, int pageSize, LoginDTO loginDTO, CancellationToken ct = default) { var merged = CriteriaRouterHelper.WithSearchAndPaging(criteria, searchText, pageOffset, pageSize); var c = CriteriaBinder.Bind(merged); var result = await _ListHandler .HandleAsync(new ObjectGroupListQuery(c), loginDTO, ct) .ConfigureAwait(false); return JsonConvert.SerializeObject(result); } public async Task GetSelectListObjectGroup( int firstNumber, int maxResult, CriteriaDTO criteriaDTO, LoginDTO loginDTO) => await _ObjectGroupDAL.GetSelectListObjectGroup(firstNumber, maxResult, loginDTO) .ConfigureAwait(false); // ── Save (insert or update) ─────────────────────────────────────────── public async Task Save(ObjectGroupDTO dto, LoginDTO loginDTO, CancellationToken ct) { if (dto == null) throw new ArgumentNullException(nameof(dto)); dto.TenantId = loginDTO.ClientId; var Trans = await _QueryExecutor.BeginTransactionAsync(loginDTO); try { bool isNew = dto.ObjectGroupId == 0; if (isNew) { var auto = await _AutoNumber.GetNumberAsync(1, AUTONUMBERCONSTANT.SWOBJECTGROUP, loginDTO, Trans); dto.ObjectGroupId = auto.StartNumber; } await _BaseEntityAppService.ExecuteSaveAsync( EntityConstant.OBJECTSWOBJECTGROUP, EventTypeConstant.SAVEOBJECTGROUPEVENTTYPEID, dto, loginDTO, async tx => { _ = isNew ? await _ObjectGroupDAL.SaveObjectGroup(dto, loginDTO, tx, ct) : await _ObjectGroupDAL.UpdateObjectGroup(dto, loginDTO, tx, ct); return dto.ObjectGroupId; }, null, -1, -1, Trans); await _QueryExecutor.CommitAsync(Trans); var keyGen = new CacheKeyGeneration(); var cacheKey = keyGen.KeyGeneration( dto.ObjectGroupId, EntityConstant.OBJECTSWOBJECTGROUP, CacheKeyLevel.CLIENT_LEVEL, loginDTO); await _KeyInvalidate.AllInvalidateCache(cacheKey); return isNew ? $"{SuccessResponse.SaveSuccessMessage} {dto.ObjectGroupId}" : $"{SuccessResponse.UpdateSuccessMessage} {dto.ObjectGroupId}"; } catch (Exception) { await _QueryExecutor.RollbackAsync(Trans); throw; } } // ── Delete (with cascade) ───────────────────────────────────────────── public async Task Delete(int objectGroupId, LoginDTO loginDTO, CancellationToken ct) { var Trans = await _QueryExecutor.BeginTransactionAsync(loginDTO); try { await _BaseEntityAppService.ExecuteSaveAsync( EntityConstant.OBJECTSWOBJECTGROUP, EventTypeConstant.DELETEOBJECTGROUPEVENTTYPEID, new ObjectGroupDTO { ObjectGroupId = objectGroupId, TenantId = loginDTO.ClientId }, loginDTO, async tx => { await _ObjectGroupDAL.DeleteAllDetails(objectGroupId, loginDTO, tx, ct); await _ObjectGroupDAL.DeleteObjectGroup(objectGroupId, loginDTO, tx, ct); return objectGroupId; }, null, -1, -1, Trans); await _QueryExecutor.CommitAsync(Trans); var keyGen = new CacheKeyGeneration(); var cacheKey = keyGen.KeyGeneration( objectGroupId, EntityConstant.OBJECTSWOBJECTGROUP, CacheKeyLevel.CLIENT_LEVEL, loginDTO); await _KeyInvalidate.AllInvalidateCache(cacheKey); return SuccessResponse.DeleteSuccessMessage; } catch (Exception) { await _QueryExecutor.RollbackAsync(Trans); throw; } } // ── Detail (ObjectReference) methods ───────────────────────────────── public async Task> GetDetails( int objectGroupId, LoginDTO loginDTO, CancellationToken ct) => await _ObjectGroupDAL.GetDetails(objectGroupId, loginDTO, ct).ConfigureAwait(false); public async Task SaveDetail( ObjectGroupDetailDTO dto, LoginDTO loginDTO, CancellationToken ct) { if (dto == null) throw new ArgumentNullException(nameof(dto)); dto.TenantId = loginDTO.ClientId; var stub = new ObjectGroupDTO { ObjectGroupId = dto.ObjectGroupId, TenantId = loginDTO.ClientId }; var Trans = await _QueryExecutor.BeginTransactionAsync(loginDTO); try { await _BaseEntityAppService.ExecuteSaveAsync( EntityConstant.OBJECTSWOBJECTGROUP, EventTypeConstant.UPDATEOBJECTGROUPEVENTTYPEID, stub, loginDTO, async tx => { await _ObjectGroupDAL.InsertDetail(dto, loginDTO, tx, ct); return dto.ObjectGroupId; }, null, -1, -1, Trans); await _QueryExecutor.CommitAsync(Trans); var keyGen = new CacheKeyGeneration(); var cacheKey = keyGen.KeyGeneration( dto.ObjectGroupId, EntityConstant.OBJECTSWOBJECTGROUP, CacheKeyLevel.CLIENT_LEVEL, loginDTO); await _KeyInvalidate.AllInvalidateCache(cacheKey); return SuccessResponse.SaveSuccessMessage; } catch (Exception) { await _QueryExecutor.RollbackAsync(Trans); throw; } } public async Task DeleteDetail( int objectReferenceId, LoginDTO loginDTO, CancellationToken ct) { var stub = new ObjectGroupDTO { ObjectGroupId = -1, TenantId = loginDTO.ClientId }; var Trans = await _QueryExecutor.BeginTransactionAsync(loginDTO); try { await _BaseEntityAppService.ExecuteSaveAsync( EntityConstant.OBJECTSWOBJECTGROUP, EventTypeConstant.UPDATEOBJECTGROUPEVENTTYPEID, stub, loginDTO, async tx => { await _ObjectGroupDAL.DeleteDetail(objectReferenceId, loginDTO, tx, ct); return objectReferenceId; }, null, -1, -1, Trans); await _QueryExecutor.CommitAsync(Trans); return SuccessResponse.DeleteSuccessMessage; } catch (Exception) { await _QueryExecutor.RollbackAsync(Trans); throw; } } public async Task SaveWithDetail(ObjectGroupDTO dto,LoginDTO loginDTO,CancellationToken ct) { if (dto == null) throw new ArgumentNullException(nameof(dto)); dto.TenantId = loginDTO.ClientId; bool isNew = dto.ObjectGroupId == 0; var trans = await _QueryExecutor.BeginTransactionAsync(loginDTO); try { if (isNew) { // Enlisted in trans (AutoNumber now accepts a shared transaction, gaps doc §4) — // a rollback of trans undoes the reservation too, so no separate compensating // RollbackAutoNumber call is needed in the catch block below anymore. var autoNumber = await _AutoNumber.GetNumberAsync(1, AUTONUMBERCONSTANT.SWOBJECTGROUP, loginDTO, trans); dto.ObjectGroupId = autoNumber.StartNumber; } await _BaseEntityAppService.ExecuteSaveAsync( EntityConstant.OBJECTSWOBJECTGROUP, isNew ? EventTypeConstant.SAVEOBJECTGROUPEVENTTYPEID : EventTypeConstant.UPDATEOBJECTGROUPEVENTTYPEID, dto, loginDTO, async tx => { if (isNew) { await _ObjectGroupDAL.SaveObjectGroup(dto,loginDTO,tx,ct); } else { await _ObjectGroupDAL.UpdateObjectGroup(dto,loginDTO,tx,ct); } // Delete all existing details await _ObjectGroupDAL.DeleteAllDetails(dto.ObjectGroupId,loginDTO,tx,ct); // Insert latest details if (dto.ObjectDetails != null && dto.ObjectDetails.Any()) { foreach (var detail in dto.ObjectDetails) { detail.ObjectGroupId = dto.ObjectGroupId; detail.TenantId = loginDTO.ClientId; await _ObjectGroupDAL.InsertDetail(detail,loginDTO,tx,ct); } } return dto.ObjectGroupId; }, null, -1, -1, trans); await _QueryExecutor.CommitAsync(trans); var keyGen = new CacheKeyGeneration(); var cacheKey = keyGen.KeyGeneration( dto.ObjectGroupId, EntityConstant.OBJECTSWOBJECTGROUP, CacheKeyLevel.CLIENT_LEVEL, loginDTO); await _KeyInvalidate.AllInvalidateCache(cacheKey); return isNew ? $"{SuccessResponse.SaveSuccessMessage} {dto.ObjectGroupId}" : $"{SuccessResponse.UpdateSuccessMessage} {dto.ObjectGroupId}"; } catch (Exception) { await _QueryExecutor.RollbackAsync(trans); throw; } } public async Task GetObjectGroupWithDetail(int objectGroupId,LoginDTO loginDTO,CancellationToken ct) { return await _ObjectGroupDAL.GetObjectGroupWithDetail(objectGroupId, loginDTO, ct); } }