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; /// /// 租户聚合的 EF Core 仓储实现。 /// public sealed class EfTenantRepository(TakeoutAppDbContext context) : ITenantRepository { /// public Task FindByIdAsync(long tenantId, CancellationToken cancellationToken = default) { return context.Tenants .AsNoTracking() .FirstOrDefaultAsync(x => x.Id == tenantId, cancellationToken); } /// public async Task> SearchAsync( TenantStatus? status, string? keyword, CancellationToken cancellationToken = default) { // 1. 构建基础查询 var query = context.Tenants.AsNoTracking(); // 2. 按状态过滤 if (status.HasValue) { query = query.Where(x => x.Status == status.Value); } // 3. 按关键字过滤 if (!string.IsNullOrWhiteSpace(keyword)) { keyword = keyword.Trim(); query = query.Where(x => EF.Functions.ILike(x.Name, $"%{keyword}%") || EF.Functions.ILike(x.Code, $"%{keyword}%") || EF.Functions.ILike(x.ContactName ?? string.Empty, $"%{keyword}%") || EF.Functions.ILike(x.ContactPhone ?? string.Empty, $"%{keyword}%")); } // 4. 排序返回 return await query .OrderByDescending(x => x.CreatedAt) .ToListAsync(cancellationToken); } /// public async Task<(IReadOnlyList Items, int Total)> SearchPagedAsync( TenantStatus? status, TenantVerificationStatus? verificationStatus, string? name, string? contactName, string? contactPhone, string? keyword, int page, int pageSize, CancellationToken cancellationToken = default) { var query = context.Tenants.AsNoTracking(); // 1. 按租户状态过滤 if (status.HasValue) { query = query.Where(x => x.Status == status.Value); } // 2. 按实名认证状态过滤(未提交视为 Draft) if (verificationStatus.HasValue) { query = from tenant in query join profile in context.TenantVerificationProfiles.AsNoTracking() on tenant.Id equals profile.TenantId into profiles from profile in profiles.DefaultIfEmpty() where (profile == null ? TenantVerificationStatus.Draft : profile.Status) == verificationStatus.Value select tenant; } // 3. 按名称/联系人/电话过滤(模糊匹配) if (!string.IsNullOrWhiteSpace(name)) { var normalizedName = name.Trim(); query = query.Where(x => EF.Functions.ILike(x.Name, $"%{normalizedName}%")); } // 4. (空行后) 按联系人过滤(模糊匹配) if (!string.IsNullOrWhiteSpace(contactName)) { var normalizedContactName = contactName.Trim(); query = query.Where(x => EF.Functions.ILike(x.ContactName ?? string.Empty, $"%{normalizedContactName}%")); } // 5. (空行后) 按联系电话过滤(模糊匹配) if (!string.IsNullOrWhiteSpace(contactPhone)) { var normalizedContactPhone = contactPhone.Trim(); query = query.Where(x => EF.Functions.ILike(x.ContactPhone ?? string.Empty, $"%{normalizedContactPhone}%")); } // 6. (空行后) 兼容关键字查询:名称/编码/联系人/电话 if (!string.IsNullOrWhiteSpace(keyword)) { var normalizedKeyword = keyword.Trim(); query = query.Where(x => EF.Functions.ILike(x.Name, $"%{normalizedKeyword}%") || EF.Functions.ILike(x.Code, $"%{normalizedKeyword}%") || EF.Functions.ILike(x.ContactName ?? string.Empty, $"%{normalizedKeyword}%") || EF.Functions.ILike(x.ContactPhone ?? string.Empty, $"%{normalizedKeyword}%")); } // 7. (空行后) 先统计总数,再按创建时间倒序分页 var total = await query.CountAsync(cancellationToken); // 8. (空行后) 查询当前页数据 var items = await query .OrderByDescending(x => x.CreatedAt) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(cancellationToken); return (items, total); } /// public Task AddTenantAsync(Tenant tenant, CancellationToken cancellationToken = default) { return context.Tenants.AddAsync(tenant, cancellationToken).AsTask(); } /// public Task UpdateTenantAsync(Tenant tenant, CancellationToken cancellationToken = default) { context.Tenants.Update(tenant); return Task.CompletedTask; } /// public Task ExistsByCodeAsync(string code, CancellationToken cancellationToken = default) { var normalized = code.Trim(); return context.Tenants.AnyAsync(x => x.Code == normalized, cancellationToken); } /// public Task ExistsByContactPhoneAsync(string phone, CancellationToken cancellationToken = default) { var normalized = phone.Trim(); return context.Tenants.AnyAsync(x => x.ContactPhone == normalized, cancellationToken); } /// public Task FindTenantIdByContactPhoneAsync(string phone, CancellationToken cancellationToken = default) { // 1. 标准化手机号 var normalized = phone.Trim(); // 2. 查询租户 ID return context.Tenants.AsNoTracking() .Where(x => x.ContactPhone == normalized) .Select(x => (long?)x.Id) .FirstOrDefaultAsync(cancellationToken); } /// public Task GetVerificationProfileAsync(long tenantId, CancellationToken cancellationToken = default) { return context.TenantVerificationProfiles .AsNoTracking() .FirstOrDefaultAsync(x => x.TenantId == tenantId, cancellationToken); } /// public async Task> GetVerificationProfilesAsync( IReadOnlyCollection tenantIds, CancellationToken cancellationToken = default) { // 1. tenantIds 为空直接返回 if (tenantIds.Count == 0) { return Array.Empty(); } // 2. 批量查询实名资料 return await context.TenantVerificationProfiles .AsNoTracking() .Where(x => tenantIds.Contains(x.TenantId)) .ToListAsync(cancellationToken); } /// public async Task UpsertVerificationProfileAsync(TenantVerificationProfile profile, CancellationToken cancellationToken = default) { // 1. 查询现有实名资料 var existing = await context.TenantVerificationProfiles .FirstOrDefaultAsync(x => x.TenantId == profile.TenantId, cancellationToken); if (existing == null) { // 2. 不存在则新增 await context.TenantVerificationProfiles.AddAsync(profile, cancellationToken); return; } // 3. 存在则更新当前值 profile.Id = existing.Id; context.Entry(existing).CurrentValues.SetValues(profile); } /// public Task GetActiveSubscriptionAsync(long tenantId, CancellationToken cancellationToken = default) { return context.TenantSubscriptions .AsNoTracking() .Where(x => x.TenantId == tenantId) .OrderByDescending(x => x.EffectiveTo) .FirstOrDefaultAsync(cancellationToken); } /// public async Task> GetSubscriptionsAsync( IReadOnlyCollection tenantIds, CancellationToken cancellationToken = default) { // 1. tenantIds 为空直接返回 if (tenantIds.Count == 0) { return Array.Empty(); } // 2. 批量查询订阅数据 return await context.TenantSubscriptions .AsNoTracking() .Where(x => tenantIds.Contains(x.TenantId)) .OrderByDescending(x => x.EffectiveTo) .ToListAsync(cancellationToken); } /// public Task FindSubscriptionByIdAsync(long tenantId, long subscriptionId, CancellationToken cancellationToken = default) { return context.TenantSubscriptions .FirstOrDefaultAsync(x => x.TenantId == tenantId && x.Id == subscriptionId, cancellationToken); } /// public Task AddSubscriptionAsync(TenantSubscription subscription, CancellationToken cancellationToken = default) { return context.TenantSubscriptions.AddAsync(subscription, cancellationToken).AsTask(); } /// public Task UpdateSubscriptionAsync(TenantSubscription subscription, CancellationToken cancellationToken = default) { context.TenantSubscriptions.Update(subscription); return Task.CompletedTask; } /// public Task AddSubscriptionHistoryAsync(TenantSubscriptionHistory history, CancellationToken cancellationToken = default) { return context.TenantSubscriptionHistories.AddAsync(history, cancellationToken).AsTask(); } /// public async Task> GetSubscriptionHistoryAsync(long tenantId, CancellationToken cancellationToken = default) { return await context.TenantSubscriptionHistories .AsNoTracking() .Where(x => x.TenantId == tenantId) .OrderByDescending(x => x.EffectiveFrom) .ToListAsync(cancellationToken); } /// public Task AddAuditLogAsync(TenantAuditLog log, CancellationToken cancellationToken = default) { return context.TenantAuditLogs.AddAsync(log, cancellationToken).AsTask(); } /// public async Task> GetAuditLogsAsync(long tenantId, CancellationToken cancellationToken = default) { return await context.TenantAuditLogs .AsNoTracking() .Where(x => x.TenantId == tenantId) .OrderByDescending(x => x.CreatedAt) .ToListAsync(cancellationToken); } /// public Task SaveChangesAsync(CancellationToken cancellationToken = default) { return context.SaveChangesAsync(cancellationToken); } }