diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPrintInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPrintInputVo.cs
index 9a2aaa0..e456744 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPrintInputVo.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPrintInputVo.cs
@@ -31,6 +31,12 @@ public class UsAppLabelPrintInputVo
public int PrintQuantity { get; set; } = 1;
///
+ /// 客户端幂等请求Id(可选)。
+ /// 同一个 clientRequestId 重复调用 print 接口时,后端会直接返回首次创建的 batchId/taskIds,不会重复写库。
+ ///
+ public string? ClientRequestId { get; set; }
+
+ ///
/// 业务基准时间(用于 DATE/TIME 等元素的计算)
///
public DateTime? BaseTime { get; set; }
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPrintOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPrintOutputDto.cs
index d7c202c..f6b6c80 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPrintOutputDto.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPrintOutputDto.cs
@@ -1,3 +1,5 @@
+using System.Collections.Generic;
+
namespace FoodLabeling.Application.Contracts.Dtos.UsAppLabeling;
///
@@ -8,5 +10,9 @@ public class UsAppLabelPrintOutputDto
public string TaskId { get; set; } = string.Empty;
public int PrintQuantity { get; set; }
+
+ public string? BatchId { get; set; }
+
+ public List TaskIds { get; set; } = new();
}
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelPrintDataDbEntity.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelPrintDataDbEntity.cs
index 415b402..2bfa4bf 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelPrintDataDbEntity.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelPrintDataDbEntity.cs
@@ -11,30 +11,14 @@ public class FlLabelPrintDataDbEntity
[SugarColumn(IsPrimaryKey = true)]
public string Id { get; set; } = string.Empty;
- public bool IsDeleted { get; set; }
+ public string PrintTaskId { get; set; } = string.Empty;
- public DateTime CreationTime { get; set; }
+ public string ElementId { get; set; } = string.Empty;
- public string? CreatorId { get; set; }
+ public string? ElementName { get; set; }
- public string? LastModifierId { get; set; }
+ public string? RenderValue { get; set; }
- public DateTime? LastModificationTime { get; set; }
-
- public string ConcurrencyStamp { get; set; } = string.Empty;
-
- public string TaskId { get; set; } = string.Empty;
-
- public int? CopyIndex { get; set; }
-
- ///
- /// 原始打印输入(json 字段,直接保存为字符串)
- ///
- public string? PrintInputJson { get; set; }
-
- ///
- /// 解析后的可打印数据(建议保存为 json 字符串)
- ///
- public string? RenderDataJson { get; set; }
+ public string? RenderConfigJson { get; set; }
}
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelPrintTaskDbEntity.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelPrintTaskDbEntity.cs
index 8b73e7e..dd56f1d 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelPrintTaskDbEntity.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelPrintTaskDbEntity.cs
@@ -11,36 +11,44 @@ public class FlLabelPrintTaskDbEntity
[SugarColumn(IsPrimaryKey = true)]
public string Id { get; set; } = string.Empty;
- public bool IsDeleted { get; set; }
+ public string? BatchId { get; set; }
- public DateTime CreationTime { get; set; }
-
- public string? CreatorId { get; set; }
+ public int CopyIndex { get; set; } = 1;
- public string? LastModifierId { get; set; }
+ public string? ClientRequestId { get; set; }
- public DateTime? LastModificationTime { get; set; }
+ public string LabelId { get; set; } = string.Empty;
- public string ConcurrencyStamp { get; set; } = string.Empty;
-
- public string? LocationId { get; set; }
+ public string TemplateId { get; set; } = string.Empty;
- public string? LabelCode { get; set; }
+ public string? LabelTypeId { get; set; }
public string? ProductId { get; set; }
- public string? LabelTypeId { get; set; }
+ public string? LocationId { get; set; }
+
+ public DateTime? BaseTime { get; set; }
- public string? TemplateCode { get; set; }
+ public string? PrintInputJson { get; set; }
- public int PrintQuantity { get; set; }
+ public string? TemplateProductDefaultValuesJson { get; set; }
- public DateTime? BaseTime { get; set; }
+ public string RenderTemplateJson { get; set; } = string.Empty;
public string? PrinterId { get; set; }
public string? PrinterMac { get; set; }
public string? PrinterAddress { get; set; }
+
+ public string Status { get; set; } = "CREATED";
+
+ public DateTime? PrintedAt { get; set; }
+
+ public string? ErrorMessage { get; set; }
+
+ public string? CreatedBy { get; set; }
+
+ public DateTime CreationTime { get; set; }
}
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppLabelingAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppLabelingAppService.cs
index d0b96b7..c26db25 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppLabelingAppService.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppLabelingAppService.cs
@@ -361,6 +361,28 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
}
var quantity = input.PrintQuantity <= 0 ? 1 : input.PrintQuantity;
+ var clientRequestId = input.ClientRequestId?.Trim();
+ if (!string.IsNullOrWhiteSpace(clientRequestId))
+ {
+ // 幂等:同一个 clientRequestId 重复调用,直接返回首次创建的任务集合
+ var existed = await _dbContext.SqlSugarClient.Queryable()
+ .Where(x => x.ClientRequestId == clientRequestId)
+ .OrderBy(x => x.CopyIndex)
+ .ToListAsync();
+
+ if (existed is not null && existed.Count > 0)
+ {
+ var existedBatchId = existed.First().BatchId;
+ var existedTaskIds = existed.Select(x => x.Id).ToList();
+ return new UsAppLabelPrintOutputDto
+ {
+ TaskId = existedTaskIds.FirstOrDefault() ?? string.Empty,
+ PrintQuantity = existedTaskIds.Count,
+ BatchId = existedBatchId,
+ TaskIds = existedTaskIds
+ };
+ }
+ }
// 校验 label + location,并补齐一些顶部字段用于任务表落库
var labelRow = await _dbContext.SqlSugarClient
@@ -372,9 +394,10 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
.Where((l, t, tpl) => l.LabelCode == labelCode)
.Select((l, t, tpl) => new
{
+ l.Id,
l.LocationId,
l.LabelTypeId,
- TemplateCode = tpl.TemplateCode
+ l.TemplateId
})
.FirstAsync();
@@ -388,84 +411,124 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
throw new UserFriendlyException("该标签不属于当前门店");
}
+ var previewProductId = await ResolvePreviewProductIdAsync(labelRow.Id, input.ProductId);
+ var normalizedPrintInput = input.PrintInputJson?.ToDictionary(x => x.Key, x => (object?)x.Value);
+
+ // 解析模板 elements(与预览一致的渲染数据)
+ var resolvedTemplate = await _labelAppService.PreviewAsync(new LabelPreviewResolveInputVo
+ {
+ LabelCode = labelCode,
+ ProductId = previewProductId,
+ BaseTime = input.BaseTime,
+ PrintInputJson = normalizedPrintInput
+ });
+
+ var templateProductDefaultValuesJson = await ResolveTemplateProductDefaultValuesJsonAsync(
+ labelRow.TemplateId,
+ previewProductId,
+ labelRow.LabelTypeId);
+
var printInputJsonStr = input.PrintInputJson is null
? null
: JsonSerializer.Serialize(input.PrintInputJson);
+ var renderTemplateJsonStr = JsonSerializer.Serialize(resolvedTemplate);
- string renderDataJsonStr;
- var snapshotOk = false;
- if (input.TemplateSnapshot.HasValue)
- {
- var snapEl = input.TemplateSnapshot.Value;
- if (snapEl.ValueKind == JsonValueKind.Object
- && snapEl.TryGetProperty("elements", out var elArr)
- && elArr.ValueKind == JsonValueKind.Array)
- {
- // App 与出纸一致的合并模板(label-template 同构),供打印历史/重打直接使用
- renderDataJsonStr = snapEl.GetRawText();
- snapshotOk = true;
- }
- }
+ var now = DateTime.Now;
+ var currentUserId = CurrentUser?.Id?.ToString();
+ var batchId = _guidGenerator.Create().ToString();
+ var taskIds = new List();
- if (!snapshotOk)
+ for (var i = 1; i <= quantity; i++)
{
- var resolvedTemplate = await _labelAppService.PreviewAsync(new LabelPreviewResolveInputVo
+ var taskId = _guidGenerator.Create().ToString();
+ taskIds.Add(taskId);
+
+ var task = new FlLabelPrintTaskDbEntity
{
- LabelCode = labelCode,
- ProductId = input.ProductId?.Trim(),
+ Id = taskId,
+ BatchId = batchId,
+ CopyIndex = i,
+ ClientRequestId = string.IsNullOrWhiteSpace(clientRequestId) ? null : clientRequestId,
+ LabelId = labelRow.Id,
+ TemplateId = labelRow.TemplateId,
+ LabelTypeId = labelRow.LabelTypeId,
+ ProductId = previewProductId,
+ LocationId = locationId,
BaseTime = input.BaseTime,
- PrintInputJson = input.PrintInputJson
- });
- renderDataJsonStr = JsonSerializer.Serialize(resolvedTemplate);
- }
+ PrintInputJson = printInputJsonStr,
+ TemplateProductDefaultValuesJson = templateProductDefaultValuesJson,
+ RenderTemplateJson = renderTemplateJsonStr,
+ PrinterId = input.PrinterId?.Trim(),
+ PrinterMac = input.PrinterMac?.Trim(),
+ PrinterAddress = input.PrinterAddress?.Trim(),
+ Status = "CREATED",
+ PrintedAt = null,
+ ErrorMessage = null,
+ CreatedBy = currentUserId,
+ CreationTime = now
+ };
- var now = DateTime.Now;
- var currentUserId = CurrentUser?.Id?.ToString();
- var taskId = _guidGenerator.Create().ToString();
+ await _dbContext.SqlSugarClient.Insertable(task).ExecuteCommandAsync();
- var task = new FlLabelPrintTaskDbEntity
- {
- Id = taskId,
- IsDeleted = false,
- CreationTime = now,
- CreatorId = currentUserId,
- ConcurrencyStamp = _guidGenerator.Create().ToString("N"),
- LocationId = locationId,
- LabelCode = labelCode,
- ProductId = input.ProductId?.Trim(),
- LabelTypeId = labelRow.LabelTypeId,
- TemplateCode = labelRow.TemplateCode,
- PrintQuantity = quantity,
- BaseTime = input.BaseTime,
- PrinterId = input.PrinterId?.Trim(),
- PrinterMac = input.PrinterMac?.Trim(),
- PrinterAddress = input.PrinterAddress?.Trim()
- };
+ var rows = resolvedTemplate.Elements.Select(e =>
+ {
+ var cfgJson = e.ConfigJson is null ? null : JsonSerializer.Serialize(e.ConfigJson);
+ string? renderValue = null;
+ if (e.ConfigJson is JsonElement je && je.ValueKind == JsonValueKind.Object && je.TryGetProperty("text", out var tv))
+ {
+ renderValue = tv.ValueKind == JsonValueKind.String ? tv.GetString() : tv.ToString();
+ }
+ else if (e.ConfigJson is Dictionary dict && dict.TryGetValue("text", out var v))
+ {
+ renderValue = v?.ToString();
+ }
- await _dbContext.SqlSugarClient.Insertable(task).ExecuteCommandAsync();
+ return new FlLabelPrintDataDbEntity
+ {
+ Id = _guidGenerator.Create().ToString(),
+ PrintTaskId = taskId,
+ ElementId = e.Id?.Trim() ?? string.Empty,
+ ElementName = e.ElementName?.Trim(),
+ RenderValue = renderValue,
+ RenderConfigJson = cfgJson
+ };
+ }).Where(x => !string.IsNullOrWhiteSpace(x.ElementId)).ToList();
- var dataRows = Enumerable.Range(1, quantity).Select(i => new FlLabelPrintDataDbEntity
- {
- Id = _guidGenerator.Create().ToString(),
- IsDeleted = false,
- CreationTime = now,
- CreatorId = currentUserId,
- ConcurrencyStamp = _guidGenerator.Create().ToString("N"),
- TaskId = taskId,
- CopyIndex = i,
- PrintInputJson = printInputJsonStr,
- RenderDataJson = renderDataJsonStr
- }).ToList();
-
- await _dbContext.SqlSugarClient.Insertable(dataRows).ExecuteCommandAsync();
+ if (rows.Count > 0)
+ {
+ await _dbContext.SqlSugarClient.Insertable(rows).ExecuteCommandAsync();
+ }
+ }
return new UsAppLabelPrintOutputDto
{
- TaskId = taskId,
- PrintQuantity = quantity
+ TaskId = taskIds.FirstOrDefault() ?? string.Empty,
+ PrintQuantity = quantity,
+ BatchId = batchId,
+ TaskIds = taskIds
};
}
+ private async Task ResolveTemplateProductDefaultValuesJsonAsync(
+ string templateId,
+ string? productId,
+ string labelTypeId)
+ {
+ if (string.IsNullOrWhiteSpace(templateId) || string.IsNullOrWhiteSpace(productId) || string.IsNullOrWhiteSpace(labelTypeId))
+ {
+ return null;
+ }
+
+ var productDefault = await _dbContext.SqlSugarClient.Queryable()
+ .Where(x => x.TemplateId == templateId)
+ .Where(x => x.ProductId == productId)
+ .Where(x => x.LabelTypeId == labelTypeId)
+ .OrderBy(x => x.OrderNum)
+ .FirstAsync();
+
+ return string.IsNullOrWhiteSpace(productDefault?.DefaultValuesJson) ? null : productDefault!.DefaultValuesJson;
+ }
+
private ISugarQueryable BuildLabelingJoinQuery(
string locationId,
List productIds,