Files
TakeoutSaaS.AdminApi/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfTenantAnnouncementRepository.cs

182 lines
6.2 KiB
C#

using Microsoft.EntityFrameworkCore;
using TakeoutSaaS.Domain.Tenants.Entities;
using TakeoutSaaS.Domain.Tenants.Enums;
using TakeoutSaaS.Domain.Tenants.Repositories;
using TakeoutSaaS.Infrastructure.App.Persistence;
namespace TakeoutSaaS.Infrastructure.App.Repositories;
/// <summary>
/// EF 租户公告仓储。
/// </summary>
public sealed class EfTenantAnnouncementRepository(TakeoutAppDbContext context) : ITenantAnnouncementRepository
{
/// <inheritdoc />
public async Task<IReadOnlyList<TenantAnnouncement>> SearchAsync(
long tenantId,
string? keyword,
AnnouncementStatus? status,
TenantAnnouncementType? type,
bool? isActive,
DateTime? effectiveFrom,
DateTime? effectiveTo,
DateTime? effectiveAt,
bool orderByPriority = false,
int? limit = null,
CancellationToken cancellationToken = default)
{
var tenantIds = new[] { tenantId, 0L };
var query = context.TenantAnnouncements.AsNoTracking()
.IgnoreQueryFilters()
.Where(x => tenantIds.Contains(x.TenantId));
if (!string.IsNullOrWhiteSpace(keyword))
{
var normalized = keyword.Trim();
query = query.Where(x =>
EF.Functions.ILike(x.Title, $"%{normalized}%")
|| EF.Functions.ILike(x.Content, $"%{normalized}%"));
}
if (status.HasValue)
{
query = query.Where(x => x.Status == status.Value);
}
if (type.HasValue)
{
query = query.Where(x => x.AnnouncementType == type.Value);
}
if (isActive.HasValue)
{
query = isActive.Value
? query.Where(x => x.Status == AnnouncementStatus.Published)
: query.Where(x => x.Status != AnnouncementStatus.Published);
}
if (effectiveFrom.HasValue)
{
query = query.Where(x => x.EffectiveFrom >= effectiveFrom.Value);
}
if (effectiveTo.HasValue)
{
query = query.Where(x => x.EffectiveTo == null || x.EffectiveTo <= effectiveTo.Value);
}
if (effectiveAt.HasValue)
{
var at = effectiveAt.Value;
query = query.Where(x => x.EffectiveFrom <= at && (x.EffectiveTo == null || x.EffectiveTo >= at));
}
// 应用排序(如果启用)
if (orderByPriority)
{
query = query.OrderByDescending(x => x.Priority).ThenByDescending(x => x.EffectiveFrom);
}
// 应用限制(如果指定)
if (limit.HasValue && limit.Value > 0)
{
query = query.Take(limit.Value);
}
return await query.ToListAsync(cancellationToken);
}
/// <inheritdoc />
public Task<TenantAnnouncement?> FindByIdInScopeAsync(long tenantId, long announcementId, CancellationToken cancellationToken = default)
{
var tenantIds = new[] { tenantId, 0L };
return context.TenantAnnouncements.AsNoTracking()
.IgnoreQueryFilters()
.FirstOrDefaultAsync(x => tenantIds.Contains(x.TenantId) && x.Id == announcementId, cancellationToken);
}
/// <inheritdoc />
public async Task<IReadOnlyList<TenantAnnouncement>> SearchUnreadAsync(
long tenantId,
long? userId,
AnnouncementStatus? status,
bool? isActive,
DateTime? effectiveAt,
CancellationToken cancellationToken = default)
{
var tenantIds = new[] { tenantId, 0L };
var announcementQuery = context.TenantAnnouncements.AsNoTracking()
.IgnoreQueryFilters()
.Where(x => tenantIds.Contains(x.TenantId));
if (status.HasValue)
{
announcementQuery = announcementQuery.Where(x => x.Status == status.Value);
}
if (isActive.HasValue)
{
announcementQuery = isActive.Value
? announcementQuery.Where(x => x.Status == AnnouncementStatus.Published)
: announcementQuery.Where(x => x.Status != AnnouncementStatus.Published);
}
if (effectiveAt.HasValue)
{
var at = effectiveAt.Value;
announcementQuery = announcementQuery.Where(x => x.EffectiveFrom <= at && (x.EffectiveTo == null || x.EffectiveTo >= at));
}
var readQuery = context.TenantAnnouncementReads.AsNoTracking()
.IgnoreQueryFilters()
.Where(x => x.TenantId == tenantId);
readQuery = userId.HasValue
? readQuery.Where(x => x.UserId == null || x.UserId == userId.Value)
: readQuery.Where(x => x.UserId == null);
var query = from announcement in announcementQuery
join read in readQuery on announcement.Id equals read.AnnouncementId into readGroup
where !readGroup.Any()
select announcement;
return await query.ToListAsync(cancellationToken);
}
/// <inheritdoc />
public Task<TenantAnnouncement?> FindByIdAsync(long tenantId, long announcementId, CancellationToken cancellationToken = default)
{
return context.TenantAnnouncements.AsNoTracking()
.FirstOrDefaultAsync(x => x.TenantId == tenantId && x.Id == announcementId, cancellationToken);
}
/// <inheritdoc />
public Task AddAsync(TenantAnnouncement announcement, CancellationToken cancellationToken = default)
{
return context.TenantAnnouncements.AddAsync(announcement, cancellationToken).AsTask();
}
/// <inheritdoc />
public Task UpdateAsync(TenantAnnouncement announcement, CancellationToken cancellationToken = default)
{
context.TenantAnnouncements.Update(announcement);
return Task.CompletedTask;
}
/// <inheritdoc />
public async Task DeleteAsync(long tenantId, long announcementId, CancellationToken cancellationToken = default)
{
var entity = await context.TenantAnnouncements.FirstOrDefaultAsync(x => x.TenantId == tenantId && x.Id == announcementId, cancellationToken);
if (entity != null)
{
context.TenantAnnouncements.Remove(entity);
}
}
/// <inheritdoc />
public Task SaveChangesAsync(CancellationToken cancellationToken = default)
{
return context.SaveChangesAsync(cancellationToken);
}
}