diff --git a/src/main/java/com/gaotao/modules/warehouse/controller/IfsInventoryInitController.java b/src/main/java/com/gaotao/modules/warehouse/controller/IfsInventoryInitController.java index f903d8a..a8e7b6d 100644 --- a/src/main/java/com/gaotao/modules/warehouse/controller/IfsInventoryInitController.java +++ b/src/main/java/com/gaotao/modules/warehouse/controller/IfsInventoryInitController.java @@ -6,14 +6,27 @@ import com.gaotao.modules.warehouse.entity.InventoryStock; import com.gaotao.modules.warehouse.entity.dto.CreateHuRequestDto; import com.gaotao.modules.warehouse.entity.dto.BatchCreateHuRequestDto; import com.gaotao.modules.warehouse.service.IfsInventoryInitService; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.Map; /** * IFS库存初始化控制器 */ +@Slf4j @RestController @RequestMapping("/ifsInventoryInit") public class IfsInventoryInitController { @@ -99,4 +112,131 @@ public class IfsInventoryInitController { return R.error("批量创建HandlingUnit失败: " + e.getMessage()); } } + + /** + * @Author AI + * @Description 下载IFS库存导入Excel模板 + * @Date 2025/10/17 + * @return ResponseEntity + **/ + @GetMapping("/downloadTemplate") + public ResponseEntity downloadTemplate() { + log.info("=== 下载IFS库存导入模板 ==="); + + try { + // 模板文件路径 + String templatePath = "D:\\wms-file\\IFSInventoryImportTemplate.xlsx"; + File templateFile = new File(templatePath); + + if (!templateFile.exists()) { + log.error("模板文件不存在: {}", templatePath); + return ResponseEntity.notFound().build(); + } + + Resource resource = new FileSystemResource(templateFile); + + // 设置文件名(中文需要编码) + String fileName = "IFS库存导入模板.xlsx"; + String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()) + .replaceAll("\\+", "%20"); + + log.info("模板下载成功: {}", templatePath); + + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename*=UTF-8''" + encodedFileName) + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(resource); + + } catch (Exception e) { + log.error("下载模板失败: {}", e.getMessage(), e); + return ResponseEntity.internalServerError().build(); + } + } + + /** + * @Author AI + * @Description 批量删除IFS库存数据(真实删除) + * @Date 2025/10/17 + * @Param [request] + * @return com.gaotao.common.utils.R + **/ + @PostMapping("/batchDeleteInventory") + public R batchDeleteInventory(@RequestBody Map request) { + log.info("=== 开始批量删除IFS库存数据 ==="); + + try { + @SuppressWarnings("unchecked") + List> items = (List>) request.get("items"); + + if (items == null || items.isEmpty()) { + return R.error("删除项不能为空"); + } + + log.info("删除数量: {}", items.size()); + + int deletedCount = ifsInventoryInitService.batchDeleteInventory(items); + + log.info("=== 批量删除完成 === 成功删除: {} 条", deletedCount); + + return R.ok().put("msg", "批量删除成功!共删除 " + deletedCount + " 条记录"); + + } catch (Exception e) { + log.error("=== 批量删除失败 === 错误信息: {}", e.getMessage(), e); + return R.error("批量删除失败: " + e.getMessage()); + } + } + + /** + * @Author AI + * @Description 导入IFS库存Excel数据 + * @Date 2025/10/17 + * @Param [file, site, uploadBy] + * @return com.gaotao.common.utils.R + **/ + @PostMapping("/uploadIfsInventoryExcel") + public R uploadIfsInventoryExcel(@RequestParam("file") MultipartFile file, + @RequestParam("site") String site, + @RequestParam("uploadBy") String uploadBy) { + log.info("=== 开始导入IFS库存Excel数据 ==="); + log.info("文件名: {}, 站点: {}, 操作人: {}", file.getOriginalFilename(), site, uploadBy); + + try { + // 校验文件 + if (file.isEmpty()) { + return R.error("上传文件不能为空"); + } + + String fileName = file.getOriginalFilename(); + if (fileName == null || (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx"))) { + return R.error("文件格式不正确,只支持.xls和.xlsx格式的Excel文件"); + } + + // 调用Service处理Excel导入 + Map result = ifsInventoryInitService.importIfsInventoryExcel(file, site, uploadBy); + + int successCount = (int) result.get("successCount"); + int failCount = (int) result.get("failCount"); + @SuppressWarnings("unchecked") + List errorMessages = (List) result.get("errorMessages"); + + log.info("=== IFS库存Excel导入完成 === 成功: {}, 失败: {}", successCount, failCount); + + if (failCount == 0) { + return R.ok().put("msg", "导入成功!共导入 " + successCount + " 条数据"); + } else { + String msg = "导入完成!成功 " + successCount + " 条,失败 " + failCount + " 条"; + if (!errorMessages.isEmpty()) { + msg += "\n失败原因:\n" + String.join("\n", errorMessages.subList(0, Math.min(5, errorMessages.size()))); + if (errorMessages.size() > 5) { + msg += "\n...(还有 " + (errorMessages.size() - 5) + " 条错误)"; + } + } + return R.ok().put("msg", msg).put("errorMessages", errorMessages); + } + } catch (Exception e) { + log.error("=== IFS库存Excel导入失败 === 错误信息: {}", e.getMessage(), e); + return R.error("导入失败: " + e.getMessage()); + } + } } diff --git a/src/main/java/com/gaotao/modules/warehouse/service/IfsInventoryInitService.java b/src/main/java/com/gaotao/modules/warehouse/service/IfsInventoryInitService.java index 2750fbf..ae20aef 100644 --- a/src/main/java/com/gaotao/modules/warehouse/service/IfsInventoryInitService.java +++ b/src/main/java/com/gaotao/modules/warehouse/service/IfsInventoryInitService.java @@ -4,8 +4,10 @@ import com.gaotao.common.utils.PageUtils; import com.gaotao.modules.warehouse.entity.InventoryStock; import com.gaotao.modules.warehouse.entity.dto.CreateHuRequestDto; import com.gaotao.modules.warehouse.entity.dto.BatchCreateHuRequestDto; +import org.springframework.web.multipart.MultipartFile; import java.util.List; +import java.util.Map; /** * IFS库存初始化服务接口 @@ -47,4 +49,22 @@ public interface IfsInventoryInitService { * @return 创建的HU ID列表 */ List batchCreateHandlingUnits(BatchCreateHuRequestDto request); + + /** + * 导入IFS库存Excel数据 + * + * @param file Excel文件 + * @param site 站点 + * @param uploadBy 上传人 + * @return 导入结果统计 + */ + Map importIfsInventoryExcel(MultipartFile file, String site, String uploadBy) throws Exception; + + /** + * 批量删除IFS库存数据(真实删除) + * + * @param items 删除项列表,每项包含site, warehouseId, partNo, batchNo, locationId, wdr + * @return 删除的记录数 + */ + int batchDeleteInventory(List> items); } diff --git a/src/main/java/com/gaotao/modules/warehouse/service/impl/IfsInventoryInitServiceImpl.java b/src/main/java/com/gaotao/modules/warehouse/service/impl/IfsInventoryInitServiceImpl.java index 7738607..798c58e 100644 --- a/src/main/java/com/gaotao/modules/warehouse/service/impl/IfsInventoryInitServiceImpl.java +++ b/src/main/java/com/gaotao/modules/warehouse/service/impl/IfsInventoryInitServiceImpl.java @@ -1,8 +1,13 @@ package com.gaotao.modules.warehouse.service.impl; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.listener.ReadListener; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.gaotao.common.utils.PageUtils; +import com.gaotao.modules.factory.dao.PartAttributeMapper; +import com.gaotao.modules.factory.entity.PartAttribute; import com.gaotao.modules.handlingunit.entity.HandlingUnit; import com.gaotao.modules.handlingunit.service.HandlingUnitIdGeneratorService; import com.gaotao.modules.handlingunit.service.HandlingUnitService; @@ -13,15 +18,18 @@ import com.gaotao.modules.warehouse.entity.dto.CreateHuRequestDto; import com.gaotao.modules.warehouse.entity.dto.BatchCreateHuRequestDto; import com.gaotao.modules.warehouse.service.IfsInventoryInitService; import com.gaotao.modules.warehouse.service.InventoryStockService; +import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import lombok.extern.slf4j.Slf4j; +import org.springframework.web.multipart.MultipartFile; +import java.io.InputStream; import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import java.text.SimpleDateFormat; +import java.util.*; /** * IFS库存初始化服务实现类 @@ -42,6 +50,12 @@ public class IfsInventoryInitServiceImpl implements IfsInventoryInitService { @Autowired private InventoryStockMapper inventoryStockMapper; + @Autowired + private JdbcTemplate jdbcTemplate; + + @Autowired + private PartAttributeMapper partAttributeMapper; + @Override public PageUtils getInventoryStockList(InventoryStock data) { IPage ipage = warehouseMapper.getIfsInventoryStockList( @@ -512,4 +526,470 @@ public class IfsInventoryInitServiceImpl implements IfsInventoryInitService { throw new RuntimeException("批量创建HU失败: " + e.getMessage()); } } + + /** + * 导入IFS库存Excel数据到inventory_stock_ifs表和part_attribute表 + *

Excel列对应关系(inventory_stock_ifs表):

+ *
    + *
  • A列: Site -> site
  • + *
  • B列: Warehouse Part No -> warehouse_id
  • + *
  • C列: Part No -> part_no
  • + *
  • D列: Part Description -> description
  • + *
  • E列: UoM -> umid
  • + *
  • F列: Lot/Batch No -> batch_no
  • + *
  • G列: W/D/R -> wdr
  • + *
  • H列: Location No -> location_id
  • + *
  • I列: Receipt Date -> manufacture_date
  • + *
  • J列: Expiration Date -> expired_date
  • + *
  • K列: Qty On Hand -> qty_on_hand
  • + *
+ *

Excel列对应关系(part_attribute表):

+ *
    + *
  • L列: is_zin_eh -> is_in_wh(是否进立库)
  • + *
  • M列: is_zxbp_eh -> is_robot_pick(是否机械手臂拣选)
  • + *
  • N列: weight -> weight(重量kg)
  • + *
  • O列: length -> length(长度mm)
  • + *
  • P列: width -> width(宽度mm)
  • + *
  • Q列: 毫米(mm) -> height(高度mm)
  • + *
  • R列: 最大直径max -> diameter(直径mm)
  • + *
  • S列: HU -> handling_unit_flag(HU标志)
  • + *
  • T列: 密封性 -> is_commonly_used(是否常用)
  • + *
  • U列: A02型号 -> (暂不处理)
  • + *
+ *

默认值:print_qty=0, inventory_stock_ifs.length=0, inventory_stock_ifs.width=0

+ *

注意:

+ *
    + *
  • inventory_stock_ifs表:只插入不更新,记录已存在则报错
  • + *
  • part_attribute表:记录已存在则跳过,不报错
  • + *
+ * + * @param file Excel文件 + * @param site 站点 + * @param uploadBy 上传人 + * @return 导入结果统计 + */ + @Override + @Transactional + public Map importIfsInventoryExcel(MultipartFile file, String site, String uploadBy) throws Exception { + log.info("=== 开始解析IFS库存Excel === 站点: {}, 操作人: {}", site, uploadBy); + + List errorMessages = new ArrayList<>(); + List> successList = new ArrayList<>(); + + try (InputStream inputStream = file.getInputStream()) { + // 使用EasyExcel读取数据 + EasyExcel.read(inputStream, new ReadListener>() { + private int currentRow = 0; + + @Override + public void invoke(Map data, AnalysisContext context) { + currentRow++; + + log.info("开始处理第{}行数据", currentRow); + + // 跳过表头行 + /*if (currentRow == 1) { + log.info("第{}行是表头行,跳过", currentRow); + return; + }*/ + + try { + // 解析Excel行数据 - inventory_stock_ifs表字段 + String rowSite = getValue(data, 0); // A列: Site + String warehouseId = getValue(data, 1); // B列: Warehouse Part No + String partNo = getValue(data, 2); // C列: Part No + String description = getValue(data, 3); // D列: Part Description + String umid = getValue(data, 4); // E列: UoM + String batchNo = getValue(data, 5); // F列: Lot/Batch No + String wdr = getValue(data, 6); // G列: W/D/R + String locationId = getValue(data, 7); // H列: Location No + String receiptDateStr = getValue(data, 8); // I列: Receipt Date + String expirationDateStr = getValue(data, 9); // J列: Expiration Date + String qtyOnHandStr = getValue(data, 10); // K列: Qty On Hand + + // 解析Excel行数据 - part_attribute表字段(L-U列,10列) + String isInWh = getValue(data, 11); // L列: is_zin_eh + String isRobotPick = getValue(data, 12); // M列: is_zxbp_eh + String weightStr = getValue(data, 13); // N列: weight + String lengthStr = getValue(data, 14); // O列: length + String widthStr = getValue(data, 15); // P列: width + String heightStr = getValue(data, 16); // Q列: 毫米(mm) + String diameterStr = getValue(data, 17); // R列: 最大直径max + String handlingUnitFlag = getValue(data, 18); // S列: HU + String isCommonlyUsed = getValue(data, 19); // T列: 密封性 + // U列(20): A02型号 - 暂不处理 + + // 检查是否为空行(所有必填字段都为空) + if (StringUtils.isBlank(rowSite) && StringUtils.isBlank(partNo) + && StringUtils.isBlank(batchNo) && StringUtils.isBlank(locationId)) { + log.info("第{}行为空行,跳过", currentRow); + return; + } + + // 验证必填字段 + if (StringUtils.isBlank(rowSite)) { + String error = "第" + currentRow + "行: Site不能为空"; + log.warn(error); + errorMessages.add(error); + return; + } + if (StringUtils.isBlank(partNo)) { + String error = "第" + currentRow + "行: Part No不能为空"; + log.warn(error); + errorMessages.add(error); + return; + } + if (StringUtils.isBlank(batchNo)) { + String error = "第" + currentRow + "行: Lot/Batch No不能为空"; + log.warn(error); + errorMessages.add(error); + return; + } + if (StringUtils.isBlank(locationId)) { + String error = "第" + currentRow + "行: Location No不能为空"; + log.warn(error); + errorMessages.add(error); + return; + } + if (StringUtils.isBlank(qtyOnHandStr)) { + String error = "第" + currentRow + "行: Qty On Hand不能为空"; + log.warn(error); + errorMessages.add(error); + return; + } + + // 解析数量 + BigDecimal qtyOnHand; + try { + qtyOnHand = new BigDecimal(qtyOnHandStr); + } catch (NumberFormatException e) { + errorMessages.add("第" + currentRow + "行: Qty On Hand格式不正确: " + qtyOnHandStr); + return; + } + + // 解析日期 + Date manufactureDate = parseDate(receiptDateStr); + Date expiredDate = parseDate(expirationDateStr); + + // 构建inventory_stock_ifs数据Map + Map rowData = new HashMap<>(); + rowData.put("site", rowSite); + rowData.put("warehouse_id", warehouseId); + rowData.put("part_no", partNo); + rowData.put("description", description); + rowData.put("umid", umid); + rowData.put("batch_no", batchNo); + rowData.put("wdr", wdr); + rowData.put("location_id", locationId); + rowData.put("manufacture_date", manufactureDate); + rowData.put("expired_date", expiredDate); + rowData.put("qty_on_hand", qtyOnHand); + rowData.put("print_qty", BigDecimal.ZERO); // 默认0 + rowData.put("length", BigDecimal.ZERO); // 默认0 + rowData.put("width", BigDecimal.ZERO); // 默认0 + rowData.put("rowNumber", currentRow); + + // 构建part_attribute数据(仅当有数据时才创建) + boolean hasPartAttributeData = StringUtils.isNotBlank(isInWh) + || StringUtils.isNotBlank(isRobotPick) + || StringUtils.isNotBlank(weightStr) + || StringUtils.isNotBlank(lengthStr) + || StringUtils.isNotBlank(widthStr) + || StringUtils.isNotBlank(heightStr) + || StringUtils.isNotBlank(diameterStr) + || StringUtils.isNotBlank(isCommonlyUsed); + + if (hasPartAttributeData) { + PartAttribute partAttribute = new PartAttribute(); + partAttribute.setSite(rowSite); + partAttribute.setPartNo(partNo); + partAttribute.setIsInWh(normalizeYN(isInWh)); + partAttribute.setIsRobotPick(normalizeYN(isRobotPick)); + partAttribute.setWeight(parseBigDecimal(weightStr)); + partAttribute.setLength(parseBigDecimal(lengthStr)); + partAttribute.setWidth(parseBigDecimal(widthStr)); + partAttribute.setHeight(parseBigDecimal(heightStr)); + partAttribute.setDiameter(parseBigDecimal(diameterStr)); + partAttribute.setIsCommonlyUsed(normalizeYN(isCommonlyUsed)); + + rowData.put("part_attribute", partAttribute); + } + + successList.add(rowData); + log.info("第{}行数据解析成功: partNo={}, batchNo={}, locationId={}, qtyOnHand={}, hasPartAttribute={}", + currentRow, partNo, batchNo, locationId, qtyOnHand, hasPartAttributeData); + + } catch (Exception e) { + log.error("第{}行数据处理失败: {}", currentRow, e.getMessage(), e); + errorMessages.add("第" + currentRow + "行: 数据处理失败 - " + e.getMessage()); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + log.info("Excel解析完成,共读取 {} 行(包含表头),成功解析 {} 行数据", currentRow, successList.size()); + } + }).sheet().doRead(); + + // 批量插入数据到数据库 + int successCount = 0; + int failCount = 0; + int partAttributeSkipped = 0; + int partAttributeSaved = 0; + + for (Map rowData : successList) { + try { + // 1. 插入或更新inventory_stock_ifs表 + insertOrUpdateIfsInventory(rowData); + + // 2. 插入part_attribute表(如果已存在则跳过) + PartAttribute partAttribute = (PartAttribute) rowData.get("part_attribute"); + if (partAttribute != null) { + boolean saved = insertPartAttributeIfNotExists(partAttribute); + if (saved) { + partAttributeSaved++; + } else { + partAttributeSkipped++; + } + } + + successCount++; + } catch (Exception e) { + failCount++; + int rowNumber = (int) rowData.get("rowNumber"); + log.error("第{}行数据插入失败: {}", rowNumber, e.getMessage(), e); + errorMessages.add("第" + rowNumber + "行: 数据库插入失败 - " + e.getMessage()); + } + } + + log.info("=== part_attribute表处理统计 === 新增: {}, 跳过: {}", partAttributeSaved, partAttributeSkipped); + + log.info("=== IFS库存Excel导入完成 === 成功: {}, 失败: {}", successCount, failCount); + + Map result = new HashMap<>(); + result.put("successCount", successCount); + result.put("failCount", failCount); + result.put("errorMessages", errorMessages); + + return result; + + } catch (Exception e) { + log.error("Excel文件读取失败: {}", e.getMessage(), e); + throw new RuntimeException("Excel文件读取失败: " + e.getMessage()); + } + } + + /** + * 从Map中安全获取字符串值 + */ + private String getValue(Map data, int index) { + String value = data.get(index); + return value != null ? value.trim() : ""; + } + + /** + * 标准化Y/N值 + * 如果是Y则返回Y,如果是N则返回N,其他值返回N + */ + private String normalizeYN(String value) { + if (StringUtils.isBlank(value)) { + return "N"; + } + value = value.trim().toUpperCase(); + return "Y".equals(value) ? "Y" : "N"; + } + + /** + * 解析BigDecimal值 + */ + private BigDecimal parseBigDecimal(String value) { + if (StringUtils.isBlank(value)) { + return null; + } + try { + return new BigDecimal(value.trim()); + } catch (NumberFormatException e) { + log.warn("无法解析数字: {}", value); + return null; + } + } + + /** + * 插入part_attribute记录(如果不存在) + * @param partAttribute 料件属性对象 + * @return true=成功插入,false=已存在跳过 + */ + private boolean insertPartAttributeIfNotExists(PartAttribute partAttribute) { + try { + // 检查是否已存在 + PartAttribute existing = partAttributeMapper.getPartAttributeByKey( + partAttribute.getSite(), + partAttribute.getPartNo() + ); + + if (existing != null) { + log.debug("料件属性已存在,跳过: site={}, partNo={}", + partAttribute.getSite(), partAttribute.getPartNo()); + return false; + } + + // 插入新记录 + partAttributeMapper.insert(partAttribute); + log.debug("料件属性新增成功: site={}, partNo={}", + partAttribute.getSite(), partAttribute.getPartNo()); + return true; + + } catch (Exception e) { + log.error("料件属性插入异常: site={}, partNo={}, error={}", + partAttribute.getSite(), partAttribute.getPartNo(), e.getMessage()); + // 不抛出异常,允许继续处理其他数据 + return false; + } + } + + /** + * 解析日期字符串 + * 支持格式:YYYY/M/D, YYYY-MM-DD + */ + private Date parseDate(String dateStr) { + if (StringUtils.isBlank(dateStr)) { + return null; + } + + try { + // 尝试多种日期格式 + String[] formats = { + "yyyy/M/d", + "yyyy-MM-dd", + "yyyy/MM/dd", + "yyyy-M-d", + "M/d/yyyy", + "MM/dd/yyyy" + }; + + for (String format : formats) { + try { + SimpleDateFormat sdf = new SimpleDateFormat(format); + sdf.setLenient(false); + return sdf.parse(dateStr); + } catch (Exception e) { + // 继续尝试下一个格式 + } + } + + log.warn("无法解析日期: {}", dateStr); + return null; + } catch (Exception e) { + log.error("日期解析异常: {}", dateStr, e); + return null; + } + } + + /** + * 批量删除IFS库存数据(真实删除) + * + * @param items 删除项列表 + * @return 删除的记录数 + */ + @Override + @Transactional + public int batchDeleteInventory(List> items) { + log.info("=== 开始批量删除IFS库存 === 删除项数量: {}", items.size()); + + int deletedCount = 0; + + for (Map item : items) { + try { + String site = item.get("site"); + String warehouseId = item.get("warehouseId"); + String partNo = item.get("partNo"); + String batchNo = item.get("batchNo"); + String locationId = item.get("locationId"); + String wdr = item.get("wdr"); + + log.info("删除记录: site={}, warehouse={}, partNo={}, batchNo={}, location={}, wdr={}", + site, warehouseId, partNo, batchNo, locationId, wdr); + + // 执行删除 + String deleteSql = "DELETE FROM inventory_stock_ifs " + + "WHERE site = ? AND ISNULL(warehouse_id, '') = ISNULL(?, '') AND part_no = ? " + + "AND batch_no = ? AND location_id = ? AND ISNULL(wdr, '') = ISNULL(?, '')"; + + int rows = jdbcTemplate.update(deleteSql, + site, + warehouseId, + partNo, + batchNo, + locationId, + wdr + ); + + deletedCount += rows; + log.debug("删除成功: 影响行数={}", rows); + + } catch (Exception e) { + log.error("删除记录失败: {}", e.getMessage(), e); + throw new RuntimeException("删除失败: " + e.getMessage()); + } + } + + log.info("=== 批量删除完成 === 总删除记录数: {}", deletedCount); + return deletedCount; + } + + /** + * 插入IFS库存数据(仅插入,不更新) + * 如果记录已存在(基于site, warehouse_id, part_no, batch_no, location_id, wdr),则抛出异常 + */ + private void insertOrUpdateIfsInventory(Map data) { + // 1. 先检查记录是否已存在 + String checkSql = "SELECT COUNT(*) FROM inventory_stock_ifs " + + "WHERE site = ? AND ISNULL(warehouse_id, '') = ISNULL(?, '') AND part_no = ? " + + "AND batch_no = ? AND location_id = ? AND ISNULL(wdr, '') = ISNULL(?, '')"; + + Integer count = jdbcTemplate.queryForObject(checkSql, Integer.class, + data.get("site"), + data.get("warehouse_id"), + data.get("part_no"), + data.get("batch_no"), + data.get("location_id"), + data.get("wdr") + ); + + // 2. 如果记录已存在,则抛出异常 + if (count != null && count > 0) { + throw new RuntimeException(String.format( + "库存记录已存在:Site=%s, Warehouse=%s, PartNo=%s, BatchNo=%s, Location=%s, WDR=%s", + data.get("site"), + data.get("warehouse_id"), + data.get("part_no"), + data.get("batch_no"), + data.get("location_id"), + data.get("wdr") + )); + } + + // 3. 插入新记录 + String insertSql = "INSERT INTO inventory_stock_ifs " + + "(site, warehouse_id, part_no, description, umid, batch_no, wdr, location_id, " + + "manufacture_date, expired_date, qty_on_hand, print_qty, length, width) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + jdbcTemplate.update(insertSql, + data.get("site"), + data.get("warehouse_id"), + data.get("part_no"), + data.get("description"), + data.get("umid"), + data.get("batch_no"), + data.get("wdr"), + data.get("location_id"), + data.get("manufacture_date"), + data.get("expired_date"), + data.get("qty_on_hand"), + data.get("print_qty"), + data.get("length"), + data.get("width") + ); + } }