feat(barcode): 新增条码打印功能的工厂仓库储位管理

- 在BarcodePrintingAddQO中新增工厂、仓库、储位相关字段验证
- 在BarcodePrintingEditQO中新增工厂仓库储位的ID和编号字段
- 在BarcodePrintingVO中新增工厂仓库储位显示字段
- 扩展BarCodeProcessStage枚举,新增入库、出库、质检等状态
- 扩展BarCodeType枚举,新增采购、库存、生产等条码类型
- 集成dictionaryItemService、IWmsWarehouseService、IWmsBinService服务
- 优化条码生成逻辑,关联工厂、仓库、储位信息
- 添加工厂仓库储位数据验证和错误处理机制
- 新增大包装条码标签HTML模板文件
This commit is contained in:
曹鹏飞 2026-03-17 18:07:05 +08:00
parent f1eb8682fe
commit 273a51613d
24 changed files with 918 additions and 91 deletions

View File

@ -7,6 +7,7 @@ import cn.hutool.core.util.StrUtil;
import com.nflg.wms.admin.util.*;
import com.nflg.wms.common.constant.BarCodeProcessStage;
import com.nflg.wms.common.constant.BarCodeType;
import com.nflg.wms.common.constant.Constant;
import com.nflg.wms.common.constant.STATE;
import com.nflg.wms.common.pojo.ApiResult;
import com.nflg.wms.common.pojo.PageData;
@ -15,10 +16,8 @@ import com.nflg.wms.common.pojo.dto.QrCodeMasterPrintDTO;
import com.nflg.wms.common.pojo.qo.*;
import com.nflg.wms.common.pojo.vo.BarcodePrintingVO;
import com.nflg.wms.common.util.*;
import com.nflg.wms.repository.entity.WmsInventoryBarcodePrinting;
import com.nflg.wms.repository.entity.WmsQrCodeMaster;
import com.nflg.wms.repository.service.IWmsInventoryBarcodePrintingService;
import com.nflg.wms.repository.service.IWmsQrCodeMasterService;
import com.nflg.wms.repository.entity.*;
import com.nflg.wms.repository.service.*;
import com.nflg.wms.starter.annotation.ApiMark;
import com.nflg.wms.starter.service.FileUploadService;
import jakarta.annotation.Resource;
@ -70,6 +69,15 @@ public class BarcodePrintingController extends BaseController {
@Resource
private IWmsQrCodeMasterService qrCodeMasterService;
@Resource
private IDictionaryItemService dictionaryItemService;
@Resource
private IWmsWarehouseService warehouseService;
@Resource
private IWmsBinService wmsBinService;
/**
* 列表
*/
@ -85,12 +93,7 @@ public class BarcodePrintingController extends BaseController {
@PostMapping("items")
@ApiMark(moduleName = "仓储物流打印", apiName = "获取编辑记录")
public ApiResult<List<BarcodePrintingVO>> getItems(@Valid @RequestBody @NotEmpty List<Long> ids) {
List<WmsInventoryBarcodePrinting> items = printingService.listByIds(ids);
List<BarcodePrintingVO> vos = new ArrayList<>();
if (CollectionUtil.isNotEmpty(items)) {
vos = Convert.toList(BarcodePrintingVO.class, items);
}
return ApiResult.success(vos);
return ApiResult.success(printingService.getVOList(ids));
}
/**
@ -144,7 +147,7 @@ public class BarcodePrintingController extends BaseController {
.limit(it.getLabelNum())
.map(index -> new WmsQrCodeMaster()
.setBarcodeCode(KeyUtil.next())
.setProcessStage(BarCodeProcessStage.Unpackaged.getState())
.setProcessStage(BarCodeProcessStage.InBound.getState())
.setBarcodeType(BarCodeType.Inventory.getState())
.setMaterialCode(it.getMaterialNo())
.setMaterialDescription(it.getMaterialDes())
@ -153,6 +156,9 @@ public class BarcodePrintingController extends BaseController {
.setBatchNo(it.getBatchNumber())
.setSerialNo(CollectionUtil.get(StrUtil.split(it.getSerialNumbers(), ","), index - 1))
.setExtendId(it.getId())
.setFactoryCode(it.getFactoryCode())
.setStorageLocation(it.getWarehouseNo())
.setBinLocation(it.getBinNo())
.setCreateUserId(UserUtil.getUserId())
.setCreateUserName(UserUtil.getUserName())
.setCreateTime(LocalDateTime.now())
@ -174,7 +180,7 @@ public class BarcodePrintingController extends BaseController {
.limit(it.getLabelNum())
.map(index -> new WmsQrCodeMaster()
.setBarcodeCode(KeyUtil.next())
.setProcessStage(BarCodeProcessStage.Unpackaged.getState())
.setProcessStage(BarCodeProcessStage.InBound.getState())
.setBarcodeType(BarCodeType.Inventory.getState())
.setMaterialCode(it.getMaterialNo())
.setMaterialDescription(it.getMaterialDes())
@ -183,6 +189,9 @@ public class BarcodePrintingController extends BaseController {
.setBatchNo(it.getBatchNumber())
.setSerialNo(CollectionUtil.get(StrUtil.split(it.getSerialNumbers(), ","), index - 1))
.setExtendId(it.getId())
.setFactoryCode(it.getFactoryCode())
.setStorageLocation(it.getWarehouseNo())
.setBinLocation(it.getBinNo())
.setCreateUserId(UserUtil.getUserId())
.setCreateUserName(UserUtil.getUserName())
.setCreateTime(LocalDateTime.now())
@ -219,8 +228,11 @@ public class BarcodePrintingController extends BaseController {
VUtil.trueThrowBusinessError(CollectionUtil.isEmpty(data)).throwMessage("导入文件内容为空");
// 检查
List<WmsInventoryBarcodePrinting> codes = new ArrayList<>();
List<DictionaryItem> factorys = dictionaryItemService.getListByDictionaryCode(Constant.DICTIONARY_FACTORY);
List<WmsWarehouse> warehouses = warehouseService.lambdaQuery().select(WmsWarehouse::getId, WmsWarehouse::getNo).list();
List<WmsBin> bins = wmsBinService.lambdaQuery().select(WmsBin::getId, WmsBin::getNo).list();
for (BarcodePrintingAddDTO item : data) {
Long factoryId,warehouseId,binId = null;
StringBuilder errorBuild = new StringBuilder();
if (StrUtil.isBlank(item.getMaterialNo())) {
errorBuild.append("物料编号必填;");
@ -228,6 +240,49 @@ public class BarcodePrintingController extends BaseController {
if (StrUtil.isBlank(item.getMaterialDes())) {
errorBuild.append("物料描述必填;");
}
if (StrUtil.isBlank(item.getFactoryCode())) {
errorBuild.append("所属工厂必填;");
factoryId = factorys.stream()
.filter(it -> StrUtil.equals(it.getCode(), item.getFactoryCode()))
.findFirst()
.map(DictionaryItem::getId)
.orElse(null);
if (Objects.isNull(factoryId)){
errorBuild.append("所属工厂不存在;");
}
} else {
factoryId = null;
}
if (StrUtil.isBlank(item.getWarehouseNo())) {
errorBuild.append("库存地点必填;");
if (Objects.nonNull(factoryId)){
warehouseId = warehouses.stream()
.filter(it -> Objects.equals(it.getFactoryId(), factoryId) && StrUtil.equals(it.getNo(), item.getWarehouseNo()))
.findFirst()
.map(WmsWarehouse::getId)
.orElse(null);
if (Objects.isNull(warehouseId)){
errorBuild.append("库存地点不存在;");
}
} else {
warehouseId = null;
}
} else {
warehouseId = null;
}
if (StrUtil.isBlank(item.getBinNo())) {
errorBuild.append("储位必填;");
if (Objects.nonNull(warehouseId)){
binId = bins.stream()
.filter(it -> Objects.equals(it.getWarehouseId(), warehouseId) && StrUtil.equals(it.getNo(), item.getBinNo()))
.findFirst()
.map(WmsBin::getId)
.orElse(null);
if (Objects.isNull(binId)){
errorBuild.append("储位不存在;");
}
}
}
if (StrUtil.isBlank(item.getUnit())) {
errorBuild.append("单位必填;");
}
@ -245,6 +300,12 @@ public class BarcodePrintingController extends BaseController {
item.setError(errorBuild.toString());
} else {
WmsInventoryBarcodePrinting entity = Convert.convert(WmsInventoryBarcodePrinting.class, item);
entity.setFactoryId(factoryId);
entity.setFactoryCode(item.getFactoryCode());
entity.setWarehouseId(warehouseId);
entity.setWarehouseNo(item.getWarehouseNo());
entity.setBinId(binId);
entity.setBinNo(item.getBinNo());
entity.setCreateBy(UserUtil.getUserName());
entity.setCreateTime(LocalDateTime.now());
codes.add(entity);
@ -260,7 +321,7 @@ public class BarcodePrintingController extends BaseController {
.limit(it.getLabelNum())
.map(index -> new WmsQrCodeMaster()
.setBarcodeCode(KeyUtil.next())
.setProcessStage(BarCodeProcessStage.Unpackaged.getState())
.setProcessStage(BarCodeProcessStage.InBound.getState())
.setBarcodeType(BarCodeType.Inventory.getState())
.setMaterialCode(it.getMaterialNo())
.setMaterialDescription(it.getMaterialDes())
@ -269,6 +330,9 @@ public class BarcodePrintingController extends BaseController {
.setBatchNo(it.getBatchNumber())
.setSerialNo(CollectionUtil.get(StrUtil.split(it.getSerialNumbers(), ","), index - 1))
.setExtendId(it.getId())
.setFactoryCode(it.getFactoryCode())
.setStorageLocation(it.getWarehouseNo())
.setBinLocation(it.getBinNo())
.setCreateUserId(UserUtil.getUserId())
.setCreateUserName(UserUtil.getUserName())
.setCreateTime(LocalDateTime.now())

View File

@ -433,10 +433,17 @@ public class NormalOrderController extends BaseController {
* @param materials 物料列表
*/
@PostMapping("exportBoxImageZip")
public ResponseEntity<byte[]> exportBoxImageZip(@RequestBody @NotEmpty List<MaterialMinQO> materials) throws Exception {
public ResponseEntity<byte[]> exportBoxImageZip(@Valid @RequestBody @NotEmpty List<MaterialMinQO> materials) throws Exception {
//TODO 箱子码
List<UserSupplier> suppliers = userSupplierService.listByIds(materials.stream().map(MaterialMinQO::getSupplierId).toList());
List<WmsQrCodeMaster> qrCodeMasters = materials.stream()
.map(it -> new WmsQrCodeMaster()
.map(it -> {
UserSupplier supplier = suppliers.stream()
.filter(s -> s.getId().equals(it.getSupplierId()))
.findFirst()
.orElse(null);
VUtil.trueThrowBusinessError(Objects.isNull(supplier)).throwMessage("供应商不存在");
return new WmsQrCodeMaster()
.setBarcodeCode(KeyUtil.next())
.setProcessStage(BarCodeProcessStage.Unpackaged.getState())
.setBarcodeType(BarCodeType.Purchase.getState())
@ -445,11 +452,14 @@ public class NormalOrderController extends BaseController {
.setPackagingType((short) 1)
.setCreateUserId(UserUtil.getUserId())
.setUnit("")
.setQuantity(BigDecimal.valueOf(1.0))
.setSupplierCode(it.getSupplierCode())
.setQuantity(BigDecimal.ZERO)
.setSupplierCode(supplier.getSupplierCode())
.setSupplierId(it.getSupplierId())
.setSupplierName(supplier.getSupplierName())
.setBatchNo(NoUtil.getBatchNo(supplier.getSupplierCode()))
.setCreateUserName(UserUtil.getUserName())
.setCreateTime(LocalDateTime.now()))
.setCreateTime(LocalDateTime.now());
})
.toList();
qrCodeMasterService.saveBatch(qrCodeMasters);
List<QrCodeMasterPrintDTO> printDTOS = qrCodeMasters.stream().map(data ->
@ -463,8 +473,8 @@ public class NormalOrderController extends BaseController {
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
for (QrCodeMasterPrintDTO it : printDTOS) {
Map<String, Object> variables = new HashMap<>();
variables.put("list", List.of(it));
String html = ThymeleafUtil.generator("/template/qrcode/", "dp-1-label", ".html", variables);
variables.put("item", it);
String html = ThymeleafUtil.generator("/template/qrcode/", "medium-box-label", ".html", variables);
ZipEntry entry = new ZipEntry(it.getBarcodeCode() + ".png");
zos.putNextEntry(entry);
byte[] imageBytes = HtmlToImageUtil.convertToPng(html, 600);
@ -485,8 +495,15 @@ public class NormalOrderController extends BaseController {
*/
@PostMapping("exportBoxPdf")
public void exportBoxPdf(HttpServletResponse response, @RequestBody @NotEmpty List<MaterialMinQO> materials) throws Exception {
List<UserSupplier> suppliers = userSupplierService.listByIds(materials.stream().map(MaterialMinQO::getSupplierId).toList());
List<WmsQrCodeMaster> qrCodeMasters = materials.stream()
.map(it -> new WmsQrCodeMaster()
.map(it -> {
UserSupplier supplier = suppliers.stream()
.filter(s -> s.getId().equals(it.getSupplierId()))
.findFirst()
.orElse(null);
VUtil.trueThrowBusinessError(Objects.isNull(supplier)).throwMessage("供应商不存在");
return new WmsQrCodeMaster()
.setBarcodeCode(KeyUtil.next())
.setProcessStage(BarCodeProcessStage.Unpackaged.getState())
.setBarcodeType(BarCodeType.Purchase.getState())
@ -495,16 +512,20 @@ public class NormalOrderController extends BaseController {
.setPackagingType((short) 1)
.setCreateUserId(UserUtil.getUserId())
.setUnit("")
.setQuantity(BigDecimal.valueOf(1.0))
.setSupplierCode(it.getSupplierCode())
.setQuantity(BigDecimal.ZERO)
.setSupplierCode(supplier.getSupplierCode())
.setSupplierId(it.getSupplierId())
.setSupplierName(supplier.getSupplierName())
.setBatchNo(NoUtil.getBatchNo(supplier.getSupplierCode()))
.setCreateUserName(UserUtil.getUserName())
.setCreateTime(LocalDateTime.now()))
.setCreateTime(LocalDateTime.now());
}
)
.toList();
qrCodeMasterService.saveBatch(qrCodeMasters);
Map<String, Object> variables = new HashMap<>();
variables.put("list", convertToPrintDTO(qrCodeMasters));
String html = ThymeleafUtil.generator("/template/qrcode/", "dp-2", ".html", variables);
String html = ThymeleafUtil.generator("/template/qrcode/", "medium-box", ".html", variables);
URL baseUrl = new ClassPathResource("template/qrcode/").getURL();
PdfGeneratorUtil.generatePdf("箱码标签图片", html, baseUrl.toString(), response);
}

View File

@ -5,12 +5,15 @@ import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.nflg.wms.admin.service.BasdeSerialNumberControllerService;
import com.nflg.wms.admin.util.*;
import com.nflg.wms.common.constant.BarCodeProcessStage;
import com.nflg.wms.common.constant.BarCodeType;
import com.nflg.wms.common.constant.UserType;
import com.nflg.wms.common.pojo.ApiResult;
import com.nflg.wms.common.pojo.PageData;
import com.nflg.wms.common.pojo.dto.PackageChildDTO;
import com.nflg.wms.common.pojo.dto.PackageDTO;
import com.nflg.wms.common.pojo.dto.QrCodeMasterPrintDTO;
import com.nflg.wms.common.pojo.qo.*;
import com.nflg.wms.common.pojo.vo.*;
import com.nflg.wms.common.util.StringUtil;
@ -21,19 +24,31 @@ import com.nflg.wms.repository.service.*;
import com.nflg.wms.starter.BaseController;
import com.nflg.wms.starter.annotation.ApiMark;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Result;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.net.URL;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* 打包管理
@ -73,7 +88,8 @@ public class PackingController extends BaseController {
Short packageType = 0;
WmsPackage wmsPackage = new WmsPackage();
wmsPackage.setId(IdUtil.getSnowflakeNextId());
wmsPackage.setPackageCode(serialNumberControllerService.generateSerialNumber(29));
// wmsPackage.setPackageCode(serialNumberControllerService.generateSerialNumber(29));
wmsPackage.setPackageCode(KeyUtil.next());
wmsPackage.setPackageStatus(packageType);
wmsPackage.setSupplierId(UserUtil.getUserId());
wmsPackage.setCreateTime(LocalDateTime.now());
@ -199,7 +215,6 @@ public class PackingController extends BaseController {
/**
* 验证条形码并返回对应的物料数量
*
* @return 返回该条形码对应的物料数量类型为BigDecimal
*/
private PackageDTO BarcodeValidation(WmsQrCodeMaster qrCodeMaster, BarCodeProcessStage processStage) {
@ -242,7 +257,6 @@ public class PackingController extends BaseController {
/**
* 对SRM收货单物料进行校验确保收货数量合法
*
* @param item 收货单物料项包含物料的基本信息已收货数量和待收货数量等
* @param quantity 当前打包的收货数量
*/
@ -408,10 +422,10 @@ public class PackingController extends BaseController {
* 拆包
*/
@PostMapping("pda/unpacking/del")
@ApiMark(moduleName = "拆包-删除", apiName = "-拆包删除")
public ApiResult<Void> unpackingDel(@Valid @RequestBody PackingItemDelQO request) {
@ApiMark(moduleName = "拆包-删除", apiName = "拆包删除")
public ApiResult<Void> unpackingDel(@RequestBody @NotNull Long id) {
WmsPackageItem item = packageItemService.lambdaQuery()
.eq(WmsPackageItem::getId, request.getId())
.eq(WmsPackageItem::getId, id)
.one();
VUtil.trueThrowBusinessError(ObjectUtil.isNull(item))
.throwMessage("此条数据不存在");
@ -450,7 +464,56 @@ public class PackingController extends BaseController {
List<WmsQrCodeMaster> childMasters = Convert.toList(WmsQrCodeMaster.class, barcodeResult.getChildren());
qrCodeMasters.addAll(childMasters);
}
packageService.unpackingDel(request.getId(), qrCodeMasters, item.getPackageId());
packageService.unpackingDel(id, qrCodeMasters, item.getPackageId());
return ApiResult.success();
}
/**
* 导出包装箱标签图片为ZIP
*/
@PostMapping("exportToZip")
public ResponseEntity<byte[]> exportToZip(@Valid @RequestBody @NotEmpty List<PackingVO> datas) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
for (PackingVO it : datas) {
Map<String, Object> variables = new HashMap<>();
variables.put("item",new QrCodeMasterPrintDTO()
.setBarcodeCode(it.getPackageCode())
.setQrCode(QRCodeUtil.generateQRCodeBase64(it.getPackageCode(), 100, 100))
.setBatchNo(NoUtil.getBatchNo(it.getSupplierCode()))
.setSupplierName(it.getSupplierName())
);
String html = ThymeleafUtil.generator("/template/qrcode/", "big-box-label", ".html", variables);
ZipEntry entry = new ZipEntry(it.getPackageCode() + ".png");
zos.putNextEntry(entry);
byte[] imageBytes = HtmlToImageUtil.convertToPng(html, 600);
zos.write(imageBytes, 0, imageBytes.length);
zos.closeEntry();
}
}
byte[] zipBytes = baos.toByteArray();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.valueOf("application/zip"));
headers.setContentLength(zipBytes.length);
return new ResponseEntity<>(zipBytes, headers, HttpStatus.OK);
}
/**
* 导出包装箱标签图片为PDF
*/
@PostMapping("exportToPdf")
public void exportToPdf(HttpServletResponse response, @RequestBody @NotEmpty List<PackingVO> datas) throws Exception {
Map<String, Object> variables = new HashMap<>();
variables.put("list", datas.stream()
.map(data -> new QrCodeMasterPrintDTO()
.setBarcodeCode(data.getPackageCode())
.setQrCode(QRCodeUtil.generateQRCodeBase64(data.getPackageCode(), 100, 100))
.setBatchNo(NoUtil.getBatchNo(data.getSupplierCode()))
.setSupplierName(data.getSupplierName())
).toList()
);
String html = ThymeleafUtil.generator("/template/qrcode/", "big-box", ".html", variables);
URL baseUrl = new ClassPathResource("template/qrcode/").getURL();
PdfGeneratorUtil.generatePdf("包装箱标签图片", html, baseUrl.toString(), response);
}
}

View File

@ -3,36 +3,51 @@ package com.nflg.wms.admin.controller;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.nflg.wms.admin.util.HtmlToImageUtil;
import com.nflg.wms.admin.util.PdfGeneratorUtil;
import com.nflg.wms.admin.util.QRCodeUtil;
import com.nflg.wms.admin.util.ThymeleafUtil;
import com.nflg.wms.common.constant.BarCodeProcessStage;
import com.nflg.wms.common.pojo.ApiResult;
import com.nflg.wms.common.pojo.PageData;
import com.nflg.wms.common.pojo.dto.InventoryDTO;
import com.nflg.wms.common.pojo.dto.PackageChildDTO;
import com.nflg.wms.common.pojo.dto.QrCodeMasterPrintDTO;
import com.nflg.wms.common.pojo.qo.*;
import com.nflg.wms.common.pojo.vo.QrCodeItemVO;
import com.nflg.wms.common.pojo.vo.QrCodeVO;
import com.nflg.wms.common.pojo.vo.StrappingVO;
import com.nflg.wms.common.pojo.vo.TransferOrderVO;
import com.nflg.wms.common.util.DateTimeUtil;
import com.nflg.wms.common.util.NumberUtil;
import com.nflg.wms.common.util.UserUtil;
import com.nflg.wms.common.util.VUtil;
import com.nflg.wms.repository.entity.WmsQrCodeMaster;
import com.nflg.wms.repository.service.IWmsQrCodeMasterService;
import com.nflg.wms.starter.BaseController;
import com.nflg.wms.starter.annotation.ApiMark;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.net.URL;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* 装箱管理
@ -325,4 +340,52 @@ public class QrCodeMasterController extends BaseController {
}
return ApiResult.success(qrCodeVO);
}
/**
* 导出标签图片为ZIP
* @param datas 二维码列表
*/
@PostMapping(value = "exportToZip", produces = "application/zip")
public ResponseEntity<byte[]> exportToZip(@Valid @RequestBody @NotEmpty List<QrCodeItemVO> datas) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
for (QrCodeItemVO it : datas) {
QrCodeMasterPrintDTO dto = Convert.convert(QrCodeMasterPrintDTO.class, it);
dto.setQuantity(NumberUtil.format(it.getQuantity()));
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);
ZipEntry entry = new ZipEntry(it.getBarcodeCode() + ".png");
zos.putNextEntry(entry);
byte[] imageBytes = HtmlToImageUtil.convertToPng(html, 600);
zos.write(imageBytes, 0, imageBytes.length);
zos.closeEntry();
}
}
byte[] zipBytes = baos.toByteArray();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.valueOf("application/zip"));
headers.setContentLength(zipBytes.length);
return new ResponseEntity<>(zipBytes, headers, HttpStatus.OK);
}
/**
* 导出标签图片为PDF
* @param datas 二维码列表
*/
@PostMapping("exportToPdf")
public void exportToPdf(HttpServletResponse response, @RequestBody @NotEmpty List<QrCodeItemVO> datas) throws Exception {
Map<String, Object> variables = new HashMap<>();
variables.put("list", datas.stream().map(data ->
{
QrCodeMasterPrintDTO dto = Convert.convert(QrCodeMasterPrintDTO.class, data);
dto.setQuantity(NumberUtil.format(data.getQuantity()));
dto.setQrCode(QRCodeUtil.generateQRCodeBase64(generateQRContent(dto), 100, 100));
return dto;
}).toList());
String html = ThymeleafUtil.generator("/template/qrcode/", "dp-1", ".html", variables);
URL baseUrl = new ClassPathResource("template/qrcode/").getURL();
PdfGeneratorUtil.generatePdf("箱码标签图片", html, baseUrl.toString(), response);
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -8,15 +8,45 @@ import java.util.Objects;
@Getter
@AllArgsConstructor
public enum BarCodeProcessStage {
/**
* 未装箱
*/
Unpackaged((short) 0, "未装箱"),
/**
* 已装箱
*/
Packaged((short) 1, "已装箱"),
/**
* 未打包
*/
UnLoaded((short) 2, "未打包"),
/**
* 已打包
*/
Loaded((short) 3, "已打包"),
/**
* 已收货
*/
Received((short) 4, "已收货"),
/**
* 质检中
*/
Checking((short) 5, "质检中"),
/**
* 已入库
*/
InBound((short) 6, "已入库"),
/**
* 已出库
*/
OutBound((short) 7, "已出库"), //已被使用只有退库的时候才会被重新激活
/**
* 已挂起
*/
Hold((short) 8, "已挂起"),
/**
* 质检完成
*/
Checked((short) 9, "质检已完成"); // 用于仓库转储出库之后此二维码还需要继续使用用一个特殊的状态标记
private final short state;
private final String description;

View File

@ -9,10 +9,25 @@ import java.util.Objects;
@AllArgsConstructor
public enum BarCodeType {
/**
* 钢构包
*/
Package((short) 0, "钢构包"),
/**
* 采购物料
*/
Purchase((short) 1, "采购物料"),
/**
* 库存物料
*/
Inventory((short) 2, "库存物料"),
/**
* 拆解物料
*/
Disassemble((short) 3, "拆解(返修)物料"),
/**
* 生产物料
*/
Production((short) 4, "生产物料");
private final short state;

View File

@ -1,5 +1,6 @@
package com.nflg.wms.common.pojo.dto;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.experimental.Accessors;
@ -81,4 +82,9 @@ public class QrCodeMasterPrintDTO {
* 送货单行号
*/
private String deliveryLineNo;
/**
* 供应商名称
*/
private String supplierName;
}

View File

@ -25,6 +25,27 @@ public class BarcodePrintingAddQO {
@ExcelColumn("物料描述(*)")
private String materialDes;
/**
* 所属工厂
*/
@NotBlank
@ExcelColumn("所属工厂(*)")
private String factoryCode;
/**
* 仓库编号
*/
@NotBlank
@ExcelColumn("库存地点(*)")
private String warehouseNo;
/**
* 储位编号
*/
@NotBlank
@ExcelColumn("储位(*)")
private String binNo;
/**
* 单位
*/

View File

@ -62,4 +62,39 @@ public class BarcodePrintingEditQO {
return NumberUtil.calculateLabelNum(qty, packingNum);
}
/**
* 所属工厂id
*/
@NotNull
private Long factoryId;
/**
* 所属工厂
*/
@NotBlank
private String factoryCode;
/**
* 仓库id
*/
@NotNull
private Long warehouseId;
/**
* 仓库编号
*/
@NotBlank
private String warehouseNo;
/**
* 储位id
*/
@NotNull
private Long binId;
/**
* 储位编号
*/
@NotBlank
private String binNo;
}

View File

@ -1,5 +1,7 @@
package com.nflg.wms.common.pojo.qo;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Data
@ -8,6 +10,7 @@ public class MaterialMinQO {
/**
* 物料编号
*/
@NotBlank
private String materialNo;
/**
@ -15,13 +18,9 @@ public class MaterialMinQO {
*/
private String materialDes;
/**
* 供应商code
*/
private String supplierCode;
/**
* 供应商Id
*/
@NotNull
private Long supplierId;
}

View File

@ -1,5 +1,6 @@
package com.nflg.wms.common.pojo.vo;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.experimental.Accessors;
@ -21,6 +22,21 @@ public class BarcodePrintingVO {
*/
private String materialDes;
/**
* 所属工厂
*/
private String factoryCode;
/**
* 仓库编号
*/
private String warehouseNo;
/**
* 储位编号
*/
private String binNo;
/**
* 单位
*/

View File

@ -19,11 +19,13 @@ public class QrCodeItemVO {
/**
* 条码类型使用枚举扩展
* @see com.nflg.wms.common.constant.BarCodeType
*/
private Short barcodeType;
/**
* 条码流程位置
* @see com.nflg.wms.common.constant.BarCodeProcessStage
*/
private Short processStage;

View File

@ -1,8 +1,10 @@
package com.nflg.wms.repository.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@ -75,6 +77,39 @@ public class WmsInventoryBarcodePrinting implements Serializable {
*/
private Integer labelNum;
/**
* 所属工厂id
*/
private Long factoryId;
/**
* 所属工厂
*/
@TableField(exist = false)
private String factoryCode;
/**
* 仓库id
*/
private Long warehouseId;
/**
* 仓库编号
*/
@TableField(exist = false)
private String warehouseNo;
/**
* 储位id
*/
private Long binId;
/**
* 储位编号
*/
@TableField(exist = false)
private String binNo;
/**
* 创建人
*/

View File

@ -1,9 +1,6 @@
package com.nflg.wms.repository.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.Version;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@ -183,4 +180,10 @@ public class WmsQrCodeMaster implements Serializable {
* wms送货单行id
*/
private Long receiptItemId;
/**
* 供应商名称
*/
@TableField(exist = false)
private String supplierName;
}

View File

@ -9,6 +9,8 @@ import com.nflg.wms.common.pojo.vo.BarcodePrintingVO;
import com.nflg.wms.common.pojo.vo.SrmOrderVO;
import com.nflg.wms.repository.entity.WmsInventoryBarcodePrinting;
import java.util.List;
/**
* <p>
* Mapper 接口
@ -19,4 +21,6 @@ import com.nflg.wms.repository.entity.WmsInventoryBarcodePrinting;
*/
public interface WmsInventoryBarcodePrintingMapper extends BaseMapper<WmsInventoryBarcodePrinting> {
IPage<BarcodePrintingVO> search(BarcodePrintingQO request, Page<?> objectPage);
List<BarcodePrintingVO> getVOList(List<Long> ids);
}

View File

@ -6,6 +6,9 @@ import com.nflg.wms.common.pojo.vo.BarcodePrintingVO;
import com.nflg.wms.repository.entity.WmsInventoryBarcodePrinting;
import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import java.util.List;
/**
* <p>
@ -17,5 +20,7 @@ import jakarta.validation.Valid;
*/
public interface IWmsInventoryBarcodePrintingService extends IService<WmsInventoryBarcodePrinting> {
IPage<BarcodePrintingVO> search(@Valid BarcodePrintingQO request);
IPage<BarcodePrintingVO> search(BarcodePrintingQO request);
List<BarcodePrintingVO> getVOList(List<Long> ids);
}

View File

@ -10,6 +10,8 @@ import com.nflg.wms.repository.service.IWmsInventoryBarcodePrintingService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <p>
* 服务实现类
@ -25,4 +27,9 @@ public class WmsInventoryBarcodePrintingServiceImpl extends ServiceImpl<WmsInven
public IPage<BarcodePrintingVO> search(BarcodePrintingQO request) {
return baseMapper.search(request, new Page<>(request.getPage(), request.getPageSize()));
}
@Override
public List<BarcodePrintingVO> getVOList(List<Long> ids) {
return baseMapper.getVOList(ids);
}
}

View File

@ -3,50 +3,56 @@
<mapper namespace="com.nflg.wms.repository.mapper.WmsInventoryBarcodePrintingMapper">
<select id="search" resultType="com.nflg.wms.common.pojo.vo.BarcodePrintingVO">
SELECT
id,
material_no,
material_des,
unit,
qty,
batch_number,
serial_numbers,
packing_num,
create_by,
create_time,
update_by,
update_time
FROM
wms_inventory_barcode_printing
SELECT bp.id,bp.material_no,bp.material_des,bp.unit,bp.qty,bp.batch_number,bp.serial_numbers,bp.packing_num
,bp.create_by,bp.create_time,bp.update_by,bp.update_time,di.name as "factory_code",w.no as "warehouse_no"
,b.no as "bin_no"
FROM wms_inventory_barcode_printing bp
left join dictionary_item di on bp.factory_id=di.id
left join wms_warehouse w on bp.warehouse_id=w.id
left join wms_bin b on bp.bin_id=b.id
<where>
<if test="request.materialNos != null and request.materialNos != ''">
AND material_no IN
AND bp.material_no IN
<foreach item="materialNo" index="index" collection="request.materialNos.split(',')"
open="(" separator="," close=")">
#{materialNo}
</foreach>
</if>
<if test="request.batchNumbers != null and request.batchNumbers != ''">
AND batch_number IN
AND bp.batch_number IN
<foreach item="batchNo" index="index" collection="request.batchNumbers.split(',')"
open="(" separator="," close=")">
#{batchNo}
</foreach>
</if>
<if test="request.serialNumber != null and request.serialNumber != ''">
AND serial_numbers ilike CONCAT('%', #{request.serialNumber}, '%')
AND bp.serial_numbers ilike CONCAT('%', #{request.serialNumber}, '%')
</if>
<if test="request != null and request.materialDes != null and request.materialDes != ''">
AND material_des ilike CONCAT('%', #{request.materialDes}, '%')
AND bp.material_des ilike CONCAT('%', #{request.materialDes}, '%')
</if>
<if test="request != null and request.startDate != null">
AND create_time >= #{request.startDate}
AND bp.create_time >= #{request.startDate}
</if>
<if test="request != null and request.endDate != null">
AND create_time &lt;= #{request.endDate}
AND bp.create_time &lt;= #{request.endDate}
</if>
</where>
order by
id desc
</select>
<select id="getVOList" resultType="com.nflg.wms.common.pojo.vo.BarcodePrintingVO">
SELECT bp.id,bp.material_no,bp.material_des,bp.unit,bp.qty,bp.batch_number,bp.serial_numbers,bp.packing_num
,bp.create_by,bp.create_time,bp.update_by,bp.update_time,di.name as "factory_code",w.no as "warehouse_no"
,b.no as "bin_no"
FROM wms_inventory_barcode_printing bp
left join dictionary_item di on bp.factory_id=di.id
left join wms_warehouse w on bp.warehouse_id=w.id
left join wms_bin b on bp.bin_id=b.id
where bp.id in
<foreach item="id" index="index" collection="ids" open="(" separator="," close=")">
#{id}
</foreach>
</select>
</mapper>

View File

@ -0,0 +1,22 @@
package com.nflg.wms.shipment.util;
import java.io.File;
public class PathUtils {
public static String getPath() {
String classPath = PathUtils.class.getProtectionDomain().getCodeSource().getLocation().getPath();
System.out.println("classPath:" + classPath);
// 如果是jar包运行获取jar包所在目录
if (classPath.contains(".jar")) {
File jarFile = new File(classPath);
return jarFile.getParent();
}
// 如果是开发环境获取target/classes目录的父目录
File classDir = new File(classPath);
if (classDir.getName().equals("classes")) {
return classDir.getParentFile().getParentFile().getAbsolutePath();
}
return new File(classPath).getAbsolutePath();
}
}

View File

@ -0,0 +1,103 @@
package com.nflg.wms.shipment.util;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.utils.PdfMerger;
import com.lowagie.text.pdf.BaseFont;
import com.nflg.wms.common.util.VUtil;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.io.FilenameUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.xhtmlrenderer.pdf.ITextRenderer;
import java.io.*;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
public class PdfGeneratorUtil {
private static final Set<String> SUPPORTED_EXTENSIONS = new HashSet<>(Arrays.asList(
"ttf", "ttc", "otf", "pfb"
));
public static void generatePdf(String name,String html, HttpServletResponse response) throws Exception {
URL baseUrl = new ClassPathResource("template/").getURL();
generatePdf(name,html,baseUrl.toString(),response);
}
public static void generatePdf(String name,String html,String baseUrl, HttpServletResponse response) throws Exception {
ITextRenderer renderer = new ITextRenderer();
loadFonts(renderer);
renderer.setDocumentFromString(html,baseUrl);
renderer.layout();
renderer.createPDF(response.getOutputStream());
response.setContentType(MediaType.APPLICATION_PDF_VALUE);
String encode = URLEncoder.encode(name + ".pdf", StandardCharsets.UTF_8);
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "inline;filename*=UTF-8''" + encode);
}
public static void generatePdf(String name, String html,OutputStream output) throws Exception {
URL baseUrl = new ClassPathResource("template/").getURL();
generatePdf(name,html,baseUrl.toString(),output);
}
public static void generatePdf(String name, String html, String baseUrl, OutputStream output) throws Exception {
ITextRenderer renderer = new ITextRenderer();
loadFonts(renderer);
renderer.setDocumentFromString(html,baseUrl);
renderer.layout();
renderer.createPDF(output);
}
public static void generatePdf(String name, List<String> htmls, HttpServletResponse response) throws Exception {
response.setContentType(MediaType.APPLICATION_PDF_VALUE);
String encode = URLEncoder.encode(name + ".pdf", StandardCharsets.UTF_8);
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "inline;filename*=UTF-8''" + encode);
URL baseUrl = new ClassPathResource("template/").getURL();
List<byte[]> pdfParts = new ArrayList<>();
for (String html : htmls) {
ITextRenderer renderer = new ITextRenderer();
loadFonts(renderer);
renderer.setDocumentFromString(html, baseUrl.toString());
renderer.layout();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
renderer.createPDF(baos);
pdfParts.add(baos.toByteArray());
baos.close();
renderer.finishPDF();
}
mergePdfs(pdfParts, response.getOutputStream());
}
private static void mergePdfs(List<byte[]> pdfBytesList, OutputStream outputStream) throws IOException {
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(outputStream));
PdfMerger merger = new PdfMerger(pdfDoc);
for (byte[] bytes : pdfBytesList) {
PdfDocument srcDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(bytes)));
merger.merge(srcDoc, 1, srcDoc.getNumberOfPages());
srcDoc.close();
}
pdfDoc.close();
}
private static void loadFonts(ITextRenderer renderer) throws IOException {
Path fontsDir = Paths.get(PathUtils.getPath(), "fonts");
VUtil.trueThrowBusinessError(!Files.exists(fontsDir) || !Files.isDirectory(fontsDir))
.throwMessage("fonts文件夹不存在: " + fontsDir);
File directory = fontsDir.toFile();
File[] fonts = directory.listFiles((dir, name) -> SUPPORTED_EXTENSIONS.contains(FilenameUtils.getExtension(name)));
VUtil.trueThrowBusinessError(Objects.isNull(fonts) || fonts.length == 0).throwMessage("未找到有效字体");
for (File font : fonts) {
renderer.getFontResolver().addFont(font.getAbsolutePath(), BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
}
}
}