using Microsoft.EntityFrameworkCore; using TelegramPanel.Data.Entities; namespace TelegramPanel.Data.Repositories; /// /// 群组仓储实现 /// public class GroupRepository : Repository, IGroupRepository { public GroupRepository(AppDbContext context) : base(context) { } private IQueryable BuildFilterQuery(int accountId, int? categoryId, string? filterType, string? membershipRole, string? search) { var query = _dbSet .AsNoTracking() .AsQueryable(); query = query.Where(g => g.CreatorAccountId != null || g.AccountGroups.Any()); membershipRole = (membershipRole ?? "all").Trim().ToLowerInvariant(); if (accountId > 0) { query = query.Where(g => g.AccountGroups.Any(x => x.AccountId == accountId)); if (membershipRole == "creator") query = query.Where(g => g.AccountGroups.Any(x => x.AccountId == accountId && x.IsCreator)); else if (membershipRole == "admin") query = query.Where(g => g.AccountGroups.Any(x => x.AccountId == accountId && x.IsAdmin && !x.IsCreator)); else if (membershipRole == "member") query = query.Where(g => g.AccountGroups.Any(x => x.AccountId == accountId && !x.IsAdmin)); } else if (membershipRole == "creator") { query = query.Where(g => g.AccountGroups.Any(x => x.IsCreator)); } else if (membershipRole == "admin") { query = query.Where(g => g.AccountGroups.Any(x => x.IsAdmin && !x.IsCreator)); } else if (membershipRole == "member") { query = query.Where(g => g.AccountGroups.Any(x => !x.IsAdmin)); } if (categoryId.HasValue && categoryId.Value > 0) query = query.Where(g => g.CategoryId == categoryId.Value); else if (categoryId.HasValue && categoryId.Value == 0) query = query.Where(g => g.CategoryId == null); filterType = (filterType ?? "all").Trim().ToLowerInvariant(); if (filterType == "public") query = query.Where(g => g.Username != null && g.Username != ""); else if (filterType == "private") query = query.Where(g => g.Username == null || g.Username == ""); search = (search ?? string.Empty).Trim(); if (!string.IsNullOrWhiteSpace(search)) { var like = $"%{search}%"; query = query.Where(g => EF.Functions.Like(g.Title, like) || (g.Username != null && EF.Functions.Like(g.Username, like)) || (g.Category != null && EF.Functions.Like(g.Category.Name, like))); } return query; } private IQueryable BuildPagedDetailQuery(IReadOnlyCollection ids, int accountId) { IQueryable query = _dbSet .AsNoTracking() .Where(g => ids.Contains(g.Id)) .Include(g => g.CreatorAccount) .Include(g => g.Category); if (accountId > 0) { query = query .Include(g => g.AccountGroups.Where(x => x.AccountId == accountId)) .AsSplitQuery(); } return query; } public override async Task GetByIdAsync(int id) { return await _dbSet .Include(g => g.CreatorAccount) .Include(g => g.Category) .Include(g => g.AccountGroups) .AsSplitQuery() .FirstOrDefaultAsync(g => g.Id == id); } public override async Task> GetAllAsync() { return await _dbSet .Include(g => g.CreatorAccount) .Include(g => g.Category) .Include(g => g.AccountGroups) .AsSplitQuery() .Where(g => g.CreatorAccountId != null || g.AccountGroups.Any()) .OrderByDescending(g => g.SyncedAt) .ToListAsync(); } public async Task GetByTelegramIdAsync(long telegramId) { return await _dbSet .Include(g => g.CreatorAccount) .Include(g => g.Category) .Include(g => g.AccountGroups) .AsSplitQuery() .FirstOrDefaultAsync(g => g.TelegramId == telegramId); } public async Task> GetByCreatorAccountAsync(int accountId) { return await _dbSet .Include(g => g.CreatorAccount) .Include(g => g.Category) .Include(g => g.AccountGroups) .AsSplitQuery() .Where(g => g.CreatorAccountId == accountId) .OrderByDescending(g => g.SyncedAt) .ToListAsync(); } public async Task DeleteOrphanedAsync(CancellationToken cancellationToken = default) { var orphanedGroups = await _dbSet .Where(g => g.CreatorAccountId == null && !g.AccountGroups.Any()) .ToListAsync(cancellationToken); if (orphanedGroups.Count == 0) return 0; _dbSet.RemoveRange(orphanedGroups); await SaveChangesWithSqliteLockRetryAsync(cancellationToken); return orphanedGroups.Count; } public async Task<(IReadOnlyList Items, int TotalCount)> QueryForViewPagedAsync( int accountId, int? categoryId, string? filterType, string? membershipRole, string? search, int pageIndex, int pageSize, CancellationToken cancellationToken = default) { if (pageIndex < 0) pageIndex = 0; if (pageSize <= 0) pageSize = 20; if (pageSize > 500) pageSize = 500; var filteredQuery = BuildFilterQuery(accountId, categoryId, filterType, membershipRole, search); var total = await filteredQuery.CountAsync(cancellationToken); if (total == 0) return (Array.Empty(), 0); var pageIds = await filteredQuery .OrderByDescending(g => g.SyncedAt) .ThenByDescending(g => g.Id) .Skip(pageIndex * pageSize) .Take(pageSize) .Select(g => g.Id) .ToListAsync(cancellationToken); if (pageIds.Count == 0) return (Array.Empty(), total); var orderMap = pageIds .Select((id, index) => new { id, index }) .ToDictionary(x => x.id, x => x.index); var items = await BuildPagedDetailQuery(pageIds, accountId) .ToListAsync(cancellationToken); var orderedItems = items .OrderBy(g => orderMap[g.Id]) .ToList(); return (orderedItems, total); } }