From 4d9450919dccd9b20d4fc21f9288148c9257da61 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 28 四月 2026 10:19:25 +0800
Subject: [PATCH] feat(ai): 添加审批待办助手功能

---
 src/main/java/com/ruoyi/basic/utils/FileUtil.java |  338 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 305 insertions(+), 33 deletions(-)

diff --git a/src/main/java/com/ruoyi/basic/utils/FileUtil.java b/src/main/java/com/ruoyi/basic/utils/FileUtil.java
index bf0b9b2..0e3d10d 100644
--- a/src/main/java/com/ruoyi/basic/utils/FileUtil.java
+++ b/src/main/java/com/ruoyi/basic/utils/FileUtil.java
@@ -1,7 +1,10 @@
 package com.ruoyi.basic.utils;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.basic.dto.StorageAttachmentDTO;
 import com.ruoyi.basic.dto.StorageAttachmentVO;
 import com.ruoyi.basic.dto.StorageBlobDTO;
 import com.ruoyi.basic.dto.StorageBlobVO;
@@ -52,9 +55,6 @@
      * @param storageBlobDTOS 鏂囦欢淇℃伅
      */
     public void saveStorageAttachment(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, List<StorageBlobDTO> storageBlobDTOS) {
-        if (CollectionUtils.isEmpty(storageBlobDTOS)) {
-            throw new RuntimeException("鏂囦欢淇℃伅涓嶈兘涓虹┖");
-        }
         if (application == null) {
             throw new RuntimeException("鏂囦欢鐢ㄩ�斾笉鑳戒负绌�");
         }
@@ -66,10 +66,54 @@
         }
         // 鍒犻櫎鏃ч檮浠朵俊鎭�
         deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
+        if (CollectionUtils.isEmpty(storageBlobDTOS)) {
+            return;
+        }
         List<StorageAttachment> storageAttachments = new ArrayList<>();
         for (StorageBlobDTO storageBlobDTO : storageBlobDTOS) {
             StorageAttachment storageAttachment = new StorageAttachment();
             storageAttachment.setApplication(application.getType());
+            storageAttachment.setRecordType(recordType.getType());
+            storageAttachment.setRecordId(recordId);
+            storageAttachment.setStorageBlobId(storageBlobDTO.getId());
+            storageAttachment.setDeleted(0L);
+            storageAttachments.add(storageAttachment);
+        }
+        storageAttachmentMapper.insert(storageAttachments);
+    }
+
+    /**
+     * 淇濆瓨闄勪欢淇℃伅
+     *
+     * @param recordType      鍏宠仈璁板綍绫诲瀷
+     * @param recordId        鍏宠仈璁板綍id
+     * @param storageBlobDTOS 鏂囦欢淇℃伅
+     */
+    public void saveStorageAttachmentByRecordTypeAndRecordId(String application,RecordTypeEnum recordType, Long recordId, List<StorageBlobDTO> storageBlobDTOS) {
+        if (recordType == null) {
+            throw new RuntimeException("鍏宠仈璁板綍绫诲瀷涓嶈兘涓虹┖");
+        }
+        if (recordId == null) {
+            throw new RuntimeException("鍏宠仈璁板綍id涓嶈兘涓虹┖");
+        }
+
+        // 鍒犻櫎鏃ч檮浠朵俊鎭�
+        if (application == null) {
+            for (StorageBlobDTO storageBlobDTO : storageBlobDTOS) {
+                deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.getByType(storageBlobDTO.getApplication()), recordType, recordId);
+            }
+        } else {
+            deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.getByType(application), recordType, recordId);
+        }
+
+        if (CollectionUtils.isEmpty(storageBlobDTOS)) {
+            deleteStorageAttachmentsByRecordTypeAndRecordId(recordType, recordId);
+        }
+
+        List<StorageAttachment> storageAttachments = new ArrayList<>();
+        for (StorageBlobDTO storageBlobDTO : storageBlobDTOS) {
+            StorageAttachment storageAttachment = new StorageAttachment();
+            storageAttachment.setApplication(Objects.requireNonNullElseGet(application, () -> ApplicationTypeEnum.getByType(storageBlobDTO.getApplication()).getType()));
             storageAttachment.setRecordType(recordType.getType());
             storageAttachment.setRecordId(recordId);
             storageAttachment.setStorageBlobId(storageBlobDTO.getId());
@@ -104,10 +148,10 @@
      *
      * @param application 鏂囦欢鐢ㄩ��
      * @param recordType  鍏宠仈璁板綍绫诲瀷
-     * @param recordId    鍏宠仈璁板綍id
+     * @param recordIds    鍏宠仈璁板綍id
      */
-    public void deleteStorageBlobsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId) {
-        if (recordId == null || recordId <= 0) {
+    public void deleteStorageBlobsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum application, RecordTypeEnum recordType, List<Long> recordIds) {
+        if (recordIds == null || recordIds.isEmpty()) {
             throw new RuntimeException("鍏宠仈璁板綍id涓嶈兘涓虹┖");
         }
         if (application == null) {
@@ -118,8 +162,31 @@
         }
         List<StorageAttachment> storageAttachments = storageAttachmentMapper.selectList(new LambdaQueryWrapper<StorageAttachment>()
                 .eq(StorageAttachment::getRecordType, recordType.getType())
-                .eq(StorageAttachment::getRecordId, recordId)
+                .in(StorageAttachment::getRecordId, recordIds)
                 .eq(StorageAttachment::getApplication, application.getType()));
+        if (CollectionUtils.isNotEmpty(storageAttachments)) {
+            List<Long> storageAttachmentIds = storageAttachments.stream().map(StorageAttachment::getStorageBlobId)
+                    .collect(Collectors.toList());
+            deleteStorageBlobsByStorageAttachmentIds(storageAttachmentIds);
+        }
+    }
+
+    /**
+     * 閫氳繃鍏宠仈璁板綍绫诲瀷銆佸叧鑱旇褰昳d鍒犻櫎鏂囦欢淇℃伅
+     *
+     * @param recordType  鍏宠仈璁板綍绫诲瀷
+     * @param recordId    鍏宠仈璁板綍id
+     */
+    public void deleteStorageBlobsByRecordTypeAndRecordId(RecordTypeEnum recordType, Long recordId) {
+        if (recordId == null || recordId <= 0) {
+            throw new RuntimeException("鍏宠仈璁板綍id涓嶈兘涓虹┖");
+        }
+        if (recordType == null) {
+            throw new RuntimeException("鍏宠仈璁板綍绫诲瀷涓嶈兘涓虹┖");
+        }
+        List<StorageAttachment> storageAttachments = storageAttachmentMapper.selectList(new LambdaQueryWrapper<StorageAttachment>()
+                .eq(StorageAttachment::getRecordType, recordType.getType())
+                .eq(StorageAttachment::getRecordId, recordId));
         if (CollectionUtils.isNotEmpty(storageAttachments)) {
             List<Long> storageAttachmentIds = storageAttachments.stream().map(StorageAttachment::getStorageBlobId)
                     .collect(Collectors.toList());
@@ -137,6 +204,13 @@
         storageAttachmentMapper.deleteByIds(storageAttachmentIds);
     }
 
+    /**
+     * 鍒犻櫎鏂囦欢鍏宠仈淇℃伅
+     *
+     * @param application 鏂囦欢鐢ㄩ��
+     * @param recordType  鍏宠仈璁板綍绫诲瀷
+     * @param recordId    鍏宠仈璁板綍id
+     */
     public void deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId) {
         if (recordId == null || recordId <= 0) {
             throw new RuntimeException("鍏宠仈璁板綍id涓嶈兘涓虹┖");
@@ -147,10 +221,53 @@
         if (recordType == null) {
             throw new RuntimeException("鍏宠仈璁板綍绫诲瀷涓嶈兘涓虹┖");
         }
-        deleteStorageBlobsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
+        deleteStorageBlobsByApplicationAndRecordTypeAndRecordIds(application, recordType, Arrays.asList(recordId));
         storageAttachmentMapper.delete(new LambdaQueryWrapper<StorageAttachment>()
                 .eq(StorageAttachment::getRecordType, recordType.getType())
                 .eq(StorageAttachment::getRecordId, recordId)
+                .eq(StorageAttachment::getApplication, application.getType()));
+    }
+
+    /**
+     * 鍒犻櫎鏂囦欢鍏宠仈淇℃伅
+     *
+     * @param recordType  鍏宠仈璁板綍绫诲瀷
+     * @param recordId    鍏宠仈璁板綍id
+     */
+    public void deleteStorageAttachmentsByRecordTypeAndRecordId(RecordTypeEnum recordType, Long recordId) {
+        if (recordId == null || recordId <= 0) {
+            throw new RuntimeException("鍏宠仈璁板綍id涓嶈兘涓虹┖");
+        }
+        if (recordType == null) {
+            throw new RuntimeException("鍏宠仈璁板綍绫诲瀷涓嶈兘涓虹┖");
+        }
+        deleteStorageBlobsByRecordTypeAndRecordId(recordType, recordId);
+        storageAttachmentMapper.delete(new LambdaQueryWrapper<StorageAttachment>()
+                .eq(StorageAttachment::getRecordType, recordType.getType())
+                .eq(StorageAttachment::getRecordId, recordId));
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎鏂囦欢鍏宠仈淇℃伅 attachment
+     *
+     * @param application 鏂囦欢鐢ㄩ��
+     * @param recordType  鍏宠仈璁板綍绫诲瀷
+     * @param recordIds   鍏宠仈璁板綍id
+     */
+    public void deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum application, RecordTypeEnum recordType, List<Long> recordIds) {
+        if (recordIds == null || recordIds.isEmpty()) {
+            throw new RuntimeException("鍏宠仈璁板綍id涓嶈兘涓虹┖");
+        }
+        if (application == null) {
+            throw new RuntimeException("鏂囦欢鐢ㄩ�斾笉鑳戒负绌�");
+        }
+        if (recordType == null) {
+            throw new RuntimeException("鍏宠仈璁板綍绫诲瀷涓嶈兘涓虹┖");
+        }
+        deleteStorageBlobsByApplicationAndRecordTypeAndRecordIds(application, recordType, recordIds);
+        storageAttachmentMapper.delete(new LambdaQueryWrapper<StorageAttachment>()
+                .eq(StorageAttachment::getRecordType, recordType.getType())
+                .in(StorageAttachment::getRecordId, recordIds)
                 .eq(StorageAttachment::getApplication, application.getType()));
     }
 
@@ -164,6 +281,46 @@
             throw new RuntimeException("鏂囦欢id涓嶈兘涓虹┖");
         }
         return storageAttachmentMapper.selectByIds(storageAttachmentIds);
+    }
+
+    /**
+     * 閫氳繃璁板綍绫诲瀷鑾峰彇鏂囦欢淇℃伅 attachment锛堝垎椤碉級
+     *
+     * @param page       鍒嗛〉鍙傛暟
+     * @param storageAttachmentDTO 鍏宠仈璁板綍淇℃伅
+     */
+    public IPage<StorageAttachmentVO> getStorageAttachmentVosPageListByApplicationAndRecordTypeAndRecordId(Page page, StorageAttachmentDTO storageAttachmentDTO) {
+        // 鍒嗛〉鏌ヨ绗﹀悎鏉′欢鐨� StorageAttachment 璁板綍
+        LambdaQueryWrapper<StorageAttachment> queryWrapper = new LambdaQueryWrapper<StorageAttachment>()
+                .eq(StorageAttachment::getRecordType, storageAttachmentDTO.getRecordType())
+                .eq(StorageAttachment::getRecordId, storageAttachmentDTO.getRecordId());
+        if (storageAttachmentDTO.getApplication() != null) {
+            queryWrapper.eq(StorageAttachment::getApplication, storageAttachmentDTO.getApplication());
+        }
+        IPage<StorageAttachmentVO> storageAttachmentIPage = storageAttachmentMapper.selectPage(page, queryWrapper);
+
+        // 杞崲涓� StorageAttachmentVO 骞惰幏鍙栧搴旂殑 StorageBlobVO
+        List<StorageAttachmentVO> storageAttachmentVOS = new ArrayList<>();
+        if (CollectionUtils.isNotEmpty(storageAttachmentIPage.getRecords())) {
+            for (StorageAttachment storageAttachment : storageAttachmentIPage.getRecords()) {
+                StorageAttachmentVO storageAttachmentVO = new StorageAttachmentVO();
+                BeanUtils.copyProperties(storageAttachment, storageAttachmentVO);
+                List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()));
+                if (CollectionUtils.isEmpty(storageBlobVOS)) {
+                    storageAttachmentVO.setStorageBlobVOS(new ArrayList<>());
+                } else {
+                    storageAttachmentVO.setStorageBlobVOS(storageBlobVOS);
+                }
+                storageAttachmentVOS.add(storageAttachmentVO);
+            }
+        }
+
+        // 鏋勫缓鍒嗛〉缁撴灉
+        IPage<StorageAttachmentVO> resultPage = new Page<>();
+        BeanUtils.copyProperties(storageAttachmentIPage, resultPage);
+        resultPage.setRecords(storageAttachmentVOS);
+
+        return resultPage;
     }
 
     /**
@@ -193,8 +350,51 @@
      *
      * @param storageAttachmentIds 鏂囦欢id
      */
-    public List<StorageBlobVO> getStorageBlobDTOsByStorageAttachmentIds(List<Long> storageAttachmentIds) {
+    public List<StorageBlobVO> getStorageBlobVOsByStorageAttachmentIds(List<Long> storageAttachmentIds) {
         List<StorageAttachment> storageAttachments = getStorageAttachmentsByStorageAttachmentIds(storageAttachmentIds);
+        if (CollectionUtils.isEmpty(storageAttachments)) {
+            return null;
+        }
+        List<Long> storageBlobIds = storageAttachments.stream().map(StorageAttachment::getStorageBlobId).collect(Collectors.toList());
+        List<StorageBlob> storageBlobs = storageBlobMapper.selectByIds(storageBlobIds);
+        List<StorageBlobVO> storageBlobDTOS = new ArrayList<>();
+        for (StorageBlob storageBlob : storageBlobs) {
+            StorageBlobVO storageBlobVO = new StorageBlobVO();
+            BeanUtils.copyProperties(storageBlob, storageBlobVO);
+            storageBlobVO.setPreviewURL(buildSignedPreviewUrl(storageBlobVO));
+            storageBlobVO.setDownloadURL(buildSignedDownloadUrl(storageBlobVO));
+            storageBlobDTOS.add(storageBlobVO);
+        }
+        return storageBlobDTOS;
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鐢ㄩ�斻�佸叧鑱旇褰曠被鍨嬨�佸叧鑱旇褰昳d鑾峰彇鏂囦欢鍏宠仈淇℃伅 attachment
+     *
+     * @param recordType  鍏宠仈璁板綍绫诲瀷
+     * @param recordId    鍏宠仈璁板綍id
+     */
+    public List<StorageAttachment> getStorageAttachmentsByRecordTypeAndRecordId(RecordTypeEnum recordType, Long recordId) {
+        if (recordId == null || recordId <= 0) {
+            throw new RuntimeException("鍏宠仈璁板綍id涓嶈兘涓虹┖");
+        }
+        if (recordType == null) {
+            throw new RuntimeException("鍏宠仈璁板綍绫诲瀷涓嶈兘涓虹┖");
+        }
+        return storageAttachmentMapper.selectList(new LambdaQueryWrapper<StorageAttachment>()
+                .eq(StorageAttachment::getRecordType, recordType.getType())
+                .eq(StorageAttachment::getRecordId, recordId));
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鐢ㄩ�斻�佸叧鑱旇褰曠被鍨嬨�佸叧鑱旇褰昳d鑾峰彇鏂囦欢淇℃伅 blob
+     *
+     * @param application 鏂囦欢鐢ㄩ��
+     * @param recordType  鍏宠仈璁板綍绫诲瀷
+     * @param recordId    鍏宠仈璁板綍id
+     */
+    public List<StorageBlobVO> getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId) {
+        List<StorageAttachment> storageAttachments = getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
         if (CollectionUtils.isEmpty(storageAttachments)) {
             return null;
         }
@@ -214,12 +414,11 @@
     /**
      * 閫氳繃鏂囦欢鐢ㄩ�斻�佸叧鑱旇褰曠被鍨嬨�佸叧鑱旇褰昳d鑾峰彇鏂囦欢淇℃伅 blob
      *
-     * @param application 鏂囦欢鐢ㄩ��
      * @param recordType  鍏宠仈璁板綍绫诲瀷
      * @param recordId    鍏宠仈璁板綍id
      */
-    public List<StorageBlobVO> getStorageBlobDTOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId) {
-        List<StorageAttachment> storageAttachments = getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
+    public List<StorageBlobVO> getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum recordType, Long recordId) {
+        List<StorageAttachment> storageAttachments = getStorageAttachmentsByRecordTypeAndRecordId(recordType, recordId);
         if (CollectionUtils.isEmpty(storageAttachments)) {
             return null;
         }
@@ -244,7 +443,7 @@
      * @param recordId    鍏宠仈璁板綍id
      * @param expired     杩囨湡鏃堕棿
      */
-    public List<StorageBlobVO> getStorageBlobDTOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, BigDecimal expired) {
+    public List<StorageBlobVO> getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, BigDecimal expired) {
         List<StorageAttachment> storageAttachments = getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
         if (CollectionUtils.isEmpty(storageAttachments)) {
             return null;
@@ -268,7 +467,7 @@
      * @param storageAttachmentIds 鏂囦欢id
      * @param expired              杩囨湡鏃堕棿
      */
-    public List<StorageBlobVO> getStorageBlobDTOsByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired) {
+    public List<StorageBlobVO> getStorageBlobVOsByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired) {
         List<StorageAttachment> storageAttachments = getStorageAttachmentsByStorageAttachmentIds(storageAttachmentIds);
         if (CollectionUtils.isEmpty(storageAttachments)) {
             return null;
@@ -300,7 +499,7 @@
         for (StorageAttachment storageAttachment : storageAttachments) {
             StorageAttachmentVO storageAttachmentVO = new StorageAttachmentVO();
             BeanUtils.copyProperties(storageAttachment, storageAttachmentVO);
-            List<StorageBlobVO> storageBlobVOS = getStorageBlobDTOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()));
+            List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()));
             if (CollectionUtils.isEmpty(storageBlobVOS)) {
                 storageAttachmentVO.setStorageBlobVOS(new ArrayList<>());
             } else {
@@ -326,7 +525,7 @@
         for (StorageAttachment storageAttachment : storageAttachments) {
             StorageAttachmentVO storageAttachmentVO = new StorageAttachmentVO();
             BeanUtils.copyProperties(storageAttachment, storageAttachmentVO);
-            List<StorageBlobVO> storageBlobVOS = getStorageBlobDTOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()), expired);
+            List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()), expired);
             if (CollectionUtils.isEmpty(storageBlobVOS)) {
                 storageAttachmentVO.setStorageBlobVOS(new ArrayList<>());
             } else {
@@ -353,7 +552,7 @@
         for (StorageAttachment storageAttachment : storageAttachments) {
             StorageAttachmentVO storageAttachmentVO = new StorageAttachmentVO();
             BeanUtils.copyProperties(storageAttachment, storageAttachmentVO);
-            List<StorageBlobVO> storageBlobVOS = getStorageBlobDTOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()));
+            List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()));
             if (CollectionUtils.isEmpty(storageBlobVOS)) {
                 storageAttachmentVO.setStorageBlobVOS(new ArrayList<>());
             } else {
@@ -381,7 +580,7 @@
         for (StorageAttachment storageAttachment : storageAttachments) {
             StorageAttachmentVO storageAttachmentVO = new StorageAttachmentVO();
             BeanUtils.copyProperties(storageAttachment, storageAttachmentVO);
-            List<StorageBlobVO> storageBlobVOS = getStorageBlobDTOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()), expired);
+            List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()), expired);
             if (CollectionUtils.isEmpty(storageBlobVOS)) {
                 storageAttachmentVO.setStorageBlobVOS(new ArrayList<>());
             } else {
@@ -399,7 +598,7 @@
      */
     public List<String> getFilePreviewURLByStorageAttachmentIds(List<Long> storageAttachmentIds) {
         List<String> res = new ArrayList<>();
-        List<StorageBlobVO> storageBlobVOS = getStorageBlobDTOsByStorageAttachmentIds(storageAttachmentIds);
+        List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(storageAttachmentIds);
         for (StorageBlobVO storageBlobVO : storageBlobVOS) {
             res.add(buildSignedPreviewUrl(storageBlobVO));
         }
@@ -414,7 +613,7 @@
      */
     public List<String> getFilePreviewURLByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired) {
         List<String> res = new ArrayList<>();
-        List<StorageBlobVO> storageBlobVOS = getStorageBlobDTOsByStorageAttachmentIds(storageAttachmentIds);
+        List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(storageAttachmentIds);
         for (StorageBlobVO storageBlobVO : storageBlobVOS) {
             res.add(buildSignedUrl(storageBlobVO, "/preview/", expired));
         }
@@ -428,7 +627,7 @@
      */
     public List<String> getFileDownloadURLByStorageAttachmentIds(List<Long> storageAttachmentIds) {
         List<String> res = new ArrayList<>();
-        List<StorageBlobVO> storageBlobVOS = getStorageBlobDTOsByStorageAttachmentIds(storageAttachmentIds);
+        List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(storageAttachmentIds);
         for (StorageBlobVO storageBlobVO : storageBlobVOS) {
             res.add(buildSignedDownloadUrl(storageBlobVO));
         }
@@ -443,11 +642,73 @@
      */
     public List<String> getFileDownloadURLByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired) {
         List<String> res = new ArrayList<>();
-        List<StorageBlobVO> storageBlobVOS = getStorageBlobDTOsByStorageAttachmentIds(storageAttachmentIds);
+        List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(storageAttachmentIds);
         for (StorageBlobVO storageBlobVO : storageBlobVOS) {
             res.add(buildSignedUrl(storageBlobVO, "/download/", expired));
         }
         return res;
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鑾峰彇鏂囦欢棰勮鍦板潃
+     *
+     * @param application 搴旂敤
+     * @param recordType  璁板綍绫诲瀷
+     * @param recordId    璁板綍id
+     */
+    public List<String> getFilePreviewURLByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId) {
+        List<StorageAttachment> storageAttachments = getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
+        if (CollectionUtils.isEmpty(storageAttachments)) {
+            return new ArrayList<>();
+        }
+        return getFilePreviewURLByStorageAttachmentIds(storageAttachments.stream().map(StorageAttachment::getId).collect(Collectors.toList()));
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鑾峰彇鏂囦欢棰勮鍦板潃瀛樺湪杩囨湡鏃堕棿
+     *
+     * @param application 搴旂敤
+     * @param recordType  璁板綍绫诲瀷
+     * @param recordId    璁板綍id
+     * @param expired     杩囨湡鏃堕棿锛堝垎閽燂級
+     */
+    public List<String> getFilePreviewURLByApplicationAndRecordTypeAndRecordIdAndExpired(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, BigDecimal expired) {
+        List<StorageAttachment> storageAttachments = getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
+        if (CollectionUtils.isEmpty(storageAttachments)) {
+            return new ArrayList<>();
+        }
+        return getFilePreviewURLByStorageAttachmentIds(storageAttachments.stream().map(StorageAttachment::getId).collect(Collectors.toList()), expired);
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鑾峰彇鏂囦欢涓嬭浇鍦板潃
+     *
+     * @param application 搴旂敤
+     * @param recordType  璁板綍绫诲瀷
+     * @param recordId    璁板綍id
+     */
+    public List<String> getFileDownloadURLByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId) {
+        List<StorageAttachment> storageAttachments = getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
+        if (CollectionUtils.isEmpty(storageAttachments)) {
+            return new ArrayList<>();
+        }
+        return getFileDownloadURLByStorageAttachmentIds(storageAttachments.stream().map(StorageAttachment::getId).collect(Collectors.toList()));
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鑾峰彇鏂囦欢涓嬭浇鍦板潃瀛樺湪杩囨湡鏃堕棿
+     *
+     * @param application 搴旂敤
+     * @param recordType  璁板綍绫诲瀷
+     * @param recordId    璁板綍id
+     * @param expired     杩囨湡鏃堕棿锛堝垎閽燂級
+     */
+    public List<String> getFileDownloadURLByApplicationAndRecordTypeAndRecordIdAndExpired(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, BigDecimal expired) {
+        List<StorageAttachment> storageAttachments = getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
+        if (CollectionUtils.isEmpty(storageAttachments)) {
+            return new ArrayList<>();
+        }
+        return getFileDownloadURLByStorageAttachmentIds(storageAttachments.stream().map(StorageAttachment::getId).collect(Collectors.toList()), expired);
     }
 
     public String buildSignedPreviewUrl(StorageBlobVO storageBlob) {
@@ -472,8 +733,28 @@
         if (storageBlob == null || !StringUtils.hasText(storageBlob.getUidFilename())) {
             throw new IllegalArgumentException("鏂囦欢淇℃伅涓嶅畬鏁�");
         }
+        String domain = StringUtils.trimTrailingCharacter(properties.getDomain(), '/');
+        String prefix = properties.getUrlPrefix().startsWith("/") ? properties.getUrlPrefix() : "/" + properties.getUrlPrefix();
+        String normalizedActionPath = StringUtils.hasText(actionPath) ? actionPath : "/preview/";
+        if (!normalizedActionPath.startsWith("/")) {
+            normalizedActionPath = "/" + normalizedActionPath;
+        }
+        if (!normalizedActionPath.endsWith("/")) {
+            normalizedActionPath = normalizedActionPath + "/";
+        }
+        String baseUrl = domain + prefix + normalizedActionPath + storageBlob.getUidFilename();
+
+        // -1 琛ㄧず姘镐箙鏈夋晥锛屼笉鐢熸垚 token锛屾敼涓� publicKey 缁勫悎鏍¢獙
+        if (expired != null && BigDecimal.valueOf(-1L).compareTo(expired) == 0) {
+            if (!StringUtils.hasText(storageBlob.getResourceKey())) {
+                throw new IllegalArgumentException("鍏紑閾炬帴缂哄皯publicKey");
+            }
+            return baseUrl + "?publicKey=" + storageBlob.getResourceKey();
+        }
+
         long now = System.currentTimeMillis();
-        long expiredMillis = expired.multiply(new BigDecimal("60000")).longValue();
+        BigDecimal expiredValue = expired == null ? new BigDecimal("120") : expired;
+        long expiredMillis = expiredValue.multiply(new BigDecimal("60000")).longValue();
         if (expiredMillis <= 0L) {
             expiredMillis = 2L * 60L * 60L * 1000L;
         }
@@ -490,16 +771,7 @@
                 .signWith(key)            // 閲嶇偣锛氫紶鍏ヤ笂闈㈢敓鎴愮殑 key 瀵硅薄锛岃�屼笉鏄� String
                 .compact();
         cacheTokenUsage(token, expiredMillis);
-        String domain = StringUtils.trimTrailingCharacter(properties.getDomain(), '/');
-        String prefix = properties.getUrlPrefix().startsWith("/") ? properties.getUrlPrefix() : "/" + properties.getUrlPrefix();
-        String normalizedActionPath = StringUtils.hasText(actionPath) ? actionPath : "/preview/";
-        if (!normalizedActionPath.startsWith("/")) {
-            normalizedActionPath = "/" + normalizedActionPath;
-        }
-        if (!normalizedActionPath.endsWith("/")) {
-            normalizedActionPath = normalizedActionPath + "/";
-        }
-        return domain + prefix + normalizedActionPath + storageBlob.getUidFilename() + "?token=" + token;
+        return baseUrl + "?token=" + token;
     }
 
     private void cacheTokenUsage(String token, long expiredMillis) {

--
Gitblit v1.9.3