27 changed files with 1098 additions and 5 deletions
@ -0,0 +1,14 @@ |
|||
package org.ycloud.aipan.annotation; |
|||
|
|||
import java.lang.annotation.Documented; |
|||
import java.lang.annotation.ElementType; |
|||
import java.lang.annotation.Retention; |
|||
import java.lang.annotation.Target; |
|||
|
|||
import static java.lang.annotation.RetentionPolicy.RUNTIME; |
|||
|
|||
@Documented |
|||
@Retention(RUNTIME) |
|||
@Target({ElementType.METHOD}) |
|||
public @interface ShareCodeCheck { |
|||
} |
@ -0,0 +1,85 @@ |
|||
package org.ycloud.aipan.aspect; |
|||
|
|||
import io.jsonwebtoken.Claims; |
|||
import jakarta.servlet.http.HttpServletRequest; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.aspectj.lang.ProceedingJoinPoint; |
|||
import org.aspectj.lang.annotation.Around; |
|||
import org.aspectj.lang.annotation.Aspect; |
|||
import org.aspectj.lang.annotation.Pointcut; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.web.context.request.RequestContextHolder; |
|||
import org.springframework.web.context.request.ServletRequestAttributes; |
|||
import org.ycloud.aipan.annotation.ShareCodeCheck; |
|||
import org.ycloud.aipan.enums.BizCodeEnum; |
|||
import org.ycloud.aipan.exception.BizException; |
|||
import org.ycloud.aipan.util.JsonData; |
|||
import org.ycloud.aipan.util.JwtUtil; |
|||
|
|||
@Aspect |
|||
@Component |
|||
@Slf4j |
|||
public class ShareCodeAspect { |
|||
|
|||
private static final ThreadLocal<Long> threadLocal = new ThreadLocal<>(); |
|||
|
|||
/** |
|||
* 设置当前线程共享shareID |
|||
* @param shareId |
|||
*/ |
|||
public static void set(Long shareId){ |
|||
threadLocal.set(shareId); |
|||
} |
|||
|
|||
/** |
|||
* 获取当前线程绑定的shareID |
|||
* @return |
|||
*/ |
|||
public static Long get(){ |
|||
return threadLocal.get(); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 定义 @Pointcut注解表达式, |
|||
* 方式一:@annotation:当执行的方法上拥有指定的注解时生效(我们采用这) |
|||
* 方式二:execution:一般用于指定方法的执行 |
|||
*/ |
|||
@Pointcut("@annotation(shareCodeCheck)") |
|||
public void pointCutShareCodeCheck(ShareCodeCheck shareCodeCheck){ |
|||
|
|||
} |
|||
|
|||
|
|||
/** |
|||
* 环绕通知, 围绕着方法执行 |
|||
* @Around 可以用来在调用一个具体方法前和调用后来完成一些具体的任务。 |
|||
* 方式一:单用 @Around("execution(* net.xdclass.controller.*.*(..))")可以 |
|||
* 方式二:用@Pointcut和@Around联合注解也可以(我们采用这个) |
|||
*/ |
|||
@Around("pointCutShareCodeCheck(shareCodeCheck)") |
|||
public Object around(ProceedingJoinPoint joinPoint, ShareCodeCheck shareCodeCheck) throws Throwable { |
|||
|
|||
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.getRequestAttributes())).getRequest(); |
|||
|
|||
String shareToken = request.getHeader("share-token"); |
|||
if(StringUtils.isBlank(shareToken)){ |
|||
throw new BizException(BizCodeEnum.SHARE_CODE_ILLEGAL); |
|||
} |
|||
Claims claims = JwtUtil.checkShareJWT(shareToken); |
|||
if(claims == null){ |
|||
log.error("share-token 解析失败"); |
|||
return JsonData.buildResult(BizCodeEnum.SHARE_CODE_ILLEGAL); |
|||
} |
|||
Long shareId = Long.valueOf(claims.get(JwtUtil.CLAIM_SHARE_KEY)+""); |
|||
set(shareId); |
|||
log.info("环绕通知执行前"); |
|||
Object obj = joinPoint.proceed(); |
|||
log.info("环绕通知执行后"); |
|||
return obj; |
|||
|
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,119 @@ |
|||
package org.ycloud.aipan.controller; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.web.bind.annotation.*; |
|||
import org.ycloud.aipan.annotation.ShareCodeCheck; |
|||
import org.ycloud.aipan.aspect.ShareCodeAspect; |
|||
import org.ycloud.aipan.controller.req.*; |
|||
import org.ycloud.aipan.dto.AccountFileDTO; |
|||
import org.ycloud.aipan.dto.ShareDTO; |
|||
import org.ycloud.aipan.dto.ShareDetailDTO; |
|||
import org.ycloud.aipan.dto.ShareSimpleDTO; |
|||
import org.ycloud.aipan.enums.BizCodeEnum; |
|||
import org.ycloud.aipan.interceptor.LoginInterceptor; |
|||
import org.ycloud.aipan.service.ShareService; |
|||
import org.ycloud.aipan.util.JsonData; |
|||
|
|||
import java.util.List; |
|||
|
|||
|
|||
@RestController |
|||
@RequestMapping("/api/share/v1") |
|||
public class ShareController { |
|||
|
|||
@Autowired |
|||
private ShareService shareService; |
|||
|
|||
/** |
|||
* 获取我的个人分享列表接口 |
|||
*/ |
|||
@GetMapping("list") |
|||
public JsonData list(){ |
|||
List<ShareDTO> list = shareService.listShare(); |
|||
return JsonData.buildSuccess(list); |
|||
} |
|||
|
|||
/** |
|||
* 创建分享链接 |
|||
*/ |
|||
@PostMapping("create") |
|||
public JsonData create(@RequestBody ShareCreateReq req){ |
|||
req.setAccountId(LoginInterceptor.threadLocal.get().getId()); |
|||
ShareDTO shareDTO = shareService.createShare(req); |
|||
return JsonData.buildSuccess(shareDTO); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 取消分享 |
|||
*/ |
|||
@PostMapping("cancel") |
|||
public JsonData cancel(@RequestBody ShareCancelReq req){ |
|||
req.setAccountId(LoginInterceptor.threadLocal.get().getId()); |
|||
shareService.cancelShare(req); |
|||
return JsonData.buildSuccess(); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 访问分享接口,返回基本的分享信息 |
|||
* 情况一:如果链接不需要校验码,则一并返回token |
|||
* 情况二:如果链接需要校验码,则返回校验码,调用对应接口校验码,再返回token |
|||
*/ |
|||
@GetMapping("visit") |
|||
public JsonData visit(@RequestParam(value = "shareId") Long shareId){ |
|||
ShareSimpleDTO shareSimpleDTO = shareService.simpleDetail(shareId); |
|||
return JsonData.buildSuccess(shareSimpleDTO); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 校验分享码,返回临时token |
|||
*/ |
|||
@PostMapping("check_share_code") |
|||
public JsonData checkShareCode(@RequestBody ShareCheckReq req){ |
|||
String shareToken = shareService.checkShareCode(req); |
|||
if(shareToken == null){ |
|||
return JsonData.buildResult(BizCodeEnum.SHARE_NOT_EXIST); |
|||
} |
|||
return JsonData.buildSuccess(shareToken); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 查看分享详情接口 |
|||
*/ |
|||
@GetMapping("detail") |
|||
@ShareCodeCheck |
|||
public JsonData detail(){ |
|||
ShareDetailDTO shareDetailDTO = shareService.detail(ShareCodeAspect.get()); |
|||
return JsonData.buildSuccess(shareDetailDTO); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 查看某个分享文件夹下的文件列表 |
|||
*/ |
|||
@PostMapping("list_share_file") |
|||
@ShareCodeCheck |
|||
public JsonData listShareFile(@RequestBody ShareFileQueryReq req){ |
|||
req.setShareId(ShareCodeAspect.get()); |
|||
List<AccountFileDTO> list = shareService.listShareFile(req); |
|||
return JsonData.buildSuccess(list); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 文件转存 |
|||
*/ |
|||
@PostMapping("transfer") |
|||
@ShareCodeCheck |
|||
public JsonData transfer(@RequestBody ShareFileTransferReq req){ |
|||
req.setShareId(ShareCodeAspect.get()); |
|||
req.setAccountId(LoginInterceptor.threadLocal.get().getId()); |
|||
shareService.transferShareFile(req); |
|||
return JsonData.buildSuccess(); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,14 @@ |
|||
package org.ycloud.aipan.controller.req; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
|
|||
@Data |
|||
public class ShareCancelReq { |
|||
|
|||
private List<Long> shareIds; |
|||
|
|||
private Long accountId; |
|||
} |
@ -0,0 +1,11 @@ |
|||
package org.ycloud.aipan.controller.req; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class ShareCheckReq { |
|||
|
|||
private Long shareId; |
|||
|
|||
private String shareCode; |
|||
} |
@ -0,0 +1,42 @@ |
|||
package org.ycloud.aipan.controller.req; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Builder; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
@AllArgsConstructor |
|||
@NoArgsConstructor |
|||
@Builder |
|||
public class ShareCreateReq { |
|||
|
|||
/** |
|||
* 分享名称 |
|||
*/ |
|||
private String shareName; |
|||
|
|||
|
|||
/** |
|||
* 分享类型,是否需要提取码 |
|||
*/ |
|||
private String shareType; |
|||
|
|||
/** |
|||
* 分享有效天数0永久,1-7天,2-30天 |
|||
*/ |
|||
private Integer shareDayType; |
|||
|
|||
/** |
|||
* 文件id列表 |
|||
*/ |
|||
private List<Long> fileIds; |
|||
|
|||
/** |
|||
* 分享人id |
|||
*/ |
|||
private Long accountId; |
|||
|
|||
} |
@ -0,0 +1,13 @@ |
|||
package org.ycloud.aipan.controller.req; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class ShareFileQueryReq { |
|||
private Long shareId; |
|||
|
|||
/** |
|||
* 进入的目标文件夹ID |
|||
*/ |
|||
private Long parentId; |
|||
} |
@ -0,0 +1,24 @@ |
|||
package org.ycloud.aipan.controller.req; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
|
|||
@Data |
|||
public class ShareFileTransferReq { |
|||
|
|||
private Long shareId; |
|||
|
|||
private Long accountId; |
|||
|
|||
/** |
|||
* 目标文件夹ID |
|||
*/ |
|||
private Long parentId; |
|||
|
|||
/** |
|||
* 转存的文件ID |
|||
*/ |
|||
private List<Long> fileIds; |
|||
} |
@ -0,0 +1,15 @@ |
|||
package org.ycloud.aipan.dto; |
|||
|
|||
import lombok.Data; |
|||
|
|||
|
|||
@Data |
|||
public class ShareAccountDTO { |
|||
|
|||
private Long id; |
|||
|
|||
private String username; |
|||
|
|||
private String avatarUrl; |
|||
|
|||
} |
@ -0,0 +1,50 @@ |
|||
package org.ycloud.aipan.dto; |
|||
|
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
import java.io.Serializable; |
|||
import java.util.Date; |
|||
|
|||
@Getter |
|||
@Setter |
|||
@Schema(name = "ShareDTO", description = "用户分享对象") |
|||
public class ShareDTO implements Serializable { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
@Schema(description = "分享id") |
|||
private Long id; |
|||
|
|||
@Schema(description = "分享名称") |
|||
private String shareName; |
|||
|
|||
@Schema(description = "分享类型(no_code没有提取码 ,need_code有提取码)") |
|||
private String shareType; |
|||
|
|||
@Schema(description = "分享类型(0 永久有效;1: 7天有效;2: 30天有效)") |
|||
private Integer shareDayType; |
|||
|
|||
@Schema(description = "分享有效天数(永久有效为0)") |
|||
private Integer shareDay; |
|||
|
|||
@Schema(description = "分享结束时间") |
|||
private Date shareEndTime; |
|||
|
|||
@Schema(description = "分享链接地址") |
|||
private String shareUrl; |
|||
|
|||
@Schema(description = "分享提取码") |
|||
private String shareCode; |
|||
|
|||
@Schema(description = "分享状态 used正常, expired已失效, cancled取消") |
|||
private String shareStatus; |
|||
|
|||
@Schema(description = "分享创建人") |
|||
private Long accountId; |
|||
|
|||
@Schema(description = "创建时间") |
|||
private Date gmtCreate; |
|||
|
|||
} |
@ -0,0 +1,53 @@ |
|||
package org.ycloud.aipan.dto; |
|||
|
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
import lombok.experimental.Accessors; |
|||
|
|||
import java.io.Serializable; |
|||
import java.util.Date; |
|||
import java.util.List; |
|||
|
|||
@Getter |
|||
@Setter |
|||
@Schema(name = "ShareDetailDTO", description = "分享链接详情对象") |
|||
@Accessors(chain = true) |
|||
public class ShareDetailDTO implements Serializable { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
@Schema(description = "分享id") |
|||
private Long id; |
|||
|
|||
@Schema(description = "分享名称") |
|||
private String shareName; |
|||
|
|||
@Schema(description = "分享类型(no_code没有提取码 ,need_code有提取码)") |
|||
private String shareType; |
|||
|
|||
@Schema(description = "分享类型(0 永久有效;1: 7天有效;2: 30天有效)") |
|||
private Integer shareDayType; |
|||
|
|||
@Schema(description = "分享有效天数(永久有效为0)") |
|||
private Integer shareDay; |
|||
|
|||
@Schema(description = "分享结束时间") |
|||
private Date shareEndTime; |
|||
|
|||
@Schema(description = "分享链接地址") |
|||
private String shareUrl; |
|||
|
|||
|
|||
/** |
|||
* 分享者信息 |
|||
*/ |
|||
private ShareAccountDTO shareAccountDTO; |
|||
|
|||
|
|||
/** |
|||
* 分享的文件信息 |
|||
*/ |
|||
private List<AccountFileDTO> fileDTOList; |
|||
|
|||
} |
@ -0,0 +1,51 @@ |
|||
package org.ycloud.aipan.dto; |
|||
|
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
import lombok.experimental.Accessors; |
|||
|
|||
import java.io.Serializable; |
|||
import java.util.Date; |
|||
|
|||
@Getter |
|||
@Setter |
|||
@Schema(name = "ShareSimpleDTO", description = "分享链接简单对象") |
|||
@Accessors(chain = true) |
|||
public class ShareSimpleDTO implements Serializable { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
@Schema(description = "分享id") |
|||
private Long id; |
|||
|
|||
@Schema(description = "分享名称") |
|||
private String shareName; |
|||
|
|||
@Schema(description = "分享类型(no_code没有提取码 ,need_code有提取码)") |
|||
private String shareType; |
|||
|
|||
@Schema(description = "分享类型(0 永久有效;1: 7天有效;2: 30天有效)") |
|||
private Integer shareDayType; |
|||
|
|||
@Schema(description = "分享有效天数(永久有效为0)") |
|||
private Integer shareDay; |
|||
|
|||
@Schema(description = "分享结束时间") |
|||
private Date shareEndTime; |
|||
|
|||
@Schema(description = "分享链接地址") |
|||
private String shareUrl; |
|||
|
|||
|
|||
/** |
|||
* 分享者信息 |
|||
*/ |
|||
private ShareAccountDTO shareAccountDTO; |
|||
|
|||
/** |
|||
* 分享令牌,不需要校验码的时候才会有这个 |
|||
*/ |
|||
private String shareToken; |
|||
|
|||
} |
@ -0,0 +1,39 @@ |
|||
package org.ycloud.aipan.enums; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
|
|||
/** |
|||
* 分享链接的日期类型 |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public enum ShareDayTypeEnum { |
|||
|
|||
PERMANENT(0,0), |
|||
|
|||
SEVEN_DAYS(1,7), |
|||
|
|||
THIRTY_DAYS(2,30); |
|||
|
|||
|
|||
private Integer dayType; |
|||
|
|||
private Integer days; |
|||
|
|||
|
|||
/** |
|||
* 根据类型获取对应的天数 |
|||
*/ |
|||
public static Integer getDaysByType(Integer dayType){ |
|||
for (ShareDayTypeEnum value : ShareDayTypeEnum.values()) { |
|||
if(value.getDayType().equals(dayType)){ |
|||
return value.getDays(); |
|||
} |
|||
} |
|||
//默认是7天有效
|
|||
return SEVEN_DAYS.days; |
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,22 @@ |
|||
package org.ycloud.aipan.enums; |
|||
|
|||
/** |
|||
* 分享状态 |
|||
*/ |
|||
public enum ShareStatusEnum { |
|||
|
|||
/** |
|||
* 已使用 |
|||
*/ |
|||
USED, |
|||
|
|||
/** |
|||
* 已过期 |
|||
*/ |
|||
EXPIRED, |
|||
|
|||
/** |
|||
* 已取消 |
|||
*/ |
|||
CANCELED; |
|||
} |
@ -0,0 +1,18 @@ |
|||
package org.ycloud.aipan.enums; |
|||
|
|||
import lombok.Getter; |
|||
|
|||
@Getter |
|||
public enum ShareTypeEnum { |
|||
|
|||
/** |
|||
* 无码 |
|||
*/ |
|||
NO_CODE, |
|||
|
|||
/** |
|||
* 需要码 |
|||
*/ |
|||
NEED_CODE; |
|||
|
|||
} |
@ -0,0 +1,29 @@ |
|||
package org.ycloud.aipan.service; |
|||
|
|||
|
|||
import org.ycloud.aipan.controller.req.*; |
|||
import org.ycloud.aipan.dto.AccountFileDTO; |
|||
import org.ycloud.aipan.dto.ShareDTO; |
|||
import org.ycloud.aipan.dto.ShareDetailDTO; |
|||
import org.ycloud.aipan.dto.ShareSimpleDTO; |
|||
|
|||
import java.util.List; |
|||
|
|||
public interface ShareService { |
|||
|
|||
List<ShareDTO> listShare(); |
|||
|
|||
ShareDTO createShare(ShareCreateReq req); |
|||
|
|||
void cancelShare(ShareCancelReq req); |
|||
|
|||
ShareSimpleDTO simpleDetail(Long shareId); |
|||
|
|||
String checkShareCode(ShareCheckReq req); |
|||
|
|||
ShareDetailDTO detail(Long shareId); |
|||
|
|||
List<AccountFileDTO> listShareFile(ShareFileQueryReq req); |
|||
|
|||
void transferShareFile(ShareFileTransferReq req); |
|||
} |
@ -0,0 +1,390 @@ |
|||
package org.ycloud.aipan.service.impl; |
|||
|
|||
import cn.hutool.core.util.IdUtil; |
|||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.ycloud.aipan.config.AccountConfig; |
|||
import org.ycloud.aipan.controller.req.*; |
|||
import org.ycloud.aipan.dto.*; |
|||
import org.ycloud.aipan.enums.BizCodeEnum; |
|||
import org.ycloud.aipan.enums.ShareDayTypeEnum; |
|||
import org.ycloud.aipan.enums.ShareStatusEnum; |
|||
import org.ycloud.aipan.enums.ShareTypeEnum; |
|||
import org.ycloud.aipan.exception.BizException; |
|||
import org.ycloud.aipan.interceptor.LoginInterceptor; |
|||
import org.ycloud.aipan.mapper.AccountFileMapper; |
|||
import org.ycloud.aipan.mapper.AccountMapper; |
|||
import org.ycloud.aipan.mapper.ShareFileMapper; |
|||
import org.ycloud.aipan.mapper.ShareMapper; |
|||
import org.ycloud.aipan.model.AccountDO; |
|||
import org.ycloud.aipan.model.AccountFileDO; |
|||
import org.ycloud.aipan.model.ShareDO; |
|||
import org.ycloud.aipan.model.ShareFileDO; |
|||
import org.ycloud.aipan.service.AccountFileService; |
|||
import org.ycloud.aipan.service.ShareService; |
|||
import org.ycloud.aipan.util.JwtUtil; |
|||
import org.ycloud.aipan.util.SpringBeanUtil; |
|||
import org.apache.commons.lang3.RandomStringUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.transaction.annotation.Transactional; |
|||
import org.springframework.util.CollectionUtils; |
|||
|
|||
import java.time.LocalDate; |
|||
import java.time.ZoneId; |
|||
import java.util.*; |
|||
import java.util.stream.Collectors; |
|||
|
|||
|
|||
@Service |
|||
@Slf4j |
|||
public class ShareServiceImpl implements ShareService { |
|||
|
|||
@Autowired |
|||
private ShareMapper shareMapper; |
|||
|
|||
@Autowired |
|||
private ShareFileMapper shareFileMapper; |
|||
|
|||
@Autowired |
|||
private AccountFileService fileService; |
|||
|
|||
@Autowired |
|||
private AccountMapper accountMapper; |
|||
|
|||
@Autowired |
|||
private AccountFileMapper accountFileMapper; |
|||
|
|||
|
|||
@Override |
|||
public List<ShareDTO> listShare() { |
|||
AccountDTO accountDTO = LoginInterceptor.threadLocal.get(); |
|||
List<ShareDO> shareDOList = shareMapper.selectList(new QueryWrapper<ShareDO>().eq("account_id", accountDTO.getId()).orderByDesc("gmt_create")); |
|||
return SpringBeanUtil.copyProperties(shareDOList, ShareDTO.class); |
|||
} |
|||
|
|||
/** |
|||
* * 检查分享文件的权限 |
|||
* * 生成分享链接和持久化数据库 |
|||
* * 生成分享详情和持久化数据库 |
|||
* @param req |
|||
* @return |
|||
*/ |
|||
@Override |
|||
@Transactional(rollbackFor = Exception.class) |
|||
public ShareDTO createShare(ShareCreateReq req) { |
|||
|
|||
//1、检查分享文件的权限
|
|||
List<Long> fileIds = req.getFileIds(); |
|||
fileService.checkFileIdLegal(fileIds, req.getAccountId()); |
|||
|
|||
//2、生成分享链接和持久化数据库
|
|||
Integer dayType = req.getShareDayType(); |
|||
Integer shareDays = ShareDayTypeEnum.getDaysByType(dayType); |
|||
Long shareId = IdUtil.getSnowflakeNextId(); |
|||
//生成分享链接
|
|||
String shareUrl = AccountConfig.PAN_FRONT_DOMAIN_SHARE_API + shareId; |
|||
log.info("shareUrl:{}",shareUrl); |
|||
ShareDO shareDO = ShareDO.builder() |
|||
.id(shareId) |
|||
.shareName(req.getShareName()) |
|||
.shareType(ShareTypeEnum.valueOf(req.getShareType()).name()) |
|||
.shareDayType(dayType) |
|||
.shareDay(shareDays) |
|||
.shareUrl(shareUrl) |
|||
.shareStatus(ShareStatusEnum.USED.name()) |
|||
.accountId(req.getAccountId()).build(); |
|||
|
|||
if(ShareDayTypeEnum.PERMANENT.getDayType().equals(dayType)){ |
|||
shareDO.setShareEndTime(Date.from(LocalDate.of(9999,12,31) |
|||
.atStartOfDay(ZoneId.systemDefault()).toInstant())); |
|||
}else { |
|||
shareDO.setShareEndTime(new Date(System.currentTimeMillis() + shareDays * 24 * 3600 * 1000L)); |
|||
} |
|||
if(ShareTypeEnum.NEED_CODE.name().equalsIgnoreCase(req.getShareType())){ |
|||
//生成提取码 6位
|
|||
String shareCode = RandomStringUtils.randomAlphabetic(6).toUpperCase(); |
|||
shareDO.setShareCode(shareCode); |
|||
} |
|||
shareMapper.insert(shareDO); |
|||
|
|||
//3、生成分享详情和持久化数据库
|
|||
List<ShareFileDO> shareFileDOS = new ArrayList<>(); |
|||
fileIds.forEach(fileId -> { |
|||
ShareFileDO shareFileDO = ShareFileDO.builder() |
|||
.shareId(shareId) |
|||
.accountFileId(fileId) |
|||
.accountId(req.getAccountId()) |
|||
.build(); |
|||
shareFileDOS.add(shareFileDO); |
|||
}); |
|||
shareFileMapper.insertBatch(shareFileDOS); |
|||
return SpringBeanUtil.copyProperties(shareDO, ShareDTO.class); |
|||
} |
|||
|
|||
@Override |
|||
public void cancelShare(ShareCancelReq req) { |
|||
List<ShareDO> shareDOList = shareMapper.selectList(new QueryWrapper<ShareDO>().eq("account_id", req.getAccountId()).in("id", req.getShareIds())); |
|||
if(shareDOList.size() != req.getShareIds().size()){ |
|||
log.error("cancelShare,shareIds:{}",req.getShareIds()); |
|||
throw new BizException(BizCodeEnum.SHARE_CANCEL_ILLEGAL); |
|||
} |
|||
// 删除分享链接
|
|||
shareMapper.deleteBatchIds(req.getShareIds()); |
|||
//删除分享详情
|
|||
shareFileMapper.delete(new QueryWrapper<ShareFileDO>().in("share_id", req.getShareIds())); |
|||
|
|||
} |
|||
|
|||
/** |
|||
* * 检查分享状态 |
|||
* * 查询分享记录实体 |
|||
* * 查询分享者信息 |
|||
* * 判断是否需要生成校验码,不需要的话可以直接生成分享token |
|||
* @param shareId |
|||
* @return |
|||
*/ |
|||
@Override |
|||
public ShareSimpleDTO simpleDetail(Long shareId) { |
|||
// 检查分享状态
|
|||
ShareDO shareDO = checkShareStatus(shareId); |
|||
ShareSimpleDTO shareSimpleDTO = SpringBeanUtil.copyProperties(shareDO, ShareSimpleDTO.class); |
|||
//查询分享者信息
|
|||
ShareAccountDTO shareAccountDTO = getShareAccount(shareDO.getAccountId()); |
|||
shareSimpleDTO.setShareAccountDTO(shareAccountDTO); |
|||
//判断是否需要校验码
|
|||
if(ShareTypeEnum.NO_CODE.name().equalsIgnoreCase(shareDO.getShareType())){ |
|||
//直接生成分享token
|
|||
String shareToken = JwtUtil.geneShareJWT(shareDO.getId()); |
|||
shareSimpleDTO.setShareToken(shareToken); |
|||
} |
|||
return shareSimpleDTO; |
|||
} |
|||
|
|||
@Override |
|||
public String checkShareCode(ShareCheckReq req) { |
|||
ShareDO shareDO = shareMapper.selectOne(new QueryWrapper<ShareDO>().eq("id", req.getShareId()).eq("share_code", req.getShareCode()).eq("share_status", ShareStatusEnum.USED.name())); |
|||
if(shareDO != null){ |
|||
//判断是否过期
|
|||
if(shareDO.getShareEndTime().getTime() > System.currentTimeMillis()){ |
|||
//生成分享token
|
|||
return JwtUtil.geneShareJWT(shareDO.getId()); |
|||
}else { |
|||
log.error("分享链接已失效:{}",req.getShareId()); |
|||
throw new BizException(BizCodeEnum.SHARE_EXPIRED); |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 分享详情接口 |
|||
* * 查询分享记录实体 |
|||
* * 检查分享状态 |
|||
* * 查询分享文件信息 |
|||
* * 查询分享者信息 |
|||
* * 构造分析详情对象返回 |
|||
* @param shareId |
|||
* @return |
|||
*/ |
|||
@Override |
|||
public ShareDetailDTO detail(Long shareId) { |
|||
//查询分享记录实体
|
|||
ShareDO shareDO = checkShareStatus(shareId); |
|||
ShareDetailDTO shareDetailDTO = SpringBeanUtil.copyProperties(shareDO, ShareDetailDTO.class); |
|||
|
|||
//查询分享文件信息
|
|||
List<AccountFileDO> accountFileDOList = getShareFileInfo(shareId); |
|||
List<AccountFileDTO> accountFileDTOList = SpringBeanUtil.copyProperties(accountFileDOList, AccountFileDTO.class); |
|||
shareDetailDTO.setFileDTOList(accountFileDTOList); |
|||
|
|||
//查询分享者信息
|
|||
ShareAccountDTO shareAccountDTO = getShareAccount(shareDO.getAccountId()); |
|||
shareDetailDTO.setShareAccountDTO(shareAccountDTO); |
|||
return shareDetailDTO; |
|||
} |
|||
|
|||
/** |
|||
* * 检查分享链接状态 |
|||
* * 查询分享ID是否在分享的文件列表中(需要获取分享文件列表的全部文件夹和子文件夹) |
|||
* * 分组后获取某个文件夹下面所有的子文件夹 |
|||
* * 根据父文件夹ID获取子文件夹列表 |
|||
* @param req |
|||
* @return |
|||
*/ |
|||
@Override |
|||
public List<AccountFileDTO> listShareFile(ShareFileQueryReq req) { |
|||
//检查分享链接状态
|
|||
ShareDO shareDO = checkShareStatus(req.getShareId()); |
|||
|
|||
//查询分享ID是否在分享的文件列表中(需要获取分享文件列表的全部文件夹和子文件夹)
|
|||
List<AccountFileDO> accountFileDOList = checkShareFileIdOnStatus(shareDO.getId(), List.of(req.getParentId())); |
|||
|
|||
List<AccountFileDTO> accountFileDTOList = SpringBeanUtil.copyProperties(accountFileDOList, AccountFileDTO.class); |
|||
|
|||
//分组后获取某个文件夹下面所有的子文件夹
|
|||
Map<Long, List<AccountFileDTO>> fileListMap = accountFileDTOList.stream() |
|||
.collect(Collectors.groupingBy(AccountFileDTO::getParentId)); |
|||
|
|||
//根据父文件夹ID获取子文件夹列表
|
|||
List<AccountFileDTO> childFileList = fileListMap.get(req.getParentId()); |
|||
|
|||
if(CollectionUtils.isEmpty(childFileList)){ |
|||
return List.of(); |
|||
} |
|||
|
|||
return childFileList; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* * 分享链接是否状态准确 |
|||
* * 转存的文件是否是分享链接里面的文件 |
|||
* * 目标文件夹是否是当前用户的 |
|||
* * 获取转存的文件 |
|||
* * 保存需要转存的文件列表(递归子文件) |
|||
* * 同步更新所有文件的accountId为当前用户的id |
|||
* * 计算存储空间大小,检查是否足够 |
|||
* * 更新关联对象信息,存储文件映射关系 |
|||
* @param req |
|||
*/ |
|||
@Override |
|||
@Transactional(rollbackFor = Exception.class) |
|||
public void transferShareFile(ShareFileTransferReq req) { |
|||
|
|||
//分享链接是否状态准确
|
|||
checkShareStatus(req.getShareId()); |
|||
|
|||
//转存的文件是否是分享链接里面的文件
|
|||
checkInShareFiles(req.getFileIds(),req.getShareId()); |
|||
|
|||
//目标文件夹是否是当前用户的
|
|||
AccountFileDO currentAccountDO = accountFileMapper.selectOne(new QueryWrapper<AccountFileDO>() |
|||
.eq("id", req.getParentId()).eq("account_id", req.getAccountId())); |
|||
if(currentAccountDO == null){ |
|||
log.error("目标文件夹不是当前用户的,{}",req); |
|||
throw new BizException(BizCodeEnum.FILE_NOT_EXISTS); |
|||
} |
|||
|
|||
//获取转存的文件
|
|||
List<AccountFileDO> shareFileList = accountFileMapper.selectBatchIds(req.getFileIds()); |
|||
//保存需要转存的文件列表(递归子文件)
|
|||
List<AccountFileDO> batchTransferFileList = fileService.findBatchCopyFileWithRecur(shareFileList, req.getParentId()); |
|||
|
|||
//同步更新所有文件的accountId为当前用户的id
|
|||
batchTransferFileList.forEach(file -> { |
|||
file.setAccountId(req.getAccountId()); |
|||
}); |
|||
|
|||
//计算存储空间大小,检查是否足够
|
|||
if(!fileService.checkAndUpdateCapacity(req.getAccountId(),batchTransferFileList.stream() |
|||
.map(accountFileDO -> accountFileDO.getFileSize() == null ? 0 : accountFileDO.getFileSize()) |
|||
.mapToLong(Long::valueOf).sum())){ |
|||
throw new BizException(BizCodeEnum.FILE_STORAGE_NOT_ENOUGH); |
|||
} |
|||
//更新关联对象信息,存储文件映射关系
|
|||
accountFileMapper.insertFileBatch(batchTransferFileList); |
|||
|
|||
|
|||
} |
|||
|
|||
private void checkInShareFiles(List<Long> fileIds, Long shareId) { |
|||
|
|||
//获取分享链接的文件
|
|||
List<ShareFileDO> shareFileDOS = shareFileMapper.selectList(new QueryWrapper<ShareFileDO>().eq("share_id", shareId)); |
|||
List<Long> shareFileIds = shareFileDOS.stream().map(ShareFileDO::getAccountFileId).toList(); |
|||
//找文件实体
|
|||
List<AccountFileDO> shareAccountFileDOList = accountFileMapper.selectBatchIds(shareFileIds); |
|||
//递归找分享链接里面的所有子文件
|
|||
List<AccountFileDO> allShareFiles = new ArrayList<>(); |
|||
fileService.findAllAccountFileDOWithRecur(allShareFiles, shareAccountFileDOList, false); |
|||
//提取全部文件的ID
|
|||
List<Long> allShareFileIds = allShareFiles.stream().map(AccountFileDO::getId).toList(); |
|||
|
|||
//判断要转存的文件是否在里面
|
|||
for (Long fileId : fileIds) { |
|||
if(!allShareFileIds.contains(fileId)){ |
|||
log.error("文件不在分享链接里面,fileId:{}",fileId); |
|||
throw new BizException(BizCodeEnum.SHARE_FILE_ILLEGAL); |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
/** |
|||
* 返回分享的文件列表,包括子文件 |
|||
* @param shareId |
|||
* @param fileIdList |
|||
* @return |
|||
*/ |
|||
private List<AccountFileDO> checkShareFileIdOnStatus(Long shareId, List<Long> fileIdList) { |
|||
//需要获取分享文件列表的全部文件夹和子文件内容
|
|||
List<AccountFileDO> shareFileInfoList = getShareFileInfo(shareId); |
|||
List<AccountFileDO> allAccountFileDOList = new ArrayList<>(); |
|||
//获取全部文件,递归
|
|||
fileService.findAllAccountFileDOWithRecur(allAccountFileDOList, shareFileInfoList, false); |
|||
|
|||
if(CollectionUtils.isEmpty(allAccountFileDOList)){ |
|||
return List.of(); |
|||
} |
|||
|
|||
//把分享的对象文件的全部文件夹放到集合里面,判断目标文件集合是否都在里面
|
|||
Set<Long> allFileIdSet = allAccountFileDOList.stream().map(AccountFileDO::getId).collect(Collectors.toSet()); |
|||
if(!allFileIdSet.containsAll(fileIdList)){ |
|||
log.error("目标文件ID列表 不再 分享的文件列表中,{}",fileIdList); |
|||
throw new BizException(BizCodeEnum.SHARE_FILE_ILLEGAL); |
|||
} |
|||
return allAccountFileDOList; |
|||
} |
|||
|
|||
private List<AccountFileDO> getShareFileInfo(Long shareId) { |
|||
|
|||
//找分享文件列表
|
|||
List<ShareFileDO> shareFileDOS = shareFileMapper.selectList(new QueryWrapper<ShareFileDO>().select("account_file_id") |
|||
.eq("share_id", shareId)); |
|||
List<Long> shareFileIdList = shareFileDOS.stream().map(ShareFileDO::getAccountFileId).toList(); |
|||
|
|||
//查找文件对象
|
|||
return accountFileMapper.selectBatchIds(shareFileIdList); |
|||
} |
|||
|
|||
/** |
|||
* 获取分享者信息 |
|||
* @param accountId |
|||
* @return |
|||
*/ |
|||
private ShareAccountDTO getShareAccount(Long accountId) { |
|||
if(accountId != null){ |
|||
AccountDO accountDO = accountMapper.selectById(accountId); |
|||
if(accountDO != null){ |
|||
return SpringBeanUtil.copyProperties(accountDO, ShareAccountDTO.class); |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 检查分享状态 |
|||
* @param shareId |
|||
* @return |
|||
*/ |
|||
private ShareDO checkShareStatus(Long shareId) { |
|||
ShareDO shareDO = shareMapper.selectById(shareId); |
|||
|
|||
if(shareDO == null){ |
|||
log.error("分享链接不存在:{}",shareId); |
|||
throw new BizException(BizCodeEnum.SHARE_NOT_EXIST); |
|||
} |
|||
//暂时未用,直接物理删除,可以调整
|
|||
if(ShareStatusEnum.CANCELED.name().equalsIgnoreCase(shareDO.getShareStatus())){ |
|||
log.error("分享链接已取消:{}",shareId); |
|||
throw new BizException(BizCodeEnum.SHARE_CANCELED); |
|||
} |
|||
//判断分享是否过期
|
|||
if(shareDO.getShareEndTime().before(new Date())){ |
|||
log.error("分享链接已过期:{}",shareId); |
|||
throw new BizException(BizCodeEnum.SHARE_EXPIRED); |
|||
} |
|||
return shareDO; |
|||
} |
|||
} |
Loading…
Reference in new issue