Add offline transfer history and tighten anonymous access
This commit is contained in:
@@ -36,6 +36,16 @@ public interface OfflineTransferSessionRepository extends JpaRepository<OfflineT
|
||||
""")
|
||||
List<OfflineTransferSession> findAllExpiredWithFiles(@Param("now") Instant now);
|
||||
|
||||
@Query("""
|
||||
select distinct session
|
||||
from OfflineTransferSession session
|
||||
left join fetch session.files
|
||||
where session.senderUserId = :senderUserId and session.expiresAt >= :now
|
||||
order by session.expiresAt desc
|
||||
""")
|
||||
List<OfflineTransferSession> findActiveWithFilesBySenderUserId(@Param("senderUserId") Long senderUserId,
|
||||
@Param("now") Instant now);
|
||||
|
||||
@Query("""
|
||||
select coalesce(sum(file.size), 0)
|
||||
from OfflineTransferFile file
|
||||
|
||||
@@ -35,21 +35,31 @@ public class TransferController {
|
||||
@PostMapping("/sessions")
|
||||
public ApiResponse<TransferSessionResponse> createSession(@AuthenticationPrincipal UserDetails userDetails,
|
||||
@Valid @RequestBody CreateTransferSessionRequest request) {
|
||||
requireAuthenticatedUser(userDetails);
|
||||
User sender = userDetailsService.loadDomainUser(userDetails.getUsername());
|
||||
User sender = loadAuthenticatedUser(userDetails);
|
||||
return ApiResponse.success(transferService.createSession(sender, request));
|
||||
}
|
||||
|
||||
@Operation(summary = "通过取件码查找快传会话")
|
||||
@GetMapping("/sessions/lookup")
|
||||
public ApiResponse<LookupTransferSessionResponse> lookupSession(@RequestParam String pickupCode) {
|
||||
return ApiResponse.success(transferService.lookupSession(pickupCode));
|
||||
public ApiResponse<LookupTransferSessionResponse> lookupSession(@AuthenticationPrincipal UserDetails userDetails,
|
||||
@RequestParam String pickupCode) {
|
||||
return ApiResponse.success(transferService.lookupSession(userDetails != null, pickupCode));
|
||||
}
|
||||
|
||||
@Operation(summary = "加入快传会话")
|
||||
@PostMapping("/sessions/{sessionId}/join")
|
||||
public ApiResponse<TransferSessionResponse> joinSession(@PathVariable String sessionId) {
|
||||
return ApiResponse.success(transferService.joinSession(sessionId));
|
||||
public ApiResponse<TransferSessionResponse> joinSession(@AuthenticationPrincipal UserDetails userDetails,
|
||||
@PathVariable String sessionId) {
|
||||
return ApiResponse.success(transferService.joinSession(userDetails != null, sessionId));
|
||||
}
|
||||
|
||||
@Operation(summary = "查看当前用户的离线快传列表")
|
||||
@GetMapping("/sessions/offline/mine")
|
||||
public ApiResponse<java.util.List<TransferSessionResponse>> listOfflineSessions(@AuthenticationPrincipal UserDetails userDetails) {
|
||||
requireAuthenticatedUser(userDetails);
|
||||
return ApiResponse.success(transferService.listOfflineSessions(
|
||||
userDetailsService.loadDomainUser(userDetails.getUsername())
|
||||
));
|
||||
}
|
||||
|
||||
@Operation(summary = "上传离线快传文件")
|
||||
@@ -70,9 +80,10 @@ public class TransferController {
|
||||
|
||||
@Operation(summary = "下载离线快传文件")
|
||||
@GetMapping("/sessions/{sessionId}/files/{fileId}/download")
|
||||
public ResponseEntity<?> downloadOfflineFile(@PathVariable String sessionId,
|
||||
public ResponseEntity<?> downloadOfflineFile(@AuthenticationPrincipal UserDetails userDetails,
|
||||
@PathVariable String sessionId,
|
||||
@PathVariable String fileId) {
|
||||
return transferService.downloadOfflineFile(sessionId, fileId);
|
||||
return transferService.downloadOfflineFile(userDetails != null, sessionId, fileId);
|
||||
}
|
||||
|
||||
@Operation(summary = "把离线快传文件存入网盘")
|
||||
@@ -112,4 +123,11 @@ public class TransferController {
|
||||
throw new BusinessException(ErrorCode.NOT_LOGGED_IN, "用户未登录");
|
||||
}
|
||||
}
|
||||
|
||||
private User loadAuthenticatedUser(UserDetails userDetails) {
|
||||
if (userDetails == null) {
|
||||
return null;
|
||||
}
|
||||
return userDetailsService.loadDomainUser(userDetails.getUsername());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,12 +59,15 @@ public class TransferService {
|
||||
pruneExpiredSessions();
|
||||
adminMetricsService.recordTransferUsage(request.files().stream().mapToLong(TransferFileItem::size).sum());
|
||||
if (request.mode() == TransferMode.OFFLINE) {
|
||||
if (sender == null) {
|
||||
throw new BusinessException(ErrorCode.NOT_LOGGED_IN, "离线快传需要登录后使用");
|
||||
}
|
||||
return createOfflineSession(sender, request);
|
||||
}
|
||||
return createOnlineSession(request);
|
||||
}
|
||||
|
||||
public LookupTransferSessionResponse lookupSession(String pickupCode) {
|
||||
public LookupTransferSessionResponse lookupSession(boolean authenticated, String pickupCode) {
|
||||
pruneExpiredSessions();
|
||||
String normalizedPickupCode = normalizePickupCode(pickupCode);
|
||||
|
||||
@@ -73,11 +76,14 @@ public class TransferService {
|
||||
return onlineSession.toLookupResponse();
|
||||
}
|
||||
|
||||
OfflineTransferSession offlineSession = getRequiredOfflineReadySessionByPickupCode(normalizedPickupCode);
|
||||
OfflineTransferSession offlineSession = offlineTransferSessionRepository.findWithFilesByPickupCode(normalizedPickupCode)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.FILE_NOT_FOUND, "取件码不存在或已失效"));
|
||||
ensureAuthenticatedForOfflineTransfer(authenticated);
|
||||
validateOfflineReadySession(offlineSession, "取件码不存在或已失效");
|
||||
return toLookupResponse(offlineSession);
|
||||
}
|
||||
|
||||
public TransferSessionResponse joinSession(String sessionId) {
|
||||
public TransferSessionResponse joinSession(boolean authenticated, String sessionId) {
|
||||
pruneExpiredSessions();
|
||||
|
||||
TransferSession onlineSession = sessionStore.findById(sessionId).orElse(null);
|
||||
@@ -90,10 +96,20 @@ public class TransferService {
|
||||
return onlineSession.toSessionResponse();
|
||||
}
|
||||
|
||||
OfflineTransferSession offlineSession = getRequiredOfflineReadySession(sessionId);
|
||||
OfflineTransferSession offlineSession = offlineTransferSessionRepository.findWithFilesBySessionId(sessionId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.FILE_NOT_FOUND, "快传会话不存在或已失效"));
|
||||
ensureAuthenticatedForOfflineTransfer(authenticated);
|
||||
validateOfflineReadySession(offlineSession, "离线快传会话不存在或已失效");
|
||||
return toSessionResponse(offlineSession);
|
||||
}
|
||||
|
||||
public List<TransferSessionResponse> listOfflineSessions(User sender) {
|
||||
pruneExpiredSessions();
|
||||
return offlineTransferSessionRepository.findActiveWithFilesBySenderUserId(sender.getId(), Instant.now()).stream()
|
||||
.map(this::toSessionResponse)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void uploadOfflineFile(User sender, String sessionId, String fileId, MultipartFile multipartFile) {
|
||||
pruneExpiredSessions();
|
||||
@@ -155,8 +171,9 @@ public class TransferService {
|
||||
return session.poll(TransferRole.from(role), Math.max(0, after));
|
||||
}
|
||||
|
||||
public ResponseEntity<?> downloadOfflineFile(String sessionId, String fileId) {
|
||||
public ResponseEntity<?> downloadOfflineFile(boolean authenticated, String sessionId, String fileId) {
|
||||
pruneExpiredSessions();
|
||||
ensureAuthenticatedForOfflineTransfer(authenticated);
|
||||
OfflineTransferSession session = getRequiredOfflineReadySession(sessionId);
|
||||
OfflineTransferFile file = getRequiredOfflineFile(session, fileId);
|
||||
ensureOfflineFileUploaded(file);
|
||||
@@ -314,24 +331,14 @@ public class TransferService {
|
||||
private OfflineTransferSession getRequiredOfflineReadySession(String sessionId) {
|
||||
OfflineTransferSession session = offlineTransferSessionRepository.findWithFilesBySessionId(sessionId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.FILE_NOT_FOUND, "离线快传会话不存在或已失效"));
|
||||
if (session.isExpired(Instant.now())) {
|
||||
throw new BusinessException(ErrorCode.FILE_NOT_FOUND, "离线快传会话不存在或已失效");
|
||||
}
|
||||
if (!session.isReady()) {
|
||||
throw new BusinessException(ErrorCode.UNKNOWN, "离线快传仍在上传中,请稍后再试");
|
||||
}
|
||||
validateOfflineReadySession(session, "离线快传会话不存在或已失效");
|
||||
return session;
|
||||
}
|
||||
|
||||
private OfflineTransferSession getRequiredOfflineReadySessionByPickupCode(String pickupCode) {
|
||||
OfflineTransferSession session = offlineTransferSessionRepository.findWithFilesByPickupCode(pickupCode)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.FILE_NOT_FOUND, "取件码不存在或已失效"));
|
||||
if (session.isExpired(Instant.now())) {
|
||||
throw new BusinessException(ErrorCode.FILE_NOT_FOUND, "取件码不存在或已失效");
|
||||
}
|
||||
if (!session.isReady()) {
|
||||
throw new BusinessException(ErrorCode.UNKNOWN, "离线快传仍在上传中,请稍后再试");
|
||||
}
|
||||
validateOfflineReadySession(session, "取件码不存在或已失效");
|
||||
return session;
|
||||
}
|
||||
|
||||
@@ -365,6 +372,21 @@ public class TransferService {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private void ensureAuthenticatedForOfflineTransfer(boolean authenticated) {
|
||||
if (!authenticated) {
|
||||
throw new BusinessException(ErrorCode.NOT_LOGGED_IN, "离线快传需要登录后使用");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateOfflineReadySession(OfflineTransferSession session, String notFoundMessage) {
|
||||
if (session.isExpired(Instant.now())) {
|
||||
throw new BusinessException(ErrorCode.FILE_NOT_FOUND, notFoundMessage);
|
||||
}
|
||||
if (!session.isReady()) {
|
||||
throw new BusinessException(ErrorCode.UNKNOWN, "离线快传仍在上传中,请稍后再试");
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizeContentType(String contentType) {
|
||||
String normalized = Objects.requireNonNullElse(contentType, "").trim();
|
||||
return normalized.isEmpty() ? "application/octet-stream" : normalized;
|
||||
|
||||
Reference in New Issue
Block a user