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