Commit 919c1b6a2ef8ce71ea81d145d2d5966aa6885b50

Authored by 李曜臣
1 parent 2748a56b

1

Showing 45 changed files with 2629 additions and 0 deletions
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelCreateInputVo.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.Label;
  2 +
  3 +public class LabelCreateInputVo
  4 +{
  5 + public string LabelCode { get; set; } = string.Empty;
  6 +
  7 + public string LabelName { get; set; } = string.Empty;
  8 +
  9 + public string TemplateCode { get; set; } = string.Empty;
  10 +
  11 + public string LocationId { get; set; } = string.Empty;
  12 +
  13 + public string LabelCategoryId { get; set; } = string.Empty;
  14 +
  15 + public string LabelTypeId { get; set; } = string.Empty;
  16 +
  17 + public List<string> ProductIds { get; set; } = new();
  18 +
  19 + public object? LabelInfoJson { get; set; }
  20 +
  21 + public bool State { get; set; } = true;
  22 +}
  23 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelGetListInputVo.cs 0 → 100644
  1 +using Volo.Abp.Application.Dtos;
  2 +
  3 +namespace FoodLabeling.Application.Contracts.Dtos.Label;
  4 +
  5 +/// <summary>
  6 +/// 标签分页列表入参(按产品展示多个标签)
  7 +/// </summary>
  8 +public class LabelGetListInputVo : PagedAndSortedResultRequestDto
  9 +{
  10 + public string? Keyword { get; set; }
  11 +
  12 + /// <summary>
  13 + /// 门店Id(可选)
  14 + /// </summary>
  15 + public string? LocationId { get; set; }
  16 +
  17 + /// <summary>
  18 + /// 产品Id(可选;用于“一个产品展示多个标签”)
  19 + /// </summary>
  20 + public string? ProductId { get; set; }
  21 +
  22 + public string? LabelCategoryId { get; set; }
  23 +
  24 + public string? LabelTypeId { get; set; }
  25 +
  26 + /// <summary>
  27 + /// 模板编码(可选)
  28 + /// </summary>
  29 + public string? TemplateCode { get; set; }
  30 +
  31 + public bool? State { get; set; }
  32 +}
  33 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelGetListOutputDto.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.Label;
  2 +
  3 +public class LabelGetListOutputDto
  4 +{
  5 + public string Id { get; set; } = string.Empty;
  6 +
  7 + public string LabelName { get; set; } = string.Empty;
  8 +
  9 + public string LocationName { get; set; } = string.Empty;
  10 +
  11 + public string LabelCategoryName { get; set; } = string.Empty;
  12 +
  13 + public string ProductCategoryName { get; set; } = string.Empty;
  14 +
  15 + public string ProductName { get; set; } = string.Empty;
  16 +
  17 + public string TemplateName { get; set; } = string.Empty;
  18 +
  19 + public string LabelTypeName { get; set; } = string.Empty;
  20 +
  21 + public bool State { get; set; }
  22 +
  23 + public DateTime LastEdited { get; set; }
  24 +
  25 + /// <summary>
  26 + /// 模板缺失字段告警(当前阶段暂定 false)
  27 + /// </summary>
  28 + public bool HasError { get; set; }
  29 +}
  30 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelGetOutputDto.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.Label;
  2 +
  3 +public class LabelGetOutputDto
  4 +{
  5 + public string Id { get; set; } = string.Empty;
  6 +
  7 + public string LabelName { get; set; } = string.Empty;
  8 +
  9 + public string LocationId { get; set; } = string.Empty;
  10 +
  11 + public string LocationName { get; set; } = string.Empty;
  12 +
  13 + public string LabelCategoryId { get; set; } = string.Empty;
  14 +
  15 + public string LabelCategoryName { get; set; } = string.Empty;
  16 +
  17 + public string LabelTypeId { get; set; } = string.Empty;
  18 +
  19 + public string LabelTypeName { get; set; } = string.Empty;
  20 +
  21 + public string TemplateCode { get; set; } = string.Empty;
  22 +
  23 + public string TemplateName { get; set; } = string.Empty;
  24 +
  25 + public bool State { get; set; }
  26 +
  27 + public object? LabelInfoJson { get; set; }
  28 +
  29 + public List<string> ProductIds { get; set; } = new();
  30 +}
  31 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelUpdateInputVo.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.Label;
  2 +
  3 +public class LabelUpdateInputVo
  4 +{
  5 + public string LabelName { get; set; } = string.Empty;
  6 +
  7 + public string TemplateCode { get; set; } = string.Empty;
  8 +
  9 + public string LocationId { get; set; } = string.Empty;
  10 +
  11 + public string LabelCategoryId { get; set; } = string.Empty;
  12 +
  13 + public string LabelTypeId { get; set; } = string.Empty;
  14 +
  15 + public List<string> ProductIds { get; set; } = new();
  16 +
  17 + public object? LabelInfoJson { get; set; }
  18 +
  19 + public bool State { get; set; } = true;
  20 +}
  21 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryCreateInputVo.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.LabelCategory;
  2 +
  3 +public class LabelCategoryCreateInputVo
  4 +{
  5 + public string CategoryCode { get; set; } = string.Empty;
  6 +
  7 + public string CategoryName { get; set; } = string.Empty;
  8 +
  9 + public string? CategoryPhotoUrl { get; set; }
  10 +
  11 + public bool State { get; set; } = true;
  12 +
  13 + public int OrderNum { get; set; }
  14 +}
  15 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetListInputVo.cs 0 → 100644
  1 +using Volo.Abp.Application.Dtos;
  2 +
  3 +namespace FoodLabeling.Application.Contracts.Dtos.LabelCategory;
  4 +
  5 +public class LabelCategoryGetListInputVo : PagedAndSortedResultRequestDto
  6 +{
  7 + public string? Keyword { get; set; }
  8 +
  9 + public bool? State { get; set; }
  10 +}
  11 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetListOutputDto.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.LabelCategory;
  2 +
  3 +public class LabelCategoryGetListOutputDto
  4 +{
  5 + public string Id { get; set; } = string.Empty;
  6 +
  7 + public string CategoryCode { get; set; } = string.Empty;
  8 +
  9 + public string CategoryName { get; set; } = string.Empty;
  10 +
  11 + public string? CategoryPhotoUrl { get; set; }
  12 +
  13 + public bool State { get; set; }
  14 +
  15 + public int OrderNum { get; set; }
  16 +
  17 + public long NoOfLabels { get; set; }
  18 +
  19 + public DateTime LastEdited { get; set; }
  20 +}
  21 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetOutputDto.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.LabelCategory;
  2 +
  3 +public class LabelCategoryGetOutputDto
  4 +{
  5 + public string Id { get; set; } = string.Empty;
  6 +
  7 + public string CategoryCode { get; set; } = string.Empty;
  8 +
  9 + public string CategoryName { get; set; } = string.Empty;
  10 +
  11 + public string? CategoryPhotoUrl { get; set; }
  12 +
  13 + public bool State { get; set; }
  14 +
  15 + public int OrderNum { get; set; }
  16 +}
  17 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryUpdateInputVo.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.LabelCategory;
  2 +
  3 +public class LabelCategoryUpdateInputVo : LabelCategoryCreateInputVo
  4 +{
  5 +}
  6 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionCreateInputVo.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.LabelMultipleOption;
  2 +
  3 +public class LabelMultipleOptionCreateInputVo
  4 +{
  5 + public string OptionCode { get; set; } = string.Empty;
  6 +
  7 + public string OptionName { get; set; } = string.Empty;
  8 +
  9 + public string? OptionValuesJson { get; set; }
  10 +
  11 + public bool State { get; set; } = true;
  12 +
  13 + public int OrderNum { get; set; }
  14 +}
  15 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionGetListInputVo.cs 0 → 100644
  1 +using Volo.Abp.Application.Dtos;
  2 +
  3 +namespace FoodLabeling.Application.Contracts.Dtos.LabelMultipleOption;
  4 +
  5 +public class LabelMultipleOptionGetListInputVo : PagedAndSortedResultRequestDto
  6 +{
  7 + public string? Keyword { get; set; }
  8 +
  9 + public bool? State { get; set; }
  10 +}
  11 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionGetListOutputDto.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.LabelMultipleOption;
  2 +
  3 +public class LabelMultipleOptionGetListOutputDto
  4 +{
  5 + public string Id { get; set; } = string.Empty;
  6 +
  7 + public string OptionCode { get; set; } = string.Empty;
  8 +
  9 + public string OptionName { get; set; } = string.Empty;
  10 +
  11 + public string? OptionValuesJson { get; set; }
  12 +
  13 + public bool State { get; set; }
  14 +
  15 + public int OrderNum { get; set; }
  16 +
  17 + public DateTime LastEdited { get; set; }
  18 +}
  19 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionGetOutputDto.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.LabelMultipleOption;
  2 +
  3 +public class LabelMultipleOptionGetOutputDto
  4 +{
  5 + public string Id { get; set; } = string.Empty;
  6 +
  7 + public string OptionCode { get; set; } = string.Empty;
  8 +
  9 + public string OptionName { get; set; } = string.Empty;
  10 +
  11 + public string? OptionValuesJson { get; set; }
  12 +
  13 + public bool State { get; set; }
  14 +
  15 + public int OrderNum { get; set; }
  16 +}
  17 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelMultipleOption/LabelMultipleOptionUpdateInputVo.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.LabelMultipleOption;
  2 +
  3 +public class LabelMultipleOptionUpdateInputVo : LabelMultipleOptionCreateInputVo
  4 +{
  5 +}
  6 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateCreateInputVo.cs 0 → 100644
  1 +using System.Text.Json.Serialization;
  2 +
  3 +namespace FoodLabeling.Application.Contracts.Dtos.LabelTemplate;
  4 +
  5 +public class LabelTemplateCreateInputVo
  6 +{
  7 + /// <summary>
  8 + /// editor JSON 里的 id,对应数据库 fl_label_template.TemplateCode
  9 + /// </summary>
  10 + [JsonPropertyName("id")]
  11 + public string TemplateCode { get; set; } = string.Empty;
  12 +
  13 + /// <summary>
  14 + /// editor JSON 里的 name,对应数据库 fl_label_template.TemplateName
  15 + /// </summary>
  16 + [JsonPropertyName("name")]
  17 + public string TemplateName { get; set; } = string.Empty;
  18 +
  19 + [JsonPropertyName("labelType")]
  20 + public string? LabelType { get; set; }
  21 +
  22 + [JsonPropertyName("unit")]
  23 + public string Unit { get; set; } = "inch";
  24 +
  25 + [JsonPropertyName("width")]
  26 + public decimal Width { get; set; }
  27 +
  28 + [JsonPropertyName("height")]
  29 + public decimal Height { get; set; }
  30 +
  31 + /// <summary>
  32 + /// editor JSON 里的 appliedLocation(ALL / SPECIFIED)
  33 + /// </summary>
  34 + [JsonPropertyName("appliedLocation")]
  35 + public string AppliedLocationType { get; set; } = "ALL";
  36 +
  37 + [JsonPropertyName("showRuler")]
  38 + public bool ShowRuler { get; set; } = true;
  39 +
  40 + [JsonPropertyName("showGrid")]
  41 + public bool ShowGrid { get; set; } = true;
  42 +
  43 + /// <summary>
  44 + /// 预留:前端可能不传;如果不传则默认 true
  45 + /// </summary>
  46 + [JsonPropertyName("state")]
  47 + public bool State { get; set; } = true;
  48 +
  49 + /// <summary>
  50 + /// elements(前端 editor 的 elements[],会全量重建)
  51 + /// </summary>
  52 + [JsonPropertyName("elements")]
  53 + public List<LabelTemplateElementDto> Elements { get; set; } = new();
  54 +
  55 + /// <summary>
  56 + /// 当 AppliedLocationType=SPECIFIED 时生效
  57 + /// </summary>
  58 + [JsonPropertyName("appliedLocationIds")]
  59 + public List<string> AppliedLocationIds { get; set; } = new();
  60 +}
  61 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateElementDto.cs 0 → 100644
  1 +using System.Text.Json.Serialization;
  2 +
  3 +namespace FoodLabeling.Application.Contracts.Dtos.LabelTemplate;
  4 +
  5 +/// <summary>
  6 +/// 模板元素(对齐你给的 editor JSON:id/type/x/y/width/height/rotation/border/config)
  7 +/// </summary>
  8 +public class LabelTemplateElementDto
  9 +{
  10 + [JsonPropertyName("id")]
  11 + public string Id { get; set; } = string.Empty;
  12 +
  13 + [JsonPropertyName("type")]
  14 + public string ElementType { get; set; } = string.Empty;
  15 +
  16 + [JsonPropertyName("x")]
  17 + public decimal PosX { get; set; }
  18 +
  19 + [JsonPropertyName("y")]
  20 + public decimal PosY { get; set; }
  21 +
  22 + [JsonPropertyName("width")]
  23 + public decimal Width { get; set; }
  24 +
  25 + [JsonPropertyName("height")]
  26 + public decimal Height { get; set; }
  27 +
  28 + [JsonPropertyName("rotation")]
  29 + public string Rotation { get; set; } = "horizontal";
  30 +
  31 + [JsonPropertyName("border")]
  32 + public string BorderType { get; set; } = "none";
  33 +
  34 + [JsonPropertyName("zIndex")]
  35 + public int ZIndex { get; set; }
  36 +
  37 + [JsonPropertyName("orderNum")]
  38 + public int OrderNum { get; set; }
  39 +
  40 + /// <summary>
  41 + /// 值来源:FIXED / AUTO_DB / PRINT_INPUT
  42 + /// </summary>
  43 + [JsonPropertyName("valueSourceType")]
  44 + public string ValueSourceType { get; set; } = "FIXED";
  45 +
  46 + [JsonPropertyName("bindingExpr")]
  47 + public string? BindingExpr { get; set; }
  48 +
  49 + [JsonPropertyName("autoQueryKey")]
  50 + public string? AutoQueryKey { get; set; }
  51 +
  52 + [JsonPropertyName("inputKey")]
  53 + public string? InputKey { get; set; }
  54 +
  55 + [JsonPropertyName("isRequiredInput")]
  56 + public bool IsRequiredInput { get; set; }
  57 +
  58 + /// <summary>
  59 + /// 元素配置
  60 + /// </summary>
  61 + [JsonPropertyName("config")]
  62 + public object? ConfigJson { get; set; }
  63 +}
  64 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateGetListInputVo.cs 0 → 100644
  1 +using Volo.Abp.Application.Dtos;
  2 +
  3 +namespace FoodLabeling.Application.Contracts.Dtos.LabelTemplate;
  4 +
  5 +/// <summary>
  6 +/// 标签模板分页查询入参
  7 +/// </summary>
  8 +public class LabelTemplateGetListInputVo : PagedAndSortedResultRequestDto
  9 +{
  10 + /// <summary>
  11 + /// 关键字(模板名称/模板编码)
  12 + /// </summary>
  13 + public string? Keyword { get; set; }
  14 +
  15 + /// <summary>
  16 + /// 门店Id(用于筛选:ALL 或指定门店)
  17 + /// </summary>
  18 + public string? LocationId { get; set; }
  19 +
  20 + /// <summary>
  21 + /// 标签类型(例如 PRICE)
  22 + /// </summary>
  23 + public string? LabelType { get; set; }
  24 +
  25 + /// <summary>
  26 + /// 状态
  27 + /// </summary>
  28 + public bool? State { get; set; }
  29 +}
  30 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateGetListOutputDto.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.LabelTemplate;
  2 +
  3 +public class LabelTemplateGetListOutputDto
  4 +{
  5 + public string Id { get; set; } = string.Empty;
  6 +
  7 + public string TemplateCode { get; set; } = string.Empty;
  8 +
  9 + public string TemplateName { get; set; } = string.Empty;
  10 +
  11 + public string? LabelType { get; set; }
  12 +
  13 + /// <summary>
  14 + /// 位置展示:All Locations 或首个指定门店的名称
  15 + /// </summary>
  16 + public string LocationText { get; set; } = string.Empty;
  17 +
  18 + /// <summary>
  19 + /// elements 数量(Contents)
  20 + /// </summary>
  21 + public int ContentsCount { get; set; }
  22 +
  23 + public string SizeText { get; set; } = string.Empty;
  24 +
  25 + public int VersionNo { get; set; }
  26 +
  27 + public DateTime LastEdited { get; set; }
  28 +}
  29 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateGetOutputDto.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.LabelTemplate;
  2 +
  3 +public class LabelTemplateGetOutputDto
  4 +{
  5 + public string Id { get; set; } = string.Empty;
  6 +
  7 + public string TemplateCode { get; set; } = string.Empty;
  8 +
  9 + public string TemplateName { get; set; } = string.Empty;
  10 +
  11 + public string? LabelType { get; set; }
  12 +
  13 + public string Unit { get; set; } = string.Empty;
  14 +
  15 + public decimal Width { get; set; }
  16 +
  17 + public decimal Height { get; set; }
  18 +
  19 + public string AppliedLocationType { get; set; } = "ALL";
  20 +
  21 + public bool ShowRuler { get; set; }
  22 +
  23 + public bool ShowGrid { get; set; }
  24 +
  25 + public int VersionNo { get; set; }
  26 +
  27 + public bool State { get; set; }
  28 +
  29 + public List<LabelTemplateElementDto> Elements { get; set; } = new();
  30 +
  31 + public List<string> AppliedLocationIds { get; set; } = new();
  32 +}
  33 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateUpdateInputVo.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.LabelTemplate;
  2 +
  3 +public class LabelTemplateUpdateInputVo : LabelTemplateCreateInputVo
  4 +{
  5 +}
  6 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeCreateInputVo.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.LabelType;
  2 +
  3 +public class LabelTypeCreateInputVo
  4 +{
  5 + public string TypeCode { get; set; } = string.Empty;
  6 +
  7 + public string TypeName { get; set; } = string.Empty;
  8 +
  9 + public bool State { get; set; } = true;
  10 +
  11 + public int OrderNum { get; set; }
  12 +}
  13 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeGetListInputVo.cs 0 → 100644
  1 +using Volo.Abp.Application.Dtos;
  2 +
  3 +namespace FoodLabeling.Application.Contracts.Dtos.LabelType;
  4 +
  5 +public class LabelTypeGetListInputVo : PagedAndSortedResultRequestDto
  6 +{
  7 + public string? Keyword { get; set; }
  8 +
  9 + public bool? State { get; set; }
  10 +}
  11 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeGetListOutputDto.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.LabelType;
  2 +
  3 +public class LabelTypeGetListOutputDto
  4 +{
  5 + public string Id { get; set; } = string.Empty;
  6 +
  7 + public string TypeCode { get; set; } = string.Empty;
  8 +
  9 + public string TypeName { get; set; } = string.Empty;
  10 +
  11 + public bool State { get; set; }
  12 +
  13 + public int OrderNum { get; set; }
  14 +
  15 + public long NoOfLabels { get; set; }
  16 +
  17 + public DateTime LastEdited { get; set; }
  18 +}
  19 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeGetOutputDto.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.LabelType;
  2 +
  3 +public class LabelTypeGetOutputDto
  4 +{
  5 + public string Id { get; set; } = string.Empty;
  6 +
  7 + public string TypeCode { get; set; } = string.Empty;
  8 +
  9 + public string TypeName { get; set; } = string.Empty;
  10 +
  11 + public bool State { get; set; }
  12 +
  13 + public int OrderNum { get; set; }
  14 +}
  15 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelType/LabelTypeUpdateInputVo.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.LabelType;
  2 +
  3 +public class LabelTypeUpdateInputVo : LabelTypeCreateInputVo
  4 +{
  5 +}
  6 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelAppService.cs 0 → 100644
  1 +using FoodLabeling.Application.Contracts.Dtos.Common;
  2 +using FoodLabeling.Application.Contracts.Dtos.Label;
  3 +using Volo.Abp.Application.Dtos;
  4 +using Volo.Abp.Application.Services;
  5 +
  6 +namespace FoodLabeling.Application.Contracts.IServices;
  7 +
  8 +/// <summary>
  9 +/// 标签管理接口(美国版)
  10 +/// </summary>
  11 +public interface ILabelAppService : IApplicationService
  12 +{
  13 + /// <summary>
  14 + /// 按产品分页列表(一个产品展示多个标签)
  15 + /// </summary>
  16 + Task<PagedResultWithPageDto<LabelGetListOutputDto>> GetListAsync(LabelGetListInputVo input);
  17 +
  18 + /// <summary>
  19 + /// 标签详情(id=LabelCode)
  20 + /// </summary>
  21 + Task<LabelGetOutputDto> GetAsync(string id);
  22 +
  23 + /// <summary>
  24 + /// 新增标签
  25 + /// </summary>
  26 + Task<LabelGetOutputDto> CreateAsync(LabelCreateInputVo input);
  27 +
  28 + /// <summary>
  29 + /// 编辑标签(id=LabelCode)
  30 + /// </summary>
  31 + Task<LabelGetOutputDto> UpdateAsync(string id, LabelUpdateInputVo input);
  32 +
  33 + /// <summary>
  34 + /// 删除标签(逻辑删除)
  35 + /// </summary>
  36 + Task DeleteAsync(string id);
  37 +
  38 + /// <summary>
  39 + /// 标签预览:解析模板 AUTO_DB/PRINT_INPUT(不做打印落库)
  40 + /// </summary>
  41 + Task<LabelTemplatePreviewDto> PreviewAsync(LabelPreviewResolveInputVo input);
  42 +}
  43 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelCategoryAppService.cs 0 → 100644
  1 +using FoodLabeling.Application.Contracts.Dtos.Common;
  2 +using FoodLabeling.Application.Contracts.Dtos.LabelCategory;
  3 +
  4 +namespace FoodLabeling.Application.Contracts.IServices;
  5 +
  6 +public interface ILabelCategoryAppService
  7 +{
  8 + Task<PagedResultWithPageDto<LabelCategoryGetListOutputDto>> GetListAsync(LabelCategoryGetListInputVo input);
  9 +
  10 + Task<LabelCategoryGetOutputDto> GetAsync(string id);
  11 +
  12 + Task<LabelCategoryGetOutputDto> CreateAsync(LabelCategoryCreateInputVo input);
  13 +
  14 + Task<LabelCategoryGetOutputDto> UpdateAsync(string id, LabelCategoryUpdateInputVo input);
  15 +
  16 + Task DeleteAsync(string id);
  17 +}
  18 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelMultipleOptionAppService.cs 0 → 100644
  1 +using FoodLabeling.Application.Contracts.Dtos.Common;
  2 +using FoodLabeling.Application.Contracts.Dtos.LabelMultipleOption;
  3 +
  4 +namespace FoodLabeling.Application.Contracts.IServices;
  5 +
  6 +public interface ILabelMultipleOptionAppService
  7 +{
  8 + Task<PagedResultWithPageDto<LabelMultipleOptionGetListOutputDto>> GetListAsync(LabelMultipleOptionGetListInputVo input);
  9 +
  10 + Task<LabelMultipleOptionGetOutputDto> GetAsync(string id);
  11 +
  12 + Task<LabelMultipleOptionGetOutputDto> CreateAsync(LabelMultipleOptionCreateInputVo input);
  13 +
  14 + Task<LabelMultipleOptionGetOutputDto> UpdateAsync(string id, LabelMultipleOptionUpdateInputVo input);
  15 +
  16 + Task DeleteAsync(string id);
  17 +}
  18 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelTemplateAppService.cs 0 → 100644
  1 +using FoodLabeling.Application.Contracts.Dtos.Common;
  2 +using FoodLabeling.Application.Contracts.Dtos.LabelTemplate;
  3 +using Volo.Abp.Application.Dtos;
  4 +using Volo.Abp.Application.Services;
  5 +
  6 +namespace FoodLabeling.Application.Contracts.IServices;
  7 +
  8 +/// <summary>
  9 +/// 标签模板管理接口(美国版对外)
  10 +/// </summary>
  11 +public interface ILabelTemplateAppService : IApplicationService
  12 +{
  13 + /// <summary>
  14 + /// 标签模板分页列表
  15 + /// </summary>
  16 + Task<PagedResultWithPageDto<LabelTemplateGetListOutputDto>> GetListAsync(LabelTemplateGetListInputVo input);
  17 +
  18 + /// <summary>
  19 + /// 标签模板详情(含 elements 与适用门店)
  20 + /// </summary>
  21 + Task<LabelTemplateGetOutputDto> GetAsync(string id);
  22 +
  23 + /// <summary>
  24 + /// 新增标签模板
  25 + /// </summary>
  26 + Task<LabelTemplateGetOutputDto> CreateAsync(LabelTemplateCreateInputVo input);
  27 +
  28 + /// <summary>
  29 + /// 编辑标签模板(版本号 +1,重建 elements)
  30 + /// </summary>
  31 + Task<LabelTemplateGetOutputDto> UpdateAsync(string id, LabelTemplateUpdateInputVo input);
  32 +
  33 + /// <summary>
  34 + /// 删除标签模板(逻辑删除;若已被标签引用则禁止)
  35 + /// </summary>
  36 + Task DeleteAsync(string id);
  37 +}
  38 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelTypeAppService.cs 0 → 100644
  1 +using FoodLabeling.Application.Contracts.Dtos.Common;
  2 +using FoodLabeling.Application.Contracts.Dtos.LabelType;
  3 +
  4 +namespace FoodLabeling.Application.Contracts.IServices;
  5 +
  6 +public interface ILabelTypeAppService
  7 +{
  8 + Task<PagedResultWithPageDto<LabelTypeGetListOutputDto>> GetListAsync(LabelTypeGetListInputVo input);
  9 +
  10 + Task<LabelTypeGetOutputDto> GetAsync(string id);
  11 +
  12 + Task<LabelTypeGetOutputDto> CreateAsync(LabelTypeCreateInputVo input);
  13 +
  14 + Task<LabelTypeGetOutputDto> UpdateAsync(string id, LabelTypeUpdateInputVo input);
  15 +
  16 + Task DeleteAsync(string id);
  17 +}
  18 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelCategoryDbEntity.cs 0 → 100644
  1 +using SqlSugar;
  2 +
  3 +namespace FoodLabeling.Application.Services.DbModels;
  4 +
  5 +[SugarTable("fl_label_category")]
  6 +public class FlLabelCategoryDbEntity
  7 +{
  8 + [SugarColumn(IsPrimaryKey = true)]
  9 + public string Id { get; set; } = string.Empty;
  10 +
  11 + public bool IsDeleted { get; set; }
  12 +
  13 + public DateTime CreationTime { get; set; }
  14 +
  15 + public string? CreatorId { get; set; }
  16 +
  17 + public string? LastModifierId { get; set; }
  18 +
  19 + public DateTime? LastModificationTime { get; set; }
  20 +
  21 + public string ConcurrencyStamp { get; set; } = string.Empty;
  22 +
  23 + public string CategoryCode { get; set; } = string.Empty;
  24 +
  25 + public string CategoryName { get; set; } = string.Empty;
  26 +
  27 + public string? CategoryPhotoUrl { get; set; }
  28 +
  29 + public int OrderNum { get; set; }
  30 +
  31 + public bool State { get; set; }
  32 +}
  33 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelDbEntity.cs 0 → 100644
  1 +using SqlSugar;
  2 +
  3 +namespace FoodLabeling.Application.Services.DbModels;
  4 +
  5 +[SugarTable("fl_label")]
  6 +public class FlLabelDbEntity
  7 +{
  8 + [SugarColumn(IsPrimaryKey = true)]
  9 + public string Id { get; set; } = string.Empty;
  10 +
  11 + public bool IsDeleted { get; set; }
  12 +
  13 + public DateTime CreationTime { get; set; }
  14 +
  15 + public string? CreatorId { get; set; }
  16 +
  17 + public string? LastModifierId { get; set; }
  18 +
  19 + public DateTime? LastModificationTime { get; set; }
  20 +
  21 + public string ConcurrencyStamp { get; set; } = string.Empty;
  22 +
  23 + public string LabelCode { get; set; } = string.Empty;
  24 +
  25 + public string LabelName { get; set; } = string.Empty;
  26 +
  27 + public string TemplateId { get; set; } = string.Empty;
  28 +
  29 + public string? LocationId { get; set; }
  30 +
  31 + public string? LabelCategoryId { get; set; }
  32 +
  33 + public string? LabelTypeId { get; set; }
  34 +
  35 + public string? LabelType { get; set; }
  36 +
  37 + public bool State { get; set; }
  38 +
  39 + /// <summary>
  40 + /// json 字段,直接保存为字符串(入参/出参自行序列化/反序列化)
  41 + /// </summary>
  42 + public string? LabelInfoJson { get; set; }
  43 +}
  44 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelMultipleOptionDbEntity.cs 0 → 100644
  1 +using SqlSugar;
  2 +
  3 +namespace FoodLabeling.Application.Services.DbModels;
  4 +
  5 +[SugarTable("fl_label_multiple_option")]
  6 +public class FlLabelMultipleOptionDbEntity
  7 +{
  8 + [SugarColumn(IsPrimaryKey = true)]
  9 + public string Id { get; set; } = string.Empty;
  10 +
  11 + public bool IsDeleted { get; set; }
  12 +
  13 + public DateTime CreationTime { get; set; }
  14 +
  15 + public string? CreatorId { get; set; }
  16 +
  17 + public string? LastModifierId { get; set; }
  18 +
  19 + public DateTime? LastModificationTime { get; set; }
  20 +
  21 + public string ConcurrencyStamp { get; set; } = string.Empty;
  22 +
  23 + public string OptionCode { get; set; } = string.Empty;
  24 +
  25 + public string OptionName { get; set; } = string.Empty;
  26 +
  27 + public string? OptionValuesJson { get; set; }
  28 +
  29 + public int OrderNum { get; set; }
  30 +
  31 + public bool State { get; set; }
  32 +}
  33 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelProductDbEntity.cs 0 → 100644
  1 +using SqlSugar;
  2 +
  3 +namespace FoodLabeling.Application.Services.DbModels;
  4 +
  5 +[SugarTable("fl_label_product")]
  6 +public class FlLabelProductDbEntity
  7 +{
  8 + [SugarColumn(IsPrimaryKey = true)]
  9 + public string Id { get; set; } = string.Empty;
  10 +
  11 + public string LabelId { get; set; } = string.Empty;
  12 +
  13 + public string ProductId { get; set; } = string.Empty;
  14 +}
  15 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateDbEntity.cs 0 → 100644
  1 +using SqlSugar;
  2 +
  3 +namespace FoodLabeling.Application.Services.DbModels;
  4 +
  5 +[SugarTable("fl_label_template")]
  6 +public class FlLabelTemplateDbEntity
  7 +{
  8 + [SugarColumn(IsPrimaryKey = true)]
  9 + public string Id { get; set; } = string.Empty;
  10 +
  11 + public bool IsDeleted { get; set; }
  12 +
  13 + public DateTime CreationTime { get; set; }
  14 +
  15 + public string? CreatorId { get; set; }
  16 +
  17 + public string? LastModifierId { get; set; }
  18 +
  19 + public DateTime? LastModificationTime { get; set; }
  20 +
  21 + public string ConcurrencyStamp { get; set; } = string.Empty;
  22 +
  23 + public string TemplateCode { get; set; } = string.Empty;
  24 +
  25 + public string TemplateName { get; set; } = string.Empty;
  26 +
  27 + public string? LabelType { get; set; }
  28 +
  29 + public string Unit { get; set; } = "inch";
  30 +
  31 + public decimal Width { get; set; }
  32 +
  33 + public decimal Height { get; set; }
  34 +
  35 + public string AppliedLocationType { get; set; } = "ALL";
  36 +
  37 + public bool ShowRuler { get; set; }
  38 +
  39 + public bool ShowGrid { get; set; }
  40 +
  41 + public int VersionNo { get; set; }
  42 +
  43 + public bool State { get; set; }
  44 +}
  45 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateElementDbEntity.cs 0 → 100644
  1 +using SqlSugar;
  2 +
  3 +namespace FoodLabeling.Application.Services.DbModels;
  4 +
  5 +[SugarTable("fl_label_template_element")]
  6 +public class FlLabelTemplateElementDbEntity
  7 +{
  8 + [SugarColumn(IsPrimaryKey = true)]
  9 + public string Id { get; set; } = string.Empty;
  10 +
  11 + public string TemplateId { get; set; } = string.Empty;
  12 +
  13 + public string ElementKey { get; set; } = string.Empty;
  14 +
  15 + public string ElementType { get; set; } = string.Empty;
  16 +
  17 + public decimal PosX { get; set; }
  18 +
  19 + public decimal PosY { get; set; }
  20 +
  21 + public decimal Width { get; set; }
  22 +
  23 + public decimal Height { get; set; }
  24 +
  25 + public string Rotation { get; set; } = "horizontal";
  26 +
  27 + public string BorderType { get; set; } = "none";
  28 +
  29 + public int ZIndex { get; set; }
  30 +
  31 + public int OrderNum { get; set; }
  32 +
  33 + public string ValueSourceType { get; set; } = "FIXED";
  34 +
  35 + public string? BindingExpr { get; set; }
  36 +
  37 + public string? AutoQueryKey { get; set; }
  38 +
  39 + public string? InputKey { get; set; }
  40 +
  41 + public bool IsRequiredInput { get; set; }
  42 +
  43 + public string? ConfigJson { get; set; }
  44 +}
  45 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTemplateLocationDbEntity.cs 0 → 100644
  1 +using SqlSugar;
  2 +
  3 +namespace FoodLabeling.Application.Services.DbModels;
  4 +
  5 +[SugarTable("fl_label_template_location")]
  6 +public class FlLabelTemplateLocationDbEntity
  7 +{
  8 + [SugarColumn(IsPrimaryKey = true)]
  9 + public string Id { get; set; } = string.Empty;
  10 +
  11 + public string TemplateId { get; set; } = string.Empty;
  12 +
  13 + public string LocationId { get; set; } = string.Empty;
  14 +}
  15 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelTypeDbEntity.cs 0 → 100644
  1 +using SqlSugar;
  2 +
  3 +namespace FoodLabeling.Application.Services.DbModels;
  4 +
  5 +[SugarTable("fl_label_type")]
  6 +public class FlLabelTypeDbEntity
  7 +{
  8 + [SugarColumn(IsPrimaryKey = true)]
  9 + public string Id { get; set; } = string.Empty;
  10 +
  11 + public bool IsDeleted { get; set; }
  12 +
  13 + public DateTime CreationTime { get; set; }
  14 +
  15 + public string? CreatorId { get; set; }
  16 +
  17 + public string? LastModifierId { get; set; }
  18 +
  19 + public DateTime? LastModificationTime { get; set; }
  20 +
  21 + public string ConcurrencyStamp { get; set; } = string.Empty;
  22 +
  23 + public string TypeCode { get; set; } = string.Empty;
  24 +
  25 + public string TypeName { get; set; } = string.Empty;
  26 +
  27 + public int OrderNum { get; set; }
  28 +
  29 + public bool State { get; set; }
  30 +}
  31 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlProductDbEntity.cs 0 → 100644
  1 +using SqlSugar;
  2 +
  3 +namespace FoodLabeling.Application.Services.DbModels;
  4 +
  5 +[SugarTable("fl_product")]
  6 +public class FlProductDbEntity
  7 +{
  8 + [SugarColumn(IsPrimaryKey = true)]
  9 + public string Id { get; set; } = string.Empty;
  10 +
  11 + public bool IsDeleted { get; set; }
  12 +
  13 + public string ProductCode { get; set; } = string.Empty;
  14 +
  15 + public string ProductName { get; set; } = string.Empty;
  16 +
  17 + public string? CategoryName { get; set; }
  18 +
  19 + public string? ProductImageUrl { get; set; }
  20 +
  21 + public bool State { get; set; }
  22 +}
  23 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelAppService.cs 0 → 100644
  1 +using System.Text.Json;
  2 +using FoodLabeling.Application.Contracts.Dtos.Common;
  3 +using FoodLabeling.Application.Contracts.Dtos.Label;
  4 +using FoodLabeling.Application.Contracts.Dtos.LabelTemplate;
  5 +using FoodLabeling.Application.Contracts.IServices;
  6 +using FoodLabeling.Application.Services.DbModels;
  7 +using FoodLabeling.Domain.Entities;
  8 +using SqlSugar;
  9 +using Volo.Abp;
  10 +using Volo.Abp.Application.Services;
  11 +using Volo.Abp.Guids;
  12 +using Volo.Abp.Uow;
  13 +using Yi.Framework.SqlSugarCore.Abstractions;
  14 +
  15 +namespace FoodLabeling.Application.Services;
  16 +
  17 +/// <summary>
  18 +/// 标签管理(一个产品展示多个标签)
  19 +/// </summary>
  20 +public class LabelAppService : ApplicationService, ILabelAppService
  21 +{
  22 + private readonly ISqlSugarDbContext _dbContext;
  23 + private readonly IGuidGenerator _guidGenerator;
  24 +
  25 + public LabelAppService(ISqlSugarDbContext dbContext, IGuidGenerator guidGenerator)
  26 + {
  27 + _dbContext = dbContext;
  28 + _guidGenerator = guidGenerator;
  29 + }
  30 +
  31 + public async Task<PagedResultWithPageDto<LabelGetListOutputDto>> GetListAsync(LabelGetListInputVo input)
  32 + {
  33 + RefAsync<int> total = 0;
  34 +
  35 + var productId = input.ProductId?.Trim();
  36 + var locationId = input.LocationId?.Trim();
  37 + var keyword = input.Keyword?.Trim();
  38 + var labelCategoryId = input.LabelCategoryId?.Trim();
  39 + var labelTypeId = input.LabelTypeId?.Trim();
  40 + var templateCode = input.TemplateCode?.Trim();
  41 +
  42 + // 先查 label-product 映射(按产品)
  43 + var query = _dbContext.SqlSugarClient.Queryable<FlLabelProductDbEntity, FlLabelDbEntity, FlProductDbEntity, FlLabelCategoryDbEntity, FlLabelTypeDbEntity, FlLabelTemplateDbEntity>(
  44 + (lp, l, p, c, t, tpl) =>
  45 + lp.LabelId == l.Id &&
  46 + lp.ProductId == p.Id &&
  47 + l.LabelCategoryId == c.Id &&
  48 + l.LabelTypeId == t.Id &&
  49 + l.TemplateId == tpl.Id)
  50 + .Where((lp, l, p, c, t, tpl) => l.IsDeleted == false)
  51 + .Where((lp, l, p, c, t, tpl) => !c.IsDeleted)
  52 + .Where((lp, l, p, c, t, tpl) => !t.IsDeleted)
  53 + .Where((lp, l, p, c, t, tpl) => !tpl.IsDeleted)
  54 + .Where((lp, l, p, c, t, tpl) => !p.IsDeleted)
  55 + .WhereIF(!string.IsNullOrWhiteSpace(productId), (lp, l, p, c, t, tpl) => lp.ProductId == productId)
  56 + .WhereIF(!string.IsNullOrWhiteSpace(locationId), (lp, l, p, c, t, tpl) => l.LocationId == locationId)
  57 + .WhereIF(!string.IsNullOrWhiteSpace(labelCategoryId), (lp, l, p, c, t, tpl) => l.LabelCategoryId == labelCategoryId)
  58 + .WhereIF(!string.IsNullOrWhiteSpace(labelTypeId), (lp, l, p, c, t, tpl) => l.LabelTypeId == labelTypeId)
  59 + .WhereIF(!string.IsNullOrWhiteSpace(templateCode), (lp, l, p, c, t, tpl) => tpl.TemplateCode == templateCode)
  60 + .WhereIF(!string.IsNullOrWhiteSpace(keyword),
  61 + (lp, l, p, c, t, tpl) =>
  62 + l.LabelName.Contains(keyword!) ||
  63 + p.ProductName.Contains(keyword!) ||
  64 + c.CategoryName.Contains(keyword!) ||
  65 + t.TypeName.Contains(keyword!))
  66 + .WhereIF(input.State != null, (lp, l, p, c, t, tpl) => l.State == input.State)
  67 + .OrderByDescending((lp, l, p, c, t, tpl) => l.LastModificationTime ?? l.CreationTime);
  68 +
  69 + if (!string.IsNullOrWhiteSpace(input.Sorting))
  70 + {
  71 + query = query.OrderBy(input.Sorting);
  72 + }
  73 +
  74 + var entities = await query.Select((lp, l, p, c, t, tpl) => new
  75 + {
  76 + LabelCode = l.LabelCode,
  77 + LabelName = l.LabelName,
  78 + LocationId = l.LocationId,
  79 + LocationName = (string?)null, // later fill
  80 + LabelCategoryName = c.CategoryName,
  81 + ProductCategoryName = p.CategoryName,
  82 + ProductName = p.ProductName,
  83 + TemplateName = tpl.TemplateName,
  84 + LabelTypeName = t.TypeName,
  85 + State = l.State,
  86 + LastEdited = l.LastModificationTime ?? l.CreationTime
  87 + })
  88 + .ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
  89 +
  90 + var locationIds = entities
  91 + .Select(x => x.LocationId)
  92 + .Where(x => !string.IsNullOrWhiteSpace(x))
  93 + .Select(x => x!.Trim())
  94 + .Distinct()
  95 + .ToList();
  96 +
  97 + var locationMap = new Dictionary<string, LocationAggregateRoot>();
  98 + if (locationIds.Count > 0)
  99 + {
  100 + var locGuids = locationIds.Where(x => Guid.TryParse(x, out _)).Select(Guid.Parse).ToList();
  101 + if (locGuids.Count > 0)
  102 + {
  103 + var locs = await _dbContext.SqlSugarClient.Queryable<LocationAggregateRoot>()
  104 + .Where(x => !x.IsDeleted)
  105 + .Where(x => locGuids.Contains(x.Id))
  106 + .ToListAsync();
  107 + locationMap = locs.ToDictionary(x => x.Id.ToString(), x => x);
  108 + }
  109 + }
  110 +
  111 + var items = entities.Select(x =>
  112 + {
  113 + var locationName = string.Empty;
  114 + if (!string.IsNullOrWhiteSpace(x.LocationId) && locationMap.TryGetValue(x.LocationId!, out var loc))
  115 + {
  116 + locationName = loc.LocationName ?? loc.LocationCode;
  117 + }
  118 + return new LabelGetListOutputDto
  119 + {
  120 + Id = x.LabelCode ?? string.Empty,
  121 + LabelName = x.LabelName ?? string.Empty,
  122 + LocationName = string.IsNullOrWhiteSpace(locationName) ? "无" : locationName,
  123 + LabelCategoryName = x.LabelCategoryName ?? string.Empty,
  124 + ProductCategoryName = x.ProductCategoryName ?? string.Empty,
  125 + ProductName = x.ProductName ?? string.Empty,
  126 + TemplateName = x.TemplateName ?? string.Empty,
  127 + LabelTypeName = x.LabelTypeName ?? string.Empty,
  128 + State = x.State,
  129 + LastEdited = x.LastEdited,
  130 + HasError = false
  131 + };
  132 + }).ToList();
  133 +
  134 + var pageSize = input.MaxResultCount <= 0 ? items.Count : input.MaxResultCount;
  135 + var pageIndex = pageSize <= 0 ? 1 : (input.SkipCount / pageSize) + 1;
  136 + var totalPages = pageSize <= 0 ? 0 : (int)Math.Ceiling(total / (double)pageSize);
  137 +
  138 + return new PagedResultWithPageDto<LabelGetListOutputDto>
  139 + {
  140 + PageIndex = pageIndex,
  141 + PageSize = pageSize,
  142 + TotalCount = total,
  143 + TotalPages = totalPages,
  144 + Items = items
  145 + };
  146 + }
  147 +
  148 + public async Task<LabelGetOutputDto> GetAsync(string id)
  149 + {
  150 + var labelCode = id?.Trim();
  151 + if (string.IsNullOrWhiteSpace(labelCode))
  152 + {
  153 + throw new UserFriendlyException("标签Code不能为空");
  154 + }
  155 +
  156 + var label = await _dbContext.SqlSugarClient.Queryable<FlLabelDbEntity>()
  157 + .FirstAsync(x => !x.IsDeleted && x.LabelCode == labelCode);
  158 + if (label is null)
  159 + {
  160 + throw new UserFriendlyException("标签不存在");
  161 + }
  162 +
  163 + var template = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateDbEntity>()
  164 + .FirstAsync(x => x.Id == label.TemplateId);
  165 + var category = await _dbContext.SqlSugarClient.Queryable<FlLabelCategoryDbEntity>()
  166 + .FirstAsync(x => x.Id == label.LabelCategoryId);
  167 + var type = await _dbContext.SqlSugarClient.Queryable<FlLabelTypeDbEntity>()
  168 + .FirstAsync(x => x.Id == label.LabelTypeId);
  169 +
  170 + LocationAggregateRoot? location = null;
  171 + if (!string.IsNullOrWhiteSpace(label.LocationId) && Guid.TryParse(label.LocationId, out var locId))
  172 + {
  173 + location = await _dbContext.SqlSugarClient.Queryable<LocationAggregateRoot>()
  174 + .FirstAsync(x => !x.IsDeleted && x.Id == locId);
  175 + }
  176 +
  177 + var productIds = await _dbContext.SqlSugarClient.Queryable<FlLabelProductDbEntity>()
  178 + .Where(x => x.LabelId == label.Id)
  179 + .Select(x => x.ProductId)
  180 + .ToListAsync();
  181 +
  182 + object? labelInfo = null;
  183 + if (!string.IsNullOrWhiteSpace(label.LabelInfoJson))
  184 + {
  185 + labelInfo = JsonSerializer.Deserialize<object>(label.LabelInfoJson);
  186 + }
  187 +
  188 + return new LabelGetOutputDto
  189 + {
  190 + Id = label.LabelCode,
  191 + LabelName = label.LabelName,
  192 + LocationId = label.LocationId ?? string.Empty,
  193 + LocationName = location?.LocationName ?? location?.LocationCode ?? "无",
  194 + LabelCategoryId = label.LabelCategoryId ?? string.Empty,
  195 + LabelCategoryName = category?.CategoryName ?? "无",
  196 + LabelTypeId = label.LabelTypeId ?? string.Empty,
  197 + LabelTypeName = type?.TypeName ?? "无",
  198 + TemplateCode = template?.TemplateCode ?? string.Empty,
  199 + TemplateName = template?.TemplateName ?? string.Empty,
  200 + State = label.State,
  201 + LabelInfoJson = labelInfo,
  202 + ProductIds = productIds
  203 + };
  204 + }
  205 +
  206 + [UnitOfWork]
  207 + public async Task<LabelGetOutputDto> CreateAsync(LabelCreateInputVo input)
  208 + {
  209 + var labelCode = input.LabelCode?.Trim();
  210 + if (string.IsNullOrWhiteSpace(labelCode))
  211 + {
  212 + labelCode = $"LBL_{_guidGenerator.Create():N}";
  213 + }
  214 +
  215 + var labelName = input.LabelName?.Trim();
  216 + if (string.IsNullOrWhiteSpace(labelName))
  217 + {
  218 + throw new UserFriendlyException("标签名称不能为空");
  219 + }
  220 +
  221 + if (input.ProductIds is null || input.ProductIds.Count == 0)
  222 + {
  223 + throw new UserFriendlyException("标签至少需要绑定一个产品");
  224 + }
  225 +
  226 + if (string.IsNullOrWhiteSpace(input.TemplateCode))
  227 + {
  228 + throw new UserFriendlyException("模板编码不能为空");
  229 + }
  230 + if (string.IsNullOrWhiteSpace(input.LocationId))
  231 + {
  232 + throw new UserFriendlyException("门店Id不能为空");
  233 + }
  234 + if (string.IsNullOrWhiteSpace(input.LabelCategoryId))
  235 + {
  236 + throw new UserFriendlyException("标签分类Id不能为空");
  237 + }
  238 + if (string.IsNullOrWhiteSpace(input.LabelTypeId))
  239 + {
  240 + throw new UserFriendlyException("标签类型Id不能为空");
  241 + }
  242 +
  243 + var template = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateDbEntity>()
  244 + .FirstAsync(x => !x.IsDeleted && x.TemplateCode == input.TemplateCode.Trim());
  245 + if (template is null)
  246 + {
  247 + throw new UserFriendlyException("模板不存在");
  248 + }
  249 +
  250 + // 唯一性校验(LabelCode)
  251 + var exists = await _dbContext.SqlSugarClient.Queryable<FlLabelDbEntity>()
  252 + .AnyAsync(x => !x.IsDeleted && x.LabelCode == labelCode);
  253 + if (exists)
  254 + {
  255 + throw new UserFriendlyException("标签编码已存在");
  256 + }
  257 +
  258 + // 插入 fl_label
  259 + var now = DateTime.Now;
  260 + var labelId = _guidGenerator.Create().ToString();
  261 + var labelEntity = new FlLabelDbEntity
  262 + {
  263 + Id = labelId,
  264 + IsDeleted = false,
  265 + CreationTime = now,
  266 + CreatorId = CurrentUser?.Id?.ToString(),
  267 + LastModifierId = CurrentUser?.Id?.ToString(),
  268 + LastModificationTime = now,
  269 + ConcurrencyStamp = string.Empty,
  270 + LabelCode = labelCode,
  271 + LabelName = labelName,
  272 + TemplateId = template.Id,
  273 + LocationId = input.LocationId?.Trim(),
  274 + LabelCategoryId = input.LabelCategoryId?.Trim(),
  275 + LabelTypeId = input.LabelTypeId?.Trim(),
  276 + State = input.State,
  277 + LabelType = null,
  278 + LabelInfoJson = input.LabelInfoJson == null ? null : JsonSerializer.Serialize(input.LabelInfoJson)
  279 + };
  280 + await _dbContext.SqlSugarClient.Insertable(labelEntity).ExecuteCommandAsync();
  281 +
  282 + // 插入 fl_label_product
  283 + var productIds = input.ProductIds.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).Distinct().ToList();
  284 + var rows = productIds.Select(pid => new FlLabelProductDbEntity
  285 + {
  286 + Id = _guidGenerator.Create().ToString(),
  287 + LabelId = labelId,
  288 + ProductId = pid
  289 + }).ToList();
  290 + await _dbContext.SqlSugarClient.Insertable(rows).ExecuteCommandAsync();
  291 +
  292 + return await GetAsync(labelCode);
  293 + }
  294 +
  295 + [UnitOfWork]
  296 + public async Task<LabelGetOutputDto> UpdateAsync(string id, LabelUpdateInputVo input)
  297 + {
  298 + var labelCode = id?.Trim();
  299 + if (string.IsNullOrWhiteSpace(labelCode))
  300 + {
  301 + throw new UserFriendlyException("标签Code不能为空");
  302 + }
  303 +
  304 + var label = await _dbContext.SqlSugarClient.Queryable<FlLabelDbEntity>()
  305 + .FirstAsync(x => !x.IsDeleted && x.LabelCode == labelCode);
  306 + if (label is null)
  307 + {
  308 + throw new UserFriendlyException("标签不存在");
  309 + }
  310 +
  311 + if (input.ProductIds is null || input.ProductIds.Count == 0)
  312 + {
  313 + throw new UserFriendlyException("标签至少需要绑定一个产品");
  314 + }
  315 +
  316 + if (string.IsNullOrWhiteSpace(input.TemplateCode))
  317 + {
  318 + throw new UserFriendlyException("模板编码不能为空");
  319 + }
  320 + if (string.IsNullOrWhiteSpace(input.LocationId))
  321 + {
  322 + throw new UserFriendlyException("门店Id不能为空");
  323 + }
  324 + if (string.IsNullOrWhiteSpace(input.LabelCategoryId))
  325 + {
  326 + throw new UserFriendlyException("标签分类Id不能为空");
  327 + }
  328 + if (string.IsNullOrWhiteSpace(input.LabelTypeId))
  329 + {
  330 + throw new UserFriendlyException("标签类型Id不能为空");
  331 + }
  332 +
  333 + var template = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateDbEntity>()
  334 + .FirstAsync(x => !x.IsDeleted && x.TemplateCode == input.TemplateCode.Trim());
  335 + if (template is null)
  336 + {
  337 + throw new UserFriendlyException("模板不存在");
  338 + }
  339 +
  340 + var now = DateTime.Now;
  341 + label.LabelName = input.LabelName?.Trim() ?? label.LabelName;
  342 + label.TemplateId = template.Id;
  343 + label.LocationId = input.LocationId?.Trim();
  344 + label.LabelCategoryId = input.LabelCategoryId?.Trim();
  345 + label.LabelTypeId = input.LabelTypeId?.Trim();
  346 + label.State = input.State;
  347 + label.LastModifierId = CurrentUser?.Id?.ToString();
  348 + label.LastModificationTime = now;
  349 + label.LabelInfoJson = input.LabelInfoJson == null ? null : JsonSerializer.Serialize(input.LabelInfoJson);
  350 +
  351 + await _dbContext.SqlSugarClient.Updateable(label).ExecuteCommandAsync();
  352 +
  353 + // 重建关联
  354 + await _dbContext.SqlSugarClient.Deleteable<FlLabelProductDbEntity>()
  355 + .Where(x => x.LabelId == label.Id)
  356 + .ExecuteCommandAsync();
  357 +
  358 + var productIds = input.ProductIds.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).Distinct().ToList();
  359 + var rows = productIds.Select(pid => new FlLabelProductDbEntity
  360 + {
  361 + Id = _guidGenerator.Create().ToString(),
  362 + LabelId = label.Id,
  363 + ProductId = pid
  364 + }).ToList();
  365 + await _dbContext.SqlSugarClient.Insertable(rows).ExecuteCommandAsync();
  366 +
  367 + return await GetAsync(labelCode);
  368 + }
  369 +
  370 + [UnitOfWork]
  371 + public async Task DeleteAsync(string id)
  372 + {
  373 + var labelCode = id?.Trim();
  374 + if (string.IsNullOrWhiteSpace(labelCode))
  375 + {
  376 + return;
  377 + }
  378 +
  379 + var label = await _dbContext.SqlSugarClient.Queryable<FlLabelDbEntity>()
  380 + .FirstAsync(x => !x.IsDeleted && x.LabelCode == labelCode);
  381 + if (label is null)
  382 + {
  383 + return;
  384 + }
  385 +
  386 + label.IsDeleted = true;
  387 + label.LastModifierId = CurrentUser?.Id?.ToString();
  388 + label.LastModificationTime = DateTime.Now;
  389 + await _dbContext.SqlSugarClient.Updateable(label).ExecuteCommandAsync();
  390 +
  391 + await _dbContext.SqlSugarClient.Deleteable<FlLabelProductDbEntity>()
  392 + .Where(x => x.LabelId == label.Id)
  393 + .ExecuteCommandAsync();
  394 + }
  395 +
  396 + /// <summary>
  397 + /// 标签预览:不落库,只把 template elements 的 AUTO_DB/PRINT_INPUT 渲染进 config
  398 + /// </summary>
  399 + [UnitOfWork]
  400 + public async Task<LabelTemplatePreviewDto> PreviewAsync(LabelPreviewResolveInputVo input)
  401 + {
  402 + var labelCode = input?.LabelCode?.Trim();
  403 + if (string.IsNullOrWhiteSpace(labelCode))
  404 + {
  405 + throw new UserFriendlyException("labelCode不能为空");
  406 + }
  407 +
  408 + var label = await _dbContext.SqlSugarClient.Queryable<FlLabelDbEntity>()
  409 + .FirstAsync(x => !x.IsDeleted && x.LabelCode == labelCode);
  410 + if (label is null)
  411 + {
  412 + throw new UserFriendlyException("标签不存在");
  413 + }
  414 +
  415 + // 选择预览产品
  416 + var productId = input?.ProductId?.Trim();
  417 + if (string.IsNullOrWhiteSpace(productId))
  418 + {
  419 + productId = await _dbContext.SqlSugarClient.Queryable<FlLabelProductDbEntity>()
  420 + .Where(x => x.LabelId == label.Id)
  421 + .Select(x => x.ProductId)
  422 + .FirstAsync();
  423 + }
  424 +
  425 + if (string.IsNullOrWhiteSpace(productId))
  426 + {
  427 + throw new UserFriendlyException("该标签未绑定产品,无法预览");
  428 + }
  429 +
  430 + var product = await _dbContext.SqlSugarClient.Queryable<FlProductDbEntity>()
  431 + .FirstAsync(x => !x.IsDeleted && x.Id == productId);
  432 + if (product is null)
  433 + {
  434 + throw new UserFriendlyException("预览产品不存在");
  435 + }
  436 +
  437 + // 取模板头 & elements
  438 + var template = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateDbEntity>()
  439 + .FirstAsync(x => !x.IsDeleted && x.Id == label.TemplateId);
  440 + if (template is null)
  441 + {
  442 + throw new UserFriendlyException("模板不存在");
  443 + }
  444 +
  445 + var elements = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateElementDbEntity>()
  446 + .Where(x => x.TemplateId == template.Id)
  447 + // SqlSugar 不提供 ThenBy,这里用组合排序键保证 OrderNum + ZIndex 的稳定顺序
  448 + .OrderBy(x => x.OrderNum * 1000000 + x.ZIndex)
  449 + .ToListAsync();
  450 +
  451 + // 解析 labelInfo(可用于补充一些数值)
  452 + Dictionary<string, object?> labelInfoDict = new();
  453 + if (!string.IsNullOrWhiteSpace(label.LabelInfoJson))
  454 + {
  455 + try
  456 + {
  457 + labelInfoDict = JsonSerializer.Deserialize<Dictionary<string, object?>>(label.LabelInfoJson)
  458 + ?? new Dictionary<string, object?>();
  459 + }
  460 + catch
  461 + {
  462 + // ignore
  463 + }
  464 + }
  465 +
  466 + var printInputJson = input?.PrintInputJson ?? new Dictionary<string, object?>();
  467 +
  468 + static int TryGetInt(object? v, int defaultValue)
  469 + {
  470 + if (v is null) return defaultValue;
  471 + if (v is int i) return i;
  472 + if (v is long l) return (int)l;
  473 + if (v is decimal d) return (int)d;
  474 + if (v is double db) return (int)db;
  475 + if (v is JsonElement je)
  476 + {
  477 + if (je.ValueKind == JsonValueKind.Number && je.TryGetInt32(out var ii)) return ii;
  478 + if (je.ValueKind == JsonValueKind.String && int.TryParse(je.GetString(), out var si)) return si;
  479 + }
  480 + if (v is string s && int.TryParse(s, out var st)) return st;
  481 + return defaultValue;
  482 + }
  483 +
  484 + static string? TryToString(object? v)
  485 + {
  486 + if (v is null) return null;
  487 + if (v is string s) return s;
  488 + if (v is JsonElement je)
  489 + {
  490 + if (je.ValueKind == JsonValueKind.String) return je.GetString();
  491 + if (je.ValueKind == JsonValueKind.Number || je.ValueKind == JsonValueKind.True || je.ValueKind == JsonValueKind.False)
  492 + {
  493 + return je.ToString();
  494 + }
  495 + }
  496 + return v.ToString();
  497 + }
  498 +
  499 + static void UpsertConfigValue(Dictionary<string, object?> cfg, string key, object? value)
  500 + {
  501 + if (cfg.ContainsKey(key))
  502 + {
  503 + cfg[key] = value;
  504 + return;
  505 + }
  506 + cfg.Add(key, value);
  507 + }
  508 +
  509 + Dictionary<string, object?> ParseConfig(string? configJson)
  510 + {
  511 + if (string.IsNullOrWhiteSpace(configJson))
  512 + {
  513 + return new Dictionary<string, object?>();
  514 + }
  515 +
  516 + try
  517 + {
  518 + return JsonSerializer.Deserialize<Dictionary<string, object?>>(configJson)
  519 + ?? new Dictionary<string, object?>();
  520 + }
  521 + catch
  522 + {
  523 + return new Dictionary<string, object?>();
  524 + }
  525 + }
  526 +
  527 + foreach (var el in elements)
  528 + {
  529 + // FIXED:不处理(直接保留 config)
  530 + // AUTO_DB:根据 type 做一个最小可用渲染(支持 TEXT_PRODUCT / BARCODE / QRCODE / DATE / TIME)
  531 + // PRINT_INPUT:按 InputKey 覆盖相应字段(优先级最高)
  532 + }
  533 +
  534 + var resolvedElements = elements.Select(el =>
  535 + {
  536 + var cfg = ParseConfig(el.ConfigJson);
  537 +
  538 + // 1) 先做 AUTO_DB:按类型兜底填充
  539 + if (string.Equals(el.ValueSourceType, "AUTO_DB", StringComparison.OrdinalIgnoreCase))
  540 + {
  541 + switch (el.ElementType)
  542 + {
  543 + case "TEXT_PRODUCT":
  544 + UpsertConfigValue(cfg, "text", product.ProductName);
  545 + break;
  546 + case "TEXT_PRICE":
  547 + {
  548 + // price 可能在 labelInfo 或 printInput 里
  549 + object? priceObj = null;
  550 + if (labelInfoDict.TryGetValue("price", out var p)) priceObj = p;
  551 + if (priceObj is null && printInputJson.TryGetValue("price", out var pp)) priceObj = pp;
  552 + var priceStr = TryToString(priceObj);
  553 + if (!string.IsNullOrWhiteSpace(priceStr))
  554 + {
  555 + UpsertConfigValue(cfg, "text", priceStr);
  556 + }
  557 + }
  558 + break;
  559 + case "BARCODE":
  560 + if (!string.IsNullOrWhiteSpace(product.ProductCode))
  561 + {
  562 + UpsertConfigValue(cfg, "data", product.ProductCode);
  563 + }
  564 + break;
  565 + case "QRCODE":
  566 + if (!string.IsNullOrWhiteSpace(product.ProductCode))
  567 + {
  568 + UpsertConfigValue(cfg, "data", product.ProductCode);
  569 + }
  570 + break;
  571 + case "DATE":
  572 + {
  573 + var offsetDays = cfg.TryGetValue("offsetDays", out var od) ? TryGetInt(od, 0) : 0;
  574 + var dt = DateTime.Today.AddDays(offsetDays);
  575 + UpsertConfigValue(cfg, "format", dt.ToString("yyyy-MM-dd"));
  576 + cfg.Remove("inputType");
  577 + }
  578 + break;
  579 + case "TIME":
  580 + {
  581 + var dt = DateTime.Now;
  582 + UpsertConfigValue(cfg, "format", dt.ToString("HH:mm"));
  583 + cfg.Remove("inputType");
  584 + }
  585 + break;
  586 + }
  587 + }
  588 +
  589 + // 2) 再做 PRINT_INPUT:按 InputKey 覆盖(如果前端传了)
  590 + if (string.Equals(el.ValueSourceType, "PRINT_INPUT", StringComparison.OrdinalIgnoreCase)
  591 + && !string.IsNullOrWhiteSpace(el.InputKey)
  592 + && printInputJson.TryGetValue(el.InputKey!, out var inputValue))
  593 + {
  594 + var s = TryToString(inputValue);
  595 + if (!string.IsNullOrWhiteSpace(s))
  596 + {
  597 + switch (el.ElementType)
  598 + {
  599 + case "TEXT_STATIC":
  600 + case "TEXT_PRODUCT":
  601 + case "TEXT_PRICE":
  602 + UpsertConfigValue(cfg, "text", s);
  603 + cfg.Remove("inputType");
  604 + break;
  605 + case "BARCODE":
  606 + case "QRCODE":
  607 + UpsertConfigValue(cfg, "data", s);
  608 + break;
  609 + case "DATE":
  610 + case "TIME":
  611 + UpsertConfigValue(cfg, "format", s);
  612 + cfg.Remove("inputType");
  613 + break;
  614 + }
  615 + }
  616 + }
  617 +
  618 + // 3) FIXED:不做任何覆盖,保持模板/标签设计时写入的固定值(由前端/设计器落库)
  619 +
  620 + return new LabelTemplateElementDto
  621 + {
  622 + Id = el.ElementKey,
  623 + ElementType = el.ElementType,
  624 + PosX = el.PosX,
  625 + PosY = el.PosY,
  626 + Width = el.Width,
  627 + Height = el.Height,
  628 + Rotation = string.IsNullOrWhiteSpace(el.Rotation) ? "horizontal" : el.Rotation,
  629 + BorderType = string.IsNullOrWhiteSpace(el.BorderType) ? "none" : el.BorderType,
  630 + ZIndex = el.ZIndex,
  631 + OrderNum = el.OrderNum,
  632 + ValueSourceType = el.ValueSourceType,
  633 + BindingExpr = el.BindingExpr,
  634 + AutoQueryKey = el.AutoQueryKey,
  635 + InputKey = el.InputKey,
  636 + IsRequiredInput = el.IsRequiredInput,
  637 + ConfigJson = cfg
  638 + };
  639 + }).ToList();
  640 +
  641 + return new LabelTemplatePreviewDto
  642 + {
  643 + Id = template.TemplateCode,
  644 + Name = template.TemplateName,
  645 + LabelType = template.LabelType ?? string.Empty,
  646 + Unit = template.Unit,
  647 + Width = template.Width,
  648 + Height = template.Height,
  649 + AppliedLocation = template.AppliedLocationType,
  650 + ShowRuler = template.ShowRuler,
  651 + ShowGrid = template.ShowGrid,
  652 + Elements = resolvedElements
  653 + };
  654 + }
  655 +}
  656 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelCategoryAppService.cs 0 → 100644
  1 +using FoodLabeling.Application.Contracts.Dtos.Common;
  2 +using FoodLabeling.Application.Contracts.Dtos.LabelCategory;
  3 +using FoodLabeling.Application.Contracts.IServices;
  4 +using FoodLabeling.Application.Services.DbModels;
  5 +using SqlSugar;
  6 +using Volo.Abp;
  7 +using Volo.Abp.Application.Services;
  8 +using Volo.Abp.Guids;
  9 +using Yi.Framework.SqlSugarCore.Abstractions;
  10 +
  11 +namespace FoodLabeling.Application.Services;
  12 +
  13 +public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppService
  14 +{
  15 + private readonly ISqlSugarDbContext _dbContext;
  16 + private readonly IGuidGenerator _guidGenerator;
  17 +
  18 + public LabelCategoryAppService(ISqlSugarDbContext dbContext, IGuidGenerator guidGenerator)
  19 + {
  20 + _dbContext = dbContext;
  21 + _guidGenerator = guidGenerator;
  22 + }
  23 +
  24 + public async Task<PagedResultWithPageDto<LabelCategoryGetListOutputDto>> GetListAsync(LabelCategoryGetListInputVo input)
  25 + {
  26 + RefAsync<int> total = 0;
  27 + var keyword = input.Keyword?.Trim();
  28 +
  29 + var query = _dbContext.SqlSugarClient.Queryable<FlLabelCategoryDbEntity>()
  30 + .Where(x => !x.IsDeleted)
  31 + .WhereIF(!string.IsNullOrWhiteSpace(keyword),
  32 + x => x.CategoryCode.Contains(keyword!) || x.CategoryName.Contains(keyword!))
  33 + .WhereIF(input.State != null, x => x.State == input.State);
  34 +
  35 + if (!string.IsNullOrWhiteSpace(input.Sorting))
  36 + {
  37 + query = query.OrderBy(input.Sorting);
  38 + }
  39 + else
  40 + {
  41 + query = query.OrderByDescending(x => x.OrderNum).OrderByDescending(x => x.CreationTime);
  42 + }
  43 +
  44 + var entities = await query.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
  45 + var ids = entities.Select(x => x.Id).ToList();
  46 +
  47 + var countRows = await _dbContext.SqlSugarClient.Queryable<FlLabelDbEntity>()
  48 + .Where(x => !x.IsDeleted)
  49 + .Where(x => x.LabelCategoryId != null && ids.Contains(x.LabelCategoryId))
  50 + .GroupBy(x => x.LabelCategoryId)
  51 + .Select(x => new { CategoryId = x.LabelCategoryId, Count = SqlFunc.AggregateCount(x.Id) })
  52 + .ToListAsync();
  53 + var countMap = countRows.ToDictionary(x => x.CategoryId!, x => (long)x.Count);
  54 +
  55 + var items = entities.Select(x => new LabelCategoryGetListOutputDto
  56 + {
  57 + Id = x.Id,
  58 + CategoryCode = x.CategoryCode,
  59 + CategoryName = x.CategoryName,
  60 + CategoryPhotoUrl = x.CategoryPhotoUrl,
  61 + State = x.State,
  62 + OrderNum = x.OrderNum,
  63 + NoOfLabels = countMap.TryGetValue(x.Id, out var count) ? count : 0,
  64 + LastEdited = x.LastModificationTime ?? x.CreationTime
  65 + }).ToList();
  66 +
  67 + return BuildPagedResult(input.SkipCount, input.MaxResultCount, total, items);
  68 + }
  69 +
  70 + public async Task<LabelCategoryGetOutputDto> GetAsync(string id)
  71 + {
  72 + var entity = await _dbContext.SqlSugarClient.Queryable<FlLabelCategoryDbEntity>()
  73 + .FirstAsync(x => x.Id == id && !x.IsDeleted);
  74 + if (entity is null)
  75 + {
  76 + throw new UserFriendlyException("标签分类不存在");
  77 + }
  78 +
  79 + return MapToGetOutput(entity);
  80 + }
  81 +
  82 + public async Task<LabelCategoryGetOutputDto> CreateAsync(LabelCategoryCreateInputVo input)
  83 + {
  84 + var code = input.CategoryCode?.Trim();
  85 + var name = input.CategoryName?.Trim();
  86 + if (string.IsNullOrWhiteSpace(code) || string.IsNullOrWhiteSpace(name))
  87 + {
  88 + throw new UserFriendlyException("分类编码和名称不能为空");
  89 + }
  90 +
  91 + var duplicated = await _dbContext.SqlSugarClient.Queryable<FlLabelCategoryDbEntity>()
  92 + .AnyAsync(x => !x.IsDeleted && (x.CategoryCode == code || x.CategoryName == name));
  93 + if (duplicated)
  94 + {
  95 + throw new UserFriendlyException("分类编码或名称已存在");
  96 + }
  97 +
  98 + var entity = new FlLabelCategoryDbEntity
  99 + {
  100 + Id = _guidGenerator.Create().ToString(),
  101 + CategoryCode = code,
  102 + CategoryName = name,
  103 + CategoryPhotoUrl = input.CategoryPhotoUrl?.Trim(),
  104 + State = input.State,
  105 + OrderNum = input.OrderNum
  106 + };
  107 +
  108 + await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync();
  109 + return await GetAsync(entity.Id);
  110 + }
  111 +
  112 + public async Task<LabelCategoryGetOutputDto> UpdateAsync(string id, LabelCategoryUpdateInputVo input)
  113 + {
  114 + var entity = await _dbContext.SqlSugarClient.Queryable<FlLabelCategoryDbEntity>()
  115 + .FirstAsync(x => x.Id == id && !x.IsDeleted);
  116 + if (entity is null)
  117 + {
  118 + throw new UserFriendlyException("标签分类不存在");
  119 + }
  120 +
  121 + var code = input.CategoryCode?.Trim();
  122 + var name = input.CategoryName?.Trim();
  123 + if (string.IsNullOrWhiteSpace(code) || string.IsNullOrWhiteSpace(name))
  124 + {
  125 + throw new UserFriendlyException("分类编码和名称不能为空");
  126 + }
  127 +
  128 + var duplicated = await _dbContext.SqlSugarClient.Queryable<FlLabelCategoryDbEntity>()
  129 + .AnyAsync(x => !x.IsDeleted && x.Id != id && (x.CategoryCode == code || x.CategoryName == name));
  130 + if (duplicated)
  131 + {
  132 + throw new UserFriendlyException("分类编码或名称已存在");
  133 + }
  134 +
  135 + entity.CategoryCode = code;
  136 + entity.CategoryName = name;
  137 + entity.CategoryPhotoUrl = input.CategoryPhotoUrl?.Trim();
  138 + entity.State = input.State;
  139 + entity.OrderNum = input.OrderNum;
  140 + entity.LastModificationTime = DateTime.Now;
  141 + entity.LastModifierId = CurrentUser?.Id?.ToString();
  142 +
  143 + await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync();
  144 + return await GetAsync(id);
  145 + }
  146 +
  147 + public async Task DeleteAsync(string id)
  148 + {
  149 + var entity = await _dbContext.SqlSugarClient.Queryable<FlLabelCategoryDbEntity>()
  150 + .FirstAsync(x => x.Id == id && !x.IsDeleted);
  151 + if (entity is null)
  152 + {
  153 + return;
  154 + }
  155 +
  156 + var used = await _dbContext.SqlSugarClient.Queryable<FlLabelDbEntity>()
  157 + .AnyAsync(x => !x.IsDeleted && x.LabelCategoryId == id);
  158 + if (used)
  159 + {
  160 + throw new UserFriendlyException("该标签分类已被标签引用,无法删除");
  161 + }
  162 +
  163 + entity.IsDeleted = true;
  164 + entity.LastModificationTime = DateTime.Now;
  165 + entity.LastModifierId = CurrentUser?.Id?.ToString();
  166 + await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync();
  167 + }
  168 +
  169 + private static LabelCategoryGetOutputDto MapToGetOutput(FlLabelCategoryDbEntity x)
  170 + {
  171 + return new LabelCategoryGetOutputDto
  172 + {
  173 + Id = x.Id,
  174 + CategoryCode = x.CategoryCode,
  175 + CategoryName = x.CategoryName,
  176 + CategoryPhotoUrl = x.CategoryPhotoUrl,
  177 + State = x.State,
  178 + OrderNum = x.OrderNum
  179 + };
  180 + }
  181 +
  182 + private static PagedResultWithPageDto<T> BuildPagedResult<T>(int skipCount, int maxResultCount, int total, List<T> items)
  183 + {
  184 + var pageSize = maxResultCount <= 0 ? items.Count : maxResultCount;
  185 + var pageIndex = pageSize <= 0 ? 1 : (skipCount / pageSize) + 1;
  186 + var totalPages = pageSize <= 0 ? 0 : (int)Math.Ceiling(total / (double)pageSize);
  187 + return new PagedResultWithPageDto<T>
  188 + {
  189 + PageIndex = pageIndex,
  190 + PageSize = pageSize,
  191 + TotalCount = total,
  192 + TotalPages = totalPages,
  193 + Items = items
  194 + };
  195 + }
  196 +}
  197 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelMultipleOptionAppService.cs 0 → 100644
  1 +using FoodLabeling.Application.Contracts.Dtos.Common;
  2 +using FoodLabeling.Application.Contracts.Dtos.LabelMultipleOption;
  3 +using FoodLabeling.Application.Contracts.IServices;
  4 +using FoodLabeling.Application.Services.DbModels;
  5 +using SqlSugar;
  6 +using Volo.Abp;
  7 +using Volo.Abp.Application.Services;
  8 +using Volo.Abp.Guids;
  9 +using Yi.Framework.SqlSugarCore.Abstractions;
  10 +
  11 +namespace FoodLabeling.Application.Services;
  12 +
  13 +public class LabelMultipleOptionAppService : ApplicationService, ILabelMultipleOptionAppService
  14 +{
  15 + private readonly ISqlSugarDbContext _dbContext;
  16 + private readonly IGuidGenerator _guidGenerator;
  17 +
  18 + public LabelMultipleOptionAppService(ISqlSugarDbContext dbContext, IGuidGenerator guidGenerator)
  19 + {
  20 + _dbContext = dbContext;
  21 + _guidGenerator = guidGenerator;
  22 + }
  23 +
  24 + public async Task<PagedResultWithPageDto<LabelMultipleOptionGetListOutputDto>> GetListAsync(LabelMultipleOptionGetListInputVo input)
  25 + {
  26 + RefAsync<int> total = 0;
  27 + var keyword = input.Keyword?.Trim();
  28 +
  29 + var query = _dbContext.SqlSugarClient.Queryable<FlLabelMultipleOptionDbEntity>()
  30 + .Where(x => !x.IsDeleted)
  31 + .WhereIF(!string.IsNullOrWhiteSpace(keyword), x => x.OptionCode.Contains(keyword!) || x.OptionName.Contains(keyword!))
  32 + .WhereIF(input.State != null, x => x.State == input.State);
  33 +
  34 + if (!string.IsNullOrWhiteSpace(input.Sorting))
  35 + {
  36 + query = query.OrderBy(input.Sorting);
  37 + }
  38 + else
  39 + {
  40 + query = query.OrderByDescending(x => x.OrderNum).OrderByDescending(x => x.CreationTime);
  41 + }
  42 +
  43 + var entities = await query.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
  44 + var items = entities.Select(x => new LabelMultipleOptionGetListOutputDto
  45 + {
  46 + Id = x.Id,
  47 + OptionCode = x.OptionCode,
  48 + OptionName = x.OptionName,
  49 + OptionValuesJson = x.OptionValuesJson,
  50 + State = x.State,
  51 + OrderNum = x.OrderNum,
  52 + LastEdited = x.LastModificationTime ?? x.CreationTime
  53 + }).ToList();
  54 +
  55 + return BuildPagedResult(input.SkipCount, input.MaxResultCount, total, items);
  56 + }
  57 +
  58 + public async Task<LabelMultipleOptionGetOutputDto> GetAsync(string id)
  59 + {
  60 + var entity = await _dbContext.SqlSugarClient.Queryable<FlLabelMultipleOptionDbEntity>()
  61 + .FirstAsync(x => x.Id == id && !x.IsDeleted);
  62 + if (entity is null)
  63 + {
  64 + throw new UserFriendlyException("多选项不存在");
  65 + }
  66 +
  67 + return new LabelMultipleOptionGetOutputDto
  68 + {
  69 + Id = entity.Id,
  70 + OptionCode = entity.OptionCode,
  71 + OptionName = entity.OptionName,
  72 + OptionValuesJson = entity.OptionValuesJson,
  73 + State = entity.State,
  74 + OrderNum = entity.OrderNum
  75 + };
  76 + }
  77 +
  78 + public async Task<LabelMultipleOptionGetOutputDto> CreateAsync(LabelMultipleOptionCreateInputVo input)
  79 + {
  80 + var code = input.OptionCode?.Trim();
  81 + var name = input.OptionName?.Trim();
  82 + if (string.IsNullOrWhiteSpace(code) || string.IsNullOrWhiteSpace(name))
  83 + {
  84 + throw new UserFriendlyException("多选项编码和名称不能为空");
  85 + }
  86 +
  87 + var duplicated = await _dbContext.SqlSugarClient.Queryable<FlLabelMultipleOptionDbEntity>()
  88 + .AnyAsync(x => !x.IsDeleted && (x.OptionCode == code || x.OptionName == name));
  89 + if (duplicated)
  90 + {
  91 + throw new UserFriendlyException("多选项编码或名称已存在");
  92 + }
  93 +
  94 + var entity = new FlLabelMultipleOptionDbEntity
  95 + {
  96 + Id = _guidGenerator.Create().ToString(),
  97 + OptionCode = code,
  98 + OptionName = name,
  99 + OptionValuesJson = input.OptionValuesJson?.Trim(),
  100 + State = input.State,
  101 + OrderNum = input.OrderNum
  102 + };
  103 +
  104 + await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync();
  105 + return await GetAsync(entity.Id);
  106 + }
  107 +
  108 + public async Task<LabelMultipleOptionGetOutputDto> UpdateAsync(string id, LabelMultipleOptionUpdateInputVo input)
  109 + {
  110 + var entity = await _dbContext.SqlSugarClient.Queryable<FlLabelMultipleOptionDbEntity>()
  111 + .FirstAsync(x => x.Id == id && !x.IsDeleted);
  112 + if (entity is null)
  113 + {
  114 + throw new UserFriendlyException("多选项不存在");
  115 + }
  116 +
  117 + var code = input.OptionCode?.Trim();
  118 + var name = input.OptionName?.Trim();
  119 + if (string.IsNullOrWhiteSpace(code) || string.IsNullOrWhiteSpace(name))
  120 + {
  121 + throw new UserFriendlyException("多选项编码和名称不能为空");
  122 + }
  123 +
  124 + var duplicated = await _dbContext.SqlSugarClient.Queryable<FlLabelMultipleOptionDbEntity>()
  125 + .AnyAsync(x => !x.IsDeleted && x.Id != id && (x.OptionCode == code || x.OptionName == name));
  126 + if (duplicated)
  127 + {
  128 + throw new UserFriendlyException("多选项编码或名称已存在");
  129 + }
  130 +
  131 + entity.OptionCode = code;
  132 + entity.OptionName = name;
  133 + entity.OptionValuesJson = input.OptionValuesJson?.Trim();
  134 + entity.State = input.State;
  135 + entity.OrderNum = input.OrderNum;
  136 + entity.LastModificationTime = DateTime.Now;
  137 + entity.LastModifierId = CurrentUser?.Id?.ToString();
  138 +
  139 + await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync();
  140 + return await GetAsync(id);
  141 + }
  142 +
  143 + public async Task DeleteAsync(string id)
  144 + {
  145 + var entity = await _dbContext.SqlSugarClient.Queryable<FlLabelMultipleOptionDbEntity>()
  146 + .FirstAsync(x => x.Id == id && !x.IsDeleted);
  147 + if (entity is null)
  148 + {
  149 + return;
  150 + }
  151 +
  152 + entity.IsDeleted = true;
  153 + entity.LastModificationTime = DateTime.Now;
  154 + entity.LastModifierId = CurrentUser?.Id?.ToString();
  155 + await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync();
  156 + }
  157 +
  158 + private static PagedResultWithPageDto<T> BuildPagedResult<T>(int skipCount, int maxResultCount, int total, List<T> items)
  159 + {
  160 + var pageSize = maxResultCount <= 0 ? items.Count : maxResultCount;
  161 + var pageIndex = pageSize <= 0 ? 1 : (skipCount / pageSize) + 1;
  162 + var totalPages = pageSize <= 0 ? 0 : (int)Math.Ceiling(total / (double)pageSize);
  163 + return new PagedResultWithPageDto<T>
  164 + {
  165 + PageIndex = pageIndex,
  166 + PageSize = pageSize,
  167 + TotalCount = total,
  168 + TotalPages = totalPages,
  169 + Items = items
  170 + };
  171 + }
  172 +}
  173 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelTemplateAppService.cs 0 → 100644
  1 +using System.Text.Json;
  2 +using FoodLabeling.Application.Contracts.Dtos.Common;
  3 +using FoodLabeling.Application.Contracts.Dtos.LabelTemplate;
  4 +using FoodLabeling.Application.Contracts.IServices;
  5 +using FoodLabeling.Application.Services.DbModels;
  6 +using FoodLabeling.Domain.Entities;
  7 +using SqlSugar;
  8 +using Volo.Abp;
  9 +using Volo.Abp.Application.Services;
  10 +using Volo.Abp.Guids;
  11 +using Volo.Abp.Uow;
  12 +using Yi.Framework.SqlSugarCore.Abstractions;
  13 +
  14 +namespace FoodLabeling.Application.Services;
  15 +
  16 +/// <summary>
  17 +/// 标签模板管理(Label Templates)
  18 +/// </summary>
  19 +public class LabelTemplateAppService : ApplicationService, ILabelTemplateAppService
  20 +{
  21 + private readonly ISqlSugarDbContext _dbContext;
  22 + private readonly IGuidGenerator _guidGenerator;
  23 +
  24 + public LabelTemplateAppService(ISqlSugarDbContext dbContext, IGuidGenerator guidGenerator)
  25 + {
  26 + _dbContext = dbContext;
  27 + _guidGenerator = guidGenerator;
  28 + }
  29 +
  30 + public async Task<PagedResultWithPageDto<LabelTemplateGetListOutputDto>> GetListAsync(LabelTemplateGetListInputVo input)
  31 + {
  32 + RefAsync<int> total = 0;
  33 + var keyword = input.Keyword?.Trim();
  34 + var locationId = input.LocationId?.Trim();
  35 + var specifiedTemplateIds = new HashSet<string>();
  36 +
  37 + if (!string.IsNullOrWhiteSpace(locationId))
  38 + {
  39 + var rows = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateLocationDbEntity>()
  40 + .Where(x => x.LocationId == locationId)
  41 + .Select(x => x.TemplateId)
  42 + .ToListAsync();
  43 +
  44 + specifiedTemplateIds = rows.ToHashSet();
  45 + }
  46 +
  47 + var query = _dbContext.SqlSugarClient.Queryable<FlLabelTemplateDbEntity>()
  48 + .Where(x => !x.IsDeleted)
  49 + .WhereIF(!string.IsNullOrWhiteSpace(keyword), x =>
  50 + x.TemplateName.Contains(keyword!) || x.TemplateCode.Contains(keyword!))
  51 + .WhereIF(!string.IsNullOrWhiteSpace(input.LabelType), x => x.LabelType == input.LabelType)
  52 + .WhereIF(input.State != null, x => x.State == input.State);
  53 +
  54 + if (!string.IsNullOrWhiteSpace(locationId))
  55 + {
  56 + query = query.Where(x => x.AppliedLocationType == "ALL" || specifiedTemplateIds.Contains(x.Id));
  57 + }
  58 +
  59 + query = !string.IsNullOrWhiteSpace(input.Sorting)
  60 + ? query.OrderBy(input.Sorting)
  61 + : query.OrderByDescending(x => x.LastModificationTime ?? x.CreationTime);
  62 +
  63 + var pageEntities = await query.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
  64 + var templateIds = pageEntities.Select(x => x.Id).ToList();
  65 +
  66 + // element count (Contents)
  67 + var elementCounts = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateElementDbEntity>()
  68 + .Where(x => templateIds.Contains(x.TemplateId))
  69 + .GroupBy(x => x.TemplateId)
  70 + .Select(x => new { TemplateId = x.TemplateId, Count = SqlFunc.AggregateCount(x.Id) })
  71 + .ToListAsync();
  72 + var elementCountMap = elementCounts.ToDictionary(x => x.TemplateId, x => (int)x.Count);
  73 +
  74 + // applied locations (for LocationText)
  75 + var templateLocationRows = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateLocationDbEntity>()
  76 + .Where(x => templateIds.Contains(x.TemplateId))
  77 + .ToListAsync();
  78 +
  79 + var locationIdSet = templateLocationRows.Select(x => x.LocationId).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList();
  80 + var locationGuidList = locationIdSet
  81 + .Where(x => Guid.TryParse(x, out _))
  82 + .Select(Guid.Parse)
  83 + .Distinct()
  84 + .ToList();
  85 +
  86 + var locationMap = new Dictionary<Guid, LocationAggregateRoot>();
  87 + if (locationGuidList.Count > 0)
  88 + {
  89 + var locations = await _dbContext.SqlSugarClient.Queryable<LocationAggregateRoot>()
  90 + .Where(x => !x.IsDeleted)
  91 + .Where(x => locationGuidList.Contains(x.Id))
  92 + .ToListAsync();
  93 + locationMap = locations.ToDictionary(x => x.Id, x => x);
  94 + }
  95 +
  96 + string GetFirstLocationName(string templateDbId)
  97 + {
  98 + var first = templateLocationRows.FirstOrDefault(x => x.TemplateId == templateDbId);
  99 + if (first is null) return "Specified";
  100 + if (Guid.TryParse(first.LocationId, out var gid) && locationMap.TryGetValue(gid, out var loc))
  101 + {
  102 + return loc.LocationName ?? loc.LocationCode;
  103 + }
  104 + return "Specified";
  105 + }
  106 +
  107 + var items = pageEntities.Select(x =>
  108 + {
  109 + var lastEdited = x.LastModificationTime ?? x.CreationTime;
  110 + var contentsCount = elementCountMap.TryGetValue(x.Id, out var c) ? c : 0;
  111 + var locationText = x.AppliedLocationType == "ALL" ? "All Locations" : GetFirstLocationName(x.Id);
  112 + var sizeText = $"{x.Width}x{x.Height}{x.Unit}";
  113 +
  114 + return new LabelTemplateGetListOutputDto
  115 + {
  116 + Id = x.TemplateCode, // front-end uses templateCode as identifier
  117 + TemplateCode = x.TemplateCode,
  118 + TemplateName = x.TemplateName,
  119 + LabelType = x.LabelType,
  120 + LocationText = locationText,
  121 + ContentsCount = contentsCount,
  122 + SizeText = sizeText,
  123 + VersionNo = x.VersionNo,
  124 + LastEdited = lastEdited
  125 + };
  126 + }).ToList();
  127 +
  128 + return BuildPagedResult(input.SkipCount, input.MaxResultCount, total, items);
  129 + }
  130 +
  131 + public async Task<LabelTemplateGetOutputDto> GetAsync(string id)
  132 + {
  133 + var template = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateDbEntity>()
  134 + .FirstAsync(x => !x.IsDeleted && x.TemplateCode == id);
  135 + if (template is null)
  136 + {
  137 + throw new UserFriendlyException("模板不存在");
  138 + }
  139 +
  140 + var elements = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateElementDbEntity>()
  141 + .Where(x => x.TemplateId == template.Id)
  142 + .OrderBy(x => x.OrderNum)
  143 + .ToListAsync();
  144 +
  145 + List<LabelTemplateElementDto> MapElements()
  146 + {
  147 + return elements.Select(e =>
  148 + {
  149 + object? cfg = null;
  150 + if (!string.IsNullOrWhiteSpace(e.ConfigJson))
  151 + {
  152 + cfg = JsonSerializer.Deserialize<object>(e.ConfigJson);
  153 + }
  154 +
  155 + return new LabelTemplateElementDto
  156 + {
  157 + Id = e.ElementKey,
  158 + ElementType = e.ElementType,
  159 + PosX = e.PosX,
  160 + PosY = e.PosY,
  161 + Width = e.Width,
  162 + Height = e.Height,
  163 + Rotation = e.Rotation,
  164 + BorderType = e.BorderType,
  165 + ZIndex = e.ZIndex,
  166 + OrderNum = e.OrderNum,
  167 + ValueSourceType = e.ValueSourceType,
  168 + BindingExpr = e.BindingExpr,
  169 + AutoQueryKey = e.AutoQueryKey,
  170 + InputKey = e.InputKey,
  171 + IsRequiredInput = e.IsRequiredInput,
  172 + ConfigJson = cfg
  173 + };
  174 + }).ToList();
  175 + }
  176 +
  177 + var appliedLocationIds = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateLocationDbEntity>()
  178 + .Where(x => x.TemplateId == template.Id)
  179 + .Select(x => x.LocationId)
  180 + .ToListAsync();
  181 +
  182 + return new LabelTemplateGetOutputDto
  183 + {
  184 + Id = template.TemplateCode,
  185 + TemplateCode = template.TemplateCode,
  186 + TemplateName = template.TemplateName,
  187 + LabelType = template.LabelType,
  188 + Unit = template.Unit,
  189 + Width = template.Width,
  190 + Height = template.Height,
  191 + AppliedLocationType = template.AppliedLocationType,
  192 + ShowRuler = template.ShowRuler,
  193 + ShowGrid = template.ShowGrid,
  194 + VersionNo = template.VersionNo,
  195 + State = template.State,
  196 + Elements = MapElements(),
  197 + AppliedLocationIds = appliedLocationIds
  198 + };
  199 + }
  200 +
  201 + [UnitOfWork]
  202 + public async Task<LabelTemplateGetOutputDto> CreateAsync(LabelTemplateCreateInputVo input)
  203 + {
  204 + var code = input.TemplateCode?.Trim();
  205 + var name = input.TemplateName?.Trim();
  206 + if (string.IsNullOrWhiteSpace(code))
  207 + {
  208 + throw new UserFriendlyException("模板编码不能为空");
  209 + }
  210 + if (string.IsNullOrWhiteSpace(name))
  211 + {
  212 + throw new UserFriendlyException("模板名称不能为空");
  213 + }
  214 +
  215 + var duplicated = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateDbEntity>()
  216 + .AnyAsync(x => !x.IsDeleted && x.TemplateCode == code);
  217 + if (duplicated)
  218 + {
  219 + throw new UserFriendlyException("模板编码已存在");
  220 + }
  221 +
  222 + var now = DateTime.Now;
  223 + var templateId = _guidGenerator.Create().ToString();
  224 + var entity = new FlLabelTemplateDbEntity
  225 + {
  226 + Id = templateId,
  227 + IsDeleted = false,
  228 + CreationTime = now,
  229 + CreatorId = CurrentUser?.Id?.ToString(),
  230 + LastModifierId = CurrentUser?.Id?.ToString(),
  231 + LastModificationTime = now,
  232 + ConcurrencyStamp = string.Empty,
  233 + TemplateCode = code,
  234 + TemplateName = name,
  235 + LabelType = input.LabelType,
  236 + Unit = string.IsNullOrWhiteSpace(input.Unit) ? "inch" : input.Unit.Trim(),
  237 + Width = input.Width,
  238 + Height = input.Height,
  239 + AppliedLocationType = string.IsNullOrWhiteSpace(input.AppliedLocationType) ? "ALL" : input.AppliedLocationType.Trim(),
  240 + ShowRuler = input.ShowRuler,
  241 + ShowGrid = input.ShowGrid,
  242 + VersionNo = 1,
  243 + State = input.State
  244 + };
  245 +
  246 + await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync();
  247 +
  248 + await RebuildTemplateElementsAndLocationsAsync(entity.Id, input.Elements, entity.AppliedLocationType, input.AppliedLocationIds);
  249 +
  250 + return await GetAsync(code);
  251 + }
  252 +
  253 + [UnitOfWork]
  254 + public async Task<LabelTemplateGetOutputDto> UpdateAsync(string id, LabelTemplateUpdateInputVo input)
  255 + {
  256 + var template = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateDbEntity>()
  257 + .FirstAsync(x => !x.IsDeleted && x.TemplateCode == id);
  258 + if (template is null)
  259 + {
  260 + throw new UserFriendlyException("模板不存在");
  261 + }
  262 +
  263 + var code = input.TemplateCode?.Trim();
  264 + var name = input.TemplateName?.Trim();
  265 + if (!string.IsNullOrWhiteSpace(code) && !string.Equals(code, template.TemplateCode, StringComparison.OrdinalIgnoreCase))
  266 + {
  267 + var duplicated = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateDbEntity>()
  268 + .AnyAsync(x => !x.IsDeleted && x.TemplateCode == code);
  269 + if (duplicated)
  270 + {
  271 + throw new UserFriendlyException("模板编码已存在");
  272 + }
  273 + }
  274 +
  275 + template.TemplateName = name ?? template.TemplateName;
  276 + template.LabelType = input.LabelType;
  277 + template.Unit = string.IsNullOrWhiteSpace(input.Unit) ? template.Unit : input.Unit.Trim();
  278 + template.Width = input.Width;
  279 + template.Height = input.Height;
  280 + template.AppliedLocationType = string.IsNullOrWhiteSpace(input.AppliedLocationType) ? template.AppliedLocationType : input.AppliedLocationType.Trim();
  281 + template.ShowRuler = input.ShowRuler;
  282 + template.ShowGrid = input.ShowGrid;
  283 + template.State = input.State;
  284 + template.VersionNo = template.VersionNo + 1;
  285 + template.LastModifierId = CurrentUser?.Id?.ToString();
  286 + template.LastModificationTime = DateTime.Now;
  287 + if (!string.IsNullOrWhiteSpace(code))
  288 + {
  289 + template.TemplateCode = code;
  290 + }
  291 +
  292 + await _dbContext.SqlSugarClient.Updateable(template).ExecuteCommandAsync();
  293 +
  294 + await RebuildTemplateElementsAndLocationsAsync(template.Id, input.Elements, template.AppliedLocationType, input.AppliedLocationIds);
  295 +
  296 + return await GetAsync(template.TemplateCode);
  297 + }
  298 +
  299 + [UnitOfWork]
  300 + public async Task DeleteAsync(string id)
  301 + {
  302 + var template = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateDbEntity>()
  303 + .FirstAsync(x => !x.IsDeleted && x.TemplateCode == id);
  304 + if (template is null)
  305 + {
  306 + return;
  307 + }
  308 +
  309 + var used = await _dbContext.SqlSugarClient.Queryable<FlLabelDbEntity>()
  310 + .AnyAsync(x => !x.IsDeleted && x.TemplateId == template.Id);
  311 +
  312 + if (used)
  313 + {
  314 + throw new UserFriendlyException("该模板已被标签引用,无法删除");
  315 + }
  316 +
  317 + template.IsDeleted = true;
  318 + template.LastModifierId = CurrentUser?.Id?.ToString();
  319 + template.LastModificationTime = DateTime.Now;
  320 + await _dbContext.SqlSugarClient.Updateable(template).ExecuteCommandAsync();
  321 +
  322 + // 删除子表数据(子表无 IsDeleted 字段)
  323 + await _dbContext.SqlSugarClient.Deleteable<FlLabelTemplateElementDbEntity>()
  324 + .Where(x => x.TemplateId == template.Id)
  325 + .ExecuteCommandAsync();
  326 + await _dbContext.SqlSugarClient.Deleteable<FlLabelTemplateLocationDbEntity>()
  327 + .Where(x => x.TemplateId == template.Id)
  328 + .ExecuteCommandAsync();
  329 + }
  330 +
  331 + private async Task RebuildTemplateElementsAndLocationsAsync(
  332 + string templateDbId,
  333 + List<LabelTemplateElementDto> elements,
  334 + string appliedLocationType,
  335 + List<string> appliedLocationIds)
  336 + {
  337 + // elements 重建
  338 + await _dbContext.SqlSugarClient.Deleteable<FlLabelTemplateElementDbEntity>()
  339 + .Where(x => x.TemplateId == templateDbId)
  340 + .ExecuteCommandAsync();
  341 +
  342 + if (elements is not null && elements.Count > 0)
  343 + {
  344 + var now = DateTime.Now;
  345 + var rows = elements.Select(e =>
  346 + {
  347 + object? cfg = e.ConfigJson;
  348 + var configJson = cfg == null ? null : JsonSerializer.Serialize(cfg);
  349 + return new FlLabelTemplateElementDbEntity
  350 + {
  351 + Id = _guidGenerator.Create().ToString(),
  352 + TemplateId = templateDbId,
  353 + ElementKey = e.Id,
  354 + ElementType = e.ElementType,
  355 + PosX = e.PosX,
  356 + PosY = e.PosY,
  357 + Width = e.Width,
  358 + Height = e.Height,
  359 + Rotation = string.IsNullOrWhiteSpace(e.Rotation) ? "horizontal" : e.Rotation,
  360 + BorderType = string.IsNullOrWhiteSpace(e.BorderType) ? "none" : e.BorderType,
  361 + ZIndex = e.ZIndex,
  362 + OrderNum = e.OrderNum,
  363 + ValueSourceType = string.IsNullOrWhiteSpace(e.ValueSourceType) ? "FIXED" : e.ValueSourceType,
  364 + BindingExpr = e.BindingExpr,
  365 + AutoQueryKey = e.AutoQueryKey,
  366 + InputKey = e.InputKey,
  367 + IsRequiredInput = e.IsRequiredInput,
  368 + ConfigJson = configJson
  369 + };
  370 + }).ToList();
  371 +
  372 + await _dbContext.SqlSugarClient.Insertable(rows).ExecuteCommandAsync();
  373 + }
  374 +
  375 + // applied locations 重建
  376 + await _dbContext.SqlSugarClient.Deleteable<FlLabelTemplateLocationDbEntity>()
  377 + .Where(x => x.TemplateId == templateDbId)
  378 + .ExecuteCommandAsync();
  379 +
  380 + if (string.Equals(appliedLocationType, "SPECIFIED", StringComparison.OrdinalIgnoreCase))
  381 + {
  382 + var ids = appliedLocationIds?.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).Distinct().ToList() ?? new();
  383 + if (ids.Count == 0)
  384 + {
  385 + throw new UserFriendlyException("指定门店模板必须至少选择一个门店");
  386 + }
  387 +
  388 + var locRows = ids.Select(locId => new FlLabelTemplateLocationDbEntity
  389 + {
  390 + Id = _guidGenerator.Create().ToString(),
  391 + TemplateId = templateDbId,
  392 + LocationId = locId
  393 + }).ToList();
  394 +
  395 + await _dbContext.SqlSugarClient.Insertable(locRows).ExecuteCommandAsync();
  396 + }
  397 + }
  398 +
  399 + private static PagedResultWithPageDto<T> BuildPagedResult<T>(int skipCount, int maxResultCount, int total, List<T> items)
  400 + {
  401 + var pageSize = maxResultCount <= 0 ? items.Count : maxResultCount;
  402 + var pageIndex = pageSize <= 0 ? 1 : (skipCount / pageSize) + 1;
  403 + var totalPages = pageSize <= 0 ? 0 : (int)Math.Ceiling(total / (double)pageSize);
  404 + return new PagedResultWithPageDto<T>
  405 + {
  406 + PageIndex = pageIndex,
  407 + PageSize = pageSize,
  408 + TotalCount = total,
  409 + TotalPages = totalPages,
  410 + Items = items
  411 + };
  412 + }
  413 +
  414 + private static PagedResultWithPageDto<T> BuildPagedResult<T>(int skipCount, int maxResultCount, RefAsync<int> total, List<T> items)
  415 + {
  416 + var pageSize = maxResultCount <= 0 ? items.Count : maxResultCount;
  417 + var pageIndex = pageSize <= 0 ? 1 : (skipCount / pageSize) + 1;
  418 + var totalPages = pageSize <= 0 ? 0 : (int)Math.Ceiling(total.Value / (double)pageSize);
  419 + return new PagedResultWithPageDto<T>
  420 + {
  421 + PageIndex = pageIndex,
  422 + PageSize = pageSize,
  423 + TotalCount = total.Value,
  424 + TotalPages = totalPages,
  425 + Items = items
  426 + };
  427 + }
  428 +}
  429 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelTypeAppService.cs 0 → 100644
  1 +using FoodLabeling.Application.Contracts.Dtos.Common;
  2 +using FoodLabeling.Application.Contracts.Dtos.LabelType;
  3 +using FoodLabeling.Application.Contracts.IServices;
  4 +using FoodLabeling.Application.Services.DbModels;
  5 +using SqlSugar;
  6 +using Volo.Abp;
  7 +using Volo.Abp.Application.Services;
  8 +using Volo.Abp.Guids;
  9 +using Yi.Framework.SqlSugarCore.Abstractions;
  10 +
  11 +namespace FoodLabeling.Application.Services;
  12 +
  13 +public class LabelTypeAppService : ApplicationService, ILabelTypeAppService
  14 +{
  15 + private readonly ISqlSugarDbContext _dbContext;
  16 + private readonly IGuidGenerator _guidGenerator;
  17 +
  18 + public LabelTypeAppService(ISqlSugarDbContext dbContext, IGuidGenerator guidGenerator)
  19 + {
  20 + _dbContext = dbContext;
  21 + _guidGenerator = guidGenerator;
  22 + }
  23 +
  24 + public async Task<PagedResultWithPageDto<LabelTypeGetListOutputDto>> GetListAsync(LabelTypeGetListInputVo input)
  25 + {
  26 + RefAsync<int> total = 0;
  27 + var keyword = input.Keyword?.Trim();
  28 +
  29 + var query = _dbContext.SqlSugarClient.Queryable<FlLabelTypeDbEntity>()
  30 + .Where(x => !x.IsDeleted)
  31 + .WhereIF(!string.IsNullOrWhiteSpace(keyword), x => x.TypeCode.Contains(keyword!) || x.TypeName.Contains(keyword!))
  32 + .WhereIF(input.State != null, x => x.State == input.State);
  33 +
  34 + if (!string.IsNullOrWhiteSpace(input.Sorting))
  35 + {
  36 + query = query.OrderBy(input.Sorting);
  37 + }
  38 + else
  39 + {
  40 + query = query.OrderByDescending(x => x.OrderNum).OrderByDescending(x => x.CreationTime);
  41 + }
  42 +
  43 + var entities = await query.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
  44 + var ids = entities.Select(x => x.Id).ToList();
  45 +
  46 + var countRows = await _dbContext.SqlSugarClient.Queryable<FlLabelDbEntity>()
  47 + .Where(x => !x.IsDeleted)
  48 + .Where(x => x.LabelTypeId != null && ids.Contains(x.LabelTypeId))
  49 + .GroupBy(x => x.LabelTypeId)
  50 + .Select(x => new { TypeId = x.LabelTypeId, Count = SqlFunc.AggregateCount(x.Id) })
  51 + .ToListAsync();
  52 + var countMap = countRows.ToDictionary(x => x.TypeId!, x => (long)x.Count);
  53 +
  54 + var items = entities.Select(x => new LabelTypeGetListOutputDto
  55 + {
  56 + Id = x.Id,
  57 + TypeCode = x.TypeCode,
  58 + TypeName = x.TypeName,
  59 + State = x.State,
  60 + OrderNum = x.OrderNum,
  61 + NoOfLabels = countMap.TryGetValue(x.Id, out var count) ? count : 0,
  62 + LastEdited = x.LastModificationTime ?? x.CreationTime
  63 + }).ToList();
  64 +
  65 + return BuildPagedResult(input.SkipCount, input.MaxResultCount, total, items);
  66 + }
  67 +
  68 + public async Task<LabelTypeGetOutputDto> GetAsync(string id)
  69 + {
  70 + var entity = await _dbContext.SqlSugarClient.Queryable<FlLabelTypeDbEntity>()
  71 + .FirstAsync(x => x.Id == id && !x.IsDeleted);
  72 + if (entity is null)
  73 + {
  74 + throw new UserFriendlyException("标签类型不存在");
  75 + }
  76 +
  77 + return MapToGetOutput(entity);
  78 + }
  79 +
  80 + public async Task<LabelTypeGetOutputDto> CreateAsync(LabelTypeCreateInputVo input)
  81 + {
  82 + var code = input.TypeCode?.Trim();
  83 + var name = input.TypeName?.Trim();
  84 + if (string.IsNullOrWhiteSpace(code) || string.IsNullOrWhiteSpace(name))
  85 + {
  86 + throw new UserFriendlyException("类型编码和名称不能为空");
  87 + }
  88 +
  89 + var duplicated = await _dbContext.SqlSugarClient.Queryable<FlLabelTypeDbEntity>()
  90 + .AnyAsync(x => !x.IsDeleted && (x.TypeCode == code || x.TypeName == name));
  91 + if (duplicated)
  92 + {
  93 + throw new UserFriendlyException("类型编码或名称已存在");
  94 + }
  95 +
  96 + var entity = new FlLabelTypeDbEntity
  97 + {
  98 + Id = _guidGenerator.Create().ToString(),
  99 + TypeCode = code,
  100 + TypeName = name,
  101 + State = input.State,
  102 + OrderNum = input.OrderNum
  103 + };
  104 +
  105 + await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync();
  106 + return await GetAsync(entity.Id);
  107 + }
  108 +
  109 + public async Task<LabelTypeGetOutputDto> UpdateAsync(string id, LabelTypeUpdateInputVo input)
  110 + {
  111 + var entity = await _dbContext.SqlSugarClient.Queryable<FlLabelTypeDbEntity>()
  112 + .FirstAsync(x => x.Id == id && !x.IsDeleted);
  113 + if (entity is null)
  114 + {
  115 + throw new UserFriendlyException("标签类型不存在");
  116 + }
  117 +
  118 + var code = input.TypeCode?.Trim();
  119 + var name = input.TypeName?.Trim();
  120 + if (string.IsNullOrWhiteSpace(code) || string.IsNullOrWhiteSpace(name))
  121 + {
  122 + throw new UserFriendlyException("类型编码和名称不能为空");
  123 + }
  124 +
  125 + var duplicated = await _dbContext.SqlSugarClient.Queryable<FlLabelTypeDbEntity>()
  126 + .AnyAsync(x => !x.IsDeleted && x.Id != id && (x.TypeCode == code || x.TypeName == name));
  127 + if (duplicated)
  128 + {
  129 + throw new UserFriendlyException("类型编码或名称已存在");
  130 + }
  131 +
  132 + entity.TypeCode = code;
  133 + entity.TypeName = name;
  134 + entity.State = input.State;
  135 + entity.OrderNum = input.OrderNum;
  136 + entity.LastModificationTime = DateTime.Now;
  137 + entity.LastModifierId = CurrentUser?.Id?.ToString();
  138 +
  139 + await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync();
  140 + return await GetAsync(id);
  141 + }
  142 +
  143 + public async Task DeleteAsync(string id)
  144 + {
  145 + var entity = await _dbContext.SqlSugarClient.Queryable<FlLabelTypeDbEntity>()
  146 + .FirstAsync(x => x.Id == id && !x.IsDeleted);
  147 + if (entity is null)
  148 + {
  149 + return;
  150 + }
  151 +
  152 + var used = await _dbContext.SqlSugarClient.Queryable<FlLabelDbEntity>()
  153 + .AnyAsync(x => !x.IsDeleted && x.LabelTypeId == id);
  154 + if (used)
  155 + {
  156 + throw new UserFriendlyException("该标签类型已被标签引用,无法删除");
  157 + }
  158 +
  159 + entity.IsDeleted = true;
  160 + entity.LastModificationTime = DateTime.Now;
  161 + entity.LastModifierId = CurrentUser?.Id?.ToString();
  162 + await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync();
  163 + }
  164 +
  165 + private static LabelTypeGetOutputDto MapToGetOutput(FlLabelTypeDbEntity x)
  166 + {
  167 + return new LabelTypeGetOutputDto
  168 + {
  169 + Id = x.Id,
  170 + TypeCode = x.TypeCode,
  171 + TypeName = x.TypeName,
  172 + State = x.State,
  173 + OrderNum = x.OrderNum
  174 + };
  175 + }
  176 +
  177 + private static PagedResultWithPageDto<T> BuildPagedResult<T>(int skipCount, int maxResultCount, int total, List<T> items)
  178 + {
  179 + var pageSize = maxResultCount <= 0 ? items.Count : maxResultCount;
  180 + var pageIndex = pageSize <= 0 ? 1 : (skipCount / pageSize) + 1;
  181 + var totalPages = pageSize <= 0 ? 0 : (int)Math.Ceiling(total / (double)pageSize);
  182 + return new PagedResultWithPageDto<T>
  183 + {
  184 + PageIndex = pageIndex,
  185 + PageSize = pageSize,
  186 + TotalCount = total,
  187 + TotalPages = totalPages,
  188 + Items = items
  189 + };
  190 + }
  191 +}
  192 +
... ...