From d4562365995a2e2659ca282a4249a1c67404bd88 Mon Sep 17 00:00:00 2001 From: daiyongfei <974332738@qq.com> Date: Fri, 4 Jul 2025 11:02:22 +0800 Subject: [PATCH] =?UTF-8?q?WEB=EF=BC=9A=E5=AF=BC=E5=85=A5=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/fuyuanshen/DromaraApplication.java | 1 - fys-common/fys-common-core/pom.xml | 38 ++ .../core/exception/BadRequestException.java | 41 ++ .../core/exception/EntityExistException.java | 38 ++ .../exception/EntityNotFoundException.java | 34 ++ .../core/exception/handler/ApiError.java | 49 +++ .../common/core/utils/CloseUtil.java | 46 ++ .../common/core/utils/file/FileUtil.java | 413 ++++++++++++++++++ fys-modules/fys-equipment/pom.xml | 8 +- .../controller/DeviceController.java | 69 +-- .../domain/query/DeviceQueryCriteria.java | 2 +- .../equipment/excel/DeviceImportParams.java | 34 ++ .../excel/UploadDeviceDataListener.java | 260 +++++++++++ .../equipment/handler/ImageWriteHandler.java | 69 +++ .../service/impl/DeviceServiceImpl.java | 9 +- .../system/service/ISysOssService.java | 3 + .../service/impl/SysOssServiceImpl.java | 30 ++ 17 files changed, 1106 insertions(+), 38 deletions(-) create mode 100644 fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/BadRequestException.java create mode 100644 fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/EntityExistException.java create mode 100644 fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/EntityNotFoundException.java create mode 100644 fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/handler/ApiError.java create mode 100644 fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/CloseUtil.java create mode 100644 fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/file/FileUtil.java create mode 100644 fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/excel/DeviceImportParams.java create mode 100644 fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/excel/UploadDeviceDataListener.java create mode 100644 fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/handler/ImageWriteHandler.java diff --git a/fys-admin/src/main/java/com/fuyuanshen/DromaraApplication.java b/fys-admin/src/main/java/com/fuyuanshen/DromaraApplication.java index fe7c806..4f755e3 100644 --- a/fys-admin/src/main/java/com/fuyuanshen/DromaraApplication.java +++ b/fys-admin/src/main/java/com/fuyuanshen/DromaraApplication.java @@ -9,7 +9,6 @@ import org.springframework.boot.context.metrics.buffering.BufferingApplicationSt * * @author Lion Li */ - @SpringBootApplication public class DromaraApplication { diff --git a/fys-common/fys-common-core/pom.xml b/fys-common/fys-common-core/pom.xml index bb1e156..1ba5b46 100644 --- a/fys-common/fys-common-core/pom.xml +++ b/fys-common/fys-common-core/pom.xml @@ -15,6 +15,10 @@ fys-common-core 核心模块 + + 5.8.35 + + @@ -56,6 +60,13 @@ hutool-core + + + cn.hutool + hutool-all + ${hutool.version} + + cn.hutool hutool-http @@ -66,6 +77,33 @@ hutool-extra + + + org.apache.poi + poi + 5.4.0 + + + org.apache.poi + poi-ooxml + 5.4.0 + + + xerces + xercesImpl + 2.12.2 + + + org.apache.poi + poi-scratchpad + 5.4.0 + + + org.apache.poi + poi-ooxml-full + 5.4.0 + + org.projectlombok lombok diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/BadRequestException.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/BadRequestException.java new file mode 100644 index 0000000..19266a7 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/BadRequestException.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019-2025 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fuyuanshen.common.core.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; + +/** + * @author Zheng Jie + * @date 2018-11-23 + * 统一异常处理 + */ +@Getter +public class BadRequestException extends RuntimeException{ + + private Integer status = BAD_REQUEST.value(); + + public BadRequestException(String msg){ + super(msg); + } + + public BadRequestException(HttpStatus status,String msg){ + super(msg); + this.status = status.value(); + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/EntityExistException.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/EntityExistException.java new file mode 100644 index 0000000..648c078 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/EntityExistException.java @@ -0,0 +1,38 @@ +/* + * Copyright 2019-2025 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fuyuanshen.common.core.exception; + +import org.springframework.util.StringUtils; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +public class EntityExistException extends RuntimeException { + + public EntityExistException(Class clazz, String field, String val) { + super(EntityExistException.generateMessage(clazz.getSimpleName(), field, val)); + } + + private static String generateMessage1(String entity, String field, String val) { + return StringUtils.capitalize(entity) + " with " + field + " " + val + " existed"; + } + + private static String generateMessage(String entity, String field, String val) { + return field + " :" + val + " 已存在"; + } + +} \ No newline at end of file diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/EntityNotFoundException.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/EntityNotFoundException.java new file mode 100644 index 0000000..841dcb8 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/EntityNotFoundException.java @@ -0,0 +1,34 @@ +/* + * Copyright 2019-2025 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fuyuanshen.common.core.exception; + +import org.springframework.util.StringUtils; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +public class EntityNotFoundException extends RuntimeException { + + public EntityNotFoundException(Class clazz, String field, String val) { + super(EntityNotFoundException.generateMessage(clazz.getSimpleName(), field, val)); + } + + private static String generateMessage(String entity, String field, String val) { + return StringUtils.capitalize(entity) + + " with " + field + " "+ val + " does not exist"; + } +} \ No newline at end of file diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/handler/ApiError.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/handler/ApiError.java new file mode 100644 index 0000000..9243976 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/handler/ApiError.java @@ -0,0 +1,49 @@ +/* + * Copyright 2019-2025 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fuyuanshen.common.core.exception.handler; + +import lombok.Data; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +@Data +public class ApiError { + + private Integer status = 400; + private Long timestamp; + private String message; + + private ApiError() { + timestamp = System.currentTimeMillis(); + } + + public static ApiError error(String message){ + ApiError apiError = new ApiError(); + apiError.setMessage(message); + return apiError; + } + + public static ApiError error(Integer status, String message){ + ApiError apiError = new ApiError(); + apiError.setStatus(status); + apiError.setMessage(message); + return apiError; + } +} + + diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/CloseUtil.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/CloseUtil.java new file mode 100644 index 0000000..809d77c --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/CloseUtil.java @@ -0,0 +1,46 @@ +/* + * Copyright 2019-2025 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fuyuanshen.common.core.utils; + +import java.io.Closeable; + +/** + * @author Zheng Jie + * @description 用于关闭各种连接,缺啥补啥 + * @date 2021-03-05 + **/ +public class CloseUtil { + + public static void close(Closeable closeable) { + if (null != closeable) { + try { + closeable.close(); + } catch (Exception e) { + // 静默关闭 + } + } + } + + public static void close(AutoCloseable closeable) { + if (null != closeable) { + try { + closeable.close(); + } catch (Exception e) { + // 静默关闭 + } + } + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/file/FileUtil.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/file/FileUtil.java new file mode 100644 index 0000000..cebd84c --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/file/FileUtil.java @@ -0,0 +1,413 @@ +/* + * Copyright 2019-2025 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fuyuanshen.common.core.utils.file; + +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.poi.excel.BigExcelWriter; +import cn.hutool.poi.excel.ExcelUtil; +import com.fuyuanshen.common.core.exception.BadRequestException; +import com.fuyuanshen.common.core.utils.CloseUtil; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.springframework.web.multipart.MultipartFile; +import java.io.*; +import java.nio.file.Files; +import java.security.MessageDigest; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * File工具类,扩展 hutool 工具包 + * + * @author Zheng Jie + * @date 2018-12-27 + */ +@Slf4j +public class FileUtil extends cn.hutool.core.io.FileUtil { + + /** + * 系统临时目录 + *
+ * windows 包含路径分割符,但Linux 不包含, + * 在windows \\==\ 前提下, + * 为安全起见 同意拼装 路径分割符, + *
+     *       java.io.tmpdir
+     *       windows : C:\Users/xxx\AppData\Local\Temp\
+     *       linux: /temp
+     * 
+ */ + public static final String SYS_TEM_DIR = System.getProperty("java.io.tmpdir") + File.separator; + /** + * 定义GB的计算常量 + */ + private static final int GB = 1024 * 1024 * 1024; + /** + * 定义MB的计算常量 + */ + private static final int MB = 1024 * 1024; + /** + * 定义KB的计算常量 + */ + private static final int KB = 1024; + + /** + * 格式化小数 + */ + private static final DecimalFormat DF = new DecimalFormat("0.00"); + + public static final String IMAGE = "图片"; + public static final String TXT = "文档"; + public static final String MUSIC = "音乐"; + public static final String VIDEO = "视频"; + public static final String OTHER = "其他"; + + + /** + * MultipartFile转File + */ + public static File toFile(MultipartFile multipartFile) { + // 获取文件名 + String fileName = multipartFile.getOriginalFilename(); + // 获取文件后缀 + String prefix = "." + getExtensionName(fileName); + File file = null; + try { + // 用uuid作为文件名,防止生成的临时文件重复 + file = new File(SYS_TEM_DIR + IdUtil.simpleUUID() + prefix); + // MultipartFile to File + multipartFile.transferTo(file); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + return file; + } + + /** + * 获取文件扩展名,不带 . + */ + public static String getExtensionName(String filename) { + if ((filename != null) && (!filename.isEmpty())) { + int dot = filename.lastIndexOf('.'); + if ((dot > -1) && (dot < (filename.length() - 1))) { + return filename.substring(dot + 1); + } + } + return filename; + } + + /** + * Java文件操作 获取不带扩展名的文件名 + */ + public static String getFileNameNoEx(String filename) { + if ((filename != null) && (!filename.isEmpty())) { + int dot = filename.lastIndexOf('.'); + if (dot > -1) { + return filename.substring(0, dot); + } + } + return filename; + } + + /** + * 文件大小转换 + */ + public static String getSize(long size) { + String resultSize; + if (size / GB >= 1) { + //如果当前Byte的值大于等于1GB + resultSize = DF.format(size / (float) GB) + "GB "; + } else if (size / MB >= 1) { + //如果当前Byte的值大于等于1MB + resultSize = DF.format(size / (float) MB) + "MB "; + } else if (size / KB >= 1) { + //如果当前Byte的值大于等于1KB + resultSize = DF.format(size / (float) KB) + "KB "; + } else { + resultSize = size + "B "; + } + return resultSize; + } + + /** + * inputStream 转 File + */ + static File inputStreamToFile(InputStream ins, String name){ + File file = new File(SYS_TEM_DIR + name); + if (file.exists()) { + return file; + } + OutputStream os = null; + try { + os = Files.newOutputStream(file.toPath()); + int bytesRead; + int len = 8192; + byte[] buffer = new byte[len]; + while ((bytesRead = ins.read(buffer, 0, len)) != -1) { + os.write(buffer, 0, bytesRead); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + CloseUtil.close(os); + CloseUtil.close(ins); + } + return file; + } + + /** + * 将文件名解析成文件的上传路径 + */ + public static File upload(MultipartFile file, String filePath) { + Date date = new Date(); + SimpleDateFormat format = new SimpleDateFormat("yyyyMMddhhmmssS"); + // 过滤非法文件名 + String name = getFileNameNoEx(verifyFilename(file.getOriginalFilename())); + String suffix = getExtensionName(file.getOriginalFilename()); + String nowStr = "-" + format.format(date); + try { + String fileName = name + nowStr + "." + suffix; + String path = filePath + fileName; + // getCanonicalFile 可解析正确各种路径 + File dest = new File(path).getCanonicalFile(); + // 检测是否存在目录 + if (!dest.getParentFile().exists()) { + if (!dest.getParentFile().mkdirs()) { + System.out.println("was not successful."); + } + } + // 文件写入 + file.transferTo(dest); + return dest; + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return null; + } + + /** + * 导出excel + */ + public static void downloadExcel(List> list, HttpServletResponse response) throws IOException { + String tempPath = SYS_TEM_DIR + IdUtil.fastSimpleUUID() + ".xlsx"; + File file = new File(tempPath); + BigExcelWriter writer = ExcelUtil.getBigWriter(file); + // 处理数据以防止CSV注入 + List> sanitizedList = list.parallelStream().map(map -> { + Map sanitizedMap = new LinkedHashMap<>(); + map.forEach((key, value) -> { + if (value instanceof String) { + String strValue = (String) value; + // 检查并处理以特殊字符开头的值 + if (strValue.startsWith("=") || strValue.startsWith("+") || strValue.startsWith("-") || strValue.startsWith("@")) { + strValue = "'" + strValue; // 添加单引号前缀 + } + sanitizedMap.put(key, strValue); + } else { + sanitizedMap.put(key, value); + } + }); + return sanitizedMap; + }).collect(Collectors.toList()); + // 一次性写出内容,使用默认样式,强制输出标题 + writer.write(sanitizedList, true); + SXSSFSheet sheet = (SXSSFSheet)writer.getSheet(); + //上面需要强转SXSSFSheet 不然没有trackAllColumnsForAutoSizing方法 + sheet.trackAllColumnsForAutoSizing(); + //列宽自适应 + writer.autoSizeColumnAll(); + //response为HttpServletResponse对象 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"); + //test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码 + response.setHeader("Content-Disposition", "attachment;filename=file.xlsx"); + ServletOutputStream out = response.getOutputStream(); + // 终止后删除临时文件 + file.deleteOnExit(); + writer.flush(out, true); + //此处记得关闭输出Servlet流 + IoUtil.close(out); + } + + public static String getFileType(String type) { + String documents = "txt doc pdf ppt pps xlsx xls docx"; + String music = "mp3 wav wma mpa ram ra aac aif m4a"; + String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg"; + String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg"; + if (image.contains(type)) { + return IMAGE; + } else if (documents.contains(type)) { + return TXT; + } else if (music.contains(type)) { + return MUSIC; + } else if (video.contains(type)) { + return VIDEO; + } else { + return OTHER; + } + } + + public static void checkSize(long maxSize, long size) { + // 1M + int len = 1024 * 1024; + if (size > (maxSize * len)) { + throw new BadRequestException("文件超出规定大小:" + maxSize + "MB"); + } + } + + /** + * 判断两个文件是否相同 + */ + public static boolean check(File file1, File file2) { + String img1Md5 = getMd5(file1); + String img2Md5 = getMd5(file2); + if(img1Md5 != null){ + return img1Md5.equals(img2Md5); + } + return false; + } + + /** + * 判断两个文件是否相同 + */ + public static boolean check(String file1Md5, String file2Md5) { + return file1Md5.equals(file2Md5); + } + + private static byte[] getByte(File file) { + // 得到文件长度 + byte[] b = new byte[(int) file.length()]; + InputStream in = null; + try { + in = Files.newInputStream(file.toPath()); + try { + System.out.println(in.read(b)); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + return null; + } finally { + CloseUtil.close(in); + } + return b; + } + + private static String getMd5(byte[] bytes) { + // 16进制字符 + char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + try { + MessageDigest mdTemp = MessageDigest.getInstance("MD5"); + mdTemp.update(bytes); + byte[] md = mdTemp.digest(); + int j = md.length; + char[] str = new char[j * 2]; + int k = 0; + // 移位 输出字符串 + for (byte byte0 : md) { + str[k++] = hexDigits[byte0 >>> 4 & 0xf]; + str[k++] = hexDigits[byte0 & 0xf]; + } + return new String(str); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return null; + } + + /** + * 下载文件 + * + * @param request / + * @param response / + * @param file / + */ + public static void downloadFile(HttpServletRequest request, HttpServletResponse response, File file, boolean deleteOnExit) { + response.setCharacterEncoding(request.getCharacterEncoding()); + response.setContentType("application/octet-stream"); + FileInputStream fis = null; + try { + fis = new FileInputStream(file); + response.setHeader("Content-Disposition", "attachment; filename=" + file.getName()); + IOUtils.copy(fis, response.getOutputStream()); + response.flushBuffer(); + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + if (fis != null) { + try { + fis.close(); + if (deleteOnExit) { + file.deleteOnExit(); + } + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + } + } + + /** + * 验证并过滤非法的文件名 + * @param fileName 文件名 + * @return 文件名 + */ + public static String verifyFilename(String fileName) { + // 过滤掉特殊字符 + fileName = fileName.replaceAll("[\\\\/:*?\"<>|~\\s]", ""); + + // 去掉文件名开头和结尾的空格和点 + fileName = fileName.trim().replaceAll("^[. ]+|[. ]+$", ""); + + // 不允许文件名超过255(在Mac和Linux中)或260(在Windows中)个字符 + int maxFileNameLength = 255; + if (System.getProperty("os.name").startsWith("Windows")) { + maxFileNameLength = 260; + } + if (fileName.length() > maxFileNameLength) { + fileName = fileName.substring(0, maxFileNameLength); + } + + // 过滤掉控制字符 + fileName = fileName.replaceAll("[\\p{Cntrl}]", ""); + + // 过滤掉 ".." 路径 + fileName = fileName.replaceAll("\\.{2,}", ""); + + // 去掉文件名开头的 ".." + fileName = fileName.replaceAll("^\\.+/", ""); + + // 保留文件名中最后一个 "." 字符,过滤掉其他 "." + fileName = fileName.replaceAll("^(.*)(\\.[^.]*)$", "$1").replaceAll("\\.", "") + + fileName.replaceAll("^(.*)(\\.[^.]*)$", "$2"); + + return fileName; + } + + public static String getMd5(File file) { + return getMd5(getByte(file)); + } +} diff --git a/fys-modules/fys-equipment/pom.xml b/fys-modules/fys-equipment/pom.xml index 87cc0b3..ba67986 100644 --- a/fys-modules/fys-equipment/pom.xml +++ b/fys-modules/fys-equipment/pom.xml @@ -118,12 +118,12 @@ 3.3.1
+ - org.apache.poi - ooxml-schemas - 1.4 + com.alibaba.fastjson2 + fastjson2 + 2.0.54 -
diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/controller/DeviceController.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/controller/DeviceController.java index d1cfb59..175a64a 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/controller/DeviceController.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/controller/DeviceController.java @@ -1,23 +1,33 @@ package com.fuyuanshen.equipment.controller; +import com.alibaba.excel.EasyExcel; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.fuyuanshen.common.core.constant.ResponseMessageConstants; import com.fuyuanshen.common.core.domain.ResponseVO; +import com.fuyuanshen.common.core.domain.model.LoginUser; +import com.fuyuanshen.common.core.utils.file.FileUtil; import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.customer.mapper.CustomerMapper; import com.fuyuanshen.equipment.domain.Device; +import com.fuyuanshen.equipment.domain.dto.DeviceExcelImportDTO; import com.fuyuanshen.equipment.domain.dto.ImportResult; import com.fuyuanshen.equipment.domain.form.DeviceForm; import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria; import com.fuyuanshen.equipment.domain.vo.CustomerVo; +import com.fuyuanshen.equipment.excel.DeviceImportParams; +import com.fuyuanshen.equipment.excel.UploadDeviceDataListener; +import com.fuyuanshen.equipment.mapper.DeviceMapper; +import com.fuyuanshen.equipment.mapper.DeviceTypeMapper; import com.fuyuanshen.equipment.service.DeviceService; import com.fuyuanshen.equipment.service.impl.DeviceExportService; -import com.fuyuanshen.system.domain.vo.SysOssVo; import com.fuyuanshen.system.service.ISysOssService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; +import io.undertow.util.BadRequestException; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -42,6 +52,9 @@ public class DeviceController { private final ISysOssService ossService; private final DeviceService deviceService; + private final DeviceMapper deviceMapper; + private final CustomerMapper customerMapper; + private final DeviceTypeMapper deviceTypeMapper; private final DeviceExportService exportService; @@ -156,36 +169,34 @@ public class DeviceController { @Operation(summary = "导入设备数据") @PostMapping(value = "/import", consumes = "multipart/form-data") - public ResponseVO importData(@Parameter(name = "文件", required = true) @RequestPart("file") MultipartFile file) { + public ResponseVO importData(@Parameter(name = "文件", required = true) @RequestPart("file") MultipartFile file) throws BadRequestException { - SysOssVo upload = ossService.upload(file); + String suffix = FileUtil.getExtensionName(file.getOriginalFilename()); + if (!("xlsx".equalsIgnoreCase(suffix))) { + throw new BadRequestException("只能上传Excel——xlsx格式文件"); + } - // String suffix = FileUtil.getExtensionName(file.getOriginalFilename()); - // if (!("xlsx".equalsIgnoreCase(suffix))) { - // throw new BadRequestException("只能上传Excel——xlsx格式文件"); - // } - // - // ImportResult result = new ImportResult(); - // try { - // User currentUser = userMapper.findByUsername(SecurityUtils.getCurrentUsername()); - // DeviceImportParams params = DeviceImportParams.builder().ip(ip).deviceService(deviceService).tenantId(currentUser.getTenantId()).file(file).filePath(filePath).deviceMapper(deviceMapper).deviceAssignmentsService(deviceAssignmentsService).deviceTypeMapper(deviceTypeMapper).userId(currentUser.getId()).userMapper(userMapper).build(); - // // 创建监听器 - // UploadDeviceDataListener listener = new UploadDeviceDataListener(params); - // // 读取Excel - // EasyExcel.read(file.getInputStream(), DeviceExcelImportDTO.class, listener).sheet().doRead(); - // // 获取导入结果 - // result = listener.getImportResult(); - // // 设置响应消息 - // String message = String.format("成功导入 %d 条数据,失败 %d 条", result.getSuccessCount(), result.getFailureCount()); - // // 返回带有正确泛型的响应 - // return ResponseVO.success(message, result); - // } catch (Exception e) { - // log.error("导入设备数据出错: {}", e.getMessage(), e); - // // 在异常情况下,设置默认结果 - // String errorMessage = String.format("导入失败: %s。成功 %d 条,失败 %d 条", e.getMessage(), result.getSuccessCount(), result.getFailureCount()); - // // 使用新方法确保类型正确 - // return ResponseVO.fail(errorMessage, result); - return null; + ImportResult result = new ImportResult(); + try { + LoginUser loginUser = LoginHelper.getLoginUser(); + DeviceImportParams params = DeviceImportParams.builder().ossService(ossService).deviceService(deviceService).tenantId(loginUser.getTenantId()).file(file).filePath("").deviceMapper(deviceMapper).deviceTypeMapper(deviceTypeMapper).userId(loginUser.getUserId()).customerMapper(customerMapper).build(); + // 创建监听器 + UploadDeviceDataListener listener = new UploadDeviceDataListener(params); + // 读取Excel + EasyExcel.read(file.getInputStream(), DeviceExcelImportDTO.class, listener).sheet().doRead(); + // 获取导入结果 + result = listener.getImportResult(); + // 设置响应消息 + String message = String.format("成功导入 %d 条数据,失败 %d 条", result.getSuccessCount(), result.getFailureCount()); + // 返回带有正确泛型的响应 + return ResponseVO.success(message, result); + } catch (Exception e) { + log.error("导入设备数据出错: {}", e.getMessage(), e); + // 在异常情况下,设置默认结果 + String errorMessage = String.format("导入失败: %s。成功 %d 条,失败 %d 条", e.getMessage(), result.getSuccessCount(), result.getFailureCount()); + // 使用新方法确保类型正确 + return ResponseVO.fail(errorMessage, result); + } } } diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceQueryCriteria.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceQueryCriteria.java index 6a098ab..8da135f 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceQueryCriteria.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceQueryCriteria.java @@ -60,7 +60,7 @@ public class DeviceQueryCriteria { private Long currentOwnerId; @Schema(name = "租户ID") - private Long tenantId; + private String tenantId; @Schema(name = "通讯方式", example = "0:4G;1:蓝牙") private Integer communicationMode; diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/excel/DeviceImportParams.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/excel/DeviceImportParams.java new file mode 100644 index 0000000..97d333d --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/excel/DeviceImportParams.java @@ -0,0 +1,34 @@ +package com.fuyuanshen.equipment.excel; + +import com.fuyuanshen.customer.mapper.CustomerMapper; +import com.fuyuanshen.equipment.mapper.DeviceMapper; +import com.fuyuanshen.equipment.mapper.DeviceTypeMapper; +import com.fuyuanshen.equipment.service.DeviceService; +import com.fuyuanshen.system.service.ISysOssService; +import lombok.*; +import org.springframework.web.multipart.MultipartFile; + +/** + * @author: 默苍璃 + * @date: 2025-06-1414:56 + */ +@AllArgsConstructor +@NoArgsConstructor(force = true) +@Getter +@Setter +@Builder +public class DeviceImportParams { + + private DeviceService deviceService; + private DeviceMapper deviceMapper; + private CustomerMapper customerMapper; + private DeviceTypeMapper deviceTypeMapper; + private ISysOssService ossService; + private MultipartFile file; + private String filePath; + private String ip; + private String username; + private Long userId; + private String tenantId; + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/excel/UploadDeviceDataListener.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/excel/UploadDeviceDataListener.java new file mode 100644 index 0000000..7869e0d --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/excel/UploadDeviceDataListener.java @@ -0,0 +1,260 @@ +package com.fuyuanshen.equipment.excel; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.fastjson2.JSON; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.fuyuanshen.common.core.domain.model.LoginUser; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.equipment.constants.DeviceConstants; +import com.fuyuanshen.equipment.domain.Device; +import com.fuyuanshen.equipment.domain.DeviceType; +import com.fuyuanshen.equipment.domain.dto.DeviceExcelImportDTO; +import com.fuyuanshen.equipment.domain.dto.ImportResult; +import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria; +import com.fuyuanshen.equipment.handler.ImageWriteHandler; +import com.fuyuanshen.system.domain.vo.SysOssVo; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.util.ZipSecureFile; +import org.apache.poi.xssf.usermodel.*; + +import java.io.File; +import java.util.*; + + +@Slf4j +public class UploadDeviceDataListener implements ReadListener { + + + // 存储图片数据的映射 + private final Map rowImageMap = new HashMap<>(); + + private final DeviceImportParams params; + + private final Map rowDeviceMap = new HashMap<>(); + private final Map rowDtoMap = new HashMap<>(); + private final List rowIndexList = new ArrayList<>(); + + private int successCount = 0; + private int failureCount = 0; + private final List failedRecords = new ArrayList<>(); + + + public UploadDeviceDataListener(DeviceImportParams params) { + this.params = params; + } + + public ImportResult getImportResult() { + ImportResult result = new ImportResult(); + result.setSuccessCount(successCount); + result.setFailureCount(failureCount); + + // 准备失败记录(包含图片数据) + List failedRecordsWithImages = new ArrayList<>(); + + for (DeviceExcelImportDTO failedRecord : failedRecords) { + // 获取原始行号 + Integer rowIndex = null; + for (Map.Entry entry : rowDtoMap.entrySet()) { + if (entry.getValue() == failedRecord) { + rowIndex = entry.getKey(); + break; + } + } + + if (rowIndex != null) { + // 创建副本,避免修改原始数据 + DeviceExcelImportDTO recordWithImage = new DeviceExcelImportDTO(); + BeanUtil.copyProperties(failedRecord, recordWithImage); + + // 设置图片数据 + byte[] imageData = rowImageMap.get(rowIndex); + if (imageData != null) { + recordWithImage.setDevicePicFromBytes(imageData); + } + + failedRecordsWithImages.add(recordWithImage); + } else { + failedRecordsWithImages.add(failedRecord); + } + } + + result.setFailedRecords(failedRecordsWithImages); + + // 生成错误报告 + if (!failedRecordsWithImages.isEmpty()) { + try { + // 生成唯一的文件名 + String fileName = "import_errors_" + System.currentTimeMillis() + ".xlsx"; + + // 创建错误报告目录 + String errorDirPath = params.getFilePath() + DeviceConstants.ERROR_REPORT_DIR; + File errorDir = new File(errorDirPath); + if (!errorDir.exists() && !errorDir.mkdirs()) { + log.error("无法创建错误报告目录: {}", errorDirPath); + return result; + } + + // 保存错误报告到文件(包含图片) + File errorFile = new File(errorDir, fileName); + EasyExcel.write(errorFile, DeviceExcelImportDTO.class).registerWriteHandler(new ImageWriteHandler()) // 添加图片处理 + .sheet("失败数据").doWrite(failedRecordsWithImages); + + // 生成访问URL + // String errorExcelUrl = params.getIp() + DeviceConstants.FILE_ACCESS_PREFIX + "/" + DeviceConstants.ERROR_REPORT_DIR + "/" + fileName; + SysOssVo upload = params.getOssService().upload(errorFile); + result.setErrorExcelUrl(upload.getUrl()); + log.info("错误报告已保存: {}", errorFile.getAbsolutePath()); + } catch (Exception e) { + log.error("生成错误报告失败", e); + } + } + + return result; + } + + + @Override + public void invoke(DeviceExcelImportDTO data, AnalysisContext context) { + log.info("解析到一条数据: {}", JSON.toJSONString(data)); + int rowIndex = context.readRowHolder().getRowIndex(); + + Device device = new Device(); + BeanUtil.copyProperties(data, device, true); + rowDeviceMap.put(rowIndex, device); + rowDtoMap.put(rowIndex, data); + rowIndexList.add(rowIndex); + + } + + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + try { + processImages(); + processDataRowByRow(); + log.info("数据处理完成!成功:{}条,失败:{}条", successCount, failureCount); + } catch (Exception e) { + log.error("数据处理失败:{}", e.getMessage(), e); + throw new RuntimeException("导入失败", e); + } + } + + + private void processDataRowByRow() { + LoginUser loginUser = LoginHelper.getLoginUser(); + for (Integer rowIndex : rowIndexList) { + Device device = rowDeviceMap.get(rowIndex); + DeviceExcelImportDTO originalDto = rowDtoMap.get(rowIndex); + try { + DeviceQueryCriteria criteria = new DeviceQueryCriteria(); + criteria.setDeviceMac(device.getDeviceMac()); + criteria.setTenantId(params.getTenantId()); + List deviceList = params.getDeviceMapper().findAll(criteria); + if (!deviceList.isEmpty()) { + throw new RuntimeException("设备MAC重复"); + } + device.setTenantId(params.getTenantId()); + + // 设备类型 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("type_name", device.getTypeName()); + wrapper.eq("customer_id", params.getUserId()); + List deviceTypes = params.getDeviceTypeMapper().selectList(wrapper); + if (CollectionUtil.isNotEmpty(deviceTypes)) { + device.setDeviceType(deviceTypes.get(0).getId()); + } + params.getDeviceService().save(device); + successCount++; + log.info("行 {} 数据插入成功", rowIndex); + } catch (Exception e) { + failureCount++; + failedRecords.add(originalDto); + log.error("行 {} 数据插入失败: {}", rowIndex, e.getMessage()); + } + } + } + + private void processImages() { + try (OPCPackage opcPackage = OPCPackage.open(params.getFile().getInputStream())) { + ZipSecureFile.setMinInflateRatio(-1.0d); + XSSFWorkbook workbook = new XSSFWorkbook(opcPackage); + XSSFSheet sheet = workbook.getSheetAt(0); + + XSSFDrawing drawing = sheet.getDrawingPatriarch(); + if (drawing == null) return; + + for (XSSFShape shape : drawing.getShapes()) { + if (shape instanceof XSSFPicture) { + XSSFPicture picture = (XSSFPicture) shape; + XSSFClientAnchor anchor = picture.getPreferredSize(); + int rowIndex = anchor.getRow1(); + int colIndex = anchor.getCol1(); + + if (colIndex == 2) { + Device device = rowDeviceMap.get(rowIndex); + if (device != null) { + try { + byte[] imageData = picture.getPictureData().getData(); + String extraValue = getCellValue(sheet, rowIndex, 4); + String imageUrl = uploadAndGenerateUrl(imageData, extraValue); + device.setDevicePic(imageUrl); + + // 2. 保存图片数据到DTO,用于错误报告 + rowImageMap.put(rowIndex, imageData); + } catch (Exception e) { + log.error("行 {} 图片处理失败: {}", rowIndex, e.getMessage()); + device.setDevicePic(null); // 设置为空,让插入继续 + } + } + } + } + } + } catch (Exception e) { + log.error("图片处理失败:{}", e.getMessage(), e); + } + } + + private String getCellValue(XSSFSheet sheet, int rowIndex, int colIndex) { + XSSFRow row = sheet.getRow(rowIndex); + if (row == null) return null; + + XSSFCell cell = row.getCell(colIndex); + if (cell == null) return null; + + return cell.toString(); + } + + private String uploadAndGenerateUrl(byte[] imageData, String deviceMac) { + if (imageData == null || imageData.length == 0) { + log.warn("图片数据为空"); + return null; + } + try { + String fileExtension = "jpg"; + String newFileName = "PS_" + new Random(8) + "." + fileExtension; + // String targetDirPath = params.getFilePath() + DeviceConstants.FILE_ACCESS_ISOLATION; + // File targetDir = new File(targetDirPath); + // + // if (!targetDir.exists() && !targetDir.mkdirs()) { + // log.error("无法创建目录: {}", targetDirPath); + // return null; + // } + // + // File newFile = new File(targetDir, newFileName); + // Files.write(newFile.toPath(), imageData); + // + // return params.getIp() + DeviceConstants.FILE_ACCESS_PREFIX + "/" + DeviceConstants.FILE_ACCESS_ISOLATION + "/" + newFileName; + SysOssVo upload = params.getOssService().upload(imageData, newFileName); + return upload.getUrl(); + } catch (Exception e) { + log.error("保存图片失败", e); + return null; + } + } + +} \ No newline at end of file diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/handler/ImageWriteHandler.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/handler/ImageWriteHandler.java new file mode 100644 index 0000000..f0fbc57 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/handler/ImageWriteHandler.java @@ -0,0 +1,69 @@ +package com.fuyuanshen.equipment.handler; + +import com.alibaba.excel.write.handler.SheetWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFSheet; + +/** + * @author: 默苍璃 + * @date: 2025-06-0718:05 + */ +public class ImageWriteHandler implements SheetWriteHandler { + + @Override + public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + // 不需要实现 + } + + @Override + public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + Workbook workbook = writeWorkbookHolder.getWorkbook(); + Sheet sheet = writeSheetHolder.getSheet(); + + // 获取设备图片列索引(假设是第4列,索引3) + int imageColIndex = 3; + + // 遍历所有行 + for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) { // 从第2行开始(跳过标题) + Row row = sheet.getRow(rowIndex); + if (row == null) continue; + + Cell imageCell = row.getCell(imageColIndex); + if (imageCell == null) continue; + + // 获取图片数据 + byte[] imageData = null; + if (imageCell.getCellType() == CellType.STRING) { + // 处理Base64编码的图片(如果需要) + } + + if (imageData != null && imageData.length > 0) { + try { + // 添加图片到工作表 + int pictureIdx = workbook.addPicture(imageData, Workbook.PICTURE_TYPE_JPEG); + + // 创建绘图对象 + if (sheet instanceof XSSFSheet) { + XSSFSheet xssfSheet = (XSSFSheet) sheet; + XSSFDrawing drawing = xssfSheet.createDrawingPatriarch(); + + // 设置图片位置 + XSSFClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, imageColIndex, rowIndex, imageColIndex + 1, rowIndex + 1); + + // 创建图片 + drawing.createPicture(anchor, pictureIdx); + } + + // 清除单元格内容 + imageCell.setBlank(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } +} \ No newline at end of file diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceServiceImpl.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceServiceImpl.java index df9f271..015d70c 100644 --- a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceServiceImpl.java +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceServiceImpl.java @@ -122,15 +122,18 @@ public class DeviceServiceImpl extends ServiceImpl impleme // 保存图片并获取URL // String imageUrl = saveDeviceImage(deviceForm.getFile(), deviceForm.getDeviceName()); - SysOssVo upload = ossService.upload(deviceForm.getFile()); - // 设置图片路径 - deviceForm.setDevicePic(upload.getUrl()); + if (deviceForm.getFile() != null) { + SysOssVo upload = ossService.upload(deviceForm.getFile()); + // 设置图片路径 + deviceForm.setDevicePic(upload.getUrl()); + } // 转换对象并插入数据库 Device device = new Device(); LoginUser loginUser = LoginHelper.getLoginUser(); device.setCurrentOwnerId(loginUser.getUserId()); device.setCreateByName(loginUser.getNickname()); + device.setTypeName(deviceTypes.get(0).getTypeName()); BeanUtil.copyProperties(deviceForm, device, true); deviceMapper.insert(device); diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysOssService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysOssService.java index 4f261ec..fe0d466 100644 --- a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysOssService.java +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysOssService.java @@ -60,6 +60,9 @@ public interface ISysOssService { */ SysOssVo upload(File file); + + public SysOssVo upload(byte[] data, String fileName); + /** * 文件下载方法,支持一次性下载完整文件 * diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysOssServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysOssServiceImpl.java index 9d3d8cf..a46dfe1 100644 --- a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysOssServiceImpl.java +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysOssServiceImpl.java @@ -219,6 +219,36 @@ public class SysOssServiceImpl implements ISysOssService, OssService { return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult); } + + + /** + * 上传二进制数据到对象存储服务,并保存文件信息到数据库 + * + * @param data 要上传的二进制数据 + * @param fileName 要上传的文件名(包括扩展名) + * @return 上传成功后的 SysOssVo 对象,包含文件信息 + * @throws ServiceException 如果上传过程中发生异常,则抛出 ServiceException 异常 + */ + @Override + public SysOssVo upload(byte[] data, String fileName) { + if (data == null || data.length == 0) { + throw new ServiceException("上传的数据为空"); + } + if (StringUtils.isBlank(fileName)) { + throw new ServiceException("文件名不能为空"); + } + + String suffix = StringUtils.substring(fileName, fileName.lastIndexOf("."), fileName.length()); + OssClient storage = OssFactory.instance(); + + UploadResult uploadResult; + uploadResult = storage.uploadSuffix(data, suffix, "image/jpeg"); // 假设是图片类型,可以根据实际需要修改 + + // 保存文件信息 + return buildResultEntity(fileName, suffix, storage.getConfigKey(), uploadResult); + } + + @NotNull private SysOssVo buildResultEntity(String originalfileName, String suffix, String configKey, UploadResult uploadResult) { SysOss oss = new SysOss();