mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-11-04 10:57:10 +08:00 
			
		
		
		
	refactor(system/file): 重构文件管理表结构,新增计算文件大小接口
This commit is contained in:
		@@ -29,11 +29,6 @@ public class ContainerConstants {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public static final String USER_NICKNAME = "UserNickname";
 | 
					    public static final String USER_NICKNAME = "UserNickname";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 文件信息
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public static final String FILE_INFO = "FileInfo";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 用户角色 ID 列表
 | 
					     * 用户角色 ID 列表
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,54 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 | 
				
			||||||
 * you may not use this file except in compliance with the License.
 | 
					 | 
				
			||||||
 * You may obtain a copy of the License at
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Unless required by applicable law or agreed to in writing, software
 | 
					 | 
				
			||||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
					 | 
				
			||||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					 | 
				
			||||||
 * See the License for the specific language governing permissions and
 | 
					 | 
				
			||||||
 * limitations under the License.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package top.continew.admin.system.config.file;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import cn.crane4j.core.container.Container;
 | 
					 | 
				
			||||||
import lombok.RequiredArgsConstructor;
 | 
					 | 
				
			||||||
import org.springframework.stereotype.Component;
 | 
					 | 
				
			||||||
import top.continew.admin.common.constant.ContainerConstants;
 | 
					 | 
				
			||||||
import top.continew.admin.system.model.entity.FileDO;
 | 
					 | 
				
			||||||
import top.continew.admin.system.service.FileService;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.Collection;
 | 
					 | 
				
			||||||
import java.util.List;
 | 
					 | 
				
			||||||
import java.util.Map;
 | 
					 | 
				
			||||||
import java.util.function.Function;
 | 
					 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * 文件信息填充容器
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author luoqiz
 | 
					 | 
				
			||||||
 * @since 2025/3/12 18:11
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
@Component
 | 
					 | 
				
			||||||
@RequiredArgsConstructor
 | 
					 | 
				
			||||||
public class FileInfoContainer implements Container<Long> {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final FileService fileService;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public String getNamespace() {
 | 
					 | 
				
			||||||
        return ContainerConstants.FILE_INFO;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public Map<Long, FileDO> get(Collection<Long> ids) {
 | 
					 | 
				
			||||||
        List<FileDO> list = fileService.listByIds(ids);
 | 
					 | 
				
			||||||
        return list.stream().collect(Collectors.toMap(FileDO::getId, Function.identity()));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -62,11 +62,11 @@ public class FileRecorderImpl implements FileRecorder {
 | 
				
			|||||||
        // 方便文件上传完成后获取文件信息
 | 
					        // 方便文件上传完成后获取文件信息
 | 
				
			||||||
        fileInfo.setId(String.valueOf(file.getId()));
 | 
					        fileInfo.setId(String.valueOf(file.getId()));
 | 
				
			||||||
        if (!URLUtils.isHttpUrl(fileInfo.getUrl())) {
 | 
					        if (!URLUtils.isHttpUrl(fileInfo.getUrl())) {
 | 
				
			||||||
            String prefix = StrUtil.blankToDefault(storage.getDomain(), storage.getEndpoint());
 | 
					            String prefix = storage.getUrlPrefix();
 | 
				
			||||||
            String url = URLUtil.completeUrl(prefix, fileInfo.getUrl());
 | 
					            String url = URLUtil.normalize(prefix + fileInfo.getUrl(), false, true);
 | 
				
			||||||
            fileInfo.setUrl(url);
 | 
					            fileInfo.setUrl(url);
 | 
				
			||||||
            if (StrUtil.isNotBlank(fileInfo.getThUrl())) {
 | 
					            if (StrUtil.isNotBlank(fileInfo.getThUrl())) {
 | 
				
			||||||
                fileInfo.setThUrl(URLUtil.completeUrl(prefix, fileInfo.getThUrl()));
 | 
					                fileInfo.setThUrl(URLUtil.normalize(prefix + fileInfo.getThUrl(), false, true));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
@@ -117,8 +117,7 @@ public class FileRecorderImpl implements FileRecorder {
 | 
				
			|||||||
            .eq(FileDO::getName, StrUtil.subAfter(url, StringConstants.SLASH, true));
 | 
					            .eq(FileDO::getName, StrUtil.subAfter(url, StringConstants.SLASH, true));
 | 
				
			||||||
        // 非 HTTP URL 场景
 | 
					        // 非 HTTP URL 场景
 | 
				
			||||||
        if (!URLUtils.isHttpUrl(url)) {
 | 
					        if (!URLUtils.isHttpUrl(url)) {
 | 
				
			||||||
            return queryWrapper.eq(FileDO::getPath, StrUtil.prependIfMissing(StrUtil
 | 
					            return queryWrapper.eq(FileDO::getPath, StrUtil.prependIfMissing(url, StringConstants.SLASH)).one();
 | 
				
			||||||
                .subBefore(url, StringConstants.SLASH, true), StringConstants.SLASH)).one();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // HTTP URL 场景
 | 
					        // HTTP URL 场景
 | 
				
			||||||
        List<FileDO> list = queryWrapper.list();
 | 
					        List<FileDO> list = queryWrapper.list();
 | 
				
			||||||
@@ -137,8 +136,7 @@ public class FileRecorderImpl implements FileRecorder {
 | 
				
			|||||||
            String urlPrefix = StrUtil.subBefore(url, StringConstants.SLASH, true);
 | 
					            String urlPrefix = StrUtil.subBefore(url, StringConstants.SLASH, true);
 | 
				
			||||||
            // http://localhost:8000/file/ + /user/avatar => http://localhost:8000/file/user/avatar
 | 
					            // http://localhost:8000/file/ + /user/avatar => http://localhost:8000/file/user/avatar
 | 
				
			||||||
            StorageDO storage = storageMap.get(file.getStorageId());
 | 
					            StorageDO storage = storageMap.get(file.getStorageId());
 | 
				
			||||||
            String prefix = StrUtil.blankToDefault(storage.getDomain(), storage.getEndpoint());
 | 
					            return urlPrefix.equals(URLUtil.normalize(storage.getUrlPrefix() + file.getParentPath(), false, true));
 | 
				
			||||||
            return urlPrefix.equals(URLUtil.normalize(prefix + file.getPath(), false, true));
 | 
					 | 
				
			||||||
        }).findFirst().orElse(null);
 | 
					        }).findFirst().orElse(null);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -83,7 +83,7 @@ public enum StorageTypeEnum implements BaseEnum<Integer> {
 | 
				
			|||||||
     * @param req 请求参数
 | 
					     * @param req 请求参数
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public void pretreatment(StorageReq req) {
 | 
					    public void pretreatment(StorageReq req) {
 | 
				
			||||||
        // 域名需要以 “/” 结尾
 | 
					        // 域名需要以 “/” 结尾(x-file-storage 在拼接路径时都是直接 + 拼接,所以规范要求每一级都要以 “/” 结尾,且后面路径不能以 “/” 开头)
 | 
				
			||||||
        if (StrUtil.isNotBlank(req.getDomain())) {
 | 
					        if (StrUtil.isNotBlank(req.getDomain())) {
 | 
				
			||||||
            req.setDomain(StrUtil.appendIfMissing(req.getDomain(), StringConstants.SLASH));
 | 
					            req.setDomain(StrUtil.appendIfMissing(req.getDomain(), StringConstants.SLASH));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,6 +36,6 @@ public interface FileMapper extends BaseMapper<FileDO> {
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return 文件资源统计信息
 | 
					     * @return 文件资源统计信息
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @Select("SELECT type, COUNT(1) number, SUM(size) size FROM sys_file GROUP BY type")
 | 
					    @Select("SELECT type, COUNT(1) number, SUM(size) size FROM sys_file WHERE type != 0 GROUP BY type")
 | 
				
			||||||
    List<FileStatisticsResp> statistics();
 | 
					    List<FileStatisticsResp> statistics();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -60,7 +60,12 @@ public class FileDO extends BaseDO {
 | 
				
			|||||||
    private Long size;
 | 
					    private Long size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 存储路径
 | 
					     * 上级目录
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String parentPath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 路径
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private String path;
 | 
					    private String path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -119,10 +124,11 @@ public class FileDO extends BaseDO {
 | 
				
			|||||||
        this.originalName = fileInfo.getOriginalFilename();
 | 
					        this.originalName = fileInfo.getOriginalFilename();
 | 
				
			||||||
        this.size = fileInfo.getSize();
 | 
					        this.size = fileInfo.getSize();
 | 
				
			||||||
        // 如果为空,则为 /;如果不为空,则调整格式为:/xxx
 | 
					        // 如果为空,则为 /;如果不为空,则调整格式为:/xxx
 | 
				
			||||||
        this.path = StrUtil.isEmpty(fileInfo.getPath())
 | 
					        this.parentPath = StrUtil.isEmpty(fileInfo.getPath())
 | 
				
			||||||
            ? StringConstants.SLASH
 | 
					            ? StringConstants.SLASH
 | 
				
			||||||
            : StrUtil.removeSuffix(StrUtil.prependIfMissing(fileInfo
 | 
					            : StrUtil.removeSuffix(StrUtil.prependIfMissing(fileInfo
 | 
				
			||||||
                .getPath(), StringConstants.SLASH), StringConstants.SLASH);
 | 
					                .getPath(), StringConstants.SLASH), StringConstants.SLASH);
 | 
				
			||||||
 | 
					        this.path = StrUtil.prependIfMissing(fileInfo.getUrl(), StringConstants.SLASH);
 | 
				
			||||||
        this.extension = fileInfo.getExt();
 | 
					        this.extension = fileInfo.getExt();
 | 
				
			||||||
        this.contentType = fileInfo.getContentType();
 | 
					        this.contentType = fileInfo.getContentType();
 | 
				
			||||||
        this.type = FileTypeEnum.getByExtension(this.extension);
 | 
					        this.type = FileTypeEnum.getByExtension(this.extension);
 | 
				
			||||||
@@ -148,15 +154,16 @@ public class FileDO extends BaseDO {
 | 
				
			|||||||
        // 暂不使用,所以保持空
 | 
					        // 暂不使用,所以保持空
 | 
				
			||||||
        fileInfo.setBasePath(StringConstants.EMPTY);
 | 
					        fileInfo.setBasePath(StringConstants.EMPTY);
 | 
				
			||||||
        fileInfo.setSize(this.size);
 | 
					        fileInfo.setSize(this.size);
 | 
				
			||||||
        fileInfo.setPath(StringConstants.SLASH.equals(this.path)
 | 
					        fileInfo.setPath(StringConstants.SLASH.equals(this.parentPath)
 | 
				
			||||||
            ? StringConstants.EMPTY
 | 
					            ? StringConstants.EMPTY
 | 
				
			||||||
            : StrUtil.appendIfMissing(StrUtil.removePrefix(this.path, StringConstants.SLASH), StringConstants.SLASH));
 | 
					            : StrUtil.appendIfMissing(StrUtil
 | 
				
			||||||
 | 
					                .removePrefix(this.parentPath, StringConstants.SLASH), StringConstants.SLASH));
 | 
				
			||||||
        fileInfo.setExt(this.extension);
 | 
					        fileInfo.setExt(this.extension);
 | 
				
			||||||
        fileInfo.setContentType(this.contentType);
 | 
					        fileInfo.setContentType(this.contentType);
 | 
				
			||||||
        if (StrUtil.isNotBlank(this.metadata)) {
 | 
					        if (StrUtil.isNotBlank(this.metadata)) {
 | 
				
			||||||
            fileInfo.setMetadata(JSONUtil.toBean(this.metadata, Map.class));
 | 
					            fileInfo.setMetadata(JSONUtil.toBean(this.metadata, Map.class));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        fileInfo.setUrl(fileInfo.getPath() + fileInfo.getFilename());
 | 
					        fileInfo.setUrl(StrUtil.removePrefix(this.path, StringConstants.SLASH));
 | 
				
			||||||
        // 缩略图信息
 | 
					        // 缩略图信息
 | 
				
			||||||
        fileInfo.setThFilename(this.thumbnailName);
 | 
					        fileInfo.setThFilename(this.thumbnailName);
 | 
				
			||||||
        fileInfo.setThSize(this.thumbnailSize);
 | 
					        fileInfo.setThSize(this.thumbnailSize);
 | 
				
			||||||
@@ -166,4 +173,11 @@ public class FileDO extends BaseDO {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        return fileInfo;
 | 
					        return fileInfo;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setParentPath(String parentPath) {
 | 
				
			||||||
 | 
					        this.parentPath = parentPath;
 | 
				
			||||||
 | 
					        this.path = StringConstants.SLASH.equals(parentPath)
 | 
				
			||||||
 | 
					            ? parentPath + this.name
 | 
				
			||||||
 | 
					            : parentPath + StringConstants.SLASH + this.name;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,14 +16,20 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package top.continew.admin.system.model.entity;
 | 
					package top.continew.admin.system.model.entity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import cn.hutool.core.lang.RegexPool;
 | 
				
			||||||
 | 
					import cn.hutool.core.util.ReUtil;
 | 
				
			||||||
 | 
					import cn.hutool.core.util.StrUtil;
 | 
				
			||||||
 | 
					import cn.hutool.core.util.URLUtil;
 | 
				
			||||||
import com.baomidou.mybatisplus.annotation.TableName;
 | 
					import com.baomidou.mybatisplus.annotation.TableName;
 | 
				
			||||||
import lombok.Data;
 | 
					import lombok.Data;
 | 
				
			||||||
import top.continew.admin.common.enums.DisEnableStatusEnum;
 | 
					import top.continew.admin.common.enums.DisEnableStatusEnum;
 | 
				
			||||||
import top.continew.admin.system.enums.StorageTypeEnum;
 | 
					 | 
				
			||||||
import top.continew.admin.common.model.entity.BaseDO;
 | 
					import top.continew.admin.common.model.entity.BaseDO;
 | 
				
			||||||
 | 
					import top.continew.admin.system.enums.StorageTypeEnum;
 | 
				
			||||||
 | 
					import top.continew.starter.core.constant.StringConstants;
 | 
				
			||||||
import top.continew.starter.security.crypto.annotation.FieldEncrypt;
 | 
					import top.continew.starter.security.crypto.annotation.FieldEncrypt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.io.Serial;
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					import java.net.URL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 存储实体
 | 
					 * 存储实体
 | 
				
			||||||
@@ -99,4 +105,28 @@ public class StorageDO extends BaseDO {
 | 
				
			|||||||
     * 状态
 | 
					     * 状态
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private DisEnableStatusEnum status;
 | 
					    private DisEnableStatusEnum status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取 URL 前缀
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * LOCAL:{@link #domain}/ <br />
 | 
				
			||||||
 | 
					     * OSS:域名不为空:{@link #domain}/;Endpoint 不是
 | 
				
			||||||
 | 
					     * IP:http(s)://{@link #bucketName}.{@link #endpoint}/;否则:{@link #endpoint}/{@link #bucketName}/
 | 
				
			||||||
 | 
					     * </p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return URL 前缀
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public String getUrlPrefix() {
 | 
				
			||||||
 | 
					        if (StrUtil.isNotBlank(this.domain) || StorageTypeEnum.LOCAL.equals(this.type)) {
 | 
				
			||||||
 | 
					            return StrUtil.appendIfMissing(this.domain, StringConstants.SLASH);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        URL url = URLUtil.url(this.endpoint);
 | 
				
			||||||
 | 
					        String host = url.getHost();
 | 
				
			||||||
 | 
					        // IP(MinIO) 则拼接 BucketName
 | 
				
			||||||
 | 
					        if (ReUtil.isMatch(RegexPool.IPV4, host) || ReUtil.isMatch(RegexPool.IPV6, host)) {
 | 
				
			||||||
 | 
					            return StrUtil
 | 
				
			||||||
 | 
					                .appendIfMissing(this.endpoint, StringConstants.SLASH) + this.bucketName + StringConstants.SLASH;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return "%s://%s.%s/".formatted(url.getProtocol(), this.bucketName, host);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -46,10 +46,10 @@ public class FileQuery implements Serializable {
 | 
				
			|||||||
    private String originalName;
 | 
					    private String originalName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 存储路径
 | 
					     * 上级目录
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @Schema(description = "存储路径", example = "/")
 | 
					    @Schema(description = "上级目录", example = "/")
 | 
				
			||||||
    private String path;
 | 
					    private String parentPath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 类型
 | 
					     * 类型
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,8 +46,8 @@ public class FileReq implements Serializable {
 | 
				
			|||||||
    private String originalName;
 | 
					    private String originalName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 存储路径
 | 
					     * 上级目录
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @Schema(description = "存储路径", example = "/")
 | 
					    @Schema(description = "上级目录", example = "/")
 | 
				
			||||||
    private String path;
 | 
					    private String parentPath;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package top.continew.admin.system.model.resp.file;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import io.swagger.v3.oas.annotations.media.Schema;
 | 
				
			||||||
 | 
					import lombok.AllArgsConstructor;
 | 
				
			||||||
 | 
					import lombok.Data;
 | 
				
			||||||
 | 
					import lombok.NoArgsConstructor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 文件夹计算大小响应参数
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Charles7c
 | 
				
			||||||
 | 
					 * @since 2025/5/16 21:32
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Data
 | 
				
			||||||
 | 
					@NoArgsConstructor
 | 
				
			||||||
 | 
					@AllArgsConstructor
 | 
				
			||||||
 | 
					@Schema(description = "文件夹计算大小响应参数")
 | 
				
			||||||
 | 
					public class FileDirCalcSizeResp implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Serial
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 大小(字节)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Schema(description = "大小(字节)", example = "4096")
 | 
				
			||||||
 | 
					    private Long size;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -63,9 +63,15 @@ public class FileResp extends BaseDetailResp {
 | 
				
			|||||||
    private String url;
 | 
					    private String url;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 存储路径
 | 
					     * 上级目录
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @Schema(description = "上级目录", example = "/2025/2/25")
 | 
					    @Schema(description = "上级目录", example = "/2025/2/25")
 | 
				
			||||||
 | 
					    private String parentPath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 路径
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Schema(description = "路径", example = "/2025/2/25/6824afe8408da079832dcfb6.jpg")
 | 
				
			||||||
    private String path;
 | 
					    private String path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,39 +47,39 @@ public interface FileService extends BaseService<FileResp, FileResp, FileQuery,
 | 
				
			|||||||
     * @throws IOException /
 | 
					     * @throws IOException /
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    default FileInfo upload(MultipartFile file) throws IOException {
 | 
					    default FileInfo upload(MultipartFile file) throws IOException {
 | 
				
			||||||
        return upload(file, getDefaultFilePath(), null);
 | 
					        return upload(file, getDefaultParentPath(), null);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 上传到默认存储
 | 
					     * 上传到默认存储
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param file       文件信息
 | 
					     * @param file       文件信息
 | 
				
			||||||
     * @param path 文件路径
 | 
					     * @param parentPath 上级目录
 | 
				
			||||||
     * @return 文件信息
 | 
					     * @return 文件信息
 | 
				
			||||||
     * @throws IOException /
 | 
					     * @throws IOException /
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    default FileInfo upload(MultipartFile file, String path) throws IOException {
 | 
					    default FileInfo upload(MultipartFile file, String parentPath) throws IOException {
 | 
				
			||||||
        return upload(file, path, null);
 | 
					        return upload(file, parentPath, null);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 上传到指定存储
 | 
					     * 上传到指定存储
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param file        文件信息
 | 
					     * @param file        文件信息
 | 
				
			||||||
     * @param path        文件路径
 | 
					     * @param parentPath  上级目录
 | 
				
			||||||
     * @param storageCode 存储编码
 | 
					     * @param storageCode 存储编码
 | 
				
			||||||
     * @return 文件信息
 | 
					     * @return 文件信息
 | 
				
			||||||
     * @throws IOException /
 | 
					     * @throws IOException /
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    FileInfo upload(MultipartFile file, String path, String storageCode) throws IOException;
 | 
					    FileInfo upload(MultipartFile file, String parentPath, String storageCode) throws IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 根据存储 ID 列表查询
 | 
					     * 创建目录
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param storageIds 存储 ID 列表
 | 
					     * @param req 请求参数
 | 
				
			||||||
     * @return 文件数量
 | 
					     * @return ID
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    Long countByStorageIds(List<Long> storageIds);
 | 
					    Long createDir(FileReq req);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 查询文件资源统计信息
 | 
					     * 查询文件资源统计信息
 | 
				
			||||||
@@ -97,23 +97,31 @@ public interface FileService extends BaseService<FileResp, FileResp, FileQuery,
 | 
				
			|||||||
    FileResp check(String fileHash);
 | 
					    FileResp check(String fileHash);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 创建目录
 | 
					     * 计算文件夹大小
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param req 请求参数
 | 
					     * @param id ID
 | 
				
			||||||
     * @return ID
 | 
					     * @return 文件夹大小(字节)
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    Long createDir(FileReq req);
 | 
					    Long calcDirSize(Long id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 获取默认文件路径
 | 
					     * 根据存储 ID 列表查询
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param storageIds 存储 ID 列表
 | 
				
			||||||
 | 
					     * @return 文件数量
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    Long countByStorageIds(List<Long> storageIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取默认上级目录
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * <p>
 | 
					     * <p>
 | 
				
			||||||
     * 默认文件路径:yyyy/MM/dd/
 | 
					     * 默认上级目录:yyyy/MM/dd/
 | 
				
			||||||
     * </p>
 | 
					     * </p>
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return 默认文件路径
 | 
					     * @return 默认上级目录
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    default String getDefaultFilePath() {
 | 
					    default String getDefaultParentPath() {
 | 
				
			||||||
        LocalDate today = LocalDate.now();
 | 
					        LocalDate today = LocalDate.now();
 | 
				
			||||||
        return today.getYear() + StringConstants.SLASH + today.getMonthValue() + StringConstants.SLASH + today
 | 
					        return today.getYear() + StringConstants.SLASH + today.getMonthValue() + StringConstants.SLASH + today
 | 
				
			||||||
            .getDayOfMonth() + StringConstants.SLASH;
 | 
					            .getDayOfMonth() + StringConstants.SLASH;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,6 @@ package top.continew.admin.system.service.impl;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import cn.hutool.core.collection.CollUtil;
 | 
					import cn.hutool.core.collection.CollUtil;
 | 
				
			||||||
import cn.hutool.core.io.file.FileNameUtil;
 | 
					import cn.hutool.core.io.file.FileNameUtil;
 | 
				
			||||||
import cn.hutool.core.map.MapUtil;
 | 
					 | 
				
			||||||
import cn.hutool.core.util.ClassUtil;
 | 
					import cn.hutool.core.util.ClassUtil;
 | 
				
			||||||
import cn.hutool.core.util.StrUtil;
 | 
					import cn.hutool.core.util.StrUtil;
 | 
				
			||||||
import cn.hutool.core.util.URLUtil;
 | 
					import cn.hutool.core.util.URLUtil;
 | 
				
			||||||
@@ -45,10 +44,10 @@ import top.continew.admin.system.service.StorageService;
 | 
				
			|||||||
import top.continew.starter.core.constant.StringConstants;
 | 
					import top.continew.starter.core.constant.StringConstants;
 | 
				
			||||||
import top.continew.starter.core.util.StrUtils;
 | 
					import top.continew.starter.core.util.StrUtils;
 | 
				
			||||||
import top.continew.starter.core.validation.CheckUtils;
 | 
					import top.continew.starter.core.validation.CheckUtils;
 | 
				
			||||||
 | 
					import top.continew.starter.core.validation.ValidationUtils;
 | 
				
			||||||
import top.continew.starter.extension.crud.service.BaseServiceImpl;
 | 
					import top.continew.starter.extension.crud.service.BaseServiceImpl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.io.IOException;
 | 
					import java.io.IOException;
 | 
				
			||||||
import java.util.Arrays;
 | 
					 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
@@ -80,11 +79,9 @@ public class FileServiceImpl extends BaseServiceImpl<FileMapper, FileDO, FileRes
 | 
				
			|||||||
                    fileStorageService.delete(fileInfo);
 | 
					                    fileStorageService.delete(fileInfo);
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    // 不允许删除非空文件夹
 | 
					                    // 不允许删除非空文件夹
 | 
				
			||||||
                    String separator = StringConstants.SLASH.equals(file.getPath())
 | 
					 | 
				
			||||||
                        ? StringConstants.EMPTY
 | 
					 | 
				
			||||||
                        : StringConstants.SLASH;
 | 
					 | 
				
			||||||
                    boolean exists = baseMapper.lambdaQuery()
 | 
					                    boolean exists = baseMapper.lambdaQuery()
 | 
				
			||||||
                        .eq(FileDO::getPath, file.getPath() + separator + file.getName())
 | 
					                        .eq(FileDO::getParentPath, file.getPath())
 | 
				
			||||||
 | 
					                        .eq(FileDO::getStorageId, entry.getKey())
 | 
				
			||||||
                        .exists();
 | 
					                        .exists();
 | 
				
			||||||
                    CheckUtils.throwIf(exists, "文件夹 [{}] 不为空,请先删除文件夹下的内容", file.getName());
 | 
					                    CheckUtils.throwIf(exists, "文件夹 [{}] 不为空,请先删除文件夹下的内容", file.getName());
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -93,7 +90,7 @@ public class FileServiceImpl extends BaseServiceImpl<FileMapper, FileDO, FileRes
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public FileInfo upload(MultipartFile file, String path, String storageCode) throws IOException {
 | 
					    public FileInfo upload(MultipartFile file, String parentPath, String storageCode) throws IOException {
 | 
				
			||||||
        // 校验文件格式
 | 
					        // 校验文件格式
 | 
				
			||||||
        String extName = FileNameUtil.extName(file.getOriginalFilename());
 | 
					        String extName = FileNameUtil.extName(file.getOriginalFilename());
 | 
				
			||||||
        List<String> allExtensions = FileTypeEnum.getAllExtensions();
 | 
					        List<String> allExtensions = FileTypeEnum.getAllExtensions();
 | 
				
			||||||
@@ -106,7 +103,7 @@ public class FileServiceImpl extends BaseServiceImpl<FileMapper, FileDO, FileRes
 | 
				
			|||||||
            .setPlatform(storage.getCode())
 | 
					            .setPlatform(storage.getCode())
 | 
				
			||||||
            .setHashCalculatorSha256(true)
 | 
					            .setHashCalculatorSha256(true)
 | 
				
			||||||
            .putAttr(ClassUtil.getClassName(StorageDO.class, false), storage)
 | 
					            .putAttr(ClassUtil.getClassName(StorageDO.class, false), storage)
 | 
				
			||||||
            .setPath(this.pretreatmentPath(path));
 | 
					            .setPath(this.pretreatmentPath(parentPath));
 | 
				
			||||||
        // 图片文件生成缩略图
 | 
					        // 图片文件生成缩略图
 | 
				
			||||||
        if (FileTypeEnum.IMAGE.getExtensions().contains(extName)) {
 | 
					        if (FileTypeEnum.IMAGE.getExtensions().contains(extName)) {
 | 
				
			||||||
            uploadPretreatment.setIgnoreThumbnailException(true, true);
 | 
					            uploadPretreatment.setIgnoreThumbnailException(true, true);
 | 
				
			||||||
@@ -129,17 +126,40 @@ public class FileServiceImpl extends BaseServiceImpl<FileMapper, FileDO, FileRes
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        // 创建父级目录
 | 
					        // 创建父级目录
 | 
				
			||||||
        this.createDir(path, storage);
 | 
					        this.createParentDir(parentPath, storage);
 | 
				
			||||||
        // 上传
 | 
					        // 上传
 | 
				
			||||||
        return uploadPretreatment.upload();
 | 
					        return uploadPretreatment.upload();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Long countByStorageIds(List<Long> storageIds) {
 | 
					    public Long createDir(FileReq req) {
 | 
				
			||||||
        if (CollUtil.isEmpty(storageIds)) {
 | 
					        String parentPath = req.getParentPath();
 | 
				
			||||||
            return 0L;
 | 
					        FileDO file = baseMapper.lambdaQuery()
 | 
				
			||||||
 | 
					            .eq(FileDO::getParentPath, parentPath)
 | 
				
			||||||
 | 
					            .eq(FileDO::getName, req.getOriginalName())
 | 
				
			||||||
 | 
					            .eq(FileDO::getType, FileTypeEnum.DIR)
 | 
				
			||||||
 | 
					            .one();
 | 
				
			||||||
 | 
					        CheckUtils.throwIfNotNull(file, "文件夹已存在");
 | 
				
			||||||
 | 
					        // 存储引擎需要一致
 | 
				
			||||||
 | 
					        StorageDO storage = storageService.getDefaultStorage();
 | 
				
			||||||
 | 
					        if (!StringConstants.SLASH.equals(parentPath)) {
 | 
				
			||||||
 | 
					            FileDO parentFile = baseMapper.lambdaQuery()
 | 
				
			||||||
 | 
					                .eq(FileDO::getPath, parentPath)
 | 
				
			||||||
 | 
					                .eq(FileDO::getType, FileTypeEnum.DIR)
 | 
				
			||||||
 | 
					                .one();
 | 
				
			||||||
 | 
					            CheckUtils.throwIfNull(parentFile, "父级文件夹不存在");
 | 
				
			||||||
 | 
					            CheckUtils.throwIfNotEqual(parentFile.getStorageId(), storage.getId(), "文件夹和父级文件夹存储引擎不一致");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return baseMapper.lambdaQuery().in(FileDO::getStorageId, storageIds).count();
 | 
					        // 创建文件夹
 | 
				
			||||||
 | 
					        FileDO dirFile = new FileDO();
 | 
				
			||||||
 | 
					        String originalName = req.getOriginalName();
 | 
				
			||||||
 | 
					        dirFile.setName(originalName);
 | 
				
			||||||
 | 
					        dirFile.setOriginalName(originalName);
 | 
				
			||||||
 | 
					        dirFile.setParentPath(parentPath);
 | 
				
			||||||
 | 
					        dirFile.setType(FileTypeEnum.DIR);
 | 
				
			||||||
 | 
					        dirFile.setStorageId(storage.getId());
 | 
				
			||||||
 | 
					        baseMapper.insert(dirFile);
 | 
				
			||||||
 | 
					        return dirFile.getId();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
@@ -165,17 +185,31 @@ public class FileServiceImpl extends BaseServiceImpl<FileMapper, FileDO, FileRes
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Long createDir(FileReq req) {
 | 
					    public Long calcDirSize(Long id) {
 | 
				
			||||||
        StorageDO storage = storageService.getDefaultStorage();
 | 
					        FileDO dirFile = super.getById(id);
 | 
				
			||||||
        FileDO file = new FileDO();
 | 
					        ValidationUtils.throwIfNotEqual(dirFile.getType(), FileTypeEnum.DIR, "ID 为 [{}] 的不是文件夹,不支持计算大小", id);
 | 
				
			||||||
        file.setName(req.getOriginalName());
 | 
					        // 查询当前文件夹下的所有子文件和子文件夹
 | 
				
			||||||
        file.setOriginalName(req.getOriginalName());
 | 
					        List<FileDO> children = baseMapper.lambdaQuery().eq(FileDO::getParentPath, dirFile.getPath()).list();
 | 
				
			||||||
        file.setSize(0L);
 | 
					        if (CollUtil.isEmpty(children)) {
 | 
				
			||||||
        file.setPath(req.getPath());
 | 
					            return 0L;
 | 
				
			||||||
        file.setType(FileTypeEnum.DIR);
 | 
					        }
 | 
				
			||||||
        file.setStorageId(storage.getId());
 | 
					        // 累加子文件大小和递归计算子文件夹大小
 | 
				
			||||||
        baseMapper.insert(file);
 | 
					        return children.stream().mapToLong(child -> {
 | 
				
			||||||
        return file.getId();
 | 
					            if (FileTypeEnum.DIR.equals(child.getType())) {
 | 
				
			||||||
 | 
					                // 递归计算子文件夹大小
 | 
				
			||||||
 | 
					                return calcDirSize(child.getId());
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                return child.getSize();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }).sum();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public Long countByStorageIds(List<Long> storageIds) {
 | 
				
			||||||
 | 
					        if (CollUtil.isEmpty(storageIds)) {
 | 
				
			||||||
 | 
					            return 0L;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return baseMapper.lambdaQuery().in(FileDO::getStorageId, storageIds).count();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
@@ -183,12 +217,14 @@ public class FileServiceImpl extends BaseServiceImpl<FileMapper, FileDO, FileRes
 | 
				
			|||||||
        super.fill(obj);
 | 
					        super.fill(obj);
 | 
				
			||||||
        if (obj instanceof FileResp fileResp) {
 | 
					        if (obj instanceof FileResp fileResp) {
 | 
				
			||||||
            StorageDO storage = storageService.getById(fileResp.getStorageId());
 | 
					            StorageDO storage = storageService.getById(fileResp.getStorageId());
 | 
				
			||||||
            String prefix = StrUtil.blankToDefault(storage.getDomain(), storage.getEndpoint());
 | 
					            String prefix = storage.getUrlPrefix();
 | 
				
			||||||
            String path = fileResp.getPath();
 | 
					            String url = URLUtil.normalize(prefix + fileResp.getPath(), false, true);
 | 
				
			||||||
            String url = URLUtil.normalize(prefix + path + StringConstants.SLASH + fileResp.getName(), false, true);
 | 
					 | 
				
			||||||
            fileResp.setUrl(url);
 | 
					            fileResp.setUrl(url);
 | 
				
			||||||
 | 
					            String parentPath = StringConstants.SLASH.equals(fileResp.getParentPath())
 | 
				
			||||||
 | 
					                ? StringConstants.EMPTY
 | 
				
			||||||
 | 
					                : fileResp.getParentPath();
 | 
				
			||||||
            String thumbnailUrl = StrUtils.blankToDefault(fileResp.getThumbnailName(), url, thName -> URLUtil
 | 
					            String thumbnailUrl = StrUtils.blankToDefault(fileResp.getThumbnailName(), url, thName -> URLUtil
 | 
				
			||||||
                .normalize(prefix + path + StringConstants.SLASH + thName, false, true));
 | 
					                .normalize(prefix + parentPath + StringConstants.SLASH + thName, false, true));
 | 
				
			||||||
            fileResp.setThumbnailUrl(thumbnailUrl);
 | 
					            fileResp.setThumbnailUrl(thumbnailUrl);
 | 
				
			||||||
            fileResp.setStorageName("%s (%s)".formatted(storage.getName(), storage.getCode()));
 | 
					            fileResp.setStorageName("%s (%s)".formatted(storage.getName(), storage.getCode()));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -198,7 +234,7 @@ public class FileServiceImpl extends BaseServiceImpl<FileMapper, FileDO, FileRes
 | 
				
			|||||||
     * 处理路径
 | 
					     * 处理路径
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * <p>
 | 
					     * <p>
 | 
				
			||||||
     * 1.如果 path 为空,则使用 {@link FileService#getDefaultFilePath()} 作为默认值 <br />
 | 
					     * 1.如果 path 为空,则使用 {@link FileService#getDefaultParentPath()} 作为默认值 <br />
 | 
				
			||||||
     * 2.如果 path 为 {@code /},则设置为空 <br />
 | 
					     * 2.如果 path 为 {@code /},则设置为空 <br />
 | 
				
			||||||
     * 3.如果 path 不以 {@code /} 结尾,则添加后缀 {@code /} <br />
 | 
					     * 3.如果 path 不以 {@code /} 结尾,则添加后缀 {@code /} <br />
 | 
				
			||||||
     * 4.如果 path 以 {@code /} 开头,则移除前缀 {@code /} <br />
 | 
					     * 4.如果 path 以 {@code /} 开头,则移除前缀 {@code /} <br />
 | 
				
			||||||
@@ -210,7 +246,7 @@ public class FileServiceImpl extends BaseServiceImpl<FileMapper, FileDO, FileRes
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    private String pretreatmentPath(String path) {
 | 
					    private String pretreatmentPath(String path) {
 | 
				
			||||||
        if (StrUtil.isBlank(path)) {
 | 
					        if (StrUtil.isBlank(path)) {
 | 
				
			||||||
            return this.getDefaultFilePath();
 | 
					            return this.getDefaultParentPath();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (StringConstants.SLASH.equals(path)) {
 | 
					        if (StringConstants.SLASH.equals(path)) {
 | 
				
			||||||
            return StringConstants.EMPTY;
 | 
					            return StringConstants.EMPTY;
 | 
				
			||||||
@@ -219,47 +255,48 @@ public class FileServiceImpl extends BaseServiceImpl<FileMapper, FileDO, FileRes
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 创建文件夹(支持多级)
 | 
					     * 创建上级文件夹(支持多级)
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * <p>
 | 
					     * <p>
 | 
				
			||||||
     * user/avatar/ => user(path:/)、avatar(path:/user)
 | 
					     * user/avatar/ => user(path:/user)、avatar(path:/user/avatar)
 | 
				
			||||||
     * </p>
 | 
					     * </p>
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param dirPath 路径
 | 
					     * @param parentPath 上级目录
 | 
				
			||||||
     * @param storage    存储配置
 | 
					     * @param storage    存储配置
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private void createDir(String dirPath, StorageDO storage) {
 | 
					    private void createParentDir(String parentPath, StorageDO storage) {
 | 
				
			||||||
        if (StrUtil.isBlank(dirPath) || StringConstants.SLASH.equals(dirPath)) {
 | 
					        if (StrUtil.isBlank(parentPath) || StringConstants.SLASH.equals(parentPath)) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // user/avatar/ => user:/、avatar:path:/user
 | 
					        // user/avatar/ => user、avatar
 | 
				
			||||||
        String[] paths = StrUtil.split(dirPath, StringConstants.SLASH, false, true).toArray(String[]::new);
 | 
					        String[] parentPathParts = StrUtil.split(parentPath, StringConstants.SLASH, false, true).toArray(String[]::new);
 | 
				
			||||||
        Map<String, String> pathMap = MapUtil.newHashMap(paths.length, true);
 | 
					        String lastPath = StringConstants.SLASH;
 | 
				
			||||||
        for (int i = 0; i < paths.length; i++) {
 | 
					        StringBuilder currentPathBuilder = new StringBuilder();
 | 
				
			||||||
            String key = paths[i];
 | 
					        for (int i = 0; i < parentPathParts.length; i++) {
 | 
				
			||||||
            String path = (i == 0)
 | 
					            String parentPathPart = parentPathParts[i];
 | 
				
			||||||
                ? StringConstants.SLASH
 | 
					            if (i > 0) {
 | 
				
			||||||
                : StringConstants.SLASH + String.join(StringConstants.SLASH, Arrays.copyOfRange(paths, 0, i));
 | 
					                lastPath = currentPathBuilder.toString();
 | 
				
			||||||
            pathMap.put(key, path);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        // 创建文件夹
 | 
					            // /user、/user/avatar
 | 
				
			||||||
        for (Map.Entry<String, String> entry : pathMap.entrySet()) {
 | 
					            currentPathBuilder.append(StringConstants.SLASH).append(parentPathPart);
 | 
				
			||||||
            String key = entry.getKey();
 | 
					            String currentPath = currentPathBuilder.toString();
 | 
				
			||||||
            String path = entry.getValue();
 | 
					            // 文件夹和文件存储引擎需要一致
 | 
				
			||||||
            if (!baseMapper.lambdaQuery()
 | 
					            FileDO dirFile = baseMapper.lambdaQuery()
 | 
				
			||||||
                .eq(FileDO::getPath, path)
 | 
					                .eq(FileDO::getPath, currentPath)
 | 
				
			||||||
                .eq(FileDO::getName, key)
 | 
					 | 
				
			||||||
                .eq(FileDO::getType, FileTypeEnum.DIR)
 | 
					                .eq(FileDO::getType, FileTypeEnum.DIR)
 | 
				
			||||||
                .exists()) {
 | 
					                .one();
 | 
				
			||||||
 | 
					            if (dirFile != null) {
 | 
				
			||||||
 | 
					                CheckUtils.throwIfNotEqual(dirFile.getStorageId(), storage.getId(), "文件夹和上传文件存储引擎不一致");
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            FileDO file = new FileDO();
 | 
					            FileDO file = new FileDO();
 | 
				
			||||||
                file.setName(key);
 | 
					            file.setName(parentPathPart);
 | 
				
			||||||
                file.setOriginalName(key);
 | 
					            file.setOriginalName(parentPathPart);
 | 
				
			||||||
                file.setSize(0L);
 | 
					            file.setPath(currentPath);
 | 
				
			||||||
                file.setPath(path);
 | 
					            file.setParentPath(lastPath);
 | 
				
			||||||
            file.setType(FileTypeEnum.DIR);
 | 
					            file.setType(FileTypeEnum.DIR);
 | 
				
			||||||
            file.setStorageId(storage.getId());
 | 
					            file.setStorageId(storage.getId());
 | 
				
			||||||
            baseMapper.insert(file);
 | 
					            baseMapper.insert(file);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -16,6 +16,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package top.continew.admin.system.service.impl;
 | 
					package top.continew.admin.system.service.impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import cn.hutool.core.bean.BeanUtil;
 | 
				
			||||||
import cn.hutool.core.map.MapUtil;
 | 
					import cn.hutool.core.map.MapUtil;
 | 
				
			||||||
import cn.hutool.core.util.StrUtil;
 | 
					import cn.hutool.core.util.StrUtil;
 | 
				
			||||||
import cn.hutool.core.util.URLUtil;
 | 
					import cn.hutool.core.util.URLUtil;
 | 
				
			||||||
@@ -78,13 +79,9 @@ public class StorageServiceImpl extends BaseServiceImpl<StorageMapper, StorageDO
 | 
				
			|||||||
        CheckUtils.throwIf(this.isCodeExists(code, null), "新增失败,[{}] 已存在", code);
 | 
					        CheckUtils.throwIf(this.isCodeExists(code, null), "新增失败,[{}] 已存在", code);
 | 
				
			||||||
        // 需要独立操作来指定默认存储
 | 
					        // 需要独立操作来指定默认存储
 | 
				
			||||||
        req.setIsDefault(false);
 | 
					        req.setIsDefault(false);
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void afterCreate(StorageReq req, StorageDO entity) {
 | 
					 | 
				
			||||||
        // 加载存储引擎
 | 
					        // 加载存储引擎
 | 
				
			||||||
        if (DisEnableStatusEnum.ENABLE.equals(entity.getStatus())) {
 | 
					        if (DisEnableStatusEnum.ENABLE.equals(req.getStatus())) {
 | 
				
			||||||
            this.load(entity);
 | 
					            this.load(BeanUtil.copyProperties(req, StorageDO.class));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -107,19 +104,16 @@ public class StorageServiceImpl extends BaseServiceImpl<StorageMapper, StorageDO
 | 
				
			|||||||
        storageType.pretreatment(req);
 | 
					        storageType.pretreatment(req);
 | 
				
			||||||
        // 卸载存储引擎
 | 
					        // 卸载存储引擎
 | 
				
			||||||
        this.unload(oldStorage);
 | 
					        this.unload(oldStorage);
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void afterUpdate(StorageReq req, StorageDO entity) {
 | 
					 | 
				
			||||||
        // 加载存储引擎
 | 
					        // 加载存储引擎
 | 
				
			||||||
        if (DisEnableStatusEnum.ENABLE.equals(entity.getStatus())) {
 | 
					        if (DisEnableStatusEnum.ENABLE.equals(newStatus)) {
 | 
				
			||||||
            this.load(entity);
 | 
					            BeanUtil.copyProperties(req, oldStorage);
 | 
				
			||||||
 | 
					            this.load(oldStorage);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void beforeDelete(List<Long> ids) {
 | 
					    public void beforeDelete(List<Long> ids) {
 | 
				
			||||||
        CheckUtils.throwIf(fileService.countByStorageIds(ids) > 0, "所选存储存在文件关联,请删除文件后重试");
 | 
					        CheckUtils.throwIf(fileService.countByStorageIds(ids) > 0, "所选存储存在文件或文件夹关联,请删除后重试");
 | 
				
			||||||
        List<StorageDO> storageList = baseMapper.lambdaQuery().in(StorageDO::getId, ids).list();
 | 
					        List<StorageDO> storageList = baseMapper.lambdaQuery().in(StorageDO::getId, ids).list();
 | 
				
			||||||
        storageList.forEach(storage -> {
 | 
					        storageList.forEach(storage -> {
 | 
				
			||||||
            CheckUtils.throwIfEqual(Boolean.TRUE, storage.getIsDefault(), "[{}] 是默认存储,不允许删除", storage.getName());
 | 
					            CheckUtils.throwIfEqual(Boolean.TRUE, storage.getIsDefault(), "[{}] 是默认存储,不允许删除", storage.getName());
 | 
				
			||||||
@@ -204,7 +198,7 @@ public class StorageServiceImpl extends BaseServiceImpl<StorageMapper, StorageDO
 | 
				
			|||||||
                config.setSecretKey(storage.getSecretKey());
 | 
					                config.setSecretKey(storage.getSecretKey());
 | 
				
			||||||
                config.setEndPoint(storage.getEndpoint());
 | 
					                config.setEndPoint(storage.getEndpoint());
 | 
				
			||||||
                config.setBucketName(storage.getBucketName());
 | 
					                config.setBucketName(storage.getBucketName());
 | 
				
			||||||
                config.setDomain(storage.getDomain());
 | 
					                config.setDomain(StrUtil.emptyIfNull(storage.getDomain()));
 | 
				
			||||||
                fileStorageList.addAll(FileStorageServiceBuilder.buildAmazonS3FileStorage(Collections
 | 
					                fileStorageList.addAll(FileStorageServiceBuilder.buildAmazonS3FileStorage(Collections
 | 
				
			||||||
                    .singletonList(config), null));
 | 
					                    .singletonList(config), null));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -67,9 +67,10 @@ public class CommonController {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Operation(summary = "上传文件", description = "上传文件")
 | 
					    @Operation(summary = "上传文件", description = "上传文件")
 | 
				
			||||||
    @PostMapping("/file")
 | 
					    @PostMapping("/file")
 | 
				
			||||||
    public FileUploadResp upload(@NotNull(message = "文件不能为空") MultipartFile file, String path) throws IOException {
 | 
					    public FileUploadResp upload(@NotNull(message = "文件不能为空") MultipartFile file,
 | 
				
			||||||
 | 
					                                 String parentPath) throws IOException {
 | 
				
			||||||
        ValidationUtils.throwIf(file::isEmpty, "文件不能为空");
 | 
					        ValidationUtils.throwIf(file::isEmpty, "文件不能为空");
 | 
				
			||||||
        FileInfo fileInfo = fileService.upload(file, path);
 | 
					        FileInfo fileInfo = fileService.upload(file, parentPath);
 | 
				
			||||||
        return FileUploadResp.builder()
 | 
					        return FileUploadResp.builder()
 | 
				
			||||||
            .id(fileInfo.getId())
 | 
					            .id(fileInfo.getId())
 | 
				
			||||||
            .url(fileInfo.getUrl())
 | 
					            .url(fileInfo.getUrl())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,19 +19,27 @@ package top.continew.admin.controller.system;
 | 
				
			|||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
 | 
					import cn.dev33.satoken.annotation.SaCheckPermission;
 | 
				
			||||||
import io.swagger.v3.oas.annotations.Operation;
 | 
					import io.swagger.v3.oas.annotations.Operation;
 | 
				
			||||||
import io.swagger.v3.oas.annotations.tags.Tag;
 | 
					import io.swagger.v3.oas.annotations.tags.Tag;
 | 
				
			||||||
 | 
					import jakarta.validation.constraints.NotNull;
 | 
				
			||||||
import lombok.RequiredArgsConstructor;
 | 
					import lombok.RequiredArgsConstructor;
 | 
				
			||||||
 | 
					import org.dromara.x.file.storage.core.FileInfo;
 | 
				
			||||||
import org.springframework.web.bind.annotation.*;
 | 
					import org.springframework.web.bind.annotation.*;
 | 
				
			||||||
 | 
					import org.springframework.web.multipart.MultipartFile;
 | 
				
			||||||
import top.continew.admin.common.controller.BaseController;
 | 
					import top.continew.admin.common.controller.BaseController;
 | 
				
			||||||
import top.continew.admin.system.model.query.FileQuery;
 | 
					import top.continew.admin.system.model.query.FileQuery;
 | 
				
			||||||
import top.continew.admin.system.model.req.FileReq;
 | 
					import top.continew.admin.system.model.req.FileReq;
 | 
				
			||||||
 | 
					import top.continew.admin.system.model.resp.file.FileDirCalcSizeResp;
 | 
				
			||||||
import top.continew.admin.system.model.resp.file.FileResp;
 | 
					import top.continew.admin.system.model.resp.file.FileResp;
 | 
				
			||||||
import top.continew.admin.system.model.resp.file.FileStatisticsResp;
 | 
					import top.continew.admin.system.model.resp.file.FileStatisticsResp;
 | 
				
			||||||
 | 
					import top.continew.admin.system.model.resp.file.FileUploadResp;
 | 
				
			||||||
import top.continew.admin.system.service.FileService;
 | 
					import top.continew.admin.system.service.FileService;
 | 
				
			||||||
 | 
					import top.continew.starter.core.validation.ValidationUtils;
 | 
				
			||||||
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
 | 
					import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
 | 
				
			||||||
import top.continew.starter.extension.crud.enums.Api;
 | 
					import top.continew.starter.extension.crud.enums.Api;
 | 
				
			||||||
import top.continew.starter.extension.crud.model.resp.IdResp;
 | 
					import top.continew.starter.extension.crud.model.resp.IdResp;
 | 
				
			||||||
import top.continew.starter.log.annotation.Log;
 | 
					import top.continew.starter.log.annotation.Log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 文件管理 API
 | 
					 * 文件管理 API
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@@ -44,6 +52,46 @@ import top.continew.starter.log.annotation.Log;
 | 
				
			|||||||
@CrudRequestMapping(value = "/system/file", api = {Api.PAGE, Api.UPDATE, Api.DELETE})
 | 
					@CrudRequestMapping(value = "/system/file", api = {Api.PAGE, Api.UPDATE, Api.DELETE})
 | 
				
			||||||
public class FileController extends BaseController<FileService, FileResp, FileResp, FileQuery, FileReq> {
 | 
					public class FileController extends BaseController<FileService, FileResp, FileResp, FileQuery, FileReq> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 上传文件
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * 公共上传文件请使用 {@link top.continew.admin.controller.common.CommonController#upload}
 | 
				
			||||||
 | 
					     * </p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param file       文件
 | 
				
			||||||
 | 
					     * @param parentPath 上级目录
 | 
				
			||||||
 | 
					     * @return 文件上传响应参数
 | 
				
			||||||
 | 
					     * @throws IOException /
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @SaCheckPermission("system:file:upload")
 | 
				
			||||||
 | 
					    @Operation(summary = "上传文件", description = "上传文件")
 | 
				
			||||||
 | 
					    @PostMapping("/upload")
 | 
				
			||||||
 | 
					    public FileUploadResp upload(@NotNull(message = "文件不能为空") MultipartFile file,
 | 
				
			||||||
 | 
					                                 String parentPath) throws IOException {
 | 
				
			||||||
 | 
					        ValidationUtils.throwIf(file::isEmpty, "文件不能为空");
 | 
				
			||||||
 | 
					        FileInfo fileInfo = baseService.upload(file, parentPath);
 | 
				
			||||||
 | 
					        return FileUploadResp.builder()
 | 
				
			||||||
 | 
					            .id(fileInfo.getId())
 | 
				
			||||||
 | 
					            .url(fileInfo.getUrl())
 | 
				
			||||||
 | 
					            .thUrl(fileInfo.getThUrl())
 | 
				
			||||||
 | 
					            .metadata(fileInfo.getMetadata())
 | 
				
			||||||
 | 
					            .build();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Operation(summary = "创建文件夹", description = "创建文件夹")
 | 
				
			||||||
 | 
					    @SaCheckPermission("system:file:createDir")
 | 
				
			||||||
 | 
					    @PostMapping("/dir")
 | 
				
			||||||
 | 
					    public IdResp<Long> createDir(@RequestBody FileReq req) {
 | 
				
			||||||
 | 
					        return new IdResp<>(baseService.createDir(req));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Operation(summary = "计算文件夹大小", description = "计算文件夹大小")
 | 
				
			||||||
 | 
					    @SaCheckPermission("system:file:calcDirSize")
 | 
				
			||||||
 | 
					    @GetMapping("/dir/{id}/size")
 | 
				
			||||||
 | 
					    public FileDirCalcSizeResp calcDirSize(@PathVariable Long id) {
 | 
				
			||||||
 | 
					        return new FileDirCalcSizeResp(baseService.calcDirSize(id));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Log(ignore = true)
 | 
					    @Log(ignore = true)
 | 
				
			||||||
    @Operation(summary = "查询文件资源统计", description = "查询文件资源统计")
 | 
					    @Operation(summary = "查询文件资源统计", description = "查询文件资源统计")
 | 
				
			||||||
    @SaCheckPermission("system:file:list")
 | 
					    @SaCheckPermission("system:file:list")
 | 
				
			||||||
@@ -59,10 +107,4 @@ public class FileController extends BaseController<FileService, FileResp, FileRe
 | 
				
			|||||||
    public FileResp checkFile(String fileHash) {
 | 
					    public FileResp checkFile(String fileHash) {
 | 
				
			||||||
        return baseService.check(fileHash);
 | 
					        return baseService.check(fileHash);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Operation(summary = "创建文件夹", description = "创建文件夹")
 | 
					 | 
				
			||||||
    @PostMapping("/dir")
 | 
					 | 
				
			||||||
    public IdResp<Long> createDir(@RequestBody FileReq req) {
 | 
					 | 
				
			||||||
        return new IdResp<>(baseService.createDir(req));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -58,6 +58,8 @@ VALUES
 | 
				
			|||||||
(1114, '修改', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:update', 4, 1, 1, NOW()),
 | 
					(1114, '修改', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:update', 4, 1, 1, NOW()),
 | 
				
			||||||
(1115, '删除', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:delete', 5, 1, 1, NOW()),
 | 
					(1115, '删除', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:delete', 5, 1, 1, NOW()),
 | 
				
			||||||
(1116, '下载', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:download', 6, 1, 1, NOW()),
 | 
					(1116, '下载', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:download', 6, 1, 1, NOW()),
 | 
				
			||||||
 | 
					(1117, '创建文件夹', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:createDir', 7, 1, 1, NOW()),
 | 
				
			||||||
 | 
					(1118, '计算文件夹大小', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:calcDirSize', 8, 1, 1, NOW()),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(1130, '字典管理', 1000, 2, '/system/dict', 'SystemDict', 'system/dict/index', NULL, 'bookmark', b'0', b'0', b'0', NULL, 7, 1, 1, NOW()),
 | 
					(1130, '字典管理', 1000, 2, '/system/dict', 'SystemDict', 'system/dict/index', NULL, 'bookmark', b'0', b'0', b'0', NULL, 7, 1, 1, NOW()),
 | 
				
			||||||
(1131, '列表', 1130, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:list', 1, 1, 1, NOW()),
 | 
					(1131, '列表', 1130, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:list', 1, 1, 1, NOW()),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -280,8 +280,9 @@ CREATE TABLE IF NOT EXISTS `sys_file` (
 | 
				
			|||||||
    `id`                 bigint(20)    NOT NULL AUTO_INCREMENT     COMMENT 'ID',
 | 
					    `id`                 bigint(20)    NOT NULL AUTO_INCREMENT     COMMENT 'ID',
 | 
				
			||||||
    `name`               varchar(255)  NOT NULL                    COMMENT '名称',
 | 
					    `name`               varchar(255)  NOT NULL                    COMMENT '名称',
 | 
				
			||||||
    `original_name`      varchar(255)  NOT NULL                    COMMENT '原始名称',
 | 
					    `original_name`      varchar(255)  NOT NULL                    COMMENT '原始名称',
 | 
				
			||||||
    `size`               bigint(20)    NOT NULL                    COMMENT '大小(字节)',
 | 
					    `size`               bigint(20)    DEFAULT NULL                COMMENT '大小(字节)',
 | 
				
			||||||
    `path`               varchar(512)  NOT NULL                    COMMENT '存储路径',
 | 
					    `parent_path`        varchar(512)  NOT NULL DEFAULT '/'        COMMENT '上级目录',
 | 
				
			||||||
 | 
					    `path`               varchar(512)  NOT NULL                    COMMENT '路径',
 | 
				
			||||||
    `extension`          varchar(32)   DEFAULT NULL                COMMENT '扩展名',
 | 
					    `extension`          varchar(32)   DEFAULT NULL                COMMENT '扩展名',
 | 
				
			||||||
    `content_type`       varchar(255)  DEFAULT NULL                COMMENT '内容类型',
 | 
					    `content_type`       varchar(255)  DEFAULT NULL                COMMENT '内容类型',
 | 
				
			||||||
    `type`               tinyint(1)    UNSIGNED NOT NULL DEFAULT 1 COMMENT '类型(0: 目录;1:其他;2:图片;3:文档;4:视频;5:音频)',
 | 
					    `type`               tinyint(1)    UNSIGNED NOT NULL DEFAULT 1 COMMENT '类型(0: 目录;1:其他;2:图片;3:文档;4:视频;5:音频)',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,6 +58,8 @@ VALUES
 | 
				
			|||||||
(1114, '修改', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:update', 4, 1, 1, NOW()),
 | 
					(1114, '修改', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:update', 4, 1, 1, NOW()),
 | 
				
			||||||
(1115, '删除', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:delete', 5, 1, 1, NOW()),
 | 
					(1115, '删除', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:delete', 5, 1, 1, NOW()),
 | 
				
			||||||
(1116, '下载', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:download', 6, 1, 1, NOW()),
 | 
					(1116, '下载', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:download', 6, 1, 1, NOW()),
 | 
				
			||||||
 | 
					(1117, '创建文件夹', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:createDir', 7, 1, 1, NOW()),
 | 
				
			||||||
 | 
					(1118, '计算文件夹大小', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:calcDirSize', 8, 1, 1, NOW()),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(1130, '字典管理', 1000, 2, '/system/dict', 'SystemDict', 'system/dict/index', NULL, 'bookmark', false, false, false, NULL, 7, 1, 1, NOW()),
 | 
					(1130, '字典管理', 1000, 2, '/system/dict', 'SystemDict', 'system/dict/index', NULL, 'bookmark', false, false, false, NULL, 7, 1, 1, NOW()),
 | 
				
			||||||
(1131, '列表', 1130, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:list', 1, 1, 1, NOW()),
 | 
					(1131, '列表', 1130, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:dict:list', 1, 1, 1, NOW()),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -466,7 +466,8 @@ CREATE TABLE IF NOT EXISTS "sys_file" (
 | 
				
			|||||||
    "id"                 int8         NOT NULL,
 | 
					    "id"                 int8         NOT NULL,
 | 
				
			||||||
    "name"               varchar(255) NOT NULL,
 | 
					    "name"               varchar(255) NOT NULL,
 | 
				
			||||||
    "original_name"      varchar(255) NOT NULL,
 | 
					    "original_name"      varchar(255) NOT NULL,
 | 
				
			||||||
    "size"               int8         NOT NULL,
 | 
					    "size"               int8         DEFAULT NULL,
 | 
				
			||||||
 | 
					    "parent_path"        varchar(512) NOT NULL DEFAULT '/',
 | 
				
			||||||
    "path"               varchar(512) NOT NULL,
 | 
					    "path"               varchar(512) NOT NULL,
 | 
				
			||||||
    "extension"          varchar(100) DEFAULT NULL,
 | 
					    "extension"          varchar(100) DEFAULT NULL,
 | 
				
			||||||
    "content_type"       varchar(255) DEFAULT NULL,
 | 
					    "content_type"       varchar(255) DEFAULT NULL,
 | 
				
			||||||
@@ -491,7 +492,8 @@ COMMENT ON COLUMN "sys_file"."id"                 IS 'ID';
 | 
				
			|||||||
COMMENT ON COLUMN "sys_file"."name"               IS '名称';
 | 
					COMMENT ON COLUMN "sys_file"."name"               IS '名称';
 | 
				
			||||||
COMMENT ON COLUMN "sys_file"."original_name"      IS '原始名称';
 | 
					COMMENT ON COLUMN "sys_file"."original_name"      IS '原始名称';
 | 
				
			||||||
COMMENT ON COLUMN "sys_file"."size"               IS '大小(字节)';
 | 
					COMMENT ON COLUMN "sys_file"."size"               IS '大小(字节)';
 | 
				
			||||||
COMMENT ON COLUMN "sys_file"."path"               IS '存储路径';
 | 
					COMMENT ON COLUMN "sys_file"."parent_path"        IS '上级目录';
 | 
				
			||||||
 | 
					COMMENT ON COLUMN "sys_file"."path"               IS '路径';
 | 
				
			||||||
COMMENT ON COLUMN "sys_file"."extension"          IS '扩展名';
 | 
					COMMENT ON COLUMN "sys_file"."extension"          IS '扩展名';
 | 
				
			||||||
COMMENT ON COLUMN "sys_file"."content_type"       IS '内容类型';
 | 
					COMMENT ON COLUMN "sys_file"."content_type"       IS '内容类型';
 | 
				
			||||||
COMMENT ON COLUMN "sys_file"."type"               IS '类型(0: 目录;1:其他;2:图片;3:文档;4:视频;5:音频)';
 | 
					COMMENT ON COLUMN "sys_file"."type"               IS '类型(0: 目录;1:其他;2:图片;3:文档;4:视频;5:音频)';
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user