diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerCheckExpiredOutputDto.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerCheckExpiredOutputDto.cs
index 7496e71..5fff58b 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerCheckExpiredOutputDto.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerCheckExpiredOutputDto.cs
@@ -8,7 +8,7 @@ public class LabelAlertTimerCheckExpiredOutputDto
/// 是否找到计时器记录
public bool Found { get; set; }
- /// 是否已过期;无记录时为 false(允许继续打印)
+ /// 是否已过期;无记录时为 false(仅展示用,不用于拦截打印)
public bool IsExpired { get; set; }
public DateTime? ExpiresAt { get; set; }
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerGetListInputVo.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerGetListInputVo.cs
index c874a62..b216881 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerGetListInputVo.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelAlertTimer/LabelAlertTimerGetListInputVo.cs
@@ -8,7 +8,8 @@ namespace FoodLabeling.Application.Contracts.Dtos.LabelAlertTimer;
public class LabelAlertTimerGetListInputVo : PagedAndSortedResultRequestDto
{
///
- /// 当前门店 Id(location.Id,Guid 字符串,必填)
+ /// 当前门店 Id(location.Id,Guid 字符串)。
+ /// list 必填;app-list 可空(空则取已选门店缓存)。
///
public string? LocationId { get; set; }
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelAlertTimerAppService.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelAlertTimerAppService.cs
index 128e58c..31d3243 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelAlertTimerAppService.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILabelAlertTimerAppService.cs
@@ -11,6 +11,11 @@ public interface ILabelAlertTimerAppService : IApplicationService
{
Task> GetListAsync(LabelAlertTimerGetListInputVo input);
+ ///
+ /// App:当前账号当前门店告警列表(含倒计时;locationId 可省略,走已选门店缓存)
+ ///
+ Task> GetAppListAsync(LabelAlertTimerGetListInputVo input);
+
Task DeleteAsync(string id);
Task CheckExpiredAsync(LabelAlertTimerCheckExpiredInputVo input);
diff --git a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelAlertTimerAppService.cs b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelAlertTimerAppService.cs
index e72e38d..9051c2f 100644
--- a/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelAlertTimerAppService.cs
+++ b/泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelAlertTimerAppService.cs
@@ -5,6 +5,7 @@ using FoodLabeling.Application.Helpers;
using FoodLabeling.Application.Services.DbModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Caching.Distributed;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Application.Services;
@@ -13,7 +14,7 @@ using Yi.Framework.SqlSugarCore.Abstractions;
namespace FoodLabeling.Application.Services;
///
-/// 标签告警计时器(App):按打印批次展示过期倒计时,过期时刻与 Print Log Expiration 同源。
+/// 标签告警计时器(App):跟踪已打印标签的过期倒计时;与可否打印无关。
///
public class LabelAlertTimerAppService : ApplicationService, ILabelAlertTimerAppService
{
@@ -21,16 +22,19 @@ public class LabelAlertTimerAppService : ApplicationService, ILabelAlertTimerApp
private const string StatusExpired = "expired";
private readonly ISqlSugarDbContext _dbContext;
+ private readonly IDistributedCache _distributedCache;
- public LabelAlertTimerAppService(ISqlSugarDbContext dbContext)
+ public LabelAlertTimerAppService(ISqlSugarDbContext dbContext, IDistributedCache distributedCache)
{
_dbContext = dbContext;
+ _distributedCache = distributedCache;
}
///
- /// 分页查询当前门店告警计时器列表
+ /// 分页查询当前门店告警计时器列表(含倒计时)
///
///
+ /// 仅展示已打印标签的过期倒计时,不用于判断能否打印。
/// 过期时刻与 Print Log「Expiration」列同源(ReportsPrintLogExpiryHelper)。
/// 同一打印批次(BatchId)无论打印多少张标签,仅一条计时器(取 CopyIndex 最小任务)。
///
@@ -51,7 +55,7 @@ public class LabelAlertTimerAppService : ApplicationService, ILabelAlertTimerApp
/// - dateDay: 可选,按 PrintedAt 自然日筛选(yyyy-MM-dd)
///
/// 分页查询入参
- /// 分页计时器列表
+ /// 分页计时器列表(含 remainingTime 倒计时秒数)
/// 成功返回分页列表
/// 参数错误或未登录/无门店权限
/// 服务器错误
@@ -76,43 +80,73 @@ public class LabelAlertTimerAppService : ApplicationService, ILabelAlertTimerApp
throw new UserFriendlyException("门店Id不能为空");
}
- await UsAppPrintLogScopeHelper.EnsureUserCanAccessLocationAsync(
- CurrentUser, _dbContext.SqlSugarClient, locationId);
-
- var db = _dbContext.SqlSugarClient;
- RefAsync total = 0;
- var query = db.Queryable()
- .Where(x => !x.IsDeleted && x.LocationId == locationId);
+ return await QueryListByLocationAsync(locationId, input);
+ }
- var (dayStart, dayEndExcl) = ResolveDateDayFilter(input.DateDay);
- if (dayStart.HasValue && dayEndExcl.HasValue)
+ ///
+ /// App:当前账号当前门店告警列表(含倒计时)
+ ///
+ ///
+ /// 供 App 警告页使用:按当前登录账号可访问的门店查询已打印标签的告警倒计时。
+ /// locationId 可省略,省略时使用管理员已选门店缓存(select-admin-scope-location);
+ /// 仍无门店时返回友好错误。过期状态仅用于展示,与可否打印无关。
+ ///
+ /// 示例请求:
+ /// ```json
+ /// {
+ /// "locationId": "11111111-1111-1111-1111-111111111111",
+ /// "skipCount": 1,
+ /// "maxResultCount": 50
+ /// }
+ /// ```
+ ///
+ /// 参数说明:
+ /// - locationId: 当前门店 Id(可选;空则取已选门店缓存)
+ /// - skipCount: 页码(从 1 开始)
+ /// - maxResultCount: 每页条数
+ /// - dateDay: 可选,按 PrintedAt 自然日筛选(yyyy-MM-dd)
+ ///
+ /// 出参倒计时字段:
+ /// - remainingTime: 剩余秒数,App 可直接做倒计时
+ /// - totalTime: 总时长(秒)
+ /// - status: running / expired
+ /// - expiresAt: 过期时刻
+ ///
+ /// 分页查询入参
+ /// 分页告警列表(含倒计时)
+ /// 成功返回分页列表
+ /// 未登录、无门店或无权限
+ /// 服务器错误
+ [Authorize]
+ [HttpPost("label-alert-timer/app-list")]
+ public virtual async Task> GetAppListAsync(
+ LabelAlertTimerGetListInputVo input)
+ {
+ if (input is null)
{
- var start = dayStart.Value;
- var endExcl = dayEndExcl.Value;
- query = query.Where(x => x.PrintedAt >= start && x.PrintedAt < endExcl);
+ throw new UserFriendlyException("入参不能为空");
}
- var pageRows = await query
- .OrderBy(x => x.ExpiresAt, OrderByType.Desc)
- .OrderBy(x => x.PrintedAt, OrderByType.Desc)
- .ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
-
- var now = DateTime.Now;
- var items = pageRows.Select(x => MapListItem(x, now)).ToList();
+ if (!CurrentUser.Id.HasValue)
+ {
+ throw new UserFriendlyException("用户未登录");
+ }
- 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);
+ var locationId = input.LocationId?.Trim();
+ if (string.IsNullOrWhiteSpace(locationId))
+ {
+ var cache = await UsAppAuthScopeHelper.GetAdminScopeCacheAsync(
+ _distributedCache,
+ CurrentUser.Id.Value);
+ locationId = cache?.Location?.Id?.Trim();
+ }
- return new PagedResultWithPageDto
+ if (string.IsNullOrWhiteSpace(locationId))
{
- PageIndex = pageIndex,
- PageSize = pageSize,
- TotalCount = totalCount,
- TotalPages = totalPages,
- Items = items
- };
+ throw new UserFriendlyException("请先选择门店或传入 locationId");
+ }
+
+ return await QueryListByLocationAsync(locationId, input);
}
///
@@ -163,11 +197,12 @@ public class LabelAlertTimerAppService : ApplicationService, ILabelAlertTimerApp
}
///
- /// 检查告警计时器是否已过期
+ /// 查询已打印标签告警的过期/倒计时状态(不拦截打印)
///
///
/// 至少提供 timerId、batchId、printTaskId 之一。
- /// 无匹配记录时 found=false、isExpired=false(允许继续打印)。
+ /// 本接口仅返回已打印批次的过期状态与剩余秒数,供展示倒计时;
+ /// 绝不用于判断「能不能打印」——打印流程不得依赖本接口结果做拦截。
///
/// 示例请求:
/// ```json
@@ -182,7 +217,7 @@ public class LabelAlertTimerAppService : ApplicationService, ILabelAlertTimerApp
/// - printTaskId: 打印任务 Id(同批次任意任务均可)
///
/// 查询入参
- /// 过期检查结果
+ /// 过期/倒计时状态(展示用)
/// 成功(含未找到记录的情况)
/// 未提供任何标识
/// 服务器错误
@@ -278,6 +313,49 @@ public class LabelAlertTimerAppService : ApplicationService, ILabelAlertTimerApp
};
}
+ private async Task> QueryListByLocationAsync(
+ string locationId,
+ LabelAlertTimerGetListInputVo input)
+ {
+ await UsAppPrintLogScopeHelper.EnsureUserCanAccessLocationAsync(
+ CurrentUser, _dbContext.SqlSugarClient, locationId);
+
+ var db = _dbContext.SqlSugarClient;
+ RefAsync total = 0;
+ var query = db.Queryable()
+ .Where(x => !x.IsDeleted && x.LocationId == locationId);
+
+ var (dayStart, dayEndExcl) = ResolveDateDayFilter(input.DateDay);
+ if (dayStart.HasValue && dayEndExcl.HasValue)
+ {
+ var start = dayStart.Value;
+ var endExcl = dayEndExcl.Value;
+ query = query.Where(x => x.PrintedAt >= start && x.PrintedAt < endExcl);
+ }
+
+ var pageRows = await query
+ .OrderBy(x => x.ExpiresAt, OrderByType.Desc)
+ .OrderBy(x => x.PrintedAt, OrderByType.Desc)
+ .ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
+
+ var now = DateTime.Now;
+ var items = pageRows.Select(x => MapListItem(x, now)).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 static LabelAlertTimerListItemDto MapListItem(FlLabelAlertTimerDbEntity row, DateTime now)
{
var remaining = Math.Max(0, (int)(row.ExpiresAt - now).TotalSeconds);
diff --git a/项目相关文档/2026-08-07告警接口文档.md b/项目相关文档/2026-08-07告警接口文档.md
index 8b7ff9d..1df3283 100644
--- a/项目相关文档/2026-08-07告警接口文档.md
+++ b/项目相关文档/2026-08-07告警接口文档.md
@@ -1,7 +1,8 @@
# 告警计时器接口文档(泰额版)
> 模块:标签告警计时器(Label Alert Timer)
-> 范围:泰额版后端(租户业务库,非 `antis-foodlabeling-host`)
+> 服务:`LabelAlertTimerAppService`
+> 范围:泰额版后端,数据在**租户业务库**(非 `antis-foodlabeling-host`)
> 认证:`Authorization: Bearer {token}`;业务请求建议带 `__tenant: {tenantId}`
> JSON:camelCase
> Base URL 示例:`http://127.0.0.1:19002`(以实际部署为准)
@@ -13,34 +14,60 @@
| 概念 | 说明 |
|------|------|
-| 过期时刻 | 与 Print Log「Expiration」列**同源**,由 `ReportsPrintLogExpiryHelper.TryResolveExpiryDateTime` 解析 |
-| 批次维度 | 一次打印(同一 `BatchId`)无论打印多少张,**仅一条**计时器(取 `CopyIndex` 最小的 `fl_label_print_task`) |
+| 用途 | 跟踪**已经打印**的标签过期时间,供 App / 前端做**倒计时与警告列表** |
+| 与打印关系 | **过期与「能不能打印」无关**;打印接口**不会**因计时器已过期而拒绝打印 |
+| 过期时刻 | 与 Print Log「Expiration」列**同源**(`ReportsPrintLogExpiryHelper.TryResolveExpiryDateTime`) |
+| 批次维度 | 同一 `BatchId` 无论打印多少张,**仅一条**计时器(取 `CopyIndex` 最小的 `fl_label_print_task`) |
| 无过期不上列表 | 模板无法解析过期时刻时,不写入计时器、不出现在列表 |
+| 列表范围 | **门店级**:该 `locationId` 下未软删的全部计时器(不限当前用户自己打印) |
| 状态 | `running`(未过期)/ `expired`(已过期) |
-| 软删 | 用户可删除计时器;删除后不再出现在列表 |
+| 软删 | 用户可删除计时器;删除后不再出现在列表,不影响历史打印任务 |
### 写入时机
-- `UsAppLabelingAppService.PrintAsync` / `ReprintAsync` 成功创建批次后自动写入
-- 幂等重试(相同 `clientRequestId`)返回前也会补写一次(Helper 内部按 `BatchId` 去重)
+- `UsAppLabelingAppService.PrintAsync` / `ReprintAsync` 成功创建批次后自动写入(`LabelAlertTimerWriteHelper.TryCreateFromPrintBatchAsync`)
+- 幂等重试(相同 `clientRequestId`)返回前也会补写一次(按 `BatchId` 唯一索引去重;含软删记录也不再插入)
### 建表
-- 脚本:`module/food-labeling-us/scripts/fl_label_alert_timer.sql`
-- 表名:`fl_label_alert_timer`
-- 新租户开通时嵌入资源自动执行;**已有租户需用具备 CREATE 权限的账号**在业务库手动执行脚本(业务账号 `netteam` 通常无建表权限,可用运维高权限账号执行)
+| 项 | 说明 |
+|----|------|
+| 脚本 | `泰额版/.../module/food-labeling-us/scripts/fl_label_alert_timer.sql` |
+| 表名 | `fl_label_alert_timer` |
+| 新租户 | 开通时嵌入资源自动执行 |
+| 已有租户 | 需用具备 CREATE 权限的账号在业务库手动执行(业务账号 `netteam` 通常无建表权限) |
+
+主要字段:`BatchId`(唯一)、`PrintTaskId`、`LabelId`、`LocationId`、`PrintedAt`、`ExpiresAt`、`DurationSeconds`、`Title`、`Subtitle`、`IsDeleted`。
---
## 2. 接口一览
-| 功能 | 方法 | 路由 |
-|------|------|------|
-| 分页列表 | POST | `/api/app/label-alert-timer/list` |
-| 软删除 | DELETE | `/api/app/label-alert-timer/{id}` |
-| 检查是否过期 | POST | `/api/app/label-alert-timer/check-expired` |
+| 功能 | 方法 | 路由 | 说明 |
+|------|------|------|------|
+| 分页列表 | POST | `/api/app/label-alert-timer/list` | `locationId` **必填** |
+| App 警告列表 | POST | `/api/app/label-alert-timer/app-list` | **推荐 App 使用**;`locationId` 可空(走已选门店缓存) |
+| 软删除 | DELETE | `/api/app/label-alert-timer/{id}` | 软删计时器 |
+| 查询过期/倒计时 | POST | `/api/app/label-alert-timer/check-expired` | 单条状态查询;**仅展示,不拦打印** |
+
+> 全部需登录。列表 / 删除 / 查询均校验当前账号可访问对应门店。
+
+### 获取 Token(App)
+
+```http
+POST /api/app/th-app-auth/login
+Content-Type: application/json
+
+{ "email": "mai@123.com", "password": "123456", "tenantId": "可选-租户Id" }
+```
+
+后续请求头:
-> 所有接口需登录;列表与删除需已绑定 `locationId` 对应门店。
+```http
+Authorization: Bearer {token}
+__tenant: {tenantId}
+Content-Type: application/json
+```
---
@@ -59,14 +86,14 @@
}
```
-| 字段 | 必填 | 说明 |
-|------|------|------|
-| locationId | 是 | 当前门店 Id |
-| skipCount | 是 | 页码(从 1 开始) |
-| maxResultCount | 是 | 每页条数 |
-| dateDay | 否 | `yyyy-MM-dd`,按 PrintedAt 自然日筛选 |
+| 字段 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| locationId | string | 是 | 当前门店 Id;空则报错「门店Id不能为空」 |
+| skipCount | number | 是 | **页码**(从 1 开始,与项目分页约定一致) |
+| maxResultCount | number | 是 | 每页条数 |
+| dateDay | string | 否 | `yyyy-MM-dd`,按 `PrintedAt` 自然日筛选 |
-### 响应示例
+### 响应
```json
{
@@ -95,30 +122,95 @@
}
```
-| 出参字段 | 说明 |
-|----------|------|
-| totalTime | 总时长(秒),等于库中 DurationSeconds |
-| remainingTime | 剩余秒数,`max(0, ExpiresAt - now)` |
-| status | `running` 或 `expired` |
+### 列表项字段(`items[]`)
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| id | string | 计时器主键 |
+| batchId | string | 打印批次 Id |
+| printTaskId | string | 代表任务 Id(CopyIndex 最小) |
+| labelId | string | 标签 Id |
+| labelCode | string \| null | 标签编码 |
+| title | string | 标题(含时长文案) |
+| subtitle | string | 副标题(含完成时刻文案) |
+| totalTime | number | 总时长(秒),同库字段 `DurationSeconds` |
+| remainingTime | number | **剩余秒数**,`max(0, ExpiresAt - now)`;**App 倒计时用此字段** |
+| status | string | `running` / `expired` |
+| expiresAt | string | 过期时刻 |
+| printedAt | string | 打印时刻 |
+| locationId | string | 门店 Id |
+| productName | string \| null | 产品名称 |
+
+排序:先 `ExpiresAt` 降序,再 `PrintedAt` 降序。
+
+---
+
+## 4. App 警告列表(推荐)
+
+**POST** `/api/app/label-alert-timer/app-list`
+
+当前登录账号可访问的门店下警告列表;出参结构与第 3 节 `list` **完全相同**(含 `remainingTime` 倒计时)。
+
+### 请求
+
+```json
+{
+ "locationId": "3a21220f-db37-3e32-7390-d55f64cd62a8",
+ "skipCount": 1,
+ "maxResultCount": 50
+}
+```
+
+| 字段 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| locationId | string | 否 | 当前门店;**为空**时取 `POST /api/app/us-app-auth/select-admin-scope-location` 写入的已选门店缓存 |
+| skipCount | number | 是 | 页码(从 1 开始) |
+| maxResultCount | number | 是 | 每页条数 |
+| dateDay | string | 否 | `yyyy-MM-dd`,按 `PrintedAt` 筛选 |
+
+### 门店解析规则
+
+1. 入参 `locationId` 有值 → 用入参
+2. 入参为空 → 读管理员已选门店缓存
+3. 仍无门店 → 400:「请先选择门店或传入 locationId」
+4. 有门店但当前账号不可访问 → 权限校验失败(与 `list` 相同)
+
+### 倒计时对接
+
+| 字段 | App 用法 |
+|------|----------|
+| remainingTime | 初始剩余秒数;进入页面后可本地每秒 `-1`,或定时重新拉列表校正 |
+| totalTime | 进度条分母:`progress = (totalTime - remainingTime) / totalTime` |
+| status | `expired` 时 remainingTime 为 0,可高亮/置顶 |
+| expiresAt | 展示绝对过期时间;与 Print Log Expiration 对齐 |
---
-## 4. 软删除
+## 5. 软删除
**DELETE** `/api/app/label-alert-timer/{id}`
-- 校验当前用户可访问该计时器所属门店
-- 设置 `IsDeleted=1`、`DeletionTime=now`
+| 项 | 说明 |
+|----|------|
+| 路径参数 id | 计时器主键 |
+| 权限 | 校验当前用户可访问该计时器所属 `LocationId` |
+| 行为 | `IsDeleted=1`,`DeletionTime=now` |
+| 不存在/已删 | 报错「计时器不存在或已删除」 |
+
+无响应体(成功即可)。
---
-## 5. 检查是否过期
+## 6. 查询过期/倒计时状态
**POST** `/api/app/label-alert-timer/check-expired`
+仅查询**已打印**批次的过期状态与剩余秒数,供单条展示。
+**不得**用于拦截打印;打印流程不要依赖本接口结果做「禁止打印」。
+
### 请求
-至少提供 `timerId`、`batchId`、`printTaskId` 之一:
+至少提供 `timerId`、`batchId`、`printTaskId` 之一(优先级:`timerId` > `batchId` > `printTaskId`):
```json
{
@@ -126,6 +218,14 @@
}
```
+| 字段 | 说明 |
+|------|------|
+| timerId | 计时器 Id |
+| batchId | 打印批次 Id |
+| printTaskId | 打印任务 Id(同批次任意任务均可,会反查 BatchId) |
+
+未提供任一标识 → 「请至少提供 timerId、batchId 或 printTaskId 之一」。
+
### 响应(找到记录)
```json
@@ -152,37 +252,72 @@
}
```
-> **对接建议**:`found=false` 时视为无告警限制,**允许继续打印**;`found=true && isExpired=true` 时可提示用户标签已过期。
+| 字段 | 说明 |
+|------|------|
+| found | 是否找到未删除的计时器 |
+| isExpired | 是否已过期;无记录时为 `false` |
+| remainingSeconds | 剩余秒数(已过期或无记录为 0);与列表的 `remainingTime` 含义相同 |
+| status | `running` / `expired`;无记录时可能为空 |
+| expiresAt / title / subtitle / timerId / batchId | 找到记录时有值 |
---
-## 6. curl 示例
+## 7. 常见错误文案
+
+| 场景 | 文案 |
+|------|------|
+| 入参为空 | 入参不能为空 |
+| 未登录 | 用户未登录 |
+| list 未传门店 | 门店Id不能为空 |
+| app-list 无门店且无缓存 | 请先选择门店或传入 locationId |
+| 删除 Id 为空 | 计时器Id不能为空 |
+| 记录不存在/已删 | 计时器不存在或已删除 |
+| check-expired 无标识 | 请至少提供 timerId、batchId 或 printTaskId 之一 |
+| 无门店权限 | 由门店权限校验抛出(与打印日志门店校验一致) |
+
+---
+
+## 8. curl 示例
```bash
-# 获取 Token 后替换 TOKEN、TENANT、LOCATION_ID
+# 1) 登录拿 Token(按实际环境替换)
+curl -s -X POST "http://127.0.0.1:19002/api/app/th-app-auth/login" \
+ -H "Content-Type: application/json" \
+ -d '{"email":"mai@123.com","password":"123456","tenantId":"TENANT_ID"}'
+
+# 2) App 警告列表(含倒计时)
+curl -s -X POST "http://127.0.0.1:19002/api/app/label-alert-timer/app-list" \
+ -H "Authorization: Bearer TOKEN" \
+ -H "__tenant: TENANT_ID" \
+ -H "Content-Type: application/json" \
+ -d '{"locationId":"LOCATION_ID","skipCount":1,"maxResultCount":50}'
+# 3) 通用分页列表
curl -s -X POST "http://127.0.0.1:19002/api/app/label-alert-timer/list" \
-H "Authorization: Bearer TOKEN" \
- -H "__tenant: TENANT" \
+ -H "__tenant: TENANT_ID" \
-H "Content-Type: application/json" \
-d '{"locationId":"LOCATION_ID","skipCount":1,"maxResultCount":20}'
+# 4) 单条过期/倒计时查询(仅展示)
curl -s -X POST "http://127.0.0.1:19002/api/app/label-alert-timer/check-expired" \
-H "Authorization: Bearer TOKEN" \
- -H "__tenant: TENANT" \
+ -H "__tenant: TENANT_ID" \
-H "Content-Type: application/json" \
-d '{"batchId":"YOUR_BATCH_ID"}'
+# 5) 软删除
curl -s -X DELETE "http://127.0.0.1:19002/api/app/label-alert-timer/TIMER_ID" \
-H "Authorization: Bearer TOKEN" \
- -H "__tenant: TENANT"
+ -H "__tenant: TENANT_ID"
```
---
-## 7. 前端对接建议
+## 9. 前端 / App 对接建议
-1. **列表页**:进入门店后拉取列表,用 `remainingTime` 做倒计时;`status=expired` 可高亮或置顶。
-2. **打印前校验**(可选):重打历史批次时调 `check-expired`,`isExpired=true` 时二次确认。
-3. **与 Print Log 一致**:展示过期时间时请与 Print Log Expiration 列对齐,避免两套算法。
-4. **删除**:用户左滑/长按删除时调 DELETE;删除仅隐藏计时器,不影响历史打印任务。
+1. **警告页**:优先调 `app-list`;用 `remainingTime`(秒)做倒计时;`status=expired` 可高亮。
+2. **不要做打印前过期拦截**:过期与可否打印无关;计时器在打印成功后才写入。
+3. **与 Print Log 一致**:展示过期时间时与 Print Log Expiration 列对齐,避免两套算法。
+4. **删除**:左滑/长按调 DELETE;仅隐藏计时器,不影响历史打印任务。
+5. **校正**:长时间停留页面时,可定时重拉 `app-list`,避免本地倒计时漂移。