打印移动到order模块

This commit is contained in:
张松
2025-02-26 17:32:55 +08:00
parent 48e3ab425b
commit d7c4f05960
25 changed files with 264 additions and 120 deletions

View File

@@ -0,0 +1,14 @@
package com.czg.service.order.mapper;
import com.mybatisflex.core.BaseMapper;
import com.czg.order.entity.MqLog;
/**
* 映射层。
*
* @author zs
* @since 2025-02-21
*/
public interface MqLogMapper extends BaseMapper<MqLog> {
}

View File

@@ -0,0 +1,165 @@
package com.czg.service.order.print;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
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 jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
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.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
/**
* @author Administrator
*/
@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/";
// 飞鹅云 API 账号
private static final String USER = "chaozhanggui2022@163.com";
private static final String UKEY = "UfWkhXxSkeSSscsU";
public FeiPrinter() {
super("fePrinter");
}
private final PrintSignLabel printSignLabel = new PrintSignLabel()
.setBr("<BR>")
.setCut("")
.setF(new String[]{"<F>", "</F>"})
.setL(new String[]{"<L>", "</L>"})
.setS(new String[]{"", ""})
.setF(new String[]{"<B>", "</B>"})
.setQr(new String[]{"<QR>", "</QR>"})
.setCenter(new String[]{"<CB>", "</CB>"})
.setBold(new String[]{"<BOLD>", "</BOLD>"});
@Override
public PrintSignLabel getSignLabelInfo() {
return printSignLabel;
}
@Override
protected void normalDishesPrint(OrderInfo orderInfo, OrderDetail orderDetail, PrintMachine machine) {
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());
sendPrintRequest(machine.getAddress(), content, null,"1");
// shopPrintLogService.save(machine, "新订单", resp[0], resp[1]); // 可以解开注释用于日志存储
}
@Override
protected void returnDishesPrint(OrderInfo orderInfo, OrderDetail orderDetail, PrintMachine machine) {
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());
sendPrintRequest(machine.getAddress(), content, null,"1");
}
@Override
protected void returnOrderPrint(OrderInfo orderInfo, PrintMachine machine, String balance, List<OrderDetail> detailList) {
ShopInfo shopInfo = shopInfoService.getById(orderInfo.getShopId());
String printerNum = "1";
if (StrUtil.isNotBlank(machine.getPrintQty())) {
printerNum = machine.getPrintQty().split("\\^")[1];
}
PrintInfoDTO printInfoDTO = new PrintInfoDTO().setShopName(shopInfo.getShopName())
.setPrintType("普通打印").setPickupNum(getPickupNum(orderInfo))
.setOrderNo(orderInfo.getOrderNo()).setTradeDate(DateUtil.date().toDateStr()).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("结算单")
.setRemark(orderInfo.getRemark()).setDiscountAmount(orderInfo.getOriginAmount().subtract(orderInfo.getPayAmount()).toPlainString());
String string = buildOrderPrintData(printInfoDTO, detailList);
sendPrintRequest(machine.getAddress(), string, null, printerNum);
}
@Override
protected void normalOrderPrint(OrderInfo orderInfo, PrintMachine machine, String balance, List<OrderDetail> detailList) {
ShopInfo shopInfo = shopInfoService.getById(orderInfo.getShopId());
String printerNum = "1";
if (StrUtil.isNotBlank(machine.getPrintQty())) {
printerNum = machine.getPrintQty().split("\\^")[1];
}
PrintInfoDTO printInfoDTO = new PrintInfoDTO().setShopName(shopInfo.getShopName())
.setPrintType("普通打印").setPickupNum(getPickupNum(orderInfo))
.setOrderNo(orderInfo.getOrderNo()).setTradeDate(DateUtil.date().toDateStr()).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("结算单")
.setRemark(orderInfo.getRemark()).setDiscountAmount(orderInfo.getOriginAmount().subtract(orderInfo.getPayAmount()).toPlainString());
String string = buildOrderPrintData(printInfoDTO, detailList);
sendPrintRequest(machine.getAddress(), string, null, printerNum);
// shopPrintLogService.save(machine, "结算单", resp[0], resp[1]);
}
@Override
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 resp = buildCallTicketData(shopName, tableName, callNum, preNum, codeUrl,shopNote, takeTime);
sendPrintRequest(machine.getAddress(), resp, voiceJson, "1");
// shopPrintLogService.save(machine, "叫号单", data, resp);
}
/**
* 生成签名
*/
private static String signature(String timeStamp) {
return DigestUtils.sha1Hex(FeiPrinter.USER + FeiPrinter.UKEY + timeStamp);
}
@Override
public <R> R sendPrintRequest(String address, String metaPrintData, String voiceData, String printerNum) {
log.info("飞蛾打印机开始发送打印请求, 设备地址: {}, 元数据: {}", address, metaPrintData);
String time = String.valueOf(System.currentTimeMillis() / 1000);
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add("user", USER);
formData.add("stime", time);
formData.add("sig", signature(time));
formData.add("apiname", "Open_printMsg");
formData.add("sn", address);
formData.add("content", metaPrintData);
formData.add("times", printerNum == null ? "1" : printerNum);
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
String result = restTemplate.postForObject(URL, requestEntity, String.class);
log.info("打印结果: {}", result);
return (R) result;
} catch (Exception e) {
log.error("打印请求失败: {}", e.getMessage());
throw new RuntimeException("飞蛾打印请求失败", e);
}
}
}

View File

@@ -0,0 +1,42 @@
package com.czg.service.order.print;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;
/**
* 打印机责任链初始化
* @author Administrator
*/
@Configuration
public class PrintConfig {
@Resource
private List<PrinterHandler> printers;
// 初始化责任链
@PostConstruct
public void initChain() {
// 检查打印处理器列表是否为空
if (printers != null && !printers.isEmpty()) {
for (int i = 0; i < printers.size() - 1; i++) {
// 设置当前处理器的下一个处理器
printers.get(i).setNextPrinter(printers.get(i + 1));
}
}
}
@Bean
@Primary
public PrinterHandler printerHandler() {
// 返回责任链的起始处理器
return printers.getFirst();
}
}

View File

@@ -0,0 +1,428 @@
package com.czg.service.order.print;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.czg.account.dto.PrintOrderDetailDTO;
import com.czg.account.entity.*;
import com.czg.account.service.CallQueueService;
import com.czg.account.service.CallTableService;
import com.czg.account.service.ShopInfoService;
import com.czg.account.service.ShopUserService;
import com.czg.config.RedisCst;
import com.czg.order.entity.OrderDetail;
import com.czg.order.entity.OrderInfo;
import com.czg.order.service.OrderDetailService;
import com.czg.order.service.OrderInfoService;
import com.czg.product.entity.ProdSku;
import com.czg.product.entity.Product;
import com.czg.product.service.ProdSkuService;
import com.czg.product.service.ProductService;
import com.czg.resp.CzgResult;
import com.czg.service.RedisService;
import com.czg.service.order.enums.OrderStatusEnums;
import com.czg.system.dto.SysParamsDTO;
import com.czg.system.service.SysParamsService;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource;
import lombok.Data;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author Administrator
*/
@Slf4j
@ToString
public abstract class PrinterHandler {
@Setter
protected PrinterHandler nextPrinter;
protected String printerBrand;
@Resource
protected OrderDetailService orderDetailService;
@Resource
protected OrderInfoService orderInfoService;
@Resource
protected RedisService redisService;
@DubboReference
protected SysParamsService sysParamsService;
@DubboReference
protected ShopUserService shopUserService;
@DubboReference
protected CallQueueService callQueueService;
@DubboReference
protected CallTableService callTableService;
@DubboReference
protected ShopInfoService shopInfoService;
@DubboReference
protected ProductService productService;
@DubboReference
protected ProdSkuService prodSkuService;
@Data
@Accessors(chain = true)
public static class PrintDetailInfo {
private boolean isPrint;
// private boolean isReturn;
private long orderId;
private long detailId;
private BigDecimal printNum;
private BigDecimal printReturnNum;
}
@Data
@Accessors(chain = true)
public static class PrintInfoDTO {
// 打印标题 退款单/结算单
private String printTitle;
private String shopName;
private String printType;
private String pickupNum;
private String orderNo;
private String tradeDate;
private String operator;
private String payAmount;
private String originalAmount;
private String balance;
private String payType;
private String integral;
private String remark;
private String outNumber;
private String discountAmount;
private String discountRadio;
// 是否退款单
private boolean isReturn;
@Data
@Accessors(chain = true)
public static class DetailInfo {
private String num;
private String productName;
private String amount;
private String skuName;
private String prodGroupInfo;
}
}
public PrinterHandler(String printerBrand) {
this.printerBrand = printerBrand;
}
public void handleRequest(PrintMachine machine, OrderInfo orderInfo, Long callQueueId) {
if (canHandleRequest(machine.getContentType(), machine.getConnectionType())) {
log.info("打印机: {}, 订单信息: {}", machine.getName(), orderInfo);
print(machine, orderInfo, callQueueId);
} else if (nextPrinter != null) {
log.info("当前打印机无法处理: {},将请求传递给下一个打印机:{}...", this.printerBrand, nextPrinter.printerBrand);
nextPrinter.handleRequest(machine, orderInfo, callQueueId);
} else {
log.warn("未找到匹配打印机");
}
}
boolean canHandleRequest(String currentBrand, String connectType) {
log.info("handle判断是否可处理: {}, 连接类型: {}, handler类型: {}", currentBrand, connectType, printerBrand);
if (StrUtil.isBlank(printerBrand)) {
throw new RuntimeException("打印机品牌未赋值");
}
return printerBrand.equals(currentBrand) && "network".equals(connectType);
}
protected List<OrderDetail> getCanPrintOrderDetails(boolean partPrint, Long orderId, List<OrderDetail> tbOrderDetailList, List<?> categoryIds) {
List<Long> productIds = tbOrderDetailList.stream().map(OrderDetail::getProductId).collect(Collectors.toList());
Map<Long, Boolean> canPrintProMap = partPrint || categoryIds.isEmpty() ? new HashMap<>() :
productService.list(new QueryWrapper().in(Product::getCategoryId, categoryIds).in(Product::getId, productIds))
.stream().collect(Collectors.toMap(Product::getId, i -> true));
ArrayList<OrderDetail> orderDetails = new ArrayList<>();
tbOrderDetailList.forEach(item -> {
Object o = redisService.get(RedisCst.getPrintOrderDetailKey(orderId, item.getId()));
if (item.getIsPrint() != null && item.getIsPrint() == 1 && (canPrintProMap.get(item.getProductId()) != null || !partPrint)) {
item.setReturnNum(item.getReturnNum() == null ? BigDecimal.ZERO : item.getReturnNum());
PrintDetailInfo printDetailInfo = o != null ? JSONObject.parseObject((String) o, PrintDetailInfo.class) : null;
if (printDetailInfo != null) {
if (item.getNum().compareTo(printDetailInfo.getPrintNum()) <= 0) {
log.info("此菜品已打印, {} {} {}", item.getProductName(), item.getSkuName(), printDetailInfo);
}
if (item.getReturnNum().compareTo(printDetailInfo.getPrintReturnNum()) <= 0) {
log.info("此退菜菜品已打印, {} {} {}", item.getProductName(), item.getSkuName(), printDetailInfo);
}
item.setNum(item.getNum().subtract(printDetailInfo.getPrintNum()));
item.setReturnNum(item.getReturnNum().subtract(printDetailInfo.getPrintReturnNum()));
orderDetails.add(item);
}else {
orderDetails.add(item);
}
} else {
log.info("此菜品不在打印分类或属于免打, {} {} {}", item.getProductName(), item.getSkuName(), item.getId());
}
});
return orderDetails;
}
protected void print(PrintMachine machine, OrderInfo orderInfo, Long callQueueId) {
String printMethod = machine.getPrintMethod();
if (StrUtil.isBlank(printMethod) && StrUtil.isBlank(machine.getPrintType())) {
throw new RuntimeException("打印机配置为空");
}
// 订单打印
if (StrUtil.isNotBlank(printMethod) && !"queue".equals(printMethod)) {
log.info("准备开始打印订单或菜品单");
// 查询订单详情
List<OrderDetail> orderDetailList = orderDetailService.list(new QueryWrapper().eq(OrderDetail::getOrderId, orderInfo.getId()));
//仅打印后厨一菜一品
switch (printMethod) {
case "one" -> onlyKitchen(machine, orderInfo, orderDetailList);
case "normal" ->
//仅打印前台
onlyFrontDesk(machine, orderInfo, orderDetailList);
case "all" -> {
//全部打印 前台+后厨
onlyFrontDesk(machine, orderInfo, orderDetailList);
onlyKitchen(machine, orderInfo, orderDetailList);
}
default -> log.warn("未知打印类型: {}", printMethod);
}
} else {
log.info("准备开始打印叫号单");
if (StrUtil.isBlank(machine.getPrintType())) {
return;
}
JSONArray options = JSONArray.parseArray(machine.getPrintType());
if (options == null || options.isEmpty()) {
log.warn("打印机: {}, 未配置: print_type", machine.getId());
return;
}
//是否包含排队取号
if (!options.contains("queue")) {
log.warn("打印机: {}, 此打印机不包含排队叫号打印", machine.getId());
return;
}
// if (printDTO == null) {
// return;
// }
onlyCallNumPrint(machine, callQueueId);
}
}
/**
* 仅打印制作单「厨房」
*/
private void onlyKitchen(PrintMachine machine, OrderInfo orderInfo, List<OrderDetail> tbOrderDetailList) {
// 判断订单是否是先付费或者已结算
if (!"after-pay".equals(orderInfo.getPayMode()) && (OrderStatusEnums.UNPAID.getCode().equals(orderInfo.getStatus()) || OrderStatusEnums.CANCELLED.getCode().equals(orderInfo.getStatus()))) {
log.warn("此订单未支付, 订单信息: {}", orderInfo);
return;
}
List<?> categoryIds = JSONObject.parseObject(StrUtil.emptyToDefault(machine.getCategoryIds(), "[]"), List.class);
if (StrUtil.isEmpty(machine.getClassifyPrint())) {
log.error("分类打印是空, classifyPrint: {}", machine.getClassifyPrint());
return;
}
// 已打印详情信息
Map<Long, OrderDetail> detailMap = tbOrderDetailList.stream().map(item -> BeanUtil.copyProperties(item, OrderDetail.class)).collect(Collectors.toMap(OrderDetail::getId, i -> i));
tbOrderDetailList = getCanPrintOrderDetails("1".equals(machine.getClassifyPrint()), orderInfo.getId(), tbOrderDetailList, categoryIds);
tbOrderDetailList.parallelStream().filter(o -> ObjectUtil.defaultIfNull(o.getIsPrint(), 0) == 1).forEach(item -> {
log.info("开始打印菜品,商品名:{}", item.getProductName());
Integer isWaitCall = ObjectUtil.defaultIfNull(item.getIsWaitCall(), 0);
if (isWaitCall == 1) {
if (!item.getProductName().contains("【等叫】")) {
item.setProductName("【等叫】" + item.getProductName());
}
}
Integer isTemporary = ObjectUtil.defaultIfNull(item.getIsTemporary(), 0);
if (isTemporary == 1) {
item.setProductId(0L);
item.setSkuId(0L);
if (!item.getProductName().contains("【临】")) {
item.setProductName("【临】" + item.getProductName());
}
}
boolean isGift = item.getPackAmount().compareTo(BigDecimal.ZERO) == 0;
if (isGift && !item.getProductName().contains("【赠】")) {
item.setProductName("【赠】" + item.getProductName());
}
// 台位费不打印
if (item.getProductId().equals(-999L)) {
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);
}
if (item.getNum().compareTo(BigDecimal.ZERO) > 0) {
normalDishesPrint(orderInfo, item, machine);
}
// 保存已打印信息
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())
.setPrintNum(orderDetail.getNum()).setPrintReturnNum(orderDetail.getReturnNum())), 3600 * 24);
});
}
/**
* 仅打印结算单「前台」
*/
private void onlyFrontDesk(PrintMachine machine, OrderInfo orderInfo, List<OrderDetail> tbOrderDetailList) {
// 判断订单是否是先付费或者已结算
if (OrderStatusEnums.UNPAID.getCode().equals(orderInfo.getStatus()) || OrderStatusEnums.CANCELLED.getCode().equals(orderInfo.getStatus())) {
log.warn("此订单未支付或已取消, 订单信息: {}", orderInfo);
return;
}
if (tbOrderDetailList.isEmpty()) {
log.info("待打印列表为空");
return;
}
List<PrintOrderDetailDTO> detailList = new ArrayList<>();
tbOrderDetailList.forEach(it -> {
Integer isTemporary = ObjectUtil.defaultIfNull(it.getIsTemporary(), 0);
String remark = "";
if (isTemporary == 0) {
ProdSku prodSku = prodSkuService.getById(it.getSkuId());
if (ObjectUtil.isNotEmpty(prodSku) && ObjectUtil.isNotEmpty(prodSku.getSpecInfo())) {
remark = prodSku.getSpecInfo();
}
}
Integer isWaitCall = ObjectUtil.defaultIfNull(it.getIsWaitCall(), 0);
if (isWaitCall == 1) {
it.setProductName("【等叫】%s".formatted(it.getProductName()));
}
if (isTemporary == 1) {
it.setProductName("【临】%s".formatted(it.getProductName()));
}
it.setPackAmount(it.getPackAmount() == null ? BigDecimal.ZERO : it.getPackAmount());
boolean isGift = it.getPackAmount().compareTo(BigDecimal.ZERO) == 0;
if (isGift) {
it.setProductName("【赠】%s".formatted(it.getProductName()));
}
BigDecimal unitPrice = it.getPrice();
if (isGift) {
unitPrice = BigDecimal.ZERO;
}
PrintOrderDetailDTO detail = new PrintOrderDetailDTO(it.getProductName(), it.getNum().toString(), unitPrice.stripTrailingZeros().toPlainString(), remark, it.getProGroupInfo());
detailList.add(detail);
});
String balance = "0";
if ("deposit".equals(orderInfo.getPayType())) {
ShopUser user = shopUserService.getOne(new QueryWrapper().eq(ShopUser::getShopId, orderInfo.getShopId()).eq(ShopUser::getUserId, orderInfo.getUserId()));
if (ObjectUtil.isNotEmpty(user) && ObjectUtil.isNotEmpty(user.getAmount())) {
balance = user.getAmount().toPlainString();
}
}
if (!detailList.isEmpty()) {
if (isReturn(orderInfo)) {
returnOrderPrint(orderInfo, machine, balance, tbOrderDetailList);
} else {
normalOrderPrint(orderInfo, machine, balance, tbOrderDetailList);
}
}
}
/**
* 打印排队小票
*/
private void onlyCallNumPrint(PrintMachine machine, Long callQueueId) {
if (callQueueId == null) {
log.warn("打印叫号小票失败id为空");
}
CallQueue queue = callQueueService.getById(callQueueId);
if (queue == null) {
log.warn("叫号记录不存在");
return;
}
CzgResult<SysParamsDTO> paramsByCode = sysParamsService.getParamsByCode("call_page_url");
SysParamsDTO params = paramsByCode.getData();
String callUrl = null;
if (params != null && StrUtil.isNotBlank(params.getParamValue())) {
callUrl = params.getParamValue();
}
CallTable tbCallTable = callTableService.getById(queue.getCallTableId());
ShopInfo shopInfo = shopInfoService.getById(queue.getShopId());
callNumPrint(machine, queue.getCallNum(), shopInfo.getShopName(), tbCallTable.getName(), tbCallTable.getNote(), String.valueOf(callQueueService.count(new QueryWrapper()
.eq(CallQueue::getShopId, queue.getShopId())
.eq(CallQueue::getCallTableId, queue.getCallTableId())
.lt(CallQueue::getId, queue.getId())
.in(CallQueue::getState, 0, 1))), callUrl == null ? "未配置页面" : StrUtil.format(callUrl, queue.getShopId(), queue.getId()), queue.getCreateTime(),
StrUtil.format("过号顺延{}桌 {}桌后需重新排号 谢谢理解!", tbCallTable.getPostponeNum(), tbCallTable.getPostponeNum()));
}
/**
* 获取取餐号
*/
protected static String getPickupNum(OrderInfo orderInfo) {
return StrUtil.isBlank(orderInfo.getTableName()) ? orderInfo.getTakeCode() : orderInfo.getTableName();
}
/**
* 根据订单信息判断是否是退单
*
* @param orderInfo 顶动感信息
* @return 是否退款
*/
protected static boolean isReturn(OrderInfo orderInfo) {
return ArrayUtil.contains(new String[]{"refunding", "part-refund", "refund"}, orderInfo.getStatus());
}
protected abstract void normalDishesPrint(OrderInfo orderInfo, OrderDetail orderDetail, PrintMachine machine);
protected abstract void returnDishesPrint(OrderInfo orderInfo, OrderDetail orderDetail, PrintMachine machine);
protected abstract void returnOrderPrint(OrderInfo orderInfo, PrintMachine machine, String balance, List<OrderDetail> detailList);
protected abstract void normalOrderPrint(OrderInfo orderInfo, PrintMachine machine, String balance, List<OrderDetail> detailList);
protected abstract void callNumPrint(PrintMachine machine, String callNum, String shopName, String tableName, String tableNote, String preNum,
String codeUrl, LocalDateTime takeTime, String shopNote);
}

View File

@@ -0,0 +1,480 @@
package com.czg.service.order.print;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.czg.order.entity.OrderDetail;
import lombok.Data;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* 打印机具体打印方法接口,提供打印元数据获取,具体打印机实现对应方法即可
*
* @author Administrator
*/
public interface PrinterImpl {
@Data
@Accessors(chain = true)
class PrintSignLabel {
private String[] center;
private String[] centerBold;
private String[] bold;
private String br;
private String[] s;
private String[] f;
private String[] l;
private String[] qr;
private String cut;
private String out;
public String getOut(int num) {
if (out != null) {
return StrUtil.format("<{}:{}>", out, num);
}
return "";
}
}
/**
* 构建订单打印元数据
* @param printInfoDTO 打印信息
* @param detailList 订单详情内容
* @return 元数据
*/
// String buildOrderPrintData(PrinterHandler.PrintInfoDTO printInfoDTO, List<OrderDetail> detailList);
/**
* 发送打印请求
*
* @param address 打印地址
* @param metaPrintData 打印元数据
* @param voiceData 语音信息
* @param printNum 打印联数
* @param <R> 返回数据类型
* @return 打印结果
*/
<R> R sendPrintRequest(String address, String metaPrintData, String voiceData, String printNum);
/**
* 获取当前打印机标签信息
*
* @return 标签信息
*/
PrintSignLabel getSignLabelInfo();
/**
* 获取对齐后的小票明细行数据
* 例如58mm 机器一行打印16个汉字/32个字母80mm机器打印24个汉字/48个字母。
*
* @param title 品名
* @param price 单价
* @param num 数量
* @param total 小计
* @param b1 品名占用字节数
* @param b2 单价占用字节数
* @param b3 数量占用字节数
* @param b4 小计占用字节数
* @return 格式化后的行数据(带<S>和<BR>标签)
*/
default String getRow(String title, String price, String num, String total,
int b1, int b2, int b3, int b4) {
PrintSignLabel signLabelInfo = getSignLabelInfo();
price = addSpace(price, b2);
num = addSpace(num, b3);
total = addSpace(total, b4);
String otherStr = " %s%s %s".formatted(price, num, total);
int titleByteLen = StrUtil.bytes(title, CharsetUtil.CHARSET_GBK).length;
StringBuilder sb = new StringBuilder();
if (titleByteLen <= b1) {
// 品名可以在一行显示,直接补足空格
title = titleAddSpace(title, b1);
sb.append(getFormatLabel(title + otherStr, signLabelInfo.s));
// sb.append(startSplitSign).append(title).append(otherStr).append(endSplitSign);
} else {
// 品名超出一行,进行拆分
List<String> lines = isEn(title)
// 英文按 b1 个字符拆分
? getStrList(title, b1)
// 中文按 b1/2 个字符拆分
: getStrList(title, b1 / 2);
// 第一行拼接其它字段
String firstLine = titleAddSpace(lines.getFirst(), b1);
sb.append(getFormatLabel(firstLine + otherStr, signLabelInfo.s)).append(signLabelInfo.br);
// sb.append(startSplitSign).append(firstLine).append(otherStr).append(endSplitSign).append("<BR>");
// 剩余的行单独换行输出
for (int i = 1; i < lines.size(); i++) {
sb.append(getFormatLabel(lines.get(i), signLabelInfo.s)).append(signLabelInfo.br);
// sb.append(startSplitSign).append(lines.get(i)).append(endSplitSign).append("BR>");
}
}
sb.append("<BR>");
return sb.toString();
}
default String getFormatLabel(String text, String[]... labels) {
StringBuilder str = new StringBuilder();
for (String[] label : labels) {
str.append(label[0]);
}
str.append(text);
for (int i = labels.length - 1; i >= 0; i--) {
str.append(labels[i][1]);
}
return str.toString();
}
/**
* 构建订单打印元数据
*
* @param printInfoDTO 打印信息
* @param detailList 订单详情
* @return 订单打印元数据
*/
default String buildOrderPrintData(PrinterHandler.PrintInfoDTO printInfoDTO, List<OrderDetail> detailList) {
PrintSignLabel signLabelInfo = getSignLabelInfo();
StringBuilder data = new StringBuilder();
data.append(getFormatLabel(printInfoDTO.getShopName(), signLabelInfo.center, signLabelInfo.f)).append(signLabelInfo.br);
// data.append(StrUtil.format("<C><F>{}</F></C><BR>", printInfoDTO.getShopName()));
// data.append("<BR>");
data.append(signLabelInfo.br);
// data.append("<OUT:30>");
data.append(getFormatLabel(StrUtil.format("{}【{}】", printInfoDTO.getPrintTitle(), printInfoDTO.getPickupNum()), signLabelInfo.center, signLabelInfo.bold))
.append(signLabelInfo.br)
.append(signLabelInfo.br);
// data.append(StrUtil.format("<C><BOLD>{}【{}】</BOLD></C><BR>", printInfoDTO.getPrintTitle(), printInfoDTO.getPickupNum()));
//if (Objects.nonNull(printInfoDTO.getOutNumber())) {
// data.append(StrUtil.format("<CB><BOLD>{}</BOLD></CB>",printInfoDTO.getOutNumber()));
//}
// data.append("<OUT:30>");
// data.append("<BR>");
data.append(getFormatLabel(StrUtil.format("订单号:{}", printInfoDTO.getOrderNo()), signLabelInfo.s))
.append(signLabelInfo.br);
// data.append(StrUtil.format("<S>订单号:{}</S><BR>", printInfoDTO.getOrderNo()));
data.append(getFormatLabel(StrUtil.format("交易时间:{}", printInfoDTO.getTradeDate()), signLabelInfo.s))
.append(signLabelInfo.br);
// data.append(StrUtil.format("<S>交易时间:{}</S><BR>", printInfoDTO.getTradeDate()));
data.append(getFormatLabel(StrUtil.format("收银员:{}", printInfoDTO.getOperator()), signLabelInfo.s))
.append(signLabelInfo.br);
// data.append(StrUtil.format("<S>收银员:{}</S><BR>", printInfoDTO.getOperator()));
data.append(signLabelInfo.br);
data.append(signLabelInfo.br);
// data.append("<OUT:15>");
// data.append("<BR>");
data.append(getFormatLabel("品名 数量 小计", signLabelInfo.s))
.append(signLabelInfo.br);
// data.append("<S>品名 数量 小计</S><BR>");
data.append(getFormatLabel("--------------------------------", signLabelInfo.s))
.append(signLabelInfo.br);
// data.append("<S>--------------------------------</S><BR>");
for (OrderDetail detail : detailList) {
String number = detail.getNum().stripTrailingZeros().toPlainString();
String row = getRow(detail.getProductName(), "", number, toPlainStr(detail.getPayAmount().stripTrailingZeros().toPlainString()), 20, 0, 3, 6);
data.append(row);
if (StrUtil.isNotBlank(detail.getSkuName())) {
data.append(getFormatLabel(StrUtil.format("规格:{}", detail.getSkuName()), signLabelInfo.s))
.append(signLabelInfo.br);
// data.append("<S>规格:").append(detail.getSkuName()).append("</S><BR>");
}
String proGroupInfo = detail.getProGroupInfo();
if (StrUtil.isBlank(proGroupInfo)) {
continue;
}
if (!JSONUtil.isTypeJSONArray(proGroupInfo)) {
continue;
}
JSONArray subItems = JSONUtil.parseArray(proGroupInfo);
for (int i = 0; i < subItems.size(); i++) {
String proName = subItems.getJSONObject(i).getStr("proName");
int qty = subItems.getJSONObject(i).getInt("number");
String subRow = getRow(" - " + proName, "", qty + ".00", "0.00", 20, 0, 3, 6);
data.append(subRow);
}
}
if (ObjectUtil.isNotNull(printInfoDTO.getDiscountAmount())) {
data.append(getFormatLabel("--------------------------------", signLabelInfo.s))
.append(signLabelInfo.br);
// data.append("<S>--------------------------------</S><BR>");
data.append(getFormatLabel(StrUtil.format("原价:{}", toPlainStr(printInfoDTO.getOriginalAmount())), signLabelInfo.s))
.append(signLabelInfo.br);
// data.append(StrUtil.format("<S>原价:{}</S><BR>", toPlainStr(printInfoDTO.getOriginalAmount())));
data.append(getFormatLabel(StrUtil.format("折扣:-{}", toPlainStr(printInfoDTO.getDiscountAmount())), signLabelInfo.s))
.append(signLabelInfo.br);
// data.append(StrUtil.format("<S>折扣:-{}</S><BR>", toPlainStr(printInfoDTO.getDiscountAmount())));
}
data.append(getFormatLabel("--------------------------------", signLabelInfo.s))
.append(signLabelInfo.br);
// data.append("<S>--------------------------------</S><BR>");
String t = "" + printInfoDTO.getPayAmount();
if (printInfoDTO.isReturn()) {
data.append(getFormatLabel(StrUtil.format("应退:{}", t), signLabelInfo.f))
.append(signLabelInfo.br);
// data.append("<F>应退").append(t).append("</F><BR>");
} else {
data.append(getFormatLabel(StrUtil.format("应收:{}", t), signLabelInfo.f))
.append(signLabelInfo.br);
// data.append(StrUtil.format("<F>应收:{}</F><BR>", t));
}
data.append(getFormatLabel("--------------------------------", signLabelInfo.s))
.append(signLabelInfo.br);
// data.append("<S>--------------------------------</S><BR>");
if (ObjectUtil.isNotEmpty(printInfoDTO.getPayType()) && ObjectUtil.isNotNull(printInfoDTO.getPayType()) && "deposit".equals(printInfoDTO.getPayType())) {
data.append(getFormatLabel(StrUtil.format("储值:{}", toPlainStr(printInfoDTO.getOriginalAmount())), signLabelInfo.s))
.append(signLabelInfo.br);
// data.append(StrUtil.format("<S>储值:{}</S><BR>", toPlainStr(printInfoDTO.getOriginalAmount())));
data.append(getFormatLabel(StrUtil.format("积分:{}", printInfoDTO.getIntegral()), signLabelInfo.s))
.append(signLabelInfo.br);
// data.append(StrUtil.format("<S>积分:{}</S><BR>", printInfoDTO.getIntegral()));
}
data.append(getFormatLabel(StrUtil.format("余额:{}", toPlainStr(printInfoDTO.getBalance())), signLabelInfo.s))
.append(signLabelInfo.br);
// data.append(StrUtil.format("<S>余额:{}</S><BR>", toPlainStr(printInfoDTO.getBalance())));
data.append(getFormatLabel("--------------------------------", signLabelInfo.s))
.append(signLabelInfo.br);
// data.append("<S>--------------------------------</S><BR>");
if (StrUtil.isNotBlank(printInfoDTO.getRemark())) {
data.append(getFormatLabel(StrUtil.format("备注:{}", printInfoDTO.getRemark()), signLabelInfo.l, signLabelInfo.bold))
.append(signLabelInfo.br);
// data.append(StrUtil.format("<L><BOLD>备注:{}</BOLD></L><BR>", printInfoDTO.getRemark()));
}
if (Objects.nonNull(printInfoDTO.getOutNumber())) {
data.append(getFormatLabel(printInfoDTO.getOutNumber(), signLabelInfo.center, signLabelInfo.qr))
.append(signLabelInfo.br);
// data.append("<QR>".concat(printInfoDTO.getOutNumber()).concat("</QR><BR>"));
}
data.append(getFormatLabel(StrUtil.format("打印时间:{}", DateUtil.date().toString()), signLabelInfo.s))
.append(signLabelInfo.br)
.append(signLabelInfo.br);
// data.append("<S>打印时间:").append(DateUtil.date().toDateStr()).append("</S><BR>");
// data.append("<OUT:180>");
data.append(signLabelInfo.getOut(180));
data.append(signLabelInfo.cut);
// data.append("<PCUT>");
return data.toString();
}
/**
* 构建菜品单打印数据
*
* @param pickupNumber 取餐号
* @param date 时间
* @param productName 商品名
* @param skuName 规格名称
* @param number 数量
* @param remark 备注
* @param proGroupInfo 套餐信息
* @return 元数据
*/
default String buildDishPrintData(boolean isReturn, String pickupNumber, String date, String productName, String skuName, BigDecimal number, String remark, String proGroupInfo) {
PrintSignLabel signLabelInfo = getSignLabelInfo();
StringBuilder builder = new StringBuilder();
if (isReturn) {
builder.append(getFormatLabel(StrUtil.format("{}【退】", pickupNumber), signLabelInfo.center, signLabelInfo.bold))
.append(signLabelInfo.br)
.append(signLabelInfo.br);
// builder.append("<C><B>").append(pickupNumber).append("【退】</B></C><BR><BR>");
} else {
builder.append(getFormatLabel(pickupNumber, signLabelInfo.center, signLabelInfo.bold))
.append(signLabelInfo.br)
.append(signLabelInfo.br);
// builder.append("<C><B>").append(pickupNumber).append("</B></C><BR><BR>");
}
builder.append(getFormatLabel(StrUtil.format("时间: {}", date), signLabelInfo.l, signLabelInfo.s))
.append(signLabelInfo.br)
.append(signLabelInfo.br);
// builder.append("<S><L>时间: ").append(date).append(" </L></S><BR><BR><BR>");
if (productName.length() > 4 || skuName.length() > 4) {
builder.append(getFormatLabel(StrUtil.format("{} x {}", productName, number.stripTrailingZeros().toPlainString()), signLabelInfo.l))
.append(signLabelInfo.br);
// builder.append("<CS:32>").append(productName).append(" x ").append(number).append("</CS><BR>");
if (StrUtil.isNotBlank(skuName)) {
builder.append(getFormatLabel(skuName, signLabelInfo.l)).append(signLabelInfo.br);
// builder.append("<CS:32>").append(skuName).append(" </CS><BR>");
}
} else {
builder.append(getFormatLabel(StrUtil.format("{} x ", productName), signLabelInfo.bold));
// builder.append("<B>").append(productName).append(" x ").append(number).append("</B><BR>");
if (StrUtil.isNotBlank(skuName)) {
builder.append(getFormatLabel(skuName, signLabelInfo.bold))
.append(signLabelInfo.br);
// builder.append("<B>").append(skuName).append(" </B><BR>");
}
}
if (StrUtil.isNotBlank(remark)) {
builder.append(signLabelInfo.br);
builder.append(getFormatLabel(StrUtil.format("备注:{}", remark), signLabelInfo.s, signLabelInfo.l))
.append(signLabelInfo.br);
// builder.append("<S><L>备注: ").append(note).append(" </L></S><BR>");
}
if (!StrUtil.isBlank(proGroupInfo) && JSONUtil.isTypeJSONArray(proGroupInfo)) {
JSONArray subItems = JSONUtil.parseArray(proGroupInfo);
for (int i = 0; i < subItems.size(); i++) {
String proName = subItems.getJSONObject(i).getStr("proName");
int qty = subItems.getJSONObject(i).getInt("number");
builder.append(StrUtil.format("({}) x ", proName))
.append(signLabelInfo.br);
// builder.append("<CS:32>(").append(i + 1).append(")").append(proName).append(" x ").append(qty).append("</CS><BR>");
}
}
builder.append(signLabelInfo.br)
.append(signLabelInfo.getOut(150))
.append(signLabelInfo.cut);
// builder.append("<OUT:150>");
// builder.append("<PCUT>");
return builder.toString();
}
/**
* 构建叫号元数据
* @param shopName 店铺名称
* @param tableName 表名
* @param callNum 号码
* @param preNum 前面还有几桌
* @param codeUrl 二维码地址
* @param shopNote 店铺备注
* @param takeTime 取号时间
* @return 元数据
*/
default String buildCallTicketData(String shopName, String tableName, String callNum, String preNum, String codeUrl, String shopNote, LocalDateTime takeTime) {
PrintSignLabel signLabelInfo = getSignLabelInfo();
StringBuilder sb = new StringBuilder();
sb.append(getFormatLabel(shopName, signLabelInfo.center, signLabelInfo.bold))
.append(signLabelInfo.br)
.append(signLabelInfo.br)
.append(getFormatLabel(StrUtil.format("{} {}", tableName, callNum), signLabelInfo.center, signLabelInfo.bold))
.append(signLabelInfo.br)
.append(getFormatLabel(StrUtil.format("前面有{}桌", preNum), signLabelInfo.center))
.append(signLabelInfo.br)
.append(getFormatLabel("怕过号扫一扫", signLabelInfo.center, signLabelInfo.bold))
.append(signLabelInfo.br)
.append(getFormatLabel(codeUrl, signLabelInfo.center, signLabelInfo.qr))
.append(signLabelInfo.br)
.append("--------------------------------").append(signLabelInfo.br)
.append(getFormatLabel(shopNote, signLabelInfo.s))
.append(signLabelInfo.br)
.append("--------------------------------").append(signLabelInfo.br)
.append(getFormatLabel(StrUtil.format("取号时间:{}", DateUtil.format(takeTime, "yyyy-MM-dd HH:mm:ss")), signLabelInfo.s))
.append(signLabelInfo.br)
.append(getFormatLabel(StrUtil.format("打印时间:{}", DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:mm:ss")), signLabelInfo.s))
.append(signLabelInfo.br)
.append(signLabelInfo.cut);
// sb.append("<C><B>").append(shopName).append("</B></C><BR><BR>")
// .append("------------------------<BR>")
// .append(StrUtil.format(StrUtil.format("<C><B>{} {}</B></C><BR>", tableName, callNum)))
// .append(StrUtil.format("<C>前面有{}桌</C><BR>", preNum))
// .append(StrUtil.format("<C><QR>{}</QR></C><BR>", codeUrl))
// .append("<C><B>怕过号扫一扫<B></C><BR>")
// .append("------------------------<BR>")
// .append(StrUtil.format("<S>{}</S><BR>", shopNote))
// .append("------------------------<BR>").append("<S>取号时间:").append(DateUtil.format(takeTime, "yyyy-MM-dd HH:mm:ss"))
// .append("</S><BR>").append("<S>打印时间:").append(DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:mm:ss")).append("</S><BR>")
// .append("<OUT:180>").append("<PCUT>");
return sb.toString();
}
/**
* 按字符数补足空格(英文、数字时使用)
*
* @param str 对应字符串
* @param size 总长度
* @return 填充之后的字符串
*/
default String addSpace(String str, int size) {
return StrUtil.fillAfter(str, ' ', size);
}
/**
* 按固定长度拆分字符串(按字符拆分)
*
* @param inputString 字符串
* @param length 每个长度
* @return 分割后的集合
*/
default List<String> getStrList(String inputString, int length) {
List<String> list = new ArrayList<>();
for (int i = 0; i < inputString.length(); i += length) {
list.add(StrUtil.sub(inputString, i, Math.min(i + length, inputString.length())));
}
return list;
}
/**
* 判断字符串是否全为英文GBK编码下每个字符均为 1 字节)
*
* @param str 字符
* @return 是否英文
*/
default boolean isEn(String str) {
return StrUtil.bytes(str, CharsetUtil.CHARSET_GBK).length == str.length();
}
/**
* 根据 GBK 字节数补空格(用于中文排版)
*
* @param str 字符串
* @param b1 总长度
* @return 添加间距的字符串
*/
default String titleAddSpace(String str, int b1) {
int byteLen = StrUtil.bytes(str, CharsetUtil.CHARSET_GBK).length;
return StrUtil.fillAfter(str, ' ', b1 - byteLen);
}
/**
* 获取填充字符串, 并且换行
*
* @param length 长度
* @param string 字符串
* @return
*/
default String getStringByEnter(String string, int length) {
PrintSignLabel signLabelInfo = getSignLabelInfo();
StringBuilder result = new StringBuilder();
while (!string.isEmpty()) {
int pos = 0, byteCount = 0;
for (int i = 0; i < string.length(); i++) {
int charByteLen = StrUtil.bytes(string.substring(i, i + 1), CharsetUtil.CHARSET_GBK).length;
if (byteCount + charByteLen > length) break;
byteCount += charByteLen;
pos = i + 1;
}
result.append(signLabelInfo.s[0]).append(string, 0, pos).append(signLabelInfo.s[1]);
string = string.substring(pos);
if (!string.isEmpty()) {
result.append("<BR>");
}
}
return result.toString();
}
/**
* 获取规格化的字符串
*
* @param str 字符串
* @return 返回规格化字符串
*/
default String toPlainStr(String str) {
if (StrUtil.isBlank(str)) {
return "0";
}
return NumberUtil.roundDown(new BigDecimal(str), 2).toPlainString();
}
}

View File

@@ -0,0 +1,201 @@
package com.czg.service.order.print;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
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.time.LocalDateTime;
import java.util.*;
/**
* 云享印打印机
*
* @author Administrator
*/
@Slf4j
@Component
public class YxyPrinter extends PrinterHandler implements PrinterImpl {
//请求地址
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";
@Resource
private ShopInfoService shopInfoService;
@Resource
private RestTemplate restTemplate;
public YxyPrinter() {
super("yxyPrinter");
}
private PrintSignLabel printSignLabel = new PrintSignLabel()
.setBr("<BR>")
.setCut("<PCUT>")
.setOut("OUT")
.setF(new String[]{"<F>", "</F>"})
.setL(new String[]{"<L>", "</L>"})
.setS(new String[]{"<S>", "</S>"})
.setQr(new String[]{"<QR>", "</QR>"})
.setCenter(new String[]{"<C>", "</C>"})
.setBold(new String[]{"<B>", "</B>"});
@Override
public PrintSignLabel getSignLabelInfo() {
return printSignLabel;
}
@Override
public <R> R sendPrintRequest(String address, String metaPrintData, String voiceData, String printNum) {
log.info("开始请求云享印,请求数据:{}, {}", voiceData, metaPrintData);
//设备名称
//行为方式 1:只打印数据 2:只播放信息 3:打印数据并播放信息
// actWay = 3;
//打印联数
// int cn = 1;
//打印内容
//播报语音数据体,字段参考文档IOT_XY_API11001
String time = String.valueOf(System.currentTimeMillis());
String uuid = UUID.randomUUID().toString();
Map<String, String> param = getToken(time, uuid);
//参数
MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
multiValueMap.add("token", param.get("TOKEN"));
multiValueMap.add("devName", address);
multiValueMap.add("actWay", 3);
multiValueMap.add("cn", printNum);
multiValueMap.add("data", metaPrintData);
multiValueMap.add("voiceJson", voiceData);
multiValueMap.add("appId", APP_ID);
multiValueMap.add("timestamp", time);
multiValueMap.add("requestId", uuid);
multiValueMap.add("userCode", USER_CODE);
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(multiValueMap, header);
String httpResponse = restTemplate.postForObject(URL_STR,
httpEntity, String.class);
log.info("请求云享印成功,响应数据: {}", httpResponse);
return (R) httpResponse;
}
@Override
protected void normalDishesPrint(OrderInfo orderInfo, OrderDetail orderDetail, PrintMachine machine) {
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());
// String voiceJson = "{\"bizType\":\"2\",\"content\":\"您有一笔新的订单,请及时处理\"}";
String voiceJson = "{\"bizType\":\"2\",\"content\":\"\"}";
sendPrintRequest(machine.getAddress(), buildDishPrintData, voiceJson, "1");
// sendPrintRequest(voiceJson, 3, 1, machine.getAddress(), data);
}
@Override
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());
// String voiceJson = "{\"bizType\":\"2\",\"content\":\"您有一笔新的订单,请及时处理\"}";
String voiceJson = "{\"bizType\":\"2\",\"content\":\"\"}";
sendPrintRequest(machine.getAddress(), buildDishPrintData, voiceJson, "1");
}
@Override
protected void returnOrderPrint(OrderInfo orderInfo, PrintMachine machine, String balance, List<OrderDetail> detailList) {
ShopInfo shopInfo = shopInfoService.getById(orderInfo.getShopId());
PrintInfoDTO printInfoDTO = new PrintInfoDTO().setShopName(shopInfo.getShopName()).setPrintType("普通打印").setPickupNum(getPickupNum(orderInfo))
.setOrderNo(orderInfo.getOrderNo()).setTradeDate(DateUtil.date().toDateStr()).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("结算单")
.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);
}
@Override
protected void normalOrderPrint(OrderInfo orderInfo, PrintMachine machine, String balance, List<OrderDetail> detailList) {
ShopInfo shopInfo = shopInfoService.getById(orderInfo.getShopId());
PrintInfoDTO printInfoDTO = new PrintInfoDTO().setShopName(shopInfo.getShopName()).setPrintType("普通打印").setPickupNum(getPickupNum(orderInfo))
.setOrderNo(orderInfo.getOrderNo()).setTradeDate(DateUtil.date().toDateStr()).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("结算单")
.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);
// shopPrintLogService.save(machine, printType, 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");
}
/**
* 获取TOKEN值
*
* @param timestamp 时间戳13位
* @param requestId 请求ID自定义
* @return token信息
*/
private static Map<String, String> getToken(String timestamp, String requestId) {
StringBuilder token = new StringBuilder();
StringBuilder encode = new StringBuilder();
SortedMap<String, Object> map = new TreeMap<>();
map.put("appId", APP_ID);
map.put("timestamp", timestamp);
map.put("requestId", requestId);
map.put("userCode", USER_CODE);
for (Map.Entry<String, Object> next : map.entrySet()) {
String key = next.getKey();
Object value = next.getValue();
token.append(key).append(value);
encode.append(key).append("=").append(value).append("&");
}
Map<String, String> finalMap = new HashMap<>();
finalMap.put("ENCODE", encode.toString());
finalMap.put("TOKEN", SecureUtil.md5(token + APP_SECRET).toUpperCase());
return finalMap;
}
}

View File

@@ -0,0 +1,18 @@
package com.czg.service.order.service.impl;
import com.czg.order.entity.MqLog;
import com.czg.order.service.MqLogService;
import com.czg.service.order.mapper.MqLogMapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* 服务层实现。
*
* @author zs
* @since 2025-02-21
*/
@Service
public class MqLogServiceImpl extends ServiceImpl<MqLogMapper, MqLog> implements MqLogService{
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.czg.service.order.mapper.MqLogMapper">
</mapper>