WIP: Record history
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Google.Apis.Sheets.v4;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@@ -781,6 +782,9 @@ public class LayersController : Controller
|
||||
|
||||
_db.Records.Add(record);
|
||||
|
||||
// Capture history
|
||||
CaptureRecordHistory(record, RecordChangeType.Created, Guid.Parse(userId));
|
||||
|
||||
// Update layer modified info
|
||||
layer.ModifiedAt = DateTime.UtcNow;
|
||||
layer.ModifiedById = Guid.Parse(userId);
|
||||
@@ -851,10 +855,17 @@ public class LayersController : Controller
|
||||
return BadRequest("Desc1 is required");
|
||||
}
|
||||
|
||||
// Capture old values before updating
|
||||
var oldCode = record.Code;
|
||||
var oldDesc1 = record.Desc1;
|
||||
|
||||
record.Desc1 = recordDto.Desc1;
|
||||
record.ModifiedAt = DateTime.UtcNow;
|
||||
record.ModifiedById = Guid.Parse(userId);
|
||||
|
||||
// Capture history
|
||||
CaptureRecordHistory(record, RecordChangeType.Updated, Guid.Parse(userId), oldCode, oldDesc1);
|
||||
|
||||
// Update layer modified info
|
||||
layer.ModifiedAt = DateTime.UtcNow;
|
||||
layer.ModifiedById = Guid.Parse(userId);
|
||||
@@ -915,6 +926,9 @@ public class LayersController : Controller
|
||||
return NotFound("Record not found");
|
||||
}
|
||||
|
||||
// Capture history before deleting
|
||||
CaptureRecordHistory(record, RecordChangeType.Deleted, Guid.Parse(userId));
|
||||
|
||||
_db.Records.Remove(record);
|
||||
|
||||
// Update layer modified info
|
||||
@@ -933,4 +947,165 @@ public class LayersController : Controller
|
||||
return BadRequest(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("{layerId:guid}/records/{recordId:guid}/history")]
|
||||
public IActionResult GetRecordHistory(Guid layerId, Guid recordId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var history = _db.RecordHistory
|
||||
.Include(h => h.ChangedBy)
|
||||
.Where(h => h.RecordId == recordId && h.LayerId == layerId)
|
||||
.OrderByDescending(h => h.ChangedAt)
|
||||
.AsNoTracking()
|
||||
.Select(h => new RecordHistoryDto
|
||||
{
|
||||
Id = h.Id,
|
||||
RecordId = h.RecordId,
|
||||
LayerId = h.LayerId,
|
||||
ChangedAt = h.ChangedAt,
|
||||
ChangedById = h.ChangedById,
|
||||
ChangedByName = h.ChangedBy != null ? h.ChangedBy.UserName ?? h.ChangedBy.Email : "Unknown",
|
||||
ChangeType = h.ChangeType.ToString(),
|
||||
Code = h.Code,
|
||||
Desc1 = h.Desc1,
|
||||
ChangedFields = h.ChangedFields,
|
||||
ChangesSummary = h.ChangesSummary,
|
||||
FormattedChange = FormatHistoryChange(h)
|
||||
})
|
||||
.ToList();
|
||||
|
||||
_logger.LogDebug("GetRecordHistory: Retrieved {Count} history entries for record {RecordId}", history.Count, recordId);
|
||||
|
||||
return Ok(history);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "GetRecordHistory: Error retrieving history for record {RecordId}", recordId);
|
||||
return BadRequest(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("{layerId:guid}/records/deleted")]
|
||||
public IActionResult GetDeletedRecords(Guid layerId)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get the most recent "Deleted" history entry for each unique RecordId in this layer
|
||||
var deletedRecords = _db.RecordHistory
|
||||
.Include(h => h.ChangedBy)
|
||||
.Where(h => h.LayerId == layerId && h.ChangeType == RecordChangeType.Deleted)
|
||||
.GroupBy(h => h.RecordId)
|
||||
.Select(g => g.OrderByDescending(h => h.ChangedAt).FirstOrDefault())
|
||||
.Where(h => h != null)
|
||||
.Select(h => new DeletedRecordDto
|
||||
{
|
||||
RecordId = h!.RecordId,
|
||||
Code = h.Code,
|
||||
Desc1 = h.Desc1,
|
||||
DeletedAt = h.ChangedAt,
|
||||
DeletedById = h.ChangedById,
|
||||
DeletedByName = h.ChangedBy != null ? h.ChangedBy.UserName ?? string.Empty : string.Empty
|
||||
})
|
||||
.OrderByDescending(d => d.DeletedAt)
|
||||
.ToList();
|
||||
|
||||
_logger.LogDebug("GetDeletedRecords: Retrieved {Count} deleted records for layer {LayerId}", deletedRecords.Count, layerId);
|
||||
return Ok(deletedRecords);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "GetDeletedRecords: Error retrieving deleted records for layer {LayerId}", layerId);
|
||||
return BadRequest(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
// Helper method to capture record history
|
||||
private void CaptureRecordHistory(Record record, RecordChangeType changeType, Guid userId, string? oldCode = null, string? oldDesc1 = null)
|
||||
{
|
||||
var changedFields = new List<string>();
|
||||
var changesSummary = new Dictionary<string, Dictionary<string, string?>>();
|
||||
|
||||
if (changeType == RecordChangeType.Updated)
|
||||
{
|
||||
if (oldCode != record.Code)
|
||||
{
|
||||
changedFields.Add("Code");
|
||||
changesSummary["Code"] = new Dictionary<string, string?>
|
||||
{
|
||||
["old"] = oldCode,
|
||||
["new"] = record.Code
|
||||
};
|
||||
}
|
||||
|
||||
if (oldDesc1 != record.Desc1)
|
||||
{
|
||||
changedFields.Add("Desc1");
|
||||
changesSummary["Desc1"] = new Dictionary<string, string?>
|
||||
{
|
||||
["old"] = oldDesc1,
|
||||
["new"] = record.Desc1
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var history = new RecordHistory
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
RecordId = record.Id,
|
||||
LayerId = record.LayerId,
|
||||
ChangedAt = DateTime.UtcNow,
|
||||
ChangedById = userId,
|
||||
ChangeType = changeType,
|
||||
Code = record.Code,
|
||||
Desc1 = record.Desc1,
|
||||
ChangedFields = changedFields.Any() ? string.Join(", ", changedFields) : null,
|
||||
ChangesSummary = changesSummary.Any() ? JsonSerializer.Serialize(changesSummary) : null
|
||||
};
|
||||
|
||||
_db.RecordHistory.Add(history);
|
||||
_logger.LogInformation("CaptureRecordHistory: Captured {ChangeType} for record {RecordId}", changeType, record.Id);
|
||||
}
|
||||
|
||||
// Helper method to format history change for display
|
||||
private static string FormatHistoryChange(RecordHistory h)
|
||||
{
|
||||
if (h.ChangeType == RecordChangeType.Created)
|
||||
{
|
||||
return $"Created record with Code: \"{h.Code}\", Description: \"{h.Desc1}\"";
|
||||
}
|
||||
|
||||
if (h.ChangeType == RecordChangeType.Deleted)
|
||||
{
|
||||
return $"Deleted record Code: \"{h.Code}\", Description: \"{h.Desc1}\"";
|
||||
}
|
||||
|
||||
// Updated
|
||||
if (!string.IsNullOrEmpty(h.ChangesSummary))
|
||||
{
|
||||
try
|
||||
{
|
||||
var changes = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, string?>>>(h.ChangesSummary);
|
||||
if (changes != null)
|
||||
{
|
||||
var parts = new List<string>();
|
||||
foreach (var (field, values) in changes)
|
||||
{
|
||||
var oldVal = values.GetValueOrDefault("old") ?? "empty";
|
||||
var newVal = values.GetValueOrDefault("new") ?? "empty";
|
||||
parts.Add($"{field}: \"{oldVal}\" → \"{newVal}\"");
|
||||
}
|
||||
return $"Updated: {string.Join(", ", parts)}";
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Fall back to simple message
|
||||
}
|
||||
}
|
||||
|
||||
return $"Updated {h.ChangedFields ?? "record"}";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user