Commit 5d2dea03230e4a3fad84f797b6a7fb1b29780f28

Authored by 杨鑫
2 parents 3b969879 c2bac760

Merge branch 'main' of http://39.98.150.180/wangming/Food-Labeling-Management-Platform

Showing 14 changed files with 252 additions and 350 deletions
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Reports/ReportsPrintLogListItemDto.cs
@@ -27,7 +27,7 @@ public class ReportsPrintLogListItemDto @@ -27,7 +27,7 @@ public class ReportsPrintLogListItemDto
27 /// <summary>模板展示(尺寸 + 模板名)</summary> 27 /// <summary>模板展示(尺寸 + 模板名)</summary>
28 public string TemplateText { get; set; } = "无"; 28 public string TemplateText { get; set; } = "无";
29 29
30 - /// <summary>打印时间展示(<c>MM/dd/yy HH:mm</c>)</summary> 30 + /// <summary>打印时间展示(<c>MM/dd/yy</c>,即 MM/DD/YY)</summary>
31 public string PrintedAt { get; set; } = "无"; 31 public string PrintedAt { get; set; } = "无";
32 32
33 /// <summary>打印人姓名</summary> 33 /// <summary>打印人姓名</summary>
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IReportsAppService.cs
@@ -13,7 +13,7 @@ public interface IReportsAppService : IApplicationService @@ -13,7 +13,7 @@ public interface IReportsAppService : IApplicationService
13 { 13 {
14 /// <summary> 14 /// <summary>
15 /// Print Log 分页列表;角色 <c>admin</c> 可查全部,否则仅当前用户打印记录。 15 /// Print Log 分页列表;角色 <c>admin</c> 可查全部,否则仅当前用户打印记录。
16 - /// 出参 <c>printedAt</c>、<c>expiryDateText</c> 统一为美式短日期 <c>MM/dd/yy HH:mm</c>(精确到分钟) 16 + /// 出参 <c>printedAt</c>、<c>expiryDateText</c> 日期部分统一为美式短日期 <c>MM/dd/yy</c>(MM/DD/YY);含时分时 <c>MM/dd/yy HH:mm</c>
17 /// </summary> 17 /// </summary>
18 Task<PagedResultWithPageDto<ReportsPrintLogListItemDto>> GetPrintLogListAsync(ReportsPrintLogGetListInputVo input); 18 Task<PagedResultWithPageDto<ReportsPrintLogListItemDto>> GetPrintLogListAsync(ReportsPrintLogGetListInputVo input);
19 19
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelEntityPartnerScopeHelper.cs
@@ -138,7 +138,7 @@ public static class LabelEntityPartnerScopeHelper @@ -138,7 +138,7 @@ public static class LabelEntityPartnerScopeHelper
138 } 138 }
139 139
140 /// <summary> 140 /// <summary>
141 - /// 保存 Company 适用范围(关联表 + <c>AppliedPartnerType</c> 列);未执行迁移脚本时 no-op。 141 + /// 保存 Company 适用范围(关联表与 <c>AppliedPartnerType</c> 列独立探测与落库);未执行迁移脚本时 no-op。
142 /// </summary> 142 /// </summary>
143 public static async Task SavePartnerScopeAsync( 143 public static async Task SavePartnerScopeAsync(
144 ISqlSugarClient db, 144 ISqlSugarClient db,
@@ -154,25 +154,54 @@ public static class LabelEntityPartnerScopeHelper @@ -154,25 +154,54 @@ public static class LabelEntityPartnerScopeHelper
154 return; 154 return;
155 } 155 }
156 156
  157 + var needsPartnerRows = NeedsPartnerRowPersistence(scope);
157 var schema = await GetSchemaStatusAsync(db); 158 var schema = await GetSchemaStatusAsync(db);
158 - if (!HasPartnerScopeForKind(schema, kind)) 159 + var hasTable = HasPartnerTableForKind(schema, kind);
  160 + var hasColumn = HasPartnerColumnForKind(schema, kind);
  161 +
  162 + if (!hasTable && !hasColumn)
159 { 163 {
160 - return; 164 + if (needsPartnerRows)
  165 + {
  166 + schema = await RefreshSchemaCacheAsync(db);
  167 + hasTable = HasPartnerTableForKind(schema, kind);
  168 + hasColumn = HasPartnerColumnForKind(schema, kind);
  169 + }
  170 + else
  171 + {
  172 + return;
  173 + }
  174 + }
  175 + else if (needsPartnerRows && !hasTable)
  176 + {
  177 + schema = await RefreshSchemaCacheAsync(db);
  178 + hasTable = HasPartnerTableForKind(schema, kind);
  179 + hasColumn = HasPartnerColumnForKind(schema, kind);
161 } 180 }
162 181
163 - await DeletePartnerScopeRowsAsync(db, kind, entityId); 182 + if (needsPartnerRows && !hasTable)
  183 + {
  184 + throw new UserFriendlyException(
  185 + "Company 适用范围关联表尚未就绪,请联系管理员执行数据库迁移(fl_label_entity_partner_scope.sql)后重试");
  186 + }
164 187
165 - if (string.Equals(scope.AppliedPartnerType, ScopeSpecified, StringComparison.OrdinalIgnoreCase)  
166 - && scope.PartnerIds.Count > 0) 188 + if (hasTable)
167 { 189 {
168 - await InsertPartnerRowsAsync(db, guidGenerator, kind, entityId, scope.PartnerIds, currentUserId, now); 190 + await DeletePartnerScopeRowsCoreAsync(db, kind, entityId);
  191 + if (needsPartnerRows)
  192 + {
  193 + await InsertPartnerRowsAsync(db, guidGenerator, kind, entityId, scope.PartnerIds, currentUserId, now);
  194 + }
169 } 195 }
170 196
171 - await SetAppliedPartnerTypeAsync(db, kind, entityId, scope.AppliedPartnerType); 197 + if (hasColumn)
  198 + {
  199 + await SetAppliedPartnerTypeAsync(db, kind, entityId, scope.AppliedPartnerType);
  200 + }
172 } 201 }
173 202
174 /// <summary> 203 /// <summary>
175 - /// 删除实体下 Company 关联行;未迁移 partner 表时跳过 204 + /// 删除实体下 Company 关联行;未迁移 partner 表时跳过(缓存过期时会刷新再试)
176 /// </summary> 205 /// </summary>
177 public static async Task DeletePartnerScopeRowsAsync( 206 public static async Task DeletePartnerScopeRowsAsync(
178 ISqlSugarClient db, 207 ISqlSugarClient db,
@@ -185,29 +214,19 @@ public static class LabelEntityPartnerScopeHelper @@ -185,29 +214,19 @@ public static class LabelEntityPartnerScopeHelper
185 } 214 }
186 215
187 var schema = await GetSchemaStatusAsync(db); 216 var schema = await GetSchemaStatusAsync(db);
188 - if (!HasPartnerTableForKind(schema, kind)) 217 + var hasTable = HasPartnerTableForKind(schema, kind);
  218 + if (!hasTable)
189 { 219 {
190 - return; 220 + schema = await RefreshSchemaCacheAsync(db);
  221 + hasTable = HasPartnerTableForKind(schema, kind);
191 } 222 }
192 223
193 - switch (kind) 224 + if (!hasTable)
194 { 225 {
195 - case LabelEntityPartnerKind.Type:  
196 - await db.Deleteable<FlLabelTypePartnerDbEntity>()  
197 - .Where(x => x.LabelTypeId == entityId)  
198 - .ExecuteCommandAsync();  
199 - break;  
200 - case LabelEntityPartnerKind.Category:  
201 - await db.Deleteable<FlLabelCategoryPartnerDbEntity>()  
202 - .Where(x => x.CategoryId == entityId)  
203 - .ExecuteCommandAsync();  
204 - break;  
205 - case LabelEntityPartnerKind.MultipleOption:  
206 - await db.Deleteable<FlLabelMultipleOptionPartnerDbEntity>()  
207 - .Where(x => x.MultipleOptionId == entityId)  
208 - .ExecuteCommandAsync();  
209 - break; 226 + return;
210 } 227 }
  228 +
  229 + await DeletePartnerScopeRowsCoreAsync(db, kind, entityId);
211 } 230 }
212 231
213 /// <summary> 232 /// <summary>
@@ -234,9 +253,10 @@ public static class LabelEntityPartnerScopeHelper @@ -234,9 +253,10 @@ public static class LabelEntityPartnerScopeHelper
234 } 253 }
235 254
236 var schema = await GetSchemaStatusAsync(db); 255 var schema = await GetSchemaStatusAsync(db);
237 - var hasScope = HasPartnerScopeForKind(schema, kind); 256 + var hasTable = HasPartnerTableForKind(schema, kind);
  257 + var hasColumn = HasPartnerColumnForKind(schema, kind);
238 258
239 - var partnerLinks = hasScope 259 + var partnerLinks = hasTable
240 ? await LoadPartnerLinksAsync(db, kind, ids) 260 ? await LoadPartnerLinksAsync(db, kind, ids)
241 : new List<(string EntityId, string PartnerId)>(); 261 : new List<(string EntityId, string PartnerId)>();
242 262
@@ -249,7 +269,7 @@ public static class LabelEntityPartnerScopeHelper @@ -249,7 +269,7 @@ public static class LabelEntityPartnerScopeHelper
249 partnerLinks.Where(x => x.EntityId == entityId).Select(x => x.PartnerId).ToList()); 269 partnerLinks.Where(x => x.EntityId == entityId).Select(x => x.PartnerId).ToList());
250 270
251 var partnerType = pIds.Count > 0 ? ScopeSpecified : ScopeAll; 271 var partnerType = pIds.Count > 0 ? ScopeSpecified : ScopeAll;
252 - if (hasScope) 272 + if (hasColumn)
253 { 273 {
254 var storedType = await GetAppliedPartnerTypeAsync(db, kind, entityId); 274 var storedType = await GetAppliedPartnerTypeAsync(db, kind, entityId);
255 if (!string.IsNullOrWhiteSpace(storedType)) 275 if (!string.IsNullOrWhiteSpace(storedType))
@@ -261,6 +281,10 @@ public static class LabelEntityPartnerScopeHelper @@ -261,6 +281,10 @@ public static class LabelEntityPartnerScopeHelper
261 partnerType = ScopeSpecified; 281 partnerType = ScopeSpecified;
262 } 282 }
263 } 283 }
  284 + else if (pIds.Count > 0)
  285 + {
  286 + partnerType = ScopeSpecified;
  287 + }
264 288
265 var companyDisplay = string.Equals(partnerType, ScopeAll, StringComparison.OrdinalIgnoreCase) 289 var companyDisplay = string.Equals(partnerType, ScopeAll, StringComparison.OrdinalIgnoreCase)
266 ? AllCompaniesDisplay 290 ? AllCompaniesDisplay
@@ -456,6 +480,41 @@ public static class LabelEntityPartnerScopeHelper @@ -456,6 +480,41 @@ public static class LabelEntityPartnerScopeHelper
456 /// <summary>单元测试或切换库后调用。</summary> 480 /// <summary>单元测试或切换库后调用。</summary>
457 public static void ResetSchemaCacheForTests() => _cachedSchema = null; 481 public static void ResetSchemaCacheForTests() => _cachedSchema = null;
458 482
  483 + private static bool NeedsPartnerRowPersistence(LabelEntityPartnerScopeSaveResult scope) =>
  484 + string.Equals(scope.AppliedPartnerType, ScopeSpecified, StringComparison.OrdinalIgnoreCase)
  485 + && scope.PartnerIds.Count > 0;
  486 +
  487 + private static async Task<LabelEntityPartnerScopeSchemaStatus> RefreshSchemaCacheAsync(ISqlSugarClient db)
  488 + {
  489 + _cachedSchema = null;
  490 + return await GetSchemaStatusAsync(db);
  491 + }
  492 +
  493 + private static async Task DeletePartnerScopeRowsCoreAsync(
  494 + ISqlSugarClient db,
  495 + LabelEntityPartnerKind kind,
  496 + string entityId)
  497 + {
  498 + switch (kind)
  499 + {
  500 + case LabelEntityPartnerKind.Type:
  501 + await db.Deleteable<FlLabelTypePartnerDbEntity>()
  502 + .Where(x => x.LabelTypeId == entityId)
  503 + .ExecuteCommandAsync();
  504 + break;
  505 + case LabelEntityPartnerKind.Category:
  506 + await db.Deleteable<FlLabelCategoryPartnerDbEntity>()
  507 + .Where(x => x.CategoryId == entityId)
  508 + .ExecuteCommandAsync();
  509 + break;
  510 + case LabelEntityPartnerKind.MultipleOption:
  511 + await db.Deleteable<FlLabelMultipleOptionPartnerDbEntity>()
  512 + .Where(x => x.MultipleOptionId == entityId)
  513 + .ExecuteCommandAsync();
  514 + break;
  515 + }
  516 + }
  517 +
459 private static void ValidatePartnerType(string type) 518 private static void ValidatePartnerType(string type)
460 { 519 {
461 if (type != ScopeAll && type != ScopeSpecified) 520 if (type != ScopeAll && type != ScopeSpecified)
@@ -514,9 +573,6 @@ public static class LabelEntityPartnerScopeHelper @@ -514,9 +573,6 @@ public static class LabelEntityPartnerScopeHelper
514 return _cachedSchema; 573 return _cachedSchema;
515 } 574 }
516 575
517 - private static bool HasPartnerScopeForKind(LabelEntityPartnerScopeSchemaStatus schema, LabelEntityPartnerKind kind) =>  
518 - HasPartnerTableForKind(schema, kind) && HasPartnerColumnForKind(schema, kind);  
519 -  
520 private static bool HasPartnerTableForKind(LabelEntityPartnerScopeSchemaStatus schema, LabelEntityPartnerKind kind) => 576 private static bool HasPartnerTableForKind(LabelEntityPartnerScopeSchemaStatus schema, LabelEntityPartnerKind kind) =>
521 kind switch 577 kind switch
522 { 578 {
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelTemplateScopeHelper.cs
@@ -150,13 +150,29 @@ public static class LabelTemplateScopeHelper @@ -150,13 +150,29 @@ public static class LabelTemplateScopeHelper
150 string? currentUserId, 150 string? currentUserId,
151 DateTime now) 151 DateTime now)
152 { 152 {
153 - var hasExtendedScope = await LabelTemplateScopeSchemaHelper.HasPartnerRegionScopeTablesAsync(db); 153 + var needsPartnerRows = string.Equals(scope.AppliedPartnerType, ScopeSpecified, StringComparison.OrdinalIgnoreCase)
  154 + && scope.PartnerIds.Count > 0;
  155 + var needsRegionRows = string.Equals(scope.AppliedRegionType, ScopeSpecified, StringComparison.OrdinalIgnoreCase)
  156 + && scope.RegionIds.Count > 0;
154 157
155 - if (hasExtendedScope) 158 + var hasPartnerTable = await LabelTemplateScopeSchemaHelper.EnsurePartnerScopeTableAsync(db, needsPartnerRows);
  159 + var hasRegionTable = await LabelTemplateScopeSchemaHelper.EnsureRegionScopeTableAsync(db, needsRegionRows);
  160 +
  161 + if (needsPartnerRows && !hasPartnerTable)
  162 + {
  163 + throw new UserFriendlyException(
  164 + "Company 适用范围关联表尚未就绪,请联系管理员执行数据库迁移(fl_label_template_scope.sql)后重试");
  165 + }
  166 +
  167 + if (hasPartnerTable)
156 { 168 {
157 await db.Deleteable<FlLabelTemplatePartnerDbEntity>() 169 await db.Deleteable<FlLabelTemplatePartnerDbEntity>()
158 .Where(x => x.TemplateId == templateId) 170 .Where(x => x.TemplateId == templateId)
159 .ExecuteCommandAsync(); 171 .ExecuteCommandAsync();
  172 + }
  173 +
  174 + if (hasRegionTable)
  175 + {
160 await db.Deleteable<FlLabelTemplateRegionDbEntity>() 176 await db.Deleteable<FlLabelTemplateRegionDbEntity>()
161 .Where(x => x.TemplateId == templateId) 177 .Where(x => x.TemplateId == templateId)
162 .ExecuteCommandAsync(); 178 .ExecuteCommandAsync();
@@ -166,9 +182,7 @@ public static class LabelTemplateScopeHelper @@ -166,9 +182,7 @@ public static class LabelTemplateScopeHelper
166 .Where(x => x.TemplateId == templateId) 182 .Where(x => x.TemplateId == templateId)
167 .ExecuteCommandAsync(); 183 .ExecuteCommandAsync();
168 184
169 - if (hasExtendedScope  
170 - && string.Equals(scope.AppliedPartnerType, ScopeSpecified, StringComparison.OrdinalIgnoreCase)  
171 - && scope.PartnerIds.Count > 0) 185 + if (hasPartnerTable && needsPartnerRows)
172 { 186 {
173 var rows = scope.PartnerIds.Select(pid => new FlLabelTemplatePartnerDbEntity 187 var rows = scope.PartnerIds.Select(pid => new FlLabelTemplatePartnerDbEntity
174 { 188 {
@@ -181,9 +195,7 @@ public static class LabelTemplateScopeHelper @@ -181,9 +195,7 @@ public static class LabelTemplateScopeHelper
181 await db.Insertable(rows).ExecuteCommandAsync(); 195 await db.Insertable(rows).ExecuteCommandAsync();
182 } 196 }
183 197
184 - if (hasExtendedScope  
185 - && string.Equals(scope.AppliedRegionType, ScopeSpecified, StringComparison.OrdinalIgnoreCase)  
186 - && scope.RegionIds.Count > 0) 198 + if (hasRegionTable && needsRegionRows)
187 { 199 {
188 var rows = scope.RegionIds.Select(gid => new FlLabelTemplateRegionDbEntity 200 var rows = scope.RegionIds.Select(gid => new FlLabelTemplateRegionDbEntity
189 { 201 {
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LabelTemplateScopeSchemaHelper.cs
@@ -116,6 +116,13 @@ public static class LabelTemplateScopeSchemaHelper @@ -116,6 +116,13 @@ public static class LabelTemplateScopeSchemaHelper
116 /// <summary>单元测试或切换库后调用。</summary> 116 /// <summary>单元测试或切换库后调用。</summary>
117 public static void ResetCacheForTests() => _cached = null; 117 public static void ResetCacheForTests() => _cached = null;
118 118
  119 + /// <summary>清除缓存并重新探测 schema(应用启动早于迁移脚本执行后调用)。</summary>
  120 + public static async Task<LabelTemplateScopeSchemaStatus> RefreshStatusAsync(ISqlSugarClient db)
  121 + {
  122 + _cached = null;
  123 + return await GetStatusAsync(db);
  124 + }
  125 +
119 /// <summary> 126 /// <summary>
120 /// 是否已创建 <c>fl_label_template_partner</c> 与 <c>fl_label_template_region</c>。 127 /// 是否已创建 <c>fl_label_template_partner</c> 与 <c>fl_label_template_region</c>。
121 /// </summary> 128 /// </summary>
@@ -126,6 +133,46 @@ public static class LabelTemplateScopeSchemaHelper @@ -126,6 +133,46 @@ public static class LabelTemplateScopeSchemaHelper
126 } 133 }
127 134
128 /// <summary> 135 /// <summary>
  136 + /// 是否已创建 <c>fl_label_template_partner</c>;缓存未命中时刷新后再探测。
  137 + /// </summary>
  138 + public static async Task<bool> EnsurePartnerScopeTableAsync(ISqlSugarClient db, bool needsPartnerRows)
  139 + {
  140 + var status = await GetStatusAsync(db);
  141 + if (status.HasPartnerScopeTable)
  142 + {
  143 + return true;
  144 + }
  145 +
  146 + if (!needsPartnerRows)
  147 + {
  148 + return false;
  149 + }
  150 +
  151 + status = await RefreshStatusAsync(db);
  152 + return status.HasPartnerScopeTable;
  153 + }
  154 +
  155 + /// <summary>
  156 + /// 是否已创建 <c>fl_label_template_region</c>;缓存未命中时刷新后再探测。
  157 + /// </summary>
  158 + public static async Task<bool> EnsureRegionScopeTableAsync(ISqlSugarClient db, bool needsRegionRows)
  159 + {
  160 + var status = await GetStatusAsync(db);
  161 + if (status.HasRegionScopeTable)
  162 + {
  163 + return true;
  164 + }
  165 +
  166 + if (!needsRegionRows)
  167 + {
  168 + return false;
  169 + }
  170 +
  171 + status = await RefreshStatusAsync(db);
  172 + return status.HasRegionScopeTable;
  173 + }
  174 +
  175 + /// <summary>
129 /// 已迁移 scope 列时,写入 <c>AppliedPartnerType</c> / <c>AppliedRegionType</c>(未迁移则 no-op)。 176 /// 已迁移 scope 列时,写入 <c>AppliedPartnerType</c> / <c>AppliedRegionType</c>(未迁移则 no-op)。
130 /// </summary> 177 /// </summary>
131 public static async Task SetAppliedScopeTypesAsync( 178 public static async Task SetAppliedScopeTypesAsync(
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/LocationScopeBindingHelper.cs
@@ -336,6 +336,34 @@ public static class LocationScopeBindingHelper @@ -336,6 +336,34 @@ public static class LocationScopeBindingHelper
336 } 336 }
337 337
338 /// <summary> 338 /// <summary>
  339 + /// 标签类型/分类/多选项门店范围落库:仅按 Region/Location 合并,不因 Company(partner)展开并集。
  340 + /// </summary>
  341 + public static async Task<List<string>> ResolveEntityLocationIdsForSaveAsync(
  342 + ISqlSugarClient db,
  343 + bool locationSpecified,
  344 + IReadOnlyList<string>? regionIds,
  345 + IReadOnlyList<string>? locationIds)
  346 + {
  347 + if (!locationSpecified)
  348 + {
  349 + return new List<string>();
  350 + }
  351 +
  352 + var merged = await MergeToLocationIdsAsync(
  353 + db,
  354 + (IReadOnlyList<string>?)null,
  355 + regionIds,
  356 + locationIds);
  357 + if (merged.Count == 0)
  358 + {
  359 + throw new UserFriendlyException("指定适用区域或门店时,至少需要匹配到一个有效门店");
  360 + }
  361 +
  362 + await ValidateLocationIdsExistAsync(db, merged);
  363 + return merged;
  364 + }
  365 +
  366 + /// <summary>
339 /// 根据已绑定门店反推适用的 Company Id(<c>fl_partner.Id</c>)。 367 /// 根据已绑定门店反推适用的 Company Id(<c>fl_partner.Id</c>)。
340 /// </summary> 368 /// </summary>
341 public static async Task<List<string>> ResolvePartnerIdsFromLocationIdsAsync( 369 public static async Task<List<string>> ResolvePartnerIdsFromLocationIdsAsync(
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/ReportsDateTimeDisplayHelper.cs
1 using System.Globalization; 1 using System.Globalization;
2 2
3 -  
4 -  
5 namespace FoodLabeling.Application.Helpers; 3 namespace FoodLabeling.Application.Helpers;
6 4
7 -  
8 -  
9 /// <summary> 5 /// <summary>
10 -  
11 -/// Reports Print Log 日期时间展示:统一为 <c>MM/dd/yy HH:mm</c>(精确到分钟)。  
12 - 6 +/// Reports Print Log 日期展示:统一为美式短日期 <c>MM/dd/yy</c>(MM/DD/YY)。
13 /// </summary> 7 /// </summary>
14 -  
15 public static class ReportsDateTimeDisplayHelper 8 public static class ReportsDateTimeDisplayHelper
16 -  
17 { 9 {
  10 + /// <summary>列表日期列统一格式(MM/DD/YY)。</summary>
  11 + public const string DateDisplayFormat = "MM/dd/yy";
18 12
19 - public const string DisplayFormat = "MM/dd/yy HH:mm";  
20 - 13 + /// <summary>含时分时在同日期格式后追加 HH:mm。</summary>
  14 + public const string DateTimeDisplayFormat = "MM/dd/yy HH:mm";
21 15
  16 + /// <summary>兼容旧引用。</summary>
  17 + public const string DisplayFormat = DateTimeDisplayFormat;
22 18
23 private static readonly string[] ExpiryParseFormats = 19 private static readonly string[] ExpiryParseFormats =
24 -  
25 { 20 {
26 -  
27 - DisplayFormat,  
28 - 21 + DateTimeDisplayFormat,
  22 + DateDisplayFormat,
29 "MM/dd/yy", 23 "MM/dd/yy",
30 -  
31 "MM/dd", 24 "MM/dd",
32 -  
33 "yyyy-MM-dd H:mm", 25 "yyyy-MM-dd H:mm",
34 -  
35 "yyyy-MM-dd", 26 "yyyy-MM-dd",
36 -  
37 "yyyy/MM/dd HH:mm", 27 "yyyy/MM/dd HH:mm",
38 -  
39 "yyyy/MM/dd", 28 "yyyy/MM/dd",
40 -  
41 "MM/dd/yyyy hh:mm tt", 29 "MM/dd/yyyy hh:mm tt",
42 -  
43 "MM/dd/yyyy h:mm tt", 30 "MM/dd/yyyy h:mm tt",
44 -  
45 "MM/dd/yyyy HH:mm", 31 "MM/dd/yyyy HH:mm",
46 -  
47 "MM/dd/yyyy", 32 "MM/dd/yyyy",
48 -  
49 "M/d/yyyy hh:mm tt", 33 "M/d/yyyy hh:mm tt",
50 -  
51 "M/d/yyyy h:mm tt", 34 "M/d/yyyy h:mm tt",
52 -  
53 "M/d/yyyy", 35 "M/d/yyyy",
54 -  
55 "dd/MM/yyyy HH:mm", 36 "dd/MM/yyyy HH:mm",
56 -  
57 "dd/MM/yyyy", 37 "dd/MM/yyyy",
58 -  
59 "d/M/yyyy HH:mm", 38 "d/M/yyyy HH:mm",
60 -  
61 "d/M/yyyy", 39 "d/M/yyyy",
62 -  
63 }; 40 };
64 41
65 -  
66 -  
67 - /// <summary>打印时间展示(Printed at 列)</summary>  
68 - 42 + /// <summary>打印时间展示(Printed at 列):统一 MM/DD/YY。</summary>
69 public static string FormatPrintedAt(DateTime printedAt) 43 public static string FormatPrintedAt(DateTime printedAt)
70 -  
71 { 44 {
72 -  
73 if (printedAt == DateTime.MinValue) 45 if (printedAt == DateTime.MinValue)
74 -  
75 { 46 {
76 -  
77 return "无"; 47 return "无";
78 -  
79 } 48 }
80 49
81 -  
82 -  
83 - return printedAt.ToString(DisplayFormat, CultureInfo.InvariantCulture);  
84 - 50 + return printedAt.ToString(DateDisplayFormat, CultureInfo.InvariantCulture);
85 } 51 }
86 52
87 -  
88 -  
89 - /// <summary>保质期展示(Expiration 列):解析后统一为 <see cref="DisplayFormat"/>。</summary>  
90 - 53 + /// <summary>保质期展示(Expiration 列):解析后统一为 <see cref="DateDisplayFormat"/> 或含时分时 <see cref="DateTimeDisplayFormat"/>。</summary>
91 public static string FormatExpiryDisplay(string? raw, DateTime? anchor = null) 54 public static string FormatExpiryDisplay(string? raw, DateTime? anchor = null)
92 -  
93 { 55 {
94 -  
95 if (string.IsNullOrWhiteSpace(raw) || string.Equals(raw.Trim(), "无", StringComparison.Ordinal)) 56 if (string.IsNullOrWhiteSpace(raw) || string.Equals(raw.Trim(), "无", StringComparison.Ordinal))
96 -  
97 { 57 {
98 -  
99 return "无"; 58 return "无";
100 -  
101 } 59 }
102 60
103 -  
104 -  
105 var s = raw.Trim(); 61 var s = raw.Trim();
106 -  
107 if (TryParseToDateTime(s, out var dt)) 62 if (TryParseToDateTime(s, out var dt))
108 -  
109 { 63 {
110 -  
111 - return dt.ToString(DisplayFormat, CultureInfo.InvariantCulture);  
112 - 64 + return FormatExpiryDateTime(dt);
113 } 65 }
114 66
115 -  
116 -  
117 - if (anchor.HasValue && TryParsePartialDateTime(s, anchor.Value, out dt))  
118 - 67 + var reference = anchor ?? DateTime.Now;
  68 + if (TryParsePartialDateTime(s, reference, out dt))
119 { 69 {
120 -  
121 - return dt.ToString(DisplayFormat, CultureInfo.InvariantCulture);  
122 - 70 + return FormatExpiryDateTime(dt);
123 } 71 }
124 72
125 -  
126 -  
127 return s; 73 return s;
128 -  
129 } 74 }
130 75
131 -  
132 -  
133 /// <summary>合并日期片段(如 07/09)与时间片段(如 11:56),缺少年份时用 anchor 补全。</summary> 76 /// <summary>合并日期片段(如 07/09)与时间片段(如 11:56),缺少年份时用 anchor 补全。</summary>
134 -  
135 public static string MergeDateAndTimeParts(string datePart, string timePart, DateTime anchor) 77 public static string MergeDateAndTimeParts(string datePart, string timePart, DateTime anchor)
136 -  
137 { 78 {
138 -  
139 var date = datePart.Trim(); 79 var date = datePart.Trim();
140 -  
141 var time = timePart.Trim(); 80 var time = timePart.Trim();
142 -  
143 if (string.IsNullOrWhiteSpace(date)) 81 if (string.IsNullOrWhiteSpace(date))
144 -  
145 { 82 {
146 -  
147 return string.IsNullOrWhiteSpace(time) ? "无" : time; 83 return string.IsNullOrWhiteSpace(time) ? "无" : time;
148 -  
149 } 84 }
150 85
151 -  
152 -  
153 if (!TryParsePartialDateTime(date, anchor, out var dt) && !TryParseToDateTime(date, out dt)) 86 if (!TryParsePartialDateTime(date, anchor, out var dt) && !TryParseToDateTime(date, out dt))
154 -  
155 { 87 {
156 -  
157 return string.IsNullOrWhiteSpace(time) ? date : $"{date} {time}"; 88 return string.IsNullOrWhiteSpace(time) ? date : $"{date} {time}";
158 -  
159 } 89 }
160 90
161 -  
162 -  
163 if (!string.IsNullOrWhiteSpace(time)) 91 if (!string.IsNullOrWhiteSpace(time))
164 -  
165 { 92 {
166 -  
167 var timeMatch = System.Text.RegularExpressions.Regex.Match( 93 var timeMatch = System.Text.RegularExpressions.Regex.Match(
168 -  
169 time, 94 time,
170 -  
171 @"^(?<h>\d{1,2}):(?<m>\d{2})", 95 @"^(?<h>\d{1,2}):(?<m>\d{2})",
172 -  
173 System.Text.RegularExpressions.RegexOptions.CultureInvariant); 96 System.Text.RegularExpressions.RegexOptions.CultureInvariant);
174 -  
175 if (timeMatch.Success && 97 if (timeMatch.Success &&
176 -  
177 int.TryParse(timeMatch.Groups["h"].Value, out var h) && 98 int.TryParse(timeMatch.Groups["h"].Value, out var h) &&
178 -  
179 int.TryParse(timeMatch.Groups["m"].Value, out var m)) 99 int.TryParse(timeMatch.Groups["m"].Value, out var m))
180 -  
181 { 100 {
182 -  
183 dt = new DateTime(dt.Year, dt.Month, dt.Day, h, m, 0, dt.Kind); 101 dt = new DateTime(dt.Year, dt.Month, dt.Day, h, m, 0, dt.Kind);
184 -  
185 } 102 }
186 -  
187 } 103 }
188 104
189 -  
190 -  
191 - return dt.ToString(DisplayFormat, CultureInfo.InvariantCulture);  
192 - 105 + return FormatExpiryDateTime(dt);
193 } 106 }
194 107
195 -  
196 -  
197 private static bool TryParsePartialDateTime(string s, DateTime anchor, out DateTime dt) 108 private static bool TryParsePartialDateTime(string s, DateTime anchor, out DateTime dt)
198 -  
199 { 109 {
200 -  
201 dt = default; 110 dt = default;
202 -  
203 var text = s.Trim(); 111 var text = s.Trim();
204 -  
205 var timeMatch = System.Text.RegularExpressions.Regex.Match( 112 var timeMatch = System.Text.RegularExpressions.Regex.Match(
206 -  
207 text, 113 text,
208 -  
209 @"^(?<date>\d{1,2}/\d{1,2}(?:/\d{2,4})?)\s+(?<time>\d{1,2}:\d{2})$", 114 @"^(?<date>\d{1,2}/\d{1,2}(?:/\d{2,4})?)\s+(?<time>\d{1,2}:\d{2})$",
210 -  
211 System.Text.RegularExpressions.RegexOptions.CultureInvariant); 115 System.Text.RegularExpressions.RegexOptions.CultureInvariant);
212 -  
213 if (timeMatch.Success) 116 if (timeMatch.Success)
214 -  
215 { 117 {
216 -  
217 text = timeMatch.Groups["date"].Value; 118 text = timeMatch.Groups["date"].Value;
218 -  
219 if (TryParsePartialDateOnly(text, anchor, out dt)) 119 if (TryParsePartialDateOnly(text, anchor, out dt))
220 -  
221 { 120 {
222 -  
223 var timePart = timeMatch.Groups["time"].Value; 121 var timePart = timeMatch.Groups["time"].Value;
224 -  
225 var parts = timePart.Split(':'); 122 var parts = timePart.Split(':');
226 -  
227 if (parts.Length == 2 && 123 if (parts.Length == 2 &&
228 -  
229 int.TryParse(parts[0], out var h) && 124 int.TryParse(parts[0], out var h) &&
230 -  
231 int.TryParse(parts[1], out var m)) 125 int.TryParse(parts[1], out var m))
232 -  
233 { 126 {
234 -  
235 dt = new DateTime(dt.Year, dt.Month, dt.Day, h, m, 0, dt.Kind); 127 dt = new DateTime(dt.Year, dt.Month, dt.Day, h, m, 0, dt.Kind);
236 -  
237 return true; 128 return true;
238 -  
239 } 129 }
240 -  
241 } 130 }
242 -  
243 } 131 }
244 132
245 -  
246 -  
247 return TryParsePartialDateOnly(text, anchor, out dt); 133 return TryParsePartialDateOnly(text, anchor, out dt);
248 -  
249 } 134 }
250 135
251 -  
252 -  
253 private static bool TryParsePartialDateOnly(string s, DateTime anchor, out DateTime dt) 136 private static bool TryParsePartialDateOnly(string s, DateTime anchor, out DateTime dt)
254 -  
255 { 137 {
256 -  
257 dt = default; 138 dt = default;
258 -  
259 var parts = s.Split('/'); 139 var parts = s.Split('/');
260 -  
261 if (parts.Length is < 2 or > 3) 140 if (parts.Length is < 2 or > 3)
262 -  
263 { 141 {
264 -  
265 return false; 142 return false;
266 -  
267 } 143 }
268 144
269 -  
270 -  
271 if (!int.TryParse(parts[0], out var month) || !int.TryParse(parts[1], out var day)) 145 if (!int.TryParse(parts[0], out var month) || !int.TryParse(parts[1], out var day))
272 -  
273 { 146 {
274 -  
275 return false; 147 return false;
276 -  
277 } 148 }
278 149
279 -  
280 -  
281 var year = anchor.Year; 150 var year = anchor.Year;
282 -  
283 if (parts.Length == 3) 151 if (parts.Length == 3)
284 -  
285 { 152 {
286 -  
287 if (!int.TryParse(parts[2], out var y)) 153 if (!int.TryParse(parts[2], out var y))
288 -  
289 { 154 {
290 -  
291 return false; 155 return false;
292 -  
293 } 156 }
294 157
295 -  
296 -  
297 year = parts[2].Length == 2 ? 2000 + y : y; 158 year = parts[2].Length == 2 ? 2000 + y : y;
298 -  
299 } 159 }
300 160
301 -  
302 -  
303 try 161 try
304 -  
305 { 162 {
306 -  
307 dt = new DateTime(year, month, day, 0, 0, 0, anchor.Kind); 163 dt = new DateTime(year, month, day, 0, 0, 0, anchor.Kind);
308 -  
309 return true; 164 return true;
310 -  
311 } 165 }
312 -  
313 catch 166 catch
314 -  
315 { 167 {
316 -  
317 return false; 168 return false;
318 -  
319 } 169 }
320 -  
321 } 170 }
322 171
323 -  
324 -  
325 private static bool TryParseToDateTime(string s, out DateTime dt) 172 private static bool TryParseToDateTime(string s, out DateTime dt)
326 -  
327 { 173 {
328 -  
329 if (DateTime.TryParse( 174 if (DateTime.TryParse(
330 -  
331 s, 175 s,
332 -  
333 CultureInfo.InvariantCulture, 176 CultureInfo.InvariantCulture,
334 -  
335 DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeLocal, 177 DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeLocal,
336 -  
337 out dt)) 178 out dt))
338 -  
339 { 179 {
340 -  
341 return true; 180 return true;
342 -  
343 } 181 }
344 182
345 -  
346 -  
347 if (DateTime.TryParse( 183 if (DateTime.TryParse(
348 -  
349 s, 184 s,
350 -  
351 CultureInfo.GetCultureInfo("en-US"), 185 CultureInfo.GetCultureInfo("en-US"),
352 -  
353 DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeLocal, 186 DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeLocal,
354 -  
355 out dt)) 187 out dt))
356 -  
357 { 188 {
358 -  
359 return true; 189 return true;
360 -  
361 } 190 }
362 191
363 -  
364 -  
365 foreach (var fmt in ExpiryParseFormats) 192 foreach (var fmt in ExpiryParseFormats)
366 -  
367 { 193 {
368 -  
369 if (DateTime.TryParseExact( 194 if (DateTime.TryParseExact(
370 -  
371 s, 195 s,
372 -  
373 fmt, 196 fmt,
374 -  
375 CultureInfo.InvariantCulture, 197 CultureInfo.InvariantCulture,
376 -  
377 DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeLocal, 198 DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeLocal,
378 -  
379 out dt)) 199 out dt))
380 -  
381 { 200 {
382 -  
383 return true; 201 return true;
384 -  
385 } 202 }
386 203
387 -  
388 -  
389 if (DateTime.TryParseExact( 204 if (DateTime.TryParseExact(
390 -  
391 s, 205 s,
392 -  
393 fmt, 206 fmt,
394 -  
395 CultureInfo.GetCultureInfo("en-US"), 207 CultureInfo.GetCultureInfo("en-US"),
396 -  
397 DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeLocal, 208 DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeLocal,
398 -  
399 out dt)) 209 out dt))
400 -  
401 { 210 {
402 -  
403 return true; 211 return true;
404 -  
405 } 212 }
406 -  
407 } 213 }
408 214
409 -  
410 -  
411 return false; 215 return false;
412 } 216 }
413 217
@@ -419,7 +223,12 @@ public static class ReportsDateTimeDisplayHelper @@ -419,7 +223,12 @@ public static class ReportsDateTimeDisplayHelper
419 return "无"; 223 return "无";
420 } 224 }
421 225
422 - return dt.ToString(DisplayFormat, CultureInfo.InvariantCulture); 226 + if (dt.Hour == 0 && dt.Minute == 0 && dt.Second == 0)
  227 + {
  228 + return dt.ToString(DateDisplayFormat, CultureInfo.InvariantCulture);
  229 + }
  230 +
  231 + return dt.ToString(DateTimeDisplayFormat, CultureInfo.InvariantCulture);
423 } 232 }
424 233
425 /// <summary>解析到期字面量(含 anchor 补全年份/时间)。</summary> 234 /// <summary>解析到期字面量(含 anchor 补全年份/时间)。</summary>
@@ -433,5 +242,3 @@ public static class ReportsDateTimeDisplayHelper @@ -433,5 +242,3 @@ public static class ReportsDateTimeDisplayHelper
433 return TryParsePartialDateTime(s, anchor, out dt); 242 return TryParsePartialDateTime(s, anchor, out dt);
434 } 243 }
435 } 244 }
436 -  
437 -  
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Helpers/ReportsPrintLogExpiryHelper.cs
@@ -747,8 +747,8 @@ public static class ReportsPrintLogExpiryHelper @@ -747,8 +747,8 @@ public static class ReportsPrintLogExpiryHelper
747 text = raw.Trim(); 747 text = raw.Trim();
748 if (TryParseOffsetPayload(text, out var amount, out var unit)) 748 if (TryParseOffsetPayload(text, out var amount, out var unit))
749 { 749 {
750 - text = ApplyOffset(anchor, amount, unit)  
751 - .ToString(ReportsDateTimeDisplayHelper.DisplayFormat, CultureInfo.InvariantCulture); 750 + text = ReportsDateTimeDisplayHelper.FormatExpiryDateTime(
  751 + ApplyOffset(anchor, amount, unit));
752 return true; 752 return true;
753 } 753 }
754 754
@@ -777,8 +777,8 @@ public static class ReportsPrintLogExpiryHelper @@ -777,8 +777,8 @@ public static class ReportsPrintLogExpiryHelper
777 777
778 if (v.ValueKind == JsonValueKind.Number && v.TryGetInt64(out var unix)) 778 if (v.ValueKind == JsonValueKind.Number && v.TryGetInt64(out var unix))
779 { 779 {
780 - text = DateTimeOffset.FromUnixTimeSeconds(unix).LocalDateTime  
781 - .ToString(ReportsDateTimeDisplayHelper.DisplayFormat, CultureInfo.InvariantCulture); 780 + text = ReportsDateTimeDisplayHelper.FormatExpiryDateTime(
  781 + DateTimeOffset.FromUnixTimeSeconds(unix).LocalDateTime);
782 return true; 782 return true;
783 } 783 }
784 784
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelCategoryDbEntity.cs
@@ -47,5 +47,10 @@ public class FlLabelCategoryDbEntity @@ -47,5 +47,10 @@ public class FlLabelCategoryDbEntity
47 /// 门店可用范围:ALL / SPECIFIED 47 /// 门店可用范围:ALL / SPECIFIED
48 /// </summary> 48 /// </summary>
49 public string AvailabilityType { get; set; } = "ALL"; 49 public string AvailabilityType { get; set; } = "ALL";
  50 +
  51 + /// <summary>
  52 + /// 适用 Company 范围:ALL / SPECIFIED
  53 + /// </summary>
  54 + public string AppliedPartnerType { get; set; } = "ALL";
50 } 55 }
51 56
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelMultipleOptionDbEntity.cs
@@ -34,5 +34,10 @@ public class FlLabelMultipleOptionDbEntity @@ -34,5 +34,10 @@ public class FlLabelMultipleOptionDbEntity
34 /// 门店可用范围:ALL / SPECIFIED 34 /// 门店可用范围:ALL / SPECIFIED
35 /// </summary> 35 /// </summary>
36 public string AvailabilityType { get; set; } = "ALL"; 36 public string AvailabilityType { get; set; } = "ALL";
  37 +
  38 + /// <summary>
  39 + /// 适用 Company 范围:ALL / SPECIFIED
  40 + /// </summary>
  41 + public string AppliedPartnerType { get; set; } = "ALL";
37 } 42 }
38 43
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTypeDbEntity.cs
@@ -32,5 +32,10 @@ public class FlLabelTypeDbEntity @@ -32,5 +32,10 @@ public class FlLabelTypeDbEntity
32 /// 门店可用范围:ALL / SPECIFIED 32 /// 门店可用范围:ALL / SPECIFIED
33 /// </summary> 33 /// </summary>
34 public string AvailabilityType { get; set; } = "ALL"; 34 public string AvailabilityType { get; set; } = "ALL";
  35 +
  36 + /// <summary>
  37 + /// 适用 Company 范围:ALL / SPECIFIED
  38 + /// </summary>
  39 + public string AppliedPartnerType { get; set; } = "ALL";
35 } 40 }
36 41
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelCategoryAppService.cs
@@ -176,6 +176,7 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ @@ -176,6 +176,7 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
176 State = input.State, 176 State = input.State,
177 ButtonAppearance = appearance, 177 ButtonAppearance = appearance,
178 AvailabilityType = availabilityType, 178 AvailabilityType = availabilityType,
  179 + AppliedPartnerType = partnerScope.AppliedPartnerType,
179 OrderNum = input.OrderNum 180 OrderNum = input.OrderNum
180 }; 181 };
181 182
@@ -219,6 +220,7 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ @@ -219,6 +220,7 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
219 entity.State = input.State; 220 entity.State = input.State;
220 entity.ButtonAppearance = appearance; 221 entity.ButtonAppearance = appearance;
221 entity.AvailabilityType = availabilityType; 222 entity.AvailabilityType = availabilityType;
  223 + entity.AppliedPartnerType = partnerScope.AppliedPartnerType;
222 entity.OrderNum = input.OrderNum; 224 entity.OrderNum = input.OrderNum;
223 entity.LastModificationTime = DateTime.Now; 225 entity.LastModificationTime = DateTime.Now;
224 entity.LastModifierId = CurrentUser?.Id?.ToString(); 226 entity.LastModifierId = CurrentUser?.Id?.ToString();
@@ -305,37 +307,14 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ @@ -305,37 +307,14 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
305 throw new UserFriendlyException("门店可用范围不合法(ALL/SPECIFIED)"); 307 throw new UserFriendlyException("门店可用范围不合法(ALL/SPECIFIED)");
306 } 308 }
307 309
308 - var partnerSpecified = string.Equals(  
309 - partnerScope.AppliedPartnerType,  
310 - LabelEntityPartnerScopeHelper.ScopeSpecified,  
311 - StringComparison.OrdinalIgnoreCase);  
312 var locationSpecified = string.Equals(availabilityType, "SPECIFIED", StringComparison.OrdinalIgnoreCase); 310 var locationSpecified = string.Equals(availabilityType, "SPECIFIED", StringComparison.OrdinalIgnoreCase);
  311 + var savedLocationIds = await LocationScopeBindingHelper.ResolveEntityLocationIdsForSaveAsync(
  312 + _dbContext.SqlSugarClient,
  313 + locationSpecified,
  314 + regionIds,
  315 + explicitLocationIds);
313 316
314 - if (partnerSpecified || locationSpecified)  
315 - {  
316 - var merged = await LocationScopeBindingHelper.MergeToLocationIdsAsync(  
317 - _dbContext.SqlSugarClient,  
318 - partnerSpecified ? partnerScope.PartnerIds : null,  
319 - locationSpecified ? regionIds : null,  
320 - locationSpecified ? explicitLocationIds : null);  
321 - if (merged.Count == 0)  
322 - {  
323 - throw new UserFriendlyException("指定适用范围后至少需要匹配到一个有效门店");  
324 - }  
325 -  
326 - if (locationSpecified)  
327 - {  
328 - await LocationScopeBindingHelper.ValidateLocationIdsExistAsync(_dbContext.SqlSugarClient, merged);  
329 - return (partnerScope, "SPECIFIED", merged);  
330 - }  
331 - }  
332 -  
333 - if (string.Equals(availabilityType, "ALL", StringComparison.OrdinalIgnoreCase))  
334 - {  
335 - return (partnerScope, "ALL", new List<string>());  
336 - }  
337 -  
338 - throw new UserFriendlyException("指定适用区域或门店时,至少需要匹配到一个有效门店"); 317 + return (partnerScope, locationSpecified ? "SPECIFIED" : "ALL", savedLocationIds);
339 } 318 }
340 319
341 private static List<string> NormalizeRegionIds(LabelCategoryCreateInputVo input) 320 private static List<string> NormalizeRegionIds(LabelCategoryCreateInputVo input)
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelMultipleOptionAppService.cs
@@ -148,6 +148,7 @@ public class LabelMultipleOptionAppService : ApplicationService, ILabelMultipleO @@ -148,6 +148,7 @@ public class LabelMultipleOptionAppService : ApplicationService, ILabelMultipleO
148 OptionValuesJson = input.OptionValuesJson?.Trim(), 148 OptionValuesJson = input.OptionValuesJson?.Trim(),
149 State = input.State, 149 State = input.State,
150 AvailabilityType = availabilityType, 150 AvailabilityType = availabilityType,
  151 + AppliedPartnerType = partnerScope.AppliedPartnerType,
151 OrderNum = input.OrderNum 152 OrderNum = input.OrderNum
152 }; 153 };
153 154
@@ -185,6 +186,7 @@ public class LabelMultipleOptionAppService : ApplicationService, ILabelMultipleO @@ -185,6 +186,7 @@ public class LabelMultipleOptionAppService : ApplicationService, ILabelMultipleO
185 entity.OptionValuesJson = input.OptionValuesJson?.Trim(); 186 entity.OptionValuesJson = input.OptionValuesJson?.Trim();
186 entity.State = input.State; 187 entity.State = input.State;
187 entity.AvailabilityType = availabilityType; 188 entity.AvailabilityType = availabilityType;
  189 + entity.AppliedPartnerType = partnerScope.AppliedPartnerType;
188 entity.OrderNum = input.OrderNum; 190 entity.OrderNum = input.OrderNum;
189 entity.LastModificationTime = DateTime.Now; 191 entity.LastModificationTime = DateTime.Now;
190 entity.LastModifierId = CurrentUser?.Id?.ToString(); 192 entity.LastModifierId = CurrentUser?.Id?.ToString();
@@ -252,37 +254,14 @@ public class LabelMultipleOptionAppService : ApplicationService, ILabelMultipleO @@ -252,37 +254,14 @@ public class LabelMultipleOptionAppService : ApplicationService, ILabelMultipleO
252 throw new UserFriendlyException("门店可用范围不合法(ALL/SPECIFIED)"); 254 throw new UserFriendlyException("门店可用范围不合法(ALL/SPECIFIED)");
253 } 255 }
254 256
255 - var partnerSpecified = string.Equals(  
256 - partnerScope.AppliedPartnerType,  
257 - LabelEntityPartnerScopeHelper.ScopeSpecified,  
258 - StringComparison.OrdinalIgnoreCase);  
259 var locationSpecified = string.Equals(availabilityType, "SPECIFIED", StringComparison.OrdinalIgnoreCase); 257 var locationSpecified = string.Equals(availabilityType, "SPECIFIED", StringComparison.OrdinalIgnoreCase);
  258 + var savedLocationIds = await LocationScopeBindingHelper.ResolveEntityLocationIdsForSaveAsync(
  259 + _dbContext.SqlSugarClient,
  260 + locationSpecified,
  261 + regionIds,
  262 + explicitLocationIds);
260 263
261 - if (partnerSpecified || locationSpecified)  
262 - {  
263 - var merged = await LocationScopeBindingHelper.MergeToLocationIdsAsync(  
264 - _dbContext.SqlSugarClient,  
265 - partnerSpecified ? partnerScope.PartnerIds : null,  
266 - locationSpecified ? regionIds : null,  
267 - locationSpecified ? explicitLocationIds : null);  
268 - if (merged.Count == 0)  
269 - {  
270 - throw new UserFriendlyException("指定适用范围后至少需要匹配到一个有效门店");  
271 - }  
272 -  
273 - if (locationSpecified)  
274 - {  
275 - await LocationScopeBindingHelper.ValidateLocationIdsExistAsync(_dbContext.SqlSugarClient, merged);  
276 - return (partnerScope, "SPECIFIED", merged);  
277 - }  
278 - }  
279 -  
280 - if (string.Equals(availabilityType, "ALL", StringComparison.OrdinalIgnoreCase))  
281 - {  
282 - return (partnerScope, "ALL", new List<string>());  
283 - }  
284 -  
285 - throw new UserFriendlyException("指定适用区域或门店时,至少需要匹配到一个有效门店"); 264 + return (partnerScope, locationSpecified ? "SPECIFIED" : "ALL", savedLocationIds);
286 } 265 }
287 266
288 private async Task SaveMultipleOptionPartnerScopeAsync( 267 private async Task SaveMultipleOptionPartnerScopeAsync(
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelTypeAppService.cs
@@ -155,6 +155,7 @@ public class LabelTypeAppService : ApplicationService, ILabelTypeAppService @@ -155,6 +155,7 @@ public class LabelTypeAppService : ApplicationService, ILabelTypeAppService
155 TypeName = name, 155 TypeName = name,
156 State = input.State, 156 State = input.State,
157 AvailabilityType = availabilityType, 157 AvailabilityType = availabilityType,
  158 + AppliedPartnerType = partnerScope.AppliedPartnerType,
158 OrderNum = input.OrderNum 159 OrderNum = input.OrderNum
159 }; 160 };
160 161
@@ -193,6 +194,7 @@ public class LabelTypeAppService : ApplicationService, ILabelTypeAppService @@ -193,6 +194,7 @@ public class LabelTypeAppService : ApplicationService, ILabelTypeAppService
193 entity.TypeName = name; 194 entity.TypeName = name;
194 entity.State = input.State; 195 entity.State = input.State;
195 entity.AvailabilityType = availabilityType; 196 entity.AvailabilityType = availabilityType;
  197 + entity.AppliedPartnerType = partnerScope.AppliedPartnerType;
196 entity.OrderNum = input.OrderNum; 198 entity.OrderNum = input.OrderNum;
197 entity.LastModificationTime = DateTime.Now; 199 entity.LastModificationTime = DateTime.Now;
198 entity.LastModifierId = CurrentUser?.Id?.ToString(); 200 entity.LastModifierId = CurrentUser?.Id?.ToString();
@@ -267,37 +269,14 @@ public class LabelTypeAppService : ApplicationService, ILabelTypeAppService @@ -267,37 +269,14 @@ public class LabelTypeAppService : ApplicationService, ILabelTypeAppService
267 throw new UserFriendlyException("门店可用范围不合法(ALL/SPECIFIED)"); 269 throw new UserFriendlyException("门店可用范围不合法(ALL/SPECIFIED)");
268 } 270 }
269 271
270 - var partnerSpecified = string.Equals(  
271 - partnerScope.AppliedPartnerType,  
272 - LabelEntityPartnerScopeHelper.ScopeSpecified,  
273 - StringComparison.OrdinalIgnoreCase);  
274 var locationSpecified = string.Equals(availabilityType, "SPECIFIED", StringComparison.OrdinalIgnoreCase); 272 var locationSpecified = string.Equals(availabilityType, "SPECIFIED", StringComparison.OrdinalIgnoreCase);
  273 + var savedLocationIds = await LocationScopeBindingHelper.ResolveEntityLocationIdsForSaveAsync(
  274 + _dbContext.SqlSugarClient,
  275 + locationSpecified,
  276 + regionIds,
  277 + explicitLocationIds);
275 278
276 - if (partnerSpecified || locationSpecified)  
277 - {  
278 - var merged = await LocationScopeBindingHelper.MergeToLocationIdsAsync(  
279 - _dbContext.SqlSugarClient,  
280 - partnerSpecified ? partnerScope.PartnerIds : null,  
281 - locationSpecified ? regionIds : null,  
282 - locationSpecified ? explicitLocationIds : null);  
283 - if (merged.Count == 0)  
284 - {  
285 - throw new UserFriendlyException("指定适用范围后至少需要匹配到一个有效门店");  
286 - }  
287 -  
288 - if (locationSpecified)  
289 - {  
290 - await LocationScopeBindingHelper.ValidateLocationIdsExistAsync(_dbContext.SqlSugarClient, merged);  
291 - return (partnerScope, "SPECIFIED", merged);  
292 - }  
293 - }  
294 -  
295 - if (string.Equals(availabilityType, "ALL", StringComparison.OrdinalIgnoreCase))  
296 - {  
297 - return (partnerScope, "ALL", new List<string>());  
298 - }  
299 -  
300 - throw new UserFriendlyException("指定适用区域或门店时,至少需要匹配到一个有效门店"); 279 + return (partnerScope, locationSpecified ? "SPECIFIED" : "ALL", savedLocationIds);
301 } 280 }
302 281
303 private async Task SaveTypePartnerScopeAsync( 282 private async Task SaveTypePartnerScopeAsync(