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;
}
}