Skip to content

Commit 02ae20f

Browse files
authored
Merge pull request #217 from 9uttery/feat/delete-albums-api-#200
[Feature] 앨범 삭제 (한 개 이상) API 구현
2 parents 86e01cc + 59242df commit 02ae20f

File tree

3 files changed

+90
-24
lines changed

3 files changed

+90
-24
lines changed

src/main/java/com/guttery/madii/common/exception/ErrorDetails.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public enum ErrorDetails {
5959
NOT_FOUND_BOOKMARK("A003", HttpStatus.BAD_REQUEST.value(), "저장한 앨범이 아닙니다."),
6060
NOT_MY_ALBUM("A004", HttpStatus.BAD_REQUEST.value(), "내가 만든 앨범이 아닙니다."),
6161
ALREADY_OFFICIAL("A005", HttpStatus.BAD_REQUEST.value(), "이미 공개된 앨범입니다."),
62+
DELETE_ALBUM_FAILED("A006", HttpStatus.BAD_REQUEST.value(), "앨범 삭제 실패했습니다."),
6263

6364
ACHIEVEMENT_NOT_FOUND("P001", HttpStatus.NOT_FOUND.value(), "오늘의 플레이리스트에서 해당 실천을 찾을 수 없습니다."),
6465
INVALID_SATISFACTION_ENUM("P002", HttpStatus.BAD_REQUEST.value(), "만족도 ENUM이 유효하지 않습니다. BAD, SO_SO, GOOD, GREAT, EXCELLENT 중 하나여야 합니다."),
@@ -72,9 +73,7 @@ public enum ErrorDetails {
7273

7374
FIREBASE_INTEGRATION_FAILED("FI001", HttpStatus.INTERNAL_SERVER_ERROR.value(), "Firebase 연동 중 오류가 발생했습니다. 다시 시도해 주세요."),
7475
FIREBASE_NOTIFICATION_SEND_ERROR("FI002", HttpStatus.INTERNAL_SERVER_ERROR.value(), "Firebase 알림 전송 중 오류가 발생했습니다."),
75-
USER_TOKEN_INFORMATION_NOT_EXISTS("FI003", HttpStatus.NOT_FOUND.value(), "사용자의 토큰 정보가 존재하지 않습니다."),
76-
77-
;
76+
USER_TOKEN_INFORMATION_NOT_EXISTS("FI003", HttpStatus.NOT_FOUND.value(), "사용자의 토큰 정보가 존재하지 않습니다.");
7877

7978
private final String code;
8079

src/main/java/com/guttery/madii/domain/albums/application/service/AlbumService.java

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,58 @@ public void deleteAlbum(Long albumId, UserPrincipal userPrincipal) {
294294
albumRepository.deleteById(albumId);
295295
}
296296

297+
public void deleteAlbums(List<Long> albumIds, UserPrincipal userPrincipal) {
298+
final User user = UserServiceHelper.findExistingUser(userRepository, userPrincipal);
299+
300+
Set<String> keys = redisTemplate.keys(KEY_PREFIX + "*");
301+
ListOperations<String, String> listOps = redisTemplate.opsForList();
302+
303+
// 실패한 albumId와 예외 메시지를 저장
304+
Map<Long, String> failedAlbumMap = new HashMap<>();
305+
306+
// 먼저 모든 유효성 및 권한 체크를 먼저 한 후, 삭제는 그 다음에 수행
307+
List<Album> albumsToDelete = new ArrayList<>();
308+
309+
for (Long albumId : albumIds) {
310+
try {
311+
Album album = albumRepository.findById(albumId)
312+
.orElseThrow(() -> CustomException.of(ErrorDetails.ALBUM_NOT_FOUND));
313+
314+
if (!album.getUser().getUserId().equals(user.getUserId())) {
315+
throw CustomException.of(ErrorDetails.NOT_MY_ALBUM);
316+
}
317+
318+
albumsToDelete.add(album);
319+
} catch (Exception e) {
320+
failedAlbumMap.put(albumId, e.getMessage());
321+
}
322+
}
323+
324+
// 하나라도 실패한 경우 전체 중단 및 rollback
325+
if (!failedAlbumMap.isEmpty()) {
326+
for (Map.Entry<Long, String> entry : failedAlbumMap.entrySet()) {
327+
log.warn("앨범 삭제 실패 - albumId: {}, 이유: {}", entry.getKey(), entry.getValue());
328+
}
329+
throw CustomException.of(ErrorDetails.DELETE_ALBUM_FAILED); // 커스텀 예외 처리
330+
}
331+
332+
// 실제 삭제 (캐시, 연관 엔티티, 앨범)
333+
for (Album album : albumsToDelete) {
334+
Long albumId = album.getAlbumId();
335+
336+
for (String key : keys) {
337+
List<String> cachedAlbumIds = listOps.range(key, 0, -1);
338+
if (cachedAlbumIds != null && cachedAlbumIds.contains("albumId:" + albumId)) {
339+
listOps.remove(key, 0, "albumId:" + albumId);
340+
}
341+
}
342+
343+
savingAlbumRepository.deleteByAlbumAlbumId(albumId);
344+
savingJoyRepository.deleteByAlbumAlbumId(albumId);
345+
albumRepository.deleteById(albumId);
346+
}
347+
}
348+
297349
public List<AlbumGetCreatedResponse> getCreatedAlbums(UserPrincipal userPrincipal) {
298350
final User user = UserServiceHelper.findExistingUser(userRepository, userPrincipal);
299351

@@ -326,7 +378,7 @@ public void putAllAlbum(AlbumPutAllRequest albumPutAllRequest, Long albumId, Use
326378
savingJoyRepository.deleteAllByJoyJoyIdIn(deletedJoyIds);
327379
}
328380

329-
for (SavingJoyDto joyDto :joys) {
381+
for (SavingJoyDto joyDto : joys) {
330382
// 2. 수정된 소확행 처리
331383
if (joyDto.joyId() != null) {
332384
// 소확행명 수정 - joy table update

src/main/java/com/guttery/madii/domain/albums/presentation/AlbumController.java

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,12 @@
1818

1919
@RequiredArgsConstructor
2020
@RestController
21-
@RequestMapping("/v1/albums")
2221
@Validated
2322
@Tag(name = "Albums", description = "Albums 관련 API")
2423
public class AlbumController {
2524
private final AlbumService albumService;
2625

27-
@PostMapping("")
26+
@PostMapping("/v1/albums")
2827
@ApiResponses(
2928
value = {
3029
@ApiResponse(
@@ -40,7 +39,7 @@ public List<AlbumCreateResponse> createAlbum(@Valid @RequestBody AlbumCreateRequ
4039
return albumService.createAlbum(albumCreateRequest, userPrincipal);
4140
}
4241

43-
@PostMapping("/{joyId}")
42+
@PostMapping("/v1/albums/{joyId}")
4443
@ApiResponses(
4544
value = {
4645
@ApiResponse(
@@ -57,7 +56,7 @@ public void addJoyToAlbum(@PathVariable Long joyId,
5756
albumService.addJoyToAlbum(joyId, albumSaveJoyRequest, userPrincipal);
5857
}
5958

60-
@PutMapping("/{albumId}")
59+
@PutMapping("/v1/albums/{albumId}")
6160
@ApiResponses(
6261
value = {
6362
@ApiResponse(
@@ -74,7 +73,7 @@ public void putMyAlbum(@PathVariable Long albumId,
7473
albumService.putMyAlbum(albumId, albumPutRequest, userPrincipal);
7574
}
7675

77-
@PutMapping("/{albumId}/status")
76+
@PutMapping("/v1/albums/{albumId}/status")
7877
@ApiResponses(
7978
value = {
8079
@ApiResponse(
@@ -90,7 +89,7 @@ public void putMyAlbumStatus(@PathVariable Long albumId,
9089
albumService.putMyAlbumStatus(albumId, userPrincipal);
9190
}
9291

93-
@GetMapping("/{albumId}")
92+
@GetMapping("/v1/albums/{albumId}")
9493
@ApiResponses(
9594
value = {
9695
@ApiResponse(
@@ -106,7 +105,7 @@ public AlbumGetDetailResponse getAlbumDetail(@PathVariable Long albumId,
106105
return albumService.getAlbumDetail(albumId, userPrincipal);
107106
}
108107

109-
@PostMapping("/{albumId}/bookmarks")
108+
@PostMapping("/v1/albums/{albumId}/bookmarks")
110109
@ApiResponses(
111110
value = {
112111
@ApiResponse(
@@ -122,7 +121,7 @@ public void createAlbumBookmark(@PathVariable Long albumId,
122121
albumService.createAlbumBookmark(albumId, userPrincipal);
123122
}
124123

125-
@DeleteMapping("/{albumId}/bookmarks")
124+
@DeleteMapping("/v1/albums/{albumId}/bookmarks")
126125
@ApiResponses(
127126
value = {
128127
@ApiResponse(
@@ -138,7 +137,7 @@ public void deleteAlbumBookmark(@PathVariable Long albumId,
138137
albumService.deleteAlbumBookmark(albumId, userPrincipal);
139138
}
140139

141-
@GetMapping("")
140+
@GetMapping("/v1/albums")
142141
@ApiResponses(
143142
value = {
144143
@ApiResponse(
@@ -153,7 +152,7 @@ public List<AlbumGetMyAllResponse> getMyAllAlbums(@AuthenticationPrincipal final
153152
return albumService.getMyAllAlbums(userPrincipal);
154153
}
155154

156-
@GetMapping("/created")
155+
@GetMapping("/v1/albums/created")
157156
@ApiResponses(
158157
value = {
159158
@ApiResponse(
@@ -168,7 +167,7 @@ public List<AlbumGetCreatedResponse> getCreatedAlbums(@AuthenticationPrincipal f
168167
return albumService.getCreatedAlbums(userPrincipal);
169168
}
170169

171-
@GetMapping("/joy/{joyId}")
170+
@GetMapping("/v1/albums/joy/{joyId}")
172171
@ApiResponses(
173172
value = {
174173
@ApiResponse(
@@ -184,7 +183,7 @@ public List<AlbumGetJoyAllResponse> getMyJoyAllAlbums(@PathVariable Long joyId,
184183
return albumService.getMyJoyAllAlbums(joyId, userPrincipal);
185184
}
186185

187-
@GetMapping("/all")
186+
@GetMapping("/v1/albums/all")
188187
@ApiResponses(
189188
value = {
190189
@ApiResponse(
@@ -200,7 +199,7 @@ public Slice<AlbumGetAllResponse> getAllAlbums(@RequestParam(required = false) L
200199
return albumService.getAllAlbums(albumId, size);
201200
}
202201

203-
@GetMapping("/{albumId}/random")
202+
@GetMapping("/v1/albums/{albumId}/random")
204203
@ApiResponses(
205204
value = {
206205
@ApiResponse(
@@ -216,7 +215,7 @@ public List<AlbumGetOthersResponse> getOtherAlbums(@PathVariable Long albumId,
216215
return albumService.getOtherAlbums(albumId, userPrincipal);
217216
}
218217

219-
@PostMapping("/recent/{albumId}")
218+
@PostMapping("/v1/albums/recent/{albumId}")
220219
@ApiResponses(
221220
value = {
222221
@ApiResponse(
@@ -232,7 +231,7 @@ public void createRecentAlbums(@PathVariable Long albumId,
232231
albumService.createRecentAlbums(albumId, userPrincipal);
233232
}
234233

235-
@GetMapping("/recent")
234+
@GetMapping("/v1/albums/recent")
236235
@ApiResponses(
237236
value = {
238237
@ApiResponse(
@@ -247,23 +246,39 @@ public List<AlbumGetRecentResponse> getRecentAlbums(@AuthenticationPrincipal fin
247246
return albumService.getRecentAlbums(userPrincipal);
248247
}
249248

250-
@DeleteMapping("/{albumId}")
249+
@DeleteMapping("/v1/albums/{albumId}")
251250
@ApiResponses(
252251
value = {
253252
@ApiResponse(
254253
responseCode = "200",
255-
description = "앨범 삭제 성공",
254+
description = "앨범 한 개 삭제 성공",
256255
useReturnTypeSchema = true
257256
)
258257
}
259258
)
260-
@Operation(summary = "앨범 삭제 API", description = "앨범 삭제 API입니다.")
259+
@Operation(summary = "앨범 삭제 (한 개) API", description = "앨범 삭제 (한 개) API입니다.")
261260
public void deleteAlbum(@PathVariable Long albumId,
262261
@AuthenticationPrincipal final UserPrincipal userPrincipal) {
263262
albumService.deleteAlbum(albumId, userPrincipal);
264263
}
265264

266-
@PostMapping("/{albumId}/report")
265+
@DeleteMapping("/v2/albums")
266+
@ApiResponses(
267+
value = {
268+
@ApiResponse(
269+
responseCode = "200",
270+
description = "앨범 한 개 이상 삭제 성공",
271+
useReturnTypeSchema = true
272+
)
273+
}
274+
)
275+
@Operation(summary = "앨범 삭제 (한 개 이상) API", description = "앨범 삭제 (한 개 이상) API입니다.")
276+
public void deleteAlbums(@RequestParam List<Long> albumIds,
277+
@AuthenticationPrincipal final UserPrincipal userPrincipal) {
278+
albumService.deleteAlbums(albumIds, userPrincipal);
279+
}
280+
281+
@PostMapping("/v1/albums/{albumId}/report")
267282
@ApiResponses(
268283
value = {
269284
@ApiResponse(
@@ -281,7 +296,7 @@ public void reportAlbum(@Valid @RequestBody AlbumReportRequest albumReportReques
281296
}
282297

283298

284-
@PutMapping("/{albumId}/all")
299+
@PutMapping("/v1/albums/{albumId}/all")
285300
@ApiResponses(
286301
value = {
287302
@ApiResponse(

0 commit comments

Comments
 (0)