Commit 599d2940d301a21fc137276c5eaf3db9ae058364
Merge branch 'main' of http://39.98.150.180/wangming/Food-Labeling-Management-Platform
Showing
11 changed files
with
324 additions
and
14 deletions
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateCreateInputVo.cs
| @@ -57,5 +57,11 @@ public class LabelTemplateCreateInputVo | @@ -57,5 +57,11 @@ public class LabelTemplateCreateInputVo | ||
| 57 | /// </summary> | 57 | /// </summary> |
| 58 | [JsonPropertyName("appliedLocationIds")] | 58 | [JsonPropertyName("appliedLocationIds")] |
| 59 | public List<string> AppliedLocationIds { get; set; } = new(); | 59 | public List<string> AppliedLocationIds { get; set; } = new(); |
| 60 | + | ||
| 61 | + /// <summary> | ||
| 62 | + /// 模板与产品/标签类型绑定默认值 | ||
| 63 | + /// </summary> | ||
| 64 | + [JsonPropertyName("templateProductDefaults")] | ||
| 65 | + public List<LabelTemplateProductDefaultDto>? TemplateProductDefaults { get; set; } | ||
| 60 | } | 66 | } |
| 61 | 67 |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateElementDto.cs
| @@ -13,6 +13,9 @@ public class LabelTemplateElementDto | @@ -13,6 +13,9 @@ public class LabelTemplateElementDto | ||
| 13 | [JsonPropertyName("type")] | 13 | [JsonPropertyName("type")] |
| 14 | public string ElementType { get; set; } = string.Empty; | 14 | public string ElementType { get; set; } = string.Empty; |
| 15 | 15 | ||
| 16 | + [JsonPropertyName("elementName")] | ||
| 17 | + public string ElementName { get; set; } = string.Empty; | ||
| 18 | + | ||
| 16 | [JsonPropertyName("x")] | 19 | [JsonPropertyName("x")] |
| 17 | public decimal PosX { get; set; } | 20 | public decimal PosX { get; set; } |
| 18 | 21 |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateGetOutputDto.cs
| @@ -29,5 +29,7 @@ public class LabelTemplateGetOutputDto | @@ -29,5 +29,7 @@ public class LabelTemplateGetOutputDto | ||
| 29 | public List<LabelTemplateElementDto> Elements { get; set; } = new(); | 29 | public List<LabelTemplateElementDto> Elements { get; set; } = new(); |
| 30 | 30 | ||
| 31 | public List<string> AppliedLocationIds { get; set; } = new(); | 31 | public List<string> AppliedLocationIds { get; set; } = new(); |
| 32 | + | ||
| 33 | + public List<LabelTemplateProductDefaultDto> TemplateProductDefaults { get; set; } = new(); | ||
| 32 | } | 34 | } |
| 33 | 35 |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateProductDefaultDto.cs
0 → 100644
| 1 | +using System.Text.Json.Serialization; | ||
| 2 | + | ||
| 3 | +namespace FoodLabeling.Application.Contracts.Dtos.LabelTemplate; | ||
| 4 | + | ||
| 5 | +/// <summary> | ||
| 6 | +/// 模板与产品/标签类型绑定后的默认值行 | ||
| 7 | +/// </summary> | ||
| 8 | +public class LabelTemplateProductDefaultDto | ||
| 9 | +{ | ||
| 10 | + [JsonPropertyName("productId")] | ||
| 11 | + public string ProductId { get; set; } = string.Empty; | ||
| 12 | + | ||
| 13 | + [JsonPropertyName("labelTypeId")] | ||
| 14 | + public string LabelTypeId { get; set; } = string.Empty; | ||
| 15 | + | ||
| 16 | + /// <summary> | ||
| 17 | + /// 默认值JSON(建议结构:{ "el-xxx": "默认值" }) | ||
| 18 | + /// </summary> | ||
| 19 | + [JsonPropertyName("defaultValues")] | ||
| 20 | + public object? DefaultValues { get; set; } | ||
| 21 | + | ||
| 22 | + [JsonPropertyName("orderNum")] | ||
| 23 | + public int OrderNum { get; set; } | ||
| 24 | +} | ||
| 25 | + |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPreviewDto.cs
| 1 | +using System; | ||
| 2 | +using System.Collections.Generic; | ||
| 1 | using FoodLabeling.Application.Contracts.Dtos.Label; | 3 | using FoodLabeling.Application.Contracts.Dtos.Label; |
| 2 | 4 | ||
| 3 | namespace FoodLabeling.Application.Contracts.Dtos.UsAppLabeling; | 5 | namespace FoodLabeling.Application.Contracts.Dtos.UsAppLabeling; |
| @@ -7,6 +9,8 @@ namespace FoodLabeling.Application.Contracts.Dtos.UsAppLabeling; | @@ -7,6 +9,8 @@ namespace FoodLabeling.Application.Contracts.Dtos.UsAppLabeling; | ||
| 7 | /// </summary> | 9 | /// </summary> |
| 8 | public class UsAppLabelPreviewDto | 10 | public class UsAppLabelPreviewDto |
| 9 | { | 11 | { |
| 12 | + public string LabelId { get; set; } = string.Empty; | ||
| 13 | + | ||
| 10 | public string LocationId { get; set; } = string.Empty; | 14 | public string LocationId { get; set; } = string.Empty; |
| 11 | 15 | ||
| 12 | public string LabelCode { get; set; } = string.Empty; | 16 | public string LabelCode { get; set; } = string.Empty; |
| @@ -23,6 +27,8 @@ public class UsAppLabelPreviewDto | @@ -23,6 +27,8 @@ public class UsAppLabelPreviewDto | ||
| 23 | 27 | ||
| 24 | public string? LabelCategoryName { get; set; } | 28 | public string? LabelCategoryName { get; set; } |
| 25 | 29 | ||
| 30 | + public DateTime? LabelLastEdited { get; set; } | ||
| 31 | + | ||
| 26 | /// <summary> | 32 | /// <summary> |
| 27 | /// 预览图(base64 png,可空;若为空,客户端可用 Template 自行渲染) | 33 | /// 预览图(base64 png,可空;若为空,客户端可用 Template 自行渲染) |
| 28 | /// </summary> | 34 | /// </summary> |
| @@ -32,5 +38,11 @@ public class UsAppLabelPreviewDto | @@ -32,5 +38,11 @@ public class UsAppLabelPreviewDto | ||
| 32 | /// 预览模板结构(与 LabelCanvas/LabelPreviewOnly 结构尽量一致) | 38 | /// 预览模板结构(与 LabelCanvas/LabelPreviewOnly 结构尽量一致) |
| 33 | /// </summary> | 39 | /// </summary> |
| 34 | public LabelTemplatePreviewDto Template { get; set; } = new(); | 40 | public LabelTemplatePreviewDto Template { get; set; } = new(); |
| 41 | + | ||
| 42 | + /// <summary> | ||
| 43 | + /// 当前预览上下文(模板+产品+标签类型)命中的默认值配置。 | ||
| 44 | + /// 数据来源:fl_label_template_product_default.DefaultValuesJson | ||
| 45 | + /// </summary> | ||
| 46 | + public Dictionary<string, object?>? TemplateProductDefaultValues { get; set; } | ||
| 35 | } | 47 | } |
| 36 | 48 |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateElementDbEntity.cs
| @@ -14,6 +14,9 @@ public class FlLabelTemplateElementDbEntity | @@ -14,6 +14,9 @@ public class FlLabelTemplateElementDbEntity | ||
| 14 | 14 | ||
| 15 | public string ElementType { get; set; } = string.Empty; | 15 | public string ElementType { get; set; } = string.Empty; |
| 16 | 16 | ||
| 17 | + [SugarColumn(ColumnName = "ElementName")] | ||
| 18 | + public string ElementName { get; set; } = string.Empty; | ||
| 19 | + | ||
| 17 | public decimal PosX { get; set; } | 20 | public decimal PosX { get; set; } |
| 18 | 21 | ||
| 19 | public decimal PosY { get; set; } | 22 | public decimal PosY { get; set; } |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateProductDefaultDbEntity.cs
0 → 100644
| 1 | +using SqlSugar; | ||
| 2 | + | ||
| 3 | +namespace FoodLabeling.Application.Services.DbModels; | ||
| 4 | + | ||
| 5 | +[SugarTable("fl_label_template_product_default")] | ||
| 6 | +public class FlLabelTemplateProductDefaultDbEntity | ||
| 7 | +{ | ||
| 8 | + [SugarColumn(IsPrimaryKey = true)] | ||
| 9 | + public string Id { get; set; } = string.Empty; | ||
| 10 | + | ||
| 11 | + public string TemplateId { get; set; } = string.Empty; | ||
| 12 | + | ||
| 13 | + public string ProductId { get; set; } = string.Empty; | ||
| 14 | + | ||
| 15 | + public string LabelTypeId { get; set; } = string.Empty; | ||
| 16 | + | ||
| 17 | + /// <summary> | ||
| 18 | + /// 默认值JSON(字符串保存) | ||
| 19 | + /// </summary> | ||
| 20 | + public string? DefaultValuesJson { get; set; } | ||
| 21 | + | ||
| 22 | + public int OrderNum { get; set; } | ||
| 23 | +} | ||
| 24 | + |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelAppService.cs
| @@ -695,6 +695,7 @@ public class LabelAppService : ApplicationService, ILabelAppService | @@ -695,6 +695,7 @@ public class LabelAppService : ApplicationService, ILabelAppService | ||
| 695 | { | 695 | { |
| 696 | Id = el.ElementKey, | 696 | Id = el.ElementKey, |
| 697 | ElementType = el.ElementType, | 697 | ElementType = el.ElementType, |
| 698 | + ElementName = el.ElementName, | ||
| 698 | PosX = el.PosX, | 699 | PosX = el.PosX, |
| 699 | PosY = el.PosY, | 700 | PosY = el.PosY, |
| 700 | Width = el.Width, | 701 | Width = el.Width, |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelTemplateAppService.cs
| @@ -157,6 +157,7 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ | @@ -157,6 +157,7 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ | ||
| 157 | { | 157 | { |
| 158 | Id = e.ElementKey, | 158 | Id = e.ElementKey, |
| 159 | ElementType = e.ElementType, | 159 | ElementType = e.ElementType, |
| 160 | + ElementName = e.ElementName, | ||
| 160 | PosX = e.PosX, | 161 | PosX = e.PosX, |
| 161 | PosY = e.PosY, | 162 | PosY = e.PosY, |
| 162 | Width = e.Width, | 163 | Width = e.Width, |
| @@ -180,6 +181,28 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ | @@ -180,6 +181,28 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ | ||
| 180 | .Select(x => x.LocationId) | 181 | .Select(x => x.LocationId) |
| 181 | .ToListAsync(); | 182 | .ToListAsync(); |
| 182 | 183 | ||
| 184 | + var defaultRows = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateProductDefaultDbEntity>() | ||
| 185 | + .Where(x => x.TemplateId == template.Id) | ||
| 186 | + .OrderBy(x => x.OrderNum) | ||
| 187 | + .ToListAsync(); | ||
| 188 | + | ||
| 189 | + var productDefaults = defaultRows.Select(x => | ||
| 190 | + { | ||
| 191 | + object? defaults = null; | ||
| 192 | + if (!string.IsNullOrWhiteSpace(x.DefaultValuesJson)) | ||
| 193 | + { | ||
| 194 | + defaults = JsonSerializer.Deserialize<object>(x.DefaultValuesJson); | ||
| 195 | + } | ||
| 196 | + | ||
| 197 | + return new LabelTemplateProductDefaultDto | ||
| 198 | + { | ||
| 199 | + ProductId = x.ProductId, | ||
| 200 | + LabelTypeId = x.LabelTypeId, | ||
| 201 | + DefaultValues = defaults, | ||
| 202 | + OrderNum = x.OrderNum | ||
| 203 | + }; | ||
| 204 | + }).ToList(); | ||
| 205 | + | ||
| 183 | return new LabelTemplateGetOutputDto | 206 | return new LabelTemplateGetOutputDto |
| 184 | { | 207 | { |
| 185 | Id = template.TemplateCode, | 208 | Id = template.TemplateCode, |
| @@ -195,7 +218,8 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ | @@ -195,7 +218,8 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ | ||
| 195 | VersionNo = template.VersionNo, | 218 | VersionNo = template.VersionNo, |
| 196 | State = template.State, | 219 | State = template.State, |
| 197 | Elements = MapElements(), | 220 | Elements = MapElements(), |
| 198 | - AppliedLocationIds = appliedLocationIds | 221 | + AppliedLocationIds = appliedLocationIds, |
| 222 | + TemplateProductDefaults = productDefaults | ||
| 199 | }; | 223 | }; |
| 200 | } | 224 | } |
| 201 | 225 | ||
| @@ -246,7 +270,12 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ | @@ -246,7 +270,12 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ | ||
| 246 | 270 | ||
| 247 | await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync(); | 271 | await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync(); |
| 248 | 272 | ||
| 249 | - await RebuildTemplateElementsAndLocationsAsync(entity.Id, input.Elements, entity.AppliedLocationType, input.AppliedLocationIds); | 273 | + await RebuildTemplateElementsLocationsAndDefaultsAsync( |
| 274 | + entity.Id, | ||
| 275 | + input.Elements, | ||
| 276 | + entity.AppliedLocationType, | ||
| 277 | + input.AppliedLocationIds, | ||
| 278 | + new List<LabelTemplateProductDefaultDto>()); | ||
| 250 | 279 | ||
| 251 | return await GetAsync(code); | 280 | return await GetAsync(code); |
| 252 | } | 281 | } |
| @@ -292,7 +321,12 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ | @@ -292,7 +321,12 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ | ||
| 292 | 321 | ||
| 293 | await _dbContext.SqlSugarClient.Updateable(template).ExecuteCommandAsync(); | 322 | await _dbContext.SqlSugarClient.Updateable(template).ExecuteCommandAsync(); |
| 294 | 323 | ||
| 295 | - await RebuildTemplateElementsAndLocationsAsync(template.Id, input.Elements, template.AppliedLocationType, input.AppliedLocationIds); | 324 | + await RebuildTemplateElementsLocationsAndDefaultsAsync( |
| 325 | + template.Id, | ||
| 326 | + input.Elements, | ||
| 327 | + template.AppliedLocationType, | ||
| 328 | + input.AppliedLocationIds, | ||
| 329 | + input.TemplateProductDefaults); | ||
| 296 | 330 | ||
| 297 | return await GetAsync(template.TemplateCode); | 331 | return await GetAsync(template.TemplateCode); |
| 298 | } | 332 | } |
| @@ -327,13 +361,17 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ | @@ -327,13 +361,17 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ | ||
| 327 | await _dbContext.SqlSugarClient.Deleteable<FlLabelTemplateLocationDbEntity>() | 361 | await _dbContext.SqlSugarClient.Deleteable<FlLabelTemplateLocationDbEntity>() |
| 328 | .Where(x => x.TemplateId == template.Id) | 362 | .Where(x => x.TemplateId == template.Id) |
| 329 | .ExecuteCommandAsync(); | 363 | .ExecuteCommandAsync(); |
| 364 | + await _dbContext.SqlSugarClient.Deleteable<FlLabelTemplateProductDefaultDbEntity>() | ||
| 365 | + .Where(x => x.TemplateId == template.Id) | ||
| 366 | + .ExecuteCommandAsync(); | ||
| 330 | } | 367 | } |
| 331 | 368 | ||
| 332 | - private async Task RebuildTemplateElementsAndLocationsAsync( | 369 | + private async Task RebuildTemplateElementsLocationsAndDefaultsAsync( |
| 333 | string templateDbId, | 370 | string templateDbId, |
| 334 | List<LabelTemplateElementDto> elements, | 371 | List<LabelTemplateElementDto> elements, |
| 335 | string appliedLocationType, | 372 | string appliedLocationType, |
| 336 | - List<string> appliedLocationIds) | 373 | + List<string> appliedLocationIds, |
| 374 | + List<LabelTemplateProductDefaultDto>? templateProductDefaults) | ||
| 337 | { | 375 | { |
| 338 | // elements 重建 | 376 | // elements 重建 |
| 339 | await _dbContext.SqlSugarClient.Deleteable<FlLabelTemplateElementDbEntity>() | 377 | await _dbContext.SqlSugarClient.Deleteable<FlLabelTemplateElementDbEntity>() |
| @@ -342,9 +380,9 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ | @@ -342,9 +380,9 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ | ||
| 342 | 380 | ||
| 343 | if (elements is not null && elements.Count > 0) | 381 | if (elements is not null && elements.Count > 0) |
| 344 | { | 382 | { |
| 345 | - var now = DateTime.Now; | ||
| 346 | var rows = elements.Select(e => | 383 | var rows = elements.Select(e => |
| 347 | { | 384 | { |
| 385 | + var elementName = EnsureElementName(e.ElementName); | ||
| 348 | object? cfg = e.ConfigJson; | 386 | object? cfg = e.ConfigJson; |
| 349 | var configJson = cfg == null ? null : JsonSerializer.Serialize(cfg); | 387 | var configJson = cfg == null ? null : JsonSerializer.Serialize(cfg); |
| 350 | return new FlLabelTemplateElementDbEntity | 388 | return new FlLabelTemplateElementDbEntity |
| @@ -353,6 +391,7 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ | @@ -353,6 +391,7 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ | ||
| 353 | TemplateId = templateDbId, | 391 | TemplateId = templateDbId, |
| 354 | ElementKey = e.Id, | 392 | ElementKey = e.Id, |
| 355 | ElementType = e.ElementType, | 393 | ElementType = e.ElementType, |
| 394 | + ElementName = elementName, | ||
| 356 | PosX = e.PosX, | 395 | PosX = e.PosX, |
| 357 | PosY = e.PosY, | 396 | PosY = e.PosY, |
| 358 | Width = e.Width, | 397 | Width = e.Width, |
| @@ -395,6 +434,73 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ | @@ -395,6 +434,73 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ | ||
| 395 | 434 | ||
| 396 | await _dbContext.SqlSugarClient.Insertable(locRows).ExecuteCommandAsync(); | 435 | await _dbContext.SqlSugarClient.Insertable(locRows).ExecuteCommandAsync(); |
| 397 | } | 436 | } |
| 437 | + | ||
| 438 | + // 模板-产品-标签类型默认值:仅在显式传入时重建,避免普通编辑误清空 | ||
| 439 | + if (templateProductDefaults is not null) | ||
| 440 | + { | ||
| 441 | + var duplicateCheckSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); | ||
| 442 | + foreach (var row in templateProductDefaults) | ||
| 443 | + { | ||
| 444 | + var productId = row.ProductId?.Trim(); | ||
| 445 | + var labelTypeId = row.LabelTypeId?.Trim(); | ||
| 446 | + if (string.IsNullOrWhiteSpace(productId) || string.IsNullOrWhiteSpace(labelTypeId)) | ||
| 447 | + { | ||
| 448 | + continue; | ||
| 449 | + } | ||
| 450 | + | ||
| 451 | + var key = $"{productId}::{labelTypeId}"; | ||
| 452 | + if (!duplicateCheckSet.Add(key)) | ||
| 453 | + { | ||
| 454 | + throw new UserFriendlyException($"模板默认值绑定重复:产品[{productId}]与标签类型[{labelTypeId}]只能存在一条"); | ||
| 455 | + } | ||
| 456 | + } | ||
| 457 | + | ||
| 458 | + await _dbContext.SqlSugarClient.Deleteable<FlLabelTemplateProductDefaultDbEntity>() | ||
| 459 | + .Where(x => x.TemplateId == templateDbId) | ||
| 460 | + .ExecuteCommandAsync(); | ||
| 461 | + | ||
| 462 | + if (templateProductDefaults.Count > 0) | ||
| 463 | + { | ||
| 464 | + var rows = templateProductDefaults.Select((x, idx) => | ||
| 465 | + { | ||
| 466 | + var productId = x.ProductId?.Trim(); | ||
| 467 | + var labelTypeId = x.LabelTypeId?.Trim(); | ||
| 468 | + if (string.IsNullOrWhiteSpace(productId)) | ||
| 469 | + { | ||
| 470 | + throw new UserFriendlyException("模板默认值绑定中,产品Id不能为空"); | ||
| 471 | + } | ||
| 472 | + | ||
| 473 | + if (string.IsNullOrWhiteSpace(labelTypeId)) | ||
| 474 | + { | ||
| 475 | + throw new UserFriendlyException("模板默认值绑定中,标签类型Id不能为空"); | ||
| 476 | + } | ||
| 477 | + | ||
| 478 | + var json = x.DefaultValues is null ? null : JsonSerializer.Serialize(x.DefaultValues); | ||
| 479 | + return new FlLabelTemplateProductDefaultDbEntity | ||
| 480 | + { | ||
| 481 | + Id = _guidGenerator.Create().ToString(), | ||
| 482 | + TemplateId = templateDbId, | ||
| 483 | + ProductId = productId, | ||
| 484 | + LabelTypeId = labelTypeId, | ||
| 485 | + DefaultValuesJson = json, | ||
| 486 | + OrderNum = x.OrderNum <= 0 ? idx + 1 : x.OrderNum | ||
| 487 | + }; | ||
| 488 | + }).ToList(); | ||
| 489 | + | ||
| 490 | + await _dbContext.SqlSugarClient.Insertable(rows).ExecuteCommandAsync(); | ||
| 491 | + } | ||
| 492 | + } | ||
| 493 | + } | ||
| 494 | + | ||
| 495 | + private static string EnsureElementName(string? elementName) | ||
| 496 | + { | ||
| 497 | + var normalizedName = elementName?.Trim(); | ||
| 498 | + if (string.IsNullOrWhiteSpace(normalizedName)) | ||
| 499 | + { | ||
| 500 | + throw new UserFriendlyException("组件名字不能为空"); | ||
| 501 | + } | ||
| 502 | + | ||
| 503 | + return normalizedName; | ||
| 398 | } | 504 | } |
| 399 | 505 | ||
| 400 | private static PagedResultWithPageDto<T> BuildPagedResult<T>(int skipCount, int maxResultCount, int total, List<T> items) | 506 | private static PagedResultWithPageDto<T> BuildPagedResult<T>(int skipCount, int maxResultCount, int total, List<T> items) |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppLabelingAppService.cs
| @@ -238,8 +238,13 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ | @@ -238,8 +238,13 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ | ||
| 238 | .Where((l, c, t, tpl) => l.LabelCode == labelCode) | 238 | .Where((l, c, t, tpl) => l.LabelCode == labelCode) |
| 239 | .Select((l, c, t, tpl) => new | 239 | .Select((l, c, t, tpl) => new |
| 240 | { | 240 | { |
| 241 | + l.Id, | ||
| 241 | l.LabelCode, | 242 | l.LabelCode, |
| 242 | l.LocationId, | 243 | l.LocationId, |
| 244 | + l.LabelTypeId, | ||
| 245 | + l.TemplateId, | ||
| 246 | + l.LastModificationTime, | ||
| 247 | + l.CreationTime, | ||
| 243 | LabelCategoryName = c.CategoryName, | 248 | LabelCategoryName = c.CategoryName, |
| 244 | TypeName = t.TypeName, | 249 | TypeName = t.TypeName, |
| 245 | TemplateCode = tpl.TemplateCode, | 250 | TemplateCode = tpl.TemplateCode, |
| @@ -259,21 +264,46 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ | @@ -259,21 +264,46 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ | ||
| 259 | throw new UserFriendlyException("该标签不属于当前门店"); | 264 | throw new UserFriendlyException("该标签不属于当前门店"); |
| 260 | } | 265 | } |
| 261 | 266 | ||
| 267 | + var previewProductId = await ResolvePreviewProductIdAsync(labelRow.Id, input.ProductId); | ||
| 268 | + | ||
| 262 | var template = await _labelAppService.PreviewAsync(new LabelPreviewResolveInputVo | 269 | var template = await _labelAppService.PreviewAsync(new LabelPreviewResolveInputVo |
| 263 | { | 270 | { |
| 264 | LabelCode = labelCode, | 271 | LabelCode = labelCode, |
| 265 | - ProductId = input.ProductId?.Trim(), | 272 | + ProductId = previewProductId, |
| 266 | BaseTime = input.BaseTime, | 273 | BaseTime = input.BaseTime, |
| 267 | PrintInputJson = input.PrintInputJson?.ToDictionary(x => x.Key, x => (object?)x.Value) | 274 | PrintInputJson = input.PrintInputJson?.ToDictionary(x => x.Key, x => (object?)x.Value) |
| 268 | }); | 275 | }); |
| 269 | 276 | ||
| 277 | + Dictionary<string, object?>? templateProductDefaultValues = null; | ||
| 278 | + if (!string.IsNullOrWhiteSpace(previewProductId)) | ||
| 279 | + { | ||
| 280 | + var productDefault = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateProductDefaultDbEntity>() | ||
| 281 | + .Where(x => x.TemplateId == labelRow.TemplateId) | ||
| 282 | + .Where(x => x.ProductId == previewProductId) | ||
| 283 | + .Where(x => x.LabelTypeId == labelRow.LabelTypeId) | ||
| 284 | + .OrderBy(x => x.OrderNum) | ||
| 285 | + .FirstAsync(); | ||
| 286 | + | ||
| 287 | + if (!string.IsNullOrWhiteSpace(productDefault?.DefaultValuesJson)) | ||
| 288 | + { | ||
| 289 | + try | ||
| 290 | + { | ||
| 291 | + templateProductDefaultValues = | ||
| 292 | + JsonSerializer.Deserialize<Dictionary<string, object?>>(productDefault.DefaultValuesJson!); | ||
| 293 | + } | ||
| 294 | + catch | ||
| 295 | + { | ||
| 296 | + templateProductDefaultValues = null; | ||
| 297 | + } | ||
| 298 | + } | ||
| 299 | + } | ||
| 300 | + | ||
| 270 | var productName = string.Empty; | 301 | var productName = string.Empty; |
| 271 | var productCategoryName = "无"; | 302 | var productCategoryName = "无"; |
| 272 | - if (!string.IsNullOrWhiteSpace(input.ProductId)) | 303 | + if (!string.IsNullOrWhiteSpace(previewProductId)) |
| 273 | { | 304 | { |
| 274 | - var pid = input.ProductId.Trim(); | ||
| 275 | var p = await _dbContext.SqlSugarClient.Queryable<FlProductDbEntity>() | 305 | var p = await _dbContext.SqlSugarClient.Queryable<FlProductDbEntity>() |
| 276 | - .FirstAsync(x => !x.IsDeleted && x.State && x.Id == pid); | 306 | + .FirstAsync(x => !x.IsDeleted && x.State && x.Id == previewProductId); |
| 277 | if (p is not null) | 307 | if (p is not null) |
| 278 | { | 308 | { |
| 279 | productName = p.ProductName ?? string.Empty; | 309 | productName = p.ProductName ?? string.Empty; |
| @@ -288,6 +318,7 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ | @@ -288,6 +318,7 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ | ||
| 288 | 318 | ||
| 289 | return new UsAppLabelPreviewDto | 319 | return new UsAppLabelPreviewDto |
| 290 | { | 320 | { |
| 321 | + LabelId = labelRow.Id, | ||
| 291 | LocationId = locationId, | 322 | LocationId = locationId, |
| 292 | LabelCode = labelCode, | 323 | LabelCode = labelCode, |
| 293 | TemplateCode = labelRow.TemplateCode, | 324 | TemplateCode = labelRow.TemplateCode, |
| @@ -296,8 +327,10 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ | @@ -296,8 +327,10 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ | ||
| 296 | ProductName = string.IsNullOrWhiteSpace(productName) ? null : productName, | 327 | ProductName = string.IsNullOrWhiteSpace(productName) ? null : productName, |
| 297 | ProductCategoryName = productCategoryName, | 328 | ProductCategoryName = productCategoryName, |
| 298 | LabelCategoryName = labelRow.LabelCategoryName, | 329 | LabelCategoryName = labelRow.LabelCategoryName, |
| 330 | + LabelLastEdited = labelRow.LastModificationTime ?? labelRow.CreationTime, | ||
| 299 | PreviewImageBase64Png = null, | 331 | PreviewImageBase64Png = null, |
| 300 | - Template = template | 332 | + Template = template, |
| 333 | + TemplateProductDefaultValues = templateProductDefaultValues | ||
| 301 | }; | 334 | }; |
| 302 | } | 335 | } |
| 303 | 336 | ||
| @@ -525,6 +558,20 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ | @@ -525,6 +558,20 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ | ||
| 525 | return string.IsNullOrWhiteSpace(s) ? null : s; | 558 | return string.IsNullOrWhiteSpace(s) ? null : s; |
| 526 | } | 559 | } |
| 527 | 560 | ||
| 561 | + private async Task<string?> ResolvePreviewProductIdAsync(string labelId, string? productId) | ||
| 562 | + { | ||
| 563 | + var resolvedProductId = productId?.Trim(); | ||
| 564 | + if (!string.IsNullOrWhiteSpace(resolvedProductId)) | ||
| 565 | + { | ||
| 566 | + return resolvedProductId; | ||
| 567 | + } | ||
| 568 | + | ||
| 569 | + return await _dbContext.SqlSugarClient.Queryable<FlLabelProductDbEntity>() | ||
| 570 | + .Where(x => x.LabelId == labelId) | ||
| 571 | + .Select(x => x.ProductId) | ||
| 572 | + .FirstAsync(); | ||
| 573 | + } | ||
| 574 | + | ||
| 528 | private static UsAppLabelTypeNodeDto BuildLabelTypeNode(LabelingTreeRow r) | 575 | private static UsAppLabelTypeNodeDto BuildLabelTypeNode(LabelingTreeRow r) |
| 529 | { | 576 | { |
| 530 | return new UsAppLabelTypeNodeDto | 577 | return new UsAppLabelTypeNodeDto |
项目相关文档/标签模块接口对接说明.md
| @@ -236,6 +236,9 @@ Swagger 地址: | @@ -236,6 +236,9 @@ Swagger 地址: | ||
| 236 | 说明: | 236 | 说明: |
| 237 | - 模板标识入参 `id` 使用 `fl_label_template.TemplateCode`。 | 237 | - 模板标识入参 `id` 使用 `fl_label_template.TemplateCode`。 |
| 238 | - 创建/编辑的 Body 字段名对齐你前端 editor JSON(`id/name/appliedLocation/elements/config`)。 | 238 | - 创建/编辑的 Body 字段名对齐你前端 editor JSON(`id/name/appliedLocation/elements/config`)。 |
| 239 | +- 模板组件 `elements[]` 的 `elementName` 为必填(前端传值,后端校验为空会报错)。 | ||
| 240 | +- `templateProductDefaults[]` 用于模板内“产品 + 标签类型”绑定默认值(进入模板详情页后的绑定列表)。 | ||
| 241 | +- **新增模板时不处理默认值**;默认值仅在后续“产品关联/编辑模板”阶段维护。 | ||
| 239 | 242 | ||
| 240 | ### 4.1 分页列表 | 243 | ### 4.1 分页列表 |
| 241 | 244 | ||
| @@ -283,6 +286,7 @@ Swagger 地址: | @@ -283,6 +286,7 @@ Swagger 地址: | ||
| 283 | "elements": [ | 286 | "elements": [ |
| 284 | { | 287 | { |
| 285 | "id": "el-fixed-title", | 288 | "id": "el-fixed-title", |
| 289 | + "elementName": "标题文本", | ||
| 286 | "type": "TEXT_STATIC", | 290 | "type": "TEXT_STATIC", |
| 287 | "x": 32, | 291 | "x": 32, |
| 288 | "y": 24, | 292 | "y": 24, |
| @@ -309,6 +313,8 @@ Swagger 地址: | @@ -309,6 +313,8 @@ Swagger 地址: | ||
| 309 | 313 | ||
| 310 | 说明: | 314 | 说明: |
| 311 | - 当 `appliedLocation=SPECIFIED` 时,`appliedLocationIds` 必须至少选择一个门店。 | 315 | - 当 `appliedLocation=SPECIFIED` 时,`appliedLocationIds` 必须至少选择一个门店。 |
| 316 | +- `elements[].elementName` 必填;为空或空白将返回友好错误:`组件名字不能为空`。 | ||
| 317 | +- 新增模板时即使传了 `templateProductDefaults`,后端也不会写入默认值数据。 | ||
| 312 | 318 | ||
| 313 | ### 4.4 编辑模板 | 319 | ### 4.4 编辑模板 |
| 314 | 320 | ||
| @@ -332,14 +338,84 @@ Swagger 地址: | @@ -332,14 +338,84 @@ Swagger 地址: | ||
| 332 | "showRuler": true, | 338 | "showRuler": true, |
| 333 | "showGrid": true, | 339 | "showGrid": true, |
| 334 | "state": true, | 340 | "state": true, |
| 335 | - "elements": [], | ||
| 336 | - "appliedLocationIds": ["11111111-1111-1111-1111-111111111111"] | 341 | + "elements": [ |
| 342 | + { | ||
| 343 | + "id": "el-price", | ||
| 344 | + "elementName": "价格文本", | ||
| 345 | + "type": "TEXT_PRICE", | ||
| 346 | + "x": 40, | ||
| 347 | + "y": 120, | ||
| 348 | + "width": 140, | ||
| 349 | + "height": 28, | ||
| 350 | + "rotation": "horizontal", | ||
| 351 | + "border": "none", | ||
| 352 | + "zIndex": 2, | ||
| 353 | + "orderNum": 2, | ||
| 354 | + "valueSourceType": "PRINT_INPUT", | ||
| 355 | + "inputKey": "price", | ||
| 356 | + "isRequiredInput": true, | ||
| 357 | + "config": { | ||
| 358 | + "text": "", | ||
| 359 | + "fontFamily": "Arial", | ||
| 360 | + "fontSize": 18, | ||
| 361 | + "fontWeight": "bold", | ||
| 362 | + "textAlign": "left" | ||
| 363 | + } | ||
| 364 | + } | ||
| 365 | + ], | ||
| 366 | + "appliedLocationIds": ["11111111-1111-1111-1111-111111111111"], | ||
| 367 | + "templateProductDefaults": [ | ||
| 368 | + { | ||
| 369 | + "productId": "3a20-xxxx", | ||
| 370 | + "labelTypeId": "3a20-yyyy", | ||
| 371 | + "defaultValues": { | ||
| 372 | + "el-fixed-title": "Chicken", | ||
| 373 | + "el-price": "2.00", | ||
| 374 | + "el-desc": "23" | ||
| 375 | + }, | ||
| 376 | + "orderNum": 1 | ||
| 377 | + } | ||
| 378 | + ] | ||
| 337 | } | 379 | } |
| 338 | ``` | 380 | ``` |
| 339 | 381 | ||
| 340 | 版本: | 382 | 版本: |
| 341 | - `VersionNo` 会在编辑时自动 `+1`。 | 383 | - `VersionNo` 会在编辑时自动 `+1`。 |
| 342 | - `elements` 会按传入内容全量重建。 | 384 | - `elements` 会按传入内容全量重建。 |
| 385 | +- 查询/预览返回的 `elements[]` 同样会带 `elementName` 字段。 | ||
| 386 | +- `templateProductDefaults` 在编辑接口中**仅当显式传入时**才会重建(同一模板先删后插)。 | ||
| 387 | +- 若编辑时不传 `templateProductDefaults`,后端会保留数据库中原有默认值,不做覆盖。 | ||
| 388 | + | ||
| 389 | +`templateProductDefaults` 结构说明: | ||
| 390 | +- 每一行需传 `productId` 与 `labelTypeId`。 | ||
| 391 | +- `defaultValues` 建议使用 `element.id => 默认文本` 结构;页面展示时可结合模板 `elements[].config.text` 作为列头与初始值。 | ||
| 392 | + | ||
| 393 | +### 4.6 模板与产品默认值关联表(新增) | ||
| 394 | + | ||
| 395 | +用于存储“模板-产品-标签类型”的默认值,推荐执行以下建表 SQL: | ||
| 396 | + | ||
| 397 | +```sql | ||
| 398 | +CREATE TABLE `fl_label_template_product_default` ( | ||
| 399 | + `Id` varchar(36) NOT NULL COMMENT '主键', | ||
| 400 | + `TemplateId` varchar(36) NOT NULL COMMENT '模板Id(关联 fl_label_template.Id)', | ||
| 401 | + `ProductId` varchar(36) NOT NULL COMMENT '产品Id(关联 fl_product.Id)', | ||
| 402 | + `LabelTypeId` varchar(36) NOT NULL COMMENT '标签类型Id(关联 fl_label_type.Id)', | ||
| 403 | + `DefaultValuesJson` text NULL COMMENT '默认值JSON(如 elementId=>默认文本)', | ||
| 404 | + `OrderNum` int NOT NULL DEFAULT 1 COMMENT '排序', | ||
| 405 | + PRIMARY KEY (`Id`), | ||
| 406 | + KEY `idx_fl_ltpd_template` (`TemplateId`), | ||
| 407 | + KEY `idx_fl_ltpd_product` (`ProductId`), | ||
| 408 | + KEY `idx_fl_ltpd_label_type` (`LabelTypeId`), | ||
| 409 | + UNIQUE KEY `uk_fl_ltpd_template_product_label_type` (`TemplateId`, `ProductId`, `LabelTypeId`) | ||
| 410 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='标签模板-产品默认值关联表'; | ||
| 411 | +``` | ||
| 412 | + | ||
| 413 | +若表已存在,可执行以下 SQL 增加唯一约束: | ||
| 414 | + | ||
| 415 | +```sql | ||
| 416 | +ALTER TABLE `fl_label_template_product_default` | ||
| 417 | +ADD UNIQUE KEY `uk_fl_ltpd_template_product_label_type` (`TemplateId`, `ProductId`, `LabelTypeId`); | ||
| 418 | +``` | ||
| 343 | 419 | ||
| 344 | ### 4.5 删除(逻辑删除) | 420 | ### 4.5 删除(逻辑删除) |
| 345 | 421 | ||
| @@ -758,7 +834,12 @@ curl -X GET "http://localhost:19001/api/app/us-app-labeling/labeling-tree?locati | @@ -758,7 +834,12 @@ curl -X GET "http://localhost:19001/api/app/us-app-labeling/labeling-tree?locati | ||
| 758 | 834 | ||
| 759 | - `template`:`LabelTemplatePreviewDto` | 835 | - `template`:`LabelTemplatePreviewDto` |
| 760 | - `width` / `height` / `unit`:模板物理尺寸 | 836 | - `width` / `height` / `unit`:模板物理尺寸 |
| 761 | - - `elements[]`:元素数组(对齐前端 editor JSON:`id/type/x/y/width/height/rotation/border/zIndex/orderNum/config`) | 837 | + - `elements[]`:元素数组(对齐前端 editor JSON:`id/elementName/type/x/y/width/height/rotation/border/zIndex/orderNum/config`) |
| 838 | +- `templateProductDefaultValues`:`object | null` | ||
| 839 | + - 来源:`fl_label_template_product_default.DefaultValuesJson` | ||
| 840 | + - 命中条件:当前预览上下文的 `templateId + productId + labelTypeId` | ||
| 841 | + - 建议结构:`{ "elementId": "默认值" }` | ||
| 842 | + - 未命中时返回 `null`(向后兼容) | ||
| 762 | 843 | ||
| 763 | `elements[].config` 内常用字段(示例): | 844 | `elements[].config` 内常用字段(示例): |
| 764 | 845 |