Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,13 @@ public interface BattleOptionRepository extends JpaRepository<BattleOption, Long
"WHERE bo.battle IN :battles " +
"ORDER BY bo.battle.id, COALESCE(bo.displayOrder, 9999), bo.label, bo.id")
List<BattleOption> findByBattleIn(@Param("battles") List<Battle> battles);

@Query("SELECT COUNT(bo) FROM BattleOption bo " +
"WHERE bo.battle.deletedAt IS NULL " +
"AND bo.imageUrl = :imageUrl " +
"AND (:excludeOptionId IS NULL OR bo.id <> :excludeOptionId)")
long countActiveImageReferences(
@Param("imageUrl") String imageUrl,
@Param("excludeOptionId") Long excludeOptionId
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ public interface BattleRepository extends JpaRepository<Battle, Long> {
// 주간 배치: 특정 기간(targetDate BETWEEN from AND to)의 배틀 조회
List<Battle> findByTargetDateBetweenAndStatusAndDeletedAtIsNull(LocalDate from, LocalDate to, BattleStatus status);

@Query("SELECT COUNT(b) FROM Battle b " +
"WHERE b.deletedAt IS NULL " +
"AND b.thumbnailUrl = :thumbnailUrl " +
"AND (:excludeBattleId IS NULL OR b.id <> :excludeBattleId)")
long countActiveThumbnailReferences(
@Param("thumbnailUrl") String thumbnailUrl,
@Param("excludeBattleId") Long excludeBattleId
);

// 탐색 탭: 전체 배틀 검색
@Query("SELECT b FROM Battle b WHERE b.status = 'PUBLISHED' AND b.deletedAt IS NULL")
List<Battle> searchAll(Pageable pageable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@ public AdminBattleDetailResponse createBattle(AdminBattleCreateRequest request,
Collectors.mapping(BattleOptionTag::getTag, Collectors.toList())
));

cleanupUnreferencedDraftAssets(request.thumbnailUrl(), request.options());

return battleConverter.toAdminDetailResponse(battle, getTagsByBattle(battle), savedOptions, optionTagsMap);
}

Expand Down Expand Up @@ -373,7 +375,7 @@ public AdminBattleDetailResponse updateBattle(Long battleId, AdminBattleUpdateRe
String existingThumbnailKey = normalizeStoredImageReference(battle.getThumbnailUrl(), FileCategory.BATTLE);
String resolvedThumbnailKey = resolveStoredImageKey(request.thumbnailUrl(), request.status(), FileCategory.BATTLE);
if (existingThumbnailKey != null && !existingThumbnailKey.equals(resolvedThumbnailKey)) {
deleteStoredAsset(existingThumbnailKey);
deleteStoredAsset(existingThumbnailKey, battle.getId(), null);
}

battle.update(
Expand Down Expand Up @@ -421,7 +423,7 @@ public AdminBattleDetailResponse updateBattle(Long battleId, AdminBattleUpdateRe
} else {
String existingOptionImageKey = normalizeStoredImageReference(option.getImageUrl(), FileCategory.PHILOSOPHER);
if (existingOptionImageKey != null && !existingOptionImageKey.equals(resolvedOptionImageKey)) {
deleteStoredAsset(existingOptionImageKey);
deleteStoredAsset(existingOptionImageKey, null, option.getId());
}
option.update(optionRequest.title(), optionRequest.stance(),
optionRequest.representative(), resolvedOptionImageKey);
Expand All @@ -435,7 +437,7 @@ public AdminBattleDetailResponse updateBattle(Long battleId, AdminBattleUpdateRe
.toList();

for (BattleOption removedOption : removedOptions) {
deleteStoredAsset(removedOption.getImageUrl());
deleteStoredAsset(removedOption.getImageUrl(), null, removedOption.getId());
List<BattleOptionTag> optionTags = battleOptionTagRepository.findByBattleOption(removedOption);
if (!optionTags.isEmpty()) {
battleOptionTagRepository.deleteAll(optionTags);
Expand All @@ -447,6 +449,8 @@ public AdminBattleDetailResponse updateBattle(Long battleId, AdminBattleUpdateRe
}
}

cleanupUnreferencedDraftAssets(request.thumbnailUrl(), request.options());

List<BattleOption> updatedOptions = battleOptionRepository.findByBattle(battle);
Map<Long, List<Tag>> optionTagsMap = battleOptionTagRepository.findByBattleWithTags(battle)
.stream()
Expand Down Expand Up @@ -585,12 +589,16 @@ private String extractPath(String value) {
return value;
}

private void deleteStoredAsset(String rawReference) {
private void deleteStoredAsset(String rawReference, Long excludeBattleId, Long excludeOptionId) {
String normalized = normalizeStoredImageReference(rawReference, null);
if (normalized == null) {
return;
}

if (hasOtherActiveReferences(normalized, excludeBattleId, excludeOptionId)) {
return;
}

if (localDraftFileStorageService.isLocalDraftReference(normalized)) {
localDraftFileStorageService.deleteIfLocalReference(normalized);
return;
Expand All @@ -599,6 +607,30 @@ private void deleteStoredAsset(String rawReference) {
s3UploadService.deleteFile(normalized);
}

private boolean hasOtherActiveReferences(String normalizedReference, Long excludeBattleId, Long excludeOptionId) {
long thumbnailReferences = battleRepository.countActiveThumbnailReferences(normalizedReference, excludeBattleId);
long optionImageReferences = battleOptionRepository.countActiveImageReferences(normalizedReference, excludeOptionId);
return (thumbnailReferences + optionImageReferences) > 0;
}

private void cleanupUnreferencedDraftAssets(String thumbnailUrl, List<AdminBattleOptionRequest> options) {
Set<String> candidates = new LinkedHashSet<>();
if (thumbnailUrl != null && !thumbnailUrl.isBlank()) {
candidates.add(thumbnailUrl);
}
if (options != null) {
options.stream()
.map(AdminBattleOptionRequest::imageUrl)
.filter(Objects::nonNull)
.filter(imageUrl -> !imageUrl.isBlank())
.forEach(candidates::add);
}

for (String rawReference : candidates) {
deleteStoredAsset(rawReference, null, null);
}
}

private BattleStatus parseBattleStatus(String status) {
if (status == null || status.isBlank() || "ALL".equalsIgnoreCase(status)) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
import com.swyp.picke.domain.vote.service.BattleVoteService;
import com.swyp.picke.global.common.exception.CustomException;
import com.swyp.picke.global.common.exception.ErrorCode;
import com.swyp.picke.global.infra.s3.service.S3PresignedUrlService;
import com.swyp.picke.global.infra.s3.enums.FileCategory;
import com.swyp.picke.global.infra.s3.util.ResourceUrlProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
Expand All @@ -43,7 +44,7 @@ public class PerspectiveCommentService {
private final UserService userQueryService;
private final BattleVoteService BattleVoteService;
private final BattleService battleService;
private final S3PresignedUrlService s3PresignedUrlService;
private final ResourceUrlProvider resourceUrlProvider;

@Transactional
public CreateCommentResponse createComment(Long perspectiveId, Long userId, CreateCommentRequest request) {
Expand Down Expand Up @@ -207,6 +208,9 @@ private String resolveCharacterImageUrl(String characterType) {
if (characterType == null || characterType.isBlank()) {
return null;
}
return s3PresignedUrlService.generatePresignedUrl(CharacterType.resolveImageKey(characterType));
return resourceUrlProvider.getImageUrl(
FileCategory.CHARACTER,
CharacterType.resolveImageKey(characterType)
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
import com.swyp.picke.domain.vote.service.BattleVoteService;
import com.swyp.picke.global.common.exception.CustomException;
import com.swyp.picke.global.common.exception.ErrorCode;
import com.swyp.picke.global.infra.s3.service.S3PresignedUrlService;
import com.swyp.picke.global.infra.s3.enums.FileCategory;
import com.swyp.picke.global.infra.s3.util.ResourceUrlProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
Expand All @@ -48,7 +49,7 @@ public class PerspectiveService {
private final UserService userQueryService;
private final UserRepository userRepository;
private final GptModerationService gptModerationService;
private final S3PresignedUrlService s3PresignedUrlService;
private final ResourceUrlProvider resourceUrlProvider;

public PerspectiveDetailResponse getPerspectiveDetail(Long perspectiveId, Long userId) {
Perspective perspective = findPerspectiveById(perspectiveId);
Expand Down Expand Up @@ -215,6 +216,9 @@ private String resolveCharacterImageUrl(String characterType) {
if (characterType == null || characterType.isBlank()) {
return null;
}
return s3PresignedUrlService.generatePresignedUrl(CharacterType.resolveImageKey(characterType));
return resourceUrlProvider.getImageUrl(
FileCategory.CHARACTER,
CharacterType.resolveImageKey(characterType)
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
import com.swyp.picke.domain.vote.service.VoteQueryService;
import com.swyp.picke.global.common.exception.CustomException;
import com.swyp.picke.global.common.exception.ErrorCode;
import com.swyp.picke.global.infra.s3.service.S3PresignedUrlService;
import com.swyp.picke.global.infra.s3.enums.FileCategory;
import com.swyp.picke.global.infra.s3.util.ResourceUrlProvider;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -48,7 +49,7 @@ public class MypageService {
private final VoteQueryService voteQueryService;
private final BattleQueryService battleQueryService;
private final PerspectiveQueryService perspectiveQueryService;
private final S3PresignedUrlService s3PresignedUrlService;
private final ResourceUrlProvider resourceUrlProvider;

@Transactional
public MypageResponse getMypage() {
Expand All @@ -74,7 +75,7 @@ public MypageResponse getMypage() {
philosopherType.getLabel(),
philosopherType.getTypeName(),
philosopherType.getDescription(),
s3PresignedUrlService.generatePresignedUrl(
resourceUrlProvider.getImageUrl(FileCategory.PHILOSOPHER,
PhilosopherType.resolveImageKey(philosopherType.getLabel())
))
: null;
Expand Down Expand Up @@ -355,7 +356,7 @@ private RecapResponse.PhilosopherCard toPhilosopherCard(PhilosopherType type) {
type.getTypeName(),
type.getDescription(),
type.getKeywordTags(),
s3PresignedUrlService.generatePresignedUrl(
resourceUrlProvider.getImageUrl(FileCategory.PHILOSOPHER,
PhilosopherType.resolveImageKey(type.getLabel())
)
);
Expand All @@ -379,15 +380,17 @@ private NotificationSettingsResponse toNotificationSettingsResponse(UserSettings

private String resolveCharacterImageUrl(CharacterType characterType) {
String imageKey = CharacterType.resolveImageKey(characterType);
return imageKey != null ? s3PresignedUrlService.generatePresignedUrl(imageKey) : null;
return imageKey != null
? resourceUrlProvider.getImageUrl(FileCategory.CHARACTER, imageKey)
: null;
}

private String resolveCharacterImageUrl(String characterType) {
if (characterType == null || characterType.isBlank()) {
return null;
}
String imageKey = CharacterType.resolveImageKey(characterType);
return s3PresignedUrlService.generatePresignedUrl(imageKey);
return resourceUrlProvider.getImageUrl(FileCategory.CHARACTER, imageKey);
}
}

Expand Down
Loading