销售出货增加打印拣配单、送货单等

This commit is contained in:
彭枝刚 2024-12-11 20:40:13 +08:00
parent 7f54066aec
commit 0fbf9c509c
8 changed files with 381 additions and 115 deletions

View File

@ -43,5 +43,32 @@
<version>4.5.0</version>
<scope>compile</scope>
</dependency>
<!--jxls开源技术渲染excel,2.9.0兼容POI版本4.1.2 参考 https://jxls.sourceforge.net/getting-started.html-->
<dependency>
<groupId>org.jxls</groupId>
<artifactId>jxls</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.jxls</groupId>
<artifactId>jxls-poi</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
<version>5.8.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.4.1</version>
</dependency>
</dependencies>
</project>

View File

@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSONObject;
import com.foreverwin.mom.common.core.constant.SecurityConstants;
import com.foreverwin.mom.common.core.util.R;
import com.foreverwin.nlv.print.dto.OperationPrintRequest;
import com.foreverwin.nlv.print.entity.PrintLabelTemplate;
import com.foreverwin.nlv.print.entity.PrintResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
@ -32,4 +33,7 @@ public interface RemotePrintService {
@PostMapping("/print/api/doPrintAndSaveLog")
R<PrintResult> remotePrintSaveLog(@RequestBody JSONObject printJson);
@PostMapping("/printlabeltemplate/api/getReportByCode")
R<byte[]> getReportByCode(@RequestBody PrintLabelTemplate dto);
}

View File

@ -0,0 +1,197 @@
package com.foreverwin.nlv.print.util;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.foreverwin.mom.admin.api.feign.RemoteParamService;
import com.foreverwin.mom.common.core.constant.SecurityConstants;
import com.foreverwin.mom.common.core.exception.BaseException;
import com.foreverwin.mom.common.core.util.SpringContextHolder;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import org.apache.commons.io.FileUtils;
import org.jxls.common.Context;
import org.jxls.util.JxlsHelper;
import javax.print.*;
import javax.print.attribute.DocAttributeSet;
import javax.print.attribute.HashDocAttributeSet;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class JxlsUtil {
/**
* 将Excel文件转换为字节数组
* 本方法主要用于将Excel模板与动态数据结合,生成新的Excel文件
*
* @param inputStream Excel模板的输入流
* @param paramMap 动态数据参数,用于填充Excel模板
* @return 转换后的Excel文件的字节数组
* @throws IOException 如果读写过程中发生错误
*/
public static byte[] transferExcel(InputStream inputStream, Map<String, Object> paramMap) throws IOException {
// 创建字节数组输出流,用于后续转换为字节数组
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// 创建上下文对象,用于存放动态数据
Context context = new Context(paramMap);
// 获取JxlsHelper实例并处理模板,将填充后的Excel写入字节数组输出流
JxlsHelper.getInstance().processTemplate(inputStream, byteArrayOutputStream, context);
// 返回转换后的字节数组
return byteArrayOutputStream.toByteArray();
}
/**
* 将输入流中的文件转换为PDF格式
* 该方法主要用于文件转换场景,将特定格式的文件(如Excel)转换为PDF
*
* @param fis 输入流,包含待转换的文件数据
* @return 转换后的PDF文件数据,如果转换失败或响应不正确,则返回null
*/
public static byte[] callPostConvertFileToPdf(InputStream fis) {
// 获取远程服务实例,用于获取转换服务的URL
RemoteParamService remoteParamService = SpringContextHolder.getBean(RemoteParamService.class);
// 通过远程服务获取转换文件至PDF的URL
String url = remoteParamService.getByKey("CONVERT_TO_PDF", SecurityConstants.FROM_IN).getData();
if (StringUtils.isEmpty(url)) {
throw new BaseException("系统参数CONVERT_TO_PDF中文档转换服务地址未配置");
}
File toFile = null;
try {
// 指定临时文件名为temp001.xlsx,这是待转换的文件
toFile = new File("temp001.xlsx");
// 将输入流中的数据复制到临时文件
FileUtils.copyInputStreamToFile(fis, toFile);
// 创建一个Map用于存储表单数据
Map<String, Object> data = new HashMap<>();
// 将临时文件添加到表单数据中,作为待转换的文件输入
data.put("fileInput", toFile);
// 发送POST请求到转换服务URL,并获取响应
HttpResponse response = HttpRequest.post(url)
.form(data)
.contentType("multipart/form-data")
.setConnectionTimeout(100000)
.execute();
// 根据HTTP响应状态判断转换是否成功,如果成功则返回响应体字节数据,否则返回null
return response.getStatus() == HttpURLConnection.HTTP_OK ? response.bodyBytes() : null;
} catch (IOException e) {
// 如果在执行过程中发生IO异常,则抛出运行时异常
throw new RuntimeException(e);
} finally {
// 确保在方法执行完毕后,删除临时文件
if (toFile != null) {
// 临时文件使用后,在这里使用delete()删除
toFile.delete();
}
}
}
/**
* 生成二维码图片字节数组
*
* @param text 要编码成二维码的文本
* @return 生成的二维码图片的字节数组
* @throws WriterException 如果编码过程中发生错误
* @throws IOException 如果输出流过程中发生错误
*/
public static byte[] generateQRCodeImage(String text) {
// 创建QR码编写器
QRCodeWriter qrCodeWriter = new QRCodeWriter();
BitMatrix bitMatrix = null;
try {
// 使用编写器编码文本,指定QR码的格式以及宽高
bitMatrix = qrCodeWriter.encode(text, BarcodeFormat.QR_CODE, 400, 400);
// 创建输出字节流来存储生成的QR码图片
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// 将BitMatrix写入到输出流中,指定图片格式为PNG
MatrixToImageWriter.writeToStream(bitMatrix, "PNG", outputStream);
// 返回输出流中的字节数组,即QR码图片数据
return outputStream.toByteArray();
} catch (WriterException e) {
// 提供更具体的错误信息
throw new RuntimeException("Failed to encode text into QR code: " + e.getMessage(), e);
} catch (IOException e) {
// 提供更具体的错误信息
throw new RuntimeException("Failed to write QR code image to stream: " + e.getMessage(), e);
} finally {
// 尝试关闭流,尽管这里不需要,但作为良好习惯展示
if (bitMatrix != null) {
// 注意:BitMatrix没有close方法,这里仅作示例
// 如果有其他需要关闭的资源,则应在此处添加相应的关闭逻辑
}
}
}
public static void printPdfFixedIpPrinter(byte[] pdfBytes,String printerIp,int printerPort) {
// String printerIp = "192.168.1.254"; // 打印机的IP地址
// int printerPort = 9100; // 打印机监听的端口
try (Socket socket = new Socket(printerIp, printerPort)) {
// 获取套接字输出流,用于向打印机发送数据
OutputStream outputStream = socket.getOutputStream();
// 将PDF数据写入输出流
outputStream.write(pdfBytes);
// 关闭输出流,通知打印机数据传输完成
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static FileInputStream convertToFileInputStream(byte[] data) throws IOException {
// 使用临时文件
File tempFile = File.createTempFile("tempfile", ".bin");
tempFile.deleteOnExit(); // 确保程序结束时删除临时文件
try (FileOutputStream fos = new FileOutputStream(tempFile);
ByteArrayInputStream bais = new ByteArrayInputStream(data)) {
byte[] buffer = new byte[1024];
int len;
while ((len = bais.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
}
// 创建FileInputStream
return new FileInputStream(tempFile);
}
public static void printPdfLocalPrinter(byte[] pdfBytes) {
try {
//构建打印请求属性集
PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
//设置打印格式,因为未确定文件类型,这里选择AUTOSENSE
DocFlavor flavor = DocFlavor.INPUT_STREAM.AUTOSENSE;
// 使用默认的打印服务
PrintService defaultPrintService = PrintServiceLookup.lookupDefaultPrintService();
// 构造待打印的文件流
FileInputStream fis = convertToFileInputStream(pdfBytes);
DocAttributeSet das = new HashDocAttributeSet();
Doc doc = new SimpleDoc(fis, flavor, das);
defaultPrintService.createPrintJob().print(doc, pras);
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] getTemplatePdfFile(){
File file = new File("/Users/snow/Documents/纳琳威/taskReportTemplatePdf.pdf"); // 替换为你的文件路径
try (FileInputStream fileInputStream = new FileInputStream(file)) {
byte[] fileContent = new byte[(int) file.length()];
fileInputStream.read(fileContent);
return fileContent;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}

View File

@ -151,5 +151,10 @@
<artifactId>gson</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.14</version>
</dependency>
</dependencies>
</project>

View File

@ -7,6 +7,7 @@ import com.foreverwin.mom.common.log.annotation.SysLog;
import com.foreverwin.mom.common.security.annotation.Inner;
import com.foreverwin.mom.common.security.util.SecurityUtils;
import com.foreverwin.nlv.print.dto.OperationPrintRequest;
import com.foreverwin.nlv.print.entity.PrintLabelTemplate;
import com.foreverwin.nlv.print.entity.PrintResult;
import com.foreverwin.nlv.print.service.PrintService;
import io.swagger.annotations.Api;

View File

@ -8,6 +8,7 @@ import com.foreverwin.mom.common.core.util.R;
import com.foreverwin.mom.common.data.tenant.TenantContextHolder;
import com.foreverwin.mom.common.excel.annotation.ResponseExcel;
import com.foreverwin.mom.common.log.annotation.SysLog;
import com.foreverwin.mom.common.security.annotation.Inner;
import com.foreverwin.mom.common.security.util.SecurityUtils;
import com.foreverwin.nlv.print.entity.PrintLabelTemplate;
import com.foreverwin.nlv.print.service.PrintLabelTemplateService;
@ -154,4 +155,11 @@ public class PrintLabelTemplateController {
.eq(PrintLabelTemplate::getTemplateCode, templateCode), false));
}
@ApiOperation(value = "获取模板二进制文件", notes = "获取模板二进制文件")
@SysLog("获取模板二进制文件")
@PostMapping("/api/getReportByCode")
public R getReportByCode(@RequestBody PrintLabelTemplate dto) {
return R.ok(printLabelTemplateService.getFileInputStream(dto));
}
}

View File

@ -21,8 +21,10 @@ public interface PrintLabelTemplateService extends IService<PrintLabelTemplate>
boolean validateAndUpdate(String site, String user, PrintLabelTemplate printLabelTemplate);
boolean uploadTemplate(String templateCode, MultipartFile uploadFile);
void doUpload(String site, String templateCode, MultipartFile multipartFile) throws Exception;
byte[] getFileInputStream(PrintLabelTemplate dto);
void doUpload(String site, String templateCode, MultipartFile multipartFile) throws Exception;
List<PrintLabelTemplate> selectList(PrintLabelTemplate printLabelTemplate);
}

View File

@ -1,8 +1,13 @@
package com.foreverwin.nlv.print.service.impl;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.util.IoUtils;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectInputStream;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.foreverwin.mom.common.core.exception.BaseException;
import com.foreverwin.mom.common.core.exception.BusinessException;
@ -37,128 +42,145 @@ import java.util.Map;
@AllArgsConstructor
public class PrintLabelTemplateServiceImpl extends ServiceImpl<PrintLabelTemplateMapper, PrintLabelTemplate> implements PrintLabelTemplateService {
private final static Logger logger = LoggerFactory.getLogger(PrintLabelTemplateServiceImpl.class);
private final static Logger logger = LoggerFactory.getLogger(PrintLabelTemplateServiceImpl.class);
private final PrintLabelTemplateMapper printLabelTemplateMapper;
private final FileTemplate fileTemplate;
private final FileProperties properties;
private final PrintLabelTemplateMapper printLabelTemplateMapper;
@Override
public PrintLabelTemplate getFullById(String handle) {
PrintLabelTemplate printLabelTemplate = getById(handle);
if (printLabelTemplate == null) {
return null;
}
return printLabelTemplate;
}
private final FileTemplate fileTemplate;
@Transactional(rollbackFor = Exception.class)
@Override
public boolean validateAndSave(String site, String user, PrintLabelTemplate printLabelTemplate) {
String handle = PrintLabelTemplate.genHandle(site, printLabelTemplate.getTemplateCode());
validatePrintLabelTemplateExist(handle);
printLabelTemplate.setHandle(handle);
printLabelTemplate.setSite(site);
printLabelTemplate.setCreatedUser(user);
printLabelTemplate.setCreatedDateTime(LocalDateTime.now());
return save(printLabelTemplate);
}
private final FileProperties properties;
@Transactional(rollbackFor = Exception.class)
@Override
public boolean validateAndUpdate(String site, String user, PrintLabelTemplate printLabelTemplate) {
return updateById(printLabelTemplate);
}
@Override
public PrintLabelTemplate getFullById(String handle) {
PrintLabelTemplate printLabelTemplate = getById(handle);
return printLabelTemplate;
}
@Override
public boolean removeById(java.io.Serializable id) {
return super.removeById(id);
}
@Transactional(rollbackFor = Exception.class)
@Override
public boolean validateAndSave(String site, String user, PrintLabelTemplate printLabelTemplate) {
String handle = PrintLabelTemplate.genHandle(site, printLabelTemplate.getTemplateCode());
validatePrintLabelTemplateExist(handle);
printLabelTemplate.setHandle(handle);
printLabelTemplate.setSite(site);
printLabelTemplate.setCreatedUser(user);
printLabelTemplate.setCreatedDateTime(LocalDateTime.now());
return save(printLabelTemplate);
}
/**
* 判断当前打印标签模板是否存在
*
* @param handle
*/
private void validatePrintLabelTemplateExist(String handle) {
//存在性校验
if (StrUtil.isNotBlank(handle)) {
PrintLabelTemplate dbPrintLabelTemplate = printLabelTemplateMapper.selectById(handle);
if (dbPrintLabelTemplate != null) {
throw BusinessException.build("打印标签模板已经存在,无法新建");
}
}
}
@Override
public boolean uploadTemplate(String templateCode, MultipartFile uploadFile) {
// 如果文件为空,则返回null
if (uploadFile == null) {
return false;
}
// 处理文件名的编码转换,确保兼容性
// new String(Objects.requireNonNull(uploadFile.getOriginalFilename()).getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
// 上传文件并获取返回的文件URL
uploadFile(uploadFile, templateCode);
return true;
}
private String uploadFile(MultipartFile file, String templateCode) {
String fileName = templateCode + ".lab";
try (InputStream inputStream = file.getInputStream()) {
fileTemplate.createBucket(properties.getBucketName());
fileTemplate.putObject(properties.getBucketName(), fileName, inputStream, file.getContentType());
} catch (Exception e) {
throw new BaseException("上传失败:" + e);
}
return fileName;
}
@Transactional(rollbackFor = Exception.class)
@Override
public boolean validateAndUpdate(String site, String user, PrintLabelTemplate printLabelTemplate) {
return updateById(printLabelTemplate);
}
@Override
public void doUpload(String site, String templateCode, MultipartFile multipartFile) throws Exception {
@Override
public boolean removeById(java.io.Serializable id) {
return super.removeById(id);
}
site = TenantContextHolder.getTenantStrId();
List<Map<String, Object>> list = printLabelTemplateMapper.selectFastCode("FastCodeBO:" + site + ",FTP_CONFIG", "PrintLabelPath");
if (list.size() == 0) {
throw new MybatisPlusException("请先在快码维护的快码编号FTP_CONFIG里维护字段代码为PrintLabelPath的模板上传路径");
}
String url = list.get(0).get("EF1") == null ? "" : list.get(0).get("EF1").toString();
int port = list.get(0).get("EF2") == null ? 21 : Integer.parseInt(list.get(0).get("EF2").toString());
String userName = list.get(0).get("EF3") == null ? "" : list.get(0).get("EF3").toString();
String password = list.get(0).get("EF4") == null ? "" : list.get(0).get("EF4").toString();
String path = list.get(0).get("EF5") == null ? "" : list.get(0).get("EF5").toString();
/**
* 判断当前打印标签模板是否存在
*
* @param handle
*/
private void validatePrintLabelTemplateExist(String handle) {
//存在性校验
if (StrUtil.isNotBlank(handle)) {
PrintLabelTemplate dbPrintLabelTemplate = printLabelTemplateMapper.selectById(handle);
if (dbPrintLabelTemplate != null) {
throw BusinessException.build("打印标签模板已经存在,无法新建");
}
}
}
FTPClient ftp = new FTPClient();
ftp.setControlEncoding("UTF-8");
ftp.connect(url, port);
ftp.login(userName, password);
ftp.enterLocalPassiveMode(); // 开启被动模式
int replyCode = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(replyCode)) {
throw new MybatisPlusException("登录FTP服务器" + url + ":" + port + "失败");
}
boolean flag = ftp.changeWorkingDirectory(path);//检查上传路径是否存在 如果不存在返回false
if (!flag) {
ftp.makeDirectory(path);//创建上传的路径 该方法只能创建一级目录,在这里如果/home/ftpUser存在则可创建image
}
ftp.changeWorkingDirectory(path);//指定上传路径
ftp.setFileType(FTP.BINARY_FILE_TYPE);//指定上传文件的类型 二进制文件
InputStream local = multipartFile.getInputStream();
ftp.storeFile(templateCode + ".lab", local);//第一个参数是文件名
logger.info("template {} uploaded to path {}, with reply: {}", templateCode, path, ftp.getReplyString());
local.close();//关闭文件流
ftp.logout();//退出
ftp.disconnect();//断开连接
}
@Override
public boolean uploadTemplate(String templateCode, MultipartFile uploadFile) {
// 如果文件为空,则返回null
if (uploadFile == null) {
return false;
}
@Override
public List<PrintLabelTemplate> selectList(PrintLabelTemplate printLabelTemplate) {
QueryWrapper<PrintLabelTemplate> queryWrapper = new QueryWrapper<>();
queryWrapper.setEntity(printLabelTemplate);
return super.list(queryWrapper);
}
// 处理文件名的编码转换,确保兼容性
// new String(Objects.requireNonNull(uploadFile.getOriginalFilename()).getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
// 上传文件并获取返回的文件URL
uploadFile(uploadFile, templateCode);
return true;
}
private String uploadFile(MultipartFile file, String templateCode) {
String fileName = templateCode + StrUtil.DOT + FileUtil.extName(file.getOriginalFilename());
try (InputStream inputStream = file.getInputStream()) {
fileTemplate.createBucket(properties.getBucketName());
fileTemplate.putObject(properties.getBucketName(), fileName, inputStream, file.getContentType());
} catch (Exception e) {
throw new BaseException("上传失败:" + e);
}
return fileName;
}
@Override
public byte[] getFileInputStream(PrintLabelTemplate dto) {
PrintLabelTemplate target = this.getOne(Wrappers.<PrintLabelTemplate>lambdaQuery()
.eq(PrintLabelTemplate::getSite, dto.getSite())
.eq(PrintLabelTemplate::getTemplateCode, dto.getTemplateCode()));
if (target == null) {
throw BusinessException.build("打印模板不存在" + dto.getTemplateCode());
}
String fileName = target.getTemplateCode() + StrUtil.DOT + FileUtil.extName(target.getTemplateName());
try (S3Object s3Object = fileTemplate.getObject(properties.getBucketName(), fileName)) {
S3ObjectInputStream s3Object1 = s3Object.getObjectContent();
if (s3Object1 == null) {
return null;
}
return IoUtils.toByteArray(s3Object.getObjectContent());
} catch (Exception e) {
throw BusinessException.build("minIo读取失败:" + e);
}
}
@Override
public void doUpload(String site, String templateCode, MultipartFile multipartFile) throws Exception {
site = TenantContextHolder.getTenantStrId();
List<Map<String, Object>> list = printLabelTemplateMapper.selectFastCode("FastCodeBO:" + site + ",FTP_CONFIG", "PrintLabelPath");
if (list.size() == 0) {
throw new MybatisPlusException("请先在快码维护的快码编号FTP_CONFIG里维护字段代码为PrintLabelPath的模板上传路径");
}
String url = list.get(0).get("EF1") == null ? "" : list.get(0).get("EF1").toString();
int port = list.get(0).get("EF2") == null ? 21 : Integer.parseInt(list.get(0).get("EF2").toString());
String userName = list.get(0).get("EF3") == null ? "" : list.get(0).get("EF3").toString();
String password = list.get(0).get("EF4") == null ? "" : list.get(0).get("EF4").toString();
String path = list.get(0).get("EF5") == null ? "" : list.get(0).get("EF5").toString();
FTPClient ftp = new FTPClient();
ftp.setControlEncoding("UTF-8");
ftp.connect(url, port);
ftp.login(userName, password);
ftp.enterLocalPassiveMode(); // 开启被动模式
int replyCode = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(replyCode)) {
throw new MybatisPlusException("登录FTP服务器" + url + ":" + port + "失败");
}
boolean flag = ftp.changeWorkingDirectory(path);//检查上传路径是否存在 如果不存在返回false
if (!flag) {
ftp.makeDirectory(path);//创建上传的路径 该方法只能创建一级目录,在这里如果/home/ftpUser存在则可创建image
}
ftp.changeWorkingDirectory(path);//指定上传路径
ftp.setFileType(FTP.BINARY_FILE_TYPE);//指定上传文件的类型 二进制文件
InputStream local = multipartFile.getInputStream();
ftp.storeFile(templateCode + ".lab", local);//第一个参数是文件名
logger.info("template {} uploaded to path {}, with reply: {}", templateCode, path, ftp.getReplyString());
local.close();//关闭文件流
ftp.logout();//退出
ftp.disconnect();//断开连接
}
@Override
public List<PrintLabelTemplate> selectList(PrintLabelTemplate printLabelTemplate) {
QueryWrapper<PrintLabelTemplate> queryWrapper = new QueryWrapper<>();
queryWrapper.setEntity(printLabelTemplate);
return super.list(queryWrapper);
}
}