perf: otimiza importacao e exportacao de linhas
This commit is contained in:
parent
b25cdaa507
commit
b5b36276cb
|
|
@ -144,7 +144,13 @@ namespace line_gestao_api.Controllers
|
||||||
if (!string.IsNullOrWhiteSpace(search))
|
if (!string.IsNullOrWhiteSpace(search))
|
||||||
{
|
{
|
||||||
var s = search.Trim();
|
var s = search.Trim();
|
||||||
reservaRows = reservaRows.Where(x => EF.Functions.ILike(x.Cliente ?? "", $"%{s}%"));
|
reservaRows = reservaRows.Where(x =>
|
||||||
|
EF.Functions.ILike(x.Linha ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Chip ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Cliente ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Usuario ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Conta ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Status ?? "", $"%{s}%"));
|
||||||
}
|
}
|
||||||
|
|
||||||
groupedQuery = reservaRows
|
groupedQuery = reservaRows
|
||||||
|
|
@ -166,7 +172,13 @@ namespace line_gestao_api.Controllers
|
||||||
if (!string.IsNullOrWhiteSpace(search))
|
if (!string.IsNullOrWhiteSpace(search))
|
||||||
{
|
{
|
||||||
var s = search.Trim();
|
var s = search.Trim();
|
||||||
query = query.Where(x => EF.Functions.ILike(x.Cliente ?? "", $"%{s}%"));
|
query = query.Where(x =>
|
||||||
|
EF.Functions.ILike(x.Linha ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Chip ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Cliente ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Usuario ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Conta ?? "", $"%{s}%") ||
|
||||||
|
EF.Functions.ILike(x.Status ?? "", $"%{s}%"));
|
||||||
}
|
}
|
||||||
|
|
||||||
groupedQuery = query.GroupBy(x => x.Cliente)
|
groupedQuery = query.GroupBy(x => x.Cliente)
|
||||||
|
|
@ -622,6 +634,122 @@ namespace line_gestao_api.Controllers
|
||||||
return Ok(ToDetailDto(x, vigencia));
|
return Ok(ToDetailDto(x, vigencia));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("export-details")]
|
||||||
|
public async Task<ActionResult<List<MobileLineDetailDto>>> GetExportDetails([FromBody] MobileLineExportDetailsRequestDto? req)
|
||||||
|
{
|
||||||
|
var orderedIds = (req?.Ids ?? new List<Guid>())
|
||||||
|
.Where(id => id != Guid.Empty)
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (orderedIds.Count == 0)
|
||||||
|
{
|
||||||
|
return Ok(new List<MobileLineDetailDto>());
|
||||||
|
}
|
||||||
|
|
||||||
|
var lines = await _db.MobileLines
|
||||||
|
.AsNoTracking()
|
||||||
|
.Include(x => x.Setor)
|
||||||
|
.Include(x => x.Aparelho)
|
||||||
|
.Where(x => orderedIds.Contains(x.Id))
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
if (lines.Count == 0)
|
||||||
|
{
|
||||||
|
return Ok(new List<MobileLineDetailDto>());
|
||||||
|
}
|
||||||
|
|
||||||
|
var vigenciaByLinha = new Dictionary<string, VigenciaLine>(StringComparer.Ordinal);
|
||||||
|
var vigenciaByItem = new Dictionary<int, VigenciaLine>();
|
||||||
|
|
||||||
|
var lineKeys = lines
|
||||||
|
.Select(x => NullIfEmptyDigits(x.Linha))
|
||||||
|
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||||
|
.Distinct(StringComparer.Ordinal)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var itemKeys = lines
|
||||||
|
.Where(x => x.Item > 0)
|
||||||
|
.Select(x => x.Item)
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (lineKeys.Count > 0 || itemKeys.Count > 0)
|
||||||
|
{
|
||||||
|
IQueryable<VigenciaLine> vigenciaQuery = _db.VigenciaLines.AsNoTracking();
|
||||||
|
|
||||||
|
if (lineKeys.Count > 0 && itemKeys.Count > 0)
|
||||||
|
{
|
||||||
|
vigenciaQuery = vigenciaQuery.Where(v => lineKeys.Contains(v.Linha!) || itemKeys.Contains(v.Item));
|
||||||
|
}
|
||||||
|
else if (lineKeys.Count > 0)
|
||||||
|
{
|
||||||
|
vigenciaQuery = vigenciaQuery.Where(v => lineKeys.Contains(v.Linha!));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vigenciaQuery = vigenciaQuery.Where(v => itemKeys.Contains(v.Item));
|
||||||
|
}
|
||||||
|
|
||||||
|
var vigencias = await vigenciaQuery.ToListAsync();
|
||||||
|
|
||||||
|
foreach (var vigencia in vigencias
|
||||||
|
.Where(v => !string.IsNullOrWhiteSpace(v.Linha))
|
||||||
|
.GroupBy(v => NullIfEmptyDigits(v.Linha)!, StringComparer.Ordinal)
|
||||||
|
.Select(g => g
|
||||||
|
.OrderByDescending(x => x.UpdatedAt)
|
||||||
|
.ThenByDescending(x => x.CreatedAt)
|
||||||
|
.First()))
|
||||||
|
{
|
||||||
|
var key = NullIfEmptyDigits(vigencia.Linha);
|
||||||
|
if (!string.IsNullOrWhiteSpace(key))
|
||||||
|
{
|
||||||
|
vigenciaByLinha[key] = vigencia;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var vigencia in vigencias
|
||||||
|
.Where(v => string.IsNullOrWhiteSpace(v.Linha))
|
||||||
|
.GroupBy(v => v.Item)
|
||||||
|
.Select(g => g
|
||||||
|
.OrderByDescending(x => x.UpdatedAt)
|
||||||
|
.ThenByDescending(x => x.CreatedAt)
|
||||||
|
.First()))
|
||||||
|
{
|
||||||
|
if (vigencia.Item > 0)
|
||||||
|
{
|
||||||
|
vigenciaByItem[vigencia.Item] = vigencia;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var linesById = lines.ToDictionary(x => x.Id);
|
||||||
|
var result = new List<MobileLineDetailDto>(orderedIds.Count);
|
||||||
|
|
||||||
|
foreach (var id in orderedIds)
|
||||||
|
{
|
||||||
|
if (!linesById.TryGetValue(id, out var line))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
VigenciaLine? vigencia = null;
|
||||||
|
var lineKey = NullIfEmptyDigits(line.Linha);
|
||||||
|
if (!string.IsNullOrWhiteSpace(lineKey))
|
||||||
|
{
|
||||||
|
vigenciaByLinha.TryGetValue(lineKey, out vigencia);
|
||||||
|
}
|
||||||
|
else if (line.Item > 0)
|
||||||
|
{
|
||||||
|
vigenciaByItem.TryGetValue(line.Item, out vigencia);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Add(ToDetailDto(line, vigencia));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
// ==========================================================
|
// ==========================================================
|
||||||
// ✅ 5. CREATE
|
// ✅ 5. CREATE
|
||||||
// ==========================================================
|
// ==========================================================
|
||||||
|
|
@ -1738,7 +1866,7 @@ namespace line_gestao_api.Controllers
|
||||||
await _db.MuregLines.ExecuteDeleteAsync();
|
await _db.MuregLines.ExecuteDeleteAsync();
|
||||||
await _db.MobileLines.ExecuteDeleteAsync();
|
await _db.MobileLines.ExecuteDeleteAsync();
|
||||||
|
|
||||||
var buffer = new List<MobileLine>(600);
|
var buffer = new List<MobileLine>(1000);
|
||||||
var imported = 0;
|
var imported = 0;
|
||||||
var maxItemFromGeral = 0;
|
var maxItemFromGeral = 0;
|
||||||
|
|
||||||
|
|
@ -1805,11 +1933,12 @@ namespace line_gestao_api.Controllers
|
||||||
imported++;
|
imported++;
|
||||||
if (item > maxItemFromGeral) maxItemFromGeral = item;
|
if (item > maxItemFromGeral) maxItemFromGeral = item;
|
||||||
|
|
||||||
if (buffer.Count >= 500)
|
if (buffer.Count >= 1000)
|
||||||
{
|
{
|
||||||
await _db.MobileLines.AddRangeAsync(buffer);
|
await _db.MobileLines.AddRangeAsync(buffer);
|
||||||
await _db.SaveChangesAsync();
|
await _db.SaveChangesAsync();
|
||||||
buffer.Clear();
|
buffer.Clear();
|
||||||
|
ClearImportTracking();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1817,6 +1946,8 @@ namespace line_gestao_api.Controllers
|
||||||
{
|
{
|
||||||
await _db.MobileLines.AddRangeAsync(buffer);
|
await _db.MobileLines.AddRangeAsync(buffer);
|
||||||
await _db.SaveChangesAsync();
|
await _db.SaveChangesAsync();
|
||||||
|
buffer.Clear();
|
||||||
|
ClearImportTracking();
|
||||||
}
|
}
|
||||||
|
|
||||||
auditSession = _spreadsheetImportAuditService.StartRun(
|
auditSession = _spreadsheetImportAuditService.StartRun(
|
||||||
|
|
@ -1828,40 +1959,48 @@ namespace line_gestao_api.Controllers
|
||||||
// ✅ IMPORTA MUREG (ALTERADO: NÃO ESTOURA ERRO SE LINHANOVA JÁ EXISTIR)
|
// ✅ IMPORTA MUREG (ALTERADO: NÃO ESTOURA ERRO SE LINHANOVA JÁ EXISTIR)
|
||||||
// =========================
|
// =========================
|
||||||
await ImportMuregFromWorkbook(wb);
|
await ImportMuregFromWorkbook(wb);
|
||||||
|
ClearImportTracking();
|
||||||
|
|
||||||
// =========================
|
// =========================
|
||||||
// ✅ IMPORTA FATURAMENTO PF/PJ
|
// ✅ IMPORTA FATURAMENTO PF/PJ
|
||||||
// =========================
|
// =========================
|
||||||
await ImportBillingFromWorkbook(wb);
|
await ImportBillingFromWorkbook(wb);
|
||||||
|
ClearImportTracking();
|
||||||
|
|
||||||
// =========================
|
// =========================
|
||||||
// ✅ IMPORTA DADOS DOS USUÁRIOS (UserDatas)
|
// ✅ IMPORTA DADOS DOS USUÁRIOS (UserDatas)
|
||||||
// =========================
|
// =========================
|
||||||
var userDataImported = await ImportUserDatasFromWorkbook(wb);
|
var userDataImported = await ImportUserDatasFromWorkbook(wb);
|
||||||
|
ClearImportTracking();
|
||||||
if (userDataImported)
|
if (userDataImported)
|
||||||
{
|
{
|
||||||
await RepairReservaClientAssignmentsAsync();
|
await RepairReservaClientAssignmentsAsync();
|
||||||
|
ClearImportTracking();
|
||||||
}
|
}
|
||||||
|
|
||||||
// =========================
|
// =========================
|
||||||
// ✅ IMPORTA VIGÊNCIA
|
// ✅ IMPORTA VIGÊNCIA
|
||||||
// =========================
|
// =========================
|
||||||
await ImportVigenciaFromWorkbook(wb);
|
await ImportVigenciaFromWorkbook(wb);
|
||||||
|
ClearImportTracking();
|
||||||
|
|
||||||
// =========================
|
// =========================
|
||||||
// ✅ IMPORTA TROCA DE NÚMERO
|
// ✅ IMPORTA TROCA DE NÚMERO
|
||||||
// =========================
|
// =========================
|
||||||
await ImportTrocaNumeroFromWorkbook(wb);
|
await ImportTrocaNumeroFromWorkbook(wb);
|
||||||
|
ClearImportTracking();
|
||||||
|
|
||||||
// =========================
|
// =========================
|
||||||
// ✅ IMPORTA CHIPS VIRGENS
|
// ✅ IMPORTA CHIPS VIRGENS
|
||||||
// =========================
|
// =========================
|
||||||
await ImportChipsVirgensFromWorkbook(wb);
|
await ImportChipsVirgensFromWorkbook(wb);
|
||||||
|
ClearImportTracking();
|
||||||
|
|
||||||
// =========================
|
// =========================
|
||||||
// ✅ IMPORTA CONTROLE DE RECEBIDOS
|
// ✅ IMPORTA CONTROLE DE RECEBIDOS
|
||||||
// =========================
|
// =========================
|
||||||
await ImportControleRecebidosFromWorkbook(wb);
|
await ImportControleRecebidosFromWorkbook(wb);
|
||||||
|
ClearImportTracking();
|
||||||
|
|
||||||
// =========================
|
// =========================
|
||||||
// ✅ IMPORTA RESUMO
|
// ✅ IMPORTA RESUMO
|
||||||
|
|
@ -1872,11 +2011,13 @@ namespace line_gestao_api.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
await ImportResumoFromWorkbook(wb, auditSession);
|
await ImportResumoFromWorkbook(wb, auditSession);
|
||||||
|
ClearImportTracking();
|
||||||
|
|
||||||
// =========================
|
// =========================
|
||||||
// ✅ IMPORTA PARCELAMENTOS
|
// ✅ IMPORTA PARCELAMENTOS
|
||||||
// =========================
|
// =========================
|
||||||
var parcelamentosSummary = await _parcelamentosImportService.ImportFromWorkbookAsync(wb, replaceAll: true);
|
var parcelamentosSummary = await _parcelamentosImportService.ImportFromWorkbookAsync(wb, replaceAll: true);
|
||||||
|
ClearImportTracking();
|
||||||
if (auditSession != null)
|
if (auditSession != null)
|
||||||
{
|
{
|
||||||
await _spreadsheetImportAuditService.SaveRunAsync(auditSession);
|
await _spreadsheetImportAuditService.SaveRunAsync(auditSession);
|
||||||
|
|
@ -2122,17 +2263,15 @@ namespace line_gestao_api.Controllers
|
||||||
// limpa MUREG antes (idempotente)
|
// limpa MUREG antes (idempotente)
|
||||||
await _db.MuregLines.ExecuteDeleteAsync();
|
await _db.MuregLines.ExecuteDeleteAsync();
|
||||||
|
|
||||||
// ✅ dicionários para resolver MobileLineId por Linha/Chip
|
// Carrega uma vez para evitar consultas por linha durante a importacao da MUREG.
|
||||||
var mobilePairs = await _db.MobileLines
|
var mobileCache = await _db.MobileLines
|
||||||
.AsNoTracking()
|
.ToDictionaryAsync(x => x.Id);
|
||||||
.Select(x => new { x.Id, x.Item, x.Linha, x.Chip })
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
var mobileByLinha = new Dictionary<string, Guid>(StringComparer.Ordinal);
|
var mobileByLinha = new Dictionary<string, Guid>(StringComparer.Ordinal);
|
||||||
var mobileByChip = new Dictionary<string, Guid>(StringComparer.Ordinal);
|
var mobileByChip = new Dictionary<string, Guid>(StringComparer.Ordinal);
|
||||||
var mobileByItem = new Dictionary<int, Guid>();
|
var mobileByItem = new Dictionary<int, Guid>();
|
||||||
|
|
||||||
foreach (var m in mobilePairs)
|
foreach (var m in mobileCache.Values)
|
||||||
{
|
{
|
||||||
if (m.Item > 0 && !mobileByItem.ContainsKey(m.Item))
|
if (m.Item > 0 && !mobileByItem.ContainsKey(m.Item))
|
||||||
mobileByItem[m.Item] = m.Id;
|
mobileByItem[m.Item] = m.Id;
|
||||||
|
|
@ -2152,10 +2291,7 @@ namespace line_gestao_api.Controllers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ cache de entidades tracked para atualizar a GERAL sem consultar toda hora
|
var buffer = new List<MuregLine>(1000);
|
||||||
var mobileCache = new Dictionary<Guid, MobileLine>();
|
|
||||||
|
|
||||||
var buffer = new List<MuregLine>(600);
|
|
||||||
var lastRow = wsM.LastRowUsed()?.RowNumber() ?? startRow;
|
var lastRow = wsM.LastRowUsed()?.RowNumber() ?? startRow;
|
||||||
|
|
||||||
for (int r = startRow; r <= lastRow; r++)
|
for (int r = startRow; r <= lastRow; r++)
|
||||||
|
|
@ -2204,12 +2340,7 @@ namespace line_gestao_api.Controllers
|
||||||
string? linhaAntigaSnapshot = linhaAntiga;
|
string? linhaAntigaSnapshot = linhaAntiga;
|
||||||
if (string.IsNullOrWhiteSpace(linhaAntigaSnapshot))
|
if (string.IsNullOrWhiteSpace(linhaAntigaSnapshot))
|
||||||
{
|
{
|
||||||
if (!mobileCache.TryGetValue(mobileLineId, out var mobTmp))
|
mobileCache.TryGetValue(mobileLineId, out var mobTmp);
|
||||||
{
|
|
||||||
mobTmp = await _db.MobileLines.FirstOrDefaultAsync(x => x.Id == mobileLineId);
|
|
||||||
if (mobTmp != null) mobileCache[mobileLineId] = mobTmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
linhaAntigaSnapshot = mobTmp?.Linha;
|
linhaAntigaSnapshot = mobTmp?.Linha;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2241,12 +2372,7 @@ namespace line_gestao_api.Controllers
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// carrega entity tracked (cache) e atualiza
|
mobileCache.TryGetValue(mobileLineId, out var mobile);
|
||||||
if (!mobileCache.TryGetValue(mobileLineId, out var mobile))
|
|
||||||
{
|
|
||||||
mobile = await _db.MobileLines.FirstOrDefaultAsync(x => x.Id == mobileLineId);
|
|
||||||
if (mobile != null) mobileCache[mobileLineId] = mobile;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mobile != null)
|
if (mobile != null)
|
||||||
{
|
{
|
||||||
|
|
@ -2277,7 +2403,7 @@ namespace line_gestao_api.Controllers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffer.Count >= 500)
|
if (buffer.Count >= 1000)
|
||||||
{
|
{
|
||||||
await _db.MuregLines.AddRangeAsync(buffer);
|
await _db.MuregLines.AddRangeAsync(buffer);
|
||||||
await _db.SaveChangesAsync();
|
await _db.SaveChangesAsync();
|
||||||
|
|
@ -2289,6 +2415,7 @@ namespace line_gestao_api.Controllers
|
||||||
{
|
{
|
||||||
await _db.MuregLines.AddRangeAsync(buffer);
|
await _db.MuregLines.AddRangeAsync(buffer);
|
||||||
await _db.SaveChangesAsync();
|
await _db.SaveChangesAsync();
|
||||||
|
buffer.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5244,9 +5371,16 @@ namespace line_gestao_api.Controllers
|
||||||
DtEfetivacaoServico = vigencia?.DtEfetivacaoServico,
|
DtEfetivacaoServico = vigencia?.DtEfetivacaoServico,
|
||||||
DtTerminoFidelizacao = vigencia?.DtTerminoFidelizacao,
|
DtTerminoFidelizacao = vigencia?.DtTerminoFidelizacao,
|
||||||
VencConta = x.VencConta,
|
VencConta = x.VencConta,
|
||||||
TipoDeChip = x.TipoDeChip
|
TipoDeChip = x.TipoDeChip,
|
||||||
|
CreatedAt = x.CreatedAt,
|
||||||
|
UpdatedAt = x.UpdatedAt
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private void ClearImportTracking()
|
||||||
|
{
|
||||||
|
_db.ChangeTracker.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
private static void ApplyReservaRule(MobileLine x)
|
private static void ApplyReservaRule(MobileLine x)
|
||||||
{
|
{
|
||||||
if (IsReservaValue(x.Cliente)) x.Cliente = "RESERVA";
|
if (IsReservaValue(x.Cliente)) x.Cliente = "RESERVA";
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,8 @@
|
||||||
public DateTime? DtTerminoFidelizacao { get; set; }
|
public DateTime? DtTerminoFidelizacao { get; set; }
|
||||||
public string? VencConta { get; set; }
|
public string? VencConta { get; set; }
|
||||||
public string? TipoDeChip { get; set; }
|
public string? TipoDeChip { get; set; }
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
public DateTime UpdatedAt { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ UPDATE REQUEST (SEM Id)
|
// ✅ UPDATE REQUEST (SEM Id)
|
||||||
|
|
@ -153,4 +155,9 @@
|
||||||
public string? Skil { get; set; }
|
public string? Skil { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class MobileLineExportDetailsRequestDto
|
||||||
|
{
|
||||||
|
public List<Guid> Ids { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue