Merge branch 'develop' into qms/develop

This commit is contained in:
曹鹏飞 2026-04-14 18:33:40 +08:00
commit e95e500e3c
19 changed files with 814 additions and 71 deletions

View File

@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.nflg.wms.admin.pojo.dto.*;
import com.nflg.wms.admin.pojo.request.UnqualifiedWarehousingRequest;
import com.nflg.wms.admin.service.NormalQMControllerService;
import com.nflg.wms.admin.service.SapService;
import com.nflg.wms.common.constant.BarCodeProcessStage;
import com.nflg.wms.common.pojo.ApiResult;
import com.nflg.wms.common.pojo.PageData;
@ -74,8 +75,11 @@ public class NormalQMController extends BaseController {
@Resource
private IWmsQrCodeMasterService qrCodeMasterService;
private SapService sapService;
/**
* 获取SRM推送过来的质检单信息
*
* @param request
* @return
*/
@ -87,6 +91,7 @@ public class NormalQMController extends BaseController {
/**
* 导出质检单
*
* @param request 查询条件
* @return
*/
@ -100,6 +105,7 @@ public class NormalQMController extends BaseController {
/**
* 质检物料上架任务PDA
*
* @param orderNo 送货单号或任务单号
* @return
*/
@ -112,6 +118,7 @@ public class NormalQMController extends BaseController {
/**
* 质检物料上架物料详情
*
* @param taskId 任务ID
**/
@GetMapping("PDA/task/item")
@ -123,6 +130,7 @@ public class NormalQMController extends BaseController {
/**
* 质检物料上架任务确认
*
* @param request
**/
@Transactional
@ -368,6 +376,7 @@ public class NormalQMController extends BaseController {
/**
* 无码质检任务上架确认
*
* @param request 任务ID
* @return
*/
@ -478,7 +487,16 @@ public class NormalQMController extends BaseController {
WmsSrmQualityInspection inspection = wmsSrmQualityInspectionService.getById(request.getId());
VUtil.trueThrowBusinessError(Objects.isNull(inspection)).throwMessage("无效的质检单");
VUtil.trueThrowBusinessError(!inspection.getInspectionResult().equals("不合格")).throwMessage("该质检单不是不合格单");
VUtil.trueThrowBusinessError(inspection.getReceiveQty().compareTo(BigDecimal.ZERO) > 0).throwMessage("该质检单已生成上架任务");
VUtil.trueThrowBusinessError(inspection.getReceiveQty().compareTo(BigDecimal.ZERO) > 0).throwMessage("该质检单已完成冲销");
List<ZWM3A27ItemDTO> itemDTOs = new ArrayList<>();
ZWM3A27ItemDTO itemDTO = new ZWM3A27ItemDTO()
.setEbeln(inspection.getPoNum())
.setEbeln(inspection.getPoNum()).
setEbelp(Integer.valueOf(inspection.getPoLineNumber())).
setMenge(inspection.getDeliveryQty()).setMatDoc(inspection.getMaterialDoc105())
.setDocYear(inspection.getMaterialDocYear105());
itemDTOs.add(itemDTO);
sapService.zwm3a27(inspection.getMaterialDoc105(), inspection.getMaterialDocYear105(), itemDTOs);
inspection.setReceiveQty(request.getReceiveQty());
wmsSrmQualityInspectionService.updateById(inspection);
return ApiResult.success();

View File

@ -54,7 +54,6 @@ import java.util.zip.ZipOutputStream;
/**
* 装箱管理
*
* @author: nflg
* @date: 2022/3/17
* @description:
@ -86,7 +85,6 @@ public class QrCodeMasterController extends BaseController {
/**
* 获取包装箱和箱内物料信息
*
* @param request
* @return
*/
@ -126,7 +124,6 @@ public class QrCodeMasterController extends BaseController {
/**
* 包装箱编码获取包装箱和箱内物料信息
*
* @param request
* @return
*/
@ -161,7 +158,6 @@ public class QrCodeMasterController extends BaseController {
/**
* 箱码Validation
*
* @param qrCodeMaster
* @param smallQrCodeMasters
*/
@ -209,7 +205,6 @@ public class QrCodeMasterController extends BaseController {
/**
* 装箱绑码
*
* @param request
* @return
*/
@ -265,7 +260,6 @@ public class QrCodeMasterController extends BaseController {
/**
* 拆箱删除
*
* @param request
* @return
*/
@ -294,7 +288,6 @@ public class QrCodeMasterController extends BaseController {
/**
* 换箱
* 针对库存地点内的物料箱合并修改物料的箱属性和库存
*
* @param request
* @return
*/
@ -402,7 +395,6 @@ public class QrCodeMasterController extends BaseController {
/**
* 物料出入库统一扫码接口
*
* @param request
* @return
* @author
@ -437,7 +429,6 @@ public class QrCodeMasterController extends BaseController {
/**
* 换箱扫码接口
*
* @param request
* @return
* @author
@ -469,11 +460,12 @@ public class QrCodeMasterController extends BaseController {
/**
* 导出标签图片为ZIP
*
* @param datas 二维码列表
*/
@PostMapping(value = "exportToZip", produces = "application/zip")
public ResponseEntity<byte[]> exportToZip(@Valid @RequestBody @NotEmpty List<QrCodeItemVO> datas) throws Exception {
VUtil.trueThrowBusinessError(datas.stream().map(QrCodeItemVO::getPackagingType).collect(Collectors.toSet()).size() > 1)
.throwMessage("只能选择一种包装类型");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
for (QrCodeItemVO it : datas) {
@ -482,7 +474,8 @@ public class QrCodeMasterController extends BaseController {
dto.setQrCode(QRCodeUtil.generateQRCodeBase64(generateQRContent(dto), 100, 100));
Map<String, Object> variables = new HashMap<>();
variables.put("list", List.of(dto));
String html = ThymeleafUtil.generator("/template/qrcode/", "dp-1-label", ".html", variables);
String html = ThymeleafUtil.generator("/template/qrcode/"
, datas.get(0).getPackagingType() == 0 ? "dp-1-label" : "medium-box-label", ".html", variables);
ZipEntry entry = new ZipEntry(it.getBarcodeCode() + ".png");
zos.putNextEntry(entry);
byte[] imageBytes = HtmlToImageUtil.convertToPng(html, 600);
@ -499,11 +492,12 @@ public class QrCodeMasterController extends BaseController {
/**
* 导出标签图片为PDF
*
* @param datas 二维码列表
*/
@PostMapping("exportToPdf")
public void exportToPdf(HttpServletResponse response, @RequestBody @NotEmpty List<QrCodeItemVO> datas) throws Exception {
VUtil.trueThrowBusinessError(datas.stream().map(QrCodeItemVO::getPackagingType).collect(Collectors.toSet()).size() > 1)
.throwMessage("只能选择一种包装类型");
Map<String, Object> variables = new HashMap<>();
variables.put("list", datas.stream().map(data ->
{
@ -512,7 +506,8 @@ public class QrCodeMasterController extends BaseController {
dto.setQrCode(QRCodeUtil.generateQRCodeBase64(generateQRContent(dto), 100, 100));
return dto;
}).toList());
String html = ThymeleafUtil.generator("/template/qrcode/", "dp-1", ".html", variables);
String html = ThymeleafUtil.generator("/template/qrcode/"
, datas.get(0).getPackagingType() == 0 ? "dp-1" : "medium-box", ".html", variables);
URL baseUrl = new ClassPathResource("template/qrcode/").getURL();
PdfGeneratorUtil.generatePdf("箱码标签图片", html, baseUrl.toString(), response);
}

View File

@ -9,6 +9,7 @@ import com.nflg.wms.admin.pojo.dto.QCMaterialSyncDTO;
import com.nflg.wms.admin.pojo.dto.ZWM3A17DTO;
import com.nflg.wms.admin.repository.SrmMaterialReceiptNoScanCodesRepository;
import com.nflg.wms.admin.repository.SrmMaterialReceiptScanCodesRepository;
import com.nflg.wms.common.constant.BarCodeProcessStage;
import com.nflg.wms.common.pojo.document.SrmMaterialReceiptNoScanCodes;
import com.nflg.wms.common.pojo.document.SrmMaterialReceiptScanCodes;
import com.nflg.wms.common.pojo.dto.*;
@ -619,37 +620,30 @@ public class NormalPGIControllerService {
}
}
//编码管理
// if (CollectionUtil.isNotEmpty(dto.getQrCodes())) {
// //同步箱码信息到物料码中
// List<WmsQrCodeMaster> parents = dto.getQrCodes()
// .stream()
// .filter(qrCode -> qrCode.getPackagingType() == 1)
// .toList();
// if (CollectionUtil.isNotEmpty(parents)) {
// List<WmsQrCodeMaster> children = qrCodeMasterService.lambdaQuery()
// .in(WmsQrCodeMaster::getParentBarcodeId, parents
// .stream()
// .map(WmsQrCodeMaster::getId)
// .toList()
// )
// .list();
// parents.forEach(p -> {
// children.stream()
// .filter(c -> c.getParentBarcodeId().equals(p.getId()))
// .forEach(c -> {
// c.setProcessStage(p.getProcessStage());
// c.setLastScanBy(UserUtil.getUserId());
// c.setLastScanByname(UserUtil.getUserName());
// c.setLastScanTime(LocalDateTime.now());
// c.setFactoryCode(p.getFactoryCode());
// c.setStorageLocation(p.getStorageLocation());
// c.setBinLocation(p.getBinLocation());
// c.setReceiptItemId(p.getReceiptItemId());
// });
// });
// dto.getQrCodes().addAll(children);
// }
// 这里需要同步一个信息就是把箱码的状态也要改为收货状态,
List<Long> parentIds = dto.getQrCodes().stream()
.filter(qrCode -> qrCode.getPackagingType() == 0)
.map(WmsQrCodeMaster::getParentBarcodeId)
.distinct()
.toList();
if (CollectionUtil.isNotEmpty(parentIds)) {
// 获取所有的父级物料信息
List<WmsQrCodeMaster> parents2 = qrCodeMasterService.lambdaQuery()
.in(WmsQrCodeMaster::getId, parentIds)
.list();
for (WmsQrCodeMaster parent : parents2) {
boolean exists = dto.getQrCodes().stream().anyMatch(qrCode ->
qrCode.getBarcodeCode().equals(parent.getBarcodeCode()));
if (!exists) {
parent.setProcessStage(BarCodeProcessStage.Received.getState());
parent.setLastScanBy(UserUtil.getUserId());
parent.setLastScanByname(UserUtil.getUserName());
parent.setLastScanTime(LocalDateTime.now());
dto.getQrCodes().add(parent);
}
}
}
qrCodeMasterService.updateBarCode(dto.getQrCodes());
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,64 @@
package com.nflg.wms.common.pojo.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
/**
* 物料码打印DTO
*/
@Data
@Accessors(chain = true)
public class MaterialCodePrintDTO {
/**
* 二维码Base64
*/
private String qrCode;
/**
* 唯一码
*/
private String no;
/**
* 物料编号
*/
private String materialNo;
/**
* 物料描述
*/
private String materialDescribe;
/**
* 数量
*/
private BigDecimal num;
/**
* 数量文本
*/
private String numText;
/**
* 单位
*/
private String unit;
/**
* 机台编号
*/
private String deviceNo;
/**
* 客户名称
*/
private String customerName;
/**
* 图片Base64
*/
private String lst;
}

View File

@ -0,0 +1,84 @@
package com.nflg.wms.common.pojo.vo;
import com.nflg.wms.common.util.NumberUtil;
import lombok.Data;
import java.math.BigDecimal;
/**
* 物料明细打印预览VO
*/
@Data
public class MaterialItemPrintVO {
private Long id;
/**
* 物料编号
*/
private String materialNo;
/**
* 物料描述
*/
private String materialDescribe;
/**
* 数量
*/
private BigDecimal num;
/**
* 数量文本
*/
private String numText;
public String getNumText() {
return NumberUtil.format(num);
}
/**
* 实发数量
*/
private BigDecimal actualNum;
/**
* 单位
*/
private String unit;
/**
* 项目类型
*/
private String projectType;
/**
* 生产订单号
*/
private String productionOrderNumber;
/**
* 箱号
*/
private String boxNo;
/**
* 机台编号
*/
private String deviceNo;
/**
* 客户名称
*/
private String customerName;
/**
* 二维码Base64
*/
private String qrCode;
/**
* 图片Base64
*/
private String image;
}

View File

@ -1,6 +1,7 @@
package com.nflg.wms.repository.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.nflg.wms.common.pojo.vo.MaterialItemPrintVO;
import com.nflg.wms.common.pojo.vo.ShipmentMaterialCodeItemVO;
import com.nflg.wms.repository.entity.WmsShipmentMaterialCodeItem;
@ -20,4 +21,8 @@ public interface WmsShipmentMaterialCodeItemMapper extends BaseMapper<WmsShipmen
List<ShipmentMaterialCodeItemVO> getByCodeId(Long codeId);
void updatePackingNum(Set<Long> ids);
List<MaterialItemPrintVO> getListByMaterialNos(Set<String> materialNos);
List<MaterialItemPrintVO> getListByItemIds(List<Long> ids);
}

View File

@ -5,6 +5,7 @@ import com.nflg.wms.common.pojo.vo.ShipmentMaterialCodeQRVO;
import com.nflg.wms.repository.entity.WmsShipmentMaterialCodeItemQr;
import java.util.List;
import java.util.Set;
/**
* <p>
@ -21,4 +22,6 @@ public interface WmsShipmentMaterialCodeItemQrMapper extends BaseMapper<WmsShipm
List<ShipmentMaterialCodeQRVO> getListVOByItemIds(List<Long> ids);
List<ShipmentMaterialCodeQRVO> getListVOByCodeIds(List<Long> ids);
List<ShipmentMaterialCodeQRVO> getListVOByMaterialNos(Set<String> materialNos);
}

View File

@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.validation.constraints.NotEmpty;
import java.util.List;
import java.util.Set;
/**
* <p>
@ -22,4 +23,6 @@ public interface IWmsShipmentMaterialCodeItemQrService extends IService<WmsShipm
List<ShipmentMaterialCodeQRVO> getListVOByItemIds(List<Long> ids);
List<ShipmentMaterialCodeQRVO> getListVOByCodeIds(List<Long> ids);
List<ShipmentMaterialCodeQRVO> getListVOByMaterialNos(Set<String> materialNos);
}

View File

@ -1,5 +1,6 @@
package com.nflg.wms.repository.service;
import com.nflg.wms.common.pojo.vo.MaterialItemPrintVO;
import com.nflg.wms.common.pojo.vo.ShipmentMaterialCodeItemVO;
import com.nflg.wms.repository.entity.WmsShipmentMaterialCodeItem;
import com.baomidou.mybatisplus.extension.service.IService;
@ -20,4 +21,8 @@ public interface IWmsShipmentMaterialCodeItemService extends IService<WmsShipmen
List<ShipmentMaterialCodeItemVO> getByCodeId(Long codeId);
void updatePackingNum(Set<Long> ids);
List<MaterialItemPrintVO> getListByMaterialNos(Set<String> materialNos);
List<MaterialItemPrintVO> getListByItemIds(List<Long> ids);
}

View File

@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Set;
/**
* <p>
@ -34,4 +35,9 @@ public class WmsShipmentMaterialCodeItemQrServiceImpl extends ServiceImpl<WmsShi
public List<ShipmentMaterialCodeQRVO> getListVOByCodeIds(List<Long> ids) {
return baseMapper.getListVOByCodeIds(ids);
}
@Override
public List<ShipmentMaterialCodeQRVO> getListVOByMaterialNos(Set<String> materialNos) {
return baseMapper.getListVOByMaterialNos(materialNos);
}
}

View File

@ -1,5 +1,6 @@
package com.nflg.wms.repository.service.impl;
import com.nflg.wms.common.pojo.vo.MaterialItemPrintVO;
import com.nflg.wms.common.pojo.vo.ShipmentMaterialCodeItemVO;
import com.nflg.wms.repository.entity.WmsShipmentMaterialCodeItem;
import com.nflg.wms.repository.mapper.WmsShipmentMaterialCodeItemMapper;
@ -30,4 +31,14 @@ public class WmsShipmentMaterialCodeItemServiceImpl extends ServiceImpl<WmsShipm
public void updatePackingNum(Set<Long> ids) {
baseMapper.updatePackingNum(ids);
}
@Override
public List<MaterialItemPrintVO> getListByMaterialNos(Set<String> materialNos) {
return baseMapper.getListByMaterialNos(materialNos);
}
@Override
public List<MaterialItemPrintVO> getListByItemIds(List<Long> ids) {
return baseMapper.getListByItemIds(ids);
}
}

View File

@ -25,4 +25,28 @@
#{id}
</foreach>
</update>
<select id="getListByMaterialNos" resultType="com.nflg.wms.common.pojo.vo.MaterialItemPrintVO">
SELECT it.id, it.material_no, it.material_describe, it.num, it.actual_num, it.unit,
it.project_type, it.production_order_number, it.box_no,
mc.device_no, mc.customer_name
FROM wms_shipment_material_code_item it
INNER JOIN wms_shipment_material_code mc ON mc."id" = it.material_code_id
WHERE it.material_no IN
<foreach item="item" collection="materialNos" separator="," open="(" close=")">
#{item}
</foreach>
</select>
<select id="getListByItemIds" resultType="com.nflg.wms.common.pojo.vo.MaterialItemPrintVO">
SELECT it.id, it.material_no, it.material_describe, it.num, it.actual_num, it.unit,
it.project_type, it.production_order_number, it.box_no,
mc.device_no, mc.customer_name
FROM wms_shipment_material_code_item it
INNER JOIN wms_shipment_material_code mc ON mc."id" = it.material_code_id
WHERE it.id IN
<foreach item="item" collection="ids" separator="," open="(" close=")">
#{item}
</foreach>
</select>
</mapper>

View File

@ -31,4 +31,15 @@
#{item}
</foreach>
</select>
<select id="getListVOByMaterialNos" resultType="com.nflg.wms.common.pojo.vo.ShipmentMaterialCodeQRVO">
SELECT qr.id,qr.no,it.material_no,it.material_describe,qr.num,it.unit,qr.status,mc.device_no,mc.customer_name
FROM wms_shipment_material_code_item_qr qr
INNER JOIN wms_shipment_material_code_item it ON qr.item_id=it."id"
INNER JOIN wms_shipment_material_code mc ON mc."id"=it.material_code_id
where it.material_no in
<foreach item="item" collection="materialNos" separator="," open="(" close=")">
#{item}
</foreach>
</select>
</mapper>

View File

@ -9,9 +9,11 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nflg.wms.common.pojo.ApiResult;
import com.nflg.wms.common.pojo.PageData;
import com.nflg.wms.common.pojo.dto.MaterialCodePrintDTO;
import com.nflg.wms.common.pojo.dto.MaterialPdfDTO;
import com.nflg.wms.common.pojo.qo.*;
import com.nflg.wms.common.pojo.vo.MaterialPdfVO;
import com.nflg.wms.common.pojo.vo.MaterialItemPrintVO;
import com.nflg.wms.common.pojo.vo.ShipmentMaterialCodeQRVO;
import com.nflg.wms.common.pojo.vo.ShipmentMaterialCodeItemVO;
import com.nflg.wms.common.util.EecExcelUtil;
@ -51,6 +53,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@ -819,4 +822,92 @@ public class MaterialCodeController extends BaseController {
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
return ApiResult.success(fileUploadService.upload("tmp/sp/" + RandomUtil.randomString(10) + "/" + material.getNo() + "老鼠图.pdf", inputStream, MediaType.APPLICATION_PDF_VALUE));
}
/**
* 导出标签图片PDF打印预览
*
* @param response HTTP响应
* @param ids 物料主数据id列表wms_shipment_material.id
*/
@PostMapping("exportToPdf")
public void exportToPdf(HttpServletResponse response, @RequestBody @NotEmpty List<Long> ids) throws Exception {
// 根据清单ID查询二维码数据
List<ShipmentMaterialCodeQRVO> datas = materialCodeItemQrService.getListVOByCodeIds(ids);
VUtil.trueThrowBusinessError(CollectionUtil.isEmpty(datas)).throwMessage("物料数据不存在");
// 取第一条数据
ShipmentMaterialCodeQRVO data = datas.get(0);
// 根据物料编号查询物料图片
Map<String, String> images = materialService.lambdaQuery()
.select(WmsShipmentMaterial::getNo, WmsShipmentMaterial::getImage)
.in(WmsShipmentMaterial::getNo, datas.stream().map(ShipmentMaterialCodeQRVO::getMaterialNo).collect(Collectors.toSet()))
.list()
.stream()
.collect(Collectors.toMap(WmsShipmentMaterial::getNo, WmsShipmentMaterial::getImage));
// 构建打印数据
Map<String, Object> variables = new HashMap<>();
Map<String, Object> info = new HashMap<>();
info.put("no", data.getNo());
info.put("materialNo", data.getMaterialNo());
info.put("numText", data.getNumText());
info.put("customerName", data.getCustomerName());
Map<String, String> ext = new HashMap<>();
ext.put("qrCode", QRCodeUtil.generateQRCodeBase64(data.getNo(), 200, 200));
ext.put("lst", images.get(data.getMaterialNo()));
variables.put("info", info);
variables.put("ext", ext);
String html = ThymeleafUtil.generator("/template/label/", "material-pdf", ".html", variables);
URL baseUrl = new ClassPathResource("template/label/").getURL();
PdfGeneratorUtil.generatePdf("物料码标签", html, baseUrl.toString(), response);
}
/**
* 根据明细ID导出物料明细PDF打印预览
*
* @param response HTTP响应
* @param ids 明细id列表wms_shipment_material_code_item.id
*/
@PostMapping("exportItemToPdf")
public void exportItemToPdf(HttpServletResponse response, @RequestBody @NotEmpty List<Long> ids) throws Exception {
// 根据明细ID查询数据
List<ShipmentMaterialCodeQRVO> datas = materialCodeItemQrService.getListVOByItemIds(ids);
VUtil.trueThrowBusinessError(CollectionUtil.isEmpty(datas)).throwMessage("没有需要导出的数据");
// 取第一条数据
ShipmentMaterialCodeQRVO data = datas.get(0);
// 根据物料编号查询物料图片
Map<String, String> images = materialService.lambdaQuery()
.select(WmsShipmentMaterial::getNo, WmsShipmentMaterial::getImage)
.in(WmsShipmentMaterial::getNo, datas.stream().map(ShipmentMaterialCodeQRVO::getMaterialNo).collect(Collectors.toSet()))
.list()
.stream()
.collect(Collectors.toMap(WmsShipmentMaterial::getNo, WmsShipmentMaterial::getImage));
// 构建打印数据
Map<String, Object> variables = new HashMap<>();
Map<String, Object> info = new HashMap<>();
info.put("no", data.getNo());
info.put("materialNo", data.getMaterialNo());
info.put("numText", data.getNumText());
info.put("customerName", data.getCustomerName());
Map<String, String> ext = new HashMap<>();
ext.put("qrCode", QRCodeUtil.generateQRCodeBase64(data.getNo(), 200, 200));
ext.put("lst", images.get(data.getMaterialNo()));
variables.put("info", info);
variables.put("ext", ext);
String html = ThymeleafUtil.generator("/template/label/", "material-pdf", ".html", variables);
URL baseUrl = new ClassPathResource("template/label/").getURL();
PdfGeneratorUtil.generatePdf("物料明细标签", html, baseUrl.toString(), response);
}
}

View File

@ -22,6 +22,7 @@ import com.nflg.wms.repository.service.*;
import com.nflg.wms.shipment.service.BasdeSerialNumberControllerService;
import com.nflg.wms.shipment.util.HtmlToImageUtil;
import com.nflg.wms.shipment.util.KeyUtil;
import com.nflg.wms.shipment.util.PdfGeneratorUtil;
import com.nflg.wms.shipment.util.QRCodeUtil;
import com.nflg.wms.shipment.util.ThymeleafUtil;
import com.nflg.wms.starter.BaseController;
@ -34,10 +35,12 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.core.io.ClassPathResource;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.io.ByteArrayOutputStream;
import java.net.URL;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
@ -406,4 +409,33 @@ public class PackagingCodeController extends BaseController {
headers.setContentLength(zipBytes.length);
return new ResponseEntity<>(zipBytes, headers, HttpStatus.OK);
}
/**
* 导出标签图片PDF打印预览
*/
@PostMapping("exportToPdf")
public void exportToPdf(HttpServletResponse response, @RequestBody @NotEmpty List<ShipmentPackagingCodeVO> list) throws Exception {
List<DictionaryItem> types = dictionaryItemService.getListByDictionaryCode("PackagingType");
// 构建打印数据
Map<String, Object> variables = new HashMap<>();
variables.put("list", list.stream()
.map(data -> {
ShipmentPackagingCodeVO vo = new ShipmentPackagingCodeVO();
vo.setNo(data.getNo());
vo.setName(data.getName());
vo.setTypeName(types.stream()
.filter(type -> type.getId().equals(data.getType()))
.map(DictionaryItem::getName)
.findFirst()
.orElse(""));
vo.setQrCode(QRCodeUtil.generateQRCodeBase64(data.getNo(), 100, 100));
return vo;
}).toList()
);
String html = ThymeleafUtil.generator("/template/label/", "packaging-pdf", ".html", variables);
URL baseUrl = new ClassPathResource("template/label/").getURL();
PdfGeneratorUtil.generatePdf("包装码标签", html, baseUrl.toString(), response);
}
}

View File

@ -0,0 +1,90 @@
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>包装码</title>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<style>
@page {
size: 1200px 800px;
margin: 0;
}
body {
width: 1200px;
height: 800px;
font-size: 20pt;
font-family: SimSun, Arial, sans-serif;
margin: 0;
padding: 0;
}
table {
width: 1200px;
height: 800px;
border: 3px solid #000;
border-collapse: collapse;
table-layout: fixed;
}
th, td {
border: 3px solid #000;
padding: 10px;
text-align: center;
vertical-align: middle;
word-wrap: break-word;
overflow-wrap: break-word;
}
.qrcode {
width: 380px;
height: 380px;
}
.lst{
width: 800px;
height: auto;
}
</style>
</head>
<body>
<table>
<tr>
<td style="text-align: center; width: 400px" rowspan="3">
<img alt="" class="qrcode" th:src="${ext.qrCode}"/>
<div style="font-size: 16pt" th:text="${info.no}">0PC7B724KV6FM</div>
</td>
<td style="width: 140px;">
机台编号
</td>
<td style="width: 200px;" th:text="${info.no}">
26LBZ4000L001
</td>
<td style="width: 140px;">
客户名称
</td>
<td style="text-align: left" th:text="${info.customerName}">
北京市京联鑫路用材料有限公司
</td>
</tr>
<tr>
<td>
物料编码
</td>
<td th:text="${info.materialNo}">
3100006701
</td>
<td>
数量
</td>
<td th:text="${info.numText}">
10
</td>
</tr>
<tr>
<td colspan="4">
<img style="width: 750px;" alt="" src="https://img-s.msn.cn/tenant/amp/entityid/AA1VWN3Q.img?w=768&h=333&m=6" class="lst" th:src="${ext.lst}"/>
</td>
</tr>
</table>
</body>
</html>

View File

@ -0,0 +1,92 @@
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>包装码标签-A4打印版</title>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<style>
@page {
size: A4 portrait;
margin: 0;
}
body {
font-size: 10pt;
font-family: SimSun, Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.label-page {
width: 210mm;
height: 297mm;
display: flex;
align-items: center;
justify-content: center;
page-break-after: always;
position: relative;
}
.label-page:last-child {
page-break-after: auto;
}
.label-content {
width: 600px;
height: 400px;
transform-origin: center center;
transform: scale(calc(210mm / 600px));
}
table {
width: 600px;
height: 400px;
border: 3px solid #000;
border-collapse: collapse;
}
th, td {
border: 3px solid #000;
}
.div-with-border {
border-bottom: 3px solid #000;
padding: 18px;
text-align: left;
width: 320px;
display: block;
vertical-align: middle
}
.qrcode {
width: 260px;
height: 260px;
margin: 10px;
}
</style>
</head>
<body>
<div class="label-page" th:each="item, iterStat : ${list}">
<div class="label-content">
<table>
<tr>
<td style="text-align: center;">
<img alt="" class="qrcode" th:src="${item.qrCode}"/>
<div style="font-size: 16pt" th:text="${item.no}">NFLG-QZ-002</div>
</td>
<td style="text-align: left;vertical-align: top">
<div class="div-with-border">包装名称: <span th:text="${item.name}">NFLG-QZ-002 测试包装名称</span></div>
<div class="div-with-border">包装类型: <span th:text="${item.typeName}">托盘</span></div>
</td>
</tr>
</table>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,210 @@
package com.nflg.wms.shipment;
import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.nflg.wms.repository.entity.WmsShipmentMaterial;
import com.nflg.wms.repository.mapper.WmsShipmentMaterialMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.*;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.io.File;
/**
* 物料图片批量上传测试
* 从桌面的Output文件夹读取所有图片并上传然后更新物料表的image字段
*/
@Slf4j
@SpringBootTest
@ContextConfiguration(classes = ShipmentApplication.class)
public class MaterialImageUploadTest {
@Resource
private WmsShipmentMaterialMapper materialMapper;
@Resource
private RestTemplate restTemplate;
// 文件上传接口地址
private static final String UPLOAD_URL = "https://pomp.nflg.net/api/admin/file/uploadSingleFile1";
@Test
public void testUploadImagesFromZip() {
// 桌面路径
String desktopPath = System.getProperty("user.home") + File.separator + "Desktop";
String outputDirPath = desktopPath + File.separator + "Output";
log.info("开始处理Output文件夹: {}", outputDirPath);
// 检查文件夹是否存在
File outputDir = new File(outputDirPath);
if (!outputDir.exists()) {
log.error("Output文件夹不存在: {}", outputDirPath);
throw new RuntimeException("Output文件夹不存在: " + outputDirPath);
}
if (!outputDir.isDirectory()) {
log.error("路径不是文件夹: {}", outputDirPath);
throw new RuntimeException("路径不是文件夹: " + outputDirPath);
}
// 递归获取所有图片文件
java.util.List<File> imageFiles = new java.util.ArrayList<>();
collectImageFiles(outputDir, imageFiles);
if (imageFiles.isEmpty()) {
log.warn("Output文件夹中没有找到图片文件");
return;
}
log.info("找到 {} 个图片文件", imageFiles.size());
// 处理每个图片文件
int successCount = 0;
int notFoundCount = 0;
int errorCount = 0;
for (File imageFile : imageFiles) {
try {
// 获取文件名不带后缀
String fileNameWithoutExt = FileUtil.getPrefix(imageFile.getName());
log.info("处理文件: {}, 文件名(无后缀): {}", imageFile.getName(), fileNameWithoutExt);
// 上传文件
String url = uploadFile(imageFile);
log.info("文件上传成功, url: {}", url);
// 根据no字段查找物料
WmsShipmentMaterial material = materialMapper.selectOne(
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<WmsShipmentMaterial>()
.eq(WmsShipmentMaterial::getNo, fileNameWithoutExt)
);
if (material != null) {
// 更新image字段
LambdaUpdateWrapper<WmsShipmentMaterial> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(WmsShipmentMaterial::getNo, fileNameWithoutExt)
.set(WmsShipmentMaterial::getImage, url);
int updateCount = materialMapper.update(null, updateWrapper);
if (updateCount > 0) {
imageFile.delete();
log.info("成功更新物料 {} 的图片url", fileNameWithoutExt);
successCount++;
} else {
log.error("更新物料 {} 的图片url失败", fileNameWithoutExt);
errorCount++;
}
} else {
log.warn("未找到物料编号为 {} 的物料记录", fileNameWithoutExt);
notFoundCount++;
}
} catch (Exception e) {
log.error("处理文件 {} 时发生错误: {}", imageFile.getName(), e.getMessage(), e);
errorCount++;
}
}
// 输出统计信息
log.info("========================================");
log.info("处理完成!");
log.info("总文件数: {}", imageFiles.size());
log.info("成功更新: {}", successCount);
log.info("未找到物料: {}", notFoundCount);
log.info("处理错误: {}", errorCount);
log.info("========================================");
}
/**
* 上传文件 - 调用HTTP接口
*/
private String uploadFile(File file) {
// 构建multipart请求
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.add("authorization", "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOjEsInJuU3RyIjoiM3pCT2dQT203ZmZTVEY5S0gxYldaZ0w0ZmJGVW85TnoiLCJuYW1lIjoi6LaF57qn566h55CG5ZGYIiwiY29kZSI6ImFkbWluIiwicm9sZXMiOlsiU3VwZXJBZG1pbiJdLCJ0eXBlIjoxfQ.Ircp8zNaLX7Wly7IZxEEdQrFo2BVfufXxXDhn7wSdrk");
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file", new FileSystemResource(file));
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
// 使用String接收响应避免Jackson反序列化LocalDateTime的问题
ResponseEntity<String> responseEntity = restTemplate.postForEntity(UPLOAD_URL, requestEntity, String.class);
// 检查响应
if (responseEntity.getStatusCode() != HttpStatus.OK || responseEntity.getBody() == null) {
throw new RuntimeException("文件上传失败HTTP状态码: " + responseEntity.getStatusCode());
}
// 手动解析JSON
String responseBody = responseEntity.getBody();
log.debug("上传响应: {}", responseBody);
cn.hutool.json.JSONObject jsonObject = cn.hutool.json.JSONUtil.parseObj(responseBody);
int code = jsonObject.getInt("code", -1);
if (code != 200) {
String message = jsonObject.getStr("message", "未知错误");
throw new RuntimeException("文件上传失败: " + message);
}
// 提取url
cn.hutool.json.JSONObject result = jsonObject.getJSONObject("result");
if (result == null) {
throw new RuntimeException("文件上传失败: 返回结果为空");
}
String url = result.getStr("url");
if (url == null || url.isEmpty()) {
throw new RuntimeException("文件上传失败: URL为空");
}
return url;
}
/**
* 递归收集所有图片文件
*/
private void collectImageFiles(File dir, java.util.List<File> imageFiles) {
File[] files = dir.listFiles();
if (files == null) {
return;
}
for (File file : files) {
if (file.isDirectory()) {
collectImageFiles(file, imageFiles);
} else {
String lowerName = file.getName().toLowerCase();
if (lowerName.endsWith(".jpg") || lowerName.endsWith(".jpeg") ||
lowerName.endsWith(".png") || lowerName.endsWith(".gif") ||
lowerName.endsWith(".bmp")) {
imageFiles.add(file);
}
}
}
}
/**
* 递归删除目录
*/
private void deleteDirectory(File file) {
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files != null) {
for (File f : files) {
deleteDirectory(f);
}
}
}
file.delete();
}
}