158 lines
6.0 KiB
C#
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"
|
|
}));
|
|
}
|
|
}
|