From 991685ca48462119c19183537bf213620b3fc1ed Mon Sep 17 00:00:00 2001 From: dzc <2388224167@qq.com> Date: Fri, 4 Sep 2026 16:23:49 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=89=93=E5=8D=B0=E6=9C=BA=E5=99=A8?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../constant/HardPrintFailureStageEnum.java | 6 +- .../api/constant/HardPrintStatusEnum.java | 4 +- .../hard/api/constant/HardTypeEnum.java | 4 +- .../hard/api/dto/HardPrintResultDto.java | 8 +- .../hard/api/dto/HardPrinterBindingDto.java | 10 +- .../hard/api/dto/HardPrinterConfigDto.java | 10 +- .../api/dto/HardPrinterInfoReportDto.java | 4 +- .../hard/api/entity/HardInvoiceEntity.java | 10 +- .../api/entity/HardPrinterBindingEntity.java | 8 +- .../api/entity/HardPrinterConfigEntity.java | 8 +- .../pjcloud/hard/api/vo/HardInvoiceVo.java | 2 +- .../hard/api/vo/HardPrinterBindingVo.java | 12 +-- .../hard/api/vo/manager/HardManagerVo.java | 6 +- .../controller/HardPrinterController.java | 32 +++--- .../controller/hard/HardApiController.java | 28 ++--- .../hard/mapper/HardPrinterBindingMapper.java | 2 +- .../hard/mapper/HardPrinterConfigMapper.java | 2 +- .../hard/service/HardInvoiceService.java | 10 +- .../hard/service/HardPrinterService.java | 26 ++--- .../service/impl/HardInvoiceServiceImpl.java | 59 +++++++--- .../service/impl/HardPrinterServiceImpl.java | 101 ++++++++++++++---- .../impl/manager/HardManagerServiceImpl.java | 25 +++-- .../HardTerminalStatusServiceImpl.java | 7 +- .../impl/order/HardOrderServiceImpl.java | 69 ++++++++---- .../hard/HardApiControllerTest.java | 8 +- .../impl/HardInvoiceServiceImplTest.java | 16 +-- .../impl/HardPrinterServiceImplTest.java | 43 +++++--- .../pjcloud/mqtt/dto/hard/HardPrintMsg.java | 6 +- 28 files changed, 333 insertions(+), 193 deletions(-) diff --git a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/constant/HardPrintFailureStageEnum.java b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/constant/HardPrintFailureStageEnum.java index b59481aeb..065e764e4 100644 --- a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/constant/HardPrintFailureStageEnum.java +++ b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/constant/HardPrintFailureStageEnum.java @@ -7,7 +7,7 @@ import lombok.Getter; import java.util.Arrays; /** - * 打印任务失败阶段,帮助管理端区分配置问题、服务端故障和终端故障。 + * 打印任务失败阶段,帮助管理端区分配置问题、服务端故障和业务终端代理打印故障。 */ @Getter @AllArgsConstructor @@ -19,10 +19,10 @@ public enum HardPrintFailureStageEnum { /** 服务端读取业务数据、绘制 PNG 或上传文件失败。 */ IMAGE_GENERATION("IMAGE_GENERATION", "图片生成"), - /** 服务端向目标打印终端发布 MQTT 消息失败。 */ + /** 服务端向绑定的来源业务终端发布 MQTT 消息失败。 */ MQTT_DISPATCH("MQTT_DISPATCH", "MQTT下发"), - /** 打印终端下载图片、连接 USB 或写入 ESC/POS 数据失败。 */ + /** 来源业务终端下载图片、连接 USB 或写入 ESC/POS 数据失败。 */ TERMINAL_PRINT("TERMINAL_PRINT", "终端打印"); /** 持久化阶段编码。 */ diff --git a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/constant/HardPrintStatusEnum.java b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/constant/HardPrintStatusEnum.java index 2e67b1aa9..4de0d8815 100644 --- a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/constant/HardPrintStatusEnum.java +++ b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/constant/HardPrintStatusEnum.java @@ -20,10 +20,10 @@ public enum HardPrintStatusEnum { /** 图片已生成并允许下发,历史枚举名保留为 GENERATE 以保持二进制兼容。 */ GENERATE("1", "待打印"), - /** 打印终端已确认完整 USB 数据和切纸命令写入成功。 */ + /** 来源业务终端已确认完整 USB 数据和切纸命令写入成功。 */ PRINT("2", "已打印"), - /** 绑定、图片生成、MQTT 下发或终端打印阶段失败,可重试原任务。 */ + /** 绑定、图片生成、MQTT 下发或业务终端代理打印阶段失败,可重试原任务。 */ FAILED("3", "打印失败"), /** 核销回退等业务动作取消了尚未打印的任务。 */ diff --git a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/constant/HardTypeEnum.java b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/constant/HardTypeEnum.java index f668557f6..d842d8637 100644 --- a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/constant/HardTypeEnum.java +++ b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/constant/HardTypeEnum.java @@ -26,7 +26,7 @@ public enum HardTypeEnum { YUN_XFJ("9", "云版消费机"), YYX_4G("10", "4G-云音响"), YYX_WIFI("11", "wifi-云音响"), - /** 运行打印专用终端模式、通过 USB 驱动尔印 ESC/POS 打印机的设备。 */ + /** 由绑定的超市或食堂 Android 终端代理驱动的无屏尔印 USB 打印机。 */ EY_PRINT("12", "尔印打印机器"), ; private final String type; @@ -48,7 +48,7 @@ public enum HardTypeEnum { } /** - * 判断指定设备类型是否为独立打印终端。 + * 判断指定设备类型是否为尔印逻辑打印机资产。 * * @param hardTypeEnum 待判断的设备类型 * @return 类型为 {@link #EY_PRINT} 时返回 {@code true} diff --git a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/HardPrintResultDto.java b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/HardPrintResultDto.java index d3d42b549..d40276456 100644 --- a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/HardPrintResultDto.java +++ b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/HardPrintResultDto.java @@ -6,10 +6,10 @@ import jakarta.validation.constraints.NotNull; import lombok.Data; /** - * EY_PRINT 终端提交的打印结果回执。 + * 食堂或超市终端代理无屏 EY_PRINT 提交的打印结果回执。 */ @Data -@Schema(description = "打印终端结果回执") +@Schema(description = "业务终端代理打印结果回执") public class HardPrintResultDto { /** hard_invoice 打印任务主键。 */ @@ -22,7 +22,7 @@ public class HardPrintResultDto { @Schema(description = "订单ID") private Long orderId; - /** 终端实际处理的重试版本。 */ + /** 来源业务终端实际处理的重试版本。 */ @NotNull(message = "重试版本不能为空") @Min(value = 0, message = "重试版本不能小于0") @Schema(description = "重试版本") @@ -33,7 +33,7 @@ public class HardPrintResultDto { @Schema(description = "是否成功") private Boolean success; - /** 本版本在终端执行的 USB 尝试次数。 */ + /** 本版本在来源业务终端执行的 USB 尝试次数。 */ @NotNull(message = "尝试次数不能为空") @Min(value = 1, message = "尝试次数不能小于1") @Schema(description = "终端尝试次数") diff --git a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/HardPrinterBindingDto.java b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/HardPrinterBindingDto.java index 756329645..cd1a4cf8e 100644 --- a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/HardPrinterBindingDto.java +++ b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/HardPrinterBindingDto.java @@ -5,10 +5,10 @@ import jakarta.validation.constraints.NotNull; import lombok.Data; /** - * 创建业务终端与尔印打印终端绑定关系的请求。 + * 创建业务终端与无屏尔印 USB 打印机绑定关系的请求。 */ @Data -@Schema(description = "打印终端绑定请求") +@Schema(description = "USB 打印机绑定请求") public class HardPrinterBindingDto { /** SUPERMARKET 或 CANTEEN 来源终端 ID。 */ @@ -16,8 +16,8 @@ public class HardPrinterBindingDto { @Schema(description = "来源业务终端ID") private Long sourceHardId; - /** EY_PRINT 目标终端 ID。 */ - @NotNull(message = "打印终端不能为空") - @Schema(description = "目标打印终端ID") + /** EY_PRINT 逻辑打印机资产 ID。 */ + @NotNull(message = "打印机不能为空") + @Schema(description = "逻辑打印机资产ID") private Long printerHardId; } diff --git a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/HardPrinterConfigDto.java b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/HardPrinterConfigDto.java index 28d56d19c..1e4bff1b3 100644 --- a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/HardPrinterConfigDto.java +++ b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/HardPrinterConfigDto.java @@ -6,15 +6,15 @@ import jakarta.validation.constraints.NotNull; import lombok.Data; /** - * 保存尔印打印终端 USB 与 ESC/POS 参数的请求。 + * 保存无屏尔印 USB 打印机参数的请求。 */ @Data -@Schema(description = "打印终端配置请求") +@Schema(description = "USB 打印机配置请求") public class HardPrinterConfigDto { - /** EY_PRINT 设备 ID。 */ - @NotNull(message = "打印终端不能为空") - @Schema(description = "打印终端ID") + /** EY_PRINT 逻辑打印机资产 ID。 */ + @NotNull(message = "打印机不能为空") + @Schema(description = "逻辑打印机资产ID") private Long printerHardId; /** 真机型号;未联调前允许为空。 */ diff --git a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/HardPrinterInfoReportDto.java b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/HardPrinterInfoReportDto.java index 6c50bdcf9..c7c5312cf 100644 --- a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/HardPrinterInfoReportDto.java +++ b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/dto/HardPrinterInfoReportDto.java @@ -9,10 +9,10 @@ import jakarta.validation.constraints.Size; import lombok.Data; /** - * 尔印打印终端上报实际 USB 打印机信息的请求。 + * 食堂或超市终端代理上报实际 USB 打印机信息的请求。 */ @Data -@Schema(description = "打印终端实际 USB 打印机信息") +@Schema(description = "业务终端代理上报的 USB 打印机信息") public class HardPrinterInfoReportDto { /** USB 描述符中的厂商和产品名称组合,最长保存 128 个字符。 */ diff --git a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/entity/HardInvoiceEntity.java b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/entity/HardInvoiceEntity.java index b94a5b6c3..7b15c8dca 100644 --- a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/entity/HardInvoiceEntity.java +++ b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/entity/HardInvoiceEntity.java @@ -44,9 +44,9 @@ public class HardInvoiceEntity extends Model { private Long hardId; /** - * 实际接收任务的尔印打印终端 ID;历史记录和绑定失败任务允许为空。 + * 任务路由关联的尔印逻辑打印机资产 ID;历史记录和绑定失败任务允许为空。 */ - @Schema(description="目标打印终端ID") + @Schema(description="逻辑打印机资产ID") private Long printerHardId; /** @@ -74,13 +74,13 @@ public class HardInvoiceEntity extends Model { private Integer printNo; /** - * 人工重试版本;终端用任务 ID 和该版本共同判断是否允许再次打印。 + * 人工重试版本;来源业务终端用任务 ID 和该版本共同判断是否允许再次打印。 */ @Schema(description="重试版本") private Integer retryVersion; /** - * 打印终端上报的 USB 累计尝试次数。 + * 来源业务终端代理上报的 USB 累计尝试次数。 */ @Schema(description="终端尝试次数") private Integer attemptCount; @@ -98,7 +98,7 @@ public class HardInvoiceEntity extends Model { private String lastError; /** - * 打印终端确认完整 USB 数据和切纸命令写入成功的时间。 + * 来源业务终端确认完整 USB 数据和切纸命令写入成功的时间。 */ @Schema(description="打印成功时间") private LocalDateTime printedAt; diff --git a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/entity/HardPrinterBindingEntity.java b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/entity/HardPrinterBindingEntity.java index ec4563070..51743f215 100644 --- a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/entity/HardPrinterBindingEntity.java +++ b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/entity/HardPrinterBindingEntity.java @@ -17,7 +17,7 @@ import lombok.NoArgsConstructor; import java.time.LocalDateTime; /** - * 业务终端与尔印打印终端的一对一绑定关系。 + * 业务 Android 终端与无屏尔印 USB 打印机资产的一对一绑定关系。 * *

记录使用物理删除,数据库分别对来源和目标设备设置唯一键,确保任何时刻都不能出现多重绑定。

*/ @@ -28,7 +28,7 @@ import java.time.LocalDateTime; @TenantTable @TableName("hard_printer_binding") @EqualsAndHashCode(callSuper = true) -@Schema(description = "业务终端与打印终端绑定关系") +@Schema(description = "业务终端与 USB 打印机绑定关系") public class HardPrinterBindingEntity extends Model { /** 绑定关系主键。 */ @@ -40,8 +40,8 @@ public class HardPrinterBindingEntity extends Model { @Schema(description = "来源业务终端ID") private Long sourceHardId; - /** 目标打印终端 ID,只允许 EY_PRINT。 */ - @Schema(description = "目标打印终端ID") + /** 逻辑打印机资产 ID,只允许 EY_PRINT;设备本身不登录、不上报心跳。 */ + @Schema(description = "逻辑打印机资产ID") private Long printerHardId; /** 双方共同所属商户 ID 快照。 */ diff --git a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/entity/HardPrinterConfigEntity.java b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/entity/HardPrinterConfigEntity.java index b053e41d1..404df7f7f 100644 --- a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/entity/HardPrinterConfigEntity.java +++ b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/entity/HardPrinterConfigEntity.java @@ -17,7 +17,7 @@ import lombok.NoArgsConstructor; import java.time.LocalDateTime; /** - * 尔印打印终端的 USB 和 ESC/POS 参数配置。 + * 无屏尔印 USB 打印机资产的 USB 和 ESC/POS 参数配置。 */ @Data @Builder @@ -26,7 +26,7 @@ import java.time.LocalDateTime; @TenantTable @TableName("hard_printer_config") @EqualsAndHashCode(callSuper = true) -@Schema(description = "尔印打印终端配置") +@Schema(description = "尔印 USB 打印机配置") public class HardPrinterConfigEntity extends Model { /** 配置主键。 */ @@ -34,8 +34,8 @@ public class HardPrinterConfigEntity extends Model { @Schema(description = "配置主键") private Long id; - /** 配置所属 EY_PRINT 设备 ID。 */ - @Schema(description = "打印终端ID") + /** 配置所属 EY_PRINT 逻辑打印机资产 ID。 */ + @Schema(description = "逻辑打印机资产ID") private Long printerHardId; /** 真机联调后填写的打印机型号。 */ diff --git a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/vo/HardInvoiceVo.java b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/vo/HardInvoiceVo.java index 31f3f961d..1b270fd23 100644 --- a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/vo/HardInvoiceVo.java +++ b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/vo/HardInvoiceVo.java @@ -67,7 +67,7 @@ public class HardInvoiceVo extends HardInvoiceEntity implements BaseUserDeptInte private String hardType; - /** 目标尔印打印终端名称。 */ + /** 任务绑定的尔印 USB 打印机资产名称。 */ private String printerHardName; /** diff --git a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/vo/HardPrinterBindingVo.java b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/vo/HardPrinterBindingVo.java index 196e35ea8..f73b35e60 100644 --- a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/vo/HardPrinterBindingVo.java +++ b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/vo/HardPrinterBindingVo.java @@ -7,11 +7,11 @@ import lombok.Data; import lombok.EqualsAndHashCode; /** - * 打印终端绑定关系及两端设备展示信息。 + * 业务终端与无屏 USB 打印机绑定关系及展示信息。 */ @Data @EqualsAndHashCode(callSuper = true) -@Schema(description = "打印终端绑定详情") +@Schema(description = "USB 打印机绑定详情") public class HardPrinterBindingVo extends HardPrinterBindingEntity { /** 来源业务终端名称。 */ @@ -22,11 +22,11 @@ public class HardPrinterBindingVo extends HardPrinterBindingEntity { @Schema(description = "来源业务终端类型") private String sourceHardType; - /** 目标打印终端名称。 */ - @Schema(description = "目标打印终端名称") + /** 逻辑打印机资产名称。 */ + @Schema(description = "逻辑打印机资产名称") private String printerHardName; - /** 目标打印终端当前配置;尚未保存时返回默认配置。 */ - @Schema(description = "打印终端配置") + /** 目标 USB 打印机当前配置;尚未保存时返回默认配置。 */ + @Schema(description = "USB 打印机配置") private HardPrinterConfigEntity printerConfig; } diff --git a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/vo/manager/HardManagerVo.java b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/vo/manager/HardManagerVo.java index e48a8d269..a884977ec 100644 --- a/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/vo/manager/HardManagerVo.java +++ b/pjcloud-hardware/pjcloud-hardware-api/src/main/java/com/bsd/pjcloud/hard/api/vo/manager/HardManagerVo.java @@ -59,16 +59,16 @@ public class HardManagerVo extends HardManagerEntity implements BaseDeptInterfac */ private String disconnectReason; - /** 当前来源业务终端所绑定的 EY_PRINT 设备 ID;未绑定或当前行为打印机时为空。 */ + /** 当前来源业务终端所绑定的 EY_PRINT 打印机资产 ID;未绑定或当前行为打印机时为空。 */ private Long boundPrinterHardId; /** 当前来源业务终端所绑定的 EY_PRINT 设备名称。 */ private String boundPrinterHardName; - /** 当前 EY_PRINT 反向绑定的来源业务终端 ID;未绑定或当前行为来源设备时为空。 */ + /** 当前 EY_PRINT 打印机资产反向绑定的来源业务终端 ID;未绑定或当前行为来源设备时为空。 */ private Long boundSourceHardId; - /** 当前 EY_PRINT 反向绑定的来源业务终端名称。 */ + /** 当前 EY_PRINT 打印机资产反向绑定的来源业务终端名称。 */ private String boundSourceHardName; /** EY_PRINT 型号;未维护配置或非打印设备时为空。 */ diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/controller/HardPrinterController.java b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/controller/HardPrinterController.java index 6cd078882..82d125488 100644 --- a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/controller/HardPrinterController.java +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/controller/HardPrinterController.java @@ -23,16 +23,16 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** - * 尔印打印终端绑定和配置管理接口。 + * 业务终端与无屏尔印 USB 打印机的绑定和配置管理接口。 */ @RestController @RequiredArgsConstructor @RequestMapping("/hard-printer") -@Tag(description = "hard-printer", name = "尔印打印终端管理") +@Tag(description = "hard-printer", name = "尔印 USB 打印机管理") @SecurityRequirement(name = HttpHeaders.AUTHORIZATION) public class HardPrinterController { - /** 打印终端绑定和配置服务。 */ + /** USB 打印机绑定和配置服务。 */ private final HardPrinterService hardPrinterService; /** @@ -49,13 +49,13 @@ public class HardPrinterController { } /** - * 查询来源设备可绑定的 EY_PRINT 终端。 + * 查询来源设备可绑定的 EY_PRINT 打印机资产。 * * @param sourceHardId 来源业务终端 ID * @return 可绑定设备列表 */ @GetMapping("/available") - @Operation(summary = "查询可绑定打印终端") + @Operation(summary = "查询可绑定打印机") @PreAuthorize("@pms.hasPermission('hard_hardManager_printer_bind')") public R listAvailable(@RequestParam Long sourceHardId) { return R.ok(hardPrinterService.listAvailablePrinters(sourceHardId)); @@ -75,14 +75,14 @@ public class HardPrinterController { } /** - * 创建来源与打印终端的一对一绑定。 + * 创建来源业务终端与 USB 打印机的一对一绑定。 * * @param dto 绑定请求 * @return 绑定详情 */ @PostMapping("/binding") - @Operation(summary = "绑定打印终端") - @SysLog("绑定打印终端") + @Operation(summary = "绑定 USB 打印机") + @SysLog("绑定 USB 打印机") @PreAuthorize("@pms.hasPermission('hard_hardManager_printer_bind')") public R bind(@Valid @RequestBody HardPrinterBindingDto dto) { return R.ok(hardPrinterService.bind(dto)); @@ -95,8 +95,8 @@ public class HardPrinterController { * @return 空成功响应 */ @DeleteMapping("/binding/{sourceHardId}") - @Operation(summary = "解绑打印终端") - @SysLog("解绑打印终端") + @Operation(summary = "解绑 USB 打印机") + @SysLog("解绑 USB 打印机") @PreAuthorize("@pms.hasPermission('hard_hardManager_printer_bind')") public R unbind(@PathVariable Long sourceHardId) { hardPrinterService.unbind(sourceHardId); @@ -104,27 +104,27 @@ public class HardPrinterController { } /** - * 查询打印终端当前参数。 + * 查询 USB 打印机当前参数。 * - * @param printerHardId EY_PRINT 设备 ID + * @param printerHardId EY_PRINT 逻辑打印机资产 ID * @return 当前或默认配置 */ @GetMapping("/config/{printerHardId}") - @Operation(summary = "查询打印终端配置") + @Operation(summary = "查询 USB 打印机配置") @PreAuthorize("@pms.hasPermission('hard_hardManager_view')") public R getConfig(@PathVariable Long printerHardId) { return R.ok(hardPrinterService.getConfig(printerHardId)); } /** - * 保存打印终端参数。 + * 保存 USB 打印机参数。 * * @param dto 配置请求 * @return 保存后的配置 */ @PutMapping("/config") - @Operation(summary = "保存打印终端配置") - @SysLog("保存打印终端配置") + @Operation(summary = "保存 USB 打印机配置") + @SysLog("保存 USB 打印机配置") @PreAuthorize("@pms.hasPermission('hard_hardManager_printer_bind')") public R saveConfig(@Valid @RequestBody HardPrinterConfigDto dto) { return R.ok(hardPrinterService.saveConfig(dto)); diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/controller/hard/HardApiController.java b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/controller/hard/HardApiController.java index 4cddf6e0f..86db3e1b9 100644 --- a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/controller/hard/HardApiController.java +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/controller/hard/HardApiController.java @@ -331,52 +331,52 @@ public class HardApiController { } /** - * 查询当前 EY_PRINT 终端绑定关系和打印机参数,用于登录后进入打印专用模式。 + * 查询当前食堂或超市终端所绑定的无屏 USB 打印机及打印参数。 * - * @param hardId DEVICE-ID 请求头中的打印终端 ID - * @return 绑定来源和打印机配置 + * @param hardId DEVICE-ID 请求头中的来源业务终端 ID + * @return 当前绑定和打印机配置;尚未绑定时数据为空 */ @GetMapping("/printer/bootstrap") - @Operation(summary = "获取打印终端启动配置") + @Operation(summary = "获取业务终端打印机绑定") public R printerBootstrap(@RequestHeader("DEVICE-ID") Long hardId) { - return R.ok(hardPrinterService.getPrinterBinding(hardId)); + return R.ok(hardPrinterService.getSourceBinding(hardId)); } /** - * 保存当前 EY_PRINT 实际连接打印机的型号、VID 和 PID,目标设备只能由请求头确定。 + * 由当前食堂或超市终端上报其实际连接打印机的型号、VID 和 PID。 * - * @param hardId DEVICE-ID 请求头中的打印终端 ID + * @param hardId DEVICE-ID 请求头中的来源业务终端 ID * @param dto Android USB Host 枚举出的打印机信息 * @return 保留纸宽、切刀和图片模式后的完整打印机配置 */ @PostMapping("/printer/info") - @Operation(summary = "上报打印终端实际打印机信息") + @Operation(summary = "业务终端上报 USB 打印机信息") public R reportPrinterInfo(@RequestHeader("DEVICE-ID") Long hardId, @Valid @RequestBody HardPrinterInfoReportDto dto) { return R.ok(hardPrinterService.reportDeviceInfo(hardId, dto)); } /** - * 拉取当前 EY_PRINT 尚未完成的任务,补偿离线期间遗漏的 MQTT 消息。 + * 拉取当前食堂或超市终端所绑定打印机尚未完成的任务,补偿离线期间遗漏的 MQTT 消息。 * - * @param hardId DEVICE-ID 请求头中的打印终端 ID + * @param hardId DEVICE-ID 请求头中的来源业务终端 ID * @return 待打印任务列表 */ @GetMapping("/printer/tasks") - @Operation(summary = "拉取打印终端待办") + @Operation(summary = "拉取业务终端代理打印待办") public R printerTasks(@RequestHeader("DEVICE-ID") Long hardId) { return R.ok(hardInvoiceService.listPendingTasks(hardId)); } /** - * 提交当前 EY_PRINT 的 USB 打印结果。 + * 由当前食堂或超市终端代理提交 USB 打印结果。 * - * @param hardId DEVICE-ID 请求头中的打印终端 ID + * @param hardId DEVICE-ID 请求头中的来源业务终端 ID * @param result 任务、订单、版本和结果 * @return 接受或幂等忽略时返回 {@code true} */ @PostMapping("/printer/result") - @Operation(summary = "提交打印终端结果") + @Operation(summary = "提交业务终端代理打印结果") public R printerResult(@RequestHeader("DEVICE-ID") Long hardId, @Valid @RequestBody HardPrintResultDto result) { return R.ok(hardInvoiceService.reportPrintResult(hardId, result)); diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/mapper/HardPrinterBindingMapper.java b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/mapper/HardPrinterBindingMapper.java index 5bfb56981..50b67b3c4 100644 --- a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/mapper/HardPrinterBindingMapper.java +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/mapper/HardPrinterBindingMapper.java @@ -5,7 +5,7 @@ import com.bsd.pjcloud.hard.api.entity.HardPrinterBindingEntity; import org.apache.ibatis.annotations.Mapper; /** - * 打印终端一对一绑定关系数据访问接口。 + * 业务终端与无屏 USB 打印机资产一对一绑定关系数据访问接口。 */ @Mapper public interface HardPrinterBindingMapper extends PjcloudBaseMapper { diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/mapper/HardPrinterConfigMapper.java b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/mapper/HardPrinterConfigMapper.java index fe7ba4128..8734046e9 100644 --- a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/mapper/HardPrinterConfigMapper.java +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/mapper/HardPrinterConfigMapper.java @@ -5,7 +5,7 @@ import com.bsd.pjcloud.hard.api.entity.HardPrinterConfigEntity; import org.apache.ibatis.annotations.Mapper; /** - * 尔印打印终端参数数据访问接口。 + * 尔印 USB 打印机资产参数数据访问接口。 */ @Mapper public interface HardPrinterConfigMapper extends PjcloudBaseMapper { diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/HardInvoiceService.java b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/HardInvoiceService.java index fae73d065..c3028a2a0 100644 --- a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/HardInvoiceService.java +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/HardInvoiceService.java @@ -95,21 +95,21 @@ public interface HardInvoiceService extends IService { int cancelPendingVoucherTasks(Long orderId); /** - * 查询指定 EY_PRINT 终端的待打印任务,供启动、重连和定时补拉。 + * 查询指定来源业务终端所绑定打印机的待打印任务,供启动、重连和定时补拉。 * - * @param printerHardId 目标打印终端 ID + * @param sourceHardId DEVICE-ID 请求头中的来源业务终端 ID * @return 待打印任务列表 */ - List listPendingTasks(Long printerHardId); + List listPendingTasks(Long sourceHardId); /** * 校验终端身份、订单和版本后记录打印结果。 * - * @param printerHardId DEVICE-ID 请求头解析出的目标设备 + * @param sourceHardId DEVICE-ID 请求头解析出的来源业务终端 * @param result 终端回执 * @return 接受或幂等忽略回执时返回 {@code true} */ - Boolean reportPrintResult(Long printerHardId, HardPrintResultDto result); + Boolean reportPrintResult(Long sourceHardId, HardPrintResultDto result); /** * 跨租户扫描并处理待生成和待打印任务。 diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/HardPrinterService.java b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/HardPrinterService.java index 9ca8b37da..17f3b79de 100644 --- a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/HardPrinterService.java +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/HardPrinterService.java @@ -12,25 +12,25 @@ import com.bsd.pjcloud.hard.api.vo.manager.HardManagerVo; import java.util.List; /** - * 尔印打印终端绑定和配置服务。 + * 业务终端与无屏尔印 USB 打印机的绑定和配置服务。 */ public interface HardPrinterService { /** - * 查询指定来源或打印设备当前绑定详情。 + * 查询指定来源终端或打印机资产当前绑定详情。 * - * @param hardId 来源业务终端或打印终端 ID + * @param hardId 来源业务终端或打印机资产 ID * @return 绑定详情;未绑定时返回 {@code null} */ HardPrinterBindingVo getBinding(Long hardId); /** - * 按目标打印终端身份读取启动绑定,拒绝来源业务终端借用打印模式接口。 + * 按当前来源业务终端身份读取其绑定打印机和打印参数。 * - * @param printerHardId DEVICE-ID 声明的 EY_PRINT 设备 ID - * @return 已绑定的来源终端及打印参数 + * @param sourceHardId DEVICE-ID 声明的 SUPERMARKET/CANTEEN 设备 ID + * @return 当前绑定及打印参数;尚未绑定时返回 {@code null} */ - HardPrinterBindingVo getPrinterBinding(Long printerHardId); + HardPrinterBindingVo getSourceBinding(Long sourceHardId); /** * 批量补充设备分页中的绑定关系和打印参数,避免管理端逐行请求。 @@ -40,10 +40,10 @@ public interface HardPrinterService { void enrichDevices(List devices); /** - * 查询来源设备同商户下尚未绑定的 EY_PRINT 设备。 + * 查询来源设备同商户下尚未绑定的 EY_PRINT 打印机资产。 * * @param sourceHardId 来源业务终端 ID - * @return 可绑定打印终端列表 + * @return 可绑定打印机资产列表 */ List listAvailablePrinters(Long sourceHardId); @@ -87,16 +87,16 @@ public interface HardPrinterService { HardPrinterConfigEntity saveConfig(HardPrinterConfigDto dto); /** - * 由当前打印终端上报实际打印机型号和 USB 标识,保留后台维护的 ESC/POS 参数。 + * 由当前来源业务终端代理上报实际 USB 打印机型号和标识,保留后台维护的 ESC/POS 参数。 * - * @param printerHardId DEVICE-ID 请求头中的 EY_PRINT 设备 ID + * @param sourceHardId DEVICE-ID 请求头中的 SUPERMARKET/CANTEEN 设备 ID * @param dto Android USB Host 读取到的型号、VID 和 PID * @return 更新后的打印机配置 */ - HardPrinterConfigEntity reportDeviceInfo(Long printerHardId, HardPrinterInfoReportDto dto); + HardPrinterConfigEntity reportDeviceInfo(Long sourceHardId, HardPrinterInfoReportDto dto); /** - * 按来源业务终端解析当前有效打印终端。 + * 按来源业务终端解析当前有效打印机资产。 * * @param sourceHardId 来源终端 ID * @return 当前绑定;未绑定时返回 {@code null} diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/HardInvoiceServiceImpl.java b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/HardInvoiceServiceImpl.java index 350787243..a861825f9 100644 --- a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/HardInvoiceServiceImpl.java +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/HardInvoiceServiceImpl.java @@ -268,7 +268,7 @@ public class HardInvoiceServiceImpl extends ServiceImpl listPendingTasks(Long printerHardId) { - requirePrinterIdentity(printerHardId); + public List listPendingTasks(Long sourceHardId) { + HardPrinterBindingEntity binding = requireSourceBinding(sourceHardId); return baseMapper.selectList(Wrappers.lambdaQuery() - .eq(HardInvoiceEntity::getPrinterHardId, printerHardId) + .eq(HardInvoiceEntity::getHardId, sourceHardId) + .eq(HardInvoiceEntity::getPrinterHardId, binding.getPrinterHardId()) .eq(HardInvoiceEntity::getStatus, HardPrintStatusEnum.GENERATE.getType()) .isNotNull(HardInvoiceEntity::getPic) .orderByAsc(HardInvoiceEntity::getCreateTime) @@ -426,14 +427,15 @@ public class HardInvoiceServiceImpl extends ServiceImpl 0 ? sourceHardId : null; HardPrinterBindingEntity binding = printerService.findBySourceHardId(normalizedSource); boolean canDispatch = binding != null; - HardInvoiceEntity task = HardInvoiceEntity.builder().deptId(order.getDeptId()).hardId(normalizedSource) + HardInvoiceEntity task = HardInvoiceEntity.builder().deptId(resolveTaskDeptId(order)).hardId(normalizedSource) .printerHardId(canDispatch ? binding.getPrinterHardId() : null).orderId(order.getId()) .printType(printType.getType()).printNo(printNo).retryVersion(0).attemptCount(0) .status(canDispatch ? HardPrintStatusEnum.PENDING_GENERATION.getType() : HardPrintStatusEnum.FAILED.getType()) @@ -530,6 +532,19 @@ public class HardInvoiceServiceImpl extends ServiceImpllambdaQuery() - .eq(HardPrinterBindingEntity::getPrinterHardId, printer.getId()) - .last("limit 1")); - if (binding == null) throw new RuntimeException("尔印打印终端尚未绑定业务终端"); - return assemble(binding); + public HardPrinterBindingVo getSourceBinding(Long sourceHardId) { + HardManagerEntity source = requireSource(sourceHardId); + HardPrinterBindingEntity binding = findBySourceHardId(source.getId()); + return binding == null ? null : assemble(binding); } /** {@inheritDoc} */ @@ -173,13 +178,13 @@ public class HardPrinterServiceImpl implements HardPrinterService { HardManagerEntity source = requireSource(dto.getSourceHardId()); HardManagerEntity printer = requirePrinter(dto.getPrinterHardId()); if (!Objects.equals(source.getTenantId(), printer.getTenantId())) { - throw new RuntimeException("来源终端和打印终端必须属于同一租户"); + throw new RuntimeException("来源终端和打印机资产必须属于同一租户"); } if (!Objects.equals(source.getMerchantId(), printer.getMerchantId())) { - throw new RuntimeException("来源终端和打印终端必须属于同一商户"); + throw new RuntimeException("来源终端和打印机资产必须属于同一商户"); } if (!Objects.equals(source.getDeptId(), printer.getDeptId())) { - throw new RuntimeException("来源终端和打印终端必须属于同一商户部门"); + throw new RuntimeException("来源终端和打印机资产必须属于同一商户部门"); } HardPrinterBindingEntity binding = HardPrinterBindingEntity.builder() .sourceHardId(source.getId()) @@ -191,12 +196,13 @@ public class HardPrinterServiceImpl implements HardPrinterService { try { bindingMapper.insert(binding); } catch (DuplicateKeyException exception) { - throw new RuntimeException("来源终端或打印终端已经绑定,请先解除原绑定", exception); + throw new RuntimeException("来源终端或打印机资产已经绑定,请先解除原绑定", exception); } if (configMapper.selectCount(Wrappers.lambdaQuery() .eq(HardPrinterConfigEntity::getPrinterHardId, printer.getId())) == 0) { configMapper.insert(defaultConfig(printer)); } + notifyBindingChangeAfterCommit(source.getDeptId()); return assemble(binding); } @@ -210,6 +216,7 @@ public class HardPrinterServiceImpl implements HardPrinterService { throw new RuntimeException("存在待生成、待打印或打印失败任务,处理完成后才能解绑"); } bindingMapper.deleteById(binding.getId()); + notifyBindingChangeAfterCommit(binding.getDeptId()); } /** {@inheritDoc} */ @@ -239,14 +246,17 @@ public class HardPrinterServiceImpl implements HardPrinterService { config.setId(existing.getId()); configMapper.updateById(config); } + HardPrinterBindingEntity binding = findByPrinterHardId(printer.getId()); + if (binding != null) notifyBindingChangeAfterCommit(binding.getDeptId()); return configMapper.selectById(config.getId()); } /** {@inheritDoc} */ @Override @Transactional(rollbackFor = Exception.class) - public HardPrinterConfigEntity reportDeviceInfo(Long printerHardId, HardPrinterInfoReportDto dto) { - HardManagerEntity printer = requirePrinter(printerHardId); + public HardPrinterConfigEntity reportDeviceInfo(Long sourceHardId, HardPrinterInfoReportDto dto) { + HardPrinterBindingEntity binding = requireSourceBinding(sourceHardId); + HardManagerEntity printer = requirePrinter(binding.getPrinterHardId()); HardPrinterConfigEntity config = configMapper.selectOne(Wrappers.lambdaQuery() .eq(HardPrinterConfigEntity::getPrinterHardId, printer.getId()) .last("limit 1")); @@ -313,21 +323,74 @@ public class HardPrinterServiceImpl implements HardPrinterService { } /** - * 校验并返回 EY_PRINT 打印终端。 + * 校验并返回 EY_PRINT 逻辑打印机资产。 * * @param hardId 打印设备 ID - * @return 有效打印设备 + * @return 有效打印机资产 */ private HardManagerEntity requirePrinter(Long hardId) { HardManagerEntity printer = hardManagerMapper.selectById(hardId); HardTypeEnum type = printer == null ? null : HardTypeEnum.valueOfByType(printer.getType()); if (printer == null || !HardTypeEnum.isPrintHard(type)) { - throw new RuntimeException("目标设备必须是尔印打印终端"); + throw new RuntimeException("目标设备必须是尔印打印机"); } - if (printer.getMerchantId() == null) throw new RuntimeException("打印终端未绑定商户"); + if (printer.getMerchantId() == null) throw new RuntimeException("打印机未绑定商户"); return printer; } + /** + * 校验当前 Android 来源终端并返回其一对一打印机绑定。 + * + * @param sourceHardId DEVICE-ID 中的来源业务终端 ID + * @return 当前来源终端的有效绑定 + */ + private HardPrinterBindingEntity requireSourceBinding(Long sourceHardId) { + HardManagerEntity source = requireSource(sourceHardId); + HardPrinterBindingEntity binding = findBySourceHardId(source.getId()); + if (binding == null) throw new RuntimeException("当前业务终端尚未绑定尔印打印机"); + return binding; + } + + /** + * 查询逻辑打印机资产当前绑定的来源业务终端。 + * + * @param printerHardId EY_PRINT 逻辑打印机资产 ID + * @return 当前一对一绑定;未绑定时为空 + */ + private HardPrinterBindingEntity findByPrinterHardId(Long printerHardId) { + if (printerHardId == null || printerHardId <= 0) return null; + return bindingMapper.selectOne(Wrappers.lambdaQuery() + .eq(HardPrinterBindingEntity::getPrinterHardId, printerHardId) + .last("limit 1")); + } + + /** + * 在本地事务提交后通知业务终端刷新绑定,MQTT 暂时不可用时不回滚已保存的数据。 + * + * @param deptId 来源业务终端所属部门 ID;为空时跳过通知 + */ + private void notifyBindingChangeAfterCommit(Long deptId) { + if (deptId == null) return; + Runnable notifyAction = () -> { + try { + mqttEventService.sendConfigChangeMsg(deptId); + } catch (Exception exception) { + log.warn("打印机绑定配置已保存,但MQTT刷新通知发送失败,deptId:{}", deptId, exception); + } + }; + if (!TransactionSynchronizationManager.isSynchronizationActive()) { + notifyAction.run(); + return; + } + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + /** 本地绑定事务提交后再通知,避免终端读取到尚未提交的旧关系。 */ + @Override + public void afterCommit() { + notifyAction.run(); + } + }); + } + /** * 校验受首期协议约束的打印参数。 * @@ -399,7 +462,7 @@ public class HardPrinterServiceImpl implements HardPrinterService { /** * 创建尚未持久化的首期默认打印配置。 * - * @param printer 打印终端 + * @param printer 逻辑打印机资产 * @return 默认配置 */ private HardPrinterConfigEntity defaultConfig(HardManagerEntity printer) { diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/manager/HardManagerServiceImpl.java b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/manager/HardManagerServiceImpl.java index 3ffb54033..bdd2d078c 100644 --- a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/manager/HardManagerServiceImpl.java +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/manager/HardManagerServiceImpl.java @@ -2,6 +2,7 @@ package com.bsd.pjcloud.hard.service.impl.manager; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; @@ -67,7 +68,7 @@ public class HardManagerServiceImpl extends ServiceImpl() - .eq(HardOrderEntity::getId, order.getId()) - .eq(HardOrderEntity::getOrderStatus, HardOrderStatusEnum.PAY.getType())) - : baseMapper.updateById(updateObj); - if (updated == 0) { - throw new I18nBizException(ErrorCodes.HARD_VERIFY_STATUS_CHANGED); + boolean remoteCodeCancelled = false; + try { + // Wallet 是独立远程事务,成功返回后若 Hardware 本地事务失败,必须显式恢复预约二维码。 + getSelf().cancelCode(order, userInfo); + remoteCodeCancelled = true; + LocalDateTime verificationTime = LocalDateTime.now(); + HardOrderEntity updateObj = HardOrderEntity.builder() + .id(order.getId()) + .androidOrderCode(dto.getAndroidOrderCode()) + .hardId(dto.getHardwareId()) + .orderStatus(HardOrderStatusEnum.CANCEL.getType()) + .verificationTime(verificationTime) + .build(); + int updated = isReservationVerification + ? baseMapper.update(updateObj, new LambdaUpdateWrapper() + .eq(HardOrderEntity::getId, order.getId()) + .eq(HardOrderEntity::getOrderStatus, HardOrderStatusEnum.PAY.getType())) + : baseMapper.updateById(updateObj); + if (updated == 0) { + throw new I18nBizException(ErrorCodes.HARD_VERIFY_STATUS_CHANGED); + } + if (isReservationVerification) createReservationVerificationArtifacts(order, verificationTime, dto.getHardwareId()); + return true; + } catch (RuntimeException exception) { + if (isReservationVerification && remoteCodeCancelled) { + rollbackReservationCodeAfterLocalFailure(order, exception); + } + throw exception; + } + } + + /** + * Hardware 本地核销事务失败后补偿已经提交的 Wallet 预约码状态,使订单仍可重新核销。补偿失败时 + * 保留原始业务异常,并把恢复异常作为 suppressed exception 记录,便于运维识别需要人工修复的订单。 + * + * @param order 本次核销失败的预约订单 + * @param originalException 触发 Hardware 本地事务回滚的原始异常 + */ + private void rollbackReservationCodeAfterLocalFailure(HardOrderEntity order, RuntimeException originalException) { + try { + R result = remoteCodeUseService.rollbackReservationCode(order.getId()); + if (result != null && result.isOk() && Boolean.TRUE.equals(result.getData())) return; + String message = result == null ? "恢复预约核销码无响应" + : StrUtil.blankToDefault(result.getMsg(), "恢复预约核销码失败"); + IllegalStateException rollbackException = new IllegalStateException(message); + originalException.addSuppressed(rollbackException); + log.error("预约核销本地事务失败且二维码补偿失败,orderId:{},message:{}", order.getId(), message); + } catch (RuntimeException rollbackException) { + originalException.addSuppressed(rollbackException); + log.error("预约核销本地事务失败且二维码补偿异常,orderId:{}", order.getId(), rollbackException); } - if (isReservationVerification) createReservationVerificationArtifacts(order, verificationTime, dto.getHardwareId()); - return true; } /** diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/test/java/com/bsd/pjcloud/hard/controller/hard/HardApiControllerTest.java b/pjcloud-hardware/pjcloud-hardware-biz/src/test/java/com/bsd/pjcloud/hard/controller/hard/HardApiControllerTest.java index 01eed55d0..003300aca 100644 --- a/pjcloud-hardware/pjcloud-hardware-biz/src/test/java/com/bsd/pjcloud/hard/controller/hard/HardApiControllerTest.java +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/test/java/com/bsd/pjcloud/hard/controller/hard/HardApiControllerTest.java @@ -95,7 +95,7 @@ class HardApiControllerTest { assertEquals(10, result.get(0).getOriginalProductCount()); } - /** 验证上报目标只使用可信 DEVICE-ID,并将服务保存结果原样返回。 */ + /** 验证来源终端代理上报只使用可信 DEVICE-ID,并将服务保存结果原样返回。 */ @Test void reportPrinterInfoUsesTrustedDeviceHeader() { HardPrinterInfoReportDto request = new HardPrinterInfoReportDto(); @@ -106,12 +106,12 @@ class HardApiControllerTest { .printerHardId(20L).printerModel(request.getPrinterModel()) .usbVendorId(10473).usbProductId(649).paperWidth(80) .autoCut("1").imageMode("GS_V0").build(); - when(hardPrinterService.reportDeviceInfo(20L, request)).thenReturn(saved); + when(hardPrinterService.reportDeviceInfo(10L, request)).thenReturn(saved); - HardPrinterConfigEntity result = (HardPrinterConfigEntity) controller.reportPrinterInfo(20L, request).getData(); + HardPrinterConfigEntity result = (HardPrinterConfigEntity) controller.reportPrinterInfo(10L, request).getData(); assertSame(saved, result); - verify(hardPrinterService).reportDeviceInfo(20L, request); + verify(hardPrinterService).reportDeviceInfo(10L, request); } /** 构造带原购、剩余数量和原行金额的购物车展示对象。 */ diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/test/java/com/bsd/pjcloud/hard/service/impl/HardInvoiceServiceImplTest.java b/pjcloud-hardware/pjcloud-hardware-biz/src/test/java/com/bsd/pjcloud/hard/service/impl/HardInvoiceServiceImplTest.java index 093338503..0b4619d4a 100644 --- a/pjcloud-hardware/pjcloud-hardware-biz/src/test/java/com/bsd/pjcloud/hard/service/impl/HardInvoiceServiceImplTest.java +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/test/java/com/bsd/pjcloud/hard/service/impl/HardInvoiceServiceImplTest.java @@ -125,8 +125,10 @@ class HardInvoiceServiceImplTest { @Test void reportPrintResultExcludesCancelledStatusAtUpdateTime() { HardInvoiceEntity pending = task(100L, HardPrintStatusEnum.GENERATE, 2); + pending.setHardId(10L); pending.setPrinterHardId(20L); - when(managerService.getById(20L)).thenReturn(printer()); + when(managerService.getById(10L)).thenReturn(source()); + when(printerService.findBySourceHardId(10L)).thenReturn(binding()); when(invoiceMapper.selectById(100L)).thenReturn(pending); when(invoiceMapper.update(isNull(), any(Wrapper.class))).thenReturn(0); HardPrintResultDto result = new HardPrintResultDto(); @@ -136,7 +138,7 @@ class HardInvoiceServiceImplTest { result.setSuccess(true); result.setAttemptCount(1); - assertFalse(service.reportPrintResult(20L, result)); + assertFalse(service.reportPrintResult(10L, result)); @SuppressWarnings("unchecked") ArgumentCaptor> wrapperCaptor = @@ -151,8 +153,10 @@ class HardInvoiceServiceImplTest { @Test void lateFailureCannotOverwritePrintedTask() { HardInvoiceEntity printed = task(100L, HardPrintStatusEnum.PRINT, 2); + printed.setHardId(10L); printed.setPrinterHardId(20L); - when(managerService.getById(20L)).thenReturn(printer()); + when(managerService.getById(10L)).thenReturn(source()); + when(printerService.findBySourceHardId(10L)).thenReturn(binding()); when(invoiceMapper.selectById(100L)).thenReturn(printed); HardPrintResultDto result = new HardPrintResultDto(); result.setTaskId(100L); @@ -162,7 +166,7 @@ class HardInvoiceServiceImplTest { result.setAttemptCount(3); result.setErrorMessage("late error"); - assertTrue(service.reportPrintResult(20L, result)); + assertTrue(service.reportPrintResult(10L, result)); verify(invoiceMapper, never()).update(isNull(), any(Wrapper.class)); } @@ -174,10 +178,6 @@ class HardInvoiceServiceImplTest { return HardManagerEntity.builder().id(10L).type(HardTypeEnum.CANTEEN.getType()).merchantId(30L).build(); } - private static HardManagerEntity printer() { - return HardManagerEntity.builder().id(20L).type(HardTypeEnum.EY_PRINT.getType()).merchantId(30L).build(); - } - private static HardPrinterBindingEntity binding() { return HardPrinterBindingEntity.builder().sourceHardId(10L).printerHardId(20L).merchantId(30L).build(); } diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/test/java/com/bsd/pjcloud/hard/service/impl/HardPrinterServiceImplTest.java b/pjcloud-hardware/pjcloud-hardware-biz/src/test/java/com/bsd/pjcloud/hard/service/impl/HardPrinterServiceImplTest.java index 55de49e9e..c13494830 100644 --- a/pjcloud-hardware/pjcloud-hardware-biz/src/test/java/com/bsd/pjcloud/hard/service/impl/HardPrinterServiceImplTest.java +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/test/java/com/bsd/pjcloud/hard/service/impl/HardPrinterServiceImplTest.java @@ -3,6 +3,7 @@ package com.bsd.pjcloud.hard.service.impl; import com.bsd.pjcloud.hard.api.constant.HardTypeEnum; import com.bsd.pjcloud.hard.api.dto.HardPrinterConfigDto; import com.bsd.pjcloud.hard.api.dto.HardPrinterInfoReportDto; +import com.bsd.pjcloud.hard.api.entity.HardPrinterBindingEntity; import com.bsd.pjcloud.hard.api.entity.HardPrinterConfigEntity; import com.bsd.pjcloud.hard.api.entity.manager.HardManagerEntity; import com.bsd.pjcloud.hard.api.vo.HardPrinterBindingVo; @@ -10,6 +11,7 @@ import com.bsd.pjcloud.hard.api.vo.manager.HardManagerVo; import com.bsd.pjcloud.hard.mapper.HardPrinterBindingMapper; import com.bsd.pjcloud.hard.mapper.HardPrinterConfigMapper; import com.bsd.pjcloud.hard.mapper.manager.HardManagerMapper; +import com.bsd.pjcloud.mqtt.api.feign.RemoteMqttEventService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.test.util.ReflectionTestUtils; @@ -33,19 +35,29 @@ import static org.mockito.Mockito.when; class HardPrinterServiceImplTest { private final HardManagerMapper managerMapper = mock(HardManagerMapper.class); - /** 打印配置数据访问对象,用于验证终端上报只修改允许字段。 */ + /** 打印机绑定数据访问对象,用于把来源业务终端解析为目标打印机资产。 */ + private final HardPrinterBindingMapper bindingMapper = mock(HardPrinterBindingMapper.class); + /** 打印配置数据访问对象,用于验证来源终端代理上报只修改允许字段。 */ private final HardPrinterConfigMapper configMapper = mock(HardPrinterConfigMapper.class); + /** MQTT 通知服务,用于隔离绑定配置刷新通知。 */ + private final RemoteMqttEventService mqttEventService = mock(RemoteMqttEventService.class); /** 使用真实业务方法和注入 Mapper 的被测服务。 */ private final HardPrinterServiceImpl service = mock(HardPrinterServiceImpl.class, CALLS_REAL_METHODS); - /** 为每个用例注入相同 Mapper,并准备一台有效的 EY_PRINT 设备。 */ + /** 为每个用例注入相同 Mapper,并准备一台来源终端及其绑定的 EY_PRINT 打印机。 */ @BeforeEach void setUp() { ReflectionTestUtils.setField(service, "hardManagerMapper", managerMapper); + ReflectionTestUtils.setField(service, "bindingMapper", bindingMapper); ReflectionTestUtils.setField(service, "configMapper", configMapper); + ReflectionTestUtils.setField(service, "mqttEventService", mqttEventService); + when(managerMapper.selectById(10L)).thenReturn(HardManagerEntity.builder() + .id(10L).type(HardTypeEnum.CANTEEN.getType()).merchantId(30L).tenantId(50L).build()); when(managerMapper.selectById(20L)).thenReturn(HardManagerEntity.builder() .id(20L).type(HardTypeEnum.EY_PRINT.getType()).merchantId(30L).tenantId(50L).build()); + when(bindingMapper.selectOne(any())).thenReturn(HardPrinterBindingEntity.builder() + .sourceHardId(10L).printerHardId(20L).merchantId(30L).tenantId(50L).build()); } @Test @@ -60,8 +72,6 @@ class HardPrinterServiceImplTest { /** 未绑定打印机的普通设备应保留空配置,不能因空打印机 ID 导致列表查询失败。 */ @Test void enrichDevicesSkipsPrinterConfigForUnboundDevice() { - HardPrinterBindingMapper bindingMapper = mock(HardPrinterBindingMapper.class); - ReflectionTestUtils.setField(service, "bindingMapper", bindingMapper); HardManagerVo device = new HardManagerVo(); device.setId(10L); device.setType(HardTypeEnum.SUPERMARKET.getType()); @@ -73,6 +83,14 @@ class HardPrinterServiceImplTest { assertNull(device.getPrinterModel()); } + /** 未绑定来源业务终端查询绑定时应返回空,由客户端保留本地 USB 测试能力。 */ + @Test + void getSourceBindingReturnsNullWhenBusinessTerminalIsUnbound() { + when(bindingMapper.selectOne(any())).thenReturn(null); + + assertNull(service.getSourceBinding(10L)); + } + @Test void usbVidAndPidMustBeConfiguredTogether() { HardPrinterConfigDto config = validConfig(); @@ -94,7 +112,7 @@ class HardPrinterServiceImplTest { assertEquals("USB VID必须在0到65535之间", exception.getMessage()); } - /** 终端重新上报真机参数时必须保留管理端维护的纸宽、切刀和图片模式。 */ + /** 来源终端重新上报真机参数时必须保留管理端维护的纸宽、切刀和图片模式。 */ @Test void reportDeviceInfoPreservesEscPosConfiguration() { HardPrinterConfigEntity existing = HardPrinterConfigEntity.builder() @@ -103,7 +121,7 @@ class HardPrinterServiceImplTest { .autoCut("0").imageMode("ESC_STAR").tenantId(50L).build(); when(configMapper.selectOne(any())).thenReturn(existing); - HardPrinterConfigEntity result = service.reportDeviceInfo(20L, reportRequest()); + HardPrinterConfigEntity result = service.reportDeviceInfo(10L, reportRequest()); assertEquals("GEZHI / micro-printer", result.getPrinterModel()); assertEquals(10473, result.getUsbVendorId()); @@ -119,7 +137,7 @@ class HardPrinterServiceImplTest { void reportDeviceInfoCreatesDefaultConfiguration() { when(configMapper.selectOne(any())).thenReturn(null); - HardPrinterConfigEntity result = service.reportDeviceInfo(20L, reportRequest()); + HardPrinterConfigEntity result = service.reportDeviceInfo(10L, reportRequest()); assertEquals(20L, result.getPrinterHardId()); assertEquals("GEZHI / micro-printer", result.getPrinterModel()); @@ -131,16 +149,13 @@ class HardPrinterServiceImplTest { verify(configMapper).insert(result); } - /** 普通业务终端不能借用设备信息接口覆盖打印终端配置。 */ + /** EY_PRINT 资产不能冒充有屏 Android 来源终端上报自身配置。 */ @Test - void reportDeviceInfoRejectsNonPrinterDevice() { - when(managerMapper.selectById(21L)).thenReturn(HardManagerEntity.builder() - .id(21L).type(HardTypeEnum.CANTEEN.getType()).merchantId(30L).tenantId(50L).build()); - + void reportDeviceInfoRejectsPrinterAssetIdentity() { RuntimeException exception = assertThrows(RuntimeException.class, - () -> service.reportDeviceInfo(21L, reportRequest())); + () -> service.reportDeviceInfo(20L, reportRequest())); - assertEquals("目标设备必须是尔印打印终端", exception.getMessage()); + assertEquals("来源终端必须是超市或食堂设备", exception.getMessage()); } /** 已绑定设备的部门属于一对一绑定身份,修改部门前必须先解绑。 */ diff --git a/pjcloud-mqtt/pjcloud-mqtt-api/src/main/java/com/bsd/pjcloud/mqtt/dto/hard/HardPrintMsg.java b/pjcloud-mqtt/pjcloud-mqtt-api/src/main/java/com/bsd/pjcloud/mqtt/dto/hard/HardPrintMsg.java index c4ebb1fc9..c6afa09cd 100644 --- a/pjcloud-mqtt/pjcloud-mqtt-api/src/main/java/com/bsd/pjcloud/mqtt/dto/hard/HardPrintMsg.java +++ b/pjcloud-mqtt/pjcloud-mqtt-api/src/main/java/com/bsd/pjcloud/mqtt/dto/hard/HardPrintMsg.java @@ -9,7 +9,7 @@ public class HardPrintMsg extends MqttHardBaseMsg { // 用户id private Long userId; - // 设备id + // 实际接收 MQTT 并代理 USB 打印的食堂或超市终端 ID private Long hardId; /** @@ -19,9 +19,9 @@ public class HardPrintMsg extends MqttHardBaseMsg { private Long sourceHardId; /** - * 实际接收 MQTT 并执行 USB 打印的 EY_PRINT 终端 ID。 + * 后台绑定的 EY_PRINT 逻辑打印机资产 ID,用于客户端校验绑定关系。 */ - @Schema(description = "目标打印终端ID") + @Schema(description = "逻辑打印机资产ID") private Long targetHardId; // 设备部门id