using SqlSugar;
namespace FoodLabeling.Application.Helpers;
///
/// 编辑/列表回显:库中可存展开 Guid;若绑定覆盖全集则将 Id 数组折叠为 ["ALL"] 哨兵(对齐 Team Member)。
///
public static class ScopeAllEchoHelper
{
public sealed class ScopeAllEchoOptions
{
public bool IsPartnerAll { get; init; }
public bool IsRegionAll { get; init; }
public bool IsLocationAll { get; init; }
/// 落库维度为 SPECIFIED 时禁止再按「恰好全选」折叠为 ALL 哨兵。
public string? AppliedPartnerType { get; init; }
public string? AppliedRegionType { get; init; }
public string? AppliedLocationType { get; init; }
}
///
/// 将 partnerIds / regionIds / locationIds 折叠为 ALL 哨兵(编辑弹窗与列表 Id 数组回显)。
///
public static async Task<(List PartnerIds, List RegionIds, List LocationIds)>
CollapseScopeIdsToAllSentinelAsync(
ISqlSugarClient db,
IReadOnlyList? partnerIds,
IReadOnlyList? regionIds,
IReadOnlyList? 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 { 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 { AllScopeBindingHelper.ScopeAll };
}
}
if (options.IsRegionAll)
{
regions = new List { AllScopeBindingHelper.ScopeAll };
}
else if (!AllScopeBindingHelper.IsDeclaredSpecified(options.AppliedRegionType) && regions.Count > 0)
{
regions = await CollapseRegionsIfFullAsync(db, partners, regions);
}
if (options.IsLocationAll)
{
locations = new List { AllScopeBindingHelper.ScopeAll };
}
else if (!AllScopeBindingHelper.IsDeclaredSpecified(options.AppliedLocationType) && locations.Count > 0)
{
locations = await CollapseLocationsIfFullAsync(db, partners, regions, locations);
}
return (partners, regions, locations);
}
/// AvailabilityType=ALL 时 Region 与 Location 均为 ALL。
public static ScopeAllEchoOptions ForAvailabilityAll(bool isAll) =>
new() { IsRegionAll = isAll, IsLocationAll = isAll };
/// LabelTemplate 各维度独立 ALL 标记。
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
};
/// 标签类型/分类/多选项:Company + Region + Location 可用范围。
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
};
/// 兼容旧调用:无 Region 类型时用 AvailabilityType 同时驱动 Region/Location。
public static ScopeAllEchoOptions ForLabelEntityScope(
string? appliedPartnerType,
string? availabilityType) =>
ForLabelEntityScope(appliedPartnerType, availabilityType, availabilityType);
/// Label:AppliedRegionType=ALL 且未落门店快照时 Region/Location 均为 ALL。
public static ScopeAllEchoOptions ForLabelRegionScope(
string? appliedRegionType,
IReadOnlyList 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> CollapseRegionsIfFullAsync(
ISqlSugarClient db,
IReadOnlyList partners,
IReadOnlyList 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 { AllScopeBindingHelper.ScopeAll };
}
return regions.ToList();
}
private static async Task> CollapseLocationsIfFullAsync(
ISqlSugarClient db,
IReadOnlyList partners,
IReadOnlyList regions,
IReadOnlyList locations)
{
var partnerContext = ResolvePartnerContextForCollapse(partners);
List 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 { AllScopeBindingHelper.ScopeAll };
}
// Region=ALL(或无具体 Region)时:仅多店才折叠,避免「Region=ALL + 选 1 店」误成 Location ALL
if (locations.Count > 1 || allLocationIds.Count > 1)
{
return new List { AllScopeBindingHelper.ScopeAll };
}
}
return locations.ToList();
}
private static List? ResolvePartnerContextForCollapse(IReadOnlyList partners)
{
if (partners.Count == 0 || AllScopeBindingHelper.IsDeclaredAll(partners[0]))
{
return null;
}
return partners.ToList();
}
}