package service import ( "fmt" "os" "os/exec" "syscall" "unsafe" "github.com/tarm/serial" ) // SerialPrinter 串口打印机 type SerialPrinter struct { Port *serial.Port } func NewSerialPrinter(portName string, baudRate int) (*SerialPrinter, error) { if baudRate <= 0 { baudRate = 9600 } if len(portName) < 3 || portName[:3] != "COM" { return nil, fmt.Errorf("非串口设备: %s", portName) } c := &serial.Config{Name: portName, Baud: baudRate} port, err := serial.OpenPort(c) if err != nil { return nil, fmt.Errorf("打开串口失败 %s: %w", portName, err) } return &SerialPrinter{Port: port}, nil } func (p *SerialPrinter) Write(data []byte) error { _, err := p.Port.Write(data); return err } func (p *SerialPrinter) Close() error { return p.Port.Close() } // === Windows RAW 打印机 === var winspool = syscall.NewLazyDLL("winspool.drv") type DOC_INFO_1 struct { pDocName *uint16 pOutputFile *uint16 pDataType *uint16 } func PrintRaw(printerName string, data []byte) error { namePtr, _ := syscall.UTF16PtrFromString(printerName) rawType, _ := syscall.UTF16PtrFromString("RAW") var handle syscall.Handle ret, _, _ := winspool.NewProc("OpenPrinterW").Call( uintptr(unsafe.Pointer(namePtr)), uintptr(unsafe.Pointer(&handle)), 0) if ret == 0 { return fmt.Errorf("OpenPrinter 失败: %s", printerName) } defer winspool.NewProc("ClosePrinter").Call(uintptr(handle)) docName, _ := syscall.UTF16PtrFromString("Ticket") di := DOC_INFO_1{pDocName: docName, pDataType: rawType} jobID, _, _ := winspool.NewProc("StartDocPrinterW").Call( uintptr(handle), 1, uintptr(unsafe.Pointer(&di))) if jobID == 0 { return fmt.Errorf("StartDocPrinter 失败") } winspool.NewProc("StartPagePrinter").Call(uintptr(handle)) var written uint32 winspool.NewProc("WritePrinter").Call( uintptr(handle), uintptr(unsafe.Pointer(&data[0])), uintptr(len(data)), uintptr(unsafe.Pointer(&written))) winspool.NewProc("EndPagePrinter").Call(uintptr(handle)) winspool.NewProc("EndDocPrinter").Call(uintptr(handle)) return nil } func copyToPrinter(printerName string, data []byte) error { tmpFile, err := os.CreateTemp("", "tkt_*.bin") if err != nil { return err } tmpPath := tmpFile.Name() defer os.Remove(tmpPath) if _, err := tmpFile.Write(data); err != nil { tmpFile.Close() return err } tmpFile.Close() cmd := exec.Command("cmd", "/C", "copy", "/B", tmpPath, fmt.Sprintf("\\\\127.0.0.1\\%s", printerName)) out, err := cmd.CombinedOutput() if err == nil { return nil } _ = out return PrintRaw(printerName, data) } var _ = os.Stderr