From 3a6cefa0f62353ec2d6d0f180d5767c9db8ddeb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=A5=8E=E5=85=B4?= <2220574228@qq.com> Date: Thu, 14 May 2026 17:55:14 +0800 Subject: [PATCH] =?UTF-8?q?oms=E5=87=BA=E5=85=A5=E5=BA=93=E9=A2=84?= =?UTF-8?q?=E7=BA=A6=E5=8D=95=E5=AF=BC=E5=85=A5=E6=8E=A5=E5=8F=A3=E5=BC=80?= =?UTF-8?q?=E5=8F=91;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/minio/MinIoUploadService.java | 4 +- .../ReservationMaterialInventoryImpl.java | 15 +- .../listener/ReservedInventoryListener.java | 89 ++++++++++++ .../listener/ValidationException.java | 20 +++ .../mapper/ReserveStockInOrderMapper.java | 2 + .../repository/util/DateUtils.java | 49 +++++++ .../repository/util/ExcelParseUtil.java | 79 ++++++++++ .../util/ReservedInventoryValidator.java | 125 ++++++++++++++++ .../util/StringToBigDecimalConverter.java | 103 +++++++++++++ .../util/StringToDateConverter.java | 135 ++++++++++++++++++ .../repository/vo/ReservedInventory.java | 71 +++++++++ ...ReserveStockInOrderApplicationService.java | 133 ++++++++++++++++- .../ReserveStockInOrderDomainService.java | 57 ++++---- .../ReservedInventoryOutListener.java | 89 ++++++++++++ .../listener/ValidationException.java | 20 +++ .../repository/util/ExcelParseUtil.java | 78 ++++++++++ .../util/ReservedInventoryOutValidator.java | 73 ++++++++++ .../util/StringToBigDecimalConverter.java | 104 ++++++++++++++ .../repository/vo/ReservedInventoryOut.java | 40 ++++++ ...eserveStockOutOrderApplicationService.java | 125 +++++++++++++++- .../ReserveStockOutOrderDomainService.java | 28 +++- .../ReserveStockOutOrderApi.java | 49 +++++++ .../ReserveStockInOrderApi.java | 47 ++++++ .../ReserveStockInOrderMapper.xml | 4 + .../persistence/MaterialInventoryImpl.java | 2 +- .../service/StockShelfOrderDomainService.java | 8 ++ 26 files changed, 1504 insertions(+), 45 deletions(-) create mode 100644 mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/listener/ReservedInventoryListener.java create mode 100644 mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/listener/ValidationException.java create mode 100644 mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/DateUtils.java create mode 100644 mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/ExcelParseUtil.java create mode 100644 mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/ReservedInventoryValidator.java create mode 100644 mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/StringToBigDecimalConverter.java create mode 100644 mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/StringToDateConverter.java create mode 100644 mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/vo/ReservedInventory.java create mode 100644 mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/listener/ReservedInventoryOutListener.java create mode 100644 mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/listener/ValidationException.java create mode 100644 mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/util/ExcelParseUtil.java create mode 100644 mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/util/ReservedInventoryOutValidator.java create mode 100644 mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/util/StringToBigDecimalConverter.java create mode 100644 mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/vo/ReservedInventoryOut.java diff --git a/mhd-common/mhd-common-core/src/main/java/minio/MinIoUploadService.java b/mhd-common/mhd-common-core/src/main/java/minio/MinIoUploadService.java index b7b289f68..85d5b5d4f 100644 --- a/mhd-common/mhd-common-core/src/main/java/minio/MinIoUploadService.java +++ b/mhd-common/mhd-common-core/src/main/java/minio/MinIoUploadService.java @@ -22,7 +22,7 @@ public class MinIoUploadService { private MinioClient minioClient; private static String bucket="ngwl"; - private static String host="http://10.102.192.5:6000"; + private static String host="http://127.0.0.1:6000"; private static String accessKey="minioadmin"; private static String secretKey="gmminioadmin"; /** @@ -139,4 +139,4 @@ public class MinIoUploadService { } return FILE_URL; }*/ -} +} \ No newline at end of file diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reservationMaterialInventory/repository/persistence/ReservationMaterialInventoryImpl.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reservationMaterialInventory/repository/persistence/ReservationMaterialInventoryImpl.java index dd0741f6d..8bc684383 100644 --- a/mhd_oms/src/main/java/com/mhd/oms/domain/reservationMaterialInventory/repository/persistence/ReservationMaterialInventoryImpl.java +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reservationMaterialInventory/repository/persistence/ReservationMaterialInventoryImpl.java @@ -125,12 +125,19 @@ public class ReservationMaterialInventoryImpl extends ServiceImpl reservationMaterialInventories = materialInventoryMapper.selectList(new LambdaQueryWrapper() + LambdaQueryWrapper param = new LambdaQueryWrapper() .eq(ReservationMaterialInventory::getInOrderNumber, inOrderNumber) - .eq(ReservationMaterialInventory::getLotNumber, lotNumber) - .eq(ReservationMaterialInventory::getBatchNumber, batchNumber) + //.eq(ReservationMaterialInventory::getLotNumber, lotNumber) + //.eq(ReservationMaterialInventory::getBatchNumber, batchNumber) .eq(ReservationMaterialInventory::getMaterialBaseInfoId, materialBaseInfoId) - .eq(ReservationMaterialInventory::getWarehouseId, warehouseId)); + .eq(ReservationMaterialInventory::getWarehouseId, warehouseId); + if(lotNumber != null && !lotNumber.isEmpty()) { + param.eq(ReservationMaterialInventory::getLotNumber, lotNumber); + } + if (batchNumber != null && !batchNumber.isEmpty()) { + param.eq(ReservationMaterialInventory::getBatchNumber, batchNumber); + } + List reservationMaterialInventories = materialInventoryMapper.selectList(param); if (reservationMaterialInventories != null && reservationMaterialInventories.size() > 0) { ReservationMaterialInventory reservationMaterialInventory = reservationMaterialInventories.get(0); Long materialInventoryId = reservationMaterialInventory.getMaterialInventoryId(); diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/listener/ReservedInventoryListener.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/listener/ReservedInventoryListener.java new file mode 100644 index 000000000..e25a36d76 --- /dev/null +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/listener/ReservedInventoryListener.java @@ -0,0 +1,89 @@ +package com.mhd.oms.domain.reserveStockInOrder.repository.listener; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.listener.ReadListener; +import com.mhd.oms.domain.reserveStockInOrder.repository.util.ExcelParseUtil; +import com.mhd.oms.domain.reserveStockInOrder.repository.vo.ReservedInventory; +import lombok.Getter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.List; + +public class ReservedInventoryListener implements ReadListener { + private static final Logger log = LoggerFactory.getLogger(ReservedInventoryListener.class); + + @Getter + private final List dataList = new ArrayList<>(); + private static final int BATCH_SIZE = 100; + + @Override + public void invoke(ReservedInventory data, AnalysisContext context) { + // 1. 获取当前行号(关键:EasyExcel 3.2.1 中 rowIndex 从0开始,需+1转为实际行号) + long currentRowNum = context.readRowHolder().getRowIndex() + 1; + log.info("读取第{}行数据:{}", currentRowNum, data); + + // 2. 核心过滤:仅保留 >= 数据起始行(第4行)的数据 + if (currentRowNum < ExcelParseUtil.TemplateConfig.DATA_START_ROW) { + log.info("第{}行(说明行/表头行),跳过解析", currentRowNum); + return; + } + + // 3. 过滤说明文本行(双重保障,避免极端情况) + if (isRemarkRow(data)) { + log.info("第{}行包含说明文本(如'保質到哪一天的日期'),跳过解析", currentRowNum); + return; + } + + // 4. 过滤空行,保留有效数据 + if (isValidDataRow(data)) { + dataList.add(data); + // 批量处理逻辑不变 + if (dataList.size() >= BATCH_SIZE) { + log.info("已读取{}条数据,执行批量处理", BATCH_SIZE); + dataList.clear(); + } + } + } + + /** + * 判断是否为说明文本行(精准匹配“保質到哪一天的日期”等关键词) + */ + private boolean isRemarkRow(ReservedInventory data) { + if (data == null) { + return false; + } + // 匹配说明行特征:货物名称为提示文本,或保质期/件数均为空 + boolean isGoodsNameRemark = data.getGoodsName() != null && + (data.getGoodsName().contains("请输入") || data.getGoodsName().contains("保質到哪一天的日期")); + boolean isEmptyRow = data.getQuantity() == null && data.getShelfLife() == null; + + return isGoodsNameRemark || isEmptyRow; + } + + /** + * 判断是否为有效数据行(至少一个必填字段有值) + */ + private boolean isValidDataRow(ReservedInventory data) { + if (data == null) { + return false; + } + return (data.getGoodsName() != null && !data.getGoodsName().trim().isEmpty()) + || data.getQuantity() != null + || data.getShelfLife() != null; + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + log.info("Excel数据读取完成,共读取{}条有效数据", dataList.size()); + } + + /** + * 可选:提前判断是否需要继续读取(优化性能) + */ + @Override + public boolean hasNext(AnalysisContext context) { + // 无需特殊处理,返回true即可 + return ReadListener.super.hasNext(context); + } +} \ No newline at end of file diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/listener/ValidationException.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/listener/ValidationException.java new file mode 100644 index 000000000..0379ea204 --- /dev/null +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/listener/ValidationException.java @@ -0,0 +1,20 @@ +package com.mhd.oms.domain.reserveStockInOrder.repository.listener; + +/** + * 数据验证异常 + * 用于Excel导入数据验证失败场景 + */ +public class ValidationException extends RuntimeException { + + public ValidationException() { + super(); + } + + public ValidationException(String message) { + super(message); + } + + public ValidationException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/mapper/ReserveStockInOrderMapper.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/mapper/ReserveStockInOrderMapper.java index 2799b5114..f972723f9 100644 --- a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/mapper/ReserveStockInOrderMapper.java +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/mapper/ReserveStockInOrderMapper.java @@ -31,6 +31,8 @@ public interface ReserveStockInOrderMapper extends BaseMapper getByName(@Param("name") String name); + public List getMaterialBaseInfoList(@Param("id") String id); public MaterialWarehouseControlPO getInfoByMaterialBaseInfoIds(@Param("id") Long id); diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/DateUtils.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/DateUtils.java new file mode 100644 index 000000000..76c9bc6ef --- /dev/null +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/DateUtils.java @@ -0,0 +1,49 @@ +package com.mhd.oms.domain.reserveStockInOrder.repository.util; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +/** + * 日期工具类:统一日期格式处理 + */ +public class DateUtils { + // 支持的日期格式(与自定义转换器一致) + public static final SimpleDateFormat[] DATE_FORMATS = { + new SimpleDateFormat("yyyy/M/d HH:mm:ss", Locale.CHINA), + new SimpleDateFormat("yyyy/M/d", Locale.CHINA), + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA), + new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA) + }; + + /** + * 字符串转Date(复用转换器逻辑) + */ + public static Date strToDate(String dateStr) throws ParseException { + if (dateStr == null || dateStr.trim().isEmpty()) { + return null; + } + dateStr = dateStr.replaceAll("\\s+", ""); // 清洗空格 + + for (SimpleDateFormat format : DATE_FORMATS) { + try { + format.setLenient(false); + return format.parse(dateStr); + } catch (ParseException e) { + continue; + } + } + throw new ParseException("日期格式不支持:" + dateStr, 0); + } + + /** + * Date转字符串 + */ + public static String dateToStr(Date date) { + if (date == null) { + return ""; + } + return DATE_FORMATS[0].format(date); // 统一输出为 2023/12/9 12:20:30 格式 + } +} \ No newline at end of file diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/ExcelParseUtil.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/ExcelParseUtil.java new file mode 100644 index 000000000..3ae7db3e8 --- /dev/null +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/ExcelParseUtil.java @@ -0,0 +1,79 @@ +package com.mhd.oms.domain.reserveStockInOrder.repository.util; + +import com.alibaba.excel.EasyExcel; +import com.mhd.oms.domain.reserveStockInOrder.repository.listener.ReservedInventoryListener; +import com.mhd.oms.domain.reserveStockInOrder.repository.vo.ReservedInventory; +import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +/** + * Excel解析工具类 + * 提供Excel文件验证和数据解析功能 + */ +public class ExcelParseUtil { + + /** + * Excel模板常量配置 + */ + public static final class TemplateConfig { + // 表头行号(从1开始) + public static final int HEADER_ROW_NUM = 2; + // 数据起始行号(从1开始) + public static final int DATA_START_ROW = 4; + + public static final int RELATIVE_HEAD_ROW = 2; // 相对表头的偏移量(表头行+2行=数据行) + // 工作表名称 + public static final String SHEET_NAME = "Sheet1"; + // 最大文件大小(10MB) + public static final long MAX_FILE_SIZE = 100 * 1024 * 1024; + } + + /** + * 解析Excel文件为实体类列表 + * @param file 上传的Excel文件 + * @return 解析并验证后的实体类列表 + * @throws IOException 文件处理异常 + */ + public static List parseExcel(MultipartFile file) throws IOException { + // 1. 验证文件格式和大小 + validateFile(file); + + // 2. 读取Excel数据 + ReservedInventoryListener listener = new ReservedInventoryListener(); + try (InputStream inputStream = file.getInputStream()) { + EasyExcel.read(inputStream, ReservedInventory.class, listener) + .sheet(TemplateConfig.SHEET_NAME) // 指定工作表 + .headRowNumber(TemplateConfig.HEADER_ROW_NUM) // 指定表头行 + .doRead(); // 执行读取 + } + + // 3. 数据验证 + List dataList = listener.getDataList(); + ReservedInventoryValidator.validateAll(dataList, TemplateConfig.DATA_START_ROW); + + return dataList; + } + + /** + * 验证Excel文件合法性 + */ + private static void validateFile(MultipartFile file) throws IOException { + // 验证文件不为空 + if (file.isEmpty()) { + throw new IOException("上传的Excel文件不能为空"); + } + + // 验证文件后缀 + String fileName = file.getOriginalFilename(); + if (fileName == null || (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls"))) { + throw new IOException("请上传Excel格式文件(.xlsx或.xls)"); + } + + // 验证文件大小 + if (file.getSize() > TemplateConfig.MAX_FILE_SIZE) { + throw new IOException("文件大小不能超过10MB"); + } + } +} \ No newline at end of file diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/ReservedInventoryValidator.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/ReservedInventoryValidator.java new file mode 100644 index 000000000..c34987a5c --- /dev/null +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/ReservedInventoryValidator.java @@ -0,0 +1,125 @@ +package com.mhd.oms.domain.reserveStockInOrder.repository.util; + +import com.mhd.oms.domain.reserveStockInOrder.repository.listener.ValidationException; +import com.mhd.oms.domain.reserveStockInOrder.repository.vo.ReservedInventory; + +import java.math.BigDecimal; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +/** + * 预约存货数据验证器 + * 实现数据合法性校验 + */ +public class ReservedInventoryValidator { + // 保持与转换器一致的格式,用于二次校验 + private static final SimpleDateFormat[] VALIDATE_FORMATS = { + new SimpleDateFormat("yyyy/M/d HH:mm:ss", Locale.CHINA), + new SimpleDateFormat("yyyy/M/d", Locale.CHINA), + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.CHINA), + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA), + new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA) + }; + + static { + // 所有格式均开启严格校验 + for (SimpleDateFormat format : VALIDATE_FORMATS) { + format.setLenient(false); + } + } + /** + * 批量验证数据列表 + * @param dataList 待验证数据 + * @param startRow 数据起始行号(用于错误定位) + */ + public static void validateAll(List dataList, int startRow) { + if (dataList == null || dataList.isEmpty()) { + throw new ValidationException("待验证的数据列表为空"); + } + + List errorList = new ArrayList<>(); + // 逐条验证 + for (int i = 0; i < dataList.size(); i++) { + ReservedInventory data = dataList.get(i); + int rowNum = startRow + i; // 计算当前数据在Excel中的行号 + try { + validateSingle(data, rowNum); + } catch (ValidationException e) { + errorList.add(e.getMessage()); + // 设置验证结果到实体类 + data.setValidationResult(e.getMessage()); + } + } + + // 汇总错误信息 + if (!errorList.isEmpty()) { + throw new ValidationException("Excel数据验证失败:\n" + String.join("\n", errorList)); + } + } + + /** + * 单个数据验证 + */ + private static void validateSingle(ReservedInventory data, int rowNum) { + List errors = new ArrayList<>(); + + // 1. 必填项验证 + if (data.getGoodsName() == null || data.getGoodsName().trim().isEmpty()) { + errors.add(String.format("第%d行:货物名称为必填项", rowNum)); + } + if (data.getQuantity() == null) { + errors.add(String.format("第%d行:件数为必填项", rowNum)); + } else if (data.getQuantity().compareTo(BigDecimal.ZERO) < 0) { + errors.add(String.format("第%d行:件数必须为正整数(当前值:%d)", rowNum, data.getQuantity())); + } + // 保质期验证:更新错误提示 + Date shelfLife = data.getShelfLife(); + if (shelfLife == null) { + errors.add(String.format("第%d行:保质期为必填项,请填写格式如 2023/12/9 12:20:30", rowNum)); + } else { + // 二次校验日期合法性(避免转换器未覆盖的异常) + boolean isValid = false; + String dateStr = null; + for (SimpleDateFormat format : VALIDATE_FORMATS) { + try { + dateStr = format.format(shelfLife); + format.parse(dateStr); + isValid = true; + break; + } catch (ParseException e) { + continue; + } + } + if (!isValid) { + errors.add(String.format("第%d行:保质期格式非法,支持 2023/12/9 12:20:30 或 2023-12-09 18:30:30(当前值:%s)", + rowNum, dateStr)); + } + // 保质期不能早于当前日期 + if (shelfLife.before(new Date())) { + SimpleDateFormat showFormat = new SimpleDateFormat("yyyy/M/d HH:mm:ss", Locale.CHINA); + errors.add(String.format("第%d行:保质期不能早于当前日期(当前值:%s)", + rowNum, showFormat.format(shelfLife))); + } + } + if (data.getOperationType() == null || !"新增".equals(data.getOperationType().trim())) { + errors.add(String.format("第%d行:操作类型必须为'新增'(当前值:%s)", rowNum, data.getOperationType())); + } + + // 2. 可选字段验证(重量非负) + if (data.getWeight() != null && data.getWeight().compareTo(BigDecimal.ZERO) < 0) { + errors.add(String.format("第%d行:重量不能为负数(当前值:%s)", rowNum, data.getWeight())); + } + + // 抛出验证错误 + if (!errors.isEmpty()) { + throw new ValidationException(String.join(";", errors)); + } + + // 验证通过 + data.setValidationResult("验证通过"); + } +} \ No newline at end of file diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/StringToBigDecimalConverter.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/StringToBigDecimalConverter.java new file mode 100644 index 000000000..c7c3affce --- /dev/null +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/StringToBigDecimalConverter.java @@ -0,0 +1,103 @@ +package com.mhd.oms.domain.reserveStockInOrder.repository.util; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import java.math.BigDecimal; +import java.util.regex.Pattern; + +/** + * EasyExcel 3.2.1 适配版:字符串转BigDecimal转换器 + * 解决WriteCellData实例化爆红问题 + */ +public class StringToBigDecimalConverter implements Converter { + + // 正则表达式:清洗非数字字符(保留小数点) + private static final Pattern NUMBER_PATTERN = Pattern.compile("[^0-9.]"); + + /** + * 支持的Java类型(BigDecimal) + */ + @Override + public Class supportJavaTypeKey() { + return BigDecimal.class; + } + + /** + * 支持的Excel单元格类型(文本+数值) + */ + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; // 覆盖文本和数值类型转换 + } + + /** + * 导入:Excel → Java(逻辑不变,确保件数正确转换) + */ + @Override + public BigDecimal convertToJavaData( + ReadCellData cellData, + ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + + // 1. 获取并清洗单元格数据 + String cellValue = getCleanedCellValue(cellData); + if (cellValue == null || cellValue.isEmpty()) { + return null; + } + + // 2. 转换为BigDecimal(处理格式异常) + try { + return new BigDecimal(cellValue); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + String.format("单元格数据[%s]无法转换为数字,请检查格式", cellValue), e); + } + } + + /** + * 导出:Java → Excel(3.2.1版本适配核心) + * 关键:使用构造方法直接实例化,不依赖Builder + */ + @Override + public WriteCellData convertToExcelData( + BigDecimal value, + ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + + // 1. 处理空值:返回空字符串单元格 + if (value == null) { + WriteCellData emptyData = new WriteCellData<>(""); + emptyData.setType(CellDataTypeEnum.STRING); // 明确指定文本类型 + return emptyData; + } + + // 2. 处理非空值:BigDecimal转字符串后写入 + String valueStr = value.toString(); + WriteCellData cellData = new WriteCellData<>(valueStr); + cellData.setType(CellDataTypeEnum.STRING); // 明确数据类型,避免自动转换异常 + return cellData; + } + + /** + * 辅助方法:获取并清洗单元格数据(处理文本/数值型) + */ + private String getCleanedCellValue(ReadCellData cellData) { + String rawValue = null; + // 根据单元格类型获取原始值 + if (cellData.getType() == CellDataTypeEnum.STRING) { + rawValue = cellData.getStringValue().trim(); + } else if (cellData.getType() == CellDataTypeEnum.NUMBER) { + rawValue = cellData.getNumberValue().toString().trim(); + } + + // 清洗非数字字符(如"5件"→"5"," 10.5 "→"10.5") + if (rawValue == null) { + return null; + } + return NUMBER_PATTERN.matcher(rawValue).replaceAll(""); + } +} \ No newline at end of file diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/StringToDateConverter.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/StringToDateConverter.java new file mode 100644 index 000000000..cf4b199d4 --- /dev/null +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/util/StringToDateConverter.java @@ -0,0 +1,135 @@ +package com.mhd.oms.domain.reserveStockInOrder.repository.util; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +import java.math.BigDecimal; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +/** + * 适配 2023/12/9 12:20:30 格式的日期转换器 + * 支持斜杠分隔、无补零日期/月份 + */ +public class StringToDateConverter implements Converter { + + // 核心修改:新增斜杠无补零格式,放在优先匹配位置 + private static final SimpleDateFormat[] DATE_FORMATS = { + // 1. 目标格式:2023/12/9 12:20:30(斜杠、无补零) + new SimpleDateFormat("yyyy/M/d HH:mm:ss", Locale.CHINA), + // 2. 简化格式:2023/12/9(斜杠、无补零) + new SimpleDateFormat("yyyy/M/d", Locale.CHINA), + // 3. 原支持格式:2023-12-09 18:30:30(横杠、有补零) + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA), + // 4. 原支持格式:2023-12-09(横杠、有补零) + new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA), + // 5. 兼容格式:2023/12/09 12:20:30(斜杠、有补零) + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.CHINA), + // 6. 兼容格式:2023/12/09(斜杠、有补零) + new SimpleDateFormat("yyyy/MM/dd", Locale.CHINA) + }; + + @Override + public Class supportJavaTypeKey() { + return Date.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; // 支持文本型单元格(实际使用格式) + } + + /** + * 导入解析:重点处理 2023/12/9 12:20:30 格式 + */ + @Override + public Date convertToJavaData( + ReadCellData cellData, + ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + + // 1. 获取并清洗单元格数据(去除空格、换行等干扰字符) + String cellValue = getCleanedDateValue(cellData); + if (cellValue == null || cellValue.trim().isEmpty()) { + return null; + } + + // 2. 按优先级尝试匹配格式(优先匹配目标格式) + for (SimpleDateFormat format : DATE_FORMATS) { + try { + format.setLenient(false); // 严格校验(避免 2023/02/30 等非法日期) + return format.parse(cellValue.trim()); + } catch (ParseException e) { + continue; // 格式不匹配,尝试下一种 + } + } + + // 3. 所有格式匹配失败,抛出明确异常 +// throw new IllegalArgumentException( +// String.format("单元格数据[%s]无法转换为日期,请使用格式:2023/12/9 12:20:30 或 2023-12-09 18:30:30", cellValue)); + return null; + } + + /** + * 导出:保持与导入格式一致(2023/12/9 12:20:30) + */ + @Override + public WriteCellData convertToExcelData( + Date value, + ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + + if (value == null) { + WriteCellData emptyData = new WriteCellData<>(""); + emptyData.setType(CellDataTypeEnum.STRING); + return emptyData; + } + + // 导出格式与导入目标格式一致:2023/12/9 12:20:30 + SimpleDateFormat targetFormat = new SimpleDateFormat("yyyy/M/d HH:mm:ss", Locale.CHINA); + String dateStr = targetFormat.format(value); + + WriteCellData cellData = new WriteCellData<>(dateStr); + cellData.setType(CellDataTypeEnum.STRING); + return cellData; + } + + /** + * 辅助方法:清洗单元格数据(处理文本型) + */ + private String getCleanedDateValue(ReadCellData cellData) { + if (cellData.getType() == CellDataTypeEnum.STRING) { + // 去除空格、制表符、换行符等干扰字符 + return cellData.getStringValue().replaceAll("\\s+", ""); + } else if (cellData.getType() == CellDataTypeEnum.NUMBER) { + // 兼容数值型日期(如 Excel 序列号 45235) + BigDecimal numberValue = cellData.getNumberValue(); + double v = numberValue.doubleValue(); + Date date = convertExcelDateToDate(v); + return getStringDate(date); + } + return null; + } + + public static Date convertExcelDateToDate(double excelDateValue) { + // Excel 的时间起始点是 1900年1月1日,但有一个需要修正的bug:Excel 误把1900年当作闰年 + // 简单处理下,减去2天(1天是起始偏移,1天是闰年bug) + int startDay = 25569; // 1970-01-01 对应的Excel序列号 + return new Date((long) ((excelDateValue - startDay) * 86400000)); + } + + public String getStringDate(Date date) { + // 创建日期格式化对象,指定格式 + SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + + // 将 Date 转换为 String + String dateString = sdf.format(date); + return dateString; + } +} \ No newline at end of file diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/vo/ReservedInventory.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/vo/ReservedInventory.java new file mode 100644 index 000000000..a6b5c2830 --- /dev/null +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/repository/vo/ReservedInventory.java @@ -0,0 +1,71 @@ +package com.mhd.oms.domain.reserveStockInOrder.repository.vo; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.mhd.oms.domain.reserveStockInOrder.repository.util.StringToBigDecimalConverter; +import com.mhd.oms.domain.reserveStockInOrder.repository.util.StringToDateConverter; +import lombok.Data; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 预约存货实体类 + * 与Excel模板字段一一映射 + */ +@Data +public class ReservedInventory { + + /** + * 货物名称(必填) + * index=2:对应Excel第3列(从0开始计数) + */ + @ExcelProperty(index = 2, value = "貨物名稱") + private String goodsName; + + /** + * 件数(改为BigDecimal类型,添加自定义转换器) + * converter:指定自定义转换器处理类型转换 + */ + @ExcelProperty( + index = 3, + value = "件數", + converter = StringToBigDecimalConverter.class // 关键:自定义转换器 + ) + private BigDecimal quantity; + + /** + * 保质期(支持 2023/12/9 12:20:30 格式) + * 已引用修改后的 StringToDateConverter + */ + @ExcelProperty( + index = 4, + value = "保質期", + converter = StringToDateConverter.class // 关键:确保引用正确 + ) + private Date shelfLife; + + /** + * 重量(可选) + */ + @ExcelProperty(index = 5, value = "重量", + converter = StringToBigDecimalConverter.class + ) + private BigDecimal weight; + + /** + * 备注(可选) + */ + @ExcelProperty(index = 6, value = "備註") + private String remarks; + + /** + * 操作类型(必填) + */ + @ExcelProperty(index = 7, value = "操作") + private String operationType; + + /** + * 验证结果(导入后自动填充,不映射Excel) + */ + private String validationResult; +} \ No newline at end of file diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/service/ReserveStockInOrderApplicationService.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/service/ReserveStockInOrderApplicationService.java index 7777fba38..8d55d32af 100644 --- a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/service/ReserveStockInOrderApplicationService.java +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/service/ReserveStockInOrderApplicationService.java @@ -7,16 +7,26 @@ import com.alibaba.fastjson2.JSONObject; import com.mhd.common.core.domain.po.UserPo; import com.mhd.common.core.enums.DictCode; import com.mhd.common.core.exception.ServiceException; +import com.mhd.common.core.utils.OrderSequence; import com.mhd.common.core.utils.StringUtils; import com.mhd.common.core.web.domain.AjaxResult; import com.mhd.common.security.utils.SecurityUtils; +import com.mhd.oms.domain.reservationMaterialInventory.repository.mapper.ReservationMaterialInventoryMapper; +import com.mhd.oms.domain.reservationMaterialInventory.repository.todo.MaterialBaseInfo; +import com.mhd.oms.domain.reservationStockInOrder.repository.po.MaterialBaseInfoPO; +import com.mhd.oms.domain.reserveInMaterialDetail.entity.ReserveInMaterialDetail; import com.mhd.oms.domain.reserveInMaterialDetail.repository.po.ReserveInMaterialDetailPO; +import com.mhd.oms.domain.reserveInMaterialDetail.repository.todo.ReserveInMaterialDetailDO; +import com.mhd.oms.domain.reserveStockInOrder.entity.ReserveStockInOrder; +import com.mhd.oms.domain.reserveStockInOrder.repository.mapper.ReserveStockInOrderMapper; import com.mhd.oms.domain.reserveStockInOrder.repository.po.ReserveStockInOrderPO; import com.mhd.oms.domain.reserveStockInOrder.repository.todo.ReserveStockInOrderDO; import com.mhd.oms.domain.reserveStockInOrder.repository.todo.ReserveStockInOrderDTO; import com.mhd.oms.domain.reserveStockInOrder.repository.vo.InAndOutListVo; +import com.mhd.oms.domain.reserveStockInOrder.repository.vo.ReservedInventory; import com.mhd.system.api.SystemServiceFeign; import com.mhd.system.api.UserServiceFeign; +import com.mhd.system.api.WmsServiceFeign; import com.mhd.system.api.domain.InMaterialDetailTZPD; import com.mhd.system.api.domain.StockInOrderTZPD; import com.mhd.system.api.domain.SysDictData; @@ -31,12 +41,13 @@ import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.math.BigDecimal; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Map; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; /** * 入库单ApplicationService @@ -50,9 +61,15 @@ public class ReserveStockInOrderApplicationService { @Autowired private ReserveStockInOrderDomainService stockInOrderDomainService; @Autowired + private ReserveStockInOrderMapper reserveStockInOrderMapper; + @Autowired + private ReservationMaterialInventoryMapper reservationMaterialInventoryMapper; + @Autowired private UserServiceFeign userServiceFeign; @Autowired private SystemServiceFeign systemServiceFeign; + @Autowired + private WmsServiceFeign wmsServiceFeign; // @Autowired // private IMaterialBaseInfoService materialBaseInfoService; @@ -1978,4 +1995,112 @@ public class ReserveStockInOrderApplicationService { BeanUtils.copyProperties(stockInOrderDTO, stockInOrderDO); return stockInOrderDomainService.queryNoticePrintDetail(stockInOrderDO); } + + public void importInData(List dataList) { + LoginUser loginUser = SecurityUtils.getLoginUser(); + ReserveStockInOrderDO reserveStockInOrder = new ReserveStockInOrderDO(); + reserveStockInOrder.setBusinessOrderNo(generateDateTimeWithRandom()); + reserveStockInOrder.setIssueStatus(0); + reserveStockInOrder.setDocumentDate(new Date()); + reserveStockInOrder.setExpectTime(new Date()); + reserveStockInOrder.setOrderStatus("新增"); + reserveStockInOrder.setNeedTransport(0); + reserveStockInOrder.setNeedCustomsDeclaration(0); + if (loginUser != null){ + Long topOrganizationId = loginUser.getUserPo().getTopOrganizationId(); + if (topOrganizationId != null && topOrganizationId != 1){ + reserveStockInOrder.setTopOrganizationId(loginUser.getUserPo().getTopOrganizationId()); + reserveStockInOrder.setOrganizationId(loginUser.getUserPo().getOrganizationId()); + reserveStockInOrder.setOrganizationName(loginUser.getUserPo().getOrganizationName()); + reserveStockInOrder.setShipperId(loginUser.getUserPo().getUserId()); + reserveStockInOrder.setShipperName(loginUser.getUserPo().getUserName()); + reserveStockInOrder.setSalesmanId(loginUser.getUserPo().getUserId().toString()); + reserveStockInOrder.setSalesmanName(loginUser.getUserPo().getUserName()); + reserveStockInOrder.setCustomerId(loginUser.getUserPo().getUserId()); + reserveStockInOrder.setCustomerName(loginUser.getUserPo().getUserName()); + reserveStockInOrder.setDocumentCreator(loginUser.getUserPo().getUserName()); + reserveStockInOrder.setCreateTime(new Date()); + reserveStockInOrder.setCreateBy(loginUser.getUserPo().getUserId()); + reserveStockInOrder.setCreateByName(loginUser.getUserPo().getUserName()); + } + } + List reserveInMaterialDetailDOList = new ArrayList<>(); + int index = 1; + for (ReservedInventory reservedInventory : dataList) { + ReserveInMaterialDetailDO reserveInMaterialDetail = new ReserveInMaterialDetailDO(); + BigDecimal quantity = reservedInventory.getQuantity(); + String goodsName = reservedInventory.getGoodsName(); + String remarks = reservedInventory.getRemarks(); + Date shelfLife = reservedInventory.getShelfLife(); + BigDecimal weight = reservedInventory.getWeight(); + + List materialBaseInfoPOS = reserveStockInOrderMapper.getByName(goodsName); + if (materialBaseInfoPOS != null&&materialBaseInfoPOS.size()>0) { + MaterialBaseInfoPO materialBaseInfoPO = materialBaseInfoPOS.get(0); + Long materialBaseInfoId = materialBaseInfoPO.getMaterialBaseInfoId(); + String materialCode = materialBaseInfoPO.getMaterialCode(); + reserveInMaterialDetail.setMaterialBaseInfoId(materialBaseInfoId); + reserveInMaterialDetail.setMaterialCode(materialCode); + reserveInMaterialDetail.setUnitCode(materialBaseInfoPO.getUnitCode()); + reserveInMaterialDetail.setUnitName(materialBaseInfoPO.getUnitName()); + } else { + throw new ServiceException(goodsName + "未找到对应物料"); + } + reserveInMaterialDetail.setMaterialName(goodsName); + reserveInMaterialDetail.setRemark(remarks); + reserveInMaterialDetail.setShelfLife(shelfLife); + reserveInMaterialDetail.setQuantity(quantity); + reserveInMaterialDetail.setReserveQuantity(quantity); + reserveInMaterialDetail.setTotalNetWeight(weight); + reserveInMaterialDetail.setTotalGrossWeight(weight); + reserveInMaterialDetail.setContractFee(BigDecimal.ZERO); + //reserveInMaterialDetail.setInOrderNumber(OrderSequence.getOrderCode("RK")); + String lot = generateDateSerialNumber(); + lot = lot+"-"+index; + index++; + reserveInMaterialDetail.setLotNo(lot); + if (loginUser != null){ + Long topOrganizationId = loginUser.getUserPo().getTopOrganizationId(); + if (topOrganizationId != null && topOrganizationId != 1){ + reserveInMaterialDetail.setTopOrganizationId(loginUser.getUserPo().getTopOrganizationId()); + reserveInMaterialDetail.setOrganizationId(loginUser.getUserPo().getOrganizationId()); + reserveInMaterialDetail.setOrganizationName(loginUser.getUserPo().getOrganizationName()); + reserveInMaterialDetail.setCreateTime(new Date()); + reserveInMaterialDetail.setCreateBy(loginUser.getUserPo().getUserId()); + reserveInMaterialDetail.setCreateByName(loginUser.getUserPo().getUserName()); + } + } + reserveInMaterialDetailDOList.add(reserveInMaterialDetail); + } + reserveStockInOrder.setMaterialDetailList(reserveInMaterialDetailDOList); + insert(reserveStockInOrder); + } + + public static String generateDateTimeWithRandom() { + // 1. 获取当前时间,格式化:yyMMddHHmmss + LocalDateTime now = LocalDateTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyMMddHHmmss"); + String dateTimePart = now.format(formatter); + + // 2. 生成四位随机数 (1000-9999,保证四位,不足补零) + int randomNum = ThreadLocalRandom.current().nextInt(1000, 10000); + String randomPart = String.valueOf(randomNum); + + // 3. 拼接返回 + return dateTimePart + randomPart; + } + + public String generateDateSerialNumber() { + // 1. 获取当前时间,格式化为:yyMMdd (2位年+2位月+2位日) + SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd"); + String datePart = sdf.format(new Date()); + + // 2. 生成 000~999 的 3 位随机数(不足补零) + Random random = new Random(); + int randomNum = random.nextInt(1000); // 0~999 + String randomPart = String.format("%03d", randomNum); // 自动补前导零 + + // 3. 拼接结果 + return datePart + randomPart; + } } \ No newline at end of file diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/service/ReserveStockInOrderDomainService.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/service/ReserveStockInOrderDomainService.java index 74ba667af..4ab9ce019 100644 --- a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/service/ReserveStockInOrderDomainService.java +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockInOrder/service/ReserveStockInOrderDomainService.java @@ -46,10 +46,8 @@ import org.springframework.util.CollectionUtils; import java.lang.reflect.Field; import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Objects; +import java.text.SimpleDateFormat; +import java.util.*; /** * 入库单 @@ -106,16 +104,16 @@ public class ReserveStockInOrderDomainService { // //设置物料信息 // setMaterialDetailInfo(stockInOrderDO); // //统计总物料种数 -// stockInOrderDO.setMaterialQuantity(stockInOrderDO.getMaterialDetailList().size()); + stockInOrderDO.setMaterialQuantity(stockInOrderDO.getMaterialDetailList().size()); // //统计总计划数 BigDecimal quantity = stockInOrderDO.getMaterialDetailList().stream().map(ReserveInMaterialDetailDO::getQuantity).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); stockInOrderDO.setQuantity(quantity); -// //统计总重量 -// BigDecimal weightLimit = stockInOrderDO.getMaterialDetailList().stream().map(ReserveInMaterialDetailDO::getWeightLimit).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); -// stockInOrderDO.setWeightLimit(weightLimit); -// //统计总体积 -// BigDecimal volumeLimit = stockInOrderDO.getMaterialDetailList().stream().map(ReserveInMaterialDetailDO::getVolumeLimit).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); -// stockInOrderDO.setVolumeLimit(volumeLimit); + //统计总重量 + BigDecimal weightLimit = stockInOrderDO.getMaterialDetailList().stream().map(ReserveInMaterialDetailDO::getWeightLimit).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); + stockInOrderDO.setWeightLimit(weightLimit); + //统计总体积 + BigDecimal volumeLimit = stockInOrderDO.getMaterialDetailList().stream().map(ReserveInMaterialDetailDO::getVolumeLimit).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); + stockInOrderDO.setVolumeLimit(volumeLimit); stockInOrderService.insert(stockInOrderDO); return materialDetailService.batchInsert(stockInOrderDO.getInOrderNumber(), stockInOrderDO.getMaterialDetailList()); } @@ -174,21 +172,6 @@ public class ReserveStockInOrderDomainService { ReserveInMaterialDetailBaseDO inMaterialDetailBaseDO = new ReserveInMaterialDetailBaseDO(); BeanUtils.copyProperties(stockInOrderPO, inMaterialDetailBaseDO); inMaterialDetailBaseDO.setInOrderNumber(stockInOrderPO.getInOrderNumber()); -// if (stockInOrderDO.getNeedTransport() ==0) { -// List businessDocumentOrders = businessDocumentOrderMapper.selectList(new LambdaQueryWrapper().eq(BusinessDocumentOrder::getInOrderNumber, stockInOrderDO.getInOrderNumber()).notIn(BusinessDocumentOrder::getOrderStatus, 4, 5)); -// if (businessDocumentOrders != null && businessDocumentOrders.size() > 0) { -// for (BusinessDocumentOrder businessDocumentOrder : businessDocumentOrders) { -// businessDocumentOrder.setOrderStatus(4L); -// String executeOrderNumber = businessDocumentOrder.getExecuteOrderNumber(); -// -// if (executeOrderNumber !=null){ -// throw new ServiceException("该业务单已生成执行单,取消失败"); -// } -// businessDocumentOrderMapper.updateById(businessDocumentOrder); -// } -// } -// } -// return materialDetailService.batchUpdate(stockInOrderDO.getMaterialDetailList(), inMaterialDetailBaseDO); } @@ -459,14 +442,22 @@ public class ReserveStockInOrderDomainService { ReservationStockInOrder reservationStockInOrder = new ReservationStockInOrder(); BeanUtils.copyProperties(byId, reservationStockInOrder,"inOrderId","issueStatus","issueTime","issueId","issueName","reservationAuditId","reservationAuditName","reservationAuditTime"); reservationStockInOrder.setIssueStatus(0); + reservationStockInOrder.setOrderTypeCode("pu_tong_ru_ku"); + reservationStockInOrder.setOrderTypeName("普通入库"); + reservationStockInOrder.setReservationOrderSource("客户预约"); reservationStockInOrderService.save(reservationStockInOrder); String inOrderNumber = reservationStockInOrder.getInOrderNumber(); List materialDetailList = materialDetailService.list(new QueryWrapper().lambda().eq(ReserveInMaterialDetail::getInOrderNumber, inOrderNumber)); List inMaterialDetailList = new ArrayList<>(); + int index = 1; for (ReserveInMaterialDetail reserveInMaterialDetail : materialDetailList) { ReservationInMaterialDetail reservationInMaterialDetail = new ReservationInMaterialDetail(); BeanUtils.copyProperties(reserveInMaterialDetail, reservationInMaterialDetail,"materialDetailId"); reservationInMaterialDetail.setQuantity(reserveInMaterialDetail.getReserveQuantity()); + String lot = generateDateSerialNumber(); + lot = lot+"-"+index; + index++; + reservationInMaterialDetail.setLotNo(lot); //reservationInMaterialDetail.set inMaterialDetailList.add(reservationInMaterialDetail); } @@ -474,6 +465,20 @@ public class ReserveStockInOrderDomainService { return update; } + public String generateDateSerialNumber() { + // 1. 获取当前时间,格式化为:yyMMdd (2位年+2位月+2位日) + SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd"); + String datePart = sdf.format(new Date()); + + // 2. 生成 000~999 的 3 位随机数(不足补零) + Random random = new Random(); + int randomNum = random.nextInt(1000); // 0~999 + String randomPart = String.format("%03d", randomNum); // 自动补前导零 + + // 3. 拼接结果 + return datePart + randomPart; + } + /** * @description 入库单返回结果 * @author ZhouGY diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/listener/ReservedInventoryOutListener.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/listener/ReservedInventoryOutListener.java new file mode 100644 index 000000000..eef8d4d12 --- /dev/null +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/listener/ReservedInventoryOutListener.java @@ -0,0 +1,89 @@ +package com.mhd.oms.domain.reserveStockOutOrder.repository.listener; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.listener.ReadListener; +import com.mhd.oms.domain.reserveStockInOrder.repository.util.ExcelParseUtil; +import com.mhd.oms.domain.reserveStockOutOrder.repository.vo.ReservedInventoryOut; +import lombok.Getter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +public class ReservedInventoryOutListener implements ReadListener { + private static final Logger log = LoggerFactory.getLogger(ReservedInventoryOutListener.class); + + @Getter + private final List dataList = new ArrayList<>(); + private static final int BATCH_SIZE = 100; + + @Override + public void invoke(ReservedInventoryOut data, AnalysisContext context) { + // 1. 获取当前行号(关键:EasyExcel 3.2.1 中 rowIndex 从0开始,需+1转为实际行号) + long currentRowNum = context.readRowHolder().getRowIndex() + 1; + log.info("读取第{}行数据:{}", currentRowNum, data); + + // 2. 核心过滤:仅保留 >= 数据起始行(第4行)的数据 + if (currentRowNum < ExcelParseUtil.TemplateConfig.DATA_START_ROW) { + log.info("第{}行(说明行/表头行),跳过解析", currentRowNum); + return; + } + + // 3. 过滤说明文本行(双重保障,避免极端情况) + if (isRemarkRow(data)) { + log.info("第{}行包含说明文本(如'保質到哪一天的日期'),跳过解析", currentRowNum); + return; + } + + // 4. 过滤空行,保留有效数据 + if (isValidDataRow(data)) { + dataList.add(data); + // 批量处理逻辑不变 + if (dataList.size() >= BATCH_SIZE) { + log.info("已读取{}条数据,执行批量处理", BATCH_SIZE); + dataList.clear(); + } + } + } + + /** + * 判断是否为说明文本行(精准匹配“保質到哪一天的日期”等关键词) + */ + private boolean isRemarkRow(ReservedInventoryOut data) { + if (data == null) { + return false; + } + // 匹配说明行特征:货物名称为提示文本,或保质期/件数均为空 + boolean isGoodsNameRemark = data.getGoodsName() != null && + (data.getGoodsName().contains("请输入") || data.getGoodsName().contains("保質到哪一天的日期")); + //boolean isEmptyRow = data.getQuantity() == null && data.getShelfLife() == null; + + return isGoodsNameRemark; + } + + /** + * 判断是否为有效数据行(至少一个必填字段有值) + */ + private boolean isValidDataRow(ReservedInventoryOut data) { + if (data == null) { + return false; + } + return (data.getGoodsName() != null && !data.getGoodsName().trim().isEmpty()) + || data.getQuantity() != null; + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + log.info("Excel数据读取完成,共读取{}条有效数据", dataList.size()); + } + + /** + * 可选:提前判断是否需要继续读取(优化性能) + */ + @Override + public boolean hasNext(AnalysisContext context) { + // 无需特殊处理,返回true即可 + return ReadListener.super.hasNext(context); + } +} \ No newline at end of file diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/listener/ValidationException.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/listener/ValidationException.java new file mode 100644 index 000000000..813b50b38 --- /dev/null +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/listener/ValidationException.java @@ -0,0 +1,20 @@ +package com.mhd.oms.domain.reserveStockOutOrder.repository.listener; + +/** + * 数据验证异常 + * 用于Excel导入数据验证失败场景 + */ +public class ValidationException extends RuntimeException { + + public ValidationException() { + super(); + } + + public ValidationException(String message) { + super(message); + } + + public ValidationException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/util/ExcelParseUtil.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/util/ExcelParseUtil.java new file mode 100644 index 000000000..693b30aba --- /dev/null +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/util/ExcelParseUtil.java @@ -0,0 +1,78 @@ +package com.mhd.oms.domain.reserveStockOutOrder.repository.util; + +import com.alibaba.excel.EasyExcel; +import com.mhd.oms.domain.reserveStockOutOrder.repository.listener.ReservedInventoryOutListener; +import com.mhd.oms.domain.reserveStockOutOrder.repository.vo.ReservedInventoryOut; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +/** + * Excel解析工具类 + * 提供Excel文件验证和数据解析功能 + */ +public class ExcelParseUtil { + + /** + * Excel模板常量配置 + */ + public static final class TemplateConfig { + // 表头行号(从1开始) + public static final int HEADER_ROW_NUM = 2; + // 数据起始行号(从1开始) + public static final int DATA_START_ROW = 4; + // 工作表名称 + public static final String SHEET_NAME = "Sheet1"; + // 最大文件大小(10MB) + public static final long MAX_FILE_SIZE = 100 * 1024 * 1024; + } + + /** + * 解析Excel文件为实体类列表 + * @param file 上传的Excel文件 + * @return 解析并验证后的实体类列表 + * @throws IOException 文件处理异常 + */ + public static List parseExcel(MultipartFile file) throws IOException { + // 1. 验证文件格式和大小 + validateFile(file); + + // 2. 读取Excel数据 + ReservedInventoryOutListener listener = new ReservedInventoryOutListener(); + try (InputStream inputStream = file.getInputStream()) { + EasyExcel.read(inputStream, ReservedInventoryOut.class, listener) + .sheet(TemplateConfig.SHEET_NAME) // 指定工作表 + .headRowNumber(TemplateConfig.HEADER_ROW_NUM) // 指定表头行 + .doRead(); // 执行读取 + } + + // 3. 数据验证 + List dataList = listener.getDataList(); + ReservedInventoryOutValidator.validateAll(dataList, TemplateConfig.DATA_START_ROW); + + return dataList; + } + + /** + * 验证Excel文件合法性 + */ + private static void validateFile(MultipartFile file) throws IOException { + // 验证文件不为空 + if (file.isEmpty()) { + throw new IOException("上传的Excel文件不能为空"); + } + + // 验证文件后缀 + String fileName = file.getOriginalFilename(); + if (fileName == null || (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls"))) { + throw new IOException("请上传Excel格式文件(.xlsx或.xls)"); + } + + // 验证文件大小 + if (file.getSize() > TemplateConfig.MAX_FILE_SIZE) { + throw new IOException("文件大小不能超过10MB"); + } + } +} \ No newline at end of file diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/util/ReservedInventoryOutValidator.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/util/ReservedInventoryOutValidator.java new file mode 100644 index 000000000..85cca9297 --- /dev/null +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/util/ReservedInventoryOutValidator.java @@ -0,0 +1,73 @@ +package com.mhd.oms.domain.reserveStockOutOrder.repository.util; + +import com.mhd.oms.domain.reserveStockInOrder.repository.listener.ValidationException; +import com.mhd.oms.domain.reserveStockOutOrder.repository.vo.ReservedInventoryOut; + +import java.math.BigDecimal; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +/** + * 预约存货数据验证器 + * 实现数据合法性校验 + */ +public class ReservedInventoryOutValidator { + + /** + * 批量验证数据列表 + * @param dataList 待验证数据 + * @param startRow 数据起始行号(用于错误定位) + */ + public static void validateAll(List dataList, int startRow) { + if (dataList == null || dataList.isEmpty()) { + throw new ValidationException("待验证的数据列表为空"); + } + + List errorList = new ArrayList<>(); + // 逐条验证 + for (int i = 0; i < dataList.size(); i++) { + ReservedInventoryOut data = dataList.get(i); + int rowNum = startRow + i; // 计算当前数据在Excel中的行号 + try { + validateSingle(data, rowNum); + } catch (ValidationException e) { + errorList.add(e.getMessage()); + // 设置验证结果到实体类 + data.setValidationResult(e.getMessage()); + } + } + + // 汇总错误信息 + if (!errorList.isEmpty()) { + throw new ValidationException("Excel数据验证失败:\n" + String.join("\n", errorList)); + } + } + + /** + * 单个数据验证 + */ + private static void validateSingle(ReservedInventoryOut data, int rowNum) { + List errors = new ArrayList<>(); + + // 1. 必填项验证 + if (data.getGoodsName() == null || data.getGoodsName().trim().isEmpty()) { + errors.add(String.format("第%d行:货物名称为必填项", rowNum)); + } + if (data.getQuantity() == null) { + errors.add(String.format("第%d行:件数为必填项", rowNum)); + } else if (data.getQuantity().compareTo(BigDecimal.ZERO) < 0) { + errors.add(String.format("第%d行:件数必须为正整数(当前值:%d)", rowNum, data.getQuantity())); + } + // 抛出验证错误 + if (!errors.isEmpty()) { + throw new ValidationException(String.join(";", errors)); + } + + // 验证通过 + data.setValidationResult("验证通过"); + } +} \ No newline at end of file diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/util/StringToBigDecimalConverter.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/util/StringToBigDecimalConverter.java new file mode 100644 index 000000000..5f3237bfe --- /dev/null +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/util/StringToBigDecimalConverter.java @@ -0,0 +1,104 @@ +package com.mhd.oms.domain.reserveStockOutOrder.repository.util; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +import java.math.BigDecimal; +import java.util.regex.Pattern; + +/** + * EasyExcel 3.2.1 适配版:字符串转BigDecimal转换器 + * 解决WriteCellData实例化爆红问题 + */ +public class StringToBigDecimalConverter implements Converter { + + // 正则表达式:清洗非数字字符(保留小数点) + private static final Pattern NUMBER_PATTERN = Pattern.compile("[^0-9.]"); + + /** + * 支持的Java类型(BigDecimal) + */ + @Override + public Class supportJavaTypeKey() { + return BigDecimal.class; + } + + /** + * 支持的Excel单元格类型(文本+数值) + */ + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; // 覆盖文本和数值类型转换 + } + + /** + * 导入:Excel → Java(逻辑不变,确保件数正确转换) + */ + @Override + public BigDecimal convertToJavaData( + ReadCellData cellData, + ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + + // 1. 获取并清洗单元格数据 + String cellValue = getCleanedCellValue(cellData); + if (cellValue == null || cellValue.isEmpty()) { + return null; + } + + // 2. 转换为BigDecimal(处理格式异常) + try { + return new BigDecimal(cellValue); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + String.format("单元格数据[%s]无法转换为数字,请检查格式", cellValue), e); + } + } + + /** + * 导出:Java → Excel(3.2.1版本适配核心) + * 关键:使用构造方法直接实例化,不依赖Builder + */ + @Override + public WriteCellData convertToExcelData( + BigDecimal value, + ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + + // 1. 处理空值:返回空字符串单元格 + if (value == null) { + WriteCellData emptyData = new WriteCellData<>(""); + emptyData.setType(CellDataTypeEnum.STRING); // 明确指定文本类型 + return emptyData; + } + + // 2. 处理非空值:BigDecimal转字符串后写入 + String valueStr = value.toString(); + WriteCellData cellData = new WriteCellData<>(valueStr); + cellData.setType(CellDataTypeEnum.STRING); // 明确数据类型,避免自动转换异常 + return cellData; + } + + /** + * 辅助方法:获取并清洗单元格数据(处理文本/数值型) + */ + private String getCleanedCellValue(ReadCellData cellData) { + String rawValue = null; + // 根据单元格类型获取原始值 + if (cellData.getType() == CellDataTypeEnum.STRING) { + rawValue = cellData.getStringValue().trim(); + } else if (cellData.getType() == CellDataTypeEnum.NUMBER) { + rawValue = cellData.getNumberValue().toString().trim(); + } + + // 清洗非数字字符(如"5件"→"5"," 10.5 "→"10.5") + if (rawValue == null) { + return null; + } + return NUMBER_PATTERN.matcher(rawValue).replaceAll(""); + } +} \ No newline at end of file diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/vo/ReservedInventoryOut.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/vo/ReservedInventoryOut.java new file mode 100644 index 000000000..618ef5324 --- /dev/null +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/repository/vo/ReservedInventoryOut.java @@ -0,0 +1,40 @@ +package com.mhd.oms.domain.reserveStockOutOrder.repository.vo; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.mhd.oms.domain.reserveStockInOrder.repository.util.StringToBigDecimalConverter; +import com.mhd.oms.domain.reserveStockInOrder.repository.util.StringToDateConverter; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 预约存货实体类 + * 与Excel模板字段一一映射 + */ +@Data +public class ReservedInventoryOut { + + /** + * 货物名称(必填) + * index=2:对应Excel第3列(从0开始计数) + */ + @ExcelProperty(index = 2, value = "貨物名稱") + private String goodsName; + + /** + * 件数(改为BigDecimal类型,添加自定义转换器) + * converter:指定自定义转换器处理类型转换 + */ + @ExcelProperty( + index = 3, + value = "件數", + converter = StringToBigDecimalConverter.class // 关键:自定义转换器 + ) + private BigDecimal quantity; + + /** + * 验证结果(导入后自动填充,不映射Excel) + */ + private String validationResult; +} \ No newline at end of file diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/service/ReserveStockOutOrderApplicationService.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/service/ReserveStockOutOrderApplicationService.java index 9e8278c16..00c53a9fc 100644 --- a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/service/ReserveStockOutOrderApplicationService.java +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/service/ReserveStockOutOrderApplicationService.java @@ -4,6 +4,7 @@ import cn.hutool.core.util.ObjectUtil; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.mhd.common.core.domain.po.UserPo; import com.mhd.common.core.enums.DictCode; import com.mhd.common.core.exception.ServiceException; @@ -13,15 +14,22 @@ import com.mhd.common.core.utils.uuid.IdGenerator; import com.mhd.common.core.web.domain.AjaxResult; import com.mhd.common.security.utils.SecurityUtils; import com.mhd.oms.domain.reservationInMaterialDetail.repository.todo.InventoryAdjustmentRecordPO; +import com.mhd.oms.domain.reservationMaterialInventory.entity.ReservationMaterialInventory; +import com.mhd.oms.domain.reservationMaterialInventory.repository.mapper.ReservationMaterialInventoryMapper; +import com.mhd.oms.domain.reservationStockInOrder.repository.po.MaterialBaseInfoPO; import com.mhd.oms.domain.reserveInMaterialDetail.repository.facade.IReserveInMaterialDetailService; +import com.mhd.oms.domain.reserveInMaterialDetail.repository.todo.ReserveInMaterialDetailDO; import com.mhd.oms.domain.reserveOutMaterialDetail.repository.facade.IReserveOutMaterialDetailService; import com.mhd.oms.domain.reserveOutMaterialDetail.repository.po.ReserveOutMaterialDetailPO; import com.mhd.oms.domain.reserveOutMaterialDetail.repository.todo.ReserveOutMaterialDetailDO; +import com.mhd.oms.domain.reserveStockInOrder.repository.mapper.ReserveStockInOrderMapper; import com.mhd.oms.domain.reserveStockInOrder.repository.todo.ReserveStockInOrderDO; +import com.mhd.oms.domain.reserveStockInOrder.repository.vo.ReservedInventory; import com.mhd.oms.domain.reserveStockOutOrder.repository.facade.IReserveStockOutOrderService; import com.mhd.oms.domain.reserveStockOutOrder.repository.po.ReserveStockOutOrderPO; import com.mhd.oms.domain.reserveStockOutOrder.repository.todo.ReserveStockOutOrderDO; import com.mhd.oms.domain.reserveStockOutOrder.repository.todo.ReserveStockOutOrderDTO; +import com.mhd.oms.domain.reserveStockOutOrder.repository.vo.ReservedInventoryOut; import com.mhd.system.api.SystemServiceFeign; import com.mhd.system.api.UserServiceFeign; import com.mhd.system.api.domain.*; @@ -34,10 +42,10 @@ import org.springframework.util.CollectionUtils; import java.lang.reflect.Field; import java.math.BigDecimal; import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; /** * 出库单ApplicationService @@ -56,6 +64,10 @@ public class ReserveStockOutOrderApplicationService { // private MaterialInventoryDomainService materialInventoryDomainService; // @Autowired // private MaterialInventoryMapper materialInventoryMapper; + @Autowired + private ReserveStockInOrderMapper reserveStockInOrderMapper; + @Autowired + private ReservationMaterialInventoryMapper reservationMaterialInventoryMapper; @Autowired private UserServiceFeign userServiceFeign; @Autowired @@ -1034,4 +1046,109 @@ public class ReserveStockOutOrderApplicationService { BeanUtils.copyProperties(stockOutOrderDTO, stockOutOrderDO); return stockOutOrderDomainService.queryOutStockNoticePrintDetail(stockOutOrderDO); } + + public void importInData(List dataList) { + LoginUser loginUser = SecurityUtils.getLoginUser(); + ReserveStockOutOrderDO reserveStockOutOrder = new ReserveStockOutOrderDO(); + reserveStockOutOrder.setBusinessOrderNo(generateDateTimeWithRandom()); + reserveStockOutOrder.setIssueStatus(0); + reserveStockOutOrder.setDocumentDate(new Date()); + reserveStockOutOrder.setExpectTime(new Date()); + reserveStockOutOrder.setNeedDeclareFlag("0"); + reserveStockOutOrder.setNeedTransportFlag("0"); + reserveStockOutOrder.setOutboundDate(new Date()); + //reserveStockOutOrder.setOrderStatus("新增"); + if (loginUser != null){ + Long topOrganizationId = loginUser.getUserPo().getTopOrganizationId(); + if (topOrganizationId != null && topOrganizationId != 1){ + reserveStockOutOrder.setTopOrganizationId(loginUser.getUserPo().getTopOrganizationId()); + reserveStockOutOrder.setOrganizationId(loginUser.getUserPo().getOrganizationId()); + reserveStockOutOrder.setOrganizationName(loginUser.getUserPo().getOrganizationName()); + reserveStockOutOrder.setShipperId(loginUser.getUserPo().getUserId()); + reserveStockOutOrder.setShipperName(loginUser.getUserPo().getUserName()); + reserveStockOutOrder.setSalesmanId(loginUser.getUserPo().getUserId().toString()); + reserveStockOutOrder.setSalesmanName(loginUser.getUserPo().getUserName()); + reserveStockOutOrder.setCustomerId(loginUser.getUserPo().getUserId()); + reserveStockOutOrder.setCustomerName(loginUser.getUserPo().getUserName()); + reserveStockOutOrder.setDocumentCreator(loginUser.getUserPo().getUserName()); + reserveStockOutOrder.setCreateTime(new Date()); + reserveStockOutOrder.setCreateBy(loginUser.getUserPo().getUserId()); + reserveStockOutOrder.setCreateByName(loginUser.getUserPo().getUserName()); + } + } + List reserveInMaterialDetailDOList = new ArrayList<>(); + int index = 1; + for (ReservedInventoryOut reservedInventory : dataList) { + ReserveOutMaterialDetailDO reserveOutMaterialDetail = new ReserveOutMaterialDetailDO(); + BigDecimal quantity = reservedInventory.getQuantity(); + String goodsName = reservedInventory.getGoodsName(); + + List materialBaseInfoPOS = reserveStockInOrderMapper.getByName(goodsName); + if (materialBaseInfoPOS != null&&materialBaseInfoPOS.size()>0) { + MaterialBaseInfoPO materialBaseInfoPO = materialBaseInfoPOS.get(0); + Long materialBaseInfoId = materialBaseInfoPO.getMaterialBaseInfoId(); + String materialCode = materialBaseInfoPO.getMaterialCode(); + reserveOutMaterialDetail.setMaterialBaseInfoId(materialBaseInfoId); + reserveOutMaterialDetail.setMaterialCode(materialCode); + List reservationMaterialInventories = reservationMaterialInventoryMapper.selectList(new LambdaQueryWrapper().eq(ReservationMaterialInventory::getMaterialBaseInfoId, materialBaseInfoId)); + if (reservationMaterialInventories!= null && reservationMaterialInventories.size()>0) { + ReservationMaterialInventory reservationMaterialInventory = reservationMaterialInventories.get(0); + reserveOutMaterialDetail.setAllocationQuantity(reservationMaterialInventory.getAllocationQuantity()); + } else { + throw new ServiceException(goodsName + "未找到对应库存"); + } + } else { + throw new ServiceException(goodsName + "未找到对应物料"); + } + reserveOutMaterialDetail.setMaterialName(goodsName); + reserveOutMaterialDetail.setQuantity(quantity); + reserveOutMaterialDetail.setContractFee(BigDecimal.ZERO); + String lot = generateDateSerialNumber(); + lot = lot+"-"+index; + index++; + reserveOutMaterialDetail.setLotNo(lot); + if (loginUser != null){ + Long topOrganizationId = loginUser.getUserPo().getTopOrganizationId(); + if (topOrganizationId != null && topOrganizationId != 1){ + reserveOutMaterialDetail.setTopOrganizationId(loginUser.getUserPo().getTopOrganizationId()); + reserveOutMaterialDetail.setOrganizationId(loginUser.getUserPo().getOrganizationId()); + reserveOutMaterialDetail.setOrganizationName(loginUser.getUserPo().getOrganizationName()); + reserveOutMaterialDetail.setCreateTime(new Date()); + reserveOutMaterialDetail.setCreateBy(loginUser.getUserPo().getUserId()); + reserveOutMaterialDetail.setCreateByName(loginUser.getUserPo().getUserName()); + } + } + reserveInMaterialDetailDOList.add(reserveOutMaterialDetail); + } + reserveStockOutOrder.setMaterialDetailList(reserveInMaterialDetailDOList); + insert(reserveStockOutOrder); + } + + public static String generateDateTimeWithRandom() { + // 1. 获取当前时间,格式化:yyMMddHHmmss + LocalDateTime now = LocalDateTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyMMddHHmmss"); + String dateTimePart = now.format(formatter); + + // 2. 生成四位随机数 (1000-9999,保证四位,不足补零) + int randomNum = ThreadLocalRandom.current().nextInt(1000, 10000); + String randomPart = String.valueOf(randomNum); + + // 3. 拼接返回 + return dateTimePart + randomPart; + } + + public String generateDateSerialNumber() { + // 1. 获取当前时间,格式化为:yyMMdd (2位年+2位月+2位日) + SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd"); + String datePart = sdf.format(new Date()); + + // 2. 生成 000~999 的 3 位随机数(不足补零) + Random random = new Random(); + int randomNum = random.nextInt(1000); // 0~999 + String randomPart = String.format("%03d", randomNum); // 自动补前导零 + + // 3. 拼接结果 + return datePart + randomPart; + } } \ No newline at end of file diff --git a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/service/ReserveStockOutOrderDomainService.java b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/service/ReserveStockOutOrderDomainService.java index b3edc9df5..dff56eb86 100644 --- a/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/service/ReserveStockOutOrderDomainService.java +++ b/mhd_oms/src/main/java/com/mhd/oms/domain/reserveStockOutOrder/service/ReserveStockOutOrderDomainService.java @@ -45,10 +45,8 @@ import org.springframework.util.CollectionUtils; import javax.annotation.Resource; import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Objects; +import java.text.SimpleDateFormat; +import java.util.*; /** * 出库单 @@ -277,13 +275,21 @@ public class ReserveStockOutOrderDomainService { ReservationStockOutOrder reservationStockOutOrder = new ReservationStockOutOrder(); BeanUtils.copyProperties(byId, reservationStockOutOrder,"outOrderId","issueStatus","issueTime","issueId","issueName","reservationAuditId","reservationAuditName","reservationAuditTime"); reservationStockOutOrder.setIssueStatus(0); + reservationStockOutOrder.setOrderTypeCode("pu_tong_chu_ku"); + reservationStockOutOrder.setOrderTypeName("普通出库"); + reservationStockOutOrder.setReservationOrderSource("客户预约"); reservationStockOutOrderService.save(reservationStockOutOrder); String outOrderNumber = reservationStockOutOrder.getOutOrderNumber(); List materialDetailList = outMaterialDetailService.list(new QueryWrapper().lambda().eq(ReserveOutMaterialDetail::getOutOrderNumber, outOrderNumber)); List inMaterialDetailList = new ArrayList<>(); + int index = 1; for (ReserveOutMaterialDetail reserveOutMaterialDetail : materialDetailList) { ReservationOutMaterialDetail reservationOutMaterialDetail = new ReservationOutMaterialDetail(); BeanUtils.copyProperties(reserveOutMaterialDetail, reservationOutMaterialDetail,"materialDetailId"); + String lot = generateDateSerialNumber(); + lot = lot+"-"+index; + index++; + reservationOutMaterialDetail.setLotNo(lot); inMaterialDetailList.add(reservationOutMaterialDetail); } reservationOutMaterialDetailService.saveBatch(inMaterialDetailList); @@ -291,6 +297,20 @@ public class ReserveStockOutOrderDomainService { return update; } + public String generateDateSerialNumber() { + // 1. 获取当前时间,格式化为:yyMMdd (2位年+2位月+2位日) + SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd"); + String datePart = sdf.format(new Date()); + + // 2. 生成 000~999 的 3 位随机数(不足补零) + Random random = new Random(); + int randomNum = random.nextInt(1000); // 0~999 + String randomPart = String.format("%03d", randomNum); // 自动补前导零 + + // 3. 拼接结果 + return datePart + randomPart; + } + /** * @description 审核入库单 * @author ZhouGY diff --git a/mhd_oms/src/main/java/com/mhd/oms/interfaces/facade/ReserveStockOutOrder/ReserveStockOutOrderApi.java b/mhd_oms/src/main/java/com/mhd/oms/interfaces/facade/ReserveStockOutOrder/ReserveStockOutOrderApi.java index 0d136c1e9..78cd80d0e 100644 --- a/mhd_oms/src/main/java/com/mhd/oms/interfaces/facade/ReserveStockOutOrder/ReserveStockOutOrderApi.java +++ b/mhd_oms/src/main/java/com/mhd/oms/interfaces/facade/ReserveStockOutOrder/ReserveStockOutOrderApi.java @@ -5,13 +5,17 @@ import com.mhd.common.core.web.controller.BaseController; import com.mhd.common.core.web.domain.AjaxResult; import com.mhd.common.core.web.page.TableDataInfo; import com.mhd.oms.domain.reserveOutMaterialDetail.repository.po.ReserveOutMaterialDetailPO; +import com.mhd.oms.domain.reserveStockInOrder.repository.listener.ValidationException; import com.mhd.oms.domain.reserveStockInOrder.repository.todo.ReserveStockInOrderDO; import com.mhd.oms.domain.reserveStockInOrder.repository.todo.ReserveStockInOrderDTO; +import com.mhd.oms.domain.reserveStockOutOrder.repository.util.ExcelParseUtil; +import com.mhd.oms.domain.reserveStockInOrder.repository.vo.ReservedInventory; import com.mhd.oms.domain.reserveStockOutOrder.entity.ReserveStockOutOrder; import com.mhd.oms.domain.reserveStockOutOrder.repository.facade.IReserveStockOutOrderService; import com.mhd.oms.domain.reserveStockOutOrder.repository.po.ReserveStockOutOrderPO; import com.mhd.oms.domain.reserveStockOutOrder.repository.todo.ReserveStockOutOrderDO; import com.mhd.oms.domain.reserveStockOutOrder.repository.todo.ReserveStockOutOrderDTO; +import com.mhd.oms.domain.reserveStockOutOrder.repository.vo.ReservedInventoryOut; import com.mhd.oms.domain.reserveStockOutOrder.service.ReserveStockOutOrderApplicationService; import com.mhd.oms.domain.reserveStockOutOrder.service.ReserveStockOutOrderAssembler; import com.mhd.system.api.domain.OutMaterialDetailTZPD; @@ -19,9 +23,13 @@ import com.mhd.system.api.domain.StockOutOrderTZPD; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; +import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -231,4 +239,45 @@ public class ReserveStockOutOrderApi extends BaseController { return AjaxResult.success(result); } + + @PostMapping("/import") + public ResponseEntity> importExcel(@RequestParam("file") MultipartFile file) { + try { + List dataList = ExcelParseUtil.parseExcel(file); + + stockOutOrderApplicationService.importInData(dataList); + + Map successResponse = new HashMap<>(); + successResponse.put("code", 200); + successResponse.put("message", "Excel导入成功"); + successResponse.put("dataCount", dataList.size()); + successResponse.put("details", dataList); + return ResponseEntity.ok(successResponse); + + } catch (ValidationException e) { + // 数据验证失败响应 + Map errorResponse = new HashMap<>(); + errorResponse.put("code", 400); + errorResponse.put("message", "数据验证失败"); + errorResponse.put("errorDetails", e.getMessage()); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse); + + } catch (IOException e) { + // 文件处理失败响应 + Map errorResponse = new HashMap<>(); + errorResponse.put("code", 500); + errorResponse.put("message", "文件处理失败"); + errorResponse.put("errorDetails", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse); + + } catch (Exception e) { + // 其他未知错误响应 + Map errorResponse = new HashMap<>(); + errorResponse.put("code", 500); + errorResponse.put("message", e.getMessage()); + errorResponse.put("errorDetails", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse); + } + } + } \ No newline at end of file diff --git a/mhd_oms/src/main/java/com/mhd/oms/interfaces/facade/reserveStockInOrder/ReserveStockInOrderApi.java b/mhd_oms/src/main/java/com/mhd/oms/interfaces/facade/reserveStockInOrder/ReserveStockInOrderApi.java index a8aae75c5..0874ec6fe 100644 --- a/mhd_oms/src/main/java/com/mhd/oms/interfaces/facade/reserveStockInOrder/ReserveStockInOrderApi.java +++ b/mhd_oms/src/main/java/com/mhd/oms/interfaces/facade/reserveStockInOrder/ReserveStockInOrderApi.java @@ -4,19 +4,26 @@ import com.mhd.common.core.web.controller.BaseController; import com.mhd.common.core.web.domain.AjaxResult; import com.mhd.common.core.web.page.TableDataInfo; import com.mhd.oms.domain.reserveInMaterialDetail.repository.po.ReserveInMaterialDetailPO; +import com.mhd.oms.domain.reserveStockInOrder.repository.listener.ValidationException; import com.mhd.oms.domain.reserveStockInOrder.repository.po.ReserveStockInOrderPO; import com.mhd.oms.domain.reserveStockInOrder.repository.todo.ReserveStockInOrderDO; import com.mhd.oms.domain.reserveStockInOrder.repository.todo.ReserveStockInOrderDTO; +import com.mhd.oms.domain.reserveStockInOrder.repository.util.ExcelParseUtil; import com.mhd.oms.domain.reserveStockInOrder.repository.vo.InAndOutListVo; +import com.mhd.oms.domain.reserveStockInOrder.repository.vo.ReservedInventory; import com.mhd.oms.domain.reserveStockInOrder.service.ReserveStockInOrderApplicationService; import com.mhd.oms.domain.reserveStockInOrder.service.ReserveStockInOrderAssembler; import com.mhd.system.api.domain.InMaterialDetailTZPD; import com.mhd.system.api.domain.StockInOrderTZPD; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; +import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -192,4 +199,44 @@ public class ReserveStockInOrderApi extends BaseController { return AjaxResult.success(result); } + @PostMapping("/import") + public ResponseEntity> importExcel(@RequestParam("file") MultipartFile file) { + try { + List dataList = ExcelParseUtil.parseExcel(file); + + stockInOrderApplicationService.importInData(dataList); + + Map successResponse = new HashMap<>(); + successResponse.put("code", 200); + successResponse.put("message", "Excel导入成功"); + successResponse.put("dataCount", dataList.size()); + successResponse.put("details", dataList); + return ResponseEntity.ok(successResponse); + + } catch (ValidationException e) { + // 数据验证失败响应 + Map errorResponse = new HashMap<>(); + errorResponse.put("code", 400); + errorResponse.put("message", "数据验证失败"); + errorResponse.put("errorDetails", e.getMessage()); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse); + + } catch (IOException e) { + // 文件处理失败响应 + Map errorResponse = new HashMap<>(); + errorResponse.put("code", 500); + errorResponse.put("message", "文件处理失败"); + errorResponse.put("errorDetails", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse); + + } catch (Exception e) { + // 其他未知错误响应 + Map errorResponse = new HashMap<>(); + errorResponse.put("code", 500); + errorResponse.put("message", e.getMessage()); + errorResponse.put("errorDetails", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse); + } + } + } \ No newline at end of file diff --git a/mhd_oms/src/main/resources/mapper/reserveStockInOrder/ReserveStockInOrderMapper.xml b/mhd_oms/src/main/resources/mapper/reserveStockInOrder/ReserveStockInOrderMapper.xml index ab9ef1728..d63bb4ddd 100644 --- a/mhd_oms/src/main/resources/mapper/reserveStockInOrder/ReserveStockInOrderMapper.xml +++ b/mhd_oms/src/main/resources/mapper/reserveStockInOrder/ReserveStockInOrderMapper.xml @@ -562,6 +562,10 @@ + + diff --git a/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/persistence/MaterialInventoryImpl.java b/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/persistence/MaterialInventoryImpl.java index c6c67e6ca..b866af172 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/persistence/MaterialInventoryImpl.java +++ b/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/persistence/MaterialInventoryImpl.java @@ -776,7 +776,7 @@ public class MaterialInventoryImpl extends ServiceImpl