材料站出库--二维码下载与生成

This commit is contained in:
hayu 2025-11-01 14:34:21 +08:00
parent e18052d756
commit 4a89d1bbf0
8 changed files with 333 additions and 0 deletions

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -498,4 +498,18 @@ public interface MaterialLeaseInfoMapper {
* @return
*/
List<MaterialLeaseApplyInfo> getPublishList(MaterialLeaseApplyInfo leaseApplyInfo);
/**
* 获取二维码
* @param details
* @return
*/
MaterialMaCodeVo getQrCodeByMaId(MaterialLeaseMaCodeDto details);
/**
* 添加二维码信息
* @param bmQrcodeInfo
* @return
*/
int insertBmQrcodeInfo(BmQrcodeInfo bmQrcodeInfo);
}

View File

@ -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<MaterialProvideNumInfo> getSubNumList(MaterialRetainedEquipmentInfo bean);
/**
* 修改ma_machine数据二维码编号
* @param bmQrcodeInfo
* @return
*/
int updateMachine(BmQrcodeInfo bmQrcodeInfo);
}

View File

@ -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<MaterialLeaseApplyDetailExport> getTotalDetailsList(MaterialLeaseApplyInfo bean);
/**
* 查询二维码
* @param bean
* @param response
* return
*/
void queryQrCode(MaterialLeaseApplyDetails bean, HttpServletResponse response);
}

View File

@ -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<EncodeHintType, Object> 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<String> 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<BmQrcodeInfo> 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<MaterialMaCodeVo> getMachineByProIdAndTypeId(Long proId,Long typeId){
MaterialLeaseApplyInfo dto = new MaterialLeaseApplyInfo();
dto.setProId(proId);

View File

@ -377,6 +377,39 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
NOW()
</trim>
</insert>
<insert id="insertBmQrcodeInfo">
insert into bm_qrcode_info
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="qrCode != null">qr_code,</if>
<if test="qrUrl != null">qr_url,</if>
<if test="typeId != null">type_id,</if>
<if test="qrType != null">qr_type,</if>
<if test="taskId != null">task_id,</if>
<if test="bindStatus != null">is_bind,</if>
<if test="maCode != null">ma_code,</if>
<if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if>
<if test="updateTime != null">update_time,</if>
<if test="remark != null">remark,</if>
<if test="companyId != null">company_id,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="qrCode != null">#{qrCode},</if>
<if test="qrUrl != null">#{qrUrl},</if>
<if test="typeId != null">#{typeId},</if>
<if test="qrType != null">#{qrType},</if>
<if test="taskId != null">#{taskId},</if>
<if test="bindStatus != null">#{bindStatus},</if>
<if test="maCode != null">#{maCode},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="remark != null">#{remark},</if>
<if test="companyId != null">#{companyId},</if>
</trim>
</insert>
<update id="updateLeaseApplyInfo">
update clz_lease_apply_info
@ -1781,4 +1814,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
WHERE
lpd.`code` = #{code}
</select>
<select id="getQrCodeByMaId" resultType="com.bonus.material.clz.domain.vo.MaterialMaCodeVo">
SELECT
mm.ma_id AS maId,
mm.ma_code AS maCode,
mm.qr_code AS qrCode,
mm.type_id AS typeId
FROM
ma_machine mm
WHERE
mm.ma_id = #{maId}
</select>
</mapper>

View File

@ -3,6 +3,12 @@
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bonus.material.clz.mapper.MaterialMachineMapper">
<update id="updateMachine">
UPDATE ma_machine
SET qr_code = #{qrCode}
WHERE ma_code = #{maCode}
and type_id = #{typeId}
</update>
<select id="getMachineInfo" resultType="com.bonus.material.ma.domain.Machine">
SELECT
sd.dept_name AS impUnitName,