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 import type { UsAppMyProfileOutputDto } from '../services/usAppAuth' 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 export function isAppAdminUser(profile: UsAppMyProfileOutputDto | null | undefined): boolean { 14 export function isAppAdminUser(profile: UsAppMyProfileOutputDto | null | undefined): boolean {
5 if (!profile) return false 15 if (!profile) return false
6 const code = (profile.primaryRoleCode ?? '').trim().toLowerCase() 16 const code = (profile.primaryRoleCode ?? '').trim().toLowerCase()
7 if (code === 'admin') return true 17 if (code === 'admin') return true
8 const display = (profile.roleDisplay ?? '').trim().toLowerCase() 18 const display = (profile.roleDisplay ?? '').trim().toLowerCase()
9 - if (!display) return false 19 + if (!display) return isCompanyAdminProfile(profile)
10 if (display.includes('administrator')) return true 20 if (display.includes('administrator')) return true
11 if (display.includes('super admin')) return true 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,14 +277,10 @@ public static class AuthScopeQueryHelper
277 return; 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 private static async Task<AuthScopeSelectLocationOutputDto> ResolveAndValidateSelectionAsync( 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,17 +348,12 @@ public static class LabelEntityPartnerScopeHelper
348 348
349 if (scopedPartnerIds.Count == 0) 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 return query.Where(t => 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 .Where(p => p.LabelTypeId == t.Id && scopedPartnerIds.Contains(p.PartnerId)) 357 .Where(p => p.LabelTypeId == t.Id && scopedPartnerIds.Contains(p.PartnerId))
363 .Any()); 358 .Any());
364 } 359 }
@@ -384,17 +379,12 @@ public static class LabelEntityPartnerScopeHelper @@ -384,17 +379,12 @@ public static class LabelEntityPartnerScopeHelper
384 379
385 if (scopedPartnerIds.Count == 0) 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 return query.Where(c => 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 .Where(p => p.CategoryId == c.Id && scopedPartnerIds.Contains(p.PartnerId)) 388 .Where(p => p.CategoryId == c.Id && scopedPartnerIds.Contains(p.PartnerId))
399 .Any()); 389 .Any());
400 } 390 }
@@ -420,17 +410,12 @@ public static class LabelEntityPartnerScopeHelper @@ -420,17 +410,12 @@ public static class LabelEntityPartnerScopeHelper
420 410
421 if (scopedPartnerIds.Count == 0) 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 return query.Where(o => 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 .Where(p => p.MultipleOptionId == o.Id && scopedPartnerIds.Contains(p.PartnerId)) 419 .Where(p => p.MultipleOptionId == o.Id && scopedPartnerIds.Contains(p.PartnerId))
435 .Any()); 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,23 +437,7 @@ public static class LabelTemplateScopeHelper
437 437
438 if (scopedLocationIds.Count == 0) 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 if (!hasExtendedScope) 443 if (!hasExtendedScope)
@@ -470,12 +454,9 @@ public static class LabelTemplateScopeHelper @@ -470,12 +454,9 @@ public static class LabelTemplateScopeHelper
470 db, scopedLocationIds); 454 db, scopedLocationIds);
471 455
472 return query.Where(t => 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 && (!SqlFunc.Subqueryable<FlLabelTemplateRegionDbEntity>() 460 && (!SqlFunc.Subqueryable<FlLabelTemplateRegionDbEntity>()
480 .Where(r => r.TemplateId == t.Id) 461 .Where(r => r.TemplateId == t.Id)
481 .Any() 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,6 +25,7 @@ public static class LocationRegionScopeHelper
25 25
26 /// <summary> 26 /// <summary>
27 /// 解析当前用户可查询的 Region(公司 + 组织名)集合。 27 /// 解析当前用户可查询的 Region(公司 + 组织名)集合。
  28 + /// Company Admin 展开为绑定 Company 下全部 Region(与 <see cref="GroupListScopeHelper"/> 一致)。
28 /// </summary> 29 /// </summary>
29 public static async Task<LocationListScopeFilter> ResolveLocationListScopeAsync( 30 public static async Task<LocationListScopeFilter> ResolveLocationListScopeAsync(
30 ICurrentUser currentUser, 31 ICurrentUser currentUser,
@@ -40,8 +41,15 @@ public static class LocationRegionScopeHelper @@ -40,8 +41,15 @@ public static class LocationRegionScopeHelper
40 return new LocationListScopeFilter(); 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 var userId = currentUser.Id.Value.ToString(); 51 var userId = currentUser.Id.Value.ToString();
44 - var rows = await dbContext.SqlSugarClient.Queryable<UserLocationDbEntity>() 52 + var rows = await db.Queryable<UserLocationDbEntity>()
45 .InnerJoin<LocationAggregateRoot>((ul, loc) => 53 .InnerJoin<LocationAggregateRoot>((ul, loc) =>
46 !loc.IsDeleted && ul.LocationId == loc.Id.ToString()) 54 !loc.IsDeleted && ul.LocationId == loc.Id.ToString())
47 .Where(ul => !ul.IsDeleted && ul.UserId == userId) 55 .Where(ul => !ul.IsDeleted && ul.UserId == userId)
@@ -57,6 +65,59 @@ public static class LocationRegionScopeHelper @@ -57,6 +65,59 @@ public static class LocationRegionScopeHelper
57 return new LocationListScopeFilter { RegionKeys = keys }; 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 /// <summary> 121 /// <summary>
61 /// 将 Region 范围应用到 <c>location</c> 查询(须在其它 Where 之前或之后均可,建议在 IsDeleted 之后)。 122 /// 将 Region 范围应用到 <c>location</c> 查询(须在其它 Where 之前或之后均可,建议在 IsDeleted 之后)。
62 /// </summary> 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,7 +19,7 @@ public static class UsAppAuthScopeHelper
19 { 19 {
20 private const string CacheKeyPrefix = "FoodLabeling:UsAppAdminScope:"; 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 if (currentUser.Id is null) 24 if (currentUser.Id is null)
25 { 25 {
@@ -32,12 +32,30 @@ public static class UsAppAuthScopeHelper @@ -32,12 +32,30 @@ public static class UsAppAuthScopeHelper
32 throw new UserFriendlyException("请使用 App 登录令牌调用该接口"); 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 public static async Task<List<AuthScopeCompanyOptionDto>> ListCompaniesAsync(ISqlSugarClient db) 59 public static async Task<List<AuthScopeCompanyOptionDto>> ListCompaniesAsync(ISqlSugarClient db)
42 { 60 {
43 return await db.Queryable<FlPartnerDbEntity>() 61 return await db.Queryable<FlPartnerDbEntity>()
@@ -119,6 +137,7 @@ public static class UsAppAuthScopeHelper @@ -119,6 +137,7 @@ public static class UsAppAuthScopeHelper
119 public static async Task<AuthScopeSelectLocationOutputDto> SelectLocationAsync( 137 public static async Task<AuthScopeSelectLocationOutputDto> SelectLocationAsync(
120 ISqlSugarClient db, 138 ISqlSugarClient db,
121 IDistributedCache cache, 139 IDistributedCache cache,
  140 + ICurrentUser currentUser,
122 Guid userId, 141 Guid userId,
123 UsAppSelectAdminScopeLocationInputVo input) 142 UsAppSelectAdminScopeLocationInputVo input)
124 { 143 {
@@ -130,6 +149,11 @@ public static class UsAppAuthScopeHelper @@ -130,6 +149,11 @@ public static class UsAppAuthScopeHelper
130 throw new UserFriendlyException("请选择公司、区域和门店"); 149 throw new UserFriendlyException("请选择公司、区域和门店");
131 } 150 }
132 151
  152 + if (!ReportsRoleHelper.IsAdminRole(currentUser))
  153 + {
  154 + await UsAppPrintLogScopeHelper.EnsureUserCanAccessLocationAsync(currentUser, db, locationId);
  155 + }
  156 +
133 var scopedIds = await LocationScopeBindingHelper.ResolveFilteredLocationIdsForListAsync( 157 var scopedIds = await LocationScopeBindingHelper.ResolveFilteredLocationIdsForListAsync(
134 db, 158 db,
135 partnerId, 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,38 +62,28 @@ public class LabelAppService : ApplicationService, ILabelAppService
62 62
63 var db = _dbContext.SqlSugarClient; 63 var db = _dbContext.SqlSugarClient;
64 var regionSchema = await LabelRegionSchemaHelper.GetStatusAsync(db); 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 var filterGroupId = groupId?.Trim(); 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 db, labelIdsQuery, filterGroupId, scopedLocationIds, regionSchema); 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,11 +71,11 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
71 var ids = entities.Select(x => x.Id).ToList(); 71 var ids = entities.Select(x => x.Id).ToList();
72 72
73 var labelStatsMap = await BuildCategoryLabelStatsMapAsync(ids); 73 var labelStatsMap = await BuildCategoryLabelStatsMapAsync(ids);
74 - var scopeMap = await BuildCategoryScopeMapAsync(entities);  
75 var partnerScopeMap = await LabelEntityPartnerScopeHelper.BuildPartnerScopeDisplayMapAsync( 74 var partnerScopeMap = await LabelEntityPartnerScopeHelper.BuildPartnerScopeDisplayMapAsync(
76 _dbContext.SqlSugarClient, 75 _dbContext.SqlSugarClient,
77 LabelEntityPartnerScopeHelper.LabelEntityPartnerKind.Category, 76 LabelEntityPartnerScopeHelper.LabelEntityPartnerKind.Category,
78 ids); 77 ids);
  78 + var scopeMap = await BuildCategoryScopeMapAsync(entities, partnerScopeMap);
79 79
80 var items = entities.Select(x => 80 var items = entities.Select(x =>
81 { 81 {
@@ -370,16 +370,7 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ @@ -370,16 +370,7 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
370 return query; 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 private async Task SaveCategoryPartnerScopeAsync( 376 private async Task SaveCategoryPartnerScopeAsync(
@@ -420,7 +411,8 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ @@ -420,7 +411,8 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
420 private const string EmptyDisplay = FoodLabelingDisplayConsts.NotAvailable; 411 private const string EmptyDisplay = FoodLabelingDisplayConsts.NotAvailable;
421 412
422 private async Task<Dictionary<string, CategoryScopeData>> BuildCategoryScopeMapAsync( 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 var result = new Dictionary<string, CategoryScopeData>(StringComparer.Ordinal); 417 var result = new Dictionary<string, CategoryScopeData>(StringComparer.Ordinal);
426 if (entities.Count == 0) 418 if (entities.Count == 0)
@@ -428,8 +420,9 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ @@ -428,8 +420,9 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
428 return result; 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 result[e.Id] = new CategoryScopeData 427 result[e.Id] = new CategoryScopeData
435 { 428 {
@@ -525,14 +518,29 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ @@ -525,14 +518,29 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
525 var regionIds = await LocationScopeBindingHelper.ResolveGroupIdsFromLocationIdsAsync( 518 var regionIds = await LocationScopeBindingHelper.ResolveGroupIdsFromLocationIdsAsync(
526 _dbContext.SqlSugarClient, locationIds); 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 result[catId] = new CategoryScopeData 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 RegionIds = regionIds, 544 RegionIds = regionIds,
537 LocationIds = locationIds 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,7 +43,7 @@ public class LabelMultipleOptionAppService : ApplicationService, ILabelMultipleO
43 43
44 query = await LabelEntityPartnerScopeHelper.ApplyMultipleOptionPartnerListFilterAsync( 44 query = await LabelEntityPartnerScopeHelper.ApplyMultipleOptionPartnerListFilterAsync(
45 _dbContext.SqlSugarClient, query, scopedPartnerIds); 45 _dbContext.SqlSugarClient, query, scopedPartnerIds);
46 - query = ApplyMultipleOptionScopeFilter(query, scopedLocationIds); 46 + query = LabelEntityListScopeHelper.ApplyMultipleOptionLocationAvailabilityFilter(query, scopedLocationIds);
47 47
48 if (!string.IsNullOrWhiteSpace(input.Sorting)) 48 if (!string.IsNullOrWhiteSpace(input.Sorting))
49 { 49 {
@@ -395,27 +395,6 @@ public class LabelMultipleOptionAppService : ApplicationService, ILabelMultipleO @@ -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 private async Task<Dictionary<string, MultipleOptionScopeData>> BuildMultipleOptionScopeMapAsync( 398 private async Task<Dictionary<string, MultipleOptionScopeData>> BuildMultipleOptionScopeMapAsync(
420 List<FlLabelMultipleOptionDbEntity> entities) 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,7 +43,7 @@ public class LabelTypeAppService : ApplicationService, ILabelTypeAppService
43 43
44 query = await LabelEntityPartnerScopeHelper.ApplyTypePartnerListFilterAsync( 44 query = await LabelEntityPartnerScopeHelper.ApplyTypePartnerListFilterAsync(
45 _dbContext.SqlSugarClient, query, scopedPartnerIds); 45 _dbContext.SqlSugarClient, query, scopedPartnerIds);
46 - query = ApplyLabelTypeScopeFilter(query, scopedLocationIds); 46 + query = LabelEntityListScopeHelper.ApplyTypeLocationAvailabilityFilter(query, scopedLocationIds);
47 47
48 if (!string.IsNullOrWhiteSpace(input.Sorting)) 48 if (!string.IsNullOrWhiteSpace(input.Sorting))
49 { 49 {
@@ -346,27 +346,6 @@ public class LabelTypeAppService : ApplicationService, ILabelTypeAppService @@ -346,27 +346,6 @@ public class LabelTypeAppService : ApplicationService, ILabelTypeAppService
346 return merged.OrderBy(x => x, StringComparer.Ordinal).ToList(); 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 private async Task SaveTypeLocationsAsync( 349 private async Task SaveTypeLocationsAsync(
371 string labelTypeId, 350 string labelTypeId,
372 string availabilityType, 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,7 +170,7 @@ public class UsAppAuthAppService : ApplicationService, IUsAppAuthAppService
170 } 170 }
171 171
172 var list = await LoadBoundLocationsAsync(CurrentUser.Id.Value); 172 var list = await LoadBoundLocationsAsync(CurrentUser.Id.Value);
173 - if (ReportsRoleHelper.IsAdminRole(CurrentUser)) 173 + if (await UsAppAuthScopeHelper.CanUseAdminScopeApisAsync(_dbContext.SqlSugarClient, CurrentUser))
174 { 174 {
175 var cached = await UsAppAuthScopeHelper.GetAdminScopeCacheAsync( 175 var cached = await UsAppAuthScopeHelper.GetAdminScopeCacheAsync(
176 _distributedCache, 176 _distributedCache,
@@ -189,39 +189,40 @@ public class UsAppAuthAppService : ApplicationService, IUsAppAuthAppService @@ -189,39 +189,40 @@ public class UsAppAuthAppService : ApplicationService, IUsAppAuthAppService
189 189
190 /// <inheritdoc /> 190 /// <inheritdoc />
191 [Authorize] 191 [Authorize]
  192 + [HttpGet("us-app-auth/admin-scope-companies")]
192 public virtual async Task<List<AuthScopeCompanyOptionDto>> GetAdminScopeCompaniesAsync() 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 /// <inheritdoc /> 199 /// <inheritdoc />
199 [Authorize] 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 /// <inheritdoc /> 208 /// <inheritdoc />
207 [Authorize] 209 [Authorize]
  210 + [HttpGet("us-app-auth/admin-scope-locations")]
208 public virtual async Task<List<AuthScopeLocationOptionDto>> GetAdminScopeLocationsAsync( 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 /// <inheritdoc /> 219 /// <inheritdoc />
220 [Authorize] 220 [Authorize]
  221 + [HttpPost("us-app-auth/select-admin-scope-location")]
221 public virtual async Task<AuthScopeSelectLocationOutputDto> SelectAdminScopeLocationAsync( 222 public virtual async Task<AuthScopeSelectLocationOutputDto> SelectAdminScopeLocationAsync(
222 AuthScopeSelectLocationInputVo input) 223 AuthScopeSelectLocationInputVo input)
223 { 224 {
224 - UsAppAuthScopeHelper.EnsureAdminAppToken(CurrentUser); 225 + await UsAppAuthScopeHelper.EnsureAppScopeCallerAsync(_dbContext.SqlSugarClient, CurrentUser);
225 if (!CurrentUser.Id.HasValue) 226 if (!CurrentUser.Id.HasValue)
226 { 227 {
227 throw new UserFriendlyException("用户未登录"); 228 throw new UserFriendlyException("用户未登录");
@@ -230,6 +231,7 @@ public class UsAppAuthAppService : ApplicationService, IUsAppAuthAppService @@ -230,6 +231,7 @@ public class UsAppAuthAppService : ApplicationService, IUsAppAuthAppService
230 return await UsAppAuthScopeHelper.SelectLocationAsync( 231 return await UsAppAuthScopeHelper.SelectLocationAsync(
231 _dbContext.SqlSugarClient, 232 _dbContext.SqlSugarClient,
232 _distributedCache, 233 _distributedCache,
  234 + CurrentUser,
233 CurrentUser.Id.Value, 235 CurrentUser.Id.Value,
234 new UsAppSelectAdminScopeLocationInputVo 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,11 +364,13 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
364 } 364 }
365 365
366 var labelRow = await _dbContext.SqlSugarClient 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 .Where((l, c, t, tpl) => !l.IsDeleted && l.State) 371 .Where((l, c, t, tpl) => !l.IsDeleted && l.State)
370 .Where((l, c, t, tpl) => !c.IsDeleted && c.State) 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 .Where((l, c, t, tpl) => !tpl.IsDeleted) 374 .Where((l, c, t, tpl) => !tpl.IsDeleted)
373 .Where((l, c, t, tpl) => l.LabelCode == labelCode) 375 .Where((l, c, t, tpl) => l.LabelCode == labelCode)
374 .Select((l, c, t, tpl) => new 376 .Select((l, c, t, tpl) => new
@@ -551,10 +553,11 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ @@ -551,10 +553,11 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
551 553
552 // 校验 label + location,并补齐一些顶部字段用于任务表落库 554 // 校验 label + location,并补齐一些顶部字段用于任务表落库
553 var labelRow = await _dbContext.SqlSugarClient 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 .Where((l, t, tpl) => !l.IsDeleted && l.State) 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 .Where((l, t, tpl) => !tpl.IsDeleted) 561 .Where((l, t, tpl) => !tpl.IsDeleted)
559 .Where((l, t, tpl) => l.LabelCode == labelCode) 562 .Where((l, t, tpl) => l.LabelCode == labelCode)
560 .Select((l, t, tpl) => new 563 .Select((l, t, tpl) => new
美国版/Food Labeling Management App UniApp/src/utils/appAdminRole.ts
1 import type { UsAppMyProfileOutputDto } from '../services/usAppAuth' 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 export function isAppAdminUser(profile: UsAppMyProfileOutputDto | null | undefined): boolean { 14 export function isAppAdminUser(profile: UsAppMyProfileOutputDto | null | undefined): boolean {
5 if (!profile) return false 15 if (!profile) return false
6 const code = (profile.primaryRoleCode ?? '').trim().toLowerCase() 16 const code = (profile.primaryRoleCode ?? '').trim().toLowerCase()
@@ -8,9 +18,9 @@ export function isAppAdminUser(profile: UsAppMyProfileOutputDto | null | undefin @@ -8,9 +18,9 @@ export function isAppAdminUser(profile: UsAppMyProfileOutputDto | null | undefin
8 if (code === 'admin') return true 18 if (code === 'admin') return true
9 if (compactCode === 'companyadmin') return true 19 if (compactCode === 'companyadmin') return true
10 const display = (profile.roleDisplay ?? '').trim().toLowerCase() 20 const display = (profile.roleDisplay ?? '').trim().toLowerCase()
11 - if (!display) return false 21 + if (!display) return isCompanyAdminProfile(profile)
12 if (display.includes('administrator')) return true 22 if (display.includes('administrator')) return true
13 if (display.includes('super admin')) return true 23 if (display.includes('super admin')) return true
14 if (display.includes('company admin')) return true 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,14 +277,10 @@ public static class AuthScopeQueryHelper
277 return; 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 private static async Task<AuthScopeSelectLocationOutputDto> ResolveAndValidateSelectionAsync( 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,17 +348,12 @@ public static class LabelEntityPartnerScopeHelper
348 348
349 if (scopedPartnerIds.Count == 0) 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 return query.Where(t => 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 .Where(p => p.LabelTypeId == t.Id && scopedPartnerIds.Contains(p.PartnerId)) 357 .Where(p => p.LabelTypeId == t.Id && scopedPartnerIds.Contains(p.PartnerId))
363 .Any()); 358 .Any());
364 } 359 }
@@ -384,17 +379,12 @@ public static class LabelEntityPartnerScopeHelper @@ -384,17 +379,12 @@ public static class LabelEntityPartnerScopeHelper
384 379
385 if (scopedPartnerIds.Count == 0) 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 return query.Where(c => 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 .Where(p => p.CategoryId == c.Id && scopedPartnerIds.Contains(p.PartnerId)) 388 .Where(p => p.CategoryId == c.Id && scopedPartnerIds.Contains(p.PartnerId))
399 .Any()); 389 .Any());
400 } 390 }
@@ -420,17 +410,12 @@ public static class LabelEntityPartnerScopeHelper @@ -420,17 +410,12 @@ public static class LabelEntityPartnerScopeHelper
420 410
421 if (scopedPartnerIds.Count == 0) 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 return query.Where(o => 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 .Where(p => p.MultipleOptionId == o.Id && scopedPartnerIds.Contains(p.PartnerId)) 419 .Where(p => p.MultipleOptionId == o.Id && scopedPartnerIds.Contains(p.PartnerId))
435 .Any()); 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,23 +437,7 @@ public static class LabelTemplateScopeHelper
437 437
438 if (scopedLocationIds.Count == 0) 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 if (!hasExtendedScope) 443 if (!hasExtendedScope)
@@ -470,12 +454,9 @@ public static class LabelTemplateScopeHelper @@ -470,12 +454,9 @@ public static class LabelTemplateScopeHelper
470 db, scopedLocationIds); 454 db, scopedLocationIds);
471 455
472 return query.Where(t => 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 && (!SqlFunc.Subqueryable<FlLabelTemplateRegionDbEntity>() 460 && (!SqlFunc.Subqueryable<FlLabelTemplateRegionDbEntity>()
480 .Where(r => r.TemplateId == t.Id) 461 .Where(r => r.TemplateId == t.Id)
481 .Any() 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,6 +25,7 @@ public static class LocationRegionScopeHelper
25 25
26 /// <summary> 26 /// <summary>
27 /// 解析当前用户可查询的 Region(公司 + 组织名)集合。 27 /// 解析当前用户可查询的 Region(公司 + 组织名)集合。
  28 + /// Company Admin 展开为绑定 Company 下全部 Region(与 <see cref="GroupListScopeHelper"/> 一致)。
28 /// </summary> 29 /// </summary>
29 public static async Task<LocationListScopeFilter> ResolveLocationListScopeAsync( 30 public static async Task<LocationListScopeFilter> ResolveLocationListScopeAsync(
30 ICurrentUser currentUser, 31 ICurrentUser currentUser,
@@ -40,8 +41,15 @@ public static class LocationRegionScopeHelper @@ -40,8 +41,15 @@ public static class LocationRegionScopeHelper
40 return new LocationListScopeFilter(); 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 var userId = currentUser.Id.Value.ToString(); 51 var userId = currentUser.Id.Value.ToString();
44 - var rows = await dbContext.SqlSugarClient.Queryable<UserLocationDbEntity>() 52 + var rows = await db.Queryable<UserLocationDbEntity>()
45 .InnerJoin<LocationAggregateRoot>((ul, loc) => 53 .InnerJoin<LocationAggregateRoot>((ul, loc) =>
46 !loc.IsDeleted && ul.LocationId == loc.Id.ToString()) 54 !loc.IsDeleted && ul.LocationId == loc.Id.ToString())
47 .Where(ul => !ul.IsDeleted && ul.UserId == userId) 55 .Where(ul => !ul.IsDeleted && ul.UserId == userId)
@@ -57,6 +65,59 @@ public static class LocationRegionScopeHelper @@ -57,6 +65,59 @@ public static class LocationRegionScopeHelper
57 return new LocationListScopeFilter { RegionKeys = keys }; 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 /// <summary> 121 /// <summary>
61 /// 将 Region 范围应用到 <c>location</c> 查询(须在其它 Where 之前或之后均可,建议在 IsDeleted 之后)。 122 /// 将 Region 范围应用到 <c>location</c> 查询(须在其它 Where 之前或之后均可,建议在 IsDeleted 之后)。
62 /// </summary> 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,7 +19,7 @@ public static class UsAppAuthScopeHelper
19 { 19 {
20 private const string CacheKeyPrefix = "FoodLabeling:UsAppAdminScope:"; 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 if (currentUser.Id is null) 24 if (currentUser.Id is null)
25 { 25 {
@@ -32,12 +32,30 @@ public static class UsAppAuthScopeHelper @@ -32,12 +32,30 @@ public static class UsAppAuthScopeHelper
32 throw new UserFriendlyException("请使用 App 登录令牌调用该接口"); 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 public static async Task<List<AuthScopeCompanyOptionDto>> ListCompaniesAsync(ISqlSugarClient db) 59 public static async Task<List<AuthScopeCompanyOptionDto>> ListCompaniesAsync(ISqlSugarClient db)
42 { 60 {
43 return await db.Queryable<FlPartnerDbEntity>() 61 return await db.Queryable<FlPartnerDbEntity>()
@@ -119,6 +137,7 @@ public static class UsAppAuthScopeHelper @@ -119,6 +137,7 @@ public static class UsAppAuthScopeHelper
119 public static async Task<AuthScopeSelectLocationOutputDto> SelectLocationAsync( 137 public static async Task<AuthScopeSelectLocationOutputDto> SelectLocationAsync(
120 ISqlSugarClient db, 138 ISqlSugarClient db,
121 IDistributedCache cache, 139 IDistributedCache cache,
  140 + ICurrentUser currentUser,
122 Guid userId, 141 Guid userId,
123 UsAppSelectAdminScopeLocationInputVo input) 142 UsAppSelectAdminScopeLocationInputVo input)
124 { 143 {
@@ -130,6 +149,11 @@ public static class UsAppAuthScopeHelper @@ -130,6 +149,11 @@ public static class UsAppAuthScopeHelper
130 throw new UserFriendlyException("请选择公司、区域和门店"); 149 throw new UserFriendlyException("请选择公司、区域和门店");
131 } 150 }
132 151
  152 + if (!ReportsRoleHelper.IsAdminRole(currentUser))
  153 + {
  154 + await UsAppPrintLogScopeHelper.EnsureUserCanAccessLocationAsync(currentUser, db, locationId);
  155 + }
  156 +
133 var scopedIds = await LocationScopeBindingHelper.ResolveFilteredLocationIdsForListAsync( 157 var scopedIds = await LocationScopeBindingHelper.ResolveFilteredLocationIdsForListAsync(
134 db, 158 db,
135 partnerId, 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,38 +62,28 @@ public class LabelAppService : ApplicationService, ILabelAppService
62 62
63 var db = _dbContext.SqlSugarClient; 63 var db = _dbContext.SqlSugarClient;
64 var regionSchema = await LabelRegionSchemaHelper.GetStatusAsync(db); 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 var filterGroupId = groupId?.Trim(); 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 db, labelIdsQuery, filterGroupId, scopedLocationIds, regionSchema); 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,11 +71,11 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
71 var ids = entities.Select(x => x.Id).ToList(); 71 var ids = entities.Select(x => x.Id).ToList();
72 72
73 var labelStatsMap = await BuildCategoryLabelStatsMapAsync(ids); 73 var labelStatsMap = await BuildCategoryLabelStatsMapAsync(ids);
74 - var scopeMap = await BuildCategoryScopeMapAsync(entities);  
75 var partnerScopeMap = await LabelEntityPartnerScopeHelper.BuildPartnerScopeDisplayMapAsync( 74 var partnerScopeMap = await LabelEntityPartnerScopeHelper.BuildPartnerScopeDisplayMapAsync(
76 _dbContext.SqlSugarClient, 75 _dbContext.SqlSugarClient,
77 LabelEntityPartnerScopeHelper.LabelEntityPartnerKind.Category, 76 LabelEntityPartnerScopeHelper.LabelEntityPartnerKind.Category,
78 ids); 77 ids);
  78 + var scopeMap = await BuildCategoryScopeMapAsync(entities, partnerScopeMap);
79 79
80 var items = entities.Select(x => 80 var items = entities.Select(x =>
81 { 81 {
@@ -370,16 +370,7 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ @@ -370,16 +370,7 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
370 return query; 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 private async Task SaveCategoryPartnerScopeAsync( 376 private async Task SaveCategoryPartnerScopeAsync(
@@ -420,7 +411,8 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ @@ -420,7 +411,8 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
420 private const string EmptyDisplay = FoodLabelingDisplayConsts.NotAvailable; 411 private const string EmptyDisplay = FoodLabelingDisplayConsts.NotAvailable;
421 412
422 private async Task<Dictionary<string, CategoryScopeData>> BuildCategoryScopeMapAsync( 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 var result = new Dictionary<string, CategoryScopeData>(StringComparer.Ordinal); 417 var result = new Dictionary<string, CategoryScopeData>(StringComparer.Ordinal);
426 if (entities.Count == 0) 418 if (entities.Count == 0)
@@ -428,8 +420,9 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ @@ -428,8 +420,9 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
428 return result; 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 result[e.Id] = new CategoryScopeData 427 result[e.Id] = new CategoryScopeData
435 { 428 {
@@ -525,14 +518,29 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ @@ -525,14 +518,29 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
525 var regionIds = await LocationScopeBindingHelper.ResolveGroupIdsFromLocationIdsAsync( 518 var regionIds = await LocationScopeBindingHelper.ResolveGroupIdsFromLocationIdsAsync(
526 _dbContext.SqlSugarClient, locationIds); 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 result[catId] = new CategoryScopeData 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 RegionIds = regionIds, 544 RegionIds = regionIds,
537 LocationIds = locationIds 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,7 +43,7 @@ public class LabelMultipleOptionAppService : ApplicationService, ILabelMultipleO
43 43
44 query = await LabelEntityPartnerScopeHelper.ApplyMultipleOptionPartnerListFilterAsync( 44 query = await LabelEntityPartnerScopeHelper.ApplyMultipleOptionPartnerListFilterAsync(
45 _dbContext.SqlSugarClient, query, scopedPartnerIds); 45 _dbContext.SqlSugarClient, query, scopedPartnerIds);
46 - query = ApplyMultipleOptionScopeFilter(query, scopedLocationIds); 46 + query = LabelEntityListScopeHelper.ApplyMultipleOptionLocationAvailabilityFilter(query, scopedLocationIds);
47 47
48 if (!string.IsNullOrWhiteSpace(input.Sorting)) 48 if (!string.IsNullOrWhiteSpace(input.Sorting))
49 { 49 {
@@ -395,27 +395,6 @@ public class LabelMultipleOptionAppService : ApplicationService, ILabelMultipleO @@ -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 private async Task<Dictionary<string, MultipleOptionScopeData>> BuildMultipleOptionScopeMapAsync( 398 private async Task<Dictionary<string, MultipleOptionScopeData>> BuildMultipleOptionScopeMapAsync(
420 List<FlLabelMultipleOptionDbEntity> entities) 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,7 +43,7 @@ public class LabelTypeAppService : ApplicationService, ILabelTypeAppService
43 43
44 query = await LabelEntityPartnerScopeHelper.ApplyTypePartnerListFilterAsync( 44 query = await LabelEntityPartnerScopeHelper.ApplyTypePartnerListFilterAsync(
45 _dbContext.SqlSugarClient, query, scopedPartnerIds); 45 _dbContext.SqlSugarClient, query, scopedPartnerIds);
46 - query = ApplyLabelTypeScopeFilter(query, scopedLocationIds); 46 + query = LabelEntityListScopeHelper.ApplyTypeLocationAvailabilityFilter(query, scopedLocationIds);
47 47
48 if (!string.IsNullOrWhiteSpace(input.Sorting)) 48 if (!string.IsNullOrWhiteSpace(input.Sorting))
49 { 49 {
@@ -346,27 +346,6 @@ public class LabelTypeAppService : ApplicationService, ILabelTypeAppService @@ -346,27 +346,6 @@ public class LabelTypeAppService : ApplicationService, ILabelTypeAppService
346 return merged.OrderBy(x => x, StringComparer.Ordinal).ToList(); 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 private async Task SaveTypeLocationsAsync( 349 private async Task SaveTypeLocationsAsync(
371 string labelTypeId, 350 string labelTypeId,
372 string availabilityType, 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,7 +161,7 @@ public class UsAppAuthAppService : ApplicationService, IUsAppAuthAppService
161 } 161 }
162 162
163 var list = await LoadBoundLocationsAsync(CurrentUser.Id.Value); 163 var list = await LoadBoundLocationsAsync(CurrentUser.Id.Value);
164 - if (ReportsRoleHelper.IsAdminRole(CurrentUser)) 164 + if (await UsAppAuthScopeHelper.CanUseAdminScopeApisAsync(_dbContext.SqlSugarClient, CurrentUser))
165 { 165 {
166 var cached = await UsAppAuthScopeHelper.GetAdminScopeCacheAsync( 166 var cached = await UsAppAuthScopeHelper.GetAdminScopeCacheAsync(
167 _distributedCache, 167 _distributedCache,
@@ -180,39 +180,40 @@ public class UsAppAuthAppService : ApplicationService, IUsAppAuthAppService @@ -180,39 +180,40 @@ public class UsAppAuthAppService : ApplicationService, IUsAppAuthAppService
180 180
181 /// <inheritdoc /> 181 /// <inheritdoc />
182 [Authorize] 182 [Authorize]
  183 + [HttpGet("us-app-auth/admin-scope-companies")]
183 public virtual async Task<List<AuthScopeCompanyOptionDto>> GetAdminScopeCompaniesAsync() 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 /// <inheritdoc /> 190 /// <inheritdoc />
190 [Authorize] 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 /// <inheritdoc /> 199 /// <inheritdoc />
198 [Authorize] 200 [Authorize]
  201 + [HttpGet("us-app-auth/admin-scope-locations")]
199 public virtual async Task<List<AuthScopeLocationOptionDto>> GetAdminScopeLocationsAsync( 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 /// <inheritdoc /> 210 /// <inheritdoc />
211 [Authorize] 211 [Authorize]
  212 + [HttpPost("us-app-auth/select-admin-scope-location")]
212 public virtual async Task<AuthScopeSelectLocationOutputDto> SelectAdminScopeLocationAsync( 213 public virtual async Task<AuthScopeSelectLocationOutputDto> SelectAdminScopeLocationAsync(
213 AuthScopeSelectLocationInputVo input) 214 AuthScopeSelectLocationInputVo input)
214 { 215 {
215 - UsAppAuthScopeHelper.EnsureAdminAppToken(CurrentUser); 216 + await UsAppAuthScopeHelper.EnsureAppScopeCallerAsync(_dbContext.SqlSugarClient, CurrentUser);
216 if (!CurrentUser.Id.HasValue) 217 if (!CurrentUser.Id.HasValue)
217 { 218 {
218 throw new UserFriendlyException("用户未登录"); 219 throw new UserFriendlyException("用户未登录");
@@ -221,6 +222,7 @@ public class UsAppAuthAppService : ApplicationService, IUsAppAuthAppService @@ -221,6 +222,7 @@ public class UsAppAuthAppService : ApplicationService, IUsAppAuthAppService
221 return await UsAppAuthScopeHelper.SelectLocationAsync( 222 return await UsAppAuthScopeHelper.SelectLocationAsync(
222 _dbContext.SqlSugarClient, 223 _dbContext.SqlSugarClient,
223 _distributedCache, 224 _distributedCache,
  225 + CurrentUser,
224 CurrentUser.Id.Value, 226 CurrentUser.Id.Value,
225 new UsAppSelectAdminScopeLocationInputVo 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,11 +364,13 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
364 } 364 }
365 365
366 var labelRow = await _dbContext.SqlSugarClient 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 .Where((l, c, t, tpl) => !l.IsDeleted && l.State) 371 .Where((l, c, t, tpl) => !l.IsDeleted && l.State)
370 .Where((l, c, t, tpl) => !c.IsDeleted && c.State) 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 .Where((l, c, t, tpl) => !tpl.IsDeleted) 374 .Where((l, c, t, tpl) => !tpl.IsDeleted)
373 .Where((l, c, t, tpl) => l.LabelCode == labelCode) 375 .Where((l, c, t, tpl) => l.LabelCode == labelCode)
374 .Select((l, c, t, tpl) => new 376 .Select((l, c, t, tpl) => new
@@ -551,10 +553,11 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ @@ -551,10 +553,11 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
551 553
552 // 校验 label + location,并补齐一些顶部字段用于任务表落库 554 // 校验 label + location,并补齐一些顶部字段用于任务表落库
553 var labelRow = await _dbContext.SqlSugarClient 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 .Where((l, t, tpl) => !l.IsDeleted && l.State) 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 .Where((l, t, tpl) => !tpl.IsDeleted) 561 .Where((l, t, tpl) => !tpl.IsDeleted)
559 .Where((l, t, tpl) => l.LabelCode == labelCode) 562 .Where((l, t, tpl) => l.LabelCode == labelCode)
560 .Select((l, t, tpl) => new 563 .Select((l, t, tpl) => new
美国版/Food Labeling Management Platform/src/services/labelCategoryService.ts
@@ -49,6 +49,8 @@ function normalizeLabelCategoryDto(raw: unknown): LabelCategoryDto { @@ -49,6 +49,8 @@ function normalizeLabelCategoryDto(raw: unknown): LabelCategoryDto {
49 ? Number(orderRaw) 49 ? Number(orderRaw)
50 : null; 50 : null;
51 const companyRaw = r.company ?? r.Company; 51 const companyRaw = r.company ?? r.Company;
  52 + const regionRaw = r.region ?? r.Region;
  53 + const locationRaw = r.location ?? r.Location;
52 return { 54 return {
53 ...(r as object), 55 ...(r as object),
54 id: String(r.id ?? r.Id ?? ""), 56 id: String(r.id ?? r.Id ?? ""),
@@ -60,6 +62,8 @@ function normalizeLabelCategoryDto(raw: unknown): LabelCategoryDto { @@ -60,6 +62,8 @@ function normalizeLabelCategoryDto(raw: unknown): LabelCategoryDto {
60 regionIds: groupIds, 62 regionIds: groupIds,
61 locationIds, 63 locationIds,
62 company: typeof companyRaw === "string" ? companyRaw : null, 64 company: typeof companyRaw === "string" ? companyRaw : null,
  65 + region: typeof regionRaw === "string" ? regionRaw : null,
  66 + location: typeof locationRaw === "string" ? locationRaw : null,
63 noOfLabels, 67 noOfLabels,
64 orderNum, 68 orderNum,
65 } as LabelCategoryDto; 69 } as LabelCategoryDto;