diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelCreateInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelCreateInputVo.cs new file mode 100644 index 0000000..5965aec --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelCreateInputVo.cs @@ -0,0 +1,23 @@ +namespace FoodLabeling.Application.Contracts.Dtos.Label; + +public class LabelCreateInputVo +{ + public string LabelCode { get; set; } = string.Empty; + + public string LabelName { get; set; } = string.Empty; + + public string TemplateCode { get; set; } = string.Empty; + + public string LocationId { get; set; } = string.Empty; + + public string LabelCategoryId { get; set; } = string.Empty; + + public string LabelTypeId { get; set; } = string.Empty; + + public List ProductIds { get; set; } = new(); + + public object? LabelInfoJson { get; set; } + + public bool State { get; set; } = true; +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelGetListInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelGetListInputVo.cs new file mode 100644 index 0000000..07dd5f8 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelGetListInputVo.cs @@ -0,0 +1,33 @@ +using Volo.Abp.Application.Dtos; + +namespace FoodLabeling.Application.Contracts.Dtos.Label; + +/// +/// 标签分页列表入参(按产品展示多个标签) +/// +public class LabelGetListInputVo : PagedAndSortedResultRequestDto +{ + public string? Keyword { get; set; } + + /// + /// 门店Id(可选) + /// + public string? LocationId { get; set; } + + /// + /// 产品Id(可选;用于“一个产品展示多个标签”) + /// + public string? ProductId { get; set; } + + public string? LabelCategoryId { get; set; } + + public string? LabelTypeId { get; set; } + + /// + /// 模板编码(可选) + /// + public string? TemplateCode { get; set; } + + public bool? State { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelGetListOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelGetListOutputDto.cs new file mode 100644 index 0000000..6938581 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelGetListOutputDto.cs @@ -0,0 +1,30 @@ +namespace FoodLabeling.Application.Contracts.Dtos.Label; + +public class LabelGetListOutputDto +{ + public string Id { get; set; } = string.Empty; + + public string LabelName { get; set; } = string.Empty; + + public string LocationName { get; set; } = string.Empty; + + public string LabelCategoryName { get; set; } = string.Empty; + + public string ProductCategoryName { get; set; } = string.Empty; + + public string ProductName { get; set; } = string.Empty; + + public string TemplateName { get; set; } = string.Empty; + + public string LabelTypeName { get; set; } = string.Empty; + + public bool State { get; set; } + + public DateTime LastEdited { get; set; } + + /// + /// 模板缺失字段告警(当前阶段暂定 false) + /// + public bool HasError { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelGetOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelGetOutputDto.cs new file mode 100644 index 0000000..77cd258 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelGetOutputDto.cs @@ -0,0 +1,31 @@ +namespace FoodLabeling.Application.Contracts.Dtos.Label; + +public class LabelGetOutputDto +{ + public string Id { get; set; } = string.Empty; + + public string LabelName { get; set; } = string.Empty; + + public string LocationId { get; set; } = string.Empty; + + public string LocationName { get; set; } = string.Empty; + + public string LabelCategoryId { get; set; } = string.Empty; + + public string LabelCategoryName { get; set; } = string.Empty; + + public string LabelTypeId { get; set; } = string.Empty; + + public string LabelTypeName { get; set; } = string.Empty; + + public string TemplateCode { get; set; } = string.Empty; + + public string TemplateName { get; set; } = string.Empty; + + public bool State { get; set; } + + public object? LabelInfoJson { get; set; } + + public List ProductIds { get; set; } = new(); +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelUpdateInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelUpdateInputVo.cs new file mode 100644 index 0000000..f656110 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelUpdateInputVo.cs @@ -0,0 +1,21 @@ +namespace FoodLabeling.Application.Contracts.Dtos.Label; + +public class LabelUpdateInputVo +{ + public string LabelName { get; set; } = string.Empty; + + public string TemplateCode { get; set; } = string.Empty; + + public string LocationId { get; set; } = string.Empty; + + public string LabelCategoryId { get; set; } = string.Empty; + + public string LabelTypeId { get; set; } = string.Empty; + + public List ProductIds { get; set; } = new(); + + public object? LabelInfoJson { get; set; } + + public bool State { get; set; } = true; +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryCreateInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryCreateInputVo.cs new file mode 100644 index 0000000..986bca2 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryCreateInputVo.cs @@ -0,0 +1,15 @@ +namespace FoodLabeling.Application.Contracts.Dtos.LabelCategory; + +public class LabelCategoryCreateInputVo +{ + public string CategoryCode { get; set; } = string.Empty; + + public string CategoryName { get; set; } = string.Empty; + + public string? CategoryPhotoUrl { get; set; } + + public bool State { get; set; } = true; + + public int OrderNum { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetListInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetListInputVo.cs new file mode 100644 index 0000000..5f93662 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetListInputVo.cs @@ -0,0 +1,11 @@ +using Volo.Abp.Application.Dtos; + +namespace FoodLabeling.Application.Contracts.Dtos.LabelCategory; + +public class LabelCategoryGetListInputVo : PagedAndSortedResultRequestDto +{ + public string? Keyword { get; set; } + + public bool? State { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetListOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetListOutputDto.cs new file mode 100644 index 0000000..db2aa7c --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetListOutputDto.cs @@ -0,0 +1,21 @@ +namespace FoodLabeling.Application.Contracts.Dtos.LabelCategory; + +public class LabelCategoryGetListOutputDto +{ + public string Id { get; set; } = string.Empty; + + public string CategoryCode { get; set; } = string.Empty; + + public string CategoryName { get; set; } = string.Empty; + + public string? CategoryPhotoUrl { get; set; } + + public bool State { get; set; } + + public int OrderNum { get; set; } + + public long NoOfLabels { get; set; } + + public DateTime LastEdited { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetOutputDto.cs new file mode 100644 index 0000000..242b820 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetOutputDto.cs @@ -0,0 +1,17 @@ +namespace FoodLabeling.Application.Contracts.Dtos.LabelCategory; + +public class LabelCategoryGetOutputDto +{ + public string Id { get; set; } = string.Empty; + + public string CategoryCode { get; set; } = string.Empty; + + public string CategoryName { get; set; } = string.Empty; + + public string? CategoryPhotoUrl { get; set; } + + public bool State { get; set; } + + public int OrderNum { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryUpdateInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryUpdateInputVo.cs new file mode 100644 index 0000000..5a52b5d --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryUpdateInputVo.cs @@ -0,0 +1,6 @@ +namespace FoodLabeling.Application.Contracts.Dtos.LabelCategory; + +public class LabelCategoryUpdateInputVo : LabelCategoryCreateInputVo +{ +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionCreateInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionCreateInputVo.cs new file mode 100644 index 0000000..52a8cf9 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionCreateInputVo.cs @@ -0,0 +1,15 @@ +namespace FoodLabeling.Application.Contracts.Dtos.LabelMultipleOption; + +public class LabelMultipleOptionCreateInputVo +{ + public string OptionCode { get; set; } = string.Empty; + + public string OptionName { get; set; } = string.Empty; + + public string? OptionValuesJson { get; set; } + + public bool State { get; set; } = true; + + public int OrderNum { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionGetListInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionGetListInputVo.cs new file mode 100644 index 0000000..dbf8365 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionGetListInputVo.cs @@ -0,0 +1,11 @@ +using Volo.Abp.Application.Dtos; + +namespace FoodLabeling.Application.Contracts.Dtos.LabelMultipleOption; + +public class LabelMultipleOptionGetListInputVo : PagedAndSortedResultRequestDto +{ + public string? Keyword { get; set; } + + public bool? State { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionGetListOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionGetListOutputDto.cs new file mode 100644 index 0000000..f7af6bc --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionGetListOutputDto.cs @@ -0,0 +1,19 @@ +namespace FoodLabeling.Application.Contracts.Dtos.LabelMultipleOption; + +public class LabelMultipleOptionGetListOutputDto +{ + public string Id { get; set; } = string.Empty; + + public string OptionCode { get; set; } = string.Empty; + + public string OptionName { get; set; } = string.Empty; + + public string? OptionValuesJson { get; set; } + + public bool State { get; set; } + + public int OrderNum { get; set; } + + public DateTime LastEdited { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionGetOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionGetOutputDto.cs new file mode 100644 index 0000000..0b181c6 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionGetOutputDto.cs @@ -0,0 +1,17 @@ +namespace FoodLabeling.Application.Contracts.Dtos.LabelMultipleOption; + +public class LabelMultipleOptionGetOutputDto +{ + public string Id { get; set; } = string.Empty; + + public string OptionCode { get; set; } = string.Empty; + + public string OptionName { get; set; } = string.Empty; + + public string? OptionValuesJson { get; set; } + + public bool State { get; set; } + + public int OrderNum { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionUpdateInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionUpdateInputVo.cs new file mode 100644 index 0000000..066e6f7 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionUpdateInputVo.cs @@ -0,0 +1,6 @@ +namespace FoodLabeling.Application.Contracts.Dtos.LabelMultipleOption; + +public class LabelMultipleOptionUpdateInputVo : LabelMultipleOptionCreateInputVo +{ +} + 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 new file mode 100644 index 0000000..1a98451 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateCreateInputVo.cs @@ -0,0 +1,61 @@ +using System.Text.Json.Serialization; + +namespace FoodLabeling.Application.Contracts.Dtos.LabelTemplate; + +public class LabelTemplateCreateInputVo +{ + /// + /// editor JSON 里的 id,对应数据库 fl_label_template.TemplateCode + /// + [JsonPropertyName("id")] + public string TemplateCode { get; set; } = string.Empty; + + /// + /// editor JSON 里的 name,对应数据库 fl_label_template.TemplateName + /// + [JsonPropertyName("name")] + public string TemplateName { get; set; } = string.Empty; + + [JsonPropertyName("labelType")] + public string? LabelType { get; set; } + + [JsonPropertyName("unit")] + public string Unit { get; set; } = "inch"; + + [JsonPropertyName("width")] + public decimal Width { get; set; } + + [JsonPropertyName("height")] + public decimal Height { get; set; } + + /// + /// editor JSON 里的 appliedLocation(ALL / SPECIFIED) + /// + [JsonPropertyName("appliedLocation")] + public string AppliedLocationType { get; set; } = "ALL"; + + [JsonPropertyName("showRuler")] + public bool ShowRuler { get; set; } = true; + + [JsonPropertyName("showGrid")] + public bool ShowGrid { get; set; } = true; + + /// + /// 预留:前端可能不传;如果不传则默认 true + /// + [JsonPropertyName("state")] + public bool State { get; set; } = true; + + /// + /// elements(前端 editor 的 elements[],会全量重建) + /// + [JsonPropertyName("elements")] + public List Elements { get; set; } = new(); + + /// + /// 当 AppliedLocationType=SPECIFIED 时生效 + /// + [JsonPropertyName("appliedLocationIds")] + public List AppliedLocationIds { get; set; } = new(); +} + 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 new file mode 100644 index 0000000..b1a8bf3 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateElementDto.cs @@ -0,0 +1,64 @@ +using System.Text.Json.Serialization; + +namespace FoodLabeling.Application.Contracts.Dtos.LabelTemplate; + +/// +/// 模板元素(对齐你给的 editor JSON:id/type/x/y/width/height/rotation/border/config) +/// +public class LabelTemplateElementDto +{ + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + [JsonPropertyName("type")] + public string ElementType { get; set; } = string.Empty; + + [JsonPropertyName("x")] + public decimal PosX { get; set; } + + [JsonPropertyName("y")] + public decimal PosY { get; set; } + + [JsonPropertyName("width")] + public decimal Width { get; set; } + + [JsonPropertyName("height")] + public decimal Height { get; set; } + + [JsonPropertyName("rotation")] + public string Rotation { get; set; } = "horizontal"; + + [JsonPropertyName("border")] + public string BorderType { get; set; } = "none"; + + [JsonPropertyName("zIndex")] + public int ZIndex { get; set; } + + [JsonPropertyName("orderNum")] + public int OrderNum { get; set; } + + /// + /// 值来源:FIXED / AUTO_DB / PRINT_INPUT + /// + [JsonPropertyName("valueSourceType")] + public string ValueSourceType { get; set; } = "FIXED"; + + [JsonPropertyName("bindingExpr")] + public string? BindingExpr { get; set; } + + [JsonPropertyName("autoQueryKey")] + public string? AutoQueryKey { get; set; } + + [JsonPropertyName("inputKey")] + public string? InputKey { get; set; } + + [JsonPropertyName("isRequiredInput")] + public bool IsRequiredInput { get; set; } + + /// + /// 元素配置 + /// + [JsonPropertyName("config")] + public object? ConfigJson { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateGetListInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateGetListInputVo.cs new file mode 100644 index 0000000..3956076 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateGetListInputVo.cs @@ -0,0 +1,30 @@ +using Volo.Abp.Application.Dtos; + +namespace FoodLabeling.Application.Contracts.Dtos.LabelTemplate; + +/// +/// 标签模板分页查询入参 +/// +public class LabelTemplateGetListInputVo : PagedAndSortedResultRequestDto +{ + /// + /// 关键字(模板名称/模板编码) + /// + public string? Keyword { get; set; } + + /// + /// 门店Id(用于筛选:ALL 或指定门店) + /// + public string? LocationId { get; set; } + + /// + /// 标签类型(例如 PRICE) + /// + public string? LabelType { get; set; } + + /// + /// 状态 + /// + public bool? State { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateGetListOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateGetListOutputDto.cs new file mode 100644 index 0000000..b91846d --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateGetListOutputDto.cs @@ -0,0 +1,29 @@ +namespace FoodLabeling.Application.Contracts.Dtos.LabelTemplate; + +public class LabelTemplateGetListOutputDto +{ + public string Id { get; set; } = string.Empty; + + public string TemplateCode { get; set; } = string.Empty; + + public string TemplateName { get; set; } = string.Empty; + + public string? LabelType { get; set; } + + /// + /// 位置展示:All Locations 或首个指定门店的名称 + /// + public string LocationText { get; set; } = string.Empty; + + /// + /// elements 数量(Contents) + /// + public int ContentsCount { get; set; } + + public string SizeText { get; set; } = string.Empty; + + public int VersionNo { get; set; } + + public DateTime LastEdited { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateGetOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateGetOutputDto.cs new file mode 100644 index 0000000..1187b11 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateGetOutputDto.cs @@ -0,0 +1,33 @@ +namespace FoodLabeling.Application.Contracts.Dtos.LabelTemplate; + +public class LabelTemplateGetOutputDto +{ + public string Id { get; set; } = string.Empty; + + public string TemplateCode { get; set; } = string.Empty; + + public string TemplateName { get; set; } = string.Empty; + + public string? LabelType { get; set; } + + public string Unit { get; set; } = string.Empty; + + public decimal Width { get; set; } + + public decimal Height { get; set; } + + public string AppliedLocationType { get; set; } = "ALL"; + + public bool ShowRuler { get; set; } + + public bool ShowGrid { get; set; } + + public int VersionNo { get; set; } + + public bool State { get; set; } + + public List Elements { get; set; } = new(); + + public List AppliedLocationIds { get; set; } = new(); +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateUpdateInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateUpdateInputVo.cs new file mode 100644 index 0000000..1ec4d23 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateUpdateInputVo.cs @@ -0,0 +1,6 @@ +namespace FoodLabeling.Application.Contracts.Dtos.LabelTemplate; + +public class LabelTemplateUpdateInputVo : LabelTemplateCreateInputVo +{ +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeCreateInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeCreateInputVo.cs new file mode 100644 index 0000000..27e3863 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeCreateInputVo.cs @@ -0,0 +1,13 @@ +namespace FoodLabeling.Application.Contracts.Dtos.LabelType; + +public class LabelTypeCreateInputVo +{ + public string TypeCode { get; set; } = string.Empty; + + public string TypeName { get; set; } = string.Empty; + + public bool State { get; set; } = true; + + public int OrderNum { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeGetListInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeGetListInputVo.cs new file mode 100644 index 0000000..af2f04c --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeGetListInputVo.cs @@ -0,0 +1,11 @@ +using Volo.Abp.Application.Dtos; + +namespace FoodLabeling.Application.Contracts.Dtos.LabelType; + +public class LabelTypeGetListInputVo : PagedAndSortedResultRequestDto +{ + public string? Keyword { get; set; } + + public bool? State { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeGetListOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeGetListOutputDto.cs new file mode 100644 index 0000000..1a63ae4 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeGetListOutputDto.cs @@ -0,0 +1,19 @@ +namespace FoodLabeling.Application.Contracts.Dtos.LabelType; + +public class LabelTypeGetListOutputDto +{ + public string Id { get; set; } = string.Empty; + + public string TypeCode { get; set; } = string.Empty; + + public string TypeName { get; set; } = string.Empty; + + public bool State { get; set; } + + public int OrderNum { get; set; } + + public long NoOfLabels { get; set; } + + public DateTime LastEdited { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeGetOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeGetOutputDto.cs new file mode 100644 index 0000000..0763156 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeGetOutputDto.cs @@ -0,0 +1,15 @@ +namespace FoodLabeling.Application.Contracts.Dtos.LabelType; + +public class LabelTypeGetOutputDto +{ + public string Id { get; set; } = string.Empty; + + public string TypeCode { get; set; } = string.Empty; + + public string TypeName { get; set; } = string.Empty; + + public bool State { get; set; } + + public int OrderNum { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeUpdateInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeUpdateInputVo.cs new file mode 100644 index 0000000..23b6698 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeUpdateInputVo.cs @@ -0,0 +1,6 @@ +namespace FoodLabeling.Application.Contracts.Dtos.LabelType; + +public class LabelTypeUpdateInputVo : LabelTypeCreateInputVo +{ +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelAppService.cs new file mode 100644 index 0000000..95e10fc --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelAppService.cs @@ -0,0 +1,43 @@ +using FoodLabeling.Application.Contracts.Dtos.Common; +using FoodLabeling.Application.Contracts.Dtos.Label; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace FoodLabeling.Application.Contracts.IServices; + +/// +/// 标签管理接口(美国版) +/// +public interface ILabelAppService : IApplicationService +{ + /// + /// 按产品分页列表(一个产品展示多个标签) + /// + Task> GetListAsync(LabelGetListInputVo input); + + /// + /// 标签详情(id=LabelCode) + /// + Task GetAsync(string id); + + /// + /// 新增标签 + /// + Task CreateAsync(LabelCreateInputVo input); + + /// + /// 编辑标签(id=LabelCode) + /// + Task UpdateAsync(string id, LabelUpdateInputVo input); + + /// + /// 删除标签(逻辑删除) + /// + Task DeleteAsync(string id); + + /// + /// 标签预览:解析模板 AUTO_DB/PRINT_INPUT(不做打印落库) + /// + Task PreviewAsync(LabelPreviewResolveInputVo input); +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelCategoryAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelCategoryAppService.cs new file mode 100644 index 0000000..32464df --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelCategoryAppService.cs @@ -0,0 +1,18 @@ +using FoodLabeling.Application.Contracts.Dtos.Common; +using FoodLabeling.Application.Contracts.Dtos.LabelCategory; + +namespace FoodLabeling.Application.Contracts.IServices; + +public interface ILabelCategoryAppService +{ + Task> GetListAsync(LabelCategoryGetListInputVo input); + + Task GetAsync(string id); + + Task CreateAsync(LabelCategoryCreateInputVo input); + + Task UpdateAsync(string id, LabelCategoryUpdateInputVo input); + + Task DeleteAsync(string id); +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelMultipleOptionAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelMultipleOptionAppService.cs new file mode 100644 index 0000000..38919ab --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelMultipleOptionAppService.cs @@ -0,0 +1,18 @@ +using FoodLabeling.Application.Contracts.Dtos.Common; +using FoodLabeling.Application.Contracts.Dtos.LabelMultipleOption; + +namespace FoodLabeling.Application.Contracts.IServices; + +public interface ILabelMultipleOptionAppService +{ + Task> GetListAsync(LabelMultipleOptionGetListInputVo input); + + Task GetAsync(string id); + + Task CreateAsync(LabelMultipleOptionCreateInputVo input); + + Task UpdateAsync(string id, LabelMultipleOptionUpdateInputVo input); + + Task DeleteAsync(string id); +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelTemplateAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelTemplateAppService.cs new file mode 100644 index 0000000..373cd9b --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelTemplateAppService.cs @@ -0,0 +1,38 @@ +using FoodLabeling.Application.Contracts.Dtos.Common; +using FoodLabeling.Application.Contracts.Dtos.LabelTemplate; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace FoodLabeling.Application.Contracts.IServices; + +/// +/// 标签模板管理接口(美国版对外) +/// +public interface ILabelTemplateAppService : IApplicationService +{ + /// + /// 标签模板分页列表 + /// + Task> GetListAsync(LabelTemplateGetListInputVo input); + + /// + /// 标签模板详情(含 elements 与适用门店) + /// + Task GetAsync(string id); + + /// + /// 新增标签模板 + /// + Task CreateAsync(LabelTemplateCreateInputVo input); + + /// + /// 编辑标签模板(版本号 +1,重建 elements) + /// + Task UpdateAsync(string id, LabelTemplateUpdateInputVo input); + + /// + /// 删除标签模板(逻辑删除;若已被标签引用则禁止) + /// + Task DeleteAsync(string id); +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelTypeAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelTypeAppService.cs new file mode 100644 index 0000000..aa4d03a --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelTypeAppService.cs @@ -0,0 +1,18 @@ +using FoodLabeling.Application.Contracts.Dtos.Common; +using FoodLabeling.Application.Contracts.Dtos.LabelType; + +namespace FoodLabeling.Application.Contracts.IServices; + +public interface ILabelTypeAppService +{ + Task> GetListAsync(LabelTypeGetListInputVo input); + + Task GetAsync(string id); + + Task CreateAsync(LabelTypeCreateInputVo input); + + Task UpdateAsync(string id, LabelTypeUpdateInputVo input); + + Task DeleteAsync(string id); +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelCategoryDbEntity.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelCategoryDbEntity.cs new file mode 100644 index 0000000..d70b724 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelCategoryDbEntity.cs @@ -0,0 +1,33 @@ +using SqlSugar; + +namespace FoodLabeling.Application.Services.DbModels; + +[SugarTable("fl_label_category")] +public class FlLabelCategoryDbEntity +{ + [SugarColumn(IsPrimaryKey = true)] + public string Id { get; set; } = string.Empty; + + public bool IsDeleted { get; set; } + + public DateTime CreationTime { get; set; } + + public string? CreatorId { get; set; } + + public string? LastModifierId { get; set; } + + public DateTime? LastModificationTime { get; set; } + + public string ConcurrencyStamp { get; set; } = string.Empty; + + public string CategoryCode { get; set; } = string.Empty; + + public string CategoryName { get; set; } = string.Empty; + + public string? CategoryPhotoUrl { get; set; } + + public int OrderNum { get; set; } + + public bool State { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelDbEntity.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelDbEntity.cs new file mode 100644 index 0000000..3199335 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelDbEntity.cs @@ -0,0 +1,44 @@ +using SqlSugar; + +namespace FoodLabeling.Application.Services.DbModels; + +[SugarTable("fl_label")] +public class FlLabelDbEntity +{ + [SugarColumn(IsPrimaryKey = true)] + public string Id { get; set; } = string.Empty; + + public bool IsDeleted { get; set; } + + public DateTime CreationTime { get; set; } + + public string? CreatorId { get; set; } + + public string? LastModifierId { get; set; } + + public DateTime? LastModificationTime { get; set; } + + public string ConcurrencyStamp { get; set; } = string.Empty; + + public string LabelCode { get; set; } = string.Empty; + + public string LabelName { get; set; } = string.Empty; + + public string TemplateId { get; set; } = string.Empty; + + public string? LocationId { get; set; } + + public string? LabelCategoryId { get; set; } + + public string? LabelTypeId { get; set; } + + public string? LabelType { get; set; } + + public bool State { get; set; } + + /// + /// json 字段,直接保存为字符串(入参/出参自行序列化/反序列化) + /// + public string? LabelInfoJson { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelMultipleOptionDbEntity.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelMultipleOptionDbEntity.cs new file mode 100644 index 0000000..b6fe0f4 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelMultipleOptionDbEntity.cs @@ -0,0 +1,33 @@ +using SqlSugar; + +namespace FoodLabeling.Application.Services.DbModels; + +[SugarTable("fl_label_multiple_option")] +public class FlLabelMultipleOptionDbEntity +{ + [SugarColumn(IsPrimaryKey = true)] + public string Id { get; set; } = string.Empty; + + public bool IsDeleted { get; set; } + + public DateTime CreationTime { get; set; } + + public string? CreatorId { get; set; } + + public string? LastModifierId { get; set; } + + public DateTime? LastModificationTime { get; set; } + + public string ConcurrencyStamp { get; set; } = string.Empty; + + public string OptionCode { get; set; } = string.Empty; + + public string OptionName { get; set; } = string.Empty; + + public string? OptionValuesJson { get; set; } + + public int OrderNum { get; set; } + + public bool State { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelProductDbEntity.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelProductDbEntity.cs new file mode 100644 index 0000000..42033ca --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelProductDbEntity.cs @@ -0,0 +1,15 @@ +using SqlSugar; + +namespace FoodLabeling.Application.Services.DbModels; + +[SugarTable("fl_label_product")] +public class FlLabelProductDbEntity +{ + [SugarColumn(IsPrimaryKey = true)] + public string Id { get; set; } = string.Empty; + + public string LabelId { get; set; } = string.Empty; + + public string ProductId { get; set; } = string.Empty; +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateDbEntity.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateDbEntity.cs new file mode 100644 index 0000000..6ca3be8 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateDbEntity.cs @@ -0,0 +1,45 @@ +using SqlSugar; + +namespace FoodLabeling.Application.Services.DbModels; + +[SugarTable("fl_label_template")] +public class FlLabelTemplateDbEntity +{ + [SugarColumn(IsPrimaryKey = true)] + public string Id { get; set; } = string.Empty; + + public bool IsDeleted { get; set; } + + public DateTime CreationTime { get; set; } + + public string? CreatorId { get; set; } + + public string? LastModifierId { get; set; } + + public DateTime? LastModificationTime { get; set; } + + public string ConcurrencyStamp { get; set; } = string.Empty; + + public string TemplateCode { get; set; } = string.Empty; + + public string TemplateName { get; set; } = string.Empty; + + public string? LabelType { get; set; } + + public string Unit { get; set; } = "inch"; + + public decimal Width { get; set; } + + public decimal Height { get; set; } + + public string AppliedLocationType { get; set; } = "ALL"; + + public bool ShowRuler { get; set; } + + public bool ShowGrid { get; set; } + + public int VersionNo { get; set; } + + public bool State { 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 new file mode 100644 index 0000000..f727092 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateElementDbEntity.cs @@ -0,0 +1,45 @@ +using SqlSugar; + +namespace FoodLabeling.Application.Services.DbModels; + +[SugarTable("fl_label_template_element")] +public class FlLabelTemplateElementDbEntity +{ + [SugarColumn(IsPrimaryKey = true)] + public string Id { get; set; } = string.Empty; + + public string TemplateId { get; set; } = string.Empty; + + public string ElementKey { get; set; } = string.Empty; + + public string ElementType { get; set; } = string.Empty; + + public decimal PosX { get; set; } + + public decimal PosY { get; set; } + + public decimal Width { get; set; } + + public decimal Height { get; set; } + + public string Rotation { get; set; } = "horizontal"; + + public string BorderType { get; set; } = "none"; + + public int ZIndex { get; set; } + + public int OrderNum { get; set; } + + public string ValueSourceType { get; set; } = "FIXED"; + + public string? BindingExpr { get; set; } + + public string? AutoQueryKey { get; set; } + + public string? InputKey { get; set; } + + public bool IsRequiredInput { get; set; } + + public string? ConfigJson { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateLocationDbEntity.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateLocationDbEntity.cs new file mode 100644 index 0000000..9f15259 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateLocationDbEntity.cs @@ -0,0 +1,15 @@ +using SqlSugar; + +namespace FoodLabeling.Application.Services.DbModels; + +[SugarTable("fl_label_template_location")] +public class FlLabelTemplateLocationDbEntity +{ + [SugarColumn(IsPrimaryKey = true)] + public string Id { get; set; } = string.Empty; + + public string TemplateId { get; set; } = string.Empty; + + public string LocationId { get; set; } = string.Empty; +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTypeDbEntity.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTypeDbEntity.cs new file mode 100644 index 0000000..875724a --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTypeDbEntity.cs @@ -0,0 +1,31 @@ +using SqlSugar; + +namespace FoodLabeling.Application.Services.DbModels; + +[SugarTable("fl_label_type")] +public class FlLabelTypeDbEntity +{ + [SugarColumn(IsPrimaryKey = true)] + public string Id { get; set; } = string.Empty; + + public bool IsDeleted { get; set; } + + public DateTime CreationTime { get; set; } + + public string? CreatorId { get; set; } + + public string? LastModifierId { get; set; } + + public DateTime? LastModificationTime { get; set; } + + public string ConcurrencyStamp { get; set; } = string.Empty; + + public string TypeCode { get; set; } = string.Empty; + + public string TypeName { get; set; } = string.Empty; + + public int OrderNum { get; set; } + + public bool State { get; set; } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlProductDbEntity.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlProductDbEntity.cs new file mode 100644 index 0000000..c4c7d45 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlProductDbEntity.cs @@ -0,0 +1,23 @@ +using SqlSugar; + +namespace FoodLabeling.Application.Services.DbModels; + +[SugarTable("fl_product")] +public class FlProductDbEntity +{ + [SugarColumn(IsPrimaryKey = true)] + public string Id { get; set; } = string.Empty; + + public bool IsDeleted { get; set; } + + public string ProductCode { get; set; } = string.Empty; + + public string ProductName { get; set; } = string.Empty; + + public string? CategoryName { get; set; } + + public string? ProductImageUrl { get; set; } + + public bool State { 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 new file mode 100644 index 0000000..b9c35ba --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelAppService.cs @@ -0,0 +1,656 @@ +using System.Text.Json; +using FoodLabeling.Application.Contracts.Dtos.Common; +using FoodLabeling.Application.Contracts.Dtos.Label; +using FoodLabeling.Application.Contracts.Dtos.LabelTemplate; +using FoodLabeling.Application.Contracts.IServices; +using FoodLabeling.Application.Services.DbModels; +using FoodLabeling.Domain.Entities; +using SqlSugar; +using Volo.Abp; +using Volo.Abp.Application.Services; +using Volo.Abp.Guids; +using Volo.Abp.Uow; +using Yi.Framework.SqlSugarCore.Abstractions; + +namespace FoodLabeling.Application.Services; + +/// +/// 标签管理(一个产品展示多个标签) +/// +public class LabelAppService : ApplicationService, ILabelAppService +{ + private readonly ISqlSugarDbContext _dbContext; + private readonly IGuidGenerator _guidGenerator; + + public LabelAppService(ISqlSugarDbContext dbContext, IGuidGenerator guidGenerator) + { + _dbContext = dbContext; + _guidGenerator = guidGenerator; + } + + public async Task> GetListAsync(LabelGetListInputVo input) + { + RefAsync total = 0; + + var productId = input.ProductId?.Trim(); + var locationId = input.LocationId?.Trim(); + var keyword = input.Keyword?.Trim(); + var labelCategoryId = input.LabelCategoryId?.Trim(); + var labelTypeId = input.LabelTypeId?.Trim(); + var templateCode = input.TemplateCode?.Trim(); + + // 先查 label-product 映射(按产品) + var query = _dbContext.SqlSugarClient.Queryable( + (lp, l, p, c, t, tpl) => + lp.LabelId == l.Id && + lp.ProductId == p.Id && + l.LabelCategoryId == c.Id && + l.LabelTypeId == t.Id && + l.TemplateId == tpl.Id) + .Where((lp, l, p, c, t, tpl) => l.IsDeleted == false) + .Where((lp, l, p, c, t, tpl) => !c.IsDeleted) + .Where((lp, l, p, c, t, tpl) => !t.IsDeleted) + .Where((lp, l, p, c, t, tpl) => !tpl.IsDeleted) + .Where((lp, l, p, c, t, tpl) => !p.IsDeleted) + .WhereIF(!string.IsNullOrWhiteSpace(productId), (lp, l, p, c, t, tpl) => lp.ProductId == productId) + .WhereIF(!string.IsNullOrWhiteSpace(locationId), (lp, l, p, c, t, tpl) => l.LocationId == locationId) + .WhereIF(!string.IsNullOrWhiteSpace(labelCategoryId), (lp, l, p, c, t, tpl) => l.LabelCategoryId == labelCategoryId) + .WhereIF(!string.IsNullOrWhiteSpace(labelTypeId), (lp, l, p, c, t, tpl) => l.LabelTypeId == labelTypeId) + .WhereIF(!string.IsNullOrWhiteSpace(templateCode), (lp, l, p, c, t, tpl) => tpl.TemplateCode == templateCode) + .WhereIF(!string.IsNullOrWhiteSpace(keyword), + (lp, l, p, c, t, tpl) => + l.LabelName.Contains(keyword!) || + p.ProductName.Contains(keyword!) || + c.CategoryName.Contains(keyword!) || + t.TypeName.Contains(keyword!)) + .WhereIF(input.State != null, (lp, l, p, c, t, tpl) => l.State == input.State) + .OrderByDescending((lp, l, p, c, t, tpl) => l.LastModificationTime ?? l.CreationTime); + + if (!string.IsNullOrWhiteSpace(input.Sorting)) + { + query = query.OrderBy(input.Sorting); + } + + var entities = await query.Select((lp, l, p, c, t, tpl) => new + { + LabelCode = l.LabelCode, + LabelName = l.LabelName, + LocationId = l.LocationId, + LocationName = (string?)null, // later fill + LabelCategoryName = c.CategoryName, + ProductCategoryName = p.CategoryName, + ProductName = p.ProductName, + TemplateName = tpl.TemplateName, + LabelTypeName = t.TypeName, + State = l.State, + LastEdited = l.LastModificationTime ?? l.CreationTime + }) + .ToPageListAsync(input.SkipCount, input.MaxResultCount, total); + + var locationIds = entities + .Select(x => x.LocationId) + .Where(x => !string.IsNullOrWhiteSpace(x)) + .Select(x => x!.Trim()) + .Distinct() + .ToList(); + + var locationMap = new Dictionary(); + if (locationIds.Count > 0) + { + var locGuids = locationIds.Where(x => Guid.TryParse(x, out _)).Select(Guid.Parse).ToList(); + if (locGuids.Count > 0) + { + var locs = await _dbContext.SqlSugarClient.Queryable() + .Where(x => !x.IsDeleted) + .Where(x => locGuids.Contains(x.Id)) + .ToListAsync(); + locationMap = locs.ToDictionary(x => x.Id.ToString(), x => x); + } + } + + var items = entities.Select(x => + { + var locationName = string.Empty; + if (!string.IsNullOrWhiteSpace(x.LocationId) && locationMap.TryGetValue(x.LocationId!, out var loc)) + { + locationName = loc.LocationName ?? loc.LocationCode; + } + return new LabelGetListOutputDto + { + Id = x.LabelCode ?? string.Empty, + LabelName = x.LabelName ?? string.Empty, + LocationName = string.IsNullOrWhiteSpace(locationName) ? "无" : locationName, + LabelCategoryName = x.LabelCategoryName ?? string.Empty, + ProductCategoryName = x.ProductCategoryName ?? string.Empty, + ProductName = x.ProductName ?? string.Empty, + TemplateName = x.TemplateName ?? string.Empty, + LabelTypeName = x.LabelTypeName ?? string.Empty, + State = x.State, + LastEdited = x.LastEdited, + HasError = false + }; + }).ToList(); + + var pageSize = input.MaxResultCount <= 0 ? items.Count : input.MaxResultCount; + var pageIndex = pageSize <= 0 ? 1 : (input.SkipCount / pageSize) + 1; + var totalPages = pageSize <= 0 ? 0 : (int)Math.Ceiling(total / (double)pageSize); + + return new PagedResultWithPageDto + { + PageIndex = pageIndex, + PageSize = pageSize, + TotalCount = total, + TotalPages = totalPages, + Items = items + }; + } + + public async Task GetAsync(string id) + { + var labelCode = id?.Trim(); + if (string.IsNullOrWhiteSpace(labelCode)) + { + throw new UserFriendlyException("标签Code不能为空"); + } + + var label = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => !x.IsDeleted && x.LabelCode == labelCode); + if (label is null) + { + throw new UserFriendlyException("标签不存在"); + } + + var template = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => x.Id == label.TemplateId); + var category = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => x.Id == label.LabelCategoryId); + var type = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => x.Id == label.LabelTypeId); + + LocationAggregateRoot? location = null; + if (!string.IsNullOrWhiteSpace(label.LocationId) && Guid.TryParse(label.LocationId, out var locId)) + { + location = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => !x.IsDeleted && x.Id == locId); + } + + var productIds = await _dbContext.SqlSugarClient.Queryable() + .Where(x => x.LabelId == label.Id) + .Select(x => x.ProductId) + .ToListAsync(); + + object? labelInfo = null; + if (!string.IsNullOrWhiteSpace(label.LabelInfoJson)) + { + labelInfo = JsonSerializer.Deserialize(label.LabelInfoJson); + } + + return new LabelGetOutputDto + { + Id = label.LabelCode, + LabelName = label.LabelName, + LocationId = label.LocationId ?? string.Empty, + LocationName = location?.LocationName ?? location?.LocationCode ?? "无", + LabelCategoryId = label.LabelCategoryId ?? string.Empty, + LabelCategoryName = category?.CategoryName ?? "无", + LabelTypeId = label.LabelTypeId ?? string.Empty, + LabelTypeName = type?.TypeName ?? "无", + TemplateCode = template?.TemplateCode ?? string.Empty, + TemplateName = template?.TemplateName ?? string.Empty, + State = label.State, + LabelInfoJson = labelInfo, + ProductIds = productIds + }; + } + + [UnitOfWork] + public async Task CreateAsync(LabelCreateInputVo input) + { + var labelCode = input.LabelCode?.Trim(); + if (string.IsNullOrWhiteSpace(labelCode)) + { + labelCode = $"LBL_{_guidGenerator.Create():N}"; + } + + var labelName = input.LabelName?.Trim(); + if (string.IsNullOrWhiteSpace(labelName)) + { + throw new UserFriendlyException("标签名称不能为空"); + } + + if (input.ProductIds is null || input.ProductIds.Count == 0) + { + throw new UserFriendlyException("标签至少需要绑定一个产品"); + } + + if (string.IsNullOrWhiteSpace(input.TemplateCode)) + { + throw new UserFriendlyException("模板编码不能为空"); + } + if (string.IsNullOrWhiteSpace(input.LocationId)) + { + throw new UserFriendlyException("门店Id不能为空"); + } + if (string.IsNullOrWhiteSpace(input.LabelCategoryId)) + { + throw new UserFriendlyException("标签分类Id不能为空"); + } + if (string.IsNullOrWhiteSpace(input.LabelTypeId)) + { + throw new UserFriendlyException("标签类型Id不能为空"); + } + + var template = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => !x.IsDeleted && x.TemplateCode == input.TemplateCode.Trim()); + if (template is null) + { + throw new UserFriendlyException("模板不存在"); + } + + // 唯一性校验(LabelCode) + var exists = await _dbContext.SqlSugarClient.Queryable() + .AnyAsync(x => !x.IsDeleted && x.LabelCode == labelCode); + if (exists) + { + throw new UserFriendlyException("标签编码已存在"); + } + + // 插入 fl_label + var now = DateTime.Now; + var labelId = _guidGenerator.Create().ToString(); + var labelEntity = new FlLabelDbEntity + { + Id = labelId, + IsDeleted = false, + CreationTime = now, + CreatorId = CurrentUser?.Id?.ToString(), + LastModifierId = CurrentUser?.Id?.ToString(), + LastModificationTime = now, + ConcurrencyStamp = string.Empty, + LabelCode = labelCode, + LabelName = labelName, + TemplateId = template.Id, + LocationId = input.LocationId?.Trim(), + LabelCategoryId = input.LabelCategoryId?.Trim(), + LabelTypeId = input.LabelTypeId?.Trim(), + State = input.State, + LabelType = null, + LabelInfoJson = input.LabelInfoJson == null ? null : JsonSerializer.Serialize(input.LabelInfoJson) + }; + await _dbContext.SqlSugarClient.Insertable(labelEntity).ExecuteCommandAsync(); + + // 插入 fl_label_product + var productIds = input.ProductIds.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).Distinct().ToList(); + var rows = productIds.Select(pid => new FlLabelProductDbEntity + { + Id = _guidGenerator.Create().ToString(), + LabelId = labelId, + ProductId = pid + }).ToList(); + await _dbContext.SqlSugarClient.Insertable(rows).ExecuteCommandAsync(); + + return await GetAsync(labelCode); + } + + [UnitOfWork] + public async Task UpdateAsync(string id, LabelUpdateInputVo input) + { + var labelCode = id?.Trim(); + if (string.IsNullOrWhiteSpace(labelCode)) + { + throw new UserFriendlyException("标签Code不能为空"); + } + + var label = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => !x.IsDeleted && x.LabelCode == labelCode); + if (label is null) + { + throw new UserFriendlyException("标签不存在"); + } + + if (input.ProductIds is null || input.ProductIds.Count == 0) + { + throw new UserFriendlyException("标签至少需要绑定一个产品"); + } + + if (string.IsNullOrWhiteSpace(input.TemplateCode)) + { + throw new UserFriendlyException("模板编码不能为空"); + } + if (string.IsNullOrWhiteSpace(input.LocationId)) + { + throw new UserFriendlyException("门店Id不能为空"); + } + if (string.IsNullOrWhiteSpace(input.LabelCategoryId)) + { + throw new UserFriendlyException("标签分类Id不能为空"); + } + if (string.IsNullOrWhiteSpace(input.LabelTypeId)) + { + throw new UserFriendlyException("标签类型Id不能为空"); + } + + var template = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => !x.IsDeleted && x.TemplateCode == input.TemplateCode.Trim()); + if (template is null) + { + throw new UserFriendlyException("模板不存在"); + } + + var now = DateTime.Now; + label.LabelName = input.LabelName?.Trim() ?? label.LabelName; + label.TemplateId = template.Id; + label.LocationId = input.LocationId?.Trim(); + label.LabelCategoryId = input.LabelCategoryId?.Trim(); + label.LabelTypeId = input.LabelTypeId?.Trim(); + label.State = input.State; + label.LastModifierId = CurrentUser?.Id?.ToString(); + label.LastModificationTime = now; + label.LabelInfoJson = input.LabelInfoJson == null ? null : JsonSerializer.Serialize(input.LabelInfoJson); + + await _dbContext.SqlSugarClient.Updateable(label).ExecuteCommandAsync(); + + // 重建关联 + await _dbContext.SqlSugarClient.Deleteable() + .Where(x => x.LabelId == label.Id) + .ExecuteCommandAsync(); + + var productIds = input.ProductIds.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).Distinct().ToList(); + var rows = productIds.Select(pid => new FlLabelProductDbEntity + { + Id = _guidGenerator.Create().ToString(), + LabelId = label.Id, + ProductId = pid + }).ToList(); + await _dbContext.SqlSugarClient.Insertable(rows).ExecuteCommandAsync(); + + return await GetAsync(labelCode); + } + + [UnitOfWork] + public async Task DeleteAsync(string id) + { + var labelCode = id?.Trim(); + if (string.IsNullOrWhiteSpace(labelCode)) + { + return; + } + + var label = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => !x.IsDeleted && x.LabelCode == labelCode); + if (label is null) + { + return; + } + + label.IsDeleted = true; + label.LastModifierId = CurrentUser?.Id?.ToString(); + label.LastModificationTime = DateTime.Now; + await _dbContext.SqlSugarClient.Updateable(label).ExecuteCommandAsync(); + + await _dbContext.SqlSugarClient.Deleteable() + .Where(x => x.LabelId == label.Id) + .ExecuteCommandAsync(); + } + + /// + /// 标签预览:不落库,只把 template elements 的 AUTO_DB/PRINT_INPUT 渲染进 config + /// + [UnitOfWork] + public async Task PreviewAsync(LabelPreviewResolveInputVo input) + { + var labelCode = input?.LabelCode?.Trim(); + if (string.IsNullOrWhiteSpace(labelCode)) + { + throw new UserFriendlyException("labelCode不能为空"); + } + + var label = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => !x.IsDeleted && x.LabelCode == labelCode); + if (label is null) + { + throw new UserFriendlyException("标签不存在"); + } + + // 选择预览产品 + var productId = input?.ProductId?.Trim(); + if (string.IsNullOrWhiteSpace(productId)) + { + productId = await _dbContext.SqlSugarClient.Queryable() + .Where(x => x.LabelId == label.Id) + .Select(x => x.ProductId) + .FirstAsync(); + } + + if (string.IsNullOrWhiteSpace(productId)) + { + throw new UserFriendlyException("该标签未绑定产品,无法预览"); + } + + var product = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => !x.IsDeleted && x.Id == productId); + if (product is null) + { + throw new UserFriendlyException("预览产品不存在"); + } + + // 取模板头 & elements + var template = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => !x.IsDeleted && x.Id == label.TemplateId); + if (template is null) + { + throw new UserFriendlyException("模板不存在"); + } + + var elements = await _dbContext.SqlSugarClient.Queryable() + .Where(x => x.TemplateId == template.Id) + // SqlSugar 不提供 ThenBy,这里用组合排序键保证 OrderNum + ZIndex 的稳定顺序 + .OrderBy(x => x.OrderNum * 1000000 + x.ZIndex) + .ToListAsync(); + + // 解析 labelInfo(可用于补充一些数值) + Dictionary labelInfoDict = new(); + if (!string.IsNullOrWhiteSpace(label.LabelInfoJson)) + { + try + { + labelInfoDict = JsonSerializer.Deserialize>(label.LabelInfoJson) + ?? new Dictionary(); + } + catch + { + // ignore + } + } + + var printInputJson = input?.PrintInputJson ?? new Dictionary(); + + static int TryGetInt(object? v, int defaultValue) + { + if (v is null) return defaultValue; + if (v is int i) return i; + if (v is long l) return (int)l; + if (v is decimal d) return (int)d; + if (v is double db) return (int)db; + if (v is JsonElement je) + { + if (je.ValueKind == JsonValueKind.Number && je.TryGetInt32(out var ii)) return ii; + if (je.ValueKind == JsonValueKind.String && int.TryParse(je.GetString(), out var si)) return si; + } + if (v is string s && int.TryParse(s, out var st)) return st; + return defaultValue; + } + + static string? TryToString(object? v) + { + if (v is null) return null; + if (v is string s) return s; + if (v is JsonElement je) + { + if (je.ValueKind == JsonValueKind.String) return je.GetString(); + if (je.ValueKind == JsonValueKind.Number || je.ValueKind == JsonValueKind.True || je.ValueKind == JsonValueKind.False) + { + return je.ToString(); + } + } + return v.ToString(); + } + + static void UpsertConfigValue(Dictionary cfg, string key, object? value) + { + if (cfg.ContainsKey(key)) + { + cfg[key] = value; + return; + } + cfg.Add(key, value); + } + + Dictionary ParseConfig(string? configJson) + { + if (string.IsNullOrWhiteSpace(configJson)) + { + return new Dictionary(); + } + + try + { + return JsonSerializer.Deserialize>(configJson) + ?? new Dictionary(); + } + catch + { + return new Dictionary(); + } + } + + foreach (var el in elements) + { + // FIXED:不处理(直接保留 config) + // AUTO_DB:根据 type 做一个最小可用渲染(支持 TEXT_PRODUCT / BARCODE / QRCODE / DATE / TIME) + // PRINT_INPUT:按 InputKey 覆盖相应字段(优先级最高) + } + + var resolvedElements = elements.Select(el => + { + var cfg = ParseConfig(el.ConfigJson); + + // 1) 先做 AUTO_DB:按类型兜底填充 + if (string.Equals(el.ValueSourceType, "AUTO_DB", StringComparison.OrdinalIgnoreCase)) + { + switch (el.ElementType) + { + case "TEXT_PRODUCT": + UpsertConfigValue(cfg, "text", product.ProductName); + break; + case "TEXT_PRICE": + { + // price 可能在 labelInfo 或 printInput 里 + object? priceObj = null; + if (labelInfoDict.TryGetValue("price", out var p)) priceObj = p; + if (priceObj is null && printInputJson.TryGetValue("price", out var pp)) priceObj = pp; + var priceStr = TryToString(priceObj); + if (!string.IsNullOrWhiteSpace(priceStr)) + { + UpsertConfigValue(cfg, "text", priceStr); + } + } + break; + case "BARCODE": + if (!string.IsNullOrWhiteSpace(product.ProductCode)) + { + UpsertConfigValue(cfg, "data", product.ProductCode); + } + break; + case "QRCODE": + if (!string.IsNullOrWhiteSpace(product.ProductCode)) + { + UpsertConfigValue(cfg, "data", product.ProductCode); + } + break; + case "DATE": + { + var offsetDays = cfg.TryGetValue("offsetDays", out var od) ? TryGetInt(od, 0) : 0; + var dt = DateTime.Today.AddDays(offsetDays); + UpsertConfigValue(cfg, "format", dt.ToString("yyyy-MM-dd")); + cfg.Remove("inputType"); + } + break; + case "TIME": + { + var dt = DateTime.Now; + UpsertConfigValue(cfg, "format", dt.ToString("HH:mm")); + cfg.Remove("inputType"); + } + break; + } + } + + // 2) 再做 PRINT_INPUT:按 InputKey 覆盖(如果前端传了) + if (string.Equals(el.ValueSourceType, "PRINT_INPUT", StringComparison.OrdinalIgnoreCase) + && !string.IsNullOrWhiteSpace(el.InputKey) + && printInputJson.TryGetValue(el.InputKey!, out var inputValue)) + { + var s = TryToString(inputValue); + if (!string.IsNullOrWhiteSpace(s)) + { + switch (el.ElementType) + { + case "TEXT_STATIC": + case "TEXT_PRODUCT": + case "TEXT_PRICE": + UpsertConfigValue(cfg, "text", s); + cfg.Remove("inputType"); + break; + case "BARCODE": + case "QRCODE": + UpsertConfigValue(cfg, "data", s); + break; + case "DATE": + case "TIME": + UpsertConfigValue(cfg, "format", s); + cfg.Remove("inputType"); + break; + } + } + } + + // 3) FIXED:不做任何覆盖,保持模板/标签设计时写入的固定值(由前端/设计器落库) + + return new LabelTemplateElementDto + { + Id = el.ElementKey, + ElementType = el.ElementType, + PosX = el.PosX, + PosY = el.PosY, + Width = el.Width, + Height = el.Height, + Rotation = string.IsNullOrWhiteSpace(el.Rotation) ? "horizontal" : el.Rotation, + BorderType = string.IsNullOrWhiteSpace(el.BorderType) ? "none" : el.BorderType, + ZIndex = el.ZIndex, + OrderNum = el.OrderNum, + ValueSourceType = el.ValueSourceType, + BindingExpr = el.BindingExpr, + AutoQueryKey = el.AutoQueryKey, + InputKey = el.InputKey, + IsRequiredInput = el.IsRequiredInput, + ConfigJson = cfg + }; + }).ToList(); + + return new LabelTemplatePreviewDto + { + Id = template.TemplateCode, + Name = template.TemplateName, + LabelType = template.LabelType ?? string.Empty, + Unit = template.Unit, + Width = template.Width, + Height = template.Height, + AppliedLocation = template.AppliedLocationType, + ShowRuler = template.ShowRuler, + ShowGrid = template.ShowGrid, + Elements = resolvedElements + }; + } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelCategoryAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelCategoryAppService.cs new file mode 100644 index 0000000..334c418 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelCategoryAppService.cs @@ -0,0 +1,197 @@ +using FoodLabeling.Application.Contracts.Dtos.Common; +using FoodLabeling.Application.Contracts.Dtos.LabelCategory; +using FoodLabeling.Application.Contracts.IServices; +using FoodLabeling.Application.Services.DbModels; +using SqlSugar; +using Volo.Abp; +using Volo.Abp.Application.Services; +using Volo.Abp.Guids; +using Yi.Framework.SqlSugarCore.Abstractions; + +namespace FoodLabeling.Application.Services; + +public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppService +{ + private readonly ISqlSugarDbContext _dbContext; + private readonly IGuidGenerator _guidGenerator; + + public LabelCategoryAppService(ISqlSugarDbContext dbContext, IGuidGenerator guidGenerator) + { + _dbContext = dbContext; + _guidGenerator = guidGenerator; + } + + public async Task> GetListAsync(LabelCategoryGetListInputVo input) + { + RefAsync total = 0; + var keyword = input.Keyword?.Trim(); + + var query = _dbContext.SqlSugarClient.Queryable() + .Where(x => !x.IsDeleted) + .WhereIF(!string.IsNullOrWhiteSpace(keyword), + x => x.CategoryCode.Contains(keyword!) || x.CategoryName.Contains(keyword!)) + .WhereIF(input.State != null, x => x.State == input.State); + + if (!string.IsNullOrWhiteSpace(input.Sorting)) + { + query = query.OrderBy(input.Sorting); + } + else + { + query = query.OrderByDescending(x => x.OrderNum).OrderByDescending(x => x.CreationTime); + } + + var entities = await query.ToPageListAsync(input.SkipCount, input.MaxResultCount, total); + var ids = entities.Select(x => x.Id).ToList(); + + var countRows = await _dbContext.SqlSugarClient.Queryable() + .Where(x => !x.IsDeleted) + .Where(x => x.LabelCategoryId != null && ids.Contains(x.LabelCategoryId)) + .GroupBy(x => x.LabelCategoryId) + .Select(x => new { CategoryId = x.LabelCategoryId, Count = SqlFunc.AggregateCount(x.Id) }) + .ToListAsync(); + var countMap = countRows.ToDictionary(x => x.CategoryId!, x => (long)x.Count); + + var items = entities.Select(x => new LabelCategoryGetListOutputDto + { + Id = x.Id, + CategoryCode = x.CategoryCode, + CategoryName = x.CategoryName, + CategoryPhotoUrl = x.CategoryPhotoUrl, + State = x.State, + OrderNum = x.OrderNum, + NoOfLabels = countMap.TryGetValue(x.Id, out var count) ? count : 0, + LastEdited = x.LastModificationTime ?? x.CreationTime + }).ToList(); + + return BuildPagedResult(input.SkipCount, input.MaxResultCount, total, items); + } + + public async Task GetAsync(string id) + { + var entity = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => x.Id == id && !x.IsDeleted); + if (entity is null) + { + throw new UserFriendlyException("标签分类不存在"); + } + + return MapToGetOutput(entity); + } + + public async Task CreateAsync(LabelCategoryCreateInputVo input) + { + var code = input.CategoryCode?.Trim(); + var name = input.CategoryName?.Trim(); + if (string.IsNullOrWhiteSpace(code) || string.IsNullOrWhiteSpace(name)) + { + throw new UserFriendlyException("分类编码和名称不能为空"); + } + + var duplicated = await _dbContext.SqlSugarClient.Queryable() + .AnyAsync(x => !x.IsDeleted && (x.CategoryCode == code || x.CategoryName == name)); + if (duplicated) + { + throw new UserFriendlyException("分类编码或名称已存在"); + } + + var entity = new FlLabelCategoryDbEntity + { + Id = _guidGenerator.Create().ToString(), + CategoryCode = code, + CategoryName = name, + CategoryPhotoUrl = input.CategoryPhotoUrl?.Trim(), + State = input.State, + OrderNum = input.OrderNum + }; + + await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync(); + return await GetAsync(entity.Id); + } + + public async Task UpdateAsync(string id, LabelCategoryUpdateInputVo input) + { + var entity = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => x.Id == id && !x.IsDeleted); + if (entity is null) + { + throw new UserFriendlyException("标签分类不存在"); + } + + var code = input.CategoryCode?.Trim(); + var name = input.CategoryName?.Trim(); + if (string.IsNullOrWhiteSpace(code) || string.IsNullOrWhiteSpace(name)) + { + throw new UserFriendlyException("分类编码和名称不能为空"); + } + + var duplicated = await _dbContext.SqlSugarClient.Queryable() + .AnyAsync(x => !x.IsDeleted && x.Id != id && (x.CategoryCode == code || x.CategoryName == name)); + if (duplicated) + { + throw new UserFriendlyException("分类编码或名称已存在"); + } + + entity.CategoryCode = code; + entity.CategoryName = name; + entity.CategoryPhotoUrl = input.CategoryPhotoUrl?.Trim(); + entity.State = input.State; + entity.OrderNum = input.OrderNum; + entity.LastModificationTime = DateTime.Now; + entity.LastModifierId = CurrentUser?.Id?.ToString(); + + await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync(); + return await GetAsync(id); + } + + public async Task DeleteAsync(string id) + { + var entity = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => x.Id == id && !x.IsDeleted); + if (entity is null) + { + return; + } + + var used = await _dbContext.SqlSugarClient.Queryable() + .AnyAsync(x => !x.IsDeleted && x.LabelCategoryId == id); + if (used) + { + throw new UserFriendlyException("该标签分类已被标签引用,无法删除"); + } + + entity.IsDeleted = true; + entity.LastModificationTime = DateTime.Now; + entity.LastModifierId = CurrentUser?.Id?.ToString(); + await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync(); + } + + private static LabelCategoryGetOutputDto MapToGetOutput(FlLabelCategoryDbEntity x) + { + return new LabelCategoryGetOutputDto + { + Id = x.Id, + CategoryCode = x.CategoryCode, + CategoryName = x.CategoryName, + CategoryPhotoUrl = x.CategoryPhotoUrl, + State = x.State, + OrderNum = x.OrderNum + }; + } + + private static PagedResultWithPageDto BuildPagedResult(int skipCount, int maxResultCount, int total, List items) + { + var pageSize = maxResultCount <= 0 ? items.Count : maxResultCount; + var pageIndex = pageSize <= 0 ? 1 : (skipCount / pageSize) + 1; + var totalPages = pageSize <= 0 ? 0 : (int)Math.Ceiling(total / (double)pageSize); + return new PagedResultWithPageDto + { + PageIndex = pageIndex, + PageSize = pageSize, + TotalCount = total, + TotalPages = totalPages, + Items = items + }; + } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelMultipleOptionAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelMultipleOptionAppService.cs new file mode 100644 index 0000000..ba8e7b2 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelMultipleOptionAppService.cs @@ -0,0 +1,173 @@ +using FoodLabeling.Application.Contracts.Dtos.Common; +using FoodLabeling.Application.Contracts.Dtos.LabelMultipleOption; +using FoodLabeling.Application.Contracts.IServices; +using FoodLabeling.Application.Services.DbModels; +using SqlSugar; +using Volo.Abp; +using Volo.Abp.Application.Services; +using Volo.Abp.Guids; +using Yi.Framework.SqlSugarCore.Abstractions; + +namespace FoodLabeling.Application.Services; + +public class LabelMultipleOptionAppService : ApplicationService, ILabelMultipleOptionAppService +{ + private readonly ISqlSugarDbContext _dbContext; + private readonly IGuidGenerator _guidGenerator; + + public LabelMultipleOptionAppService(ISqlSugarDbContext dbContext, IGuidGenerator guidGenerator) + { + _dbContext = dbContext; + _guidGenerator = guidGenerator; + } + + public async Task> GetListAsync(LabelMultipleOptionGetListInputVo input) + { + RefAsync total = 0; + var keyword = input.Keyword?.Trim(); + + var query = _dbContext.SqlSugarClient.Queryable() + .Where(x => !x.IsDeleted) + .WhereIF(!string.IsNullOrWhiteSpace(keyword), x => x.OptionCode.Contains(keyword!) || x.OptionName.Contains(keyword!)) + .WhereIF(input.State != null, x => x.State == input.State); + + if (!string.IsNullOrWhiteSpace(input.Sorting)) + { + query = query.OrderBy(input.Sorting); + } + else + { + query = query.OrderByDescending(x => x.OrderNum).OrderByDescending(x => x.CreationTime); + } + + var entities = await query.ToPageListAsync(input.SkipCount, input.MaxResultCount, total); + var items = entities.Select(x => new LabelMultipleOptionGetListOutputDto + { + Id = x.Id, + OptionCode = x.OptionCode, + OptionName = x.OptionName, + OptionValuesJson = x.OptionValuesJson, + State = x.State, + OrderNum = x.OrderNum, + LastEdited = x.LastModificationTime ?? x.CreationTime + }).ToList(); + + return BuildPagedResult(input.SkipCount, input.MaxResultCount, total, items); + } + + public async Task GetAsync(string id) + { + var entity = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => x.Id == id && !x.IsDeleted); + if (entity is null) + { + throw new UserFriendlyException("多选项不存在"); + } + + return new LabelMultipleOptionGetOutputDto + { + Id = entity.Id, + OptionCode = entity.OptionCode, + OptionName = entity.OptionName, + OptionValuesJson = entity.OptionValuesJson, + State = entity.State, + OrderNum = entity.OrderNum + }; + } + + public async Task CreateAsync(LabelMultipleOptionCreateInputVo input) + { + var code = input.OptionCode?.Trim(); + var name = input.OptionName?.Trim(); + if (string.IsNullOrWhiteSpace(code) || string.IsNullOrWhiteSpace(name)) + { + throw new UserFriendlyException("多选项编码和名称不能为空"); + } + + var duplicated = await _dbContext.SqlSugarClient.Queryable() + .AnyAsync(x => !x.IsDeleted && (x.OptionCode == code || x.OptionName == name)); + if (duplicated) + { + throw new UserFriendlyException("多选项编码或名称已存在"); + } + + var entity = new FlLabelMultipleOptionDbEntity + { + Id = _guidGenerator.Create().ToString(), + OptionCode = code, + OptionName = name, + OptionValuesJson = input.OptionValuesJson?.Trim(), + State = input.State, + OrderNum = input.OrderNum + }; + + await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync(); + return await GetAsync(entity.Id); + } + + public async Task UpdateAsync(string id, LabelMultipleOptionUpdateInputVo input) + { + var entity = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => x.Id == id && !x.IsDeleted); + if (entity is null) + { + throw new UserFriendlyException("多选项不存在"); + } + + var code = input.OptionCode?.Trim(); + var name = input.OptionName?.Trim(); + if (string.IsNullOrWhiteSpace(code) || string.IsNullOrWhiteSpace(name)) + { + throw new UserFriendlyException("多选项编码和名称不能为空"); + } + + var duplicated = await _dbContext.SqlSugarClient.Queryable() + .AnyAsync(x => !x.IsDeleted && x.Id != id && (x.OptionCode == code || x.OptionName == name)); + if (duplicated) + { + throw new UserFriendlyException("多选项编码或名称已存在"); + } + + entity.OptionCode = code; + entity.OptionName = name; + entity.OptionValuesJson = input.OptionValuesJson?.Trim(); + entity.State = input.State; + entity.OrderNum = input.OrderNum; + entity.LastModificationTime = DateTime.Now; + entity.LastModifierId = CurrentUser?.Id?.ToString(); + + await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync(); + return await GetAsync(id); + } + + public async Task DeleteAsync(string id) + { + var entity = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => x.Id == id && !x.IsDeleted); + if (entity is null) + { + return; + } + + entity.IsDeleted = true; + entity.LastModificationTime = DateTime.Now; + entity.LastModifierId = CurrentUser?.Id?.ToString(); + await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync(); + } + + private static PagedResultWithPageDto BuildPagedResult(int skipCount, int maxResultCount, int total, List items) + { + var pageSize = maxResultCount <= 0 ? items.Count : maxResultCount; + var pageIndex = pageSize <= 0 ? 1 : (skipCount / pageSize) + 1; + var totalPages = pageSize <= 0 ? 0 : (int)Math.Ceiling(total / (double)pageSize); + return new PagedResultWithPageDto + { + PageIndex = pageIndex, + PageSize = pageSize, + TotalCount = total, + TotalPages = totalPages, + Items = items + }; + } +} + 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 new file mode 100644 index 0000000..fbdba75 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelTemplateAppService.cs @@ -0,0 +1,429 @@ +using System.Text.Json; +using FoodLabeling.Application.Contracts.Dtos.Common; +using FoodLabeling.Application.Contracts.Dtos.LabelTemplate; +using FoodLabeling.Application.Contracts.IServices; +using FoodLabeling.Application.Services.DbModels; +using FoodLabeling.Domain.Entities; +using SqlSugar; +using Volo.Abp; +using Volo.Abp.Application.Services; +using Volo.Abp.Guids; +using Volo.Abp.Uow; +using Yi.Framework.SqlSugarCore.Abstractions; + +namespace FoodLabeling.Application.Services; + +/// +/// 标签模板管理(Label Templates) +/// +public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppService +{ + private readonly ISqlSugarDbContext _dbContext; + private readonly IGuidGenerator _guidGenerator; + + public LabelTemplateAppService(ISqlSugarDbContext dbContext, IGuidGenerator guidGenerator) + { + _dbContext = dbContext; + _guidGenerator = guidGenerator; + } + + public async Task> GetListAsync(LabelTemplateGetListInputVo input) + { + RefAsync total = 0; + var keyword = input.Keyword?.Trim(); + var locationId = input.LocationId?.Trim(); + var specifiedTemplateIds = new HashSet(); + + if (!string.IsNullOrWhiteSpace(locationId)) + { + var rows = await _dbContext.SqlSugarClient.Queryable() + .Where(x => x.LocationId == locationId) + .Select(x => x.TemplateId) + .ToListAsync(); + + specifiedTemplateIds = rows.ToHashSet(); + } + + var query = _dbContext.SqlSugarClient.Queryable() + .Where(x => !x.IsDeleted) + .WhereIF(!string.IsNullOrWhiteSpace(keyword), x => + x.TemplateName.Contains(keyword!) || x.TemplateCode.Contains(keyword!)) + .WhereIF(!string.IsNullOrWhiteSpace(input.LabelType), x => x.LabelType == input.LabelType) + .WhereIF(input.State != null, x => x.State == input.State); + + if (!string.IsNullOrWhiteSpace(locationId)) + { + query = query.Where(x => x.AppliedLocationType == "ALL" || specifiedTemplateIds.Contains(x.Id)); + } + + query = !string.IsNullOrWhiteSpace(input.Sorting) + ? query.OrderBy(input.Sorting) + : query.OrderByDescending(x => x.LastModificationTime ?? x.CreationTime); + + var pageEntities = await query.ToPageListAsync(input.SkipCount, input.MaxResultCount, total); + var templateIds = pageEntities.Select(x => x.Id).ToList(); + + // element count (Contents) + var elementCounts = await _dbContext.SqlSugarClient.Queryable() + .Where(x => templateIds.Contains(x.TemplateId)) + .GroupBy(x => x.TemplateId) + .Select(x => new { TemplateId = x.TemplateId, Count = SqlFunc.AggregateCount(x.Id) }) + .ToListAsync(); + var elementCountMap = elementCounts.ToDictionary(x => x.TemplateId, x => (int)x.Count); + + // applied locations (for LocationText) + var templateLocationRows = await _dbContext.SqlSugarClient.Queryable() + .Where(x => templateIds.Contains(x.TemplateId)) + .ToListAsync(); + + var locationIdSet = templateLocationRows.Select(x => x.LocationId).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList(); + var locationGuidList = locationIdSet + .Where(x => Guid.TryParse(x, out _)) + .Select(Guid.Parse) + .Distinct() + .ToList(); + + var locationMap = new Dictionary(); + if (locationGuidList.Count > 0) + { + var locations = await _dbContext.SqlSugarClient.Queryable() + .Where(x => !x.IsDeleted) + .Where(x => locationGuidList.Contains(x.Id)) + .ToListAsync(); + locationMap = locations.ToDictionary(x => x.Id, x => x); + } + + string GetFirstLocationName(string templateDbId) + { + var first = templateLocationRows.FirstOrDefault(x => x.TemplateId == templateDbId); + if (first is null) return "Specified"; + if (Guid.TryParse(first.LocationId, out var gid) && locationMap.TryGetValue(gid, out var loc)) + { + return loc.LocationName ?? loc.LocationCode; + } + return "Specified"; + } + + var items = pageEntities.Select(x => + { + var lastEdited = x.LastModificationTime ?? x.CreationTime; + var contentsCount = elementCountMap.TryGetValue(x.Id, out var c) ? c : 0; + var locationText = x.AppliedLocationType == "ALL" ? "All Locations" : GetFirstLocationName(x.Id); + var sizeText = $"{x.Width}x{x.Height}{x.Unit}"; + + return new LabelTemplateGetListOutputDto + { + Id = x.TemplateCode, // front-end uses templateCode as identifier + TemplateCode = x.TemplateCode, + TemplateName = x.TemplateName, + LabelType = x.LabelType, + LocationText = locationText, + ContentsCount = contentsCount, + SizeText = sizeText, + VersionNo = x.VersionNo, + LastEdited = lastEdited + }; + }).ToList(); + + return BuildPagedResult(input.SkipCount, input.MaxResultCount, total, items); + } + + public async Task GetAsync(string id) + { + var template = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => !x.IsDeleted && x.TemplateCode == id); + if (template is null) + { + throw new UserFriendlyException("模板不存在"); + } + + var elements = await _dbContext.SqlSugarClient.Queryable() + .Where(x => x.TemplateId == template.Id) + .OrderBy(x => x.OrderNum) + .ToListAsync(); + + List MapElements() + { + return elements.Select(e => + { + object? cfg = null; + if (!string.IsNullOrWhiteSpace(e.ConfigJson)) + { + cfg = JsonSerializer.Deserialize(e.ConfigJson); + } + + return new LabelTemplateElementDto + { + Id = e.ElementKey, + ElementType = e.ElementType, + PosX = e.PosX, + PosY = e.PosY, + Width = e.Width, + Height = e.Height, + Rotation = e.Rotation, + BorderType = e.BorderType, + ZIndex = e.ZIndex, + OrderNum = e.OrderNum, + ValueSourceType = e.ValueSourceType, + BindingExpr = e.BindingExpr, + AutoQueryKey = e.AutoQueryKey, + InputKey = e.InputKey, + IsRequiredInput = e.IsRequiredInput, + ConfigJson = cfg + }; + }).ToList(); + } + + var appliedLocationIds = await _dbContext.SqlSugarClient.Queryable() + .Where(x => x.TemplateId == template.Id) + .Select(x => x.LocationId) + .ToListAsync(); + + return new LabelTemplateGetOutputDto + { + Id = template.TemplateCode, + TemplateCode = template.TemplateCode, + TemplateName = template.TemplateName, + LabelType = template.LabelType, + Unit = template.Unit, + Width = template.Width, + Height = template.Height, + AppliedLocationType = template.AppliedLocationType, + ShowRuler = template.ShowRuler, + ShowGrid = template.ShowGrid, + VersionNo = template.VersionNo, + State = template.State, + Elements = MapElements(), + AppliedLocationIds = appliedLocationIds + }; + } + + [UnitOfWork] + public async Task CreateAsync(LabelTemplateCreateInputVo input) + { + var code = input.TemplateCode?.Trim(); + var name = input.TemplateName?.Trim(); + if (string.IsNullOrWhiteSpace(code)) + { + throw new UserFriendlyException("模板编码不能为空"); + } + if (string.IsNullOrWhiteSpace(name)) + { + throw new UserFriendlyException("模板名称不能为空"); + } + + var duplicated = await _dbContext.SqlSugarClient.Queryable() + .AnyAsync(x => !x.IsDeleted && x.TemplateCode == code); + if (duplicated) + { + throw new UserFriendlyException("模板编码已存在"); + } + + var now = DateTime.Now; + var templateId = _guidGenerator.Create().ToString(); + var entity = new FlLabelTemplateDbEntity + { + Id = templateId, + IsDeleted = false, + CreationTime = now, + CreatorId = CurrentUser?.Id?.ToString(), + LastModifierId = CurrentUser?.Id?.ToString(), + LastModificationTime = now, + ConcurrencyStamp = string.Empty, + TemplateCode = code, + TemplateName = name, + LabelType = input.LabelType, + Unit = string.IsNullOrWhiteSpace(input.Unit) ? "inch" : input.Unit.Trim(), + Width = input.Width, + Height = input.Height, + AppliedLocationType = string.IsNullOrWhiteSpace(input.AppliedLocationType) ? "ALL" : input.AppliedLocationType.Trim(), + ShowRuler = input.ShowRuler, + ShowGrid = input.ShowGrid, + VersionNo = 1, + State = input.State + }; + + await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync(); + + await RebuildTemplateElementsAndLocationsAsync(entity.Id, input.Elements, entity.AppliedLocationType, input.AppliedLocationIds); + + return await GetAsync(code); + } + + [UnitOfWork] + public async Task UpdateAsync(string id, LabelTemplateUpdateInputVo input) + { + var template = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => !x.IsDeleted && x.TemplateCode == id); + if (template is null) + { + throw new UserFriendlyException("模板不存在"); + } + + var code = input.TemplateCode?.Trim(); + var name = input.TemplateName?.Trim(); + if (!string.IsNullOrWhiteSpace(code) && !string.Equals(code, template.TemplateCode, StringComparison.OrdinalIgnoreCase)) + { + var duplicated = await _dbContext.SqlSugarClient.Queryable() + .AnyAsync(x => !x.IsDeleted && x.TemplateCode == code); + if (duplicated) + { + throw new UserFriendlyException("模板编码已存在"); + } + } + + template.TemplateName = name ?? template.TemplateName; + template.LabelType = input.LabelType; + template.Unit = string.IsNullOrWhiteSpace(input.Unit) ? template.Unit : input.Unit.Trim(); + template.Width = input.Width; + template.Height = input.Height; + template.AppliedLocationType = string.IsNullOrWhiteSpace(input.AppliedLocationType) ? template.AppliedLocationType : input.AppliedLocationType.Trim(); + template.ShowRuler = input.ShowRuler; + template.ShowGrid = input.ShowGrid; + template.State = input.State; + template.VersionNo = template.VersionNo + 1; + template.LastModifierId = CurrentUser?.Id?.ToString(); + template.LastModificationTime = DateTime.Now; + if (!string.IsNullOrWhiteSpace(code)) + { + template.TemplateCode = code; + } + + await _dbContext.SqlSugarClient.Updateable(template).ExecuteCommandAsync(); + + await RebuildTemplateElementsAndLocationsAsync(template.Id, input.Elements, template.AppliedLocationType, input.AppliedLocationIds); + + return await GetAsync(template.TemplateCode); + } + + [UnitOfWork] + public async Task DeleteAsync(string id) + { + var template = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => !x.IsDeleted && x.TemplateCode == id); + if (template is null) + { + return; + } + + var used = await _dbContext.SqlSugarClient.Queryable() + .AnyAsync(x => !x.IsDeleted && x.TemplateId == template.Id); + + if (used) + { + throw new UserFriendlyException("该模板已被标签引用,无法删除"); + } + + template.IsDeleted = true; + template.LastModifierId = CurrentUser?.Id?.ToString(); + template.LastModificationTime = DateTime.Now; + await _dbContext.SqlSugarClient.Updateable(template).ExecuteCommandAsync(); + + // 删除子表数据(子表无 IsDeleted 字段) + 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( + string templateDbId, + List elements, + string appliedLocationType, + List appliedLocationIds) + { + // elements 重建 + await _dbContext.SqlSugarClient.Deleteable() + .Where(x => x.TemplateId == templateDbId) + .ExecuteCommandAsync(); + + if (elements is not null && elements.Count > 0) + { + var now = DateTime.Now; + var rows = elements.Select(e => + { + object? cfg = e.ConfigJson; + var configJson = cfg == null ? null : JsonSerializer.Serialize(cfg); + return new FlLabelTemplateElementDbEntity + { + Id = _guidGenerator.Create().ToString(), + TemplateId = templateDbId, + ElementKey = e.Id, + ElementType = e.ElementType, + PosX = e.PosX, + PosY = e.PosY, + Width = e.Width, + Height = e.Height, + Rotation = string.IsNullOrWhiteSpace(e.Rotation) ? "horizontal" : e.Rotation, + BorderType = string.IsNullOrWhiteSpace(e.BorderType) ? "none" : e.BorderType, + ZIndex = e.ZIndex, + OrderNum = e.OrderNum, + ValueSourceType = string.IsNullOrWhiteSpace(e.ValueSourceType) ? "FIXED" : e.ValueSourceType, + BindingExpr = e.BindingExpr, + AutoQueryKey = e.AutoQueryKey, + InputKey = e.InputKey, + IsRequiredInput = e.IsRequiredInput, + ConfigJson = configJson + }; + }).ToList(); + + await _dbContext.SqlSugarClient.Insertable(rows).ExecuteCommandAsync(); + } + + // applied locations 重建 + await _dbContext.SqlSugarClient.Deleteable() + .Where(x => x.TemplateId == templateDbId) + .ExecuteCommandAsync(); + + if (string.Equals(appliedLocationType, "SPECIFIED", StringComparison.OrdinalIgnoreCase)) + { + var ids = appliedLocationIds?.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).Distinct().ToList() ?? new(); + if (ids.Count == 0) + { + throw new UserFriendlyException("指定门店模板必须至少选择一个门店"); + } + + var locRows = ids.Select(locId => new FlLabelTemplateLocationDbEntity + { + Id = _guidGenerator.Create().ToString(), + TemplateId = templateDbId, + LocationId = locId + }).ToList(); + + await _dbContext.SqlSugarClient.Insertable(locRows).ExecuteCommandAsync(); + } + } + + private static PagedResultWithPageDto BuildPagedResult(int skipCount, int maxResultCount, int total, List items) + { + var pageSize = maxResultCount <= 0 ? items.Count : maxResultCount; + var pageIndex = pageSize <= 0 ? 1 : (skipCount / pageSize) + 1; + var totalPages = pageSize <= 0 ? 0 : (int)Math.Ceiling(total / (double)pageSize); + return new PagedResultWithPageDto + { + PageIndex = pageIndex, + PageSize = pageSize, + TotalCount = total, + TotalPages = totalPages, + Items = items + }; + } + + private static PagedResultWithPageDto BuildPagedResult(int skipCount, int maxResultCount, RefAsync total, List items) + { + var pageSize = maxResultCount <= 0 ? items.Count : maxResultCount; + var pageIndex = pageSize <= 0 ? 1 : (skipCount / pageSize) + 1; + var totalPages = pageSize <= 0 ? 0 : (int)Math.Ceiling(total.Value / (double)pageSize); + return new PagedResultWithPageDto + { + PageIndex = pageIndex, + PageSize = pageSize, + TotalCount = total.Value, + TotalPages = totalPages, + Items = items + }; + } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelTypeAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelTypeAppService.cs new file mode 100644 index 0000000..e76ca4f --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelTypeAppService.cs @@ -0,0 +1,192 @@ +using FoodLabeling.Application.Contracts.Dtos.Common; +using FoodLabeling.Application.Contracts.Dtos.LabelType; +using FoodLabeling.Application.Contracts.IServices; +using FoodLabeling.Application.Services.DbModels; +using SqlSugar; +using Volo.Abp; +using Volo.Abp.Application.Services; +using Volo.Abp.Guids; +using Yi.Framework.SqlSugarCore.Abstractions; + +namespace FoodLabeling.Application.Services; + +public class LabelTypeAppService : ApplicationService, ILabelTypeAppService +{ + private readonly ISqlSugarDbContext _dbContext; + private readonly IGuidGenerator _guidGenerator; + + public LabelTypeAppService(ISqlSugarDbContext dbContext, IGuidGenerator guidGenerator) + { + _dbContext = dbContext; + _guidGenerator = guidGenerator; + } + + public async Task> GetListAsync(LabelTypeGetListInputVo input) + { + RefAsync total = 0; + var keyword = input.Keyword?.Trim(); + + var query = _dbContext.SqlSugarClient.Queryable() + .Where(x => !x.IsDeleted) + .WhereIF(!string.IsNullOrWhiteSpace(keyword), x => x.TypeCode.Contains(keyword!) || x.TypeName.Contains(keyword!)) + .WhereIF(input.State != null, x => x.State == input.State); + + if (!string.IsNullOrWhiteSpace(input.Sorting)) + { + query = query.OrderBy(input.Sorting); + } + else + { + query = query.OrderByDescending(x => x.OrderNum).OrderByDescending(x => x.CreationTime); + } + + var entities = await query.ToPageListAsync(input.SkipCount, input.MaxResultCount, total); + var ids = entities.Select(x => x.Id).ToList(); + + var countRows = await _dbContext.SqlSugarClient.Queryable() + .Where(x => !x.IsDeleted) + .Where(x => x.LabelTypeId != null && ids.Contains(x.LabelTypeId)) + .GroupBy(x => x.LabelTypeId) + .Select(x => new { TypeId = x.LabelTypeId, Count = SqlFunc.AggregateCount(x.Id) }) + .ToListAsync(); + var countMap = countRows.ToDictionary(x => x.TypeId!, x => (long)x.Count); + + var items = entities.Select(x => new LabelTypeGetListOutputDto + { + Id = x.Id, + TypeCode = x.TypeCode, + TypeName = x.TypeName, + State = x.State, + OrderNum = x.OrderNum, + NoOfLabels = countMap.TryGetValue(x.Id, out var count) ? count : 0, + LastEdited = x.LastModificationTime ?? x.CreationTime + }).ToList(); + + return BuildPagedResult(input.SkipCount, input.MaxResultCount, total, items); + } + + public async Task GetAsync(string id) + { + var entity = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => x.Id == id && !x.IsDeleted); + if (entity is null) + { + throw new UserFriendlyException("标签类型不存在"); + } + + return MapToGetOutput(entity); + } + + public async Task CreateAsync(LabelTypeCreateInputVo input) + { + var code = input.TypeCode?.Trim(); + var name = input.TypeName?.Trim(); + if (string.IsNullOrWhiteSpace(code) || string.IsNullOrWhiteSpace(name)) + { + throw new UserFriendlyException("类型编码和名称不能为空"); + } + + var duplicated = await _dbContext.SqlSugarClient.Queryable() + .AnyAsync(x => !x.IsDeleted && (x.TypeCode == code || x.TypeName == name)); + if (duplicated) + { + throw new UserFriendlyException("类型编码或名称已存在"); + } + + var entity = new FlLabelTypeDbEntity + { + Id = _guidGenerator.Create().ToString(), + TypeCode = code, + TypeName = name, + State = input.State, + OrderNum = input.OrderNum + }; + + await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync(); + return await GetAsync(entity.Id); + } + + public async Task UpdateAsync(string id, LabelTypeUpdateInputVo input) + { + var entity = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => x.Id == id && !x.IsDeleted); + if (entity is null) + { + throw new UserFriendlyException("标签类型不存在"); + } + + var code = input.TypeCode?.Trim(); + var name = input.TypeName?.Trim(); + if (string.IsNullOrWhiteSpace(code) || string.IsNullOrWhiteSpace(name)) + { + throw new UserFriendlyException("类型编码和名称不能为空"); + } + + var duplicated = await _dbContext.SqlSugarClient.Queryable() + .AnyAsync(x => !x.IsDeleted && x.Id != id && (x.TypeCode == code || x.TypeName == name)); + if (duplicated) + { + throw new UserFriendlyException("类型编码或名称已存在"); + } + + entity.TypeCode = code; + entity.TypeName = name; + entity.State = input.State; + entity.OrderNum = input.OrderNum; + entity.LastModificationTime = DateTime.Now; + entity.LastModifierId = CurrentUser?.Id?.ToString(); + + await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync(); + return await GetAsync(id); + } + + public async Task DeleteAsync(string id) + { + var entity = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => x.Id == id && !x.IsDeleted); + if (entity is null) + { + return; + } + + var used = await _dbContext.SqlSugarClient.Queryable() + .AnyAsync(x => !x.IsDeleted && x.LabelTypeId == id); + if (used) + { + throw new UserFriendlyException("该标签类型已被标签引用,无法删除"); + } + + entity.IsDeleted = true; + entity.LastModificationTime = DateTime.Now; + entity.LastModifierId = CurrentUser?.Id?.ToString(); + await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync(); + } + + private static LabelTypeGetOutputDto MapToGetOutput(FlLabelTypeDbEntity x) + { + return new LabelTypeGetOutputDto + { + Id = x.Id, + TypeCode = x.TypeCode, + TypeName = x.TypeName, + State = x.State, + OrderNum = x.OrderNum + }; + } + + private static PagedResultWithPageDto BuildPagedResult(int skipCount, int maxResultCount, int total, List items) + { + var pageSize = maxResultCount <= 0 ? items.Count : maxResultCount; + var pageIndex = pageSize <= 0 ? 1 : (skipCount / pageSize) + 1; + var totalPages = pageSize <= 0 ? 0 : (int)Math.Ceiling(total / (double)pageSize); + return new PagedResultWithPageDto + { + PageIndex = pageIndex, + PageSize = pageSize, + TotalCount = total, + TotalPages = totalPages, + Items = items + }; + } +} +