using Microsoft.EntityFrameworkCore; using Tiku.Application.Assets; using Tiku.Application.Catalog; using Tiku.Domain.Content; namespace Tiku.Infrastructure.Assets; internal sealed class AssetImportJobQueryService(AssetManagementDependencies dependencies) : AssetManagementServiceBase(dependencies), IAssetImportJobQueryService { public async Task> GetImportJobsAsync( AssetManagementActor actor, ImportJobFilter filter, CancellationToken cancellationToken = default) { var query = questionBankPersistence.ContentImportJobs .AsNoTracking() .Where(job => job.TenantId == actor.TenantId); if (!string.IsNullOrWhiteSpace(filter.Status) && Enum.TryParse(filter.Status, true, out var status)) query = query.Where(job => job.Status == status); if (!string.IsNullOrWhiteSpace(filter.ImportType) && Enum.TryParse(filter.ImportType, true, out var importType)) query = query.Where(job => job.ImportType == importType); if (!string.IsNullOrWhiteSpace(filter.SourceFormat) && Enum.TryParse(filter.SourceFormat, true, out var sourceFormat)) query = query.Where(job => job.SourceFormat == sourceFormat); var items = await query .OrderByDescending(job => job.CreatedAt) .Take(ResolveLimit(filter.Limit)) .Select(job => ToJobItem(job)) .ToArrayAsync(cancellationToken); return new CatalogList(items); } public async Task GetImportJobAsync( AssetManagementActor actor, Guid jobId, CancellationToken cancellationToken = default) { var job = await questionBankPersistence.ContentImportJobs .AsNoTracking() .Where(item => item.TenantId == actor.TenantId && item.Id == jobId) .Select(item => ToJobItem(item)) .SingleOrDefaultAsync(cancellationToken); if (job is null) throw new AssetManagementException("Import job was not found.", "import_job_not_found"); var items = await questionBankPersistence.ContentImportItems .AsNoTracking() .Where(item => item.TenantId == actor.TenantId && item.JobId == jobId) .OrderBy(item => item.RowNo) .Take(MaxLimit) .Select(item => new ContentImportItemModel( item.Id, item.JobId, item.RowNo, item.ExternalId, item.Status, item.TargetType, item.TargetId, item.SourcePayload, item.NormalizedPayload, item.ContentHash, item.IssuesCount)) .ToArrayAsync(cancellationToken); var issues = await questionBankPersistence.ContentImportIssues .AsNoTracking() .Where(issue => issue.TenantId == actor.TenantId && issue.JobId == jobId) .OrderBy(issue => issue.RowNo) .ThenBy(issue => issue.CreatedAt) .Take(MaxLimit) .Select(issue => new ContentImportIssueModel( issue.Id, issue.JobId, issue.ItemId, issue.RowNo, issue.Severity, issue.Code, issue.FieldPath, issue.Message, issue.Details)) .ToArrayAsync(cancellationToken); return new ContentImportJobDetail(job, items, issues); } }