zhuguifei
2025-04-28 442928123f63ee497d766f9a7a14f0a6ee067e25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
package org.jeecg.modules.doc.component;
 
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.jeecg.common.constant.CacheConstant;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.modules.doc.constant.Constant;
import org.jeecg.modules.doc.entity.DocFile;
import org.jeecg.modules.doc.entity.DocFilePath;
import org.jeecg.modules.doc.mapper.DocFileMapper;
import org.jeecg.modules.doc.mapper.DocFilePathMapper;
import org.jeecg.modules.doc.service.IDocFilePathService;
import org.jeecg.modules.doc.vo.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
 
@Slf4j
@Component
public class DocFileDealComp {
 
 
 
    @Autowired
    private IDocFilePathService docFilePathService;
 
    @Resource
    DocFilePathMapper filePathMapper;
 
    @Resource
    DocFileMapper docFileMapper;
 
    @Autowired
    private RedisUtil redisUtil;
 
    /**
     * 组织一个树目录节点
     *
     * @param treeNode
     * @param id
     * @param filePath
     * @param nodeNameQueue
     * @return
     */
    public DocTree insertTreeNode(DocTree treeNode, long id, String filePath, Queue<String> nodeNameQueue, String pathId){
        List<DocTree> childrenTreeNodes = treeNode.getChildren();
        String currentNodeName = nodeNameQueue.peek();
 
        if (currentNodeName == null){
            return treeNode;
        }
        QiwenFile qiwenFile = new QiwenFile(filePath, currentNodeName, true);
        filePath = qiwenFile.getPath();
 
        if (!isExistPath(childrenTreeNodes, currentNodeName)){  //1、判断有没有该子节点,如果没有则插入
            //插入
            DocTree resultTreeNode = new DocTree();
 
            resultTreeNode.setPath(filePath);
            resultTreeNode.setTitle(nodeNameQueue.poll());
            resultTreeNode.setId(++id);
            resultTreeNode.setLeaf(true);
            resultTreeNode.setKey(treeNode.getKey()+"-"+childrenTreeNodes.size());
            resultTreeNode.getSlots().setIcon("folder");
            resultTreeNode.getScopedSlots().setTitle("custom");
            childrenTreeNodes.add(resultTreeNode);
 
        }else{  //2、如果有,则跳过
            nodeNameQueue.poll();
        }
 
        if (nodeNameQueue.size() != 0) {
            for (int i = 0; i < childrenTreeNodes.size(); i++) {
 
                DocTree childrenTreeNode = childrenTreeNodes.get(i);
                if (currentNodeName.equals(childrenTreeNode.getTitle())){
                    childrenTreeNode = insertTreeNode(childrenTreeNode, id * 10, filePath, nodeNameQueue, pathId);
                    childrenTreeNodes.remove(i);
                    childrenTreeNodes.add(childrenTreeNode);
                   // treeNode.setChildren(childrenTreeNodes);
                }
            }
        }else{
            for (int i = 0; i < childrenTreeNodes.size(); i++) {
 
                DocTree childrenTreeNode = childrenTreeNodes.get(i);
                if (currentNodeName.equals(childrenTreeNode.getTitle())){
                    childrenTreeNode.setPathId(pathId);
                    childrenTreeNodes.remove(i);
                    childrenTreeNodes.add(childrenTreeNode);
                }
            }
            //treeNode.setChildren(childrenTreeNodes);
            //treeNode.setLeaf(false);
        }
 
        return treeNode;
 
    }
 
    /**
     * 判断该路径在树节点中是否已经存在
     * @param childrenTreeNodes
     * @param path
     * @return
     */
    public boolean isExistPath(List<DocTree> childrenTreeNodes, String path){
        boolean isExistPath = false;
 
        try {
            for (int i = 0; i < childrenTreeNodes.size(); i++){
                if (path.equals(childrenTreeNodes.get(i).getTitle())){
                    isExistPath = true;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
 
 
        return isExistPath;
    }
 
    public boolean isDirExist(String fileName, String filePath, long userId){
        LambdaQueryWrapper<DocFilePath> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(DocFilePath::getFileName, fileName)
                .eq(DocFilePath::getFilePath, QiwenFile.formatPath(filePath))
                .eq(DocFilePath::getDeleteFlag, Constant.DEL_FALSE)
                .eq(DocFilePath::getIsDir, Constant.IS_DIR);
        List<DocFilePath> list = docFilePathService.list(lambdaQueryWrapper);
        if (list != null && !list.isEmpty()) {
            return true;
        }
        return false;
    }
 
    /**
     * 递归遍历树的节点,对节点子节点按key值进行排序
     * @param node
     */
    public void sortByKey(DocTree node){
        List<DocTree> children = node.getChildren();
        Collections.sort(children, (o1, o2) -> {
 
 
            int o1Key = Integer.valueOf(o1.getKey().substring(o1.getKey().lastIndexOf('-')));
            int o2Key = Integer.valueOf(o2.getKey().substring(o2.getKey().lastIndexOf('-')));
            return o2Key-o1Key;
        });
        for (DocTree child: children) {
            this.sortByKey(child);
        }
    }
 
    public void checkLeaf(DocTree node) {
        List<DocTree> children = node.getChildren();
        for (DocTree dt: children) {
            if(dt.getChildren().size() > 0) {
                dt.setLeaf(false);
            }
            this.checkLeaf(dt);
        }
    }
 
    /**
     * 获取重复文件名
     *
     * 场景1: 文件还原时,在 savefilePath 路径下,保存 测试.txt 文件重名,则会生成 测试(1).txt
     * 场景2: 上传文件时,在 savefilePath 路径下,保存 测试.txt 文件重名,则会生成 测试(1).txt
     *
     * @param userFile
     * @param savefilePath
     * @return
     */
    public String getRepeatFileName(DocFilePath userFile, String savefilePath) {
        String fileName = userFile.getFileName();
        String extendName = userFile.getExtendName();
        Integer deleteFlag = userFile.getDeleteFlag();
 
        int isDir = userFile.getIsDir();
        LambdaQueryWrapper<DocFilePath> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(DocFilePath::getFilePath, savefilePath)
                .eq(DocFilePath::getDeleteFlag, deleteFlag)
                .eq(DocFilePath::getFileName, fileName)
                .eq(DocFilePath::getIsDir, isDir);
        if (userFile.getIsDir() == Constant.IS_NOT_DIR) {
            lambdaQueryWrapper.eq(DocFilePath::getExtendName, extendName);
        }
        List<DocFilePath> list = filePathMapper.selectList(lambdaQueryWrapper);
        if (list == null) {
            return fileName;
        }
        if (list.isEmpty()) {
            return fileName;
        }
        int i = 0;
 
        while (list != null && !list.isEmpty()) {
            i++;
            LambdaQueryWrapper<DocFilePath> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
            lambdaQueryWrapper1.eq(DocFilePath::getFilePath, savefilePath)
                    .eq(DocFilePath::getDeleteFlag, deleteFlag)
                    .eq(DocFilePath::getFileName, fileName + "(" + i + ")")
                    .eq(DocFilePath::getIsDir, isDir);
            if (userFile.getIsDir() == Constant.IS_NOT_DIR) {
                lambdaQueryWrapper1.eq(DocFilePath::getExtendName, extendName);
            }
            list = filePathMapper.selectList(lambdaQueryWrapper1);
        }
 
        return fileName + "(" + i + ")";
 
    }
 
    /**
     * 还原父文件路径
     * <p>
     * 1、回收站文件还原操作会将文件恢复到原来的路径下,当还原文件的时候,如果父目录已经不存在了,则需要把父目录给还原
     * 2、上传目录
     *
     * @param
     * @param childId 文件pathID
     */
    public synchronized void restoreParentFilePath(QiwenFile qiwenFile, String childId) {
        if (qiwenFile.isFile()) {
            qiwenFile = qiwenFile.getParentFile();
        }
        while(qiwenFile.getParent() != null) {
            String fileName = qiwenFile.getName();
            String parentFilePath = qiwenFile.getParent();
 
//            QueryWrapper<DocFilePath> qw = new QueryWrapper<>();
//            qw.eq("file_path", parentFilePath).eq("file_name", fileName).eq("delete_flag", Constant.DEL_FALSE).eq("is_dir", Constant.IS_DIR);
 
            List<DocFilePath> userFileList = docFilePathService.getDocFilePathsByPathName(parentFilePath, fileName);
            String parentId;
            DocFilePath userFile;
            if (userFileList.size() == 0) {
                userFile = PathFileUtil.getDir( parentFilePath, fileName, null);
                try {
                    userFileList.add(userFile);
                    redisUtil.set(CacheConstant.DOC_FILE_PATH_CACHE+"::"+parentFilePath+':'+fileName,userFileList);
                    filePathMapper.insert(userFile);
 
                } catch (Exception e) {
//                    System.out.println("重复:::");
                    if (e.getMessage().contains("Duplicate entry")) {
                        log.error("重复了!!" + "【" + fileName+"】" + "["+ parentFilePath + "]");
                        //ignore
                    } else {
                        log.error("异常了");
//                        log.error(e.getMessage());
                    }
                }
                qiwenFile = new QiwenFile(parentFilePath, true);
            } else {
                userFile = userFileList.get(0);
                qiwenFile = new QiwenFile("/", true);
            }
            parentId = userFile.getPathId();
            filePathMapper.update(null,
                    new LambdaUpdateWrapper<DocFilePath>().set(DocFilePath::getParentId,parentId).eq(DocFilePath::getPathId, childId));
 
            childId = userFile.getPathId();
 
        }
    }
 
 
 
    /**
     * 拷贝文件
     * 场景:修改的文件被多处引用时,需要重新拷贝一份,然后在新的基础上修改
     * @param fileBean
     * @param userFile
     * @return
     */
    public String copyFile(DocFile fileBean, DocFilePath userFile) {
        Copier copier = new LocalStorageCopier();
        Downloader downloader = new LocalStorageDownloader();
        DownloadFile downloadFile = new DownloadFile();
        downloadFile.setFileUrl(fileBean.getFileUrl());
        CopyFile copyFile = new CopyFile();
        copyFile.setExtendName(userFile.getExtendName());
        String fileUrl = copier.copy(downloader.getInputStream(downloadFile), copyFile);
        if (downloadFile.getOssClient() != null) {
            downloadFile.getOssClient().shutdown();
        }
        fileBean.setFileUrl(fileUrl);
        fileBean.setFileId(null);
        docFileMapper.insert(fileBean);
        userFile.setFileId(fileBean.getFileId());
        userFile.setUpdateTime(DateUtils.getDate());
        filePathMapper.updateById(userFile);
        return fileUrl;
    }
 
    public void saveFileInputStream(String fileUrl, InputStream inputStream) throws IOException {
        Writer writer1 = new LocalStorageWriter();
        WriteFile writeFile = new WriteFile();
        writeFile.setFileUrl(fileUrl);
        int fileSize = inputStream.available();
        writeFile.setFileSize(fileSize);
        writer1.write(inputStream, writeFile);
    }
 
    public String getIdentifierByFile(String fileUrl, int storageType) throws IOException {
        DownloadFile downloadFile = new DownloadFile();
        downloadFile.setFileUrl(fileUrl);
        InputStream inputStream = new LocalStorageDownloader().getInputStream(downloadFile);
        String md5Str = DigestUtils.md5Hex(inputStream);
        return md5Str;
    }
 
    /**
     * 删除重复的子目录文件
     *
     * 当还原目录的时候,如果其子目录在文件系统中已存在,则还原之后进行去重操作
     * @param filePath
     */
    public void deleteRepeatSubDirFile(String filePath) {
        log.debug("删除子目录:"+filePath);
        LambdaQueryWrapper<DocFilePath> lambdaQueryWrapper = new LambdaQueryWrapper<>();
 
        lambdaQueryWrapper.select(DocFilePath::getFileName, DocFilePath::getFilePath)
                .likeRight(DocFilePath::getFilePath, PathFileUtil.formatLikePath(filePath))
                .eq(DocFilePath::getIsDir, Constant.IS_DIR)
                .eq(DocFilePath::getDeleteFlag, Constant.DEL_FALSE)
                .groupBy(DocFilePath::getFilePath, DocFilePath::getFileName)
                .having("count(file_name) >= 2");
        List<DocFilePath> repeatList = docFilePathService.list(lambdaQueryWrapper);
 
        for (DocFilePath userFile : repeatList) {
            LambdaQueryWrapper<DocFilePath> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
            lambdaQueryWrapper1.eq(DocFilePath::getFilePath, userFile.getFilePath())
                    .eq(DocFilePath::getFileName, userFile.getFileName())
                    .eq(DocFilePath::getDeleteFlag, Constant.DEL_FALSE);
            List<DocFilePath> userFiles = docFilePathService.list(lambdaQueryWrapper1);
            for (int i = 0; i < userFiles.size() - 1; i ++) {
                docFilePathService.removeById(userFiles.get(i).getPathId());
            }
        }
    }
 
    public DocFilePath getParent(String filePath) {
        QiwenFile qiwenFile = new QiwenFile(filePath, true);
        DocFilePath parent = docFilePathService.getOne(new LambdaQueryWrapper<DocFilePath>()
                .eq(DocFilePath::getFilePath, qiwenFile.getParent())
                .eq(DocFilePath::getFileName, qiwenFile.getName())
                .eq(DocFilePath::getDeleteFlag, Constant.DEL_FALSE)
                .eq(DocFilePath::getIsDir, Constant.IS_DIR));
        return parent;
    }
 
    /**
     * 判定是不是有访问权限的祖先节点,如果是前端需要展示该文件
     * @param subVisits
     * @param i
     * @return
     */
    public boolean isParentsOrChild(Map<String,DocFilePath> subVisits, DocFilePath i, boolean visit) {
        // 遍历权限列表
        Iterator<String> iterator = subVisits.keySet().iterator();
        while (iterator.hasNext()){
            String id = iterator.next();
            DocFilePath dfp = subVisits.get(id);
            if (dfp == null) continue;
            // 获取有权限的文件全路径
            String path = new QiwenFile(dfp.getFilePath(),dfp.getFileName(),true).getPath();
 
            // 判断当前文件是不是有权限文件的子级
            if ( i.getFilePath().startsWith( path )) {
                return true;
            }
            // 如果是访问权限,需要判断当前文件是不是有访问权限文件的祖先节点
            if (visit && i.isDirectory()) {
                String curPath = new QiwenFile(i.getFilePath(), i.getFileName(), true).getPath();
                if (dfp.getFilePath().startsWith(curPath)){
                    i.setPermission(true);
                    return true;
                }
            }
        }
        return false;
    }
 
    /**
     * 判定是不是有访问权限的祖先节点,如果是前端需要展示该文件
     * @param subVisits
     * @param i
     * @return
     */
    public boolean isParentsOrChild(Map<String,DocFilePath> subVisits, RecoveryFileListVo i, boolean visit) {
        // 遍历权限列表
        Iterator<String> iterator = subVisits.keySet().iterator();
        while (iterator.hasNext()){
            String id = iterator.next();
            DocFilePath dfp = subVisits.get(id);
            if (dfp == null) continue;
            // 获取有权限的文件全路径
            String path = new QiwenFile(dfp.getFilePath(),dfp.getFileName(),true).getPath();
 
            // 判断当前文件是不是有权限文件的子级
            try {
                if (i.getFilePath().startsWith(path)) {
                    return true;
                }
            }catch (Exception e) {
                e.printStackTrace();
            }
            // 如果是访问权限,需要判断当前文件是不是有访问权限文件的祖先节点
            if (visit && i.isDirectory()) {
                String curPath = new QiwenFile(i.getFilePath(), i.getFileName(), true).getPath();
                if (dfp.getFilePath().startsWith(curPath)){
                  //  i.setPermission(true);
                    return true;
                }
            }
        }
        return false;
    }
}