using Application.Abstractions.Data;
using Domain.Entities.Stocks;
using Microsoft.EntityFrameworkCore;
namespace Infrastructure.StockData;
///
/// SEIBro 수집 마커(SeibroFetchMarker rolling · SeibroSweptDate date-sweep) 조회/upsert 헬퍼 — 리뷰 결함 #1 해소 배선의 공용 경로.
/// 각 SyncService 가 완료 판정을 "데이터 행 존재"에서 "조회 시도(0행 포함) 마커 존재"로 바꾸는 데 쓴다.
/// • rolling: LoadFetchMarkers 로 (키→마지막조회시각) 사전을 만들어 rolling 대상을 정렬(미조회 최우선→오래된 순), fetch 후 TouchFetchMarker.
/// • date-sweep: IsSwept 로 "데이터 존재 OR 마커 존재" 판정(기존 적재분 재조회도 회피), fetch(0행 포함) 후 MarkSwept.
/// EntityKey 는 문자열 — 복합키는 배치가 구분자로 결합해 전달한다. JobKey 는 job 상수.
///
public static class SeibroMarkers
{
/// 해당 job 의 (EntityKey → LastCheckedAt) 사전 로드 — rolling 대상 정렬 기준.
public static async Task> LoadFetchMarkersAsync(IAppDbContext db, string jobKey, CancellationToken ct)
{
return await db.SeibroFetchMarker.AsNoTracking().Where(c => c.JobKey == jobKey).ToDictionaryAsync(c => c.EntityKey, c => c.LastCheckedAt, ct);
}
/// 조회 시도(0행 포함) 후 rolling 마커 upsert — 있으면 Touch, 없으면 Create. 즉시 SaveChanges.
public static async Task TouchFetchMarkerAsync(IAppDbContext db, string jobKey, string entityKey, CancellationToken ct)
{
var marker = await db.SeibroFetchMarker.FirstOrDefaultAsync(c => c.JobKey == jobKey && c.EntityKey == entityKey, ct);
if (marker is null)
{
await db.SeibroFetchMarker.AddAsync(SeibroFetchMarker.Create(jobKey, entityKey), ct);
}
else
{
marker.Touch();
}
await db.SaveChangesAsync(ct);
}
/// 해당 (job, 날짜[, 축])이 이미 조회 완료(0행 포함)로 기록됐는지.
public static async Task IsSweptAsync(IAppDbContext db, string jobKey, DateOnly date, string? discriminator, CancellationToken ct)
{
var disc = string.IsNullOrWhiteSpace(discriminator) ? string.Empty : discriminator.Trim();
return await db.SeibroSweptDate.AsNoTracking().AnyAsync(c => c.JobKey == jobKey && c.Date == date && c.Discriminator == disc, ct);
}
/// 조회 완료(0행 포함) 후 날짜 스윕 마커 기록 — 이미 있으면 no-op. 즉시 SaveChanges.
public static async Task MarkSweptAsync(IAppDbContext db, string jobKey, DateOnly date, string? discriminator, CancellationToken ct)
{
var disc = string.IsNullOrWhiteSpace(discriminator) ? string.Empty : discriminator.Trim();
if (await db.SeibroSweptDate.AnyAsync(c => c.JobKey == jobKey && c.Date == date && c.Discriminator == disc, ct))
{
return;
}
await db.SeibroSweptDate.AddAsync(SeibroSweptDate.Create(jobKey, date, disc), ct);
await db.SaveChangesAsync(ct);
}
}