From 1a1f86c5ff19cd784bb91f719d837f9eb9f2c5dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=B9=E9=B9=8F=E9=A3=9E?= Date: Mon, 7 Apr 2025 17:13:02 +0800 Subject: [PATCH] =?UTF-8?q?refactor(file):=20=E4=BC=98=E5=8C=96=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E4=B8=8A=E4=BC=A0=E5=92=8C=E4=B8=8B=E8=BD=BD=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改了文件上传接口,支持 InputStream 参数 - 优化了文件下载逻辑,使用临时文件和 ZipOutputStream - 更新了相关实体类和控制器的方法- 新增了 RemindUserRequest 和 ZipDownloadRequest 类 --- .../admin/controller/DeviceController.java | 77 ++++++++++-------- .../admin/controller/FileController.java | 11 +-- .../admin/controller/TicketController.java | 2 +- ...增导入模板.xlsx => deviceForAdd.xlsx} | Bin ...导入模板.xlsx => deviceForUpdate.xlsx} | Bin .../cfs/controller/TiketController.java | 2 +- .../common/pojo/dto/ChatMessageDTO.java | 5 +- .../pojo/request/AddChatMessageRequest.java | 4 +- .../pojo/request/RemindUserRequest.java | 17 ++++ .../pojo/request/ZipDownloadRequest.java | 18 ++++ .../common/pojo/vo/FileUploadVO.java | 2 + .../starter/service/FileUploadService.java | 10 +++ .../service/impl/OSSFileUploadService.java | 8 +- 13 files changed, 111 insertions(+), 45 deletions(-) rename nflg-mobilebroken-admin/src/main/resources/templates/{设备新增导入模板.xlsx => deviceForAdd.xlsx} (100%) rename nflg-mobilebroken-admin/src/main/resources/templates/{设备更新导入模板.xlsx => deviceForUpdate.xlsx} (100%) create mode 100644 nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/RemindUserRequest.java create mode 100644 nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/ZipDownloadRequest.java diff --git a/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/DeviceController.java b/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/DeviceController.java index 9e4b1e48..0d280b9b 100644 --- a/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/DeviceController.java +++ b/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/DeviceController.java @@ -3,8 +3,8 @@ package com.nflg.mobilebroken.admin.controller; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; -import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.google.common.collect.Sets; import com.nflg.mobilebroken.admin.annotation.ApiMark; @@ -23,6 +23,7 @@ import com.nflg.mobilebroken.common.util.*; import com.nflg.mobilebroken.repository.entity.*; import com.nflg.mobilebroken.repository.service.*; import com.nflg.mobilebroken.starter.annotation.MethodInfoMark; +import com.nflg.mobilebroken.starter.service.FileUploadService; import org.springframework.core.io.ClassPathResource; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -36,9 +37,10 @@ import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; import javax.validation.constraints.NotEmpty; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URLEncoder; -import java.nio.file.Paths; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -76,6 +78,9 @@ public class DeviceController extends ControllerBase { @Resource private IDeviceComponentService deviceComponentService; + @Resource + private FileUploadService fileUploadService; + /** * 获取设备列表 @@ -258,10 +263,10 @@ public class DeviceController extends ControllerBase { public void downTemplateForAdd(HttpServletResponse response) throws Exception { response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + URLEncoder.encode("设备新增导入模板.xlsx", "UTF-8")); - ClassPathResource resource = new ClassPathResource("templates/设备新增导入模板.xlsx"); + ClassPathResource resource = new ClassPathResource("templates/deviceForAdd.xlsx"); List datas = new ArrayList<>(); new Workbook() - .addSheet(new TemplateSheet(Paths.get(resource.getURI())) + .addSheet(new TemplateSheet(resource.getInputStream()) .setData(datas) .setData("@list:deviceStateDesc", dictionaryItemService.getListByDictionaryCode(Constant.DICTIONARY_DEVICE_STATE) .stream().map(DictionaryItem::getName).collect(Collectors.toList()))) @@ -275,24 +280,28 @@ public class DeviceController extends ControllerBase { @Transactional @PostMapping("importForAdd") @ApiMark(moduleName = "设备管理", apiName = "新增导入") - public void importForAdd(HttpServletResponse response, @RequestParam(value = "file") MultipartFile file) throws IOException { + public ApiResult importForAdd(HttpServletResponse response, @RequestParam(value = "file") MultipartFile file) throws IOException { List data = EecExcelUtil.getExcelContext(file.getInputStream(), DeviceAddImportDTO.class); VUtils.trueThrowBusinessError(CollectionUtil.isEmpty(data)).throwMessage("导入文件内容为空"); if (addCheckAndImport(data)) { - response.setContentType("application/json"); - response.setCharacterEncoding("UTF-8"); - byte[] datas = StrUtil.bytes(JSONUtil.toJsonStr(ApiResult.success())); - response.getOutputStream().write(datas, 0, datas.length); + return ApiResult.success(); } else { response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + URLEncoder.encode("设备新增.xlsx", "UTF-8")); - ClassPathResource resource = new ClassPathResource("templates/设备新增导入模板.xlsx"); - new Workbook() - .addSheet(new TemplateSheet(Paths.get(resource.getURI())) - .setData(data) - .setData("@list:deviceStateDesc", dictionaryItemService.getListByDictionaryCode(Constant.DICTIONARY_DEVICE_STATE) - .stream().map(DictionaryItem::getName).collect(Collectors.toList()))) - .writeTo(response.getOutputStream()); + ClassPathResource resource = new ClassPathResource("templates/deviceForAdd.xlsx"); + try(ByteArrayOutputStream osOut = new ByteArrayOutputStream()) { + new Workbook() + .addSheet(new TemplateSheet(resource.getInputStream()) + .setData(data) + .setData("@list:deviceStateDesc", dictionaryItemService.getListByDictionaryCode(Constant.DICTIONARY_DEVICE_STATE) + .stream().map(DictionaryItem::getName).collect(Collectors.toList()))) + .writeTo(osOut); + try(ByteArrayInputStream isIn = new ByteArrayInputStream(osOut.toByteArray())) { + return ApiResult.error(STATE.BusinessError, "导入文件失败",fileUploadService.upload("temp/" +DateTimeUtil.format(LocalDate.now(),"yyyyMMdd")+"/"+ IdUtil.fastUUID() + ".xlsx", isIn)); + } + }catch (Exception e){ + return ApiResult.error(STATE.BusinessError, "保存文件出错"); + } } } @@ -446,7 +455,7 @@ public class DeviceController extends ControllerBase { */ @PostMapping("exportSelect") @ApiMark(moduleName = "设备管理", apiName = "导出选中的设备") - public void exportSelect(HttpServletResponse response,@Valid @RequestBody @NotEmpty List ids) throws IOException { + public void exportSelect(HttpServletResponse response,@Valid @RequestBody @NotEmpty List ids) throws Exception { List devices=deviceService.listByIds(ids); List states = dictionaryItemService.getListByDictionaryCode(Constant.DICTIONARY_DEVICE_STATE); List datas=devices.stream().map(d->{ @@ -470,9 +479,9 @@ public class DeviceController extends ControllerBase { }).collect(Collectors.toList()); response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + URLEncoder.encode("设备更新.xlsx", "UTF-8")); - ClassPathResource resource = new ClassPathResource("templates/设备更新导入模板.xlsx"); + ClassPathResource resource = new ClassPathResource("templates/deviceForUpdate.xlsx"); new Workbook() - .addSheet(new TemplateSheet(Paths.get(resource.getURI())) + .addSheet(new TemplateSheet(resource.getInputStream()) .setData(datas) .setData("@list:deviceStateDesc", dictionaryItemService.getListByDictionaryCode(Constant.DICTIONARY_DEVICE_STATE) .stream().map(DictionaryItem::getName).collect(Collectors.toList()))) @@ -486,24 +495,26 @@ public class DeviceController extends ControllerBase { @Transactional @PostMapping("importForUpdate") @ApiMark(moduleName = "设备管理", apiName = "更新导入") - public void importForUpdate(HttpServletResponse response, @RequestParam(value = "file") MultipartFile file) throws IOException { + public ApiResult importForUpdate(@RequestParam(value = "file") MultipartFile file) throws IOException { List data = EecExcelUtil.getExcelContext(file.getInputStream(), DeviceUpdateImportDTO.class); VUtils.trueThrowBusinessError(CollectionUtil.isEmpty(data)).throwMessage("导入文件内容为空"); if (updateCheckAndImport(data)) { - response.setContentType("application/json"); - response.setCharacterEncoding("UTF-8"); - byte[] datas = StrUtil.bytes(JSONUtil.toJsonStr(ApiResult.success())); - response.getOutputStream().write(datas, 0, datas.length); + return ApiResult.success(); } else { - response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); - response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + URLEncoder.encode("设备更新.xlsx", "UTF-8")); - ClassPathResource resource = new ClassPathResource("templates/设备更新导入模板.xlsx"); - new Workbook() - .addSheet(new TemplateSheet(Paths.get(resource.getURI())) - .setData(data) - .setData("@list:deviceStateDesc", dictionaryItemService.getListByDictionaryCode(Constant.DICTIONARY_DEVICE_STATE) - .stream().map(DictionaryItem::getName).collect(Collectors.toList()))) - .writeTo(response.getOutputStream()); + ClassPathResource resource = new ClassPathResource("templates/deviceForUpdate.xlsx"); + try(ByteArrayOutputStream osOut = new ByteArrayOutputStream()) { + new Workbook() + .addSheet(new TemplateSheet(resource.getInputStream()) + .setData(data) + .setData("@list:deviceStateDesc", dictionaryItemService.getListByDictionaryCode(Constant.DICTIONARY_DEVICE_STATE) + .stream().map(DictionaryItem::getName).collect(Collectors.toList()))) + .writeTo(osOut); + try(ByteArrayInputStream isIn = new ByteArrayInputStream(osOut.toByteArray())) { + return ApiResult.error(STATE.BusinessError, "导入文件失败",fileUploadService.upload("temp/" +DateTimeUtil.format(LocalDate.now(),"yyyyMMdd")+"/"+ IdUtil.fastUUID() + ".xlsx", isIn)); + } + }catch (Exception e){ + return ApiResult.error(STATE.BusinessError, "保存文件出错"); + } } } diff --git a/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/FileController.java b/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/FileController.java index 637d3204..f4fa770c 100644 --- a/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/FileController.java +++ b/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/FileController.java @@ -10,6 +10,7 @@ import com.nflg.mobilebroken.common.exception.NflgException; import com.nflg.mobilebroken.common.pojo.ApiResult; import com.nflg.mobilebroken.common.pojo.PageData; import com.nflg.mobilebroken.common.pojo.request.FileSearchRequest; +import com.nflg.mobilebroken.common.pojo.request.ZipDownloadRequest; import com.nflg.mobilebroken.common.pojo.vo.FileUploadVO; import com.nflg.mobilebroken.common.pojo.vo.FileVO; import com.nflg.mobilebroken.common.util.AdminUserUtil; @@ -191,18 +192,18 @@ public class FileController extends ControllerBase { /** * 文件压缩下载 * - * @param fileIds 文件id列表 + * @param request 请求参数 */ @PostMapping("zipDownload") - public ResponseEntity zipDownload(@Valid @RequestParam @NotNull List fileIds) throws IOException { - List urls = fileUploadRecordService.listByIds(fileIds).stream().map(FileUploadRecord::getUrl).collect(Collectors.toList()); + public ResponseEntity zipDownload(@Valid @RequestBody ZipDownloadRequest request) throws IOException { + List urls = fileUploadRecordService.listByIds(request.getFileIds()).stream().map(FileUploadRecord::getUrl).collect(Collectors.toList()); VUtils.trueThrowBusinessError(CollectionUtil.isEmpty(urls)).throwMessage("没有需要下载的文件"); Path tempZipFile = Files.createTempFile("files", ".zip"); try (FileOutputStream fos = new FileOutputStream(tempZipFile.toFile()); ZipOutputStream zos = new ZipOutputStream(fos)) { for (String fileUrl : urls) { - Request request = new Request.Builder().url(fileUrl).build(); - try (Response response = client.newCall(request).execute()) { + Request r = new Request.Builder().url(fileUrl).build(); + try (Response response = client.newCall(r).execute()) { if (response.isSuccessful() && response.body() != null) { String fileName = fileUrl.substring(fileUrl.lastIndexOf('/') + 1); ZipEntry zipEntry = new ZipEntry(fileName); diff --git a/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/TicketController.java b/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/TicketController.java index 8af8c3ba..ec7128f4 100644 --- a/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/TicketController.java +++ b/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/TicketController.java @@ -726,7 +726,7 @@ public class TicketController extends ControllerBase { .setCreateTime(Instant.now()) .setAttachments(request.getAttachments()) .setImages(request.getImages()) - .setRemindUserIds(request.getRemindUserIds()); + .setRemindUsers(request.getRemindUsers()); if(Objects.nonNull(request.getQuoteId())){ ChatMessageDTO quoteMessage = ticketChatService.getMessage(request.getTicketId(), request.getQuoteId()); message.setQuote(quoteMessage); diff --git a/nflg-mobilebroken-admin/src/main/resources/templates/设备新增导入模板.xlsx b/nflg-mobilebroken-admin/src/main/resources/templates/deviceForAdd.xlsx similarity index 100% rename from nflg-mobilebroken-admin/src/main/resources/templates/设备新增导入模板.xlsx rename to nflg-mobilebroken-admin/src/main/resources/templates/deviceForAdd.xlsx diff --git a/nflg-mobilebroken-admin/src/main/resources/templates/设备更新导入模板.xlsx b/nflg-mobilebroken-admin/src/main/resources/templates/deviceForUpdate.xlsx similarity index 100% rename from nflg-mobilebroken-admin/src/main/resources/templates/设备更新导入模板.xlsx rename to nflg-mobilebroken-admin/src/main/resources/templates/deviceForUpdate.xlsx diff --git a/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TiketController.java b/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TiketController.java index 2e985517..5a3836db 100644 --- a/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TiketController.java +++ b/nflg-mobilebroken-cfs-app/src/main/java/com/nflg/mobilebroken/cfs/controller/TiketController.java @@ -323,7 +323,7 @@ public class TiketController extends ControllerBase { .setCreateTime(Instant.now()) .setAttachments(request.getAttachments()) .setImages(request.getImages()) - .setRemindUserIds(request.getRemindUserIds()); + .setRemindUsers(request.getRemindUsers()); if(Objects.nonNull(request.getQuoteId())){ ChatMessageDTO quoteMessage = ticketChatService.getMessage(request.getTicketId(), request.getQuoteId()); message.setQuote(quoteMessage); diff --git a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/dto/ChatMessageDTO.java b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/dto/ChatMessageDTO.java index 1597608c..b2e7cdd2 100644 --- a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/dto/ChatMessageDTO.java +++ b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/dto/ChatMessageDTO.java @@ -1,6 +1,7 @@ package com.nflg.mobilebroken.common.pojo.dto; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.nflg.mobilebroken.common.pojo.request.RemindUserRequest; import com.nflg.mobilebroken.common.pojo.vo.FileUploadVO; import lombok.Data; import lombok.experimental.Accessors; @@ -45,6 +46,6 @@ public class ChatMessageDTO { // 引用的消息 private ChatMessageDTO quote; - //被艾特的用户id列表 - private List remindUserIds; + //被艾特的用户列表 + private List remindUsers; } diff --git a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/AddChatMessageRequest.java b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/AddChatMessageRequest.java index 5df56477..3d496eb7 100644 --- a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/AddChatMessageRequest.java +++ b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/AddChatMessageRequest.java @@ -25,6 +25,6 @@ public class AddChatMessageRequest { // 引用的消息 private String quoteId; - //被艾特的用户id列表 - private List remindUserIds; + //被艾特的用户列表 + private List remindUsers; } diff --git a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/RemindUserRequest.java b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/RemindUserRequest.java new file mode 100644 index 00000000..cc2cbb7b --- /dev/null +++ b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/RemindUserRequest.java @@ -0,0 +1,17 @@ +package com.nflg.mobilebroken.common.pojo.request; + +import lombok.Data; + +@Data +public class RemindUserRequest { + + /** + * 用户id + */ + private Integer id; + + /** + * 用户名 + */ + private String name; +} diff --git a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/ZipDownloadRequest.java b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/ZipDownloadRequest.java new file mode 100644 index 00000000..fc009cc2 --- /dev/null +++ b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/request/ZipDownloadRequest.java @@ -0,0 +1,18 @@ +package com.nflg.mobilebroken.common.pojo.request; + +import lombok.Data; + +import javax.validation.Valid; +import javax.validation.constraints.NotEmpty; +import java.util.List; + +@Valid +@Data +public class ZipDownloadRequest { + + /** + * 文件id列表 + */ + @NotEmpty + private List fileIds; +} diff --git a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/vo/FileUploadVO.java b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/vo/FileUploadVO.java index a420aad2..46867b4a 100644 --- a/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/vo/FileUploadVO.java +++ b/nflg-mobilebroken-common/src/main/java/com/nflg/mobilebroken/common/pojo/vo/FileUploadVO.java @@ -2,11 +2,13 @@ package com.nflg.mobilebroken.common.pojo.vo; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import lombok.experimental.Accessors; @Data @Accessors(chain = true) @AllArgsConstructor +@NoArgsConstructor public class FileUploadVO { /** diff --git a/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/service/FileUploadService.java b/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/service/FileUploadService.java index 7694fcb5..c0d944bd 100644 --- a/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/service/FileUploadService.java +++ b/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/service/FileUploadService.java @@ -3,6 +3,7 @@ package com.nflg.mobilebroken.starter.service; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.io.InputStream; public interface FileUploadService { @@ -14,4 +15,13 @@ public interface FileUploadService { * @throws IOException */ String upload(String filePath,MultipartFile file) throws IOException; + + /** + * 上传文件 + * @param filePath 文件完整路径,如: /cfs/xxx.jpg + * @param stream 文件流 + * @return 可访问的url + * @throws IOException + */ + String upload(String filePath, InputStream stream) throws IOException; } \ No newline at end of file diff --git a/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/service/impl/OSSFileUploadService.java b/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/service/impl/OSSFileUploadService.java index 30e4b082..d7e9fe1a 100644 --- a/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/service/impl/OSSFileUploadService.java +++ b/nflg-mobilebroken-starter/src/main/java/com/nflg/mobilebroken/starter/service/impl/OSSFileUploadService.java @@ -13,6 +13,7 @@ import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import java.io.IOException; +import java.io.InputStream; @Service @ConditionalOnProperty(name = "file.upload.type", havingValue = "oss") @@ -30,8 +31,13 @@ public class OSSFileUploadService implements FileUploadService { @Override public String upload(String filePath, MultipartFile file) throws IOException { + return upload(filePath, file.getInputStream()); + } + + @Override + public String upload(String filePath, InputStream stream) { // 上传文件到OSS - PutObjectResult result = ossClient.putObject(new PutObjectRequest(bucketName, filePath, file.getInputStream())); + PutObjectResult result = ossClient.putObject(new PutObjectRequest(bucketName, filePath, stream)); //log.debug("上传文件结果: " + result); return StrUtil.format("{}/{}",domain, filePath); }