Files
tiku-backend.net/Tiku.Infrastructure/Content/EducationCatalog/EducationCatalogManagementService.cs
xiong 33375a38d7
Some checks failed
ci / release-gate (push) Has been cancelled
refactor(architecture): harden module boundaries
2026-08-04 12:10:36 +08:00

119 lines
5.6 KiB
C#

using Microsoft.EntityFrameworkCore;
using Tiku.Application.Catalog;
using Tiku.Application.Content;
using Tiku.Domain.Catalog;
using Tiku.Infrastructure.Security;
namespace Tiku.Infrastructure.Content;
internal sealed class EducationCatalogManagementService(DirectContentServiceDependencies dependencies)
: DirectContentServiceBase(dependencies), IEducationCatalogManagementService
{
public async Task<CatalogList<School>> GetSchoolsAsync(
DirectContentActor actor,
AdminLimitFilter filter,
CancellationToken cancellationToken = default)
{
var scope = await RequireDataScopeAsync(actor, cancellationToken);
var regionIds = scope.RegionIds.ToArray();
var query = catalogPersistence.Schools.AsNoTracking()
.Where(item => item.TenantId == actor.TenantId)
.ApplyDataScope(scope, null, item => item.RegionId.HasValue && regionIds.Contains(item.RegionId.Value));
if (filter.RegionId.HasValue) query = query.Where(item => item.RegionId == filter.RegionId.Value);
if (!string.IsNullOrWhiteSpace(filter.Keyword))
{
var keyword = filter.Keyword.Trim();
query = query.Where(item => item.Name.Contains(keyword));
}
return new CatalogList<School>(await query
.OrderBy(item => item.Name)
.Take(ResolveLimit(filter.Limit))
.ToArrayAsync(cancellationToken));
}
public async Task<ContentManagementResult<School>> UpsertSchoolAsync(
DirectContentActor actor,
SchoolCommand command,
CancellationToken cancellationToken = default)
{
var scope = await RequireDataScopeAsync(actor, cancellationToken);
ArgumentException.ThrowIfNullOrWhiteSpace(command.Name);
await AssertReferenceAsync<Region>(actor.TenantId, command.RegionId, "region_not_found", cancellationToken);
var item = await ResolveByIdOrLegacyAsync(catalogPersistence.Schools, actor.TenantId, command.Id, command.LegacyId,
cancellationToken);
var isNew = item is null;
EnsureRegionWriteAllowed(scope, actor, item?.RegionId, command.RegionId, isNew, "school_not_found");
item ??= new School { Id = command.Id ?? Guid.NewGuid(), TenantId = actor.TenantId };
item.RegionId = command.RegionId;
item.LegacyId = Normalize(command.LegacyId);
item.Name = command.Name.Trim();
item.ProfessionalExamDate = Normalize(command.ProfessionalExamDate);
item.Metadata = JsonObjectOrDefault(command.Metadata);
if (isNew) catalogPersistence.Schools.Add(item);
await unitOfWork.SaveChangesAsync(cancellationToken);
return new ContentManagementResult<School>(item);
}
public async Task<CatalogList<Major>> GetMajorsAsync(
DirectContentActor actor,
AdminLimitFilter filter,
CancellationToken cancellationToken = default)
{
var scope = await RequireDataScopeAsync(actor, cancellationToken);
var regionIds = scope.RegionIds.ToArray();
var query = catalogPersistence.Majors.AsNoTracking()
.Where(item => item.TenantId == actor.TenantId)
.ApplyDataScope(scope, null, item => item.RegionId.HasValue && regionIds.Contains(item.RegionId.Value));
if (filter.RegionId.HasValue) query = query.Where(item => item.RegionId == filter.RegionId.Value);
if (filter.SchoolId.HasValue) query = query.Where(item => item.SchoolId == filter.SchoolId.Value);
if (!string.Equals(filter.Status, "all", StringComparison.OrdinalIgnoreCase))
query = query.Where(item => item.IsActive);
if (!string.IsNullOrWhiteSpace(filter.Keyword))
{
var keyword = filter.Keyword.Trim();
query = query.Where(item =>
item.Name.Contains(keyword) || (item.Description != null && item.Description.Contains(keyword)));
}
return new CatalogList<Major>(await query
.OrderBy(item => item.SortOrder)
.ThenBy(item => item.Name)
.Take(ResolveLimit(filter.Limit))
.ToArrayAsync(cancellationToken));
}
public async Task<ContentManagementResult<Major>> UpsertMajorAsync(
DirectContentActor actor,
MajorCommand command,
CancellationToken cancellationToken = default)
{
var scope = await RequireDataScopeAsync(actor, cancellationToken);
ArgumentException.ThrowIfNullOrWhiteSpace(command.Name);
await AssertReferenceAsync<Region>(actor.TenantId, command.RegionId, "region_not_found", cancellationToken);
await AssertReferenceAsync<School>(actor.TenantId, command.SchoolId, "school_not_found", cancellationToken);
var item = await ResolveByIdOrLegacyAsync(catalogPersistence.Majors, actor.TenantId, command.Id, command.LegacyId,
cancellationToken);
var isNew = item is null;
EnsureRegionWriteAllowed(scope, actor, item?.RegionId, command.RegionId, isNew, "major_not_found");
item ??= new Major { Id = command.Id ?? Guid.NewGuid(), TenantId = actor.TenantId };
item.RegionId = command.RegionId;
item.SchoolId = command.SchoolId;
item.LegacyId = Normalize(command.LegacyId);
item.Name = command.Name.Trim();
item.Description = Normalize(command.Description);
item.StudyTips = Normalize(command.StudyTips);
item.SortOrder = command.Order ?? item.SortOrder;
item.IsActive = command.IsActive ?? item.IsActive;
if (isNew) catalogPersistence.Majors.Add(item);
await unitOfWork.SaveChangesAsync(cancellationToken);
return new ContentManagementResult<Major>(item);
}
}