176 lines
6.0 KiB
C#
176 lines
6.0 KiB
C#
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;
|
||
|
||
/// <summary>
|
||
/// 字典查询服务。
|
||
/// </summary>
|
||
public sealed class DictionaryQueryService(
|
||
IDictionaryGroupRepository groupRepository,
|
||
IDictionaryItemRepository itemRepository)
|
||
{
|
||
/// <summary>
|
||
/// 获取字典分组分页数据。
|
||
/// </summary>
|
||
public async Task<PagedResult<DictionaryGroupDto>> 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<DictionaryGroupDto>(groups.Count);
|
||
foreach (var group in groups)
|
||
{
|
||
IReadOnlyList<DictionaryItemDto>? 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<DictionaryGroupDto>(items, query.Page, query.PageSize, total);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取字典分组详情。
|
||
/// </summary>
|
||
public async Task<DictionaryGroupDto?> GetGroupByIdAsync(long groupId, CancellationToken cancellationToken = default)
|
||
{
|
||
var group = await groupRepository.GetByIdAsync(groupId, cancellationToken);
|
||
if (group == null)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
return DictionaryMapper.ToGroupDto(group);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取分组下字典项列表。
|
||
/// </summary>
|
||
public async Task<IReadOnlyList<DictionaryItemDto>> 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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取合并后的字典项列表。
|
||
/// </summary>
|
||
public async Task<IReadOnlyList<DictionaryItemDto>> 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<DictionaryItemDto>();
|
||
}
|
||
|
||
var systemItems = await itemRepository.GetByGroupIdAsync(0, systemGroup.Id, cancellationToken);
|
||
return systemItems
|
||
.Where(item => item.IsEnabled)
|
||
.OrderBy(item => item.SortOrder)
|
||
.Select(DictionaryMapper.ToItemDto)
|
||
.ToList();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 批量获取字典项。
|
||
/// </summary>
|
||
public async Task<IReadOnlyDictionary<string, IReadOnlyList<DictionaryItemDto>>> BatchGetDictionariesAsync(
|
||
IEnumerable<string> codes,
|
||
CancellationToken cancellationToken = default)
|
||
{
|
||
var normalizedCodes = codes
|
||
.Where(DictionaryCode.IsValid)
|
||
.Select(code => new DictionaryCode(code).Value)
|
||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||
.ToArray();
|
||
|
||
var result = new Dictionary<string, IReadOnlyList<DictionaryItemDto>>(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;
|
||
}
|
||
}
|