Sfoglia il codice sorgente

Merge branch 'fyc-vpp' of uskycloud/usky-modules into feature/service-vpp-20260701

hanzhengyi 21 ore fa
parent
commit
ecb3d2845b

+ 6 - 0
service-vpp/service-vpp-biz/pom.xml

@@ -85,6 +85,12 @@
             <artifactId>taos-jdbcdriver</artifactId>
             <version>3.6.3</version>
         </dependency>
+
+        <dependency>
+            <groupId>com.usky</groupId>
+            <artifactId>service-file</artifactId>
+            <version>0.0.1</version>
+        </dependency>
     </dependencies>
 
     <build>

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

@@ -22,7 +22,7 @@ import java.net.UnknownHostException;
  */
 @EnableCustomSwagger2
 @EnableFeignClients(basePackages = "com.usky")
-@MapperScan(value = "com.usky.vpp.mapper")
+@MapperScan({"com.usky.vpp.mapper","com.ruoyi.file.mapper"})
 @ComponentScan("com.usky")
 @SpringBootApplication
 @EnableScheduling

+ 59 - 7
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/controller/web/ArchiveController.java

@@ -1,10 +1,17 @@
 package com.usky.vpp.controller.web;
 
 import com.usky.common.core.bean.ApiResult;
+import com.usky.common.core.bean.CommonPage;
 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
@@ -16,16 +23,61 @@ public class ArchiveController {
     @Autowired
     private VppArchiveService vppArchiveService;
 
+    /**
+     * 创建电子档案
+     */
+    @PostMapping
+    public ApiResult<Boolean> create(@RequestBody VppFileArchiveRequestVO vo) {
+        return vppArchiveService.create(vo) ? ApiResult.success(true) : ApiResult.error("创建电子档案失败!请重试!");
+    }
+
+    /**
+     * 查询电子档案
+     * archiveName 档案名称 模糊匹配
+     * archiveType 档案类型 1图纸 2文档 3设计稿 4验收报告 5其他
+     * siteId 站点id
+     * bizType 业务类型
+     * bizId 业务主键
+     * pageNum 页码
+     * pageSize 页大小
+     */
     @GetMapping
-    public ApiResult<Object> page() {
-        return ApiResult.success(null);
+    public ApiResult<CommonPage<VppFileArchiveResponseVO>> page(@RequestParam(value = "archiveName", required = false) String archiveName,
+                                                                @RequestParam(value = "archiveType", required = false) Integer archiveType,
+                                                                @RequestParam(value = "siteId", required = false) Long siteId,
+                                                                @RequestParam(value = "bizType", required = false) String bizType,
+                                                                @RequestParam(value = "bizId", required = false) Long bizId,
+                                                                @RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum,
+                                                                @RequestParam(value = "pageSize", required = false, defaultValue = "20") Integer pageSize) {
+        return ApiResult.success(vppArchiveService.page(archiveName, archiveType, siteId, bizType, bizId, pageNum, pageSize));
     }
-    @PostMapping
-    public ApiResult<Object> upload() {
-        return ApiResult.success(null);
+
+    /**
+     * 更新电子档案
+     * version 字段自增,每次更新+0.1
+     */
+    @PutMapping
+    public ApiResult<Boolean> upload(@RequestBody VppFileArchiveRequestVO vo) {
+        return vppArchiveService.upload(vo) ? ApiResult.success(true) : ApiResult.error("更新电子档案失败!请重试!");
     }
+
+    /**
+     * 删除电子档案
+     */
     @DeleteMapping(value = "/{id}")
-    public ApiResult<Void> delete(@PathVariable("id") Long id) {
-        return ApiResult.success();
+    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);
     }
 }

+ 7 - 0
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/domain/VppFileArchive.java

@@ -8,6 +8,8 @@ import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
 import java.io.Serializable;
 import java.time.LocalDateTime;
 
@@ -30,21 +32,25 @@ public class VppFileArchive implements Serializable {
     /**
      * 档案名称
      */
+    @NotBlank(message = "档案名称不能为空!")
     private String archiveName;
 
     /**
      * 原始文件名
      */
+    @NotBlank(message = "原始文件名不能为空!")
     private String fileName;
 
     /**
      * 存储路径
      */
+    @NotBlank(message = "文件路径不能为空!")
     private String fileUrl;
 
     /**
      * 档案类型 1图纸 2文档 3设计稿 4验收报告 5其他
      */
+    @NotNull(message = "请选择档案类型!")
     private Integer archiveType;
 
     /**
@@ -70,6 +76,7 @@ public class VppFileArchive implements Serializable {
     /**
      * 站点id
      */
+    @NotNull(message = "站点不能为空!")
     private Long siteId;
 
     /**

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

@@ -1,7 +1,12 @@
 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;
 
 /**
@@ -12,4 +17,19 @@ public interface VppArchiveService {
     default Object stub(String action, Map<String, Object> params) {
         return null;
     }
+
+    Boolean create(VppFileArchiveRequestVO vo);
+
+    CommonPage<VppFileArchiveResponseVO> page(String archiveName, Integer archiveType, Long siteId, String bizType, Long bizId, Integer pageNum, Integer pageSize);
+
+    Boolean upload(VppFileArchiveRequestVO vo);
+
+    Boolean delete(Long id);
+
+    /**
+     * 批量导出电子档案,根据主键ID列表下载文件并打包为ZIP
+     * @param ids 主键ID列表
+     * @return ResponseEntity 包装的 ZIP 字节数组
+     */
+    void export(List<Long> ids, HttpServletResponse response);
 }

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

@@ -1,11 +1,350 @@
 package com.usky.vpp.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.file.domain.FilesUpload;
+import com.ruoyi.file.mapper.FilesMapper;
+import com.usky.common.core.bean.CommonPage;
+import com.usky.common.security.utils.SecurityUtils;
+import com.usky.vpp.domain.VppFileArchive;
+import com.usky.vpp.mapper.VppFileArchiveMapper;
 import com.usky.vpp.service.VppArchiveService;
+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 默认实现(待按业务完善)
  */
 @Service
 public class VppArchiveServiceImpl implements VppArchiveService {
+
+    @Autowired
+    private FilesMapper filesMapper;
+    @Autowired
+    private VppFileArchiveMapper vppFileArchiveMapper;
+
+    @Override
+    public Boolean create(VppFileArchiveRequestVO vo) {
+        String username = SecurityUtils.getUsername();
+        Integer tenantId = SecurityUtils.getTenantId();
+
+        LambdaQueryWrapper<FilesUpload> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(FilesUpload::getFilesName, vo.getName())
+                .eq(FilesUpload::getUrl, vo.getUrl())
+                //.eq(FilesUpload::getEnable, "1")
+                .eq(FilesUpload::getIsDelete, 0);
+        FilesUpload filesUpload = filesMapper.selectOne(queryWrapper);
+
+        VppFileArchive vppFileArchive = new VppFileArchive();
+        vppFileArchive.setArchiveName(vo.getArchiveName());
+        vppFileArchive.setArchiveType(vo.getArchiveType());
+        vppFileArchive.setFileName(filesUpload.getFilesName());
+        vppFileArchive.setFileUrl(filesUpload.getUrl());
+        vppFileArchive.setFileType(filesUpload.getType());
+        vppFileArchive.setFileSize(filesUpload.getSize());
+        vppFileArchive.setBizId(vo.getBizId() != null ? vo.getBizId() : 0L);
+        vppFileArchive.setBizType(StringUtils.isNotBlank(vo.getBizType()) ? vo.getBizType() : "");
+        vppFileArchive.setSiteId(vo.getSiteId());
+        vppFileArchive.setCreateTime(LocalDateTime.now());
+        vppFileArchive.setCreatedBy(username);
+        vppFileArchive.setVersion("V1.0");
+        vppFileArchive.setTenantId(tenantId);
+        vppFileArchive.setRemark(StringUtils.isNotBlank(vo.getRemark()) ? vo.getRemark() : "");
+        vppFileArchive.setDeleteFlag(0);
+        int insert = vppFileArchiveMapper.insert(vppFileArchive);
+
+        return insert > 0;
+    }
+
+    @Override
+    public CommonPage<VppFileArchiveResponseVO> page(String archiveName, Integer archiveType, Long siteId, String bizType, Long bizId, Integer pageNum, Integer pageSize) {
+        IPage<VppFileArchive> page = new Page<>(pageNum, pageSize);
+
+        LambdaQueryWrapper<VppFileArchive> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(VppFileArchive::getDeleteFlag, 0)
+                .eq(VppFileArchive::getTenantId, SecurityUtils.getTenantId())
+                .eq(siteId != null, VppFileArchive::getSiteId, siteId)
+                .eq(archiveType != null, VppFileArchive::getArchiveType, archiveType)
+                .eq(StringUtils.isNotBlank(bizType), VppFileArchive::getBizType, bizType)
+                .eq(bizId != null, VppFileArchive::getBizId, bizId)
+                .like(StringUtils.isNotBlank(archiveName), VppFileArchive::getArchiveName, archiveName)
+                .orderByDesc(VppFileArchive::getCreateTime);
+        page = vppFileArchiveMapper.selectPage(page, queryWrapper);
+
+        List<VppFileArchiveResponseVO> list = page.getRecords().stream().map(entity -> {
+            VppFileArchiveResponseVO responseVO = new VppFileArchiveResponseVO();
+            responseVO.setId(entity.getId());
+            responseVO.setArchiveName(entity.getArchiveName());
+            responseVO.setFileName(entity.getFileName());
+            responseVO.setFileUrl(entity.getFileUrl());
+            responseVO.setArchiveType(entity.getArchiveType());
+            responseVO.setFileType(entity.getFileType());
+            responseVO.setFileSize(convertFileSize(entity.getFileSize()));
+            responseVO.setBizType(entity.getBizType());
+            responseVO.setBizId(entity.getBizId());
+            responseVO.setSiteId(entity.getSiteId());
+            responseVO.setRemark(entity.getRemark());
+            responseVO.setCreatedBy(entity.getCreatedBy());
+            responseVO.setCreateTime(entity.getCreateTime());
+            responseVO.setUpdateTime(entity.getUpdateTime());
+            responseVO.setUpdatedBy(entity.getUpdatedBy());
+            responseVO.setVersion(entity.getVersion());
+            responseVO.setTenantId(entity.getTenantId());
+            // TODO 查询站点名称
+            // responseVO.setSiteName();
+            return responseVO;
+        }).collect(java.util.stream.Collectors.toList());
+
+        return new CommonPage<>(list, page.getTotal(), pageSize, pageNum);
+    }
+
+    @Override
+    public Boolean upload(VppFileArchiveRequestVO vo) {
+        if (vo.getId() == null) {
+            throw new RuntimeException("主键ID不能为空!");
+        }
+
+        LambdaQueryWrapper<VppFileArchive> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(VppFileArchive::getId, vo.getId())
+                .eq(VppFileArchive::getTenantId, SecurityUtils.getTenantId())
+                .eq(VppFileArchive::getDeleteFlag, 0);
+        VppFileArchive existingRecord = vppFileArchiveMapper.selectOne(queryWrapper);
+
+        if (existingRecord == null) {
+            throw new RuntimeException("档案记录不存在或已被删除!");
+        }
+
+        // 根据 name + url 关联 FilesUpload 表,同步更新文件信息
+        if (StringUtils.isNotBlank(vo.getName()) && StringUtils.isNotBlank(vo.getUrl())) {
+            LambdaQueryWrapper<FilesUpload> fileQueryWrapper = new LambdaQueryWrapper<>();
+            fileQueryWrapper.eq(FilesUpload::getFilesName, vo.getName())
+                    .eq(FilesUpload::getUrl, vo.getUrl())
+                    .eq(FilesUpload::getIsDelete, 0);
+            FilesUpload filesUpload = filesMapper.selectOne(fileQueryWrapper);
+
+            if (filesUpload != null) {
+                existingRecord.setFileName(filesUpload.getFilesName());
+                existingRecord.setFileUrl(filesUpload.getUrl());
+                existingRecord.setFileType(filesUpload.getType());
+                existingRecord.setFileSize(filesUpload.getSize());
+            } else {
+                throw new RuntimeException("未找到匹配的文件记录,请确认文件名与路径是否正确!");
+            }
+        }
+
+        // 仅更新非空字段
+        if (StringUtils.isNotBlank(vo.getArchiveName())) {
+            existingRecord.setArchiveName(vo.getArchiveName());
+        }
+        if (vo.getArchiveType() != null) {
+            existingRecord.setArchiveType(vo.getArchiveType());
+        }
+        if (StringUtils.isNotBlank(vo.getRemark())) {
+            existingRecord.setRemark(vo.getRemark());
+        }
+        if (vo.getSiteId() != null) {
+            existingRecord.setSiteId(vo.getSiteId());
+        }
+
+        // 版本号自增 +0.1
+        String currentVersion = existingRecord.getVersion();
+        if (StringUtils.isNotBlank(currentVersion) && currentVersion.startsWith("V")) {
+            try {
+                double versionNum = Double.parseDouble(currentVersion.substring(1));
+                versionNum += 0.1;
+                existingRecord.setVersion("V" + String.format("%.1f", versionNum));
+            } catch (NumberFormatException e) {
+                existingRecord.setVersion(currentVersion + ".1");
+            }
+        }
+
+        existingRecord.setUpdateTime(LocalDateTime.now());
+        existingRecord.setUpdatedBy(SecurityUtils.getUsername());
+
+        int update = vppFileArchiveMapper.updateById(existingRecord);
+        return update > 0;
+    }
+
+    @Override
+    public Boolean delete(Long id) {
+        LambdaQueryWrapper<VppFileArchive> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(VppFileArchive::getId, id)
+                .eq(VppFileArchive::getTenantId, SecurityUtils.getTenantId());
+        VppFileArchive vppFileArchive = vppFileArchiveMapper.selectOne(queryWrapper);
+        vppFileArchive.setDeleteFlag(1);
+        vppFileArchive.setDeletedAt(LocalDateTime.now());
+        vppFileArchive.setUpdatedBy(SecurityUtils.getUsername());
+        int i = vppFileArchiveMapper.updateById(vppFileArchive);
+        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)
+     * @param size 数值型文件大小
+     * @return 带单位后缀的格式化字符串
+     */
+    private String convertFileSize(Double size) {
+        if (size == null || size <= 0) {
+            return "0 B";
+        }
+        final String[] units = {"B", "KB", "MB", "GB", "TB"};
+        int unitIndex = 0;
+        double result = size;
+        while (result >= 1024 && unitIndex < units.length - 1) {
+            result /= 1024;
+            unitIndex++;
+        }
+        return String.format("%.2f %s", result, units[unitIndex]);
+    }
 }

+ 57 - 2
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/vo/VppFileArchiveRequestVO.java

@@ -13,15 +13,70 @@ import javax.validation.constraints.NotBlank;
 @Data
 public class VppFileArchiveRequestVO {
 
+    /**
+     * 主键
+     */
+    private Long id;
+
     /**
      * 文件名
      */
-    @NotBlank(message = "文件名称不能为空")
     private String name;
 
     /**
      * 文件路径
      */
-    @NotBlank(message = "文件路径不能为空")
     private String url;
+
+    /**
+     * 站点主键ID
+     */
+    private Long siteId;
+
+    /**
+     * 档案名称
+     */
+    private String archiveName;
+
+    /**
+     * 档案类型 1图纸 2文档 3设计稿 4验收报告 5其他
+     */
+    private Integer archiveType;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 业务类型
+     */
+    private String bizType;
+
+    /**
+     * 业务主键
+     */
+    private Long bizId;
+
+    /**
+     * 页码
+     */
+    private Integer pageNum;
+
+    /**
+     * 页大小
+     */
+    private Integer pageSize;
+
+    /**
+     * 校验档案类型是否合法
+     * 合法值:1图纸 2文档 3设计稿 4验收报告 5其他
+     * @return true-合法 false-非法
+     */
+    public boolean isValidArchiveType() {
+        if (this.archiveType == null) {
+            return false;
+        }
+        return this.archiveType >= 1 && this.archiveType <= 5;
+    }
 }

+ 124 - 0
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/vo/VppFileArchiveResponseVO.java

@@ -0,0 +1,124 @@
+package com.usky.vpp.service.vo;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+/**
+ *
+ * @author fyc
+ * @email yuchuan.fu@chinausky.com
+ * @date 2026/7/1
+ */
+@Data
+public class VppFileArchiveResponseVO {
+
+    /**
+     * 主键
+     */
+    private Long id;
+
+    /**
+     * 档案名称
+     */
+    private String archiveName;
+
+    /**
+     * 原始文件名
+     */
+    private String fileName;
+
+    /**
+     * 存储路径
+     */
+    private String fileUrl;
+
+    /**
+     * 档案类型 1图纸 2文档 3设计稿 4验收报告 5其他
+     */
+    private Integer archiveType;
+
+    /**
+     * 文件类型
+     */
+    private String fileType;
+
+    /**
+     * 文件大小kb
+     */
+    private String fileSize;
+
+    /**
+     * 业务类型
+     */
+    private String bizType;
+
+    /**
+     * 业务主键
+     */
+    private Long bizId;
+
+    /**
+     * 站点id
+     */
+    private Long siteId;
+
+    /**
+     * 站点名称
+     */
+    private String siteName;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 上传人
+     */
+    private String createdBy;
+
+    /**
+     * 创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+
+    /**
+     * 更新人
+     */
+    private String updatedBy;
+
+    /**
+     * 软删除标识 0未删除 1已删除
+     */
+    private Integer isDelete;
+
+    /**
+     * 软删除时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime deleteTime;
+
+    /**
+     * 版本号 默认V1.0
+     */
+    private String version;
+
+    /**
+     * 租户id
+     */
+    private Integer tenantId;
+}