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