Добавить таблицу для учета комментариев и действий пользователя для вывода статистики по ChangeLog #30
@ -9,7 +9,7 @@ using DD.Persistence.Models.Common;
|
|||||||
namespace DD.Persistence.API.Controllers;
|
namespace DD.Persistence.API.Controllers;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Authorize]
|
//[Authorize]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class ChangeLogController : ControllerBase, IChangeLogApi
|
public class ChangeLogController : ControllerBase, IChangeLogApi
|
||||||
{
|
{
|
||||||
@ -25,10 +25,13 @@ public class ChangeLogController : ControllerBase, IChangeLogApi
|
|||||||
public async Task<IActionResult> Add(
|
public async Task<IActionResult> Add(
|
||||||
|
|||||||
[FromRoute] Guid idDiscriminator,
|
[FromRoute] Guid idDiscriminator,
|
||||||
[FromBody] ChangeLogValuesDto dto,
|
[FromBody] ChangeLogValuesDto dto,
|
||||||
ng.frolov
commented
Было бы здорова дать методам контроллеров текстовое описание для сваггера. Было бы здорова дать методам контроллеров текстовое описание для сваггера.
|
|||||||
|
string comment,
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
var userId = User.GetUserId<Guid>();
|
//var userId = User.GetUserId<Guid>();
|
||||||
var result = await repository.AddRange(userId, idDiscriminator, [dto], token);
|
var userId = Guid.NewGuid();
|
||||||
|
var changeLogCommit = new ChangeLogCommitDto(userId, comment, [dto]);
|
||||||
ng.frolov
commented
Скорее всего этот метод лишний. Скорее всего этот метод лишний.
|
|||||||
|
var result = await repository.AddRange(idDiscriminator, changeLogCommit, token);
|
||||||
|
|
||||||
return CreatedAtAction(nameof(Add), result);
|
return CreatedAtAction(nameof(Add), result);
|
||||||
}
|
}
|
||||||
@ -38,10 +41,13 @@ public class ChangeLogController : ControllerBase, IChangeLogApi
|
|||||||
public async Task<IActionResult> AddRange(
|
public async Task<IActionResult> AddRange(
|
||||||
[FromRoute] Guid idDiscriminator,
|
[FromRoute] Guid idDiscriminator,
|
||||||
[FromBody] IEnumerable<ChangeLogValuesDto> dtos,
|
[FromBody] IEnumerable<ChangeLogValuesDto> dtos,
|
||||||
|
string comment,
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
var userId = User.GetUserId<Guid>();
|
//var userId = User.GetUserId<Guid>();
|
||||||
var result = await repository.AddRange(userId, idDiscriminator, dtos, token);
|
var userId = Guid.NewGuid();
|
||||||
|
var changeLogCommit = new ChangeLogCommitDto(userId, comment, dtos);
|
||||||
|
var result = await repository.AddRange(idDiscriminator, changeLogCommit, token);
|
||||||
|
|
||||||
return CreatedAtAction(nameof(AddRange), result);
|
return CreatedAtAction(nameof(AddRange), result);
|
||||||
}
|
}
|
||||||
@ -71,10 +77,13 @@ public class ChangeLogController : ControllerBase, IChangeLogApi
|
|||||||
public async Task<IActionResult> ClearAndAddRange(
|
public async Task<IActionResult> ClearAndAddRange(
|
||||||
[FromRoute] Guid idDiscriminator,
|
[FromRoute] Guid idDiscriminator,
|
||||||
[FromBody] IEnumerable<ChangeLogValuesDto> dtos,
|
[FromBody] IEnumerable<ChangeLogValuesDto> dtos,
|
||||||
|
string comment,
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
var userId = User.GetUserId<Guid>();
|
//var userId = User.GetUserId<Guid>();
|
||||||
var result = await repository.ClearAndAddRange(userId, idDiscriminator, dtos, token);
|
var userId = Guid.NewGuid();
|
||||||
|
var changeLogCommit = new ChangeLogCommitDto(userId, comment, dtos);
|
||||||
|
var result = await repository.ClearAndAddRange(idDiscriminator, changeLogCommit, token);
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,10 +91,13 @@ public class ChangeLogController : ControllerBase, IChangeLogApi
|
|||||||
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
||||||
public async Task<IActionResult> Update(
|
public async Task<IActionResult> Update(
|
||||||
ChangeLogValuesDto dto,
|
ChangeLogValuesDto dto,
|
||||||
|
string comment,
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
var userId = User.GetUserId<Guid>();
|
//var userId = User.GetUserId<Guid>();
|
||||||
var result = await repository.UpdateRange(userId, [dto], token);
|
var userId = Guid.NewGuid();
|
||||||
|
var changeLogCommit = new ChangeLogCommitDto(userId, comment, [dto]);
|
||||||
|
var result = await repository.UpdateRange(changeLogCommit, token);
|
||||||
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
@ -94,10 +106,13 @@ public class ChangeLogController : ControllerBase, IChangeLogApi
|
|||||||
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
[ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)]
|
||||||
public async Task<IActionResult> UpdateRange(
|
public async Task<IActionResult> UpdateRange(
|
||||||
IEnumerable<ChangeLogValuesDto> dtos,
|
IEnumerable<ChangeLogValuesDto> dtos,
|
||||||
|
string comment,
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
var userId = User.GetUserId<Guid>();
|
//var userId = User.GetUserId<Guid>();
|
||||||
var result = await repository.UpdateRange(userId, dtos, token);
|
var userId = Guid.NewGuid();
|
||||||
|
var changeLogCommit = new ChangeLogCommitDto(userId, comment, dtos);
|
||||||
|
var result = await repository.UpdateRange(changeLogCommit, token);
|
||||||
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|||||||
namespace DD.Persistence.Database.Postgres.Migrations
|
namespace DD.Persistence.Database.Postgres.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PersistencePostgresContext))]
|
[DbContext(typeof(PersistencePostgresContext))]
|
||||||
[Migration("20250205114037_Init")]
|
[Migration("20250211124554_Init")]
|
||||||
partial class Init
|
partial class Init
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -41,14 +41,14 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
.HasColumnType("uuid")
|
.HasColumnType("uuid")
|
||||||
.HasComment("Автор изменения");
|
.HasComment("Автор изменения");
|
||||||
|
|
||||||
|
b.Property<Guid>("IdCommit")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasComment("Id коммита");
|
||||||
|
|
||||||
b.Property<Guid>("IdDiscriminator")
|
b.Property<Guid>("IdDiscriminator")
|
||||||
.HasColumnType("uuid")
|
.HasColumnType("uuid")
|
||||||
.HasComment("Дискриминатор таблицы");
|
.HasComment("Дискриминатор таблицы");
|
||||||
|
|
||||||
b.Property<Guid?>("IdEditor")
|
|
||||||
.HasColumnType("uuid")
|
|
||||||
.HasComment("Редактор");
|
|
||||||
|
|
||||||
b.Property<Guid?>("IdNext")
|
b.Property<Guid?>("IdNext")
|
||||||
.HasColumnType("uuid")
|
.HasColumnType("uuid")
|
||||||
.HasComment("Id заменяющей записи");
|
.HasComment("Id заменяющей записи");
|
||||||
@ -64,9 +64,36 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("IdCommit");
|
||||||
|
|
||||||
b.ToTable("change_log");
|
b.ToTable("change_log");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLogCommit", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasComment("Id коммита");
|
||||||
|
|
||||||
|
b.Property<string>("Comment")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasComment("Комментарий к коммиту");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("Creation")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasComment("Дата создания коммита");
|
||||||
|
|
||||||
|
b.Property<Guid>("IdCommitAuthor")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasComment("Пользователь, создавший коммит");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("change_log_commit");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.DataSourceSystem", b =>
|
modelBuilder.Entity("DD.Persistence.Database.Entity.DataSourceSystem", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("SystemId")
|
b.Property<Guid>("SystemId")
|
||||||
@ -214,6 +241,17 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
b.ToTable("timestamped_values");
|
b.ToTable("timestamped_values");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLog", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DD.Persistence.Database.Entity.ChangeLogCommit", "Commit")
|
||||||
|
.WithMany("ChangeLogItems")
|
||||||
|
.HasForeignKey("IdCommit")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Commit");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
|
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("DD.Persistence.Database.Entity.DataSourceSystem", "System")
|
b.HasOne("DD.Persistence.Database.Entity.DataSourceSystem", "System")
|
||||||
@ -224,6 +262,11 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
|
|
||||||
b.Navigation("System");
|
b.Navigation("System");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLogCommit", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("ChangeLogItems");
|
||||||
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,21 +13,17 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "change_log",
|
name: "change_log_commit",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false, comment: "Ключ записи"),
|
Id = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id коммита"),
|
||||||
IdDiscriminator = table.Column<Guid>(type: "uuid", nullable: false, comment: "Дискриминатор таблицы"),
|
IdCommitAuthor = table.Column<Guid>(type: "uuid", nullable: false, comment: "Пользователь, создавший коммит"),
|
||||||
IdAuthor = table.Column<Guid>(type: "uuid", nullable: false, comment: "Автор изменения"),
|
Creation = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата создания коммита"),
|
||||||
IdEditor = table.Column<Guid>(type: "uuid", nullable: true, comment: "Редактор"),
|
Comment = table.Column<string>(type: "text", nullable: false, comment: "Комментарий к коммиту")
|
||||||
Creation = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата создания записи"),
|
|
||||||
Obsolete = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true, comment: "Дата устаревания (например при удалении)"),
|
|
||||||
IdNext = table.Column<Guid>(type: "uuid", nullable: true, comment: "Id заменяющей записи"),
|
|
||||||
Value = table.Column<string>(type: "jsonb", nullable: false, comment: "Значение")
|
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
table.PrimaryKey("PK_change_log", x => x.Id);
|
table.PrimaryKey("PK_change_log_commit", x => x.Id);
|
||||||
});
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
@ -98,6 +94,30 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
table.PrimaryKey("PK_timestamped_values", x => new { x.DiscriminatorId, x.Timestamp });
|
table.PrimaryKey("PK_timestamped_values", x => new { x.DiscriminatorId, x.Timestamp });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "change_log",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false, comment: "Ключ записи"),
|
||||||
|
IdDiscriminator = table.Column<Guid>(type: "uuid", nullable: false, comment: "Дискриминатор таблицы"),
|
||||||
|
IdAuthor = table.Column<Guid>(type: "uuid", nullable: false, comment: "Автор изменения"),
|
||||||
|
Creation = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата создания записи"),
|
||||||
|
Obsolete = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true, comment: "Дата устаревания (например при удалении)"),
|
||||||
|
IdNext = table.Column<Guid>(type: "uuid", nullable: true, comment: "Id заменяющей записи"),
|
||||||
|
Value = table.Column<string>(type: "jsonb", nullable: false, comment: "Значение"),
|
||||||
|
IdCommit = table.Column<Guid>(type: "uuid", nullable: false, comment: "Id коммита")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_change_log", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_change_log_change_log_commit_IdCommit",
|
||||||
|
column: x => x.IdCommit,
|
||||||
|
principalTable: "change_log_commit",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "tech_message",
|
name: "tech_message",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
@ -120,6 +140,11 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_change_log_IdCommit",
|
||||||
|
table: "change_log",
|
||||||
|
column: "IdCommit");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_tech_message_SystemId",
|
name: "IX_tech_message_SystemId",
|
||||||
table: "tech_message",
|
table: "tech_message",
|
||||||
@ -147,6 +172,9 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "timestamped_values");
|
name: "timestamped_values");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "change_log_commit");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "data_source_system");
|
name: "data_source_system");
|
||||||
}
|
}
|
@ -38,14 +38,14 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
.HasColumnType("uuid")
|
.HasColumnType("uuid")
|
||||||
.HasComment("Автор изменения");
|
.HasComment("Автор изменения");
|
||||||
|
|
||||||
|
b.Property<Guid>("IdCommit")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasComment("Id коммита");
|
||||||
|
|
||||||
b.Property<Guid>("IdDiscriminator")
|
b.Property<Guid>("IdDiscriminator")
|
||||||
.HasColumnType("uuid")
|
.HasColumnType("uuid")
|
||||||
.HasComment("Дискриминатор таблицы");
|
.HasComment("Дискриминатор таблицы");
|
||||||
|
|
||||||
b.Property<Guid?>("IdEditor")
|
|
||||||
.HasColumnType("uuid")
|
|
||||||
.HasComment("Редактор");
|
|
||||||
|
|
||||||
b.Property<Guid?>("IdNext")
|
b.Property<Guid?>("IdNext")
|
||||||
.HasColumnType("uuid")
|
.HasColumnType("uuid")
|
||||||
.HasComment("Id заменяющей записи");
|
.HasComment("Id заменяющей записи");
|
||||||
@ -61,9 +61,36 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("IdCommit");
|
||||||
|
|
||||||
b.ToTable("change_log");
|
b.ToTable("change_log");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLogCommit", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasComment("Id коммита");
|
||||||
|
|
||||||
|
b.Property<string>("Comment")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasComment("Комментарий к коммиту");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("Creation")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasComment("Дата создания коммита");
|
||||||
|
|
||||||
|
b.Property<Guid>("IdCommitAuthor")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasComment("Пользователь, создавший коммит");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("change_log_commit");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.DataSourceSystem", b =>
|
modelBuilder.Entity("DD.Persistence.Database.Entity.DataSourceSystem", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("SystemId")
|
b.Property<Guid>("SystemId")
|
||||||
@ -211,6 +238,17 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
b.ToTable("timestamped_values");
|
b.ToTable("timestamped_values");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLog", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DD.Persistence.Database.Entity.ChangeLogCommit", "Commit")
|
||||||
|
.WithMany("ChangeLogItems")
|
||||||
|
.HasForeignKey("IdCommit")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Commit");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
|
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("DD.Persistence.Database.Entity.DataSourceSystem", "System")
|
b.HasOne("DD.Persistence.Database.Entity.DataSourceSystem", "System")
|
||||||
@ -221,6 +259,11 @@ namespace DD.Persistence.Database.Postgres.Migrations
|
|||||||
|
|
||||||
b.Navigation("System");
|
b.Navigation("System");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLogCommit", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("ChangeLogItems");
|
||||||
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,9 +22,6 @@ public class ChangeLog : IChangeLog
|
|||||||
[Comment("Автор изменения")]
|
[Comment("Автор изменения")]
|
||||||
public Guid IdAuthor { get; set; }
|
public Guid IdAuthor { get; set; }
|
||||||
ng.frolov
commented
Комментарий про денормализацию БД Комментарий про денормализацию БД
|
|||||||
|
|
||||||
[Comment("Редактор")]
|
|
||||||
public Guid? IdEditor { get; set; }
|
|
||||||
|
|
||||||
[Comment("Дата создания записи")]
|
[Comment("Дата создания записи")]
|
||||||
public DateTimeOffset Creation { get; set; }
|
public DateTimeOffset Creation { get; set; }
|
||||||
|
|
||||||
@ -36,4 +33,10 @@ public class ChangeLog : IChangeLog
|
|||||||
|
|
||||||
[Column(TypeName = "jsonb"), Comment("Значение")]
|
[Column(TypeName = "jsonb"), Comment("Значение")]
|
||||||
public required IDictionary<string, object> Value { get; set; }
|
public required IDictionary<string, object> Value { get; set; }
|
||||||
|
|
||||||
|
[Required, Comment("Id коммита")]
|
||||||
|
public Guid IdCommit { get; set; }
|
||||||
ng.frolov
commented
Для устаревших записей у нас есть 2 коммита: один при создании записи и еще один при устаревании. Для устаревших записей у нас есть 2 коммита: один при создании записи и еще один при устаревании.
Кстати туда же можно унести инфо о пользователях и датах.
|
|||||||
|
|
||||||
|
[Required, ForeignKey(nameof(IdCommit)), Comment("Коммит пользователя")]
|
||||||
|
public virtual ChangeLogCommit Commit { get; set; } = null!;
|
||||||
}
|
}
|
||||||
|
32
DD.Persistence.Database/Entity/ChangeLogCommit.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DD.Persistence.Database.Entity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Таблица c коммитами пользователей
|
||||||
|
/// </summary>
|
||||||
|
[Table("change_log_commit")]
|
||||||
|
public class ChangeLogCommit
|
||||||
|
{
|
||||||
|
[Key, Comment("Id коммита")]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
[Comment("Пользователь, создавший коммит")]
|
||||||
|
public Guid IdCommitAuthor { get; set; }
|
||||||
|
|
||||||
|
[Comment("Дата создания коммита")]
|
||||||
|
public DateTimeOffset Creation { get; set; }
|
||||||
|
|
||||||
|
[Comment("Комментарий к коммиту")]
|
||||||
|
public required string Comment { get; set; }
|
||||||
|
|
||||||
|
[Required, InverseProperty(nameof(ChangeLog.Commit)), Comment("Журнал изменений")]
|
||||||
|
public virtual ICollection<ChangeLog> ChangeLogItems { get; set; } = null!;
|
||||||
|
}
|
@ -28,4 +28,4 @@ public class TechMessage : ITimestampedItem
|
|||||||
|
|
||||||
[Comment("Статус события")]
|
[Comment("Статус события")]
|
||||||
public int EventState { get; set; }
|
public int EventState { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,6 @@ public interface IChangeLog
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Guid IdAuthor { get; set; }
|
public Guid IdAuthor { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Редактор
|
|
||||||
/// </summary>
|
|
||||||
public Guid? IdEditor { get; set; }
|
|
||||||
ng.frolov
commented
Свойства не должны быть реализованы в интерфейсе. Свойства не должны быть реализованы в интерфейсе.
А почему автор коммита устаревания удален, а автор коммита создания нет?
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Дата создания записи
|
/// Дата создания записи
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -16,6 +16,8 @@ public class PersistenceDbContext : DbContext
|
|||||||
|
|
||||||
public DbSet<ChangeLog> ChangeLog => Set<ChangeLog>();
|
public DbSet<ChangeLog> ChangeLog => Set<ChangeLog>();
|
||||||
|
|
||||||
|
public DbSet<ChangeLogCommit> ChangeLogAction => Set<ChangeLogCommit>();
|
||||||
|
|
||||||
public DbSet<TechMessage> TechMessage => Set<TechMessage>();
|
public DbSet<TechMessage> TechMessage => Set<TechMessage>();
|
||||||
|
|
||||||
public DbSet<ParameterData> ParameterData => Set<ParameterData>();
|
public DbSet<ParameterData> ParameterData => Set<ParameterData>();
|
||||||
|
@ -18,12 +18,15 @@ public class ChangeLogRepository : IChangeLogRepository
|
|||||||
this.db = db;
|
this.db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<int> AddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
|
public async Task<int> AddRange(Guid idDiscriminator, ChangeLogCommitDto commitDto, CancellationToken token)
|
||||||
{
|
{
|
||||||
|
var commit = CreateCommit(commitDto);
|
||||||
|
db.Set<ChangeLogCommit>().Add(commit);
|
||||||
|
|
||||||
var entities = new List<ChangeLog>();
|
var entities = new List<ChangeLog>();
|
||||||
foreach (var dto in dtos)
|
foreach (var values in commitDto.ChangeLogItems)
|
||||||
{
|
{
|
||||||
var entity = CreateEntityFromDto(idAuthor, idDiscriminator, dto);
|
var entity = CreateChangeLogFromDto(idDiscriminator, commit.Id, commit.IdCommitAuthor, values);
|
||||||
entities.Add(entity);
|
entities.Add(entity);
|
||||||
}
|
}
|
||||||
db.Set<ChangeLog>().AddRange(entities);
|
db.Set<ChangeLog>().AddRange(entities);
|
||||||
@ -71,20 +74,20 @@ public class ChangeLogRepository : IChangeLogRepository
|
|||||||
foreach (var entity in entities)
|
foreach (var entity in entities)
|
||||||
{
|
{
|
||||||
entity.Obsolete = updateTime;
|
entity.Obsolete = updateTime;
|
||||||
entity.IdEditor = idEditor;
|
//entity.IdEditor = idEditor;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await db.SaveChangesAsync(token);
|
return await db.SaveChangesAsync(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<int> ClearAndAddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
|
public async Task<int> ClearAndAddRange(Guid idDiscriminator, ChangeLogCommitDto commitDto, CancellationToken token)
|
||||||
{
|
{
|
||||||
var result = 0;
|
var result = 0;
|
||||||
|
|
||||||
ng.frolov
commented
Этот метод должен помечать все записи относящиеся к дискриминатору как удаленные и добавлять новые. Этот метод должен помечать все записи относящиеся к дискриминатору как удаленные и добавлять новые.
|
|||||||
using var transaction = await db.Database.BeginTransactionAsync(token);
|
using var transaction = await db.Database.BeginTransactionAsync(token);
|
||||||
|
|
||||||
result += await MarkAsDeleted(idAuthor, idDiscriminator, token);
|
result += await MarkAsDeleted(commitDto.IdAuthor, idDiscriminator, token);
|
||||||
result += await AddRange(idAuthor, idDiscriminator, dtos, token);
|
result += await AddRange(idDiscriminator, commitDto, token);
|
||||||
ng.frolov
commented
эта логкальная переменная дальше нигде не используется. Зачем она здесь? эта логкальная переменная дальше нигде не используется. Зачем она здесь?
|
|||||||
|
|
||||||
await transaction.CommitAsync(token);
|
await transaction.CommitAsync(token);
|
||||||
|
|
||||||
@ -92,19 +95,26 @@ public class ChangeLogRepository : IChangeLogRepository
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<int> UpdateRange(Guid idEditor, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
|
public async Task<int> UpdateRange(ChangeLogCommitDto commitDto, CancellationToken token)
|
||||||
{
|
{
|
||||||
var dbSet = db.Set<ChangeLog>();
|
var dbSet = db.Set<ChangeLog>();
|
||||||
|
|
||||||
var updatedIds = dtos.Select(d => d.Id);
|
var updatedIds = commitDto.ChangeLogItems.Select(d => d.Id);
|
||||||
var updatedEntities = dbSet
|
var updatedEntities = dbSet
|
||||||
.Where(s => updatedIds.Contains(s.Id))
|
.Where(s => updatedIds.Contains(s.Id))
|
||||||
.ToDictionary(s => s.Id);
|
.ToDictionary(s => s.Id);
|
||||||
|
|
||||||
var result = 0;
|
var result = 0;
|
||||||
using var transaction = await db.Database.BeginTransactionAsync(token);
|
|
||||||
|
|
||||||
foreach (var dto in dtos)
|
var commit = CreateCommit(commitDto);
|
||||||
|
db.Set<ChangeLogCommit>().Add(commit);
|
||||||
ng.frolov
commented
Linq очень не оптимально материализует в словари.
Linq очень не оптимально материализует в словари.
Тут лучше материализовать сперва в массив, а затем массив в словарь.
+ Используй асинхронные методы материализации в асинхронных методах репозиториев
|
|||||||
|
db.SaveChanges();
|
||||||
|
|
||||||
|
//using var transaction = await db.Database.BeginTransactionAsync(token);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var dto in commitDto.ChangeLogItems)
|
||||||
{
|
{
|
||||||
var updatedEntity = updatedEntities.GetValueOrDefault(dto.Id);
|
var updatedEntity = updatedEntities.GetValueOrDefault(dto.Id);
|
||||||
if (updatedEntity is null)
|
if (updatedEntity is null)
|
||||||
@ -112,16 +122,15 @@ public class ChangeLogRepository : IChangeLogRepository
|
|||||||
throw new ArgumentException($"Entity with id = {dto.Id} doesn't exist in Db", nameof(dto));
|
throw new ArgumentException($"Entity with id = {dto.Id} doesn't exist in Db", nameof(dto));
|
||||||
}
|
}
|
||||||
|
|
||||||
var newEntity = CreateEntityFromDto(idEditor, updatedEntity.IdDiscriminator, dto);
|
var newEntity = CreateChangeLogFromDto(commitDto.IdAuthor, updatedEntity.IdDiscriminator, commit.Id, dto);
|
||||||
dbSet.Add(newEntity);
|
dbSet.Add(newEntity);
|
||||||
|
|
||||||
updatedEntity.IdNext = newEntity.Id;
|
updatedEntity.IdNext = newEntity.Id;
|
||||||
updatedEntity.Obsolete = DateTimeOffset.UtcNow;
|
updatedEntity.Obsolete = DateTimeOffset.UtcNow;
|
||||||
updatedEntity.IdEditor = idEditor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result = await db.SaveChangesAsync(token);
|
result = await db.SaveChangesAsync(token);
|
||||||
await transaction.CommitAsync(token);
|
//await transaction.CommitAsync(token);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
@ -195,7 +204,7 @@ public class ChangeLogRepository : IChangeLogRepository
|
|||||||
return datesOnly;
|
return datesOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ChangeLog CreateEntityFromDto(Guid idAuthor, Guid idDiscriminator, ChangeLogValuesDto dto)
|
private static ChangeLog CreateChangeLogFromDto(Guid idDiscriminator, Guid idCommit, Guid idAuthor, ChangeLogValuesDto dto)
|
||||||
{
|
{
|
||||||
var entity = new ChangeLog()
|
var entity = new ChangeLog()
|
||||||
{
|
{
|
||||||
@ -203,14 +212,23 @@ public class ChangeLogRepository : IChangeLogRepository
|
|||||||
Creation = DateTimeOffset.UtcNow,
|
Creation = DateTimeOffset.UtcNow,
|
||||||
IdAuthor = idAuthor,
|
IdAuthor = idAuthor,
|
||||||
IdDiscriminator = idDiscriminator,
|
IdDiscriminator = idDiscriminator,
|
||||||
IdEditor = idAuthor,
|
Value = dto.Value,
|
||||||
|
IdCommit = idCommit,
|
||||||
Value = dto.Value
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ChangeLogCommit CreateCommit(ChangeLogCommitDto commitDto)
|
||||||
|
{
|
||||||
|
return new ChangeLogCommit()
|
||||||
|
{
|
||||||
|
Comment = commitDto.Comment,
|
||||||
|
Creation = DateTimeOffset.UtcNow,
|
||||||
|
IdCommitAuthor = commitDto.IdAuthor
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<ChangeLogValuesDto>> GetGtDate(Guid idDiscriminator, DateTimeOffset dateBegin, CancellationToken token)
|
public async Task<IEnumerable<ChangeLogValuesDto>> GetGtDate(Guid idDiscriminator, DateTimeOffset dateBegin, CancellationToken token)
|
||||||
{
|
{
|
||||||
var date = dateBegin.ToUniversalTime();
|
var date = dateBegin.ToUniversalTime();
|
||||||
|
33
DD.Persistence.Models/Requests/ChangeLogCommitDto.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
namespace DD.Persistence.Models.Requests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Модель коммита с изменениями
|
||||||
|
/// </summary>
|
||||||
|
public class ChangeLogCommitDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Пользователь, совершающий коммит
|
||||||
|
/// </summary>
|
||||||
|
public Guid IdAuthor { get; set; }
|
||||||
ng.frolov
commented
По правильному стоит разделить эту Dto на 2. Сделать отдельную dto для создания нового коммита, так как это поле генерируется внутри репозитория. По правильному стоит разделить эту Dto на 2. Сделать отдельную dto для создания нового коммита, так как это поле генерируется внутри репозитория.
|
|||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Комментарий
|
||||||
|
/// </summary>
|
||||||
|
public string Comment { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Набор изменений
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<ChangeLogValuesDto> ChangeLogItems { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public ChangeLogCommitDto(Guid idAuthor, string comment, IEnumerable<ChangeLogValuesDto> changeLogItems)
|
||||||
|
{
|
||||||
|
IdAuthor = idAuthor;
|
||||||
|
Comment = comment;
|
||||||
|
ChangeLogItems = changeLogItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -14,9 +14,10 @@ public interface IChangeLogApi : ISyncWithDiscriminatorApi<ChangeLogValuesDto>
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="idDiscriminator"></param>
|
/// <param name="idDiscriminator"></param>
|
||||||
/// <param name="dtos"></param>
|
/// <param name="dtos"></param>
|
||||||
|
/// <param name="comment"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IActionResult> ClearAndAddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
|
Task<IActionResult> ClearAndAddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, string comment, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получение данных на текущую дату (с пагинацией)
|
/// Получение данных на текущую дату (с пагинацией)
|
||||||
@ -52,34 +53,38 @@ public interface IChangeLogApi : ISyncWithDiscriminatorApi<ChangeLogValuesDto>
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="idDiscriminator"></param>
|
/// <param name="idDiscriminator"></param>
|
||||||
/// <param name="dto"></param>
|
/// <param name="dto"></param>
|
||||||
|
/// <param name="comment">комментарий</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IActionResult> Add(Guid idDiscriminator, ChangeLogValuesDto dto, CancellationToken token);
|
Task<IActionResult> Add(Guid idDiscriminator, ChangeLogValuesDto dto, string comment, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Добавить несколько записей
|
/// Добавить несколько записей
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="idDiscriminator"></param>
|
/// <param name="idDiscriminator"></param>
|
||||||
/// <param name="dtos"></param>
|
/// <param name="dtos"></param>
|
||||||
|
/// <param name="comment">комментарий</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IActionResult> AddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
|
Task<IActionResult> AddRange(Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, string comment, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Обновить одну запись
|
/// Обновить одну запись
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dto"></param>
|
/// <param name="dto"></param>
|
||||||
|
/// <param name="comment"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IActionResult> Update(ChangeLogValuesDto dto, CancellationToken token);
|
Task<IActionResult> Update(ChangeLogValuesDto dto, string comment, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Обновить несколько записей
|
/// Обновить несколько записей
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dtos"></param>
|
/// <param name="dtos"></param>
|
||||||
|
/// <param name="comment">комментарий</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IActionResult> UpdateRange(IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
|
Task<IActionResult> UpdateRange(IEnumerable<ChangeLogValuesDto> dtos, string comment, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Удалить одну запись
|
/// Удалить одну запись
|
||||||
|
@ -1,24 +1,23 @@
|
|||||||
using DD.Persistence.Models;
|
using DD.Persistence.Models;
|
||||||
using DD.Persistence.Models.Common;
|
using DD.Persistence.Models.Common;
|
||||||
using DD.Persistence.Models.Requests;
|
using DD.Persistence.Models.Requests;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
||||||
|
|
||||||
namespace DD.Persistence.Repositories;
|
namespace DD.Persistence.Repositories;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Интерфейс для работы с историческими данными
|
/// Интерфейс для работы с историческими данными
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TDto"></typeparam>
|
|
||||||
public interface IChangeLogRepository : ISyncWithDiscriminatorRepository<ChangeLogValuesDto>
|
public interface IChangeLogRepository : ISyncWithDiscriminatorRepository<ChangeLogValuesDto>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Добавление записей
|
/// Добавление записей
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="idAuthor">пользователь, который добавляет</param>
|
|
||||||
/// <param name="idDiscriminator">ключ справочника</param>
|
/// <param name="idDiscriminator">ключ справочника</param>
|
||||||
/// <param name="dtos"></param>
|
/// <param name="dto">коммит с изменениями</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<int> AddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
|
Task<int> AddRange(Guid idDiscriminator, ChangeLogCommitDto dto, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Пометить записи как удаленные
|
/// Пометить записи как удаленные
|
||||||
@ -41,21 +40,19 @@ public interface IChangeLogRepository : ISyncWithDiscriminatorRepository<ChangeL
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Очистить и добавить новые
|
/// Очистить и добавить новые
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="idAuthor"></param>
|
|
||||||
/// <param name="idDiscriminator"></param>
|
/// <param name="idDiscriminator"></param>
|
||||||
/// <param name="dtos"></param>
|
/// <param name="commitDto">коммит с изменениями</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<int> ClearAndAddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
|
Task<int> ClearAndAddRange(Guid idDiscriminator, ChangeLogCommitDto commitDto, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Редактирование записей
|
/// Редактирование записей
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="idEditor">пользователь, который редактирует</param>
|
/// <param name="commitDto">коммит с изменениями</param>
|
||||||
/// <param name="dtos"></param>
|
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<int> UpdateRange(Guid idEditor, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token);
|
Task<int> UpdateRange(ChangeLogCommitDto commitDto, CancellationToken token);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получение актуальных записей на определенный момент времени (с пагинацией)
|
/// Получение актуальных записей на определенный момент времени (с пагинацией)
|
||||||
|
Должен остаться только один