feat(learning): harden access and practice sessions
This commit is contained in:
@@ -3,12 +3,15 @@ using Tiku.Application.Catalog;
|
||||
using Tiku.Application.Content;
|
||||
using Tiku.Domain.Common;
|
||||
using Tiku.Domain.Content;
|
||||
using Tiku.Domain.Learning;
|
||||
using Tiku.Domain.QuestionBanks;
|
||||
using Tiku.Infrastructure.Persistence;
|
||||
|
||||
namespace Tiku.Infrastructure.Content;
|
||||
|
||||
public sealed class ContentNavigationQueryService(IQuestionBankPersistence dbContext) : IContentNavigationQueryService
|
||||
public sealed class ContentNavigationQueryService(
|
||||
IQuestionBankPersistence dbContext,
|
||||
ILearningPersistence learningPersistence) : IContentNavigationQueryService
|
||||
{
|
||||
private const int DefaultLimit = 100;
|
||||
private const int MaxLimit = 1000;
|
||||
@@ -22,6 +25,9 @@ public sealed class ContentNavigationQueryService(IQuestionBankPersistence dbCon
|
||||
.Where(entry =>
|
||||
entry.TenantId == filter.TenantId &&
|
||||
entry.IsActive);
|
||||
if (filter.AllowedRegionIds is not null)
|
||||
query = query.Where(entry => entry.RegionId == null ||
|
||||
filter.AllowedRegionIds.Contains(entry.RegionId.Value));
|
||||
|
||||
if (!filter.IncludeHidden) query = query.Where(entry => entry.Visibility != ContentVisibility.Hidden);
|
||||
|
||||
@@ -67,6 +73,9 @@ public sealed class ContentNavigationQueryService(IQuestionBankPersistence dbCon
|
||||
.Where(node =>
|
||||
node.TenantId == filter.TenantId &&
|
||||
node.EntryId == filter.EntryId.Value);
|
||||
if (filter.AllowedRegionIds is not null)
|
||||
query = query.Where(node => node.RegionId == null ||
|
||||
filter.AllowedRegionIds.Contains(node.RegionId.Value));
|
||||
|
||||
if (!filter.IncludeInactive) query = query.Where(node => node.IsActive);
|
||||
|
||||
@@ -121,6 +130,13 @@ public sealed class ContentNavigationQueryService(IQuestionBankPersistence dbCon
|
||||
.Where(collection =>
|
||||
collection.TenantId == filter.TenantId &&
|
||||
collection.Status == ContentStatus.Active);
|
||||
var allowedSliceIds = filter.AllowedContentSliceIds?.ToArray() ?? [];
|
||||
query = query.Where(collection => learningPersistence.ContentSlices.Any(slice =>
|
||||
slice.TenantId == filter.TenantId &&
|
||||
allowedSliceIds.Contains(slice.Id) &&
|
||||
slice.Status == ContentSliceStatus.Active &&
|
||||
slice.ResourceType == LearningContentResourceType.Collection &&
|
||||
slice.ResourceId == collection.Id));
|
||||
|
||||
if (filter.RegionId.HasValue)
|
||||
query = query.Where(collection =>
|
||||
@@ -173,6 +189,13 @@ public sealed class ContentNavigationQueryService(IQuestionBankPersistence dbCon
|
||||
.Where(blueprint =>
|
||||
blueprint.TenantId == filter.TenantId &&
|
||||
blueprint.Status == ContentStatus.Active);
|
||||
var allowedSliceIds = filter.AllowedContentSliceIds?.ToArray() ?? [];
|
||||
query = query.Where(blueprint => learningPersistence.ContentSlices.Any(slice =>
|
||||
slice.TenantId == filter.TenantId &&
|
||||
allowedSliceIds.Contains(slice.Id) &&
|
||||
slice.Status == ContentSliceStatus.Active &&
|
||||
slice.ResourceType == LearningContentResourceType.Blueprint &&
|
||||
slice.ResourceId == blueprint.Id));
|
||||
|
||||
if (filter.RegionId.HasValue)
|
||||
query = query.Where(blueprint => blueprint.RegionId == filter.RegionId.Value || blueprint.RegionId == null);
|
||||
@@ -222,20 +245,25 @@ public sealed class ContentNavigationQueryService(IQuestionBankPersistence dbCon
|
||||
{
|
||||
if (!filter.CollectionId.HasValue) throw new RequiredFieldException("collectionId is required.");
|
||||
|
||||
var allowedSliceIds = filter.AllowedContentSliceIds?.ToArray() ?? [];
|
||||
var collectionExists = await dbContext.QuestionCollections
|
||||
.AsNoTracking()
|
||||
.AnyAsync(
|
||||
collection =>
|
||||
collection.TenantId == filter.TenantId &&
|
||||
collection.Id == filter.CollectionId.Value &&
|
||||
collection.Status == ContentStatus.Active,
|
||||
collection.Status == ContentStatus.Active &&
|
||||
learningPersistence.ContentSlices.Any(slice =>
|
||||
slice.TenantId == filter.TenantId &&
|
||||
allowedSliceIds.Contains(slice.Id) &&
|
||||
slice.Status == ContentSliceStatus.Active &&
|
||||
slice.ResourceType == LearningContentResourceType.Collection &&
|
||||
slice.ResourceId == collection.Id),
|
||||
cancellationToken);
|
||||
|
||||
if (!collectionExists) throw new ContentNavigationNotFoundException("Question collection was not found.");
|
||||
|
||||
var emptyOptions = JsonDefaults.Array();
|
||||
var emptyCorrectOptionIndices = JsonDefaults.Array();
|
||||
var emptySubQuestions = JsonDefaults.Array();
|
||||
var query =
|
||||
from item in dbContext.QuestionCollectionItems.AsNoTracking()
|
||||
join question in dbContext.Questions.AsNoTracking()
|
||||
@@ -271,11 +299,6 @@ public sealed class ContentNavigationQueryService(IQuestionBankPersistence dbCon
|
||||
version == null ? null : version.Id,
|
||||
version == null ? null : version.Content,
|
||||
version == null ? emptyOptions : version.Options,
|
||||
version == null ? null : version.CorrectOptionIndex,
|
||||
version == null ? emptyCorrectOptionIndices : version.CorrectOptionIndices,
|
||||
version == null ? null : version.AnswerText,
|
||||
version == null ? null : version.Explanation,
|
||||
version == null ? emptySubQuestions : version.SubQuestions,
|
||||
version == null ? null : version.CodeLang,
|
||||
version == null ? null : version.CodeTemplate);
|
||||
|
||||
@@ -327,4 +350,4 @@ public sealed class ContentNavigationQueryService(IQuestionBankPersistence dbCon
|
||||
: value.Replace("_", string.Empty, StringComparison.Ordinal)
|
||||
.Replace("-", string.Empty, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user