diff --git a/src/main/java/com/xujie/modules/npcIqc/controller/NpcIqcController.java b/src/main/java/com/xujie/modules/npcIqc/controller/NpcIqcController.java index d1237ac..ed839ec 100644 --- a/src/main/java/com/xujie/modules/npcIqc/controller/NpcIqcController.java +++ b/src/main/java/com/xujie/modules/npcIqc/controller/NpcIqcController.java @@ -2,13 +2,24 @@ package com.xujie.modules.npcIqc.controller; import com.xujie.common.utils.PageUtils; import com.xujie.common.utils.R; +import com.xujie.modules.npcIqc.data.NpcIqcExcelDTO; import com.xujie.modules.npcIqc.entity.NpcIqc; import com.xujie.modules.npcIqc.service.NpcIqcService; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.core.io.ByteArrayResource; +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.ByteArrayOutputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.List; @RestController @RequestMapping("npcIqc") @@ -26,4 +37,126 @@ public class NpcIqcController { return R.ok().put("page", page); } + /** + * 下载NPC IQC导入模板 + */ + @PostMapping("/downloadTemplate") + public ResponseEntity downloadTemplate() { + try { + // 直接在内存中生成 Excel 模板 + Workbook workbook = new XSSFWorkbook(); + Sheet sheet = workbook.createSheet("NPC IQC数据"); + + // 创建表头样式 + CellStyle headerStyle = workbook.createCellStyle(); + Font headerFont = workbook.createFont(); + headerFont.setBold(true); + headerStyle.setFont(headerFont); + headerStyle.setAlignment(HorizontalAlignment.CENTER); + headerStyle.setVerticalAlignment(VerticalAlignment.CENTER); + // 设置边框 + headerStyle.setBorderTop(BorderStyle.THIN); + headerStyle.setBorderBottom(BorderStyle.THIN); + headerStyle.setBorderLeft(BorderStyle.THIN); + headerStyle.setBorderRight(BorderStyle.THIN); + // 设置背景色 + headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + + // 创建表头行 + Row headerRow = sheet.createRow(0); + String[] headers = { + "Inspection Type", "PUR ORDER", "Supplier", "Date Received", "Item", + "Quantity", "Sample Size", "Dim A", "Dim B", "Dim C", "Dim D", "Dim E", + "Total", "Status", "PROBLEM", "Comments", "INSPECTOR NAME", + "SUPPLIER EMAIL ADDRESS", "CA Request", "Rework Hours", "Rework Rate", + "Rework Total", "Material Cost", "Created By", "Collection", "Entry Date" + }; + for (int i = 0; i < headers.length; i++) { + Cell cell = headerRow.createCell(i); + cell.setCellValue(headers[i]); + cell.setCellStyle(headerStyle); + } + + // 添加示例数据行 + Row dataRow1 = sheet.createRow(1); + dataRow1.createCell(0).setCellValue("STANDARD"); // Inspection Type + dataRow1.createCell(1).setCellValue("C6304"); // PUR ORDER + dataRow1.createCell(2).setCellValue("REBEC INDUSTRY (VIET NAM) CO., LTD"); // Supplier + dataRow1.createCell(3).setCellValue("21-Apr-26"); // Date Received + dataRow1.createCell(4).setCellValue("NDB-RBLU-PAD-FSC"); // Item + dataRow1.createCell(5).setCellValue(7600); // Quantity + dataRow1.createCell(6).setCellValue(200); // Sample Size + dataRow1.createCell(7).setCellValue(0); // Dim A + dataRow1.createCell(8).setCellValue(0); // Dim B + dataRow1.createCell(9).setCellValue(200); // Dim C + dataRow1.createCell(10).setCellValue(0); // Dim D + dataRow1.createCell(11).setCellValue(0); // Dim E + dataRow1.createCell(12).setCellValue("200"); // Total + dataRow1.createCell(13).setCellValue("Reject"); // Status + dataRow1.createCell(14).setCellValue("COLOUR COMES OFF ONTO YOUR HANDS"); // PROBLEM + dataRow1.createCell(15).setCellValue("RETURN SHIPMENT"); // Comments + dataRow1.createCell(16).setCellValue("MCHURCHILL"); // INSPECTOR NAME + dataRow1.createCell(17).setCellValue(""); // SUPPLIER EMAIL ADDRESS + dataRow1.createCell(18).setCellValue(""); // CA Request + dataRow1.createCell(19).setCellValue(0); // Rework Hours + dataRow1.createCell(20).setCellValue(0.79642); // Rework Rate + dataRow1.createCell(21).setCellValue(0); // Rework Total + dataRow1.createCell(22).setCellValue(0); // Material Cost + dataRow1.createCell(23).setCellValue("MCHURCHILL"); // Created By + dataRow1.createCell(24).setCellValue("1521165"); // Collection + dataRow1.createCell(25).setCellValue("2026/4/30 9:58"); // Entry Date + + // 设置列宽 + for (int i = 0; i < headers.length; i++) { + sheet.setColumnWidth(i, 18 * 256); + } + + // 写入到字节数组 + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + workbook.write(outputStream); + workbook.close(); + + byte[] bytes = outputStream.toByteArray(); + ByteArrayResource resource = new ByteArrayResource(bytes); + + String fileName = "NPC_IQC导入模板.xlsx"; + String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()) + .replaceAll("\\+", "%20"); + + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename*=UTF-8''" + encodedFileName) + .header(HttpHeaders.CONTENT_LENGTH, String.valueOf(bytes.length)) + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(resource); + + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.internalServerError().build(); + } + } + + /** + * 预览上传的 Excel 数据 + */ + @PostMapping("/previewUpload") + public R previewUpload(@RequestParam("file") MultipartFile file) throws Exception { + List list = npcIqcService.previewUpload(file); + return R.ok().put("data", list); + } + + /** + * 批量保存上传的数据 + */ + @PostMapping("/batchSave") + public R batchSave(@RequestParam("file") MultipartFile file) { + try { + npcIqcService.batchSave(file); + return R.ok(); + } catch (Exception e) { + return R.error(e.getMessage()); + } + } + } diff --git a/src/main/java/com/xujie/modules/npcIqc/data/NpcIqcExcelDTO.java b/src/main/java/com/xujie/modules/npcIqc/data/NpcIqcExcelDTO.java new file mode 100644 index 0000000..abec19b --- /dev/null +++ b/src/main/java/com/xujie/modules/npcIqc/data/NpcIqcExcelDTO.java @@ -0,0 +1,93 @@ +package com.xujie.modules.npcIqc.data; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * NPC IQC Excel导入DTO + */ +@Data +public class NpcIqcExcelDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @ExcelProperty(value = "Inspection Type", index = 0) + private String inspectionType; + + @ExcelProperty(value = "PUR ORDER", index = 1) + private String purOrder; + + @ExcelProperty(value = "Supplier", index = 2) + private String supplier; + + @ExcelProperty(value = "Date Received", index = 3) + private String dateReceived; + + @ExcelProperty(value = "Item", index = 4) + private String item; + + @ExcelProperty(value = "Quantity", index = 5) + private Integer quantity; + + @ExcelProperty(value = "Sample Size", index = 6) + private Integer sampleSize; + + @ExcelProperty(value = "Dim A", index = 7) + private BigDecimal dimA; + + @ExcelProperty(value = "Dim B", index = 8) + private BigDecimal dimB; + + @ExcelProperty(value = "Dim C", index = 9) + private BigDecimal dimC; + + @ExcelProperty(value = "Dim D", index = 10) + private BigDecimal dimD; + + @ExcelProperty(value = "Dim E", index = 11) + private BigDecimal dimE; + + @ExcelProperty(value = "Total", index = 12) + private String total; + + @ExcelProperty(value = "Status", index = 13) + private String status; + + @ExcelProperty(value = "PROBLEM", index = 14) + private String problem; + + @ExcelProperty(value = "Comments", index = 15) + private String comments; + + @ExcelProperty(value = "INSPECTOR NAME", index = 16) + private String inspectorName; + + @ExcelProperty(value = "SUPPLIER EMAIL ADDRESS", index = 17) + private String supplierEmail; + + @ExcelProperty(value = "CA Request", index = 18) + private String caRequest; + + @ExcelProperty(value = "Rework Hours", index = 19) + private BigDecimal reworkHours; + + @ExcelProperty(value = "Rework Rate", index = 20) + private BigDecimal reworkRate; + + @ExcelProperty(value = "Rework Total", index = 21) + private BigDecimal reworkTotal; + + @ExcelProperty(value = "Material Cost", index = 22) + private BigDecimal materialCost; + + @ExcelProperty(value = "Created By", index = 23) + private String createdBy; + + @ExcelProperty(value = "Collection", index = 24) + private String collection; + + @ExcelProperty(value = "Entry Date", index = 25) + private String entryDate; +} diff --git a/src/main/java/com/xujie/modules/npcIqc/entity/NpcIqc.java b/src/main/java/com/xujie/modules/npcIqc/entity/NpcIqc.java index ec61a4d..ad7afe0 100644 --- a/src/main/java/com/xujie/modules/npcIqc/entity/NpcIqc.java +++ b/src/main/java/com/xujie/modules/npcIqc/entity/NpcIqc.java @@ -1,5 +1,6 @@ package com.xujie.modules.npcIqc.entity; +import com.baomidou.mybatisplus.annotation.TableField; import com.xujie.common.utils.QueryPage; import lombok.Data; import com.fasterxml.jackson.annotation.JsonFormat; @@ -20,6 +21,7 @@ public class NpcIqc extends QueryPage { private String supplierNo; + @TableField(exist = false) private String supplierName; @DateTimeFormat(pattern = "yyyy-MM-dd") @@ -28,6 +30,7 @@ public class NpcIqc extends QueryPage { private String itemCode; + @TableField(exist = false) private String itemName; private Integer quantity; diff --git a/src/main/java/com/xujie/modules/npcIqc/service/NpcIqcService.java b/src/main/java/com/xujie/modules/npcIqc/service/NpcIqcService.java index f9ee7ed..5ccfd9a 100644 --- a/src/main/java/com/xujie/modules/npcIqc/service/NpcIqcService.java +++ b/src/main/java/com/xujie/modules/npcIqc/service/NpcIqcService.java @@ -2,8 +2,23 @@ package com.xujie.modules.npcIqc.service; import com.baomidou.mybatisplus.extension.service.IService; import com.xujie.common.utils.PageUtils; +import com.xujie.modules.npcIqc.data.NpcIqcExcelDTO; import com.xujie.modules.npcIqc.entity.NpcIqc; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.List; public interface NpcIqcService extends IService { PageUtils myPage(NpcIqc npciqc); + + /** + * 预览上传的 Excel 数据 + */ + List previewUpload(MultipartFile file) throws IOException; + + /** + * 批量保存上传的数据 + */ + void batchSave(MultipartFile file) throws Exception; } diff --git a/src/main/java/com/xujie/modules/npcIqc/service/impl/NpcIqcServiceImpl.java b/src/main/java/com/xujie/modules/npcIqc/service/impl/NpcIqcServiceImpl.java index 0b0c0d2..1521b73 100644 --- a/src/main/java/com/xujie/modules/npcIqc/service/impl/NpcIqcServiceImpl.java +++ b/src/main/java/com/xujie/modules/npcIqc/service/impl/NpcIqcServiceImpl.java @@ -1,19 +1,212 @@ package com.xujie.modules.npcIqc.service.impl; +import com.alibaba.excel.EasyExcel; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xujie.common.utils.PageUtils; +import com.xujie.modules.npcIqc.data.NpcIqcExcelDTO; import com.xujie.modules.npcIqc.entity.NpcIqc; import com.xujie.modules.npcIqc.mapper.NpcIqcMapper; import com.xujie.modules.npcIqc.service.NpcIqcService; +import com.xujie.modules.srm.mapper.SrmSupplierMapper; +import com.xujie.modules.sys.entity.SysUserEntity; +import org.apache.shiro.SecurityUtils; +import org.springframework.beans.factory.annotation.Autowired; 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.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; @Service public class NpcIqcServiceImpl extends ServiceImpl implements NpcIqcService { + + @Autowired + private SrmSupplierMapper srmSupplierMapper; + @Override public PageUtils myPage(NpcIqc npciqc) { Page page = new Page<>(npciqc.getPage(), npciqc.getLimit()); Page resultList = baseMapper.myPage(page, npciqc); return new PageUtils(resultList); } + + @Override + public List previewUpload(MultipartFile file) throws IOException { + // 使用 EasyExcel 读取 Excel 数据 + List list = EasyExcel.read(file.getInputStream()) + .head(NpcIqcExcelDTO.class) + .sheet() + .doReadSync(); + return list; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void batchSave(MultipartFile file) throws Exception { + // 获取当前用户信息 + SysUserEntity user = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); + String site = user.getSite(); + + // 定义多种日期格式 + SimpleDateFormat[] dateFormats = { + new SimpleDateFormat("dd-MMM-yy", java.util.Locale.ENGLISH), // 21-Apr-26 + new SimpleDateFormat("dd-M月-yy", java.util.Locale.CHINA), // 21-4月-26 (中文月份) + new SimpleDateFormat("yyyy-MM-dd"), // 2026-04-21 + new SimpleDateFormat("yyyy/MM/dd"), // 2026/04/21 + new SimpleDateFormat("MM/dd/yyyy"), // 04/21/2026 + new SimpleDateFormat("dd/MM/yyyy"), // 21/04/2026 + new SimpleDateFormat("yyyy年MM月dd日"), // 2026年04月21日 + new SimpleDateFormat("MMM dd, yyyy", java.util.Locale.ENGLISH), // Apr 21, 2026 + new SimpleDateFormat("dd-MMM-yyyy", java.util.Locale.ENGLISH), // 21-Apr-2026 + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"), // 2026-04-21 10:30:00 + new SimpleDateFormat("yyyy/MM/dd HH:mm") // 2026/04/21 10:30 + }; + + SimpleDateFormat entryDateFormat = new SimpleDateFormat("yyyy/M/d H:mm"); + + // 1. 读取 Excel 数据 + List excelList = EasyExcel.read(file.getInputStream()) + .head(NpcIqcExcelDTO.class) + .sheet() + .doReadSync(); + + if (excelList == null || excelList.isEmpty()) { + throw new RuntimeException("导入数据不能为空!"); + } + + // 2. 逐行校验并转换 + int rowNum = 1; // Excel 行号(从1开始,表头不计) + for (NpcIqcExcelDTO excelDTO : excelList) { + rowNum++; + + // 校验必填字段 + if (!StringUtils.hasText(excelDTO.getPurOrder())) { + throw new RuntimeException("第" + rowNum + "行:PUR ORDER不能为空"); + } + if (!StringUtils.hasText(excelDTO.getItem())) { + throw new RuntimeException("第" + rowNum + "行:Item不能为空"); + } + + // 生成 IQC 单号 + srmSupplierMapper.updateTransNo(site, "IQC"); + String iqcNo = srmSupplierMapper.getTransNo(site, "IQC"); + + // 创建 NpcIqc 实体 + NpcIqc npciqc = new NpcIqc(); + + // 设置主键字段 + npciqc.setSite(site); + npciqc.setIqcNo(iqcNo); + + // 基本字段映射 + npciqc.setInspectionType(excelDTO.getInspectionType()); + npciqc.setPurOrder(excelDTO.getPurOrder()); + + // 供应商信息:Excel中是完整名称,暂时保存到 supplierNo 字段 + // 如果后续需要关联查询,建议建立供应商映射表 + if (StringUtils.hasText(excelDTO.getSupplier())) { + String supplierFull = excelDTO.getSupplier().trim(); + npciqc.setSupplierNo(supplierFull); // 保存完整名称到 supplier_no + npciqc.setSupplierName(supplierFull); // 同时保存到非持久化字段用于显示 + } + + // 解析 Date Received (支持多种格式) + if (StringUtils.hasText(excelDTO.getDateReceived())) { + Date dateReceived = parseDate(excelDTO.getDateReceived().trim(), dateFormats, rowNum, "Date Received"); + npciqc.setDateReceived(dateReceived); + } + + // Item 作为产品编码 + if (StringUtils.hasText(excelDTO.getItem())) { + npciqc.setItemCode(excelDTO.getItem().trim()); + } + + npciqc.setQuantity(excelDTO.getQuantity()); + npciqc.setSampleSize(excelDTO.getSampleSize()); + npciqc.setDimA(excelDTO.getDimA()); + npciqc.setDimB(excelDTO.getDimB()); + npciqc.setDimC(excelDTO.getDimC()); + npciqc.setDimD(excelDTO.getDimD()); + npciqc.setDimE(excelDTO.getDimE()); + npciqc.setInspectionTotal(excelDTO.getTotal()); + npciqc.setInspectionStatus(excelDTO.getStatus()); + npciqc.setProblem(excelDTO.getProblem()); + npciqc.setComments(excelDTO.getComments()); + npciqc.setInspectorName(excelDTO.getInspectorName()); + npciqc.setSupplierEmail(excelDTO.getSupplierEmail()); + + // 解析 CA Request(可能是 Yes/No 或 true/false,空值默认为 false) + if (StringUtils.hasText(excelDTO.getCaRequest())) { + String caReq = excelDTO.getCaRequest().trim().toLowerCase(); + npciqc.setCaRequest("yes".equals(caReq) || "true".equals(caReq)); + } else { + npciqc.setCaRequest(false); + } + + npciqc.setReworkHours(excelDTO.getReworkHours()); + npciqc.setReworkRate(excelDTO.getReworkRate()); + npciqc.setReworkTotal(excelDTO.getReworkTotal()); + npciqc.setMaterialCost(excelDTO.getMaterialCost()); + + // 设置创建人(优先使用 Excel 中的值,否则使用当前用户) + if (StringUtils.hasText(excelDTO.getCreatedBy())) { + npciqc.setCreatedBy(excelDTO.getCreatedBy().trim()); + } else { + npciqc.setCreatedBy(user.getUsername()); + } + + // Collection 可能是数字或字符串 + if (StringUtils.hasText(excelDTO.getCollection())) { + npciqc.setCollectionBatch(excelDTO.getCollection().trim()); + } + + // 解析 Entry Date (优先使用带时间的格式,其次尝试其他格式) + if (StringUtils.hasText(excelDTO.getEntryDate())) { + String entryDateStr = excelDTO.getEntryDate().trim(); + Date entryDate = null; + + // 先尝试带时间的格式 + try { + entryDate = entryDateFormat.parse(entryDateStr); + } catch (Exception e) { + // 再尝试其他日期格式 + entryDate = parseDate(entryDateStr, dateFormats, rowNum, "Entry Date"); + } + + npciqc.setEntryDate(entryDate); + } else { + // 如果没有提供 Entry Date,使用当前时间 + npciqc.setEntryDate(new Date()); + } + + // 保存到数据库 + this.save(npciqc); + } + } + + /** + * 尝试使用多种日期格式解析日期字符串 + * @param dateStr 日期字符串 + * @param formats 日期格式数组 + * @param rowNum 行号(用于错误提示) + * @param fieldName 字段名称(用于错误提示) + * @return 解析后的日期 + */ + private Date parseDate(String dateStr, SimpleDateFormat[] formats, int rowNum, String fieldName) { + for (SimpleDateFormat format : formats) { + try { + return format.parse(dateStr); + } catch (Exception e) { + // 继续尝试下一个格式 + } + } + throw new RuntimeException("第" + rowNum + "行:" + fieldName + "日期格式错误:" + dateStr + + ",支持的格式包括:yyyy-MM-dd, dd-MMM-yy, yyyy/MM/dd, MM/dd/yyyy 等"); + } } diff --git a/src/main/resources/mapper/npcIqc/NpcIqcMapper.xml b/src/main/resources/mapper/npcIqc/NpcIqcMapper.xml index e026063..9aa4708 100644 --- a/src/main/resources/mapper/npcIqc/NpcIqcMapper.xml +++ b/src/main/resources/mapper/npcIqc/NpcIqcMapper.xml @@ -12,7 +12,7 @@ s.supplier_name, n.date_received, n.item_code, - p.item_name, + p.part_desc, n.quantity, n.sample_size, n.dim_a, @@ -40,7 +40,7 @@ FROM npc_iqc n LEFT JOIN - npc_supplier s ON n.supplier_no = s.supplier_no + srm_supplier s ON n.supplier_no = s.supplier_no LEFT JOIN part p ON n.item_code = p.part_no left join