Files
tiku-backend.net/Tiku.Infrastructure/Learning/QuestionReview/QuestionReviewService.cs

158 lines
6.0 KiB
C#

using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Tiku.Application.Learning;
using Tiku.Application.QuestionBanks;
using Tiku.Domain.Content;
using Tiku.Domain.Learning;
namespace Tiku.Infrastructure.Learning;
internal sealed class QuestionReviewService(LearningServiceDependencies dependencies)
: LearningActivityServiceBase(dependencies), IQuestionReviewService
{
public async Task<LearningList<FavoriteQuestionItem>> GetFavoriteQuestionsAsync(
LearningActor actor,
LearningLimitFilter filter,
CancellationToken cancellationToken = default)
{
var items = await dbContext.FavoriteQuestions
.AsNoTracking()
.Where(item =>
item.TenantId == actor.TenantId &&
item.UserId == actor.UserId)
.OrderByDescending(item => item.CreatedAt)
.Take(ResolveLimit(filter.Limit))
.Select(item => new FavoriteQuestionItem(
item.QuestionReferenceId,
new QuestionLocator(
item.QuestionOwnerTenantId == item.TenantId ? QuestionSource.Tenant : QuestionSource.Platform,
item.QuestionId),
item.CreatedAt))
.ToArrayAsync(cancellationToken);
return new LearningList<FavoriteQuestionItem>(items);
}
public async Task<LearningActionResult> ToggleFavoriteQuestionAsync(
LearningActor actor,
QuestionActionCommand command,
CancellationToken cancellationToken = default)
{
var reference = await questionReferenceService.ResolveAsync(
actor.TenantId,
actor.UserId,
command.Locator,
cancellationToken);
var favorite = command.Favorite ?? true;
var item = await dbContext.FavoriteQuestions.FindAsync(
[actor.TenantId, actor.UserId, reference.Id],
cancellationToken);
if (favorite)
{
if (item is null)
dbContext.FavoriteQuestions.Add(new FavoriteQuestion
{
TenantId = actor.TenantId,
UserId = actor.UserId,
QuestionReferenceId = reference.Id,
QuestionOwnerTenantId = reference.QuestionOwnerTenantId,
QuestionId = reference.QuestionId,
Source = reference.Source.ToString().ToLowerInvariant(),
CreatedAt = DateTimeOffset.UtcNow
});
}
else if (item is not null)
{
dbContext.FavoriteQuestions.Remove(item);
}
await dbContext.SaveChangesAsync(cancellationToken);
return new LearningActionResult(true, favorite);
}
public async Task<LearningList<WrongQuestionItem>> GetWrongQuestionsAsync(
LearningActor actor,
LearningLimitFilter filter,
CancellationToken cancellationToken = default)
{
var query = dbContext.WrongQuestions
.AsNoTracking()
.Where(item =>
item.TenantId == actor.TenantId &&
item.UserId == actor.UserId);
if (!string.Equals(filter.Status, "all", StringComparison.OrdinalIgnoreCase))
query = query.Where(item => item.ResolvedAt == null);
var items = await query
.OrderByDescending(item => item.LastWrongAt)
.Take(ResolveLimit(filter.Limit))
.Select(item => new WrongQuestionItem(
item.QuestionReferenceId,
new QuestionLocator(
item.QuestionOwnerTenantId == item.TenantId ? QuestionSource.Tenant : QuestionSource.Platform,
item.QuestionId),
item.WrongCount,
item.LastWrongAt,
item.ResolvedAt))
.ToArrayAsync(cancellationToken);
return new LearningList<WrongQuestionItem>(items);
}
public async Task<LearningActionResult> ResolveWrongQuestionAsync(
LearningActor actor,
QuestionActionCommand command,
CancellationToken cancellationToken = default)
{
var reference = await questionReferenceService.ResolveAsync(
actor.TenantId,
actor.UserId,
command.Locator,
cancellationToken);
var item = await dbContext.WrongQuestions.FindAsync(
[actor.TenantId, actor.UserId, reference.Id],
cancellationToken);
if (item is null)
throw new LearningResourceNotFoundException("wrong_question_not_found", "Wrong question was not found.");
item.ResolvedAt = DateTimeOffset.UtcNow;
await dbContext.SaveChangesAsync(cancellationToken);
return new LearningActionResult(true);
}
public async Task<WrongQuestionReviewPlan> GetWrongQuestionReviewPlanAsync(
LearningActor actor,
LearningLimitFilter filter,
CancellationToken cancellationToken = default)
{
var items = await dbContext.WrongQuestions.AsNoTracking()
.Where(item =>
item.TenantId == actor.TenantId &&
item.UserId == actor.UserId &&
item.ResolvedAt == null)
.OrderByDescending(item => item.WrongCount)
.ThenBy(item => item.LastWrongAt)
.Take(ResolveLimit(filter.Limit))
.Select(item => new WrongQuestionReviewPlanItem(
item.QuestionReferenceId,
new QuestionLocator(
item.QuestionOwnerTenantId == item.TenantId ? QuestionSource.Tenant : QuestionSource.Platform,
item.QuestionId),
item.WrongCount,
item.LastWrongAt))
.ToArrayAsync(cancellationToken);
return new WrongQuestionReviewPlan(
items,
JsonSerializer.SerializeToElement(new
{
mode = "wrong_review",
questionCount = items.Length,
recommendedEndpoint = "/api/student/learning/practice-sessions"
}));
}
}