diff --git a/src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelExcelServiceImpl.java b/src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelExcelServiceImpl.java index 37ed90d4..d689519d 100644 --- a/src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelExcelServiceImpl.java +++ b/src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelExcelServiceImpl.java @@ -264,21 +264,21 @@ public class CoDelExcelServiceImpl implements CoDelExcelService { String cnativeKey = "cnative_" + cmcInvoice; String salesAreaKey = "salesArea_" + cmcInvoice; - // 从请求参数中获取按发票号设置的客户信息 - String customerName = request.getParameter(customerNameKey); - String localShipAddress = request.getParameter(localShipAddressKey); - String overseasShipper = request.getParameter(overseasShipperKey); - String overseasAddress = request.getParameter(overseasAddressKey); - String cnative = request.getParameter(cnativeKey); - String salesArea = request.getParameter(salesAreaKey); + // 从请求参数中获取按发票号设置的客户信息,并进行 HTML 反转义处理 + String customerName = unescapeHtml(request.getParameter(customerNameKey)); + String localShipAddress = unescapeHtml(request.getParameter(localShipAddressKey)); + String overseasShipper = unescapeHtml(request.getParameter(overseasShipperKey)); + String overseasAddress = unescapeHtml(request.getParameter(overseasAddressKey)); + String cnative = unescapeHtml(request.getParameter(cnativeKey)); + String salesArea = unescapeHtml(request.getParameter(salesAreaKey)); // 如果按发票号的信息不存在,则使用全局信息 - headerList.setCustomerName(customerName != null ? customerName : inData.getCustomerName()); - headerList.setLocalShipAddress(localShipAddress != null ? localShipAddress : inData.getLocalShipAddress()); - headerList.setOverseasShipper(overseasShipper != null ? overseasShipper : inData.getOverseasShipper()); - headerList.setOverseasAddress(overseasAddress != null ? overseasAddress : inData.getOverseasAddress()); - headerList.setCnative(cnative != null ? cnative : inData.getCnative()); - headerList.setSalesArea(salesArea != null ? salesArea : inData.getSalesArea()); + headerList.setCustomerName(customerName != null ? customerName : unescapeHtml(inData.getCustomerName())); + headerList.setLocalShipAddress(localShipAddress != null ? localShipAddress : unescapeHtml(inData.getLocalShipAddress())); + headerList.setOverseasShipper(overseasShipper != null ? overseasShipper : unescapeHtml(inData.getOverseasShipper())); + headerList.setOverseasAddress(overseasAddress != null ? overseasAddress : unescapeHtml(inData.getOverseasAddress())); + headerList.setCnative(cnative != null ? cnative : unescapeHtml(inData.getCnative())); + headerList.setSalesArea(salesArea != null ? salesArea : unescapeHtml(inData.getSalesArea())); headerList.setCmcInvoice(cmcInvoice); headerList.setCreateBy(inData.getUsername()); coDelMapper.saveEcssCoDelNotifyHeader(headerList); @@ -3446,40 +3446,77 @@ public class CoDelExcelServiceImpl implements CoDelExcelService { } /** - * 根据装箱明细的物料计算体积 - * 每种物料(每个pn是一种物料)维护的箱子的长*宽*高*箱数 + * 根据装箱明细计算总体积 * - * @return 总体积 + *

计算逻辑:

+ * + * + *

修复说明:

+ * 原方法使用 SQL 的 GROUP BY pn + SUM(DISTINCT box_qty), + * 导致10个相同pn的箱子被统计为1个箱子(DISTINCT去重)。 + * 现改为按箱号遍历,确保每个箱子都被正确计算。 + * + * @param notifyHeader 发货通知单头信息 + * @return 总体积(立方米,保留2位小数) */ private BigDecimal calculateVolumeByMaterials(EcssCoDelNotifyHeaderData notifyHeader) { - List palletDetailList = coDelMapper.getCoDelPalletDetailGroupByPn(notifyHeader); + try { + // 1. 查询箱子列表(每个箱子包含box_qty、长宽高等信息) + List boxList = coDelMapper.selectBoxList(notifyHeader); - if (palletDetailList == null || palletDetailList.isEmpty()) { - return BigDecimal.ZERO; - } + if (boxList == null || boxList.isEmpty()) { + log.debug("未找到装箱数据,发货单号: {}", notifyHeader.getDelNo()); + return BigDecimal.ZERO; + } - try { - // 获取所有物料编号 - List pns = palletDetailList.stream() - .map(detail -> (String) detail.get("pn")) - .filter(Objects::nonNull) - .distinct() - .collect(Collectors.toList()); + // 2. 查询所有装箱明细(获取每个箱子里的物料信息) + EcssCoDelPalletHeaderData queryParam = new EcssCoDelPalletHeaderData(); + queryParam.setSite(notifyHeader.getSite()); + queryParam.setBuNo(notifyHeader.getBuNo()); + queryParam.setDelNo(notifyHeader.getDelNo()); + + // 获取所有明细中的物料编号 + Set allPns = new HashSet<>(); + for (Map box : boxList) { + Object seqNoObj = box.get("item_no"); + if (seqNoObj == null) { + continue; + } - if (pns.isEmpty()) { + queryParam.setSeqNo(Integer.valueOf(seqNoObj.toString())); + List detailList = coDelMapper.selectPalletDetailList(queryParam); + + if (detailList != null && !detailList.isEmpty()) { + for (Map detail : detailList) { + String pn = (String) detail.get("pn"); + if (pn != null) { + allPns.add(pn); + } + } + } + } + + if (allPns.isEmpty()) { + log.warn("装箱明细中未找到物料信息,发货单号: {}", notifyHeader.getDelNo()); return BigDecimal.ZERO; } - // 根据物料编号获取packageNo - List packageNoList = coDelMapper.getPackageNoByPn(notifyHeader.getSite(), pns); + // 3. 获取物料对应的包装信息 + List pnList = new ArrayList<>(allPns); + List packageNoList = coDelMapper.getPackageNoByPn(notifyHeader.getSite(), pnList); Map partNoToPackageNoMap = packageNoList.stream() .collect(Collectors.toMap( map -> (String) map.get("pn"), map -> (String) map.get("packageNo"), - (existing, replacement) -> existing // 处理重复key的情况 + (existing, replacement) -> existing )); - // 获取所有packageNo对应的包装信息 + // 4. 获取包装的长宽高信息 List packageNos = packageNoList.stream() .map(map -> (String) map.get("packageNo")) .filter(Objects::nonNull) @@ -3487,11 +3524,10 @@ public class CoDelExcelServiceImpl implements CoDelExcelService { .collect(Collectors.toList()); if (packageNos.isEmpty()) { - log.warn("未找到物料对应的包装信息,物料编号: {}", pns); + log.warn("未找到物料对应的包装信息,发货单号: {}", notifyHeader.getDelNo()); return BigDecimal.ZERO; } - // 获取包装信息(长宽高) List packageDataList = coDelMapper.getPackageList(notifyHeader.getSite(), notifyHeader.getBuNo(), packageNos); Map packageNoToDataMap = packageDataList.stream() .collect(Collectors.toMap( @@ -3500,61 +3536,67 @@ public class CoDelExcelServiceImpl implements CoDelExcelService { (existing, replacement) -> existing )); - // 计算总体积 + // 5. 遍历每个箱子,计算体积 BigDecimal totalVolume = BigDecimal.ZERO; - for (Map detail : palletDetailList) { - String pn = (String) detail.get("pn"); - Object boxQtyObj = detail.get("box_qty"); + int processedBoxCount = 0; - if (pn == null || boxQtyObj == null) { + for (Map box : boxList) { + Object seqNoObj = box.get("item_no"); + if (seqNoObj == null) { continue; } - // 获取箱数 - BigDecimal boxQty; - if (boxQtyObj instanceof BigDecimal) { - boxQty = (BigDecimal) boxQtyObj; - } else { - boxQty = new BigDecimal(boxQtyObj.toString()); + // 查询该箱子的明细,获取主要物料(取第一个物料) + queryParam.setSeqNo(Integer.valueOf(seqNoObj.toString())); + List detailList = coDelMapper.selectPalletDetailList(queryParam); + + if (detailList == null || detailList.isEmpty()) { + log.warn("箱号 {} 没有装箱明细", seqNoObj); + continue; } - // 获取包装信息 - String packageNo = partNoToPackageNoMap.get(pn); - if (packageNo == null) { - log.warn("物料 {} 未维护packageNo", pn); + // 获取该箱子的第一个物料作为代表物料 + String mainPn = (String) detailList.get(0).get("pn"); + if (mainPn == null) { + log.warn("箱号 {} 的物料编号为空", seqNoObj); continue; } - EcssPackageData packageData = packageNoToDataMap.get(packageNo); - if (packageData == null) { - log.warn("包装编号 {} 未找到对应的包装信息", packageNo); + // 获取包装信息 + String packageNo = partNoToPackageNoMap.get(mainPn); + if (packageNo == null) { + log.warn("箱号 {} 的物料 {} 未维护packageNo", seqNoObj, mainPn); continue; } - // 检查长宽高是否都有值 - if (packageData.getLength() == null || packageData.getWidth() == null || packageData.getHeight() == null) { - log.warn("包装编号 {} 的长宽高信息不完整", packageNo); + EcssPackageData packageData = packageNoToDataMap.get(packageNo); + if (packageData == null || packageData.getLength() == null + || packageData.getWidth() == null || packageData.getHeight() == null) { + log.warn("箱号 {} 的包装 {} 信息不完整", seqNoObj, packageNo); continue; } - // 计算该物料的体积:长*宽*高*箱数 - BigDecimal materialVolume = packageData.getLength() + // 计算单箱体积:长 * 宽 * 高 + BigDecimal singleBoxVolume = packageData.getLength() .multiply(packageData.getWidth()) - .multiply(packageData.getHeight()) - .multiply(boxQty); + .multiply(packageData.getHeight()); - totalVolume = totalVolume.add(materialVolume); + // 累加到总体积 + totalVolume = totalVolume.add(singleBoxVolume); + processedBoxCount++; - log.debug("物料 {} 包装 {} 箱数 {} 单箱体积 {} 总体积 {}", - pn, packageNo, boxQty, - packageData.getLength().multiply(packageData.getWidth()).multiply(packageData.getHeight()), - materialVolume); + log.debug("箱号 {} 物料 {} 包装 {} 单箱体积 {} m³", + seqNoObj, mainPn, packageNo, singleBoxVolume.setScale(4, RoundingMode.HALF_UP)); } + log.info("体积计算完成,发货单号: {}, 处理箱数: {}/{}, 总体积: {} m³", + notifyHeader.getDelNo(), processedBoxCount, boxList.size(), + totalVolume.setScale(2, RoundingMode.HALF_UP)); + return totalVolume.setScale(2, RoundingMode.HALF_UP); } catch (Exception e) { - log.error("计算物料体积时发生异常", e); + log.error("计算物料体积时发生异常,发货单号: {}", notifyHeader.getDelNo(), e); return BigDecimal.ZERO; } } @@ -4919,4 +4961,34 @@ public class CoDelExcelServiceImpl implements CoDelExcelService { return lineCount; } + /** + * HTML 反转义处理 + * 将 HTML 实体字符转换回原始字符 + * + * + * @param text 需要反转义的文本 + * @return 反转义后的文本 + */ + private String unescapeHtml(String text) { + if (text == null || text.isEmpty()) { + return text; + } + + // 当前临时方案:处理常见的 HTML 实体 + String result = text; + result = result.replace("&", "&"); // & 符号 + result = result.replace("<", "<"); // < 符号 + result = result.replace(">", ">"); // > 符号 + result = result.replace(""", "\""); // " 双引号 + result = result.replace("'", "'"); // ' 单引号 + result = result.replace("'", "'"); // ' 单引号(XML风格) + result = result.replace(" ", " "); // 空格 + result = result.replace("©", "©"); // 版权符号 + result = result.replace("®", "®"); // 注册商标 + result = result.replace("×", "×"); // 乘号 + result = result.replace("÷", "÷"); // 除号 + + return result; + } + }