feat: 添加打印机管理模块,支持局域网打印
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Agent
2026-03-29 06:38:27 +00:00
parent de0a9c1fa7
commit 4dddae325c
6 changed files with 335 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
package com.example.building.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.building.entity.Printer;
/**
* 打印机服务接口
*/
public interface PrinterService extends IService<Printer> {
/**
* 获取默认打印机
*/
Printer getDefaultPrinter();
/**
* 打印订单小票
*/
void printOrder(String orderId);
}

View File

@@ -0,0 +1,151 @@
package com.example.building.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.building.entity.Order;
import com.example.building.entity.Printer;
import com.example.building.mapper.OrderMapper;
import com.example.building.mapper.PrinterMapper;
import com.example.building.service.PrinterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* 打印机服务实现
*/
@Service
public class PrinterServiceImpl extends ServiceImpl<PrinterMapper, Printer> implements PrinterService {
@Autowired
private OrderMapper orderMapper;
@Override
public Printer getDefaultPrinter() {
return this.getOne(new LambdaQueryWrapper<Printer>()
.eq(Printer::getIsDefault, 1)
.eq(Printer::getStatus, 1)
.last("LIMIT 1"));
}
@Override
public void printOrder(String orderId) {
Printer printer = getDefaultPrinter();
if (printer == null) {
throw new RuntimeException("未找到默认打印机,请先配置打印机");
}
// 获取订单信息
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new RuntimeException("订单不存在");
}
// 生成ESC/POS指令
byte[] data = buildReceipt(order);
// 发送到打印机
sendToPrinter(printer.getIp(), printer.getPort(), data);
}
/**
* 生成小票ESC/POS指令
*/
private byte[] buildReceipt(Order order) {
StringBuilder sb = new StringBuilder();
// 初始化打印机
sb.append("\u001B@"); // ESC @
// 居中对齐
sb.append("\u001Ba\u0001"); // ESC a 1
// 打印标题
sb.append("\u001B!\u0038"); // ESC ! 56 大字+居中
sb.append("【订单小票】\n");
sb.append("\u001B!\u0000"); // ESC ! 0 恢复正常
// 分隔线
sb.append("--------------------------------\n");
// 左对齐
sb.append("\u001Ba\u0000"); // ESC a 0
// 订单信息
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
sb.append("订单号: ").append(order.getOrderNo()).append("\n");
sb.append("时间: ").append(order.getCreatedAt().format(formatter)).append("\n");
sb.append("客户: ").append(order.getCustomerName() != null ? order.getCustomerName() : "散客").append("\n");
sb.append("电话: ").append(order.getCustomerPhone() != null ? order.getCustomerPhone() : "-").append("\n");
// 分隔线
sb.append("--------------------------------\n");
// 商品表头
sb.append(String.format("%-10s %4s %8s %8s\n", "商品", "数量", "单价", "小计"));
sb.append("--------------------------------\n");
// TODO: 获取订单明细,这里暂时用固定内容演示
sb.append(String.format("%-10s %4d %8.2f %8.2f\n",
"商品示例", 1, order.getTotalAmount().doubleValue(), order.getActualAmount().doubleValue()));
// 分隔线
sb.append("--------------------------------\n");
// 金额信息
sb.append(String.format("%-16s %10s\n", "原价合计:", "¥" + order.getTotalAmount()));
sb.append(String.format("%-16s %10s\n", "优惠金额:", "" + order.getDiscountAmount()));
sb.append("\u001B!\u0008"); // ESC ! 8 放大
sb.append(String.format("%-16s %10s\n", "实付金额:", "¥" + order.getActualAmount()));
sb.append("\u001B!\u0000"); // ESC ! 0 恢复正常
// 支付方式
String paymentMethod = getPaymentText(order.getPaymentMethod());
sb.append("支付方式: ").append(paymentMethod).append("\n");
// 备注
if (order.getRemark() != null && !order.getRemark().isEmpty()) {
sb.append("备注: ").append(order.getRemark()).append("\n");
}
// 尾部
sb.append("--------------------------------\n");
sb.append("\u001Ba\u0001"); // 居中
sb.append("谢谢惠顾,欢迎下次光临!\n");
sb.append("\n\n\n");
// 切纸指令
sb.append("\u001Bd\u0003"); // ESC d 3 走纸3行
sb.append("\u001Bv\u0000"); // GS v 0 切纸
return sb.toString().getBytes(StandardCharsets.UTF_8);
}
private String getPaymentText(String method) {
if (method == null) return "-";
switch (method) {
case "cash": return "现金";
case "wechat": return "微信";
case "alipay": return "支付宝";
default: return method;
}
}
/**
* 发送数据到打印机
*/
private void sendToPrinter(String ip, int port, byte[] data) {
try (Socket socket = new Socket(ip, port);
OutputStream out = socket.getOutputStream()) {
out.write(data);
out.flush();
} catch (Exception e) {
throw new RuntimeException("打印失败: " + e.getMessage());
}
}
}