using GB5Shared.DaprCache; using GB5Shared.DTO.Framework.Criteria; using GB5Shared.DTO.Framework.Login; using GB5Shared.EntityHandler; using GB5Shared.GenerateAutoNumber; using GB5Shared.ListQuery; using GB5Shared.QueryExecutor; using GB5Shared.Resource.Response; using Newtonsoft.Json; using SwDAL.CustomCode.SyncGroup; using SwDAL.DTO.SyncGroup; using static GB5Shared.GB5Constant.Constant; namespace SwBLL.SyncGroup; public class SyncGroupBLL : ISyncGroupBLL { private readonly ISyncGroupDAL _SyncGroupDAL; private readonly AutoNumber _AutoNumber; private readonly IQueryExecutor _QueryExecutor; private readonly KeyInvalidate _KeyInvalidate; private readonly IListHandler _ListHandler; private readonly BaseEntityAppService _BaseEntityAppService; public SyncGroupBLL( ISyncGroupDAL syncGroupDAL, AutoNumber autoNumber, IQueryExecutor queryExecutor, KeyInvalidate keyInvalidate, IListHandler listHandler, BaseEntityAppService baseEntityAppService) { _SyncGroupDAL = syncGroupDAL; _AutoNumber = autoNumber; _QueryExecutor = queryExecutor; _KeyInvalidate = keyInvalidate; _ListHandler = listHandler; _BaseEntityAppService = baseEntityAppService; } // ── Read — typed pass-through ───────────────────────────────────────── public async Task GetById(int syncGroupId, LoginDTO loginDTO, CancellationToken ct) { var dto = await _SyncGroupDAL.GetById(syncGroupId, loginDTO, ct).ConfigureAwait(false); if (dto is not null) dto.SyncTables = (await _SyncGroupDAL.GetTables(syncGroupId, loginDTO, ct).ConfigureAwait(false)).ToList(); return dto; } 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 SyncGroupListQuery(c), loginDTO, ct) .ConfigureAwait(false); return JsonConvert.SerializeObject(result); } public async Task GetSelectListSyncGroup( int firstNumber, int maxResult, CriteriaDTO criteriaDTO, LoginDTO loginDTO) => await _SyncGroupDAL.GetSelectListSyncGroup(firstNumber, maxResult,criteriaDTO, loginDTO).ConfigureAwait(false); // ── Save (insert or update) ─────────────────────────────────────────── public async Task Save(SyncGroupDTO dto, LoginDTO loginDTO, CancellationToken ct) { if (dto == null) throw new ArgumentNullException(nameof(dto)); dto.TenantId = loginDTO.ClientId; bool isNew = dto.SyncGroupId == 0; var Trans = await _QueryExecutor.BeginTransactionAsync(loginDTO); GB5Shared.DTO.Framework.AutoNumber.AutoNumberDTO? an = null; try { if (isNew) { an = await _AutoNumber.GetNumberAsync(1, AUTONUMBERCONSTANT.SWSYNCGROUP, loginDTO); dto.SyncGroupId = an.StartNumber; } await _BaseEntityAppService.ExecuteSaveAsync( EntityConstant.OBJECTSWSYNCGROUP, isNew ? EventTypeConstant.SAVESYNCGROUPEVENTTYPEID : EventTypeConstant.UPDATESYNCGROUPEVENTTYPEID, dto, loginDTO, async tx => { _ = isNew ? await _SyncGroupDAL.SaveSyncGroup(dto, loginDTO, tx, ct) : await _SyncGroupDAL.UpdateSyncGroup(dto, loginDTO, tx, ct); // Replace sync tables: delete all, then reinsert from dto.SyncTables await _SyncGroupDAL.DeleteAllSyncTables(dto.SyncGroupId, loginDTO, tx, ct); foreach (var table in dto.SyncTables) { table.SyncGroupId = dto.SyncGroupId; table.TenantId = loginDTO.ClientId; await _SyncGroupDAL.InsertSyncTable(table, loginDTO, tx, ct); } return dto.SyncGroupId; }, null, -1, -1, Trans); await _QueryExecutor.CommitAsync(Trans); var keyGen = new CacheKeyGeneration(); var cacheKey = keyGen.KeyGeneration( dto.SyncGroupId, EntityConstant.OBJECTSWSYNCGROUP, CacheKeyLevel.CLIENT_LEVEL, loginDTO); await _KeyInvalidate.AllInvalidateCache(cacheKey); return isNew ? $"{SuccessResponse.SaveSuccessMessage} {dto.SyncGroupId}" : $"{SuccessResponse.UpdateSuccessMessage} {dto.SyncGroupId}"; } catch (Exception) { await _QueryExecutor.RollbackAsync(Trans); if (an != null && isNew) await _AutoNumber.RollbackAutoNumber(AUTONUMBERCONSTANT.SWSYNCGROUP, an.StartNumber, loginDTO); throw; } } // ── SaveSyncTable ───────────────────────────────────────────────────── public async Task SaveSyncTable(SyncTableDTO dto, LoginDTO loginDTO, CancellationToken ct) { if (dto == null) throw new ArgumentNullException(nameof(dto)); if (dto.SyncGroupId == 0) throw new ArgumentException("SyncGroupId is required.", nameof(dto)); if (string.IsNullOrWhiteSpace(dto.TableName)) throw new ArgumentException("TableName is required.", nameof(dto)); dto.TenantId = loginDTO.ClientId; var stubDto = new SyncGroupDTO { SyncGroupId = dto.SyncGroupId, TenantId = loginDTO.ClientId }; var Trans = await _QueryExecutor.BeginTransactionAsync(loginDTO); try { await _BaseEntityAppService.ExecuteSaveAsync( EntityConstant.OBJECTSWSYNCGROUP, EventTypeConstant.UPDATESYNCGROUPEVENTTYPEID, stubDto, loginDTO, async tx => { await _SyncGroupDAL.InsertSyncTable(dto, loginDTO, tx, ct); return dto.SyncGroupId; }, null, -1, -1, Trans); await _QueryExecutor.CommitAsync(Trans); var keyGen = new CacheKeyGeneration(); var cacheKey = keyGen.KeyGeneration( dto.SyncGroupId, EntityConstant.OBJECTSWSYNCGROUP, CacheKeyLevel.CLIENT_LEVEL, loginDTO); await _KeyInvalidate.AllInvalidateCache(cacheKey); return SuccessResponse.SaveSuccessMessage; } catch (Exception) { await _QueryExecutor.RollbackAsync(Trans); throw; } } // ── DeleteSyncTable ─────────────────────────────────────────────────── public async Task DeleteSyncTable(int syncTableId, LoginDTO loginDTO, CancellationToken ct) { var stubDto = new SyncGroupDTO { TenantId = loginDTO.ClientId }; var Trans = await _QueryExecutor.BeginTransactionAsync(loginDTO); try { await _BaseEntityAppService.ExecuteSaveAsync( EntityConstant.OBJECTSWSYNCGROUP, EventTypeConstant.UPDATESYNCGROUPEVENTTYPEID, stubDto, loginDTO, async tx => { await _SyncGroupDAL.DeleteSyncTable(syncTableId, loginDTO, tx, ct); return syncTableId; }, null, -1, -1, Trans); await _QueryExecutor.CommitAsync(Trans); return SuccessResponse.DeleteSuccessMessage; } catch (Exception) { await _QueryExecutor.RollbackAsync(Trans); throw; } } // ── Delete ──────────────────────────────────────────────────────────── public async Task Delete(int syncGroupId, LoginDTO loginDTO, CancellationToken ct) { if (await _SyncGroupDAL.ExistsInClientDb(syncGroupId, loginDTO)) throw new InvalidOperationException("Cannot delete a sync group that is assigned to a client database."); var Trans = await _QueryExecutor.BeginTransactionAsync(loginDTO); try { await _BaseEntityAppService.ExecuteSaveAsync( EntityConstant.OBJECTSWSYNCGROUP, EventTypeConstant.DELETESYNCGROUPEVENTTYPEID, new SyncGroupDTO { SyncGroupId = syncGroupId, TenantId = loginDTO.ClientId }, loginDTO, async tx => { await _SyncGroupDAL.DeleteAllSyncTables(syncGroupId, loginDTO, tx, ct); await _SyncGroupDAL.DeleteSyncGroup(syncGroupId, loginDTO, tx, ct); return syncGroupId; }, null, -1, -1, Trans); await _QueryExecutor.CommitAsync(Trans); var keyGen = new CacheKeyGeneration(); var cacheKey = keyGen.KeyGeneration( syncGroupId, EntityConstant.OBJECTSWSYNCGROUP, CacheKeyLevel.CLIENT_LEVEL, loginDTO); await _KeyInvalidate.AllInvalidateCache(cacheKey); return SuccessResponse.DeleteSuccessMessage; } catch (Exception) { await _QueryExecutor.RollbackAsync(Trans); throw; } } //---------------------------------Get SyncGroup with SyncTable----------------------------------------- public async Task GetSyncGroup(int syncGroupId, LoginDTO login, CancellationToken ct) { var header = await _SyncGroupDAL.GetSyncGroupHeader(syncGroupId, login, ct).ConfigureAwait(false); if (header == null)return JsonConvert.SerializeObject(null); header.SyncTables = (await _SyncGroupDAL.GetSyncTables(syncGroupId, login, ct).ConfigureAwait(false)).ToList(); return JsonConvert.SerializeObject(header); } }