ScopeAllEchoHelper.cs 8.67 KB
using SqlSugar;

namespace FoodLabeling.Application.Helpers;

/// <summary>
/// 编辑/列表回显:库中可存展开 Guid;若绑定覆盖全集则将 Id 数组折叠为 <c>["ALL"]</c> 哨兵(对齐 Team Member)。
/// </summary>
public static class ScopeAllEchoHelper
{
    public sealed class ScopeAllEchoOptions
    {
        public bool IsPartnerAll { get; init; }

        public bool IsRegionAll { get; init; }

        public bool IsLocationAll { get; init; }

        /// <summary>落库维度为 SPECIFIED 时禁止再按「恰好全选」折叠为 ALL 哨兵。</summary>
        public string? AppliedPartnerType { get; init; }

        public string? AppliedRegionType { get; init; }

        public string? AppliedLocationType { get; init; }
    }

    /// <summary>
    /// 将 partnerIds / regionIds / locationIds 折叠为 ALL 哨兵(编辑弹窗与列表 Id 数组回显)。
    /// </summary>
    public static async Task<(List<string> PartnerIds, List<string> RegionIds, List<string> LocationIds)>
        CollapseScopeIdsToAllSentinelAsync(
            ISqlSugarClient db,
            IReadOnlyList<string>? partnerIds,
            IReadOnlyList<string>? regionIds,
            IReadOnlyList<string>? locationIds,
            ScopeAllEchoOptions? options = null)
    {
        options ??= new ScopeAllEchoOptions();
        var partners = LocationScopeBindingHelper.NormalizeIds(partnerIds);
        var regions = LocationScopeBindingHelper.NormalizeIds(regionIds);
        var locations = LocationScopeBindingHelper.NormalizeIds(locationIds);

        if (options.IsPartnerAll)
        {
            partners = new List<string> { AllScopeBindingHelper.ScopeAll };
        }
        else if (!AllScopeBindingHelper.IsDeclaredSpecified(options.AppliedPartnerType) && partners.Count > 0)
        {
            var allPartners = await AllScopeBindingHelper.ResolveAllPartnerIdsAsync(db);
            if (allPartners.Count > 0 && AllScopeBindingHelper.IsFullIdSelection(partners, allPartners))
            {
                partners = new List<string> { AllScopeBindingHelper.ScopeAll };
            }
        }

        if (options.IsRegionAll)
        {
            regions = new List<string> { AllScopeBindingHelper.ScopeAll };
        }
        else if (!AllScopeBindingHelper.IsDeclaredSpecified(options.AppliedRegionType) && regions.Count > 0)
        {
            regions = await CollapseRegionsIfFullAsync(db, partners, regions);
        }

        if (options.IsLocationAll)
        {
            locations = new List<string> { AllScopeBindingHelper.ScopeAll };
        }
        else if (!AllScopeBindingHelper.IsDeclaredSpecified(options.AppliedLocationType) && locations.Count > 0)
        {
            locations = await CollapseLocationsIfFullAsync(db, partners, regions, locations);
        }

        return (partners, regions, locations);
    }

    /// <summary>AvailabilityType=ALL 时 Region 与 Location 均为 ALL。</summary>
    public static ScopeAllEchoOptions ForAvailabilityAll(bool isAll) =>
        new() { IsRegionAll = isAll, IsLocationAll = isAll };

    /// <summary>LabelTemplate 各维度独立 ALL 标记。</summary>
    public static ScopeAllEchoOptions ForLabelTemplateDimensions(
        string? appliedPartnerType,
        string? appliedRegionType,
        string? appliedLocationType) =>
        new()
        {
            IsPartnerAll = AllScopeBindingHelper.IsDeclaredAll(appliedPartnerType),
            IsRegionAll = AllScopeBindingHelper.IsDeclaredAll(appliedRegionType),
            IsLocationAll = AllScopeBindingHelper.IsDeclaredAll(appliedLocationType),
            AppliedPartnerType = appliedPartnerType,
            AppliedRegionType = appliedRegionType,
            AppliedLocationType = appliedLocationType
        };

    /// <summary>标签类型/分类/多选项:Company + Region + Location 可用范围。</summary>
    public static ScopeAllEchoOptions ForLabelEntityScope(
        string? appliedPartnerType,
        string? appliedRegionType,
        string? availabilityType) =>
        new()
        {
            IsPartnerAll = AllScopeBindingHelper.IsDeclaredAll(appliedPartnerType),
            IsRegionAll = AllScopeBindingHelper.IsDeclaredAll(appliedRegionType),
            IsLocationAll = AllScopeBindingHelper.IsDeclaredAll(availabilityType),
            AppliedPartnerType = appliedPartnerType,
            AppliedRegionType = appliedRegionType,
            AppliedLocationType = availabilityType
        };

    /// <summary>兼容旧调用:无 Region 类型时用 AvailabilityType 同时驱动 Region/Location。</summary>
    public static ScopeAllEchoOptions ForLabelEntityScope(
        string? appliedPartnerType,
        string? availabilityType) =>
        ForLabelEntityScope(appliedPartnerType, availabilityType, availabilityType);

    /// <summary>Label:AppliedRegionType=ALL 且未落门店快照时 Region/Location 均为 ALL。</summary>
    public static ScopeAllEchoOptions ForLabelRegionScope(
        string? appliedRegionType,
        IReadOnlyList<string> locationIds)
    {
        var isRegionAll = AllScopeBindingHelper.IsDeclaredAll(appliedRegionType);
        var normalizedLocations = LocationScopeBindingHelper.NormalizeIds(locationIds);
        var isLocationAll = isRegionAll && normalizedLocations.Count == 0;
        return new ScopeAllEchoOptions
        {
            IsRegionAll = isRegionAll,
            IsLocationAll = isLocationAll,
            AppliedRegionType = appliedRegionType,
            AppliedLocationType = isLocationAll
                ? AllScopeBindingHelper.ScopeAll
                : AllScopeBindingHelper.ScopeSpecified
        };
    }

    private static async Task<List<string>> CollapseRegionsIfFullAsync(
        ISqlSugarClient db,
        IReadOnlyList<string> partners,
        IReadOnlyList<string> regions)
    {
        var partnerContext = ResolvePartnerContextForCollapse(partners);
        var allRegionIds = partnerContext is { Count: > 0 }
            ? await LocationScopeBindingHelper.ResolveGroupIdsFromPartnerIdsAsync(db, partnerContext)
            : await AllScopeBindingHelper.ResolveAllRegionIdsAsync(db, null);

        if (allRegionIds.Count > 0 && AllScopeBindingHelper.IsFullIdSelection(regions, allRegionIds))
        {
            return new List<string> { AllScopeBindingHelper.ScopeAll };
        }

        return regions.ToList();
    }

    private static async Task<List<string>> CollapseLocationsIfFullAsync(
        ISqlSugarClient db,
        IReadOnlyList<string> partners,
        IReadOnlyList<string> regions,
        IReadOnlyList<string> locations)
    {
        var partnerContext = ResolvePartnerContextForCollapse(partners);
        List<string> allLocationIds;

        if (regions.Count == 1 && AllScopeBindingHelper.IsDeclaredAll(regions[0]))
        {
            allLocationIds = partnerContext is { Count: > 0 }
                ? await LocationScopeBindingHelper.ResolveLocationIdsFromPartnerIdsAsync(db, partnerContext)
                : await AllScopeBindingHelper.ResolveAllLocationIdsAsync(db, null, null);
        }
        else if (regions.Count > 0 && !regions.Any(r => AllScopeBindingHelper.IsDeclaredAll(r)))
        {
            allLocationIds = await AllScopeBindingHelper.ResolveAllLocationIdsAsync(db, partnerContext, regions);
        }
        else if (partnerContext is { Count: > 0 })
        {
            allLocationIds = await LocationScopeBindingHelper.ResolveLocationIdsFromPartnerIdsAsync(db, partnerContext);
        }
        else
        {
            allLocationIds = await AllScopeBindingHelper.ResolveAllLocationIdsAsync(db, null, null);
        }

        if (allLocationIds.Count > 0 && AllScopeBindingHelper.IsFullIdSelection(locations, allLocationIds))
        {
            // 具体 Region:覆盖该区全部门店时一律回显 ["ALL"](含区内仅 1 店;前端 ALL 常展开为具体 Guid)
            var hasConcreteRegions = regions.Count > 0
                                     && !regions.Any(AllScopeBindingHelper.IsDeclaredAll);
            if (hasConcreteRegions)
            {
                return new List<string> { AllScopeBindingHelper.ScopeAll };
            }

            // Region=ALL(或无具体 Region)时:仅多店才折叠,避免「Region=ALL + 选 1 店」误成 Location ALL
            if (locations.Count > 1 || allLocationIds.Count > 1)
            {
                return new List<string> { AllScopeBindingHelper.ScopeAll };
            }
        }

        return locations.ToList();
    }

    private static List<string>? ResolvePartnerContextForCollapse(IReadOnlyList<string> partners)
    {
        if (partners.Count == 0 || AllScopeBindingHelper.IsDeclaredAll(partners[0]))
        {
            return null;
        }

        return partners.ToList();
    }
}