5
5
import org .codeNbug .mainserver .domain .admin .dto .response .AdminLoginResponse ;
6
6
import org .codeNbug .mainserver .domain .admin .dto .response .AdminSignupResponse ;
7
7
import org .codeNbug .mainserver .domain .admin .dto .response .DashboardStatsResponse ;
8
+ import org .codeNbug .mainserver .domain .admin .dto .response .EventAdminDto ;
8
9
import org .codeNbug .mainserver .domain .admin .dto .response .ModifyRoleResponse ;
10
+ import org .codeNbug .mainserver .domain .admin .dto .response .TicketAdminDto ;
9
11
import org .codeNbug .mainserver .domain .event .entity .Event ;
12
+ import org .codeNbug .mainserver .domain .event .entity .EventStatusEnum ;
10
13
import org .codeNbug .mainserver .domain .manager .repository .EventRepository ;
14
+ import org .codeNbug .mainserver .domain .notification .entity .NotificationEnum ;
15
+ import org .codeNbug .mainserver .domain .notification .service .NotificationService ;
16
+ import org .codeNbug .mainserver .domain .purchase .entity .Purchase ;
17
+ import org .codeNbug .mainserver .domain .purchase .repository .PurchaseRepository ;
11
18
import org .codeNbug .mainserver .domain .ticket .repository .TicketRepository ;
19
+ import org .codeNbug .mainserver .global .exception .globalException .BadRequestException ;
12
20
import org .codeNbug .mainserver .global .exception .globalException .DuplicateEmailException ;
13
21
import org .codenbug .user .domain .user .entity .User ;
14
22
import org .codenbug .user .domain .user .repository .UserRepository ;
23
31
import lombok .RequiredArgsConstructor ;
24
32
import lombok .extern .slf4j .Slf4j ;
25
33
34
+ import java .util .ArrayList ;
26
35
import java .util .HashMap ;
27
36
import java .util .List ;
28
37
import java .util .Map ;
38
+ import java .util .stream .Collectors ;
29
39
30
40
/**
31
41
* 관리자 관련 서비스
@@ -41,6 +51,8 @@ public class AdminService {
41
51
private final TicketRepository ticketRepository ;
42
52
private final PasswordEncoder passwordEncoder ;
43
53
private final TokenService tokenService ;
54
+ private final NotificationService notificationService ;
55
+ private final PurchaseRepository purchaseRepository ;
44
56
45
57
/**
46
58
* 관리자 회원가입 서비스
@@ -314,4 +326,261 @@ else if ("sns".equals(userType)) {
314
326
log .error (">> 유효하지 않은 사용자 타입: {}" , userType );
315
327
throw new IllegalArgumentException ("유효하지 않은 사용자 타입입니다: " + userType );
316
328
}
329
+
330
+ /**
331
+ * 모든 이벤트 목록을 조회하고 각 이벤트의 티켓 정보를 포함하여 반환합니다.
332
+ *
333
+ * @return 이벤트 관리자 DTO 목록
334
+ */
335
+ @ Transactional (readOnly = true )
336
+ public List <EventAdminDto > getAllEvents () {
337
+ log .info (">> 모든 이벤트 목록 조회" );
338
+
339
+ try {
340
+ // 삭제되지 않은 이벤트만 조회
341
+ List <Event > events = eventRepository .findAllByIsDeletedFalse ();
342
+ log .debug (">> 이벤트 목록 조회 완료: {} 개" , events .size ());
343
+
344
+ // 각 이벤트에 대한 정보와 티켓 정보를 포함한 DTO 생성
345
+ List <EventAdminDto > eventDtos = events .stream ()
346
+ .map (event -> {
347
+ // 해당 이벤트의 판매된 티켓 수 조회
348
+ int soldTickets = ticketRepository .countPaidTicketsByEventId (event .getEventId ());
349
+ log .debug (">> 이벤트 ID={}, 판매된 티켓 수={}" , event .getEventId (), soldTickets );
350
+
351
+ // DTO 생성
352
+ return EventAdminDto .fromEntity (event , soldTickets );
353
+ })
354
+ .collect (Collectors .toList ());
355
+
356
+ log .info (">> 이벤트 목록 조회 완료: {} 개의 이벤트" , eventDtos .size ());
357
+
358
+ return eventDtos ;
359
+ } catch (Exception e ) {
360
+ log .error (">> 이벤트 목록 조회 중 오류: {}" , e .getMessage (), e );
361
+ throw new RuntimeException ("이벤트 목록 조회 중 오류가 발생했습니다: " + e .getMessage ());
362
+ }
363
+ }
364
+
365
+ /**
366
+ * 모든 티켓 목록을 조회합니다.
367
+ *
368
+ * @return 티켓 관리자 DTO 목록
369
+ */
370
+ @ Transactional (readOnly = true )
371
+ public List <TicketAdminDto > getAllTickets () {
372
+ log .info (">> 모든 티켓 목록 조회" );
373
+
374
+ try {
375
+ // 모든 티켓 조회
376
+ List <TicketAdminDto > tickets = ticketRepository .findAllTicketsForAdmin ();
377
+ log .debug (">> 티켓 목록 조회 완료: {} 개" , tickets .size ());
378
+
379
+ log .info (">> 티켓 목록 조회 완료: {} 개의 티켓" , tickets .size ());
380
+
381
+ return tickets ;
382
+ } catch (Exception e ) {
383
+ log .error (">> 티켓 목록 조회 중 오류: {}" , e .getMessage (), e );
384
+ throw new RuntimeException ("티켓 목록 조회 중 오류가 발생했습니다: " + e .getMessage ());
385
+ }
386
+ }
387
+
388
+ /**
389
+ * 특정 이벤트 정보를 조회합니다.
390
+ *
391
+ * @param eventId 조회할 이벤트 ID
392
+ * @return 이벤트 관리자 DTO
393
+ * @throws RuntimeException 이벤트를 찾을 수 없거나 오류 발생 시
394
+ */
395
+ @ Transactional (readOnly = true )
396
+ public EventAdminDto getEvent (Long eventId ) {
397
+ log .info (">> 이벤트 상세 정보 조회: id={}" , eventId );
398
+
399
+ try {
400
+ // 이벤트 조회
401
+ Event event = eventRepository .findById (eventId )
402
+ .orElseThrow (() -> new RuntimeException ("해당 이벤트를 찾을 수 없습니다: " + eventId ));
403
+
404
+ // 해당 이벤트의 판매된 티켓 수 조회
405
+ int soldTickets = ticketRepository .countPaidTicketsByEventId (eventId );
406
+ log .debug (">> 이벤트 ID={}, 판매된 티켓 수={}" , event .getEventId (), soldTickets );
407
+
408
+ // DTO 생성
409
+ EventAdminDto eventDto = EventAdminDto .fromEntity (event , soldTickets );
410
+
411
+ log .info (">> 이벤트 상세 정보 조회 완료: id={}, 제목={}" , eventId , eventDto .getTitle ());
412
+
413
+ return eventDto ;
414
+ } catch (Exception e ) {
415
+ log .error (">> 이벤트 상세 정보 조회 중 오류: {}" , e .getMessage (), e );
416
+ throw new RuntimeException ("이벤트 상세 정보 조회 중 오류가 발생했습니다: " + e .getMessage ());
417
+ }
418
+ }
419
+
420
+ /**
421
+ * 삭제 대기 중인 이벤트 목록을 조회합니다.
422
+ *
423
+ * @return 삭제 대기 중인 이벤트 목록
424
+ */
425
+ @ Transactional (readOnly = true )
426
+ public List <EventAdminDto > getDeletedEvents () {
427
+ log .info (">> 삭제 대기 중인 이벤트 목록 조회" );
428
+
429
+ try {
430
+ // 삭제된 이벤트 조회
431
+ List <Event > events = eventRepository .findAllByIsDeletedTrue ();
432
+ log .debug (">> 삭제된 이벤트 목록 조회 완료: {} 개" , events .size ());
433
+
434
+ // 각 이벤트에 대한 정보와 티켓 정보를 포함한 DTO 생성
435
+ List <EventAdminDto > eventDtos = events .stream ()
436
+ .map (event -> {
437
+ // 해당 이벤트의 판매된 티켓 수 조회
438
+ int soldTickets = ticketRepository .countPaidTicketsByEventId (event .getEventId ());
439
+ log .debug (">> 이벤트 ID={}, 판매된 티켓 수={}" , event .getEventId (), soldTickets );
440
+
441
+ // DTO 생성
442
+ return EventAdminDto .fromEntity (event , soldTickets );
443
+ })
444
+ .collect (Collectors .toList ());
445
+
446
+ log .info (">> 삭제된 이벤트 목록 조회 완료: {} 개의 이벤트" , eventDtos .size ());
447
+
448
+ return eventDtos ;
449
+ } catch (Exception e ) {
450
+ log .error (">> 삭제된 이벤트 목록 조회 중 오류: {}" , e .getMessage (), e );
451
+ throw new RuntimeException ("삭제된 이벤트 목록 조회 중 오류가 발생했습니다: " + e .getMessage ());
452
+ }
453
+ }
454
+
455
+ /**
456
+ * 삭제된 이벤트를 복구합니다.
457
+ *
458
+ * @param eventId 복구할 이벤트 ID
459
+ * @param status 복구 후 설정할 이벤트 상태
460
+ * @return 복구된 이벤트 정보
461
+ * @throws IllegalAccessException 이벤트가 삭제되지 않은 경우
462
+ */
463
+ @ Transactional
464
+ public EventAdminDto restoreEvent (Long eventId , EventStatusEnum status ) throws IllegalAccessException {
465
+ log .info (">> 이벤트 복구 처리 시작: eventId={}, status={}" , eventId , status );
466
+
467
+ Event event = eventRepository .findById (eventId )
468
+ .orElseThrow (() -> {
469
+ log .error (">> 이벤트 복구 실패: 존재하지 않는 이벤트 - eventId={}" , eventId );
470
+ return new BadRequestException ("존재하지 않는 이벤트입니다." );
471
+ });
472
+
473
+ // 이벤트가 삭제되지 않았다면 400에러 전송
474
+ if (!event .getIsDeleted ()) {
475
+ log .warn (">> 이벤트 복구 실패: 삭제되지 않은 이벤트 - eventId={}" , eventId );
476
+ throw new IllegalAccessException ("삭제되지 않은 이벤트입니다." );
477
+ }
478
+
479
+ // 이벤트 상태 변경
480
+ event .setIsDeleted (false );
481
+ event .setStatus (status );
482
+ log .info (">> 이벤트 상태 변경 완료: eventId={}, status={}" , eventId , status );
483
+
484
+ // 알림 처리는 메인 로직과 분리하여 예외 처리
485
+ try {
486
+ // 해당 이벤트 구매자들 조회
487
+ List <Purchase > purchases = purchaseRepository .findAllByEventId (eventId );
488
+ log .debug (">> 이벤트 구매자 조회 완료: eventId={}, 구매자 수={}" , eventId , purchases .size ());
489
+
490
+ // 모든 구매자에게 행사 복구 알림 전송
491
+ String notificationContent = String .format (
492
+ "[%s] 행사가 복구되었습니다. 예매 내역을 확인해주세요." ,
493
+ event .getInformation ().getTitle ()
494
+ );
495
+
496
+ for (Purchase purchase : purchases ) {
497
+ try {
498
+ Long userId = purchase .getUser ().getUserId ();
499
+ notificationService .createNotification (
500
+ userId ,
501
+ NotificationEnum .EVENT ,
502
+ notificationContent
503
+ );
504
+ log .debug (">> 알림 전송 완료: userId={}, eventId={}" , userId , eventId );
505
+ } catch (Exception e ) {
506
+ log .error (">> 행사 복구 알림 전송 실패. 사용자ID: {}, 구매ID: {}, 오류: {}" ,
507
+ purchase .getUser ().getUserId (), purchase .getId (), e .getMessage (), e );
508
+ // 개별 사용자 알림 실패는 다른 사용자 알림이나 이벤트 복구에 영향을 주지 않음
509
+ }
510
+ }
511
+ } catch (Exception e ) {
512
+ log .error (">> 행사 복구 알림 처리 실패. 이벤트ID: {}, 오류: {}" , eventId , e .getMessage (), e );
513
+ // 알림 전체 실패는 이벤트 복구에 영향을 주지 않도록 예외를 무시함
514
+ }
515
+
516
+ // 복구된 이벤트 정보 반환
517
+ int soldTickets = ticketRepository .countPaidTicketsByEventId (eventId );
518
+ EventAdminDto eventDto = EventAdminDto .fromEntity (event , soldTickets );
519
+
520
+ log .info (">> 이벤트 복구 처리 완료: eventId={}" , eventId );
521
+ return eventDto ;
522
+ }
523
+
524
+ /**
525
+ * 이벤트를 삭제 처리하는 메서드입니다.
526
+ * 관리자 권한으로 이벤트를 삭제하고, 관련된 구매자들에게 알림을 전송합니다.
527
+ *
528
+ * @param eventId 삭제할 이벤트 ID
529
+ * @throws IllegalAccessException 이미 삭제된 이벤트인 경우
530
+ */
531
+ @ Transactional
532
+ public void deleteEvent (Long eventId ) throws IllegalAccessException {
533
+ log .info (">> 이벤트 삭제 처리 시작: eventId={}" , eventId );
534
+
535
+ Event event = eventRepository .findById (eventId )
536
+ .orElseThrow (() -> {
537
+ log .error (">> 이벤트 삭제 실패: 존재하지 않는 이벤트 - eventId={}" , eventId );
538
+ return new BadRequestException ("존재하지 않는 이벤트입니다." );
539
+ });
540
+
541
+ // 이벤트가 이미 삭제되었다면 400에러 전송
542
+ if (event .getIsDeleted ()) {
543
+ log .warn (">> 이벤트 삭제 실패: 이미 삭제된 이벤트 - eventId={}" , eventId );
544
+ throw new IllegalAccessException ("이미 삭제된 이벤트입니다." );
545
+ }
546
+
547
+ // 이벤트 상태 변경
548
+ event .setIsDeleted (true );
549
+ event .setStatus (EventStatusEnum .CANCELLED );
550
+ log .info (">> 이벤트 상태 변경 완료: eventId={}, status=CANCELLED" , eventId );
551
+
552
+ // 알림 처리는 메인 로직과 분리하여 예외 처리
553
+ try {
554
+ // 해당 이벤트 구매자들 조회
555
+ List <Purchase > purchases = purchaseRepository .findAllByEventId (eventId );
556
+ log .debug (">> 이벤트 구매자 조회 완료: eventId={}, 구매자 수={}" , eventId , purchases .size ());
557
+
558
+ // 모든 구매자에게 행사 취소 알림 전송
559
+ String notificationContent = String .format (
560
+ "[%s] 행사가 취소되었습니다. 예매 내역을 확인해주세요." ,
561
+ event .getInformation ().getTitle ()
562
+ );
563
+
564
+ for (Purchase purchase : purchases ) {
565
+ try {
566
+ Long userId = purchase .getUser ().getUserId ();
567
+ notificationService .createNotification (
568
+ userId ,
569
+ NotificationEnum .EVENT ,
570
+ notificationContent
571
+ );
572
+ log .debug (">> 알림 전송 완료: userId={}, eventId={}" , userId , eventId );
573
+ } catch (Exception e ) {
574
+ log .error (">> 행사 취소 알림 전송 실패. 사용자ID: {}, 구매ID: {}, 오류: {}" ,
575
+ purchase .getUser ().getUserId (), purchase .getId (), e .getMessage (), e );
576
+ // 개별 사용자 알림 실패는 다른 사용자 알림이나 이벤트 취소에 영향을 주지 않음
577
+ }
578
+ }
579
+ } catch (Exception e ) {
580
+ log .error (">> 행사 취소 알림 처리 실패. 이벤트ID: {}, 오류: {}" , eventId , e .getMessage (), e );
581
+ // 알림 전체 실패는 이벤트 취소에 영향을 주지 않도록 예외를 무시함
582
+ }
583
+
584
+ log .info (">> 이벤트 삭제 처리 완료: eventId={}" , eventId );
585
+ }
317
586
}
0 commit comments