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 } /// - /// 检查告警计时器是否已过期 + /// 查询已打印标签告警的过期/倒计时状态(不拦截打印) /// /// /// 至少提供 timerIdbatchIdprintTaskId 之一。 - /// 无匹配记录时 found=falseisExpired=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`,避免本地倒计时漂移。