diff --git a/.github/workflows/dev-CI.yml b/.github/workflows/dev-CI.yml
index 9e682ab2..3776a7c6 100644
--- a/.github/workflows/dev-CI.yml
+++ b/.github/workflows/dev-CI.yml
@@ -53,6 +53,10 @@ jobs:
DEV_COOLSMS_KEY: ${{ secrets.DEV_COOLSMS_KEY }}
DEV_COOLSMS_NUMBER: ${{ secrets.DEV_COOLSMS_NUMBER }}
DEV_COOLSMS_SECRET: ${{ secrets.DEV_COOLSMS_SECRET }}
+ DEV_ACCESS_TOKEN_EXPIRE_TIME: ${{ secrets.DEV_ACCESS_TOKEN_EXPIRE_TIME }}
+ DEV_REFRESH_TOKEN_EXPIRE_TIME: ${{ secrets.DEV_REFRESH_TOKEN_EXPIRE_TIME }}
+ DEV_ALLOWED_ORIGINS: ${{ secrets.DEV_ALLOWED_ORIGINS }}
+ DEV_SERVER_URL: ${{ secrets.DEV_SERVER_URL }}
run: |
cd ./src/main/resources
envsubst < application-dev.yml > application-dev.tmp.yml && mv application-dev.tmp.yml application-dev.yml
@@ -81,11 +85,11 @@ jobs:
docker build -f Dockerfile-dev --platform linux/amd64 -t hoonyworld/beat-dev .
docker push hoonyworld/beat-dev
- # Trigger Jenkins job - Jenkins 작업 트리거
- - name: Trigger Jenkins job
- uses: appleboy/jenkins-action@master
- with:
- url: ${{ secrets.DEV_WEBHOOK_URL }}
- user: "beat"
- token: ${{ secrets.DEV_JENKINS_API_TOKEN }}
- job: "beat-project"
\ No newline at end of file
+# # Trigger Jenkins job - Jenkins 작업 트리거
+# - name: Trigger Jenkins job
+# uses: appleboy/jenkins-action@master
+# with:
+# url: ${{ secrets.DEV_WEBHOOK_URL }}
+# user: "beat"
+# token: ${{ secrets.DEV_JENKINS_API_TOKEN }}
+# job: "beat-project"
\ No newline at end of file
diff --git a/.github/workflows/prod-CI.yml b/.github/workflows/prod-CI.yml
index c478ff71..deed64ad 100644
--- a/.github/workflows/prod-CI.yml
+++ b/.github/workflows/prod-CI.yml
@@ -53,6 +53,10 @@ jobs:
PROD_COOLSMS_KEY: ${{ secrets.PROD_COOLSMS_KEY }}
PROD_COOLSMS_NUMBER: ${{ secrets.PROD_COOLSMS_NUMBER }}
PROD_COOLSMS_SECRET: ${{ secrets.PROD_COOLSMS_SECRET }}
+ PROD_ACCESS_TOKEN_EXPIRE_TIME: ${{ secrets.PROD_ACCESS_TOKEN_EXPIRE_TIME }}
+ PROD_REFRESH_TOKEN_EXPIRE_TIME: ${{ secrets.PROD_REFRESH_TOKEN_EXPIRE_TIME }}
+ PROD_ALLOWED_ORIGINS: ${{ secrets.PROD_ALLOWED_ORIGINS }}
+ PROD_SERVER_URL: ${{ secrets.PROD_SERVER_URL }}
run: |
cd ./src/main/resources
envsubst < application-prod.yml > application-prod.tmp.yml && mv application-prod.tmp.yml application-prod.yml
@@ -81,11 +85,11 @@ jobs:
docker build --platform linux/amd64 -t donghoon0203/beat-prod .
docker push donghoon0203/beat-prod
- # Trigger Jenkins job - Jenkins 작업 트리거
- - name: Trigger Jenkins job
- uses: appleboy/jenkins-action@master
- with:
- url: ${{ secrets.PROD_WEBHOOK_URL }}
- user: "beat"
- token: ${{ secrets.PROD_JENKINS_API_TOKEN }}
- job: "beat-project"
+# # Trigger Jenkins job - Jenkins 작업 트리거
+# - name: Trigger Jenkins job
+# uses: appleboy/jenkins-action@master
+# with:
+# url: ${{ secrets.PROD_WEBHOOK_URL }}
+# user: "beat"
+# token: ${{ secrets.PROD_JENKINS_API_TOKEN }}
+# job: "beat-project"
diff --git a/.gitignore b/.gitignore
index e0586444..bf9eeb1d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -178,3 +178,6 @@ Temporary Items
# End of https://www.toptal.com/developers/gitignore/api/intellij,java,macos.gradle/
.idea/
+
+# Ignore application-local.yml
+src/main/resources/application-local.yml
\ No newline at end of file
diff --git a/HELP.md b/HELP.md
deleted file mode 100644
index 8826d98e..00000000
--- a/HELP.md
+++ /dev/null
@@ -1,30 +0,0 @@
-# Getting Started
-
-### Reference Documentation
-For further reference, please consider the following sections:
-
-* [Official Gradle documentation](https://docs.gradle.org)
-* [Spring Boot Gradle Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/3.3.1/gradle-plugin/reference/html/)
-* [Create an OCI image](https://docs.spring.io/spring-boot/docs/3.3.1/gradle-plugin/reference/html/#build-image)
-* [Spring Web](https://docs.spring.io/spring-boot/docs/3.3.1/reference/htmlsingle/index.html#web)
-* [Spring Data JPA](https://docs.spring.io/spring-boot/docs/3.3.1/reference/htmlsingle/index.html#data.sql.jpa-and-spring-data)
-* [Spring Security](https://docs.spring.io/spring-boot/docs/3.3.1/reference/htmlsingle/index.html#web.security)
-* [Spring Boot DevTools](https://docs.spring.io/spring-boot/docs/3.3.1/reference/htmlsingle/index.html#using.devtools)
-
-### Guides
-The following guides illustrate how to use some features concretely:
-
-* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/)
-* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/)
-* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/)
-* [Accessing data with MySQL](https://spring.io/guides/gs/accessing-data-mysql/)
-* [Accessing Data with JPA](https://spring.io/guides/gs/accessing-data-jpa/)
-* [Securing a Web Application](https://spring.io/guides/gs/securing-web/)
-* [Spring Boot and OAuth2](https://spring.io/guides/tutorials/spring-boot-oauth2/)
-* [Authenticating a User with LDAP](https://spring.io/guides/gs/authenticating-ldap/)
-
-### Additional Links
-These additional references should also help you:
-
-* [Gradle Build Scans – insights for your project's build](https://scans.gradle.com#gradle)
-
diff --git a/README.md b/README.md
index 1329a49e..675db961 100644
--- a/README.md
+++ b/README.md
@@ -114,4 +114,4 @@ BEAT와 함께 효율적이고 체계적으로 공연을 관리해 볼까요?
## 👥 Contributors
-- [BEAT Client Repository](https://github.com/TEAM-BEAT/BEAT-Client)
\ No newline at end of file
+- [BEAT Client Repository](https://github.com/TEAM-BEAT/BEAT-Client)
diff --git a/build.gradle b/build.gradle
index 82a3303b..dd665e45 100644
--- a/build.gradle
+++ b/build.gradle
@@ -28,7 +28,7 @@ repositories {
dependencies {
// Spring
implementation 'org.springframework.boot:spring-boot-starter-web'
- developmentOnly 'org.springframework.boot:spring-boot-devtools'
+// developmentOnly 'org.springframework.boot:spring-boot-devtools'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
// Database
diff --git a/src/main/java/com/beat/BeatApplication.java b/src/main/java/com/beat/BeatApplication.java
index 263f8261..da272990 100644
--- a/src/main/java/com/beat/BeatApplication.java
+++ b/src/main/java/com/beat/BeatApplication.java
@@ -5,9 +5,13 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableFeignClients
+@EnableScheduling
+@EnableAsync
@ImportAutoConfiguration({FeignAutoConfiguration.class})
public class BeatApplication {
diff --git a/src/main/java/com/beat/admin/adapter/in/api/AdminApi.java b/src/main/java/com/beat/admin/adapter/in/api/AdminApi.java
new file mode 100644
index 00000000..f5fb2ea6
--- /dev/null
+++ b/src/main/java/com/beat/admin/adapter/in/api/AdminApi.java
@@ -0,0 +1,134 @@
+package com.beat.admin.adapter.in.api;
+
+import com.beat.admin.application.dto.response.CarouselFindAllResponse;
+import com.beat.admin.application.dto.response.UserFindAllResponse;
+import com.beat.admin.application.dto.request.CarouselHandleRequest;
+import com.beat.admin.application.dto.response.CarouselHandleAllResponse;
+import com.beat.global.auth.annotation.CurrentMember;
+import com.beat.global.common.dto.ErrorResponse;
+import com.beat.global.common.dto.SuccessResponse;
+import com.beat.global.external.s3.application.dto.BannerPresignedUrlFindResponse;
+import com.beat.global.external.s3.application.dto.CarouselPresignedUrlFindAllResponse;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.util.List;
+
+@Tag(name = "Admin", description = "관리자 제어 API")
+public interface AdminApi {
+
+ @Operation(summary = "유저 정보 조회", description = "관리자가 유저들의 정보를 조회하는 GET API")
+ @ApiResponses(
+ value = {
+ @ApiResponse(
+ responseCode = "200",
+ description = "관리자 권한으로 모든 유저 조회에 성공하였습니다.",
+ content = @Content(schema = @Schema(implementation = SuccessResponse.class))
+ ),
+ @ApiResponse(
+ responseCode = "404",
+ description = "회원이 없습니다",
+ content = @Content(schema = @Schema(implementation = ErrorResponse.class))
+ )
+ }
+ )
+ ResponseEntity> readAllUsers(
+ @CurrentMember Long memberId
+ );
+
+ @Operation(summary = "캐러셀에 업로드 할 이미지에 대한 presigned URL 발급", description = "관리자가 캐러셀에 업로드 할 이미지에 대한 presigned URL을 발급 받는 GET API")
+ @ApiResponses(
+ value = {
+ @ApiResponse(
+ responseCode = "200",
+ description = "캐러셀 Presigned URL 발급 성공",
+ content = @Content(schema = @Schema(implementation = SuccessResponse.class))
+ ),
+ @ApiResponse(
+ responseCode = "404",
+ description = "회원이 없습니다.",
+ content = @Content(schema = @Schema(implementation = ErrorResponse.class))
+ )
+ }
+ )
+ ResponseEntity> createAllCarouselPresignedUrls(
+ @CurrentMember Long memberId,
+ @RequestParam List carouselImages
+ );
+
+ @Operation(summary = "배너에 업로드 할 이미지에 대한 presigned URL 발급", description = "관리자가 배너에 업로드 할 이미지에 대한 presigned URL을 발급 받는 GET API")
+ @ApiResponses(
+ value = {
+ @ApiResponse(
+ responseCode = "200",
+ description = "배너 Presigned URL 발급 성공",
+ content = @Content(schema = @Schema(implementation = SuccessResponse.class))
+ ),
+ @ApiResponse(
+ responseCode = "404",
+ description = "회원이 없습니다.",
+ content = @Content(schema = @Schema(implementation = ErrorResponse.class))
+ )
+ }
+ )
+ ResponseEntity> createBannerPresignedUrl(
+ @CurrentMember Long memberId,
+ @RequestParam String bannerImage
+ );
+
+ @Operation(summary = "캐러셀에 등록된 모든 공연 정보 조회", description = "관리자가 현재 캐러셀에 등록된 모든 공연 정보를 조회하는 GET API")
+ @ApiResponses(
+ value = {
+ @ApiResponse(
+ responseCode = "200",
+ description = "관리자 권한으로 현재 캐러셀에 등록된 모든 공연 조회에 성공하였습니다.",
+ content = @Content(schema = @Schema(implementation = SuccessResponse.class))
+ ),
+ @ApiResponse(
+ responseCode = "404",
+ description = "회원이 없습니다.",
+ content = @Content(schema = @Schema(implementation = ErrorResponse.class))
+ )
+ }
+ )
+ ResponseEntity> readAllCarouselImages(
+ @CurrentMember Long memberId
+ );
+
+ @Operation(summary = "캐러셀 이미지 수정", description = "관리자가 캐러셀 이미지를 수정하는 PUT API")
+ @ApiResponses(
+ value = {
+ @ApiResponse(
+ responseCode = "200",
+ description = "캐러셀 이미지 수정 성공",
+ content = @Content(schema = @Schema(implementation = SuccessResponse.class))
+ ),
+ @ApiResponse(
+ responseCode = "404",
+ description = "회원이 없습니다.",
+ content = @Content(schema = @Schema(implementation = ErrorResponse.class))
+ ),
+ @ApiResponse(
+ responseCode = "404",
+ description = "해당 홍보 정보를 찾을 수 없습니다.",
+ content = @Content(schema = @Schema(implementation = ErrorResponse.class))
+ ),
+ @ApiResponse(
+ responseCode = "404",
+ description = "해당 공연 정보를 찾을 수 없습니다.",
+ content = @Content(schema = @Schema(implementation = ErrorResponse.class))
+ )
+ }
+ )
+ ResponseEntity> processCarouselImages(
+ @CurrentMember Long memberId,
+ @RequestBody CarouselHandleRequest request
+ );
+}
diff --git a/src/main/java/com/beat/admin/adapter/in/api/AdminController.java b/src/main/java/com/beat/admin/adapter/in/api/AdminController.java
new file mode 100644
index 00000000..20dd1f31
--- /dev/null
+++ b/src/main/java/com/beat/admin/adapter/in/api/AdminController.java
@@ -0,0 +1,79 @@
+package com.beat.admin.adapter.in.api;
+
+import com.beat.admin.application.dto.response.CarouselFindAllResponse;
+import com.beat.admin.application.dto.request.CarouselHandleRequest;
+import com.beat.admin.application.dto.response.CarouselHandleAllResponse;
+import com.beat.admin.exception.AdminSuccessCode;
+import com.beat.admin.application.dto.response.UserFindAllResponse;
+import com.beat.admin.facade.AdminFacade;
+import com.beat.global.auth.annotation.CurrentMember;
+import com.beat.global.common.dto.SuccessResponse;
+import com.beat.global.external.s3.application.dto.BannerPresignedUrlFindResponse;
+import com.beat.global.external.s3.application.dto.CarouselPresignedUrlFindAllResponse;
+
+import lombok.RequiredArgsConstructor;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/admin")
+@RequiredArgsConstructor
+public class AdminController implements AdminApi {
+
+ private final AdminFacade adminFacade;
+
+ @Override
+ @GetMapping("/users")
+ public ResponseEntity> readAllUsers(@CurrentMember Long memberId) {
+ UserFindAllResponse response = adminFacade.checkMemberAndFindAllUsers(memberId);
+ return ResponseEntity.status(HttpStatus.OK)
+ .body(SuccessResponse.of(AdminSuccessCode.FETCH_ALL_USERS_SUCCESS, response));
+ }
+
+ @Override
+ @GetMapping("/carousels/presigned-url")
+ public ResponseEntity> createAllCarouselPresignedUrls(
+ @CurrentMember Long memberId, @RequestParam List carouselImages) {
+ CarouselPresignedUrlFindAllResponse response = adminFacade.checkMemberAndIssueAllPresignedUrlsForCarousel(
+ memberId, carouselImages);
+ return ResponseEntity.ok(SuccessResponse.of(AdminSuccessCode.CAROUSEL_PRESIGNED_URL_ISSUED, response));
+ }
+
+ @Override
+ @GetMapping("/banner/presigned-url")
+ public ResponseEntity> createBannerPresignedUrl(
+ @CurrentMember Long memberId, @RequestParam String bannerImage) {
+ BannerPresignedUrlFindResponse response = adminFacade.checkMemberAndIssuePresignedUrlForBanner(memberId,
+ bannerImage);
+ return ResponseEntity.status(HttpStatus.OK)
+ .body(SuccessResponse.of(AdminSuccessCode.BANNER_PRESIGNED_URL_ISSUED, response));
+ }
+
+ @Override
+ @GetMapping("/carousels")
+ public ResponseEntity> readAllCarouselImages(
+ @CurrentMember Long memberId) {
+ CarouselFindAllResponse response = adminFacade.checkMemberAndFindAllPromotionsSortedByCarouselNumber(memberId);
+ return ResponseEntity.status(HttpStatus.OK)
+ .body(SuccessResponse.of(AdminSuccessCode.FETCH_ALL_CAROUSEL_PROMOTIONS_SUCCESS, response));
+ }
+
+ @Override
+ @PutMapping("/carousels")
+ public ResponseEntity> processCarouselImages(
+ @CurrentMember Long memberId,
+ @RequestBody CarouselHandleRequest request) {
+ CarouselHandleAllResponse response = adminFacade.checkMemberAndProcessAllPromotionsSortedByCarouselNumber(memberId, request);
+ return ResponseEntity.status(HttpStatus.OK)
+ .body(SuccessResponse.of(AdminSuccessCode.UPDATE_ALL_CAROUSEL_PROMOTIONS_SUCCESS, response));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/beat/admin/application/AdminService.java b/src/main/java/com/beat/admin/application/AdminService.java
new file mode 100644
index 00000000..530e79ec
--- /dev/null
+++ b/src/main/java/com/beat/admin/application/AdminService.java
@@ -0,0 +1,89 @@
+package com.beat.admin.application;
+
+import com.beat.admin.application.dto.request.CarouselHandleRequest.PromotionGenerateRequest;
+import com.beat.admin.application.dto.request.CarouselHandleRequest.PromotionModifyRequest;
+import com.beat.admin.port.in.AdminUseCase;
+import com.beat.domain.performance.domain.Performance;
+import com.beat.domain.performance.port.in.PerformanceUseCase;
+import com.beat.domain.promotion.domain.Promotion;
+import com.beat.domain.promotion.port.in.PromotionUseCase;
+
+import lombok.RequiredArgsConstructor;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Optional;
+
+@Service
+@RequiredArgsConstructor
+public class AdminService implements AdminUseCase {
+
+ private final PromotionUseCase promotionUseCase;
+ private final PerformanceUseCase performanceUseCase;
+
+ @Override
+ @Transactional(readOnly = true)
+ public List findAllPromotionsSortedByCarouselNumber() {
+ List promotions = promotionUseCase.findAllPromotions();
+ return sortPromotionsByCarouselNumber(promotions);
+ }
+
+ @Override
+ @Transactional
+ public List processPromotionsAndSortByPromotionId(List modifyRequests,
+ List generateRequests, List deletePromotionIds) {
+
+ handlePromotionDeletion(deletePromotionIds);
+ List modifiedPromotions = handlePromotionModification(modifyRequests);
+ List addedPromotions = handlePromotionGeneration(generateRequests);
+
+ List applyPromotionChanges = new ArrayList<>(modifiedPromotions);
+ applyPromotionChanges.addAll(addedPromotions);
+
+ return sortPromotionsByCarouselNumber(applyPromotionChanges);
+ }
+
+ private void handlePromotionDeletion(List deletePromotionIds) {
+ if (!deletePromotionIds.isEmpty()) {
+ promotionUseCase.deletePromotionsByPromotionIds(deletePromotionIds);
+ }
+ }
+
+ private List handlePromotionModification(List modifyRequests) {
+ return modifyRequests.stream()
+ .map(modifyRequest -> {
+
+ Promotion promotion = promotionUseCase.findById(modifyRequest.promotionId());
+
+ Performance performance = Optional.ofNullable(modifyRequest.performanceId())
+ .map(performanceUseCase::findById)
+ .orElse(null);
+
+ return promotionUseCase.modifyPromotion(promotion, performance, modifyRequest);
+ })
+ .toList();
+ }
+
+ private List handlePromotionGeneration(List generateRequests) {
+ return generateRequests.stream()
+ .map(generateRequest -> {
+ Performance performance = Optional.ofNullable(generateRequest.performanceId())
+ .map(performanceUseCase::findById)
+ .orElse(null);
+
+ return promotionUseCase.createPromotion(generateRequest.newImageUrl(), performance,
+ generateRequest.redirectUrl(), generateRequest.isExternal(), generateRequest.carouselNumber());
+ })
+ .toList();
+ }
+
+ private List sortPromotionsByCarouselNumber(List promotions) {
+ return promotions.stream()
+ .sorted(Comparator.comparing(Promotion::getCarouselNumber, Comparator.comparingInt(Enum::ordinal)))
+ .toList();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/beat/admin/application/dto/request/CarouselHandleRequest.java b/src/main/java/com/beat/admin/application/dto/request/CarouselHandleRequest.java
new file mode 100644
index 00000000..c5aa18a7
--- /dev/null
+++ b/src/main/java/com/beat/admin/application/dto/request/CarouselHandleRequest.java
@@ -0,0 +1,29 @@
+package com.beat.admin.application.dto.request;
+
+import java.util.List;
+
+import com.beat.domain.promotion.domain.CarouselNumber;
+
+public record CarouselHandleRequest(
+ List carousels
+) {
+
+ public record PromotionModifyRequest(
+ Long promotionId,
+ CarouselNumber carouselNumber,
+ String newImageUrl,
+ boolean isExternal,
+ String redirectUrl,
+ Long performanceId
+ ) implements PromotionHandleRequest {
+ }
+
+ public record PromotionGenerateRequest(
+ CarouselNumber carouselNumber,
+ String newImageUrl,
+ boolean isExternal,
+ String redirectUrl,
+ Long performanceId
+ ) implements PromotionHandleRequest {
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/beat/admin/application/dto/request/PromotionHandleRequest.java b/src/main/java/com/beat/admin/application/dto/request/PromotionHandleRequest.java
new file mode 100644
index 00000000..e293d603
--- /dev/null
+++ b/src/main/java/com/beat/admin/application/dto/request/PromotionHandleRequest.java
@@ -0,0 +1,28 @@
+package com.beat.admin.application.dto.request;
+
+import static com.beat.admin.application.dto.request.CarouselHandleRequest.*;
+
+import com.beat.domain.promotion.domain.CarouselNumber;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+@JsonTypeInfo(
+ use = JsonTypeInfo.Id.NAME,
+ property = "type"
+)
+@JsonSubTypes({
+ @JsonSubTypes.Type(value = PromotionModifyRequest.class, name = "modify"),
+ @JsonSubTypes.Type(value = PromotionGenerateRequest.class, name = "generate")
+})
+public sealed interface PromotionHandleRequest
+ permits PromotionModifyRequest, PromotionGenerateRequest {
+ CarouselNumber carouselNumber();
+
+ String newImageUrl();
+
+ boolean isExternal();
+
+ String redirectUrl();
+
+ Long performanceId();
+}
\ No newline at end of file
diff --git a/src/main/java/com/beat/admin/application/dto/response/CarouselFindAllResponse.java b/src/main/java/com/beat/admin/application/dto/response/CarouselFindAllResponse.java
new file mode 100644
index 00000000..878aa861
--- /dev/null
+++ b/src/main/java/com/beat/admin/application/dto/response/CarouselFindAllResponse.java
@@ -0,0 +1,41 @@
+package com.beat.admin.application.dto.response;
+
+import com.beat.domain.performance.domain.Performance;
+import com.beat.domain.promotion.domain.CarouselNumber;
+import com.beat.domain.promotion.domain.Promotion;
+
+import java.util.List;
+import java.util.Optional;
+
+public record CarouselFindAllResponse(
+ List carousels
+) {
+ public static CarouselFindAllResponse from(List promotions) {
+ List responses = promotions.stream()
+ .map(CarouselFindResponse::from)
+ .toList();
+ return new CarouselFindAllResponse(responses);
+ }
+
+ public record CarouselFindResponse(
+ Long promotionId,
+ CarouselNumber carouselNumber,
+ String newImageUrl,
+ boolean isExternal,
+ String redirectUrl,
+ Long performanceId
+ ) {
+ public static CarouselFindResponse from(Promotion promotion) {
+ return new CarouselFindResponse(
+ promotion.getId(),
+ promotion.getCarouselNumber(),
+ promotion.getPromotionPhoto(),
+ promotion.isExternal(),
+ promotion.getRedirectUrl(),
+ Optional.ofNullable(promotion.getPerformance())
+ .map(Performance::getId)
+ .orElse(null)
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/beat/admin/application/dto/response/CarouselHandleAllResponse.java b/src/main/java/com/beat/admin/application/dto/response/CarouselHandleAllResponse.java
new file mode 100644
index 00000000..999224b0
--- /dev/null
+++ b/src/main/java/com/beat/admin/application/dto/response/CarouselHandleAllResponse.java
@@ -0,0 +1,36 @@
+package com.beat.admin.application.dto.response;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.beat.domain.promotion.domain.Promotion;
+
+public record CarouselHandleAllResponse(
+ List modifiedPromotions
+) {
+
+ public static CarouselHandleAllResponse from(List promotions) {
+ List modifiedPromotions = promotions.stream()
+ .map(PromotionResponse::from)
+ .collect(Collectors.toList());
+ return new CarouselHandleAllResponse(modifiedPromotions);
+ }
+
+ public record PromotionResponse(
+ Long promotionId,
+ String newImageUrl,
+ boolean isExternal,
+ String redirectUrl,
+ String carouselNumber
+ ) {
+ public static PromotionResponse from(Promotion promotion) {
+ return new PromotionResponse(
+ promotion.getId(),
+ promotion.getPromotionPhoto(),
+ promotion.isExternal(),
+ promotion.getRedirectUrl(),
+ promotion.getCarouselNumber().name()
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/beat/admin/application/dto/response/UserFindAllResponse.java b/src/main/java/com/beat/admin/application/dto/response/UserFindAllResponse.java
new file mode 100644
index 00000000..a5274131
--- /dev/null
+++ b/src/main/java/com/beat/admin/application/dto/response/UserFindAllResponse.java
@@ -0,0 +1,28 @@
+package com.beat.admin.application.dto.response;
+
+import com.beat.domain.user.domain.Users;
+
+import java.util.List;
+
+public record UserFindAllResponse(
+ List users
+) {
+ public static UserFindAllResponse from(List users) {
+ List userFindResponses = users.stream()
+ .map(UserFindResponse::from)
+ .toList();
+ return new UserFindAllResponse(userFindResponses);
+ }
+
+ public record UserFindResponse(
+ Long id,
+ String role
+ ) {
+ public static UserFindResponse from(Users user) {
+ return new UserFindResponse(
+ user.getId(),
+ user.getRole().getRoleName()
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/beat/admin/exception/AdminSuccessCode.java b/src/main/java/com/beat/admin/exception/AdminSuccessCode.java
new file mode 100644
index 00000000..7125c6a3
--- /dev/null
+++ b/src/main/java/com/beat/admin/exception/AdminSuccessCode.java
@@ -0,0 +1,19 @@
+package com.beat.admin.exception;
+
+import com.beat.global.common.exception.base.BaseSuccessCode;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor
+public enum AdminSuccessCode implements BaseSuccessCode {
+ FETCH_ALL_USERS_SUCCESS(200, "관리자 권한으로 모든 유저 조회에 성공하였습니다."),
+ CAROUSEL_PRESIGNED_URL_ISSUED(200, "캐러셀 Presigned URL 발급 성공"),
+ BANNER_PRESIGNED_URL_ISSUED(200, "배너 Presigned URL 발급 성공"),
+ FETCH_ALL_CAROUSEL_PROMOTIONS_SUCCESS(200, "관리자 권한으로 현재 캐러셀에 등록된 모든 공연 조회에 성공하였습니다."),
+ UPDATE_ALL_CAROUSEL_PROMOTIONS_SUCCESS(200, "관리자 권한으로 캐러셀 수정에 성공하였습니다.")
+ ;
+
+ private final int status;
+ private final String message;
+}
\ No newline at end of file
diff --git a/src/main/java/com/beat/admin/facade/AdminFacade.java b/src/main/java/com/beat/admin/facade/AdminFacade.java
new file mode 100644
index 00000000..968b1d5b
--- /dev/null
+++ b/src/main/java/com/beat/admin/facade/AdminFacade.java
@@ -0,0 +1,111 @@
+package com.beat.admin.facade;
+
+import com.beat.admin.application.dto.request.PromotionHandleRequest;
+import com.beat.admin.application.dto.response.CarouselFindAllResponse;
+import com.beat.admin.application.dto.response.UserFindAllResponse;
+import com.beat.admin.application.dto.request.CarouselHandleRequest;
+import com.beat.admin.application.dto.request.CarouselHandleRequest.PromotionGenerateRequest;
+import com.beat.admin.application.dto.request.CarouselHandleRequest.PromotionModifyRequest;
+import com.beat.admin.application.dto.response.CarouselHandleAllResponse;
+import com.beat.admin.port.in.AdminUseCase;
+import com.beat.domain.member.port.in.MemberUseCase;
+import com.beat.domain.promotion.domain.Promotion;
+import com.beat.domain.promotion.port.in.PromotionUseCase;
+import com.beat.domain.user.domain.Users;
+import com.beat.domain.user.port.in.UserUseCase;
+import com.beat.global.external.s3.application.dto.BannerPresignedUrlFindResponse;
+import com.beat.global.external.s3.application.dto.CarouselPresignedUrlFindAllResponse;
+import com.beat.global.external.s3.port.in.FileUseCase;
+
+import lombok.RequiredArgsConstructor;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Service
+@Transactional
+@RequiredArgsConstructor
+public class AdminFacade {
+ private final FileUseCase fileUseCase;
+ private final AdminUseCase adminUsecase;
+ private final MemberUseCase memberUseCase;
+ private final UserUseCase userUseCase;
+ private final PromotionUseCase promotionUseCase;
+
+ public UserFindAllResponse checkMemberAndFindAllUsers(Long memberId) {
+ memberUseCase.findMemberByMemberId(memberId);
+ List users = userUseCase.findAllUsers();
+ return UserFindAllResponse.from(users);
+ }
+
+ public CarouselPresignedUrlFindAllResponse checkMemberAndIssueAllPresignedUrlsForCarousel(Long memberId,
+ List carouselImages) {
+ memberUseCase.findMemberByMemberId(memberId);
+ Map carouselPresignedUrls = fileUseCase.issueAllPresignedUrlsForCarousel(carouselImages);
+ return CarouselPresignedUrlFindAllResponse.from(carouselPresignedUrls);
+ }
+
+ public BannerPresignedUrlFindResponse checkMemberAndIssuePresignedUrlForBanner(Long memberId, String bannerImage) {
+ memberUseCase.findMemberByMemberId(memberId);
+ String bannerPresignedUrl = fileUseCase.issuePresignedUrlForBanner(bannerImage);
+ return BannerPresignedUrlFindResponse.from(bannerPresignedUrl);
+ }
+
+ public CarouselFindAllResponse checkMemberAndFindAllPromotionsSortedByCarouselNumber(Long memberId) {
+ memberUseCase.findMemberByMemberId(memberId);
+ List promotions = adminUsecase.findAllPromotionsSortedByCarouselNumber();
+ return CarouselFindAllResponse.from(promotions);
+ }
+
+ public CarouselHandleAllResponse checkMemberAndProcessAllPromotionsSortedByCarouselNumber(Long memberId,
+ CarouselHandleRequest request) {
+
+ memberUseCase.findMemberByMemberId(memberId);
+
+ List modifyRequests = new ArrayList<>();
+ List generateRequests = new ArrayList<>();
+ Set requestPromotionIds = new HashSet<>();
+
+ categorizePromotionRequestsByPromotionId(request, modifyRequests, generateRequests, requestPromotionIds);
+
+ List allExistingPromotions = promotionUseCase.findAllPromotions();
+
+ List deletePromotionIds = extractDeletePromotionIds(allExistingPromotions, requestPromotionIds);
+
+ List sortedPromotions = adminUsecase.processPromotionsAndSortByPromotionId(modifyRequests,
+ generateRequests, deletePromotionIds);
+
+ return CarouselHandleAllResponse.from(sortedPromotions);
+ }
+
+ private void categorizePromotionRequestsByPromotionId(CarouselHandleRequest request,
+ List modifyRequests, List generateRequests,
+ Set requestPromotionIds) {
+
+ for (PromotionHandleRequest promotionRequest : request.carousels()) {
+ if (promotionRequest instanceof PromotionModifyRequest modifyRequest) {
+ modifyRequests.add(modifyRequest);
+ requestPromotionIds.add(modifyRequest.promotionId());
+ } else if (promotionRequest instanceof PromotionGenerateRequest generateRequest) {
+ generateRequests.add(generateRequest);
+ }
+ }
+ }
+
+ private List extractDeletePromotionIds(List allExistingPromotions, Set requestPromotionIds) {
+ Set allExistingPromotionIds = allExistingPromotions.stream()
+ .map(Promotion::getId)
+ .collect(Collectors.toSet());
+
+ return allExistingPromotionIds.stream()
+ .filter(existingId -> !requestPromotionIds.contains(existingId))
+ .toList();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/beat/admin/port/in/AdminUseCase.java b/src/main/java/com/beat/admin/port/in/AdminUseCase.java
new file mode 100644
index 00000000..93fa39fc
--- /dev/null
+++ b/src/main/java/com/beat/admin/port/in/AdminUseCase.java
@@ -0,0 +1,14 @@
+package com.beat.admin.port.in;
+
+import com.beat.admin.application.dto.request.CarouselHandleRequest.PromotionGenerateRequest;
+import com.beat.admin.application.dto.request.CarouselHandleRequest.PromotionModifyRequest;
+import com.beat.domain.promotion.domain.Promotion;
+
+import java.util.List;
+
+public interface AdminUseCase {
+ List findAllPromotionsSortedByCarouselNumber();
+
+ List processPromotionsAndSortByPromotionId(List modifyRequests,
+ List generateRequests, List deletePromotionIds);
+}
\ No newline at end of file
diff --git a/src/main/java/com/beat/domain/booking/api/BookingController.java b/src/main/java/com/beat/domain/booking/api/BookingController.java
index 3f54b30b..dae62784 100644
--- a/src/main/java/com/beat/domain/booking/api/BookingController.java
+++ b/src/main/java/com/beat/domain/booking/api/BookingController.java
@@ -47,7 +47,7 @@ public ResponseEntity> createMemberBookin
@Operation(summary = "회원 예매 조회 API", description = "회원이 예매를 조회하는 GET API입니다.")
@GetMapping("/member/retrieve")
- public ResponseEntity> getMemberBookings(
+ public ResponseEntity>> getMemberBookings(
@CurrentMember Long memberId) {
List response = memberBookingRetrieveService.findMemberBookings(memberId);
return ResponseEntity.status(HttpStatus.OK)
diff --git a/src/main/java/com/beat/domain/booking/api/TicketController.java b/src/main/java/com/beat/domain/booking/api/TicketController.java
index 1e03e66e..ef10a3d7 100644
--- a/src/main/java/com/beat/domain/booking/api/TicketController.java
+++ b/src/main/java/com/beat/domain/booking/api/TicketController.java
@@ -1,9 +1,10 @@
package com.beat.domain.booking.api;
import com.beat.domain.booking.application.TicketService;
-import com.beat.domain.booking.application.dto.TicketDeleteRequest;
+import com.beat.domain.booking.application.dto.TicketCancelRequest;
import com.beat.domain.booking.application.dto.TicketRetrieveResponse;
import com.beat.domain.booking.application.dto.TicketUpdateRequest;
+import com.beat.domain.booking.domain.BookingStatus;
import com.beat.domain.booking.exception.BookingSuccessCode;
import com.beat.global.auth.annotation.CurrentMember;
import com.beat.global.common.dto.SuccessResponse;
@@ -26,8 +27,8 @@ public ResponseEntity> getTickets(
@CurrentMember Long memberId,
@PathVariable Long performanceId,
@RequestParam(required = false) ScheduleNumber scheduleNumber,
- @RequestParam(required = false) Boolean isPaymentCompleted) {
- TicketRetrieveResponse response = ticketService.getTickets(memberId, performanceId, scheduleNumber, isPaymentCompleted);
+ @RequestParam(required = false) BookingStatus bookingStatus) {
+ TicketRetrieveResponse response = ticketService.getTickets(memberId, performanceId, scheduleNumber, bookingStatus);
return ResponseEntity.ok(SuccessResponse.of(BookingSuccessCode.TICKET_RETRIEVE_SUCCESS, response));
}
@@ -37,15 +38,15 @@ public ResponseEntity> updateTickets(
@CurrentMember Long memberId,
@RequestBody TicketUpdateRequest request) {
ticketService.updateTickets(memberId, request);
- return ResponseEntity.ok(SuccessResponse.of(BookingSuccessCode.TICKET_UPDATE_SUCCESS, null));
+ return ResponseEntity.ok(SuccessResponse.from(BookingSuccessCode.TICKET_UPDATE_SUCCESS));
}
- @Operation(summary = "예매자 삭제 API", description = "메이커가 자신의 공연에 대한 예매자의 정보를 삭제하는 DELETE API입니다.")
- @DeleteMapping
- public ResponseEntity> deleteTickets(
+ @Operation(summary = "예매자 취소 API", description = "메이커가 자신의 공연에 대한 1명 이상의 예매자의 정보를 취소 상태로 변경하는 PATCH API입니다.")
+ @PatchMapping
+ public ResponseEntity> cancelTickets(
@CurrentMember Long memberId,
- @RequestBody TicketDeleteRequest ticketDeleteRequest) {
- ticketService.deleteTickets(memberId, ticketDeleteRequest);
- return ResponseEntity.ok(SuccessResponse.from(BookingSuccessCode.TICKET_DELETE_SUCCESS));
+ @RequestBody TicketCancelRequest ticketCancelRequest) {
+ ticketService.cancelTickets(memberId, ticketCancelRequest);
+ return ResponseEntity.ok(SuccessResponse.from(BookingSuccessCode.TICKET_CANCEL_SUCCESS));
}
}
diff --git a/src/main/java/com/beat/domain/booking/application/GuestBookingRetrieveService.java b/src/main/java/com/beat/domain/booking/application/GuestBookingRetrieveService.java
index 8a3aaa24..831d796b 100644
--- a/src/main/java/com/beat/domain/booking/application/GuestBookingRetrieveService.java
+++ b/src/main/java/com/beat/domain/booking/application/GuestBookingRetrieveService.java
@@ -83,7 +83,7 @@ private GuestBookingRetrieveResponse toBookingResponse(Booking booking) {
performance.getAccountNumber(),
performance.getAccountHolder(),
calculateDueDate(schedule.getPerformanceDate()),
- booking.isPaymentCompleted(),
+ booking.getBookingStatus(),
booking.getCreatedAt(),
performance.getPosterImage(),
totalPaymentAmount
diff --git a/src/main/java/com/beat/domain/booking/application/GuestBookingService.java b/src/main/java/com/beat/domain/booking/application/GuestBookingService.java
index f15601c8..0aee93f7 100644
--- a/src/main/java/com/beat/domain/booking/application/GuestBookingService.java
+++ b/src/main/java/com/beat/domain/booking/application/GuestBookingService.java
@@ -12,17 +12,15 @@
import com.beat.global.common.exception.BadRequestException;
import com.beat.global.common.exception.NotFoundException;
import lombok.RequiredArgsConstructor;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+@Slf4j
@Service
@RequiredArgsConstructor
public class GuestBookingService {
- private static final Logger logger = LoggerFactory.getLogger(GuestBookingService.class);
-
private final ScheduleRepository scheduleRepository;
private final BookingRepository bookingRepository;
private final UserRepository userRepository;
@@ -37,7 +35,7 @@ public GuestBookingResponse createGuestBooking(GuestBookingRequest guestBookingR
throw new BadRequestException(ScheduleErrorCode.INSUFFICIENT_TICKETS);
}
- schedule.setSoldTicketCount(schedule.getSoldTicketCount() + guestBookingRequest.purchaseTicketCount());
+ updateSoldTicketCountAndIsBooking(schedule, guestBookingRequest.purchaseTicketCount());
Users users = bookingRepository.findFirstByBookerNameAndBookerPhoneNumberAndBirthDateAndPassword(
guestBookingRequest.bookerName(),
@@ -58,7 +56,7 @@ public GuestBookingResponse createGuestBooking(GuestBookingRequest guestBookingR
guestBookingRequest.purchaseTicketCount(),
guestBookingRequest.bookerName(),
guestBookingRequest.bookerPhoneNumber(),
- guestBookingRequest.isPaymentCompleted(),
+ guestBookingRequest.bookingStatus(),
guestBookingRequest.birthDate(),
guestBookingRequest.password(),
schedule,
@@ -66,7 +64,7 @@ public GuestBookingResponse createGuestBooking(GuestBookingRequest guestBookingR
);
bookingRepository.save(booking);
- logger.info("Booking created: {}", booking);
+ log.info("Guest Booking created: {}", booking);
return GuestBookingResponse.of(
booking.getId(),
@@ -76,11 +74,19 @@ public GuestBookingResponse createGuestBooking(GuestBookingRequest guestBookingR
schedule.getScheduleNumber(),
booking.getBookerName(),
booking.getBookerPhoneNumber(),
- booking.isPaymentCompleted(),
+ booking.getBookingStatus(),
schedule.getPerformance().getBankName(),
schedule.getPerformance().getAccountNumber(),
totalPaymentAmount,
booking.getCreatedAt()
);
}
+
+ private void updateSoldTicketCountAndIsBooking(Schedule schedule, int purchaseTicketCount) {
+ schedule.setSoldTicketCount(schedule.getSoldTicketCount() + purchaseTicketCount);
+
+ if (schedule.getTotalTicketCount() == schedule.getSoldTicketCount()) {
+ schedule.updateIsBooking(false);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/beat/domain/booking/application/MemberBookingRetrieveService.java b/src/main/java/com/beat/domain/booking/application/MemberBookingRetrieveService.java
index 82c6981c..a12f20ec 100644
--- a/src/main/java/com/beat/domain/booking/application/MemberBookingRetrieveService.java
+++ b/src/main/java/com/beat/domain/booking/application/MemberBookingRetrieveService.java
@@ -64,7 +64,7 @@ private MemberBookingRetrieveResponse toMemberBookingResponse(Booking booking) {
performance.getAccountNumber(),
performance.getAccountHolder(),
calculateDueDate(schedule.getPerformanceDate()),
- booking.isPaymentCompleted(),
+ booking.getBookingStatus(),
booking.getCreatedAt(),
performance.getPosterImage(),
totalPaymentAmount
diff --git a/src/main/java/com/beat/domain/booking/application/MemberBookingService.java b/src/main/java/com/beat/domain/booking/application/MemberBookingService.java
index 2d6d9ccb..fbe57b3e 100644
--- a/src/main/java/com/beat/domain/booking/application/MemberBookingService.java
+++ b/src/main/java/com/beat/domain/booking/application/MemberBookingService.java
@@ -16,9 +16,11 @@
import com.beat.global.common.exception.BadRequestException;
import com.beat.global.common.exception.NotFoundException;
import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+@Slf4j
@Service
@RequiredArgsConstructor
public class MemberBookingService {
@@ -38,7 +40,7 @@ public MemberBookingResponse createMemberBooking(Long memberId, MemberBookingReq
throw new BadRequestException(ScheduleErrorCode.INSUFFICIENT_TICKETS);
}
- schedule.setSoldTicketCount(schedule.getSoldTicketCount() + memberBookingRequest.purchaseTicketCount());
+ updateSoldTicketCountAndIsBooking(schedule, memberBookingRequest.purchaseTicketCount());
Member member = memberRepository.findById(memberId).orElseThrow(
() -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND));
@@ -50,7 +52,7 @@ public MemberBookingResponse createMemberBooking(Long memberId, MemberBookingReq
memberBookingRequest.purchaseTicketCount(),
memberBookingRequest.bookerName(),
memberBookingRequest.bookerPhoneNumber(),
- memberBookingRequest.isPaymentCompleted(),
+ memberBookingRequest.bookingStatus(),
null,
null,
schedule,
@@ -59,6 +61,8 @@ public MemberBookingResponse createMemberBooking(Long memberId, MemberBookingReq
bookingRepository.save(booking);
scheduleRepository.save(schedule);
+ log.info("Member Booking created: {}", booking);
+
return MemberBookingResponse.of(
booking.getId(),
schedule.getId(),
@@ -67,11 +71,19 @@ public MemberBookingResponse createMemberBooking(Long memberId, MemberBookingReq
schedule.getScheduleNumber(),
booking.getBookerName(),
booking.getBookerPhoneNumber(),
- booking.isPaymentCompleted(),
+ booking.getBookingStatus(),
schedule.getPerformance().getBankName(),
schedule.getPerformance().getAccountNumber(),
- memberBookingRequest.totalPaymentAmount(),
+ memberBookingRequest.totalPaymentAmount(), // 비회원 예매처럼 int totalPaymentAmount = ticketPrice * guestBookingRequest.purchaseTicketCount();로 계산해서 반영하기 + 요청한 총 가격 == 티켓 가격 * 수 같은지 검증하는 로직 추가하기
booking.getCreatedAt()
);
}
+
+ private void updateSoldTicketCountAndIsBooking(Schedule schedule, int purchaseTicketCount) {
+ schedule.setSoldTicketCount(schedule.getSoldTicketCount() + purchaseTicketCount);
+
+ if (schedule.getTotalTicketCount() == schedule.getSoldTicketCount()) {
+ schedule.updateIsBooking(false);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/beat/domain/booking/application/TicketService.java b/src/main/java/com/beat/domain/booking/application/TicketService.java
index 4a5a4e58..cd0b9e4a 100644
--- a/src/main/java/com/beat/domain/booking/application/TicketService.java
+++ b/src/main/java/com/beat/domain/booking/application/TicketService.java
@@ -1,8 +1,25 @@
package com.beat.domain.booking.application;
-import com.beat.domain.booking.application.dto.*;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import net.nurigo.java_sdk.exceptions.CoolsmsException;
+
+import com.beat.domain.booking.application.dto.TicketCancelRequest;
+import com.beat.domain.booking.application.dto.TicketDetail;
+import com.beat.domain.booking.application.dto.TicketRetrieveResponse;
+import com.beat.domain.booking.application.dto.TicketUpdateDetail;
+import com.beat.domain.booking.application.dto.TicketUpdateRequest;
import com.beat.domain.booking.dao.TicketRepository;
import com.beat.domain.booking.domain.Booking;
+import com.beat.domain.booking.domain.BookingStatus;
+import com.beat.domain.booking.exception.BookingErrorCode;
+import com.beat.domain.booking.exception.TicketErrorCode;
import com.beat.domain.member.dao.MemberRepository;
import com.beat.domain.member.domain.Member;
import com.beat.domain.member.exception.MemberErrorCode;
@@ -12,129 +29,145 @@
import com.beat.domain.schedule.dao.ScheduleRepository;
import com.beat.domain.schedule.domain.Schedule;
import com.beat.domain.schedule.domain.ScheduleNumber;
-import com.beat.domain.booking.exception.BookingErrorCode;
-import com.beat.global.common.exception.ForbiddenException;
-import com.beat.global.common.exception.NotFoundException;
import com.beat.domain.user.dao.UserRepository;
import com.beat.domain.user.domain.Users;
import com.beat.domain.user.exception.UserErrorCode;
-import lombok.RequiredArgsConstructor;
-import net.nurigo.java_sdk.exceptions.CoolsmsException;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
+import com.beat.global.common.exception.BadRequestException;
+import com.beat.global.common.exception.ForbiddenException;
+import com.beat.global.common.exception.NotFoundException;
-import java.util.List;
-import java.util.stream.Collectors;
+import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class TicketService {
- private final TicketRepository ticketRepository;
- private final PerformanceRepository performanceRepository;
- private final MemberRepository memberRepository;
- private final UserRepository userRepository;
- private final ScheduleRepository scheduleRepository;
- private final CoolSmsService coolSmsService;
-
- public TicketRetrieveResponse getTickets(Long memberId, Long performanceId, ScheduleNumber scheduleNumber, Boolean isPaymentCompleted) {
- Member member = memberRepository.findById(memberId).orElseThrow(
- () -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND));
-
- Users user = userRepository.findById(member.getUser().getId()).orElseThrow(
- () -> new NotFoundException(UserErrorCode.USER_NOT_FOUND));
-
- Performance performance = performanceRepository.findById(performanceId)
- .orElseThrow(() -> new NotFoundException(PerformanceErrorCode.PERFORMANCE_NOT_FOUND));
-
- List bookings;
-
- if (scheduleNumber != null && isPaymentCompleted != null) {
- bookings = ticketRepository.findBySchedulePerformanceIdAndScheduleScheduleNumberAndIsPaymentCompleted(performanceId, scheduleNumber, isPaymentCompleted);
- } else if (scheduleNumber != null) {
- bookings = ticketRepository.findBySchedulePerformanceIdAndScheduleScheduleNumber(performanceId, scheduleNumber);
- } else if (isPaymentCompleted != null) {
- bookings = ticketRepository.findBySchedulePerformanceIdAndIsPaymentCompleted(performanceId, isPaymentCompleted);
- } else {
- bookings = ticketRepository.findBySchedulePerformanceId(performanceId);
- }
-
- List bookingList = bookings.stream()
- .map(booking -> TicketDetail.of(
- booking.getId(),
- booking.getBookerName(),
- booking.getBookerPhoneNumber(),
- booking.getSchedule().getId(),
- booking.getPurchaseTicketCount(),
- booking.getCreatedAt(),
- booking.isPaymentCompleted(),
- booking.getSchedule().getScheduleNumber().name()
- ))
- .collect(Collectors.toList());
-
- return TicketRetrieveResponse.of(
- performance.getPerformanceTitle(),
- performance.getTotalScheduleCount(),
- bookingList
- );
- }
-
- @Transactional
- public void updateTickets(Long memberId, TicketUpdateRequest request) {
- Member member = memberRepository.findById(memberId).orElseThrow(
- () -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND));
-
- Users user = userRepository.findById(member.getUser().getId()).orElseThrow(
- () -> new NotFoundException(UserErrorCode.USER_NOT_FOUND));
-
- Performance performance = performanceRepository.findById(request.performanceId())
- .orElseThrow(() -> new NotFoundException(BookingErrorCode.NO_PERFORMANCE_FOUND));
-
- for (TicketUpdateDetail detail : request.bookingList()) {
- Booking booking = ticketRepository.findById(detail.bookingId())
- .orElseThrow(() -> new NotFoundException(BookingErrorCode.NO_BOOKING_FOUND));
-
- boolean wasPaymentCompleted = booking.isPaymentCompleted();
- booking.setIsPaymentCompleted(detail.isPaymentCompleted());
- ticketRepository.save(booking);
-
- if (!wasPaymentCompleted && detail.isPaymentCompleted()) {
- String message = String.format("%s님, BEAT에서의 %s의 예매가 확정되었습니다.", detail.bookerName(), request.performanceTitle());
- try {
- coolSmsService.sendSms(detail.bookerPhoneNumber(), message);
- } catch (CoolsmsException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
- @Transactional
- public void deleteTickets(Long memberId, TicketDeleteRequest ticketDeleteRequest) {
- Member member = memberRepository.findById(memberId)
- .orElseThrow(() -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND));
-
- Long userId = member.getUser().getId();
-
- Performance performance = performanceRepository.findById(ticketDeleteRequest.performanceId())
- .orElseThrow(() -> new NotFoundException(BookingErrorCode.NO_PERFORMANCE_FOUND));
-
- if (!performance.getUsers().getId().equals(userId)) {
- throw new ForbiddenException(PerformanceErrorCode.NOT_PERFORMANCE_OWNER);
- }
-
- for (Long bookingId : ticketDeleteRequest.bookingList()) {
- Booking booking = ticketRepository.findById(bookingId)
- .orElseThrow(() -> new NotFoundException(BookingErrorCode.NO_BOOKING_FOUND));
-
- ticketRepository.delete(booking);
-
- Schedule schedule = booking.getSchedule();
-
- ticketRepository.delete(booking);
-
- schedule.decreaseSoldTicketCount(booking.getPurchaseTicketCount());
- scheduleRepository.save(schedule);
- }
- }
+ private final TicketRepository ticketRepository;
+ private final PerformanceRepository performanceRepository;
+ private final MemberRepository memberRepository;
+ private final UserRepository userRepository;
+ private final ScheduleRepository scheduleRepository;
+ private final CoolSmsService coolSmsService;
+
+ public TicketRetrieveResponse getTickets(Long memberId, Long performanceId, ScheduleNumber scheduleNumber,
+ BookingStatus bookingStatus) {
+ Member member = memberRepository.findById(memberId).orElseThrow(
+ () -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND));
+
+ Users user = userRepository.findById(member.getUser().getId()).orElseThrow(
+ () -> new NotFoundException(UserErrorCode.USER_NOT_FOUND));
+
+ Performance performance = performanceRepository.findById(performanceId)
+ .orElseThrow(() -> new NotFoundException(PerformanceErrorCode.PERFORMANCE_NOT_FOUND));
+
+ List bookings = findBookings(performanceId, scheduleNumber, bookingStatus);
+
+ List bookingList = bookings.stream()
+ .map(booking -> TicketDetail.of(
+ booking.getId(),
+ booking.getBookerName(),
+ booking.getBookerPhoneNumber(),
+ booking.getSchedule().getId(),
+ booking.getPurchaseTicketCount(),
+ booking.getCreatedAt(),
+ booking.getBookingStatus(),
+ booking.getSchedule().getScheduleNumber().name()
+ ))
+ .collect(Collectors.toList());
+
+ return TicketRetrieveResponse.of(
+ performance.getPerformanceTitle(),
+ performance.getTotalScheduleCount(),
+ bookingList
+ );
+ }
+
+ private List findBookings(Long performanceId, ScheduleNumber scheduleNumber, BookingStatus bookingStatus) {
+ if (scheduleNumber != null && bookingStatus != null) {
+ return ticketRepository.findBySchedulePerformanceIdAndScheduleScheduleNumberAndBookingStatus(performanceId,
+ scheduleNumber, bookingStatus);
+ } else if (scheduleNumber != null) {
+ return ticketRepository.findBySchedulePerformanceIdAndScheduleScheduleNumber(performanceId, scheduleNumber);
+ } else if (bookingStatus != null) {
+ return ticketRepository.findBySchedulePerformanceIdAndBookingStatus(performanceId, bookingStatus);
+ } else {
+ return ticketRepository.findBySchedulePerformanceId(performanceId);
+ }
+ }
+
+ @Transactional
+ public void updateTickets(Long memberId, TicketUpdateRequest request) {
+ Member member = memberRepository.findById(memberId).orElseThrow(
+ () -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND));
+
+ Users user = userRepository.findById(member.getUser().getId()).orElseThrow(
+ () -> new NotFoundException(UserErrorCode.USER_NOT_FOUND));
+
+ Performance performance = performanceRepository.findById(request.performanceId())
+ .orElseThrow(() -> new NotFoundException(BookingErrorCode.NO_PERFORMANCE_FOUND));
+
+ for (TicketUpdateDetail detail : request.bookingList()) {
+ Booking booking = ticketRepository.findById(detail.bookingId())
+ .orElseThrow(() -> new NotFoundException(BookingErrorCode.NO_BOOKING_FOUND));
+
+ if (booking.getBookingStatus() == BookingStatus.BOOKING_CONFIRMED
+ && detail.bookingStatus() != BookingStatus.BOOKING_CONFIRMED) {
+ throw new BadRequestException(TicketErrorCode.PAYMENT_COMPLETED_TICKET_UPDATE_NOT_ALLOWED);
+ }
+
+ if (booking.getBookingStatus() == BookingStatus.CHECKING_PAYMENT
+ && detail.bookingStatus() == BookingStatus.BOOKING_CONFIRMED) {
+ booking.setBookingStatus(BookingStatus.BOOKING_CONFIRMED);
+ ticketRepository.save(booking);
+
+ String message = String.format("[BEAT] %s님 %s 예매 확정되었습니다.", detail.bookerName(),
+ request.performanceTitle());
+ try {
+ coolSmsService.sendSms(detail.bookerPhoneNumber(), message);
+ } catch (CoolsmsException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ @Transactional
+ public void cancelTickets(Long memberId, TicketCancelRequest ticketCancelRequest) {
+ Member member = memberRepository.findById(memberId)
+ .orElseThrow(() -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND));
+
+ Long userId = member.getUser().getId();
+
+ Performance performance = performanceRepository.findById(ticketCancelRequest.performanceId())
+ .orElseThrow(() -> new NotFoundException(BookingErrorCode.NO_PERFORMANCE_FOUND));
+
+ if (!performance.getUsers().getId().equals(userId)) {
+ throw new ForbiddenException(PerformanceErrorCode.NOT_PERFORMANCE_OWNER);
+ }
+
+ for (Long bookingId : ticketCancelRequest.bookingList()) {
+ Booking booking = ticketRepository.findById(bookingId)
+ .orElseThrow(() -> new NotFoundException(BookingErrorCode.NO_BOOKING_FOUND));
+
+ booking.setBookingStatus(BookingStatus.BOOKING_CANCELLED);
+ ticketRepository.save(booking);
+
+ Schedule schedule = booking.getSchedule();
+ schedule.decreaseSoldTicketCount(booking.getPurchaseTicketCount());
+
+ if (!schedule.isBooking()) {
+ schedule.updateIsBooking(true);
+ scheduleRepository.save(schedule);
+ }
+ }
+ }
+
+ @Scheduled(cron = "0 0 4 * * ?")
+ @Transactional
+ public void deleteOldCancelledBookings() {
+ LocalDateTime oneYearAgo = LocalDateTime.now().minusYears(1);
+ List oldCancelledBookings = ticketRepository.findByBookingStatusAndCancellationDateBefore(
+ BookingStatus.BOOKING_CANCELLED, oneYearAgo);
+ ticketRepository.deleteAll(oldCancelledBookings);
+ }
}
diff --git a/src/main/java/com/beat/domain/booking/application/dto/GuestBookingRequest.java b/src/main/java/com/beat/domain/booking/application/dto/GuestBookingRequest.java
index 3ea0647d..891ccab3 100644
--- a/src/main/java/com/beat/domain/booking/application/dto/GuestBookingRequest.java
+++ b/src/main/java/com/beat/domain/booking/application/dto/GuestBookingRequest.java
@@ -1,5 +1,6 @@
package com.beat.domain.booking.application.dto;
+import com.beat.domain.booking.domain.BookingStatus;
import com.beat.domain.schedule.domain.ScheduleNumber;
public record GuestBookingRequest(
@@ -11,9 +12,9 @@ public record GuestBookingRequest(
String birthDate,
String password,
int totalPaymentAmount,
- boolean isPaymentCompleted
+ BookingStatus bookingStatus
) {
- public static GuestBookingRequest of(Long scheduleId, int purchaseTicketCount, ScheduleNumber scheduleNumber, String bookerName, String bookerPhoneNumber, String birthDate, String password, int totalPaymentAmount, boolean isPaymentCompleted) {
- return new GuestBookingRequest(scheduleId, purchaseTicketCount, scheduleNumber, bookerName, bookerPhoneNumber, birthDate, password, totalPaymentAmount, isPaymentCompleted);
+ public static GuestBookingRequest of(Long scheduleId, int purchaseTicketCount, ScheduleNumber scheduleNumber, String bookerName, String bookerPhoneNumber, String birthDate, String password, int totalPaymentAmount, BookingStatus bookingStatus) {
+ return new GuestBookingRequest(scheduleId, purchaseTicketCount, scheduleNumber, bookerName, bookerPhoneNumber, birthDate, password, totalPaymentAmount, bookingStatus);
}
}
\ No newline at end of file
diff --git a/src/main/java/com/beat/domain/booking/application/dto/GuestBookingResponse.java b/src/main/java/com/beat/domain/booking/application/dto/GuestBookingResponse.java
index ecf16df1..c9694cd5 100644
--- a/src/main/java/com/beat/domain/booking/application/dto/GuestBookingResponse.java
+++ b/src/main/java/com/beat/domain/booking/application/dto/GuestBookingResponse.java
@@ -1,5 +1,6 @@
package com.beat.domain.booking.application.dto;
+import com.beat.domain.booking.domain.BookingStatus;
import com.beat.domain.performance.domain.BankName;
import com.beat.domain.schedule.domain.ScheduleNumber;
@@ -13,7 +14,7 @@ public record GuestBookingResponse(
ScheduleNumber scheduleNumber,
String bookerName,
String bookerPhoneNumber,
- boolean isPaymentCompleted,
+ BookingStatus bookingStatus,
BankName bankName,
String accountNumber,
int totalPaymentAmount,
@@ -27,7 +28,7 @@ public static GuestBookingResponse of(
ScheduleNumber scheduleNumber,
String bookerName,
String bookerPhoneNumber,
- boolean isPaymentCompleted,
+ BookingStatus bookingStatus,
BankName bankName,
String accountNumber,
int totalPaymentAmount,
@@ -40,7 +41,7 @@ public static GuestBookingResponse of(
scheduleNumber,
bookerName,
bookerPhoneNumber,
- isPaymentCompleted,
+ bookingStatus,
bankName,
accountNumber,
totalPaymentAmount,
diff --git a/src/main/java/com/beat/domain/booking/application/dto/GuestBookingRetrieveResponse.java b/src/main/java/com/beat/domain/booking/application/dto/GuestBookingRetrieveResponse.java
index 9a490638..65903380 100644
--- a/src/main/java/com/beat/domain/booking/application/dto/GuestBookingRetrieveResponse.java
+++ b/src/main/java/com/beat/domain/booking/application/dto/GuestBookingRetrieveResponse.java
@@ -1,5 +1,6 @@
package com.beat.domain.booking.application.dto;
+import com.beat.domain.booking.domain.BookingStatus;
import com.beat.domain.performance.domain.BankName;
import com.beat.domain.schedule.domain.ScheduleNumber;
@@ -20,7 +21,7 @@ public record GuestBookingRetrieveResponse(
String accountNumber,
String accountHolder,
int dueDate,
- boolean isPaymentCompleted,
+ BookingStatus bookingStatus,
LocalDateTime createdAt,
String posterImage,
int totalPaymentAmount
@@ -40,7 +41,7 @@ public static GuestBookingRetrieveResponse of(
String accountNumber,
String accountHolder,
int dueDate,
- boolean isPaymentCompleted,
+ BookingStatus bookingStatus,
LocalDateTime createdAt,
String posterImage,
int totalPaymentAmount
@@ -60,7 +61,7 @@ public static GuestBookingRetrieveResponse of(
accountNumber,
accountHolder,
dueDate,
- isPaymentCompleted,
+ bookingStatus,
createdAt,
posterImage,
totalPaymentAmount
diff --git a/src/main/java/com/beat/domain/booking/application/dto/MemberBookingRequest.java b/src/main/java/com/beat/domain/booking/application/dto/MemberBookingRequest.java
index d0a84a7d..608acfdd 100644
--- a/src/main/java/com/beat/domain/booking/application/dto/MemberBookingRequest.java
+++ b/src/main/java/com/beat/domain/booking/application/dto/MemberBookingRequest.java
@@ -1,5 +1,6 @@
package com.beat.domain.booking.application.dto;
+import com.beat.domain.booking.domain.BookingStatus;
import com.beat.domain.schedule.domain.ScheduleNumber;
public record MemberBookingRequest(
@@ -8,6 +9,6 @@ public record MemberBookingRequest(
int purchaseTicketCount,
String bookerName,
String bookerPhoneNumber,
- boolean isPaymentCompleted,
+ BookingStatus bookingStatus,
int totalPaymentAmount
) { }
\ No newline at end of file
diff --git a/src/main/java/com/beat/domain/booking/application/dto/MemberBookingResponse.java b/src/main/java/com/beat/domain/booking/application/dto/MemberBookingResponse.java
index dce4f10d..9e3342fe 100644
--- a/src/main/java/com/beat/domain/booking/application/dto/MemberBookingResponse.java
+++ b/src/main/java/com/beat/domain/booking/application/dto/MemberBookingResponse.java
@@ -1,5 +1,6 @@
package com.beat.domain.booking.application.dto;
+import com.beat.domain.booking.domain.BookingStatus;
import com.beat.domain.performance.domain.BankName;
import com.beat.domain.schedule.domain.ScheduleNumber;
@@ -13,7 +14,7 @@ public record MemberBookingResponse(
ScheduleNumber scheduleNumber,
String bookerName,
String bookerPhoneNumber,
- boolean isPaymentCompleted,
+ BookingStatus bookingStatus,
BankName bankName,
String accountNumber,
int totalPaymentAmount,
@@ -27,7 +28,7 @@ public static MemberBookingResponse of(
ScheduleNumber scheduleNumber,
String bookerName,
String bookerPhoneNumber,
- boolean isPaymentCompleted,
+ BookingStatus bookingStatus,
BankName bankName,
String accountNumber,
int totalPaymentAmount,
@@ -41,7 +42,7 @@ public static MemberBookingResponse of(
scheduleNumber,
bookerName,
bookerPhoneNumber,
- isPaymentCompleted,
+ bookingStatus,
bankName,
accountNumber,
totalPaymentAmount,
diff --git a/src/main/java/com/beat/domain/booking/application/dto/MemberBookingRetrieveResponse.java b/src/main/java/com/beat/domain/booking/application/dto/MemberBookingRetrieveResponse.java
index 134767bc..cfc82200 100644
--- a/src/main/java/com/beat/domain/booking/application/dto/MemberBookingRetrieveResponse.java
+++ b/src/main/java/com/beat/domain/booking/application/dto/MemberBookingRetrieveResponse.java
@@ -1,5 +1,6 @@
package com.beat.domain.booking.application.dto;
+import com.beat.domain.booking.domain.BookingStatus;
import com.beat.domain.performance.domain.BankName;
import com.beat.domain.schedule.domain.ScheduleNumber;
@@ -21,7 +22,7 @@ public record MemberBookingRetrieveResponse(
String accountNumber,
String accountHolder,
int dueDate,
- boolean isPaymentCompleted,
+ BookingStatus bookingStatus,
LocalDateTime createdAt,
String posterImage,
int totalPaymentAmount
@@ -42,7 +43,7 @@ public static MemberBookingRetrieveResponse of(
String accountNumber,
String accountHolder,
int dueDate,
- boolean isPaymentCompleted,
+ BookingStatus bookingStatus,
LocalDateTime createdAt,
String posterImage,
int totalPaymentAmount
@@ -63,7 +64,7 @@ public static MemberBookingRetrieveResponse of(
accountNumber,
accountHolder,
dueDate,
- isPaymentCompleted,
+ bookingStatus,
createdAt,
posterImage,
totalPaymentAmount
diff --git a/src/main/java/com/beat/domain/booking/application/dto/TicketDeleteRequest.java b/src/main/java/com/beat/domain/booking/application/dto/TicketCancelRequest.java
similarity index 51%
rename from src/main/java/com/beat/domain/booking/application/dto/TicketDeleteRequest.java
rename to src/main/java/com/beat/domain/booking/application/dto/TicketCancelRequest.java
index 57c2e756..65341f8c 100644
--- a/src/main/java/com/beat/domain/booking/application/dto/TicketDeleteRequest.java
+++ b/src/main/java/com/beat/domain/booking/application/dto/TicketCancelRequest.java
@@ -2,11 +2,11 @@
import java.util.List;
-public record TicketDeleteRequest(
+public record TicketCancelRequest(
Long performanceId,
List bookingList
) {
- public static TicketDeleteRequest of(Long performanceId, List bookingList) {
- return new TicketDeleteRequest(performanceId, bookingList);
+ public static TicketCancelRequest of(Long performanceId, List bookingList) {
+ return new TicketCancelRequest(performanceId, bookingList);
}
}
diff --git a/src/main/java/com/beat/domain/booking/application/dto/TicketDetail.java b/src/main/java/com/beat/domain/booking/application/dto/TicketDetail.java
index 013eb32c..f72dee2f 100644
--- a/src/main/java/com/beat/domain/booking/application/dto/TicketDetail.java
+++ b/src/main/java/com/beat/domain/booking/application/dto/TicketDetail.java
@@ -1,5 +1,7 @@
package com.beat.domain.booking.application.dto;
+import com.beat.domain.booking.domain.BookingStatus;
+
import java.time.LocalDateTime;
public record TicketDetail(
@@ -9,7 +11,7 @@ public record TicketDetail(
Long scheduleId,
int purchaseTicketCount,
LocalDateTime createdAt,
- boolean isPaymentCompleted,
+ BookingStatus bookingStatus,
String scheduleNumber
) {
public static TicketDetail of(
@@ -19,8 +21,8 @@ public static TicketDetail of(
Long scheduleId,
int purchaseTicketCount,
LocalDateTime createdAt,
- boolean isPaymentCompleted,
+ BookingStatus bookingStatus,
String scheduleNumber) {
- return new TicketDetail(bookingId, bookerName, bookerPhoneNumber, scheduleId, purchaseTicketCount, createdAt, isPaymentCompleted, scheduleNumber);
+ return new TicketDetail(bookingId, bookerName, bookerPhoneNumber, scheduleId, purchaseTicketCount, createdAt, bookingStatus, scheduleNumber);
}
}
diff --git a/src/main/java/com/beat/domain/booking/application/dto/TicketUpdateDetail.java b/src/main/java/com/beat/domain/booking/application/dto/TicketUpdateDetail.java
index 38e868df..79c2fd29 100644
--- a/src/main/java/com/beat/domain/booking/application/dto/TicketUpdateDetail.java
+++ b/src/main/java/com/beat/domain/booking/application/dto/TicketUpdateDetail.java
@@ -1,5 +1,7 @@
package com.beat.domain.booking.application.dto;
+import com.beat.domain.booking.domain.BookingStatus;
+
import java.time.LocalDateTime;
public record TicketUpdateDetail(
@@ -9,7 +11,7 @@ public record TicketUpdateDetail(
Long scheduleId,
int purchaseTicketCount,
LocalDateTime createdAt,
- boolean isPaymentCompleted,
+ BookingStatus bookingStatus,
String scheduleNumber
) {
public static TicketUpdateDetail of(
@@ -19,8 +21,8 @@ public static TicketUpdateDetail of(
Long scheduleId,
int purchaseTicketCount,
LocalDateTime createdAt,
- boolean isPaymentCompleted,
+ BookingStatus bookingStatus,
String scheduleNumber) {
- return new TicketUpdateDetail(bookingId, bookerName, bookerPhoneNumber, scheduleId, purchaseTicketCount, createdAt, isPaymentCompleted, scheduleNumber);
+ return new TicketUpdateDetail(bookingId, bookerName, bookerPhoneNumber, scheduleId, purchaseTicketCount, createdAt, bookingStatus, scheduleNumber);
}
}
diff --git a/src/main/java/com/beat/domain/booking/dao/BookingRepository.java b/src/main/java/com/beat/domain/booking/dao/BookingRepository.java
index 111be517..cd12d617 100644
--- a/src/main/java/com/beat/domain/booking/dao/BookingRepository.java
+++ b/src/main/java/com/beat/domain/booking/dao/BookingRepository.java
@@ -1,37 +1,38 @@
package com.beat.domain.booking.dao;
-import com.beat.domain.booking.domain.Booking;
+import java.util.List;
+import java.util.Optional;
+
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
-import java.util.List;
-import java.util.Optional;
+import com.beat.domain.booking.domain.Booking;
public interface BookingRepository extends JpaRepository {
- @Query("SELECT b FROM Booking b " +
- "JOIN b.schedule s " +
- "JOIN s.performance p " +
- "WHERE b.bookerName = :bookerName " +
- "AND b.bookerPhoneNumber = :bookerPhoneNumber " +
- "AND b.password = :password " +
- "AND b.birthDate = :birthDate")
- Optional> findByBookerNameAndBookerPhoneNumberAndPasswordAndBirthDate(
- @Param("bookerName") String bookerName,
- @Param("bookerPhoneNumber") String bookerPhoneNumber,
- @Param("password") String password,
- @Param("birthDate") String birthDate
- );
+ @Query("SELECT b FROM Booking b " +
+ "JOIN b.schedule s " +
+ "JOIN s.performance p " +
+ "WHERE b.bookerName = :bookerName " +
+ "AND b.bookerPhoneNumber = :bookerPhoneNumber " +
+ "AND b.password = :password " +
+ "AND b.birthDate = :birthDate")
+ Optional> findByBookerNameAndBookerPhoneNumberAndPasswordAndBirthDate(
+ @Param("bookerName") String bookerName,
+ @Param("bookerPhoneNumber") String bookerPhoneNumber,
+ @Param("password") String password,
+ @Param("birthDate") String birthDate
+ );
- Optional findFirstByBookerNameAndBookerPhoneNumberAndBirthDateAndPassword(
- String bookerName,
- String bookerPhoneNumber,
- String birthDate,
- String password
- );
+ Optional findFirstByBookerNameAndBookerPhoneNumberAndBirthDateAndPassword(
+ String bookerName,
+ String bookerPhoneNumber,
+ String birthDate,
+ String password
+ );
- List findByUsersId(Long userId);
+ List findByUsersId(Long userId);
- @Query("SELECT COUNT(b) > 0 FROM Booking b WHERE b.schedule.id IN :scheduleIds")
- boolean existsByScheduleIdIn(@Param("scheduleIds") List scheduleIds);
+ @Query("SELECT COUNT(b) > 0 FROM Booking b WHERE b.schedule.id IN :scheduleIds AND b.bookingStatus != 'BOOKING_CANCELLED'")
+ boolean existsByScheduleIdIn(@Param("scheduleIds") List scheduleIds);
}
\ No newline at end of file
diff --git a/src/main/java/com/beat/domain/booking/dao/TicketRepository.java b/src/main/java/com/beat/domain/booking/dao/TicketRepository.java
index aad02e7e..870884e1 100644
--- a/src/main/java/com/beat/domain/booking/dao/TicketRepository.java
+++ b/src/main/java/com/beat/domain/booking/dao/TicketRepository.java
@@ -1,9 +1,11 @@
package com.beat.domain.booking.dao;
import com.beat.domain.booking.domain.Booking;
+import com.beat.domain.booking.domain.BookingStatus;
import com.beat.domain.schedule.domain.ScheduleNumber;
import org.springframework.data.jpa.repository.JpaRepository;
+import java.time.LocalDateTime;
import java.util.List;
public interface TicketRepository extends JpaRepository {
@@ -11,7 +13,9 @@ public interface TicketRepository extends JpaRepository {
List findBySchedulePerformanceIdAndScheduleScheduleNumber(Long performanceId, ScheduleNumber scheduleNumber);
- List findBySchedulePerformanceIdAndIsPaymentCompleted(Long performanceId, boolean isPaymentCompleted);
+ List findBySchedulePerformanceIdAndBookingStatus(Long performanceId, BookingStatus bookingStatus);
- List findBySchedulePerformanceIdAndScheduleScheduleNumberAndIsPaymentCompleted(Long performanceId, ScheduleNumber scheduleNumber, boolean isPaymentCompleted);
+ List findBySchedulePerformanceIdAndScheduleScheduleNumberAndBookingStatus(Long performanceId, ScheduleNumber scheduleNumber, BookingStatus bookingStatus);
+
+ List findByBookingStatusAndCancellationDateBefore(BookingStatus bookingStatus, LocalDateTime cancellationDate);
}
diff --git a/src/main/java/com/beat/domain/booking/domain/Booking.java b/src/main/java/com/beat/domain/booking/domain/Booking.java
index 39fba2fe..0b3ebfcf 100644
--- a/src/main/java/com/beat/domain/booking/domain/Booking.java
+++ b/src/main/java/com/beat/domain/booking/domain/Booking.java
@@ -4,6 +4,8 @@
import com.beat.domain.user.domain.Users;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
@@ -38,11 +40,15 @@ public class Booking {
private String bookerPhoneNumber;
@Column(nullable = false)
- private boolean isPaymentCompleted = false;
+ @Enumerated(EnumType.STRING)
+ private BookingStatus bookingStatus = BookingStatus.CHECKING_PAYMENT;
@Column(nullable = false)
private LocalDateTime createdAt = LocalDateTime.now();
+ @Column(nullable = true)
+ private LocalDateTime cancellationDate;
+
@Column(nullable = true)
private String birthDate;
@@ -60,23 +66,23 @@ public class Booking {
private Users users;
@Builder
- public Booking(int purchaseTicketCount, String bookerName, String bookerPhoneNumber, boolean isPaymentCompleted, String birthDate, String password, Schedule schedule, Users users) {
+ public Booking(int purchaseTicketCount, String bookerName, String bookerPhoneNumber, BookingStatus bookingStatus, String birthDate, String password, Schedule schedule, Users users) {
this.purchaseTicketCount = purchaseTicketCount;
this.bookerName = bookerName;
this.bookerPhoneNumber = bookerPhoneNumber;
- this.isPaymentCompleted = isPaymentCompleted;
+ this.bookingStatus = bookingStatus;
this.birthDate = birthDate;
this.password = password;
this.schedule = schedule;
this.users = users;
}
- public static Booking create(int purchaseTicketCount, String bookerName, String bookerPhoneNumber, boolean isPaymentCompleted, String birthDate, String password, Schedule schedule, Users users) {
+ public static Booking create(int purchaseTicketCount, String bookerName, String bookerPhoneNumber, BookingStatus bookingStatus, String birthDate, String password, Schedule schedule, Users users) {
return Booking.builder()
.purchaseTicketCount(purchaseTicketCount)
.bookerName(bookerName)
.bookerPhoneNumber(bookerPhoneNumber)
- .isPaymentCompleted(isPaymentCompleted)
+ .bookingStatus(bookingStatus)
.birthDate(birthDate)
.password(password)
.schedule(schedule)
@@ -84,8 +90,10 @@ public static Booking create(int purchaseTicketCount, String bookerName, String
.build();
}
- public void setIsPaymentCompleted(boolean isPaymentCompleted) {
- this.isPaymentCompleted = isPaymentCompleted;
+ public void setBookingStatus(BookingStatus bookingStatus) {
+ this.bookingStatus = bookingStatus;
+ if (bookingStatus == BookingStatus.BOOKING_CANCELLED) {
+ this.cancellationDate = LocalDateTime.now();
+ }
}
-
}
\ No newline at end of file
diff --git a/src/main/java/com/beat/domain/booking/domain/BookingStatus.java b/src/main/java/com/beat/domain/booking/domain/BookingStatus.java
new file mode 100644
index 00000000..74d5a757
--- /dev/null
+++ b/src/main/java/com/beat/domain/booking/domain/BookingStatus.java
@@ -0,0 +1,14 @@
+package com.beat.domain.booking.domain;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor
+public enum BookingStatus {
+ CHECKING_PAYMENT("입금확인중"),
+ BOOKING_CONFIRMED("예매 확정"),
+ BOOKING_CANCELLED("예매 취소");
+
+ private final String displayname;
+}
diff --git a/src/main/java/com/beat/domain/booking/exception/BookingSuccessCode.java b/src/main/java/com/beat/domain/booking/exception/BookingSuccessCode.java
index e77708ff..a24a08ce 100644
--- a/src/main/java/com/beat/domain/booking/exception/BookingSuccessCode.java
+++ b/src/main/java/com/beat/domain/booking/exception/BookingSuccessCode.java
@@ -13,7 +13,7 @@ public enum BookingSuccessCode implements BaseSuccessCode {
GUEST_BOOKING_RETRIEVE_SUCCESS(200, "비회원 예매 조회가 성공적으로 완료되었습니다."),
TICKET_RETRIEVE_SUCCESS(200, "예매자 목록 조회가 성공적으로 완료되었습니다."),
TICKET_UPDATE_SUCCESS(200, "예매자 입금여부 수정이 성공적으로 완료되었습니다."),
- TICKET_DELETE_SUCCESS(200, "예매자 삭제 요청이 성공했습니다.")
+ TICKET_CANCEL_SUCCESS(200, "예매취소 요청이 성공했습니다.")
;
private final int status;
diff --git a/src/main/java/com/beat/domain/booking/exception/TicketErrorCode.java b/src/main/java/com/beat/domain/booking/exception/TicketErrorCode.java
new file mode 100644
index 00000000..b3376117
--- /dev/null
+++ b/src/main/java/com/beat/domain/booking/exception/TicketErrorCode.java
@@ -0,0 +1,14 @@
+package com.beat.domain.booking.exception;
+
+import com.beat.global.common.exception.base.BaseErrorCode;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor
+public enum TicketErrorCode implements BaseErrorCode {
+ PAYMENT_COMPLETED_TICKET_UPDATE_NOT_ALLOWED(400, "이미 결제가 완료된 티켓의 상태는 변경할 수 없습니다.");
+
+ private final int status;
+ private final String message;
+}
diff --git a/src/main/java/com/beat/domain/cast/dao/CastRepository.java b/src/main/java/com/beat/domain/cast/dao/CastRepository.java
index 37eb520b..98e6214b 100644
--- a/src/main/java/com/beat/domain/cast/dao/CastRepository.java
+++ b/src/main/java/com/beat/domain/cast/dao/CastRepository.java
@@ -2,6 +2,7 @@
import com.beat.domain.cast.domain.Cast;
import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
import java.util.List;
@@ -9,4 +10,7 @@ public interface CastRepository extends JpaRepository {
List findByPerformanceId(Long performanceId);
List findAllByPerformanceId(Long performanceId);
+
+ @Query("SELECT c.id FROM Cast c WHERE c.performance.id = :performanceId")
+ List findIdsByPerformanceId(Long performanceId);
}
diff --git a/src/main/java/com/beat/domain/cast/exception/CastErrorCode.java b/src/main/java/com/beat/domain/cast/exception/CastErrorCode.java
index 9658d8ab..15189c27 100644
--- a/src/main/java/com/beat/domain/cast/exception/CastErrorCode.java
+++ b/src/main/java/com/beat/domain/cast/exception/CastErrorCode.java
@@ -7,6 +7,7 @@
@Getter
@RequiredArgsConstructor
public enum CastErrorCode implements BaseErrorCode {
+ CAST_NOT_BELONG_TO_PERFORMANCE(403,"해당 등장인물은 해당 공연에 속해 있지 않습니다."),
CAST_NOT_FOUND(404, "등장인물이 존재하지 않습니다.")
;
diff --git a/src/main/java/com/beat/domain/member/api/MemberController.java b/src/main/java/com/beat/domain/member/api/MemberController.java
index 08c06b30..f246a74d 100644
--- a/src/main/java/com/beat/domain/member/api/MemberController.java
+++ b/src/main/java/com/beat/domain/member/api/MemberController.java
@@ -1,6 +1,8 @@
package com.beat.domain.member.api;
+import com.beat.domain.member.application.AuthenticationService;
import com.beat.domain.member.application.MemberService;
+import com.beat.domain.member.application.SocialLoginService;
import com.beat.domain.member.dto.*;
import com.beat.domain.member.exception.MemberSuccessCode;
import com.beat.global.auth.client.dto.MemberLoginRequest;
@@ -20,8 +22,10 @@
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class MemberController {
- private final MemberService memberService;
private final TokenService tokenService;
+ private final AuthenticationService authenticationService;
+ private final SocialLoginService socialLoginService;
+
private final static int COOKIE_MAX_AGE = 7 * 24 * 60 * 60;
private final static String REFRESH_TOKEN = "refreshToken";
@@ -32,7 +36,7 @@ public ResponseEntity> signUp(
@RequestBody final MemberLoginRequest loginRequest,
HttpServletResponse response
) {
- LoginSuccessResponse loginSuccessResponse = memberService.create(authorizationCode, loginRequest);
+ LoginSuccessResponse loginSuccessResponse = socialLoginService.handleSocialLogin(authorizationCode, loginRequest);
ResponseCookie cookie = ResponseCookie.from(REFRESH_TOKEN, loginSuccessResponse.refreshToken())
.maxAge(COOKIE_MAX_AGE)
.path("/")
@@ -42,7 +46,7 @@ public ResponseEntity> signUp(
.build();
response.setHeader("Set-Cookie", cookie.toString());
return ResponseEntity.status(HttpStatus.OK)
- .body(SuccessResponse.of(MemberSuccessCode.SIGN_UP_SUCCESS, LoginSuccessResponse.of(loginSuccessResponse.accessToken(), null, loginSuccessResponse.nickname())));
+ .body(SuccessResponse.of(MemberSuccessCode.SIGN_UP_SUCCESS, LoginSuccessResponse.of(loginSuccessResponse.accessToken(), null, loginSuccessResponse.nickname(), loginSuccessResponse.role())));
}
@Operation(summary = "access token 재발급 API", description = "refresh token으로 access token을 재발급하는 GET API입니다.")
@@ -50,7 +54,7 @@ public ResponseEntity> signUp(
public ResponseEntity> refreshToken(
@RequestParam final String refreshToken
) {
- AccessTokenGetSuccess accessTokenGetSuccess = memberService.refreshToken(refreshToken);
+ AccessTokenGetSuccess accessTokenGetSuccess = authenticationService.generateAccessTokenFromRefreshToken(refreshToken);
return ResponseEntity.status(HttpStatus.OK)
.body(SuccessResponse.of(MemberSuccessCode.ISSUE_REFRESH_TOKEN_SUCCESS, accessTokenGetSuccess));
}
diff --git a/src/main/java/com/beat/domain/member/application/AuthenticationService.java b/src/main/java/com/beat/domain/member/application/AuthenticationService.java
new file mode 100644
index 00000000..49e844c7
--- /dev/null
+++ b/src/main/java/com/beat/domain/member/application/AuthenticationService.java
@@ -0,0 +1,134 @@
+package com.beat.domain.member.application;
+
+import com.beat.domain.member.dto.AccessTokenGetSuccess;
+import com.beat.domain.member.dto.LoginSuccessResponse;
+import com.beat.domain.user.domain.Role;
+import com.beat.domain.user.domain.Users;
+import com.beat.global.auth.client.dto.MemberInfoResponse;
+import com.beat.global.auth.jwt.application.TokenService;
+import com.beat.global.auth.jwt.exception.TokenErrorCode;
+import com.beat.global.auth.jwt.provider.JwtTokenProvider;
+import com.beat.global.auth.jwt.provider.JwtValidationType;
+import com.beat.global.auth.security.AdminAuthentication;
+import com.beat.global.auth.security.MemberAuthentication;
+import com.beat.global.common.exception.BadRequestException;
+import com.beat.global.common.exception.BeatException;
+import com.beat.global.common.exception.UnauthorizedException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Collection;
+import java.util.List;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class AuthenticationService {
+
+ private final JwtTokenProvider jwtTokenProvider;
+ private final TokenService tokenService;
+
+ /**
+ * 사용자의 로그인 성공 시 Access Token과 Refresh Token을 생성하고,
+ * 로그인 성공 응답 객체(LoginSuccessResponse)를 반환하는 메서드.
+ *
+ * @param memberId 회원의 고유 ID
+ * @param user 로그인한 사용자의 정보가 포함된 Users 객체
+ * @param memberInfoResponse 로그인 시 외부로부터 전달된 회원 정보
+ * @return 로그인 성공 응답(LoginSuccessResponse)
+ */
+ public LoginSuccessResponse generateLoginSuccessResponse(final Long memberId, final Users user, final MemberInfoResponse memberInfoResponse) {
+ String nickname = memberInfoResponse.nickname();
+ Role role = user.getRole();
+ Collection authorities = List.of(role.toGrantedAuthority());
+
+ log.info("Starting login success response generation for memberId: {}, nickname: {}, role: {}", memberId, nickname, role.getRoleName());
+
+ UsernamePasswordAuthenticationToken authenticationToken = createAuthenticationToken(memberId, role, authorities);
+ String refreshToken = issueAndSaveRefreshToken(memberId, authenticationToken);
+ String accessToken = jwtTokenProvider.issueAccessToken(authenticationToken);
+
+ log.info("Login success for authorities: {}, accessToken: {}, refreshToken: {}", authorities, accessToken, refreshToken);
+
+ return LoginSuccessResponse.of(accessToken, refreshToken, nickname, role.getRoleName());
+ }
+
+ /**
+ * Refresh Token을 사용하여 새로운 Access Token을 생성하는 메서드.
+ *
+ * Refresh Token에서 사용자 ID와 Role 정보를 추출한 후,
+ * Role에 따라 Admin 또는 Member 권한으로 새로운 Access Token을 발급합니다.
+ *
+ * @param refreshToken 사용자의 Refresh Token
+ * @return 새로운 Access Token 정보가 포함된 AccessTokenGetSuccess 객체
+ */
+ @Transactional
+ public AccessTokenGetSuccess generateAccessTokenFromRefreshToken(final String refreshToken) {
+ log.info("Validation result for refresh token: {}", jwtTokenProvider.validateToken(refreshToken));
+
+ JwtValidationType validationType = jwtTokenProvider.validateToken(refreshToken);
+ if (!validationType.equals(JwtValidationType.VALID_JWT)) {
+ log.warn("Invalid refresh token: {}", validationType);
+ throw switch (validationType) {
+ case EXPIRED_JWT_TOKEN -> new UnauthorizedException(TokenErrorCode.REFRESH_TOKEN_EXPIRED_ERROR);
+ case INVALID_JWT_TOKEN -> new BadRequestException(TokenErrorCode.INVALID_REFRESH_TOKEN_ERROR);
+ case INVALID_JWT_SIGNATURE -> new BadRequestException(TokenErrorCode.REFRESH_TOKEN_SIGNATURE_ERROR);
+ case UNSUPPORTED_JWT_TOKEN -> new BadRequestException(TokenErrorCode.UNSUPPORTED_REFRESH_TOKEN_ERROR);
+ case EMPTY_JWT -> new BadRequestException(TokenErrorCode.REFRESH_TOKEN_EMPTY_ERROR);
+ default -> new BeatException(TokenErrorCode.UNKNOWN_REFRESH_TOKEN_ERROR);
+ };
+ }
+
+ Long memberId = jwtTokenProvider.getMemberIdFromJwt(refreshToken);
+
+ if (!memberId.equals(tokenService.findIdByRefreshToken(refreshToken))) {
+ log.error("MemberId mismatch: token does not match the stored refresh token");
+ throw new BadRequestException(TokenErrorCode.REFRESH_TOKEN_MEMBER_ID_MISMATCH_ERROR);
+ }
+
+ Role role = jwtTokenProvider.getRoleFromJwt(refreshToken);
+ Collection authorities = List.of(role.toGrantedAuthority());
+
+ UsernamePasswordAuthenticationToken authenticationToken = createAuthenticationToken(memberId, role, authorities);
+ log.info("Generated new access token for memberId: {}, role: {}, authorities: {}",
+ memberId, role.getRoleName(), authorities);
+ return AccessTokenGetSuccess.of(jwtTokenProvider.issueAccessToken(authenticationToken));
+ }
+
+ /**
+ * Refresh Token을 발급하고 저장하는 메서드.
+ * 발급된 Refresh Token을 TokenService에 저장
+ *
+ * @param memberId 회원의 고유 ID
+ * @param authenticationToken 사용자 인증 정보
+ * @return 발급된 Refresh Token
+ */
+ private String issueAndSaveRefreshToken(Long memberId, UsernamePasswordAuthenticationToken authenticationToken) {
+ String refreshToken = jwtTokenProvider.issueRefreshToken(authenticationToken);
+ log.info("Issued new refresh token for memberId: {}", memberId);
+ tokenService.saveRefreshToken(memberId, refreshToken);
+ return refreshToken;
+ }
+
+ /**
+ * 사용자 Role에 따라 적절한 Authentication 객체(Admin 또는 Member)를 생성하는 메서드.
+ *
+ * @param memberId 회원의 고유 ID
+ * @param role 사용자 Role (ADMIN 또는 MEMBER)
+ * @param authorities 사용자에게 부여된 권한 목록
+ * @return 생성된 Admin 또는 Member Authentication 객체
+ */
+ private UsernamePasswordAuthenticationToken createAuthenticationToken(Long memberId, Role role, Collection authorities) {
+ if (role == Role.ADMIN) {
+ log.info("Creating AdminAuthentication for memberId: {}", memberId);
+ return new AdminAuthentication(memberId, null, authorities);
+ } else {
+ log.info("Creating MemberAuthentication for memberId: {}", memberId);
+ return new MemberAuthentication(memberId, null, authorities);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/beat/domain/member/application/MemberRegistrationService.java b/src/main/java/com/beat/domain/member/application/MemberRegistrationService.java
new file mode 100644
index 00000000..5f19fc65
--- /dev/null
+++ b/src/main/java/com/beat/domain/member/application/MemberRegistrationService.java
@@ -0,0 +1,49 @@
+package com.beat.domain.member.application;
+
+import com.beat.domain.member.dao.MemberRepository;
+import com.beat.domain.member.domain.Member;
+import com.beat.domain.user.dao.UserRepository;
+import com.beat.domain.user.domain.Role;
+import com.beat.domain.user.domain.Users;
+import com.beat.global.auth.client.dto.MemberInfoResponse;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class MemberRegistrationService {
+
+ private final UserRepository userRepository;
+ private final MemberRepository memberRepository;
+
+ @Transactional
+ public Long registerMemberWithUserInfo(final MemberInfoResponse memberInfoResponse) {
+ Users users = Users.createWithRole(Role.MEMBER);
+
+ log.info("Granting MEMBER role to new user with role: {}", users.getRole());
+
+ users = userRepository.save(users);
+ userRepository.flush();
+
+ log.info("Registering new user with role: {}", users.getRole());
+
+ Member member = Member.create(
+ memberInfoResponse.nickname(),
+ memberInfoResponse.email(),
+ users,
+ memberInfoResponse.socialId(),
+ memberInfoResponse.socialType()
+ );
+
+ memberRepository.save(member);
+
+ log.info("Member registered with memberId: {}, role: {}", member.getId(), users.getRole());
+
+ return member.getId();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/beat/domain/member/application/MemberService.java b/src/main/java/com/beat/domain/member/application/MemberService.java
index d0c68a6f..5fdb3a4b 100644
--- a/src/main/java/com/beat/domain/member/application/MemberService.java
+++ b/src/main/java/com/beat/domain/member/application/MemberService.java
@@ -3,20 +3,15 @@
import com.beat.domain.member.dao.MemberRepository;
import com.beat.domain.member.domain.Member;
import com.beat.domain.member.domain.SocialType;
-import com.beat.domain.member.dto.*;
import com.beat.domain.member.exception.MemberErrorCode;
+import com.beat.domain.member.port.in.MemberUseCase;
import com.beat.domain.user.dao.UserRepository;
import com.beat.domain.user.domain.Users;
-import com.beat.global.auth.client.dto.MemberInfoResponse;
-import com.beat.global.auth.client.dto.MemberLoginRequest;
-import com.beat.global.auth.client.service.KakaoSocialService;
-import com.beat.global.auth.jwt.application.TokenService;
-import com.beat.global.auth.jwt.exception.TokenErrorCode;
-import com.beat.global.auth.jwt.provider.JwtTokenProvider;
-import com.beat.global.auth.security.MemberAuthentication;
import com.beat.global.common.exception.*;
+
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -24,114 +19,35 @@
@RequiredArgsConstructor
@Transactional(readOnly = true)
@Service
-public class MemberService {
- private final UserRepository userRepository;
- private final MemberRepository memberRepository;
- private final JwtTokenProvider jwtTokenProvider;
- private final TokenService tokenService;
- private final KakaoSocialService kakaoSocialService;
-
- @Transactional
- public LoginSuccessResponse create(
- final String authorizationCode,
- final MemberLoginRequest loginRequest
- ) {
- return getTokenDto(getUserInfoResponse(authorizationCode, loginRequest));
- }
-
- public MemberInfoResponse getUserInfoResponse(
- final String authorizationCode,
- final MemberLoginRequest loginRequest
- ) {
- switch (loginRequest.socialType()) {
- case KAKAO:
- return kakaoSocialService.login(authorizationCode, loginRequest);
- default:
- throw new BadRequestException(MemberErrorCode.SOCIAL_TYPE_BAD_REQUEST);
- }
- }
-
- @Transactional
- public Long createUser(final MemberInfoResponse userResponse) {
- Users users = Users.create();
- users = userRepository.save(users);
-
- Member member = Member.create(
- userResponse.nickname(),
- userResponse.email(),
- users,
- userResponse.socialId(),
- userResponse.socialType()
- );
-
- memberRepository.save(member);
-
- return member.getId();
- }
-
- public Member getBySocialId(
- final Long socialId,
- final SocialType socialType) {
- Member member = memberRepository.findBySocialTypeAndSocialId(socialId, socialType).orElseThrow(
- () -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND)
- );
- return member;
- }
-
- @Transactional
- public AccessTokenGetSuccess refreshToken(
- final String refreshToken
- ) {
- Long memberId = jwtTokenProvider.getUserFromJwt(refreshToken);
- if (!memberId.equals(tokenService.findIdByRefreshToken(refreshToken))) {
- throw new BadRequestException(TokenErrorCode.TOKEN_INCORRECT_ERROR);
- }
- MemberAuthentication memberAuthentication = new MemberAuthentication(memberId, null, null);
- return AccessTokenGetSuccess.of(
- jwtTokenProvider.issueAccessToken(memberAuthentication)
- );
- }
-
- public boolean isExistingMember(
- final Long socialId,
- final SocialType socialType
- ) {
- return memberRepository.findBySocialTypeAndSocialId(socialId, socialType).isPresent();
- }
-
- public LoginSuccessResponse getTokenByMemberId(
- final Long id,
- final MemberInfoResponse memberInfoResponse
- ) {
- MemberAuthentication memberAuthentication = new MemberAuthentication(id, null, null);
- String refreshToken = jwtTokenProvider.issueRefreshToken(memberAuthentication);
- tokenService.saveRefreshToken(id, refreshToken);
- String nickname = memberInfoResponse.nickname();
- return LoginSuccessResponse.of(
- jwtTokenProvider.issueAccessToken(memberAuthentication),
- refreshToken, nickname
- );
- }
-
- @Transactional
- public void deleteUser(
- final Long id
- ) {
- Users users = userRepository.findById(id)
- .orElseThrow(
- () -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND)
- );
- userRepository.delete(users);
- }
-
- private LoginSuccessResponse getTokenDto(
- final MemberInfoResponse userResponse
- ) {
- if (isExistingMember(userResponse.socialId(), userResponse.socialType())) {
- return getTokenByMemberId(getBySocialId(userResponse.socialId(), userResponse.socialType()).getId(), userResponse);
- } else {
- Long id = createUser(userResponse);
- return getTokenByMemberId(id, userResponse);
- }
- }
+public class MemberService implements MemberUseCase {
+ private final UserRepository userRepository;
+ private final MemberRepository memberRepository;
+
+ @Override
+ @Transactional(readOnly = true)
+ public Member findMemberByMemberId(Long memberId) {
+ return memberRepository.findById(memberId)
+ .orElseThrow(() -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND));
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public boolean checkMemberExistsBySocialIdAndSocialType(final Long socialId, final SocialType socialType) {
+ return memberRepository.findBySocialTypeAndSocialId(socialId, socialType).isPresent();
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Member findMemberBySocialIdAndSocialType(final Long socialId, final SocialType socialType) {
+ return memberRepository.findBySocialTypeAndSocialId(socialId, socialType)
+ .orElseThrow(() -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND));
+ }
+
+ @Transactional
+ public void deleteUser(final Long id) {
+ Users users = userRepository.findById(id)
+ .orElseThrow(() -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND));
+
+ userRepository.delete(users);
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/beat/domain/member/application/SocialLoginService.java b/src/main/java/com/beat/domain/member/application/SocialLoginService.java
new file mode 100644
index 00000000..36b304c9
--- /dev/null
+++ b/src/main/java/com/beat/domain/member/application/SocialLoginService.java
@@ -0,0 +1,119 @@
+package com.beat.domain.member.application;
+
+import com.beat.domain.member.domain.Member;
+import com.beat.domain.member.domain.SocialType;
+import com.beat.domain.member.dto.LoginSuccessResponse;
+import com.beat.domain.member.exception.MemberErrorCode;
+import com.beat.domain.member.port.in.MemberUseCase;
+import com.beat.domain.user.domain.Users;
+import com.beat.domain.user.port.in.UserUseCase;
+import com.beat.global.auth.client.application.KakaoSocialService;
+import com.beat.global.auth.client.application.SocialService;
+import com.beat.global.auth.client.dto.MemberInfoResponse;
+import com.beat.global.auth.client.dto.MemberLoginRequest;
+import com.beat.global.common.exception.BadRequestException;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class SocialLoginService {
+
+ private final MemberRegistrationService memberRegistrationService;
+ private final AuthenticationService authenticationService;
+ private final KakaoSocialService kakaoSocialService;
+ private final MemberUseCase memberUseCase;
+
+ /**
+ * 소셜 로그인 또는 회원가입을 처리하는 메서드.
+ * 소셜 서비스에서 받은 authorizationCode와 로그인 요청 정보를 기반으로
+ * 사용자 정보를 조회하고, 로그인 또는 회원가입 후 성공 응답을 반환.
+ *
+ * @param authorizationCode 소셜 인증 코드
+ * @param loginRequest 로그인 요청 정보
+ * @return 로그인 성공 응답(LoginSuccessResponse)
+ */
+ @Transactional
+ public LoginSuccessResponse handleSocialLogin(final String authorizationCode,
+ final MemberLoginRequest loginRequest) {
+ MemberInfoResponse memberInfoResponse = findMemberInfoFromSocialService(authorizationCode, loginRequest);
+ return generateLoginResponseFromMemberInfo(memberInfoResponse);
+ }
+
+ /**
+ * 소셜 서비스에서 사용자 정보를 조회하는 메서드.
+ * 소셜 타입에 따라 적절한 소셜 서비스를 사용하여 로그인 정보를 가져옴.
+ *
+ * @param authorizationCode 소셜 인증 코드
+ * @param loginRequest 로그인 요청 정보
+ * @return 소셜 서비스에서 가져온 사용자 정보(MemberInfoResponse)
+ */
+ private MemberInfoResponse findMemberInfoFromSocialService(final String authorizationCode,
+ final MemberLoginRequest loginRequest) {
+ SocialService socialService = findSocialService(loginRequest.socialType());
+ return socialService.login(authorizationCode, loginRequest);
+ }
+
+ /**
+ * 소셜 타입에 맞는 SocialService를 반환하는 메서드.
+ * 소셜 로그인 타입이 KAKAO인지, GOOGLE인지 등에 따라 적절한 서비스를 반환.
+ *
+ * @param socialType 소셜 타입(KAKAO, GOOGLE 등)
+ * @return 적절한 SocialService 구현체
+ */
+ private SocialService findSocialService(SocialType socialType) {
+ return switch (socialType) {
+ case KAKAO -> kakaoSocialService;
+ // case GOOGLE -> googleSocialService;
+ default -> throw new BadRequestException(MemberErrorCode.SOCIAL_TYPE_BAD_REQUEST);
+ };
+ }
+
+ /**
+ * 사용자 정보를 기반으로 로그인 또는 회원가입을 처리한 후 로그인 성공 응답을 생성하는 메서드.
+ * 사용자가 존재하면 로그인 처리를, 존재하지 않으면 회원가입 후 로그인 처리를 수행.
+ *
+ * @param memberInfoResponse 소셜 서비스에서 가져온 사용자 정보
+ * @return 로그인 성공 응답(LoginSuccessResponse)
+ */
+ private LoginSuccessResponse generateLoginResponseFromMemberInfo(final MemberInfoResponse memberInfoResponse) {
+ log.info("Attempting to find or register member for socialId: {}, socialType: {}",
+ memberInfoResponse.socialId(), memberInfoResponse.socialType());
+
+ Long memberId = findOrRegisterMember(memberInfoResponse);
+ log.info("Found or registered member with memberId: {}", memberId);
+
+ Member member = memberUseCase.findMemberByMemberId(memberId);
+ Users user = member.getUser();
+
+ log.info("User role before generating token: {}", user.getRole());
+
+ return authenticationService.generateLoginSuccessResponse(memberId, user, memberInfoResponse);
+ }
+
+ /**
+ * 사용자 정보(Social ID와 Social Type)를 통해 기존 회원을 찾거나,
+ * 없으면 새로운 회원을 등록하는 메서드.
+ *
+ * @param memberInfoResponse 소셜 서비스에서 가져온 사용자 정보
+ * @return 등록된 회원 또는 기존 회원의 ID
+ */
+ private Long findOrRegisterMember(final MemberInfoResponse memberInfoResponse) {
+ boolean memberExists = memberUseCase.checkMemberExistsBySocialIdAndSocialType(memberInfoResponse.socialId(),
+ memberInfoResponse.socialType());
+
+ if (memberExists) {
+ Member existingMember = memberUseCase.findMemberBySocialIdAndSocialType(memberInfoResponse.socialId(),
+ memberInfoResponse.socialType());
+ log.info("Existing member role: {}", existingMember.getUser().getRole());
+ return existingMember.getId();
+ }
+
+ return memberRegistrationService.registerMemberWithUserInfo(memberInfoResponse);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/beat/domain/member/domain/Member.java b/src/main/java/com/beat/domain/member/domain/Member.java
index 90951b30..02a67ac7 100644
--- a/src/main/java/com/beat/domain/member/domain/Member.java
+++ b/src/main/java/com/beat/domain/member/domain/Member.java
@@ -2,7 +2,17 @@
import com.beat.domain.BaseTimeEntity;
import com.beat.domain.user.domain.Users;
-import jakarta.persistence.*;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@@ -13,7 +23,7 @@
@Entity
@Getter
-@NoArgsConstructor
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member extends BaseTimeEntity {
@Id
@@ -29,7 +39,7 @@ public class Member extends BaseTimeEntity {
@Column(nullable = true)
private LocalDateTime deletedAt;
- @ManyToOne
+ @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = true)
@OnDelete(action = OnDeleteAction.CASCADE)
private Users user;
diff --git a/src/main/java/com/beat/domain/member/dto/LoginSuccessResponse.java b/src/main/java/com/beat/domain/member/dto/LoginSuccessResponse.java
index 57b0576e..026bd3d1 100644
--- a/src/main/java/com/beat/domain/member/dto/LoginSuccessResponse.java
+++ b/src/main/java/com/beat/domain/member/dto/LoginSuccessResponse.java
@@ -3,13 +3,15 @@
public record LoginSuccessResponse(
String accessToken,
String refreshToken,
- String nickname
+ String nickname,
+ String role
) {
public static LoginSuccessResponse of(
final String accessToken,
final String refreshToken,
- final String nickname
+ final String nickname,
+ final String role
) {
- return new LoginSuccessResponse(accessToken, refreshToken, nickname);
+ return new LoginSuccessResponse(accessToken, refreshToken, nickname, role);
}
}
diff --git a/src/main/java/com/beat/domain/member/port/in/MemberUseCase.java b/src/main/java/com/beat/domain/member/port/in/MemberUseCase.java
new file mode 100644
index 00000000..d6a194b1
--- /dev/null
+++ b/src/main/java/com/beat/domain/member/port/in/MemberUseCase.java
@@ -0,0 +1,14 @@
+package com.beat.domain.member.port.in;
+
+import com.beat.domain.member.domain.Member;
+import com.beat.domain.member.domain.SocialType;
+
+public interface MemberUseCase {
+ Member findMemberByMemberId(Long memberId);
+
+ boolean checkMemberExistsBySocialIdAndSocialType(Long socialId, SocialType socialType);
+
+ Member findMemberBySocialIdAndSocialType(Long socialId, SocialType socialType);
+
+ void deleteUser(Long id);
+}
diff --git a/src/main/java/com/beat/domain/performance/api/HomeApi.java b/src/main/java/com/beat/domain/performance/api/HomeApi.java
new file mode 100644
index 00000000..0b6e9e58
--- /dev/null
+++ b/src/main/java/com/beat/domain/performance/api/HomeApi.java
@@ -0,0 +1,33 @@
+package com.beat.domain.performance.api;
+
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import com.beat.domain.performance.application.dto.home.HomeFindAllResponse;
+import com.beat.domain.performance.domain.Genre;
+import com.beat.global.common.dto.ErrorResponse;
+import com.beat.global.common.dto.SuccessResponse;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+
+@Tag(name = "Home", description = "홈 화면에서 공연 및 홍보목록 조회 API")
+public interface HomeApi {
+
+ @Operation(summary = "전체 공연 및 홍보 목록 조회", description = "홈 화면에서 전체 공연 목록 및 홍보 목록을 조회하는 GET API")
+ @ApiResponses(
+ value = {
+ @ApiResponse(
+ responseCode = "200",
+ description = "홈 화면 공연 목록 조회가 성공적으로 완료되었습니다.",
+ content = @Content(schema = @Schema(implementation = SuccessResponse.class))
+ )
+ }
+ )
+ ResponseEntity> getHomePerformanceList(
+ @RequestParam(required = false) Genre genre);
+}
\ No newline at end of file
diff --git a/src/main/java/com/beat/domain/performance/api/HomeController.java b/src/main/java/com/beat/domain/performance/api/HomeController.java
index 7a7a7159..8ebd44b2 100644
--- a/src/main/java/com/beat/domain/performance/api/HomeController.java
+++ b/src/main/java/com/beat/domain/performance/api/HomeController.java
@@ -1,13 +1,14 @@
package com.beat.domain.performance.api;
-import com.beat.domain.performance.application.PerformanceService;
-import com.beat.domain.performance.application.dto.home.HomeRequest;
-import com.beat.domain.performance.application.dto.home.HomeResponse;
+import com.beat.domain.performance.application.HomeService;
+import com.beat.domain.performance.application.dto.home.HomeFindRequest;
+import com.beat.domain.performance.application.dto.home.HomeFindAllResponse;
import com.beat.domain.performance.domain.Genre;
import com.beat.domain.performance.exception.PerformanceSuccessCode;
import com.beat.global.common.dto.SuccessResponse;
-import io.swagger.v3.oas.annotations.Operation;
+
import lombok.RequiredArgsConstructor;
+
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -17,19 +18,19 @@
@RestController
@RequestMapping("/api/main")
@RequiredArgsConstructor
-public class HomeController {
-
- private final PerformanceService performanceService;
-
- @Operation(summary = "전체공연목록, 홍보목록 조회 API", description = "홈화면에서 전체공연목록, 홍보목록을 조회하는 GET API입니다.")
- @GetMapping
- public ResponseEntity> getHomePerformanceList(@RequestParam(required = false) String genre) {
- HomeRequest homeRequest;
- if (genre != null) {
- homeRequest = new HomeRequest(Genre.valueOf(genre));
- } else {
- homeRequest = new HomeRequest(null);
- } HomeResponse homeResponse = performanceService.getHomePerformanceList(homeRequest);
- return ResponseEntity.ok(SuccessResponse.of(PerformanceSuccessCode.HOME_PERFORMANCE_RETRIEVE_SUCCESS, homeResponse));
- }
+public class HomeController implements HomeApi {
+
+ private final HomeService homeService;
+
+ @Override
+ @GetMapping
+ public ResponseEntity> getHomePerformanceList(
+ @RequestParam(required = false) Genre genre) {
+
+ HomeFindRequest request = new HomeFindRequest(genre);
+
+ HomeFindAllResponse response = homeService.findHomePerformanceList(request);
+ return ResponseEntity.ok(
+ SuccessResponse.of(PerformanceSuccessCode.HOME_PERFORMANCE_RETRIEVE_SUCCESS, response));
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/beat/domain/performance/api/PerformanceController.java b/src/main/java/com/beat/domain/performance/api/PerformanceController.java
index adf3a9a4..6cc25c77 100644
--- a/src/main/java/com/beat/domain/performance/api/PerformanceController.java
+++ b/src/main/java/com/beat/domain/performance/api/PerformanceController.java
@@ -1,20 +1,22 @@
package com.beat.domain.performance.api;
import com.beat.domain.performance.application.PerformanceManagementService;
-import com.beat.domain.performance.application.PerformanceUpdateService;
-import com.beat.domain.performance.application.dto.BookingPerformanceDetailResponse;
-import com.beat.domain.performance.application.dto.MakerPerformanceResponse;
-import com.beat.domain.performance.application.dto.PerformanceDetailResponse;
-import com.beat.domain.performance.application.dto.PerformanceEditResponse;
+import com.beat.domain.performance.application.PerformanceModifyService;
+import com.beat.domain.performance.application.dto.bookingPerformanceDetail.BookingPerformanceDetailResponse;
+import com.beat.domain.performance.application.dto.makerPerformance.MakerPerformanceResponse;
+import com.beat.domain.performance.application.dto.performanceDetail.PerformanceDetailResponse;
+import com.beat.domain.performance.application.dto.modify.PerformanceModifyDetailResponse;
import com.beat.domain.performance.application.dto.create.PerformanceRequest;
import com.beat.domain.performance.application.dto.create.PerformanceResponse;
-import com.beat.domain.performance.application.dto.update.PerformanceUpdateRequest;
-import com.beat.domain.performance.application.dto.update.PerformanceUpdateResponse;
+import com.beat.domain.performance.application.dto.modify.PerformanceModifyRequest;
+import com.beat.domain.performance.application.dto.modify.PerformanceModifyResponse;
import com.beat.domain.performance.exception.PerformanceSuccessCode;
import com.beat.domain.performance.application.PerformanceService;
import com.beat.global.auth.annotation.CurrentMember;
import com.beat.global.common.dto.SuccessResponse;
import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@@ -34,7 +36,7 @@ public class PerformanceController {
private final PerformanceService performanceService;
private final PerformanceManagementService performanceManagementService;
- private final PerformanceUpdateService performanceUpdateService;
+ private final PerformanceModifyService performanceModifyService;
@Operation(summary = "공연 생성 API", description = "공연을 생성하는 POST API입니다.")
@PostMapping
@@ -47,21 +49,35 @@ public ResponseEntity> createPerformance(
}
@Operation(summary = "공연 정보 수정 API", description = "공연 정보를 수정하는 PUT API입니다.")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "공연 정보 수정 성공"),
+ @ApiResponse(responseCode = "400", description = "잘못된 요청 - 회차 최대 개수 초과"),
+ @ApiResponse(responseCode = "400", description = "잘못된 요청 - 티켓 가격은 음수일 수 없습니다."),
+ @ApiResponse(responseCode = "400", description = "잘못된 요청 - 예매자가 존재하여 가격을 수정할 수 없습니다."),
+ @ApiResponse(responseCode = "403", description = "권한 없음 - 해당 공연의 소유자가 아닙니다."),
+ @ApiResponse(responseCode = "404", description = "존재하지 않는 공연 ID로 수정 요청을 보낼 수 없습니다."),
+ @ApiResponse(responseCode = "404", description = "존재하지 않는 회원 ID로 수정 요청을 보낼 수 없습니다."),
+ @ApiResponse(responseCode = "404", description = "존재하지 않는 회차 ID로 수정 요청을 보낼 수 없습니다."),
+ @ApiResponse(responseCode = "404", description = "존재하지 않는 등장인물 ID로 수정 요청을 보낼 수 없습니다."),
+ @ApiResponse(responseCode = "404", description = "존재하지 않는 스태프 ID로 수정 요청을 보낼 수 없습니다."),
+ @ApiResponse(responseCode = "404", description = "존재하지 않는 상세이미지 ID로 수정 요청을 보낼 수 없습니다."),
+ @ApiResponse(responseCode = "500", description = "서버 내부 오류")
+ })
@PutMapping
- public ResponseEntity> updatePerformance(
+ public ResponseEntity> updatePerformance(
@CurrentMember Long memberId,
- @RequestBody PerformanceUpdateRequest performanceUpdateRequest) {
- PerformanceUpdateResponse response = performanceUpdateService.updatePerformance(memberId, performanceUpdateRequest);
+ @RequestBody PerformanceModifyRequest performanceModifyRequest) {
+ PerformanceModifyResponse response = performanceModifyService.modifyPerformance(memberId, performanceModifyRequest);
return ResponseEntity.status(HttpStatus.OK)
.body(SuccessResponse.of(PerformanceSuccessCode.PERFORMANCE_UPDATE_SUCCESS, response));
}
@Operation(summary = "공연 수정 페이지 정보 조회 API", description = "공연 정보를 조회하는 GET API입니다.")
@GetMapping("/{performanceId}")
- public ResponseEntity> getPerformanceForEdit(
+ public ResponseEntity> getPerformanceForEdit(
@CurrentMember Long memberId,
@PathVariable Long performanceId) {
- PerformanceEditResponse response = performanceService.getPerformanceEdit(memberId, performanceId);
+ PerformanceModifyDetailResponse response = performanceService.getPerformanceEdit(memberId, performanceId);
return ResponseEntity.ok(SuccessResponse.of(PerformanceSuccessCode.PERFORMANCE_MODIFY_PAGE_SUCCESS, response));
}
diff --git a/src/main/java/com/beat/domain/performance/application/HomeService.java b/src/main/java/com/beat/domain/performance/application/HomeService.java
new file mode 100644
index 00000000..6ac6887c
--- /dev/null
+++ b/src/main/java/com/beat/domain/performance/application/HomeService.java
@@ -0,0 +1,71 @@
+package com.beat.domain.performance.application;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.beat.domain.performance.application.dto.home.HomeFindAllResponse;
+import com.beat.domain.performance.application.dto.home.HomeFindRequest;
+import com.beat.domain.performance.application.dto.home.HomePerformanceDetail;
+import com.beat.domain.performance.application.dto.home.HomePromotionDetail;
+import com.beat.domain.performance.dao.PerformanceRepository;
+import com.beat.domain.performance.domain.Genre;
+import com.beat.domain.performance.domain.Performance;
+import com.beat.domain.promotion.domain.Promotion;
+import com.beat.domain.promotion.port.in.PromotionUseCase;
+import com.beat.domain.schedule.application.ScheduleService;
+
+import lombok.RequiredArgsConstructor;
+
+@Service
+@RequiredArgsConstructor
+public class HomeService {
+
+ private final ScheduleService scheduleService;
+ private final PromotionUseCase promotionUseCase;
+
+ private final PerformanceRepository performanceRepository;
+
+ @Transactional(readOnly = true)
+ public HomeFindAllResponse findHomePerformanceList(HomeFindRequest homeFindRequest) {
+
+ List performances = findPerformancesByGenre(homeFindRequest);
+ List promotions = findAllPromotionsSortedByCarouselNumber();
+
+ if (performances.isEmpty()) {
+ return HomeFindAllResponse.of(promotions, new ArrayList<>());
+ }
+
+ List sortedPerformances = performances.stream()
+ .map(performance -> {
+ int minDueDate = scheduleService.getMinDueDateForPerformance(performance.getId());
+ return HomePerformanceDetail.of(performance, minDueDate);
+ })
+ .sorted(Comparator.comparingInt(detail -> detail.dueDate() < 0 ? 1 : 0)
+ .thenComparingInt(detail -> Math.abs(detail.dueDate())))
+ .toList();
+
+ return HomeFindAllResponse.of(promotions, sortedPerformances);
+ }
+
+ private List findPerformancesByGenre(HomeFindRequest homeFindRequest) {
+ Genre genre = homeFindRequest.genre();
+
+ if (genre != null) {
+ return performanceRepository.findByGenre(genre);
+ }
+
+ return performanceRepository.findAll();
+ }
+
+ private List findAllPromotionsSortedByCarouselNumber() {
+ return promotionUseCase.findAllPromotions()
+ .stream()
+ .sorted(Comparator.comparing(Promotion::getCarouselNumber, Comparator.comparingInt(Enum::ordinal)))
+ .map(HomePromotionDetail::from)
+ .toList();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/beat/domain/performance/application/PerformanceManagementService.java b/src/main/java/com/beat/domain/performance/application/PerformanceManagementService.java
index 0e3efa88..289a77ac 100644
--- a/src/main/java/com/beat/domain/performance/application/PerformanceManagementService.java
+++ b/src/main/java/com/beat/domain/performance/application/PerformanceManagementService.java
@@ -1,5 +1,14 @@
package com.beat.domain.performance.application;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
import com.beat.domain.booking.dao.BookingRepository;
import com.beat.domain.cast.dao.CastRepository;
import com.beat.domain.cast.domain.Cast;
@@ -7,181 +16,220 @@
import com.beat.domain.member.domain.Member;
import com.beat.domain.member.exception.MemberErrorCode;
import com.beat.domain.performance.application.dto.create.CastResponse;
+import com.beat.domain.performance.application.dto.create.PerformanceImageResponse;
import com.beat.domain.performance.application.dto.create.PerformanceRequest;
import com.beat.domain.performance.application.dto.create.PerformanceResponse;
import com.beat.domain.performance.application.dto.create.ScheduleResponse;
import com.beat.domain.performance.application.dto.create.StaffResponse;
+import com.beat.domain.performance.dao.PerformanceImageRepository;
import com.beat.domain.performance.dao.PerformanceRepository;
import com.beat.domain.performance.domain.Performance;
+import com.beat.domain.performance.domain.PerformanceImage;
import com.beat.domain.performance.exception.PerformanceErrorCode;
import com.beat.domain.schedule.dao.ScheduleRepository;
import com.beat.domain.schedule.domain.Schedule;
import com.beat.domain.staff.dao.StaffRepository;
import com.beat.domain.staff.domain.Staff;
import com.beat.domain.user.domain.Users;
+import com.beat.global.common.exception.BadRequestException;
import com.beat.global.common.exception.ForbiddenException;
import com.beat.global.common.exception.NotFoundException;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
+import com.beat.global.common.scheduler.application.JobSchedulerService;
-import java.time.LocalDate;
-import java.time.temporal.ChronoUnit;
-import java.util.List;
-import java.util.stream.Collectors;
+import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class PerformanceManagementService {
- private final PerformanceRepository performanceRepository;
- private final ScheduleRepository scheduleRepository;
- private final CastRepository castRepository;
- private final StaffRepository staffRepository;
- private final BookingRepository bookingRepository;
- private final MemberRepository memberRepository;
-
- @Transactional
- public PerformanceResponse createPerformance(Long memberId, PerformanceRequest request) {
- Member member = memberRepository.findById(memberId)
- .orElseThrow(() -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND));
-
- Users user = member.getUser();
-
- Performance performance = Performance.create(
- request.performanceTitle(),
- request.genre(),
- request.runningTime(),
- request.performanceDescription(),
- request.performanceAttentionNote(),
- request.bankName(),
- request.accountNumber(),
- request.accountHolder(),
- request.posterImage(),
- request.performanceTeamName(),
- request.performanceVenue(),
- request.performanceContact(),
- request.performancePeriod(),
- request.ticketPrice(),
- request.totalScheduleCount(),
- user
- );
- performanceRepository.save(performance);
-
- List schedules = request.scheduleList().stream()
- .map(scheduleRequest -> Schedule.create(
- scheduleRequest.performanceDate(),
- scheduleRequest.totalTicketCount(),
- 0,
- true,
- scheduleRequest.scheduleNumber(),
- performance
- ))
- .collect(Collectors.toList());
- scheduleRepository.saveAll(schedules);
-
- List casts = request.castList().stream()
- .map(castRequest -> Cast.create(
- castRequest.castName(),
- castRequest.castRole(),
- castRequest.castPhoto(),
- performance
- ))
- .collect(Collectors.toList());
- castRepository.saveAll(casts);
-
- List staffs = request.staffList().stream()
- .map(staffRequest -> Staff.create(
- staffRequest.staffName(),
- staffRequest.staffRole(),
- staffRequest.staffPhoto(),
- performance
- ))
- .collect(Collectors.toList());
- staffRepository.saveAll(staffs);
-
- return mapToPerformanceResponse(performance, schedules, casts, staffs);
- }
-
- private PerformanceResponse mapToPerformanceResponse(Performance performance, List schedules, List casts, List staffs) {
- List scheduleResponses = schedules.stream()
- .map(schedule -> ScheduleResponse.of(
- schedule.getId(),
- schedule.getPerformanceDate(),
- schedule.getTotalTicketCount(),
- calculateDueDate(schedule.getPerformanceDate().toLocalDate()),
- schedule.getScheduleNumber()
- ))
- .collect(Collectors.toList());
-
- List castResponses = casts.stream()
- .map(cast -> CastResponse.of(
- cast.getId(),
- cast.getCastName(),
- cast.getCastRole(),
- cast.getCastPhoto()
- ))
- .collect(Collectors.toList());
-
- List staffResponses = staffs.stream()
- .map(staff -> StaffResponse.of(
- staff.getId(),
- staff.getStaffName(),
- staff.getStaffRole(),
- staff.getStaffPhoto()
- ))
- .collect(Collectors.toList());
-
- return PerformanceResponse.of(
- performance.getUsers().getId(),
- performance.getId(),
- performance.getPerformanceTitle(),
- performance.getGenre(),
- performance.getRunningTime(),
- performance.getPerformanceDescription(),
- performance.getPerformanceAttentionNote(),
- performance.getBankName(),
- performance.getAccountNumber(),
- performance.getAccountHolder(),
- performance.getPosterImage(),
- performance.getPerformanceTeamName(),
- performance.getPerformanceVenue(),
- performance.getPerformanceContact(),
- performance.getPerformancePeriod(),
- performance.getTicketPrice(),
- performance.getTotalScheduleCount(),
- scheduleResponses,
- castResponses,
- staffResponses
- );
- }
-
- private int calculateDueDate(LocalDate performanceDate) {
- return (int) ChronoUnit.DAYS.between(LocalDate.now(), performanceDate);
- }
-
- @Transactional
- public void deletePerformance(Long memberId, Long performanceId) {
- Member member = memberRepository.findById(memberId)
- .orElseThrow(() -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND));
-
- Long userId = member.getUser().getId();
-
- Performance performance = performanceRepository.findById(performanceId)
- .orElseThrow(() -> new NotFoundException(PerformanceErrorCode.PERFORMANCE_NOT_FOUND));
-
- if (!performance.getUsers().getId().equals(userId)) {
- throw new ForbiddenException(PerformanceErrorCode.NOT_PERFORMANCE_OWNER);
- }
-
- List scheduleIds = scheduleRepository.findIdsByPerformanceId(performanceId);
-
- boolean hasBookings = bookingRepository.existsByScheduleIdIn(scheduleIds);
-
- if (hasBookings) {
- throw new ForbiddenException(PerformanceErrorCode.PERFORMANCE_DELETE_FAILED);
- }
-
- performanceRepository.delete(performance);
- }
+ private final PerformanceRepository performanceRepository;
+ private final ScheduleRepository scheduleRepository;
+ private final CastRepository castRepository;
+ private final StaffRepository staffRepository;
+ private final BookingRepository bookingRepository;
+ private final MemberRepository memberRepository;
+ private final PerformanceImageRepository performanceImageRepository;
+ private final JobSchedulerService jobSchedulerService;
+
+ @Transactional
+ public PerformanceResponse createPerformance(Long memberId, PerformanceRequest request) {
+ Member member = memberRepository.findById(memberId)
+ .orElseThrow(() -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND));
+
+ Users user = member.getUser();
+
+ if (request.performanceDescription().length() > 500) {
+ throw new BadRequestException(PerformanceErrorCode.INVALID_PERFORMANCE_DESCRIPTION_LENGTH);
+ }
+
+ if (request.performanceAttentionNote().length() > 500) {
+ throw new BadRequestException(PerformanceErrorCode.INVALID_ATTENTION_NOTE_LENGTH);
+ }
+
+ Performance performance = Performance.create(
+ request.performanceTitle(),
+ request.genre(),
+ request.runningTime(),
+ request.performanceDescription(),
+ request.performanceAttentionNote(),
+ request.bankName(),
+ request.accountNumber(),
+ request.accountHolder(),
+ request.posterImage(),
+ request.performanceTeamName(),
+ request.performanceVenue(),
+ request.performanceContact(),
+ request.performancePeriod(),
+ request.ticketPrice(),
+ request.totalScheduleCount(),
+ user
+ );
+ performanceRepository.save(performance);
+
+ List schedules = request.scheduleList().stream()
+ .map(scheduleRequest -> {
+ if (scheduleRequest.performanceDate().isBefore(LocalDateTime.now())) {
+ throw new BadRequestException(PerformanceErrorCode.PAST_SCHEDULE_NOT_ALLOWED);
+ }
+ return Schedule.create(
+ scheduleRequest.performanceDate(),
+ scheduleRequest.totalTicketCount(),
+ 0,
+ true,
+ scheduleRequest.scheduleNumber(),
+ performance
+ );
+ })
+ .collect(Collectors.toList());
+ scheduleRepository.saveAll(schedules);
+
+ schedules.forEach(jobSchedulerService::addScheduleIfNotExists);
+
+ List casts = request.castList().stream()
+ .map(castRequest -> Cast.create(
+ castRequest.castName(),
+ castRequest.castRole(),
+ castRequest.castPhoto(),
+ performance
+ ))
+ .collect(Collectors.toList());
+ castRepository.saveAll(casts);
+
+ List staffs = request.staffList().stream()
+ .map(staffRequest -> Staff.create(
+ staffRequest.staffName(),
+ staffRequest.staffRole(),
+ staffRequest.staffPhoto(),
+ performance
+ ))
+ .collect(Collectors.toList());
+ staffRepository.saveAll(staffs);
+
+ List performanceImageList = request.performanceImageList().stream()
+ .map(performanceImageRequest -> PerformanceImage.create(
+ performanceImageRequest.performanceImage(),
+ performance
+ ))
+ .collect(Collectors.toList());
+ performanceImageRepository.saveAll(performanceImageList);
+
+ return mapToPerformanceResponse(performance, schedules, casts, staffs, performanceImageList);
+ }
+
+ private PerformanceResponse mapToPerformanceResponse(Performance performance, List schedules,
+ List casts, List staffs, List performanceImages) {
+ List scheduleResponses = schedules.stream()
+ .map(schedule -> ScheduleResponse.of(
+ schedule.getId(),
+ schedule.getPerformanceDate(),
+ schedule.getTotalTicketCount(),
+ calculateDueDate(schedule.getPerformanceDate().toLocalDate()),
+ schedule.getScheduleNumber()
+ ))
+ .collect(Collectors.toList());
+
+ List castResponses = casts.stream()
+ .map(cast -> CastResponse.of(
+ cast.getId(),
+ cast.getCastName(),
+ cast.getCastRole(),
+ cast.getCastPhoto()
+ ))
+ .collect(Collectors.toList());
+
+ List staffResponses = staffs.stream()
+ .map(staff -> StaffResponse.of(
+ staff.getId(),
+ staff.getStaffName(),
+ staff.getStaffRole(),
+ staff.getStaffPhoto()
+ ))
+ .collect(Collectors.toList());
+
+ List performanceImageResponses = performanceImages.stream()
+ .map(image -> PerformanceImageResponse.of(
+ image.getId(),
+ image.getPerformanceImage()
+ ))
+ .collect(Collectors.toList());
+
+ return PerformanceResponse.of(
+ performance.getUsers().getId(),
+ performance.getId(),
+ performance.getPerformanceTitle(),
+ performance.getGenre(),
+ performance.getRunningTime(),
+ performance.getPerformanceDescription(),
+ performance.getPerformanceAttentionNote(),
+ performance.getBankName(),
+ performance.getAccountNumber(),
+ performance.getAccountHolder(),
+ performance.getPosterImage(),
+ performance.getPerformanceTeamName(),
+ performance.getPerformanceVenue(),
+ performance.getPerformanceContact(),
+ performance.getPerformancePeriod(),
+ performance.getTicketPrice(),
+ performance.getTotalScheduleCount(),
+ scheduleResponses,
+ castResponses,
+ staffResponses,
+ performanceImageResponses
+ );
+ }
+
+ private int calculateDueDate(LocalDate performanceDate) {
+ return (int)ChronoUnit.DAYS.between(LocalDate.now(), performanceDate);
+ }
+
+ @Transactional
+ public void deletePerformance(Long memberId, Long performanceId) {
+ Member member = memberRepository.findById(memberId)
+ .orElseThrow(() -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND));
+
+ Long userId = member.getUser().getId();
+
+ Performance performance = performanceRepository.findById(performanceId)
+ .orElseThrow(() -> new NotFoundException(PerformanceErrorCode.PERFORMANCE_NOT_FOUND));
+
+ if (!performance.getUsers().getId().equals(userId)) {
+ throw new ForbiddenException(PerformanceErrorCode.NOT_PERFORMANCE_OWNER);
+ }
+
+ List scheduleIds = scheduleRepository.findIdsByPerformanceId(performanceId);
+
+ boolean hasBookings = bookingRepository.existsByScheduleIdIn(scheduleIds);
+
+ if (hasBookings) {
+ throw new ForbiddenException(PerformanceErrorCode.PERFORMANCE_DELETE_FAILED);
+ }
+
+ // 모든 스케줄에 대해 등록된 TaskScheduler 작업을 취소
+ List schedules = scheduleRepository.findByPerformanceId(performanceId);
+ for (Schedule schedule : schedules) {
+ jobSchedulerService.cancelScheduledTaskForPerformance(schedule);
+ }
+
+ performanceRepository.delete(performance);
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/beat/domain/performance/application/PerformanceModifyService.java b/src/main/java/com/beat/domain/performance/application/PerformanceModifyService.java
new file mode 100644
index 00000000..48db2f41
--- /dev/null
+++ b/src/main/java/com/beat/domain/performance/application/PerformanceModifyService.java
@@ -0,0 +1,603 @@
+package com.beat.domain.performance.application;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.beat.domain.booking.dao.BookingRepository;
+import com.beat.domain.cast.dao.CastRepository;
+import com.beat.domain.cast.domain.Cast;
+import com.beat.domain.cast.exception.CastErrorCode;
+import com.beat.domain.member.dao.MemberRepository;
+import com.beat.domain.member.domain.Member;
+import com.beat.domain.member.exception.MemberErrorCode;
+import com.beat.domain.performance.application.dto.modify.PerformanceModifyRequest;
+import com.beat.domain.performance.application.dto.modify.PerformanceModifyResponse;
+import com.beat.domain.performance.application.dto.modify.cast.CastModifyRequest;
+import com.beat.domain.performance.application.dto.modify.cast.CastModifyResponse;
+import com.beat.domain.performance.application.dto.modify.performanceImage.PerformanceImageModifyRequest;
+import com.beat.domain.performance.application.dto.modify.performanceImage.PerformanceImageModifyResponse;
+import com.beat.domain.performance.application.dto.modify.schedule.ScheduleModifyRequest;
+import com.beat.domain.performance.application.dto.modify.schedule.ScheduleModifyResponse;
+import com.beat.domain.performance.application.dto.modify.staff.StaffModifyRequest;
+import com.beat.domain.performance.application.dto.modify.staff.StaffModifyResponse;
+import com.beat.domain.performance.dao.PerformanceImageRepository;
+import com.beat.domain.performance.dao.PerformanceRepository;
+import com.beat.domain.performance.domain.Performance;
+import com.beat.domain.performance.domain.PerformanceImage;
+import com.beat.domain.performance.exception.PerformanceErrorCode;
+import com.beat.domain.performance.exception.PerformanceImageErrorCode;
+import com.beat.domain.schedule.dao.ScheduleRepository;
+import com.beat.domain.schedule.domain.Schedule;
+import com.beat.domain.schedule.domain.ScheduleNumber;
+import com.beat.domain.schedule.exception.ScheduleErrorCode;
+import com.beat.domain.staff.dao.StaffRepository;
+import com.beat.domain.staff.domain.Staff;
+import com.beat.domain.staff.exception.StaffErrorCode;
+import com.beat.global.common.exception.BadRequestException;
+import com.beat.global.common.exception.ForbiddenException;
+import com.beat.global.common.exception.NotFoundException;
+import com.beat.global.common.scheduler.application.JobSchedulerService;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class PerformanceModifyService {
+
+ private final PerformanceRepository performanceRepository;
+ private final ScheduleRepository scheduleRepository;
+ private final MemberRepository memberRepository;
+ private final CastRepository castRepository;
+ private final StaffRepository staffRepository;
+ private final BookingRepository bookingRepository;
+ private final PerformanceImageRepository performanceImageRepository;
+ private final JobSchedulerService jobSchedulerService;
+
+ @Transactional
+ public PerformanceModifyResponse modifyPerformance(Long memberId, PerformanceModifyRequest request) {
+ log.info("Starting updatePerformance for memberId: {}, performanceId: {}", memberId, request.performanceId());
+
+ Member member = validateMember(memberId);
+ Long userId = member.getUser().getId();
+
+ Performance performance = findPerformance(request.performanceId());
+
+ validateOwnership(userId, performance);
+
+ List scheduleIds = scheduleRepository.findIdsByPerformanceId(request.performanceId());
+ boolean isBookerExist = bookingRepository.existsByScheduleIdIn(scheduleIds);
+
+ if (isBookerExist && request.ticketPrice() != performance.getTicketPrice()) {
+ log.error("Ticket price update failed due to existing bookings for performanceId: {}", performance.getId());
+ throw new BadRequestException(PerformanceErrorCode.PRICE_UPDATE_NOT_ALLOWED);
+ }
+
+ updatePerformanceDetails(performance, request, isBookerExist);
+
+ List modifiedSchedules = processSchedules(request.scheduleModifyRequests(),
+ performance);
+ List modifiedCasts = processCasts(request.castModifyRequests(), performance);
+ List modifiedStaffs = processStaffs(request.staffModifyRequests(), performance);
+ List modifiedPerformanceImages = processPerformanceImages(
+ request.performanceImageModifyRequests(), performance);
+
+ PerformanceModifyResponse response = completeModifyResponse(performance, modifiedSchedules, modifiedCasts,
+ modifiedStaffs, modifiedPerformanceImages);
+
+ log.info("Successfully completed updatePerformance for performanceId: {}", request.performanceId());
+ return response;
+ }
+
+ private Member validateMember(Long memberId) {
+ log.debug("Validating memberId: {}", memberId);
+ return memberRepository.findById(memberId)
+ .orElseThrow(() -> {
+ log.error("Member not found: memberId: {}", memberId);
+ return new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND);
+ });
+ }
+
+ private Performance findPerformance(Long performanceId) {
+ log.debug("Finding performance with performanceId: {}", performanceId);
+ return performanceRepository.findById(performanceId)
+ .orElseThrow(() -> {
+ log.error("Performance not found: performanceId: {}", performanceId);
+ return new NotFoundException(PerformanceErrorCode.PERFORMANCE_NOT_FOUND);
+ });
+ }
+
+ private void validateOwnership(Long userId, Performance performance) {
+ if (!performance.getUsers().getId().equals(userId)) {
+ log.error("User ID {} does not own performance ID {}", userId, performance.getId());
+ throw new ForbiddenException(PerformanceErrorCode.NOT_PERFORMANCE_OWNER);
+ }
+ }
+
+ private void updatePerformanceDetails(Performance performance, PerformanceModifyRequest request,
+ boolean isBookerExist) {
+ log.debug("Updating performance details for performanceId: {}", performance.getId());
+
+ performance.update(
+ request.performanceTitle(),
+ request.genre(),
+ request.runningTime(),
+ request.performanceDescription(),
+ request.performanceAttentionNote(),
+ request.bankName(),
+ request.accountNumber(),
+ request.accountHolder(),
+ request.posterImage(),
+ request.performanceTeamName(),
+ request.performanceVenue(),
+ request.performanceContact(),
+ request.performancePeriod(),
+ request.totalScheduleCount()
+ );
+
+ if (!isBookerExist) {
+ log.debug("Updating ticket price to {}", request.ticketPrice());
+ performance.updateTicketPrice(request.ticketPrice());
+ }
+
+ performanceRepository.save(performance);
+ log.debug("Performance details updated for performanceId: {}", performance.getId());
+ }
+
+ private List processSchedules(List scheduleRequests,
+ Performance performance) {
+ List existingScheduleIds = scheduleRepository.findIdsByPerformanceId(performance.getId());
+
+ List requestScheduleIds = scheduleRequests.stream()
+ .map(ScheduleModifyRequest::scheduleId)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+
+ List schedulesToDelete = existingScheduleIds.stream()
+ .filter(id -> !requestScheduleIds.contains(id))
+ .collect(Collectors.toList());
+
+ deleteRemainingSchedules(schedulesToDelete);
+
+ List schedules = scheduleRequests.stream()
+ .map(request -> {
+ Schedule schedule;
+ if (request.scheduleId() == null) {
+ schedule = addSchedule(request, performance);
+ } else {
+ schedule = updateSchedule(request, performance);
+ }
+
+ // isBooking이 true인 경우만 스케줄러에 등록
+ if (schedule.isBooking()) {
+ jobSchedulerService.addScheduleIfNotExists(schedule);
+ }
+
+ return schedule;
+ })
+ .collect(Collectors.toList());
+
+ assignScheduleNumbers(schedules);
+
+ return schedules.stream()
+ .map(schedule -> ScheduleModifyResponse.of(
+ schedule.getId(),
+ schedule.getPerformanceDate(),
+ schedule.getTotalTicketCount(),
+ calculateDueDate(schedule.getPerformanceDate()),
+ schedule.getScheduleNumber()
+ ))
+ .collect(Collectors.toList());
+ }
+
+ private void assignScheduleNumbers(List schedules) {
+ schedules.sort(Comparator.comparing(Schedule::getPerformanceDate));
+
+ for (int i = 0; i < schedules.size(); i++) {
+ ScheduleNumber scheduleNumber = ScheduleNumber.values()[i];
+ schedules.get(i).updateScheduleNumber(scheduleNumber);
+ }
+ }
+
+ private Schedule addSchedule(ScheduleModifyRequest request, Performance performance) {
+ log.debug("Adding schedules for performanceId: {}", performance.getId());
+
+ if (request.performanceDate().isBefore(LocalDateTime.now())) {
+ throw new BadRequestException(PerformanceErrorCode.PAST_SCHEDULE_NOT_ALLOWED);
+ }
+
+ long existingSchedulesCount = scheduleRepository.countByPerformanceId(performance.getId());
+
+ if ((existingSchedulesCount + 1) > 10) {
+ throw new BadRequestException(PerformanceErrorCode.MAX_SCHEDULE_LIMIT_EXCEEDED);
+ }
+
+ Schedule schedule = Schedule.create(
+ request.performanceDate(),
+ request.totalTicketCount(),
+ 0,
+ true,
+ ScheduleNumber.FIRST, // 임시로 1회차
+ performance
+ );
+
+ Schedule savedSchedule = scheduleRepository.save(schedule);
+ log.debug("Added schedule with scheduleId: {} for performanceId: {}", savedSchedule.getId(),
+ performance.getId());
+ return savedSchedule;
+ }
+
+ private Schedule updateSchedule(ScheduleModifyRequest request, Performance performance) {
+ log.debug("Updating schedules for scheduleId: {}", request.scheduleId());
+
+ Schedule schedule = scheduleRepository.findById(request.scheduleId())
+ .orElseThrow(() -> {
+ log.error("Schedule not found: scheduleId: {}", request.scheduleId());
+ return new NotFoundException(ScheduleErrorCode.NO_SCHEDULE_FOUND);
+ });
+
+ if (!schedule.getPerformance().equals(performance)) {
+ throw new ForbiddenException(ScheduleErrorCode.SCHEDULE_NOT_BELONG_TO_PERFORMANCE);
+ }
+
+ // 종료된 스케줄(기존 스케쥴 날짜가 과거인 경우)은 날짜 변경 불가
+ if (schedule.getPerformanceDate().isBefore(LocalDateTime.now())) {
+ if (!schedule.getPerformanceDate().isEqual(request.performanceDate())) {
+ throw new BadRequestException(
+ PerformanceErrorCode.SCHEDULE_MODIFICATION_NOT_ALLOWED_FOR_ENDED_SCHEDULE);
+ }
+ // 날짜 변경이 없으면 그대로 반환
+ return schedule;
+ }
+
+ // 종료되지 않은 스케줄을 과거 날짜로 수정 시 에러 발생
+ if (request.performanceDate().isBefore(LocalDateTime.now())) {
+ throw new BadRequestException(PerformanceErrorCode.PAST_SCHEDULE_NOT_ALLOWED);
+ }
+
+ // 티켓 수 관련 검증
+ if (request.totalTicketCount() != schedule.getTotalTicketCount()) {
+ // 판매된 티켓 수보다 적은 totalTicketCount로 변경하려는 경우 예외 처리
+ if (request.totalTicketCount() < schedule.getSoldTicketCount()) {
+ throw new BadRequestException(PerformanceErrorCode.INVALID_TICKET_COUNT);
+ }
+
+ // 매진 상태로 변경 (soldTicketCount와 totalTicketCount가 동일하고, 기존 isBooking이 true인 경우)
+ if (request.totalTicketCount() == schedule.getSoldTicketCount() && schedule.isBooking()) {
+ schedule.updateIsBooking(false); // 매진 처리 (isBooking = false)
+ }
+
+ // 매진이 풀리는 경우 (totalTicketCount 증가, 기존 isBooking이 false인 경우)
+ else if (request.totalTicketCount() > schedule.getTotalTicketCount() && !schedule.isBooking()) {
+ schedule.updateIsBooking(true); // 매진 풀림 처리 (isBooking = true)
+ }
+ }
+
+ jobSchedulerService.cancelScheduledTaskForPerformance(schedule);
+
+ schedule.update(
+ request.performanceDate(),
+ request.totalTicketCount(),
+ schedule.getScheduleNumber() // 기존 scheduleNumber 유지
+ );
+ return scheduleRepository.save(schedule);
+ }
+
+ private void deleteRemainingSchedules(List scheduleIds) {
+ if (scheduleIds == null || scheduleIds.isEmpty()) {
+ log.debug("No schedules to delete");
+ return;
+ }
+
+ scheduleIds.forEach(scheduleId -> {
+ Schedule schedule = scheduleRepository.findById(scheduleId)
+ .orElseThrow(() -> {
+ log.error("Schedule not found: scheduleId: {}", scheduleId);
+ return new NotFoundException(ScheduleErrorCode.NO_SCHEDULE_FOUND);
+ });
+
+ jobSchedulerService.cancelScheduledTaskForPerformance(schedule);
+
+ scheduleRepository.delete(schedule);
+ log.debug("Deleted schedule with scheduleId: {}", scheduleId);
+ });
+ }
+
+ private List processCasts(List castRequests, Performance performance) {
+ log.debug("Processing casts for performanceId: {}", performance.getId());
+
+ List existingCastIds = castRepository.findIdsByPerformanceId(performance.getId());
+
+ List responses = castRequests.stream()
+ .map(request -> {
+ if (request.castId() == null) {
+ return addCast(request, performance);
+ } else {
+ existingCastIds.remove(request.castId());
+ return updateCast(request, performance);
+ }
+ })
+ .toList();
+
+ deleteRemainingCasts(existingCastIds);
+
+ return responses;
+ }
+
+ private CastModifyResponse addCast(CastModifyRequest request, Performance performance) {
+ log.debug("Adding casts for performanceId: {}", performance.getId());
+
+ Cast cast = Cast.create(
+ request.castName(),
+ request.castRole(),
+ request.castPhoto(),
+ performance
+ );
+ Cast savedCast = castRepository.save(cast);
+ log.debug("Added cast with castId: {} for performanceId: {}", savedCast.getId(), performance.getId());
+ return CastModifyResponse.of(
+ savedCast.getId(),
+ savedCast.getCastName(),
+ savedCast.getCastRole(),
+ savedCast.getCastPhoto()
+ );
+ }
+
+ private CastModifyResponse updateCast(CastModifyRequest request, Performance performance) {
+ log.debug("Updating casts for castId: {}", request.castId());
+
+ Cast cast = castRepository.findById(request.castId())
+ .orElseThrow(() -> {
+ log.error("Cast not found: castId: {}", request.castId());
+ return new NotFoundException(CastErrorCode.CAST_NOT_FOUND);
+ });
+
+ if (!cast.getPerformance().equals(performance)) {
+ throw new ForbiddenException(CastErrorCode.CAST_NOT_BELONG_TO_PERFORMANCE);
+ }
+
+ cast.update(
+ request.castName(),
+ request.castRole(),
+ request.castPhoto()
+ );
+ castRepository.save(cast);
+ log.debug("Updated cast with castId: {}", cast.getId());
+ return CastModifyResponse.of(
+ cast.getId(),
+ cast.getCastName(),
+ cast.getCastRole(),
+ cast.getCastPhoto()
+ );
+ }
+
+ private void deleteRemainingCasts(List castIds) {
+ if (castIds == null || castIds.isEmpty()) {
+ log.debug("No casts to delete");
+ return;
+ }
+
+ castIds.forEach(castId -> {
+ Cast cast = castRepository.findById(castId)
+ .orElseThrow(() -> {
+ log.error("Cast not found: castId: {}", castId);
+ return new NotFoundException(CastErrorCode.CAST_NOT_FOUND);
+ });
+ castRepository.delete(cast);
+ log.debug("Deleted cast with castId: {}", castId);
+ });
+ }
+
+ private List processStaffs(List staffRequests, Performance performance) {
+ log.debug("Processing staffs for performanceId: {}", performance.getId());
+
+ List existingStaffIds = staffRepository.findIdsByPerformanceId(performance.getId());
+
+ List responses = staffRequests.stream()
+ .map(request -> {
+ if (request.staffId() == null) {
+ return addStaff(request, performance);
+ } else {
+ existingStaffIds.remove(request.staffId()); // 요청에 포함된 ID는 삭제 후보에서 제거
+ return updateStaff(request, performance);
+ }
+ })
+ .collect(Collectors.toList());
+
+ deleteRemainingStaffs(existingStaffIds);
+
+ return responses;
+ }
+
+ private StaffModifyResponse addStaff(StaffModifyRequest request, Performance performance) {
+ log.debug("Adding staffs for performanceId: {}", performance.getId());
+
+ Staff staff = Staff.create(
+ request.staffName(),
+ request.staffRole(),
+ request.staffPhoto(),
+ performance
+ );
+ Staff savedStaff = staffRepository.save(staff);
+ log.debug("Added staff with staffId: {} for performanceId: {}", savedStaff.getId(), performance.getId());
+ return StaffModifyResponse.of(
+ savedStaff.getId(),
+ savedStaff.getStaffName(),
+ savedStaff.getStaffRole(),
+ savedStaff.getStaffPhoto()
+ );
+ }
+
+ private StaffModifyResponse updateStaff(StaffModifyRequest request, Performance performance) {
+ log.debug("Updating staffs for staffId: {}", request.staffId());
+
+ Staff staff = staffRepository.findById(request.staffId())
+ .orElseThrow(() -> {
+ log.error("Staff not found: staffId: {}", request.staffId());
+ return new NotFoundException(StaffErrorCode.STAFF_NOT_FOUND);
+ });
+
+ if (!staff.getPerformance().equals(performance)) {
+ throw new ForbiddenException(StaffErrorCode.STAFF_NOT_BELONG_TO_PERFORMANCE);
+ }
+
+ staff.update(
+ request.staffName(),
+ request.staffRole(),
+ request.staffPhoto()
+ );
+ staffRepository.save(staff);
+ log.debug("Updated staff with staffId: {}", staff.getId());
+ return StaffModifyResponse.of(
+ staff.getId(),
+ staff.getStaffName(),
+ staff.getStaffRole(),
+ staff.getStaffPhoto()
+ );
+ }
+
+ private void deleteRemainingStaffs(List staffIds) {
+ if (staffIds == null || staffIds.isEmpty()) {
+ log.debug("No staffs to delete");
+ return;
+ }
+
+ staffIds.forEach(staffId -> {
+ Staff staff = staffRepository.findById(staffId)
+ .orElseThrow(() -> {
+ log.error("Staff not found: staffId: {}", staffId);
+ return new NotFoundException(StaffErrorCode.STAFF_NOT_FOUND);
+ });
+ staffRepository.delete(staff);
+ log.debug("Deleted staff with staffId: {}", staffId);
+ });
+ }
+
+ private int calculateDueDate(LocalDateTime performanceDate) {
+ return (int)ChronoUnit.DAYS.between(LocalDate.now(), performanceDate.toLocalDate());
+ }
+
+ private List processPerformanceImages(
+ List performanceImageRequests, Performance performance) {
+ log.debug("Processing performanceImages for performanceId: {}", performance.getId());
+
+ List existingPerformanceImageIds = performanceImageRepository.findIdsByPerformanceId(performance.getId());
+
+ List responses = performanceImageRequests.stream()
+ .map(request -> {
+ if (request.performanceImageId() == null) {
+ return addPerformanceImage(request, performance);
+ } else {
+ existingPerformanceImageIds.remove(request.performanceImageId());
+ return updatePerformanceImage(request, performance);
+ }
+ })
+ .toList();
+
+ deleteRemainingPerformanceImages(existingPerformanceImageIds);
+
+ return responses;
+ }
+
+ private PerformanceImageModifyResponse addPerformanceImage(PerformanceImageModifyRequest request,
+ Performance performance) {
+ log.debug("Adding performanceImages for performanceId: {}", performance.getId());
+
+ PerformanceImage performanceImage = PerformanceImage.create(
+ request.performanceImage(),
+ performance
+ );
+ PerformanceImage savedPerformanceImage = performanceImageRepository.save(performanceImage);
+ log.debug("Added performanceImage: {}", savedPerformanceImage.getId());
+ return PerformanceImageModifyResponse.of(
+ savedPerformanceImage.getId(),
+ savedPerformanceImage.getPerformanceImage()
+ );
+ }
+
+ private PerformanceImageModifyResponse updatePerformanceImage(PerformanceImageModifyRequest request,
+ Performance performance) {
+ log.debug("Updating performanceImages for performanceId: {}", performance.getId());
+
+ PerformanceImage performanceImage = performanceImageRepository.findById(request.performanceImageId())
+ .orElseThrow(() -> {
+ log.error("PerformanceImage not found: performanceId: {}", request.performanceImageId());
+ return new NotFoundException(PerformanceImageErrorCode.PERFORMANCE_IMAGE_NOT_FOUND);
+ });
+
+ if (!performanceImage.getPerformance().equals(performance)) {
+ throw new ForbiddenException(PerformanceImageErrorCode.PERFORMANCE_IMAGE_NOT_BELONG_TO_PERFORMANCE);
+ }
+
+ performanceImage.update(
+ request.performanceImage()
+ );
+ performanceImageRepository.save(performanceImage);
+ log.debug("Updated performanceImage: {}", performanceImage.getId());
+ return PerformanceImageModifyResponse.of(
+ performanceImage.getId(),
+ performanceImage.getPerformanceImage()
+ );
+ }
+
+ private void deleteRemainingPerformanceImages(List performanceImageIds) {
+ if (performanceImageIds == null || performanceImageIds.isEmpty()) {
+ log.debug("No performanceImages to delete");
+ return;
+ }
+
+ performanceImageIds.forEach(performanceImageId -> {
+ PerformanceImage performanceImage = performanceImageRepository.findById(performanceImageId)
+ .orElseThrow(() -> {
+ log.error("PerformanceImage not found: performanceImageId: {}", performanceImageId);
+ return new NotFoundException(PerformanceImageErrorCode.PERFORMANCE_IMAGE_NOT_FOUND);
+ });
+ performanceImageRepository.delete(performanceImage);
+ log.debug("Deleted performanceImage: {}", performanceImageId);
+ });
+ }
+
+ private PerformanceModifyResponse completeModifyResponse(
+ Performance performance,
+ List scheduleModifyResponses,
+ List castModifyResponses,
+ List staffModifyResponses,
+ List performanceImageModifyResponses
+ ) {
+ log.debug("Creating PerformanceModifyResponse for performanceId: {}", performance.getId());
+ PerformanceModifyResponse response = PerformanceModifyResponse.of(
+ performance.getUsers().getId(),
+ performance.getId(),
+ performance.getPerformanceTitle(),
+ performance.getGenre(),
+ performance.getRunningTime(),
+ performance.getPerformanceDescription(),
+ performance.getPerformanceAttentionNote(),
+ performance.getBankName(),
+ performance.getAccountNumber(),
+ performance.getAccountHolder(),
+ performance.getPosterImage(),
+ performance.getPerformanceTeamName(),
+ performance.getPerformanceVenue(),
+ performance.getPerformanceContact(),
+ performance.getPerformancePeriod(),
+ performance.getTicketPrice(),
+ performance.getTotalScheduleCount(),
+ scheduleModifyResponses,
+ castModifyResponses,
+ staffModifyResponses,
+ performanceImageModifyResponses
+ );
+ log.info("PerformanceModifyResponse created successfully for performanceId: {}", performance.getId());
+ return response;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/beat/domain/performance/application/PerformanceService.java b/src/main/java/com/beat/domain/performance/application/PerformanceService.java
index 1907a176..e1352dda 100644
--- a/src/main/java/com/beat/domain/performance/application/PerformanceService.java
+++ b/src/main/java/com/beat/domain/performance/application/PerformanceService.java
@@ -1,25 +1,43 @@
package com.beat.domain.performance.application;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
import com.beat.domain.booking.dao.BookingRepository;
+import com.beat.domain.cast.dao.CastRepository;
import com.beat.domain.cast.domain.Cast;
import com.beat.domain.member.dao.MemberRepository;
import com.beat.domain.member.domain.Member;
import com.beat.domain.member.exception.MemberErrorCode;
-import com.beat.domain.performance.application.dto.*;
+import com.beat.domain.performance.application.dto.bookingPerformanceDetail.BookingPerformanceDetailResponse;
+import com.beat.domain.performance.application.dto.bookingPerformanceDetail.BookingPerformanceDetailScheduleResponse;
import com.beat.domain.performance.application.dto.create.CastResponse;
+import com.beat.domain.performance.application.dto.create.PerformanceImageResponse;
import com.beat.domain.performance.application.dto.create.ScheduleResponse;
import com.beat.domain.performance.application.dto.create.StaffResponse;
-import com.beat.domain.performance.application.dto.home.HomePerformanceDetail;
-import com.beat.domain.performance.application.dto.home.HomePromotionDetail;
-import com.beat.domain.performance.application.dto.home.HomeRequest;
-import com.beat.domain.performance.application.dto.home.HomeResponse;
+import com.beat.domain.performance.application.dto.makerPerformance.MakerPerformanceDetailResponse;
+import com.beat.domain.performance.application.dto.makerPerformance.MakerPerformanceResponse;
+import com.beat.domain.performance.application.dto.modify.PerformanceModifyDetailResponse;
+import com.beat.domain.performance.application.dto.performanceDetail.PerformanceDetailCastResponse;
+import com.beat.domain.performance.application.dto.performanceDetail.PerformanceDetailImageResponse;
+import com.beat.domain.performance.application.dto.performanceDetail.PerformanceDetailResponse;
+import com.beat.domain.performance.application.dto.performanceDetail.PerformanceDetailScheduleResponse;
+import com.beat.domain.performance.application.dto.performanceDetail.PerformanceDetailStaffResponse;
+import com.beat.domain.performance.dao.PerformanceImageRepository;
import com.beat.domain.performance.dao.PerformanceRepository;
import com.beat.domain.performance.domain.Performance;
+import com.beat.domain.performance.domain.PerformanceImage;
import com.beat.domain.performance.exception.PerformanceErrorCode;
-import com.beat.domain.promotion.dao.PromotionRepository;
-import com.beat.domain.promotion.domain.Promotion;
+import com.beat.domain.performance.port.in.PerformanceUseCase;
import com.beat.domain.schedule.application.ScheduleService;
import com.beat.domain.schedule.dao.ScheduleRepository;
-import com.beat.domain.cast.dao.CastRepository;
import com.beat.domain.schedule.domain.Schedule;
import com.beat.domain.staff.dao.StaffRepository;
import com.beat.domain.staff.domain.Staff;
@@ -28,276 +46,189 @@
import com.beat.domain.user.exception.UserErrorCode;
import com.beat.global.common.exception.ForbiddenException;
import com.beat.global.common.exception.NotFoundException;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.temporal.ChronoUnit;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+@Slf4j
@Service
@RequiredArgsConstructor
-public class PerformanceService {
- private final PerformanceRepository performanceRepository;
- private final ScheduleRepository scheduleRepository;
- private final CastRepository castRepository;
- private final StaffRepository staffRepository;
- private final ScheduleService scheduleService;
- private final PromotionRepository promotionRepository;
- private final MemberRepository memberRepository;
- private final UserRepository userRepository;
- private final BookingRepository bookingRepository;
-
- @Transactional(readOnly = true)
- public PerformanceDetailResponse getPerformanceDetail(Long performanceId) {
- Performance performance = performanceRepository.findById(performanceId)
- .orElseThrow(() -> new NotFoundException(PerformanceErrorCode.PERFORMANCE_NOT_FOUND));
-
- List scheduleList = scheduleRepository.findByPerformanceId(performanceId).stream()
- .map(schedule -> PerformanceDetailSchedule.of(
- schedule.getId(),
- schedule.getPerformanceDate(),
- schedule.getScheduleNumber().name()
- )).collect(Collectors.toList());
-
- List castList = castRepository.findByPerformanceId(performanceId).stream()
- .map(cast -> PerformanceDetailCast.of(
- cast.getId(),
- cast.getCastName(),
- cast.getCastRole(),
- cast.getCastPhoto()
- )).collect(Collectors.toList());
-
- List staffList = staffRepository.findByPerformanceId(performanceId).stream()
- .map(staff -> PerformanceDetailStaff.of(
- staff.getId(),
- staff.getStaffName(),
- staff.getStaffRole(),
- staff.getStaffPhoto()
- )).collect(Collectors.toList());
-
- return PerformanceDetailResponse.of(
- performance.getId(),
- performance.getPerformanceTitle(),
- performance.getPerformancePeriod(),
- scheduleList,
- performance.getTicketPrice(),
- performance.getGenre().name(),
- performance.getPosterImage(),
- performance.getRunningTime(),
- performance.getPerformanceVenue(),
- performance.getPerformanceDescription(),
- performance.getPerformanceAttentionNote(),
- performance.getPerformanceContact(),
- performance.getPerformanceTeamName(),
- castList,
- staffList
- );
- }
-
- @Transactional
- public BookingPerformanceDetailResponse getBookingPerformanceDetail(Long performanceId) {
- Performance performance = performanceRepository.findById(performanceId)
- .orElseThrow(() -> new NotFoundException(PerformanceErrorCode.PERFORMANCE_NOT_FOUND));
-
- List scheduleList = scheduleRepository.findByPerformanceId(performanceId).stream()
- .map(schedule -> {
- scheduleService.updateBookingStatus(schedule);
- return BookingPerformanceDetailSchedule.of(
- schedule.getId(),
- schedule.getPerformanceDate(),
- schedule.getScheduleNumber().name(),
- scheduleService.getAvailableTicketCount(schedule),
- schedule.isBooking()
- );
- }).collect(Collectors.toList());
-
- return BookingPerformanceDetailResponse.of(
- performance.getId(),
- performance.getPerformanceTitle(),
- performance.getPerformancePeriod(),
- scheduleList,
- performance.getTicketPrice(),
- performance.getGenre().name(),
- performance.getPosterImage(),
- performance.getPerformanceVenue(),
- performance.getPerformanceTeamName(),
- performance.getBankName() != null ? performance.getBankName().name() : null,
- performance.getAccountNumber(),
- performance.getAccountHolder()
- );
- }
-
- @Transactional(readOnly = true)
- public HomeResponse getHomePerformanceList(HomeRequest homeRequest) {
- List performances;
-
- if (homeRequest.genre() != null) {
- performances = performanceRepository.findByGenre(homeRequest.genre());
- } else {
- performances = performanceRepository.findAll();
- }
-
- if (performances.isEmpty()) {
- List promotions = getPromotions();
- return HomeResponse.of(promotions, new ArrayList<>());
- }
-
- List performanceDetails = performances.stream()
- .map(performance -> {
- List schedules = scheduleRepository.findByPerformanceId(performance.getId());
- int minDueDate = scheduleService.getMinDueDate(schedules);
-
- return HomePerformanceDetail.of(
- performance.getId(),
- performance.getPerformanceTitle(),
- performance.getPerformancePeriod(),
- performance.getTicketPrice(),
- minDueDate,
- performance.getGenre().name(),
- performance.getPosterImage(),
- performance.getPerformanceVenue()
- );
- })
- .collect(Collectors.toList());
-
- // 두 개의 스트림을 각각 처리하여 병합
- List positiveDueDates = performanceDetails.stream()
- .filter(detail -> detail.dueDate() >= 0)
- .sorted((p1, p2) -> Integer.compare(p1.dueDate(), p2.dueDate()))
- .collect(Collectors.toList());
-
- List negativeDueDates = performanceDetails.stream()
- .filter(detail -> detail.dueDate() < 0)
- .sorted((p1, p2) -> Integer.compare(p2.dueDate(), p1.dueDate()))
- .collect(Collectors.toList());
-
- // 병합된 리스트
- positiveDueDates.addAll(negativeDueDates);
-
- List promotions = getPromotions();
-
- return HomeResponse.of(promotions, positiveDueDates);
- }
-
- private List getPromotions() {
- List promotionList = promotionRepository.findAll();
- return promotionList.stream()
- .map(promotion -> HomePromotionDetail.of(
- promotion.getId(),
- promotion.getPromotionPhoto(),
- promotion.getPerformance().getId()
- ))
- .collect(Collectors.toList());
- }
-
- @Transactional(readOnly = true)
- public MakerPerformanceResponse getMemberPerformances(Long memberId) {
- Member member = memberRepository.findById(memberId).orElseThrow(
- () -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND));
-
- Users user = userRepository.findById(member.getUser().getId()).orElseThrow(
- () -> new NotFoundException(UserErrorCode.USER_NOT_FOUND));
-
- List performances = performanceRepository.findByUsersId(user.getId());
-
- List performanceDetails = performances.stream()
- .map(performance -> MakerPerformanceDetail.of(
- performance.getId(),
- performance.getGenre().name(),
- performance.getPerformanceTitle(),
- performance.getPosterImage(),
- performance.getPerformancePeriod()
- ))
- .collect(Collectors.toList());
-
- return MakerPerformanceResponse.of(user.getId(), performanceDetails);
- }
-
- @Transactional
- public PerformanceEditResponse getPerformanceEdit(Long memberId, Long performanceId) {
- Member member = memberRepository.findById(memberId)
- .orElseThrow(() -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND));
-
- Long userId = member.getUser().getId();
-
- Performance performance = performanceRepository.findById(performanceId)
- .orElseThrow(() -> new NotFoundException(PerformanceErrorCode.PERFORMANCE_NOT_FOUND));
-
- if (!performance.getUsers().getId().equals(userId)) {
- throw new ForbiddenException(PerformanceErrorCode.NOT_PERFORMANCE_OWNER);
- }
-
- List scheduleIds = scheduleRepository.findIdsByPerformanceId(performanceId);
-
- boolean isBookerExist = bookingRepository.existsByScheduleIdIn(scheduleIds);
-
- List schedules = scheduleRepository.findAllByPerformanceId(performanceId);
- List casts = castRepository.findAllByPerformanceId(performanceId);
- List staffs = staffRepository.findAllByPerformanceId(performanceId);
-
- return mapToPerformanceEditResponse(performance, schedules, casts, staffs, isBookerExist);
- }
-
- private PerformanceEditResponse mapToPerformanceEditResponse(Performance performance, List schedules, List casts, List staffs, boolean isBookerExist) {
- List scheduleResponses = schedules.stream()
- .map(schedule -> ScheduleResponse.of(
- schedule.getId(),
- schedule.getPerformanceDate(),
- schedule.getTotalTicketCount(),
- calculateDueDate(schedule.getPerformanceDate()),
- schedule.getScheduleNumber()
- ))
- .collect(Collectors.toList());
-
- List castResponses = casts.stream()
- .map(cast -> CastResponse.of(
- cast.getId(),
- cast.getCastName(),
- cast.getCastRole(),
- cast.getCastPhoto()
- ))
- .collect(Collectors.toList());
-
- List staffResponses = staffs.stream()
- .map(staff -> StaffResponse.of(
- staff.getId(),
- staff.getStaffName(),
- staff.getStaffRole(),
- staff.getStaffPhoto()
- ))
- .collect(Collectors.toList());
-
- return PerformanceEditResponse.of(
- performance.getUsers().getId(),
- performance.getId(),
- performance.getPerformanceTitle(),
- performance.getGenre(),
- performance.getRunningTime(),
- performance.getPerformanceDescription(),
- performance.getPerformanceAttentionNote(),
- performance.getBankName(),
- performance.getAccountNumber(),
- performance.getAccountHolder(),
- performance.getPosterImage(),
- performance.getPerformanceTeamName(),
- performance.getPerformanceVenue(),
- performance.getPerformanceContact(),
- performance.getPerformancePeriod(),
- performance.getTicketPrice(),
- performance.getTotalScheduleCount(),
- isBookerExist,
- scheduleResponses,
- castResponses,
- staffResponses
- );
- }
-
- private int calculateDueDate(LocalDateTime performanceDate) {
- return (int) ChronoUnit.DAYS.between(LocalDate.now(), performanceDate.toLocalDate());
- }
+public class PerformanceService implements PerformanceUseCase {
+ private final PerformanceRepository performanceRepository;
+ private final ScheduleRepository scheduleRepository;
+ private final CastRepository castRepository;
+ private final StaffRepository staffRepository;
+ private final ScheduleService scheduleService;
+ private final MemberRepository memberRepository;
+ private final UserRepository userRepository;
+ private final BookingRepository bookingRepository;
+ private final PerformanceImageRepository performanceImageRepository;
+
+ @Transactional(readOnly = true)
+ public PerformanceDetailResponse getPerformanceDetail(Long performanceId) {
+ Performance performance = performanceRepository.findById(performanceId)
+ .orElseThrow(() -> new NotFoundException(PerformanceErrorCode.PERFORMANCE_NOT_FOUND));
+
+ List scheduleList = scheduleRepository.findByPerformanceId(performanceId)
+ .stream()
+ .map(schedule -> {
+ int dueDate = scheduleService.calculateDueDate(schedule);
+ return PerformanceDetailScheduleResponse.of(schedule.getId(), schedule.getPerformanceDate(),
+ schedule.getScheduleNumber().name(), dueDate, schedule.isBooking());
+ })
+ .collect(Collectors.toList());
+
+ int minDueDate = scheduleService.getMinDueDate(scheduleRepository.findAllByPerformanceId(performanceId));
+
+ List castList = castRepository.findByPerformanceId(performanceId)
+ .stream()
+ .map(cast -> PerformanceDetailCastResponse.of(cast.getId(), cast.getCastName(), cast.getCastRole(),
+ cast.getCastPhoto()))
+ .collect(Collectors.toList());
+
+ List staffList = staffRepository.findByPerformanceId(performanceId)
+ .stream()
+ .map(staff -> PerformanceDetailStaffResponse.of(staff.getId(), staff.getStaffName(), staff.getStaffRole(),
+ staff.getStaffPhoto()))
+ .collect(Collectors.toList());
+
+ List performanceImageList = performanceImageRepository.findAllByPerformanceId(
+ performanceId)
+ .stream()
+ .map(image -> PerformanceDetailImageResponse.of(image.getId(), image.getPerformanceImage()))
+ .collect(Collectors.toList());
+
+ log.info("Successfully completed getPerformanceDetail for performanceId: {}", performanceId);
+ return PerformanceDetailResponse.of(performance.getId(), performance.getPerformanceTitle(),
+ performance.getPerformancePeriod(), scheduleList, performance.getTicketPrice(),
+ performance.getGenre().name(), performance.getPosterImage(), performance.getRunningTime(),
+ performance.getPerformanceVenue(), performance.getPerformanceDescription(),
+ performance.getPerformanceAttentionNote(), performance.getPerformanceContact(),
+ performance.getPerformanceTeamName(), castList, staffList, minDueDate, performanceImageList);
+ }
+
+ @Transactional(readOnly = true)
+ public BookingPerformanceDetailResponse getBookingPerformanceDetail(Long performanceId) {
+ Performance performance = performanceRepository.findById(performanceId)
+ .orElseThrow(() -> new NotFoundException(PerformanceErrorCode.PERFORMANCE_NOT_FOUND));
+
+ List scheduleList = scheduleRepository.findByPerformanceId(
+ performanceId).stream().map(schedule -> {
+ int dueDate = scheduleService.calculateDueDate(schedule);
+ return BookingPerformanceDetailScheduleResponse.of(schedule.getId(), schedule.getPerformanceDate(),
+ schedule.getScheduleNumber().name(), scheduleService.getAvailableTicketCount(schedule),
+ schedule.isBooking(), dueDate);
+ }).collect(Collectors.toList());
+
+ return BookingPerformanceDetailResponse.of(performance.getId(), performance.getPerformanceTitle(),
+ performance.getPerformancePeriod(), scheduleList, performance.getTicketPrice(),
+ performance.getGenre().name(), performance.getPosterImage(), performance.getPerformanceVenue(),
+ performance.getPerformanceTeamName(),
+ performance.getBankName() != null ? performance.getBankName().name() : null, performance.getAccountNumber(),
+ performance.getAccountHolder());
+ }
+
+ @Transactional(readOnly = true)
+ public MakerPerformanceResponse getMemberPerformances(Long memberId) {
+ Member member = memberRepository.findById(memberId)
+ .orElseThrow(() -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND));
+
+ Users user = userRepository.findById(member.getUser().getId())
+ .orElseThrow(() -> new NotFoundException(UserErrorCode.USER_NOT_FOUND));
+
+ List performances = performanceRepository.findByUsersId(user.getId());
+
+ List performanceDetails = performances.stream().map(performance -> {
+ List schedules = scheduleRepository.findByPerformanceId(performance.getId());
+ int minDueDate = scheduleService.getMinDueDate(schedules);
+
+ return MakerPerformanceDetailResponse.of(performance.getId(), performance.getGenre().name(),
+ performance.getPerformanceTitle(), performance.getPosterImage(), performance.getPerformancePeriod(),
+ minDueDate);
+ }).collect(Collectors.toList());
+
+ List positiveDueDates = performanceDetails.stream()
+ .filter(detail -> detail.minDueDate() >= 0)
+ .sorted(Comparator.comparingInt(MakerPerformanceDetailResponse::minDueDate))
+ .collect(Collectors.toList());
+
+ List negativeDueDates = performanceDetails.stream()
+ .filter(detail -> detail.minDueDate() < 0)
+ .sorted(Comparator.comparingInt(MakerPerformanceDetailResponse::minDueDate).reversed())
+ .collect(Collectors.toList());
+
+ positiveDueDates.addAll(negativeDueDates);
+
+ return MakerPerformanceResponse.of(user.getId(), positiveDueDates);
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Performance findById(Long performanceId) {
+ return performanceRepository.findById(performanceId)
+ .orElseThrow(() -> new NotFoundException(PerformanceErrorCode.PERFORMANCE_NOT_FOUND));
+ }
+
+ @Transactional
+ public PerformanceModifyDetailResponse getPerformanceEdit(Long memberId, Long performanceId) {
+ Member member = memberRepository.findById(memberId)
+ .orElseThrow(() -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND));
+
+ Long userId = member.getUser().getId();
+
+ Performance performance = performanceRepository.findById(performanceId)
+ .orElseThrow(() -> new NotFoundException(PerformanceErrorCode.PERFORMANCE_NOT_FOUND));
+
+ if (!performance.getUsers().getId().equals(userId)) {
+ throw new ForbiddenException(PerformanceErrorCode.NOT_PERFORMANCE_OWNER);
+ }
+
+ List scheduleIds = scheduleRepository.findIdsByPerformanceId(performanceId);
+
+ boolean isBookerExist = bookingRepository.existsByScheduleIdIn(scheduleIds);
+
+ List schedules = scheduleRepository.findAllByPerformanceId(performanceId);
+ List casts = castRepository.findAllByPerformanceId(performanceId);
+ List staffs = staffRepository.findAllByPerformanceId(performanceId);
+ List performanceImages = performanceImageRepository.findAllByPerformanceId(performanceId);
+
+ return mapToPerformanceEditResponse(performance, schedules, casts, staffs, performanceImages, isBookerExist);
+ }
+
+ private PerformanceModifyDetailResponse mapToPerformanceEditResponse(Performance performance,
+ List schedules, List casts, List staffs, List performanceImages,
+ boolean isBookerExist) {
+ List scheduleResponses = schedules.stream()
+ .map(schedule -> ScheduleResponse.of(schedule.getId(), schedule.getPerformanceDate(),
+ schedule.getTotalTicketCount(), calculateDueDate(schedule.getPerformanceDate()),
+ schedule.getScheduleNumber()))
+ .collect(Collectors.toList());
+
+ List castResponses = casts.stream()
+ .map(cast -> CastResponse.of(cast.getId(), cast.getCastName(), cast.getCastRole(), cast.getCastPhoto()))
+ .collect(Collectors.toList());
+
+ List staffResponses = staffs.stream()
+ .map(staff -> StaffResponse.of(staff.getId(), staff.getStaffName(), staff.getStaffRole(),
+ staff.getStaffPhoto()))
+ .collect(Collectors.toList());
+
+ List performanceImageResponses = performanceImages.stream()
+ .map(performanceImage -> PerformanceImageResponse.of(performanceImage.getId(),
+ performanceImage.getPerformanceImage()))
+ .collect(Collectors.toList());
+
+ return PerformanceModifyDetailResponse.of(performance.getUsers().getId(), performance.getId(),
+ performance.getPerformanceTitle(), performance.getGenre(), performance.getRunningTime(),
+ performance.getPerformanceDescription(), performance.getPerformanceAttentionNote(),
+ performance.getBankName(), performance.getAccountNumber(), performance.getAccountHolder(),
+ performance.getPosterImage(), performance.getPerformanceTeamName(), performance.getPerformanceVenue(),
+ performance.getPerformanceContact(), performance.getPerformancePeriod(), performance.getTicketPrice(),
+ performance.getTotalScheduleCount(), isBookerExist, scheduleResponses, castResponses, staffResponses,
+ performanceImageResponses);
+ }
+
+ private int calculateDueDate(LocalDateTime performanceDate) {
+ return (int)ChronoUnit.DAYS.between(LocalDate.now(), performanceDate.toLocalDate());
+ }
}
diff --git a/src/main/java/com/beat/domain/performance/application/PerformanceUpdateService.java b/src/main/java/com/beat/domain/performance/application/PerformanceUpdateService.java
deleted file mode 100644
index 70a4c993..00000000
--- a/src/main/java/com/beat/domain/performance/application/PerformanceUpdateService.java
+++ /dev/null
@@ -1,165 +0,0 @@
-package com.beat.domain.performance.application;
-
-import com.beat.domain.cast.dao.CastRepository;
-import com.beat.domain.cast.domain.Cast;
-import com.beat.domain.cast.exception.CastErrorCode;
-import com.beat.domain.member.dao.MemberRepository;
-import com.beat.domain.member.exception.MemberErrorCode;
-import com.beat.domain.performance.application.dto.update.*;
-import com.beat.domain.performance.dao.PerformanceRepository;
-import com.beat.domain.performance.domain.Performance;
-import com.beat.domain.performance.exception.PerformanceErrorCode;
-import com.beat.domain.schedule.dao.ScheduleRepository;
-import com.beat.domain.schedule.domain.Schedule;
-import com.beat.domain.schedule.domain.ScheduleNumber;
-import com.beat.domain.schedule.exception.ScheduleErrorCode;
-import com.beat.domain.staff.dao.StaffRepository;
-import com.beat.domain.staff.domain.Staff;
-import com.beat.domain.staff.exception.StaffErrorCode;
-import com.beat.global.common.exception.NotFoundException;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.temporal.ChronoUnit;
-import java.util.List;
-import java.util.stream.Collectors;
-
-@Service
-@RequiredArgsConstructor
-public class PerformanceUpdateService {
- private final PerformanceRepository performanceRepository;
- private final ScheduleRepository scheduleRepository;
- private final MemberRepository memberRepository;
- private final CastRepository castRepository;
- private final StaffRepository staffRepository;
-
- @Transactional
- public PerformanceUpdateResponse updatePerformance(Long memberId, PerformanceUpdateRequest request) {
- memberRepository.findById(memberId).orElseThrow(() -> new NotFoundException(MemberErrorCode.MEMBER_NOT_FOUND));
-
- Performance performance = performanceRepository.findById(request.performanceId())
- .orElseThrow(() -> new NotFoundException(PerformanceErrorCode.PERFORMANCE_NOT_FOUND));
-
- performance.update(
- request.performanceTitle(),
- request.genre(),
- request.runningTime(),
- request.performanceDescription(),
- request.performanceAttentionNote(),
- request.bankName(),
- request.accountNumber(),
- request.accountHolder(),
- request.posterImage(),
- request.performanceTeamName(),
- request.performanceVenue(),
- request.performanceContact(),
- request.performancePeriod(),
- request.totalScheduleCount()
- );
- performanceRepository.save(performance);
-
- List schedules = request.scheduleList().stream()
- .map(scheduleRequest -> {
- Schedule schedule = scheduleRepository.findById(scheduleRequest.scheduleId())
- .orElseThrow(() -> new NotFoundException(ScheduleErrorCode.NO_SCHEDULE_FOUND));
- schedule.update(
- scheduleRequest.performanceDate(),
- scheduleRequest.totalTicketCount(),
- ScheduleNumber.valueOf(scheduleRequest.scheduleNumber())
- );
- return schedule;
- })
- .collect(Collectors.toList());
- scheduleRepository.saveAll(schedules);
-
- List casts = request.castList().stream()
- .map(castRequest -> {
- Cast cast = castRepository.findById(castRequest.castId())
- .orElseThrow(() -> new NotFoundException(CastErrorCode.CAST_NOT_FOUND));
- cast.update(
- castRequest.castName(),
- castRequest.castRole(),
- castRequest.castPhoto()
- );
- return cast;
- })
- .collect(Collectors.toList());
- castRepository.saveAll(casts);
-
- List staffs = request.staffList().stream()
- .map(staffRequest -> {
- Staff staff = staffRepository.findById(staffRequest.staffId())
- .orElseThrow(() -> new NotFoundException(StaffErrorCode.STAFF_NOT_FOUND));
- staff.update(
- staffRequest.staffName(),
- staffRequest.staffRole(),
- staffRequest.staffPhoto()
- );
- return staff;
- })
- .collect(Collectors.toList());
- staffRepository.saveAll(staffs);
-
- return mapToPerformanceResponse(performance, schedules, casts, staffs);
- }
-
- private PerformanceUpdateResponse mapToPerformanceResponse(Performance performance, List schedules, List casts, List staffs) {
- List scheduleResponses = schedules.stream()
- .map(schedule -> ScheduleUpdateResponse.of(
- schedule.getId(),
- schedule.getPerformanceDate(),
- schedule.getTotalTicketCount(),
- calculateDueDate(schedule.getPerformanceDate()),
- schedule.getScheduleNumber()
- ))
- .collect(Collectors.toList());
-
- List castResponses = casts.stream()
- .map(cast -> CastUpdateResponse.of(
- cast.getId(),
- cast.getCastName(),
- cast.getCastRole(),
- cast.getCastPhoto()
- ))
- .collect(Collectors.toList());
-
- List staffResponses = staffs.stream()
- .map(staff -> StaffUpdateResponse.of(
- staff.getId(),
- staff.getStaffName(),
- staff.getStaffRole(),
- staff.getStaffPhoto()
- ))
- .collect(Collectors.toList());
-
- return PerformanceUpdateResponse.of(
- performance.getUsers().getId(),
- performance.getId(),
- performance.getPerformanceTitle(),
- performance.getGenre(),
- performance.getRunningTime(),
- performance.getPerformanceDescription(),
- performance.getPerformanceAttentionNote(),
- performance.getBankName(),
- performance.getAccountNumber(),
- performance.getAccountHolder(),
- performance.getPosterImage(),
- performance.getPerformanceTeamName(),
- performance.getPerformanceVenue(),
- performance.getPerformanceContact(),
- performance.getPerformancePeriod(),
- performance.getTicketPrice(),
- performance.getTotalScheduleCount(),
- scheduleResponses,
- castResponses,
- staffResponses
- );
- }
-
- private int calculateDueDate(LocalDateTime performanceDate) {
- return (int) ChronoUnit.DAYS.between(LocalDate.now(), performanceDate.toLocalDate());
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/beat/domain/performance/application/dto/BookingPerformanceDetailSchedule.java b/src/main/java/com/beat/domain/performance/application/dto/BookingPerformanceDetailSchedule.java
deleted file mode 100644
index 0cbe3616..00000000
--- a/src/main/java/com/beat/domain/performance/application/dto/BookingPerformanceDetailSchedule.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.beat.domain.performance.application.dto;
-
-import java.time.LocalDateTime;
-
-public record BookingPerformanceDetailSchedule(
- Long scheduleId,
- LocalDateTime performanceDate,
- String scheduleNumber,
- int availableTicketCount,
- boolean isBooking
-) {
- public static BookingPerformanceDetailSchedule of(Long scheduleId, LocalDateTime performanceDate, String scheduleNumber, int availableTicketCount, boolean isBooking) {
- return new BookingPerformanceDetailSchedule(scheduleId, performanceDate, scheduleNumber, availableTicketCount, isBooking);
- }
-
-}
diff --git a/src/main/java/com/beat/domain/performance/application/dto/MakerPerformanceDetail.java b/src/main/java/com/beat/domain/performance/application/dto/MakerPerformanceDetail.java
deleted file mode 100644
index 21dcaa61..00000000
--- a/src/main/java/com/beat/domain/performance/application/dto/MakerPerformanceDetail.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.beat.domain.performance.application.dto;
-
-public record MakerPerformanceDetail(
- Long performanceId,
- String genre,
- String performanceTitle,
- String posterImage,
- String performancePeriod
-) {
- public static MakerPerformanceDetail of(
- Long performanceId,
- String genre,
- String performanceTitle,
- String posterImage,
- String performancePeriod) {
- return new MakerPerformanceDetail(performanceId, genre, performanceTitle, posterImage, performancePeriod);
- }
-}
diff --git a/src/main/java/com/beat/domain/performance/application/dto/PerformanceDetailCast.java b/src/main/java/com/beat/domain/performance/application/dto/PerformanceDetailCast.java
deleted file mode 100644
index 2ce813eb..00000000
--- a/src/main/java/com/beat/domain/performance/application/dto/PerformanceDetailCast.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.beat.domain.performance.application.dto;
-
-public record PerformanceDetailCast(
- Long castId,
- String castName,
- String castRole,
- String castPhoto
-) {
- public static PerformanceDetailCast of(Long castId, String castName, String castRole, String castPhoto) {
- return new PerformanceDetailCast(castId, castName, castRole, castPhoto);
- }
-}
diff --git a/src/main/java/com/beat/domain/performance/application/dto/PerformanceDetailResponse.java b/src/main/java/com/beat/domain/performance/application/dto/PerformanceDetailResponse.java
deleted file mode 100644
index a8f48a30..00000000
--- a/src/main/java/com/beat/domain/performance/application/dto/PerformanceDetailResponse.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.beat.domain.performance.application.dto;
-
-import java.util.List;
-
-public record PerformanceDetailResponse(
- Long performanceId,
- String performanceTitle,
- String performancePeriod,
- List scheduleList,
- int ticketPrice,
- String genre,
- String posterImage,
- int runningTime,
- String performanceVenue,
- String performanceDescription,
- String performanceAttentionNote,
- String performanceContact,
- String performanceTeamName,
- List castList,
- List staffList
-) {
- public static PerformanceDetailResponse of(
- Long performanceId,
- String performanceTitle,
- String performancePeriod,
- List scheduleList,
- int ticketPrice,
- String genre,
- String posterImage,
- int runningTime,
- String performanceVenue,
- String performanceDescription,
- String performanceAttentionNote,
- String performanceContact,
- String performanceTeamName,
- List castList,
- List staffList
- ) {
- return new PerformanceDetailResponse(performanceId, performanceTitle, performancePeriod, scheduleList, ticketPrice, genre, posterImage, runningTime, performanceVenue, performanceDescription, performanceAttentionNote, performanceContact, performanceTeamName, castList, staffList);
- }
-
-
-}
diff --git a/src/main/java/com/beat/domain/performance/application/dto/PerformanceDetailSchedule.java b/src/main/java/com/beat/domain/performance/application/dto/PerformanceDetailSchedule.java
deleted file mode 100644
index dcfe54c7..00000000
--- a/src/main/java/com/beat/domain/performance/application/dto/PerformanceDetailSchedule.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.beat.domain.performance.application.dto;
-
-import java.time.LocalDateTime;
-
-public record PerformanceDetailSchedule(
- Long scheduleId,
- LocalDateTime performanceDate,
- String scheduleNumber
-) {
- public static PerformanceDetailSchedule of(Long scheduleId, LocalDateTime performanceDate, String scheduleNumber) {
- return new PerformanceDetailSchedule(scheduleId, performanceDate, scheduleNumber);
- }
-}
diff --git a/src/main/java/com/beat/domain/performance/application/dto/PerformanceDetailStaff.java b/src/main/java/com/beat/domain/performance/application/dto/PerformanceDetailStaff.java
deleted file mode 100644
index cf088eaf..00000000
--- a/src/main/java/com/beat/domain/performance/application/dto/PerformanceDetailStaff.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.beat.domain.performance.application.dto;
-
-public record PerformanceDetailStaff(
- Long staffId,
- String staffName,
- String staffRole,
- String staffPhoto
-) {
- public static PerformanceDetailStaff of(Long staffId, String staffName, String staffRole, String staffPhoto) {
- return new PerformanceDetailStaff(staffId, staffName, staffRole, staffPhoto);
- }
-}
diff --git a/src/main/java/com/beat/domain/performance/application/dto/BookingPerformanceDetailResponse.java b/src/main/java/com/beat/domain/performance/application/dto/bookingPerformanceDetail/BookingPerformanceDetailResponse.java
similarity index 82%
rename from src/main/java/com/beat/domain/performance/application/dto/BookingPerformanceDetailResponse.java
rename to src/main/java/com/beat/domain/performance/application/dto/bookingPerformanceDetail/BookingPerformanceDetailResponse.java
index 24dcf02c..d42afafa 100644
--- a/src/main/java/com/beat/domain/performance/application/dto/BookingPerformanceDetailResponse.java
+++ b/src/main/java/com/beat/domain/performance/application/dto/bookingPerformanceDetail/BookingPerformanceDetailResponse.java
@@ -1,4 +1,4 @@
-package com.beat.domain.performance.application.dto;
+package com.beat.domain.performance.application.dto.bookingPerformanceDetail;
import java.util.List;
@@ -6,7 +6,7 @@ public record BookingPerformanceDetailResponse(
Long performanceId,
String performanceTitle,
String performancePeriod,
- List scheduleList,
+ List scheduleList,
int ticketPrice,
String genre,
String posterImage,
@@ -20,7 +20,7 @@ public static BookingPerformanceDetailResponse of(
Long performanceId,
String performanceTitle,
String performancePeriod,
- List scheduleList,
+ List scheduleList,
int ticketPrice,
String genre,
String posterImage,
diff --git a/src/main/java/com/beat/domain/performance/application/dto/bookingPerformanceDetail/BookingPerformanceDetailScheduleResponse.java b/src/main/java/com/beat/domain/performance/application/dto/bookingPerformanceDetail/BookingPerformanceDetailScheduleResponse.java
new file mode 100644
index 00000000..4181e1bb
--- /dev/null
+++ b/src/main/java/com/beat/domain/performance/application/dto/bookingPerformanceDetail/BookingPerformanceDetailScheduleResponse.java
@@ -0,0 +1,17 @@
+package com.beat.domain.performance.application.dto.bookingPerformanceDetail;
+
+import java.time.LocalDateTime;
+
+public record BookingPerformanceDetailScheduleResponse(
+ Long scheduleId,
+ LocalDateTime performanceDate,
+ String scheduleNumber,
+ int availableTicketCount,
+ boolean isBooking,
+ int dueDate
+) {
+ public static BookingPerformanceDetailScheduleResponse of(Long scheduleId, LocalDateTime performanceDate, String scheduleNumber, int availableTicketCount, boolean isBooking, int dueDate) {
+ return new BookingPerformanceDetailScheduleResponse(scheduleId, performanceDate, scheduleNumber, availableTicketCount, isBooking, dueDate);
+ }
+
+}
diff --git a/src/main/java/com/beat/domain/performance/application/dto/create/PerformanceImageRequest.java b/src/main/java/com/beat/domain/performance/application/dto/create/PerformanceImageRequest.java
new file mode 100644
index 00000000..6081829a
--- /dev/null
+++ b/src/main/java/com/beat/domain/performance/application/dto/create/PerformanceImageRequest.java
@@ -0,0 +1,6 @@
+package com.beat.domain.performance.application.dto.create;
+
+public record PerformanceImageRequest(
+ String performanceImage
+) {
+}
diff --git a/src/main/java/com/beat/domain/performance/application/dto/create/PerformanceImageResponse.java b/src/main/java/com/beat/domain/performance/application/dto/create/PerformanceImageResponse.java
new file mode 100644
index 00000000..abf14ff3
--- /dev/null
+++ b/src/main/java/com/beat/domain/performance/application/dto/create/PerformanceImageResponse.java
@@ -0,0 +1,16 @@
+package com.beat.domain.performance.application.dto.create;
+
+public record PerformanceImageResponse(
+ Long imageId,
+ String imageUrl
+) {
+ public static PerformanceImageResponse of(
+ Long imageId,
+ String imageUrl
+ ) {
+ return new PerformanceImageResponse(
+ imageId,
+ imageUrl
+ );
+ }
+}
diff --git a/src/main/java/com/beat/domain/performance/application/dto/create/PerformanceRequest.java b/src/main/java/com/beat/domain/performance/application/dto/create/PerformanceRequest.java
index 1ae592f9..2663abf6 100644
--- a/src/main/java/com/beat/domain/performance/application/dto/create/PerformanceRequest.java
+++ b/src/main/java/com/beat/domain/performance/application/dto/create/PerformanceRequest.java
@@ -23,5 +23,6 @@ public record PerformanceRequest(
int totalScheduleCount,
List scheduleList,
List castList,
- List staffList
+ List staffList,
+ List performanceImageList
) {}
\ No newline at end of file
diff --git a/src/main/java/com/beat/domain/performance/application/dto/create/PerformanceResponse.java b/src/main/java/com/beat/domain/performance/application/dto/create/PerformanceResponse.java
index 9d34b8da..4fbbbf0b 100644
--- a/src/main/java/com/beat/domain/performance/application/dto/create/PerformanceResponse.java
+++ b/src/main/java/com/beat/domain/performance/application/dto/create/PerformanceResponse.java
@@ -25,7 +25,8 @@ public record PerformanceResponse(
int totalScheduleCount,
List scheduleList,
List castList,
- List staffList
+ List staffList,
+ List performanceImageList
) {
public static PerformanceResponse of(
Long userId,
@@ -47,7 +48,8 @@ public static PerformanceResponse of(
int totalScheduleCount,
List scheduleList,
List castList,
- List