From a4baaa73a80394707b9437478b205c4ed87de901 Mon Sep 17 00:00:00 2001 From: 李曜臣 Date: Mon, 30 Mar 2026 14:57:58 +0800 Subject: [PATCH] 模板与产品关联实现 --- 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateCreateInputVo.cs | 6 ++++++ 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateElementDto.cs | 3 +++ 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateGetOutputDto.cs | 2 ++ 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateProductDefaultDto.cs | 25 +++++++++++++++++++++++++ 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPreviewDto.cs | 12 ++++++++++++ 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateElementDbEntity.cs | 3 +++ 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateProductDefaultDbEntity.cs | 24 ++++++++++++++++++++++++ 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelAppService.cs | 1 + 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelTemplateAppService.cs | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppLabelingAppService.cs | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 项目相关文档/标签模块接口对接说明.md | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 11 files changed, 324 insertions(+), 14 deletions(-) create mode 100644 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateProductDefaultDto.cs create mode 100644 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateProductDefaultDbEntity.cs diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateCreateInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateCreateInputVo.cs index 1a98451..cc34717 100644 --- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateCreateInputVo.cs +++ b/美国版/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 /// [JsonPropertyName("appliedLocationIds")] public List AppliedLocationIds { get; set; } = new(); + + /// + /// 模板与产品/标签类型绑定默认值 + /// + [JsonPropertyName("templateProductDefaults")] + public List? TemplateProductDefaults { get; set; } } diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateElementDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateElementDto.cs index b1a8bf3..4e24238 100644 --- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateElementDto.cs +++ b/美国版/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 [JsonPropertyName("type")] public string ElementType { get; set; } = string.Empty; + [JsonPropertyName("elementName")] + public string ElementName { get; set; } = string.Empty; + [JsonPropertyName("x")] public decimal PosX { get; set; } diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateGetOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateGetOutputDto.cs index 1187b11..b72cd37 100644 --- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateGetOutputDto.cs +++ b/美国版/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 public List Elements { get; set; } = new(); public List AppliedLocationIds { get; set; } = new(); + + public List TemplateProductDefaults { get; set; } = new(); } diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateProductDefaultDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateProductDefaultDto.cs new file mode 100644 index 0000000..1f4322b --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateProductDefaultDto.cs @@ -0,0 +1,25 @@ +using System.Text.Json.Serialization; + +namespace FoodLabeling.Application.Contracts.Dtos.LabelTemplate; + +/// +/// 模板与产品/标签类型绑定后的默认值行 +/// +public class LabelTemplateProductDefaultDto +{ + [JsonPropertyName("productId")] + public string ProductId { get; set; } = string.Empty; + + [JsonPropertyName("labelTypeId")] + public string LabelTypeId { get; set; } = string.Empty; + + /// + /// 默认值JSON(建议结构:{ "el-xxx": "默认值" }) + /// + [JsonPropertyName("defaultValues")] + public object? DefaultValues { get; set; } + + [JsonPropertyName("orderNum")] + public int OrderNum { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPreviewDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPreviewDto.cs index b9206a5..12a4f26 100644 --- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPreviewDto.cs +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPreviewDto.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Generic; using FoodLabeling.Application.Contracts.Dtos.Label; namespace FoodLabeling.Application.Contracts.Dtos.UsAppLabeling; @@ -7,6 +9,8 @@ namespace FoodLabeling.Application.Contracts.Dtos.UsAppLabeling; /// public class UsAppLabelPreviewDto { + public string LabelId { get; set; } = string.Empty; + public string LocationId { get; set; } = string.Empty; public string LabelCode { get; set; } = string.Empty; @@ -23,6 +27,8 @@ public class UsAppLabelPreviewDto public string? LabelCategoryName { get; set; } + public DateTime? LabelLastEdited { get; set; } + /// /// 预览图(base64 png,可空;若为空,客户端可用 Template 自行渲染) /// @@ -32,5 +38,11 @@ public class UsAppLabelPreviewDto /// 预览模板结构(与 LabelCanvas/LabelPreviewOnly 结构尽量一致) /// public LabelTemplatePreviewDto Template { get; set; } = new(); + + /// + /// 当前预览上下文(模板+产品+标签类型)命中的默认值配置。 + /// 数据来源:fl_label_template_product_default.DefaultValuesJson + /// + public Dictionary? TemplateProductDefaultValues { get; set; } } diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateElementDbEntity.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateElementDbEntity.cs index f727092..c9f2f8f 100644 --- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateElementDbEntity.cs +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateElementDbEntity.cs @@ -14,6 +14,9 @@ public class FlLabelTemplateElementDbEntity public string ElementType { get; set; } = string.Empty; + [SugarColumn(ColumnName = "ElementName")] + public string ElementName { get; set; } = string.Empty; + public decimal PosX { get; set; } public decimal PosY { get; set; } diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateProductDefaultDbEntity.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateProductDefaultDbEntity.cs new file mode 100644 index 0000000..9da371e --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateProductDefaultDbEntity.cs @@ -0,0 +1,24 @@ +using SqlSugar; + +namespace FoodLabeling.Application.Services.DbModels; + +[SugarTable("fl_label_template_product_default")] +public class FlLabelTemplateProductDefaultDbEntity +{ + [SugarColumn(IsPrimaryKey = true)] + public string Id { get; set; } = string.Empty; + + public string TemplateId { get; set; } = string.Empty; + + public string ProductId { get; set; } = string.Empty; + + public string LabelTypeId { get; set; } = string.Empty; + + /// + /// 默认值JSON(字符串保存) + /// + public string? DefaultValuesJson { get; set; } + + public int OrderNum { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelAppService.cs index 178285c..74b1434 100644 --- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelAppService.cs +++ b/美国版/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 { Id = el.ElementKey, ElementType = el.ElementType, + ElementName = el.ElementName, PosX = el.PosX, PosY = el.PosY, Width = el.Width, diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelTemplateAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelTemplateAppService.cs index 79e1e60..d713d61 100644 --- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelTemplateAppService.cs +++ b/美国版/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 { Id = e.ElementKey, ElementType = e.ElementType, + ElementName = e.ElementName, PosX = e.PosX, PosY = e.PosY, Width = e.Width, @@ -180,6 +181,28 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ .Select(x => x.LocationId) .ToListAsync(); + var defaultRows = await _dbContext.SqlSugarClient.Queryable() + .Where(x => x.TemplateId == template.Id) + .OrderBy(x => x.OrderNum) + .ToListAsync(); + + var productDefaults = defaultRows.Select(x => + { + object? defaults = null; + if (!string.IsNullOrWhiteSpace(x.DefaultValuesJson)) + { + defaults = JsonSerializer.Deserialize(x.DefaultValuesJson); + } + + return new LabelTemplateProductDefaultDto + { + ProductId = x.ProductId, + LabelTypeId = x.LabelTypeId, + DefaultValues = defaults, + OrderNum = x.OrderNum + }; + }).ToList(); + return new LabelTemplateGetOutputDto { Id = template.TemplateCode, @@ -195,7 +218,8 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ VersionNo = template.VersionNo, State = template.State, Elements = MapElements(), - AppliedLocationIds = appliedLocationIds + AppliedLocationIds = appliedLocationIds, + TemplateProductDefaults = productDefaults }; } @@ -246,7 +270,12 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync(); - await RebuildTemplateElementsAndLocationsAsync(entity.Id, input.Elements, entity.AppliedLocationType, input.AppliedLocationIds); + await RebuildTemplateElementsLocationsAndDefaultsAsync( + entity.Id, + input.Elements, + entity.AppliedLocationType, + input.AppliedLocationIds, + new List()); return await GetAsync(code); } @@ -292,7 +321,12 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ await _dbContext.SqlSugarClient.Updateable(template).ExecuteCommandAsync(); - await RebuildTemplateElementsAndLocationsAsync(template.Id, input.Elements, template.AppliedLocationType, input.AppliedLocationIds); + await RebuildTemplateElementsLocationsAndDefaultsAsync( + template.Id, + input.Elements, + template.AppliedLocationType, + input.AppliedLocationIds, + input.TemplateProductDefaults); return await GetAsync(template.TemplateCode); } @@ -327,13 +361,17 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ await _dbContext.SqlSugarClient.Deleteable() .Where(x => x.TemplateId == template.Id) .ExecuteCommandAsync(); + await _dbContext.SqlSugarClient.Deleteable() + .Where(x => x.TemplateId == template.Id) + .ExecuteCommandAsync(); } - private async Task RebuildTemplateElementsAndLocationsAsync( + private async Task RebuildTemplateElementsLocationsAndDefaultsAsync( string templateDbId, List elements, string appliedLocationType, - List appliedLocationIds) + List appliedLocationIds, + List? templateProductDefaults) { // elements 重建 await _dbContext.SqlSugarClient.Deleteable() @@ -342,9 +380,9 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ if (elements is not null && elements.Count > 0) { - var now = DateTime.Now; var rows = elements.Select(e => { + var elementName = EnsureElementName(e.ElementName); object? cfg = e.ConfigJson; var configJson = cfg == null ? null : JsonSerializer.Serialize(cfg); return new FlLabelTemplateElementDbEntity @@ -353,6 +391,7 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ TemplateId = templateDbId, ElementKey = e.Id, ElementType = e.ElementType, + ElementName = elementName, PosX = e.PosX, PosY = e.PosY, Width = e.Width, @@ -395,6 +434,73 @@ public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppServ await _dbContext.SqlSugarClient.Insertable(locRows).ExecuteCommandAsync(); } + + // 模板-产品-标签类型默认值:仅在显式传入时重建,避免普通编辑误清空 + if (templateProductDefaults is not null) + { + var duplicateCheckSet = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var row in templateProductDefaults) + { + var productId = row.ProductId?.Trim(); + var labelTypeId = row.LabelTypeId?.Trim(); + if (string.IsNullOrWhiteSpace(productId) || string.IsNullOrWhiteSpace(labelTypeId)) + { + continue; + } + + var key = $"{productId}::{labelTypeId}"; + if (!duplicateCheckSet.Add(key)) + { + throw new UserFriendlyException($"模板默认值绑定重复:产品[{productId}]与标签类型[{labelTypeId}]只能存在一条"); + } + } + + await _dbContext.SqlSugarClient.Deleteable() + .Where(x => x.TemplateId == templateDbId) + .ExecuteCommandAsync(); + + if (templateProductDefaults.Count > 0) + { + var rows = templateProductDefaults.Select((x, idx) => + { + var productId = x.ProductId?.Trim(); + var labelTypeId = x.LabelTypeId?.Trim(); + if (string.IsNullOrWhiteSpace(productId)) + { + throw new UserFriendlyException("模板默认值绑定中,产品Id不能为空"); + } + + if (string.IsNullOrWhiteSpace(labelTypeId)) + { + throw new UserFriendlyException("模板默认值绑定中,标签类型Id不能为空"); + } + + var json = x.DefaultValues is null ? null : JsonSerializer.Serialize(x.DefaultValues); + return new FlLabelTemplateProductDefaultDbEntity + { + Id = _guidGenerator.Create().ToString(), + TemplateId = templateDbId, + ProductId = productId, + LabelTypeId = labelTypeId, + DefaultValuesJson = json, + OrderNum = x.OrderNum <= 0 ? idx + 1 : x.OrderNum + }; + }).ToList(); + + await _dbContext.SqlSugarClient.Insertable(rows).ExecuteCommandAsync(); + } + } + } + + private static string EnsureElementName(string? elementName) + { + var normalizedName = elementName?.Trim(); + if (string.IsNullOrWhiteSpace(normalizedName)) + { + throw new UserFriendlyException("组件名字不能为空"); + } + + return normalizedName; } private static PagedResultWithPageDto BuildPagedResult(int skipCount, int maxResultCount, int total, List items) diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppLabelingAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppLabelingAppService.cs index 7b0d54d..9e1b95a 100644 --- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppLabelingAppService.cs +++ b/美国版/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 .Where((l, c, t, tpl) => l.LabelCode == labelCode) .Select((l, c, t, tpl) => new { + l.Id, l.LabelCode, l.LocationId, + l.LabelTypeId, + l.TemplateId, + l.LastModificationTime, + l.CreationTime, LabelCategoryName = c.CategoryName, TypeName = t.TypeName, TemplateCode = tpl.TemplateCode, @@ -259,21 +264,46 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ throw new UserFriendlyException("该标签不属于当前门店"); } + var previewProductId = await ResolvePreviewProductIdAsync(labelRow.Id, input.ProductId); + var template = await _labelAppService.PreviewAsync(new LabelPreviewResolveInputVo { LabelCode = labelCode, - ProductId = input.ProductId?.Trim(), + ProductId = previewProductId, BaseTime = input.BaseTime, PrintInputJson = input.PrintInputJson?.ToDictionary(x => x.Key, x => (object?)x.Value) }); + Dictionary? templateProductDefaultValues = null; + if (!string.IsNullOrWhiteSpace(previewProductId)) + { + var productDefault = await _dbContext.SqlSugarClient.Queryable() + .Where(x => x.TemplateId == labelRow.TemplateId) + .Where(x => x.ProductId == previewProductId) + .Where(x => x.LabelTypeId == labelRow.LabelTypeId) + .OrderBy(x => x.OrderNum) + .FirstAsync(); + + if (!string.IsNullOrWhiteSpace(productDefault?.DefaultValuesJson)) + { + try + { + templateProductDefaultValues = + JsonSerializer.Deserialize>(productDefault.DefaultValuesJson!); + } + catch + { + templateProductDefaultValues = null; + } + } + } + var productName = string.Empty; var productCategoryName = "无"; - if (!string.IsNullOrWhiteSpace(input.ProductId)) + if (!string.IsNullOrWhiteSpace(previewProductId)) { - var pid = input.ProductId.Trim(); var p = await _dbContext.SqlSugarClient.Queryable() - .FirstAsync(x => !x.IsDeleted && x.State && x.Id == pid); + .FirstAsync(x => !x.IsDeleted && x.State && x.Id == previewProductId); if (p is not null) { productName = p.ProductName ?? string.Empty; @@ -288,6 +318,7 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ return new UsAppLabelPreviewDto { + LabelId = labelRow.Id, LocationId = locationId, LabelCode = labelCode, TemplateCode = labelRow.TemplateCode, @@ -296,8 +327,10 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ ProductName = string.IsNullOrWhiteSpace(productName) ? null : productName, ProductCategoryName = productCategoryName, LabelCategoryName = labelRow.LabelCategoryName, + LabelLastEdited = labelRow.LastModificationTime ?? labelRow.CreationTime, PreviewImageBase64Png = null, - Template = template + Template = template, + TemplateProductDefaultValues = templateProductDefaultValues }; } @@ -508,6 +541,20 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ return string.IsNullOrWhiteSpace(s) ? null : s; } + private async Task ResolvePreviewProductIdAsync(string labelId, string? productId) + { + var resolvedProductId = productId?.Trim(); + if (!string.IsNullOrWhiteSpace(resolvedProductId)) + { + return resolvedProductId; + } + + return await _dbContext.SqlSugarClient.Queryable() + .Where(x => x.LabelId == labelId) + .Select(x => x.ProductId) + .FirstAsync(); + } + private static UsAppLabelTypeNodeDto BuildLabelTypeNode(LabelingTreeRow r) { return new UsAppLabelTypeNodeDto diff --git a/项目相关文档/标签模块接口对接说明.md b/项目相关文档/标签模块接口对接说明.md index bbcdd08..12501f2 100644 --- a/项目相关文档/标签模块接口对接说明.md +++ b/项目相关文档/标签模块接口对接说明.md @@ -236,6 +236,9 @@ Swagger 地址: 说明: - 模板标识入参 `id` 使用 `fl_label_template.TemplateCode`。 - 创建/编辑的 Body 字段名对齐你前端 editor JSON(`id/name/appliedLocation/elements/config`)。 +- 模板组件 `elements[]` 的 `elementName` 为必填(前端传值,后端校验为空会报错)。 +- `templateProductDefaults[]` 用于模板内“产品 + 标签类型”绑定默认值(进入模板详情页后的绑定列表)。 +- **新增模板时不处理默认值**;默认值仅在后续“产品关联/编辑模板”阶段维护。 ### 4.1 分页列表 @@ -283,6 +286,7 @@ Swagger 地址: "elements": [ { "id": "el-fixed-title", + "elementName": "标题文本", "type": "TEXT_STATIC", "x": 32, "y": 24, @@ -309,6 +313,8 @@ Swagger 地址: 说明: - 当 `appliedLocation=SPECIFIED` 时,`appliedLocationIds` 必须至少选择一个门店。 +- `elements[].elementName` 必填;为空或空白将返回友好错误:`组件名字不能为空`。 +- 新增模板时即使传了 `templateProductDefaults`,后端也不会写入默认值数据。 ### 4.4 编辑模板 @@ -332,14 +338,84 @@ Swagger 地址: "showRuler": true, "showGrid": true, "state": true, - "elements": [], - "appliedLocationIds": ["11111111-1111-1111-1111-111111111111"] + "elements": [ + { + "id": "el-price", + "elementName": "价格文本", + "type": "TEXT_PRICE", + "x": 40, + "y": 120, + "width": 140, + "height": 28, + "rotation": "horizontal", + "border": "none", + "zIndex": 2, + "orderNum": 2, + "valueSourceType": "PRINT_INPUT", + "inputKey": "price", + "isRequiredInput": true, + "config": { + "text": "", + "fontFamily": "Arial", + "fontSize": 18, + "fontWeight": "bold", + "textAlign": "left" + } + } + ], + "appliedLocationIds": ["11111111-1111-1111-1111-111111111111"], + "templateProductDefaults": [ + { + "productId": "3a20-xxxx", + "labelTypeId": "3a20-yyyy", + "defaultValues": { + "el-fixed-title": "Chicken", + "el-price": "2.00", + "el-desc": "23" + }, + "orderNum": 1 + } + ] } ``` 版本: - `VersionNo` 会在编辑时自动 `+1`。 - `elements` 会按传入内容全量重建。 +- 查询/预览返回的 `elements[]` 同样会带 `elementName` 字段。 +- `templateProductDefaults` 在编辑接口中**仅当显式传入时**才会重建(同一模板先删后插)。 +- 若编辑时不传 `templateProductDefaults`,后端会保留数据库中原有默认值,不做覆盖。 + +`templateProductDefaults` 结构说明: +- 每一行需传 `productId` 与 `labelTypeId`。 +- `defaultValues` 建议使用 `element.id => 默认文本` 结构;页面展示时可结合模板 `elements[].config.text` 作为列头与初始值。 + +### 4.6 模板与产品默认值关联表(新增) + +用于存储“模板-产品-标签类型”的默认值,推荐执行以下建表 SQL: + +```sql +CREATE TABLE `fl_label_template_product_default` ( + `Id` varchar(36) NOT NULL COMMENT '主键', + `TemplateId` varchar(36) NOT NULL COMMENT '模板Id(关联 fl_label_template.Id)', + `ProductId` varchar(36) NOT NULL COMMENT '产品Id(关联 fl_product.Id)', + `LabelTypeId` varchar(36) NOT NULL COMMENT '标签类型Id(关联 fl_label_type.Id)', + `DefaultValuesJson` text NULL COMMENT '默认值JSON(如 elementId=>默认文本)', + `OrderNum` int NOT NULL DEFAULT 1 COMMENT '排序', + PRIMARY KEY (`Id`), + KEY `idx_fl_ltpd_template` (`TemplateId`), + KEY `idx_fl_ltpd_product` (`ProductId`), + KEY `idx_fl_ltpd_label_type` (`LabelTypeId`), + UNIQUE KEY `uk_fl_ltpd_template_product_label_type` (`TemplateId`, `ProductId`, `LabelTypeId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='标签模板-产品默认值关联表'; +``` + +若表已存在,可执行以下 SQL 增加唯一约束: + +```sql +ALTER TABLE `fl_label_template_product_default` +ADD UNIQUE KEY `uk_fl_ltpd_template_product_label_type` (`TemplateId`, `ProductId`, `LabelTypeId`); +``` ### 4.5 删除(逻辑删除) @@ -758,7 +834,12 @@ curl -X GET "http://localhost:19001/api/app/us-app-labeling/labeling-tree?locati - `template`:`LabelTemplatePreviewDto` - `width` / `height` / `unit`:模板物理尺寸 - - `elements[]`:元素数组(对齐前端 editor JSON:`id/type/x/y/width/height/rotation/border/zIndex/orderNum/config`) + - `elements[]`:元素数组(对齐前端 editor JSON:`id/elementName/type/x/y/width/height/rotation/border/zIndex/orderNum/config`) +- `templateProductDefaultValues`:`object | null` + - 来源:`fl_label_template_product_default.DefaultValuesJson` + - 命中条件:当前预览上下文的 `templateId + productId + labelTypeId` + - 建议结构:`{ "elementId": "默认值" }` + - 未命中时返回 `null`(向后兼容) `elements[].config` 内常用字段(示例): -- libgit2 0.21.4