From a4f592a23b746e3b847eae90b12067bc209b639c Mon Sep 17 00:00:00 2001 From: "han\\hanst" Date: Mon, 25 May 2026 13:11:05 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=87=E8=B4=AD=E5=AF=BC=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PartSparePurchaseImportController.java | 80 +++ .../PartSparePurchaseImportQueryData.java | 69 +++ .../entity/PartSparePurchaseImportEntity.java | 109 ++++ .../mapper/PartSparePurchaseImportMapper.java | 22 + .../PartSparePurchaseImportServiceImpl.java | 497 ++++++++++++++++++ .../PartSparePurchaseImportService.java | 28 + .../pms/PartSparePurchaseImportMapper.xml | 115 ++++ 7 files changed, 920 insertions(+) create mode 100644 src/main/java/com/xujie/sys/modules/pms/controller/PartSparePurchaseImportController.java create mode 100644 src/main/java/com/xujie/sys/modules/pms/data/PartSparePurchaseImportQueryData.java create mode 100644 src/main/java/com/xujie/sys/modules/pms/entity/PartSparePurchaseImportEntity.java create mode 100644 src/main/java/com/xujie/sys/modules/pms/mapper/PartSparePurchaseImportMapper.java create mode 100644 src/main/java/com/xujie/sys/modules/pms/service/Impl/PartSparePurchaseImportServiceImpl.java create mode 100644 src/main/java/com/xujie/sys/modules/pms/service/PartSparePurchaseImportService.java create mode 100644 src/main/resources/mapper/pms/PartSparePurchaseImportMapper.xml diff --git a/src/main/java/com/xujie/sys/modules/pms/controller/PartSparePurchaseImportController.java b/src/main/java/com/xujie/sys/modules/pms/controller/PartSparePurchaseImportController.java new file mode 100644 index 00000000..58f2c84b --- /dev/null +++ b/src/main/java/com/xujie/sys/modules/pms/controller/PartSparePurchaseImportController.java @@ -0,0 +1,80 @@ +package com.xujie.sys.modules.pms.controller; + +import com.xujie.sys.common.utils.PageUtils; +import com.xujie.sys.common.utils.R; +import com.xujie.sys.modules.pms.data.PartSparePurchaseImportQueryData; +import com.xujie.sys.modules.pms.entity.PartSparePurchaseImportEntity; +import com.xujie.sys.modules.pms.service.PartSparePurchaseImportService; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@RestController +@RequestMapping("/pms/partspare/purchaseImport") +public class PartSparePurchaseImportController { + + @Autowired + private PartSparePurchaseImportService partSparePurchaseImportService; + + @PostMapping("/queryPage") + public R queryPage(@RequestBody PartSparePurchaseImportQueryData data) { + PageUtils page = partSparePurchaseImportService.queryPage(data); + return R.ok() + .put("page", page) + .put("summary", partSparePurchaseImportService.querySummary(data)); + } + + @PostMapping("/save") + public R save(@RequestBody PartSparePurchaseImportEntity data) { + partSparePurchaseImportService.saveData(data); + return R.ok("保存成功"); + } + + @PostMapping("/update") + public R update(@RequestBody PartSparePurchaseImportEntity data) { + partSparePurchaseImportService.updateData(data); + return R.ok("修改成功"); + } + + @PostMapping("/delete") + public R delete(@RequestBody Object[] ids) { + if (ids == null || ids.length == 0) { + return R.error("请先选择需要删除的数据"); + } + List idList = new ArrayList<>(); + for (Object id : ids) { + if (id == null) { + continue; + } + try { + idList.add(Long.parseLong(String.valueOf(id))); + } catch (Exception e) { + return R.error("删除参数错误,ID格式无效"); + } + } + if (idList.isEmpty()) { + return R.error("请先选择需要删除的数据"); + } + partSparePurchaseImportService.deleteData(idList.toArray(new Long[0])); + return R.ok("删除成功"); + } + + @PostMapping("/importExcel") + public R importExcel(@RequestParam("file") MultipartFile file, + @RequestParam("site") String site, + @RequestParam("buNo") String buNo) { + int count = partSparePurchaseImportService.importExcel(file, site, buNo); + return R.ok("导入成功").put("count", count); + } + + @GetMapping("/downloadTemplate") + public void downloadTemplate(HttpServletResponse response) { + partSparePurchaseImportService.downloadTemplate(response); + } +} diff --git a/src/main/java/com/xujie/sys/modules/pms/data/PartSparePurchaseImportQueryData.java b/src/main/java/com/xujie/sys/modules/pms/data/PartSparePurchaseImportQueryData.java new file mode 100644 index 00000000..2c2b706e --- /dev/null +++ b/src/main/java/com/xujie/sys/modules/pms/data/PartSparePurchaseImportQueryData.java @@ -0,0 +1,69 @@ +package com.xujie.sys.modules.pms.data; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 备件采购订单导入记录查询条件 + */ +@Data +public class PartSparePurchaseImportQueryData { + + private String username; + + private String site; + + private String buNo; + + private String supplierName; + + private String currencyType; + + private String orderNo; + + private String itemCode; + + private String itemName; + + private String projectBu; + + private String requestNo; + + private String requestBy; + + private String importBy; + + private String sourceFileName; + + @DateTimeFormat(pattern = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private Date orderDateStart; + + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date orderDateEnd; + + @DateTimeFormat(pattern = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private Date importDateStart; + + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date importDateEnd; + + private BigDecimal qtyStart; + + private BigDecimal qtyEnd; + + private BigDecimal localTotalAmountStart; + + private BigDecimal localTotalAmountEnd; + + private Integer page = 1; + + private Integer limit = 20; +} diff --git a/src/main/java/com/xujie/sys/modules/pms/entity/PartSparePurchaseImportEntity.java b/src/main/java/com/xujie/sys/modules/pms/entity/PartSparePurchaseImportEntity.java new file mode 100644 index 00000000..44742192 --- /dev/null +++ b/src/main/java/com/xujie/sys/modules/pms/entity/PartSparePurchaseImportEntity.java @@ -0,0 +1,109 @@ +package com.xujie.sys.modules.pms.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 备件采购订单导入记录实体 + */ +@Data +@TableName("part_spare_purchase_import") +public class PartSparePurchaseImportEntity { + + @TableId(type = IdType.ASSIGN_ID) + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + private String site; + + @TableField("bu_no") + private String buNo; + + @TableField("order_date") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private Date orderDate; + + @TableField("supplier_name") + private String supplierName; + + @TableField("currency_type") + private String currencyType; + + @TableField("order_no") + private String orderNo; + + @TableField("item_code") + private String itemCode; + + @TableField("item_name") + private String itemName; + + private BigDecimal qty; + + @TableField("local_unit_price") + private BigDecimal localUnitPrice; + + @TableField("local_amount") + private BigDecimal localAmount; + + @TableField("local_tax_amount") + private BigDecimal localTaxAmount; + + @TableField("local_total_amount") + private BigDecimal localTotalAmount; + + @TableField("unit_name") + private String unitName; + + @TableField("project_bu") + private String projectBu; + + @TableField("request_no") + private String requestNo; + + @TableField("request_by") + private String requestBy; + + @TableField("import_date") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date importDate; + + @TableField("import_by") + private String importBy; + + @TableField("source_file_name") + private String sourceFileName; + + private String remark; + + @TableField("created_by") + private String createdBy; + + @TableField("created_date") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date createdDate; + + @TableField("updated_by") + private String updatedBy; + + @TableField("updated_date") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date updatedDate; + + @TableField("is_deleted") + private String isDeleted; +} diff --git a/src/main/java/com/xujie/sys/modules/pms/mapper/PartSparePurchaseImportMapper.java b/src/main/java/com/xujie/sys/modules/pms/mapper/PartSparePurchaseImportMapper.java new file mode 100644 index 00000000..61a7accd --- /dev/null +++ b/src/main/java/com/xujie/sys/modules/pms/mapper/PartSparePurchaseImportMapper.java @@ -0,0 +1,22 @@ +package com.xujie.sys.modules.pms.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xujie.sys.modules.pms.data.PartSparePurchaseImportQueryData; +import com.xujie.sys.modules.pms.entity.PartSparePurchaseImportEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Map; + +@Mapper +public interface PartSparePurchaseImportMapper extends BaseMapper { + + IPage queryPage( + Page page, + @Param("query") PartSparePurchaseImportQueryData query + ); + + Map querySummary(@Param("query") PartSparePurchaseImportQueryData query); +} diff --git a/src/main/java/com/xujie/sys/modules/pms/service/Impl/PartSparePurchaseImportServiceImpl.java b/src/main/java/com/xujie/sys/modules/pms/service/Impl/PartSparePurchaseImportServiceImpl.java new file mode 100644 index 00000000..ffaaef5c --- /dev/null +++ b/src/main/java/com/xujie/sys/modules/pms/service/Impl/PartSparePurchaseImportServiceImpl.java @@ -0,0 +1,497 @@ +package com.xujie.sys.modules.pms.service.Impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.xujie.sys.common.utils.PageUtils; +import com.xujie.sys.modules.pms.data.PartSparePurchaseImportQueryData; +import com.xujie.sys.modules.pms.entity.PartSparePurchaseImportEntity; +import com.xujie.sys.modules.pms.mapper.PartSparePurchaseImportMapper; +import com.xujie.sys.modules.pms.service.PartSparePurchaseImportService; +import com.xujie.sys.modules.sys.entity.SysUserEntity; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.apache.shiro.SecurityUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.net.URLEncoder; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; +import java.time.temporal.TemporalAccessor; +import java.util.*; + +@Slf4j +@Service +public class PartSparePurchaseImportServiceImpl extends ServiceImpl + implements PartSparePurchaseImportService { + + private static final int IMPORT_BATCH_SIZE = 100; + + @Override + public PageUtils queryPage(PartSparePurchaseImportQueryData queryData) { + queryData.setUsername(getCurrentUsername()); + queryData.setBuNo(normalizeBuNo(queryData.getSite(), queryData.getBuNo())); + long pageNo = queryData.getPage() == null || queryData.getPage() < 1 ? 1 : queryData.getPage(); + long pageSize = queryData.getLimit() == null || queryData.getLimit() < 1 ? 20 : queryData.getLimit(); + IPage page = baseMapper.queryPage( + new Page<>(pageNo, pageSize), + queryData + ); + return new PageUtils(page); + } + + @Override + public Map querySummary(PartSparePurchaseImportQueryData queryData) { + queryData.setUsername(getCurrentUsername()); + queryData.setBuNo(normalizeBuNo(queryData.getSite(), queryData.getBuNo())); + Map summary = baseMapper.querySummary(queryData); + Map result = new HashMap<>(2); + result.put("totalQty", toBigDecimal(summary == null ? null : summary.get("totalQty"))); + result.put("totalLocalTotalAmount", toBigDecimal(summary == null ? null : summary.get("totalLocalTotalAmount"))); + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveData(PartSparePurchaseImportEntity data) { + data.setBuNo(normalizeBuNo(data.getSite(), data.getBuNo())); + validateBaseData(data); + String username = getCurrentUsername(); + Date now = new Date(); + data.setId(null); + data.setImportDate(now); + data.setImportBy(username); + data.setCreatedBy(username); + data.setCreatedDate(now); + data.setUpdatedBy(username); + data.setUpdatedDate(now); + data.setIsDeleted("0"); + this.save(data); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateData(PartSparePurchaseImportEntity data) { + if (data.getId() == null) { + throw new RuntimeException("参数错误:记录ID不能为空"); + } + data.setBuNo(normalizeBuNo(data.getSite(), data.getBuNo())); + validateBaseData(data); + PartSparePurchaseImportEntity dbData = this.getOne( + new LambdaQueryWrapper() + .eq(PartSparePurchaseImportEntity::getId, data.getId()) + .eq(PartSparePurchaseImportEntity::getIsDeleted, "0") + ); + if (dbData == null) { + throw new RuntimeException("记录不存在或已删除,请刷新后重试"); + } + + dbData.setSite(data.getSite()); + dbData.setBuNo(data.getBuNo()); + dbData.setOrderDate(data.getOrderDate()); + dbData.setSupplierName(data.getSupplierName()); + dbData.setCurrencyType(data.getCurrencyType()); + dbData.setOrderNo(data.getOrderNo()); + dbData.setItemCode(data.getItemCode()); + dbData.setItemName(data.getItemName()); + dbData.setQty(data.getQty()); + dbData.setLocalUnitPrice(data.getLocalUnitPrice()); + dbData.setLocalAmount(data.getLocalAmount()); + dbData.setLocalTaxAmount(data.getLocalTaxAmount()); + dbData.setLocalTotalAmount(data.getLocalTotalAmount()); + dbData.setUnitName(data.getUnitName()); + dbData.setProjectBu(data.getProjectBu()); + dbData.setRequestNo(data.getRequestNo()); + dbData.setRequestBy(data.getRequestBy()); + dbData.setRemark(data.getRemark()); + dbData.setUpdatedBy(getCurrentUsername()); + dbData.setUpdatedDate(new Date()); + this.updateById(dbData); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteData(Long[] ids) { + if (ids == null || ids.length == 0) { + throw new RuntimeException("请先选择需要删除的数据"); + } + List idList = Arrays.asList(ids); + long existsCount = this.lambdaQuery() + .in(PartSparePurchaseImportEntity::getId, idList) + .count(); + if (existsCount == 0) { + throw new RuntimeException("记录不存在或已删除,请刷新后重试"); + } + boolean success = this.removeByIds(idList); + if (!success) { + throw new RuntimeException("删除失败,请稍后重试"); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int importExcel(MultipartFile file, String site, String buNo) { + if (!StringUtils.hasText(site)) { + throw new RuntimeException("工厂不能为空"); + } + String normalizedBuNo = normalizeBuNo(site, buNo); + if (!StringUtils.hasText(normalizedBuNo)) { + throw new RuntimeException("BU不能为空"); + } + if (file == null || file.isEmpty()) { + throw new RuntimeException("上传文件不能为空"); + } + String fileName = file.getOriginalFilename(); + if (!StringUtils.hasText(fileName)) { + throw new RuntimeException("上传文件名不能为空"); + } + String lowerName = fileName.toLowerCase(); + if (!lowerName.endsWith(".xlsx") && !lowerName.endsWith(".xls")) { + throw new RuntimeException("请上传Excel文件(.xlsx 或 .xls)"); + } + + String username = getCurrentUsername(); + Date importTime = new Date(); + List rows = parseExcel(file, site, normalizedBuNo, fileName, username, importTime); + if (rows.isEmpty()) { + throw new RuntimeException("未读取到有效导入数据"); + } + this.saveBatch(rows, IMPORT_BATCH_SIZE); + return rows.size(); + } + + @Override + public void downloadTemplate(HttpServletResponse response) { + try (Workbook workbook = new XSSFWorkbook()) { + Sheet sheet = workbook.createSheet("采购订单导入"); + Row header = sheet.createRow(0); + String[] headers = { + "日期", "供应商", "币种", "订单编号", "存货编码", "存货名称", "数量", + "本币单价", "本币金额", "本币税额", "本币价税合计", "单位", + "项目目录(BU)", "请单编号", "请购员" + }; + for (int i = 0; i < headers.length; i++) { + Cell cell = header.createCell(i); + cell.setCellValue(headers[i]); + sheet.setColumnWidth(i, 18 * 256); + } + + Row sample = sheet.createRow(1); + sample.createCell(0).setCellValue("2026-04-02"); + sample.createCell(1).setCellValue("南海发展工程有限公司"); + sample.createCell(2).setCellValue("人民币"); + sample.createCell(3).setCellValue("CPMFL2026040019"); + sample.createCell(4).setCellValue("401.000000"); + sample.createCell(5).setCellValue("叉车手柄切片"); + sample.createCell(6).setCellValue("3"); + sample.createCell(7).setCellValue("1"); + sample.createCell(8).setCellValue("3"); + sample.createCell(9).setCellValue("0.39"); + sample.createCell(10).setCellValue("3.39"); + sample.createCell(11).setCellValue("起"); + sample.createCell(12).setCellValue("39"); + sample.createCell(13).setCellValue("CPMFL2026040019"); + sample.createCell(14).setCellValue("段晓浩"); + + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("UTF-8"); + response.setHeader( + "Content-Disposition", + "attachment;filename=" + URLEncoder.encode("采购订单导入模板.xlsx", "UTF-8") + ); + workbook.write(response.getOutputStream()); + response.flushBuffer(); + } catch (Exception e) { + log.error("下载采购订单导入模板失败: {}", e.getMessage(), e); + throw new RuntimeException("下载模板失败: " + e.getMessage()); + } + } + + private List parseExcel( + MultipartFile file, + String site, + String buNo, + String sourceFileName, + String username, + Date importTime + ) { + List result = new ArrayList<>(); + DataFormatter formatter = new DataFormatter(); + try (Workbook workbook = WorkbookFactory.create(file.getInputStream())) { + Sheet sheet = workbook.getSheetAt(0); + if (sheet == null) { + return result; + } + Row headerRow = sheet.getRow(0); + Map headerMap = buildHeaderMap(headerRow, formatter); + HeaderIndex index = resolveHeaderIndex(headerMap); + + for (int i = 1; i <= sheet.getLastRowNum(); i++) { + Row row = sheet.getRow(i); + if (row == null || isEmptyRow(row, formatter)) { + continue; + } + PartSparePurchaseImportEntity entity = new PartSparePurchaseImportEntity(); + entity.setSite(site); + entity.setBuNo(buNo); + entity.setOrderDate(parseDateCell(row.getCell(index.orderDate), formatter)); + entity.setSupplierName(getCellString(row.getCell(index.supplierName), formatter)); + entity.setCurrencyType(getCellString(row.getCell(index.currencyType), formatter)); + entity.setOrderNo(getCellString(row.getCell(index.orderNo), formatter)); + entity.setItemCode(getCellString(row.getCell(index.itemCode), formatter)); + entity.setItemName(getCellString(row.getCell(index.itemName), formatter)); + entity.setQty(parseDecimal(row.getCell(index.qty), formatter)); + entity.setLocalUnitPrice(parseDecimal(row.getCell(index.localUnitPrice), formatter)); + entity.setLocalAmount(parseDecimal(row.getCell(index.localAmount), formatter)); + entity.setLocalTaxAmount(parseDecimal(row.getCell(index.localTaxAmount), formatter)); + entity.setLocalTotalAmount(parseDecimal(row.getCell(index.localTotalAmount), formatter)); + entity.setUnitName(getCellString(row.getCell(index.unitName), formatter)); + entity.setProjectBu(getCellString(row.getCell(index.projectBu), formatter)); + entity.setRequestNo(getCellString(row.getCell(index.requestNo), formatter)); + entity.setRequestBy(getCellString(row.getCell(index.requestBy), formatter)); + entity.setImportDate(importTime); + entity.setImportBy(username); + entity.setSourceFileName(sourceFileName); + entity.setCreatedBy(username); + entity.setCreatedDate(importTime); + entity.setUpdatedBy(username); + entity.setUpdatedDate(importTime); + entity.setIsDeleted("0"); + + // 至少有订单编号或存货编码才作为有效行 + if (!StringUtils.hasText(entity.getOrderNo()) && !StringUtils.hasText(entity.getItemCode())) { + continue; + } + validateBaseData(entity); + result.add(entity); + } + } catch (IOException e) { + throw new RuntimeException("解析Excel失败: " + e.getMessage()); + } catch (Exception e) { + throw new RuntimeException("导入失败: " + e.getMessage()); + } + return result; + } + + private Map buildHeaderMap(Row headerRow, DataFormatter formatter) { + Map headerMap = new HashMap<>(); + if (headerRow == null) { + return headerMap; + } + for (int i = 0; i < headerRow.getLastCellNum(); i++) { + String value = normalizeHeader(getCellString(headerRow.getCell(i), formatter)); + if (StringUtils.hasText(value)) { + headerMap.put(value, i); + } + } + return headerMap; + } + + private HeaderIndex resolveHeaderIndex(Map headerMap) { + HeaderIndex index = new HeaderIndex(); + index.orderDate = resolveColumn(headerMap, 1, "日期", "订单日期"); + index.supplierName = resolveColumn(headerMap, 2, "供应商", "供应商名称"); + index.currencyType = resolveColumn(headerMap, 3, "币种"); + index.orderNo = resolveColumn(headerMap, 4, "订单编号", "采购订单号"); + index.itemCode = resolveColumn(headerMap, 5, "存货编码", "物料编码"); + index.itemName = resolveColumn(headerMap, 6, "存货名称", "物料名称"); + index.qty = resolveColumn(headerMap, 7, "数量", "订单数量"); + index.localUnitPrice = resolveColumn(headerMap, 8, "本币单价"); + index.localAmount = resolveColumn(headerMap, 9, "本币金额"); + index.localTaxAmount = resolveColumn(headerMap, 10, "本币税额"); + index.localTotalAmount = resolveColumn(headerMap, 11, "本币价税合计"); + index.unitName = resolveColumn(headerMap, 12, "单位"); + index.projectBu = resolveColumn(headerMap, 13, "项目目录(bu)", "项目目录(bu)", "项目目录bu", "项目目录"); + index.requestNo = resolveColumn(headerMap, 14, "请单编号", "请购单号", "请购编号"); + index.requestBy = resolveColumn(headerMap, 15, "请购员", "请购人", "采购员"); + return index; + } + + private Integer resolveColumn(Map headerMap, int fallbackIndex, String... aliases) { + for (String alias : aliases) { + Integer index = headerMap.get(normalizeHeader(alias)); + if (index != null) { + return index; + } + } + return fallbackIndex; + } + + private String normalizeHeader(String header) { + if (!StringUtils.hasText(header)) { + return ""; + } + return header.trim() + .replace("(", "(") + .replace(")", ")") + .replace(" ", "") + .toLowerCase(); + } + + private boolean isEmptyRow(Row row, DataFormatter formatter) { + int cells = row.getLastCellNum(); + for (int i = 0; i < cells; i++) { + if (StringUtils.hasText(getCellString(row.getCell(i), formatter))) { + return false; + } + } + return true; + } + + private String getCellString(Cell cell, DataFormatter formatter) { + if (cell == null) { + return ""; + } + return formatter.formatCellValue(cell).trim(); + } + + private BigDecimal parseDecimal(Cell cell, DataFormatter formatter) { + String value = getCellString(cell, formatter); + if (!StringUtils.hasText(value)) { + return null; + } + String normalized = value.replace(",", "").replace(" ", ""); + if (!StringUtils.hasText(normalized)) { + return null; + } + try { + return new BigDecimal(normalized); + } catch (Exception e) { + throw new RuntimeException("数值格式错误: " + value); + } + } + + private Date parseDateCell(Cell cell, DataFormatter formatter) { + if (cell == null) { + return null; + } + if (cell.getCellType() == CellType.NUMERIC && DateUtil.isCellDateFormatted(cell)) { + return cell.getDateCellValue(); + } + String value = getCellString(cell, formatter); + if (!StringUtils.hasText(value)) { + return null; + } + return parseDateString(value); + } + + private Date parseDateString(String value) { + List formatters = Arrays.asList( + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"), + DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"), + DateTimeFormatter.ofPattern("yyyy-MM-dd"), + DateTimeFormatter.ofPattern("yyyy/MM/dd"), + DateTimeFormatter.ofPattern("yyyyMMdd"), + new DateTimeFormatterBuilder() + .appendOptional(DateTimeFormatter.ofPattern("yyyy-M-d")) + .appendOptional(DateTimeFormatter.ofPattern("yyyy/M/d")) + .toFormatter() + ); + for (DateTimeFormatter formatter : formatters) { + try { + TemporalAccessor accessor = formatter.parseBest(value, LocalDateTime::from, LocalDate::from); + if (accessor instanceof LocalDateTime) { + return Date.from(((LocalDateTime) accessor).atZone(ZoneId.systemDefault()).toInstant()); + } + if (accessor instanceof LocalDate) { + return Date.from(((LocalDate) accessor).atStartOfDay(ZoneId.systemDefault()).toInstant()); + } + } catch (DateTimeParseException ignored) { + } + } + throw new RuntimeException("日期格式错误: " + value); + } + + private BigDecimal toBigDecimal(Object value) { + if (value == null) { + return BigDecimal.ZERO; + } + if (value instanceof BigDecimal) { + return (BigDecimal) value; + } + if (value instanceof Number) { + return new BigDecimal(value.toString()); + } + String text = value.toString(); + if (!StringUtils.hasText(text)) { + return BigDecimal.ZERO; + } + try { + return new BigDecimal(text.trim()); + } catch (Exception e) { + return BigDecimal.ZERO; + } + } + + private String normalizeBuNo(String site, String buNo) { + if (!StringUtils.hasText(buNo)) { + return buNo; + } + String siteText = StringUtils.hasText(site) ? site.trim() : ""; + String buText = buNo.trim(); + if (!StringUtils.hasText(siteText)) { + return buText; + } + String prefix = siteText + "_"; + if (buText.startsWith(prefix)) { + return buText.substring(prefix.length()); + } + return buText; + } + + private void validateBaseData(PartSparePurchaseImportEntity data) { + if (!StringUtils.hasText(data.getSite())) { + throw new RuntimeException("工厂不能为空"); + } + if (!StringUtils.hasText(data.getBuNo())) { + throw new RuntimeException("BU不能为空"); + } + if (!StringUtils.hasText(data.getOrderNo())) { + throw new RuntimeException("订单编号不能为空"); + } + if (!StringUtils.hasText(data.getItemCode())) { + throw new RuntimeException("存货编码不能为空"); + } + if (data.getQty() != null && data.getQty().compareTo(BigDecimal.ZERO) < 0) { + throw new RuntimeException("数量不能小于0"); + } + } + + private String getCurrentUsername() { + Object principal = SecurityUtils.getSubject().getPrincipal(); + if (principal instanceof SysUserEntity) { + return ((SysUserEntity) principal).getUsername(); + } + return "system"; + } + + private static class HeaderIndex { + private Integer orderDate; + private Integer supplierName; + private Integer currencyType; + private Integer orderNo; + private Integer itemCode; + private Integer itemName; + private Integer qty; + private Integer localUnitPrice; + private Integer localAmount; + private Integer localTaxAmount; + private Integer localTotalAmount; + private Integer unitName; + private Integer projectBu; + private Integer requestNo; + private Integer requestBy; + } +} diff --git a/src/main/java/com/xujie/sys/modules/pms/service/PartSparePurchaseImportService.java b/src/main/java/com/xujie/sys/modules/pms/service/PartSparePurchaseImportService.java new file mode 100644 index 00000000..e6ab9ef4 --- /dev/null +++ b/src/main/java/com/xujie/sys/modules/pms/service/PartSparePurchaseImportService.java @@ -0,0 +1,28 @@ +package com.xujie.sys.modules.pms.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.xujie.sys.common.utils.PageUtils; +import com.xujie.sys.modules.pms.data.PartSparePurchaseImportQueryData; +import com.xujie.sys.modules.pms.entity.PartSparePurchaseImportEntity; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.servlet.http.HttpServletResponse; +import java.math.BigDecimal; +import java.util.Map; + +public interface PartSparePurchaseImportService extends IService { + + PageUtils queryPage(PartSparePurchaseImportQueryData queryData); + + Map querySummary(PartSparePurchaseImportQueryData queryData); + + void saveData(PartSparePurchaseImportEntity data); + + void updateData(PartSparePurchaseImportEntity data); + + void deleteData(Long[] ids); + + int importExcel(MultipartFile file, String site, String buNo); + + void downloadTemplate(HttpServletResponse response); +} diff --git a/src/main/resources/mapper/pms/PartSparePurchaseImportMapper.xml b/src/main/resources/mapper/pms/PartSparePurchaseImportMapper.xml new file mode 100644 index 00000000..d5fc1210 --- /dev/null +++ b/src/main/resources/mapper/pms/PartSparePurchaseImportMapper.xml @@ -0,0 +1,115 @@ + + + + + + + a.is_deleted = '0' + and a.site in (select site from eam_access_site where username = #{query.username}) + and (a.site + '-' + a.bu_no) in (select * from dbo.query_bu(#{query.username})) + + and a.site = #{query.site} + + + and a.bu_no = #{query.buNo} + + + and a.supplier_name like '%' + #{query.supplierName} + '%' + + + and a.currency_type = #{query.currencyType} + + + and a.order_no like '%' + #{query.orderNo} + '%' + + + and a.item_code like '%' + #{query.itemCode} + '%' + + + and a.item_name like '%' + #{query.itemName} + '%' + + + and a.project_bu like '%' + #{query.projectBu} + '%' + + + and a.request_no like '%' + #{query.requestNo} + '%' + + + and a.request_by like '%' + #{query.requestBy} + '%' + + + and a.import_by like '%' + #{query.importBy} + '%' + + + and a.source_file_name like '%' + #{query.sourceFileName} + '%' + + + and a.order_date >= #{query.orderDateStart} + + + and a.order_date <= #{query.orderDateEnd} + + + and a.import_date >= #{query.importDateStart} + + + and a.import_date <= #{query.importDateEnd} + + + and a.qty >= #{query.qtyStart} + + + and a.qty <= #{query.qtyEnd} + + + and a.local_total_amount >= #{query.localTotalAmountStart} + + + and a.local_total_amount <= #{query.localTotalAmountEnd} + + + + + + + + +