diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/PrintLogGetListInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/PrintLogGetListInputVo.cs
new file mode 100644
index 0000000..f819a65
--- /dev/null
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/PrintLogGetListInputVo.cs
@@ -0,0 +1,15 @@
+using Volo.Abp.Application.Dtos;
+
+namespace FoodLabeling.Application.Contracts.Dtos.UsAppLabeling;
+
+///
+/// App 打印日志分页查询入参(仅当前登录账号 + 当前门店)
+///
+public class PrintLogGetListInputVo : PagedAndSortedResultRequestDto
+{
+ ///
+ /// 当前门店 Id(location.Id,Guid 字符串)
+ ///
+ public string LocationId { get; set; } = string.Empty;
+}
+
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/PrintLogItemDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/PrintLogItemDto.cs
new file mode 100644
index 0000000..920b592
--- /dev/null
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/PrintLogItemDto.cs
@@ -0,0 +1,44 @@
+namespace FoodLabeling.Application.Contracts.Dtos.UsAppLabeling;
+
+///
+/// 打印日志列表项
+///
+public class PrintLogItemDto
+{
+ /// 任务Id(fl_label_print_task.Id)
+ public string TaskId { get; set; } = string.Empty;
+
+ /// 批次Id(同一次点击 Print 共享)
+ public string? BatchId { get; set; }
+
+ /// 第几份(从 1 开始)
+ public int CopyIndex { get; set; }
+
+ /// 标签Id
+ public string LabelId { get; set; } = string.Empty;
+
+ /// 标签编码
+ public string LabelCode { get; set; } = string.Empty;
+
+ /// 产品Id
+ public string? ProductId { get; set; }
+
+ /// 产品名称
+ public string ProductName { get; set; } = "无";
+
+ /// 标签类型名称(来自 fl_label_type.TypeName)
+ public string TypeName { get; set; } = string.Empty;
+
+ /// 模板尺寸(来自 fl_label_template.Width/Height/Unit)
+ public string? LabelSizeText { get; set; }
+
+ /// 打印时间(PrintedAt ?? CreationTime)
+ public DateTime PrintedAt { get; set; }
+
+ /// 操作人姓名(当前登录账号 Name)
+ public string OperatorName { get; set; } = string.Empty;
+
+ /// 门店名称
+ public string LocationName { get; set; } = "无";
+}
+
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelReprintInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelReprintInputVo.cs
new file mode 100644
index 0000000..0c18998
--- /dev/null
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelReprintInputVo.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+
+namespace FoodLabeling.Application.Contracts.Dtos.UsAppLabeling;
+
+///
+/// App 重新打印入参(根据历史任务Id重打)
+///
+public class UsAppLabelReprintInputVo
+{
+ ///
+ /// 当前门店Id(用于权限校验,必须与历史任务一致)
+ ///
+ public string LocationId { get; set; } = string.Empty;
+
+ ///
+ /// 历史打印任务Id(fl_label_print_task.Id)
+ ///
+ public string TaskId { get; set; } = string.Empty;
+
+ ///
+ /// 重新打印份数(<=0 则按 1 处理;默认 1)
+ ///
+ public int PrintQuantity { get; set; } = 1;
+
+ ///
+ /// 客户端幂等请求Id(可选)。
+ /// 同一个 clientRequestId 重复调用 reprint 接口时,后端会直接返回首次创建的 batchId/taskIds,不会重复写库。
+ ///
+ public string? ClientRequestId { get; set; }
+
+ ///
+ /// 重新打印时可覆盖打印机Id(可选)
+ ///
+ public string? PrinterId { get; set; }
+
+ ///
+ /// 重新打印时可覆盖打印机蓝牙 MAC(可选)
+ ///
+ public string? PrinterMac { get; set; }
+
+ ///
+ /// 重新打印时可覆盖打印机地址(可选)
+ ///
+ public string? PrinterAddress { get; set; }
+}
+
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IUsAppLabelingAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IUsAppLabelingAppService.cs
index 0006eec..7cbe514 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IUsAppLabelingAppService.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IUsAppLabelingAppService.cs
@@ -1,4 +1,5 @@
using FoodLabeling.Application.Contracts.Dtos.UsAppLabeling;
+using FoodLabeling.Application.Contracts.Dtos.Common;
using Volo.Abp.Application.Services;
namespace FoodLabeling.Application.Contracts.IServices;
@@ -22,4 +23,14 @@ public interface IUsAppLabelingAppService : IApplicationService
/// App 打印:创建打印任务并落库打印明细(fl_label_print_task / fl_label_print_data)
///
Task PrintAsync(UsAppLabelPrintInputVo input);
+
+ ///
+ /// App 重新打印:根据历史任务Id重打(创建新任务与明细)
+ ///
+ Task ReprintAsync(UsAppLabelReprintInputVo input);
+
+ ///
+ /// App 打印日志:获取当前登录账号在当前门店打印的记录(分页,时间倒序)
+ ///
+ Task> GetPrintLogListAsync(PrintLogGetListInputVo input);
}
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 c26db25..11d27c6 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
@@ -4,16 +4,21 @@ using System.Globalization;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
+using FoodLabeling.Application.Contracts.Dtos.Common;
using FoodLabeling.Application.Contracts.Dtos.Label;
using FoodLabeling.Application.Contracts.Dtos.UsAppLabeling;
using FoodLabeling.Application.Contracts.IServices;
+using FoodLabeling.Application.Helpers;
using FoodLabeling.Application.Services.DbModels;
+using FoodLabeling.Domain.Entities;
using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.Guids;
using Volo.Abp.Uow;
+using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace FoodLabeling.Application.Services;
@@ -26,12 +31,18 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
private readonly ISqlSugarDbContext _dbContext;
private readonly ILabelAppService _labelAppService;
private readonly IGuidGenerator _guidGenerator;
+ private readonly ISqlSugarRepository _userRepository;
- public UsAppLabelingAppService(ISqlSugarDbContext dbContext, ILabelAppService labelAppService, IGuidGenerator guidGenerator)
+ public UsAppLabelingAppService(
+ ISqlSugarDbContext dbContext,
+ ILabelAppService labelAppService,
+ IGuidGenerator guidGenerator,
+ ISqlSugarRepository userRepository)
{
_dbContext = dbContext;
_labelAppService = labelAppService;
_guidGenerator = guidGenerator;
+ _userRepository = userRepository;
}
///
@@ -509,6 +520,297 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
};
}
+ ///
+ /// App 重新打印:根据历史任务Id重打(创建新任务与明细)
+ ///
+ [Authorize]
+ [UnitOfWork]
+ public virtual async Task ReprintAsync(UsAppLabelReprintInputVo input)
+ {
+ if (input is null)
+ {
+ throw new UserFriendlyException("入参不能为空");
+ }
+
+ var locationId = input.LocationId?.Trim();
+ if (string.IsNullOrWhiteSpace(locationId))
+ {
+ throw new UserFriendlyException("门店Id不能为空");
+ }
+
+ var taskId = input.TaskId?.Trim();
+ if (string.IsNullOrWhiteSpace(taskId))
+ {
+ throw new UserFriendlyException("taskId不能为空");
+ }
+
+ var quantity = input.PrintQuantity <= 0 ? 1 : input.PrintQuantity;
+ var clientRequestId = input.ClientRequestId?.Trim();
+ if (!string.IsNullOrWhiteSpace(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
+ };
+ }
+ }
+
+ var currentUserId = CurrentUser?.Id?.ToString();
+ if (string.IsNullOrWhiteSpace(currentUserId))
+ {
+ throw new UserFriendlyException("未登录");
+ }
+
+ var old = await _dbContext.SqlSugarClient.Queryable()
+ .FirstAsync(x => x.Id == taskId);
+ if (old is null)
+ {
+ throw new UserFriendlyException("打印任务不存在");
+ }
+
+ // 仅允许重打自己在当前门店的任务
+ if (!string.Equals(old.CreatedBy?.Trim(), currentUserId, StringComparison.OrdinalIgnoreCase))
+ {
+ throw new UserFriendlyException("无权限重打该任务");
+ }
+ if (!string.Equals(old.LocationId?.Trim(), locationId, StringComparison.OrdinalIgnoreCase))
+ {
+ throw new UserFriendlyException("该任务不属于当前门店");
+ }
+
+ LabelTemplatePreviewDto? resolvedTemplate = null;
+ try
+ {
+ resolvedTemplate = JsonSerializer.Deserialize(old.RenderTemplateJson);
+ }
+ catch
+ {
+ resolvedTemplate = null;
+ }
+
+ if (resolvedTemplate is null)
+ {
+ throw new UserFriendlyException("历史任务渲染快照解析失败,无法重打");
+ }
+
+ var now = DateTime.Now;
+ var batchId = _guidGenerator.Create().ToString();
+ var taskIds = new List();
+
+ for (var i = 1; i <= quantity; i++)
+ {
+ var newTaskId = _guidGenerator.Create().ToString();
+ taskIds.Add(newTaskId);
+
+ var newTask = new FlLabelPrintTaskDbEntity
+ {
+ Id = newTaskId,
+ BatchId = batchId,
+ CopyIndex = i,
+ ClientRequestId = string.IsNullOrWhiteSpace(clientRequestId) ? null : clientRequestId,
+ LabelId = old.LabelId,
+ TemplateId = old.TemplateId,
+ LabelTypeId = old.LabelTypeId,
+ ProductId = old.ProductId,
+ LocationId = old.LocationId,
+ BaseTime = old.BaseTime,
+ PrintInputJson = old.PrintInputJson,
+ TemplateProductDefaultValuesJson = old.TemplateProductDefaultValuesJson,
+ RenderTemplateJson = old.RenderTemplateJson,
+ PrinterId = string.IsNullOrWhiteSpace(input.PrinterId) ? old.PrinterId : input.PrinterId.Trim(),
+ PrinterMac = string.IsNullOrWhiteSpace(input.PrinterMac) ? old.PrinterMac : input.PrinterMac.Trim(),
+ PrinterAddress = string.IsNullOrWhiteSpace(input.PrinterAddress) ? old.PrinterAddress : input.PrinterAddress.Trim(),
+ Status = "CREATED",
+ PrintedAt = null,
+ ErrorMessage = null,
+ CreatedBy = currentUserId,
+ CreationTime = now
+ };
+
+ await _dbContext.SqlSugarClient.Insertable(newTask).ExecuteCommandAsync();
+
+ 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();
+ }
+
+ return new FlLabelPrintDataDbEntity
+ {
+ Id = _guidGenerator.Create().ToString(),
+ PrintTaskId = newTaskId,
+ ElementId = e.Id?.Trim() ?? string.Empty,
+ ElementName = e.ElementName?.Trim(),
+ RenderValue = renderValue,
+ RenderConfigJson = cfgJson
+ };
+ }).Where(x => !string.IsNullOrWhiteSpace(x.ElementId)).ToList();
+
+ if (rows.Count > 0)
+ {
+ await _dbContext.SqlSugarClient.Insertable(rows).ExecuteCommandAsync();
+ }
+ }
+
+ return new UsAppLabelPrintOutputDto
+ {
+ TaskId = taskIds.FirstOrDefault() ?? string.Empty,
+ PrintQuantity = quantity,
+ BatchId = batchId,
+ TaskIds = taskIds
+ };
+ }
+
+ ///
+ /// App 打印日志:获取当前登录账号在当前门店打印的记录(分页,时间倒序)
+ ///
+ ///
+ /// 仅返回满足:
+ /// - CreatedBy == CurrentUser.Id
+ /// - LocationId == input.LocationId
+ /// 的打印任务记录(fl_label_print_task)。
+ ///
+ /// 示例请求:
+ /// ```json
+ /// {
+ /// "locationId": "11111111-1111-1111-1111-111111111111",
+ /// "skipCount": 1,
+ /// "maxResultCount": 20
+ /// }
+ /// ```
+ ///
+ /// 参数说明:
+ /// - locationId: 当前门店 Id(必填)
+ /// - skipCount: 页码(从 1 开始,遵循本项目约定)
+ /// - maxResultCount: 每页条数
+ ///
+ /// 分页查询入参
+ /// 分页打印日志
+ /// 成功
+ /// 参数错误/未登录
+ /// 服务器错误
+ [Authorize]
+ [HttpPost]
+ public virtual async Task> GetPrintLogListAsync(PrintLogGetListInputVo input)
+ {
+ if (input is null)
+ {
+ throw new UserFriendlyException("入参不能为空");
+ }
+
+ if (!CurrentUser.Id.HasValue)
+ {
+ throw new UserFriendlyException("用户未登录");
+ }
+
+ var locationId = input.LocationId?.Trim();
+ if (string.IsNullOrWhiteSpace(locationId))
+ {
+ throw new UserFriendlyException("门店Id不能为空");
+ }
+
+ var currentUserIdStr = CurrentUser.Id.Value.ToString();
+
+ var currentUser = await _userRepository.GetByIdAsync(CurrentUser.Id.Value);
+ var operatorName = currentUser?.Name?.Trim() ?? string.Empty;
+
+ var locationName = "无";
+ if (Guid.TryParse(locationId, out var locationGuid))
+ {
+ var loc = await _dbContext.SqlSugarClient.Queryable()
+ .Where(x => !x.IsDeleted && x.Id == locationGuid)
+ .Select(x => new { x.LocationCode, x.LocationName })
+ .FirstAsync();
+ if (loc is not null)
+ {
+ var name = loc.LocationName?.Trim();
+ if (!string.IsNullOrWhiteSpace(name))
+ {
+ locationName = name;
+ }
+ }
+ }
+
+ RefAsync total = 0;
+
+ var query = _dbContext.SqlSugarClient
+ .Queryable()
+ .LeftJoin((t, l) => t.LabelId == l.Id)
+ .LeftJoin((t, l, p) => t.ProductId == p.Id)
+ .LeftJoin((t, l, p, lt) => t.LabelTypeId == lt.Id)
+ .LeftJoin((t, l, p, lt, tpl) => t.TemplateId == tpl.Id)
+ .Where((t, l, p, lt, tpl) => t.CreatedBy == currentUserIdStr && t.LocationId == locationId)
+ .OrderBy((t, l, p, lt, tpl) => SqlFunc.IsNull(t.PrintedAt, t.CreationTime), OrderByType.Desc)
+ .OrderBy((t, l, p, lt, tpl) => t.CreationTime, OrderByType.Desc)
+ .Select((t, l, p, lt, tpl) => new
+ {
+ t.Id,
+ t.BatchId,
+ t.CopyIndex,
+ t.LabelId,
+ LabelCode = l.LabelCode,
+ t.ProductId,
+ ProductName = p.ProductName,
+ TypeName = lt.TypeName,
+ TemplateWidth = tpl.Width,
+ TemplateHeight = tpl.Height,
+ TemplateUnit = tpl.Unit,
+ t.PrintedAt,
+ t.CreationTime
+ });
+
+ var pageRows = await query.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
+
+ var items = pageRows.Select(x => new PrintLogItemDto
+ {
+ TaskId = x.Id,
+ BatchId = x.BatchId,
+ CopyIndex = x.CopyIndex,
+ LabelId = x.LabelId,
+ LabelCode = x.LabelCode ?? string.Empty,
+ ProductId = x.ProductId,
+ ProductName = string.IsNullOrWhiteSpace(x.ProductName) ? "无" : x.ProductName.Trim(),
+ TypeName = x.TypeName ?? string.Empty,
+ LabelSizeText = FormatLabelSizeWithUnit(x.TemplateWidth, x.TemplateHeight, x.TemplateUnit),
+ PrintedAt = x.PrintedAt ?? x.CreationTime,
+ OperatorName = operatorName,
+ LocationName = locationName
+ }).ToList();
+
+ var pageSize = input.MaxResultCount <= 0 ? items.Count : input.MaxResultCount;
+ var pageIndex = pageSize <= 0 ? 1 : PagedQueryConvention.PageIndexFromSkipCount(input.SkipCount);
+ var totalCount = (long)total;
+ var totalPages = pageSize <= 0 ? 0 : (int)Math.Ceiling(totalCount / (double)pageSize);
+
+ return new PagedResultWithPageDto
+ {
+ PageIndex = pageIndex,
+ PageSize = pageSize,
+ TotalCount = totalCount,
+ TotalPages = totalPages,
+ Items = items
+ };
+ }
+
private async Task ResolveTemplateProductDefaultValuesJsonAsync(
string templateId,
string? productId,
@@ -657,4 +959,13 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
? $"{ws}\"x{hs}\""
: $"{ws}x{hs}{u}";
}
+
+ private static string? FormatLabelSizeWithUnit(decimal w, decimal h, string unit)
+ {
+ var u = (unit ?? "inch").Trim().ToLowerInvariant();
+ var ws = w.ToString(CultureInfo.InvariantCulture);
+ var hs = h.ToString(CultureInfo.InvariantCulture);
+ var normalizedUnit = u is "in" ? "inch" : u;
+ return $"{ws}x{hs}{normalizedUnit}";
+ }
}
diff --git a/项目相关文档/标签模块接口对接说明.md b/项目相关文档/标签模块接口对接说明.md
index 12501f2..b3000d6 100644
--- a/项目相关文档/标签模块接口对接说明.md
+++ b/项目相关文档/标签模块接口对接说明.md
@@ -964,3 +964,113 @@ curl -X POST "http://localhost:19001/api/app/us-app-labeling/print" \
-d '{"locationId":"11111111-1111-1111-1111-111111111111","labelCode":"LBL_CHICKEN_DEFROST","productId":"22222222-2222-2222-2222-222222222222","printQuantity":2,"baseTime":"2026-03-26T10:30:00","printInputJson":{"price":"12.99"}}'
```
+---
+
+## 接口 10:App 打印日志(当前登录账号 + 当前门店)
+
+**场景**:移动端“打印记录/历史”页面。只展示**当前登录账号**在**当前门店**打印的记录,便于追溯/重打。
+
+### 10.1 分页获取打印日志
+
+#### HTTP
+
+- **方法**:`POST`(与本模块其它复杂入参接口一致;若与 Swagger 不一致,**以 Swagger 为准**)
+- **路径**:`/api/app/us-app-labeling/get-print-log-list`
+- **鉴权**:需要登录(`Authorization: Bearer ...`)
+
+#### 入参(Body:`PrintLogGetListInputVo`)
+
+> 本项目分页约定:`skipCount` 表示 **页码(从 1 开始)**,不是 0 基 offset。
+
+| 参数名(JSON) | 类型 | 必填 | 说明 |
+|---|---|---|---|
+| `locationId` | string | 是 | 当前门店Id(仅返回该门店记录) |
+| `skipCount` | number | 否 | 页码,从 1 开始;默认 1 |
+| `maxResultCount` | number | 否 | 每页条数;默认按后端/ABP 默认 |
+
+#### 过滤条件(后端固定逻辑)
+
+- `fl_label_print_task.CreatedBy == CurrentUser.Id`
+- `fl_label_print_task.LocationId == locationId`
+- 按时间倒序:`PrintedAt ?? CreationTime`(越新的越靠前)
+
+#### 出参(`PagedResultWithPageDto`)
+
+| 字段 | 类型 | 说明 |
+|---|---|---|
+| `pageIndex` | number | 当前页码(从 1 开始) |
+| `pageSize` | number | 每页条数 |
+| `totalCount` | number | 总条数 |
+| `totalPages` | number | 总页数 |
+| `items` | PrintLogItemDto[] | 列表 |
+
+`PrintLogItemDto`:
+
+| 字段 | 类型 | 说明 |
+|---|---|---|
+| `taskId` | string | 任务Id(fl_label_print_task.Id) |
+| `batchId` | string | 批次Id(同一次点击 Print 共享) |
+| `copyIndex` | number | 第几份(从 1 开始) |
+| `labelId` | string | 标签Id |
+| `labelCode` | string | 标签编码(来自 fl_label.LabelCode) |
+| `productId` | string | 产品Id |
+| `productName` | string | 产品名(来自 fl_product.ProductName;无则 “无”) |
+| `typeName` | string | 标签类型名称(来自 fl_label_type.TypeName) |
+| `labelSizeText` | string | 模板尺寸(宽高+单位,如 `2.00x2.00inch` / `6.00x4.00cm`) |
+| `printedAt` | string | 打印时间(PrintedAt ?? CreationTime) |
+| `operatorName` | string | 操作人姓名(当前登录账号 Name) |
+| `locationName` | string | 门店名称 |
+
+#### curl
+
+```bash
+curl -X POST "http://localhost:19001/api/app/us-app-labeling/get-print-log-list" \
+ -H "Authorization: " \
+ -H "Content-Type: application/json" \
+ -d '{"locationId":"11111111-1111-1111-1111-111111111111","skipCount":1,"maxResultCount":20}'
+```
+
+---
+
+## 接口 11:App 重新打印(根据任务Id重打)
+
+**场景**:移动端“打印记录/历史”页面点击 **Reprint**。后端根据历史任务 `taskId` 创建一批新的打印任务与明细。
+
+### 11.1 重打
+
+#### HTTP
+
+- **方法**:`POST`
+- **路径**:`/api/app/us-app-labeling/reprint`(若与 Swagger 不一致,**以 Swagger 为准**)
+- **鉴权**:需要登录(`Authorization: Bearer ...`)
+
+#### 入参(Body:`UsAppLabelReprintInputVo`)
+
+| 参数名(JSON) | 类型 | 必填 | 说明 |
+|---|---|---|---|
+| `locationId` | string | 是 | 当前门店Id(后端校验历史任务必须属于该门店) |
+| `taskId` | string | 是 | 历史打印任务Id(`fl_label_print_task.Id`) |
+| `printQuantity` | number | 否 | 重新打印份数;`<=0` 按 1 处理;默认 1 |
+| `clientRequestId` | string | 否 | 客户端幂等请求Id;同一个值重复调用会直接返回首次创建的 `batchId/taskIds`,避免重复写库 |
+| `printerId` | string | 否 | 可选,覆盖历史任务的打印机Id |
+| `printerMac` | string | 否 | 可选,覆盖历史任务的打印机MAC |
+| `printerAddress` | string | 否 | 可选,覆盖历史任务的打印机地址 |
+
+#### 权限校验(后端固定逻辑)
+
+- 历史任务必须满足:`CreatedBy == CurrentUser.Id`
+- 且 `LocationId == locationId`
+
+#### 出参(`UsAppLabelPrintOutputDto`)
+
+字段与接口 9 一致:`taskId / printQuantity / batchId / taskIds`
+
+#### curl
+
+```bash
+curl -X POST "http://localhost:19001/api/app/us-app-labeling/reprint" \
+ -H "Authorization: " \
+ -H "Content-Type: application/json" \
+ -d '{"locationId":"11111111-1111-1111-1111-111111111111","taskId":"3a205389-78dd-4750-51ab-720344c9f607","printQuantity":1}'
+```
+