完善网盘服务

This commit is contained in:
2024-01-09 21:46:08 +08:00
parent 01b5788256
commit e9bd170633
6 changed files with 486 additions and 212 deletions

View File

@@ -34,7 +34,6 @@ import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import javax.crypto.Mac;
@@ -54,159 +53,165 @@ public class SignFilter extends OncePerRequestFilter {
@Autowired RedisTemplate<String, String> redisTemplate;
private static String getFieldFromHeaderOrQs(HttpServletRequest request, String name) {
return StringUtils.defaultIfEmpty(request.getHeader(name), request.getParameter(name));
}
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (sysConfigService.isEnable(Constants.SYSCONFIG_ENABLE_REQUEST_SIGN)) {
StopWatch stopWatch = new StopWatch();
stopWatch.start("基本请求头");
StringBuilder sb = new StringBuilder();
// 请求方法
sb.append(StringUtils.defaultString(request.getMethod().toUpperCase()));
sb.append(StringUtils.defaultString("\n"));
// 请求地址
sb.append(
StringUtils.defaultString(
ObjectUtils.defaultIfNull(
request.getHeader(Constants.HTTP_HEADER_CLIENT_URL),
request.getRequestURI())));
sb.append(StringUtils.defaultString("\n"));
// 请求参数
sb.append(StringUtils.defaultString(request.getQueryString()));
sb.append(StringUtils.defaultString("\n"));
// 请求头
String token =
ObjectUtils.defaultIfNull(
request.getHeader(Constants.HTTP_HEADER_TOKEN), "anonymous");
String ts = request.getHeader(Constants.HTTP_HEADER_TS);
if (StringUtils.isEmpty(ts)) {
RequestUtils.writeResponse(
new BizException(Constants.HTTP_HEADER_TS + " 请求头缺失"), response);
return;
}
String random = request.getHeader(Constants.HTTP_HEADER_CLIENT_RANDOM);
if (StringUtils.isEmpty(random)) {
RequestUtils.writeResponse(
new BizException(Constants.HTTP_HEADER_CLIENT_RANDOM + " 请求头缺失"), response);
return;
}
long tsi = 0;
try {
tsi = Long.parseLong(ts);
} catch (Exception e) {
RequestUtils.writeResponse(
new BizException(Constants.HTTP_HEADER_TS + " 请求头格式错误"), response);
return;
}
long current = System.currentTimeMillis();
if (Math.abs(tsi - current) > TIME_DIFF_MS) {
RequestUtils.writeResponse(
new BizException(
Constants.HTTP_HEADER_TS
+ " 客户端时间误差过大,请校准时间. 服务器时间为: "
+ LocalDateTime.now()),
response);
return;
}
sb.append(StringUtils.defaultString(token));
sb.append(StringUtils.defaultString("\n"));
sb.append(StringUtils.defaultString(ts));
sb.append(StringUtils.defaultString("\n"));
sb.append(StringUtils.defaultString(random));
sb.append(StringUtils.defaultString("\n"));
stopWatch.stop();
stopWatch.start("sha256Hex");
String sha256Hex;
MediaType mediaType =
MediaType.parseMediaType(
ObjectUtils.defaultIfNull(
request.getContentType(),
MediaType.APPLICATION_OCTET_STREAM_VALUE));
// 文件上传不对body进行签名
if (mediaType.isCompatibleWith(MediaType.MULTIPART_FORM_DATA)) {
sha256Hex = DigestUtils.sha256Hex("").toUpperCase();
} else {
sha256Hex = DigestUtils.sha256Hex(request.getInputStream()).toUpperCase();
}
sb.append(StringUtils.defaultString(sha256Hex));
sb.append(StringUtils.defaultString("\n"));
stopWatch.stop();
stopWatch.start("签名");
Mac mac =
HmacUtils.getInitializedMac(
HmacAlgorithms.HMAC_SHA_256, token.getBytes(StandardCharsets.UTF_8));
mac.update(sb.toString().getBytes(StandardCharsets.UTF_8));
String sign = Hex.encodeHexString(mac.doFinal()).toUpperCase();
stopWatch.stop();
String clientSign = request.getHeader(Constants.HTTP_HEADER_CLIENT_SIGN);
if (!sign.equals(clientSign)) {
log.debug("key: {} \ndata:{}", token, sb);
log.warn("签名错误: {} {}", clientSign, sign);
sysLogService.saveSysLog(request, "签名模块", "签名错误", clientSign + " " + sign);
RequestUtils.writeResponse(
new BizException(Constants.HTTP_HEADER_CLIENT_SIGN + " 请求头签名错误"), response);
return;
}
stopWatch.start("重复请求");
String cr =
redisTemplate.opsForValue().get(Constants.CACHE_CLIENT_RANDOM_PREFIX + random);
if (StringUtils.equals(cr, "1")) {
sysLogService.saveSysLog(request, "签名模块", "重复请求", "");
RequestUtils.writeResponse(new BizException("请求已过期, 请刷新页面重试"), response);
return;
} else {
redisTemplate
.opsForValue()
.set(
Constants.CACHE_CLIENT_RANDOM_PREFIX + random,
"1",
TIME_DIFF_MS,
TimeUnit.MILLISECONDS);
}
stopWatch.stop();
if (request instanceof CacheBodyRequestWrapper cacheBodyRequestWrapper) {
cacheBodyRequestWrapper.reset();
}
log.debug("签名验证通过: {}", stopWatch.prettyPrint());
if (!sysConfigService.isEnable(Constants.SYSCONFIG_ENABLE_REQUEST_SIGN)) {
doFilter(request, response, filterChain);
return;
}
StopWatch stopWatch = new StopWatch();
stopWatch.start("基本请求头");
StringBuilder sb = new StringBuilder();
// 请求方法
sb.append(StringUtils.defaultString(request.getMethod().toUpperCase()));
sb.append(StringUtils.defaultString("\n"));
// 请求地址
sb.append(
StringUtils.defaultString(
ObjectUtils.defaultIfNull(
getFieldFromHeaderOrQs(request, Constants.HTTP_HEADER_CLIENT_URL),
request.getRequestURI())));
sb.append(StringUtils.defaultString("\n"));
// 请求参数
sb.append(StringUtils.defaultString(request.getQueryString()));
sb.append(StringUtils.defaultString("\n"));
// 请求头
String token =
ObjectUtils.defaultIfNull(
getFieldFromHeaderOrQs(request, Constants.HTTP_HEADER_TOKEN), "anonymous");
String ts = getFieldFromHeaderOrQs(request, Constants.HTTP_HEADER_TS);
if (StringUtils.isEmpty(ts)) {
RequestUtils.writeResponse(
new BizException(Constants.HTTP_HEADER_TS + " 请求头缺失"), response);
return;
}
String random = getFieldFromHeaderOrQs(request, Constants.HTTP_HEADER_CLIENT_RANDOM);
if (StringUtils.isEmpty(random)) {
RequestUtils.writeResponse(
new BizException(Constants.HTTP_HEADER_CLIENT_RANDOM + " 请求头缺失"), response);
return;
}
long tsi = 0;
try {
tsi = Long.parseLong(ts);
} catch (Exception e) {
RequestUtils.writeResponse(
new BizException(Constants.HTTP_HEADER_TS + " 请求头格式错误"), response);
return;
}
long current = System.currentTimeMillis();
if (Math.abs(tsi - current) > TIME_DIFF_MS) {
RequestUtils.writeResponse(
new BizException(
Constants.HTTP_HEADER_TS
+ " 客户端时间误差过大,请校准时间. 服务器时间为: "
+ LocalDateTime.now()),
response);
return;
}
sb.append(StringUtils.defaultString(token));
sb.append(StringUtils.defaultString("\n"));
sb.append(StringUtils.defaultString(ts));
sb.append(StringUtils.defaultString("\n"));
sb.append(StringUtils.defaultString(random));
sb.append(StringUtils.defaultString("\n"));
stopWatch.stop();
stopWatch.start("sha256Hex");
String sha256Hex;
MediaType mediaType =
MediaType.parseMediaType(
ObjectUtils.defaultIfNull(
request.getContentType(),
MediaType.APPLICATION_OCTET_STREAM_VALUE));
// 文件上传不对body进行签名
if (mediaType.isCompatibleWith(MediaType.MULTIPART_FORM_DATA)) {
sha256Hex = DigestUtils.sha256Hex("").toUpperCase();
} else {
sha256Hex = DigestUtils.sha256Hex(request.getInputStream()).toUpperCase();
}
sb.append(StringUtils.defaultString(sha256Hex));
sb.append(StringUtils.defaultString("\n"));
stopWatch.stop();
stopWatch.start("签名");
Mac mac =
HmacUtils.getInitializedMac(
HmacAlgorithms.HMAC_SHA_256, token.getBytes(StandardCharsets.UTF_8));
mac.update(sb.toString().getBytes(StandardCharsets.UTF_8));
String sign = Hex.encodeHexString(mac.doFinal()).toUpperCase();
stopWatch.stop();
String clientSign = getFieldFromHeaderOrQs(request, Constants.HTTP_HEADER_CLIENT_SIGN);
if (!sign.equals(clientSign)) {
log.debug("key: {} \ndata:{}", token, sb);
log.warn("签名错误: {} {}", clientSign, sign);
sysLogService.saveSysLog(request, "签名模块", "签名错误", clientSign + " " + sign);
RequestUtils.writeResponse(
new BizException(Constants.HTTP_HEADER_CLIENT_SIGN + " 请求头签名错误"), response);
return;
}
stopWatch.start("重复请求");
String cr =
redisTemplate.opsForValue().get(Constants.CACHE_CLIENT_RANDOM_PREFIX + random);
if (StringUtils.equals(cr, "1")) {
sysLogService.saveSysLog(request, "签名模块", "重复请求", "");
RequestUtils.writeResponse(new BizException("请求已过期, 请刷新页面重试"), response);
return;
} else {
redisTemplate
.opsForValue()
.set(
Constants.CACHE_CLIENT_RANDOM_PREFIX + random,
"1",
TIME_DIFF_MS,
TimeUnit.MILLISECONDS);
}
stopWatch.stop();
if (request instanceof CacheBodyRequestWrapper cacheBodyRequestWrapper) {
cacheBodyRequestWrapper.reset();
}
log.debug("签名验证通过: {}", stopWatch.prettyPrint());
doFilter(request, response, filterChain);
}

View File

@@ -26,13 +26,39 @@ import java.util.List;
public class NetDiskController {
@Autowired private NetDiskService service;
@PostMapping("/getById")
public NetDiskDto getById(@RequestBody IdRequest request) {
return this.service.getById(request.getId());
}
/**
* id转路径
*
* @param request
* @return
*/
@PostMapping("/idToPath")
public String idToPath(@RequestBody IdRequest request) {
return this.service.idToPath(request);
}
/**
* 路径转id
*
* @param request
* @return
*/
@PostMapping("/pathToId")
public String pathToId(@RequestBody IdRequest request) {
return this.service.pathToId(request);
}
/**
* 列出下级文件和文件夹
*
* @param request
* @return
*/
@PostMapping("/ls")
@SysLog(action = "ls", message = "id")
public List<NetDiskDto> ls(@RequestBody IdRequest request) {
@@ -40,29 +66,71 @@ public class NetDiskController {
}
/**
* 列出文件树
*
* @param request
* @return
*/
@PostMapping("/tree")
public NetDiskTreeDto tree(@RequestBody IdRequest request) {
return this.service.tree(request);
}
/**
* 创建文件夹
*
* @param request
* @return
*/
@PostMapping("/createDir")
@SysLog(action = "createDir", message = "name")
public NetDiskDto createDir (@RequestBody CreateDirDto request) {
public NetDiskDto createDir(@RequestBody CreateDirDto request) {
return this.service.createDir(request);
}
/**
* 批量创建文件夹
*
* @param request
* @return
*/
@PostMapping("/batchCreateDir")
@SysLog(action = "createDir", message = "names.join(',')")
public List<NetDiskDto> batchCreateDir (@RequestBody BatchCreateDirDto request) {
public List<NetDiskDto> batchCreateDir(@RequestBody BatchCreateDirDto request) {
return this.service.batchCreateDir(request);
}
/**
* 预创建文件, 创建文件之前调用, 用于秒传和文件分片进度
*
* @param request
* @return
*/
@PostMapping("/preCreateFile")
@SysLog(action = "preCreateFile", message = "name")
public PreCreateFileDto preCreateFile (@RequestBody CreateFileDto request) {
public PreCreateFileDto preCreateFile(@RequestBody CreateFileDto request) {
return this.service.preCreateFile(request);
}
/**
* 创建文件分片
*
* @param parent
* @param size
* @param sha256
* @param mineType
* @param multipartFile
* @param ossKey
* @param name
* @param code
* @param remarks
* @param sortKey
* @return
*/
@PostMapping("/createFileSlice")
public NetDiskDto createFileSlice (
public NetDiskDto createFileSlice(
@RequestParam("parent") String parent,
@RequestParam("size") Long size,
@RequestParam("sha256") String sha256,
@@ -72,11 +140,8 @@ public class NetDiskController {
@RequestParam("name") String name,
@RequestParam("code") String code,
@RequestParam("remarks") String remarks,
@RequestParam("sortKey") String sortKey
@RequestParam("sortKey") String sortKey) {
) {
CreateFileDto request = new CreateFileDto();
request.setParent(parent);
request.setSize(size);
@@ -87,32 +152,44 @@ public class NetDiskController {
request.setName(name);
request.setCode(code);
request.setRemarks(remarks);
try{
try {
request.setSortKey(Integer.valueOf(sortKey));
}catch (Exception e){
} catch (Exception e) {
}
return this.service.createFileSlice(request);
}
/**
* 创建文件
*
* @param parent
* @param size
* @param sha256
* @param mineType
* @param multipartFile
* @param ossKey
* @param name
* @param code
* @param remarks
* @param sortKey
* @return
*/
@PostMapping("/createFile")
public NetDiskDto createFile (
@RequestParam("parent") String parent,
@RequestParam("size") Long size,
@RequestParam("sha256") String sha256,
@RequestParam("mineType") String mineType,
@RequestParam("multipartFile") MultipartFile multipartFile,
@RequestParam("ossKey") String ossKey,
@RequestParam("name") String name,
@RequestParam("code") String code,
@RequestParam("remarks") String remarks,
@RequestParam("sortKey") String sortKey
public NetDiskDto createFile(
@RequestParam("parent") String parent,
@RequestParam("size") Long size,
@RequestParam("sha256") String sha256,
@RequestParam("mineType") String mineType,
@RequestParam("multipartFile") MultipartFile multipartFile,
@RequestParam("ossKey") String ossKey,
@RequestParam("name") String name,
@RequestParam("code") String code,
@RequestParam("remarks") String remarks,
@RequestParam("sortKey") String sortKey) {
) {
CreateFileDto request = new CreateFileDto();
request.setParent(parent);
request.setSize(size);
@@ -123,51 +200,96 @@ public class NetDiskController {
request.setName(name);
request.setCode(code);
request.setRemarks(remarks);
try{
try {
request.setSortKey(Integer.valueOf(sortKey));
}catch (Exception e){
request.setSortKey(Integer.valueOf(sortKey));
} catch (Exception e) {
}
return this.service.createFile(request);
}
/**
* 移动
*
* @param request
* @return
*/
@PostMapping("/move")
@SysLog(action = "move", message = "srcId + ' -> ' + targetId")
public NetDiskDto move(@RequestBody MoveDto request) {
return this.service.move(request);
}
/**
* 复制
*
* @param request
* @return
*/
@PostMapping("/copy")
@SysLog(action = "copy", message = "srcId + ' -> ' + targetId")
public NetDiskDto copy (@RequestBody CopyDto request) {
public NetDiskDto copy(@RequestBody CopyDto request) {
return this.service.copy(request);
}
/**
* 删除
*
* @param request
* @return
*/
@PostMapping("/remove")
@SysLog(action = "remove", message = "id")
public Object remove (@RequestBody IdRequest request) {
public Object remove(@RequestBody IdRequest request) {
this.service.remove(request);
return null;
}
/**
* 通过阿里云OSS链接下载文件夹
*
* @param request
* @param response
*/
@PostMapping("/downloadDir")
@SysLog(action = "downloadDir", message = "id")
public void remove (@RequestBody IdRequest request, HttpServletResponse response) {
public void downloadDir(@RequestBody IdRequest request, HttpServletResponse response) {
this.service.downloadDir(request, response);
}
/**
* 通过阿里云OSS链接下载文件
*
* @param id
* @param attachment
* @return
*/
@GetMapping("/downloadFile")
public ResponseEntity<Object> downloadFile(
@RequestParam("id") String id,
@RequestParam(value = "attachment", defaultValue = "false") Boolean attachment) {
public ResponseEntity<Object> downloadFile(@RequestParam("id") String id, @RequestParam(value = "attachment", defaultValue = "false") Boolean attachment) {
return ResponseEntity.status(HttpStatus.TEMPORARY_REDIRECT).header("Location", this.service.downloadFile(id, attachment)).body(null);
return ResponseEntity.status(HttpStatus.TEMPORARY_REDIRECT)
.header("Location", this.service.downloadFile(id, attachment))
.body(null);
}
/**
* 本地下载文件
*
* @param id
* @param attachment
* @param response
*/
@GetMapping("/downloadFileLocal")
public void downloadFile(
@RequestParam("id") String id,
@RequestParam(value = "attachment", defaultValue = "false") Boolean attachment,
HttpServletResponse response) {
this.service.downloadFileLocal(id, attachment, response);
}
}

View File

@@ -0,0 +1,12 @@
package cn.lihongjie.coal.netDisk.dto;
import lombok.Data;
import java.util.*;
@Data
public class NetDiskTreeDto extends NetDiskDto {
private List<NetDiskTreeDto> children = new ArrayList<>();
}

View File

@@ -31,4 +31,10 @@ public interface NetDiskMapper
NetDiskEntity toEntity(CreateFileDto request);
List<NetDiskDto> toDto(List<NetDiskEntity> children);
@Mappings({
@Mapping(source = "children", target = "children", ignore = false, qualifiedByName = "copyChildrenTree" )
})
@Named("copyChildrenTree")
NetDiskTreeDto toTreeDto(NetDiskEntity root);
}

View File

@@ -3,6 +3,8 @@ package cn.lihongjie.coal.netDisk.repository;
import cn.lihongjie.coal.base.dao.BaseRepository;
import cn.lihongjie.coal.netDisk.entity.NetDiskEntity;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@@ -22,4 +24,47 @@ public interface NetDiskRepository extends BaseRepository<NetDiskEntity> {
List<NetDiskEntity> findALlByParentNullAndOrganizationId(String organizationId);
long countByParentNullAndOrganizationIdAndName(String organizationId, String name);
@Query(
value =
"""
with recursive
tmp as (select t.*, 0 as level
from t_net_disk t
where t.id = :id
union all
select p.*, t.level + 1 as level
from t_net_disk p
inner join tmp t on p.id = t.parent_id),
tmp2 as (select * from tmp order by level desc)
select string_agg(name, '/')
from tmp2
""",
nativeQuery = true)
String idToPath(@Param("id") String id);
@Query(
value =
"""
with recursive tmp as (select regexp_split_to_array(:path, '/') as arr)
, tmp2 as (select tmp.arr, 1 as index, t.id as id, ARRAY_LENGTH(tmp.arr, 1) as len
from tmp
inner join t_net_disk t on t.parent_id is null and t.organization_id = :organizationId
union all
select tx.arr as arr, tx.index + 1 as index, t.id as id, tx.len
from t_net_disk t
inner join tmp2 tx on t.parent_id = tx.id and t.name = tx.arr[tx.index + 1])
select id
from tmp2 where index = len
""",
nativeQuery = true)
String pathToId(@Param("path") String path, @Param("organizationId") String organizationId);
}

View File

@@ -33,6 +33,7 @@ import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.domain.Page;
@@ -47,10 +48,7 @@ import java.io.InputStream;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
@Service
@@ -65,6 +63,42 @@ public class NetDiskService extends BaseService<NetDiskEntity, NetDiskRepository
@Autowired private NetDiskMapper mapper;
@Autowired private ConversionService conversionService;
public String idToPath(IdRequest request) {
String id = request.getId();
String ans = idToPath(id);
return StringUtils.startsWith(ans, "//")? StringUtils.substring(ans, 1): ans;
}
private String idToPath(String id) {
return this.repository.idToPath(id);
}
public String pathToId(IdRequest request) {
String id = this.repository.pathToId(request.getId(), Ctx.currentUser().getOrganizationId());
if (StringUtils.isEmpty(id)) {
throw new BizException("路径不存在");
}
return id;
}
private NetDiskEntity findRoot() {
try {
return findRootDir().get(0);
} catch (ArrayIndexOutOfBoundsException e) {
IdRequest request = new IdRequest("/");
handlerRootDir(request);
return get(request.getId());
}
}
private static void checkEntry(CreateFileDto request, List<NetDiskEntity> allSlice) {
if (allSlice.isEmpty() && request.getSliceIndex() != 0) {
throw new BizException("第一个分片的index必须是0");
@@ -135,24 +169,7 @@ public class NetDiskService extends BaseService<NetDiskEntity, NetDiskRepository
private void handlerRootDir(IdRequest request) {
if (StringUtils.equals(request.getId(), "/")) {
List<NetDiskEntity> rootDir =
this.repository.findAll(
new Specification<NetDiskEntity>() {
@Override
public Predicate toPredicate(
Root<NetDiskEntity> root,
CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.and(
criteriaBuilder.isNull(root.get("parent")),
criteriaBuilder.equal(
root.get("organizationId"),
Ctx.currentUser().getOrganizationId()),
criteriaBuilder.equal(root.get("entryType"), "0"),
criteriaBuilder.equal(root.get("name"), "/"));
}
});
List<NetDiskEntity> rootDir = findRootDir();
if (rootDir.isEmpty()) {
NetDiskEntity entity = new NetDiskEntity();
@@ -168,6 +185,27 @@ public class NetDiskService extends BaseService<NetDiskEntity, NetDiskRepository
}
}
@NotNull
private List<NetDiskEntity> findRootDir() {
return this.repository.findAll(
new Specification<NetDiskEntity>() {
@Override
public Predicate toPredicate(
Root<NetDiskEntity> root,
CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.and(
criteriaBuilder.isNull(root.get("parent")),
criteriaBuilder.equal(
root.get("organizationId"),
Ctx.currentUser().getOrganizationId()),
criteriaBuilder.equal(root.get("entryType"), "0"),
criteriaBuilder.equal(root.get("name"), "/"));
}
});
}
public NetDiskDto createDir(CreateDirDto request) {
validateDirName(request.getName());
@@ -741,4 +779,50 @@ public class NetDiskService extends BaseService<NetDiskEntity, NetDiskRepository
save(entity);
}
}
@SneakyThrows
public void downloadFileLocal(String id, Boolean attachment, HttpServletResponse response) {
NetDiskEntity disk = get(id);
if (!disk.getEntryType().equals("1")) {
throw new BizException("只能下载文件");
}
String ossKey = disk.getOssKey();
OSSObject ossObject = ossClient.getObject(aliyunProperty.getOSS().getBucketName(), ossKey);
response.setContentType(disk.getMineType());
if (attachment) {
response.setHeader(
"Content-Disposition",
"attachment;filename="
+ URLEncoder.encode(disk.getName(), StandardCharsets.UTF_8));
} else {
response.setHeader(
"Content-Disposition",
"inline;filename=" + URLEncoder.encode(disk.getName(), StandardCharsets.UTF_8));
}
ossObject.getObjectContent().transferTo(response.getOutputStream());
}
public NetDiskTreeDto tree(IdRequest request) {
handlerRootDir(request);
if (StringUtils.isEmpty(request.getId())) {
return this.mapper.toTreeDto(findRoot());
}
NetDiskEntity entity = get(request.getId());
return this.mapper.toTreeDto(entity);
}
}