365 lines
15 KiB
C#
365 lines
15 KiB
C#
using eSPJ.Models;
|
|
|
|
namespace eSPJ.Services
|
|
{
|
|
public class DetailPenjemputanService
|
|
{
|
|
private readonly IDetailPenjemputanStore _store;
|
|
private readonly IWebHostEnvironment _env;
|
|
private readonly ILogger<DetailPenjemputanService> _logger;
|
|
|
|
public DetailPenjemputanService(
|
|
IWebHostEnvironment env,
|
|
IDetailPenjemputanStore store,
|
|
ILogger<DetailPenjemputanService> logger)
|
|
{
|
|
_env = env;
|
|
_store = store;
|
|
_logger = logger;
|
|
}
|
|
|
|
private static string SanitizePathSegment(string? value, string fallback = "umum")
|
|
{
|
|
var safe = string.Concat((value ?? string.Empty).Trim().Select(c =>
|
|
char.IsLetterOrDigit(c) || c == '-' || c == '_'
|
|
? c
|
|
: '-'));
|
|
|
|
while (safe.Contains("--"))
|
|
{
|
|
safe = safe.Replace("--", "-");
|
|
}
|
|
|
|
safe = safe.Trim('-');
|
|
return string.IsNullOrWhiteSpace(safe) ? fallback : safe;
|
|
}
|
|
|
|
public async Task<List<TpsData>> GetAllTpsDataAsync()
|
|
{
|
|
return await _store.GetSubmittedAsync();
|
|
}
|
|
|
|
public async Task<List<TpsData>> GetRecordsByNomorSpjAsync(string nomorSpj)
|
|
{
|
|
return await _store.GetByNomorSpjAsync(nomorSpj);
|
|
}
|
|
|
|
public async Task<bool> SaveTpsDataAsync(List<TpsData> data)
|
|
{
|
|
try
|
|
{
|
|
foreach (var item in data)
|
|
{
|
|
await _store.SaveSubmittedAsync(item);
|
|
}
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error saving TPS data to JSON");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public async Task<DetailPenjemputanResponse> SubmitPenjemputanAsync(DetailPenjemputanRequest request)
|
|
{
|
|
try
|
|
{
|
|
if (string.IsNullOrEmpty(request.TpsName))
|
|
{
|
|
return new DetailPenjemputanResponse
|
|
{
|
|
Success = false,
|
|
Message = "Nama TPS harus diisi"
|
|
};
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(request.NamaPetugas))
|
|
{
|
|
return new DetailPenjemputanResponse
|
|
{
|
|
Success = false,
|
|
Message = "Nama petugas harus diisi"
|
|
};
|
|
}
|
|
|
|
var existingRecord = await GetRecordDetailAsync(request.NomorSpj, request.SpjDetailId, request.LokasiAngkutId, request.TpsName);
|
|
|
|
var now = DateTime.Now;
|
|
var datePart = now.ToString("yyyy-MM-dd");
|
|
var spjFolder = SanitizePathSegment(request.NomorSpj, "spj-umum");
|
|
var tpsFolder = SanitizePathSegment(request.TpsName, "tps-1");
|
|
var uploadPath = Path.Combine(_env.ContentRootPath, "uploads", "penjemputan", datePart, spjFolder, tpsFolder);
|
|
var uploadBaseUrl = $"/uploads/penjemputan/{datePart}/{spjFolder}/{tpsFolder}";
|
|
if (!Directory.Exists(uploadPath))
|
|
{
|
|
Directory.CreateDirectory(uploadPath);
|
|
}
|
|
|
|
var tpsData = existingRecord != null
|
|
? CloneRecord(existingRecord)
|
|
: new TpsData();
|
|
|
|
tpsData.NomorSpj = request.NomorSpj;
|
|
tpsData.LokasiAngkutId = request.LokasiAngkutId;
|
|
tpsData.SpjDetailId = request.SpjDetailId;
|
|
tpsData.Name = request.TpsName;
|
|
tpsData.Latitude = request.Latitude;
|
|
tpsData.Longitude = request.Longitude;
|
|
tpsData.AlamatJalan = request.AlamatJalan;
|
|
tpsData.WaktuKedatangan = request.WaktuKedatangan;
|
|
tpsData.TotalTimbangan = request.TotalTimbangan;
|
|
tpsData.TotalOrganik = request.TotalOrganik;
|
|
tpsData.TotalAnorganik = request.TotalAnorganik;
|
|
tpsData.TotalResidu = request.TotalResidu;
|
|
tpsData.NamaPetugas = request.NamaPetugas;
|
|
tpsData.IsSubmit = true;
|
|
tpsData.SubmittedAt ??= DateTime.Now;
|
|
tpsData.UpdatedAt = DateTime.Now;
|
|
|
|
if (request.FotoKedatangan != null && request.FotoKedatangan.Any())
|
|
{
|
|
tpsData.FotoKedatangan = new List<string>();
|
|
foreach (var file in request.FotoKedatangan)
|
|
{
|
|
var fileName = $"kedatangan_{Guid.NewGuid()}{Path.GetExtension(file.FileName)}";
|
|
var filePath = Path.Combine(uploadPath, fileName);
|
|
using (var stream = new FileStream(filePath, FileMode.Create))
|
|
{
|
|
await file.CopyToAsync(stream);
|
|
}
|
|
tpsData.FotoKedatangan.Add($"{uploadBaseUrl}/{fileName}");
|
|
}
|
|
tpsData.FotoKedatanganUploaded = tpsData.FotoKedatangan.Count > 0;
|
|
}
|
|
else if (existingRecord?.FotoKedatangan?.Any() == true)
|
|
{
|
|
tpsData.FotoKedatangan = new List<string>(existingRecord.FotoKedatangan);
|
|
tpsData.FotoKedatanganUploaded = existingRecord.FotoKedatanganUploaded || tpsData.FotoKedatangan.Count > 0;
|
|
}
|
|
else
|
|
{
|
|
return new DetailPenjemputanResponse
|
|
{
|
|
Success = false,
|
|
Message = "Foto kedatangan harus diupload"
|
|
};
|
|
}
|
|
|
|
if (request.FotoTimbangan != null && request.FotoTimbangan.Any() && request.BeratTimbangan != null && request.JenisSampahList != null)
|
|
{
|
|
tpsData.Timbangan = new List<TimbanganItem>();
|
|
for (int i = 0; i < request.FotoTimbangan.Count; i++)
|
|
{
|
|
var file = request.FotoTimbangan[i];
|
|
var fileName = $"timbangan_{Guid.NewGuid()}{Path.GetExtension(file.FileName)}";
|
|
var filePath = Path.Combine(uploadPath, fileName);
|
|
using (var stream = new FileStream(filePath, FileMode.Create))
|
|
{
|
|
await file.CopyToAsync(stream);
|
|
}
|
|
|
|
var jenisSampah = JenisSampah.Residu;
|
|
if (i < request.JenisSampahList.Count && Enum.TryParse<JenisSampah>(request.JenisSampahList[i], out var parsed))
|
|
{
|
|
jenisSampah = parsed;
|
|
}
|
|
|
|
tpsData.Timbangan.Add(new TimbanganItem
|
|
{
|
|
FotoFileName = $"{uploadBaseUrl}/{fileName}",
|
|
Berat = new List<decimal> { i < request.BeratTimbangan.Count ? request.BeratTimbangan[i] : 0 },
|
|
LokasiAngkut = new List<string>(),
|
|
JenisSampah = new List<JenisSampah> { jenisSampah },
|
|
IsUploaded = true,
|
|
WaktuUpload = DateTime.Now
|
|
});
|
|
}
|
|
}
|
|
else if (existingRecord?.Timbangan?.Any() == true)
|
|
{
|
|
tpsData.Timbangan = existingRecord.Timbangan;
|
|
}
|
|
else
|
|
{
|
|
return new DetailPenjemputanResponse
|
|
{
|
|
Success = false,
|
|
Message = "Foto timbangan harus diupload"
|
|
};
|
|
}
|
|
|
|
if (request.FotoPetugas != null && request.FotoPetugas.Any())
|
|
{
|
|
tpsData.FotoPetugas = new List<string>();
|
|
foreach (var file in request.FotoPetugas)
|
|
{
|
|
var fileName = $"petugas_{Guid.NewGuid()}{Path.GetExtension(file.FileName)}";
|
|
var filePath = Path.Combine(uploadPath, fileName);
|
|
using (var stream = new FileStream(filePath, FileMode.Create))
|
|
{
|
|
await file.CopyToAsync(stream);
|
|
}
|
|
tpsData.FotoPetugas.Add($"{uploadBaseUrl}/{fileName}");
|
|
}
|
|
tpsData.FotoPetugasUploaded = tpsData.FotoPetugas.Count > 0;
|
|
}
|
|
else if (existingRecord?.FotoPetugas?.Any() == true)
|
|
{
|
|
tpsData.FotoPetugas = new List<string>(existingRecord.FotoPetugas);
|
|
tpsData.FotoPetugasUploaded = existingRecord.FotoPetugasUploaded || tpsData.FotoPetugas.Count > 0;
|
|
}
|
|
else
|
|
{
|
|
return new DetailPenjemputanResponse
|
|
{
|
|
Success = false,
|
|
Message = "Foto petugas harus diupload"
|
|
};
|
|
}
|
|
|
|
await _store.SaveSubmittedAsync(tpsData);
|
|
|
|
return new DetailPenjemputanResponse
|
|
{
|
|
Success = true,
|
|
Message = "Data penjemputan berhasil disimpan",
|
|
Data = tpsData
|
|
};
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error submitting penjemputan data");
|
|
return new DetailPenjemputanResponse
|
|
{
|
|
Success = false,
|
|
Message = $"Terjadi kesalahan: {ex.Message}"
|
|
};
|
|
}
|
|
}
|
|
|
|
private Task<RecordSaveResponse> SaveRecordAsync(RecordSaveRequest request)
|
|
{
|
|
return SaveRecordInternalAsync(request);
|
|
}
|
|
|
|
private async Task<RecordSaveResponse> SaveRecordInternalAsync(RecordSaveRequest request)
|
|
{
|
|
try
|
|
{
|
|
await _store.SaveRecordAsync(request);
|
|
|
|
return new RecordSaveResponse { Success = true, Message = "Data tersimpan." };
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error saving penjemputan record");
|
|
return new RecordSaveResponse { Success = false, Message = $"Gagal menyimpan data: {ex.Message}" };
|
|
}
|
|
}
|
|
|
|
public async Task<RecordSaveResponse> SaveRecordNonTpsAsync(RecordSaveRequest request)
|
|
{
|
|
return await SaveRecordAsync(request);
|
|
}
|
|
|
|
public async Task<RecordSaveResponse> SaveRecordTpsAsync(RecordSaveRequest request)
|
|
{
|
|
return await SaveRecordAsync(request);
|
|
}
|
|
|
|
public async Task<List<TpsData>> GetSubmittedByNomorSpjAsync(string nomorSpj)
|
|
{
|
|
var normalizedNomorSpj = (nomorSpj ?? string.Empty).Trim();
|
|
var allData = await _store.GetSubmittedAsync();
|
|
return allData
|
|
.Where(item => string.Equals((item.NomorSpj ?? string.Empty).Trim(), normalizedNomorSpj, StringComparison.OrdinalIgnoreCase))
|
|
.OrderBy(item => item.SubmittedAt ?? DateTime.MinValue)
|
|
.ToList();
|
|
}
|
|
|
|
public async Task<TpsData?> GetSubmittedDetailAsync(string nomorSpj, string? spjDetailId = null, string? lokasiAngkutId = null, string? namaTps = null)
|
|
{
|
|
return await _store.GetSubmittedDetailAsync(nomorSpj, spjDetailId, lokasiAngkutId, namaTps);
|
|
}
|
|
|
|
public async Task<TpsData?> GetRecordDetailAsync(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 _store.GetByNomorSpjAsync(normalizedNomorSpj);
|
|
return allData
|
|
.OrderByDescending(item => item.UpdatedAt)
|
|
.FirstOrDefault(item =>
|
|
(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)));
|
|
}
|
|
|
|
private static TpsData CloneRecord(TpsData source)
|
|
{
|
|
return new TpsData
|
|
{
|
|
NomorSpj = source.NomorSpj,
|
|
LokasiAngkutId = source.LokasiAngkutId,
|
|
SpjDetailId = source.SpjDetailId,
|
|
Name = source.Name,
|
|
Index = source.Index,
|
|
Latitude = source.Latitude,
|
|
Longitude = source.Longitude,
|
|
AlamatJalan = source.AlamatJalan,
|
|
WaktuKedatangan = source.WaktuKedatangan,
|
|
FotoKedatangan = new List<string>(source.FotoKedatangan ?? new List<string>()),
|
|
FotoKedatanganUploaded = source.FotoKedatanganUploaded,
|
|
Timbangan = (source.Timbangan ?? new List<TimbanganItem>()).Select(item => new TimbanganItem
|
|
{
|
|
FotoFileName = item.FotoFileName,
|
|
Berat = new List<decimal>(item.Berat ?? new List<decimal>()),
|
|
LokasiAngkut = new List<string>(item.LokasiAngkut ?? new List<string>()),
|
|
JenisSampah = new List<JenisSampah>(item.JenisSampah ?? new List<JenisSampah>()),
|
|
IsUploaded = item.IsUploaded,
|
|
WaktuUpload = item.WaktuUpload
|
|
}).ToList(),
|
|
TotalOrganik = source.TotalOrganik,
|
|
TotalAnorganik = source.TotalAnorganik,
|
|
TotalResidu = source.TotalResidu,
|
|
TotalTimbangan = source.TotalTimbangan,
|
|
FotoPetugas = new List<string>(source.FotoPetugas ?? new List<string>()),
|
|
FotoPetugasUploaded = source.FotoPetugasUploaded,
|
|
NamaPetugas = source.NamaPetugas,
|
|
IsSubmit = source.IsSubmit,
|
|
UpdatedAt = source.UpdatedAt,
|
|
SubmittedAt = source.SubmittedAt,
|
|
};
|
|
}
|
|
|
|
public async Task<OcrTimbanganResponse> ProcessOcrTimbanganAsync(IFormFile foto)
|
|
{
|
|
try
|
|
{
|
|
await Task.Delay(500);
|
|
|
|
return new OcrTimbanganResponse
|
|
{
|
|
Success = true,
|
|
Weight = "54.50",
|
|
Raw = "54.50 kg",
|
|
Message = "OCR processed successfully (mock)"
|
|
};
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error processing OCR timbangan");
|
|
return new OcrTimbanganResponse
|
|
{
|
|
Success = false,
|
|
Message = $"Terjadi kesalahan: {ex.Message}"
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|