From 4a89d1bbf0a343af7d4dd6750ae77daf4698fa3f Mon Sep 17 00:00:00 2001 From: hayu <1604366271@qq.com> Date: Sat, 1 Nov 2025 14:34:21 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9D=90=E6=96=99=E7=AB=99=E5=87=BA=E5=BA=93--?= =?UTF-8?q?=E4=BA=8C=E7=BB=B4=E7=A0=81=E4=B8=8B=E8=BD=BD=E4=B8=8E=E7=94=9F?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MaterialLeaseInfoController.java | 8 + .../clz/domain/vo/MaterialMaCodeVo.java | 6 + .../clz/mapper/MaterialLeaseInfoMapper.java | 14 ++ .../clz/mapper/MaterialMachineMapper.java | 8 + .../clz/service/MaterialLeaseInfoService.java | 10 + .../impl/MaterialLeaseInfoServiceImpl.java | 237 ++++++++++++++++++ .../material/clz/MaterialLeaseInfoMapper.xml | 44 ++++ .../material/clz/MaterialMachineMapper.xml | 6 + 8 files changed, 333 insertions(+) diff --git a/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/controller/MaterialLeaseInfoController.java b/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/controller/MaterialLeaseInfoController.java index c7e63afd..c5c49496 100644 --- a/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/controller/MaterialLeaseInfoController.java +++ b/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/controller/MaterialLeaseInfoController.java @@ -12,6 +12,7 @@ import com.bonus.common.log.annotation.SysLog; import com.bonus.common.log.enums.OperaType; import com.bonus.material.back.domain.vo.MaCodeVo; import com.bonus.material.basic.domain.BmQrcodeInfo; +import com.bonus.material.basic.domain.report.DownloadRequest; import com.bonus.material.clz.domain.lease.*; import com.bonus.material.common.annotation.PreventRepeatSubmit; import com.bonus.material.clz.domain.vo.lease.LeaseTotalInfo; @@ -24,6 +25,7 @@ import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotNull; +import java.io.IOException; import java.util.List; /** @@ -444,4 +446,10 @@ public class MaterialLeaseInfoController extends BaseController { public AjaxResult getLeaseDataByCode(LeaseTotalInfo info) { return materialLeaseInfoService.getLeaseDataByCode(info); } + + @ApiOperation("二维码打包下载") + @PostMapping("/downloadQrCode") + public void downloadQrCode(@RequestBody MaterialLeaseApplyDetails bean, HttpServletResponse response) throws IOException { + materialLeaseInfoService.queryQrCode(bean, response); + } } diff --git a/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/domain/vo/MaterialMaCodeVo.java b/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/domain/vo/MaterialMaCodeVo.java index 49565fb5..761cf372 100644 --- a/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/domain/vo/MaterialMaCodeVo.java +++ b/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/domain/vo/MaterialMaCodeVo.java @@ -100,4 +100,10 @@ public class MaterialMaCodeVo { @Excel(name = "出库时间", width = 30, dateFormat = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd") private Date outTime; + + /** + * 二维码 + */ + @ApiModelProperty(value = "二维码") + private String qrCode; } diff --git a/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/mapper/MaterialLeaseInfoMapper.java b/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/mapper/MaterialLeaseInfoMapper.java index e8a84fde..b2eed7b0 100644 --- a/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/mapper/MaterialLeaseInfoMapper.java +++ b/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/mapper/MaterialLeaseInfoMapper.java @@ -498,4 +498,18 @@ public interface MaterialLeaseInfoMapper { * @return */ List getPublishList(MaterialLeaseApplyInfo leaseApplyInfo); + + /** + * 获取二维码 + * @param details + * @return + */ + MaterialMaCodeVo getQrCodeByMaId(MaterialLeaseMaCodeDto details); + + /** + * 添加二维码信息 + * @param bmQrcodeInfo + * @return + */ + int insertBmQrcodeInfo(BmQrcodeInfo bmQrcodeInfo); } diff --git a/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/mapper/MaterialMachineMapper.java b/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/mapper/MaterialMachineMapper.java index 4991a684..0a28a339 100644 --- a/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/mapper/MaterialMachineMapper.java +++ b/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/mapper/MaterialMachineMapper.java @@ -1,6 +1,7 @@ package com.bonus.material.clz.mapper; import com.bonus.material.back.domain.vo.MaCodeVo; +import com.bonus.material.basic.domain.BmQrcodeInfo; import com.bonus.material.clz.domain.TeamVo; import com.bonus.material.clz.domain.machine.MaterialUseStorageInfo; import com.bonus.material.clz.domain.vo.*; @@ -341,4 +342,11 @@ public interface MaterialMachineMapper { * @return */ List getSubNumList(MaterialRetainedEquipmentInfo bean); + + /** + * 修改ma_machine数据,二维码编号 + * @param bmQrcodeInfo + * @return + */ + int updateMachine(BmQrcodeInfo bmQrcodeInfo); } diff --git a/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/service/MaterialLeaseInfoService.java b/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/service/MaterialLeaseInfoService.java index d68a40b2..45a05e7d 100644 --- a/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/service/MaterialLeaseInfoService.java +++ b/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/service/MaterialLeaseInfoService.java @@ -7,6 +7,7 @@ import com.bonus.material.clz.domain.lease.*; import com.bonus.material.clz.domain.vo.lease.LeaseTotalInfo; import com.bonus.material.clz.domain.vo.lease.MaterialLeaseApplyRequestVo; +import javax.servlet.http.HttpServletResponse; import java.util.List; /** @@ -171,4 +172,13 @@ public interface MaterialLeaseInfoService { * @return */ List getTotalDetailsList(MaterialLeaseApplyInfo bean); + + /** + * 查询二维码 + * @param bean + * @param response + * return + */ + void queryQrCode(MaterialLeaseApplyDetails bean, HttpServletResponse response); + } diff --git a/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/service/impl/MaterialLeaseInfoServiceImpl.java b/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/service/impl/MaterialLeaseInfoServiceImpl.java index e1dee0de..40076a40 100644 --- a/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/service/impl/MaterialLeaseInfoServiceImpl.java +++ b/bonus-modules/bonus-material/src/main/java/com/bonus/material/clz/service/impl/MaterialLeaseInfoServiceImpl.java @@ -1,7 +1,9 @@ package com.bonus.material.clz.service.impl; import cn.hutool.core.collection.CollectionUtil; +import com.bonus.common.biz.config.BackstageApplication; import com.bonus.common.biz.config.DateTimeHelper; +import com.bonus.common.biz.config.QrCodeUtils; import com.bonus.common.biz.constant.MaterialConstants; import com.bonus.common.biz.domain.BmFileInfo; import com.bonus.common.biz.domain.TypeTreeBuild; @@ -22,6 +24,7 @@ import com.bonus.material.basic.domain.BmAgreementInfo; import com.bonus.material.basic.domain.BmQrcodeInfo; import com.bonus.material.basic.mapper.BmAgreementInfoMapper; import com.bonus.material.basic.mapper.BmFileInfoMapper; +import com.bonus.material.basic.mapper.BmQrcodeInfoMapper; import com.bonus.material.clz.domain.BmTeam; import com.bonus.material.clz.domain.lease.*; import com.bonus.material.clz.domain.vo.MaterialMaCodeVo; @@ -42,7 +45,14 @@ import com.bonus.material.task.domain.TmTask; import com.bonus.material.task.domain.TmTaskAgreement; import com.bonus.material.task.mapper.TmTaskAgreementMapper; import com.bonus.material.task.mapper.TmTaskMapper; +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.MultiFormatWriter; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; +import org.apache.poi.util.IOUtils; import org.hibernate.validator.internal.util.StringHelper; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Service; @@ -50,11 +60,27 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import javax.annotation.Resource; +import javax.imageio.ImageIO; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponse; +import java.awt.*; +import java.awt.font.FontRenderContext; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; import java.math.BigDecimal; import java.math.RoundingMode; +import java.net.URLEncoder; import java.text.SimpleDateFormat; +import java.time.LocalDate; import java.util.*; +import java.util.List; import java.util.stream.Collectors; +import java.util.zip.Deflater; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; /** * @Author ma_sh @@ -94,6 +120,9 @@ public class MaterialLeaseInfoServiceImpl implements MaterialLeaseInfoService { @Resource private IwsTeamUserMapper iwsTeamUserMapper; + @Resource + private BmQrcodeInfoMapper bmQrcodeInfoMapper; + /** * 查询领料任务列表 * @@ -508,6 +537,214 @@ public class MaterialLeaseInfoServiceImpl implements MaterialLeaseInfoService { return sortedList; } + /** + * 查询或生成二维码 + */ + @Override + public void queryQrCode(MaterialLeaseApplyDetails bean, HttpServletResponse response) { + if (bean == null || bean.getMaCodeList() == null || bean.getMaCodeList().isEmpty()) { + throw new RuntimeException("二维码数据为空"); + } + // 临时文件目录 + File tempDir = new File(System.getProperty("java.io.tmpdir"), "qr_temp_" + System.currentTimeMillis()); + if (!tempDir.exists()) { + tempDir.mkdirs(); + } + try { + final int qrSize = 512; + final int padding = 20; + final int fontSize = 68; + final int lineHeight = fontSize + 10; + Font font = new Font("Arial", Font.PLAIN, fontSize); + + Map hints = new HashMap<>(); + hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); + hints.put(EncodeHintType.MARGIN, 1); + + for (MaterialLeaseMaCodeDto details : bean.getMaCodeList()) { + if (details.getMaId() == null) { + continue; + } + String qrCode = ""; + String maCode = ""; + //查询该设备的二维码 + MaterialMaCodeVo machine = materialLeaseInfoMapper.getQrCodeByMaId(details); + if (machine != null) { + if (!StringHelper.isNullOrEmptyString(machine.getQrCode())) { + qrCode = machine.getQrCode(); + } else { + qrCode = generateQrCode(machine.getMaCode(), machine.getTypeId()); + } + maCode = machine.getMaCode(); + } + + // 1. 准备二维码文本 + String qrText = "http://ahjj.jypxks.com:9988/imw/backstage/machine/qrCodePage?qrcode=" + qrCode; + + // 2. 生成二维码 BitMatrix + BitMatrix bitMatrix = new MultiFormatWriter().encode(qrText, BarcodeFormat.QR_CODE, qrSize, qrSize, hints); + + // 3) 转 BufferedImage + BufferedImage qrImage = new BufferedImage(qrSize, qrSize, BufferedImage.TYPE_INT_RGB); + for (int x = 0; x < qrSize; x++) { + for (int y = 0; y < qrSize; y++) { + qrImage.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF); + } + } + + // 4) 在二维码下方绘制文字并换行 + BufferedImage finalImg = drawTextBelow(qrImage, maCode, font, qrSize, padding, lineHeight, fontSize); + + // 5) 写入临时文件 + String safeFileName = sanitizeFileName(maCode) + ".png"; + File outFile = new File(tempDir, safeFileName); + ImageIO.write(finalImg, "png", outFile); + } + + // 6) 把所有图片打包为 zip 并输出(使用 Commons Compress,支持设置编码) + String zipName = "QrCode_" + LocalDate.now().toString() + ".zip"; + response.setContentType("application/zip"); + response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + URLEncoder.encode(zipName, "UTF-8")); + + try (ServletOutputStream sos = response.getOutputStream(); + java.util.zip.ZipOutputStream zos = new java.util.zip.ZipOutputStream(sos)) { + zos.setLevel(Deflater.BEST_SPEED); + for (File file : tempDir.listFiles()) { + String entryName = file.getName(); + ZipEntry ze = new ZipEntry(entryName); + zos.putNextEntry(ze); + try (InputStream fis = new FileInputStream(file)) { + IOUtils.copy(fis, zos); + } + zos.closeEntry(); + } + zos.finish(); + } + + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("生成二维码失败:" + e.getMessage()); + } finally { + // 清理临时文件 + FileUtils.deleteQuietly(tempDir); + } + } + + /** 简单的文件名清洗(避免非法字符) */ + private String sanitizeFileName(String name) { + if (name == null) { + return "file"; + } + return name.replaceAll("[\\\\/:*?\"<>|]", "_"); + } + + private BufferedImage drawTextBelow(BufferedImage qrImage, String text, Font font, int qrSize, int padding, int lineHeight, int fontSize) { + // 1) 计算换行(以 qrSize 为最大宽度) + FontRenderContext frc = new FontRenderContext(null, true, true); + List lines = new ArrayList<>(); + StringBuilder line = new StringBuilder(); + for (char c : text.toCharArray()) { + String testLine = line.toString() + c; + double width = font.getStringBounds(testLine, frc).getWidth(); + if (width > qrSize) { + if (line.length() > 0) { + lines.add(line.toString()); + } + line = new StringBuilder(String.valueOf(c)); + } else { + line.append(c); + } + } + if (line.length() > 0) { + lines.add(line.toString()); + } + + // 2) 计算最终图片尺寸 + int totalWidth = qrSize + padding * 2; + int totalHeight = qrSize + padding * 2 + lines.size() * lineHeight; + + BufferedImage result = new BufferedImage(totalWidth, totalHeight, BufferedImage.TYPE_INT_RGB); + Graphics2D g = result.createGraphics(); + // 开启抗锯齿 + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + + // 背景 + g.setColor(Color.WHITE); + g.fillRect(0, 0, totalWidth, totalHeight); + + // 绘制二维码(居中在 padding 区域) + g.drawImage(qrImage, padding, padding, qrSize, qrSize, null); + + // 绘制文字 + g.setColor(Color.BLACK); + g.setFont(font); + FontMetrics fm = g.getFontMetrics(); + for (int i = 0; i < lines.size(); i++) { + String ln = lines.get(i); + int textWidth = fm.stringWidth(ln); + int x = (totalWidth - textWidth) / 2; + // y 坐标:qrSize + padding + baseline 偏移(用 fontSize 进行计算) + int y = qrSize + padding + (int)(fontSize * 0.9) + i * lineHeight; + g.drawString(ln, x, y); + } + + g.dispose(); + return result; + } + + + + /** + * 生成二维码编号等信息 + */ + public String generateQrCode(String maCode,String typeId){ + BmQrcodeInfo bmQrcodeInfo = new BmQrcodeInfo(); + String genMonth = DateTimeHelper.getNowMonth(); + List list = bmQrcodeInfoMapper.selectByMonth(genMonth); + BmQrcodeInfo bean = null; + int num = 0; + if (list != null && list.size() > 0) { + bean = list.get(0); + num = Integer.parseInt(bean.getQrCode().split("-")[1]); + } + genMonth = genMonth.replace("-", ""); + String code = genMonth + "-" + String.format("%5d", num + 1).replace(" ", "0"); + // 新购管理-二维码打印-新增 + String url = BackstageApplication.getUrl() + "backstage/machine/qrCodePage?qrcode=" + code; + // // 二维码的图片格式 + String format = "jpg"; + //设置路径 + String mkdirsName = "images"; + // linux 系统路径 + String saveDirectory = "/data/imw/" + mkdirsName + "/"; + String os = System.getProperty("os.name"); + if (os.toLowerCase().startsWith("win")) { + //本地路径 + saveDirectory = "D://files/" + mkdirsName + "/"; + } + // 生成二维码 + File files = new File(saveDirectory); + if (!files.exists()) { + files.mkdirs(); + } + QrCodeUtils.generateQRImage(url, saveDirectory, code + ".jpg", format); + String qrUrl = saveDirectory + code + ".jpg"; + bmQrcodeInfo.setQrCode(code); + bmQrcodeInfo.setQrUrl(qrUrl); + bmQrcodeInfo.setCreateTime(DateUtils.getNowDate()); + bmQrcodeInfo.setCreateBy(SecurityUtils.getLoginUser().getUserid().toString()); + bmQrcodeInfo.setBindStatus("1"); + bmQrcodeInfo.setMaCode(maCode); + bmQrcodeInfo.setTypeId(Long.valueOf(typeId)); + //新增bm_qrcode_info数据 + materialLeaseInfoMapper.insertBmQrcodeInfo(bmQrcodeInfo); + //修改设备表 + materialMachineMapper.updateMachine(bmQrcodeInfo); + return code; + } + private List getMachineByProIdAndTypeId(Long proId,Long typeId){ MaterialLeaseApplyInfo dto = new MaterialLeaseApplyInfo(); dto.setProId(proId); diff --git a/bonus-modules/bonus-material/src/main/resources/mapper/material/clz/MaterialLeaseInfoMapper.xml b/bonus-modules/bonus-material/src/main/resources/mapper/material/clz/MaterialLeaseInfoMapper.xml index 28219431..5ca960de 100644 --- a/bonus-modules/bonus-material/src/main/resources/mapper/material/clz/MaterialLeaseInfoMapper.xml +++ b/bonus-modules/bonus-material/src/main/resources/mapper/material/clz/MaterialLeaseInfoMapper.xml @@ -377,6 +377,39 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" NOW() + + insert into bm_qrcode_info + + qr_code, + qr_url, + type_id, + qr_type, + task_id, + is_bind, + ma_code, + create_by, + create_time, + update_by, + update_time, + remark, + company_id, + + + #{qrCode}, + #{qrUrl}, + #{typeId}, + #{qrType}, + #{taskId}, + #{bindStatus}, + #{maCode}, + #{createBy}, + #{createTime}, + #{updateBy}, + #{updateTime}, + #{remark}, + #{companyId}, + + update clz_lease_apply_info @@ -1781,4 +1814,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" WHERE lpd.`code` = #{code} + diff --git a/bonus-modules/bonus-material/src/main/resources/mapper/material/clz/MaterialMachineMapper.xml b/bonus-modules/bonus-material/src/main/resources/mapper/material/clz/MaterialMachineMapper.xml index d48c421f..80b89a71 100644 --- a/bonus-modules/bonus-material/src/main/resources/mapper/material/clz/MaterialMachineMapper.xml +++ b/bonus-modules/bonus-material/src/main/resources/mapper/material/clz/MaterialMachineMapper.xml @@ -3,6 +3,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> + + UPDATE ma_machine + SET qr_code = #{qrCode} + WHERE ma_code = #{maCode} + and type_id = #{typeId} +