Pārlūkot izejas kodu

电子档案接口代码全部提交

fuyuchuan 1 dienu atpakaļ
vecāks
revīzija
04cc7c9c33

+ 16 - 0
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/controller/web/ArchiveController.java

@@ -6,8 +6,12 @@ import com.usky.vpp.service.VppArchiveService;
 import com.usky.vpp.service.vo.VppFileArchiveRequestVO;
 import com.usky.vpp.service.vo.VppFileArchiveResponseVO;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
 /**
  * 虚拟电厂 - Archive 接口
  * 网关前缀: /prod-api/service-vpp
@@ -64,4 +68,16 @@ public class ArchiveController {
     public ApiResult<Boolean> delete(@PathVariable("id") Long id) {
         return vppArchiveService.delete(id) ? ApiResult.success(true) : ApiResult.error("删除电子档案失败!请重试!");
     }
+
+    /**
+     * 批量导出电子档案
+     * 根据主键ID数组查询档案,下载文件并打包为ZIP返回
+     * 请求示例: GET /archive/export?ids=1&ids=2&ids=3
+     *
+     * @param ids 主键ID列表
+     */
+    @GetMapping("/export")
+    public void export(@RequestParam("ids") List<Long> ids, HttpServletResponse response) {
+        vppArchiveService.export(ids, response);
+    }
 }

+ 1 - 1
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/job/VppUnPollScheduler.java

@@ -22,7 +22,7 @@ public class VppUnPollScheduler {
     @Autowired
     private VppUnIntegrationService integrationService;
 
-    //@Scheduled(fixedDelayString = "#{${vpp.un.poll-interval-sec:10} * 1000}")
+    @Scheduled(fixedDelayString = "#{${vpp.un.poll-interval-sec:10} * 1000}")
     public void pollUn() {
         if (!properties.isPollActive()) {
             return;

+ 10 - 0
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/VppArchiveService.java

@@ -3,7 +3,10 @@ package com.usky.vpp.service;
 import com.usky.common.core.bean.CommonPage;
 import com.usky.vpp.service.vo.VppFileArchiveRequestVO;
 import com.usky.vpp.service.vo.VppFileArchiveResponseVO;
+import org.springframework.http.ResponseEntity;
 
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -22,4 +25,11 @@ public interface VppArchiveService {
     Boolean upload(VppFileArchiveRequestVO vo);
 
     Boolean delete(Long id);
+
+    /**
+     * 批量导出电子档案,根据主键ID列表下载文件并打包为ZIP
+     * @param ids 主键ID列表
+     * @return ResponseEntity 包装的 ZIP 字节数组
+     */
+    void export(List<Long> ids, HttpServletResponse response);
 }

+ 144 - 0
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/impl/VppArchiveServiceImpl.java

@@ -14,10 +14,28 @@ import com.usky.vpp.service.vo.VppFileArchiveRequestVO;
 import com.usky.vpp.service.vo.VppFileArchiveResponseVO;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
 
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 
 /**
  * VppArchiveService 默认实现(待按业务完善)
@@ -96,6 +114,7 @@ public class VppArchiveServiceImpl implements VppArchiveService {
             responseVO.setUpdateTime(entity.getUpdateTime());
             responseVO.setUpdatedBy(entity.getUpdatedBy());
             responseVO.setVersion(entity.getVersion());
+            responseVO.setTenantId(entity.getTenantId());
             // TODO 查询站点名称
             // responseVO.setSiteName();
             return responseVO;
@@ -184,6 +203,131 @@ public class VppArchiveServiceImpl implements VppArchiveService {
         return i > 0;
     }
 
+    @Override
+    public void export(List<Long> ids, HttpServletResponse response) {
+        // ========== 参数校验 ==========
+        if (ids == null || ids.isEmpty()) {
+            writeErrorResponse(response, HttpStatus.BAD_REQUEST.value(), "请选择要导出的档案!");
+            return;
+        }
+
+        // ========== 查询档案记录 ==========
+        LambdaQueryWrapper<VppFileArchive> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.in(VppFileArchive::getId, ids)
+                .eq(VppFileArchive::getDeleteFlag, 0)
+                .eq(VppFileArchive::getTenantId, SecurityUtils.getTenantId());
+        List<VppFileArchive> archives = vppFileArchiveMapper.selectList(queryWrapper);
+
+        if (archives == null || archives.isEmpty()) {
+            writeErrorResponse(response, HttpStatus.BAD_REQUEST.value(), "没有找到有效的档案记录!");
+            return;
+        }
+
+        // ========== 构造 ZIP 文件名 ==========
+        String zipFileName = "电子档案_"
+                + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))
+                + ".zip";
+
+        // ========== 设置响应头(必须在写入流之前)==========
+        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
+        response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
+                "attachment; filename=" + encodeFileName(zipFileName));
+
+        // ========== 生成 ZIP 并写入响应流 ==========
+        RestTemplate restTemplate = new RestTemplate();
+        Set<String> usedNames = new HashSet<>();
+
+        try (ServletOutputStream sos = response.getOutputStream();
+             ZipOutputStream zos = new ZipOutputStream(sos)) {
+
+            for (VppFileArchive archive : archives) {
+                String fileUrl = archive.getFileUrl();
+                String fileName = archive.getFileName();
+
+                if (StringUtils.isBlank(fileUrl) || StringUtils.isBlank(fileName)) {
+                    continue;
+                }
+
+                String zipEntryName = resolveDuplicateName(fileName, usedNames);
+                usedNames.add(zipEntryName);
+
+                try {
+                    byte[] fileBytes = restTemplate.getForObject(fileUrl, byte[].class);
+                    if (fileBytes != null && fileBytes.length > 0) {
+                        ZipEntry zipEntry = new ZipEntry(zipEntryName);
+                        zos.putNextEntry(zipEntry);
+                        zos.write(fileBytes);
+                        zos.closeEntry();
+                    }
+                } catch (Exception e) {
+                    ZipEntry errorEntry = new ZipEntry(zipEntryName + ".error.txt");
+                    zos.putNextEntry(errorEntry);
+                    zos.write(("下载失败: " + e.getMessage()).getBytes(StandardCharsets.UTF_8));
+                    zos.closeEntry();
+                }
+            }
+            zos.finish();
+
+        } catch (Exception e) {
+            // 如果响应头还没提交,可以返回错误信息
+            if (!response.isCommitted()) {
+                writeErrorResponse(response, HttpStatus.INTERNAL_SERVER_ERROR.value(),
+                        "导出电子档案失败: " + e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * 处理 ZIP 内重名文件,自动添加编号后缀
+     * @param fileName  原始文件名
+     * @param usedNames 已使用的文件名集合
+     * @return 去重后的文件名
+     */
+    private String resolveDuplicateName(String fileName, Set<String> usedNames) {
+        if (!usedNames.contains(fileName)) {
+            return fileName;
+        }
+        String baseName = fileName;
+        String extension = "";
+        int dotIndex = fileName.lastIndexOf('.');
+        if (dotIndex > 0) {
+            baseName = fileName.substring(0, dotIndex);
+            extension = fileName.substring(dotIndex);
+        }
+        int counter = 1;
+        String newName;
+        do {
+            newName = baseName + "(" + counter + ")" + extension;
+            counter++;
+        } while (usedNames.contains(newName));
+        return newName;
+    }
+
+    /**
+     * 向响应中写入 JSON 错误信息
+     */
+    private void writeErrorResponse(HttpServletResponse response, int status, String message) {
+        response.setStatus(status);
+        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
+        try {
+            response.getWriter().write("{\"code\":\"" + status + "\",\"msg\":\"" + message + "\"}");
+        } catch (IOException e) {
+            // 忽略写入异常
+        }
+    }
+
+    /**
+     * 编码文件名,兼容各浏览器
+     */
+    private String encodeFileName(String fileName) {
+        try {
+            return URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()).replace("+", "%20");
+        } catch (UnsupportedEncodingException e) {
+            return fileName;
+        }
+    }
+
     /**
      * 文件大小格式转换工具方法
      * 按1024进制自动递进换算单位(KB → MB → GB → TB)