forked from xiongyuxing/tiku-backend.net
100 lines
3.9 KiB
C#
100 lines
3.9 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Tiku.Application.QuestionBanks;
|
|
using Tiku.Application.Security;
|
|
using Tiku.Domain.Content;
|
|
using Tiku.Domain.QuestionBanks;
|
|
using Tiku.Domain.Tenancy;
|
|
using Tiku.Infrastructure.Persistence;
|
|
|
|
namespace Tiku.Infrastructure.QuestionBanks;
|
|
|
|
public sealed class QuestionReferenceService(
|
|
TikuDbContext dbContext,
|
|
IPublicQuestionAccessPolicy accessPolicy,
|
|
ITenantExecutionScope tenantExecutionScope) : IQuestionReferenceService
|
|
{
|
|
public async Task<TenantQuestionReference> ResolveAsync(
|
|
Guid tenantId,
|
|
Guid? userId,
|
|
QuestionLocator locator,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var ownerTenantId = locator.Source switch
|
|
{
|
|
QuestionSource.Tenant => await ResolveTenantQuestionAsync(tenantId, locator.QuestionId, cancellationToken),
|
|
QuestionSource.Platform => await ResolvePlatformQuestionAsync(tenantId, locator.QuestionId, cancellationToken),
|
|
_ => throw new QuestionLocatorException("question_source_invalid", "Question source is invalid.")
|
|
};
|
|
|
|
var existing = await dbContext.TenantQuestionReferences.SingleOrDefaultAsync(
|
|
reference =>
|
|
reference.TenantId == tenantId &&
|
|
reference.QuestionOwnerTenantId == ownerTenantId &&
|
|
reference.QuestionId == locator.QuestionId,
|
|
cancellationToken);
|
|
if (existing is not null)
|
|
{
|
|
return existing;
|
|
}
|
|
|
|
var reference = new TenantQuestionReference
|
|
{
|
|
TenantId = tenantId,
|
|
QuestionOwnerTenantId = ownerTenantId,
|
|
QuestionId = locator.QuestionId,
|
|
Source = locator.Source,
|
|
CreatedBy = userId
|
|
};
|
|
dbContext.TenantQuestionReferences.Add(reference);
|
|
return reference;
|
|
}
|
|
|
|
private async Task<Guid> ResolveTenantQuestionAsync(
|
|
Guid tenantId,
|
|
Guid questionId,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var exists = await dbContext.Questions.AsNoTracking().AnyAsync(
|
|
question =>
|
|
question.TenantId == tenantId &&
|
|
question.Id == questionId &&
|
|
question.Status == QuestionStatus.Published,
|
|
cancellationToken);
|
|
return exists
|
|
? tenantId
|
|
: throw new QuestionLocatorException("question_not_found", "Tenant question was not found.");
|
|
}
|
|
|
|
private async Task<Guid> ResolvePlatformQuestionAsync(
|
|
Guid tenantId,
|
|
Guid questionId,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
await accessPolicy.EnsureCanStartAsync(tenantId, cancellationToken);
|
|
var ownerTenantId = await tenantExecutionScope.ExecuteAsync(
|
|
new SystemScopeRequest(
|
|
tenantId, SystemScopeCallerType.PublicQuestionBank, nameof(QuestionReferenceService),
|
|
"Resolve a platform question for an entitled tenant", Guid.NewGuid().ToString("N")),
|
|
async (provider, token) =>
|
|
{
|
|
var systemDbContext = provider.GetRequiredService<TikuDbContext>();
|
|
return await systemDbContext.Questions.AsNoTracking()
|
|
.Where(question =>
|
|
question.Id == questionId &&
|
|
question.Status == QuestionStatus.Published)
|
|
.Join(
|
|
systemDbContext.Tenants.AsNoTracking().Where(tenant => tenant.Mode == TenantMode.PlatformOwned),
|
|
question => question.TenantId,
|
|
tenant => tenant.Id,
|
|
(question, tenant) => (Guid?)tenant.Id)
|
|
.SingleOrDefaultAsync(token);
|
|
},
|
|
cancellationToken);
|
|
|
|
return ownerTenantId ?? throw new QuestionLocatorException(
|
|
"question_not_found",
|
|
"Platform question was not found.");
|
|
}
|
|
}
|