Compare commits

..

1 Commits

Author SHA1 Message Date
2c66adfae7 Наработки для спецификаций 2025-02-10 16:41:31 +05:00
62 changed files with 816 additions and 694 deletions

View File

@ -9,7 +9,7 @@ using DD.Persistence.Models.Common;
namespace DD.Persistence.API.Controllers;
[ApiController]
[Authorize]
//[Authorize]
[Route("api/[controller]")]
public class ChangeLogController : ControllerBase, IChangeLogApi
{

View File

@ -12,7 +12,7 @@ namespace DD.Persistence.API.Controllers;
/// Работа с уставками
/// </summary>
[ApiController]
[Authorize]
//[Authorize]
[Route("api/[controller]")]
public class SetpointController : ControllerBase, ISetpointApi
{
@ -105,8 +105,8 @@ public class SetpointController : ControllerBase, ISetpointApi
[ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)]
public async Task<IActionResult> Add(Guid setpointKey, object newValue, CancellationToken token)
{
var userId = User.GetUserId<Guid>();
await setpointRepository.Add(setpointKey, (JsonElement)newValue, userId, token);
//var userId = User.GetUserId<Guid>();
await setpointRepository.Add(setpointKey, (JsonElement)newValue, Guid.NewGuid(), token);
return CreatedAtAction(nameof(Add), true);
}

View File

@ -1,5 +1,4 @@
using DD.Persistence.Filter.Models.Abstractions;
using DD.Persistence.Models;
using DD.Persistence.Models;
using DD.Persistence.Models.Common;
using DD.Persistence.Repositories;
using DD.Persistence.Services.Interfaces;
@ -46,7 +45,6 @@ public class TimestampedValuesController : ControllerBase
/// </summary>
/// <param name="discriminatorIds">Набор дискриминаторов</param>
/// <param name="timestampBegin">Фильтр позднее даты</param>
/// <param name="filterTree">Кастомный фильтр по набору значений</param>
/// <param name="columnNames">Фильтр свойств набора</param>
/// <param name="skip"></param>
/// <param name="take"></param>
@ -54,14 +52,9 @@ public class TimestampedValuesController : ControllerBase
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<TimestampedValuesDto>), (int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.NoContent)]
public async Task<ActionResult<IEnumerable<TimestampedValuesDto>>> Get([FromQuery] IEnumerable<Guid> discriminatorIds,
DateTimeOffset? timestampBegin,
[FromQuery] TNode? filterTree,
[FromQuery] string[]? columnNames,
int skip, int take,
CancellationToken token)
public async Task<ActionResult<IEnumerable<TimestampedValuesDto>>> Get([FromQuery] IEnumerable<Guid> discriminatorIds, DateTimeOffset? timestampBegin, [FromQuery] string[]? columnNames, int skip, int take, CancellationToken token)
{
var result = await timestampedValuesService.Get(discriminatorIds, timestampBegin, filterTree, columnNames, skip, take, token);
var result = await timestampedValuesService.Get(discriminatorIds, timestampBegin, columnNames, skip, take, token);
return result.Any() ? Ok(result) : NoContent();
}

View File

@ -28,8 +28,4 @@
<ProjectReference Include="..\DD.Persistence\DD.Persistence.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Docs\" />
</ItemGroup>
</Project>

View File

@ -1,14 +1,16 @@
using DD.Persistence.Filter.Models.Abstractions;
using DD.Persistence.Models.Configurations;
using DD.Persistence.Services;
using DD.Persistence.Services.Interfaces;
using Mapster;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using DD.Persistence.Models;
using DD.Persistence.Models.Configurations;
using DD.Persistence.Services;
using DD.Persistence.Services.Interfaces;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Reflection;
using System.Text.Json.Nodes;
using DD.Persistence.Database.Entity;
namespace DD.Persistence.API;
@ -28,7 +30,6 @@ public static class DependencyInjection
new OpenApiSchema {Type = "number", Format = "float" }
]
});
c.MapType<TNode>(() => new OpenApiSchema { Type = "string" });
c.CustomOperationIds(e =>
{

View File

@ -1,359 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36" version="24.8.3">
<diagram name="Страница — 1" id="7k5Wemfp-yc9piGHxsiE">
<mxGraphModel dx="2049" dy="1054" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1300" pageHeight="1050" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="_dnhcZFeje3u91oS2JwL-1" value="" style="endArrow=none;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="355" y="930" as="sourcePoint" />
<mxPoint x="355" y="210" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-2" value="" style="endArrow=none;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="555" y="930" as="sourcePoint" />
<mxPoint x="555" y="210" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-4" value="&lt;b&gt;FRONT&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#76608a;strokeColor=#432D57;fontColor=#ffffff;" vertex="1" parent="1">
<mxGeometry x="325" y="180" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-5" value="&lt;b&gt;BACK&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#76608a;fontColor=#ffffff;strokeColor=#432D57;" vertex="1" parent="1">
<mxGeometry x="525" y="180" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-7" value="&lt;b&gt;CACHE&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#76608a;fontColor=#ffffff;strokeColor=#432D57;" vertex="1" parent="1">
<mxGeometry x="725" y="180" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-8" value="" style="endArrow=none;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="955" y="930" as="sourcePoint" />
<mxPoint x="955" y="210" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-9" value="&lt;b&gt;COMMIT REPOSITORY&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#76608a;fontColor=#ffffff;strokeColor=#432D57;" vertex="1" parent="1">
<mxGeometry x="880" y="180" width="155" height="30" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-32" value="" style="endArrow=none;html=1;rounded=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;align=center;" edge="1" parent="1" target="_dnhcZFeje3u91oS2JwL-7">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="755" y="930" as="sourcePoint" />
<mxPoint x="756.5000000000002" y="300" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-52" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="555" y="300" as="sourcePoint" />
<mxPoint x="755" y="300" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-53" value="&lt;span style=&quot;font-size: 12px; text-wrap-mode: wrap;&quot;&gt;GetOrCreate(userId, message)&lt;/span&gt;" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-52">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-67" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="755" y="310" as="sourcePoint" />
<mxPoint x="955" y="310.83" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-68" value="CreateCommit(userId, message)" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-67">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-69" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="955" y="330" as="sourcePoint" />
<mxPoint x="755" y="330" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-70" value="CommitId" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-69">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-88" value="" style="endArrow=none;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="1155" y="930" as="sourcePoint" />
<mxPoint x="1155" y="210" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-89" value="&lt;b&gt;CHANGE LOG REPOSITORY&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#76608a;fontColor=#ffffff;strokeColor=#432D57;" vertex="1" parent="1">
<mxGeometry x="1060" y="180" width="185" height="30" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-90" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="755" y="340" as="sourcePoint" />
<mxPoint x="555" y="340" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-91" value="CommitId" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-90">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-93" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="555" y="380" as="sourcePoint" />
<mxPoint x="1155" y="380" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-94" value="AddRange(items, commitId)" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-93">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-96" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1155" y="410" as="sourcePoint" />
<mxPoint x="555" y="410" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-97" value="CREATED" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-96">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-100" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="555" y="420" as="sourcePoint" />
<mxPoint x="355" y="420" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-101" value="CREATED" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-100">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-102" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=9;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="555" y="420" as="sourcePoint" />
<mxPoint x="555" y="290" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-104" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="355" y="290" as="sourcePoint" />
<mxPoint x="555" y="290" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-105" value="Insert(items, message) [POST]" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-104">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-107" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=9;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="354.13" y="420" as="sourcePoint" />
<mxPoint x="354.13" y="290" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-108" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="355" y="520" as="sourcePoint" />
<mxPoint x="555" y="520" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-109" value="Update(items, message) [PUT]" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-108">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-110" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="555" y="530" as="sourcePoint" />
<mxPoint x="755" y="530" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-111" value="&lt;span style=&quot;font-size: 12px; text-wrap-mode: wrap;&quot;&gt;GetOrCreate(userId, message)&lt;/span&gt;" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-110">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-112" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="755" y="560" as="sourcePoint" />
<mxPoint x="555" y="560" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-113" value="CommitId" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-112">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-114" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=9;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="554.5699999999999" y="650" as="sourcePoint" />
<mxPoint x="554.5699999999999" y="520" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-115" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="555" y="610" as="sourcePoint" />
<mxPoint x="1155" y="610" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-116" value="UpdateRange(items, commitId)" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-115">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-117" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1155" y="640" as="sourcePoint" />
<mxPoint x="555" y="640" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-118" value="OK" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-117">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-119" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="555" y="650" as="sourcePoint" />
<mxPoint x="355" y="650" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-120" value="OK" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-119">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-121" value="" style="edgeStyle=elbowEdgeStyle;elbow=horizontal;endArrow=classic;html=1;curved=0;rounded=0;endSize=8;startSize=8;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="755" y="530" as="sourcePoint" />
<mxPoint x="755" y="560" as="targetPoint" />
<Array as="points">
<mxPoint x="775" y="550" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-122" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="355" y="760" as="sourcePoint" />
<mxPoint x="555" y="760" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-123" value="Delete(items, message) [DELETE]" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-122">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-124" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="555" y="770" as="sourcePoint" />
<mxPoint x="755" y="770" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-125" value="&lt;span style=&quot;font-size: 12px; text-wrap-mode: wrap;&quot;&gt;GetOrCreate(userId, message)&lt;/span&gt;" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-124">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-126" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="755" y="800" as="sourcePoint" />
<mxPoint x="555" y="800" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-127" value="CommitId" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-126">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-128" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=9;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="554.5699999999999" y="890" as="sourcePoint" />
<mxPoint x="554.5699999999999" y="760" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-129" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="555" y="850" as="sourcePoint" />
<mxPoint x="1155" y="850" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-130" value="UpdateRange(items, commitId)" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-129">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-131" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1155" y="880" as="sourcePoint" />
<mxPoint x="555" y="880" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-132" value="OK" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-131">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-133" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="555" y="890" as="sourcePoint" />
<mxPoint x="355" y="890" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-134" value="OK" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-133">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-135" value="" style="edgeStyle=elbowEdgeStyle;elbow=horizontal;endArrow=classic;html=1;curved=0;rounded=0;endSize=8;startSize=8;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="755" y="770" as="sourcePoint" />
<mxPoint x="755" y="800" as="targetPoint" />
<Array as="points">
<mxPoint x="775" y="790" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-136" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=9;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="354.57" y="650" as="sourcePoint" />
<mxPoint x="354.57" y="520" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-137" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=9;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="354.57" y="890" as="sourcePoint" />
<mxPoint x="354.57" y="760" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-138" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=10;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="1155" y="411" as="sourcePoint" />
<mxPoint x="1155" y="381" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-139" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=10;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="1155" y="641" as="sourcePoint" />
<mxPoint x="1155" y="611" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-140" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=10;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="1154.75" y="881" as="sourcePoint" />
<mxPoint x="1154.75" y="851" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-141" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=10;curved=1;targetPerimeterSpacing=3;fillColor=#dae8fc;gradientColor=#7ea6e0;strokeColor=#6c8ebf;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="755" y="830" as="sourcePoint" />
<mxPoint x="754.93" y="312" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-142" value="" style="endArrow=none;html=1;rounded=0;fillColor=#dae8fc;strokeColor=#6c8ebf;strokeWidth=10;opacity=50;gradientColor=#7ea6e0;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="165" y="882" as="sourcePoint" />
<mxPoint x="165.22" y="292" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-143" value="&lt;font color=&quot;#330066&quot;&gt;User&lt;/font&gt;" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;fillColor=#76608a;strokeColor=#432D57;fontColor=#ffffff;align=center;" vertex="1" parent="1">
<mxGeometry x="155" y="180" width="21.5" height="43" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-146" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="170" y="280" as="sourcePoint" />
<mxPoint x="360" y="280" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-147" value="SAVE" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-146">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-148" value="" style="endArrow=none;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="165.22" y="930" as="sourcePoint" />
<mxPoint x="165.22" y="210" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-149" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="355" y="892" as="sourcePoint" />
<mxPoint x="165" y="892" as="targetPoint" />
<Array as="points">
<mxPoint x="265" y="892" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-150" value="OK" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-149">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-152" value="&lt;h1 style=&quot;margin-top: 0px;&quot;&gt;UML-диаграмма процесса пакетного редактирования&lt;/h1&gt;&lt;p&gt;.&lt;/p&gt;" style="text;html=1;whiteSpace=wrap;overflow=hidden;rounded=0;align=center;" vertex="1" parent="1">
<mxGeometry x="75" y="50" width="1150" height="70" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-154" value="frame" style="shape=umlFrame;whiteSpace=wrap;html=1;pointerEvents=0;" vertex="1" parent="1">
<mxGeometry x="120" y="250" width="1120" height="680" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-156" value="&lt;p style=&quot;line-height: 120%;&quot;&gt;Время жизни кеша задается внутри проекта&lt;/p&gt;" style="shape=note;size=20;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#d0cee2;strokeColor=#56517e;opacity=50;" vertex="1" parent="1">
<mxGeometry x="760" y="660" width="120" height="100" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@ -48,7 +48,4 @@ Password: 12345
}
```
## Пакетное редактирование (на примере ChangeLog)
UML-диаграмма процесса редактирования находится по [ссылке](https://git.ddrilling.ru/on.nemtina/persistence/src/branch/master/DD.Persistence.API/Docs/ChangeLog_actions.drawio.xml)

View File

@ -28,6 +28,7 @@ COPY ["DD.Persistence/DD.Persistence.csproj", "DD.Persistence/"]
COPY ["DD.Persistence.Database/DD.Persistence.Database.csproj", "DD.Persistence.Database/"]
COPY ["DD.Persistence.Database.Postgres/DD.Persistence.Database.Postgres.csproj", "DD.Persistence.Database.Postgres/"]
COPY ["DD.Persistence.Models/DD.Persistence.Models.csproj", "DD.Persistence.Models/"]
COPY ["DD.Persistence.Repository/DD.Persistence.Repository.csproj", "DD.Persistence.Repository/"]
RUN dotnet restore "./DD.Persistence.App/DD.Persistence.App.csproj"

View File

@ -1,10 +1,10 @@
{
"DbConnection": {
"Host": "postgres",
"Host": "localhost",
"Port": 5432,
"Database": "persistence",
"Username": "postgres",
"Password": "postgres"
"Password": "q"
},
"NeedUseKeyCloak": false,
"AuthUser": {

View File

@ -6,7 +6,7 @@
}
},
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Database=persistence;Username=postgres;Password=postgres;Persist Security Info=True"
"DefaultConnection": "Host=localhost:5462;Database=persistence;Username=postgres;Password=postgres;Persist Security Info=True"
},
"AllowedHosts": "*",
"NeedUseKeyCloak": false,

View File

@ -24,14 +24,12 @@ public interface ITimestampedValuesClient : IDisposable
/// </summary>
/// <param name="discriminatorIds">Набор дискриминаторов (идентификаторов)</param>
/// <param name="timestampBegin">Фильтр позднее даты</param>
/// <param name="filterTree">Кастомный фильтр по набору значений</param>
/// <param name="columnNames">Фильтр свойств набора</param>
/// <param name="skip"></param>
/// <param name="take"></param>
/// <param name="token"></param>
Task<IEnumerable<TimestampedValuesDto>> Get(IEnumerable<Guid> discriminatorIds,
DateTimeOffset? timestampBegin,
string? filterTree,
IEnumerable<string>? columnNames,
int skip,
int take,
@ -42,12 +40,11 @@ public interface ITimestampedValuesClient : IDisposable
/// </summary>
/// <param name="discriminatorId"></param>
/// <param name="geTimestamp"></param>
/// <param name="filterTree"></param>
/// <param name="columnNames">Фильтр свойств набора</param>
/// <param name="skip"></param>
/// <param name="take"></param>
/// <param name="token"></param>
Task<IEnumerable<T>> Get<T>(Guid discriminatorId, DateTimeOffset? geTimestamp, string? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
Task<IEnumerable<T>> Get<T>(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
/// <summary>
/// Получить данные, начиная с заданной отметки времени

View File

@ -23,7 +23,6 @@ public interface IRefitTimestampedValuesClient : IRefitClient, IDisposable
[Get($"{baseUrl}")]
Task<IApiResponse<IEnumerable<TimestampedValuesDto>>> Get([Query(CollectionFormat.Multi)] IEnumerable<Guid> discriminatorIds,
DateTimeOffset? timestampBegin,
[Query] string? filterTree,
[Query(CollectionFormat.Multi)] IEnumerable<string>? columnNames,
int skip,
int take,

View File

@ -32,17 +32,17 @@ public class TimestampedValuesClient : BaseClient, ITimestampedValuesClient
}
/// <inheritdoc/>
public async Task<IEnumerable<TimestampedValuesDto>> Get(IEnumerable<Guid> discriminatorIds, DateTimeOffset? geTimestamp, string? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
public async Task<IEnumerable<TimestampedValuesDto>> Get(IEnumerable<Guid> discriminatorIds, DateTimeOffset? geTimestamp, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
{
var result = await ExecuteGetResponse(
async () => await refitTimestampedSetClient.Get(discriminatorIds, geTimestamp, filterTree, columnNames, skip, take, token), token);
async () => await refitTimestampedSetClient.Get(discriminatorIds, geTimestamp, columnNames, skip, take, token), token);
return result!;
}
/// <inheritdoc/>
public async Task<IEnumerable<T>> Get<T>(Guid discriminatorId, DateTimeOffset? geTimestamp, string? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
public async Task<IEnumerable<T>> Get<T>(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
{
var data = await Get([discriminatorId], geTimestamp, filterTree, columnNames, skip, take, token);
var data = await Get([discriminatorId], geTimestamp, columnNames, skip, take, token);
var mapper = GetMapper<T>(discriminatorId);
return data.Select(mapper.DeserializeTimeStampedData);

View File

@ -13,7 +13,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace DD.Persistence.Database.Postgres.Migrations
{
[DbContext(typeof(PersistencePostgresContext))]
[Migration("20250210055116_Init")]
[Migration("20250205114037_Init")]
partial class Init
{
/// <inheritdoc />
@ -37,14 +37,14 @@ namespace DD.Persistence.Database.Postgres.Migrations
.HasColumnType("timestamp with time zone")
.HasComment("Дата создания записи");
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Дискриминатор таблицы");
b.Property<Guid>("IdAuthor")
.HasColumnType("uuid")
.HasComment("Автор изменения");
b.Property<Guid>("IdDiscriminator")
.HasColumnType("uuid")
.HasComment("Дискриминатор таблицы");
b.Property<Guid?>("IdEditor")
.HasColumnType("uuid")
.HasComment("Редактор");

View File

@ -17,7 +17,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false, comment: "Ключ записи"),
DiscriminatorId = 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: "Автор изменения"),
IdEditor = table.Column<Guid>(type: "uuid", nullable: true, comment: "Редактор"),
Creation = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата создания записи"),

View File

@ -0,0 +1,230 @@
// <auto-generated />
using System;
using System.Text.Json;
using DD.Persistence.Database.Model;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace DD.Persistence.Database.Postgres.Migrations
{
[DbContext(typeof(PersistencePostgresContext))]
[Migration("20250210104036_IdDiscriminatorUpdateToDiscriminatorId")]
partial class IdDiscriminatorUpdateToDiscriminatorId
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLog", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasComment("Ключ записи");
b.Property<DateTimeOffset>("Creation")
.HasColumnType("timestamp with time zone")
.HasComment("Дата создания записи");
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Дискриминатор таблицы");
b.Property<Guid>("IdAuthor")
.HasColumnType("uuid")
.HasComment("Автор изменения");
b.Property<Guid?>("IdEditor")
.HasColumnType("uuid")
.HasComment("Редактор");
b.Property<Guid?>("IdNext")
.HasColumnType("uuid")
.HasComment("Id заменяющей записи");
b.Property<DateTimeOffset?>("Obsolete")
.HasColumnType("timestamp with time zone")
.HasComment("Дата устаревания (например при удалении)");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("jsonb")
.HasComment("Значение");
b.HasKey("Id");
b.ToTable("change_log");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.DataSourceSystem", b =>
{
b.Property<Guid>("SystemId")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasComment("Id системы - источника данных");
b.Property<string>("Description")
.HasColumnType("text")
.HasComment("Описание системы - источника данных");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("varchar(256)")
.HasComment("Наименование системы - источника данных");
b.HasKey("SystemId");
b.ToTable("data_source_system");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.ParameterData", b =>
{
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Дискриминатор системы");
b.Property<int>("ParameterId")
.HasColumnType("integer")
.HasComment("Id параметра");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone")
.HasComment("Временная отметка");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("varchar(256)")
.HasComment("Значение параметра в виде строки");
b.HasKey("DiscriminatorId", "ParameterId", "Timestamp");
b.ToTable("parameter_data");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.SchemeProperty", b =>
{
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Идентификатор схемы данных");
b.Property<int>("Index")
.HasColumnType("integer")
.HasComment("Индекс поля");
b.Property<byte>("PropertyKind")
.HasColumnType("smallint")
.HasComment("Тип индексируемого поля");
b.Property<string>("PropertyName")
.IsRequired()
.HasColumnType("text")
.HasComment("Наименования индексируемого поля");
b.HasKey("DiscriminatorId", "Index");
b.ToTable("scheme_property");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.Setpoint", b =>
{
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Ключ");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone")
.HasComment("Дата создания уставки");
b.Property<Guid>("IdUser")
.HasColumnType("uuid")
.HasComment("Id автора последнего изменения");
b.Property<JsonElement>("Value")
.HasColumnType("jsonb")
.HasComment("Значение уставки");
b.HasKey("DiscriminatorId", "Timestamp");
b.ToTable("setpoint");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
{
b.Property<Guid>("EventId")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasComment("Id события");
b.Property<int>("CategoryId")
.HasColumnType("integer")
.HasComment("Id Категории важности");
b.Property<int>("EventState")
.HasColumnType("integer")
.HasComment("Статус события");
b.Property<Guid>("SystemId")
.HasColumnType("uuid")
.HasComment("Id системы, к которой относится сообщение");
b.Property<string>("Text")
.IsRequired()
.HasColumnType("varchar(512)")
.HasComment("Текст сообщения");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone")
.HasComment("Дата возникновения");
b.HasKey("EventId");
b.HasIndex("SystemId");
b.ToTable("tech_message");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedValues", b =>
{
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Дискриминатор системы");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone")
.HasComment("Временная отметка");
b.Property<string>("Values")
.IsRequired()
.HasColumnType("jsonb")
.HasComment("Данные");
b.HasKey("DiscriminatorId", "Timestamp");
b.ToTable("timestamped_values");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
{
b.HasOne("DD.Persistence.Database.Entity.DataSourceSystem", "System")
.WithMany()
.HasForeignKey("SystemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("System");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,38 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace DD.Persistence.Database.Postgres.Migrations
{
/// <inheritdoc />
public partial class IdDiscriminatorUpdateToDiscriminatorId : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "Key",
table: "setpoint",
newName: "DiscriminatorId");
migrationBuilder.RenameColumn(
name: "IdDiscriminator",
table: "change_log",
newName: "DiscriminatorId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "DiscriminatorId",
table: "setpoint",
newName: "Key");
migrationBuilder.RenameColumn(
name: "DiscriminatorId",
table: "change_log",
newName: "IdDiscriminator");
}
}
}

View File

@ -135,7 +135,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
modelBuilder.Entity("DD.Persistence.Database.Entity.Setpoint", b =>
{
b.Property<Guid>("Key")
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Ключ");
@ -151,7 +151,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
.HasColumnType("jsonb")
.HasComment("Значение уставки");
b.HasKey("Key", "Timestamp");
b.HasKey("DiscriminatorId", "Timestamp");
b.ToTable("setpoint");
});

View File

@ -42,8 +42,6 @@ public static class DependencyInjection
MapsterSetup();
//services.AddTransient(typeof(PersistenceRepository<TimestampedValues>));
services.AddTransient<ISetpointRepository, SetpointRepository>();
services.AddTransient<IChangeLogRepository, ChangeLogRepository>();
services.AddTransient<ITimestampedValuesRepository, TimestampedValuesRepository>();

View File

@ -11,7 +11,7 @@ namespace DD.Persistence.Database.Entity;
/// Часть записи, описывающая изменение
/// </summary>
[Table("change_log")]
public class ChangeLog : IDiscriminatorItem, IChangeLog
public class ChangeLog : IChangeLog, IDiscriminatorItem
{
[Key, Comment("Ключ записи")]
public Guid Id { get; set; }

View File

@ -7,7 +7,7 @@ namespace DD.Persistence.Database.Entity;
[Table("parameter_data")]
[PrimaryKey(nameof(DiscriminatorId), nameof(ParameterId), nameof(Timestamp))]
public class ParameterData : IDiscriminatorItem, ITimestampedItem
public class ParameterData : ITimestampedItem
{
[Required, Comment("Дискриминатор системы")]
public Guid DiscriminatorId { get; set; }

View File

@ -1,5 +1,4 @@
using DD.Persistence.Database.EntityAbstractions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json;
@ -8,7 +7,7 @@ namespace DD.Persistence.Database.Entity;
[Table("scheme_property")]
[PrimaryKey(nameof(DiscriminatorId), nameof(Index))]
public class SchemeProperty : IDiscriminatorItem
public class SchemeProperty
{
[Comment("Идентификатор схемы данных")]
public Guid DiscriminatorId { get; set; }

View File

@ -6,11 +6,11 @@ using System.Text.Json;
namespace DD.Persistence.Database.Entity;
[Table("setpoint")]
[PrimaryKey(nameof(Key), nameof(Timestamp))]
public class Setpoint : ITimestampedItem
[PrimaryKey(nameof(DiscriminatorId), nameof(Timestamp))]
public class Setpoint : ITimestampedItem, IDiscriminatorItem
{
[Comment("Ключ")]
public Guid Key { get; set; }
public Guid DiscriminatorId { get; set; }
[Column(TypeName = "jsonb"), Comment("Значение уставки")]
public required JsonElement Value { get; set; }

View File

@ -17,15 +17,15 @@ public class TechMessage : ITimestampedItem
[Comment("Дата возникновения")]
public DateTimeOffset Timestamp { get; set; }
[Column(TypeName = "varchar(512)"), Comment("Текст сообщения")]
public required string Text { get; set; }
[Column(TypeName = "varchar(512)"), Comment("Текст сообщения")]
public required string Text { get; set; }
[Required, Comment("Id системы, к которой относится сообщение")]
public required Guid SystemId { get; set; }
[Required, Comment("Id системы, к которой относится сообщение")]
public required Guid SystemId { get; set; }
[Required, ForeignKey(nameof(SystemId)), Comment("Система, к которой относится сообщение")]
public virtual required DataSourceSystem System { get; set; }
[Required, ForeignKey(nameof(SystemId)), Comment("Система, к которой относится сообщение")]
public virtual required DataSourceSystem System { get; set; }
[Comment("Статус события")]
public int EventState { get; set; }
}
[Comment("Статус события")]
public int EventState { get; set; }
}

View File

@ -7,7 +7,7 @@ namespace DD.Persistence.Database.Entity;
[Table("timestamped_values")]
[PrimaryKey(nameof(DiscriminatorId), nameof(Timestamp))]
public class TimestampedValues : IDiscriminatorItem, ITimestampedItem, IValuesItem
public class TimestampedValues : ITimestampedItem, IValuesItem
{
[Comment("Временная отметка"), Key]
public DateTimeOffset Timestamp { get; set; }

View File

@ -1,4 +1,10 @@
namespace DD.Persistence.Database.EntityAbstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DD.Persistence.Database.EntityAbstractions;
public interface IDiscriminatorItem
{
/// <summary>

View File

@ -1,8 +1,7 @@
using Ardalis.Specification;
using Ardalis.Specification.EntityFrameworkCore;
using DD.Persistence.Database.Entity;
using DD.Persistence.Database.EntityAbstractions;
using DD.Persistence.Database.Specifications.Operation;
using DD.Persistence.Database.Postgres.Extensions;
using DD.Persistence.Database.Specifications;
using DD.Persistence.Database.Specifications.ValuesItem;
using DD.Persistence.Filter.Models;
using DD.Persistence.Filter.Models.Abstractions;
@ -14,17 +13,8 @@ using System.Text.Json;
namespace DD.Persistence.Database.Postgres.Helpers;
public static class FilterBuilder
{
public static IQueryable<TEntity> ApplyFilter<TEntity>(this IQueryable<TEntity> query, DataSchemeDto dataSchemeDto, TNode root)
where TEntity : class, IValuesItem
{
var filterSpec = dataSchemeDto.BuildFilter<TEntity>(root);
if (filterSpec != null)
return query.WithSpecification(filterSpec);
return query;
}
private static ISpecification<TEntity>? BuildFilter<TEntity>(this DataSchemeDto dataSchemeDto, TNode root)
where TEntity : IValuesItem
public static ISpecification<TEntity>? BuildFilter<TEntity>(this DataSchemeDto dataSchemeDto, TNode root)
where TEntity : IValuesItem
{
var result = dataSchemeDto.BuildSpecificationByNextNode<TEntity>(root);
@ -58,10 +48,10 @@ public static class FilterBuilder
switch (vertex.Operation)
{
case OperationEnum.And:
result = new AndSpec<TEntity>(leftSpecification, rigthSpecification);
result = new AndSpecification<TEntity>(leftSpecification, rigthSpecification);
break;
case OperationEnum.Or:
result = new OrSpec<TEntity>(leftSpecification, rigthSpecification);
result = new OrSpecification<TEntity>(leftSpecification, rigthSpecification);
break;
}
@ -96,21 +86,21 @@ public static class FilterBuilder
private static Dictionary<OperationEnum, Func<int, string?, ISpecification<TEntity>>> StringSpecifications<TEntity>()
where TEntity : IValuesItem => new()
{
{ OperationEnum.Equal, (int index, string? value) => new ValueEqualSpec<TEntity>(index, value) },
{ OperationEnum.NotEqual, (int index, string? value) => new ValueNotEqualSpec<TEntity>(index, value) },
{ OperationEnum.Greate, (int index, string? value) => new ValueGreateSpec<TEntity>(index, value) },
{ OperationEnum.GreateOrEqual, (int index, string? value) => new ValueGreateOrEqualSpec<TEntity>(index, value) },
{ OperationEnum.Less, (int index, string? value) => new ValueLessSpec<TEntity>(index, value) },
{ OperationEnum.LessOrEqual, (int index, string? value) => new ValueLessOrEqualSpec<TEntity>(index, value) }
{ OperationEnum.Equal, (int index, string? value) => new ValueEqaulSpecification<TEntity>(index, value) },
{ OperationEnum.NotEqual, (int index, string? value) => new ValueNotEqualSpecification<TEntity>(index, value) },
{ OperationEnum.Greate, (int index, string? value) => new ValueGreateSpecification<TEntity>(index, value) },
{ OperationEnum.GreateOrEqual, (int index, string? value) => new ValueGreateOrEqualSpecification<TEntity>(index, value) },
{ OperationEnum.Less, (int index, string? value) => new ValueLessSpecification<TEntity>(index, value) },
{ OperationEnum.LessOrEqual, (int index, string? value) => new ValueLessOrEqualSpecification<TEntity>(index, value) }
};
private static Dictionary<OperationEnum, Func<int, double?, ISpecification<TEntity>>> DoubleSpecifications<TEntity>()
where TEntity : IValuesItem => new()
{
{ OperationEnum.Equal, (int index, double? value) => new ValueEqualSpec<TEntity>(index, value) },
{ OperationEnum.NotEqual, (int index, double? value) => new ValueNotEqualSpec<TEntity>(index, value) },
{ OperationEnum.Greate, (int index, double? value) => new ValueGreateSpec<TEntity>(index, value) },
{ OperationEnum.GreateOrEqual, (int index, double? value) => new ValueGreateOrEqualSpec<TEntity>(index, value) },
{ OperationEnum.Less, (int index, double? value) => new ValueLessSpec<TEntity>(index, value) },
{ OperationEnum.LessOrEqual, (int index, double? value) => new ValueLessOrEqualSpec<TEntity>(index, value) }
{ OperationEnum.Equal, (int index, double? value) => new ValueEqaulSpecification<TEntity>(index, value) },
{ OperationEnum.NotEqual, (int index, double? value) => new ValueNotEqualSpecification<TEntity>(index, value) },
{ OperationEnum.Greate, (int index, double? value) => new ValueGreateSpecification<TEntity>(index, value) },
{ OperationEnum.GreateOrEqual, (int index, double? value) => new ValueGreateOrEqualSpecification<TEntity>(index, value) },
{ OperationEnum.Less, (int index, double? value) => new ValueLessSpecification<TEntity>(index, value) },
{ OperationEnum.LessOrEqual, (int index, double? value) => new ValueLessOrEqualSpecification<TEntity>(index, value) }
};
}

View File

@ -1,5 +1,9 @@
using DD.Persistence.Database.Entity;
using Ardalis.Specification;
using Ardalis.Specification.EntityFrameworkCore;
using DD.Persistence.Database.Entity;
using DD.Persistence.Database.Postgres.Helpers;
using DD.Persistence.Database.Specifications.ChangeLog;
using DD.Persistence.Database.Specifications.Common.DiscriminatorItem;
using DD.Persistence.Models;
using DD.Persistence.Models.Common;
using DD.Persistence.Models.Requests;
@ -12,10 +16,13 @@ namespace DD.Persistence.Database.Repositories;
public class ChangeLogRepository : IChangeLogRepository
{
private readonly DbContext db;
private readonly ObsoleteIsNullSpec<ChangeLog> obsoleteIsNullSpecification;
public ChangeLogRepository(DbContext db)
{
this.db = db;
this.obsoleteIsNullSpecification = new ObsoleteIsNullSpec<ChangeLog>();
}
public async Task<int> AddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
@ -35,9 +42,11 @@ public class ChangeLogRepository : IChangeLogRepository
public async Task<int> MarkAsDeleted(Guid idEditor, IEnumerable<Guid> ids, CancellationToken token)
{
var containsIdsSpecification = new IdContainsSpec<ChangeLog>(ids);
var query = db.Set<ChangeLog>()
.Where(s => ids.Contains(s.Id))
.Where(s => s.Obsolete == null);
.WithSpecification(containsIdsSpecification)
.WithSpecification(obsoleteIsNullSpecification);
if (query.Count() != ids.Count())
{
@ -53,9 +62,11 @@ public class ChangeLogRepository : IChangeLogRepository
public async Task<int> MarkAsDeleted(Guid idEditor, Guid idDiscriminator, CancellationToken token)
{
var specDiscriminatorEqual = new DiscriminatorEqualSpec<ChangeLog>(idDiscriminator);
var query = db.Set<ChangeLog>()
.Where(s => s.DiscriminatorId == idDiscriminator)
.Where(e => e.Obsolete == null);
.WithSpecification(specDiscriminatorEqual)
.WithSpecification(obsoleteIsNullSpecification);
var entities = await query.ToArrayAsync(token);
@ -97,8 +108,10 @@ public class ChangeLogRepository : IChangeLogRepository
var dbSet = db.Set<ChangeLog>();
var updatedIds = dtos.Select(d => d.Id);
var containsIdsSpecification = new IdContainsSpec<ChangeLog>(updatedIds);
var updatedEntities = dbSet
.Where(s => updatedIds.Contains(s.Id))
.WithSpecification(containsIdsSpecification)
.ToDictionary(s => s.Id);
var result = 0;
@ -134,7 +147,11 @@ public class ChangeLogRepository : IChangeLogRepository
PaginationRequest paginationRequest,
CancellationToken token)
{
var query = CreateQuery(idDiscriminator);
var specDiscriminatorEqual = new DiscriminatorEqualSpec<ChangeLog>(idDiscriminator);
var query = db.Set<ChangeLog>()
.WithSpecification(specDiscriminatorEqual);
query = query.Apply(momentUtc);
var result = await query.ApplyPagination(paginationRequest, Convert, token);
@ -142,22 +159,21 @@ public class ChangeLogRepository : IChangeLogRepository
return result;
}
private IQueryable<ChangeLog> CreateQuery(Guid idDiscriminator)
{
var query = db.Set<ChangeLog>().Where(e => e.DiscriminatorId == idDiscriminator);
return query;
}
public async Task<IEnumerable<ChangeLogDto>> GetChangeLogForInterval(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token)
{
var query = db.Set<ChangeLog>().Where(s => s.DiscriminatorId == idDiscriminator);
var specDiscriminatorEqual = new DiscriminatorEqualSpec<ChangeLog>(idDiscriminator);
var query = db.Set<ChangeLog>()
.WithSpecification(specDiscriminatorEqual);
var min = new DateTimeOffset(dateBegin.ToUniversalTime().Date, TimeSpan.Zero);
var max = new DateTimeOffset(dateEnd.ToUniversalTime().Date, TimeSpan.Zero);
var createdQuery = query.Where(e => e.Creation >= min && e.Creation <= max);
var editedQuery = query.Where(e => e.Obsolete != null && e.Obsolete >= min && e.Obsolete <= max);
var specCreatedByDateRange = new FromCreationDateRangeSpec<ChangeLog>(min, max);
var specHistoryByDateRange = new FromObsoleteDateRangeSpece<ChangeLog>(min, max);
var createdQuery = query.WithSpecification(specCreatedByDateRange);
var editedQuery = query.WithSpecification(specHistoryByDateRange);
query = createdQuery.Union(editedQuery);
var entities = await query.ToArrayAsync(token);
@ -171,7 +187,11 @@ public class ChangeLogRepository : IChangeLogRepository
public async Task<IEnumerable<DateOnly>> GetDatesChange(Guid idDiscriminator, CancellationToken token)
{
var query = db.Set<ChangeLog>().Where(e => e.DiscriminatorId == idDiscriminator);
var specDiscriminatorEqual = new DiscriminatorEqualSpec<ChangeLog>(idDiscriminator);
var specObsoleteNotNull = new ObsoleteNotNullSpec<ChangeLog>();
var query = db.Set<ChangeLog>()
.WithSpecification(specDiscriminatorEqual);
var datesCreateQuery = query
.Select(e => e.Creation)
@ -180,7 +200,7 @@ public class ChangeLogRepository : IChangeLogRepository
var datesCreate = await datesCreateQuery.ToArrayAsync(token);
var datesUpdateQuery = query
.Where(e => e.Obsolete != null)
.WithSpecification(specObsoleteNotNull)
.Select(e => e.Obsolete!.Value)
.Distinct();
@ -214,9 +234,13 @@ public class ChangeLogRepository : IChangeLogRepository
public async Task<IEnumerable<ChangeLogValuesDto>> GetGtDate(Guid idDiscriminator, DateTimeOffset dateBegin, CancellationToken token)
{
var date = dateBegin.ToUniversalTime();
var specDiscriminatorEqual = new DiscriminatorEqualSpec<ChangeLog>(idDiscriminator);
var specCreatedOrUpdatedGeDate = new CreatedOrUpdatedGeDateSpec<ChangeLog>(date);
var query = db.Set<ChangeLog>()
.Where(e => e.DiscriminatorId == idDiscriminator)
.Where(e => e.Creation >= date || e.Obsolete >= date);
.WithSpecification(specDiscriminatorEqual)
.WithSpecification(specCreatedOrUpdatedGeDate);
var entities = await query.ToArrayAsync(token);
@ -227,29 +251,34 @@ public class ChangeLogRepository : IChangeLogRepository
public async Task<DatesRangeDto?> GetDatesRange(Guid idDiscriminator, CancellationToken token)
{
var query = db.Set<ChangeLog>()
.Where(e => e.DiscriminatorId == idDiscriminator)
.GroupBy(e => 1)
.Select(group => new
{
Min = group.Min(e => e.Creation),
Max = group.Max(e => e.Obsolete.HasValue && e.Obsolete > e.Creation
? e.Obsolete.Value
: e.Creation),
});
return null;
//var specDiscriminatorEqual = new DiscriminatorEqualSpec<ChangeLog>(idDiscriminator);
//var evaluator =
// SpecificationEvaluator.Default
//var query = db.Set<ChangeLog>()
// //.Where(e => e.IdDiscriminator == idDiscriminator)
// .GroupBy(e => 1)
// .WithSpecification(specDiscriminatorEqual)
// .Select(group => new
// {
// Min = group.Min(e => e.Creation),
// Max = group.Max(e => e.Obsolete.HasValue && e.Obsolete > e.Creation
// ? e.Obsolete.Value
// : e.Creation),
// });
var values = await query.FirstOrDefaultAsync(token);
//var values = await query.FirstOrDefaultAsync(token);
if (values is null)
{
return null;
}
//if (values is null)
//{
// return null;
//}
return new DatesRangeDto
{
From = values.Min,
To = values.Max,
};
//return new DatesRangeDto
//{
// From = values.Min,
// To = values.Max,
//};
}
private ChangeLogValuesDto Convert(ChangeLog entity) => entity.Adapt<ChangeLogValuesDto>();

View File

@ -12,7 +12,7 @@ public class DataSourceSystemRepository : IDataSourceSystemRepository
{
this.db = db;
}
protected IQueryable<DataSourceSystem> GetQueryReadOnly() => db.Set<DataSourceSystem>();
protected virtual IQueryable<DataSourceSystem> GetQueryReadOnly() => db.Set<DataSourceSystem>();
public virtual async Task Add(DataSourceSystemDto dataSourceSystemDto, CancellationToken token)
{

View File

@ -0,0 +1,62 @@
using Ardalis.Specification;
using Ardalis.Specification.EntityFrameworkCore;
using DD.Persistence.Database.Entity;
using DD.Persistence.Database.EntityAbstractions;
using DD.Persistence.Models.Common;
namespace DD.Persistence.Database.Postgres.Repositories;
public class MyPartialEvaluator : IEvaluator
{
private MyPartialEvaluator() { }
public static MyPartialEvaluator Instance { get; } = new MyPartialEvaluator();
public bool IsCriteriaEvaluator { get; } = true;
public IQueryable<T> GetQuery<T>(IQueryable<T> query, ISpecification<T> specification) where T : class
{
// Проверяем, есть ли свойство Timestamp в типе T
var timestampProperty = typeof(T).GetProperty("Timestamp");
if (timestampProperty != null && timestampProperty.PropertyType == typeof(DateTimeOffset))
{
// Если свойство существует и имеет тип DateTime, выполняем группировку и выборку
var t = query
.GroupBy(x => 1)
.Select(group => new
{
Min = group.Min(e => (DateTimeOffset)timestampProperty.GetValue(e)),
Max = group.Max(e => (DateTimeOffset)timestampProperty.GetValue(e)),
})
.AsQueryable() as IQueryable<T>;
return t;
}
return query;
}
}
public class Test
{
public Test()
{
}
public DateTimeOffset Min { get; set; }
public DateTimeOffset Max { get; set; }
}
public class MySpecificationEvaluator : SpecificationEvaluator
{
public static MySpecificationEvaluator GroupBy { get; } = new MySpecificationEvaluator();
private MySpecificationEvaluator() : base()
{
Evaluators.Add(MyPartialEvaluator.Instance);
}
}

View File

@ -15,7 +15,7 @@ public class ParameterRepository : IParameterRepository
this.db = db;
}
protected IQueryable<ParameterData> GetQueryReadOnly() => db.Set<ParameterData>();
protected virtual IQueryable<ParameterData> GetQueryReadOnly() => db.Set<ParameterData>();
public async Task<DatesRangeDto> GetDatesRangeAsync(Guid idDiscriminator, CancellationToken token)
{

View File

@ -12,7 +12,7 @@ public class SchemePropertyRepository : ISchemePropertyRepository
{
this.db = db;
}
protected IQueryable<SchemeProperty> GetQueryReadOnly() => db.Set<SchemeProperty>();
protected virtual IQueryable<SchemeProperty> GetQueryReadOnly() => db.Set<SchemeProperty>();
public virtual async Task AddRange(DataSchemeDto dataSchemeDto, CancellationToken token)
{

View File

@ -1,4 +1,8 @@
using Ardalis.Specification;
using Ardalis.Specification.EntityFrameworkCore;
using DD.Persistence.Database.Entity;
using DD.Persistence.Database.Specifications.Common;
using DD.Persistence.Database.Specifications.Common.DiscriminatorItem;
using DD.Persistence.Models;
using DD.Persistence.Models.Common;
using DD.Persistence.Repositories;
@ -16,7 +20,7 @@ namespace DD.Persistence.Database.Postgres.Repositories
this.db = db;
}
protected IQueryable<Setpoint> GetQueryReadOnly() => db.Set<Setpoint>();
protected virtual IQueryable<Setpoint> GetQueryReadOnly() => db.Set<Setpoint>();
public async Task<IEnumerable<SetpointValueDto>> GetCurrent(
IEnumerable<Guid> setpointKeys,
@ -24,9 +28,11 @@ namespace DD.Persistence.Database.Postgres.Repositories
{
var query = GetQueryReadOnly();
var entities = await query
.Where(e => setpointKeys.Contains(e.Key))
.GroupBy(e => e.Key)
var specDiscriminatorContains = new DiscriminatorContainsSpec<Setpoint>(setpointKeys);
var entities = await db.Set<Setpoint>()
.WithSpecification(specDiscriminatorContains)
.GroupBy(e => e.DiscriminatorId)
.Select(g => g.OrderByDescending(x => x.Timestamp).FirstOrDefault())
.ToArrayAsync(token);
@ -37,23 +43,27 @@ namespace DD.Persistence.Database.Postgres.Repositories
{
var query = GetQueryReadOnly();
var entities = await query
.Where(e => setpointKeys.Contains(e.Key))
.GroupBy(e => e.Key)
var specDiscriminatorContains = new DiscriminatorContainsSpec<Setpoint>(setpointKeys);
var entities = await db.Set<Setpoint>()
.WithSpecification(specDiscriminatorContains)
.GroupBy(e => e.DiscriminatorId)
.Select(g => g.OrderByDescending(x => x.Timestamp).FirstOrDefault())
.ToDictionaryAsync(x=> x.Key, x => (object)x.Value, token);
.ToDictionaryAsync(x=> x.DiscriminatorId, x => (object)x.Value, token);
return entities;
}
public async Task<IEnumerable<SetpointValueDto>> GetHistory(IEnumerable<Guid> setpointKeys, DateTimeOffset historyMoment, CancellationToken token)
{
var query = GetQueryReadOnly();
var entities = await query
.Where(e => setpointKeys.Contains(e.Key))
var specDiscriminatorContains = new DiscriminatorContainsSpec<Setpoint>(setpointKeys);
var entities = await db.Set<Setpoint>()
.WithSpecification(specDiscriminatorContains)
.ToArrayAsync(token);
var filteredEntities = entities
.GroupBy(e => e.Key)
.GroupBy(e => e.DiscriminatorId)
.Select(e => e.OrderBy(o => o.Timestamp))
.Select(e => e.Where(e => e.Timestamp <= historyMoment).Last());
var dtos = filteredEntities
@ -77,31 +87,35 @@ namespace DD.Persistence.Database.Postgres.Repositories
public async Task<DatesRangeDto> GetDatesRangeAsync(CancellationToken token)
{
var query = GetQueryReadOnly()
var spec = new DatesRangeSpec<Setpoint>();
var query = db.Set<Setpoint>()
.WithSpecification(spec, MySpecificationEvaluator.GroupBy);
var query2 = GetQueryReadOnly()
.GroupBy(e => 1)
.Select(group => new
{
Min = group.Min(e => e.Timestamp),
Max = group.Max(e => e.Timestamp),
});
var values = await query.FirstOrDefaultAsync(token);
var result = new DatesRangeDto()
{
From = values?.Min ?? DateTimeOffset.MinValue,
To = values?.Max ?? DateTimeOffset.MaxValue
};
return result;
var values = await query.FirstOrDefaultAsync(token);
return null;
}
public async Task<Dictionary<Guid, IEnumerable<SetpointLogDto>>> GetLog(IEnumerable<Guid> setpointKeys, CancellationToken token)
{
var query = GetQueryReadOnly();
var entities = await query
.Where(e => setpointKeys.Contains(e.Key))
var specDiscriminatorContains = new DiscriminatorContainsSpec<Setpoint>(setpointKeys);
var entities = await db.Set<Setpoint>()
.WithSpecification(specDiscriminatorContains)
.ToArrayAsync(token);
var dtos = entities
.GroupBy(e => e.Key)
.GroupBy(e => e.DiscriminatorId)
.ToDictionary(e => e.Key, v => v.Select(z => z.Adapt<SetpointLogDto>()));
return dtos;
@ -111,7 +125,7 @@ namespace DD.Persistence.Database.Postgres.Repositories
{
var entity = new Setpoint()
{
Key = setpointKey,
DiscriminatorId = setpointKey,
Value = newValue,
IdUser = idUser,
Timestamp = DateTimeOffset.UtcNow.ToUniversalTime()

View File

@ -20,7 +20,7 @@ namespace DD.Persistence.Database.Postgres.Repositories
this.sourceSystemRepository = sourceSystemRepository;
}
protected IQueryable<TechMessage> GetQueryReadOnly() => db.Set<TechMessage>()
protected virtual IQueryable<TechMessage> GetQueryReadOnly() => db.Set<TechMessage>()
.Include(e => e.System);
public async Task<PaginationContainer<TechMessageDto>> GetPage(PaginationRequest request, CancellationToken token)

View File

@ -1,6 +1,4 @@
using DD.Persistence.Database.Entity;
using DD.Persistence.Database.Postgres.Helpers;
using DD.Persistence.Filter.Models.Abstractions;
using DD.Persistence.Models;
using DD.Persistence.Models.Common;
using DD.Persistence.Repositories;
@ -10,16 +8,15 @@ namespace DD.Persistence.Database.Postgres.Repositories;
public class TimestampedValuesRepository : ITimestampedValuesRepository
{
private readonly DbContext db;
private readonly ISchemePropertyRepository schemePropertyRepository;
public TimestampedValuesRepository(DbContext db, ISchemePropertyRepository schemePropertyRepository)
public TimestampedValuesRepository(DbContext db)
{
this.db = db;
this.schemePropertyRepository = schemePropertyRepository;
}
protected IQueryable<TimestampedValues> GetQueryReadOnly() => db.Set<TimestampedValues>();
protected virtual IQueryable<TimestampedValues> GetQueryReadOnly() => this.db.Set<TimestampedValues>();
public async Task<int> AddRange(Guid discriminatorId, IEnumerable<TimestampedValuesDto> dtos, CancellationToken token)
public async virtual Task<int> AddRange(Guid discriminatorId, IEnumerable<TimestampedValuesDto> dtos, CancellationToken token)
{
var timestampedValuesEntities = dtos.Select(dto => new TimestampedValues()
{
@ -27,47 +24,36 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
Timestamp = dto.Timestamp.ToUniversalTime(),
Values = dto.Values.Values.ToArray()
});
await db.AddRangeAsync(timestampedValuesEntities, token);
await db.Set<TimestampedValues>().AddRangeAsync(timestampedValuesEntities, token);
var result = await db.SaveChangesAsync(token);
return result;
}
public async Task<IDictionary<Guid, IEnumerable<(DateTimeOffset Timestamp, object[] Values)>>> Get(IEnumerable<Guid> discriminatorIds,
DateTimeOffset? geTimestamp,
TNode? filterTree,
public async virtual Task<IDictionary<Guid, IEnumerable<(DateTimeOffset Timestamp, object[] Values)>>> Get(IEnumerable<Guid> discriminatorIds,
DateTimeOffset? timestampBegin,
IEnumerable<string>? columnNames,
int skip,
int take,
CancellationToken token)
{
var resultQuery = Array.Empty<TimestampedValues>().AsQueryable();
foreach (var discriminatorId in discriminatorIds)
var query = GetQueryReadOnly()
.Where(entity => discriminatorIds.Contains(entity.DiscriminatorId));
// Фильтрация по дате
if (timestampBegin.HasValue)
{
var scheme = await schemePropertyRepository.Get(discriminatorId, token);
if (scheme == null)
throw new NotSupportedException($"Для переданного дискриминатора {discriminatorId} не была обнаружена схема данных");
var geTimestampUtc = geTimestamp!.Value.ToUniversalTime();
var query = GetQueryReadOnly()
.Where(e => e.DiscriminatorId == discriminatorId)
.Where(entity => entity.Timestamp >= geTimestampUtc);
if (filterTree != null)
query = query.ApplyFilter(scheme, filterTree);
resultQuery = resultQuery.Any() ? resultQuery.Union(query) : query;
query = ApplyGeTimestamp(query, timestampBegin.Value);
}
var groupedQuery = resultQuery!
// Группировка отсортированных значений по DiscriminatorId
var groupQuery = query
.GroupBy(e => e.DiscriminatorId)
.Select(g => KeyValuePair.Create(
g.Key,
g.OrderBy(i => i.Timestamp).Skip(skip).Take(take))
);
var entities = await groupedQuery.ToArrayAsync(token);
.Select(g => KeyValuePair.Create(g.Key, g.OrderBy(i => i.Timestamp).Skip(skip).Take(take)));
var entities = await groupQuery.ToArrayAsync(token);
var result = entities.ToDictionary(k => k.Key, v => v.Value.Select(e => (
e.Timestamp,
e.Values
@ -76,7 +62,7 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
return result;
}
public async Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token)
public async virtual Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token)
{
var query = GetQueryReadOnly()
.OrderBy(e => e.Timestamp)
@ -91,7 +77,7 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
return result;
}
public async Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetLast(Guid discriminatorId, int takeCount, CancellationToken token)
public async virtual Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetLast(Guid discriminatorId, int takeCount, CancellationToken token)
{
var query = GetQueryReadOnly()
.OrderByDescending(e => e.Timestamp)
@ -107,7 +93,7 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
}
// ToDo: прореживание должно осуществляться до материализации
public async Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetResampledData(
public async virtual Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetResampledData(
Guid discriminatorId,
DateTimeOffset dateBegin,
double intervalSec = 600d,
@ -128,11 +114,10 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
return result;
}
public async Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetGtDate(Guid discriminatorId, DateTimeOffset gtTimestamp, CancellationToken token)
public async virtual Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token)
{
var gtTimestampUtc = gtTimestamp.ToUniversalTime();
var query = GetQueryReadOnly()
.Where(entity => entity.Timestamp > gtTimestampUtc);
.Where(e => e.Timestamp > timestampBegin);
var entities = await query.ToArrayAsync(token);
var result = entities.Select(e => (
@ -143,7 +128,7 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
return result;
}
public async Task<DatesRangeDto?> GetDatesRange(Guid discriminatorId, CancellationToken token)
public async virtual Task<DatesRangeDto?> GetDatesRange(Guid discriminatorId, CancellationToken token)
{
var query = GetQueryReadOnly()
.GroupBy(entity => entity.DiscriminatorId)
@ -168,12 +153,26 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
return dto;
}
public async Task<int> Count(Guid discriminatorId, CancellationToken token)
public virtual Task<int> Count(Guid discriminatorId, CancellationToken token)
{
var query = GetQueryReadOnly()
.Where(e => e.DiscriminatorId == discriminatorId);
var dbSet = db.Set<TimestampedValues>();
var query = dbSet.Where(entity => entity.DiscriminatorId == discriminatorId);
var result = await query.CountAsync(token);
return query.CountAsync(token);
}
/// <summary>
/// Применить фильтр по дате
/// </summary>
/// <param name="query"></param>
/// <param name="timestampBegin"></param>
/// <returns></returns>
private IQueryable<TimestampedValues> ApplyGeTimestamp(IQueryable<TimestampedValues> query, DateTimeOffset timestampBegin)
{
var geTimestampUtc = timestampBegin.ToUniversalTime();
var result = query
.Where(entity => entity.Timestamp >= geTimestampUtc);
return result;
}

View File

@ -1,9 +1,9 @@
using Ardalis.Specification;
namespace DD.Persistence.Database.Specifications.Operation;
public class AndSpec<TEntity> : Specification<TEntity>
namespace DD.Persistence.Database.Specifications;
public class AndSpecification<TEntity> : Specification<TEntity>
{
public AndSpec(ISpecification<TEntity> first, ISpecification<TEntity> second)
public AndSpecification(ISpecification<TEntity> first, ISpecification<TEntity> second)
{
if (first is null || second is null)
return;

View File

@ -0,0 +1,17 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DD.Persistence.Database.Specifications.ChangeLog;
public class CreatedOrUpdatedGeDateSpec<TEntity> : Specification<TEntity>
where TEntity : IChangeLog
{
public CreatedOrUpdatedGeDateSpec(DateTimeOffset date)
{
Query.Where(e => e.Creation >= date || e.Obsolete >= date);
}
}

View File

@ -0,0 +1,25 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
namespace DD.Persistence.Database.Specifications.ChangeLog;
/// <summary>
/// Спецификация для поиска созданных за определённый период записей IChangeLog
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class FromCreationDateRangeSpec<TEntity> : Specification<TEntity>
where TEntity : IChangeLog
{
public FromCreationDateRangeSpec(DateTimeOffset? min, DateTimeOffset? max)
{
if (min.HasValue)
{
Query.Where(e => e.Creation >= min);
}
if (max.HasValue){
Query.Where(e => e.Creation <= max);
}
//Query.Where(e => e.Creation >= min && e.Creation <= max);
}
}

View File

@ -0,0 +1,18 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
namespace DD.Persistence.Database.Specifications.ChangeLog;
/// <summary>
/// Спецификация для поиска исторических записей IChangeLog за определённый период
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class FromObsoleteDateRangeSpece<TEntity> : Specification<TEntity>
where TEntity : IChangeLog
{
public FromObsoleteDateRangeSpece(DateTimeOffset min, DateTimeOffset max)
{
Query.Where(e => e.Obsolete != null && e.Obsolete >= min && e.Obsolete <= max);
}
}

View File

@ -0,0 +1,22 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
namespace DD.Persistence.Database.Specifications.ChangeLog;
/// <summary>
/// Спецификация для поиска записей IChangeLog по массиву ключей
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class IdContainsSpec<TEntity> : Specification<TEntity>
where TEntity : IChangeLog
{
public IdContainsSpec()
{
}
public IdContainsSpec(IEnumerable<Guid> ids)
{
Query.Where(s => ids.Contains(s.Id));
}
}

View File

@ -0,0 +1,18 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
namespace DD.Persistence.Database.Specifications.ChangeLog;
/// <summary>
/// Спецификация для актуальных значений IChangeLog
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ObsoleteIsNullSpec<TEntity> : Specification<TEntity>
where TEntity : IChangeLog
{
public ObsoleteIsNullSpec()
{
Query.Where(e => e.Obsolete == null);
}
}

View File

@ -0,0 +1,18 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
namespace DD.Persistence.Database.Specifications.ChangeLog;
/// <summary>
/// Спецификация для поиска не актуальных значений IChangeLog
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ObsoleteNotNullSpec<TEntity> : Specification<TEntity>
where TEntity : IChangeLog
{
public ObsoleteNotNullSpec()
{
Query.Where(e => e.Obsolete != null);
}
}

View File

@ -0,0 +1,19 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
namespace DD.Persistence.Database.Specifications.Common;
public class DatesRangeSpec<TEntity> : Specification<TEntity>
where TEntity : IDiscriminatorItem
{
public DatesRangeSpec()
{
//Query;
}
}

View File

@ -0,0 +1,18 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
namespace DD.Persistence.Database.Specifications.Common.DiscriminatorItem;
public class DiscriminatorContainsSpec<TEntity> : Specification<TEntity>
where TEntity : IDiscriminatorItem
{
public DiscriminatorContainsSpec(IEnumerable<Guid> discriminatorIds)
{
Query.Where(e => discriminatorIds.Contains(e.DiscriminatorId));
}
}

View File

@ -0,0 +1,18 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
namespace DD.Persistence.Database.Specifications.Common.DiscriminatorItem;
public class DiscriminatorEqualSpec<TEntity> : Specification<TEntity>
where TEntity : IDiscriminatorItem
{
public DiscriminatorEqualSpec(Guid discriminatorId)
{
Query.Where(e => e.DiscriminatorId == discriminatorId);
}
}

View File

@ -1,15 +0,0 @@
using Ardalis.Specification;
using DD.Persistence.Database.Postgres.Extensions;
namespace DD.Persistence.Database.Specifications.Operation;
public class OrSpec<TEntity> : Specification<TEntity>
{
public OrSpec(ISpecification<TEntity> first, ISpecification<TEntity> second)
{
var orExpression = first.Or(second);
if (orExpression == null)
return;
Query.Where(orExpression);
}
}

View File

@ -0,0 +1,15 @@
using Ardalis.Specification;
using DD.Persistence.Database.Postgres.Extensions;
namespace DD.Persistence.Database.Specifications;
public class OrSpecification<TEntity> : Specification<TEntity>
{
public OrSpecification(ISpecification<TEntity> first, ISpecification<TEntity> second)
{
var orExpression = first.Or(second);
if (orExpression == null)
return;
Query.Where(orExpression);
}
}

View File

@ -7,15 +7,15 @@ namespace DD.Persistence.Database.Specifications.ValuesItem;
/// Спецификация эквивалентности значений IValuesItem в соответствии с индексацией
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ValueEqualSpec<TEntity> : Specification<TEntity>
public class ValueEqaulSpecification<TEntity> : Specification<TEntity>
where TEntity : IValuesItem
{
public ValueEqualSpec(int index, string? value)
public ValueEqaulSpecification(int index, string? value)
{
Query.Where(e => Convert.ToString(e.Values[index]) == value);
}
public ValueEqualSpec(int index, double? value)
public ValueEqaulSpecification(int index, double? value)
{
Query.Where(e => Convert.ToDouble(e.Values[index]) == value);
}

View File

@ -7,15 +7,15 @@ namespace DD.Persistence.Database.Specifications.ValuesItem;
/// Спецификация "больше либо равно" для значений IValuesItem в соответствии с индексацией
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ValueGreateOrEqualSpec<TEntity> : Specification<TEntity>
public class ValueGreateOrEqualSpecification<TEntity> : Specification<TEntity>
where TEntity : IValuesItem
{
public ValueGreateOrEqualSpec(int index, string? value)
public ValueGreateOrEqualSpecification(int index, string? value)
{
Query.Where(e => string.Compare(Convert.ToString(e.Values[index]), value) >= 0);
}
public ValueGreateOrEqualSpec(int index, double? value)
public ValueGreateOrEqualSpecification(int index, double? value)
{
Query.Where(e => Convert.ToDouble(e.Values[index]) >= value);
}

View File

@ -7,15 +7,15 @@ namespace DD.Persistence.Database.Specifications.ValuesItem;
/// Спецификация "больше" для значений IValuesItem в соответствии с индексацией
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ValueGreateSpec<TEntity> : Specification<TEntity>
public class ValueGreateSpecification<TEntity> : Specification<TEntity>
where TEntity : IValuesItem
{
public ValueGreateSpec(int index, string? value)
public ValueGreateSpecification(int index, string? value)
{
Query.Where(e => string.Compare(Convert.ToString(e.Values[index]), value) > 0);
}
public ValueGreateSpec(int index, double? value)
public ValueGreateSpecification(int index, double? value)
{
Query.Where(e => Convert.ToDouble(e.Values[index]) > value);
}

View File

@ -7,15 +7,15 @@ namespace DD.Persistence.Database.Specifications.ValuesItem;
/// Спецификация "меньше либо равно" для значений IValuesItem в соответствии с индексацией
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ValueLessOrEqualSpec<TEntity> : Specification<TEntity>
public class ValueLessOrEqualSpecification<TEntity> : Specification<TEntity>
where TEntity : IValuesItem
{
public ValueLessOrEqualSpec(int index, string? value)
public ValueLessOrEqualSpecification(int index, string? value)
{
Query.Where(e => string.Compare(Convert.ToString(e.Values[index]), value) <= 0);
}
public ValueLessOrEqualSpec(int index, double? value)
public ValueLessOrEqualSpecification(int index, double? value)
{
Query.Where(e => Convert.ToDouble(e.Values[index]) <= value);
}

View File

@ -7,15 +7,15 @@ namespace DD.Persistence.Database.Specifications.ValuesItem;
/// Спецификация "меньше" для значений IValuesItem в соответствии с индексацией
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ValueLessSpec<TEntity> : Specification<TEntity>
public class ValueLessSpecification<TEntity> : Specification<TEntity>
where TEntity : IValuesItem
{
public ValueLessSpec(int index, string? value)
public ValueLessSpecification(int index, string? value)
{
Query.Where(e => string.Compare(Convert.ToString(e.Values[index]), value) < 0);
}
public ValueLessSpec(int index, double? value)
public ValueLessSpecification(int index, double? value)
{
Query.Where(e => Convert.ToDouble(e.Values[index]) < value);
}

View File

@ -7,15 +7,15 @@ namespace DD.Persistence.Database.Specifications.ValuesItem;
/// Спецификация неравенства значений IValuesItem в соответствии с индексацией
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ValueNotEqualSpec<TEntity> : Specification<TEntity>
public class ValueNotEqualSpecification<TEntity> : Specification<TEntity>
where TEntity : IValuesItem
{
public ValueNotEqualSpec(int index, string? value)
public ValueNotEqualSpecification(int index, string? value)
{
Query.Where(e => Convert.ToString(e.Values[index]) != value);
}
public ValueNotEqualSpec(int index, double? value)
public ValueNotEqualSpecification(int index, double? value)
{
Query.Where(e => Convert.ToDouble(e.Values[index]) != value);
}

View File

@ -38,7 +38,7 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest
}
[Fact]
public async Task Get_returns_BadRequest()
public async Task Get_returns_success()
{
//arrange
Cleanup();
@ -49,18 +49,11 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest
var secondDiscriminatorId = Guid.NewGuid();
discriminatorIds.Append(secondDiscriminatorId);
try
{
//act
var response = await timestampedValuesClient.Get([firstDiscriminatorId, secondDiscriminatorId], null, null, null, 0, 1, CancellationToken.None);
}
catch (Exception ex)
{
var expectedMessage = $"На сервере произошла ошибка, в результате которой он не может успешно обработать запрос";
//act
var response = await timestampedValuesClient.Get([firstDiscriminatorId, secondDiscriminatorId], null, null, 0, 1, CancellationToken.None);
//assert
Assert.Equal(expectedMessage, ex.Message);
}
//assert
Assert.Null(response);
}
[Fact]
@ -77,25 +70,22 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest
var timestampBegin = DateTimeOffset.UtcNow.AddDays(-1);
var columnNames = new List<string>() { "A", "C" };
var skip = 0;
var take = 6; // Ровно столько значений будет удовлетворять фильтру (\"A\">3) (для одного дискриминатора)
var customFilter = "(\"A\">3)";
var skip = 2;
var take = 16;
var dtos = (await AddRange(firstDiscriminatorId)).ToList();
dtos.AddRange(await AddRange(secondDiscriminatorId));
//act
var response = await timestampedValuesClient.Get([firstDiscriminatorId, secondDiscriminatorId],
timestampBegin, customFilter, columnNames, skip, take, CancellationToken.None);
timestampBegin, columnNames, skip, take, CancellationToken.None);
//assert
Assert.NotNull(response);
Assert.NotEmpty(response);
var expectedCount = take * 2;
var actualCount = response.Count();
Assert.Equal(expectedCount, actualCount);
Assert.Equal(take, actualCount);
var actualColumnNames = response.SelectMany(e => e.Values.Keys).Distinct().ToList();
Assert.Equal(columnNames, actualColumnNames);
@ -388,7 +378,7 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest
var response = await timestampedValuesClient.AddRange(discriminatorId, generatedDtos, CancellationToken.None);
// assert
//Assert.Equal(generatedDtos.Count(), response);
Assert.Equal(generatedDtos.Count(), response);
return generatedDtos;
}

View File

@ -80,10 +80,13 @@ public class FilterBuilderShould
.AsQueryable();
//act
queryableData = queryableData.ApplyFilter(dataScheme, root);
var specification = dataScheme.BuildFilter<TimestampedValues>(root);
//assert
var result = queryableData.ToList();
Assert.NotNull(specification);
var query = SpecificationEvaluator.GetQuery(queryableData, specification);
var result = query.ToList();
Assert.NotNull(result);
Assert.NotEmpty(result);
@ -165,13 +168,13 @@ public class FilterBuilderShould
.AsQueryable();
//act
queryableData = queryableData.ApplyFilter(dataScheme, root);
var specification = dataScheme.BuildFilter<TimestampedValues>(root);
//assert
var result = queryableData.ToList();
Assert.NotNull(specification);
Assert.NotNull(result);
Assert.NotEmpty(result);
var query = SpecificationEvaluator.GetQuery(queryableData, specification);
var result = query.ToList();
Assert.NotNull(result);
Assert.NotEmpty(result);
@ -260,13 +263,13 @@ public class FilterBuilderShould
.AsQueryable();
//act
queryableData = queryableData.ApplyFilter(dataScheme, root);
var specification = dataScheme.BuildFilter<TimestampedValues>(root);
//assert
var result = queryableData.ToList();
Assert.NotNull(specification);
Assert.NotNull(result);
Assert.NotEmpty(result);
var query = SpecificationEvaluator.GetQuery(queryableData, specification);
var result = query.ToList();
Assert.NotNull(result);
Assert.NotEmpty(result);

View File

@ -9,7 +9,7 @@ public class TimestampedValuesServiceShould
{
private readonly ITimestampedValuesRepository timestampedValuesRepository = Substitute.For<ITimestampedValuesRepository>();
private readonly ISchemePropertyRepository dataSchemeRepository = Substitute.For<ISchemePropertyRepository>();
private readonly TimestampedValuesService timestampedValuesService;
private TimestampedValuesService timestampedValuesService;
public TimestampedValuesServiceShould()
{
@ -34,7 +34,7 @@ public class TimestampedValuesServiceShould
.AddHours(-1)
.ToUniversalTime();
var getResult = await timestampedValuesService
.Get(discriminatorIds, geTimestamp, null, columnNames, 0, count, CancellationToken.None);
.Get(discriminatorIds, geTimestamp, columnNames, 0, count, CancellationToken.None);
Assert.NotNull(getResult);
Assert.Empty(getResult);
}

View File

@ -24,20 +24,20 @@ public class TreeBuilderTest
OperationEnum.And,
new TVertex(
OperationEnum.Or,
new TLeaf(OperationEnum.Equal, "A", 1.0),
new TLeaf(OperationEnum.Equal, "B", 2.0)
new TLeaf(OperationEnum.Equal, "A", 1),
new TLeaf(OperationEnum.Equal, "B", 2)
),
new TVertex(
OperationEnum.Or,
new TLeaf(OperationEnum.Equal, "C", 3.0),
new TLeaf(OperationEnum.Equal, "C", 3),
new TVertex(
OperationEnum.Or,
new TLeaf(OperationEnum.Equal, "D", 4.0),
new TLeaf(OperationEnum.Equal, "E", 5.0)
new TLeaf(OperationEnum.Equal, "D", 4),
new TLeaf(OperationEnum.Equal, "E", 5)
)
)
),
new TLeaf(OperationEnum.Equal, "F", 6.0)
new TLeaf(OperationEnum.Equal, "F", 6)
));
var actualRoot = JsonConvert.SerializeObject(root);
Assert.Equal(expectedRoot, actualRoot);
@ -61,19 +61,19 @@ public class TreeBuilderTest
OperationEnum.Or,
new TVertex(
OperationEnum.Or,
new TLeaf(OperationEnum.Equal, "A", 1.0),
new TLeaf(OperationEnum.NotEqual, "B", 1.0)
new TLeaf(OperationEnum.Equal, "A", 1),
new TLeaf(OperationEnum.NotEqual, "B", 1)
),
new TVertex(
OperationEnum.Or,
new TLeaf(OperationEnum.Greate, "C", 1.0),
new TLeaf(OperationEnum.GreateOrEqual, "D", 1.0)
new TLeaf(OperationEnum.Greate, "C", 1),
new TLeaf(OperationEnum.GreateOrEqual, "D", 1)
)
),
new TVertex(
OperationEnum.Or,
new TLeaf(OperationEnum.Less, "E", 1.0),
new TLeaf(OperationEnum.LessOrEqual, "F", 1.0)
new TLeaf(OperationEnum.Less, "E", 1),
new TLeaf(OperationEnum.LessOrEqual, "F", 1)
)
));
var actualRoot = JsonConvert.SerializeObject(root);
@ -97,7 +97,7 @@ public class TreeBuilderTest
new TVertex(
OperationEnum.Or,
new TLeaf(OperationEnum.Equal, "A", 1.2345),
new TLeaf(OperationEnum.Equal, "B", 12345.0)
new TLeaf(OperationEnum.Equal, "B", 12345)
),
new TLeaf(OperationEnum.Equal, "C", "12345")
));

View File

@ -1,14 +1,11 @@
using DD.Persistence.Filter.Models.Enumerations;
using DD.Persistence.Filter.TreeBuilder;
using System.Diagnostics.CodeAnalysis;
namespace DD.Persistence.Filter.Models.Abstractions;
/// <summary>
/// Абстрактная модель вершины
/// </summary>
public abstract class TNode : IParsable<TNode?>
public abstract class TNode
{
/// <inheritdoc/>
public TNode(OperationEnum operation)
@ -21,30 +18,6 @@ public abstract class TNode : IParsable<TNode?>
/// </summary>
public OperationEnum Operation { get; }
/// <inheritdoc/>
public static TNode? Parse(string s, IFormatProvider? provider)
{
var result = s.BuildTree();
return result;
}
/// <inheritdoc/>
public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out TNode result)
{
if (string.IsNullOrEmpty(s))
{
result = default(TNode);
return false;
}
result = s.BuildTree();
if (result is null)
return false;
return true;
}
/// <summary>
/// Принять посетителя
/// </summary>

View File

@ -62,16 +62,15 @@ abstract class TerminalExpression : IExpression
private static object? ParseValue(string value)
{
if (double.TryParse(value, out _))
value = value.Replace('.', ',');
if (value.Contains(',') && double.TryParse(value, out _))
{
return double.Parse(value);
}
// ToDo: избавиться
var doubleValue= value.Replace('.', ',');
if (double.TryParse(doubleValue, out _))
if (int.TryParse(value, out _))
{
return double.Parse(doubleValue);
return int.Parse(value);
}
value = value.Trim('\"');

View File

@ -1,5 +1,4 @@
using DD.Persistence.Filter.Models.Abstractions;
using DD.Persistence.Models;
using DD.Persistence.Models;
using DD.Persistence.RepositoriesAbstractions;
namespace DD.Persistence.Repositories;
@ -31,7 +30,6 @@ public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBase
/// </summary>
/// <param name="idDiscriminators">Набор дискриминаторов (идентификаторов)</param>
/// <param name="geTimestamp">Фильтр позднее даты</param>
/// <param name="filterTree"></param>
/// <param name="columnNames">Фильтр свойств набора. Можно запросить только некоторые свойства из набора</param>
/// <param name="skip"></param>
/// <param name="take"></param>
@ -39,7 +37,6 @@ public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBase
/// <returns></returns>
Task<IDictionary<Guid, IEnumerable<(DateTimeOffset Timestamp, object[] Values)>>> Get(IEnumerable<Guid> idDiscriminators,
DateTimeOffset? geTimestamp,
TNode? filterTree,
IEnumerable<string>? columnNames,
int skip,
int take,

View File

@ -1,5 +1,4 @@
using DD.Persistence.Filter.Models.Abstractions;
using DD.Persistence.Models;
using DD.Persistence.Models;
using DD.Persistence.Models.Common;
namespace DD.Persistence.Services.Interfaces;
@ -23,14 +22,13 @@ public interface ITimestampedValuesService
/// </summary>
/// <param name="discriminatorIds">Набор дискриминаторов (идентификаторов)</param>
/// <param name="geTimestamp"></param>
/// <param name="filterTree"></param>
/// <param name="columnNames"></param>
/// <param name="skip"></param>
/// <param name="take"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<TimestampedValuesDto>> Get(IEnumerable<Guid> discriminatorIds, DateTimeOffset? geTimestamp,
TNode? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
/// <summary>
/// Получение данных с начала

View File

@ -1,5 +1,4 @@
using DD.Persistence.Extensions;
using DD.Persistence.Filter.Models.Abstractions;
using DD.Persistence.Models;
using DD.Persistence.Repositories;
using DD.Persistence.Services.Interfaces;
@ -35,9 +34,9 @@ public class TimestampedValuesService : ITimestampedValuesService
}
/// <inheritdoc/>
public async Task<IEnumerable<TimestampedValuesDto>> Get(IEnumerable<Guid> discriminatorIds, DateTimeOffset? geTimestamp, TNode? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
public async Task<IEnumerable<TimestampedValuesDto>> Get(IEnumerable<Guid> discriminatorIds, DateTimeOffset? geTimestamp, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
{
var result = await timestampedValuesRepository.Get(discriminatorIds, geTimestamp, filterTree, columnNames, skip, take, token);
var result = await timestampedValuesRepository.Get(discriminatorIds, geTimestamp, columnNames, skip, take, token);
var dtos = await BindingToDataScheme(result, token);