using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; using ZB.MOM.WW.OtOpcUa.Configuration; using ZB.MOM.WW.OtOpcUa.Configuration.Entities; using ZB.MOM.WW.OtOpcUa.Configuration.Enums; namespace ZB.MOM.WW.OtOpcUa.Admin.Services; /// /// Owns the draft → diff → publish workflow (decision #89). Publish + rollback call into the /// stored procedures; diff queries sp_ComputeGenerationDiff. /// public sealed class GenerationService(OtOpcUaConfigDbContext db) { public async Task CreateDraftAsync(string clusterId, string createdBy, CancellationToken ct) { var gen = new ConfigGeneration { ClusterId = clusterId, Status = GenerationStatus.Draft, CreatedBy = createdBy, CreatedAt = DateTime.UtcNow, }; db.ConfigGenerations.Add(gen); await db.SaveChangesAsync(ct); return gen; } public Task> ListRecentAsync(string clusterId, int limit, CancellationToken ct) => db.ConfigGenerations.AsNoTracking() .Where(g => g.ClusterId == clusterId) .OrderByDescending(g => g.GenerationId) .Take(limit) .ToListAsync(ct); public async Task PublishAsync(string clusterId, long draftGenerationId, string? notes, CancellationToken ct) { await db.Database.ExecuteSqlRawAsync( "EXEC dbo.sp_PublishGeneration @ClusterId = {0}, @DraftGenerationId = {1}, @Notes = {2}", [clusterId, draftGenerationId, (object?)notes ?? DBNull.Value], ct); } public async Task RollbackAsync(string clusterId, long targetGenerationId, string? notes, CancellationToken ct) { await db.Database.ExecuteSqlRawAsync( "EXEC dbo.sp_RollbackToGeneration @ClusterId = {0}, @TargetGenerationId = {1}, @Notes = {2}", [clusterId, targetGenerationId, (object?)notes ?? DBNull.Value], ct); } public async Task> ComputeDiffAsync(long from, long to, CancellationToken ct) { var results = new List(); await using var conn = (SqlConnection)db.Database.GetDbConnection(); if (conn.State != System.Data.ConnectionState.Open) await conn.OpenAsync(ct); await using var cmd = conn.CreateCommand(); cmd.CommandText = "EXEC dbo.sp_ComputeGenerationDiff @FromGenerationId = @f, @ToGenerationId = @t"; cmd.Parameters.AddWithValue("@f", from); cmd.Parameters.AddWithValue("@t", to); await using var reader = await cmd.ExecuteReaderAsync(ct); while (await reader.ReadAsync(ct)) results.Add(new DiffRow(reader.GetString(0), reader.GetString(1), reader.GetString(2))); return results; } } public sealed record DiffRow(string TableName, string LogicalId, string ChangeKind);