Commit 04a4c4559df225f2f499264a1ef47530f496be0f

Authored by 李曜臣
1 parent e9fbe796

2026-07-14

Showing 29 changed files with 698 additions and 348 deletions
泰额版/Food Labeling Management App UniApp/src/utils/appAdminRole.ts
1 1 import type { UsAppMyProfileOutputDto } from '../services/usAppAuth'
2 2  
3   -/** 与后端 ReportsRoleHelper.IsAdminRole 对齐(App 管理员级联选店) */
  3 +function isCompanyAdminProfile(profile: UsAppMyProfileOutputDto): boolean {
  4 + const code = (profile.primaryRoleCode ?? '').trim().toLowerCase()
  5 + if (code === 'companyadmin' || code.includes('partner')) return true
  6 + const display = (profile.roleDisplay ?? '').trim().toLowerCase()
  7 + if (!display) return false
  8 + if (display === 'company admin') return true
  9 + if (display.includes('partner admin')) return true
  10 + return false
  11 +}
  12 +
  13 +/** 平台管理员或 Company Admin(App 级联选店) */
4 14 export function isAppAdminUser(profile: UsAppMyProfileOutputDto | null | undefined): boolean {
5 15 if (!profile) return false
6 16 const code = (profile.primaryRoleCode ?? '').trim().toLowerCase()
7 17 if (code === 'admin') return true
8 18 const display = (profile.roleDisplay ?? '').trim().toLowerCase()
9   - if (!display) return false
  19 + if (!display) return isCompanyAdminProfile(profile)
10 20 if (display.includes('administrator')) return true
11 21 if (display.includes('super admin')) return true
12   - return false
  22 + return isCompanyAdminProfile(profile)
13 23 }
... ...
泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/AuthScopeQueryHelper.cs
... ... @@ -277,14 +277,10 @@ public static class AuthScopeQueryHelper
277 277 return;
278 278 }
279 279  
280   - var lid = locationId.Trim();
281   - var userIdStr = currentUser.Id!.Value.ToString();
282   - var bound = await dbContext.SqlSugarClient.Queryable<UserLocationDbEntity>()
283   - .AnyAsync(x => !x.IsDeleted && x.UserId == userIdStr && x.LocationId == lid);
284   - if (!bound)
285   - {
286   - throw new UserFriendlyException("当前账号未绑定该门店,无法选择");
287   - }
  280 + await UsAppPrintLogScopeHelper.EnsureUserCanAccessLocationAsync(
  281 + currentUser,
  282 + dbContext.SqlSugarClient,
  283 + locationId);
288 284 }
289 285  
290 286 private static async Task<AuthScopeSelectLocationOutputDto> ResolveAndValidateSelectionAsync(
... ...
泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/EntityLocationScopeDisplayHelper.cs 0 → 100644
  1 +using FoodLabeling.Domain.Shared;
  2 +using SqlSugar;
  3 +
  4 +namespace FoodLabeling.Application.Helpers;
  5 +
  6 +/// <summary>
  7 +/// 标签类型/分类/多选项等:列表 Region/Location 列展示(适用全部时分别为 All Region / All Location)。
  8 +/// </summary>
  9 +public static class EntityLocationScopeDisplayHelper
  10 +{
  11 + /// <summary>
  12 + /// 根据可用范围类型与已解析的 Region/Location Id、名称,生成列表展示文案。
  13 + /// </summary>
  14 + public static async Task<(string Region, string Location)> BuildListDisplayAsync(
  15 + ISqlSugarClient db,
  16 + string? availabilityType,
  17 + IReadOnlyList<string> regionIds,
  18 + IReadOnlyList<string> locationIds,
  19 + IEnumerable<string> regionNames,
  20 + IEnumerable<string> locationNames,
  21 + IReadOnlyList<string>? partnerIdsForContext)
  22 + {
  23 + if (AllScopeBindingHelper.IsDeclaredAll(availabilityType))
  24 + {
  25 + return (AllScopeBindingHelper.AllRegionsDisplay, AllScopeBindingHelper.AllLocationsDisplay);
  26 + }
  27 +
  28 + var normRegionIds = LocationScopeBindingHelper.NormalizeIds(regionIds);
  29 + var normLocationIds = LocationScopeBindingHelper.NormalizeIds(locationIds);
  30 + var partnerContext = partnerIdsForContext is { Count: > 0 } ? partnerIdsForContext : null;
  31 +
  32 + var allRegionIds = await AllScopeBindingHelper.ResolveAllRegionIdsAsync(db, partnerContext);
  33 + var regionIsAll = normRegionIds.Count > 0
  34 + && allRegionIds.Count > 0
  35 + && AllScopeBindingHelper.IsFullIdSelection(normRegionIds, allRegionIds);
  36 +
  37 + var regionContextForLocations = regionIsAll ? null : normRegionIds.Count > 0 ? normRegionIds : null;
  38 + var allLocationIds = await AllScopeBindingHelper.ResolveAllLocationIdsAsync(
  39 + db,
  40 + partnerContext,
  41 + regionContextForLocations);
  42 + var locationIsAll = normLocationIds.Count > 0
  43 + && allLocationIds.Count > 0
  44 + && AllScopeBindingHelper.IsFullIdSelection(normLocationIds, allLocationIds);
  45 +
  46 + var regionDisplay = regionIsAll
  47 + ? AllScopeBindingHelper.AllRegionsDisplay
  48 + : JoinDistinctNames(regionNames);
  49 +
  50 + var locationDisplay = locationIsAll
  51 + ? AllScopeBindingHelper.AllLocationsDisplay
  52 + : JoinDistinctNames(locationNames);
  53 +
  54 + return (regionDisplay, locationDisplay);
  55 + }
  56 +
  57 + private static string JoinDistinctNames(IEnumerable<string> names)
  58 + {
  59 + var ordered = names
  60 + .Where(x => !string.IsNullOrWhiteSpace(x))
  61 + .Select(x => x.Trim())
  62 + .Distinct(StringComparer.OrdinalIgnoreCase)
  63 + .OrderBy(x => x, StringComparer.OrdinalIgnoreCase)
  64 + .ToList();
  65 + return ordered.Count > 0 ? string.Join(", ", ordered) : FoodLabelingDisplayConsts.NotAvailable;
  66 + }
  67 +}
... ...
泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelEntityListScopeHelper.cs 0 → 100644
  1 +using FoodLabeling.Application.Services.DbModels;
  2 +using SqlSugar;
  3 +
  4 +namespace FoodLabeling.Application.Helpers;
  5 +
  6 +/// <summary>
  7 +/// 标签类型/分类/多选项列表:非平台管理员仅可见其绑定 Company 范围内数据。
  8 +/// </summary>
  9 +public static class LabelEntityListScopeHelper
  10 +{
  11 + /// <summary>
  12 + /// 标签类型列表:按 Company + Region/Location 范围过滤(Company Admin 不可见 All Companies 数据)。
  13 + /// </summary>
  14 + public static ISugarQueryable<FlLabelTypeDbEntity> ApplyTypeLocationAvailabilityFilter(
  15 + ISugarQueryable<FlLabelTypeDbEntity> query,
  16 + List<string>? scopedLocationIds)
  17 + {
  18 + if (scopedLocationIds is null)
  19 + {
  20 + return query;
  21 + }
  22 +
  23 + if (scopedLocationIds.Count == 0)
  24 + {
  25 + return query.Where(_ => false);
  26 + }
  27 +
  28 + return query.Where(t =>
  29 + (t.AvailabilityType == AllScopeBindingHelper.ScopeAll
  30 + && t.AppliedPartnerType == LabelEntityPartnerScopeHelper.ScopeSpecified)
  31 + || (t.AvailabilityType == AllScopeBindingHelper.ScopeSpecified
  32 + && SqlFunc.Subqueryable<FlLabelTypeLocationDbEntity>()
  33 + .Where(tl => tl.LabelTypeId == t.Id && scopedLocationIds.Contains(tl.LocationId))
  34 + .Any()));
  35 + }
  36 +
  37 + /// <summary>
  38 + /// 标签分类列表:按 Company + Region/Location 范围过滤。
  39 + /// </summary>
  40 + public static ISugarQueryable<FlLabelCategoryDbEntity> ApplyCategoryLocationAvailabilityFilter(
  41 + ISugarQueryable<FlLabelCategoryDbEntity> query,
  42 + List<string>? scopedLocationIds)
  43 + {
  44 + if (scopedLocationIds is null)
  45 + {
  46 + return query;
  47 + }
  48 +
  49 + if (scopedLocationIds.Count == 0)
  50 + {
  51 + return query.Where(_ => false);
  52 + }
  53 +
  54 + return query.Where(c =>
  55 + (c.AvailabilityType == AllScopeBindingHelper.ScopeAll
  56 + && c.AppliedPartnerType == LabelEntityPartnerScopeHelper.ScopeSpecified)
  57 + || (c.AvailabilityType == AllScopeBindingHelper.ScopeSpecified
  58 + && SqlFunc.Subqueryable<FlLabelCategoryLocationDbEntity>()
  59 + .Where(cl => cl.CategoryId == c.Id && scopedLocationIds.Contains(cl.LocationId))
  60 + .Any()));
  61 + }
  62 +
  63 + /// <summary>
  64 + /// 标签多选项列表:按 Company + Region/Location 范围过滤。
  65 + /// </summary>
  66 + public static ISugarQueryable<FlLabelMultipleOptionDbEntity> ApplyMultipleOptionLocationAvailabilityFilter(
  67 + ISugarQueryable<FlLabelMultipleOptionDbEntity> query,
  68 + List<string>? scopedLocationIds)
  69 + {
  70 + if (scopedLocationIds is null)
  71 + {
  72 + return query;
  73 + }
  74 +
  75 + if (scopedLocationIds.Count == 0)
  76 + {
  77 + return query.Where(_ => false);
  78 + }
  79 +
  80 + return query.Where(o =>
  81 + (o.AvailabilityType == AllScopeBindingHelper.ScopeAll
  82 + && o.AppliedPartnerType == LabelEntityPartnerScopeHelper.ScopeSpecified)
  83 + || (o.AvailabilityType == AllScopeBindingHelper.ScopeSpecified
  84 + && SqlFunc.Subqueryable<FlLabelMultipleOptionLocationDbEntity>()
  85 + .Where(ol => ol.MultipleOptionId == o.Id && scopedLocationIds.Contains(ol.LocationId))
  86 + .Any()));
  87 + }
  88 +}
... ...
泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelEntityPartnerScopeHelper.cs
... ... @@ -348,17 +348,12 @@ public static class LabelEntityPartnerScopeHelper
348 348  
349 349 if (scopedPartnerIds.Count == 0)
350 350 {
351   - return query.Where(t =>
352   - !SqlFunc.Subqueryable<FlLabelTypePartnerDbEntity>()
353   - .Where(p => p.LabelTypeId == t.Id)
354   - .Any());
  351 + return query.Where(_ => false);
355 352 }
356 353  
357 354 return query.Where(t =>
358   - !SqlFunc.Subqueryable<FlLabelTypePartnerDbEntity>()
359   - .Where(p => p.LabelTypeId == t.Id)
360   - .Any()
361   - || SqlFunc.Subqueryable<FlLabelTypePartnerDbEntity>()
  355 + t.AppliedPartnerType == ScopeSpecified
  356 + && SqlFunc.Subqueryable<FlLabelTypePartnerDbEntity>()
362 357 .Where(p => p.LabelTypeId == t.Id && scopedPartnerIds.Contains(p.PartnerId))
363 358 .Any());
364 359 }
... ... @@ -384,17 +379,12 @@ public static class LabelEntityPartnerScopeHelper
384 379  
385 380 if (scopedPartnerIds.Count == 0)
386 381 {
387   - return query.Where(c =>
388   - !SqlFunc.Subqueryable<FlLabelCategoryPartnerDbEntity>()
389   - .Where(p => p.CategoryId == c.Id)
390   - .Any());
  382 + return query.Where(_ => false);
391 383 }
392 384  
393 385 return query.Where(c =>
394   - !SqlFunc.Subqueryable<FlLabelCategoryPartnerDbEntity>()
395   - .Where(p => p.CategoryId == c.Id)
396   - .Any()
397   - || SqlFunc.Subqueryable<FlLabelCategoryPartnerDbEntity>()
  386 + c.AppliedPartnerType == ScopeSpecified
  387 + && SqlFunc.Subqueryable<FlLabelCategoryPartnerDbEntity>()
398 388 .Where(p => p.CategoryId == c.Id && scopedPartnerIds.Contains(p.PartnerId))
399 389 .Any());
400 390 }
... ... @@ -420,17 +410,12 @@ public static class LabelEntityPartnerScopeHelper
420 410  
421 411 if (scopedPartnerIds.Count == 0)
422 412 {
423   - return query.Where(o =>
424   - !SqlFunc.Subqueryable<FlLabelMultipleOptionPartnerDbEntity>()
425   - .Where(p => p.MultipleOptionId == o.Id)
426   - .Any());
  413 + return query.Where(_ => false);
427 414 }
428 415  
429 416 return query.Where(o =>
430   - !SqlFunc.Subqueryable<FlLabelMultipleOptionPartnerDbEntity>()
431   - .Where(p => p.MultipleOptionId == o.Id)
432   - .Any()
433   - || SqlFunc.Subqueryable<FlLabelMultipleOptionPartnerDbEntity>()
  417 + o.AppliedPartnerType == ScopeSpecified
  418 + && SqlFunc.Subqueryable<FlLabelMultipleOptionPartnerDbEntity>()
434 419 .Where(p => p.MultipleOptionId == o.Id && scopedPartnerIds.Contains(p.PartnerId))
435 420 .Any());
436 421 }
... ...
泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelTemplateScopeHelper.cs
... ... @@ -437,23 +437,7 @@ public static class LabelTemplateScopeHelper
437 437  
438 438 if (scopedLocationIds.Count == 0)
439 439 {
440   - return hasExtendedScope
441   - ? query.Where(t =>
442   - t.AppliedLocationType == ScopeAll
443   - && !SqlFunc.Subqueryable<FlLabelTemplateLocationDbEntity>()
444   - .Where(l => l.TemplateId == t.Id)
445   - .Any()
446   - && !SqlFunc.Subqueryable<FlLabelTemplatePartnerDbEntity>()
447   - .Where(p => p.TemplateId == t.Id)
448   - .Any()
449   - && !SqlFunc.Subqueryable<FlLabelTemplateRegionDbEntity>()
450   - .Where(r => r.TemplateId == t.Id)
451   - .Any())
452   - : query.Where(t =>
453   - t.AppliedLocationType == ScopeAll
454   - && !SqlFunc.Subqueryable<FlLabelTemplateLocationDbEntity>()
455   - .Where(l => l.TemplateId == t.Id)
456   - .Any());
  440 + return query.Where(_ => false);
457 441 }
458 442  
459 443 if (!hasExtendedScope)
... ... @@ -470,12 +454,9 @@ public static class LabelTemplateScopeHelper
470 454 db, scopedLocationIds);
471 455  
472 456 return query.Where(t =>
473   - (!SqlFunc.Subqueryable<FlLabelTemplatePartnerDbEntity>()
474   - .Where(p => p.TemplateId == t.Id)
475   - .Any()
476   - || SqlFunc.Subqueryable<FlLabelTemplatePartnerDbEntity>()
477   - .Where(p => p.TemplateId == t.Id && scopedPartnerIds.Contains(p.PartnerId))
478   - .Any())
  457 + SqlFunc.Subqueryable<FlLabelTemplatePartnerDbEntity>()
  458 + .Where(p => p.TemplateId == t.Id && scopedPartnerIds.Contains(p.PartnerId))
  459 + .Any()
479 460 && (!SqlFunc.Subqueryable<FlLabelTemplateRegionDbEntity>()
480 461 .Where(r => r.TemplateId == t.Id)
481 462 .Any()
... ...
泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LocationRegionScopeHelper.cs
... ... @@ -25,6 +25,7 @@ public static class LocationRegionScopeHelper
25 25  
26 26 /// <summary>
27 27 /// 解析当前用户可查询的 Region(公司 + 组织名)集合。
  28 + /// Company Admin 展开为绑定 Company 下全部 Region(与 <see cref="GroupListScopeHelper"/> 一致)。
28 29 /// </summary>
29 30 public static async Task<LocationListScopeFilter> ResolveLocationListScopeAsync(
30 31 ICurrentUser currentUser,
... ... @@ -40,8 +41,15 @@ public static class LocationRegionScopeHelper
40 41 return new LocationListScopeFilter();
41 42 }
42 43  
  44 + var db = dbContext.SqlSugarClient;
  45 +
  46 + if (await TeamMemberRoleHelper.IsCompanyAdminUserAsync(db, currentUser))
  47 + {
  48 + return await ResolveCompanyAdminLocationListScopeAsync(db, currentUser.Id.Value);
  49 + }
  50 +
43 51 var userId = currentUser.Id.Value.ToString();
44   - var rows = await dbContext.SqlSugarClient.Queryable<UserLocationDbEntity>()
  52 + var rows = await db.Queryable<UserLocationDbEntity>()
45 53 .InnerJoin<LocationAggregateRoot>((ul, loc) =>
46 54 !loc.IsDeleted && ul.LocationId == loc.Id.ToString())
47 55 .Where(ul => !ul.IsDeleted && ul.UserId == userId)
... ... @@ -57,6 +65,59 @@ public static class LocationRegionScopeHelper
57 65 return new LocationListScopeFilter { RegionKeys = keys };
58 66 }
59 67  
  68 + private static async Task<LocationListScopeFilter> ResolveCompanyAdminLocationListScopeAsync(
  69 + ISqlSugarClient db,
  70 + Guid userId)
  71 + {
  72 + var locationIds = await LocationScopeBindingHelper.ResolveBoundLocationIdsForUserAsync(db, userId);
  73 + if (locationIds.Count == 0)
  74 + {
  75 + return new LocationListScopeFilter();
  76 + }
  77 +
  78 + var partnerIds = await LocationScopeBindingHelper.ResolvePartnerIdsFromLocationIdsAsync(db, locationIds);
  79 + if (partnerIds.Count == 0)
  80 + {
  81 + return new LocationListScopeFilter();
  82 + }
  83 +
  84 + var partners = await db.Queryable<FlPartnerDbEntity>()
  85 + .Where(p => !p.IsDeleted && partnerIds.Contains(p.Id))
  86 + .Select(p => new { p.Id, p.PartnerName })
  87 + .ToListAsync();
  88 +
  89 + var groups = await db.Queryable<FlGroupDbEntity>()
  90 + .Where(g => !g.IsDeleted && partnerIds.Contains(g.PartnerId))
  91 + .Select(g => new { g.PartnerId, g.GroupName })
  92 + .ToListAsync();
  93 +
  94 + var keys = new HashSet<(string Partner, string GroupName)>();
  95 + foreach (var group in groups)
  96 + {
  97 + var groupName = group.GroupName?.Trim();
  98 + if (string.IsNullOrEmpty(groupName))
  99 + {
  100 + continue;
  101 + }
  102 +
  103 + var partner = partners.FirstOrDefault(p =>
  104 + string.Equals(p.Id, group.PartnerId, StringComparison.OrdinalIgnoreCase));
  105 + var partnerName = partner?.PartnerName?.Trim();
  106 + if (!string.IsNullOrEmpty(partnerName))
  107 + {
  108 + keys.Add((partnerName, groupName));
  109 + }
  110 +
  111 + var partnerId = group.PartnerId?.Trim();
  112 + if (!string.IsNullOrEmpty(partnerId))
  113 + {
  114 + keys.Add((partnerId, groupName));
  115 + }
  116 + }
  117 +
  118 + return new LocationListScopeFilter { RegionKeys = keys.ToList() };
  119 + }
  120 +
60 121 /// <summary>
61 122 /// 将 Region 范围应用到 <c>location</c> 查询(须在其它 Where 之前或之后均可,建议在 IsDeleted 之后)。
62 123 /// </summary>
... ...
泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/UsAppAuthScopeHelper.cs
... ... @@ -19,7 +19,7 @@ public static class UsAppAuthScopeHelper
19 19 {
20 20 private const string CacheKeyPrefix = "FoodLabeling:UsAppAdminScope:";
21 21  
22   - public static void EnsureAdminAppToken(ICurrentUser currentUser)
  22 + public static async Task EnsureAppScopeCallerAsync(ISqlSugarClient db, ICurrentUser currentUser)
23 23 {
24 24 if (currentUser.Id is null)
25 25 {
... ... @@ -32,12 +32,30 @@ public static class UsAppAuthScopeHelper
32 32 throw new UserFriendlyException("请使用 App 登录令牌调用该接口");
33 33 }
34 34  
35   - if (!ReportsRoleHelper.IsAdminRole(currentUser))
  35 + if (!await CanUseAdminScopeApisAsync(db, currentUser))
36 36 {
37   - throw new UserFriendlyException("仅管理员可使用公司/区域/门店筛选接口");
  37 + throw new UserFriendlyException("仅管理员或 Company Admin 可使用公司/区域/门店筛选接口");
38 38 }
39 39 }
40 40  
  41 + /// <summary>
  42 + /// 平台管理员或 Company Admin 可使用 App 级联选店接口。
  43 + /// </summary>
  44 + public static async Task<bool> CanUseAdminScopeApisAsync(ISqlSugarClient db, ICurrentUser currentUser)
  45 + {
  46 + if (currentUser.Id is null)
  47 + {
  48 + return false;
  49 + }
  50 +
  51 + if (ReportsRoleHelper.IsAdminRole(currentUser))
  52 + {
  53 + return true;
  54 + }
  55 +
  56 + return await TeamMemberRoleHelper.IsCompanyAdminUserAsync(db, currentUser);
  57 + }
  58 +
41 59 public static async Task<List<AuthScopeCompanyOptionDto>> ListCompaniesAsync(ISqlSugarClient db)
42 60 {
43 61 return await db.Queryable<FlPartnerDbEntity>()
... ... @@ -119,6 +137,7 @@ public static class UsAppAuthScopeHelper
119 137 public static async Task<AuthScopeSelectLocationOutputDto> SelectLocationAsync(
120 138 ISqlSugarClient db,
121 139 IDistributedCache cache,
  140 + ICurrentUser currentUser,
122 141 Guid userId,
123 142 UsAppSelectAdminScopeLocationInputVo input)
124 143 {
... ... @@ -130,6 +149,11 @@ public static class UsAppAuthScopeHelper
130 149 throw new UserFriendlyException("请选择公司、区域和门店");
131 150 }
132 151  
  152 + if (!ReportsRoleHelper.IsAdminRole(currentUser))
  153 + {
  154 + await UsAppPrintLogScopeHelper.EnsureUserCanAccessLocationAsync(currentUser, db, locationId);
  155 + }
  156 +
133 157 var scopedIds = await LocationScopeBindingHelper.ResolveFilteredLocationIdsForListAsync(
134 158 db,
135 159 partnerId,
... ...
泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelAppService.cs
... ... @@ -62,38 +62,28 @@ public class LabelAppService : ApplicationService, ILabelAppService
62 62  
63 63 var db = _dbContext.SqlSugarClient;
64 64 var regionSchema = await LabelRegionSchemaHelper.GetStatusAsync(db);
65   - var scopedLocationIds = await LocationScopeBindingHelper.ResolveScopedLocationIdsAsync(
66   - db, groupId, locationId);
  65 + var scopedLocationIds = await TeamMemberListScopeHelper.ResolveListLocationIdsAsync(
  66 + CurrentUser,
  67 + _dbContext,
  68 + partnerId,
  69 + groupId,
  70 + locationId);
67 71 var filterGroupId = groupId?.Trim();
68   - if (!string.IsNullOrWhiteSpace(filterGroupId))
  72 + if (scopedLocationIds is not null)
69 73 {
70   - labelIdsQuery = scopedLocationIds is { Count: 0 }
71   - ? labelIdsQuery.Where(_ => false)
72   - : LabelRegionScopeHelper.ApplyLabelRegionListFilter(
  74 + if (scopedLocationIds.Count == 0)
  75 + {
  76 + labelIdsQuery = labelIdsQuery.Where(_ => false);
  77 + }
  78 + else if (!string.IsNullOrWhiteSpace(filterGroupId))
  79 + {
  80 + labelIdsQuery = LabelRegionScopeHelper.ApplyLabelRegionListFilter(
73 81 db, labelIdsQuery, filterGroupId, scopedLocationIds, regionSchema);
74   - }
75   - else if (scopedLocationIds is not null)
76   - {
77   - labelIdsQuery = scopedLocationIds.Count == 0
78   - ? labelIdsQuery.Where(_ => false)
79   - : LabelRegionScopeHelper.ApplyLabelLocationListFilter(
80   - db, labelIdsQuery, scopedLocationIds, regionSchema);
81   - }
82   - else if (!string.IsNullOrWhiteSpace(groupId))
83   - {
84   - labelIdsQuery = LabelRegionScopeHelper.ApplyGroupIdListFilter(
85   - db, labelIdsQuery, groupId, regionSchema);
86   - }
87   - else if (!string.IsNullOrWhiteSpace(partnerId))
88   - {
89   - var partnerLocationIds = await LocationScopeBindingHelper.ResolveFilteredLocationIdsForListAsync(
90   - db, partnerId, null, null);
91   - if (partnerLocationIds is not null)
  82 + }
  83 + else
92 84 {
93   - labelIdsQuery = partnerLocationIds.Count == 0
94   - ? labelIdsQuery.Where(_ => false)
95   - : LabelRegionScopeHelper.ApplyLabelLocationListFilter(
96   - db, labelIdsQuery, partnerLocationIds, regionSchema);
  85 + labelIdsQuery = LabelRegionScopeHelper.ApplyLabelLocationListFilter(
  86 + db, labelIdsQuery, scopedLocationIds, regionSchema);
97 87 }
98 88 }
99 89  
... ...
泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelCategoryAppService.cs
... ... @@ -71,11 +71,11 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
71 71 var ids = entities.Select(x => x.Id).ToList();
72 72  
73 73 var labelStatsMap = await BuildCategoryLabelStatsMapAsync(ids);
74   - var scopeMap = await BuildCategoryScopeMapAsync(entities);
75 74 var partnerScopeMap = await LabelEntityPartnerScopeHelper.BuildPartnerScopeDisplayMapAsync(
76 75 _dbContext.SqlSugarClient,
77 76 LabelEntityPartnerScopeHelper.LabelEntityPartnerKind.Category,
78 77 ids);
  78 + var scopeMap = await BuildCategoryScopeMapAsync(entities, partnerScopeMap);
79 79  
80 80 var items = entities.Select(x =>
81 81 {
... ... @@ -370,16 +370,7 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
370 370 return query;
371 371 }
372 372  
373   - if (scopedLocationIds.Count == 0)
374   - {
375   - return query.Where(c => c.AvailabilityType == "ALL");
376   - }
377   -
378   - return query.Where(c =>
379   - c.AvailabilityType == "ALL" ||
380   - SqlFunc.Subqueryable<FlLabelCategoryLocationDbEntity>()
381   - .Where(cl => cl.CategoryId == c.Id && scopedLocationIds.Contains(cl.LocationId))
382   - .Any());
  373 + return LabelEntityListScopeHelper.ApplyCategoryLocationAvailabilityFilter(query, scopedLocationIds);
383 374 }
384 375  
385 376 private async Task SaveCategoryPartnerScopeAsync(
... ... @@ -420,7 +411,8 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
420 411 private const string EmptyDisplay = FoodLabelingDisplayConsts.NotAvailable;
421 412  
422 413 private async Task<Dictionary<string, CategoryScopeData>> BuildCategoryScopeMapAsync(
423   - List<FlLabelCategoryDbEntity> entities)
  414 + List<FlLabelCategoryDbEntity> entities,
  415 + Dictionary<string, LabelEntityPartnerScopeHelper.LabelEntityPartnerScopeDisplay> partnerScopeMap)
424 416 {
425 417 var result = new Dictionary<string, CategoryScopeData>(StringComparer.Ordinal);
426 418 if (entities.Count == 0)
... ... @@ -428,8 +420,9 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
428 420 return result;
429 421 }
430 422  
431   - foreach (var e in entities.Where(x =>
432   - !string.Equals(x.AvailabilityType, "SPECIFIED", StringComparison.OrdinalIgnoreCase)))
  423 + var entityById = entities.ToDictionary(x => x.Id, StringComparer.Ordinal);
  424 +
  425 + foreach (var e in entities.Where(x => AllScopeBindingHelper.IsDeclaredAll(x.AvailabilityType)))
433 426 {
434 427 result[e.Id] = new CategoryScopeData
435 428 {
... ... @@ -525,14 +518,29 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
525 518 var regionIds = await LocationScopeBindingHelper.ResolveGroupIdsFromLocationIdsAsync(
526 519 _dbContext.SqlSugarClient, locationIds);
527 520  
  521 + entityById.TryGetValue(catId, out var entity);
  522 + partnerScopeMap.TryGetValue(catId, out var partnerScope);
  523 + var partnerContext = entity is not null
  524 + && string.Equals(
  525 + entity.AppliedPartnerType,
  526 + LabelEntityPartnerScopeHelper.ScopeSpecified,
  527 + StringComparison.OrdinalIgnoreCase)
  528 + ? partnerScope?.PartnerIds
  529 + : null;
  530 +
  531 + var (regionDisplay, locationDisplay) = await EntityLocationScopeDisplayHelper.BuildListDisplayAsync(
  532 + _dbContext.SqlSugarClient,
  533 + entity?.AvailabilityType,
  534 + regionIds,
  535 + locationIds,
  536 + regions,
  537 + locationNames,
  538 + partnerContext);
  539 +
528 540 result[catId] = new CategoryScopeData
529 541 {
530   - Region = regions.Count > 0
531   - ? string.Join(", ", regions.OrderBy(x => x, StringComparer.OrdinalIgnoreCase))
532   - : EmptyDisplay,
533   - Location = locationNames.Count > 0
534   - ? string.Join(", ", locationNames.OrderBy(x => x, StringComparer.OrdinalIgnoreCase))
535   - : EmptyDisplay,
  542 + Region = regionDisplay,
  543 + Location = locationDisplay,
536 544 RegionIds = regionIds,
537 545 LocationIds = locationIds
538 546 };
... ...
泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelMultipleOptionAppService.cs
... ... @@ -43,7 +43,7 @@ public class LabelMultipleOptionAppService : ApplicationService, ILabelMultipleO
43 43  
44 44 query = await LabelEntityPartnerScopeHelper.ApplyMultipleOptionPartnerListFilterAsync(
45 45 _dbContext.SqlSugarClient, query, scopedPartnerIds);
46   - query = ApplyMultipleOptionScopeFilter(query, scopedLocationIds);
  46 + query = LabelEntityListScopeHelper.ApplyMultipleOptionLocationAvailabilityFilter(query, scopedLocationIds);
47 47  
48 48 if (!string.IsNullOrWhiteSpace(input.Sorting))
49 49 {
... ... @@ -395,27 +395,6 @@ public class LabelMultipleOptionAppService : ApplicationService, ILabelMultipleO
395 395 };
396 396 }
397 397  
398   - private static ISugarQueryable<FlLabelMultipleOptionDbEntity> ApplyMultipleOptionScopeFilter(
399   - ISugarQueryable<FlLabelMultipleOptionDbEntity> query,
400   - List<string>? scopedLocationIds)
401   - {
402   - if (scopedLocationIds is null)
403   - {
404   - return query;
405   - }
406   -
407   - if (scopedLocationIds.Count == 0)
408   - {
409   - return query.Where(o => o.AvailabilityType == "ALL");
410   - }
411   -
412   - return query.Where(o =>
413   - o.AvailabilityType == "ALL" ||
414   - SqlFunc.Subqueryable<FlLabelMultipleOptionLocationDbEntity>()
415   - .Where(ol => ol.MultipleOptionId == o.Id && scopedLocationIds.Contains(ol.LocationId))
416   - .Any());
417   - }
418   -
419 398 private async Task<Dictionary<string, MultipleOptionScopeData>> BuildMultipleOptionScopeMapAsync(
420 399 List<FlLabelMultipleOptionDbEntity> entities)
421 400 {
... ...
泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelTypeAppService.cs
... ... @@ -43,7 +43,7 @@ public class LabelTypeAppService : ApplicationService, ILabelTypeAppService
43 43  
44 44 query = await LabelEntityPartnerScopeHelper.ApplyTypePartnerListFilterAsync(
45 45 _dbContext.SqlSugarClient, query, scopedPartnerIds);
46   - query = ApplyLabelTypeScopeFilter(query, scopedLocationIds);
  46 + query = LabelEntityListScopeHelper.ApplyTypeLocationAvailabilityFilter(query, scopedLocationIds);
47 47  
48 48 if (!string.IsNullOrWhiteSpace(input.Sorting))
49 49 {
... ... @@ -346,27 +346,6 @@ public class LabelTypeAppService : ApplicationService, ILabelTypeAppService
346 346 return merged.OrderBy(x => x, StringComparer.Ordinal).ToList();
347 347 }
348 348  
349   - private static ISugarQueryable<FlLabelTypeDbEntity> ApplyLabelTypeScopeFilter(
350   - ISugarQueryable<FlLabelTypeDbEntity> query,
351   - List<string>? scopedLocationIds)
352   - {
353   - if (scopedLocationIds is null)
354   - {
355   - return query;
356   - }
357   -
358   - if (scopedLocationIds.Count == 0)
359   - {
360   - return query.Where(t => t.AvailabilityType == "ALL");
361   - }
362   -
363   - return query.Where(t =>
364   - t.AvailabilityType == "ALL" ||
365   - SqlFunc.Subqueryable<FlLabelTypeLocationDbEntity>()
366   - .Where(tl => tl.LabelTypeId == t.Id && scopedLocationIds.Contains(tl.LocationId))
367   - .Any());
368   - }
369   -
370 349 private async Task SaveTypeLocationsAsync(
371 350 string labelTypeId,
372 351 string availabilityType,
... ...
泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppAuthAppService.cs
... ... @@ -170,7 +170,7 @@ public class UsAppAuthAppService : ApplicationService, IUsAppAuthAppService
170 170 }
171 171  
172 172 var list = await LoadBoundLocationsAsync(CurrentUser.Id.Value);
173   - if (ReportsRoleHelper.IsAdminRole(CurrentUser))
  173 + if (await UsAppAuthScopeHelper.CanUseAdminScopeApisAsync(_dbContext.SqlSugarClient, CurrentUser))
174 174 {
175 175 var cached = await UsAppAuthScopeHelper.GetAdminScopeCacheAsync(
176 176 _distributedCache,
... ... @@ -189,39 +189,40 @@ public class UsAppAuthAppService : ApplicationService, IUsAppAuthAppService
189 189  
190 190 /// <inheritdoc />
191 191 [Authorize]
  192 + [HttpGet("us-app-auth/admin-scope-companies")]
192 193 public virtual async Task<List<AuthScopeCompanyOptionDto>> GetAdminScopeCompaniesAsync()
193 194 {
194   - UsAppAuthScopeHelper.EnsureAdminAppToken(CurrentUser);
195   - return await UsAppAuthScopeHelper.ListCompaniesAsync(_dbContext.SqlSugarClient);
  195 + await UsAppAuthScopeHelper.EnsureAppScopeCallerAsync(_dbContext.SqlSugarClient, CurrentUser);
  196 + return await AuthScopeQueryHelper.GetCompaniesAsync(CurrentUser, _dbContext);
196 197 }
197 198  
198 199 /// <inheritdoc />
199 200 [Authorize]
200   - public virtual async Task<List<AuthScopeRegionOptionDto>> GetAdminScopeRegionsAsync(string partnerId)
  201 + [HttpGet("us-app-auth/admin-scope-regions")]
  202 + public virtual async Task<List<AuthScopeRegionOptionDto>> GetAdminScopeRegionsAsync([FromQuery] string partnerId)
201 203 {
202   - UsAppAuthScopeHelper.EnsureAdminAppToken(CurrentUser);
203   - return await UsAppAuthScopeHelper.ListRegionsAsync(_dbContext.SqlSugarClient, partnerId);
  204 + await UsAppAuthScopeHelper.EnsureAppScopeCallerAsync(_dbContext.SqlSugarClient, CurrentUser);
  205 + return await AuthScopeQueryHelper.GetRegionsAsync(CurrentUser, _dbContext, partnerId);
204 206 }
205 207  
206 208 /// <inheritdoc />
207 209 [Authorize]
  210 + [HttpGet("us-app-auth/admin-scope-locations")]
208 211 public virtual async Task<List<AuthScopeLocationOptionDto>> GetAdminScopeLocationsAsync(
209   - string partnerId,
210   - string groupId)
  212 + [FromQuery] string partnerId,
  213 + [FromQuery] string groupId)
211 214 {
212   - UsAppAuthScopeHelper.EnsureAdminAppToken(CurrentUser);
213   - return await UsAppAuthScopeHelper.ListLocationsAsync(
214   - _dbContext.SqlSugarClient,
215   - partnerId,
216   - groupId);
  215 + await UsAppAuthScopeHelper.EnsureAppScopeCallerAsync(_dbContext.SqlSugarClient, CurrentUser);
  216 + return await AuthScopeQueryHelper.GetLocationsAsync(CurrentUser, _dbContext, partnerId, groupId);
217 217 }
218 218  
219 219 /// <inheritdoc />
220 220 [Authorize]
  221 + [HttpPost("us-app-auth/select-admin-scope-location")]
221 222 public virtual async Task<AuthScopeSelectLocationOutputDto> SelectAdminScopeLocationAsync(
222 223 AuthScopeSelectLocationInputVo input)
223 224 {
224   - UsAppAuthScopeHelper.EnsureAdminAppToken(CurrentUser);
  225 + await UsAppAuthScopeHelper.EnsureAppScopeCallerAsync(_dbContext.SqlSugarClient, CurrentUser);
225 226 if (!CurrentUser.Id.HasValue)
226 227 {
227 228 throw new UserFriendlyException("用户未登录");
... ... @@ -230,6 +231,7 @@ public class UsAppAuthAppService : ApplicationService, IUsAppAuthAppService
230 231 return await UsAppAuthScopeHelper.SelectLocationAsync(
231 232 _dbContext.SqlSugarClient,
232 233 _distributedCache,
  234 + CurrentUser,
233 235 CurrentUser.Id.Value,
234 236 new UsAppSelectAdminScopeLocationInputVo
235 237 {
... ...
泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppLabelingAppService.cs
... ... @@ -364,11 +364,13 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
364 364 }
365 365  
366 366 var labelRow = await _dbContext.SqlSugarClient
367   - .Queryable<FlLabelDbEntity, FlLabelCategoryDbEntity, FlLabelTypeDbEntity, FlLabelTemplateDbEntity>(
368   - (l, c, t, tpl) => l.LabelCategoryId == c.Id && l.LabelTypeId == t.Id && l.TemplateId == tpl.Id)
  367 + .Queryable<FlLabelDbEntity>()
  368 + .InnerJoin<FlLabelCategoryDbEntity>((l, c) => l.LabelCategoryId == c.Id)
  369 + .LeftJoin<FlLabelTypeDbEntity>((l, c, t) => l.LabelTypeId == t.Id)
  370 + .InnerJoin<FlLabelTemplateDbEntity>((l, c, t, tpl) => l.TemplateId == tpl.Id)
369 371 .Where((l, c, t, tpl) => !l.IsDeleted && l.State)
370 372 .Where((l, c, t, tpl) => !c.IsDeleted && c.State)
371   - .Where((l, c, t, tpl) => !t.IsDeleted && t.State)
  373 + .Where((l, c, t, tpl) => t.Id == null || (!t.IsDeleted && t.State))
372 374 .Where((l, c, t, tpl) => !tpl.IsDeleted)
373 375 .Where((l, c, t, tpl) => l.LabelCode == labelCode)
374 376 .Select((l, c, t, tpl) => new
... ... @@ -551,10 +553,11 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
551 553  
552 554 // 校验 label + location,并补齐一些顶部字段用于任务表落库
553 555 var labelRow = await _dbContext.SqlSugarClient
554   - .Queryable<FlLabelDbEntity, FlLabelTypeDbEntity, FlLabelTemplateDbEntity>(
555   - (l, t, tpl) => l.LabelTypeId == t.Id && l.TemplateId == tpl.Id)
  556 + .Queryable<FlLabelDbEntity>()
  557 + .LeftJoin<FlLabelTypeDbEntity>((l, t) => l.LabelTypeId == t.Id)
  558 + .InnerJoin<FlLabelTemplateDbEntity>((l, t, tpl) => l.TemplateId == tpl.Id)
556 559 .Where((l, t, tpl) => !l.IsDeleted && l.State)
557   - .Where((l, t, tpl) => !t.IsDeleted && t.State)
  560 + .Where((l, t, tpl) => t.Id == null || (!t.IsDeleted && t.State))
558 561 .Where((l, t, tpl) => !tpl.IsDeleted)
559 562 .Where((l, t, tpl) => l.LabelCode == labelCode)
560 563 .Select((l, t, tpl) => new
... ...
美国版/Food Labeling Management App UniApp/src/utils/appAdminRole.ts
1 1 import type { UsAppMyProfileOutputDto } from '../services/usAppAuth'
2 2  
3   -/** 与后端 ReportsRoleHelper.IsAdminRole 对齐(App 管理员级联选店) */
  3 +function isCompanyAdminProfile(profile: UsAppMyProfileOutputDto): boolean {
  4 + const code = (profile.primaryRoleCode ?? '').trim().toLowerCase()
  5 + if (code === 'companyadmin' || code.includes('partner')) return true
  6 + const display = (profile.roleDisplay ?? '').trim().toLowerCase()
  7 + if (!display) return false
  8 + if (display === 'company admin') return true
  9 + if (display.includes('partner admin')) return true
  10 + return false
  11 +}
  12 +
  13 +/** 平台管理员或 Company Admin(App 级联选店) */
4 14 export function isAppAdminUser(profile: UsAppMyProfileOutputDto | null | undefined): boolean {
5 15 if (!profile) return false
6 16 const code = (profile.primaryRoleCode ?? '').trim().toLowerCase()
... ... @@ -8,9 +18,9 @@ export function isAppAdminUser(profile: UsAppMyProfileOutputDto | null | undefin
8 18 if (code === 'admin') return true
9 19 if (compactCode === 'companyadmin') return true
10 20 const display = (profile.roleDisplay ?? '').trim().toLowerCase()
11   - if (!display) return false
  21 + if (!display) return isCompanyAdminProfile(profile)
12 22 if (display.includes('administrator')) return true
13 23 if (display.includes('super admin')) return true
14 24 if (display.includes('company admin')) return true
15   - return false
  25 + return isCompanyAdminProfile(profile)
16 26 }
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/AuthScopeQueryHelper.cs
... ... @@ -277,14 +277,10 @@ public static class AuthScopeQueryHelper
277 277 return;
278 278 }
279 279  
280   - var lid = locationId.Trim();
281   - var userIdStr = currentUser.Id!.Value.ToString();
282   - var bound = await dbContext.SqlSugarClient.Queryable<UserLocationDbEntity>()
283   - .AnyAsync(x => !x.IsDeleted && x.UserId == userIdStr && x.LocationId == lid);
284   - if (!bound)
285   - {
286   - throw new UserFriendlyException("当前账号未绑定该门店,无法选择");
287   - }
  280 + await UsAppPrintLogScopeHelper.EnsureUserCanAccessLocationAsync(
  281 + currentUser,
  282 + dbContext.SqlSugarClient,
  283 + locationId);
288 284 }
289 285  
290 286 private static async Task<AuthScopeSelectLocationOutputDto> ResolveAndValidateSelectionAsync(
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/EntityLocationScopeDisplayHelper.cs 0 → 100644
  1 +using FoodLabeling.Domain.Shared;
  2 +using SqlSugar;
  3 +
  4 +namespace FoodLabeling.Application.Helpers;
  5 +
  6 +/// <summary>
  7 +/// 标签类型/分类/多选项等:列表 Region/Location 列展示(适用全部时分别为 All Region / All Location)。
  8 +/// </summary>
  9 +public static class EntityLocationScopeDisplayHelper
  10 +{
  11 + /// <summary>
  12 + /// 根据可用范围类型与已解析的 Region/Location Id、名称,生成列表展示文案。
  13 + /// </summary>
  14 + public static async Task<(string Region, string Location)> BuildListDisplayAsync(
  15 + ISqlSugarClient db,
  16 + string? availabilityType,
  17 + IReadOnlyList<string> regionIds,
  18 + IReadOnlyList<string> locationIds,
  19 + IEnumerable<string> regionNames,
  20 + IEnumerable<string> locationNames,
  21 + IReadOnlyList<string>? partnerIdsForContext)
  22 + {
  23 + if (AllScopeBindingHelper.IsDeclaredAll(availabilityType))
  24 + {
  25 + return (AllScopeBindingHelper.AllRegionsDisplay, AllScopeBindingHelper.AllLocationsDisplay);
  26 + }
  27 +
  28 + var normRegionIds = LocationScopeBindingHelper.NormalizeIds(regionIds);
  29 + var normLocationIds = LocationScopeBindingHelper.NormalizeIds(locationIds);
  30 + var partnerContext = partnerIdsForContext is { Count: > 0 } ? partnerIdsForContext : null;
  31 +
  32 + var allRegionIds = await AllScopeBindingHelper.ResolveAllRegionIdsAsync(db, partnerContext);
  33 + var regionIsAll = normRegionIds.Count > 0
  34 + && allRegionIds.Count > 0
  35 + && AllScopeBindingHelper.IsFullIdSelection(normRegionIds, allRegionIds);
  36 +
  37 + var regionContextForLocations = regionIsAll ? null : normRegionIds.Count > 0 ? normRegionIds : null;
  38 + var allLocationIds = await AllScopeBindingHelper.ResolveAllLocationIdsAsync(
  39 + db,
  40 + partnerContext,
  41 + regionContextForLocations);
  42 + var locationIsAll = normLocationIds.Count > 0
  43 + && allLocationIds.Count > 0
  44 + && AllScopeBindingHelper.IsFullIdSelection(normLocationIds, allLocationIds);
  45 +
  46 + var regionDisplay = regionIsAll
  47 + ? AllScopeBindingHelper.AllRegionsDisplay
  48 + : JoinDistinctNames(regionNames);
  49 +
  50 + var locationDisplay = locationIsAll
  51 + ? AllScopeBindingHelper.AllLocationsDisplay
  52 + : JoinDistinctNames(locationNames);
  53 +
  54 + return (regionDisplay, locationDisplay);
  55 + }
  56 +
  57 + private static string JoinDistinctNames(IEnumerable<string> names)
  58 + {
  59 + var ordered = names
  60 + .Where(x => !string.IsNullOrWhiteSpace(x))
  61 + .Select(x => x.Trim())
  62 + .Distinct(StringComparer.OrdinalIgnoreCase)
  63 + .OrderBy(x => x, StringComparer.OrdinalIgnoreCase)
  64 + .ToList();
  65 + return ordered.Count > 0 ? string.Join(", ", ordered) : FoodLabelingDisplayConsts.NotAvailable;
  66 + }
  67 +}
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelEntityListScopeHelper.cs 0 → 100644
  1 +using FoodLabeling.Application.Services.DbModels;
  2 +using SqlSugar;
  3 +
  4 +namespace FoodLabeling.Application.Helpers;
  5 +
  6 +/// <summary>
  7 +/// 标签类型/分类/多选项列表:非平台管理员仅可见其绑定 Company 范围内数据。
  8 +/// </summary>
  9 +public static class LabelEntityListScopeHelper
  10 +{
  11 + /// <summary>
  12 + /// 标签类型列表:按 Company + Region/Location 范围过滤(Company Admin 不可见 All Companies 数据)。
  13 + /// </summary>
  14 + public static ISugarQueryable<FlLabelTypeDbEntity> ApplyTypeLocationAvailabilityFilter(
  15 + ISugarQueryable<FlLabelTypeDbEntity> query,
  16 + List<string>? scopedLocationIds)
  17 + {
  18 + if (scopedLocationIds is null)
  19 + {
  20 + return query;
  21 + }
  22 +
  23 + if (scopedLocationIds.Count == 0)
  24 + {
  25 + return query.Where(_ => false);
  26 + }
  27 +
  28 + return query.Where(t =>
  29 + (t.AvailabilityType == AllScopeBindingHelper.ScopeAll
  30 + && t.AppliedPartnerType == LabelEntityPartnerScopeHelper.ScopeSpecified)
  31 + || (t.AvailabilityType == AllScopeBindingHelper.ScopeSpecified
  32 + && SqlFunc.Subqueryable<FlLabelTypeLocationDbEntity>()
  33 + .Where(tl => tl.LabelTypeId == t.Id && scopedLocationIds.Contains(tl.LocationId))
  34 + .Any()));
  35 + }
  36 +
  37 + /// <summary>
  38 + /// 标签分类列表:按 Company + Region/Location 范围过滤。
  39 + /// </summary>
  40 + public static ISugarQueryable<FlLabelCategoryDbEntity> ApplyCategoryLocationAvailabilityFilter(
  41 + ISugarQueryable<FlLabelCategoryDbEntity> query,
  42 + List<string>? scopedLocationIds)
  43 + {
  44 + if (scopedLocationIds is null)
  45 + {
  46 + return query;
  47 + }
  48 +
  49 + if (scopedLocationIds.Count == 0)
  50 + {
  51 + return query.Where(_ => false);
  52 + }
  53 +
  54 + return query.Where(c =>
  55 + (c.AvailabilityType == AllScopeBindingHelper.ScopeAll
  56 + && c.AppliedPartnerType == LabelEntityPartnerScopeHelper.ScopeSpecified)
  57 + || (c.AvailabilityType == AllScopeBindingHelper.ScopeSpecified
  58 + && SqlFunc.Subqueryable<FlLabelCategoryLocationDbEntity>()
  59 + .Where(cl => cl.CategoryId == c.Id && scopedLocationIds.Contains(cl.LocationId))
  60 + .Any()));
  61 + }
  62 +
  63 + /// <summary>
  64 + /// 标签多选项列表:按 Company + Region/Location 范围过滤。
  65 + /// </summary>
  66 + public static ISugarQueryable<FlLabelMultipleOptionDbEntity> ApplyMultipleOptionLocationAvailabilityFilter(
  67 + ISugarQueryable<FlLabelMultipleOptionDbEntity> query,
  68 + List<string>? scopedLocationIds)
  69 + {
  70 + if (scopedLocationIds is null)
  71 + {
  72 + return query;
  73 + }
  74 +
  75 + if (scopedLocationIds.Count == 0)
  76 + {
  77 + return query.Where(_ => false);
  78 + }
  79 +
  80 + return query.Where(o =>
  81 + (o.AvailabilityType == AllScopeBindingHelper.ScopeAll
  82 + && o.AppliedPartnerType == LabelEntityPartnerScopeHelper.ScopeSpecified)
  83 + || (o.AvailabilityType == AllScopeBindingHelper.ScopeSpecified
  84 + && SqlFunc.Subqueryable<FlLabelMultipleOptionLocationDbEntity>()
  85 + .Where(ol => ol.MultipleOptionId == o.Id && scopedLocationIds.Contains(ol.LocationId))
  86 + .Any()));
  87 + }
  88 +}
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelEntityPartnerScopeHelper.cs
... ... @@ -348,17 +348,12 @@ public static class LabelEntityPartnerScopeHelper
348 348  
349 349 if (scopedPartnerIds.Count == 0)
350 350 {
351   - return query.Where(t =>
352   - !SqlFunc.Subqueryable<FlLabelTypePartnerDbEntity>()
353   - .Where(p => p.LabelTypeId == t.Id)
354   - .Any());
  351 + return query.Where(_ => false);
355 352 }
356 353  
357 354 return query.Where(t =>
358   - !SqlFunc.Subqueryable<FlLabelTypePartnerDbEntity>()
359   - .Where(p => p.LabelTypeId == t.Id)
360   - .Any()
361   - || SqlFunc.Subqueryable<FlLabelTypePartnerDbEntity>()
  355 + t.AppliedPartnerType == ScopeSpecified
  356 + && SqlFunc.Subqueryable<FlLabelTypePartnerDbEntity>()
362 357 .Where(p => p.LabelTypeId == t.Id && scopedPartnerIds.Contains(p.PartnerId))
363 358 .Any());
364 359 }
... ... @@ -384,17 +379,12 @@ public static class LabelEntityPartnerScopeHelper
384 379  
385 380 if (scopedPartnerIds.Count == 0)
386 381 {
387   - return query.Where(c =>
388   - !SqlFunc.Subqueryable<FlLabelCategoryPartnerDbEntity>()
389   - .Where(p => p.CategoryId == c.Id)
390   - .Any());
  382 + return query.Where(_ => false);
391 383 }
392 384  
393 385 return query.Where(c =>
394   - !SqlFunc.Subqueryable<FlLabelCategoryPartnerDbEntity>()
395   - .Where(p => p.CategoryId == c.Id)
396   - .Any()
397   - || SqlFunc.Subqueryable<FlLabelCategoryPartnerDbEntity>()
  386 + c.AppliedPartnerType == ScopeSpecified
  387 + && SqlFunc.Subqueryable<FlLabelCategoryPartnerDbEntity>()
398 388 .Where(p => p.CategoryId == c.Id && scopedPartnerIds.Contains(p.PartnerId))
399 389 .Any());
400 390 }
... ... @@ -420,17 +410,12 @@ public static class LabelEntityPartnerScopeHelper
420 410  
421 411 if (scopedPartnerIds.Count == 0)
422 412 {
423   - return query.Where(o =>
424   - !SqlFunc.Subqueryable<FlLabelMultipleOptionPartnerDbEntity>()
425   - .Where(p => p.MultipleOptionId == o.Id)
426   - .Any());
  413 + return query.Where(_ => false);
427 414 }
428 415  
429 416 return query.Where(o =>
430   - !SqlFunc.Subqueryable<FlLabelMultipleOptionPartnerDbEntity>()
431   - .Where(p => p.MultipleOptionId == o.Id)
432   - .Any()
433   - || SqlFunc.Subqueryable<FlLabelMultipleOptionPartnerDbEntity>()
  417 + o.AppliedPartnerType == ScopeSpecified
  418 + && SqlFunc.Subqueryable<FlLabelMultipleOptionPartnerDbEntity>()
434 419 .Where(p => p.MultipleOptionId == o.Id && scopedPartnerIds.Contains(p.PartnerId))
435 420 .Any());
436 421 }
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelTemplateScopeHelper.cs
... ... @@ -437,23 +437,7 @@ public static class LabelTemplateScopeHelper
437 437  
438 438 if (scopedLocationIds.Count == 0)
439 439 {
440   - return hasExtendedScope
441   - ? query.Where(t =>
442   - t.AppliedLocationType == ScopeAll
443   - && !SqlFunc.Subqueryable<FlLabelTemplateLocationDbEntity>()
444   - .Where(l => l.TemplateId == t.Id)
445   - .Any()
446   - && !SqlFunc.Subqueryable<FlLabelTemplatePartnerDbEntity>()
447   - .Where(p => p.TemplateId == t.Id)
448   - .Any()
449   - && !SqlFunc.Subqueryable<FlLabelTemplateRegionDbEntity>()
450   - .Where(r => r.TemplateId == t.Id)
451   - .Any())
452   - : query.Where(t =>
453   - t.AppliedLocationType == ScopeAll
454   - && !SqlFunc.Subqueryable<FlLabelTemplateLocationDbEntity>()
455   - .Where(l => l.TemplateId == t.Id)
456   - .Any());
  440 + return query.Where(_ => false);
457 441 }
458 442  
459 443 if (!hasExtendedScope)
... ... @@ -470,12 +454,9 @@ public static class LabelTemplateScopeHelper
470 454 db, scopedLocationIds);
471 455  
472 456 return query.Where(t =>
473   - (!SqlFunc.Subqueryable<FlLabelTemplatePartnerDbEntity>()
474   - .Where(p => p.TemplateId == t.Id)
475   - .Any()
476   - || SqlFunc.Subqueryable<FlLabelTemplatePartnerDbEntity>()
477   - .Where(p => p.TemplateId == t.Id && scopedPartnerIds.Contains(p.PartnerId))
478   - .Any())
  457 + SqlFunc.Subqueryable<FlLabelTemplatePartnerDbEntity>()
  458 + .Where(p => p.TemplateId == t.Id && scopedPartnerIds.Contains(p.PartnerId))
  459 + .Any()
479 460 && (!SqlFunc.Subqueryable<FlLabelTemplateRegionDbEntity>()
480 461 .Where(r => r.TemplateId == t.Id)
481 462 .Any()
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LocationRegionScopeHelper.cs
... ... @@ -25,6 +25,7 @@ public static class LocationRegionScopeHelper
25 25  
26 26 /// <summary>
27 27 /// 解析当前用户可查询的 Region(公司 + 组织名)集合。
  28 + /// Company Admin 展开为绑定 Company 下全部 Region(与 <see cref="GroupListScopeHelper"/> 一致)。
28 29 /// </summary>
29 30 public static async Task<LocationListScopeFilter> ResolveLocationListScopeAsync(
30 31 ICurrentUser currentUser,
... ... @@ -40,8 +41,15 @@ public static class LocationRegionScopeHelper
40 41 return new LocationListScopeFilter();
41 42 }
42 43  
  44 + var db = dbContext.SqlSugarClient;
  45 +
  46 + if (await TeamMemberRoleHelper.IsCompanyAdminUserAsync(db, currentUser))
  47 + {
  48 + return await ResolveCompanyAdminLocationListScopeAsync(db, currentUser.Id.Value);
  49 + }
  50 +
43 51 var userId = currentUser.Id.Value.ToString();
44   - var rows = await dbContext.SqlSugarClient.Queryable<UserLocationDbEntity>()
  52 + var rows = await db.Queryable<UserLocationDbEntity>()
45 53 .InnerJoin<LocationAggregateRoot>((ul, loc) =>
46 54 !loc.IsDeleted && ul.LocationId == loc.Id.ToString())
47 55 .Where(ul => !ul.IsDeleted && ul.UserId == userId)
... ... @@ -57,6 +65,59 @@ public static class LocationRegionScopeHelper
57 65 return new LocationListScopeFilter { RegionKeys = keys };
58 66 }
59 67  
  68 + private static async Task<LocationListScopeFilter> ResolveCompanyAdminLocationListScopeAsync(
  69 + ISqlSugarClient db,
  70 + Guid userId)
  71 + {
  72 + var locationIds = await LocationScopeBindingHelper.ResolveBoundLocationIdsForUserAsync(db, userId);
  73 + if (locationIds.Count == 0)
  74 + {
  75 + return new LocationListScopeFilter();
  76 + }
  77 +
  78 + var partnerIds = await LocationScopeBindingHelper.ResolvePartnerIdsFromLocationIdsAsync(db, locationIds);
  79 + if (partnerIds.Count == 0)
  80 + {
  81 + return new LocationListScopeFilter();
  82 + }
  83 +
  84 + var partners = await db.Queryable<FlPartnerDbEntity>()
  85 + .Where(p => !p.IsDeleted && partnerIds.Contains(p.Id))
  86 + .Select(p => new { p.Id, p.PartnerName })
  87 + .ToListAsync();
  88 +
  89 + var groups = await db.Queryable<FlGroupDbEntity>()
  90 + .Where(g => !g.IsDeleted && partnerIds.Contains(g.PartnerId))
  91 + .Select(g => new { g.PartnerId, g.GroupName })
  92 + .ToListAsync();
  93 +
  94 + var keys = new HashSet<(string Partner, string GroupName)>();
  95 + foreach (var group in groups)
  96 + {
  97 + var groupName = group.GroupName?.Trim();
  98 + if (string.IsNullOrEmpty(groupName))
  99 + {
  100 + continue;
  101 + }
  102 +
  103 + var partner = partners.FirstOrDefault(p =>
  104 + string.Equals(p.Id, group.PartnerId, StringComparison.OrdinalIgnoreCase));
  105 + var partnerName = partner?.PartnerName?.Trim();
  106 + if (!string.IsNullOrEmpty(partnerName))
  107 + {
  108 + keys.Add((partnerName, groupName));
  109 + }
  110 +
  111 + var partnerId = group.PartnerId?.Trim();
  112 + if (!string.IsNullOrEmpty(partnerId))
  113 + {
  114 + keys.Add((partnerId, groupName));
  115 + }
  116 + }
  117 +
  118 + return new LocationListScopeFilter { RegionKeys = keys.ToList() };
  119 + }
  120 +
60 121 /// <summary>
61 122 /// 将 Region 范围应用到 <c>location</c> 查询(须在其它 Where 之前或之后均可,建议在 IsDeleted 之后)。
62 123 /// </summary>
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/UsAppAuthScopeHelper.cs
... ... @@ -19,7 +19,7 @@ public static class UsAppAuthScopeHelper
19 19 {
20 20 private const string CacheKeyPrefix = "FoodLabeling:UsAppAdminScope:";
21 21  
22   - public static void EnsureAdminAppToken(ICurrentUser currentUser)
  22 + public static async Task EnsureAppScopeCallerAsync(ISqlSugarClient db, ICurrentUser currentUser)
23 23 {
24 24 if (currentUser.Id is null)
25 25 {
... ... @@ -32,12 +32,30 @@ public static class UsAppAuthScopeHelper
32 32 throw new UserFriendlyException("请使用 App 登录令牌调用该接口");
33 33 }
34 34  
35   - if (!ReportsRoleHelper.IsAdminRole(currentUser))
  35 + if (!await CanUseAdminScopeApisAsync(db, currentUser))
36 36 {
37   - throw new UserFriendlyException("仅管理员可使用公司/区域/门店筛选接口");
  37 + throw new UserFriendlyException("仅管理员或 Company Admin 可使用公司/区域/门店筛选接口");
38 38 }
39 39 }
40 40  
  41 + /// <summary>
  42 + /// 平台管理员或 Company Admin 可使用 App 级联选店接口。
  43 + /// </summary>
  44 + public static async Task<bool> CanUseAdminScopeApisAsync(ISqlSugarClient db, ICurrentUser currentUser)
  45 + {
  46 + if (currentUser.Id is null)
  47 + {
  48 + return false;
  49 + }
  50 +
  51 + if (ReportsRoleHelper.IsAdminRole(currentUser))
  52 + {
  53 + return true;
  54 + }
  55 +
  56 + return await TeamMemberRoleHelper.IsCompanyAdminUserAsync(db, currentUser);
  57 + }
  58 +
41 59 public static async Task<List<AuthScopeCompanyOptionDto>> ListCompaniesAsync(ISqlSugarClient db)
42 60 {
43 61 return await db.Queryable<FlPartnerDbEntity>()
... ... @@ -119,6 +137,7 @@ public static class UsAppAuthScopeHelper
119 137 public static async Task<AuthScopeSelectLocationOutputDto> SelectLocationAsync(
120 138 ISqlSugarClient db,
121 139 IDistributedCache cache,
  140 + ICurrentUser currentUser,
122 141 Guid userId,
123 142 UsAppSelectAdminScopeLocationInputVo input)
124 143 {
... ... @@ -130,6 +149,11 @@ public static class UsAppAuthScopeHelper
130 149 throw new UserFriendlyException("请选择公司、区域和门店");
131 150 }
132 151  
  152 + if (!ReportsRoleHelper.IsAdminRole(currentUser))
  153 + {
  154 + await UsAppPrintLogScopeHelper.EnsureUserCanAccessLocationAsync(currentUser, db, locationId);
  155 + }
  156 +
133 157 var scopedIds = await LocationScopeBindingHelper.ResolveFilteredLocationIdsForListAsync(
134 158 db,
135 159 partnerId,
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelAppService.cs
... ... @@ -62,38 +62,28 @@ public class LabelAppService : ApplicationService, ILabelAppService
62 62  
63 63 var db = _dbContext.SqlSugarClient;
64 64 var regionSchema = await LabelRegionSchemaHelper.GetStatusAsync(db);
65   - var scopedLocationIds = await LocationScopeBindingHelper.ResolveScopedLocationIdsAsync(
66   - db, groupId, locationId);
  65 + var scopedLocationIds = await TeamMemberListScopeHelper.ResolveListLocationIdsAsync(
  66 + CurrentUser,
  67 + _dbContext,
  68 + partnerId,
  69 + groupId,
  70 + locationId);
67 71 var filterGroupId = groupId?.Trim();
68   - if (!string.IsNullOrWhiteSpace(filterGroupId))
  72 + if (scopedLocationIds is not null)
69 73 {
70   - labelIdsQuery = scopedLocationIds is { Count: 0 }
71   - ? labelIdsQuery.Where(_ => false)
72   - : LabelRegionScopeHelper.ApplyLabelRegionListFilter(
  74 + if (scopedLocationIds.Count == 0)
  75 + {
  76 + labelIdsQuery = labelIdsQuery.Where(_ => false);
  77 + }
  78 + else if (!string.IsNullOrWhiteSpace(filterGroupId))
  79 + {
  80 + labelIdsQuery = LabelRegionScopeHelper.ApplyLabelRegionListFilter(
73 81 db, labelIdsQuery, filterGroupId, scopedLocationIds, regionSchema);
74   - }
75   - else if (scopedLocationIds is not null)
76   - {
77   - labelIdsQuery = scopedLocationIds.Count == 0
78   - ? labelIdsQuery.Where(_ => false)
79   - : LabelRegionScopeHelper.ApplyLabelLocationListFilter(
80   - db, labelIdsQuery, scopedLocationIds, regionSchema);
81   - }
82   - else if (!string.IsNullOrWhiteSpace(groupId))
83   - {
84   - labelIdsQuery = LabelRegionScopeHelper.ApplyGroupIdListFilter(
85   - db, labelIdsQuery, groupId, regionSchema);
86   - }
87   - else if (!string.IsNullOrWhiteSpace(partnerId))
88   - {
89   - var partnerLocationIds = await LocationScopeBindingHelper.ResolveFilteredLocationIdsForListAsync(
90   - db, partnerId, null, null);
91   - if (partnerLocationIds is not null)
  82 + }
  83 + else
92 84 {
93   - labelIdsQuery = partnerLocationIds.Count == 0
94   - ? labelIdsQuery.Where(_ => false)
95   - : LabelRegionScopeHelper.ApplyLabelLocationListFilter(
96   - db, labelIdsQuery, partnerLocationIds, regionSchema);
  85 + labelIdsQuery = LabelRegionScopeHelper.ApplyLabelLocationListFilter(
  86 + db, labelIdsQuery, scopedLocationIds, regionSchema);
97 87 }
98 88 }
99 89  
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelCategoryAppService.cs
... ... @@ -71,11 +71,11 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
71 71 var ids = entities.Select(x => x.Id).ToList();
72 72  
73 73 var labelStatsMap = await BuildCategoryLabelStatsMapAsync(ids);
74   - var scopeMap = await BuildCategoryScopeMapAsync(entities);
75 74 var partnerScopeMap = await LabelEntityPartnerScopeHelper.BuildPartnerScopeDisplayMapAsync(
76 75 _dbContext.SqlSugarClient,
77 76 LabelEntityPartnerScopeHelper.LabelEntityPartnerKind.Category,
78 77 ids);
  78 + var scopeMap = await BuildCategoryScopeMapAsync(entities, partnerScopeMap);
79 79  
80 80 var items = entities.Select(x =>
81 81 {
... ... @@ -370,16 +370,7 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
370 370 return query;
371 371 }
372 372  
373   - if (scopedLocationIds.Count == 0)
374   - {
375   - return query.Where(c => c.AvailabilityType == "ALL");
376   - }
377   -
378   - return query.Where(c =>
379   - c.AvailabilityType == "ALL" ||
380   - SqlFunc.Subqueryable<FlLabelCategoryLocationDbEntity>()
381   - .Where(cl => cl.CategoryId == c.Id && scopedLocationIds.Contains(cl.LocationId))
382   - .Any());
  373 + return LabelEntityListScopeHelper.ApplyCategoryLocationAvailabilityFilter(query, scopedLocationIds);
383 374 }
384 375  
385 376 private async Task SaveCategoryPartnerScopeAsync(
... ... @@ -420,7 +411,8 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
420 411 private const string EmptyDisplay = FoodLabelingDisplayConsts.NotAvailable;
421 412  
422 413 private async Task<Dictionary<string, CategoryScopeData>> BuildCategoryScopeMapAsync(
423   - List<FlLabelCategoryDbEntity> entities)
  414 + List<FlLabelCategoryDbEntity> entities,
  415 + Dictionary<string, LabelEntityPartnerScopeHelper.LabelEntityPartnerScopeDisplay> partnerScopeMap)
424 416 {
425 417 var result = new Dictionary<string, CategoryScopeData>(StringComparer.Ordinal);
426 418 if (entities.Count == 0)
... ... @@ -428,8 +420,9 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
428 420 return result;
429 421 }
430 422  
431   - foreach (var e in entities.Where(x =>
432   - !string.Equals(x.AvailabilityType, "SPECIFIED", StringComparison.OrdinalIgnoreCase)))
  423 + var entityById = entities.ToDictionary(x => x.Id, StringComparer.Ordinal);
  424 +
  425 + foreach (var e in entities.Where(x => AllScopeBindingHelper.IsDeclaredAll(x.AvailabilityType)))
433 426 {
434 427 result[e.Id] = new CategoryScopeData
435 428 {
... ... @@ -525,14 +518,29 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
525 518 var regionIds = await LocationScopeBindingHelper.ResolveGroupIdsFromLocationIdsAsync(
526 519 _dbContext.SqlSugarClient, locationIds);
527 520  
  521 + entityById.TryGetValue(catId, out var entity);
  522 + partnerScopeMap.TryGetValue(catId, out var partnerScope);
  523 + var partnerContext = entity is not null
  524 + && string.Equals(
  525 + entity.AppliedPartnerType,
  526 + LabelEntityPartnerScopeHelper.ScopeSpecified,
  527 + StringComparison.OrdinalIgnoreCase)
  528 + ? partnerScope?.PartnerIds
  529 + : null;
  530 +
  531 + var (regionDisplay, locationDisplay) = await EntityLocationScopeDisplayHelper.BuildListDisplayAsync(
  532 + _dbContext.SqlSugarClient,
  533 + entity?.AvailabilityType,
  534 + regionIds,
  535 + locationIds,
  536 + regions,
  537 + locationNames,
  538 + partnerContext);
  539 +
528 540 result[catId] = new CategoryScopeData
529 541 {
530   - Region = regions.Count > 0
531   - ? string.Join(", ", regions.OrderBy(x => x, StringComparer.OrdinalIgnoreCase))
532   - : EmptyDisplay,
533   - Location = locationNames.Count > 0
534   - ? string.Join(", ", locationNames.OrderBy(x => x, StringComparer.OrdinalIgnoreCase))
535   - : EmptyDisplay,
  542 + Region = regionDisplay,
  543 + Location = locationDisplay,
536 544 RegionIds = regionIds,
537 545 LocationIds = locationIds
538 546 };
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelMultipleOptionAppService.cs
... ... @@ -43,7 +43,7 @@ public class LabelMultipleOptionAppService : ApplicationService, ILabelMultipleO
43 43  
44 44 query = await LabelEntityPartnerScopeHelper.ApplyMultipleOptionPartnerListFilterAsync(
45 45 _dbContext.SqlSugarClient, query, scopedPartnerIds);
46   - query = ApplyMultipleOptionScopeFilter(query, scopedLocationIds);
  46 + query = LabelEntityListScopeHelper.ApplyMultipleOptionLocationAvailabilityFilter(query, scopedLocationIds);
47 47  
48 48 if (!string.IsNullOrWhiteSpace(input.Sorting))
49 49 {
... ... @@ -395,27 +395,6 @@ public class LabelMultipleOptionAppService : ApplicationService, ILabelMultipleO
395 395 };
396 396 }
397 397  
398   - private static ISugarQueryable<FlLabelMultipleOptionDbEntity> ApplyMultipleOptionScopeFilter(
399   - ISugarQueryable<FlLabelMultipleOptionDbEntity> query,
400   - List<string>? scopedLocationIds)
401   - {
402   - if (scopedLocationIds is null)
403   - {
404   - return query;
405   - }
406   -
407   - if (scopedLocationIds.Count == 0)
408   - {
409   - return query.Where(o => o.AvailabilityType == "ALL");
410   - }
411   -
412   - return query.Where(o =>
413   - o.AvailabilityType == "ALL" ||
414   - SqlFunc.Subqueryable<FlLabelMultipleOptionLocationDbEntity>()
415   - .Where(ol => ol.MultipleOptionId == o.Id && scopedLocationIds.Contains(ol.LocationId))
416   - .Any());
417   - }
418   -
419 398 private async Task<Dictionary<string, MultipleOptionScopeData>> BuildMultipleOptionScopeMapAsync(
420 399 List<FlLabelMultipleOptionDbEntity> entities)
421 400 {
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelTypeAppService.cs
... ... @@ -43,7 +43,7 @@ public class LabelTypeAppService : ApplicationService, ILabelTypeAppService
43 43  
44 44 query = await LabelEntityPartnerScopeHelper.ApplyTypePartnerListFilterAsync(
45 45 _dbContext.SqlSugarClient, query, scopedPartnerIds);
46   - query = ApplyLabelTypeScopeFilter(query, scopedLocationIds);
  46 + query = LabelEntityListScopeHelper.ApplyTypeLocationAvailabilityFilter(query, scopedLocationIds);
47 47  
48 48 if (!string.IsNullOrWhiteSpace(input.Sorting))
49 49 {
... ... @@ -346,27 +346,6 @@ public class LabelTypeAppService : ApplicationService, ILabelTypeAppService
346 346 return merged.OrderBy(x => x, StringComparer.Ordinal).ToList();
347 347 }
348 348  
349   - private static ISugarQueryable<FlLabelTypeDbEntity> ApplyLabelTypeScopeFilter(
350   - ISugarQueryable<FlLabelTypeDbEntity> query,
351   - List<string>? scopedLocationIds)
352   - {
353   - if (scopedLocationIds is null)
354   - {
355   - return query;
356   - }
357   -
358   - if (scopedLocationIds.Count == 0)
359   - {
360   - return query.Where(t => t.AvailabilityType == "ALL");
361   - }
362   -
363   - return query.Where(t =>
364   - t.AvailabilityType == "ALL" ||
365   - SqlFunc.Subqueryable<FlLabelTypeLocationDbEntity>()
366   - .Where(tl => tl.LabelTypeId == t.Id && scopedLocationIds.Contains(tl.LocationId))
367   - .Any());
368   - }
369   -
370 349 private async Task SaveTypeLocationsAsync(
371 350 string labelTypeId,
372 351 string availabilityType,
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppAuthAppService.cs
... ... @@ -161,7 +161,7 @@ public class UsAppAuthAppService : ApplicationService, IUsAppAuthAppService
161 161 }
162 162  
163 163 var list = await LoadBoundLocationsAsync(CurrentUser.Id.Value);
164   - if (ReportsRoleHelper.IsAdminRole(CurrentUser))
  164 + if (await UsAppAuthScopeHelper.CanUseAdminScopeApisAsync(_dbContext.SqlSugarClient, CurrentUser))
165 165 {
166 166 var cached = await UsAppAuthScopeHelper.GetAdminScopeCacheAsync(
167 167 _distributedCache,
... ... @@ -180,39 +180,40 @@ public class UsAppAuthAppService : ApplicationService, IUsAppAuthAppService
180 180  
181 181 /// <inheritdoc />
182 182 [Authorize]
  183 + [HttpGet("us-app-auth/admin-scope-companies")]
183 184 public virtual async Task<List<AuthScopeCompanyOptionDto>> GetAdminScopeCompaniesAsync()
184 185 {
185   - UsAppAuthScopeHelper.EnsureAdminAppToken(CurrentUser);
186   - return await UsAppAuthScopeHelper.ListCompaniesAsync(_dbContext.SqlSugarClient);
  186 + await UsAppAuthScopeHelper.EnsureAppScopeCallerAsync(_dbContext.SqlSugarClient, CurrentUser);
  187 + return await AuthScopeQueryHelper.GetCompaniesAsync(CurrentUser, _dbContext);
187 188 }
188 189  
189 190 /// <inheritdoc />
190 191 [Authorize]
191   - public virtual async Task<List<AuthScopeRegionOptionDto>> GetAdminScopeRegionsAsync(string partnerId)
  192 + [HttpGet("us-app-auth/admin-scope-regions")]
  193 + public virtual async Task<List<AuthScopeRegionOptionDto>> GetAdminScopeRegionsAsync([FromQuery] string partnerId)
192 194 {
193   - UsAppAuthScopeHelper.EnsureAdminAppToken(CurrentUser);
194   - return await UsAppAuthScopeHelper.ListRegionsAsync(_dbContext.SqlSugarClient, partnerId);
  195 + await UsAppAuthScopeHelper.EnsureAppScopeCallerAsync(_dbContext.SqlSugarClient, CurrentUser);
  196 + return await AuthScopeQueryHelper.GetRegionsAsync(CurrentUser, _dbContext, partnerId);
195 197 }
196 198  
197 199 /// <inheritdoc />
198 200 [Authorize]
  201 + [HttpGet("us-app-auth/admin-scope-locations")]
199 202 public virtual async Task<List<AuthScopeLocationOptionDto>> GetAdminScopeLocationsAsync(
200   - string partnerId,
201   - string groupId)
  203 + [FromQuery] string partnerId,
  204 + [FromQuery] string groupId)
202 205 {
203   - UsAppAuthScopeHelper.EnsureAdminAppToken(CurrentUser);
204   - return await UsAppAuthScopeHelper.ListLocationsAsync(
205   - _dbContext.SqlSugarClient,
206   - partnerId,
207   - groupId);
  206 + await UsAppAuthScopeHelper.EnsureAppScopeCallerAsync(_dbContext.SqlSugarClient, CurrentUser);
  207 + return await AuthScopeQueryHelper.GetLocationsAsync(CurrentUser, _dbContext, partnerId, groupId);
208 208 }
209 209  
210 210 /// <inheritdoc />
211 211 [Authorize]
  212 + [HttpPost("us-app-auth/select-admin-scope-location")]
212 213 public virtual async Task<AuthScopeSelectLocationOutputDto> SelectAdminScopeLocationAsync(
213 214 AuthScopeSelectLocationInputVo input)
214 215 {
215   - UsAppAuthScopeHelper.EnsureAdminAppToken(CurrentUser);
  216 + await UsAppAuthScopeHelper.EnsureAppScopeCallerAsync(_dbContext.SqlSugarClient, CurrentUser);
216 217 if (!CurrentUser.Id.HasValue)
217 218 {
218 219 throw new UserFriendlyException("用户未登录");
... ... @@ -221,6 +222,7 @@ public class UsAppAuthAppService : ApplicationService, IUsAppAuthAppService
221 222 return await UsAppAuthScopeHelper.SelectLocationAsync(
222 223 _dbContext.SqlSugarClient,
223 224 _distributedCache,
  225 + CurrentUser,
224 226 CurrentUser.Id.Value,
225 227 new UsAppSelectAdminScopeLocationInputVo
226 228 {
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppLabelingAppService.cs
... ... @@ -364,11 +364,13 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
364 364 }
365 365  
366 366 var labelRow = await _dbContext.SqlSugarClient
367   - .Queryable<FlLabelDbEntity, FlLabelCategoryDbEntity, FlLabelTypeDbEntity, FlLabelTemplateDbEntity>(
368   - (l, c, t, tpl) => l.LabelCategoryId == c.Id && l.LabelTypeId == t.Id && l.TemplateId == tpl.Id)
  367 + .Queryable<FlLabelDbEntity>()
  368 + .InnerJoin<FlLabelCategoryDbEntity>((l, c) => l.LabelCategoryId == c.Id)
  369 + .LeftJoin<FlLabelTypeDbEntity>((l, c, t) => l.LabelTypeId == t.Id)
  370 + .InnerJoin<FlLabelTemplateDbEntity>((l, c, t, tpl) => l.TemplateId == tpl.Id)
369 371 .Where((l, c, t, tpl) => !l.IsDeleted && l.State)
370 372 .Where((l, c, t, tpl) => !c.IsDeleted && c.State)
371   - .Where((l, c, t, tpl) => !t.IsDeleted && t.State)
  373 + .Where((l, c, t, tpl) => t.Id == null || (!t.IsDeleted && t.State))
372 374 .Where((l, c, t, tpl) => !tpl.IsDeleted)
373 375 .Where((l, c, t, tpl) => l.LabelCode == labelCode)
374 376 .Select((l, c, t, tpl) => new
... ... @@ -551,10 +553,11 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
551 553  
552 554 // 校验 label + location,并补齐一些顶部字段用于任务表落库
553 555 var labelRow = await _dbContext.SqlSugarClient
554   - .Queryable<FlLabelDbEntity, FlLabelTypeDbEntity, FlLabelTemplateDbEntity>(
555   - (l, t, tpl) => l.LabelTypeId == t.Id && l.TemplateId == tpl.Id)
  556 + .Queryable<FlLabelDbEntity>()
  557 + .LeftJoin<FlLabelTypeDbEntity>((l, t) => l.LabelTypeId == t.Id)
  558 + .InnerJoin<FlLabelTemplateDbEntity>((l, t, tpl) => l.TemplateId == tpl.Id)
556 559 .Where((l, t, tpl) => !l.IsDeleted && l.State)
557   - .Where((l, t, tpl) => !t.IsDeleted && t.State)
  560 + .Where((l, t, tpl) => t.Id == null || (!t.IsDeleted && t.State))
558 561 .Where((l, t, tpl) => !tpl.IsDeleted)
559 562 .Where((l, t, tpl) => l.LabelCode == labelCode)
560 563 .Select((l, t, tpl) => new
... ...
美国版/Food Labeling Management Platform/src/services/labelCategoryService.ts
... ... @@ -49,6 +49,8 @@ function normalizeLabelCategoryDto(raw: unknown): LabelCategoryDto {
49 49 ? Number(orderRaw)
50 50 : null;
51 51 const companyRaw = r.company ?? r.Company;
  52 + const regionRaw = r.region ?? r.Region;
  53 + const locationRaw = r.location ?? r.Location;
52 54 return {
53 55 ...(r as object),
54 56 id: String(r.id ?? r.Id ?? ""),
... ... @@ -60,6 +62,8 @@ function normalizeLabelCategoryDto(raw: unknown): LabelCategoryDto {
60 62 regionIds: groupIds,
61 63 locationIds,
62 64 company: typeof companyRaw === "string" ? companyRaw : null,
  65 + region: typeof regionRaw === "string" ? regionRaw : null,
  66 + location: typeof locationRaw === "string" ? locationRaw : null,
63 67 noOfLabels,
64 68 orderNum,
65 69 } as LabelCategoryDto;
... ...