From 7384b67c5007b55b12ac877742a16d964b9446cd Mon Sep 17 00:00:00 2001 From: wangw <1594593906@qq.com> Date: Tue, 30 Dec 2025 10:23:04 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=93=E5=8D=B0=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/czg/mq/PrintMqListener.java | 7 +- .../com/czg/order/dto/OrderInfoPrintDTO.java | 4 - .../order/service/PrintMachineLogService.java | 2 +- .../czg/service/order/print/FeiPrinter.java | 95 +++- .../service/order/print/PrinterHandler.java | 33 +- .../czg/service/order/print/PrinterImpl.java | 10 +- .../czg/service/order/print/YxyPrinter.java | 84 ++-- .../impl/OrderInfoCustomServiceImpl.java | 6 +- .../impl/PrintMachineLogServiceImpl.java | 475 ++++++++++-------- 9 files changed, 415 insertions(+), 301 deletions(-) diff --git a/cash-api/order-server/src/main/java/com/czg/mq/PrintMqListener.java b/cash-api/order-server/src/main/java/com/czg/mq/PrintMqListener.java index ef97c341c..33f610667 100644 --- a/cash-api/order-server/src/main/java/com/czg/mq/PrintMqListener.java +++ b/cash-api/order-server/src/main/java/com/czg/mq/PrintMqListener.java @@ -12,7 +12,6 @@ import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.context.annotation.Lazy; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Component; import java.util.function.Consumer; @@ -61,7 +60,7 @@ public class PrintMqListener { } Boolean printOrder = jsonObject.getBoolean("printOrder"); funUtil.runFunAndCheckKey(() -> { - printerHandler.handler(orderId, printOrder != null && !printOrder ? PrinterHandler.PrintTypeEnum.ONE : PrinterHandler.PrintTypeEnum.ONE_AND_ORDER, null); + printerHandler.handler(orderId, printOrder != null && !printOrder ? PrinterHandler.PrintTypeEnum.ONE : PrinterHandler.PrintTypeEnum.ONE_AND_ORDER); return null; }, RedisCst.getLockKey("orderPrint", orderId)); }); @@ -72,7 +71,7 @@ public class PrintMqListener { */ @RabbitListener(queues = {"${spring.profiles.active}-" + RabbitConstants.Queue.ORDER_HANDOVER_PRINT_QUEUE}) public void handoverPrint(String id) { - invokeFun("handoverPrint", "java.order", id, (data) -> printerHandler.handler(data, PrinterHandler.PrintTypeEnum.HANDOVER, null)); + invokeFun("handoverPrint", "java.order", id, (data) -> printerHandler.handler(data, PrinterHandler.PrintTypeEnum.HANDOVER)); } /** @@ -80,6 +79,6 @@ public class PrintMqListener { */ @RabbitListener(queues = {"${spring.profiles.active}-" + RabbitConstants.Queue.CALL_TABLE_PRINT_QUEUE}) public void callTablePrint(String id) { - invokeFun("handoverPrint", "java.order", id, (data) -> printerHandler.handler(data, PrinterHandler.PrintTypeEnum.CALL, null)); + invokeFun("handoverPrint", "java.order", id, (data) -> printerHandler.handler(data, PrinterHandler.PrintTypeEnum.CALL)); } } diff --git a/cash-common/cash-common-service/src/main/java/com/czg/order/dto/OrderInfoPrintDTO.java b/cash-common/cash-common-service/src/main/java/com/czg/order/dto/OrderInfoPrintDTO.java index bbe02a34c..51afbc2ce 100644 --- a/cash-common/cash-common-service/src/main/java/com/czg/order/dto/OrderInfoPrintDTO.java +++ b/cash-common/cash-common-service/src/main/java/com/czg/order/dto/OrderInfoPrintDTO.java @@ -24,9 +24,5 @@ public class OrderInfoPrintDTO implements Serializable { */ @NotNull(message = "打印类型不为空") private Integer type; - /** - * 打印机id - */ - private Long machineId; } diff --git a/cash-common/cash-common-service/src/main/java/com/czg/order/service/PrintMachineLogService.java b/cash-common/cash-common-service/src/main/java/com/czg/order/service/PrintMachineLogService.java index 22d8d77f8..40c77ab7b 100644 --- a/cash-common/cash-common-service/src/main/java/com/czg/order/service/PrintMachineLogService.java +++ b/cash-common/cash-common-service/src/main/java/com/czg/order/service/PrintMachineLogService.java @@ -11,6 +11,6 @@ import com.czg.order.entity.PrintMachineLog; * @since 2025-03-11 */ public interface PrintMachineLogService extends IService { - void save(PrintMachine config, String bizType, String printContent, Object respJson); + void save(PrintMachine config, String bizType, String printContent, String respJson); } diff --git a/cash-service/order-service/src/main/java/com/czg/service/order/print/FeiPrinter.java b/cash-service/order-service/src/main/java/com/czg/service/order/print/FeiPrinter.java index 3665d34dd..aa1b6494a 100644 --- a/cash-service/order-service/src/main/java/com/czg/service/order/print/FeiPrinter.java +++ b/cash-service/order-service/src/main/java/com/czg/service/order/print/FeiPrinter.java @@ -1,17 +1,18 @@ package com.czg.service.order.print; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.text.UnicodeUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; import com.czg.account.dto.HandoverRecordDTO; -import com.czg.account.entity.HandoverRecord; import com.czg.account.entity.PrintMachine; import com.czg.account.entity.ShopInfo; -import com.czg.account.service.ShopInfoService; import com.czg.order.entity.OrderDetail; import com.czg.order.entity.OrderInfo; import com.czg.service.order.enums.OrderStatusEnums; -import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.digest.DigestUtils; import org.springframework.http.HttpEntity; @@ -20,12 +21,13 @@ import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; import java.math.BigDecimal; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * @author Administrator @@ -33,11 +35,6 @@ import java.util.List; @Component @Slf4j public class FeiPrinter extends PrinterHandler implements PrinterImpl { - @Resource - private RestTemplate restTemplate; - - @Resource - private ShopInfoService shopInfoService; // API 地址 private static final String URL = "http://api.feieyun.cn/Api/Open/"; @@ -72,10 +69,8 @@ public class FeiPrinter extends PrinterHandler implements PrinterImpl { String remark = orderDetail.getRemark(); String content = buildDishPrintData(false, getPickupNum(orderInfo), orderInfo.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), orderDetail.getProductName(), orderDetail.getSkuName(), orderDetail.getNum(), remark, orderDetail.getProGroupInfo(), orderDetail.getId(), orderDetail.isUrgent()); - Object o = sendPrintRequest(machine.getAddress(), content, null, "1"); + String o = sendPrintRequest(machine.getAddress(), content, null, "1"); printMachineLogService.save(machine, "新订单", content, o); - -// printMachineLogService.save(machine, "新订单", , ); } @@ -84,7 +79,7 @@ public class FeiPrinter extends PrinterHandler implements PrinterImpl { String remark = orderDetail.getRemark(); String content = buildDishPrintData(true, getPickupNum(orderInfo), orderInfo.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), orderDetail.getProductName(), orderDetail.getSkuName(), orderDetail.getReturnNum(), remark, orderDetail.getProGroupInfo(), orderDetail.getId(), orderDetail.isUrgent()); - Object o = sendPrintRequest(machine.getAddress(), content, null, "1"); + String o = sendPrintRequest(machine.getAddress(), content, null, "1"); printMachineLogService.save(machine, "退款单", content, o); } @@ -106,8 +101,8 @@ public class FeiPrinter extends PrinterHandler implements PrinterImpl { .setRemark(orderInfo.getRemark()) .setDiscountAmount(orderInfo.getOriginAmount().subtract(orderInfo.getPayAmount()).toPlainString()); String string = buildOrderPrintData(printInfoDTO, detailList); - Object o = sendPrintRequest(machine.getAddress(), string, null, printerNum); - printMachineLogService.save(machine, "退款单", string, o); + String o = sendPrintRequest(machine.getAddress(), string, null, printerNum); + printMachineLogService.save(machine, "结算单", string, o); } @@ -134,16 +129,16 @@ public class FeiPrinter extends PrinterHandler implements PrinterImpl { .setRemark(orderInfo.getRemark()) .setDiscountAmount((orderInfo.getOriginAmount().add(orderInfo.getSeatAmount()).add(orderInfo.getPackFee()).subtract(orderInfo.getPayAmount())).toPlainString()); printInfoDTO.setPrintTitle(printInfoDTO.getPrintTitle()); - if(orderInfo.getSeatNum() != null && orderInfo.getSeatAmount().compareTo(BigDecimal.ZERO) > 0){ + if (orderInfo.getSeatNum() != null && orderInfo.getSeatAmount().compareTo(BigDecimal.ZERO) > 0) { printInfoDTO.setSeatNum(orderInfo.getSeatNum().toString()); printInfoDTO.setSeatAmount(orderInfo.getSeatAmount().toPlainString()); } - if(orderInfo.getPackFee().compareTo(BigDecimal.ZERO) > 0){ + if (orderInfo.getPackFee().compareTo(BigDecimal.ZERO) > 0) { printInfoDTO.setPackFee(orderInfo.getPackFee().toPlainString()); } String string = buildOrderPrintData(printInfoDTO, detailList); - Object resp = sendPrintRequest(machine.getAddress(), string, null, printerNum); + String resp = sendPrintRequest(machine.getAddress(), string, null, printerNum); printMachineLogService.save(machine, "结算单", string, resp); } @@ -151,7 +146,7 @@ public class FeiPrinter extends PrinterHandler implements PrinterImpl { protected void callNumPrint(PrintMachine machine, String callNum, String shopName, String tableName, String tableNote, String preNum, String codeUrl, LocalDateTime takeTime, String shopNote) { String voiceJson = "{\"bizType\":\"2\",\"content\":\"您有一条新的排号记录\"}"; String data = buildCallTicketData(shopName, tableName, callNum, preNum, codeUrl, shopNote, takeTime); - Object resp = sendPrintRequest(machine.getAddress(), data, voiceJson, "1"); + String resp = sendPrintRequest(machine.getAddress(), data, voiceJson, "1"); printMachineLogService.save(machine, "叫号单", data, resp); } @@ -170,7 +165,7 @@ public class FeiPrinter extends PrinterHandler implements PrinterImpl { } @Override - public R sendPrintRequest(String address, String metaPrintData, String voiceData, String printerNum) { + public String sendPrintRequest(String address, String metaPrintData, String voiceData, String printerNum) { log.info("飞蛾打印机开始发送打印请求, 设备地址: {}, 元数据: {}", address, metaPrintData); String time = String.valueOf(System.currentTimeMillis() / 1000); MultiValueMap formData = new LinkedMultiValueMap<>(); @@ -189,12 +184,68 @@ public class FeiPrinter extends PrinterHandler implements PrinterImpl { HttpEntity> requestEntity = new HttpEntity<>(formData, headers); String result = restTemplate.postForObject(URL, requestEntity, String.class); - log.info("打印结果: {}", result); - return (R) result; + log.info("飞鹅打印结果: {}", result); + return result; } catch (Exception e) { log.error("打印请求失败: {}", e.getMessage()); throw new RuntimeException("飞蛾打印请求失败", e); } } + /** + * 检查飞鹅打印机 打印任务是否已打印 + * + * @param printOrderId 打印订单编号 + * @return null-未知错误,true-已打印,false-未打印 + */ + public Boolean checkFPrintStatus(String printOrderId) { + String time = String.valueOf(System.currentTimeMillis() / 1000); + Map paramMap = new HashMap<>(); + paramMap.put("user", USER); + paramMap.put("stime", time); + paramMap.put("sig", signature(time)); + paramMap.put("apiname", "Open_queryOrderState"); + paramMap.put("orderid", printOrderId); + Boolean ret; + try { + String resp = HttpUtil.post(URL, paramMap, 1000 * 5); + //成功 {"msg":"ok","ret":0,"data":true,"serverExecutedTime":4} + //失败 {"msg":"ok","ret":0,"data":false,"serverExecutedTime":4} + JSONObject json = JSONUtil.parseObj(UnicodeUtil.toString(resp)); + log.info("飞鹅打印机 打印任务状态响应: {}", json); + ret = json.getBool("data"); + } catch (Exception e) { + ret = null; + } + return ret; + } + + /** + * 检查飞鹅打印机是否在线 + * + * @param sn 设备编号 + * @return 在线,工作状态正常。/离线。/未知错误 + */ + public String checkOnline(String sn) { + String time = String.valueOf(System.currentTimeMillis() / 1000); + Map paramMap = new HashMap<>(); + paramMap.put("user", USER); + paramMap.put("stime", time); + paramMap.put("sig", signature(time)); + paramMap.put("apiname", "Open_queryPrinterStatus"); + paramMap.put("sn", sn); + String msg; + try { + String resp = HttpUtil.post(URL, paramMap, 1000 * 5); + //成功 开机 {"msg":"ok","ret":0,"data":"在线,工作状态正常。","serverExecutedTime":4} + //成功 离线 {"msg":"ok","ret":0,"data":"离线。","serverExecutedTime":7} + JSONObject json = JSONUtil.parseObj(UnicodeUtil.toString(resp)); + log.info("飞鹅打印机状态响应: {}", json); + msg = json.getStr("data"); + } catch (Exception e) { + msg = "未知错误"; + } + return msg; + } + } diff --git a/cash-service/order-service/src/main/java/com/czg/service/order/print/PrinterHandler.java b/cash-service/order-service/src/main/java/com/czg/service/order/print/PrinterHandler.java index 32303d319..6ee130dae 100644 --- a/cash-service/order-service/src/main/java/com/czg/service/order/print/PrinterHandler.java +++ b/cash-service/order-service/src/main/java/com/czg/service/order/print/PrinterHandler.java @@ -34,6 +34,7 @@ import lombok.ToString; import lombok.experimental.Accessors; import lombok.extern.slf4j.Slf4j; import org.apache.dubbo.config.annotation.DubboReference; +import org.springframework.web.client.RestTemplate; import java.math.BigDecimal; import java.time.LocalDateTime; @@ -50,6 +51,8 @@ public abstract class PrinterHandler { protected PrinterHandler nextPrinter; protected String printerBrand; + @Resource + protected RestTemplate restTemplate; @Resource protected OrderDetailService orderDetailService; @Resource @@ -81,7 +84,11 @@ public abstract class PrinterHandler { @Getter public enum PrintTypeEnum { HANDOVER("交班", "handover"), - ORDER("订单", "order"), ONE("菜品", "one"), CALL("叫号", "call"), ONE_AND_ORDER("菜品和结算单同时打印", "oneAndOrder"), PRE_ORDER("预结算单", "preOrder"); + ORDER("订单", "order"), + ONE("菜品", "one"), + CALL("叫号", "call"), + ONE_AND_ORDER("菜品和结算单同时打印", "oneAndOrder"), + PRE_ORDER("预结算单", "preOrder"); private final String name; private final String code; @@ -145,10 +152,9 @@ public abstract class PrinterHandler { * @param printType 打印类型,JSON数组 refund-确认退款单 handover-交班单 queue-排队取号 * @return 打印机列表 */ - private List getPrintMachine(Long shopId, String subType, String printMethod, String printType, Long machineId) { + private List getPrintMachine(Long shopId, String subType, String printMethod, String printType) { QueryWrapper wrapper = new QueryWrapper() .eq(PrintMachine::getStatus, 1) - .eq(PrintMachine::getId, machineId) .eq(PrintMachine::getShopId, shopId) .eq(PrintMachine::getSubType, subType) .eq(PrintMachine::getConnectionType, "网络"); @@ -171,10 +177,6 @@ public abstract class PrinterHandler { wrapper.like(PrintMachine::getPrintType, printType); } List list = printMachineService.list(wrapper); -// for (PrintMachine item : list) { -// //实际打印以传递的参数为准 -// item.setPrintMethod(printMethod); -// } if (list.isEmpty()) { log.error("店铺未配置打印机,店铺id: {}", shopId); return list; @@ -190,7 +192,7 @@ public abstract class PrinterHandler { * @param data 传递的数据 * @param printTypeEnum order returnOrder preOrder one call handover */ - public void handler(String data, PrintTypeEnum printTypeEnum, Long machineId) { + public void handler(String data, PrintTypeEnum printTypeEnum) { Long shopId; String printMethod = ""; String printType = ""; @@ -261,7 +263,7 @@ public abstract class PrinterHandler { } Object finalObj = obj; - getPrintMachine(shopId, "cash", printMethod, printType, machineId).forEach(machine -> { + getPrintMachine(shopId, "cash", printMethod, printType).forEach(machine -> { handleRequest(machine, finalObj, printTypeEnum); // printPlaceTicket(isReturn, machine, orderInfo, shopInfo); }); @@ -447,17 +449,7 @@ public abstract class PrinterHandler { log.info("台位费商品,不打印"); return; } -// ProdSku sku = prodSkuService.getById(item.getSkuId()); -// if (isTemporary == 0 && sku == null) { -// log.error("商品不存在, id: {}", item.getSkuId()); -// return; -// } else if (isTemporary == 1) { -// sku = new ProdSku(); -// } - -// String remark = StrUtil.isNotBlank(sku.getSpecInfo()) ? sku.getSpecInfo() : ""; -// item.setRemark(remark); if (item.getReturnNum().compareTo(BigDecimal.ZERO) > 0) { returnDishesPrint(orderInfo, item, machine); } @@ -468,7 +460,8 @@ public abstract class PrinterHandler { // 保存已打印信息 OrderDetail orderDetail = detailMap.get(item.getId()); - redisService.set(RedisCst.getPrintOrderDetailKey(orderInfo.getId(), item.getId()), JSONObject.toJSONString(new PrintDetailInfo().setPrint(item.getIsPrint() == 1).setDetailId(item.getId()) + redisService.set(RedisCst.getPrintOrderDetailKey(orderInfo.getId(), item.getId()), + JSONObject.toJSONString(new PrintDetailInfo().setPrint(item.getIsPrint() == 1).setDetailId(item.getId()) .setPrintNum(orderDetail.getNum()).setPrintReturnNum(orderDetail.getReturnNum())), 3600 * 24); }); diff --git a/cash-service/order-service/src/main/java/com/czg/service/order/print/PrinterImpl.java b/cash-service/order-service/src/main/java/com/czg/service/order/print/PrinterImpl.java index 3d0f9c125..33024622b 100644 --- a/cash-service/order-service/src/main/java/com/czg/service/order/print/PrinterImpl.java +++ b/cash-service/order-service/src/main/java/com/czg/service/order/print/PrinterImpl.java @@ -9,7 +9,6 @@ import cn.hutool.json.JSONUtil; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.czg.account.dto.HandoverRecordDTO; -import com.czg.account.entity.HandoverRecord; import com.czg.order.entity.OrderDetail; import lombok.Data; import lombok.experimental.Accessors; @@ -69,10 +68,9 @@ public interface PrinterImpl { * @param metaPrintData 打印元数据 * @param voiceData 语音信息 * @param printNum 打印联数 - * @param 返回数据类型 * @return 打印结果 */ - R sendPrintRequest(String address, String metaPrintData, String voiceData, String printNum); + String sendPrintRequest(String address, String metaPrintData, String voiceData, String printNum); /** * 获取当前打印机标签信息 @@ -560,12 +558,6 @@ public interface PrinterImpl { return str; } - public static void main(String[] args) { - System.out.println("水煮肉片".length()); - System.out.println(StrUtil.repeat(' ', 8)); - System.out.println(StrUtil.fillAfter("水煮肉片", ' ', 21)); - } - /** * 获取填充字符串, 并且换行 * diff --git a/cash-service/order-service/src/main/java/com/czg/service/order/print/YxyPrinter.java b/cash-service/order-service/src/main/java/com/czg/service/order/print/YxyPrinter.java index 3d9f19c70..3f32358a9 100644 --- a/cash-service/order-service/src/main/java/com/czg/service/order/print/YxyPrinter.java +++ b/cash-service/order-service/src/main/java/com/czg/service/order/print/YxyPrinter.java @@ -4,11 +4,10 @@ import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.SecureUtil; +import cn.hutool.http.HttpUtil; import com.czg.account.dto.HandoverRecordDTO; -import com.czg.account.entity.HandoverRecord; import com.czg.account.entity.PrintMachine; import com.czg.account.entity.ShopInfo; -import com.czg.account.service.ShopInfoService; import com.czg.order.entity.OrderDetail; import com.czg.order.entity.OrderInfo; import com.czg.service.order.enums.OrderStatusEnums; @@ -20,9 +19,7 @@ import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; -import javax.annotation.Resource; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.*; @@ -45,12 +42,6 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl { //APPSECRET private static final String APP_SECRET = "2022bsjZF544GAH"; - @Resource - private ShopInfoService shopInfoService; - - @Resource - private RestTemplate restTemplate; - public YxyPrinter() { super("云想印"); } @@ -75,7 +66,7 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl { @Override - public R sendPrintRequest(String address, String metaPrintData, String voiceData, String printNum) { + public String sendPrintRequest(String address, String metaPrintData, String voiceData, String printNum) { log.info("开始请求云享印,请求数据:{}, {}", voiceData, metaPrintData); //设备名称 //行为方式 1:只打印数据 2:只播放信息 3:打印数据并播放信息 @@ -87,10 +78,9 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl { String time = String.valueOf(System.currentTimeMillis()); String uuid = UUID.randomUUID().toString(); - Map param = getToken(time, uuid); //参数 MultiValueMap multiValueMap = new LinkedMultiValueMap<>(); - multiValueMap.add("token", param.get("TOKEN")); + multiValueMap.add("token", getToken(time, uuid)); multiValueMap.add("devName", address); multiValueMap.add("actWay", 3); multiValueMap.add("cn", printNum); @@ -108,7 +98,7 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl { httpEntity, String.class); log.info("请求云享印成功,响应数据: {}", httpResponse); - return (R) httpResponse; + return httpResponse; } @Override @@ -116,9 +106,7 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl { String buildDishPrintData = buildDishPrintData(false, getPickupNum(orderInfo), DateUtil.format(orderDetail.getCreateTime(), "yyyy-MM-dd HH:mm:ss"), orderDetail.getProductName(), orderDetail.getSkuName(), orderDetail.getNum(), orderDetail.getRemark(), orderDetail.getProGroupInfo(), orderDetail.getId(), orderDetail.isUrgent()); String voiceJson = "{\"bizType\":\"2\",\"content\":\"您有一笔新的订单,请及时处理\"}"; -// String voiceJson = "{\"bizType\":\"2\",\"content\":\"\"}"; - Object resp = sendPrintRequest(machine.getAddress(), buildDishPrintData, voiceJson, "1"); -// sendPrintRequest(voiceJson, 3, 1, machine.getAddress(), data); + String resp = sendPrintRequest(machine.getAddress(), buildDishPrintData, voiceJson, "1"); printMachineLogService.save(machine, "新订单", buildDishPrintData, resp); } @@ -127,9 +115,8 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl { protected void returnDishesPrint(OrderInfo orderInfo, OrderDetail orderDetail, PrintMachine machine) { String buildDishPrintData = buildDishPrintData(true, getPickupNum(orderInfo), DateUtil.format(orderDetail.getCreateTime(), "yyyy-MM-dd HH:mm:ss"), orderDetail.getProductName(), orderDetail.getSkuName(), orderDetail.getReturnNum(), orderDetail.getRemark(), orderDetail.getProGroupInfo(), orderDetail.getId(), orderDetail.isUrgent()); -// String voiceJson = "{\"bizType\":\"2\",\"content\":\"您有一笔新的订单,请及时处理\"}"; String voiceJson = "{\"bizType\":\"2\",\"content\":\"\"}"; - Object resp = sendPrintRequest(machine.getAddress(), buildDishPrintData, voiceJson, "1"); + String resp = sendPrintRequest(machine.getAddress(), buildDishPrintData, voiceJson, "1"); printMachineLogService.save(machine, "退款单", buildDishPrintData, resp); } @@ -141,18 +128,17 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl { .setOrderNo(orderInfo.getOrderNo()).setTradeDate(DateUtil.format(orderInfo.getCreateTime(), "yyyy-MM-dd HH:mm:ss")).setOperator("【POS-1】001").setPayAmount(orderInfo.getPayAmount().toPlainString()) .setOriginalAmount(orderInfo.getOriginAmount().toPlainString()).setReturn(isReturn(orderInfo)) .setBalance(balance).setPayType((ObjectUtil.isEmpty(orderInfo.getPayType()) || ObjectUtil.isNull(orderInfo.getPayType()) ? "" : orderInfo.getPayType())).setIntegral("0") - .setOutNumber(orderInfo.getTakeCode()).setPrintTitle("结算单") + .setOutNumber(orderInfo.getTakeCode()).setPrintTitle("退款单") .setRemark(orderInfo.getRemark()).setDiscountAmount(orderInfo.getOriginAmount().subtract(orderInfo.getPayAmount()).toPlainString()); String data = buildOrderPrintData(printInfoDTO, detailList); String voiceJson = "{\"PbizType\":\"2\",\"content\":\"您有一笔新的订单,请及时处理\"}"; -// String voiceJson = "{\"bizType\":\"2\",\"content\":\"\"}"; String printerNum = "1"; if (StrUtil.isNotBlank(machine.getPrintQty())) { printerNum = machine.getPrintQty().split("\\^")[1]; } String resp = sendPrintRequest(machine.getAddress(), data, voiceJson, printerNum); - printMachineLogService.save(machine, "新订单", data, resp); + printMachineLogService.save(machine, "退款单", data, resp); } @@ -173,33 +159,38 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl { .setRemark(orderInfo.getRemark()) .setDiscountAmount((orderInfo.getOriginAmount().add(orderInfo.getSeatAmount()).add(orderInfo.getPackFee()).subtract(orderInfo.getPayAmount())).toPlainString()); printInfoDTO.setPrintTitle(printInfoDTO.getPrintTitle()); - if(orderInfo.getSeatNum() != null && orderInfo.getSeatAmount().compareTo(BigDecimal.ZERO) > 0){ + if (orderInfo.getSeatNum() != null && orderInfo.getSeatAmount().compareTo(BigDecimal.ZERO) > 0) { printInfoDTO.setSeatNum(orderInfo.getSeatNum().toString()); printInfoDTO.setSeatAmount(orderInfo.getSeatAmount().toPlainString()); } - if(orderInfo.getPackFee().compareTo(BigDecimal.ZERO) > 0){ + if (orderInfo.getPackFee().compareTo(BigDecimal.ZERO) > 0) { printInfoDTO.setPackFee(orderInfo.getPackFee().toPlainString()); } String data = buildOrderPrintData(printInfoDTO, detailList); String voiceJson = "{\"PbizType\":\"2\",\"content\":\"您有一笔新的订单,请及时处理\"}"; -// String voiceJson = "{\"bizType\":\"2\",\"content\":\"\"}"; String printerNum = "1"; if (StrUtil.isNotBlank(machine.getPrintQty())) { printerNum = machine.getPrintQty().split("\\^")[1]; } String resp = sendPrintRequest(machine.getAddress(), data, voiceJson, printerNum); -// printMachineLogService.save(machine, printType, data, resp); - printMachineLogService.save(machine, "新订单", data, resp); + printMachineLogService.save(machine, "结算单", data, resp); } + /** + * 叫号单打印 + */ @Override protected void callNumPrint(PrintMachine machine, String callNum, String shopName, String tableName, String tableNote, String preNum, String codeUrl, LocalDateTime takeTime, String shopNote) { String resp = buildCallTicketData(shopName, tableName, callNum, preNum, codeUrl, shopNote, takeTime); sendPrintRequest(machine.getAddress(), resp, null, "1"); } + + /** + * 交班单打印 + */ @Override protected void handoverPrint(PrintMachine machine, HandoverRecordDTO record) { String string = buildHandoverData(record); @@ -214,9 +205,9 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl { * @param requestId 请求ID,自定义 * @return token信息 */ - private static Map getToken(String timestamp, String requestId) { + private static String getToken(String timestamp, String requestId) { StringBuilder token = new StringBuilder(); - StringBuilder encode = new StringBuilder(); +// StringBuilder encode = new StringBuilder(); SortedMap map = new TreeMap<>(); map.put("appId", APP_ID); map.put("timestamp", timestamp); @@ -226,13 +217,38 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl { String key = next.getKey(); Object value = next.getValue(); token.append(key).append(value); - encode.append(key).append("=").append(value).append("&"); +// encode.append(key).append("=").append(value).append("&"); } - Map finalMap = new HashMap<>(); - finalMap.put("ENCODE", encode.toString()); - finalMap.put("TOKEN", SecureUtil.md5(token + APP_SECRET).toUpperCase()); - return finalMap; +// Map finalMap = new HashMap<>(); +// finalMap.put("ENCODE", encode.toString()); +// finalMap.put("TOKEN", SecureUtil.md5(token + APP_SECRET).toUpperCase()); +// return finalMap; + return SecureUtil.md5(token + APP_SECRET).toUpperCase(); } + /** + * 检查打印状态 + * + * @param devName 设备名称,(唯一) 对应配置表中的address字段即(IP地址/打印机编号) + * @param taskId 打印任务id,用于复查打印状态,云想印=orderId + */ + public String checkPrintStatus(String devName, String taskId) { + String time = String.valueOf(System.currentTimeMillis()); + String uuid = UUID.randomUUID().toString(); + + Map paramMap = new HashMap<>(); + + paramMap.put("devName", devName); + paramMap.put("orderId", taskId); + paramMap.put("token", getToken(time, uuid)); + paramMap.put("appId", APP_ID); + paramMap.put("timestamp", time); + paramMap.put("requestId", uuid); + paramMap.put("userCode", USER_CODE); + String s = HttpUtil.get("https://ioe.car900.com/v1/openApi/dev/findOrder.json", paramMap, 1000 * 5); + log.info("云想印打印机:{},任务:{}状态:{}", devName, taskId, s); + return s; + } + } diff --git a/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/OrderInfoCustomServiceImpl.java b/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/OrderInfoCustomServiceImpl.java index df23df62c..887e8fe76 100644 --- a/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/OrderInfoCustomServiceImpl.java +++ b/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/OrderInfoCustomServiceImpl.java @@ -1523,13 +1523,13 @@ public class OrderInfoCustomServiceImpl implements OrderInfoCustomService { switch (orderInfoPrintDTO.getType()) { case 0: - printerHandler.handler(orderInfo.getId().toString(), PrinterHandler.PrintTypeEnum.ONE_AND_ORDER, orderInfoPrintDTO.getMachineId()); + printerHandler.handler(orderInfo.getId().toString(), PrinterHandler.PrintTypeEnum.ONE_AND_ORDER); break; case 1: - printerHandler.handler(orderInfo.getId().toString(), PrinterHandler.PrintTypeEnum.PRE_ORDER, orderInfoPrintDTO.getMachineId()); + printerHandler.handler(orderInfo.getId().toString(), PrinterHandler.PrintTypeEnum.PRE_ORDER); break; case 2: - printerHandler.handler(orderInfo.getId().toString(), PrinterHandler.PrintTypeEnum.ORDER, orderInfoPrintDTO.getMachineId()); + printerHandler.handler(orderInfo.getId().toString(), PrinterHandler.PrintTypeEnum.ORDER); break; } return true; diff --git a/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/PrintMachineLogServiceImpl.java b/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/PrintMachineLogServiceImpl.java index 66c7b7bee..20bd6f3d0 100644 --- a/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/PrintMachineLogServiceImpl.java +++ b/cash-service/order-service/src/main/java/com/czg/service/order/service/impl/PrintMachineLogServiceImpl.java @@ -2,28 +2,31 @@ package com.czg.service.order.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.date.DateUtil; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.text.UnicodeUtil; import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.util.StrUtil; -import cn.hutool.crypto.SecureUtil; -import cn.hutool.http.HttpUtil; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson2.JSONObject; import com.czg.account.entity.PrintMachine; import com.czg.market.service.OrderInfoService; import com.czg.order.entity.PrintMachineLog; import com.czg.order.service.PrintMachineLogService; import com.czg.service.order.mapper.PrintMachineLogMapper; +import com.czg.service.order.print.FeiPrinter; +import com.czg.service.order.print.YxyPrinter; import com.mybatisflex.spring.service.impl.ServiceImpl; import jakarta.annotation.Resource; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.codec.digest.DigestUtils; +import org.springframework.context.annotation.Lazy; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; -import java.util.*; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; /** * 店铺小票打印记录ServiceImpl @@ -35,135 +38,22 @@ import java.util.*; @Service @RequiredArgsConstructor public class PrintMachineLogServiceImpl extends ServiceImpl implements PrintMachineLogService { - //请求地址 - private static final String URL_STR = "https://ioe.car900.com/v1/openApi/dev/customPrint.json"; - //APPID - private static final String APP_ID = "ZF544"; - //USERCODE - private static final String USER_CODE = "ZF544"; - //APPSECRET - private static final String APP_SECRET = "2022bsjZF544GAH"; - - public static final String URL = "http://api.feieyun.cn/Api/Open/";//不需要修改 - - public static final String USER = "chaozhanggui2022@163.com";//*必填*:账号名 - public static final String UKEY = "UfWkhXxSkeSSscsU";//*必填*: 飞鹅云后台注册账号后生成的UKEY 【备注:这不是填打印机的KEY】 - public static final String SN = "960238952";//*必填*:打印机编号,必须要在管理后台里添加打印机或调用API接口添加之后,才能调用API @Resource private OrderInfoService orderInfoService; + @Lazy + @Resource + private YxyPrinter yxyPrinter; + @Lazy + @Resource + private FeiPrinter feiPrinter; - /** - * 获取TOKEN值 - * - * @param timestamp 时间戳,13位 - * @param requestId 请求ID,自定义 - * @return - */ - private static Map getToken(String timestamp, String requestId) { - StringBuilder token = new StringBuilder(); - StringBuilder encode = new StringBuilder(); - SortedMap map = new TreeMap<>(); - map.put("appId", APP_ID); - map.put("timestamp", timestamp); - map.put("requestId", requestId); - map.put("userCode", USER_CODE); - for (Map.Entry next : map.entrySet()) { - String key = next.getKey(); - Object value = next.getValue(); - token.append(key).append(value); - encode.append(key).append("=").append(value).append("&"); - } - System.out.println("token" + token); - Map finalMap = new HashMap<>(); - finalMap.put("ENCODE", encode.toString()); - finalMap.put("TOKEN", SecureUtil.md5(token + APP_SECRET).toUpperCase()); - return finalMap; - } - - /** - * 检查打印状态 - * - * @param devName 设备名称,(唯一) 对应配置表中的address字段即(IP地址/打印机编号) - * @param taskId 打印任务id,用于复查打印状态,云想印=orderId - * @return - */ - public static String checkPrintStatus(String devName, String taskId) { - String time = String.valueOf(System.currentTimeMillis()); - String uuid = UUID.randomUUID().toString(); - - Map param = getToken(time, uuid); - String token = param.get("TOKEN"); - Map paramMap = new HashMap<>(); - - paramMap.put("devName", devName); - paramMap.put("orderId", taskId); - paramMap.put("token", token); - paramMap.put("appId", APP_ID); - paramMap.put("timestamp", time); - paramMap.put("requestId", uuid); - paramMap.put("userCode", USER_CODE); - - return HttpUtil.get("https://ioe.car900.com/v1/openApi/dev/findOrder.json", paramMap, 1000 * 5); - } - - private static String signature(String USER, String UKEY, String STIME) { - return DigestUtils.sha1Hex(USER + UKEY + STIME); - } - - /** - * 检查飞鹅打印机打印任务是否已打印 - * - * @param printOrderId 打印订单编号 - * @return null-未知错误,true-已打印,false-未打印 - */ - public static Boolean checkFPrintStatus(String printOrderId) { - String STIME = String.valueOf(System.currentTimeMillis() / 1000); - Map paramMap = new HashMap<>(); - paramMap.put("user", USER); - paramMap.put("stime", STIME); - paramMap.put("sig", signature(USER, UKEY, STIME)); - paramMap.put("apiname", "Open_queryOrderState"); - paramMap.put("orderid", printOrderId); - Boolean ret; - try { - String resp = HttpUtil.post(URL, paramMap, 1000 * 5); - //成功 {"msg":"ok","ret":0,"data":true,"serverExecutedTime":4} - //失败 {"msg":"ok","ret":0,"data":false,"serverExecutedTime":4} - JSONObject json = JSONUtil.parseObj(UnicodeUtil.toString(resp)); - ret = json.getBool("data"); - } catch (Exception e) { - ret = null; - } - return ret; - } - - /** - * 检查飞鹅打印机是否在线 - * - * @param sn 设备编号 - * @return 在线,工作状态正常。/离线。/未知错误 - */ - public static String checkOnline(String sn) { - String STIME = String.valueOf(System.currentTimeMillis() / 1000); - Map paramMap = new HashMap<>(); - paramMap.put("user", USER); - paramMap.put("stime", STIME); - paramMap.put("sig", signature(USER, UKEY, STIME)); - paramMap.put("apiname", "Open_queryPrinterStatus"); - paramMap.put("sn", sn); - String msg; - try { - String resp = HttpUtil.post(URL, paramMap, 1000 * 5); - //成功 开机 {"msg":"ok","ret":0,"data":"在线,工作状态正常。","serverExecutedTime":4} - //成功 离线 {"msg":"ok","ret":0,"data":"离线。","serverExecutedTime":7} - JSONObject json = JSONUtil.parseObj(UnicodeUtil.toString(resp)); - msg = json.getStr("data"); - } catch (Exception e) { - msg = "未知错误"; - } - return msg; - } + Map yxxStatusMap = Map.of( + 0, "离线(设备上线后自动补打)", + 1, "在线", + 2, "获取失败", + 3, "未激活", + 4, "设备已禁用"); /** * 保存打印记录 @@ -175,7 +65,7 @@ public class PrintMachineLogServiceImpl extends ServiceImpl yxxStatusMap = MapUtil.builder(0, "离线(设备上线后自动补打)").put(1, "在线").put(2, "获取失败").put(3, "未激活").put(4, "设备已禁用").build(); + JSONObject resp = JSONObject.parseObject(respJson); // 云想印 if ("云想印".equals(config.getContentType())) { - cn.hutool.json.JSONObject resp = JSONUtil.parseObj(respJson); - int code = resp.getInt("code"); - cn.hutool.json.JSONObject data = resp.getJSONObject("data").getJSONObject("data"); + int code = resp.getIntValue("code"); + JSONObject respData = resp.getJSONObject("data"); + JSONObject data = respData.getJSONObject("data"); //设备状态,0: 离线, 1: 在线, 2: 获取失败, 3:未激活, 4:设备已禁用 - int status = data.getInt("status"); + int status = data.getIntValue("status"); if (code != 0) { failFlag = 1; respCode = code + ""; - respMsg = resp.getStr("msg"); + respMsg = resp.getString("msg"); } else if (status != 1) { failFlag = 1; respCode = code + ""; - respMsg = status + "_" + yxxStatusMap.get(status); } if (code == 0) { - String taskId = resp.getJSONObject("data").getStr("orderId"); - entity.setTaskId(taskId); + entity.setTaskId(respData.getString("orderId")); } // 飞鹅云打印机暂时没有适配,先return不做打印记录 } else if ("飞鹅".equals(config.getContentType())) { - cn.hutool.json.JSONObject resp = JSONUtil.parseObj(respJson); - int ret = resp.getInt("ret"); + int ret = resp.getIntValue("ret"); if (ret != 0) { failFlag = 1; respCode = ret + ""; - respMsg = resp.getStr("msg"); + respMsg = resp.getString("msg"); } else { - String printOrderId = resp.getStr("data"); - entity.setTaskId(printOrderId); + entity.setTaskId(resp.getString("data")); } } else { // 其他打印机暂时没有适配,先return不做打印记录 return; } entity.setBizType(bizType); -// entity.setCreateUserId(config.getCurrentUserId()); -// entity.setCreateUserName(config.getCurrentUserName()); -// if (StrUtil.isNotBlank(config.getCurrentUserNickName())) { -// entity.setCreateUserName(StrUtil.concat(true, config.getCurrentUserNickName(), " | ", config.getCurrentUserName())); -// } entity.setPrintContent(printContent); entity.setCreateTime(DateUtil.date().toLocalDateTime()); if (failFlag == 0) { @@ -238,64 +118,251 @@ public class PrintMachineLogServiceImpl extends ServiceImpl checkPrintStatus(config, entity)); + } - // 云想印 - if ("云想印".equals(config.getContentType())) { - // 延迟3ms,复查打印状态 (用户可以根据设备信息查询到当前设备的在线情况(注:该接口只能提供参考,设备的离线状态是在设备离线3分钟后才会生效)) - ThreadUtil.safeSleep(1000 * 5); - String jsonStr = checkPrintStatus(config.getAddress(), entity.getTaskId()); - cn.hutool.json.JSONObject resp = JSONUtil.parseObj(jsonStr); - int code = resp.getInt("code"); - if (code == 0) { - cn.hutool.json.JSONObject data = resp.getJSONObject("data"); - boolean status = data.containsKey("status"); - if (!status) { + /** + * 类级别成员变量:基于虚拟线程的固定大小(5)定时线程池 + * // Java 21+ 虚拟线程工厂,支持命名 + */ + private final ScheduledExecutorService virtualThreadScheduler = Executors.newScheduledThreadPool( + 5, + Thread.ofVirtual().name("print-query-vt-", 0).factory() + ); + + /** + * 打印机状态查询(解决 retryFuture 爆红问题 + 虚拟线程 + 轮询重试) + * + * @param config 打印机配置 + * @param entity 打印日志实体 + */ + public void checkPrintStatus(PrintMachine config, PrintMachineLog entity) { + // 最大重试次数 + int maxRetryTimes = 5; + AtomicInteger executedTimes = new AtomicInteger(0); + + // 原子引用包装ScheduledFuture,用于后续取消轮询 + AtomicReference> retryFutureRef = new AtomicReference<>(); + + // 核心查询任务(修正后,逻辑内聚) + Runnable printQueryTask = () -> { + int currentTimes = executedTimes.incrementAndGet(); + boolean isPrintSuccess = false; + boolean isLastTask = false; + + try { + // 1. 云想印打印机状态查询 + if ("云想印".equals(config.getContentType())) { + String jsonStr = yxyPrinter.checkPrintStatus(config.getAddress(), entity.getTaskId()); + log.info("云想印打印状态查询结果(第{}次,虚拟线程:{}):{}", + currentTimes, Thread.currentThread().getName(), jsonStr); + JSONObject resp = JSONObject.parseObject(jsonStr); + int code = resp.getIntValue("code"); + if (code == 0) { + JSONObject data = resp.getJSONObject("data"); + if (data.containsKey("status")) { + isPrintSuccess = data.getBooleanValue("status", false); + updatePrintLogEntity(entity, isPrintSuccess); + } + } + } + // 2. 飞鹅云打印机状态查询 + else if ("飞鹅".equals(config.getContentType())) { + Boolean success = feiPrinter.checkFPrintStatus(entity.getTaskId()); + if (success == null) { + entity.setFailFlag(1); + entity.setRespMsg("打印失败,未知错误"); + entity.setPrintTime(null); + } else if (success) { + isPrintSuccess = true; + updatePrintLogEntity(entity, true); + } else { + String msg = feiPrinter.checkOnline(entity.getAddress()); + if (msg.indexOf("在线,工作状态正常") > 0) { + isPrintSuccess = true; + updatePrintLogEntity(entity, true); + } else { + isPrintSuccess = false; + entity.setFailFlag(1); + entity.setPrintTime(null); + entity.setRespMsg(StrUtil.concat(true, "打印失败,", "_", msg)); + } + } + }else { + log.info("打印类型为其他类型,终止打印状态查询轮询任务"); + ScheduledFuture future = retryFutureRef.get(); + if (future != null && !future.isCancelled()) { + boolean cancelSuccess = future.cancel(false); // 取消后续轮询(不中断当前任务) + log.info("其他打印类型,取消轮询任务:{}", cancelSuccess ? "成功" : "失败"); + } + } + + // 3. 打印成功:取消后续轮询任务 + if (isPrintSuccess) { + isLastTask = true; + ScheduledFuture future = retryFutureRef.get(); + if (future != null && !future.isCancelled()) { + future.cancel(false); // 不中断当前任务,仅取消后续任务 + } return; } - boolean success = data.getBool("status", false); - if (entity.getFailFlag() == 0 && success) { - entity.setFailFlag(0); - entity.setRespMsg("打印成功"); - entity.setPrintTime(entity.getCreateTime()); - } else if (entity.getFailFlag() == 1 && success) { - entity.setFailFlag(0); - entity.setPrintTime(DateUtil.date().toLocalDateTime()); - entity.setRespMsg("打印成功"); - // 如果设备在线 and 休眠5秒后查询结果是未打印,即视为设备已离线,云端3分钟后才会同步到离线信息 - } else if (entity.getFailFlag() == 0 && !success) { - entity.setFailFlag(1); - entity.setPrintTime(null); - entity.setRespMsg("0_离线(设备上线后自动补打)"); - } else { - entity.setFailFlag(1); - entity.setPrintTime(null); - entity.setRespMsg(StrUtil.concat(true, "打印失败,", "_", entity.getRespMsg())); + + // 4. 达到最大重试次数:取消后续轮询任务 + if (currentTimes >= maxRetryTimes) { + isLastTask = true; + ScheduledFuture future = retryFutureRef.get(); + if (future != null && !future.isCancelled()) { + future.cancel(false); + } + } + + } catch (Exception e) { + log.error("第{}次打印机状态查询异常(虚拟线程:{})", + currentTimes, Thread.currentThread().getName(), e); + // 异常时达到最大重试次数,同样取消任务 + if (currentTimes >= maxRetryTimes) { + ScheduledFuture future = retryFutureRef.get(); + if (future != null && !future.isCancelled()) { + boolean cancelSuccess = future.cancel(false); + log.info("查询异常且达到最大重试次数,取消轮询任务:{}", cancelSuccess ? "成功" : "失败"); + } + } + } finally { + //仅当是最后一次任务时,才执行更新操作 + if (isLastTask) { + super.updateById(entity); } } - // 飞鹅云打印机 - } else if ("飞鹅".equals(config.getContentType())) { - ThreadUtil.safeSleep(1000 * 5); - Boolean success = checkFPrintStatus(entity.getTaskId()); - if (success == null) { - entity.setFailFlag(1); - entity.setRespMsg("打印失败,未知错误"); - } else if (success) { - entity.setFailFlag(0); - entity.setPrintTime(DateUtil.date().toLocalDateTime()); - entity.setRespMsg("打印成功"); + }; + + // 修正:统一使用scheduleAtFixedRate,避免任务重复执行 + // 首次延迟10秒执行,后续每隔30秒执行一次(符合原逻辑的首次查询延迟,后续轮询间隔) + ScheduledFuture retryFuture = virtualThreadScheduler.scheduleAtFixedRate( + printQueryTask, + 10, + 30, + TimeUnit.SECONDS + ); + + // 修正:先赋值AtomicReference,再让任务可能执行,避免线程安全隐患 + retryFutureRef.set(retryFuture); + // 修正:关闭钩子仅注册一次(通过静态代码块或类初始化时注册,避免重复注册) + registerShutdownHookOnce(); + } + + /** + * 统一更新打印日志实体 + * + * @param entity 打印日志实体 + * @param isPrintSuccess 是否打印成功 + */ + private void updatePrintLogEntity(PrintMachineLog entity, boolean isPrintSuccess) { + if (isPrintSuccess) { + entity.setFailFlag(0); + entity.setRespMsg("打印成功"); + entity.setPrintTime(entity.getFailFlag() == 0 ? entity.getCreateTime() : DateUtil.date().toLocalDateTime()); + } else { + entity.setFailFlag(1); + entity.setPrintTime(null); + if (entity.getFailFlag() == 0) { + entity.setRespMsg("0_离线(设备上线后自动补打)"); } else { - String msg = checkOnline(entity.getAddress()); - if (msg.indexOf("在线,工作状态正常") > 0) { - entity.setFailFlag(0); - entity.setPrintTime(DateUtil.date().toLocalDateTime()); - entity.setRespMsg("打印成功"); - } else { - entity.setFailFlag(1); - entity.setPrintTime(null); - entity.setRespMsg(StrUtil.concat(true, "打印失败,", "_", msg)); + entity.setRespMsg(StrUtil.concat(true, "打印失败,", "_", entity.getRespMsg())); + } + } + } + + +// public void checkPrintStatus(PrintMachine config, PrintMachineLog entity) { +// // 云想印 +// if ("云想印".equals(config.getContentType())) { +// String jsonStr = yxyPrinter.checkPrintStatus(config.getAddress(), entity.getTaskId()); +// log.info("云想印打印状态查询结果:{}", jsonStr); +// JSONObject resp = JSONObject.parseObject(jsonStr); +// int code = resp.getIntValue("code"); +// if (code == 0) { +// JSONObject data = resp.getJSONObject("data"); +// boolean status = data.containsKey("status"); +// if (!status) { +// return; +// } +// boolean success = data.getBooleanValue("status", false); +// if (entity.getFailFlag() == 0 && success) { +// entity.setFailFlag(0); +// entity.setRespMsg("打印成功"); +// entity.setPrintTime(entity.getCreateTime()); +// } else if (entity.getFailFlag() == 1 && success) { +// entity.setFailFlag(0); +// entity.setPrintTime(DateUtil.date().toLocalDateTime()); +// entity.setRespMsg("打印成功"); +// // 如果设备在线 and 休眠5秒后查询结果是未打印,即视为设备已离线,云端3分钟后才会同步到离线信息 +// } else if (entity.getFailFlag() == 0 && !success) { +// entity.setFailFlag(1); +// entity.setPrintTime(null); +// entity.setRespMsg("0_离线(设备上线后自动补打)"); +// } else { +// entity.setFailFlag(1); +// entity.setPrintTime(null); +// entity.setRespMsg(StrUtil.concat(true, "打印失败,", "_", entity.getRespMsg())); +// } +// } +// // 飞鹅云打印机 +// } else if ("飞鹅".equals(config.getContentType())) { +// Boolean success = feiPrinter.checkFPrintStatus(entity.getTaskId()); +// if (success == null) { +// entity.setFailFlag(1); +// entity.setRespMsg("打印失败,未知错误"); +// } else if (success) { +// entity.setFailFlag(0); +// entity.setPrintTime(DateUtil.date().toLocalDateTime()); +// entity.setRespMsg("打印成功"); +// } else { +// String msg = feiPrinter.checkOnline(entity.getAddress()); +// if (msg.indexOf("在线,工作状态正常") > 0) { +// entity.setFailFlag(0); +// entity.setPrintTime(DateUtil.date().toLocalDateTime()); +// entity.setRespMsg("打印成功"); +// } else { +// entity.setFailFlag(1); +// entity.setPrintTime(null); +// entity.setRespMsg(StrUtil.concat(true, "打印失败,", "_", msg)); +// } +// } +// } +// super.updateById(entity); +// } + + // 静态标识,确保关闭钩子仅注册一次 + private static volatile boolean shutdownHookRegistered = false; + // 锁对象,保证线程安全 + private static final Object HOOK_LOCK = new Object(); + + /** + * 统一注册JVM关闭钩子(仅执行一次) + */ + private void registerShutdownHookOnce() { + if (!shutdownHookRegistered) { + synchronized (HOOK_LOCK) { + // 双重校验锁,避免多线程下重复注册 + if (!shutdownHookRegistered) { + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + if (virtualThreadScheduler != null && !virtualThreadScheduler.isShutdown()) { + virtualThreadScheduler.shutdown(); + try { + if (!virtualThreadScheduler.awaitTermination(10, TimeUnit.SECONDS)) { + log.warn("虚拟线程调度器10秒内未关闭,强制关闭..."); + virtualThreadScheduler.shutdownNow(); + } + } catch (InterruptedException e) { + log.error("等待虚拟线程调度器终止时被中断,强制关闭", e); + virtualThreadScheduler.shutdownNow(); + Thread.currentThread().interrupt(); // 保留中断状态 + } + } + }, "PrinterScheduler-ShutdownHook")); + shutdownHookRegistered = true; } } } - super.updateById(entity); } }