diff --git a/bonus-admin/src/main/java/com/bonus/web/controller/file/FileUploadController.java b/bonus-admin/src/main/java/com/bonus/web/controller/file/FileUploadController.java index e048279..00e04ac 100644 --- a/bonus-admin/src/main/java/com/bonus/web/controller/file/FileUploadController.java +++ b/bonus-admin/src/main/java/com/bonus/web/controller/file/FileUploadController.java @@ -8,7 +8,7 @@ import com.bonus.common.domain.ocr.vo.OcrResponse; import com.bonus.file.config.MinioConfig; import com.bonus.file.service.FileUploadService; import com.bonus.file.service.SourceFileService; -import com.bonus.file.util.FileUtil; +import com.bonus.common.utils.FileUtil; import com.bonus.file.util.MinioUtil; import com.bonus.ocr.service.OcrService; import org.springframework.web.bind.annotation.*; diff --git a/bonus-admin/src/main/java/com/bonus/web/service/common/CommonUploadService.java b/bonus-admin/src/main/java/com/bonus/web/service/common/CommonUploadService.java index 3e6e853..3897b2d 100644 --- a/bonus-admin/src/main/java/com/bonus/web/service/common/CommonUploadService.java +++ b/bonus-admin/src/main/java/com/bonus/web/service/common/CommonUploadService.java @@ -8,7 +8,7 @@ import com.bonus.common.domain.ocr.vo.OcrResponse; import com.bonus.file.config.MinioConfig; import com.bonus.file.enums.UploadSuffixEnum; import com.bonus.file.service.FileUploadService; -import com.bonus.file.util.FileUtil; +import com.bonus.common.utils.FileUtil; import com.bonus.file.util.MinioUtil; import com.bonus.ocr.service.OcrService; import lombok.extern.slf4j.Slf4j; diff --git a/bonus-admin/src/main/java/com/bonus/web/util/ImportExcelJsonDataUtil.java b/bonus-admin/src/main/java/com/bonus/web/util/ImportExcelJsonDataUtil.java new file mode 100644 index 0000000..94fdee3 --- /dev/null +++ b/bonus-admin/src/main/java/com/bonus/web/util/ImportExcelJsonDataUtil.java @@ -0,0 +1,63 @@ +package com.bonus.web.util; + +import com.alibaba.fastjson2.JSONObject; +import org.apache.poi.ss.usermodel.Row; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * @className:ImportExcelJsonData + * @author:cwchen + * @date:2025-10-31-17:02 + * @version:1.0 + * @description: + */ +public class ImportExcelJsonDataUtil { + + /** + * 获取工器具数据 + * @param obj + * @return JSONObject + * @author cwchen + * @date 2025/10/31 17:04 + */ + public static JSONObject getToolObj(Row row, JSONObject obj, List> filename){ + obj.put("rowNo", row.getRowNum() + 1); + if (row.getCell(0) != null) { + // 工器具名称 + obj.put("toolName", row.getCell(0).getStringCellValue()); + } + if (row.getCell(1) != null) { + // 规格型号 + obj.put("model", row.getCell(1).getStringCellValue()); + } + if (row.getCell(1) != null) { + // 单位 + obj.put("unit", row.getCell(1).getStringCellValue()); + } + if (row.getCell(1) != null) { + // 技术参数 + obj.put("technicalParameters", row.getCell(1).getStringCellValue()); + } + if (row.getCell(1) != null) { + // 主要作用 + obj.put("mainFunction", row.getCell(1).getStringCellValue()); + } + if (row.getCell(1) != null) { + // 备注 + obj.put("remark", row.getCell(1).getStringCellValue()); + } + // 工器具图片 + String result = "TOOL" + "_" + row.getRowNum(); + for (Map map : filename) { + for (String key_name : map.keySet()) { + if (Objects.equals(result, key_name)) { + obj.put("files", map.get(key_name)); + } + } + } + return obj; + } +} diff --git a/bonus-admin/src/main/java/com/bonus/web/util/ImportExcelUtils.java b/bonus-admin/src/main/java/com/bonus/web/util/ImportExcelUtils.java new file mode 100644 index 0000000..06a0903 --- /dev/null +++ b/bonus-admin/src/main/java/com/bonus/web/util/ImportExcelUtils.java @@ -0,0 +1,287 @@ +package com.bonus.web.util; + +import com.alibaba.fastjson2.JSONObject; +import com.bonus.common.core.domain.AjaxResult; +import com.bonus.common.exception.ServiceException; +import com.bonus.common.utils.ExcelImageUtils; +import com.bonus.common.utils.FileUtil; +import com.bonus.web.service.common.CommonUploadService; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.hssf.usermodel.*; +import org.apache.poi.ooxml.POIXMLDocumentPart; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.*; +import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.bonus.common.constant.ImportBeanConstants; + +@Component("ImportExcelUtils") +public class ImportExcelUtils { + + @Resource(name = "CommonUploadService") + private CommonUploadService commonUploadService; + + private static final Logger log = LoggerFactory.getLogger(ImportExcelUtils.class); + + + public List readExcel(MultipartFile file, Class mClass, String uploadPath) throws Exception { + String fileName = file.getOriginalFilename(); + log.info("OriginalFilename:{}", fileName); + if (!StringUtils.endsWithAny(fileName, ".xls", ".xlsx")) { + throw new ServiceException("不支持excel以外的文件导入!"); + } + List list = new ArrayList<>(); + InputStream inputStream = file.getInputStream(); + String className = mClass.getSimpleName(); + log.info("className:{}", className); + //根据指定的文件输入流导入Excel从而产生Workbook对象 + Workbook workbook = null; + Sheet sheet = null; + Map mapData = null; + if (null != fileName && fileName.endsWith(".xls")) { + workbook = new HSSFWorkbook(inputStream); + //获取Excel文档中的第一个表单 + sheet = workbook.getSheetAt(0); + if (!checkModal(sheet, className)) { + throw new ServiceException("模板错误,请重新选择模板!"); + } + mapData = getPicturesXls((HSSFSheet) sheet, className); + } + if (null != fileName && fileName.endsWith(".xlsx")) { + workbook = new XSSFWorkbook(inputStream); + //获取Excel文档中的第一个表单 + sheet = workbook.getSheetAt(0); + if (!checkModal(sheet, className)) { + throw new ServiceException("模板错误,请重新选择模板!"); + } + mapData = getPicturesXlsx((XSSFSheet) sheet, className); + } + List> filenames = new ArrayList<>(); + if (ImportBeanConstants.TOOL_IMPORT_DTO.equals(className)) { // 工器具 + log.info("开始读取并写入图片"); + filenames = writeImg(mapData, className, uploadPath); + log.info("图片:{},写入完成!", filenames); + } + int sheetCount = 0; + if (workbook != null) { + workbook.getNumberOfSheets(); + } + log.info("Sheet(表单)数量:{}", sheetCount); + log.info("filenames:{}", filenames); + //获得最后一条记录得的行号,从0开始 + int totalRowNum = 0; + if (sheet != null) { + totalRowNum = sheet.getLastRowNum(); + } + log.info("总记录数:{}", (totalRowNum + 1)); + list = createBean(sheet, mClass, filenames); + inputStream.close(); + return list; + } + + private static boolean checkModal(Sheet sheet, String className) { + int colNum = sheet.getRow(0).getLastCellNum(); + if (ImportBeanConstants.TOOL_IMPORT_DTO.equals(className)) { + return colNum == ImportBeanConstants.TOOL_IMPORT_DTO_NUM; + } + return false; + } + + private static List createBean(Sheet sheet, Class mClass, List> filename) throws Exception { + if (sheet == null || sheet.getLastRowNum() < 0) { + return null; + } + List list = new ArrayList<>(); + int last = sheet.getRow(0).getLastCellNum();// 总列数 + log.info("列数:{}", last); + try { + for (Row row : sheet) { + if (row == null) { + continue; + } + // 第一行是标题栏 + if (ImportBeanConstants.TOOL_IMPORT_DTO.equals(mClass.getSimpleName())) { + if (row.getRowNum() < 1) { + continue; + } + } + boolean isBlankRow = true; + for (Cell c : row) { + if (c.getCellType() != CellType.BLANK) { + isBlankRow = false; + break; + } + } + if (isBlankRow) { + continue; + } + JSONObject obj = new JSONObject(); + if (ImportBeanConstants.TOOL_IMPORT_DTO.equals(mClass.getSimpleName())) { // 工器具 + setExcleTString(5, row); + obj = ImportExcelJsonDataUtil.getToolObj(row, obj, filename); + } + list.add(obj); + } + } catch (IllegalStateException e) { + e.printStackTrace(); + throw new ServiceException("模板中含有单元格数据格式不正确"); + } + return list; + } + + + /** + * 获取图片和位置 (xlsx) + * + * @param sheet + * @return + * @throws IOException + */ + public static Map getPicturesXlsx(XSSFSheet sheet, String className) throws IOException { + Map map = new HashMap<>(16); + List list = sheet.getRelations(); + for (POIXMLDocumentPart part : list) { + if (part instanceof XSSFDrawing) { + XSSFDrawing drawing = (XSSFDrawing) part; + List shapes = drawing.getShapes(); + + for (XSSFShape shape : shapes) { + XSSFPicture picture = (XSSFPicture) shape; + XSSFClientAnchor anchor = picture.getPreferredSize(); + CTMarker marker = anchor.getFrom(); + Row row = sheet.getRow(marker.getRow()); + if (row == null) { + continue; + } + String key_name = null; + if (ImportBeanConstants.TOOL_IMPORT_DTO.equals(className)) { + key_name = setKey(picture, row, ImportBeanConstants.TOOL_IMPORT_DTO); + } else { + key_name = ""; + } + map.put(key_name, picture.getPictureData()); + } + } + } + + + return map; + } + + /** + * 获取图片和位置 (xls) + * + * @param sheet + * @return + * @throws IOException + */ + public static Map getPicturesXls(HSSFSheet sheet, String className) throws IOException { + int i = 0; + Map map = new HashMap(16); + if (sheet.getDrawingPatriarch() != null) { + List list = sheet.getDrawingPatriarch().getChildren(); + for (HSSFShape shape : list) { + if (shape instanceof HSSFPicture) { + HSSFPicture picture = (HSSFPicture) shape; + int pictureIndex = ((HSSFPicture) shape).getPictureIndex(); + if (pictureIndex != -1) { + HSSFClientAnchor cAnchor = picture.getClientAnchor(); + Row row = sheet.getRow(cAnchor.getRow1()); + if (row == null) { + continue; + } + String key_name = null; + if (ImportBeanConstants.TOOL_IMPORT_DTO.equals(className)) { // 工器具 + if (cAnchor.getCol1() == 6) { + key_name = "TOOL" + "_" + cAnchor.getRow1(); + } + } + map.put(key_name, picture.getPictureData()); + } + } + } + } + return map; + } + + //图片写出 + public List> writeImg(Map mapData, String className, String uploadPath) throws IOException { + int pictureIndex = 0; + if (mapData == null || mapData.size() == 0) { + return new ArrayList<>(); + } + Object[] keyArr = mapData.keySet().toArray(); + ArrayList> filename = new ArrayList<>(); + for (int i = 0; i < mapData.size(); i++) { + Map map = new HashMap<>(16); + // 获取图片流 + PictureData pic = mapData.get(keyArr[i]); + // 获取图片索引 + String picName = keyArr[i].toString(); + byte[] data = pic.getData(); + String picPath = null; + // 获取文件后缀 + String fileExtension = ExcelImageUtils.getFileExtension(pic); + // 生成自定义文件名 + String fileName = String.format("sheet%d_picture%d.%s", + i, pictureIndex++, fileExtension); + String params = null; + if (ImportBeanConstants.TOOL_IMPORT_DTO.equals(className)) { // 工器具 + params = "{\"suffix\":\"tools_database\",\"fileUploadType\":\"tools\"}"; + } + MultipartFile multipartFile = FileUtil.byteArrayToMultipartFile(data, fileName); + AjaxResult ajaxResult = commonUploadService.uploadSmallFile(multipartFile, params); + JSONObject objData = (JSONObject) ajaxResult.get("data"); + JSONObject fileRes = objData.getJSONObject("fileRes"); + filename.add(fileRes); + } + return filename; + } + + + private static String setKey(XSSFPicture picture, Row row, String className) { + if (row == null) { + return null; + } + ; + String result = picture.getPreferredSize().toString(); + String pattern = "\\[1-9]\\d*\\<\\/xdr\\:col\\>"; + Pattern r = Pattern.compile(pattern); + String col = ""; + Matcher m = r.matcher(result); + if (m.find()) { + col = m.group(0); + } else { + System.out.println("NO MATCH"); + } + if (ImportBeanConstants.TOOL_IMPORT_DTO.equals(className)) { // 工器具 + switch (col) { + case "6": + return "TOOL" + "_" + row.getRowNum(); + } + } else { + return ""; + } + + return null; + } + + private static void setExcleTString(int j, Row row) { + for (int i = 0; i < j; i++) { + if (row.getCell(i) != null) { + row.getCell(i).setCellType(CellType.STRING); + } + } + } +} diff --git a/bonus-common/src/main/java/com/bonus/common/constant/ImportBeanConstants.java b/bonus-common/src/main/java/com/bonus/common/constant/ImportBeanConstants.java new file mode 100644 index 0000000..298f9c7 --- /dev/null +++ b/bonus-common/src/main/java/com/bonus/common/constant/ImportBeanConstants.java @@ -0,0 +1,16 @@ +package com.bonus.common.constant; + +/** + * @className:ImportBeanConstants + * @author:cwchen + * @date:2025-10-31-16:54 + * @version:1.0 + * @description:导入实体类常量 + */ +public class ImportBeanConstants { + + /**工器具导入实体类*/ + public static final String TOOL_IMPORT_DTO = "ToolImportDto"; + /**工器具导入实体类列数*/ + public static final int TOOL_IMPORT_DTO_NUM = 7; +} diff --git a/bonus-common/src/main/java/com/bonus/common/domain/mainDatabase/importdto/ToolImportDto.java b/bonus-common/src/main/java/com/bonus/common/domain/mainDatabase/importdto/ToolImportDto.java new file mode 100644 index 0000000..7c61dde --- /dev/null +++ b/bonus-common/src/main/java/com/bonus/common/domain/mainDatabase/importdto/ToolImportDto.java @@ -0,0 +1,61 @@ +package com.bonus.common.domain.mainDatabase.importdto; + +import com.bonus.common.core.domain.model.LoginUser; +import com.bonus.common.domain.file.po.ResourceFilePo; +import com.bonus.common.domain.mainDatabase.dto.TechnicalDto; +import com.bonus.common.domain.mainDatabase.dto.ToolDto; +import com.bonus.common.utils.SecurityUtils; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.List; +import java.util.Optional; + +/** + * @className:ToolImportDto + * @author:cwchen + * @date:2025-10-31-16:52 + * @version:1.0 + * @description:工器具导入-dto + */ +@Data +public class ToolImportDto { + + /** + * 工器具名称 + */ + private String toolName; + + /** + * 规格型号 + */ + private String model; + + /** + * 单位 + */ + private String unit; + + /** + * 技术参数 + */ + private String technicalParameters; + + /** + * 主要作用 + */ + private String mainFunction; + + /** + * 备注 + */ + private String remark; + + /**文件*/ + private List files; + +} diff --git a/bonus-common/src/main/java/com/bonus/common/utils/ByteArrayMultipartFile.java b/bonus-common/src/main/java/com/bonus/common/utils/ByteArrayMultipartFile.java new file mode 100644 index 0000000..5b29dc8 --- /dev/null +++ b/bonus-common/src/main/java/com/bonus/common/utils/ByteArrayMultipartFile.java @@ -0,0 +1,75 @@ +package com.bonus.common.utils; +import org.springframework.web.multipart.MultipartFile; +import java.io.*; +/** + * @className:ByteArrayMultipartFile + * @author:cwchen + * @date:2025-10-31-17:46 + * @version:1.0 + * @description: 字节数组转MultipartFile + */ + + +public class ByteArrayMultipartFile implements MultipartFile { + + private final byte[] fileContent; + private final String fileName; + private final String contentType; + + public ByteArrayMultipartFile(byte[] fileContent, String fileName) { + this(fileContent, fileName, null); + } + + public ByteArrayMultipartFile(byte[] fileContent, String fileName, String contentType) { + this.fileContent = fileContent != null ? fileContent : new byte[0]; + this.fileName = fileName; + this.contentType = contentType; + } + + @Override + public String getName() { + return fileName; + } + + @Override + public String getOriginalFilename() { + return fileName; + } + + @Override + public String getContentType() { + return contentType; + } + + @Override + public boolean isEmpty() { + return fileContent.length == 0; + } + + @Override + public long getSize() { + return fileContent.length; + } + + @Override + public byte[] getBytes() throws IOException { + return fileContent; + } + + @Override + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(fileContent); + } + + @Override + public void transferTo(File dest) throws IOException, IllegalStateException { + try (FileOutputStream fos = new FileOutputStream(dest)) { + fos.write(fileContent); + } + } + + @Override + public void transferTo(java.nio.file.Path dest) throws IOException, IllegalStateException { + java.nio.file.Files.write(dest, fileContent); + } +} \ No newline at end of file diff --git a/bonus-common/src/main/java/com/bonus/common/utils/ExcelImageUtils.java b/bonus-common/src/main/java/com/bonus/common/utils/ExcelImageUtils.java new file mode 100644 index 0000000..6eb7420 --- /dev/null +++ b/bonus-common/src/main/java/com/bonus/common/utils/ExcelImageUtils.java @@ -0,0 +1,66 @@ +package com.bonus.common.utils; +import org.apache.poi.ss.usermodel.*; + +import java.util.Map; +import java.util.HashMap; +/** + * @className:ExcelImageUtils + * @author:cwchen + * @date:2025-10-31-17:40 + * @version:1.0 + * @description:excel图片类型 + */ + + +public class ExcelImageUtils { + + private static final Map IMAGE_INFO_MAP = new HashMap<>(); + + static { + IMAGE_INFO_MAP.put(Workbook.PICTURE_TYPE_JPEG, new ImageInfo("jpg", "image/jpeg")); + IMAGE_INFO_MAP.put(Workbook.PICTURE_TYPE_PNG, new ImageInfo("png", "image/png")); + IMAGE_INFO_MAP.put(Workbook.PICTURE_TYPE_EMF, new ImageInfo("emf", "image/x-emf")); + IMAGE_INFO_MAP.put(Workbook.PICTURE_TYPE_WMF, new ImageInfo("wmf", "image/x-wmf")); + IMAGE_INFO_MAP.put(Workbook.PICTURE_TYPE_DIB, new ImageInfo("bmp", "image/bmp")); + } + + public static String getFileExtension(PictureData pictureData) { + ImageInfo info = IMAGE_INFO_MAP.get(pictureData.getPictureType()); + return info != null ? info.getExtension() : "dat"; + } + + public static String getMimeType(PictureData pictureData) { + ImageInfo info = IMAGE_INFO_MAP.get(pictureData.getPictureType()); + return info != null ? info.getMimeType() : "application/octet-stream"; + } + + public static boolean isSupportedImageType(PictureData pictureData) { + return IMAGE_INFO_MAP.containsKey(pictureData.getPictureType()); + } + + public static byte[] getImageData(PictureData pictureData) { + return pictureData.getData(); + } + + public static long getImageSize(PictureData pictureData) { + return pictureData.getData().length; + } + + private static class ImageInfo { + private final String extension; + private final String mimeType; + + public ImageInfo(String extension, String mimeType) { + this.extension = extension; + this.mimeType = mimeType; + } + + public String getExtension() { + return extension; + } + + public String getMimeType() { + return mimeType; + } + } +} diff --git a/bonus-file/src/main/java/com/bonus/file/util/FileUtil.java b/bonus-common/src/main/java/com/bonus/common/utils/FileUtil.java similarity index 90% rename from bonus-file/src/main/java/com/bonus/file/util/FileUtil.java rename to bonus-common/src/main/java/com/bonus/common/utils/FileUtil.java index 82ec74d..bd716f1 100644 --- a/bonus-file/src/main/java/com/bonus/file/util/FileUtil.java +++ b/bonus-common/src/main/java/com/bonus/common/utils/FileUtil.java @@ -1,4 +1,4 @@ -package com.bonus.file.util; +package com.bonus.common.utils; import org.springframework.web.multipart.MultipartFile; @@ -154,4 +154,16 @@ public class FileUtil { return IMAGE_EXTENSIONS.contains(extension); } + /** + * 将byte数组转换为MultipartFile + * @param fileContent 文件内容字节数组 + * @param originalFileName 原始文件名 + * @return MultipartFile对象 + */ + public static MultipartFile byteArrayToMultipartFile(byte[] fileContent, String originalFileName) { + // 自动检测文件类型 + String contentType = getMimeTypeByFilename(originalFileName); + return new ByteArrayMultipartFile(fileContent, originalFileName, contentType); + } + }