Browse Source

批量修改箱明细

java8
han\hanst 1 month ago
parent
commit
872c77eb68
  1. 18
      src/main/java/com/xujie/sys/modules/ecss/controller/CoDelController.java
  2. 6
      src/main/java/com/xujie/sys/modules/ecss/service/CoDelService.java
  3. 407
      src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelServiceImpl.java

18
src/main/java/com/xujie/sys/modules/ecss/controller/CoDelController.java

@ -261,6 +261,24 @@ public class CoDelController {
return R.ok();
}
/**
* @Author System
* @Description 批量修改装箱信息Box和明细
* @Date 2025/12/05
* @Param [batchData] 包含boxList和detailList的批量更新数据
* @return com.xujie.sys.common.utils.R
**/
@PostMapping("/batchUpdatePackingInfo")
@ResponseBody
public R batchUpdatePackingInfo(@RequestBody Map<String, Object> batchData){
try {
coDelService.batchUpdatePackingInfo(batchData);
return R.ok();
} catch (Exception e) {
return R.error("批量修改失败: " + e.getMessage());
}
}
@PostMapping("/saveCoDelPalletDataByExcel")
public R saveCoDelPalletDataByExcel(@RequestParam(value = "file") MultipartFile file,
@RequestParam(value = "palletRecords", required = false) String palletRecords,

6
src/main/java/com/xujie/sys/modules/ecss/service/CoDelService.java

@ -70,6 +70,12 @@ public interface CoDelService {
void deleteDetailInfo(EcssCoDelPalletData detailData);
/**
* 批量修改装箱信息Box和明细
* @param batchData 包含boxList和detailList的批量更新数据
*/
void batchUpdatePackingInfo(Map<String, Object> batchData);
//void deleteEmptyBoxAfterDetailDelete(EcssCoDelPalletData detailData);
void saveCoDelPalletDataByExcel(MultipartFile file, EcssCoDelNotifyHeaderData data, String palletRecords);

407
src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelServiceImpl.java

@ -665,11 +665,28 @@ public class CoDelServiceImpl implements CoDelService {
// 使用动态索引读取数据必填列
task.setCustomerPO(getStringCellValue(row, poIdx));
task.setPn(getStringCellValue(row, pnIdx));
task.setQty(getNumericCellValueOrDefault(row, qtyIdx));
// 读取Qty (pcs)捕获格式错误
try {
task.setQty(getNumericCellValueOrDefault(row, qtyIdx, "Qty (pcs)"));
} catch (RuntimeException e) {
currentSheetError.addErrorDetail("第" + (j+1) + "行的 " + e.getMessage());
hasRowError = true;
continue;
}
task.setDestination(getStringCellValue(row, destinationIdx));
task.setShippingMode(getStringCellValue(row, shippingModeIdx));
task.setCurrency(getStringCellValue(row, currencyIdx));
task.setTp(getNumericCellValueOrDefault(row, tpIdx));
// 读取TP捕获格式错误
try {
task.setTp(getNumericCellValueOrDefault(row, tpIdx, "TP"));
} catch (RuntimeException e) {
currentSheetError.addErrorDetail("第" + (j+1) + "行的 " + e.getMessage());
hasRowError = true;
continue;
}
// 处理CMC Invoice如果CMC Invoice为空/0/Excel错误/超过20位则取Shipping Number如果两者都为空则报错
String cmcInvoiceValue = getStringCellValueSafe(row, columnMap, "CMC Invoice");
@ -974,11 +991,30 @@ public class CoDelServiceImpl implements CoDelService {
return -1;
}
/**
* 获取数值单元格的值无列名版本保持向后兼容遇到不支持的类型返回null
*/
private BigDecimal getNumericCellValueOrDefault(XSSFRow row, int columnIndex) {
return getNumericCellValueOrDefault(row, columnIndex, null);
}
/**
* 获取数值单元格的值
* @param row Excel行
* @param columnIndex 列索引
* @param columnName 列名用于错误提示 "Qty (pcs)""TP" 等必填列会抛出异常其他列返回null
* @return BigDecimal值
*/
private BigDecimal getNumericCellValueOrDefault(XSSFRow row, int columnIndex, String columnName) {
Cell cell = row.getCell(columnIndex);
if (cell == null || cell.getCellType() == CellType.BLANK) {
return null;
}
// 必填数字列列表
boolean isRequiredColumn = columnName != null &&
("Qty (pcs)".equalsIgnoreCase(columnName) || "TP".equalsIgnoreCase(columnName));
switch (cell.getCellType()) {
case NUMERIC:
BigDecimal value = BigDecimal.valueOf(cell.getNumericCellValue());
@ -991,7 +1027,10 @@ public class CoDelServiceImpl implements CoDelService {
BigDecimal stringValue = new BigDecimal(cell.getStringCellValue());
return stringValue.setScale(6, RoundingMode.HALF_UP); // 四舍五入保留四位小数
} catch (NumberFormatException e) {
throw new RuntimeException("无效的数值格式: " + cell.getStringCellValue());
if (isRequiredColumn) {
throw new RuntimeException("[" + columnName + "] 列无效的数值格式: " + cell.getStringCellValue());
}
return null;
}
case FORMULA:
// 获取缓存结果
@ -1007,12 +1046,21 @@ public class CoDelServiceImpl implements CoDelService {
BigDecimal stringValue = new BigDecimal(cell.getStringCellValue());
return stringValue.setScale(6, RoundingMode.HALF_UP); // 四舍五入保留四位小数
} catch (NumberFormatException e) {
throw new RuntimeException("无效的数值格式: " + cell.getStringCellValue());
if (isRequiredColumn) {
throw new RuntimeException("[" + columnName + "] 列无效的数值格式: " + cell.getStringCellValue());
}
default:return null;
return null;
}
default:
return null;
}
default:
throw new RuntimeException("不支持的单元格类型: " + cell.getCellType());
// 如果是必填列抛出异常提示
if (isRequiredColumn) {
throw new RuntimeException("[" + columnName + "] 列不支持的单元格类型: " + cell.getCellType());
}
// 其他非必填列返回null
return null;
}
}
@ -1726,7 +1774,7 @@ public class CoDelServiceImpl implements CoDelService {
throw new RuntimeException("导入失败:物料:" + excelData.getPn() + "不存在!");
}
excelData.setPartNo(parts.get(0).getPartNo());
excelData.setQty(getNumericCellValueOrDefault(row, 7));
excelData.setQty(getNumericCellValueOrDefault(row, 7, "Qty (pcs)"));
excelData.setBoxQty(getNumericCellValueOrDefault(row, 2));
excelData.setRolls(getNumericCellValueOrDefault(row, 8));
excelData.setGrossWeight(getNumericCellValueOrDefault(row, 3));
@ -4024,7 +4072,7 @@ public class CoDelServiceImpl implements CoDelService {
task.setBuNo(inData.getBuNo()); // bu
task.setSku(getStringCellValue(row, 0));
task.setSo(getStringCellValue(row, 1));
task.setQty(getNumericCellValueOrDefault(row, 2));
task.setQty(getNumericCellValueOrDefault(row, 2, "Qty (pcs)"));
List<EcssWalMartOrder> orderDataList = sqlSession.selectList("ecssMapper" + "." + "searchWalMartOrderList", task);
if (!orderDataList.isEmpty()) {
if (sb.toString().length()>1) {
@ -4424,6 +4472,349 @@ public class CoDelServiceImpl implements CoDelService {
}
}
/**
* 批量修改装箱信息Box和明细
*
* @param batchData 包含boxList和detailList的批量更新数据
*/
@Override
@Transactional
public void batchUpdatePackingInfo(Map<String, Object> batchData) {
String site = (String) batchData.get("site");
String buNo = (String) batchData.get("buNo");
String delNo = (String) batchData.get("delNo");
String updateBy = (String) batchData.get("updateBy");
@SuppressWarnings("unchecked")
List<Map<String, Object>> boxList = (List<Map<String, Object>>) batchData.get("boxList");
@SuppressWarnings("unchecked")
List<Map<String, Object>> detailList = (List<Map<String, Object>>) batchData.get("detailList");
log.info("=== 开始批量修改装箱信息 ===");
log.info("发货单号: {}, Box数量: {}, 明细数量: {}",
delNo,
boxList != null ? boxList.size() : 0,
detailList != null ? detailList.size() : 0);
// 获取发货通知单信息
EcssCoDelNotifyHeaderData notifyHeader = coDelMapper.getEcssCoDelNotifyHeader(site, delNo);
boolean isCustomsCleared = "已报关".equals(notifyHeader.getNotifyStatus());
// 保存修改前的数据用于邮件对比
List<Map> oldBoxList = coDelMapper.selectBoxList(notifyHeader);
EcssCoDelPalletHeaderData queryData = new EcssCoDelPalletHeaderData();
queryData.setSite(site);
queryData.setBuNo(buNo);
queryData.setDelNo(delNo);
List<EcssCoDelPalletDetailData> oldPalletDetailList = coDelMapper.searchEcssCoDelPalletDetailData(queryData);
// 记录修改内容
List<Map<String, Object>> boxChanges = new ArrayList<>();
List<Map<String, Object>> detailChanges = new ArrayList<>();
// 1. 处理Box修改
if (boxList != null && !boxList.isEmpty()) {
for (Map<String, Object> box : boxList) {
Map<String, Object> boxChange = new HashMap<>();
boxChange.put("item_no", box.get("item_no"));
// 获取修改前的Box数据
Map oldBox = oldBoxList.stream()
.filter(b -> String.valueOf(((Map)b).get("item_no")).equals(String.valueOf(box.get("item_no"))))
.findFirst().orElse(null);
if (oldBox != null) {
// 记录变更
if (box.containsKey("box_qty")) {
boxChange.put("box_qty_old", oldBox.get("box_qty"));
boxChange.put("box_qty_new", box.get("box_qty"));
}
if (box.containsKey("grossWeight")) {
boxChange.put("grossWeight_old", oldBox.get("grossWeight"));
boxChange.put("grossWeight_new", box.get("grossWeight"));
}
if (box.containsKey("netWeight")) {
boxChange.put("netWeight_old", oldBox.get("netWeight"));
boxChange.put("netWeight_new", box.get("netWeight"));
}
}
boxChanges.add(boxChange);
// 执行更新
Map<String, Object> updateParams = new HashMap<>();
updateParams.put("site", site);
updateParams.put("buNo", buNo);
updateParams.put("delNo", delNo);
updateParams.put("item_no", box.get("item_no"));
updateParams.put("palletRemark", box.get("palletRemark"));
updateParams.put("box_qty", box.get("box_qty"));
updateParams.put("grossWeight", box.get("grossWeight"));
updateParams.put("netWeight", box.get("netWeight"));
updateParams.put("updateBy", updateBy);
coDelMapper.updateBoxInfo(updateParams);
log.info("更新Box: item_no={}", box.get("item_no"));
}
}
// 2. 处理明细修改
if (detailList != null && !detailList.isEmpty()) {
for (Map<String, Object> detail : detailList) {
Map<String, Object> detailChange = new HashMap<>();
detailChange.put("seqNo", detail.get("seqNo"));
detailChange.put("itemNo", detail.get("itemNo"));
detailChange.put("poNo", detail.get("poNo"));
detailChange.put("pn", detail.get("pn"));
// 获取修改前的明细数据
EcssCoDelPalletDetailData oldDetail = oldPalletDetailList.stream()
.filter(d -> String.valueOf(d.getSeqNo()).equals(String.valueOf(detail.get("seqNo")))
&& String.valueOf(d.getItemNo()).equals(String.valueOf(detail.get("itemNo"))))
.findFirst().orElse(null);
BigDecimal oldQty = null;
if (oldDetail != null) {
oldQty = oldDetail.getQty();
// 记录变更
if (detail.containsKey("qty")) {
detailChange.put("qty_old", oldDetail.getQty());
detailChange.put("qty_new", detail.get("qty"));
}
if (detail.containsKey("rolls")) {
detailChange.put("rolls_old", oldDetail.getRolls());
detailChange.put("rolls_new", detail.get("rolls"));
}
}
detailChanges.add(detailChange);
// 构造更新参数
EcssCoDelPalletData detailData = new EcssCoDelPalletData();
detailData.setSite(site);
detailData.setBuNo(buNo);
detailData.setDelNo(delNo);
detailData.setSeqNo(detail.get("seqNo") != null ? Integer.valueOf(detail.get("seqNo").toString()) : null);
detailData.setItemNo(detail.get("itemNo") != null ? Integer.valueOf(detail.get("itemNo").toString()) : null);
detailData.setNotifyDetailItemNo(detail.get("notifyDetailItemNo") != null ? Integer.valueOf(detail.get("notifyDetailItemNo").toString()) : null);
detailData.setPoNo((String) detail.get("poNo"));
detailData.setPn((String) detail.get("pn"));
detailData.setQty(detail.get("qty") != null ? new BigDecimal(detail.get("qty").toString()) : null);
detailData.setOldQty(oldQty);
detailData.setRolls(detail.get("rolls") != null ? new BigDecimal(detail.get("rolls").toString()) : null);
detailData.setUpdateBy(updateBy);
// 执行更新
coDelMapper.updateDetailInfo(detailData);
// 更新发货通知单明细的剩余可装箱数量
Integer notifyDetailItemNo = detailData.getNotifyDetailItemNo();
BigDecimal qty = detailData.getQty();
if (notifyDetailItemNo != null && qty != null && oldQty != null) {
EcssCoDelNotifyDetailData notifyDetail = coDelMapper.getEcssCoDelNotifyDetailByItemNo(
site, buNo, delNo, notifyDetailItemNo
);
if (notifyDetail != null) {
BigDecimal currentSurplusQty = notifyDetail.getSurplusQty() != null ? notifyDetail.getSurplusQty() : BigDecimal.ZERO;
BigDecimal newSurplusQty = currentSurplusQty.add(oldQty).subtract(qty);
notifyDetail.setSurplusQty(newSurplusQty);
coDelMapper.updateEcssCoDelNotifyDetailSurplus(notifyDetail);
}
}
log.info("更新明细: seqNo={}, itemNo={}", detail.get("seqNo"), detail.get("itemNo"));
}
}
log.info("=== 批量修改装箱信息完成 === Box: {}, 明细: {}", boxChanges.size(), detailChanges.size());
// 3. 如果状态为已报关发送邮件通知
if (isCustomsCleared && (!boxChanges.isEmpty() || !detailChanges.isEmpty())) {
sendBatchUpdateNotificationEmail(notifyHeader, boxChanges, detailChanges, updateBy);
}
}
/**
* 发送批量修改通知邮件
*
* @param notifyHeader 发货通知单头数据
* @param boxChanges Box修改记录
* @param detailChanges 明细修改记录
* @param updateBy 修改人
*/
private void sendBatchUpdateNotificationEmail(EcssCoDelNotifyHeaderData notifyHeader,
List<Map<String, Object>> boxChanges,
List<Map<String, Object>> detailChanges,
String updateBy) {
try {
log.info("开始发送批量修改通知邮件,发货通知单号:{}", notifyHeader.getDelNo());
// 生成邮件内容
String emailContent = generateBatchUpdateEmailContent(notifyHeader, boxChanges, detailChanges, updateBy);
// 获取发货通知单创建人邮箱
SysUserEntity creator = coDelMapper.queryByUserName(notifyHeader.getCreateBy());
if (creator == null || StringUtils.isBlank(creator.getEmail())) {
log.warn("发货通知单创建人{}不存在或没有配置邮箱地址", notifyHeader.getCreateBy());
return;
}
String creatorEmail = creator.getEmail();
// 发送邮件
String subject = String.format("发货通知单%s【发票:%s】批量修改通知",
notifyHeader.getDelNo(), notifyHeader.getCmcInvoice());
String[] mailAddress = {creatorEmail};
sendMailUtil(subject, emailContent, mailAddress, notifyHeader);
log.info("批量修改通知邮件发送成功,收件人:{}", creatorEmail);
} catch (Exception e) {
log.error("发送批量修改通知邮件失败,发货通知单号:{}, 错误信息:{}",
notifyHeader.getDelNo(), e.getMessage(), e);
}
}
/**
* 生成批量修改邮件内容
*
* @param notifyHeader 发货通知单头数据
* @param boxChanges Box修改记录
* @param detailChanges 明细修改记录
* @param updateBy 修改人
* @return HTML格式的邮件内容
*/
private String generateBatchUpdateEmailContent(EcssCoDelNotifyHeaderData notifyHeader,
List<Map<String, Object>> boxChanges,
List<Map<String, Object>> detailChanges,
String updateBy) {
StringBuilder emailContent = new StringBuilder();
emailContent.append("<!DOCTYPE html>");
emailContent.append("<html>");
emailContent.append("<head>");
emailContent.append("<meta charset='UTF-8'>");
emailContent.append("<style>");
emailContent.append("body { font-family: Arial, sans-serif; margin: 20px; }");
emailContent.append("table { border-collapse: collapse; width: 100%; margin: 10px 0; }");
emailContent.append("th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }");
emailContent.append("th { background-color: #beb256; color: white; font-weight: bold; }");
emailContent.append(".old-value { color: #999; text-decoration: line-through; }");
emailContent.append(".new-value { color: #e74c3c; font-weight: bold; }");
emailContent.append(".arrow { color: #3498db; margin: 0 5px; }");
emailContent.append(".section-title { color: #333; font-size: 16px; font-weight: bold; margin: 20px 0 10px 0; border-left: 4px solid #4CAF50; padding-left: 10px; }");
emailContent.append(".summary { background-color: #f9f9f9; padding: 15px; border-radius: 5px; margin: 10px 0; }");
emailContent.append("</style>");
emailContent.append("</head>");
emailContent.append("<body>");
// 邮件标题
emailContent.append("<h2 style='color: #2c3e50;'>📦 发货通知单批量修改通知</h2>");
// 基本信息
emailContent.append("<div class='summary'>");
emailContent.append("<p><strong>发货通知单号:</strong>").append(notifyHeader.getDelNo()).append("</p>");
emailContent.append("<p><strong>发票号:</strong>").append(notifyHeader.getCmcInvoice()).append("</p>");
emailContent.append("<p><strong>客户:</strong>").append(notifyHeader.getCustomerName() != null ? notifyHeader.getCustomerName() : "-").append("</p>");
emailContent.append("<p><strong>修改人:</strong>").append(updateBy).append("</p>");
emailContent.append("<p><strong>修改时间:</strong>").append(new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date())).append("</p>");
emailContent.append("</div>");
// Box修改记录
if (boxChanges != null && !boxChanges.isEmpty()) {
emailContent.append("<div class='section-title'>📋 箱信息修改(共 ").append(boxChanges.size()).append(" 条)</div>");
emailContent.append("<table>");
emailContent.append("<tr><th>序号</th><th>箱数</th><th>毛重</th><th>净重</th></tr>");
for (Map<String, Object> change : boxChanges) {
emailContent.append("<tr>");
emailContent.append("<td>").append(change.get("item_no")).append("</td>");
// 箱数
emailContent.append("<td>");
if (change.containsKey("box_qty_old") && change.containsKey("box_qty_new")) {
emailContent.append("<span class='old-value'>").append(change.get("box_qty_old")).append("</span>");
emailContent.append("<span class='arrow'>→</span>");
emailContent.append("<span class='new-value'>").append(change.get("box_qty_new")).append("</span>");
} else {
emailContent.append("-");
}
emailContent.append("</td>");
// 毛重
emailContent.append("<td>");
if (change.containsKey("grossWeight_old") && change.containsKey("grossWeight_new")) {
emailContent.append("<span class='old-value'>").append(change.get("grossWeight_old")).append("</span>");
emailContent.append("<span class='arrow'>→</span>");
emailContent.append("<span class='new-value'>").append(change.get("grossWeight_new")).append("</span>");
} else {
emailContent.append("-");
}
emailContent.append("</td>");
// 净重
emailContent.append("<td>");
if (change.containsKey("netWeight_old") && change.containsKey("netWeight_new")) {
emailContent.append("<span class='old-value'>").append(change.get("netWeight_old")).append("</span>");
emailContent.append("<span class='arrow'>→</span>");
emailContent.append("<span class='new-value'>").append(change.get("netWeight_new")).append("</span>");
} else {
emailContent.append("-");
}
emailContent.append("</td>");
emailContent.append("</tr>");
}
emailContent.append("</table>");
}
// 明细修改记录
if (detailChanges != null && !detailChanges.isEmpty()) {
emailContent.append("<div class='section-title'>📝 明细信息修改(共 ").append(detailChanges.size()).append(" 条)</div>");
emailContent.append("<table>");
emailContent.append("<tr><th>序号</th><th>PO</th><th>PN</th><th>数量</th><th>Rolls</th></tr>");
for (Map<String, Object> change : detailChanges) {
emailContent.append("<tr>");
emailContent.append("<td>").append(change.get("seqNo")).append("-").append(change.get("itemNo")).append("</td>");
emailContent.append("<td>").append(change.get("poNo") != null ? change.get("poNo") : "-").append("</td>");
emailContent.append("<td>").append(change.get("pn") != null ? change.get("pn") : "-").append("</td>");
// 数量
emailContent.append("<td>");
if (change.containsKey("qty_old") && change.containsKey("qty_new")) {
emailContent.append("<span class='old-value'>").append(change.get("qty_old")).append("</span>");
emailContent.append("<span class='arrow'>→</span>");
emailContent.append("<span class='new-value'>").append(change.get("qty_new")).append("</span>");
} else {
emailContent.append("-");
}
emailContent.append("</td>");
// Rolls
emailContent.append("<td>");
if (change.containsKey("rolls_old") && change.containsKey("rolls_new")) {
emailContent.append("<span class='old-value'>").append(change.get("rolls_old")).append("</span>");
emailContent.append("<span class='arrow'>→</span>");
emailContent.append("<span class='new-value'>").append(change.get("rolls_new")).append("</span>");
} else {
emailContent.append("-");
}
emailContent.append("</td>");
emailContent.append("</tr>");
}
emailContent.append("</table>");
}
// 页脚
emailContent.append("<hr style='margin-top: 30px; border: none; border-top: 1px solid #ddd;'>");
emailContent.append("<p style='color: #999; font-size: 12px;'>此邮件由系统自动发送,请勿直接回复。如有疑问请联系相关人员。</p>");
emailContent.append("</body>");
emailContent.append("</html>");
return emailContent.toString();
}
@Override
public List<Map<String, Object>> getCustomerTemplateList(Map<String, Object> params) {
return coDelMapper.getCustomerTemplateList(params);

Loading…
Cancel
Save