diff --git a/AsbCloudApp/Data/LastDataDto.cs b/AsbCloudApp/Data/MeasureDto.cs similarity index 72% rename from AsbCloudApp/Data/LastDataDto.cs rename to AsbCloudApp/Data/MeasureDto.cs index ae83c4b2..adc1c93c 100644 --- a/AsbCloudApp/Data/LastDataDto.cs +++ b/AsbCloudApp/Data/MeasureDto.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; namespace AsbCloudApp.Data { - public class LastDataDto + public class MeasureDto { public int Id { get; set; } @@ -13,9 +13,7 @@ namespace AsbCloudApp.Data public string CategoryName { get; set; } - public DateTime DateInsert { get; set; } - - public DateTime DateModify { get; set; } + public DateTime Timestamp { get; set; } public Dictionary Data { get; set; } } diff --git a/AsbCloudApp/Services/ILastDataService.cs b/AsbCloudApp/Services/IMeasureService.cs similarity index 50% rename from AsbCloudApp/Services/ILastDataService.cs rename to AsbCloudApp/Services/IMeasureService.cs index d9011307..756fbc9b 100644 --- a/AsbCloudApp/Services/ILastDataService.cs +++ b/AsbCloudApp/Services/IMeasureService.cs @@ -5,13 +5,13 @@ using System.Threading.Tasks; namespace AsbCloudApp.Services { - public interface ILastDataService + public interface IMeasureService { Task> GetCategoriesAsync(CancellationToken token); - Task> GetHisoryAsync(int idWell, int idCategory, CancellationToken token); - Task> GetAllLastAsync(int idWell, CancellationToken token); - Task InsertAsync(int idWell, LastDataDto data, CancellationToken token); - Task UpdateAsync(int idWell, LastDataDto data, CancellationToken token); + Task> GetHisoryAsync(int idWell, int idCategory, CancellationToken token); + Task> GetAllLastAsync(int idWell, CancellationToken token); + Task InsertAsync(int idWell, MeasureDto data, CancellationToken token); + Task UpdateAsync(int idWell, MeasureDto data, CancellationToken token); Task MarkAsDeleteAsync(int idWell, int idData, CancellationToken token); Task DeleteAsync(int idWell, int idData, CancellationToken token); } diff --git a/AsbCloudDb/Model/AsbCloudDbContext.cs b/AsbCloudDb/Model/AsbCloudDbContext.cs index 5c6f3427..b85180a5 100644 --- a/AsbCloudDb/Model/AsbCloudDbContext.cs +++ b/AsbCloudDb/Model/AsbCloudDbContext.cs @@ -17,22 +17,23 @@ namespace AsbCloudDb.Model public virtual DbSet Companies { get; set; } public virtual DbSet DataSaubBases { get; set; } public virtual DbSet Deposits { get; set; } - public virtual DbSet Users { get; set; } - public virtual DbSet UserRoles { get; set; } - public virtual DbSet Wells { get; set; } - public virtual DbSet ReportProperties { get; set; } - public virtual DbSet Files { get; set; } public virtual DbSet FileCategories { get; set; } + public virtual DbSet Files { get; set; } + public virtual DbSet Measures { get; set; } + public virtual DbSet MeasureCategories { get; set; } + public virtual DbSet ReportProperties { get; set; } public virtual DbSet Telemetries { get; set; } + public virtual DbSet TelemetryAnalysis { get; set; } public virtual DbSet TelemetryEvents { get; set; } public virtual DbSet TelemetryMessages { get; set; } public virtual DbSet TelemetryUsers { get; set; } - public virtual DbSet TelemetryOperations { get; set; } - public virtual DbSet TelemetryAnalysis { get; set; } - public virtual DbSet WellSectionTypes { get; set; } + public virtual DbSet Users { get; set; } + public virtual DbSet UserRoles { get; set; } + public virtual DbSet Wells { get; set; } public virtual DbSet WellOperations { get; set; } + public virtual DbSet TelemetryOperations { get; set; } + public virtual DbSet WellSectionTypes { get; set; } public virtual DbSet WellTypes { get; set; } - public virtual DbSet LastData { get; set; } //var options = new DbContextOptionsBuilder() // .UseNpgsql("Host=localhost;Database=postgres;Username=postgres;Password=q;Persist Security Info=True") @@ -305,6 +306,14 @@ namespace AsbCloudDb.Model new WellType{ Id = 2, Caption = "Горизонтальная"}, }); }); + + modelBuilder.Entity(entity => { + entity.HasData(new List { + new MeasureCategory{ Id = 1, Name = "Показатели бурового раствора", ShortName = "Раствор"}, + new MeasureCategory{ Id = 2, Name = "Шламограмма", ShortName = "Шламограмма"}, + new MeasureCategory{ Id = 3, Name = "ННБ", ShortName = "ННБ"}, + }); + }); } // Эти данные не должны быть в релизе @@ -337,6 +346,14 @@ namespace AsbCloudDb.Model }); }); + modelBuilder.Entity(entity => { + entity.HasData(new List { + new Measure{ Id = 1, IdCategory = 1, IdWell = 1, Timestamp = DateTime.Now.AddHours(1), Data = new Dictionary{ {"a", 1 }}}, + new Measure{ Id = 2, IdCategory = 1, IdWell = 1, Timestamp = DateTime.Now.AddHours(2), Data = new Dictionary{ {"a", 2 }}}, + new Measure{ Id = 3, IdCategory = 2, IdWell = 1, Timestamp = DateTime.Now.AddHours(3), Data = new Dictionary{ {"a", 3 }}}, + }); + }); + modelBuilder.Entity(entity => { entity.HasData(new List{ diff --git a/AsbCloudDb/Model/IAsbCloudDbContext.cs b/AsbCloudDb/Model/IAsbCloudDbContext.cs index 66025b0b..5ca813d6 100644 --- a/AsbCloudDb/Model/IAsbCloudDbContext.cs +++ b/AsbCloudDb/Model/IAsbCloudDbContext.cs @@ -28,7 +28,8 @@ namespace AsbCloudDb.Model DbSet WellSectionTypes { get; set; } DbSet WellOperations { get; set; } DbSet WellTypes { get; set; } - DbSet LastData { get; set; } + DbSet Measures { get; set; } + DbSet MeasureCategories { get; set; } int SaveChanges(); int SaveChanges(bool acceptAllChangesOnSuccess); diff --git a/AsbCloudDb/Model/LastData.cs b/AsbCloudDb/Model/Measure.cs similarity index 63% rename from AsbCloudDb/Model/LastData.cs rename to AsbCloudDb/Model/Measure.cs index 1c8eb032..ffb17dfb 100644 --- a/AsbCloudDb/Model/LastData.cs +++ b/AsbCloudDb/Model/Measure.cs @@ -7,8 +7,8 @@ using System.Text.Json.Serialization; namespace AsbCloudDb.Model { - [Table("t_last_data"), Comment("Таблица c данными для вкладки \'Последние данные\'")] - public class LastData : IId + [Table("t_measure"), Comment("Таблица c данными для вкладки \'Последние данные\'")] + public class Measure : IId { [Key] [Column("id")] @@ -20,21 +20,22 @@ namespace AsbCloudDb.Model [Column("id_category"), Comment("id категории")] public int IdCategory { get; set; } - [Column("date_insert"), Comment("время добавления")] - public DateTime DateInsert { get; set; } - - [Column("date_modify"), Comment("время изменения")] - public DateTime DateModify { get; set; } + [Column("timestamp"), Comment("время добавления")] + public DateTime Timestamp { get; set; } [Column("data", TypeName = "jsonb"), Comment("Данные таблицы последних данных")] public Dictionary Data { get; set; } + [Column("is_deleted"), Comment("Пометка удаленным")] + public bool IsDeleted { get; set; } + [JsonIgnore] [ForeignKey(nameof(IdWell))] public virtual Well Well { get; set; } [JsonIgnore] [ForeignKey(nameof(IdCategory))] - public virtual LastDataCategory Category { get; set; } + [InverseProperty(nameof(Model.MeasureCategory.Measures))] + public virtual MeasureCategory Category { get; set; } } } diff --git a/AsbCloudDb/Model/LastDataCategory.cs b/AsbCloudDb/Model/MeasureCategory.cs similarity index 63% rename from AsbCloudDb/Model/LastDataCategory.cs rename to AsbCloudDb/Model/MeasureCategory.cs index d0b75682..3349a039 100644 --- a/AsbCloudDb/Model/LastDataCategory.cs +++ b/AsbCloudDb/Model/MeasureCategory.cs @@ -1,11 +1,12 @@ using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace AsbCloudDb.Model { - [Table("t_last_data_category"), Comment("Категория последних данных")] - public class LastDataCategory : IId + [Table("t_measure_category"), Comment("Категория последних данных")] + public class MeasureCategory : IId { [Key] [Column("id")] @@ -16,5 +17,8 @@ namespace AsbCloudDb.Model [Column("short_name"), Comment("Короткое название категории")] public string ShortName { get; set; } + + [InverseProperty(nameof(Model.Measure.Category))] + public virtual ICollection Measures { get; set; } } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 094b6881..189473eb 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -38,7 +38,7 @@ namespace AsbCloudInfrastructure services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); + services.AddTransient(); return services; } diff --git a/AsbCloudInfrastructure/Services/LastDataService.cs b/AsbCloudInfrastructure/Services/LastDataService.cs deleted file mode 100644 index f8f36df2..00000000 --- a/AsbCloudInfrastructure/Services/LastDataService.cs +++ /dev/null @@ -1,69 +0,0 @@ -using AsbCloudApp.Data; -using AsbCloudApp.Services; -using AsbCloudDb.Model; -using AsbCloudInfrastructure.Services.Cache; -using Mapster; -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace AsbCloudInfrastructure.Services -{ - public class LastDataService : ILastDataService - { - private readonly IAsbCloudDbContext db; - private readonly CacheTable cacheCategories; - - public LastDataService(IAsbCloudDbContext db, Cache.CacheDb cacheDb) - { - this.db = db; - cacheCategories = cacheDb.GetCachedTable((DbContext)db); - } - - public async Task> GetCategoriesAsync(CancellationToken token) - { - var entities = await cacheCategories.WhereAsync(token).ConfigureAwait(false); - var dto = entities.ToDictionary(e=>e.Id, e=>e.Name); - return dto; - } - - public async Task> GetAllLastAsync(int idWell, CancellationToken token) - { - var entities = await db.LastData - .Include(e => e.Category) - .Where(e => e.IdWell == idWell) - .AsNoTracking() - .ToListAsync(token) - .ConfigureAwait(false); - var dtos = entities.Adapt((d, s) => d.CategoryName = s.Category.Name); - return dtos; - } - - public Task> GetHisoryAsync(int idWell, int idCategory, CancellationToken token) - { - throw new System.NotImplementedException(); - } - - public Task InsertAsync(int idWell, LastDataDto data, CancellationToken token) - { - throw new System.NotImplementedException(); - } - - public Task MarkAsDeleteAsync(int idWell, int idData, CancellationToken token) - { - throw new System.NotImplementedException(); - } - - public Task UpdateAsync(int idWell, LastDataDto data, CancellationToken token) - { - throw new System.NotImplementedException(); - } - - public Task DeleteAsync(int idWell, int idData, CancellationToken token) - { - throw new System.NotImplementedException(); - } - } -} diff --git a/AsbCloudInfrastructure/Services/MeasureService.cs b/AsbCloudInfrastructure/Services/MeasureService.cs new file mode 100644 index 00000000..7cc58fde --- /dev/null +++ b/AsbCloudInfrastructure/Services/MeasureService.cs @@ -0,0 +1,141 @@ +using AsbCloudApp.Data; +using AsbCloudApp.Services; +using AsbCloudDb.Model; +using AsbCloudInfrastructure.Services.Cache; +using Mapster; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudInfrastructure.Services +{ + public class MeasureService : IMeasureService + { + private readonly IAsbCloudDbContext db; + private readonly CacheTable cacheCategories; + + public MeasureService(IAsbCloudDbContext db, Cache.CacheDb cacheDb) + { + this.db = db; + cacheCategories = cacheDb.GetCachedTable((DbContext)db); + } + + public async Task> GetCategoriesAsync(CancellationToken token) + { + var entities = await cacheCategories.WhereAsync(token).ConfigureAwait(false); + var dto = entities.ToDictionary(e=>e.Id, e=>e.Name); + return dto; + } + + public async Task> GetAllLastAsync(int idWell, CancellationToken token) + { + var categories = await cacheCategories.WhereAsync(token).ConfigureAwait(false); + if (!categories.Any()) + return default; + + var queries = categories.Select(c => db.Measures + .Where(m => m.IdWell == idWell && m.IdCategory == c.Id && !m.IsDeleted) + .OrderByDescending(m => m.Timestamp) + .Take(1) + ); + + var qi = queries.GetEnumerator(); + qi.MoveNext(); + var query = qi.Current; + while (qi.MoveNext()) + query = query.Union(qi.Current); + + //var query = db.Measures + // .Where(e => e.IdWell == idWell) + // .GroupBy(e => e.IdCategory) + // .Select(g => g.Where(e => e.Timestamp == g.Max(m => m.Timestamp)).First()); + + //var query = db.Measures + // .Where(m => m.IdWell == idWell && m.Timestamp == db.Measures + // .Where(subm => subm.IdWell == m.IdWell && subm.IdCategory == m.IdCategory).Max(subm => subm.Timestamp)); + + var entities = await query + .AsNoTracking() + .ToListAsync(token) + .ConfigureAwait(false); + + var dtos = entities.Adapt((d, s) => d.CategoryName = s.Category.Name); + return dtos; + } + + public async Task> GetHisoryAsync(int idWell, int idCategory, CancellationToken token) + { + var query = db.Measures.Include(m => m.Category) + .Where(m => m.IdWell == idWell && m.IdCategory == idCategory && !m.IsDeleted); + + var entities = await query + .AsNoTracking() + .ToListAsync(token) + .ConfigureAwait(false); + + var dtos = entities.Adapt((d, s) => d.CategoryName = s.Category.Name); + return dtos; + } + + public Task InsertAsync(int idWell, MeasureDto data, CancellationToken token) + { + if (data.IdCategory < 1) + throw new ArgumentException("wrong idCategory", nameof(data)); + if (data.Data is null) + throw new ArgumentException("data.data is not optional", nameof(data)); + var entity = data.Adapt(); + entity.Id = 0; + entity.IdWell = idWell; + db.Measures.Add(entity); + return db.SaveChangesAsync(token); + } + + public async Task UpdateAsync(int idWell, MeasureDto data, CancellationToken token) + { + if (data.Id < 1) + throw new ArgumentException("wrong id", nameof(data)); + if (data.IdCategory < 1) + throw new ArgumentException("wrong idCategory", nameof(data)); + if (data.Data is null) + throw new ArgumentException("data.data is not optional", nameof(data)); + + var entity = await db.Measures + .Where(m => m.Id == data.Id && !m.IsDeleted) + .FirstOrDefaultAsync(token) + .ConfigureAwait(false); + + if (entity is null) + throw new ArgumentException("id doesn't exist", nameof(data)); + + entity.Timestamp = data.Timestamp; + entity.Data = data.Data; + + return await db.SaveChangesAsync(token).ConfigureAwait(false); + } + + public async Task MarkAsDeleteAsync(int idWell, int idData, CancellationToken token) + { + if (idData < 1) + throw new ArgumentException("wrong id", nameof(idData)); + + var entity = await db.Measures.Where(m => m.IdWell == idWell && m.Id == idData) + .FirstOrDefaultAsync(token) + .ConfigureAwait(false); + + entity.IsDeleted = true; + + return await db.SaveChangesAsync(token).ConfigureAwait(false); + } + + public Task DeleteAsync(int idWell, int idData, CancellationToken token) + { + if (idData < 1) + throw new ArgumentException("wrong id", nameof(idData)); + db.Measures.RemoveRange(db.Measures.Where(m => m.IdWell == idWell && m.Id == idData)); + return db.SaveChangesAsync(token); + } + } +} diff --git a/AsbCloudWebApi/Controllers/LastDataController.cs b/AsbCloudWebApi/Controllers/MeasureController.cs similarity index 89% rename from AsbCloudWebApi/Controllers/LastDataController.cs rename to AsbCloudWebApi/Controllers/MeasureController.cs index 19bc0c36..af166536 100644 --- a/AsbCloudWebApi/Controllers/LastDataController.cs +++ b/AsbCloudWebApi/Controllers/MeasureController.cs @@ -9,13 +9,13 @@ namespace AsbCloudWebApi.Controllers { [ApiController] [Authorize] - [Route("api/well/{idWell}/lastData")] - public class LastDataController: ControllerBase + [Route("api/well/{idWell}/measure")] + public class MeasureController: ControllerBase { - private readonly ILastDataService lastDataService; + private readonly IMeasureService lastDataService; private readonly IWellService wellService; - public LastDataController(ILastDataService lastDataService, IWellService wellService) + public MeasureController(IMeasureService lastDataService, IWellService wellService) { this.lastDataService = lastDataService; this.wellService = wellService; @@ -55,7 +55,7 @@ namespace AsbCloudWebApi.Controllers } [HttpPost] - public async Task InsertAsync([FromRoute] int idWell, LastDataDto data, CancellationToken token = default) + public async Task InsertAsync([FromRoute] int idWell, MeasureDto data, CancellationToken token = default) { if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false)) return Forbid(); @@ -65,7 +65,7 @@ namespace AsbCloudWebApi.Controllers } [HttpPut] - public async Task UpdateAsync([FromRoute] int idWell, LastDataDto data, CancellationToken token = default) + public async Task UpdateAsync([FromRoute] int idWell, MeasureDto data, CancellationToken token = default) { if (!await CanUserAccessToWellAsync(idWell, token).ConfigureAwait(false)) return Forbid(); diff --git a/ConsoleApp1/ConsoleApp1.csproj b/ConsoleApp1/ConsoleApp1.csproj index b780c0f6..40fd5adb 100644 --- a/ConsoleApp1/ConsoleApp1.csproj +++ b/ConsoleApp1/ConsoleApp1.csproj @@ -23,4 +23,11 @@ + + + + + + + diff --git a/ConsoleApp1/Program.cs b/ConsoleApp1/Program.cs index 224d7bf8..05e2b497 100644 --- a/ConsoleApp1/Program.cs +++ b/ConsoleApp1/Program.cs @@ -1,10 +1,10 @@ -//using AsbCloudApp.Data; -//using AsbCloudDb.Model; -//using AsbCloudInfrastructure.Services; -//using AsbCloudInfrastructure.Services.Cache; +using AsbCloudApp.Data; +using AsbCloudDb.Model; +using AsbCloudInfrastructure.Services; +using AsbCloudInfrastructure.Services.Cache; //using AsbSaubReport; //using AutoMapper; -//using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using System; namespace ConsoleApp1 @@ -15,10 +15,13 @@ namespace ConsoleApp1 static void Main(/*string[] args*/) { - //var options = new DbContextOptionsBuilder() - // .UseNpgsql("Host=localhost;Database=postgres;Username=postgres;Password=q;Persist Security Info=True") - // .Options; - //var context = new AsbCloudDbContext(options); + var options = new DbContextOptionsBuilder() + .UseNpgsql("Host=localhost;Database=postgres;Username=postgres;Password=q;Persist Security Info=True") + .Options; + var context = new AsbCloudDbContext(options); + + var mservice = new MeasureService(context, new CacheDb()); + var r = mservice.GetAllLastAsync(1, default).Result; //var idWell = 1; //var dataSource = new ReportDataSourcePgCloud(context, idWell); @@ -36,7 +39,6 @@ namespace ConsoleApp1 //var s = generator.GetPagesCount(); //var fileName = generator.Make(); - Console.WriteLine("Done. Press any key to quit."); Console.ReadKey(); }