feat(db): PendingSecuredWrite entity + migration + repository (T14b)
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Entities.SecuredWrites;
|
||||
|
||||
/// <summary>
|
||||
/// Central operational state row for a two-person ("secured") write through its
|
||||
/// lifecycle (M7 OPC UA / MxGateway UX, Task T14b). One row per pending write in the
|
||||
/// central <c>PendingSecuredWrites</c> MS SQL table — append-once at submission then
|
||||
/// mutated as the request is approved/rejected and executed against the target.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Persistence-ignorant POCO; the EF Core mapping lives in the Configuration Database
|
||||
/// component (<c>PendingSecuredWriteEntityTypeConfiguration</c>). Unlike the partitioned
|
||||
/// append-only <c>AuditLog</c> this entity backs mutable operational state on a standard
|
||||
/// non-partitioned table on the <c>[PRIMARY]</c> filegroup; no DB-role restriction
|
||||
/// applies. It mirrors the <see cref="ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit.SiteCall"/>
|
||||
/// entity/config/repository shape.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// All timestamps are UTC, like every timestamp in the system.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public sealed class PendingSecuredWrite
|
||||
{
|
||||
/// <summary>Surrogate identity key assigned by the store.</summary>
|
||||
public long Id { get; set; }
|
||||
|
||||
/// <summary>Site id the secured write targets.</summary>
|
||||
public required string SiteId { get; set; }
|
||||
|
||||
/// <summary>Data connection name within the site the write is routed through.</summary>
|
||||
public required string ConnectionName { get; set; }
|
||||
|
||||
/// <summary>Fully-qualified tag path the value is written to.</summary>
|
||||
public required string TagPath { get; set; }
|
||||
|
||||
/// <summary>JSON-serialised value to write (interpreted per <see cref="ValueType"/>).</summary>
|
||||
public required string ValueJson { get; set; }
|
||||
|
||||
/// <summary>The target data type name (e.g. <c>Boolean</c>, <c>Double</c>, <c>String</c>).</summary>
|
||||
public required string ValueType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Lifecycle status — one of
|
||||
/// <c>Pending|Approved|Rejected|Executed|Failed|Expired</c>.
|
||||
/// </summary>
|
||||
public required string Status { get; set; }
|
||||
|
||||
/// <summary>The operator who submitted (requested) the secured write.</summary>
|
||||
public required string OperatorUser { get; set; }
|
||||
|
||||
/// <summary>Optional free-text comment supplied by the requesting operator.</summary>
|
||||
public string? OperatorComment { get; set; }
|
||||
|
||||
/// <summary>UTC instant the secured write was submitted.</summary>
|
||||
public required DateTime SubmittedAtUtc { get; set; }
|
||||
|
||||
/// <summary>The verifier who approved/rejected the write; <c>null</c> while pending.</summary>
|
||||
public string? VerifierUser { get; set; }
|
||||
|
||||
/// <summary>Optional free-text comment supplied by the verifier on decision.</summary>
|
||||
public string? VerifierComment { get; set; }
|
||||
|
||||
/// <summary>UTC instant the write was approved/rejected; <c>null</c> while pending.</summary>
|
||||
public DateTime? DecidedAtUtc { get; set; }
|
||||
|
||||
/// <summary>UTC instant the approved write was executed against the target; <c>null</c> until executed.</summary>
|
||||
public DateTime? ExecutedAtUtc { get; set; }
|
||||
|
||||
/// <summary>Most recent execution error message; <c>null</c> when no failure has occurred.</summary>
|
||||
public string? ExecutionError { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.SecuredWrites;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Operational-state data access for the central <c>PendingSecuredWrites</c> table
|
||||
/// (M7 OPC UA / MxGateway UX, Task T14b). One row per pending two-person secured
|
||||
/// write; rows are inserted at submission and mutated as the request is decided and
|
||||
/// executed. Mirrors the <c>SiteCalls</c> (Site Call Audit #22) repository shape.
|
||||
/// </summary>
|
||||
public interface ISecuredWriteRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Inserts <paramref name="securedWrite"/> and returns the store-generated
|
||||
/// <see cref="PendingSecuredWrite.Id"/>.
|
||||
/// </summary>
|
||||
/// <param name="securedWrite">The pending secured write to persist.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the generated identity of the inserted row.</returns>
|
||||
Task<long> AddAsync(PendingSecuredWrite securedWrite, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the row for the given id, or <c>null</c> if none exists.
|
||||
/// </summary>
|
||||
/// <param name="id">The identity to look up.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to the matching <see cref="PendingSecuredWrite"/>, or <c>null</c> if no row exists.</returns>
|
||||
Task<PendingSecuredWrite?> GetAsync(long id, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Persists the current state of <paramref name="securedWrite"/> (matched by
|
||||
/// <see cref="PendingSecuredWrite.Id"/>).
|
||||
/// </summary>
|
||||
/// <param name="securedWrite">The tracked entity whose changes to persist.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task UpdateAsync(PendingSecuredWrite securedWrite, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Returns up to <paramref name="take"/> rows (skipping <paramref name="skip"/>)
|
||||
/// optionally filtered by <paramref name="status"/> and <paramref name="siteId"/>,
|
||||
/// ordered by <c>SubmittedAtUtc DESC, Id DESC</c>. A <c>null</c> filter argument
|
||||
/// matches every row.
|
||||
/// </summary>
|
||||
/// <param name="status">Status filter; <c>null</c> matches every status.</param>
|
||||
/// <param name="siteId">Site id filter; <c>null</c> matches every site.</param>
|
||||
/// <param name="skip">Number of rows to skip (offset paging).</param>
|
||||
/// <param name="take">Maximum number of rows to return.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to a page of matching rows, newest submission first.</returns>
|
||||
Task<IReadOnlyList<PendingSecuredWrite>> QueryAsync(
|
||||
string? status,
|
||||
string? siteId,
|
||||
int skip,
|
||||
int take,
|
||||
CancellationToken ct = default);
|
||||
}
|
||||
Reference in New Issue
Block a user