eSPJ/Services/FileDetailPenjemputanStore.cs

341 lines
15 KiB
C#

using System.Text.Json;
using eSPJ.Models;
namespace eSPJ.Services
{
public class FileDetailPenjemputanStore : IDetailPenjemputanStore
{
private readonly string _storeFilePath;
private readonly ILogger<FileDetailPenjemputanStore> _logger;
private static readonly SemaphoreSlim _fileLock = new SemaphoreSlim(1, 1);
public FileDetailPenjemputanStore(
IWebHostEnvironment env,
ILogger<FileDetailPenjemputanStore> logger)
{
_logger = logger;
_storeFilePath = Path.Combine(env.ContentRootPath, "Data", "detail-penjemputan.json");
}
public async Task<List<TpsData>> GetAllAsync()
{
await _fileLock.WaitAsync();
try
{
return await ReadAllFromDiskAsync();
}
finally
{
_fileLock.Release();
}
}
private async Task<List<TpsData>> ReadAllFromDiskAsync()
{
try
{
if (!File.Exists(_storeFilePath))
{
return new List<TpsData>();
}
var json = await File.ReadAllTextAsync(_storeFilePath);
if (string.IsNullOrWhiteSpace(json))
{
return new List<TpsData>();
}
var data = JsonSerializer.Deserialize<List<TpsData>>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
return data?.Where(item => item != null).ToList() ?? new List<TpsData>();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error reading submitted penjemputan data");
return new List<TpsData>();
}
}
public async Task<List<TpsData>> GetByNomorSpjAsync(string nomorSpj)
{
var normalizedNomorSpj = (nomorSpj ?? string.Empty).Trim();
if (string.IsNullOrWhiteSpace(normalizedNomorSpj))
{
return new List<TpsData>();
}
var allData = await GetAllAsync();
return allData
.Where(item => string.Equals((item.NomorSpj ?? string.Empty).Trim(), normalizedNomorSpj, StringComparison.OrdinalIgnoreCase))
.OrderByDescending(item => item.UpdatedAt)
.GroupBy(GetRecordIdentity, StringComparer.OrdinalIgnoreCase)
.Select(group => group.First())
.OrderBy(item => item.Name)
.ToList();
}
public async Task<List<TpsData>> GetSubmittedAsync()
{
var allData = await GetAllAsync();
return allData
.Where(item => item.IsSubmit)
.OrderByDescending(item => item.UpdatedAt)
.GroupBy(GetRecordIdentity, StringComparer.OrdinalIgnoreCase)
.Select(group => group.First())
.ToList();
}
private static string GetRecordIdentity(TpsData item)
{
return string.Join("::", new[]
{
item.NomorSpj?.Trim() ?? string.Empty,
item.SpjDetailId?.Trim() ?? string.Empty,
item.LokasiAngkutId?.Trim() ?? string.Empty,
item.Name?.Trim() ?? string.Empty,
});
}
public async Task<TpsData?> GetSubmittedDetailAsync(string nomorSpj, string? spjDetailId = null, string? lokasiAngkutId = null, string? namaTps = null)
{
var normalizedNomorSpj = (nomorSpj ?? string.Empty).Trim();
var normalizedSpjDetailId = (spjDetailId ?? string.Empty).Trim();
var normalizedLokasiAngkutId = (lokasiAngkutId ?? string.Empty).Trim();
var normalizedNamaTps = (namaTps ?? string.Empty).Trim();
var allData = await GetSubmittedAsync();
return allData.FirstOrDefault(item =>
string.Equals((item.NomorSpj ?? string.Empty).Trim(), normalizedNomorSpj, StringComparison.OrdinalIgnoreCase) &&
(string.IsNullOrWhiteSpace(normalizedSpjDetailId) || string.Equals((item.SpjDetailId ?? string.Empty).Trim(), normalizedSpjDetailId, StringComparison.OrdinalIgnoreCase)) &&
(string.IsNullOrWhiteSpace(normalizedLokasiAngkutId) || string.Equals((item.LokasiAngkutId ?? string.Empty).Trim(), normalizedLokasiAngkutId, StringComparison.OrdinalIgnoreCase)) &&
(string.IsNullOrWhiteSpace(normalizedNamaTps) || string.Equals((item.Name ?? string.Empty).Trim(), normalizedNamaTps, StringComparison.OrdinalIgnoreCase)));
}
public async Task SaveSubmittedAsync(TpsData data)
{
await _fileLock.WaitAsync();
try
{
var allData = await ReadAllFromDiskAsync();
data.IsSubmit = true;
data.UpdatedAt = DateTime.Now;
if (!data.SubmittedAt.HasValue)
{
data.SubmittedAt = DateTime.Now;
}
var existingIndex = FindRecordIndex(allData, data.NomorSpj, data.SpjDetailId, data.LokasiAngkutId, data.Name);
if (existingIndex >= 0)
{
allData[existingIndex] = data;
}
else
{
allData.Add(data);
}
await WriteAllToDiskAsync(allData);
}
finally
{
_fileLock.Release();
}
}
public async Task SaveRecordAsync(RecordSaveRequest request)
{
await _fileLock.WaitAsync();
try
{
var allData = await ReadAllFromDiskAsync();
var mapped = MapRequestToRecord(request);
mapped.UpdatedAt = DateTime.Now;
var existingIndex = FindRecordIndex(allData, mapped.NomorSpj, mapped.SpjDetailId, mapped.LokasiAngkutId, mapped.Name);
if (existingIndex >= 0)
{
var existing = allData[existingIndex];
mapped.NomorSpj = string.IsNullOrWhiteSpace(mapped.NomorSpj) ? existing.NomorSpj : mapped.NomorSpj;
mapped.LokasiAngkutId = string.IsNullOrWhiteSpace(mapped.LokasiAngkutId) ? existing.LokasiAngkutId : mapped.LokasiAngkutId;
mapped.SpjDetailId = string.IsNullOrWhiteSpace(mapped.SpjDetailId) ? existing.SpjDetailId : mapped.SpjDetailId;
mapped.Name = string.IsNullOrWhiteSpace(mapped.Name) ? existing.Name : mapped.Name;
mapped.Latitude = string.IsNullOrWhiteSpace(mapped.Latitude) ? existing.Latitude : mapped.Latitude;
mapped.Longitude = string.IsNullOrWhiteSpace(mapped.Longitude) ? existing.Longitude : mapped.Longitude;
mapped.AlamatJalan = string.IsNullOrWhiteSpace(mapped.AlamatJalan) ? existing.AlamatJalan : mapped.AlamatJalan;
mapped.WaktuKedatangan = string.IsNullOrWhiteSpace(mapped.WaktuKedatangan) ? existing.WaktuKedatangan : mapped.WaktuKedatangan;
mapped.FotoKedatangan = mapped.FotoKedatangan.Count > 0 ? mapped.FotoKedatangan : (existing.FotoKedatangan ?? new List<string>());
mapped.FotoKedatanganUploaded = mapped.FotoKedatanganUploaded || existing.FotoKedatanganUploaded;
mapped.Timbangan = MergeTimbangan(existing.Timbangan, mapped.Timbangan);
mapped.TotalOrganik = mapped.TotalOrganik != 0 ? mapped.TotalOrganik : existing.TotalOrganik;
mapped.TotalAnorganik = mapped.TotalAnorganik != 0 ? mapped.TotalAnorganik : existing.TotalAnorganik;
mapped.TotalResidu = mapped.TotalResidu != 0 ? mapped.TotalResidu : existing.TotalResidu;
mapped.TotalTimbangan = mapped.TotalTimbangan != 0 ? mapped.TotalTimbangan : existing.TotalTimbangan;
mapped.FotoPetugas = mapped.FotoPetugas.Count > 0 ? mapped.FotoPetugas : (existing.FotoPetugas ?? new List<string>());
mapped.FotoPetugasUploaded = mapped.FotoPetugasUploaded || existing.FotoPetugasUploaded;
mapped.NamaPetugas = string.IsNullOrWhiteSpace(mapped.NamaPetugas) ? existing.NamaPetugas : mapped.NamaPetugas;
mapped.IsSubmit = existing.IsSubmit || mapped.IsSubmit;
mapped.SubmittedAt = existing.SubmittedAt;
allData[existingIndex] = mapped;
}
else
{
allData.Add(mapped);
}
await WriteAllToDiskAsync(allData);
}
finally
{
_fileLock.Release();
}
}
private static List<TimbanganItem> MergeTimbangan(List<TimbanganItem>? existingItems, List<TimbanganItem>? incomingItems)
{
var existing = existingItems ?? new List<TimbanganItem>();
var incoming = incomingItems ?? new List<TimbanganItem>();
if (incoming.Count == 0)
{
return existing;
}
var maxCount = Math.Max(existing.Count, incoming.Count);
var merged = new List<TimbanganItem>(maxCount);
for (var index = 0; index < maxCount; index++)
{
var existingItem = index < existing.Count ? existing[index] : null;
var incomingItem = index < incoming.Count ? incoming[index] : null;
if (existingItem == null && incomingItem == null)
{
continue;
}
if (existingItem == null)
{
merged.Add(CloneTimbanganItem(incomingItem!));
continue;
}
if (incomingItem == null)
{
merged.Add(CloneTimbanganItem(existingItem));
continue;
}
merged.Add(new TimbanganItem
{
FotoFileName = string.IsNullOrWhiteSpace(incomingItem.FotoFileName)
? existingItem.FotoFileName
: incomingItem.FotoFileName,
Berat = incomingItem.Berat != null && incomingItem.Berat.Count > 0
? new List<decimal>(incomingItem.Berat)
: new List<decimal>(existingItem.Berat ?? new List<decimal>()),
LokasiAngkut = incomingItem.LokasiAngkut != null && incomingItem.LokasiAngkut.Count > 0
? new List<string>(incomingItem.LokasiAngkut)
: new List<string>(existingItem.LokasiAngkut ?? new List<string>()),
JenisSampah = incomingItem.JenisSampah != null && incomingItem.JenisSampah.Count > 0
? new List<JenisSampah>(incomingItem.JenisSampah)
: new List<JenisSampah>(existingItem.JenisSampah ?? new List<JenisSampah>()),
IsUploaded = existingItem.IsUploaded || incomingItem.IsUploaded,
WaktuUpload = incomingItem.WaktuUpload ?? existingItem.WaktuUpload,
});
}
return merged;
}
private static TimbanganItem CloneTimbanganItem(TimbanganItem source)
{
return new TimbanganItem
{
FotoFileName = source.FotoFileName,
Berat = new List<decimal>(source.Berat ?? new List<decimal>()),
LokasiAngkut = new List<string>(source.LokasiAngkut ?? new List<string>()),
JenisSampah = new List<JenisSampah>(source.JenisSampah ?? new List<JenisSampah>()),
IsUploaded = source.IsUploaded,
WaktuUpload = source.WaktuUpload,
};
}
private async Task WriteAllToDiskAsync(List<TpsData> data)
{
var directory = Path.GetDirectoryName(_storeFilePath);
if (!string.IsNullOrWhiteSpace(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
data = data
.Where(item => item != null)
.OrderByDescending(item => item.UpdatedAt)
.GroupBy(GetRecordIdentity, StringComparer.OrdinalIgnoreCase)
.Select(group => group.First())
.OrderBy(item => item.Name)
.ToList();
var options = new JsonSerializerOptions { WriteIndented = true };
var json = JsonSerializer.Serialize(data, options);
var tempPath = _storeFilePath + ".tmp";
await File.WriteAllTextAsync(tempPath, json);
File.Move(tempPath, _storeFilePath, overwrite: true);
}
private static int FindRecordIndex(List<TpsData> allData, string? nomorSpj, string? spjDetailId, string? lokasiAngkutId, string? namaTps)
{
return allData.FindIndex(item =>
item != null &&
!string.IsNullOrWhiteSpace(nomorSpj) &&
string.Equals(item.NomorSpj, nomorSpj, StringComparison.OrdinalIgnoreCase) &&
string.Equals(item.SpjDetailId, spjDetailId ?? string.Empty, StringComparison.OrdinalIgnoreCase) &&
string.Equals(item.LokasiAngkutId, lokasiAngkutId ?? string.Empty, StringComparison.OrdinalIgnoreCase) &&
string.Equals(item.Name, namaTps ?? string.Empty, StringComparison.OrdinalIgnoreCase));
}
private static TpsData MapRequestToRecord(RecordSaveRequest request)
{
return new TpsData
{
NomorSpj = request.NomorSpj,
LokasiAngkutId = request.LokasiAngkutId,
SpjDetailId = request.SpjDetailId,
Name = request.NamaTps,
Latitude = request.Latitude,
Longitude = request.Longitude,
AlamatJalan = request.AlamatJalan,
WaktuKedatangan = request.WaktuKedatangan,
FotoKedatangan = request.FotoKedatanganFileNames ?? new List<string>(),
FotoKedatanganUploaded = request.FotoKedatanganUploaded,
Timbangan = (request.Timbangan ?? new List<RecordTimbanganItem>())
.Where(item => item != null)
.Select(item => new TimbanganItem
{
FotoFileName = item.FotoFileName,
Berat = new List<decimal> { item.Berat },
LokasiAngkut = new List<string>(),
JenisSampah = Enum.TryParse<JenisSampah>(item.JenisSampah, out var parsedJenis)
? new List<JenisSampah> { parsedJenis }
: new List<JenisSampah> { JenisSampah.Residu },
IsUploaded = item.Uploaded,
WaktuUpload = DateTime.Now
}).ToList(),
TotalOrganik = request.TotalOrganik,
TotalAnorganik = request.TotalAnorganik,
TotalResidu = request.TotalResidu,
TotalTimbangan = request.TotalTimbangan,
FotoPetugas = request.FotoPetugasFileNames ?? new List<string>(),
FotoPetugasUploaded = request.FotoPetugasUploaded,
NamaPetugas = request.NamaPetugas,
IsSubmit = request.IsSubmit,
UpdatedAt = DateTime.Now,
SubmittedAt = request.IsSubmit ? DateTime.Now : null,
};
}
}
}