打印机相关实现

This commit is contained in:
张松 2025-02-25 16:54:17 +08:00
parent 5719fdef74
commit c1ff2084f6
6 changed files with 217 additions and 343 deletions

View File

@ -52,7 +52,7 @@ public class PrintMqListener {
throw new RuntimeException("订单信息不存在"); throw new RuntimeException("订单信息不存在");
} }
getPrintMachine(orderInfo.getShopId(), "cash", "normal", "order").forEach(machine -> { getPrintMachine(orderInfo.getShopId(), "cash", "one", "order").forEach(machine -> {
printerHandler.handleRequest(machine, orderInfo); printerHandler.handleRequest(machine, orderInfo);
// printPlaceTicket(isReturn, machine, orderInfo, shopInfo); // printPlaceTicket(isReturn, machine, orderInfo, shopInfo);
}); });

View File

@ -1,5 +1,6 @@
package com.czg.account.dto.print; package com.czg.account.dto.print;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.Data; import lombok.Data;
@ -23,6 +24,7 @@ public class PrinterEditDTO {
/** /**
* ip地址 * ip地址
*/ */
@NotBlank(message = "地址不为空")
private String address; private String address;
/** /**
@ -39,6 +41,7 @@ public class PrinterEditDTO {
/** /**
* 打印机品牌 * 打印机品牌
*/ */
@NotBlank(message = "打印机品牌不为空")
private String contentType; private String contentType;

View File

@ -66,12 +66,27 @@ public class FeiPrinter extends PrinterHandler implements PrinterImpl{
super("fePrinter"); 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 @Override
protected void normalDishesPrint(OrderInfo orderInfo, OrderDetail orderDetail, PrintMachine machine) { protected void normalDishesPrint(OrderInfo orderInfo, OrderDetail orderDetail, PrintMachine machine) {
String remark = orderDetail.getRemark(); String remark = orderDetail.getRemark();
String content = buildDishPrintData(getPickupNum(orderInfo), orderInfo.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), String content = buildDishPrintData(false, getPickupNum(orderInfo), orderInfo.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
orderDetail.getProductName(), orderDetail.getNum(), remark, orderDetail.getProGroupInfo()); orderDetail.getProductName(), orderDetail.getSkuName(), orderDetail.getNum(), remark, orderDetail.getProGroupInfo());
sendPrintRequest(machine.getAddress(), content, "1"); sendPrintRequest(machine.getAddress(), content, null,"1");
// shopPrintLogService.save(machine, "新订单", resp[0], resp[1]); // 可以解开注释用于日志存储 // shopPrintLogService.save(machine, "新订单", resp[0], resp[1]); // 可以解开注释用于日志存储
} }
@ -100,7 +115,7 @@ public class FeiPrinter extends PrinterHandler implements PrinterImpl{
.setOutNumber(orderInfo.getTakeCode()).setPrintTitle("结算单") .setOutNumber(orderInfo.getTakeCode()).setPrintTitle("结算单")
.setRemark(orderInfo.getRemark()).setDiscountAmount(orderInfo.getOriginAmount().subtract(orderInfo.getPayAmount()).toPlainString()); .setRemark(orderInfo.getRemark()).setDiscountAmount(orderInfo.getOriginAmount().subtract(orderInfo.getPayAmount()).toPlainString());
String string = buildOrderPrintData(printInfoDTO, detailList); String string = buildOrderPrintData(printInfoDTO, detailList);
sendPrintRequest(machine.getAddress(), string, printerNum); sendPrintRequest(machine.getAddress(), string, null, printerNum);
// shopPrintLogService.save(machine, "结算单", resp[0], resp[1]); // shopPrintLogService.save(machine, "结算单", resp[0], resp[1]);
} }
@ -116,128 +131,102 @@ public class FeiPrinter extends PrinterHandler implements PrinterImpl{
return DigestUtils.sha1Hex(FeiPrinter.USER + FeiPrinter.UKEY + timeStamp); return DigestUtils.sha1Hex(FeiPrinter.USER + FeiPrinter.UKEY + timeStamp);
} }
// @Override
@Override // public String buildOrderPrintData(PrintInfoDTO printInfoDTO, List<OrderDetail> detailList) {
public String getStartSplitSign() { // StringBuilder data = new StringBuilder();
return ""; // data.append(StrUtil.format("<CB>{}</CB><BR>", printInfoDTO.getShopName()));
} // data.append("<BR>");
// data.append(StrUtil.format("<C><BOLD>{}【{}】</C></BOLD><BR>", printInfoDTO.getPrintTitle(), printInfoDTO.getPickupNum()));
@Override // data.append("<BR>");
public String getEndSplitSign() { // data.append(StrUtil.format("订单号:{}<BR>", printInfoDTO.getOrderNo()));
return ""; // data.append(StrUtil.format("交易时间:{}<BR>", printInfoDTO.getTradeDate()));
} // data.append(StrUtil.format("收银员:{}<BR>", printInfoDTO.getOperator()));
// data.append("<BR>");
private PrintSignLabel printSignLabel = new PrintSignLabel() // data.append("品名 数量 小计<BR>");
.setBr("<BR>") // data.append("--------------------------------<BR>");
.setCut("PCUT") // for (OrderDetail detail : detailList) {
.setF(new String[]{"<F>", "</F>"}) // String productName = detail.getProductName();
.setL(new String[]{"<L>", "</L>"}) // String number = detail.getNum().stripTrailingZeros().toPlainString();
.setS(new String[]{"", ""}) // String amount = toPlainStr(detail.getPayAmount().toPlainString());
.setF(new String[]{"<B>", "</B>"}) // //58mm的机器,一行打印16个汉字,32个字母; 80mm的机器,一行打印24个汉字,48个字母
.setQr(new String[]{"<QR>", "</QR>"}) // //展示4列 b1代表名称列占用14个字节 b2单价列6个字节 b3数量列3个字节 b4金额列6个字节-->这里的字节数可按自己需求自由改写14+6+3+6再加上代码写的3个空格就是32了58mm打印机一行总占32字节
.setCenter(new String[]{"<CB>", "</CB>"}) // //String row = FeieYunUtil.getRow(productName, "",number, amount, 14, 6,3, 6)
.setBold(new String[]{"<BOLD>", "</BOLD>"}); // //展示3列 b1代表名称列占用20个字节 b2单价列0个字节 b3数量列3个字节 b4金额列6个字节-->这里的字节数可按自己需求自由改写20+0+3+6再加上代码写的3个空格就是32了58mm打印机一行总占32字节
@Override // String row = getRow(productName, "", number, amount, 20, 0, 3, 6);
public PrintSignLabel getSignLabelInfo() { // data.append(row);
return printSignLabel; // if (StrUtil.isNotBlank(detail.getSkuName())) {
} // data.append("规格:").append(detail.getSkuName()).append("<BR>");
// }
@Override // String proGroupInfo = detail.getProGroupInfo();
public String buildOrderPrintData(PrintInfoDTO printInfoDTO, List<OrderDetail> detailList) { // if (StrUtil.isBlank(proGroupInfo)) {
StringBuilder data = new StringBuilder(); // continue;
data.append(StrUtil.format("<CB>{}</CB><BR>", printInfoDTO.getShopName())); // }
data.append("<BR>"); // if (!JSONUtil.isTypeJSONArray(proGroupInfo)) {
data.append(StrUtil.format("<C><BOLD>{}【{}】</C></BOLD><BR>", printInfoDTO.getPrintTitle(), printInfoDTO.getPickupNum())); // continue;
data.append("<BR>"); // }
data.append(StrUtil.format("订单号:{}<BR>", printInfoDTO.getOrderNo())); // JSONArray subItems = JSONUtil.parseArray(proGroupInfo);
data.append(StrUtil.format("交易时间:{}<BR>", printInfoDTO.getTradeDate())); // for (int i = 0; i < subItems.size(); i++) {
data.append(StrUtil.format("收银员:{}<BR>", printInfoDTO.getOperator())); // String proName = subItems.getJSONObject(i).getStr("proName");
data.append("<BR>"); // int qty = subItems.getJSONObject(i).getInt("number");
data.append("品名 数量 小计<BR>"); // String subRow = getRow(" - %s".formatted(proName), "", "%d.00".formatted(qty), "0.00", 20, 0, 3, 6);
data.append("--------------------------------<BR>"); // data.append(subRow);
for (OrderDetail detail : detailList) { // }
String productName = detail.getProductName(); // }
String number = detail.getNum().stripTrailingZeros().toPlainString(); // if (ObjectUtil.isNotNull(printInfoDTO.getDiscountAmount())) {
String amount = toPlainStr(detail.getPayAmount().toPlainString()); // data.append("--------------------------------<BR>");
//58mm的机器,一行打印16个汉字,32个字母; 80mm的机器,一行打印24个汉字,48个字母 // data.append(StrUtil.format("原价:{}<BR>", toPlainStr(printInfoDTO.getOriginalAmount())));
//展示4列 b1代表名称列占用14个字节 b2单价列6个字节 b3数量列3个字节 b4金额列6个字节-->这里的字节数可按自己需求自由改写14+6+3+6再加上代码写的3个空格就是32了58mm打印机一行总占32字节 // data.append(StrUtil.format("折扣:-{}<BR>", toPlainStr(printInfoDTO.getDiscountAmount())));
//String row = FeieYunUtil.getRow(productName, "",number, amount, 14, 6,3, 6) // }
//展示3列 b1代表名称列占用20个字节 b2单价列0个字节 b3数量列3个字节 b4金额列6个字节-->这里的字节数可按自己需求自由改写20+0+3+6再加上代码写的3个空格就是32了58mm打印机一行总占32字节 // data.append("--------------------------------<BR>");
String row = getRow(productName, "", number, amount, 20, 0, 3, 6); // String t = "" + printInfoDTO.getPayAmount();
data.append(row); // if (printInfoDTO.isReturn()) {
if (StrUtil.isNotBlank(detail.getSkuName())) { // data.append(StrUtil.format("<B>应退:{}</B><BR>", t));
data.append("规格:").append(detail.getSkuName()).append("<BR>"); // } else {
} // data.append(StrUtil.format("<B>应收:{}</B><BR>", t));
String proGroupInfo = detail.getProGroupInfo(); // }
if (StrUtil.isBlank(proGroupInfo)) { // data.append("--------------------------------<BR>");
continue; // if (ObjectUtil.isNotEmpty(printInfoDTO.getPayType()) && ObjectUtil.isNotNull(printInfoDTO.getPayType()) && printInfoDTO.getPayType().equals("deposit")) {
} // data.append(StrUtil.format("储值:{}<BR>", toPlainStr(printInfoDTO.getOriginalAmount())));
if (!JSONUtil.isTypeJSONArray(proGroupInfo)) { // data.append(StrUtil.format("积分:{}<BR>", printInfoDTO.getIntegral()));
continue; // }
} // data.append(StrUtil.format("余额:{}<BR>", toPlainStr(printInfoDTO.getBalance())));
JSONArray subItems = JSONUtil.parseArray(proGroupInfo); // data.append("--------------------------------<BR>");
for (int i = 0; i < subItems.size(); i++) { // if (StrUtil.isNotBlank(printInfoDTO.getRemark())) {
String proName = subItems.getJSONObject(i).getStr("proName"); // data.append(StrUtil.format("<L><BOLD>备注:{}</BOLD></L><BR>", printInfoDTO.getRemark()));
int qty = subItems.getJSONObject(i).getInt("number"); // }
String subRow = getRow(" - %s".formatted(proName), "", "%d.00".formatted(qty), "0.00", 20, 0, 3, 6); // data.append("打印时间:").append(DateUtil.date().toDateStr()).append("<BR>");
data.append(subRow); // data.append("<CUT>");
} // return data.toString();
} // }
if (ObjectUtil.isNotNull(printInfoDTO.getDiscountAmount())) {
data.append("--------------------------------<BR>");
data.append(StrUtil.format("原价:{}<BR>", toPlainStr(printInfoDTO.getOriginalAmount())));
data.append(StrUtil.format("折扣:-{}<BR>", toPlainStr(printInfoDTO.getDiscountAmount())));
}
data.append("--------------------------------<BR>");
String t = "" + printInfoDTO.getPayAmount();
if (printInfoDTO.isReturn()) {
data.append(StrUtil.format("<B>应退:{}</B><BR>", t));
} else {
data.append(StrUtil.format("<B>应收:{}</B><BR>", t));
}
data.append("--------------------------------<BR>");
if (ObjectUtil.isNotEmpty(printInfoDTO.getPayType()) && ObjectUtil.isNotNull(printInfoDTO.getPayType()) && printInfoDTO.getPayType().equals("deposit")) {
data.append(StrUtil.format("储值:{}<BR>", toPlainStr(printInfoDTO.getOriginalAmount())));
data.append(StrUtil.format("积分:{}<BR>", printInfoDTO.getIntegral()));
}
data.append(StrUtil.format("余额:{}<BR>", toPlainStr(printInfoDTO.getBalance())));
data.append("--------------------------------<BR>");
if (StrUtil.isNotBlank(printInfoDTO.getRemark())) {
data.append(StrUtil.format("<L><BOLD>备注:{}</BOLD></L><BR>", printInfoDTO.getRemark()));
}
data.append("打印时间:").append(DateUtil.date().toDateStr()).append("<BR>");
data.append("<CUT>");
return data.toString();
}
/** /**
* 构建打印内容 * 构建打印内容
*/ */
@Override // @Override
public String buildDishPrintData(String pickupNumber, String date, String productName, // public String buildDishPrintData(String pickupNumber, String date, String productName,
BigDecimal number, String remark, String proGroupInfo) { // BigDecimal number, String remark, String proGroupInfo) {
StringBuilder builder = new StringBuilder() // StringBuilder builder = new StringBuilder()
.append("<CB>").append(pickupNumber).append("</CB><BR>") // .append("<CB>").append(pickupNumber).append("</CB><BR>")
.append("<L>时间: ").append(date).append(" </L><BR>") // .append("<L>时间: ").append(date).append(" </L><BR>")
.append("<B><BOLD>").append(productName).append(" x ") // .append("<B><BOLD>").append(productName).append(" x ")
.append(number.stripTrailingZeros().toPlainString()).append("</BOLD></B><BR>") // .append(number.stripTrailingZeros().toPlainString()).append("</BOLD></B><BR>")
.append("<B><BOLD>").append(StrUtil.emptyToDefault(remark, "")).append("</BOLD></B><BR>"); // .append("<B><BOLD>").append(StrUtil.emptyToDefault(remark, "")).append("</BOLD></B><BR>");
//
if (StrUtil.isNotBlank(proGroupInfo) && JSONUtil.isTypeJSONArray(proGroupInfo)) { // if (StrUtil.isNotBlank(proGroupInfo) && JSONUtil.isTypeJSONArray(proGroupInfo)) {
JSONArray subItems = JSONUtil.parseArray(proGroupInfo); // JSONArray subItems = JSONUtil.parseArray(proGroupInfo);
for (int i = 0; i < subItems.size(); i++) { // for (int i = 0; i < subItems.size(); i++) {
builder.append("<B>(").append(i + 1).append(")") // builder.append("<B>(").append(i + 1).append(")")
.append(subItems.getJSONObject(i).getStr("proName")) // .append(subItems.getJSONObject(i).getStr("proName"))
.append(" x ").append(subItems.getJSONObject(i).getInt("number")) // .append(" x ").append(subItems.getJSONObject(i).getInt("number"))
.append("</B><BR>"); // .append("</B><BR>");
} // }
} // }
return builder.toString(); // return builder.toString();
} // }
@Override @Override
public <R> R sendPrintRequest(String address, String metaPrintData, String printerNum) { public <R> R sendPrintRequest(String address, String metaPrintData, String voiceData, String printerNum) {
log.info("飞蛾打印机开始发送打印请求, 设备地址: {}, 元数据: {}", address, metaPrintData); log.info("飞蛾打印机开始发送打印请求, 设备地址: {}, 元数据: {}", address, metaPrintData);
String time = String.valueOf(System.currentTimeMillis() / 1000); String time = String.valueOf(System.currentTimeMillis() / 1000);
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>(); MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();

View File

@ -34,7 +34,6 @@ public interface PrinterImpl {
private String[] l; private String[] l;
private String[] qr; private String[] qr;
private String cut; private String cut;
} }
@ -46,45 +45,26 @@ public interface PrinterImpl {
*/ */
// String buildOrderPrintData(PrinterHandler.PrintInfoDTO printInfoDTO, List<OrderDetail> detailList); // String buildOrderPrintData(PrinterHandler.PrintInfoDTO printInfoDTO, List<OrderDetail> detailList);
/**
* 构建菜品单打印数据
*
* @param pickupNumber 取餐号
* @param date 时间
* @param productName 商品名
* @param number 数量
* @param remark 备注
* @param proGroupInfo 套餐信息
* @return 元数据
*/
String buildDishPrintData(String pickupNumber, String date, String productName, BigDecimal number, String remark, String proGroupInfo);
/** /**
* 发送打印请求 * 发送打印请求
* *
* @param address 打印地址 * @param address 打印地址
* @param metaPrintData 打印元数据 * @param metaPrintData 打印元数据
* @param voiceData 语音信息
* @param printNum 打印联数 * @param printNum 打印联数
* @param <R> 返回数据类型 * @param <R> 返回数据类型
* @return 打印结果 * @return 打印结果
*/ */
<R> R sendPrintRequest(String address, String metaPrintData, String printNum); <R> R sendPrintRequest(String address, String metaPrintData, String voiceData, String printNum);
/**
* 获取当前打印机标签信息
*
* @return 标签信息
*/
PrintSignLabel getSignLabelInfo(); PrintSignLabel getSignLabelInfo();
/**
* 分割起始标签 <S>
*
* @return 标签
*/
String getStartSplitSign();
/**
* 分割结束标签 </S>
*
* @return 标签
*/
String getEndSplitSign();
/** /**
* 获取对齐后的小票明细行数据 * 获取对齐后的小票明细行数据
@ -170,7 +150,7 @@ public interface PrinterImpl {
// data.append("<OUT:30>"); // data.append("<OUT:30>");
// data.append("<BR>"); // data.append("<BR>");
data.append(getFormatLabel(StrUtil.format("订单号:{}", printInfoDTO.getOrderNo()), signLabelInfo.s)) data.append(getFormatLabel(StrUtil.format("订单号:{}", printInfoDTO.getOrderNo()), signLabelInfo.s))
.append(signLabelInfo.br); .append(signLabelInfo.br);
// data.append(StrUtil.format("<S>订单号:{}</S><BR>", printInfoDTO.getOrderNo())); // data.append(StrUtil.format("<S>订单号:{}</S><BR>", printInfoDTO.getOrderNo()));
data.append(getFormatLabel(StrUtil.format("交易时间:{}", printInfoDTO.getTradeDate()), signLabelInfo.s)) data.append(getFormatLabel(StrUtil.format("交易时间:{}", printInfoDTO.getTradeDate()), signLabelInfo.s))
.append(signLabelInfo.br); .append(signLabelInfo.br);
@ -193,7 +173,7 @@ public interface PrinterImpl {
String row = getRow(detail.getProductName(), "", number, toPlainStr(detail.getPayAmount().stripTrailingZeros().toPlainString()), 20, 0, 3, 6); String row = getRow(detail.getProductName(), "", number, toPlainStr(detail.getPayAmount().stripTrailingZeros().toPlainString()), 20, 0, 3, 6);
data.append(row); data.append(row);
if (StrUtil.isNotBlank(detail.getSkuName())) { if (StrUtil.isNotBlank(detail.getSkuName())) {
data.append(getFormatLabel(detail.getSkuName(), signLabelInfo.s)) data.append(getFormatLabel(StrUtil.format("规格:{}", detail.getSkuName()), signLabelInfo.s))
.append(signLabelInfo.br); .append(signLabelInfo.br);
// data.append("<S>规格:").append(detail.getSkuName()).append("</S><BR>"); // data.append("<S>规格:").append(detail.getSkuName()).append("</S><BR>");
} }
@ -232,7 +212,7 @@ public interface PrinterImpl {
.append(signLabelInfo.br); .append(signLabelInfo.br);
// data.append("<F>应退").append(t).append("</F><BR>"); // data.append("<F>应退").append(t).append("</F><BR>");
} else { } else {
data.append(getFormatLabel(StrUtil.format("应收:{}", t) , signLabelInfo.f)) data.append(getFormatLabel(StrUtil.format("应收:{}", t), signLabelInfo.f))
.append(signLabelInfo.br); .append(signLabelInfo.br);
// data.append(StrUtil.format("<F>应收:{}</F><BR>", t)); // data.append(StrUtil.format("<F>应收:{}</F><BR>", t));
} }
@ -273,6 +253,74 @@ public interface PrinterImpl {
return data.toString(); 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.bold, 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.bold, 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 = cn.hutool.json.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);
// builder.append("<OUT:150>");
// builder.append("<PCUT>");
return builder.toString();
}
/** /**
* 按字符数补足空格英文数字时使用 * 按字符数补足空格英文数字时使用
@ -331,6 +379,7 @@ public interface PrinterImpl {
* @return * @return
*/ */
default String getStringByEnter(String string, int length) { default String getStringByEnter(String string, int length) {
PrintSignLabel signLabelInfo = getSignLabelInfo();
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
while (!string.isEmpty()) { while (!string.isEmpty()) {
int pos = 0, byteCount = 0; int pos = 0, byteCount = 0;
@ -340,7 +389,7 @@ public interface PrinterImpl {
byteCount += charByteLen; byteCount += charByteLen;
pos = i + 1; pos = i + 1;
} }
result.append(getStartSplitSign()).append(string, 0, pos).append(getEndSplitSign()); result.append(signLabelInfo.s[0]).append(string, 0, pos).append(signLabelInfo.s[1]);
string = string.substring(pos); string = string.substring(pos);
if (!string.isEmpty()) { if (!string.isEmpty()) {
result.append("<BR>"); result.append("<BR>");

View File

@ -26,9 +26,14 @@ import javax.annotation.Resource;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.*; import java.util.*;
/**
* 云享印打印机
*
* @author Administrator
*/
@Slf4j @Slf4j
@Component @Component
public class YxyPrinter extends PrinterHandler { public class YxyPrinter extends PrinterHandler implements PrinterImpl {
//请求地址 //请求地址
private static final String URL_STR = "https://ioe.car900.com/v1/openApi/dev/customPrint.json"; private static final String URL_STR = "https://ioe.car900.com/v1/openApi/dev/customPrint.json";
@ -42,6 +47,9 @@ public class YxyPrinter extends PrinterHandler {
@Resource @Resource
private ShopInfoService shopInfoService; private ShopInfoService shopInfoService;
@Resource
private RestTemplate restTemplate;
public YxyPrinter() { public YxyPrinter() {
super("yxyPrinter"); super("yxyPrinter");
} }
@ -55,20 +63,20 @@ public class YxyPrinter extends PrinterHandler {
.setQr(new String[]{"<QR>", "</QR>"}) .setQr(new String[]{"<QR>", "</QR>"})
.setCenter(new String[]{"<CB>", "</CB>"}) .setCenter(new String[]{"<CB>", "</CB>"})
.setBold(new String[]{"<BOLD>", "</BOLD>"}); .setBold(new String[]{"<BOLD>", "</BOLD>"});
@Override
public PrinterImpl.PrintSignLabel getSignLabelInfo() { public PrinterImpl.PrintSignLabel getSignLabelInfo() {
return printSignLabel; return printSignLabel;
} }
/** @Override
* 打印票据 public <R> R sendPrintRequest(String address, String metaPrintData, String voiceData, String printNum) {
*/ log.info("开始请求云享印,请求数据:{}, {}", voiceData, metaPrintData);
public static String printTickets(String voiceJson, Integer actWay, Integer cn, String devName, String data) {
log.info("开始请求云享印,请求数据:{}, {}", voiceJson, data);
//设备名称 //设备名称
//行为方式 1:只打印数据 2:只播放信息 3:打印数据并播放信息 //行为方式 1:只打印数据 2:只播放信息 3:打印数据并播放信息
// actWay = 3; // actWay = 3;
// //打印联数 //打印联数
// int cn = 1; // int cn = 1;
//打印内容 //打印内容
//播报语音数据体,字段参考文档IOT_XY_API11001 //播报语音数据体,字段参考文档IOT_XY_API11001
@ -79,16 +87,15 @@ public class YxyPrinter extends PrinterHandler {
//参数 //参数
MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>(); MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
multiValueMap.add("token", param.get("TOKEN")); multiValueMap.add("token", param.get("TOKEN"));
multiValueMap.add("devName", devName); multiValueMap.add("devName", address);
multiValueMap.add("actWay", actWay); multiValueMap.add("actWay", 3);
multiValueMap.add("cn", cn); multiValueMap.add("cn", printNum);
multiValueMap.add("data", data); multiValueMap.add("data", metaPrintData);
multiValueMap.add("voiceJson", voiceJson); multiValueMap.add("voiceJson", voiceData);
multiValueMap.add("appId", APP_ID); multiValueMap.add("appId", APP_ID);
multiValueMap.add("timestamp", time); multiValueMap.add("timestamp", time);
multiValueMap.add("requestId", uuid); multiValueMap.add("requestId", uuid);
multiValueMap.add("userCode", USER_CODE); multiValueMap.add("userCode", USER_CODE);
RestTemplate restTemplate = new RestTemplate();
HttpHeaders header = new HttpHeaders(); HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_FORM_URLENCODED); header.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
@ -97,7 +104,7 @@ public class YxyPrinter extends PrinterHandler {
httpEntity, String.class); httpEntity, String.class);
log.info("请求云享印成功,响应数据: {}", httpResponse); log.info("请求云享印成功,响应数据: {}", httpResponse);
return httpResponse; return (R) httpResponse;
} }
@Override @Override
@ -108,7 +115,7 @@ public class YxyPrinter extends PrinterHandler {
orderDetail.getNum().intValue(), orderDetail.getSkuName(), orderDetail.getRemark(), orderDetail.getProGroupInfo()); orderDetail.getNum().intValue(), orderDetail.getSkuName(), orderDetail.getRemark(), orderDetail.getProGroupInfo());
// String voiceJson = "{\"bizType\":\"2\",\"content\":\"您有一笔新的订单,请及时处理\"}"; // String voiceJson = "{\"bizType\":\"2\",\"content\":\"您有一笔新的订单,请及时处理\"}";
String voiceJson = "{\"bizType\":\"2\",\"content\":\"\"}"; String voiceJson = "{\"bizType\":\"2\",\"content\":\"\"}";
printTickets(voiceJson, 3, 1, machine.getAddress(), data); // sendPrintRequest(voiceJson, 3, 1, machine.getAddress(), data);
} }
@Override @Override
@ -131,14 +138,14 @@ public class YxyPrinter extends PrinterHandler {
.setOutNumber(orderInfo.getTakeCode()).setPrintTitle("结算单") .setOutNumber(orderInfo.getTakeCode()).setPrintTitle("结算单")
.setRemark(orderInfo.getRemark()).setDiscountAmount(orderInfo.getOriginAmount().subtract(orderInfo.getPayAmount()).toPlainString()); .setRemark(orderInfo.getRemark()).setDiscountAmount(orderInfo.getOriginAmount().subtract(orderInfo.getPayAmount()).toPlainString());
String data = getCashPrintData(printInfoDTO, detailList); String data = buildOrderPrintData(printInfoDTO, detailList);
String voiceJson = "{\"PbizType\":\"2\",\"content\":\"您有一笔新的订单,请及时处理\"}"; String voiceJson = "{\"PbizType\":\"2\",\"content\":\"您有一笔新的订单,请及时处理\"}";
// String voiceJson = "{\"bizType\":\"2\",\"content\":\"\"}"; // String voiceJson = "{\"bizType\":\"2\",\"content\":\"\"}";
String printerNum = "1"; String printerNum = "1";
if (StrUtil.isNotBlank(machine.getPrintQty())) { if (StrUtil.isNotBlank(machine.getPrintQty())) {
printerNum = machine.getPrintQty().split("\\^")[1]; printerNum = machine.getPrintQty().split("\\^")[1];
} }
String resp = printTickets(voiceJson, 3, Integer.valueOf(printerNum), machine.getAddress(), data); String resp = sendPrintRequest(machine.getAddress(), data, voiceJson, printerNum);
// shopPrintLogService.save(machine, printType, data, resp); // shopPrintLogService.save(machine, printType, data, resp);
} }
@ -222,178 +229,4 @@ public class YxyPrinter extends PrinterHandler {
} }
// 按字符数补足空格英文数字时使用
public static String addSpace(String str, int size) {
return StrUtil.fillAfter(str, ' ', size);
}
// 按固定长度拆分字符串按字符拆分
public static 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 字节数补空格用于中文排版
public static String titleAddSpace(String str, int b1) {
int byteLen = StrUtil.bytes(str, CharsetUtil.CHARSET_GBK).length;
return StrUtil.fillAfter(str, ' ', b1 - byteLen);
}
// 判断字符串是否全为英文GBK编码下每个字符均为 1 字节
public static boolean isEn(String str) {
return StrUtil.bytes(str, CharsetUtil.CHARSET_GBK).length == str.length();
}
// 将字符串按 GBK 字节长度拆分每段包装在 <S> 标签内并用 <BR> 换行
public static String getStringByEnter(int length, String string) {
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("<S>").append(string, 0, pos).append("</S>");
string = string.substring(pos);
if (!string.isEmpty()) {
result.append("<BR>");
}
}
return result.toString();
}
/**
* 获取对齐后的小票明细行数据
* 例如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>标签
*/
public static String getRow(String title, String price, String num, String total,
int b1, int b2, int b3, int b4) {
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("<S>").append(title).append(otherStr).append("</S>");
} else {
// 品名超出一行进行拆分
List<String> lines = isEn(title)
// 英文按 b1 个字符拆分
? getStrList(title, b1)
// 中文按 b1/2 个字符拆分
: getStrList(title, b1 / 2);
// 第一行拼接其它字段
String firstLine = titleAddSpace(lines.getFirst(), b1);
sb.append("<S>").append(firstLine).append(otherStr).append("</S><BR>");
// 剩余的行单独换行输出
for (int i = 1; i < lines.size(); i++) {
sb.append("<S>").append(lines.get(i)).append("</S><BR>");
}
}
sb.append("<BR>");
return sb.toString();
}
private static String toPlainStr(String str) {
if (StrUtil.isBlank(str)) {
return "0";
}
return NumberUtil.roundDown(new BigDecimal(str), 2).toPlainString();
}
public static String getCashPrintData(PrintInfoDTO printInfoDTO, List<OrderDetail> detailList) {
StringBuilder data = new StringBuilder();
data.append(StrUtil.format("<C><F>{}</F></C><BR>", printInfoDTO.getShopName()));
data.append("<BR>");
data.append("<OUT:30>");
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(StrUtil.format("<S>订单号:{}</S><BR>", printInfoDTO.getOrderNo()));
data.append(StrUtil.format("<S>交易时间:{}</S><BR>", printInfoDTO.getTradeDate()));
data.append(StrUtil.format("<S>收银员:{}</S><BR>", printInfoDTO.getOperator()));
data.append("<OUT:15>");
data.append("<BR>");
data.append("<S>品名 数量 小计</S><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("<S>规格:").append(detail.getSkuName()).append("</S><BR>");
}
String proGroupInfo = detail.getProGroupInfo();
if (StrUtil.isBlank(proGroupInfo)) {
continue;
}
if (!JSONUtil.isTypeJSONArray(proGroupInfo)) {
continue;
}
JSONArray subItems = cn.hutool.json.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("<S>--------------------------------</S><BR>");
data.append(StrUtil.format("<S>原价:{}</S><BR>", toPlainStr(printInfoDTO.getOriginalAmount())));
data.append(StrUtil.format("<S>折扣:-{}</S><BR>", toPlainStr(printInfoDTO.getDiscountAmount())));
}
data.append("<S>--------------------------------</S><BR>");
String t = "" + printInfoDTO.getPayAmount();
if (printInfoDTO.isReturn()) {
data.append("<F>应退").append(t).append("</F><BR>");
data.append(StrUtil.format("<F>应收:{}</F><BR>", t));
} else {
data.append(StrUtil.format("<F>应收:{}</F><BR>", t));
}
data.append("<S>--------------------------------</S><BR>");
if (ObjectUtil.isNotEmpty(printInfoDTO.getPayType()) && ObjectUtil.isNotNull(printInfoDTO.getPayType()) && "deposit".equals(printInfoDTO.getPayType())) {
data.append(StrUtil.format("<S>储值:{}</S><BR>", toPlainStr(printInfoDTO.getOriginalAmount())));
data.append(StrUtil.format("<S>积分:{}</S><BR>", printInfoDTO.getIntegral()));
}
data.append(StrUtil.format("<S>余额:{}</S><BR>", toPlainStr(printInfoDTO.getBalance())));
data.append("<S>--------------------------------</S><BR>");
if (StrUtil.isNotBlank(printInfoDTO.getRemark())) {
data.append(StrUtil.format("<L><BOLD>备注:{}</BOLD></L><BR>", printInfoDTO.getRemark()));
}
if (Objects.nonNull(printInfoDTO.getOutNumber())) {
data.append("<QR>".concat(printInfoDTO.getOutNumber()).concat("</QR><BR>"));
}
data.append("<S>打印时间:").append(DateUtil.date().toDateStr()).append("</S><BR>");
data.append("<OUT:180>");
data.append("<PCUT>");
return data.toString();
}
} }

View File

@ -52,7 +52,7 @@ public class PrintMachineServiceImpl extends ServiceImpl<PrintMachineMapper, Pri
throw new ApiNotPrintException("打印机不存在"); throw new ApiNotPrintException("打印机不存在");
} }
BeanUtil.copyProperties(printerEditDTO, printerEditDTO); BeanUtil.copyProperties(printerEditDTO, printMachine);
return updateById(printMachine); return updateById(printMachine);
} }
} }