using TakeoutSaaS.Application.Dictionary.Contracts; using TakeoutSaaS.Application.Dictionary.Models; using TakeoutSaaS.Domain.Dictionary.Enums; using TakeoutSaaS.Domain.Dictionary.Repositories; using TakeoutSaaS.Domain.Dictionary.ValueObjects; using TakeoutSaaS.Shared.Abstractions.Constants; using TakeoutSaaS.Shared.Abstractions.Exceptions; using TakeoutSaaS.Shared.Abstractions.Results; namespace TakeoutSaaS.Application.Dictionary.Services; /// /// 字典查询服务。 /// public sealed class DictionaryQueryService( IDictionaryGroupRepository groupRepository, IDictionaryItemRepository itemRepository) { /// /// 获取字典分组分页数据。 /// public async Task> GetGroupsAsync( DictionaryGroupQuery query, CancellationToken cancellationToken = default) { // 1. 解析查询租户与作用域 var tenantId = query.TenantId ?? 0; if (query.Scope == DictionaryScope.Business && tenantId <= 0) { throw new BusinessException(ErrorCodes.ValidationFailed, "Scope=Business 时必须指定 TenantId"); } // 2. 确定作用域与目标租户 var scope = query.Scope ?? (tenantId == 0 ? DictionaryScope.System : DictionaryScope.Business); if (scope == DictionaryScope.System) { tenantId = 0; } // 3. 查询分页数据 var sortDescending = string.Equals(query.SortOrder, "desc", StringComparison.OrdinalIgnoreCase); var targetTenant = scope == DictionaryScope.System ? 0 : tenantId; var groups = await groupRepository.GetPagedAsync( targetTenant, scope, query.Keyword, query.IsEnabled, query.Page, query.PageSize, query.SortBy, sortDescending, cancellationToken); var total = await groupRepository.CountAsync( targetTenant, scope, query.Keyword, query.IsEnabled, cancellationToken); // 4. 转换为 DTO var items = new List(groups.Count); foreach (var group in groups) { IReadOnlyList? groupItems = null; if (query.IncludeItems) { var groupItemEntities = await itemRepository.GetByGroupIdAsync(group.TenantId, group.Id, cancellationToken); groupItems = groupItemEntities .Where(item => item.IsEnabled) .OrderBy(item => item.SortOrder) .Select(DictionaryMapper.ToItemDto) .ToList(); } items.Add(DictionaryMapper.ToGroupDto(group, groupItems)); } return new PagedResult(items, query.Page, query.PageSize, total); } /// /// 获取字典分组详情。 /// public async Task GetGroupByIdAsync(long groupId, CancellationToken cancellationToken = default) { var group = await groupRepository.GetByIdAsync(groupId, cancellationToken); if (group == null) { return null; } return DictionaryMapper.ToGroupDto(group); } /// /// 获取分组下字典项列表。 /// public async Task> GetItemsByGroupIdAsync(long groupId, CancellationToken cancellationToken = default) { var group = await groupRepository.GetByIdAsync(groupId, cancellationToken); if (group == null) { throw new BusinessException(ErrorCodes.NotFound, "字典分组不存在"); } var items = await itemRepository.GetByGroupIdAsync(group.TenantId, groupId, cancellationToken); return items .Where(item => item.IsEnabled) .OrderBy(item => item.SortOrder) .Select(DictionaryMapper.ToItemDto) .ToList(); } /// /// 获取合并后的字典项列表。 /// public async Task> GetMergedDictionaryAsync(string code, CancellationToken cancellationToken = default) { if (!DictionaryCode.IsValid(code)) { throw new BusinessException(ErrorCodes.ValidationFailed, "字典编码格式不正确"); } // 1. 管理端默认读取系统字典(TenantId=0) var normalized = new DictionaryCode(code); var systemGroup = await groupRepository.GetByCodeAsync(0, normalized, cancellationToken); if (systemGroup == null || !systemGroup.IsEnabled) { return Array.Empty(); } var systemItems = await itemRepository.GetByGroupIdAsync(0, systemGroup.Id, cancellationToken); return systemItems .Where(item => item.IsEnabled) .OrderBy(item => item.SortOrder) .Select(DictionaryMapper.ToItemDto) .ToList(); } /// /// 批量获取字典项。 /// public async Task>> BatchGetDictionariesAsync( IEnumerable codes, CancellationToken cancellationToken = default) { var normalizedCodes = codes .Where(DictionaryCode.IsValid) .Select(code => new DictionaryCode(code).Value) .Distinct(StringComparer.OrdinalIgnoreCase) .ToArray(); var result = new Dictionary>(StringComparer.OrdinalIgnoreCase); if (normalizedCodes.Length == 0) { return result; } var tasks = normalizedCodes.Select(async code => { var items = await GetMergedDictionaryAsync(code, cancellationToken); return (code, items); }); foreach (var pair in await Task.WhenAll(tasks)) { result[pair.code] = pair.items; } return result; } }