diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlSugarDbContextFactory.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlSugarDbContextFactory.cs
index 3c1725e..dd28a19 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlSugarDbContextFactory.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlSugarDbContextFactory.cs
@@ -21,10 +21,13 @@ namespace Yi.Framework.SqlSugarCore
{
#region Properties
+ private ISqlSugarClient? _sqlSugarClient;
+ private readonly object _clientLock = new();
+
///
- /// SqlSugar客户端实例
+ /// SqlSugar 客户端(延迟按当前租户解析连接串,避免构造时 CurrentTenant 尚未就绪落到 host 库)
///
- public ISqlSugarClient SqlSugarClient { get; private set; }
+ public ISqlSugarClient SqlSugarClient => GetOrCreateClient();
///
/// 延迟服务提供者
@@ -75,22 +78,62 @@ namespace Yi.Framework.SqlSugarCore
public SqlSugarDbContextFactory(IAbpLazyServiceProvider lazyServiceProvider)
{
LazyServiceProvider = lazyServiceProvider;
+ }
- // 异步获取租户配置
- var tenantConfiguration = AsyncHelper.RunSync(async () => await TenantConfigurationWrapper.GetAsync());
-
- // 构建数据库连接配置
- var connectionConfig = BuildConnectionConfig(options =>
+ private ISqlSugarClient GetOrCreateClient()
+ {
+ // 必须每次按当前租户解析连接串:MultiTenancyMiddleware 查 YiTenant 时会先 Change(null)
+ // 创建主库客户端;若缓存不切换,后续业务会误连 host。
+ var tenantConfiguration =
+ AsyncHelper.RunSync(async () => await TenantConfigurationWrapper.GetAsync());
+ var connectionString = tenantConfiguration.GetCurrentConnectionString();
+ var dbType = GetCurrentDbType(tenantConfiguration.GetCurrentConnectionName());
+
+ if (_sqlSugarClient is not null &&
+ string.Equals(
+ _sqlSugarClient.CurrentConnectionConfig?.ConnectionString,
+ connectionString,
+ StringComparison.Ordinal))
{
- options.ConnectionString = tenantConfiguration.GetCurrentConnectionString();
- options.DbType = GetCurrentDbType(tenantConfiguration.GetCurrentConnectionName());
- });
+ return _sqlSugarClient;
+ }
- // 创建SqlSugar客户端实例
- SqlSugarClient = new SqlSugarClient(connectionConfig);
+ lock (_clientLock)
+ {
+ if (_sqlSugarClient is not null &&
+ string.Equals(
+ _sqlSugarClient.CurrentConnectionConfig?.ConnectionString,
+ connectionString,
+ StringComparison.Ordinal))
+ {
+ return _sqlSugarClient;
+ }
- // 配置数据库AOP
- ConfigureDbAop(SqlSugarClient);
+ if (_sqlSugarClient is not null)
+ {
+ try
+ {
+ _sqlSugarClient.Dispose();
+ }
+ catch
+ {
+ // ignore dispose race
+ }
+
+ _sqlSugarClient = null;
+ }
+
+ var connectionConfig = BuildConnectionConfig(options =>
+ {
+ options.ConnectionString = connectionString;
+ options.DbType = dbType;
+ });
+
+ var client = new SqlSugarClient(connectionConfig);
+ ConfigureDbAop(client);
+ _sqlSugarClient = client;
+ return _sqlSugarClient;
+ }
}
///
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserBriefDto.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserBriefDto.cs
index 4567f6d..be7e2be 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserBriefDto.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserBriefDto.cs
@@ -5,6 +5,9 @@ namespace FoodLabeling.Application.Contracts.Dtos.AuthSession;
///
public class CurrentUserBriefDto
{
+ ///
+ /// 当前登录用户 Id(非租户 Id)。
+ ///
public Guid Id { get; set; }
public string UserName { get; set; } = string.Empty;
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserMenuPermissionsOutputDto.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserMenuPermissionsOutputDto.cs
index e73ed13..246ffd2 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserMenuPermissionsOutputDto.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserMenuPermissionsOutputDto.cs
@@ -5,6 +5,11 @@ namespace FoodLabeling.Application.Contracts.Dtos.AuthSession;
///
public class CurrentUserMenuPermissionsOutputDto
{
+ ///
+ /// 当前登录用户 Id(JSON: userId)。与 . 一致,非租户 Id。
+ ///
+ public Guid UserId { get; set; }
+
public CurrentUserBriefDto User { get; set; } = new();
public List RoleCodes { get; set; } = new();
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Common/ITrainingFileScopeInput.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Common/ITrainingFileScopeInput.cs
new file mode 100644
index 0000000..3e252a2
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Common/ITrainingFileScopeInput.cs
@@ -0,0 +1,25 @@
+namespace FoodLabeling.Application.Contracts.Dtos.Common;
+
+///
+/// 培训文件创建/编辑入参中的 Company / Region / Location 适用范围字段。
+///
+public interface ITrainingFileScopeInput : ILabelEntityPartnerScopeInput
+{
+ /// 适用 Region:ALL / SPECIFIED
+ string? AppliedRegionType { get; }
+
+ /// 适用 Region(fl_group.Id);与 合并;可含 ALL
+ List? RegionIds { get; }
+
+ /// 与 相同;可含 ALL
+ List? GroupIds { get; }
+
+ /// 适用 Location:ALL / SPECIFIED(与 二选一)
+ string? AvailabilityType { get; }
+
+ /// 适用 Location:ALL / SPECIFIED( 别名)
+ string? AppliedLocationType { get; }
+
+ /// 适用门店(location.Id);可含 ALL
+ List? LocationIds { get; }
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelBatchCreateItemInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelBatchCreateItemInputVo.cs
index 00df67d..6a91e82 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelBatchCreateItemInputVo.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelBatchCreateItemInputVo.cs
@@ -13,6 +13,9 @@ public class LabelBatchCreateItemInputVo
public List? PartnerIds { get; set; }
+ /// 适用 Company(单选)
+ public List? CompanyIds { get; set; }
+
public string? AppliedRegionType { get; set; }
public List? RegionIds { get; set; }
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelCreateInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelCreateInputVo.cs
index 759481e..69d0d12 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelCreateInputVo.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelCreateInputVo.cs
@@ -2,6 +2,7 @@ namespace FoodLabeling.Application.Contracts.Dtos.Label;
public class LabelCreateInputVo
{
+ /// 标签编码(可选;未传或空字符串时由后端自动生成唯一编码)
public string? LabelCode { get; set; }
public string LabelName { get; set; } = string.Empty;
@@ -9,32 +10,38 @@ public class LabelCreateInputVo
public string TemplateCode { get; set; } = string.Empty;
///
- /// 适用 Company(fl_partner.Id);与 合并,用于解析所属门店
+ /// 适用 Company(fl_partner.Id);与 / 合并,单选
///
public string? PartnerId { get; set; }
///
- /// 适用 Company 多选(fl_partner.Id)
+ /// 适用 Company 多选字段(仅支持 1 个;与 相同)
///
public List? PartnerIds { get; set; }
///
- /// 适用 Region 范围:ALL(全选)/ SPECIFIED(按 )。传了有效 regionIds 时按 SPECIFIED 处理。
+ /// 适用 Company(单选,fl_partner.Id);与 / 合并
+ ///
+ public List? CompanyIds { get; set; }
+
+ ///
+ /// 适用 Region 范围:ALL / SPECIFIED。
+ /// 亦可在 / 传哨兵 ["ALL"](即使本字段为 SPECIFIED 亦归档 ALL;POST/PUT 均支持)。
///
public string? AppliedRegionType { get; set; }
///
- /// 适用 Region 多选(fl_group.Id);与 合并,落库 fl_label_region
+ /// 适用 Region 多选(fl_group.Id);与 合并;可含 ALL
///
public List? RegionIds { get; set; }
///
- /// 适用 Region 多选(与 相同)
+ /// 适用 Region 多选(与 相同);可含 ALL
///
public List? GroupIds { get; set; }
///
- /// 适用门店 Id 数组(location.Id,落库 fl_label_location);主字段
+ /// 适用门店 Id 数组(location.Id);可含 ALL;落库 fl_label_location
///
public List? LocationIds { get; set; }
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelGetOutputDto.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelGetOutputDto.cs
index fc8d69d..8b2d0f2 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelGetOutputDto.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelGetOutputDto.cs
@@ -17,11 +17,14 @@ public class LabelGetOutputDto
public string LocationName { get; set; } = string.Empty;
- /// 适用 Company Id(由所属门店反推)
+ /// 适用 Company Id(单选;优先落库 fl_label.PartnerId)
public string? PartnerId { get; set; }
public List PartnerIds { get; set; } = new();
+ /// 与 相同(兼容字段,单选)
+ public List CompanyIds { get; set; } = new();
+
/// 适用 Region 范围:ALL / SPECIFIED
public string AppliedRegionType { get; set; } = "SPECIFIED";
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelUpdateInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelUpdateInputVo.cs
index c1904fa..f5dab1a 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelUpdateInputVo.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelUpdateInputVo.cs
@@ -10,17 +10,23 @@ public class LabelUpdateInputVo
public List? PartnerIds { get; set; }
+ /// 适用 Company(单选);与 / 合并
+ public List? CompanyIds { get; set; }
+
///
- /// 适用 Region 范围:ALL / SPECIFIED(见 )
+ /// 适用 Region 范围:ALL / SPECIFIED。
+ /// 亦可在 / 传哨兵 ["ALL"](与新增一致,PUT 支持)。
///
public string? AppliedRegionType { get; set; }
+ /// 适用 Region 多选;可含 ALL
public List? RegionIds { get; set; }
+ /// 与 相同;可含 ALL
public List? GroupIds { get; set; }
///
- /// 适用门店 Id 数组(location.Id,落库 fl_label_location);主字段,全量覆盖
+ /// 适用门店 Id 数组;可含 ALL;落库 fl_label_location,全量覆盖
///
public List? LocationIds { get; set; }
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerCheckExpiredInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerCheckExpiredInputVo.cs
new file mode 100644
index 0000000..a8fdf22
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerCheckExpiredInputVo.cs
@@ -0,0 +1,13 @@
+namespace FoodLabeling.Application.Contracts.Dtos.LabelAlertTimer;
+
+///
+/// 检查告警计时器是否过期入参(至少提供一个标识)
+///
+public class LabelAlertTimerCheckExpiredInputVo
+{
+ public string? TimerId { get; set; }
+
+ public string? BatchId { get; set; }
+
+ public string? PrintTaskId { get; set; }
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerCheckExpiredOutputDto.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerCheckExpiredOutputDto.cs
new file mode 100644
index 0000000..5fff58b
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerCheckExpiredOutputDto.cs
@@ -0,0 +1,29 @@
+namespace FoodLabeling.Application.Contracts.Dtos.LabelAlertTimer;
+
+///
+/// 检查告警计时器是否过期出参
+///
+public class LabelAlertTimerCheckExpiredOutputDto
+{
+ /// 是否找到计时器记录
+ public bool Found { get; set; }
+
+ /// 是否已过期;无记录时为 false(仅展示用,不用于拦截打印)
+ public bool IsExpired { get; set; }
+
+ public DateTime? ExpiresAt { get; set; }
+
+ /// 剩余秒数(已过期为 0)
+ public int RemainingSeconds { get; set; }
+
+ /// 状态:expired 或 running
+ public string? Status { get; set; }
+
+ public string? Title { get; set; }
+
+ public string? Subtitle { get; set; }
+
+ public string? TimerId { get; set; }
+
+ public string? BatchId { get; set; }
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerGetListInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerGetListInputVo.cs
new file mode 100644
index 0000000..b216881
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerGetListInputVo.cs
@@ -0,0 +1,20 @@
+using Volo.Abp.Application.Dtos;
+
+namespace FoodLabeling.Application.Contracts.Dtos.LabelAlertTimer;
+
+///
+/// 告警计时器分页列表入参
+///
+public class LabelAlertTimerGetListInputVo : PagedAndSortedResultRequestDto
+{
+ ///
+ /// 当前门店 Id(location.Id,Guid 字符串)。
+ /// list 必填;app-list 可空(空则取已选门店缓存)。
+ ///
+ public string? LocationId { get; set; }
+
+ ///
+ /// 打印日期(yyyy-MM-dd);按 PrintedAt 筛选该日记录。
+ ///
+ public string? DateDay { get; set; }
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerListItemDto.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerListItemDto.cs
new file mode 100644
index 0000000..f5a783a
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerListItemDto.cs
@@ -0,0 +1,38 @@
+namespace FoodLabeling.Application.Contracts.Dtos.LabelAlertTimer;
+
+///
+/// 告警计时器列表项
+///
+public class LabelAlertTimerListItemDto
+{
+ public string Id { get; set; } = string.Empty;
+
+ public string BatchId { get; set; } = string.Empty;
+
+ public string PrintTaskId { get; set; } = string.Empty;
+
+ public string LabelId { get; set; } = string.Empty;
+
+ public string? LabelCode { get; set; }
+
+ public string Title { get; set; } = string.Empty;
+
+ public string Subtitle { get; set; } = string.Empty;
+
+ /// 总时长(秒),同 DurationSeconds
+ public int TotalTime { get; set; }
+
+ /// 剩余时长(秒)
+ public int RemainingTime { get; set; }
+
+ /// 状态:expired 或 running
+ public string Status { get; set; } = string.Empty;
+
+ public DateTime ExpiresAt { get; set; }
+
+ public DateTime PrintedAt { get; set; }
+
+ public string LocationId { get; set; } = string.Empty;
+
+ public string? ProductName { get; set; }
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryCreateInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryCreateInputVo.cs
index 3e630ec..b05eb6a 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryCreateInputVo.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryCreateInputVo.cs
@@ -4,7 +4,8 @@ namespace FoodLabeling.Application.Contracts.Dtos.LabelCategory;
public class LabelCategoryCreateInputVo : ILabelEntityPartnerScopeInput
{
- public string CategoryCode { get; set; } = string.Empty;
+ /// 分类编码(可选;未传或空字符串时由后端自动生成唯一编码)
+ public string? CategoryCode { get; set; }
public string CategoryName { get; set; } = string.Empty;
@@ -37,25 +38,28 @@ public class LabelCategoryCreateInputVo : ILabelEntityPartnerScopeInput
public List? CompanyIds { get; set; }
///
- /// 门店可用范围:ALL / SPECIFIED
+ /// 门店可用范围:ALL / SPECIFIED。
+ /// regionIds/groupIds/locationIds 可传哨兵 ["ALL"](大小写不敏感);
+ /// 即使本字段为 SPECIFIED,Id 数组为 ALL 时仍归档为 ALL(POST/PUT 均支持)。
///
public string AvailabilityType { get; set; } = "ALL";
///
- /// 适用 Region(多选),fl_group.Id;与 合并去重
+ /// 适用 Region(多选),fl_group.Id;与 合并去重;可含 ALL
///
public List? RegionIds { get; set; }
///
- /// 适用 Region(多选),与 相同
+ /// 适用 Region(多选),与 相同;可含 ALL
///
public List? GroupIds { get; set; }
///
- /// 适用门店(多选),location.Id;与 Region 合并后写入 fl_label_category_location
+ /// 适用门店(多选),location.Id;可含 ALL;与 Region 合并后写入 fl_label_category_location
///
public List? LocationIds { get; set; }
- public int OrderNum { get; set; }
+ /// 排序;未传或 null 时默认 0
+ public int? OrderNum { get; set; }
}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionCreateInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionCreateInputVo.cs
index 7802d0d..3371738 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionCreateInputVo.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionCreateInputVo.cs
@@ -4,7 +4,7 @@ namespace FoodLabeling.Application.Contracts.Dtos.LabelMultipleOption;
public class LabelMultipleOptionCreateInputVo : ILabelEntityPartnerScopeInput
{
- /// 多选项编码(可选;未传或空字符串时存空,列表/详情出参为「无」)
+ /// 多选项编码(可选;未传或空字符串时由后端自动生成唯一编码)
public string? OptionCode { get; set; }
public string OptionName { get; set; } = string.Empty;
@@ -25,25 +25,27 @@ public class LabelMultipleOptionCreateInputVo : ILabelEntityPartnerScopeInput
public List? CompanyIds { get; set; }
///
- /// 门店可用范围:ALL / SPECIFIED;传了 或 时自动为 SPECIFIED
+ /// 门店可用范围:ALL / SPECIFIED;传了 或 时自动为 SPECIFIED。
+ /// Id 数组可传哨兵 ALL(POST/PUT 新增与编辑均支持),归档为 availabilityType=ALL 且不写门店快照。
///
public string AvailabilityType { get; set; } = "ALL";
///
- /// 适用 Region(多选),fl_group.Id;与 合并去重
+ /// 适用 Region(多选),fl_group.Id;与 合并去重。可传 ALL。
///
public List? RegionIds { get; set; }
///
- /// 适用 Region(多选),与 相同
+ /// 适用 Region(多选),与 相同。可传 ALL。
///
public List? GroupIds { get; set; }
///
- /// 适用门店(多选),location.Id;与 Region 合并后写入 fl_label_multiple_option_location
+ /// 适用门店(多选),location.Id;与 Region 合并后写入 fl_label_multiple_option_location。可传 ALL。
///
public List? LocationIds { get; set; }
- public int OrderNum { get; set; }
+ /// 排序;未传或 null 时默认 0
+ public int? OrderNum { get; set; }
}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeCreateInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeCreateInputVo.cs
index 131dcdc..42a8f3b 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeCreateInputVo.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeCreateInputVo.cs
@@ -4,7 +4,8 @@ namespace FoodLabeling.Application.Contracts.Dtos.LabelType;
public class LabelTypeCreateInputVo : ILabelEntityPartnerScopeInput
{
- public string TypeCode { get; set; } = string.Empty;
+ /// 类型编码;为空时由后端自动生成唯一编码
+ public string? TypeCode { get; set; }
public string TypeName { get; set; } = string.Empty;
@@ -22,25 +23,28 @@ public class LabelTypeCreateInputVo : ILabelEntityPartnerScopeInput
public List? CompanyIds { get; set; }
///
- /// 门店可用范围:ALL / SPECIFIED;传了 或 时自动为 SPECIFIED
+ /// 门店可用范围:ALL / SPECIFIED。
+ /// regionIds/groupIds/locationIds 可传哨兵 ["ALL"](大小写不敏感);
+ /// 即使本字段为 SPECIFIED,Id 数组为 ALL 时仍归档为 ALL(POST/PUT 均支持)。
///
public string AvailabilityType { get; set; } = "ALL";
///
- /// 适用 Region(多选),fl_group.Id;与 合并去重
+ /// 适用 Region(多选),fl_group.Id;与 合并去重;可含 ALL
///
public List? RegionIds { get; set; }
///
- /// 适用 Region(多选),与 相同
+ /// 适用 Region(多选),与 相同;可含 ALL
///
public List? GroupIds { get; set; }
///
- /// 适用门店(多选),location.Id;与 Region 合并后写入 fl_label_type_location
+ /// 适用门店(多选),location.Id;可含 ALL;与 Region 合并后写入 fl_label_type_location
///
public List? LocationIds { get; set; }
- public int OrderNum { get; set; }
+ /// 排序;未传或 null 时默认 0
+ public int? OrderNum { get; set; }
}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Product/ProductBatchImportOnlineInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Product/ProductBatchImportOnlineInputVo.cs
new file mode 100644
index 0000000..bb89576
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Product/ProductBatchImportOnlineInputVo.cs
@@ -0,0 +1,12 @@
+namespace FoodLabeling.Application.Contracts.Dtos.Product;
+
+///
+/// 产品 JSON 在线批量导入请求体
+///
+public class ProductBatchImportOnlineInputVo
+{
+ ///
+ /// 待导入行,每元素与单条 POST /api/app/product 的 一致
+ ///
+ public List Items { get; set; } = new();
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Product/ProductBatchImportOnlineResultDto.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Product/ProductBatchImportOnlineResultDto.cs
new file mode 100644
index 0000000..9019b7a
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Product/ProductBatchImportOnlineResultDto.cs
@@ -0,0 +1,31 @@
+namespace FoodLabeling.Application.Contracts.Dtos.Product;
+
+///
+/// 产品 JSON 在线批量导入结果
+///
+public class ProductBatchImportOnlineResultDto
+{
+ public int SuccessCount { get; set; }
+
+ public int FailCount { get; set; }
+
+ public List Errors { get; set; } = new();
+}
+
+///
+/// 产品 JSON 在线批量导入单条失败信息
+///
+public class ProductBatchImportOnlineErrorDto
+{
+ ///
+ /// 在请求 items 数组中的序号(从 0 开始)
+ ///
+ public int Index { get; set; }
+
+ ///
+ /// 产品名称(productName),便于定位失败行
+ ///
+ public string? ProductName { get; set; }
+
+ public string Message { get; set; } = string.Empty;
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Product/ProductCreateInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Product/ProductCreateInputVo.cs
index 5f849e5..aa46c7d 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Product/ProductCreateInputVo.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Product/ProductCreateInputVo.cs
@@ -1,4 +1,6 @@
using System.Collections.Generic;
+using System.Text.Json.Serialization;
+using FoodLabeling.Application.Contracts.Json;
namespace FoodLabeling.Application.Contracts.Dtos.Product;
@@ -35,18 +37,46 @@ public class ProductCreateInputVo
public string? AvailabilityType { get; set; }
///
- /// 适用 Company(fl_partner.Id,UI 称 Company);展开该公司下全部门店后与 Region/门店合并写入 fl_location_product
+ /// 适用 Company:ALL / SPECIFIED(产品 Company 仅支持 SPECIFIED + 单选具体 Guid)。
///
+ public string? AppliedPartnerType { get; set; }
+
+ ///
+ /// 适用 Company(fl_partner.Id,UI 称 Company);展开该公司下全部门店后与 Region/门店合并写入 fl_location_product。
+ /// 与 二选一或同值;产品 Company 仅支持单选,不支持 ALL。
+ /// 兼容前端误传数组:"partnerId":["{guid}"] 与字符串等价。
+ ///
+ [JsonConverter(typeof(StringOrFirstArrayItemJsonConverter))]
public string? PartnerId { get; set; }
///
- /// 适用 Region(fl_group.Id,UI 称 Region;库字段为 location.GroupName)
+ /// 适用 Company Id 列表(与 同义,前端常用字段)。仅支持单选。
+ ///
+ public List? PartnerIds { get; set; }
+
+ ///
+ /// 适用 Company(推荐前端字段)。仅支持单选:数组最多 1 个具体 Guid;不支持 ALL。
+ /// 传多个 Guid 或含 ALL 将报错;与 同时传时须一致。
+ ///
+ public List? CompanyIds { get; set; }
+
+ /// 适用 Region:ALL / SPECIFIED(与 独立,用于前端显式声明)
+ public string? AppliedRegionType { get; set; }
+
+ ///
+ /// 适用 Region(fl_group.Id,UI 称 Region;库字段为 location.GroupName)。
+ /// 与 合并;含 ALL 且无具体门店时,有具体 Company 则展开该公司门店,否则归档全局 ALL。
///
public List? GroupIds { get; set; }
///
- /// 适用门店 Id 列表;与 合并后写入 fl_location_product。
- /// 不传则不在本接口写入门店关联。
+ /// 适用 Region(与 同义,前端常用字段)。
+ ///
+ public List? RegionIds { get; set; }
+
+ ///
+ /// 适用门店 Id 列表。有具体 Guid 时以门店为准;含 ALL 且已指定 Company/Region 时表示该范围内全选(SPECIFIED 快照);
+ /// 无具体 Company/Region 时归档全局 ALL。
///
public List? LocationIds { get; set; }
}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Product/ProductGetOutputDto.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Product/ProductGetOutputDto.cs
index a5da767..2d6b923 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Product/ProductGetOutputDto.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Product/ProductGetOutputDto.cs
@@ -29,17 +29,25 @@ public class ProductGetOutputDto
/// 适用门店:ALL / SPECIFIED
public string AvailabilityType { get; set; } = "SPECIFIED";
- /// 适用 Company Id(fl_partner.Id,由关联门店反推;多公司时取第一个)
+ /// 适用 Company Id(fl_partner.Id,由关联门店反推;多公司时取第一个;ALL 时为 null)
public string? PartnerId { get; set; }
- /// 适用 Company Id 列表(去重)
+ /// 适用 Company Id 列表(去重;历史数据可能多条)
public List PartnerIds { get; set; } = new();
+ ///
+ /// 适用 Company(与编辑入参对齐)。单选:0~1 个具体 Guid;不支持 ALL。
+ ///
+ public List CompanyIds { get; set; } = new();
+
/// 适用 Region Id(fl_group.Id,由关联门店反推)
public List GroupIds { get; set; } = new();
+ /// 适用 Region(与 同义)
+ public List RegionIds { get; set; } = new();
+
///
- /// 适用门店 Id 列表(来自 fl_location_product)
+ /// 适用门店 Id 列表(来自 fl_location_product;覆盖 Region 全集时可折叠为 ["ALL"])
///
public List LocationIds { get; set; } = new();
}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryCreateInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryCreateInputVo.cs
index 77be6d3..435f05d 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryCreateInputVo.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryCreateInputVo.cs
@@ -1,13 +1,13 @@
namespace FoodLabeling.Application.Contracts.Dtos.ProductCategory;
+using FoodLabeling.Application.Contracts.Dtos.Common;
+
///
/// 产品模块:新增类别入参
///
-public class ProductCategoryCreateInputVo
+public class ProductCategoryCreateInputVo : ILabelEntityPartnerScopeInput
{
- ///
- /// 类别编码(可选,不传或空字符串表示无编码)
- ///
+ /// 类别编码(可选;未传或空字符串时由后端自动生成唯一编码)
public string? CategoryCode { get; set; }
public string CategoryName { get; set; } = string.Empty;
@@ -30,25 +30,40 @@ public class ProductCategoryCreateInputVo
public bool State { get; set; } = true;
///
- /// 门店可用范围:ALL / SPECIFIED
+ /// 适用 Company:ALL / SPECIFIED。
+ /// partnerIds/companyIds 可传哨兵 ["ALL"](大小写不敏感)。
+ ///
+ public string? AppliedPartnerType { get; set; }
+
+ /// 适用 Company(fl_partner.Id)
+ public List? PartnerIds { get; set; }
+
+ /// 与 相同(兼容字段)
+ public List? CompanyIds { get; set; }
+
+ ///
+ /// 门店可用范围:ALL / SPECIFIED。
+ /// regionIds/groupIds/locationIds 可传哨兵 ["ALL"](大小写不敏感);
+ /// 即使本字段为 SPECIFIED,Id 数组为 ALL 时仍归档为 ALL(POST/PUT 均支持)。
///
public string AvailabilityType { get; set; } = "ALL";
///
- /// 适用 Region(**多选**),fl_group.Id 数组;与 等价,推荐本字段。
+ /// 适用 Region(**多选**),fl_group.Id;可含 ALL;与 合并去重。
///
public List? RegionIds { get; set; }
///
- /// 适用 Region(多选),与 相同;传任一会合并去重。
+ /// 适用 Region(多选),与 相同;可含 ALL。
///
public List? GroupIds { get; set; }
///
- /// 适用门店(**多选**),location.Id 数组;与 Region 合并后写入 fl_product_category_location。
+ /// 适用门店(**多选**),location.Id;可含 ALL;与 Region 合并后写入 fl_product_category_location。
///
public List? LocationIds { get; set; }
- public int OrderNum { get; set; } = 0;
+ /// 排序;未传或 null 时默认 0
+ public int? OrderNum { get; set; }
}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryGetListOutputDto.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryGetListOutputDto.cs
index ebfc336..7be3554 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryGetListOutputDto.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryGetListOutputDto.cs
@@ -21,6 +21,16 @@ public class ProductCategoryGetListOutputDto
public string AvailabilityType { get; set; } = "ALL";
+ public string AppliedPartnerType { get; set; } = "ALL";
+
+ /// 适用 Company 展示
+ public string Company { get; set; } = string.Empty;
+
+ public List PartnerIds { get; set; } = new();
+
+ /// 与 相同
+ public List CompanyIds { get; set; } = new();
+
public int OrderNum { get; set; }
public DateTime LastEdited { get; set; }
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryGetOutputDto.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryGetOutputDto.cs
index 09e0891..7727aa2 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryGetOutputDto.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryGetOutputDto.cs
@@ -22,6 +22,16 @@ public class ProductCategoryGetOutputDto
public string AvailabilityType { get; set; } = "ALL";
+ public string AppliedPartnerType { get; set; } = "ALL";
+
+ /// 列表/详情 Company 展示
+ public string Company { get; set; } = string.Empty;
+
+ public List PartnerIds { get; set; } = new();
+
+ /// 与 相同
+ public List CompanyIds { get; set; } = new();
+
/// 适用 Region Id 列表(多选,fl_group.Id;由绑定门店反推)
public List RegionIds { get; set; } = new();
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductLocation/ProductLocationGetListInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductLocation/ProductLocationGetListInputVo.cs
index f0864f1..7e5ff02 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductLocation/ProductLocationGetListInputVo.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductLocation/ProductLocationGetListInputVo.cs
@@ -9,6 +9,12 @@ namespace FoodLabeling.Application.Contracts.Dtos.ProductLocation;
public class ProductLocationGetListInputVo : PagedAndSortedResultRequestDto
{
///
+ /// Company Id(fl_partner.Id);未传 时按该公司下门店展开,
+ /// 并包含 AvailabilityType=ALL 的产品。
+ ///
+ public string? PartnerId { get; set; }
+
+ ///
/// 门店Id(location.Id,string 表示)
///
public string? LocationId { get; set; }
@@ -18,4 +24,3 @@ public class ProductLocationGetListInputVo : PagedAndSortedResultRequestDto
///
public string? ProductId { get; set; }
}
-
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacRole/RbacRoleCreateInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacRole/RbacRoleCreateInputVo.cs
index 41bf2f0..6bb0c2e 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacRole/RbacRoleCreateInputVo.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacRole/RbacRoleCreateInputVo.cs
@@ -24,11 +24,16 @@ public class RbacRoleCreateInputVo
public int? OrderNum { get; set; }
///
- /// 绑定菜单 Id;与 accessPermissions 同时传时以本字段为准
+ /// 绑定菜单 Id;与 menuPermissionKeys / accessPermissions 同时传时以本字段为准
///
public List? MenuIds { get; set; }
///
+ /// 绑定菜单 Id(字符串 Guid 列表);与 等价, 优先
+ ///
+ public List? MenuPermissionKeys { get; set; }
+
+ ///
/// 按 PermissionCode 绑定菜单:JSON 数组字符串(如 ["manage_labels"])、英文逗号分隔;传空字符串表示清空绑定;不传则不修改已有绑定(仅编辑时)
///
public string? AccessPermissions { get; set; }
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacRole/RbacRoleGetListOutputDto.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacRole/RbacRoleGetListOutputDto.cs
index d8d2210..5d709ed 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacRole/RbacRoleGetListOutputDto.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacRole/RbacRoleGetListOutputDto.cs
@@ -28,5 +28,10 @@ public class RbacRoleGetListOutputDto
/// 角色访问权限码列表(与库字段 AccessPermissionCodes 对应)
///
public List AccessPermissionCodes { get; set; } = new();
+
+ ///
+ /// 已绑定菜单 Id 列表(字符串 Guid,与前端 menuPermissionKeys 字段一致)
+ ///
+ public List MenuPermissionKeys { get; set; } = new();
}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/TeamMember/TeamMemberBatchImportOnlineInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/TeamMember/TeamMemberBatchImportOnlineInputVo.cs
new file mode 100644
index 0000000..02a9d49
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/TeamMember/TeamMemberBatchImportOnlineInputVo.cs
@@ -0,0 +1,12 @@
+namespace FoodLabeling.Application.Contracts.Dtos.TeamMember;
+
+///
+/// 成员 JSON 在线批量导入请求体
+///
+public class TeamMemberBatchImportOnlineInputVo
+{
+ ///
+ /// 待导入行,每元素与单条 POST /api/app/team-member 的 一致
+ ///
+ public List Items { get; set; } = new();
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/TeamMember/TeamMemberBatchImportOnlineResultDto.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/TeamMember/TeamMemberBatchImportOnlineResultDto.cs
new file mode 100644
index 0000000..fec2e63
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/TeamMember/TeamMemberBatchImportOnlineResultDto.cs
@@ -0,0 +1,31 @@
+namespace FoodLabeling.Application.Contracts.Dtos.TeamMember;
+
+///
+/// 成员 JSON 在线批量导入结果
+///
+public class TeamMemberBatchImportOnlineResultDto
+{
+ public int SuccessCount { get; set; }
+
+ public int FailCount { get; set; }
+
+ public List Errors { get; set; } = new();
+}
+
+///
+/// 成员 JSON 在线批量导入单条失败信息
+///
+public class TeamMemberBatchImportOnlineErrorDto
+{
+ ///
+ /// 在请求 items 数组中的序号(从 0 开始)
+ ///
+ public int Index { get; set; }
+
+ ///
+ /// 登录账号(userName),便于定位失败行
+ ///
+ public string? UserName { get; set; }
+
+ public string Message { get; set; } = string.Empty;
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/TeamMember/TeamMemberCreateInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/TeamMember/TeamMemberCreateInputVo.cs
index 646bc84..398e174 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/TeamMember/TeamMemberCreateInputVo.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/TeamMember/TeamMemberCreateInputVo.cs
@@ -28,20 +28,28 @@ public class TeamMemberCreateInputVo
public List? PartnerIds { get; set; }
///
- /// 适用 Region 多选(fl_group.Id);Company Admin 仅传 Company 时可省略,后端自动绑定该公司下全部 Region 与门店
+ /// 适用 Region 多选(fl_group.Id);可含 ALL 哨兵(大小写不敏感,与具体 Guid 同传时以 ALL 为准)。
+ /// Company Admin 仅传 Company 时可省略;locationIds 为空且含 ALL 时展开该公司全部 Region 下门店。
///
public List? RegionIds { get; set; }
///
- /// 适用 Region 多选(与 相同)
+ /// 适用 Region 多选(与 相同);可含 ALL。
///
public List? GroupIds { get; set; }
///
- /// 适用门店多选(location.Id);Company Admin 仅传 Company 时可省略,与 Region 合并后写入 userlocation
+ /// 适用门店多选(location.Id);可含 ALL 哨兵。
+ /// 有具体 Region 时展开该 Region 下门店;仅有 Company 时展开该公司全部门店。
+ /// Company Admin 仅传 Company 时可省略。
///
public List? LocationIds { get; set; }
+ ///
+ /// 适用门店多选别名(与 合并解析);可含 ALL 或门店 Guid。
+ ///
+ public List? Locations { get; set; }
+
public bool State { get; set; } = true;
}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/TeamMember/TeamMemberGetOutputDto.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/TeamMember/TeamMemberGetOutputDto.cs
index bf245f9..9df7631 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/TeamMember/TeamMemberGetOutputDto.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/TeamMember/TeamMemberGetOutputDto.cs
@@ -19,12 +19,13 @@ public class TeamMemberGetOutputDto
/// 适用 Company Id(多选,由绑定门店反推)
public List PartnerIds { get; set; } = new();
- /// 适用 Region Id(多选,fl_group.Id)
+ /// 适用 Region Id(多选,fl_group.Id;覆盖该公司全部 Region 时回显 ["ALL"])
public List RegionIds { get; set; } = new();
/// 与 相同
public List GroupIds { get; set; } = new();
+ /// 绑定门店 Id;覆盖该公司全部门店时回显 ["ALL"](库中仍存展开 Guid)
public List LocationIds { get; set; } = new();
public List AssignedLocations { get; set; } = new();
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/TeamMember/TeamMemberUpdateInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/TeamMember/TeamMemberUpdateInputVo.cs
index 163b529..df91908 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/TeamMember/TeamMemberUpdateInputVo.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/TeamMember/TeamMemberUpdateInputVo.cs
@@ -28,20 +28,26 @@ public class TeamMemberUpdateInputVo
public List? PartnerIds { get; set; }
///
- /// 适用 Region 多选(fl_group.Id);Company Admin 仅传 Company 时可省略
+ /// 适用 Region 多选(fl_group.Id);可含 ALL 哨兵(大小写不敏感,与具体 Guid 同传时以 ALL 为准)。
///
public List? RegionIds { get; set; }
///
- /// 适用 Region 多选(与 相同)
+ /// 适用 Region 多选(与 相同);可含 ALL。
///
public List? GroupIds { get; set; }
///
- /// 适用门店多选(location.Id);Company Admin 仅传 Company 时可省略
+ /// 适用门店多选(location.Id);可含 ALL 哨兵。
+ /// 有具体 Region 时表示该 Region 下全部门店;仅有 Company 时按公司全部门店落库。
///
public List? LocationIds { get; set; }
+ ///
+ /// 适用门店多选别名(与 合并解析);可含 ALL 或门店 Guid。
+ ///
+ public List? Locations { get; set; }
+
public bool State { get; set; } = true;
}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingCategoryCreateInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingCategoryCreateInputVo.cs
new file mode 100644
index 0000000..b46a65e
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingCategoryCreateInputVo.cs
@@ -0,0 +1,11 @@
+namespace FoodLabeling.Application.Contracts.Dtos.Training;
+
+public class TrainingCategoryCreateInputVo
+{
+ public string CategoryName { get; set; } = string.Empty;
+
+ /// 空=一级分类;有值=二级分类
+ public string? ParentId { get; set; }
+
+ public int OrderNum { get; set; }
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingCategoryGetOutputDto.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingCategoryGetOutputDto.cs
new file mode 100644
index 0000000..45cb875
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingCategoryGetOutputDto.cs
@@ -0,0 +1,16 @@
+namespace FoodLabeling.Application.Contracts.Dtos.Training;
+
+public class TrainingCategoryGetOutputDto
+{
+ public string Id { get; set; } = string.Empty;
+
+ public string CategoryName { get; set; } = string.Empty;
+
+ public string? ParentId { get; set; }
+
+ public int OrderNum { get; set; }
+
+ public DateTime CreationTime { get; set; }
+
+ public DateTime? LastModificationTime { get; set; }
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingCategoryTreeInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingCategoryTreeInputVo.cs
new file mode 100644
index 0000000..ca4aa55
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingCategoryTreeInputVo.cs
@@ -0,0 +1,13 @@
+namespace FoodLabeling.Application.Contracts.Dtos.Training;
+
+public class TrainingCategoryTreeInputVo
+{
+ /// 关键字(匹配分类名或文件名)
+ public string? Keyword { get; set; }
+
+ /// 按门店筛选可见文件;不传则不过滤文件权限
+ public string? LocationId { get; set; }
+
+ /// 是否包含文件列表,默认 true
+ public bool IncludeFiles { get; set; } = true;
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingCategoryTreeNodeDto.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingCategoryTreeNodeDto.cs
new file mode 100644
index 0000000..ffeb807
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingCategoryTreeNodeDto.cs
@@ -0,0 +1,16 @@
+namespace FoodLabeling.Application.Contracts.Dtos.Training;
+
+public class TrainingCategoryTreeNodeDto
+{
+ public string Id { get; set; } = string.Empty;
+
+ public string CategoryName { get; set; } = string.Empty;
+
+ public string? ParentId { get; set; }
+
+ public int OrderNum { get; set; }
+
+ public List Children { get; set; } = new();
+
+ public List Files { get; set; } = new();
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingCategoryUpdateInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingCategoryUpdateInputVo.cs
new file mode 100644
index 0000000..9f7c6c4
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingCategoryUpdateInputVo.cs
@@ -0,0 +1,8 @@
+namespace FoodLabeling.Application.Contracts.Dtos.Training;
+
+public class TrainingCategoryUpdateInputVo
+{
+ public string CategoryName { get; set; } = string.Empty;
+
+ public int OrderNum { get; set; }
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileDto.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileDto.cs
new file mode 100644
index 0000000..1f0306c
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileDto.cs
@@ -0,0 +1,50 @@
+namespace FoodLabeling.Application.Contracts.Dtos.Training;
+
+public class TrainingFileDto
+{
+ public string Id { get; set; } = string.Empty;
+
+ public string CategoryId { get; set; } = string.Empty;
+
+ public string FileName { get; set; } = string.Empty;
+
+ public string FileUrl { get; set; } = string.Empty;
+
+ /// image / doc / other
+ public string FileType { get; set; } = "other";
+
+ public long FileSize { get; set; }
+
+ public int OrderNum { get; set; }
+
+ public string AppliedPartnerType { get; set; } = "ALL";
+
+ /// Company 展示(ALL 时为 All Companies)
+ public string Company { get; set; } = string.Empty;
+
+ public List PartnerIds { get; set; } = new();
+
+ /// 与 相同
+ public List CompanyIds { get; set; } = new();
+
+ public string AppliedRegionType { get; set; } = "ALL";
+
+ /// Region 展示
+ public string Region { get; set; } = string.Empty;
+
+ public List RegionIds { get; set; } = new();
+
+ /// 与 相同
+ public List GroupIds { get; set; } = new();
+
+ public string AvailabilityType { get; set; } = "ALL";
+
+ /// Location 展示
+ public string Location { get; set; } = string.Empty;
+
+ public List LocationIds { get; set; } = new();
+
+ public DateTime CreationTime { get; set; }
+
+ public DateTime? LastModificationTime { get; set; }
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileScopeInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileScopeInputVo.cs
new file mode 100644
index 0000000..bfc1e58
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileScopeInputVo.cs
@@ -0,0 +1,27 @@
+using FoodLabeling.Application.Contracts.Dtos.Common;
+
+namespace FoodLabeling.Application.Contracts.Dtos.Training;
+
+public class TrainingFileScopeInputVo : ITrainingFileScopeInput
+{
+ public string? AppliedPartnerType { get; set; }
+
+ public List? PartnerIds { get; set; }
+
+ public List? CompanyIds { get; set; }
+
+ /// 适用 Region:ALL / SPECIFIED
+ public string? AppliedRegionType { get; set; }
+
+ public List? RegionIds { get; set; }
+
+ public List? GroupIds { get; set; }
+
+ /// 适用 Location:ALL / SPECIFIED
+ public string? AvailabilityType { get; set; }
+
+ /// 适用 Location:ALL / SPECIFIED( 别名)
+ public string? AppliedLocationType { get; set; }
+
+ public List? LocationIds { get; set; }
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileScopeOutputDto.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileScopeOutputDto.cs
new file mode 100644
index 0000000..77b51ce
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileScopeOutputDto.cs
@@ -0,0 +1,26 @@
+namespace FoodLabeling.Application.Contracts.Dtos.Training;
+
+public class TrainingFileScopeOutputDto
+{
+ public string AppliedPartnerType { get; set; } = "ALL";
+
+ public string Company { get; set; } = string.Empty;
+
+ public List PartnerIds { get; set; } = new();
+
+ public List CompanyIds { get; set; } = new();
+
+ public string AppliedRegionType { get; set; } = "ALL";
+
+ public string Region { get; set; } = string.Empty;
+
+ public List RegionIds { get; set; } = new();
+
+ public List GroupIds { get; set; } = new();
+
+ public string AvailabilityType { get; set; } = "ALL";
+
+ public string Location { get; set; } = string.Empty;
+
+ public List LocationIds { get; set; } = new();
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileSortInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileSortInputVo.cs
new file mode 100644
index 0000000..b5286db
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileSortInputVo.cs
@@ -0,0 +1,6 @@
+namespace FoodLabeling.Application.Contracts.Dtos.Training;
+
+public class TrainingFileSortInputVo
+{
+ public List Items { get; set; } = new();
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileSortItemVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileSortItemVo.cs
new file mode 100644
index 0000000..81ec9f4
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileSortItemVo.cs
@@ -0,0 +1,8 @@
+namespace FoodLabeling.Application.Contracts.Dtos.Training;
+
+public class TrainingFileSortItemVo
+{
+ public string Id { get; set; } = string.Empty;
+
+ public int OrderNum { get; set; }
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileUpdateInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileUpdateInputVo.cs
new file mode 100644
index 0000000..9cc01c7
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileUpdateInputVo.cs
@@ -0,0 +1,37 @@
+using FoodLabeling.Application.Contracts.Dtos.Common;
+
+namespace FoodLabeling.Application.Contracts.Dtos.Training;
+
+public class TrainingFileUpdateInputVo : ITrainingFileScopeInput
+{
+ public string FileName { get; set; } = string.Empty;
+
+ public int OrderNum { get; set; }
+
+ /// 适用 Company:ALL / SPECIFIED
+ public string? AppliedPartnerType { get; set; }
+
+ /// 适用 Company(fl_partner.Id);可含 ALL
+ public List? PartnerIds { get; set; }
+
+ /// 与 相同;可含 ALL
+ public List? CompanyIds { get; set; }
+
+ /// 适用 Region:ALL / SPECIFIED
+ public string? AppliedRegionType { get; set; }
+
+ /// 适用 Region(fl_group.Id);与 合并;可含 ALL
+ public List? RegionIds { get; set; }
+
+ /// 与 相同;可含 ALL
+ public List? GroupIds { get; set; }
+
+ /// 适用 Location:ALL / SPECIFIED
+ public string? AvailabilityType { get; set; }
+
+ /// 适用 Location:ALL / SPECIFIED( 别名)
+ public string? AppliedLocationType { get; set; }
+
+ /// 适用门店(location.Id);可含 ALL
+ public List? LocationIds { get; set; }
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileUploadInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileUploadInputVo.cs
new file mode 100644
index 0000000..f92b59a
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileUploadInputVo.cs
@@ -0,0 +1,53 @@
+using FoodLabeling.Application.Contracts.Dtos.Common;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+
+namespace FoodLabeling.Application.Contracts.Dtos.Training;
+
+public class TrainingFileUploadInputVo : ITrainingFileScopeInput
+{
+ [FromForm(Name = "file")]
+ public IFormFile File { get; set; } = default!;
+
+ [FromForm(Name = "categoryId")]
+ public string CategoryId { get; set; } = string.Empty;
+
+ [FromForm(Name = "orderNum")]
+ public int OrderNum { get; set; }
+
+ /// 适用 Company:ALL / SPECIFIED
+ [FromForm(Name = "appliedPartnerType")]
+ public string? AppliedPartnerType { get; set; }
+
+ /// 适用 Company(fl_partner.Id);可含 ALL
+ [FromForm(Name = "partnerIds")]
+ public List? PartnerIds { get; set; }
+
+ /// 与 相同;可含 ALL
+ [FromForm(Name = "companyIds")]
+ public List? CompanyIds { get; set; }
+
+ /// 适用 Region:ALL / SPECIFIED
+ [FromForm(Name = "appliedRegionType")]
+ public string? AppliedRegionType { get; set; }
+
+ /// 适用 Region(fl_group.Id);与 合并;可含 ALL
+ [FromForm(Name = "regionIds")]
+ public List? RegionIds { get; set; }
+
+ /// 与 相同;可含 ALL
+ [FromForm(Name = "groupIds")]
+ public List? GroupIds { get; set; }
+
+ /// 适用 Location:ALL / SPECIFIED
+ [FromForm(Name = "availabilityType")]
+ public string? AvailabilityType { get; set; }
+
+ /// 适用 Location:ALL / SPECIFIED( 别名)
+ [FromForm(Name = "appliedLocationType")]
+ public string? AppliedLocationType { get; set; }
+
+ /// 适用门店(location.Id);可含 ALL
+ [FromForm(Name = "locationIds")]
+ public List? LocationIds { get; set; }
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileUploadOutputDto.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileUploadOutputDto.cs
new file mode 100644
index 0000000..893443d
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/TrainingFileUploadOutputDto.cs
@@ -0,0 +1,14 @@
+namespace FoodLabeling.Application.Contracts.Dtos.Training;
+
+public class TrainingFileUploadOutputDto
+{
+ public string Id { get; set; } = string.Empty;
+
+ public string FileName { get; set; } = string.Empty;
+
+ public string FileUrl { get; set; } = string.Empty;
+
+ public string FileType { get; set; } = "other";
+
+ public long FileSize { get; set; }
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/UsAppTrainingTreeInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/UsAppTrainingTreeInputVo.cs
new file mode 100644
index 0000000..4d0fc19
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Training/UsAppTrainingTreeInputVo.cs
@@ -0,0 +1,8 @@
+namespace FoodLabeling.Application.Contracts.Dtos.Training;
+
+public class UsAppTrainingTreeInputVo
+{
+ public string LocationId { get; set; } = string.Empty;
+
+ public string? Keyword { get; set; }
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IAuthSessionAppService.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IAuthSessionAppService.cs
index 601be57..2df43a9 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IAuthSessionAppService.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IAuthSessionAppService.cs
@@ -12,8 +12,11 @@ public interface IAuthSessionAppService : IApplicationService
/// 获取当前登录用户的角色编码、权限码与可见菜单树
///
///
- /// 与框架 UserManager.GetInfoAsync 一致;用户名为 admin 时返回全部未删除菜单(与 AccountService.GetVue3Router 行为对齐)。
- /// 返回体额外包含:lastUpdated(系统编辑全局时间戳,与任意写接口成功联动;无戳时回退用户 LastModificationTime)、role(角色展示名,多角色英文逗号拼接)、fullName(姓名优先,其次昵称、用户名)。
+ /// 非 SaaS(EnabledSaasMultiTenancy=false)且用户名为 admin 时返回全部未删除菜单(与 AccountService.GetVue3Router 行为对齐)。
+ /// SaaS 开启时:平台主库登录(JWT / __tenant 无业务租户 Id,含全 0)即使用户名为 admin 也仅返回平台菜单
+ /// (PermissionCode 以 menu.platform 开头或 Router 以 /platform 开头,含祖先节点以成树);
+ /// 公司业务租户登录时即使用户名为 admin 也按 RoleMenu 关联查询,与 UpdateCompanyMenus 同步的管理员菜单权限一致。
+ /// 返回体额外包含:userId(当前登录用户 Id,与 user.id 一致,非租户 Id)、lastUpdated(系统编辑全局时间戳,与任意写接口成功联动;无戳时回退用户 LastModificationTime)、role(角色展示名,多角色英文逗号拼接)、fullName(姓名优先,其次昵称、用户名)。
/// 角色名通过 Role 表直查(RoleDbEntity),避免走仓储 IDataPermission。
///
/// 用户简要信息、权限码与菜单树
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelAlertTimerAppService.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelAlertTimerAppService.cs
new file mode 100644
index 0000000..31d3243
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelAlertTimerAppService.cs
@@ -0,0 +1,22 @@
+using FoodLabeling.Application.Contracts.Dtos.Common;
+using FoodLabeling.Application.Contracts.Dtos.LabelAlertTimer;
+using Volo.Abp.Application.Services;
+
+namespace FoodLabeling.Application.Contracts.IServices;
+
+///
+/// 标签告警计时器(App)
+///
+public interface ILabelAlertTimerAppService : IApplicationService
+{
+ Task> GetListAsync(LabelAlertTimerGetListInputVo input);
+
+ ///
+ /// App:当前账号当前门店告警列表(含倒计时;locationId 可省略,走已选门店缓存)
+ ///
+ Task> GetAppListAsync(LabelAlertTimerGetListInputVo input);
+
+ Task DeleteAsync(string id);
+
+ Task CheckExpiredAsync(LabelAlertTimerCheckExpiredInputVo input);
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelAppService.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelAppService.cs
index 7506e37..5abc6f2 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelAppService.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelAppService.cs
@@ -21,7 +21,8 @@ public interface ILabelAppService : IApplicationService
Task GetAsync(string id);
///
- /// 新增标签。Body 支持 appliedRegionType(ALL/SPECIFIED)、regionIds / groupIds(落库 fl_label_region)、locationIds(主字段,落库 fl_label_location);locationId 与 locationIds 合并;labelTypeId 可选。
+ /// 新增标签。Body 支持 appliedRegionType(ALL/SPECIFIED)、
+ /// regionIds / groupIds / locationIds(可含哨兵 ALL,归档为 AppliedRegionType=ALL)。
///
Task CreateAsync(LabelCreateInputVo input);
@@ -31,7 +32,7 @@ public interface ILabelAppService : IApplicationService
Task BatchCreateAsync(LabelBatchCreateInputVo input);
///
- /// 编辑标签(id=LabelCode)。适用 Region / 门店字段同 。
+ /// 编辑标签(id=LabelCode)。范围传参与 一致,支持 Id 数组传 ALL。
///
Task UpdateAsync(string id, LabelUpdateInputVo input);
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelCategoryAppService.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelCategoryAppService.cs
index 32464df..fb18670 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelCategoryAppService.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelCategoryAppService.cs
@@ -9,8 +9,10 @@ public interface ILabelCategoryAppService
Task GetAsync(string id);
+ /// 新增标签分类;regionIds/groupIds/locationIds 支持哨兵 ALL。
Task CreateAsync(LabelCategoryCreateInputVo input);
+ /// 编辑标签分类;范围传参与新增一致,支持 ALL。
Task UpdateAsync(string id, LabelCategoryUpdateInputVo input);
Task DeleteAsync(string id);
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelMultipleOptionAppService.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelMultipleOptionAppService.cs
index 38919ab..36e6f99 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelMultipleOptionAppService.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelMultipleOptionAppService.cs
@@ -9,8 +9,14 @@ public interface ILabelMultipleOptionAppService
Task GetAsync(string id);
+ ///
+ /// 新增多选项;regionIds、groupIds、locationIds 可传 ALL 哨兵(POST)。
+ ///
Task CreateAsync(LabelMultipleOptionCreateInputVo input);
+ ///
+ /// 编辑多选项;适用范围与新增相同,regionIds、groupIds、locationIds 可传 ALL(PUT)。
+ ///
Task UpdateAsync(string id, LabelMultipleOptionUpdateInputVo input);
Task DeleteAsync(string id);
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelTemplateAppService.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelTemplateAppService.cs
index 63bfc57..1a855d1 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelTemplateAppService.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelTemplateAppService.cs
@@ -25,12 +25,14 @@ public interface ILabelTemplateAppService : IApplicationService
///
/// 新增标签模板;body 支持 Company/Region/Location 三维范围(各维度 ALL/SPECIFIED + Id 数组)。
+ /// regionIds、groupIds、locationIds、appliedLocationIds 可传 ALL 哨兵(POST)。
///
Task CreateAsync(LabelTemplateCreateInputVo input);
///
/// 编辑标签模板(版本号 +1,重建 elements);适用范围多选规则同新增。
/// body 支持 printOrientation(vertical / horizontal,横打不交换 Width/Height)。
+ /// regionIds、groupIds、locationIds、appliedLocationIds 可传 ALL 哨兵(PUT)。
///
Task UpdateAsync(string id, LabelTemplateUpdateInputVo input);
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelTypeAppService.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelTypeAppService.cs
index aa4d03a..41d04aa 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelTypeAppService.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelTypeAppService.cs
@@ -9,8 +9,10 @@ public interface ILabelTypeAppService
Task GetAsync(string id);
+ /// 新增标签类型;regionIds/groupIds/locationIds 支持哨兵 ALL。
Task CreateAsync(LabelTypeCreateInputVo input);
+ /// 编辑标签类型;范围传参与新增一致,支持 ALL。
Task UpdateAsync(string id, LabelTypeUpdateInputVo input);
Task DeleteAsync(string id);
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IProductAppService.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IProductAppService.cs
index e40d991..6aee5eb 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IProductAppService.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IProductAppService.cs
@@ -32,8 +32,10 @@ public interface IProductAppService : IApplicationService
///
///
/// 可选;为空时后端生成唯一编码(如 PRD_ + Guid)。
- /// 若传 (Company)、(Region)
- /// 和/或 ,合并后写入 fl_location_product。
+ /// Company 传 (推荐,仅单选具体 Guid,不支持 ALL)
+ /// 或 ;
+ /// 再与 / 合并写入 fl_location_product。
+ /// Region/Location 数组含 ["ALL"] 时,即使 AvailabilityType 为 SPECIFIED 也归档为 ALL,清空 fl_location_product 快照。
///
Task CreateAsync(ProductCreateInputVo input);
@@ -41,8 +43,8 @@ public interface IProductAppService : IApplicationService
/// 编辑产品
///
///
- /// 当请求体包含 、
- /// 和/或 时,合并后整表替换门店关联;均不传则不改。
+ /// 当请求体包含 companyIds/partnerId、groupIds 和/或 locationIds 时,合并后整表替换门店关联;均不传则不改。
+ /// Company 仅支持单选(companyIds 最多 1 个具体 Guid,不支持 ALL)。Region/Location ALL 哨兵规则同 。
///
Task UpdateAsync(Guid id, ProductUpdateInputVo input);
@@ -74,5 +76,36 @@ public interface IProductAppService : IApplicationService
/// 批量编辑产品(JSON 一次提交多行,与单条 PUT 字段一致)
///
Task UpdateProductsBulkAsync(ProductBulkUpdateInputVo input);
+
+ ///
+ /// JSON 在线批量导入产品(逐行调用 ,部分成功)
+ ///
+ ///
+ /// 请求体为 JSON,每行字段与单条新增 一致。
+ ///
+ /// 示例请求:
+ /// ```json
+ /// {
+ /// "items": [
+ /// {
+ /// "productName": "Tuna & Bacon Sub",
+ /// "categoryId": "CATEGORY_ID",
+ /// "productCode": "40001",
+ /// "state": true,
+ /// "locationIds": ["LOCATION_GUID"]
+ /// }
+ /// ]
+ /// }
+ /// ```
+ ///
+ /// 参数说明:
+ /// - items: 待导入行数组;index 从 0 起;单次最多 MaxImportRows 条(默认 5000)
+ ///
+ /// 批量导入请求体
+ /// 成功数、失败数及失败明细(index、productName、message)
+ /// 全部或部分行处理完成,见返回体中的计数与 errors
+ /// 整单校验失败(如 items 为空、超过单次条数上限)
+ /// 服务器错误
+ Task BatchImportOnlineAsync(ProductBatchImportOnlineInputVo input);
}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IProductCategoryAppService.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IProductCategoryAppService.cs
index 69e6316..752a87b 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IProductCategoryAppService.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IProductCategoryAppService.cs
@@ -17,12 +17,12 @@ public interface IProductCategoryAppService : IApplicationService
Task GetAsync(string id);
///
- /// 新增类别;categoryCode 可选;body 传 regionIds(Region 多选)与 locationIds(门店多选)绑定适用范围。
+ /// 新增类别;支持 companyIds/partnerIds 与 Region/Location 哨兵 ALL(POST)。
///
Task CreateAsync(ProductCategoryCreateInputVo input);
///
- /// 编辑类别;categoryCode 可选;regionIds/locationIds 多选数组规则同新增;传空数组 [] 可清空对应范围。
+ /// 编辑类别;范围传参与新增一致,companyIds 全选回显/保存支持 ["ALL"](PUT)。
///
Task UpdateAsync(string id, ProductCategoryUpdateInputVo input);
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ITeamMemberAppService.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ITeamMemberAppService.cs
index 1c8c36d..63564d8 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ITeamMemberAppService.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ITeamMemberAppService.cs
@@ -14,8 +14,14 @@ public interface ITeamMemberAppService
Task GetAsync(Guid id);
+ ///
+ /// 新增成员(POST /api/app/team-member)。locationIds / regionIds / groupIds / locations 可传 ALL 哨兵。
+ ///
Task CreateAsync(TeamMemberCreateInputVo input);
+ ///
+ /// 更新成员(PUT /api/app/team-member/{id})。范围传参规则与 相同。
+ ///
Task UpdateAsync(Guid id, TeamMemberUpdateInputVo input);
Task DeleteAsync(Guid id);
@@ -39,4 +45,37 @@ public interface ITeamMemberAppService
/// 批量编辑成员(JSON 一次提交多行)
///
Task UpdateTeamMembersBulkAsync(TeamMemberBulkUpdateInputVo input);
+
+ ///
+ /// JSON 在线批量导入成员(逐行调用 ,部分成功)
+ ///
+ ///
+ /// 请求体为 JSON,每行字段与单条新增 一致;
+ /// password 为空时使用配置 TeamMemberImportDefaultPassword。
+ ///
+ /// 示例请求:
+ /// ```json
+ /// {
+ /// "items": [
+ /// {
+ /// "fullName": "John Doe",
+ /// "userName": "john@example.com",
+ /// "email": "john@example.com",
+ /// "roleId": "ROLE_GUID",
+ /// "locationIds": ["LOCATION_GUID"],
+ /// "state": true
+ /// }
+ /// ]
+ /// }
+ /// ```
+ ///
+ /// 参数说明:
+ /// - items: 待导入行数组;index 从 0 起;单次最多 MaxImportRows 条(默认 5000)
+ ///
+ /// 批量导入请求体
+ /// 成功数、失败数及失败明细(index、userName、message)
+ /// 全部或部分行处理完成,见返回体中的计数与 errors
+ /// 整单校验失败(如 items 为空、超过单次条数上限)
+ /// 服务器错误
+ Task BatchImportOnlineAsync(TeamMemberBatchImportOnlineInputVo input);
}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ITrainingAppService.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ITrainingAppService.cs
new file mode 100644
index 0000000..f9ec1f1
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ITrainingAppService.cs
@@ -0,0 +1,27 @@
+using FoodLabeling.Application.Contracts.Dtos.Training;
+using Volo.Abp.Application.Services;
+
+namespace FoodLabeling.Application.Contracts.IServices;
+
+public interface ITrainingAppService : IApplicationService
+{
+ Task> GetCategoryTreeAsync(TrainingCategoryTreeInputVo input);
+
+ Task CreateCategoryAsync(TrainingCategoryCreateInputVo input);
+
+ Task UpdateCategoryAsync(string id, TrainingCategoryUpdateInputVo input);
+
+ Task DeleteCategoryAsync(string id);
+
+ Task UploadFileAsync(TrainingFileUploadInputVo input);
+
+ Task UpdateFileAsync(string id, TrainingFileUpdateInputVo input);
+
+ Task DeleteFileAsync(string id);
+
+ Task SortFilesAsync(TrainingFileSortInputVo input);
+
+ Task GetFileScopeAsync(string id);
+
+ Task SetFileScopeAsync(string id, TrainingFileScopeInputVo input);
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IUsAppTrainingAppService.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IUsAppTrainingAppService.cs
new file mode 100644
index 0000000..cde8409
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IUsAppTrainingAppService.cs
@@ -0,0 +1,12 @@
+using FoodLabeling.Application.Contracts.Dtos.Training;
+using Volo.Abp.Application.Services;
+
+namespace FoodLabeling.Application.Contracts.IServices;
+
+///
+/// App 培训 / 资料中心
+///
+public interface IUsAppTrainingAppService : IApplicationService
+{
+ Task> GetTreeAsync(UsAppTrainingTreeInputVo input);
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Json/StringOrFirstArrayItemJsonConverter.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Json/StringOrFirstArrayItemJsonConverter.cs
new file mode 100644
index 0000000..fce83cd
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Json/StringOrFirstArrayItemJsonConverter.cs
@@ -0,0 +1,64 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace FoodLabeling.Application.Contracts.Json;
+
+///
+/// 反序列化时兼容 JSON 字符串或字符串数组(取首个非空元素),用于前端误把单选字段传成数组的场景(如 partnerId)。
+///
+public sealed class StringOrFirstArrayItemJsonConverter : JsonConverter
+{
+ public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ switch (reader.TokenType)
+ {
+ case JsonTokenType.Null:
+ return null;
+ case JsonTokenType.String:
+ return reader.GetString();
+ case JsonTokenType.StartArray:
+ {
+ string? first = null;
+ while (reader.Read())
+ {
+ if (reader.TokenType == JsonTokenType.EndArray)
+ {
+ break;
+ }
+
+ if (reader.TokenType == JsonTokenType.Null)
+ {
+ continue;
+ }
+
+ if (reader.TokenType != JsonTokenType.String)
+ {
+ throw new JsonException("partnerId 数组元素必须是字符串");
+ }
+
+ var item = reader.GetString()?.Trim();
+ if (first is null && !string.IsNullOrWhiteSpace(item))
+ {
+ first = item;
+ }
+ }
+
+ return first;
+ }
+ default:
+ throw new JsonException($"无法将 JSON token {reader.TokenType} 转为字符串(partnerId)");
+ }
+ }
+
+ public override void Write(Utf8JsonWriter writer, string? value, JsonSerializerOptions options)
+ {
+ if (value is null)
+ {
+ writer.WriteNullValue();
+ }
+ else
+ {
+ writer.WriteStringValue(value);
+ }
+ }
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/UsAppJwtClaims.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/UsAppJwtClaims.cs
index 2727138..faa7318 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/UsAppJwtClaims.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/UsAppJwtClaims.cs
@@ -10,4 +10,7 @@ public static class UsAppJwtClaims
/// 美国版移动端 App
public const string ClientKindUsApp = "us-app";
+
+ /// 泰额版 App(us-app-auth 转发 th-app-auth 签发)
+ public const string ClientKindThApp = "th_app";
}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/FoodLabeling.Application.csproj b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/FoodLabeling.Application.csproj
index 9caa24b..0b777f9 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/FoodLabeling.Application.csproj
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/FoodLabeling.Application.csproj
@@ -4,10 +4,35 @@
+
+
+ FoodLabeling.TenantMigrations.fl_entity_applied_region_type.sql
+
+
+ FoodLabeling.TenantMigrations.fl_label_partner_id.sql
+
+
+ FoodLabeling.TenantMigrations.fl_product_category_partner_scope.sql
+
+
+ FoodLabeling.TenantMigrations.fl_userlocation.sql
+
+
+ FoodLabeling.TenantMigrations.fl_team_member_scope.sql
+
+
+ FoodLabeling.TenantMigrations.fl_training.sql
+
+
+ FoodLabeling.TenantMigrations.fl_label_alert_timer.sql
+
+
+
+
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/AllScopeBindingHelper.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/AllScopeBindingHelper.cs
index 46b1dee..63aebad 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/AllScopeBindingHelper.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/AllScopeBindingHelper.cs
@@ -23,22 +23,36 @@ public static class AllScopeBindingHelper
return selected.Count == 0;
}
- if (selected.Count != universe.Count)
+ var universeSet = new HashSet(
+ universe.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()),
+ StringComparer.OrdinalIgnoreCase);
+ var selectedSet = new HashSet(
+ selected.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()),
+ StringComparer.OrdinalIgnoreCase);
+
+ if (selectedSet.Count != universeSet.Count)
{
return false;
}
- var set = new HashSet(universe, StringComparer.Ordinal);
- return selected.All(id => set.Contains(id));
+ return selectedSet.SetEquals(universeSet);
}
- /// 解析维度类型:显式 ALL、空数组 ALL、或 Id 列表全选时视为 ALL。
+ ///
+ /// 解析维度类型:显式 ALL、空数组 ALL、或 Id 列表全选时视为 ALL。
+ /// 入参显式 时保留 SPECIFIED 并落库全部 Id,不因「当前上下文全选」折叠为 ALL。
+ ///
public static string ResolveDimensionType(
string? declaredType,
IReadOnlyList ids,
bool hasArrayInPayload,
bool isFullSelection)
{
+ if (IsDeclaredSpecified(declaredType))
+ {
+ return ScopeSpecified;
+ }
+
if (isFullSelection || (ids.Count == 0 && IsDeclaredAll(declaredType)))
{
return ScopeAll;
@@ -61,6 +75,9 @@ public static class AllScopeBindingHelper
public static bool IsDeclaredAll(string? type) =>
string.Equals((type ?? ScopeAll).Trim(), ScopeAll, StringComparison.OrdinalIgnoreCase);
+ public static bool IsDeclaredSpecified(string? type) =>
+ string.Equals((type ?? string.Empty).Trim(), ScopeSpecified, StringComparison.OrdinalIgnoreCase);
+
/// 全部 Company(fl_partner.Id)。
public static async Task> ResolveAllPartnerIdsAsync(ISqlSugarClient db)
{
@@ -87,20 +104,51 @@ public static class AllScopeBindingHelper
return LocationScopeBindingHelper.NormalizeIds(rows);
}
- /// 全部门店;可按 Company / Region 限定。
+ ///
+ /// 全部门店;可按 Company / Region 限定。
+ /// 同时传 Company 与 Region 时取交集(该 Region 下且属于这些 Company 的门店),
+ /// 禁止并集——否则回显「区内全选」无法折叠为 locationIds:["ALL"]。
+ ///
public static async Task> ResolveAllLocationIdsAsync(
ISqlSugarClient db,
IReadOnlyList? partnerIds,
IReadOnlyList? regionIds)
{
+ var partners = LocationScopeBindingHelper.NormalizeIds(partnerIds);
+ var regions = LocationScopeBindingHelper.FilterConcreteScopeIds(regionIds);
+
+ if (regions.Count > 0)
+ {
+ var fromRegions = await LocationScopeBindingHelper.ResolveLocationIdsFromGroupIdsAsync(db, regions);
+ if (partners.Count == 0)
+ {
+ return LocationScopeBindingHelper.NormalizeIds(fromRegions);
+ }
+
+ var partnerLocSet = new HashSet(
+ await LocationScopeBindingHelper.ResolveLocationIdsFromPartnerIdsAsync(db, partners),
+ StringComparer.OrdinalIgnoreCase);
+ return LocationScopeBindingHelper.NormalizeIds(
+ fromRegions.Where(id => partnerLocSet.Contains(id)).ToList());
+ }
+
+ if (partners.Count > 0)
+ {
+ return await LocationScopeBindingHelper.ResolveLocationIdsFromPartnerIdsAsync(db, partners);
+ }
+
var merged = await LocationScopeBindingHelper.MergeToLocationIdsAsync(
db,
- partnerIds,
- regionIds,
+ (IReadOnlyList?)null,
+ null,
null);
return LocationScopeBindingHelper.NormalizeIds(merged);
}
+ /// Id 数组是否含 ALL 哨兵(大小写不敏感;与具体 Guid 同传时以 ALL 为准)。
+ public static bool HasAllScopeSentinelSelection(IReadOnlyList? ids) =>
+ LocationScopeBindingHelper.ContainsAllScopeSentinel(ids);
+
/// Company 维度是否应存为 ALL(含「勾选了全部 Company」)。
public static async Task<(string Type, List Ids)> NormalizePartnerScopeAsync(
ISqlSugarClient db,
@@ -110,6 +158,10 @@ public static class AllScopeBindingHelper
bool hasArrayInPayload)
{
var ids = LabelEntityPartnerScopeHelper.NormalizePartnerIds(partnerIds, companyIds);
+ if (HasAllScopeSentinelSelection(ids))
+ {
+ return (ScopeAll, new List());
+ }
var allPartners = await ResolveAllPartnerIdsAsync(db);
// 勾选当前全部 Company(含前端仍传 SPECIFIED + 全量 Id)→ 归档 ALL,后续新增 Company 动态适用
var isFull = ids.Count > 0 && IsFullIdSelection(ids, allPartners);
@@ -125,7 +177,12 @@ public static class AllScopeBindingHelper
bool hasArrayInPayload,
IReadOnlyList? partnerIdsForContext)
{
- var ids = LocationScopeBindingHelper.NormalizeIds(regionIds);
+ if (HasAllScopeSentinelSelection(regionIds))
+ {
+ return (ScopeAll, new List());
+ }
+
+ var ids = LocationScopeBindingHelper.FilterConcreteScopeIds(regionIds);
var allRegions = await ResolveAllRegionIdsAsync(db, partnerIdsForContext);
var isFull = ids.Count > 0 && IsFullIdSelection(ids, allRegions);
var type = ResolveDimensionType(declaredType, ids, hasArrayInPayload, isFull);
@@ -141,7 +198,12 @@ public static class AllScopeBindingHelper
IReadOnlyList? partnerIdsForContext,
IReadOnlyList? regionIdsForContext)
{
- var ids = LocationScopeBindingHelper.NormalizeIds(locationIds);
+ if (HasAllScopeSentinelSelection(locationIds))
+ {
+ return (ScopeAll, new List());
+ }
+
+ var ids = LocationScopeBindingHelper.FilterConcreteScopeIds(locationIds);
var allLocations = await ResolveAllLocationIdsAsync(db, partnerIdsForContext, regionIdsForContext);
var isFull = ids.Count > 0 && IsFullIdSelection(ids, allLocations);
var type = ResolveDimensionType(declaredType, ids, hasArrayInPayload, isFull);
@@ -162,7 +224,7 @@ public static class AllScopeBindingHelper
return new List { partnerId.Trim() };
}
- var regionIds = LocationScopeBindingHelper.NormalizeIds(regionOrGroupIds);
+ var regionIds = LocationScopeBindingHelper.FilterConcreteScopeIds(regionOrGroupIds);
if (regionIds.Count > 0)
{
var rows = await db.Queryable()
@@ -173,7 +235,7 @@ public static class AllScopeBindingHelper
return normalized.Count > 0 ? normalized : null;
}
- var locIds = LocationScopeBindingHelper.NormalizeIds(locationIds);
+ var locIds = LocationScopeBindingHelper.FilterConcreteScopeIds(locationIds);
if (locIds.Count == 0)
{
return null;
@@ -195,27 +257,55 @@ public static class AllScopeBindingHelper
bool hasScopeArrays,
IReadOnlyList? partnerIdsForContext)
{
+ var concreteLocations = LocationScopeBindingHelper.FilterConcreteScopeIds(locationIds);
+ var concreteRegions = LocationScopeBindingHelper.FilterConcreteScopeIds(regionIds);
+
+ // 具体 Region(非全局 ALL)下即使覆盖该区全部门店,也只存 SPECIFIED 快照,不升全局 AvailabilityType=ALL
+ if (concreteRegions.Count > 0)
+ {
+ return false;
+ }
+
+ // locationIds 含 ALL 且无具体 Region:由调用方按 Company 展开或归档全局 ALL
+ if (HasAllScopeSentinelSelection(locationIds))
+ {
+ return true;
+ }
+
+ if (HasAllScopeSentinelSelection(regionIds) && concreteLocations.Count == 0)
+ {
+ return true;
+ }
+
if (IsDeclaredAll(declaredAvailabilityType)
- && LocationScopeBindingHelper.NormalizeIds(regionIds).Count == 0
- && LocationScopeBindingHelper.NormalizeIds(locationIds).Count == 0)
+ && concreteRegions.Count == 0
+ && concreteLocations.Count == 0)
{
return true;
}
if (hasScopeArrays
- && LocationScopeBindingHelper.NormalizeIds(regionIds).Count == 0
- && LocationScopeBindingHelper.NormalizeIds(locationIds).Count == 0
+ && concreteRegions.Count == 0
+ && concreteLocations.Count == 0
&& IsDeclaredAll(declaredAvailabilityType))
{
return true;
}
- // 前端 Select All 常传 SPECIFIED + 当前全量 Id:覆盖 Company 上下文全部门店时归档 ALL
- var merged = await LocationScopeBindingHelper.MergeToLocationIdsAsync(
+ // 前端 Select All 常传 SPECIFIED + 当前全量 Id:覆盖上下文全部门店时归档 ALL。
+ // 有具体 locationIds 时:只按这些门店落 SPECIFIED 快照,禁止再升成 AvailabilityType=ALL
+ // (否则「Region=ALL + 选 1 店」在公司仅 1 店或误判全选时会清空快照并回显成全局 ALL)。
+ if (concreteLocations.Count > 0)
+ {
+ return false;
+ }
+
+ List merged = await LocationScopeBindingHelper.MergeToLocationIdsAsync(
db,
(IReadOnlyList?)null,
- regionIds,
- locationIds);
+ concreteRegions,
+ concreteLocations);
+
if (merged.Count == 0)
{
return false;
@@ -224,14 +314,14 @@ public static class AllScopeBindingHelper
var partnerContext = LocationScopeBindingHelper.NormalizeIds(partnerIdsForContext);
if (partnerContext.Count == 0)
{
- partnerContext = await ResolvePartnerContextFromScopeAsync(db, null, regionIds, locationIds)
+ partnerContext = await ResolvePartnerContextFromScopeAsync(db, null, concreteRegions, concreteLocations)
?? new List();
}
var allLocations = await ResolveAllLocationIdsAsync(
db,
partnerContext.Count > 0 ? partnerContext : null,
- null);
+ concreteRegions.Count > 0 ? concreteRegions : null);
return IsFullIdSelection(merged, allLocations);
}
@@ -244,10 +334,35 @@ public static class AllScopeBindingHelper
IReadOnlyList? locationIds,
bool hasScopePayload)
{
+ // partnerId 字符串为 ALL 哨兵:全选 Company,归档 ALL(禁止把 ALL 当 Guid 查库)
+ if (LocationScopeBindingHelper.IsAllScopeSentinel(partnerId))
+ {
+ return true;
+ }
+
+ var concreteLocations = LocationScopeBindingHelper.FilterConcreteScopeIds(locationIds);
+ var concreteGroups = LocationScopeBindingHelper.FilterConcreteScopeIds(groupIds);
+
+ // 具体 Region 下不升全局 AvailabilityType=ALL(与 ShouldTreatMergedLocationScopeAsAllAsync 一致)
+ if (concreteGroups.Count > 0)
+ {
+ return false;
+ }
+
+ if (HasAllScopeSentinelSelection(locationIds))
+ {
+ return true;
+ }
+
+ if (HasAllScopeSentinelSelection(groupIds) && concreteLocations.Count == 0)
+ {
+ return true;
+ }
+
if (IsDeclaredAll(declaredAvailabilityType)
&& string.IsNullOrWhiteSpace(partnerId)
- && LocationScopeBindingHelper.NormalizeIds(groupIds).Count == 0
- && LocationScopeBindingHelper.NormalizeIds(locationIds).Count == 0)
+ && concreteGroups.Count == 0
+ && concreteLocations.Count == 0)
{
return true;
}
@@ -257,20 +372,29 @@ public static class AllScopeBindingHelper
return false;
}
+ // 有具体门店时不升全局 ALL
+ if (concreteLocations.Count > 0)
+ {
+ return false;
+ }
+
var merged = await LocationScopeBindingHelper.MergeToLocationIdsAsync(
db,
partnerId,
- groupIds,
- locationIds);
+ concreteGroups,
+ concreteLocations);
+
if (merged.Count == 0)
{
return false;
}
- // 未传 partnerId 时从 Region/门店反推 Company,避免用「全系统门店」误判导致无法归档 ALL
var partnerContext = await ResolvePartnerContextFromScopeAsync(
- db, partnerId, groupIds, locationIds);
- var allLocations = await ResolveAllLocationIdsAsync(db, partnerContext, null);
+ db, partnerId, concreteGroups, concreteLocations);
+ var allLocations = await ResolveAllLocationIdsAsync(
+ db,
+ partnerContext,
+ concreteGroups.Count > 0 ? concreteGroups : null);
return IsFullIdSelection(merged, allLocations);
}
@@ -302,4 +426,169 @@ public static class AllScopeBindingHelper
: await LocationScopeBindingHelper.ResolvePartnerIdsFromLocationIdsAsync(db, locs);
return (partnerIds, regionIds, locs);
}
+
+ ///
+ /// 标签/产品分类等:解析 Region + Location 落库(对齐 Label:Region=ALL 时可保留具体门店快照)。
+ ///
+ public sealed class LabelEntityRegionLocationSaveResult
+ {
+ public string AvailabilityType { get; init; } = ScopeSpecified;
+
+ public string AppliedRegionType { get; init; } = ScopeAll;
+
+ public List LocationIds { get; init; } = new();
+ }
+
+ public static async Task ResolveLabelEntityRegionLocationForSaveAsync(
+ ISqlSugarClient db,
+ string? declaredAvailabilityType,
+ IReadOnlyList? partnerIdsForContext,
+ IReadOnlyList? mergedRegionIdsRaw,
+ IReadOnlyList? locationIdsRaw,
+ bool hasScopeArrays)
+ {
+ var normalizedLocationIds = LocationScopeBindingHelper.NormalizeIds(locationIdsRaw);
+ var mergedRegionIds = LocationScopeBindingHelper.NormalizeIds(mergedRegionIdsRaw);
+ var regionIds = LocationScopeBindingHelper.FilterConcreteScopeIds(mergedRegionIds);
+ var explicitLocationIds = LocationScopeBindingHelper.FilterConcreteScopeIds(normalizedLocationIds);
+ var locationHasAll = HasAllScopeSentinelSelection(normalizedLocationIds);
+ var regionHasAllSentinel = HasAllScopeSentinelSelection(mergedRegionIds);
+ var hasConcretePartner = LocationScopeBindingHelper.NormalizeIds(partnerIdsForContext).Count > 0;
+ // 用户显式传具体门店 Id 时一律 SPECIFIED,不因「恰好覆盖 Region 全集」归档 ALL
+ var userExplicitLocations = explicitLocationIds.Count > 0 && !locationHasAll;
+
+ // 前端 Select All Region 常展开为全量 Guid,需识别为 Region=ALL
+ var regionHasAll = regionHasAllSentinel;
+ if (!regionHasAll && regionIds.Count > 0)
+ {
+ var allRegions = await ResolveAllRegionIdsAsync(
+ db,
+ hasConcretePartner ? partnerIdsForContext : null);
+ regionHasAll = allRegions.Count > 0 && IsFullIdSelection(regionIds, allRegions);
+ }
+
+ // location ALL 哨兵,或具体 Region 下「空选/全选门店」:有 Company/Region 上下文则展开 SPECIFIED
+ var locationCoversConcreteRegions = false;
+ if (!locationHasAll && !userExplicitLocations && regionIds.Count > 0 && !regionHasAll)
+ {
+ if (explicitLocationIds.Count == 0)
+ {
+ locationCoversConcreteRegions = true;
+ }
+ else
+ {
+ var regionUniverse = await ResolveAllLocationIdsAsync(
+ db,
+ hasConcretePartner ? partnerIdsForContext : null,
+ regionIds);
+ locationCoversConcreteRegions = regionUniverse.Count > 0
+ && IsFullIdSelection(explicitLocationIds, regionUniverse);
+ }
+ }
+
+ if (!userExplicitLocations && (locationHasAll || locationCoversConcreteRegions))
+ {
+ var expandRegions = regionHasAll ? null : (regionIds.Count > 0 ? regionIds : null);
+ var expanded = await LocationScopeBindingHelper.ExpandScopedAllLocationsForSaveAsync(
+ db,
+ hasConcretePartner ? partnerIdsForContext : null,
+ expandRegions);
+ if (expanded is not null)
+ {
+ return new LabelEntityRegionLocationSaveResult
+ {
+ AvailabilityType = ScopeSpecified,
+ AppliedRegionType = regionHasAll || regionIds.Count == 0 ? ScopeAll : ScopeSpecified,
+ LocationIds = expanded
+ };
+ }
+
+ return new LabelEntityRegionLocationSaveResult
+ {
+ AvailabilityType = ScopeAll,
+ AppliedRegionType = ScopeAll,
+ LocationIds = new List()
+ };
+ }
+
+ // Region=ALL + 具体门店:Region 存 ALL,门店 SPECIFIED 快照(可回显 regionIds=["ALL"] + 单个 locationId)
+ if (regionHasAll && explicitLocationIds.Count > 0)
+ {
+ await LocationScopeBindingHelper.ValidateLocationIdsExistAsync(db, explicitLocationIds);
+ return new LabelEntityRegionLocationSaveResult
+ {
+ AvailabilityType = ScopeSpecified,
+ AppliedRegionType = ScopeAll,
+ LocationIds = explicitLocationIds
+ };
+ }
+
+ // Region=ALL 且无具体门店 → 双 ALL
+ if (regionHasAll)
+ {
+ return new LabelEntityRegionLocationSaveResult
+ {
+ AvailabilityType = ScopeAll,
+ AppliedRegionType = ScopeAll,
+ LocationIds = new List()
+ };
+ }
+
+ // 有具体门店(可同传具体 Region):以门店为准
+ if (explicitLocationIds.Count > 0)
+ {
+ await LocationScopeBindingHelper.ValidateLocationIdsExistAsync(db, explicitLocationIds);
+ return new LabelEntityRegionLocationSaveResult
+ {
+ AvailabilityType = ScopeSpecified,
+ AppliedRegionType = ScopeSpecified,
+ LocationIds = explicitLocationIds
+ };
+ }
+
+ if (await ShouldTreatMergedLocationScopeAsAllAsync(
+ db,
+ declaredAvailabilityType,
+ regionIds,
+ explicitLocationIds,
+ hasScopeArrays,
+ partnerIdsForContext))
+ {
+ return new LabelEntityRegionLocationSaveResult
+ {
+ AvailabilityType = ScopeAll,
+ AppliedRegionType = ScopeAll,
+ LocationIds = new List()
+ };
+ }
+
+ var availabilityType = (declaredAvailabilityType ?? ScopeAll).Trim().ToUpperInvariant();
+ if (regionIds.Count > 0)
+ {
+ availabilityType = ScopeSpecified;
+ }
+ else if (hasScopeArrays && IsDeclaredAll(availabilityType))
+ {
+ availabilityType = ScopeAll;
+ }
+
+ if (availabilityType != ScopeAll && availabilityType != ScopeSpecified)
+ {
+ throw new UserFriendlyException("门店可用范围不合法(ALL/SPECIFIED)");
+ }
+
+ var locationSpecified = string.Equals(availabilityType, ScopeSpecified, StringComparison.OrdinalIgnoreCase);
+ var savedLocationIds = await LocationScopeBindingHelper.ResolveEntityLocationIdsForSaveAsync(
+ db,
+ locationSpecified,
+ regionIds,
+ explicitLocationIds);
+
+ return new LabelEntityRegionLocationSaveResult
+ {
+ AvailabilityType = locationSpecified ? ScopeSpecified : ScopeAll,
+ AppliedRegionType = locationSpecified ? ScopeSpecified : ScopeAll,
+ LocationIds = savedLocationIds
+ };
+ }
}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/EntityLocationScopeDisplayHelper.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/EntityLocationScopeDisplayHelper.cs
index c53fee6..92fb678 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/EntityLocationScopeDisplayHelper.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/EntityLocationScopeDisplayHelper.cs
@@ -11,6 +11,10 @@ public static class EntityLocationScopeDisplayHelper
///
/// 根据可用范围类型与已解析的 Region/Location Id、名称,生成列表展示文案。
///
+ ///
+ /// 有具体门店 Guid 时始终展示门店名称,不因「覆盖某 Region 下全部门店」误显示 All Location
+ /// (常见于 AppliedRegionType=ALL + 单个 locationIds)。
+ ///
public static async Task<(string Region, string Location)> BuildListDisplayAsync(
ISqlSugarClient db,
string? availabilityType,
@@ -18,7 +22,8 @@ public static class EntityLocationScopeDisplayHelper
IReadOnlyList locationIds,
IEnumerable regionNames,
IEnumerable locationNames,
- IReadOnlyList? partnerIdsForContext)
+ IReadOnlyList? partnerIdsForContext,
+ string? appliedRegionType = null)
{
if (AllScopeBindingHelper.IsDeclaredAll(availabilityType))
{
@@ -29,27 +34,33 @@ public static class EntityLocationScopeDisplayHelper
var normLocationIds = LocationScopeBindingHelper.NormalizeIds(locationIds);
var partnerContext = partnerIdsForContext is { Count: > 0 } ? partnerIdsForContext : null;
- var allRegionIds = await AllScopeBindingHelper.ResolveAllRegionIdsAsync(db, partnerContext);
- var regionIsAll = normRegionIds.Count > 0
- && allRegionIds.Count > 0
- && AllScopeBindingHelper.IsFullIdSelection(normRegionIds, allRegionIds);
-
- var regionContextForLocations = regionIsAll ? null : normRegionIds.Count > 0 ? normRegionIds : null;
- var allLocationIds = await AllScopeBindingHelper.ResolveAllLocationIdsAsync(
- db,
- partnerContext,
- regionContextForLocations);
- var locationIsAll = normLocationIds.Count > 0
- && allLocationIds.Count > 0
- && AllScopeBindingHelper.IsFullIdSelection(normLocationIds, allLocationIds);
-
- var regionDisplay = regionIsAll
- ? AllScopeBindingHelper.AllRegionsDisplay
- : JoinDistinctNames(regionNames);
+ string regionDisplay;
+ if (AllScopeBindingHelper.IsDeclaredAll(appliedRegionType)
+ || AllScopeBindingHelper.HasAllScopeSentinelSelection(normRegionIds))
+ {
+ regionDisplay = AllScopeBindingHelper.AllRegionsDisplay;
+ }
+ else
+ {
+ var allRegionIds = await AllScopeBindingHelper.ResolveAllRegionIdsAsync(db, partnerContext);
+ var regionIsAll = normRegionIds.Count > 0
+ && allRegionIds.Count > 0
+ && AllScopeBindingHelper.IsFullIdSelection(normRegionIds, allRegionIds);
+ regionDisplay = regionIsAll
+ ? AllScopeBindingHelper.AllRegionsDisplay
+ : JoinDistinctNames(regionNames);
+ }
- var locationDisplay = locationIsAll
- ? AllScopeBindingHelper.AllLocationsDisplay
- : JoinDistinctNames(locationNames);
+ // 具体门店 Guid → 显示门店名;仅 AvailabilityType=ALL 或 locationIds 含 ALL 哨兵才显示 All Location
+ string locationDisplay;
+ if (AllScopeBindingHelper.HasAllScopeSentinelSelection(normLocationIds))
+ {
+ locationDisplay = AllScopeBindingHelper.AllLocationsDisplay;
+ }
+ else
+ {
+ locationDisplay = JoinDistinctNames(locationNames);
+ }
return (regionDisplay, locationDisplay);
}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelAlertTimerWriteHelper.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelAlertTimerWriteHelper.cs
new file mode 100644
index 0000000..2c945b6
--- /dev/null
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelAlertTimerWriteHelper.cs
@@ -0,0 +1,148 @@
+using System.Globalization;
+using FoodLabeling.Application.Services.DbModels;
+using FoodLabeling.Domain.Shared.Helpers;
+using SqlSugar;
+
+namespace FoodLabeling.Application.Helpers;
+
+///
+/// 打印成功后写入告警计时器(按 BatchId 幂等,一条批次一条计时器)。
+///
+public static class LabelAlertTimerWriteHelper
+{
+ ///
+ /// 根据打印批次创建告警计时器;已存在未删除记录或无有效过期时刻时直接返回。
+ ///
+ public static async Task TryCreateFromPrintBatchAsync(
+ ISqlSugarClient db,
+ string batchId,
+ string? createdBy,
+ CancellationToken ct = default)
+ {
+ var bid = batchId?.Trim();
+ if (string.IsNullOrWhiteSpace(bid))
+ {
+ return;
+ }
+
+ ct.ThrowIfCancellationRequested();
+
+ // BatchId 有唯一索引:含软删记录也不再插入,避免幂等补写撞唯一约束
+ var exists = await db.Queryable()
+ .AnyAsync(x => x.BatchId == bid);
+ if (exists)
+ {
+ return;
+ }
+
+ var task = (await db.Queryable()
+ .Where(x => x.BatchId == bid)
+ .OrderBy(x => x.CopyIndex)
+ .Take(1)
+ .ToListAsync(ct))
+ .FirstOrDefault();
+ if (task is null)
+ {
+ return;
+ }
+
+ var printedAt = task.PrintedAt ?? task.BaseTime ?? task.CreationTime;
+ if (!ReportsPrintLogExpiryHelper.TryResolveExpiryDateTime(
+ task.PrintInputJson,
+ task.RenderTemplateJson,
+ task.BaseTime,
+ printedAt,
+ out var expiresAt))
+ {
+ return;
+ }
+
+ var label = (await db.Queryable()
+ .Where(x => x.Id == task.LabelId)
+ .Select(x => new { x.LabelName, x.LabelCode })
+ .Take(1)
+ .ToListAsync(ct))
+ .FirstOrDefault();
+
+ var labelName = label?.LabelName?.Trim();
+ if (string.IsNullOrWhiteSpace(labelName))
+ {
+ labelName = FoodLabelingDisplayConsts.NotAvailable;
+ }
+
+ string? productName = null;
+ if (!string.IsNullOrWhiteSpace(task.ProductId))
+ {
+ productName = (await db.Queryable()
+ .Where(x => x.Id == task.ProductId && !x.IsDeleted)
+ .Select(x => x.ProductName)
+ .Take(1)
+ .ToListAsync(ct))
+ .FirstOrDefault();
+ }
+
+ var durationSeconds = Math.Max(0, (int)(expiresAt - printedAt).TotalSeconds);
+ var title = BuildTitle(labelName, durationSeconds);
+ var subtitle = BuildSubtitle(durationSeconds, expiresAt);
+ var now = DateTime.Now;
+
+ var entity = new FlLabelAlertTimerDbEntity
+ {
+ Id = YitIdHelper.NextId().ToString(),
+ BatchId = bid,
+ PrintTaskId = task.Id,
+ LabelId = task.LabelId,
+ LabelCode = label?.LabelCode?.Trim(),
+ LabelName = labelName,
+ ProductId = task.ProductId,
+ ProductName = string.IsNullOrWhiteSpace(productName) ? null : productName.Trim(),
+ LocationId = task.LocationId?.Trim() ?? string.Empty,
+ PrintedAt = printedAt,
+ BaseTime = task.BaseTime,
+ ExpiresAt = expiresAt,
+ DurationSeconds = durationSeconds,
+ Title = title,
+ Subtitle = subtitle,
+ IsDeleted = false,
+ DeletionTime = null,
+ CreatedBy = createdBy,
+ CreationTime = now
+ };
+
+ await db.Insertable(entity).ExecuteCommandAsync();
+ }
+
+ private static string BuildTitle(string labelName, int durationSeconds)
+ {
+ var hoursText = FormatDurationHoursLabel(durationSeconds);
+ return string.IsNullOrWhiteSpace(hoursText) ? labelName : $"{labelName} ({hoursText})";
+ }
+
+ private static string BuildSubtitle(int durationSeconds, DateTime expiresAt)
+ {
+ var hoursText = FormatDurationHoursLabel(durationSeconds);
+ var timeText = expiresAt.ToString("h:mm tt", CultureInfo.CurrentCulture);
+ return string.IsNullOrWhiteSpace(hoursText)
+ ? $"Completes at {timeText}"
+ : $"{hoursText} Completes at {timeText}";
+ }
+
+ private static string FormatDurationHoursLabel(int durationSeconds)
+ {
+ if (durationSeconds <= 0)
+ {
+ return string.Empty;
+ }
+
+ var totalHours = durationSeconds / 3600.0;
+ if (totalHours >= 1)
+ {
+ var rounded = (int)Math.Round(totalHours, MidpointRounding.AwayFromZero);
+ rounded = Math.Max(1, rounded);
+ return rounded == 1 ? "1 hour" : $"{rounded} hours";
+ }
+
+ var minutes = Math.Max(1, (int)Math.Ceiling(durationSeconds / 60.0));
+ return minutes == 1 ? "1 minute" : $"{minutes} minutes";
+ }
+}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelEntityListScopeHelper.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelEntityListScopeHelper.cs
index d8c8296..5b75c4a 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelEntityListScopeHelper.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelEntityListScopeHelper.cs
@@ -4,12 +4,37 @@ using SqlSugar;
namespace FoodLabeling.Application.Helpers;
///
-/// 标签类型/分类/多选项列表:非平台管理员仅可见其绑定 Company 范围内数据。
+/// 标签类型/分类/多选项列表:按可见门店范围过滤。
+/// AvailabilityType=ALL 时传任意 Region/Location 筛选均命中。
///
public static class LabelEntityListScopeHelper
{
///
- /// 标签类型列表:按 Company + Region/Location 范围过滤(Company Admin 不可见 All Companies 数据)。
+ /// 是否应用门店 Availability 过滤。
+ /// 仅传 partnerId(未传 groupId/locationId)时返回 false:只按 Company 维度筛,
+ /// 避免「多公司绑定」在第二家公司暂无门店或门店未落入关联表时被误杀。
+ ///
+ public static bool ShouldApplyLocationAvailabilityFilter(
+ string? partnerId,
+ string? groupId,
+ string? locationId)
+ {
+ if (!string.IsNullOrWhiteSpace(groupId) || !string.IsNullOrWhiteSpace(locationId))
+ {
+ return true;
+ }
+
+ // 仅 PartnerId:Company 过滤由 Apply*PartnerListFilter 负责
+ if (!string.IsNullOrWhiteSpace(partnerId))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// 标签类型列表:门店可用范围过滤(ALL 命中任意门店筛选)。
///
public static ISugarQueryable ApplyTypeLocationAvailabilityFilter(
ISugarQueryable query,
@@ -22,12 +47,12 @@ public static class LabelEntityListScopeHelper
if (scopedLocationIds.Count == 0)
{
- return query.Where(_ => false);
+ // 无可见门店时仍保留 AvailabilityType=ALL(仅 Company 筛选时公司可能暂无门店)
+ return query.Where(t => t.AvailabilityType == AllScopeBindingHelper.ScopeAll);
}
return query.Where(t =>
- (t.AvailabilityType == AllScopeBindingHelper.ScopeAll
- && t.AppliedPartnerType == LabelEntityPartnerScopeHelper.ScopeSpecified)
+ t.AvailabilityType == AllScopeBindingHelper.ScopeAll
|| (t.AvailabilityType == AllScopeBindingHelper.ScopeSpecified
&& SqlFunc.Subqueryable()
.Where(tl => tl.LabelTypeId == t.Id && scopedLocationIds.Contains(tl.LocationId))
@@ -35,7 +60,7 @@ public static class LabelEntityListScopeHelper
}
///
- /// 标签分类列表:按 Company + Region/Location 范围过滤。
+ /// 标签分类列表:门店可用范围过滤(ALL 命中任意门店筛选)。
///
public static ISugarQueryable ApplyCategoryLocationAvailabilityFilter(
ISugarQueryable query,
@@ -48,12 +73,11 @@ public static class LabelEntityListScopeHelper
if (scopedLocationIds.Count == 0)
{
- return query.Where(_ => false);
+ return query.Where(c => c.AvailabilityType == AllScopeBindingHelper.ScopeAll);
}
return query.Where(c =>
- (c.AvailabilityType == AllScopeBindingHelper.ScopeAll
- && c.AppliedPartnerType == LabelEntityPartnerScopeHelper.ScopeSpecified)
+ c.AvailabilityType == AllScopeBindingHelper.ScopeAll
|| (c.AvailabilityType == AllScopeBindingHelper.ScopeSpecified
&& SqlFunc.Subqueryable()
.Where(cl => cl.CategoryId == c.Id && scopedLocationIds.Contains(cl.LocationId))
@@ -61,7 +85,7 @@ public static class LabelEntityListScopeHelper
}
///
- /// 标签多选项列表:按 Company + Region/Location 范围过滤。
+ /// 标签多选项列表:门店可用范围过滤(ALL 命中任意门店筛选)。
///
public static ISugarQueryable ApplyMultipleOptionLocationAvailabilityFilter(
ISugarQueryable query,
@@ -74,12 +98,11 @@ public static class LabelEntityListScopeHelper
if (scopedLocationIds.Count == 0)
{
- return query.Where(_ => false);
+ return query.Where(o => o.AvailabilityType == AllScopeBindingHelper.ScopeAll);
}
return query.Where(o =>
- (o.AvailabilityType == AllScopeBindingHelper.ScopeAll
- && o.AppliedPartnerType == LabelEntityPartnerScopeHelper.ScopeSpecified)
+ o.AvailabilityType == AllScopeBindingHelper.ScopeAll
|| (o.AvailabilityType == AllScopeBindingHelper.ScopeSpecified
&& SqlFunc.Subqueryable()
.Where(ol => ol.MultipleOptionId == o.Id && scopedLocationIds.Contains(ol.LocationId))
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelEntityPartnerScopeHelper.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelEntityPartnerScopeHelper.cs
index 6498f4a..6483617 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelEntityPartnerScopeHelper.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelEntityPartnerScopeHelper.cs
@@ -26,7 +26,10 @@ public static class LabelEntityPartnerScopeHelper
Category,
/// 标签多选项 fl_label_multiple_option
- MultipleOption
+ MultipleOption,
+
+ /// 产品分类 fl_product_category
+ ProductCategory
}
public sealed class LabelEntityPartnerScopeSaveResult
@@ -60,6 +63,10 @@ public static class LabelEntityPartnerScopeHelper
public bool HasCategoryPartnerTable { get; init; }
public bool HasMultipleOptionPartnerTable { get; init; }
+
+ public bool HasProductCategoryPartnerColumn { get; init; }
+
+ public bool HasProductCategoryPartnerTable { get; init; }
}
///
@@ -185,7 +192,7 @@ public static class LabelEntityPartnerScopeHelper
if (needsPartnerRows && !hasTable)
{
throw new UserFriendlyException(
- "Company 适用范围关联表尚未就绪,请联系管理员执行数据库迁移(fl_label_entity_partner_scope.sql)后重试");
+ "Company 适用范围关联表尚未就绪,请联系管理员执行数据库迁移(fl_label_entity_partner_scope.sql 或 fl_product_category_partner_scope.sql)后重试");
}
if (hasTable)
@@ -325,11 +332,30 @@ public static class LabelEntityPartnerScopeHelper
? AllCompaniesDisplay
: BuildCompanyDisplay(pIds, partnerNameById);
+ List displayPartnerIds;
+ if (string.Equals(partnerType, ScopeAll, StringComparison.OrdinalIgnoreCase))
+ {
+ displayPartnerIds = new List { AllScopeBindingHelper.ScopeAll };
+ }
+ else
+ {
+ var collapsed = await ScopeAllEchoHelper.CollapseScopeIdsToAllSentinelAsync(
+ db,
+ pIds,
+ null,
+ null,
+ new ScopeAllEchoHelper.ScopeAllEchoOptions
+ {
+ AppliedPartnerType = partnerType
+ });
+ displayPartnerIds = collapsed.PartnerIds;
+ }
+
result[entityId] = new LabelEntityPartnerScopeDisplay
{
Company = companyDisplay,
AppliedPartnerType = partnerType,
- PartnerIds = pIds
+ PartnerIds = displayPartnerIds
};
}
@@ -383,11 +409,13 @@ public static class LabelEntityPartnerScopeHelper
return query.Where(_ => false);
}
+ // Company=ALL:传任意 partnerId 均命中;SPECIFIED:须与关联表有交集
return query.Where(t =>
- t.AppliedPartnerType == ScopeSpecified
- && SqlFunc.Subqueryable()
- .Where(p => p.LabelTypeId == t.Id && scopedPartnerIds.Contains(p.PartnerId))
- .Any());
+ t.AppliedPartnerType == ScopeAll
+ || (t.AppliedPartnerType == ScopeSpecified
+ && SqlFunc.Subqueryable()
+ .Where(p => p.LabelTypeId == t.Id && scopedPartnerIds.Contains(p.PartnerId))
+ .Any()));
}
///
@@ -415,10 +443,11 @@ public static class LabelEntityPartnerScopeHelper
}
return query.Where(c =>
- c.AppliedPartnerType == ScopeSpecified
- && SqlFunc.Subqueryable()
- .Where(p => p.CategoryId == c.Id && scopedPartnerIds.Contains(p.PartnerId))
- .Any());
+ c.AppliedPartnerType == ScopeAll
+ || (c.AppliedPartnerType == ScopeSpecified
+ && SqlFunc.Subqueryable()
+ .Where(p => p.CategoryId == c.Id && scopedPartnerIds.Contains(p.PartnerId))
+ .Any()));
}
///
@@ -446,14 +475,48 @@ public static class LabelEntityPartnerScopeHelper
}
return query.Where(o =>
- o.AppliedPartnerType == ScopeSpecified
- && SqlFunc.Subqueryable()
- .Where(p => p.MultipleOptionId == o.Id && scopedPartnerIds.Contains(p.PartnerId))
- .Any());
+ o.AppliedPartnerType == ScopeAll
+ || (o.AppliedPartnerType == ScopeSpecified
+ && SqlFunc.Subqueryable()
+ .Where(p => p.MultipleOptionId == o.Id && scopedPartnerIds.Contains(p.PartnerId))
+ .Any()));
+ }
+
+ ///
+ /// 列表筛选:按可见 Company 过滤产品分类。
+ ///
+ public static async Task> ApplyProductCategoryPartnerListFilterAsync(
+ ISqlSugarClient db,
+ ISugarQueryable query,
+ IReadOnlyList? scopedPartnerIds)
+ {
+ if (scopedPartnerIds is null)
+ {
+ return query;
+ }
+
+ var schema = await GetSchemaStatusAsync(db);
+ if (!HasPartnerTableForKind(schema, LabelEntityPartnerKind.ProductCategory))
+ {
+ return query;
+ }
+
+ if (scopedPartnerIds.Count == 0)
+ {
+ return query.Where(_ => false);
+ }
+
+ return query.Where(c =>
+ c.AppliedPartnerType == ScopeAll
+ || (c.AppliedPartnerType == ScopeSpecified
+ && SqlFunc.Subqueryable()
+ .Where(p => p.CategoryId == c.Id && scopedPartnerIds.Contains(p.PartnerId))
+ .Any()));
}
///
/// 由 Query partnerId 解析列表 Company 筛选 Id;未传则返回 null。
+ /// Id 统一为小写 Guid,避免与关联表大小写不一致导致 IN 匹配失败。
///
public static async Task?> ResolveScopedPartnerIdsForListAsync(
ISqlSugarClient db,
@@ -467,7 +530,13 @@ public static class LabelEntityPartnerScopeHelper
var exists = await db.Queryable()
.AnyAsync(x => !x.IsDeleted && x.Id == pid);
- return exists ? new List { pid } : new List();
+ if (!exists)
+ {
+ return new List();
+ }
+
+ var key = TeamMemberListScopeHelper.NormalizeScopeKey(pid);
+ return string.IsNullOrEmpty(key) ? new List { pid } : new List { key };
}
///
@@ -514,6 +583,11 @@ public static class LabelEntityPartnerScopeHelper
.Where(x => x.MultipleOptionId == entityId)
.ExecuteCommandAsync();
break;
+ case LabelEntityPartnerKind.ProductCategory:
+ await db.Deleteable()
+ .Where(x => x.CategoryId == entityId)
+ .ExecuteCommandAsync();
+ break;
}
}
@@ -564,7 +638,10 @@ public static class LabelEntityPartnerScopeHelper
db, "fl_label_multiple_option", "AppliedPartnerType"),
HasTypePartnerTable = await TableExistsAsync(db, "fl_label_type_partner"),
HasCategoryPartnerTable = await TableExistsAsync(db, "fl_label_category_partner"),
- HasMultipleOptionPartnerTable = await TableExistsAsync(db, "fl_label_multiple_option_partner")
+ HasMultipleOptionPartnerTable = await TableExistsAsync(db, "fl_label_multiple_option_partner"),
+ HasProductCategoryPartnerColumn = await ColumnExistsAsync(
+ db, "fl_product_category", "AppliedPartnerType"),
+ HasProductCategoryPartnerTable = await TableExistsAsync(db, "fl_product_category_partner")
};
}
catch
@@ -581,6 +658,7 @@ public static class LabelEntityPartnerScopeHelper
LabelEntityPartnerKind.Type => schema.HasTypePartnerTable,
LabelEntityPartnerKind.Category => schema.HasCategoryPartnerTable,
LabelEntityPartnerKind.MultipleOption => schema.HasMultipleOptionPartnerTable,
+ LabelEntityPartnerKind.ProductCategory => schema.HasProductCategoryPartnerTable,
_ => false
};
@@ -590,6 +668,7 @@ public static class LabelEntityPartnerScopeHelper
LabelEntityPartnerKind.Type => schema.HasTypePartnerColumn,
LabelEntityPartnerKind.Category => schema.HasCategoryPartnerColumn,
LabelEntityPartnerKind.MultipleOption => schema.HasMultipleOptionPartnerColumn,
+ LabelEntityPartnerKind.ProductCategory => schema.HasProductCategoryPartnerColumn,
_ => false
};
@@ -637,6 +716,7 @@ public static class LabelEntityPartnerScopeHelper
LabelEntityPartnerKind.Type => "fl_label_type",
LabelEntityPartnerKind.Category => "fl_label_category",
LabelEntityPartnerKind.MultipleOption => "fl_label_multiple_option",
+ LabelEntityPartnerKind.ProductCategory => "fl_product_category",
_ => string.Empty
};
@@ -666,6 +746,7 @@ public static class LabelEntityPartnerScopeHelper
LabelEntityPartnerKind.Type => "fl_label_type",
LabelEntityPartnerKind.Category => "fl_label_category",
LabelEntityPartnerKind.MultipleOption => "fl_label_multiple_option",
+ LabelEntityPartnerKind.ProductCategory => "fl_product_category",
_ => string.Empty
};
@@ -709,6 +790,13 @@ public static class LabelEntityPartnerScopeHelper
.ToListAsync();
return rows.Select(x => (x.MultipleOptionId, x.PartnerId)).ToList();
}
+ case LabelEntityPartnerKind.ProductCategory:
+ {
+ var rows = await db.Queryable()
+ .Where(x => entityIds.Contains(x.CategoryId))
+ .ToListAsync();
+ return rows.Select(x => (x.CategoryId, x.PartnerId)).ToList();
+ }
default:
return new List<(string EntityId, string PartnerId)>();
}
@@ -764,6 +852,19 @@ public static class LabelEntityPartnerScopeHelper
await db.Insertable(rows).ExecuteCommandAsync();
break;
}
+ case LabelEntityPartnerKind.ProductCategory:
+ {
+ var rows = partnerIds.Select(pid => new FlProductCategoryPartnerDbEntity
+ {
+ Id = guidGenerator.Create().ToString(),
+ CategoryId = entityId,
+ PartnerId = pid,
+ CreationTime = now,
+ CreatorId = currentUserId
+ }).ToList();
+ await db.Insertable(rows).ExecuteCommandAsync();
+ break;
+ }
}
}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelQueryHelper.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelQueryHelper.cs
index d7f7ce9..b3bc4f3 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelQueryHelper.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelQueryHelper.cs
@@ -28,6 +28,7 @@ public static class LabelQueryHelper
LabelName = x.LabelName,
TemplateId = x.TemplateId,
LocationId = x.LocationId,
+ PartnerId = x.PartnerId,
LabelCategoryId = x.LabelCategoryId,
LabelTypeId = x.LabelTypeId,
LabelType = x.LabelType,
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelRegionScopeHelper.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelRegionScopeHelper.cs
index 09fd63b..d38cc65 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelRegionScopeHelper.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelRegionScopeHelper.cs
@@ -58,12 +58,40 @@ public static class LabelRegionScopeHelper
IReadOnlyList? regionIds,
IReadOnlyList? groupIds,
string? locationId,
- IReadOnlyList? locationIds)
+ IReadOnlyList? locationIds,
+ IReadOnlyList? partnerIdsForContext = null)
{
- var mergedRegionIds = NormalizeRegionIds(regionIds, groupIds);
- var explicitLocationIds = MergeExplicitLocationIds(locationId, locationIds);
+ var mergedRegionIdsRaw = NormalizeRegionIds(regionIds, groupIds);
+ var explicitLocationIdsRaw = MergeExplicitLocationIds(locationId, locationIds);
+ var regionHasAll = LocationScopeBindingHelper.ContainsAllScopeSentinel(mergedRegionIdsRaw);
+ var locationHasAll = LocationScopeBindingHelper.ContainsAllScopeSentinel(explicitLocationIdsRaw);
+ var mergedRegionIds = LocationScopeBindingHelper.FilterConcreteScopeIds(mergedRegionIdsRaw);
+ var explicitLocationIds = LocationScopeBindingHelper.FilterConcreteScopeIds(explicitLocationIdsRaw);
var type = (appliedRegionType ?? AppliedRegionAll).Trim().ToUpperInvariant();
var hasScopeArrays = regionIds is not null || groupIds is not null || locationIds is not null;
+ var partnerContext = LocationScopeBindingHelper.NormalizeIds(partnerIdsForContext);
+
+ // locationIds / regionIds 含 ALL 哨兵:不归档为 Guid 校验,按全选处理(Company 由 fl_label.PartnerId 落库)
+ if (locationHasAll || (regionHasAll && explicitLocationIds.Count == 0))
+ {
+ return new LabelRegionScopeSaveResult
+ {
+ AppliedRegionType = AppliedRegionAll,
+ RegionIds = new List(),
+ LocationIds = new List()
+ };
+ }
+
+ if (regionHasAll && explicitLocationIds.Count > 0)
+ {
+ await LocationScopeBindingHelper.ValidateLocationIdsExistAsync(db, explicitLocationIds);
+ return new LabelRegionScopeSaveResult
+ {
+ AppliedRegionType = AppliedRegionAll,
+ RegionIds = new List(),
+ LocationIds = explicitLocationIds
+ };
+ }
if (mergedRegionIds.Count > 0)
{
@@ -95,8 +123,10 @@ public static class LabelRegionScopeHelper
await LocationScopeBindingHelper.ValidateLocationIdsExistAsync(db, explicitLocationIds);
}
- var partnerContext = await ResolvePartnerContextAsync(db, mergedRegionIds, explicitLocationIds);
- var allRegions = await AllScopeBindingHelper.ResolveAllRegionIdsAsync(db, partnerContext);
+ var resolvedPartnerContext = partnerContext.Count > 0
+ ? partnerContext
+ : await ResolvePartnerContextAsync(db, mergedRegionIds, explicitLocationIds);
+ var allRegions = await AllScopeBindingHelper.ResolveAllRegionIdsAsync(db, resolvedPartnerContext);
var regionsFull = mergedRegionIds.Count > 0
&& AllScopeBindingHelper.IsFullIdSelection(mergedRegionIds, allRegions);
@@ -107,7 +137,7 @@ public static class LabelRegionScopeHelper
if (declaredOrFullAll)
{
var allLocationsForPartner = await AllScopeBindingHelper.ResolveAllLocationIdsAsync(
- db, partnerContext, null);
+ db, resolvedPartnerContext, null);
var locationsFullForPartner = explicitLocationIds.Count == 0
|| (allLocationsForPartner.Count > 0
&& AllScopeBindingHelper.IsFullIdSelection(explicitLocationIds, allLocationsForPartner));
@@ -135,7 +165,7 @@ public static class LabelRegionScopeHelper
if (mergedRegionIds.Count == 0 && explicitLocationIds.Count > 0)
{
var allLocations = await AllScopeBindingHelper.ResolveAllLocationIdsAsync(
- db, partnerContext, null);
+ db, resolvedPartnerContext, null);
if (allLocations.Count > 0
&& AllScopeBindingHelper.IsFullIdSelection(explicitLocationIds, allLocations))
{
@@ -157,7 +187,7 @@ public static class LabelRegionScopeHelper
// 部分 Region:门店 Select All(空或覆盖该区域全集)→ 只落 Region,不写门店快照(新区店靠 Region 动态匹配)
var allLocationsInRegions = await AllScopeBindingHelper.ResolveAllLocationIdsAsync(
- db, null, mergedRegionIds);
+ db, resolvedPartnerContext, mergedRegionIds);
var locationsFullInRegions = explicitLocationIds.Count == 0
|| (allLocationsInRegions.Count > 0
&& AllScopeBindingHelper.IsFullIdSelection(explicitLocationIds, allLocationsInRegions));
@@ -179,7 +209,7 @@ public static class LabelRegionScopeHelper
// 部分 Region + 部分门店 → 校验门店属于所选 Region 后落快照
var mergedLocations = await LocationScopeBindingHelper.MergeToLocationIdsAsync(
- db, (IReadOnlyList?)null, mergedRegionIds, explicitLocationIds);
+ db, resolvedPartnerContext, mergedRegionIds, explicitLocationIds);
if (mergedLocations.Count == 0)
{
throw new UserFriendlyException("指定 Region 下未匹配到有效门店,请检查 Region 或 locationIds");
@@ -405,8 +435,8 @@ public static class LabelRegionScopeHelper
return new LabelRegionScopeDisplay
{
Region = AllRegionsDisplay,
- RegionIds = new List(),
- GroupIds = new List()
+ RegionIds = new List { AllScopeBindingHelper.ScopeAll },
+ GroupIds = new List { AllScopeBindingHelper.ScopeAll }
};
}
@@ -435,11 +465,18 @@ public static class LabelRegionScopeHelper
? string.Join(", ", names.Where(n => !string.IsNullOrWhiteSpace(n)).Select(n => n!.Trim()).Distinct())
: "?";
+ var collapsed = await ScopeAllEchoHelper.CollapseScopeIdsToAllSentinelAsync(
+ db,
+ null,
+ regionIds,
+ locationIds,
+ ScopeAllEchoHelper.ForLabelRegionScope(appliedRegionType, locationIds));
+
return new LabelRegionScopeDisplay
{
Region = regionText,
- RegionIds = regionIds,
- GroupIds = regionIds
+ RegionIds = collapsed.RegionIds,
+ GroupIds = collapsed.RegionIds
};
}
@@ -454,7 +491,7 @@ public static class LabelRegionScopeHelper
return new LabelLocationScopeDisplay
{
Location = AllLocationsDisplay,
- LocationIds = new List()
+ LocationIds = new List { AllScopeBindingHelper.ScopeAll }
};
}
@@ -482,10 +519,17 @@ public static class LabelRegionScopeHelper
? string.Join(", ", names.Where(n => !string.IsNullOrWhiteSpace(n)).Select(n => n!.Trim()).Distinct())
: "?";
+ var collapsed = await ScopeAllEchoHelper.CollapseScopeIdsToAllSentinelAsync(
+ db,
+ null,
+ null,
+ locationIds,
+ ScopeAllEchoHelper.ForLabelRegionScope(appliedRegionType, locationIds));
+
return new LabelLocationScopeDisplay
{
Location = locationText,
- LocationIds = locationIds.ToList()
+ LocationIds = collapsed.LocationIds
};
}
@@ -593,7 +637,24 @@ public static class LabelRegionScopeHelper
new { all = AppliedRegionAll, gid });
}
- /// ? scoped ?????fl_label_location ???? LocationId??
+ ///
+ /// 无可见门店时:仅保留 AppliedRegionType=ALL 的标签(用于 PartnerId 筛选但该公司暂无门店)。
+ ///
+ public static ISugarQueryable ApplyLabelAllRegionOnlyFilter(
+ ISqlSugarClient db,
+ ISugarQueryable query,
+ LabelRegionSchemaHelper.LabelRegionSchemaStatus schema)
+ {
+ if (!schema.HasAppliedRegionTypeColumn)
+ {
+ // 未迁移列时无法识别 ALL,保守返回空
+ return query.Where(_ => false);
+ }
+
+ return query.Where("AppliedRegionType = @all", new { all = AppliedRegionAll });
+ }
+
+ /// 按 scoped 门店过滤;AppliedRegionType=ALL 对任意门店筛选均命中。
public static ISugarQueryable ApplyLabelLocationListFilter(
ISqlSugarClient db,
ISugarQueryable query,
@@ -602,7 +663,7 @@ public static class LabelRegionScopeHelper
{
if (scopedLocationIds.Count == 0)
{
- return query.Where(_ => false);
+ return ApplyLabelAllRegionOnlyFilter(db, query, schema);
}
return ApplyLabelLocationMatchFilter(query, scopedLocationIds, schema);
@@ -648,6 +709,14 @@ public static class LabelRegionScopeHelper
var locationIds = await GetLocationIdsForLabelAsync(db, label.Id, label.LocationId);
if (locationIds.Count == 0)
{
+ // 指定 Company 的 ALL:仅该公司下门店可用
+ if (!string.IsNullOrWhiteSpace(label.PartnerId))
+ {
+ var locPartners = await LocationScopeBindingHelper.ResolvePartnerIdsFromLocationIdsAsync(
+ db, new List { locationId.Trim() });
+ return locPartners.Contains(label.PartnerId.Trim(), StringComparer.OrdinalIgnoreCase);
+ }
+
return true;
}
@@ -784,6 +853,26 @@ public static class LabelRegionScopeHelper
List scopedLocationIds,
LabelRegionSchemaHelper.LabelRegionSchemaStatus schema)
{
+ if (schema.HasAppliedRegionTypeColumn && schema.HasLabelLocationTable)
+ {
+ return query.Where(
+ """
+ (AppliedRegionType = @all
+ OR LocationId IN (@locs)
+ OR EXISTS (SELECT 1 FROM fl_label_location ll WHERE ll.LabelId = fl_label.Id AND ll.LocationId IN (@locs)))
+ """,
+ new { all = AppliedRegionAll, locs = scopedLocationIds });
+ }
+
+ if (schema.HasAppliedRegionTypeColumn)
+ {
+ return query.Where(
+ """
+ (AppliedRegionType = @all OR LocationId IN (@locs))
+ """,
+ new { all = AppliedRegionAll, locs = scopedLocationIds });
+ }
+
if (schema.HasLabelLocationTable)
{
return query.Where(
@@ -829,17 +918,18 @@ public static class LabelRegionScopeHelper
private static async Task ValidateRegionIdsExistAsync(ISqlSugarClient db, List regionIds)
{
- if (regionIds.Count == 0)
+ var ids = LocationScopeBindingHelper.FilterConcreteScopeIds(regionIds);
+ if (ids.Count == 0)
{
return;
}
var count = await db.Queryable()
- .Where(g => !g.IsDeleted && regionIds.Contains(g.Id))
+ .Where(g => !g.IsDeleted && ids.Contains(g.Id))
.CountAsync();
- if (count != regionIds.Count)
+ if (count != ids.Count)
{
- throw new UserFriendlyException("????? Region Id???????");
+ throw new UserFriendlyException("存在无效的 Region Id,请刷新后重试");
}
}
}
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelTemplateScopeHelper.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelTemplateScopeHelper.cs
index 674a63b..1056ac9 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelTemplateScopeHelper.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelTemplateScopeHelper.cs
@@ -58,43 +58,73 @@ public static class LabelTemplateScopeHelper
///
/// 解析新增/编辑入参中的 Company / Region / Location 范围。
+ /// Create/Update 共用;regionIds、groupIds、locationIds、appliedLocationIds 可传哨兵 ALL,
+ /// 即使 appliedRegionType / appliedLocation 为 SPECIFIED 也会归档为对应维度 ALL 且不写关联快照。
///
public static async Task ResolveScopeForSaveAsync(
ISqlSugarClient db,
LabelTemplateCreateInputVo input)
{
- var partnerIds = NormalizePartnerIds(input);
- var regionIds = NormalizeRegionIds(input);
- var locationIds = MergeExplicitLocationIds(input);
+ var mergedRegionIds = NormalizeRegionIds(input);
+ var mergedLocationIds = MergeExplicitLocationIds(input);
+ var hasRegionArray = input.RegionIds is not null || input.GroupIds is not null;
+ var hasLocationArray = input.LocationIds is not null || input.AppliedLocationIds is not null;
+
+ // Create/Update 共用:优先识别 ALL 哨兵,避免后续 Count>0 或 Guid 存在性校验误伤编辑回显的 ["ALL"]
+ var locationHasAll = AllScopeBindingHelper.HasAllScopeSentinelSelection(mergedLocationIds);
+ var regionHasAll = AllScopeBindingHelper.HasAllScopeSentinelSelection(mergedRegionIds);
+ var concreteLocations = LocationScopeBindingHelper.FilterConcreteScopeIds(mergedLocationIds);
var (partnerType, partnerIdsForSave) = await AllScopeBindingHelper.NormalizePartnerScopeAsync(
db,
input.AppliedPartnerType,
- partnerIds,
- null,
+ input.PartnerIds,
+ input.CompanyIds,
input.PartnerIds is not null || input.CompanyIds is not null);
- partnerIds = partnerIdsForSave;
+ var partnerIds = partnerIdsForSave;
var partnerContext = string.Equals(partnerType, ScopeSpecified, StringComparison.OrdinalIgnoreCase)
? partnerIds
: null;
- var (regionType, regionIdsForSave) = await AllScopeBindingHelper.NormalizeRegionScopeAsync(
- db,
- input.AppliedRegionType,
- regionIds,
- input.RegionIds is not null || input.GroupIds is not null,
- partnerContext);
- regionIds = regionIdsForSave;
+ string regionType;
+ List regionIds;
+ if (regionHasAll && concreteLocations.Count == 0)
+ {
+ regionType = ScopeAll;
+ regionIds = new List();
+ }
+ else
+ {
+ var regionResult = await AllScopeBindingHelper.NormalizeRegionScopeAsync(
+ db,
+ input.AppliedRegionType,
+ LocationScopeBindingHelper.FilterConcreteScopeIds(mergedRegionIds),
+ hasRegionArray,
+ partnerContext);
+ regionType = regionResult.Type;
+ regionIds = regionResult.Ids;
+ }
- var (locationType, locationIdsForSave) = await AllScopeBindingHelper.NormalizeLocationScopeAsync(
- db,
- input.AppliedLocationType,
- locationIds,
- input.LocationIds is not null || input.AppliedLocationIds is not null,
- partnerContext,
- string.Equals(regionType, ScopeSpecified, StringComparison.OrdinalIgnoreCase) ? regionIds : null);
- locationIds = locationIdsForSave;
+ string locationType;
+ List locationIds;
+ if (locationHasAll)
+ {
+ locationType = ScopeAll;
+ locationIds = new List();
+ }
+ else
+ {
+ var locationResult = await AllScopeBindingHelper.NormalizeLocationScopeAsync(
+ db,
+ input.AppliedLocationType,
+ concreteLocations,
+ hasLocationArray,
+ partnerContext,
+ string.Equals(regionType, ScopeSpecified, StringComparison.OrdinalIgnoreCase) ? regionIds : null);
+ locationType = locationResult.Type;
+ locationIds = locationResult.Ids;
+ }
ValidateDimensionType("Company", partnerType);
ValidateDimensionType("Region", regionType);
@@ -134,7 +164,9 @@ public static class LabelTemplateScopeHelper
|| string.Equals(regionType, ScopeSpecified, StringComparison.OrdinalIgnoreCase)
|| string.Equals(locationType, ScopeSpecified, StringComparison.OrdinalIgnoreCase);
- if (anySpecified)
+ if (anySpecified
+ && (string.Equals(regionType, ScopeSpecified, StringComparison.OrdinalIgnoreCase)
+ || string.Equals(locationType, ScopeSpecified, StringComparison.OrdinalIgnoreCase)))
{
var merged = await LocationScopeBindingHelper.MergeToLocationIdsAsync(
db,
@@ -300,6 +332,8 @@ public static class LabelTemplateScopeHelper
.Where(x => templateIds.Contains(x.TemplateId))
.ToListAsync();
+ var storedScopeTypes = await LabelTemplateScopeSchemaHelper.GetAppliedScopeTypesMapAsync(db, templateIds);
+
var partnerIdSet = partnerLinks.Select(x => x.PartnerId).Distinct(StringComparer.Ordinal).ToList();
var regionIdSet = regionLinks.Select(x => x.GroupId).Distinct(StringComparer.Ordinal).ToList();
var locationIdSet = locationLinks.Select(x => x.LocationId).Distinct(StringComparer.Ordinal).ToList();
@@ -360,9 +394,16 @@ public static class LabelTemplateScopeHelper
var partnerType = pIds.Count > 0 ? ScopeSpecified : ScopeAll;
var regionType = rIds.Count > 0 ? ScopeSpecified : ScopeAll;
+ if (storedScopeTypes.TryGetValue(template.Id, out var storedTypes))
+ {
+ partnerType = NormalizeScopeType(storedTypes.PartnerType, partnerType);
+ regionType = NormalizeScopeType(storedTypes.RegionType, regionType);
+ }
+
if (hasExtendedScope
&& pIds.Count == 0
- && lIds.Count > 0)
+ && lIds.Count > 0
+ && !string.Equals(partnerType, ScopeSpecified, StringComparison.OrdinalIgnoreCase))
{
pIds = await LocationScopeBindingHelper.ResolvePartnerIdsFromLocationIdsAsync(db, lIds);
if (pIds.Count > 0)
@@ -373,7 +414,8 @@ public static class LabelTemplateScopeHelper
if (hasExtendedScope
&& rIds.Count == 0
- && lIds.Count > 0)
+ && lIds.Count > 0
+ && !string.Equals(regionType, ScopeSpecified, StringComparison.OrdinalIgnoreCase))
{
rIds = await LocationScopeBindingHelper.ResolveGroupIdsFromLocationIdsAsync(db, lIds);
if (rIds.Count > 0)
@@ -401,6 +443,13 @@ public static class LabelTemplateScopeHelper
? AllLocationsDisplay
: FormatLocationNames(lIds, locById);
+ var collapsed = await ScopeAllEchoHelper.CollapseScopeIdsToAllSentinelAsync(
+ db,
+ pIds,
+ rIds,
+ lIds,
+ ScopeAllEchoHelper.ForLabelTemplateDimensions(partnerType, regionType, locationType));
+
result[template.Id] = new LabelTemplateScopeDisplay
{
Company = companyDisplay,
@@ -409,9 +458,9 @@ public static class LabelTemplateScopeHelper
AppliedPartnerType = partnerType,
AppliedRegionType = regionType,
AppliedLocationType = locationType,
- PartnerIds = pIds,
- RegionIds = rIds,
- LocationIds = lIds
+ PartnerIds = collapsed.PartnerIds,
+ RegionIds = collapsed.RegionIds,
+ LocationIds = collapsed.LocationIds
};
}
@@ -419,31 +468,58 @@ public static class LabelTemplateScopeHelper
}
///
- /// 列表权限:按当前用户可见门店筛选模板(各维度 AND)。
- /// 为 null 时不限制(管理员且未传 Query 筛选);
- /// 非空时仅返回与可见 Company/Region/Location 匹配的模板,不包含未绑定任何范围的「全局」模板。
+ /// 列表筛选:按当前用户可见范围筛选模板。
+ /// 仅传 partnerId 时只按 Company 维度过滤(多公司绑定不会被门店 AND 误杀)。
///
public static async Task> ApplyTemplateScopeFilterAsync(
ISqlSugarClient db,
ISugarQueryable query,
- List? scopedLocationIds)
+ List? scopedLocationIds,
+ string? partnerId = null,
+ string? groupId = null,
+ string? locationId = null)
{
+ var hasExtendedScope = await LabelTemplateScopeSchemaHelper.HasPartnerRegionScopeTablesAsync(db);
+ var schema = await LabelTemplateScopeSchemaHelper.GetStatusAsync(db);
+
+ // 仅 PartnerId:只返回 fl_label_template_partner 含该公司的模板。
+ // 不含 AppliedPartnerType=ALL(全公司模板在未传 PartnerId 时可见);
+ // 也不把「无关联行」当成 ALL,避免其他公司数据漏出。
+ if (!LabelEntityListScopeHelper.ShouldApplyLocationAvailabilityFilter(partnerId, groupId, locationId)
+ && !string.IsNullOrWhiteSpace(partnerId))
+ {
+ var pid = partnerId.Trim();
+ if (!schema.HasPartnerScopeTable)
+ {
+ return query.Where(_ => false);
+ }
+
+ return query.Where(t =>
+ SqlFunc.Subqueryable()
+ .Where(p => p.TemplateId == t.Id && p.PartnerId == pid)
+ .Any());
+ }
+
if (scopedLocationIds is null)
{
return query;
}
- var hasExtendedScope = await LabelTemplateScopeSchemaHelper.HasPartnerRegionScopeTablesAsync(db);
-
if (scopedLocationIds.Count == 0)
{
- return query.Where(_ => false);
+ // 无可见门店:保留 Location=ALL(及无 location 关联)的模板
+ return query.Where(t =>
+ t.AppliedLocationType == ScopeAll
+ || !SqlFunc.Subqueryable()
+ .Where(l => l.TemplateId == t.Id)
+ .Any());
}
if (!hasExtendedScope)
{
return query.Where(t =>
- SqlFunc.Subqueryable()
+ t.AppliedLocationType == ScopeAll
+ || SqlFunc.Subqueryable()
.Where(l => l.TemplateId == t.Id && scopedLocationIds.Contains(l.LocationId))
.Any());
}
@@ -453,6 +529,30 @@ public static class LabelTemplateScopeHelper
var scopedGroupIds = await LocationScopeBindingHelper.ResolveGroupIdsFromLocationIdsAsync(
db, scopedLocationIds);
+ // Company:AppliedPartnerType=ALL 或关联命中(不用「无行=ALL」)
+ if (schema.HasAppliedPartnerTypeColumn)
+ {
+ return query.Where(
+ """
+ (AppliedPartnerType = @all
+ OR EXISTS (SELECT 1 FROM fl_label_template_partner p
+ WHERE p.TemplateId = fl_label_template.Id AND p.PartnerId IN (@partnerIds)))
+ AND (NOT EXISTS (SELECT 1 FROM fl_label_template_region r WHERE r.TemplateId = fl_label_template.Id)
+ OR EXISTS (SELECT 1 FROM fl_label_template_region r
+ WHERE r.TemplateId = fl_label_template.Id AND r.GroupId IN (@groupIds)))
+ AND (AppliedLocationType = @all
+ OR EXISTS (SELECT 1 FROM fl_label_template_location l
+ WHERE l.TemplateId = fl_label_template.Id AND l.LocationId IN (@locs)))
+ """,
+ new
+ {
+ all = ScopeAll,
+ partnerIds = scopedPartnerIds,
+ groupIds = scopedGroupIds.Count > 0 ? scopedGroupIds : new List