Files
TakeoutSaaS.AdminApi/src/Application/TakeoutSaaS.Application/Dictionary/Services/DictionaryQueryService.cs
2026-02-04 10:46:32 +08:00

176 lines
6.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}