diff --git a/.gitignore b/.gitignore index 1b795906..0f99195b 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,5 @@ logs/ docker/docker-compose.yml /docker/.env + +firebase-genti.json \ No newline at end of file diff --git a/genti-api/src/main/java/com/gt/genti/auth/api/AuthApi.java b/genti-api/src/main/java/com/gt/genti/auth/api/AuthApi.java new file mode 100644 index 00000000..1d877080 --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/auth/api/AuthApi.java @@ -0,0 +1,88 @@ +package com.gt.genti.auth.api; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import com.gt.genti.auth.dto.request.AppleLoginRequestDto; +import com.gt.genti.auth.dto.request.SocialAppLoginRequest; +import com.gt.genti.auth.dto.request.TokenRefreshRequestDto; +import com.gt.genti.auth.dto.response.OauthJwtResponse; +import com.gt.genti.auth.dto.response.SocialLoginResponse; +import com.gt.genti.error.ResponseCode; +import com.gt.genti.jwt.TokenResponse; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.swagger.EnumResponse; +import com.gt.genti.swagger.EnumResponses; +import com.gt.genti.user.model.OauthPlatform; +import com.gt.genti.user.model.UserRole; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.headers.Header; +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 jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +; + +@Tag(name = "[AuthController] 인증 컨트롤러", description = "로그인을 처리한 후 토큰을 전달합니다.") +public interface AuthApi { + @Operation(summary = "oauth 로그인 페이지 호출", description = "구글, 카카오 oauth로그인페이지 로 Redirect 됩니다. url은 HttpHeader에 포함되어있습니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "303", description = "리디렉션을 위한 응답 헤더를 포함합니다.", headers = { + @Header(name = "Location", description = "리디렉션 URL", schema = @Schema(type = "string")) + }, content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity login( + @Parameter(description = "호출할 Oauth platform 종류", example = "KAKAO", schema = @Schema(allowableValues = { + "KAKAO"})) + @RequestParam(name = "oauthPlatform") OauthPlatform oauthPlatform); + + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.AppleOauthClaimInvalid), + @EnumResponse(ResponseCode.AppleOauthIdTokenExpired), + @EnumResponse(ResponseCode.AppleOauthIdTokenIncorrect), + @EnumResponse(ResponseCode.AppleOauthIdTokenInvalid), + @EnumResponse(ResponseCode.AppleOauthJwtValueInvalid), + @EnumResponse(ResponseCode.AppleOauthPublicKeyInvalid), + }) + ResponseEntity> loginApple( + @RequestBody @Valid AppleLoginRequestDto request); + + ResponseEntity> kakaoLogin( + @RequestParam(name = "code") String code); + + @Deprecated + ResponseEntity> googleLogin( + @RequestParam(name = "code") String code); + + @Operation(summary = "테스트용 jwt 토큰 발급", description = "") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity> getTestJwt( + @NotNull @RequestParam(name = "role", value = "role") UserRole role); + + @Operation(summary = "oauth platform에서 로그인 후 받은 토큰을 전달하여 가입/로그인", description = "현재 애플, 카카오 지원합니다") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.AppleOauthClaimInvalid), + @EnumResponse(ResponseCode.AppleOauthIdTokenExpired), + @EnumResponse(ResponseCode.AppleOauthIdTokenIncorrect), + @EnumResponse(ResponseCode.AppleOauthIdTokenInvalid), + @EnumResponse(ResponseCode.AppleOauthJwtValueInvalid), + @EnumResponse(ResponseCode.AppleOauthPublicKeyInvalid), + }) + ResponseEntity> loginOrSignUpWithOAuthToken( + @RequestBody @Valid SocialAppLoginRequest socialAppLoginRequest); + + ResponseEntity> reissue( + @RequestBody @Valid TokenRefreshRequestDto tokenRefreshRequestDto + ); +} diff --git a/genti-api/src/main/java/com/gt/genti/auth/controller/AuthController.java b/genti-api/src/main/java/com/gt/genti/auth/controller/AuthController.java index d3dfa0a0..40e787c2 100644 --- a/genti-api/src/main/java/com/gt/genti/auth/controller/AuthController.java +++ b/genti-api/src/main/java/com/gt/genti/auth/controller/AuthController.java @@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import com.gt.genti.auth.api.AuthApi; import com.gt.genti.auth.dto.request.AppleLoginRequest; import com.gt.genti.auth.dto.request.AppleLoginRequestDto; import com.gt.genti.auth.dto.request.SocialAppLoginRequest; @@ -22,7 +23,6 @@ import com.gt.genti.auth.dto.response.OauthJwtResponse; import com.gt.genti.auth.dto.response.SocialLoginResponse; import com.gt.genti.auth.service.AuthService; -import com.gt.genti.error.ResponseCode; import com.gt.genti.jwt.JwtTokenProvider; import com.gt.genti.jwt.TokenGenerateCommand; import com.gt.genti.jwt.TokenResponse; @@ -30,19 +30,11 @@ import com.gt.genti.model.LogItem; import com.gt.genti.model.LogRequester; import com.gt.genti.model.Logging; -import com.gt.genti.swagger.EnumResponse; -import com.gt.genti.swagger.EnumResponses; -import com.gt.genti.user.model.AuthUser; import com.gt.genti.user.model.OauthPlatform; import com.gt.genti.user.model.UserRole; -import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.headers.Header; -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 jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; @@ -52,17 +44,11 @@ @Controller @RequiredArgsConstructor @RequestMapping("/auth/v1") -public class AuthController { +public class AuthController implements AuthApi { private final JwtTokenProvider jwtTokenProvider; private final AuthService authService; - @Operation(summary = "oauth 로그인 페이지 호출", description = "구글, 카카오 oauth로그인페이지 로 Redirect 됩니다. url은 HttpHeader에 포함되어있습니다.") - @ApiResponses(value = { - @ApiResponse(responseCode = "303", description = "리디렉션을 위한 응답 헤더를 포함합니다.", headers = { - @Header(name = "Location", description = "리디렉션 URL", schema = @Schema(type = "string")) - }, content = @Content(schema = @Schema(hidden = true))) - }) @GetMapping("/login/oauth2") @Logging(item = LogItem.OAUTH_WEB, action = LogAction.LOGIN, requester = LogRequester.ANONYMOUS) public ResponseEntity login( @@ -73,15 +59,6 @@ public ResponseEntity login( return new ResponseEntity<>(httpHeaders, HttpStatus.SEE_OTHER); } - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.AppleOauthClaimInvalid), - @EnumResponse(ResponseCode.AppleOauthIdTokenExpired), - @EnumResponse(ResponseCode.AppleOauthIdTokenIncorrect), - @EnumResponse(ResponseCode.AppleOauthIdTokenInvalid), - @EnumResponse(ResponseCode.AppleOauthJwtValueInvalid), - @EnumResponse(ResponseCode.AppleOauthPublicKeyInvalid), - }) @PostMapping("/login/oauth2/code/apple") @Logging(item = LogItem.OAUTH_APPLE, action = LogAction.LOGIN, requester = LogRequester.ANONYMOUS) public ResponseEntity> loginApple( @@ -89,9 +66,6 @@ public ResponseEntity> loginApple( return success(authService.webLogin(AppleLoginRequest.of(OauthPlatform.APPLE, request.getToken()))); } - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - }) @GetMapping("/login/oauth2/code/kakao") @Logging(item = LogItem.OAUTH_KAKAO, action = LogAction.LOGIN, requester = LogRequester.ANONYMOUS) public ResponseEntity> kakaoLogin( @@ -100,9 +74,6 @@ public ResponseEntity> kakaoLogin( } @Deprecated - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - }) @GetMapping("/login/oauth2/code/google") @Logging(item = LogItem.OAUTH_GOOGLE, action = LogAction.LOGIN, requester = LogRequester.ANONYMOUS) public ResponseEntity> googleLogin( @@ -110,14 +81,11 @@ public ResponseEntity> googleLogin( return success(authService.webLogin(SocialLoginRequestImpl.of(OauthPlatform.GOOGLE, code))); } - @Operation(summary = "테스트용 jwt 토큰 발급", description = "") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @GetMapping("/login/testjwt") public ResponseEntity> getTestJwt( @NotNull @RequestParam(name = "role", value = "role") UserRole role) { - Map userIdMapper = Map.of(UserRole.USER, "2", UserRole.ADMIN, "1", UserRole.CREATOR, "4", UserRole.OAUTH_FIRST_JOIN, String.valueOf(Long.MAX_VALUE)); + Map userIdMapper = Map.of(UserRole.USER, "2", UserRole.ADMIN, "1", UserRole.CREATOR, "4", + UserRole.OAUTH_FIRST_JOIN, String.valueOf(Long.MAX_VALUE)); String userId = userIdMapper.get(role); TokenGenerateCommand command = TokenGenerateCommand.builder() .userId(userId) @@ -129,16 +97,6 @@ public ResponseEntity> getTestJwt( TokenResponse.of(accessToken, refreshToken)); } - @Operation(summary = "oauth platform에서 로그인 후 받은 토큰을 전달하여 가입/로그인", description = "현재 애플, 카카오 지원합니다") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.AppleOauthClaimInvalid), - @EnumResponse(ResponseCode.AppleOauthIdTokenExpired), - @EnumResponse(ResponseCode.AppleOauthIdTokenIncorrect), - @EnumResponse(ResponseCode.AppleOauthIdTokenInvalid), - @EnumResponse(ResponseCode.AppleOauthJwtValueInvalid), - @EnumResponse(ResponseCode.AppleOauthPublicKeyInvalid), - }) @PostMapping("/login/oauth2/token") @Logging(item = LogItem.OAUTH_APP, action = LogAction.LOGIN, requester = LogRequester.ANONYMOUS) public ResponseEntity> loginOrSignUpWithOAuthToken( @@ -149,7 +107,7 @@ public ResponseEntity> loginOrSignUpWithOAuthToken( @PostMapping("/reissue") public ResponseEntity> reissue( @RequestBody @Valid TokenRefreshRequestDto tokenRefreshRequestDto - ){ + ) { return success(authService.reissue(tokenRefreshRequestDto)); } diff --git a/genti-api/src/main/java/com/gt/genti/creator/api/CreatorApi.java b/genti-api/src/main/java/com/gt/genti/creator/api/CreatorApi.java new file mode 100644 index 00000000..69100b62 --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/creator/api/CreatorApi.java @@ -0,0 +1,50 @@ +package com.gt.genti.creator.api; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; + +import com.gt.genti.creator.dto.request.AccountUpdateRequestDto; +import com.gt.genti.creator.dto.request.CreatorStatusUpdateRequestDto; +import com.gt.genti.creator.dto.response.CreatorFindResponseDto; +import com.gt.genti.creator.dto.response.CreatorStatusUpdateResponseDto; +import com.gt.genti.error.ResponseCode; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.swagger.AuthorizedCreator; +import com.gt.genti.swagger.EnumResponse; +import com.gt.genti.swagger.EnumResponses; +import com.gt.genti.user.model.AuthUser; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; + +@Tag(name = "[CreatorController] 공급자 정보 컨트롤러", description = "공급자의 정보를 조회,수정합니다.") +@AuthorizedCreator +public interface CreatorApi { + + @Operation(summary = "공급자 내정보 보기", description = "공급자의 내정보 보기") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.CreatorNotFound) + }) + ResponseEntity> getCreatorInfo( + @AuthUser Long userId); + + @Operation(summary = "공급자 계좌정보 수정", description = "공급자의 내 계좌정보 수정") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.CreatorNotFound) + }) + ResponseEntity> updateAccountInfo( + @AuthUser Long userId, + @RequestBody @Valid AccountUpdateRequestDto accountUpdateRequestDto); + + @Operation(summary = "공급자 작업가능상태 수정", description = "공급자의 내 작업가능상태 수정") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.CreatorNotFound) + }) + ResponseEntity> updateCreatorStatus( + @AuthUser Long userId, + @RequestBody @Valid CreatorStatusUpdateRequestDto creatorStatusUpdateRequestDto); +} diff --git a/genti-api/src/main/java/com/gt/genti/creator/controller/CreatorController.java b/genti-api/src/main/java/com/gt/genti/creator/controller/CreatorController.java index 3913811e..8cb1c47b 100644 --- a/genti-api/src/main/java/com/gt/genti/creator/controller/CreatorController.java +++ b/genti-api/src/main/java/com/gt/genti/creator/controller/CreatorController.java @@ -7,52 +7,37 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.gt.genti.creator.api.CreatorApi; import com.gt.genti.creator.dto.request.AccountUpdateRequestDto; import com.gt.genti.creator.dto.request.CreatorStatusUpdateRequestDto; import com.gt.genti.creator.dto.response.CreatorFindResponseDto; import com.gt.genti.creator.dto.response.CreatorStatusUpdateResponseDto; import com.gt.genti.creator.service.CreatorService; -import com.gt.genti.error.ResponseCode; import com.gt.genti.model.LogAction; import com.gt.genti.model.LogItem; import com.gt.genti.model.LogRequester; import com.gt.genti.model.Logging; import com.gt.genti.response.GentiResponse; import com.gt.genti.response.GentiResponse.ApiResult; -import com.gt.genti.swagger.EnumResponse; -import com.gt.genti.swagger.EnumResponses; import com.gt.genti.user.model.AuthUser; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -@Tag(name = "[CreatorController] 공급자 정보 컨트롤러", description = "공급자의 정보를 조회,수정합니다.") @RestController @RequestMapping("/api/v1/creators") @RequiredArgsConstructor -public class CreatorController { +public class CreatorController implements CreatorApi { private final CreatorService creatorService; - @Operation(summary = "공급자 내정보 보기", description = "공급자의 내정보 보기") @Logging(item = LogItem.CREATOR_MY, action = LogAction.VIEW, requester = LogRequester.CREATOR) - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.CreatorNotFound) - }) @GetMapping public ResponseEntity> getCreatorInfo( @AuthUser Long userId) { return GentiResponse.success(creatorService.getCreatorInfo(userId)); } - @Operation(summary = "공급자 계좌정보 수정", description = "공급자의 내 계좌정보 수정") @Logging(item = LogItem.CREATOR_ACCOUNT, action = LogAction.UPDATE, requester = LogRequester.CREATOR) - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.CreatorNotFound) - }) @PostMapping("/account") public ResponseEntity> updateAccountInfo( @AuthUser Long userId, @@ -60,12 +45,7 @@ public ResponseEntity> updateAccountInfo( return GentiResponse.success(creatorService.updateAccountInfo(userId, accountUpdateRequestDto)); } - @Operation(summary = "공급자 작업가능상태 수정", description = "공급자의 내 작업가능상태 수정") @Logging(item = LogItem.CREATOR_STATUS, action = LogAction.UPDATE, requester = LogRequester.CREATOR) - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.CreatorNotFound) - }) @PostMapping("/status") public ResponseEntity> updateCreatorStatus( @AuthUser Long userId, diff --git a/genti-api/src/main/java/com/gt/genti/picture/api/PictureApi.java b/genti-api/src/main/java/com/gt/genti/picture/api/PictureApi.java new file mode 100644 index 00000000..c857a274 --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/picture/api/PictureApi.java @@ -0,0 +1,37 @@ +package com.gt.genti.picture.api; + +import java.util.List; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; + +import com.gt.genti.aws.dto.PreSignedUrlRequestDto; +import com.gt.genti.aws.dto.PreSignedUrlResponseDto; +import com.gt.genti.error.ResponseCode; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.swagger.Authorized; +import com.gt.genti.swagger.EnumResponse; +import com.gt.genti.swagger.EnumResponses; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; + +@Authorized +@Tag(name = "[PictureController] 공통 사진 컨트롤러", description = "사진 업로드 url을 요청합니다.") +public interface PictureApi { + + @Operation(summary = "사진 업로드 url 1개 요청", description = "사진 1개의 업로드 url 요청") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity> getUploadUrl( + @RequestBody @Valid PreSignedUrlRequestDto preSignedUrlRequestDto); + + @Operation(summary = "사진 업로드 url 리스트 요청", description = "사진 여러개의 업로드 url 요청") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity>> getUploadUrls( + @RequestBody @Valid List requestDtoList); +} diff --git a/genti-api/src/main/java/com/gt/genti/picture/controller/PictureController.java b/genti-api/src/main/java/com/gt/genti/picture/controller/PictureController.java index e813dd95..b690a500 100644 --- a/genti-api/src/main/java/com/gt/genti/picture/controller/PictureController.java +++ b/genti-api/src/main/java/com/gt/genti/picture/controller/PictureController.java @@ -10,33 +10,24 @@ import com.gt.genti.aws.dto.PreSignedUrlRequestDto; import com.gt.genti.aws.dto.PreSignedUrlResponseDto; -import com.gt.genti.error.ResponseCode; import com.gt.genti.model.LogAction; import com.gt.genti.model.LogItem; import com.gt.genti.model.LogRequester; import com.gt.genti.model.Logging; +import com.gt.genti.picture.api.PictureApi; import com.gt.genti.picture.service.UploadUrlService; import com.gt.genti.response.GentiResponse; import com.gt.genti.response.GentiResponse.ApiResult; -import com.gt.genti.swagger.EnumResponse; -import com.gt.genti.swagger.EnumResponses; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -@Tag(name = "[PictureController] 공통 사진 컨트롤러", description = "사진 업로드 url을 요청합니다.") @RestController @RequestMapping("/api/v1/presigned-url") @RequiredArgsConstructor -public class PictureController { +public class PictureController implements PictureApi { private final UploadUrlService uploadUrlService; - @Operation(summary = "사진 업로드 url 1개 요청", description = "사진 1개의 업로드 url 요청") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @Logging(item = LogItem.PICTURE_PRESIGNED_URL, action = LogAction.GET, requester = LogRequester.ANONYMOUS) @PostMapping public ResponseEntity> getUploadUrl( @@ -44,10 +35,6 @@ public ResponseEntity> getUploadUrl( return GentiResponse.success(uploadUrlService.getUploadUrl(preSignedUrlRequestDto)); } - @Operation(summary = "사진 업로드 url 리스트 요청", description = "사진 여러개의 업로드 url 요청") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @Logging(item = LogItem.PICTURE_PRESIGNED_URL, action = LogAction.GET, requester = LogRequester.ANONYMOUS) @PostMapping("/many") public ResponseEntity>> getUploadUrls( diff --git a/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/api/AdminPGREQApi.java b/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/api/AdminPGREQApi.java new file mode 100644 index 00000000..25b32638 --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/api/AdminPGREQApi.java @@ -0,0 +1,82 @@ +package com.gt.genti.picturegeneraterequest.api; + +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestParam; + +import com.gt.genti.error.ResponseCode; +import com.gt.genti.picturegeneraterequest.dto.response.PGREQAdminMatchedDetailFindByAdminResponseDto; +import com.gt.genti.picturegeneraterequest.dto.response.PGREQCreatorSubmittedDetailFindByAdminResponseDto; +import com.gt.genti.picturegenerateresponse.service.mapper.PictureGenerateResponseStatusForAdmin; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.swagger.AuthorizedAdmin; +import com.gt.genti.swagger.EnumResponse; +import com.gt.genti.swagger.EnumResponses; +import com.gt.genti.validator.ValidEnum; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +@AuthorizedAdmin +@Tag(name = "[AdminPGREQController] 어드민 사진생성요청 컨트롤러", description = "사진생성요청을 조회") +public interface AdminPGREQApi { + + @Operation(summary = "어드민에게 매칭된 사진생성요청 전체조회", description = "사진생성요청 전체를 매칭대상(어드민,공급자), 응답의 상태를 조건으로 조회하고 페이지네이션 조회합니다.") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity>> getAllAdminMatchedPGREQ( + @Parameter(description = "페이지 번호 (0-based)", example = "0", required = true) + @RequestParam(name = "page", defaultValue = "0") @NotNull @Min(0) int page, + @Parameter(description = "페이지 당 요소 개수 >=1", example = "10", required = true) + @RequestParam(name = "size", defaultValue = "10") @NotNull @Min(1) int size, + @Parameter(description = "정렬 조건 - 기본값 생성일시", example = "createdAt", schema = @Schema(allowableValues = {"id", + "createdAt"})) + @RequestParam(name = "sortBy", defaultValue = "createdAt") String sortBy, + @Parameter(description = "정렬 방향 - 기본값 내림차순", example = "desc", schema = @Schema(allowableValues = {"acs", + "desc"})) + @RequestParam(name = "direction", defaultValue = "desc") String direction, + @Parameter(description = "현재 작업(사진생성응답)의 상태로 조회한다. ALL 조회 가능, EXPIRED는 MVP상 도메인로직엔 없지만 어드민페이지의 성격 상 필요하다고 생각하여 추가했습니다.", + examples = {@ExampleObject(name = "BEFORE_WORK", description = "작업 대기", value = "BEFORE_WORK"), + @ExampleObject(name = "IN_PROGRESS", description = "작업 중", value = "IN_PROGRESS"), + @ExampleObject(name = "COMPLETED", description = "작업 완료", value = "COMPLETED"), + @ExampleObject(name = "EXPIRED", description = "만료됨 (기존의 협의된 내용은 아니지만 어드민페이지에 필요하다고 생각했습니다. 추가 부탁드립니다.)", value = "EXPIRED"), + @ExampleObject(name = "ALL", description = "전체", value = "ALL")}) + @RequestParam(name = "status", defaultValue = "ALL") + @ValidEnum(value = PictureGenerateResponseStatusForAdmin.class, hasAllOption = true) String status, + @Parameter(description = "유저의 email") + @RequestParam(name = "email", required = false) @Email(message = "올바른 email 형식이 아닙니다.") String email + ); + + @Operation(summary = "공급자->어드민(얼굴붙여야하는 요청)", description = "사진생성요청 전체를 매칭대상(어드민,공급자), 응답의 상태를 조건으로 조회하고 페이지네이션 조회합니다.") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity>> getAllCreatorSubmittedPGREQ( + @Parameter(description = "페이지 번호 (0-based)", example = "0", required = true) + @RequestParam(name = "page", defaultValue = "0") @NotNull @Min(0) int page, + @Parameter(description = "페이지 당 요소 개수 >=1", example = "10", required = true) + @RequestParam(name = "size", defaultValue = "10") @NotNull @Min(1) int size, + @Parameter(description = "정렬 조건 - 기본값 생성일시", example = "createdAt", schema = @Schema(allowableValues = {"id", + "createdAt"})) + @RequestParam(name = "sortBy", defaultValue = "createdAt") String sortBy, + @Parameter(description = "정렬 방향 - 기본값 내림차순", example = "desc", schema = @Schema(allowableValues = {"acs", + "desc"})) + @RequestParam(name = "direction", defaultValue = "desc") String direction, + @Parameter(description = "현재 작업(사진생성응답)의 상태로 조회한다. ALL 조회 가능, EXPIRED는 MVP상 도메인로직엔 없지만 어드민페이지의 성격 상 필요하다고 생각하여 추가했습니다.", + examples = {@ExampleObject(name = "BEFORE_WORK", description = "작업 대기", value = "BEFORE_WORK"), + @ExampleObject(name = "IN_PROGRESS", description = "작업 중", value = "IN_PROGRESS"), + @ExampleObject(name = "COMPLETED", description = "작업 완료", value = "COMPLETED"), + @ExampleObject(name = "EXPIRED", description = "만료됨 (기존의 협의된 내용은 아니지만 어드민페이지에 필요하다고 생각했습니다. 추가 부탁드립니다.)", value = "EXPIRED"), + @ExampleObject(name = "ALL", description = "전체", value = "ALL")}) + @RequestParam(name = "status", defaultValue = "ALL") @ValidEnum(value = PictureGenerateResponseStatusForAdmin.class, hasAllOption = true) String status, + @Parameter(description = "유저의 email") + @RequestParam(name = "email", required = false) @Email(message = "올바른 email 형식이 아닙니다.") String email + ); +} diff --git a/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/api/CreatorPGREQApi.java b/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/api/CreatorPGREQApi.java new file mode 100644 index 00000000..91d10053 --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/api/CreatorPGREQApi.java @@ -0,0 +1,83 @@ +package com.gt.genti.picturegeneraterequest.api; + +import java.util.List; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; + +import com.gt.genti.error.ResponseCode; +import com.gt.genti.picturegeneraterequest.dto.response.PGREQBriefFindByCreatorResponseDto; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.swagger.AuthorizedCreator; +import com.gt.genti.swagger.EnumResponse; +import com.gt.genti.swagger.EnumResponses; +import com.gt.genti.user.model.AuthUser; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; + +@AuthorizedCreator +@Tag(name = "[CreatorPGREQController] 공급자의 사진생성요청 컨트롤러", description = "사진생성요청 관련 작업 api") +public interface CreatorPGREQApi { + + @Operation(summary = "내게 매칭된 요청 조회", description = "나(공급자)에게 매칭된 사진생성요청을 조회합니다. (수락할지 말지 선택하기위한 최소 정보 보여주기)") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.CreatorNotFound), + @EnumResponse(ResponseCode.PictureGenerateRequestNotFound) + }) + ResponseEntity> getAssignedPictureGenerateRequestBrief( + @AuthUser Long userId + ); + + @Operation(summary = "매칭 수락", description = "내게(공급자) 매칭된 사진생성요청을 수락합니다.") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.PictureGenerateRequestNotAssignedToCreator), + @EnumResponse(ResponseCode.CreatorNotFound), + @EnumResponse(ResponseCode.PictureGenerateRequestNotFound) + }) + ResponseEntity> acceptPictureGenerateRequest( + @AuthUser Long userId, + @Parameter(description = "수락할 사진생성요청의 id", example = "1") + @PathVariable(value = "pictureGenerateRequestId") Long pictureGenerateRequestId); + + @Operation(summary = "매칭 거절", description = "내게(공급자) 매칭된 사진생성요청을 거절합니다.") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.PictureGenerateRequestNotAssignedToCreator), + @EnumResponse(ResponseCode.PictureGenerateRequestNotFound) + }) + ResponseEntity> rejectPictureGenerateRequest( + @AuthUser Long userId, + @Parameter(description = "거절할 사진생성요청의 id", example = "1") + @PathVariable(value = "pictureGenerateRequestId") Long pictureGenerateRequestId); + + @Operation(summary = "작업 진행중인(최대 3개) 사진생성요청 조회", description = "내가(공급자)진행중인 사진생성요청&응답을 조회합니다.") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.CreatorNotFound), + }) + ResponseEntity>> getInProgressPictureGenerateRequestDetail( + @AuthUser Long userId + ); + + @Deprecated + @Operation(summary = "작업한(하는중인) 사진생성요청&응답 전체조회", description = "내가(공급자) 작업한(하고있는) 사진생성요청&응답을 전체 조회합니다.", deprecated = true) + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity>> getAssignedPictureGenerateRequestsAll( + @AuthUser Long userId); + + @Deprecated + @Operation(summary = "사진생성요청&응답 1개 자세히조회", description = "내가(공급자) 작업중인 사진생성요청&응답 1개를 자세히 조회합니다.", deprecated = true) + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity> getPictureGenerateRequestDetail( + @AuthUser Long userId, + @Parameter(description = "조회할 사진생성요청의 id", example = "1") + @PathVariable(value = "pictureGenerateRequestId") Long pictureGenerateRequestId); +} diff --git a/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/api/UserPGREQApi.java b/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/api/UserPGREQApi.java new file mode 100644 index 00000000..5dc693f9 --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/api/UserPGREQApi.java @@ -0,0 +1,80 @@ +package com.gt.genti.picturegeneraterequest.api; + +import java.util.List; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; + +import com.gt.genti.error.ResponseCode; +import com.gt.genti.picturegeneraterequest.dto.request.PGREQSaveRequestDto; +import com.gt.genti.picturegeneraterequest.dto.response.PGREQBriefFindByUserResponseDto; +import com.gt.genti.picturegeneraterequest.dto.response.PGREQStatusResponseDto; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.swagger.AuthorizedUser; +import com.gt.genti.swagger.EnumResponse; +import com.gt.genti.swagger.EnumResponses; +import com.gt.genti.user.model.AuthUser; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; + + +@AuthorizedUser +@Tag(name = "[UserPGREQController] 유저의 사진 생성 요청", description = "사진 생성 요청을 생성, 조회, 수정합니다.") +public interface UserPGREQApi { + + @Deprecated + @Operation(summary = "내 요청 전체조회", description = "내가 요청한 사진생성요청 전체 조회", deprecated = true) + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity>> getAllUsersPictureGenerateRequest( + @AuthUser Long userId + ); + + @Operation(summary = "현재 진행중인 사진생성요청의 상태 조회", description = "작업이 진행중인 사진 생성요청이 있다면 해당 상태를 조회한다.") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.UserNotFound) + }) + ResponseEntity> getPendingPGRESStatus( + @AuthUser Long userId + ); + + @Operation(summary = "취소된 사진생성요청 확인(폐기)처리", description = "사진생성요청이 오류로 인해 취소된 경우 실행하여 초기화한 후 새로운 요청을 할 수 있도록 변경") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.UserNotFound) + }) + ResponseEntity> confirmCanceledPGREQ( + @AuthUser Long userId, + @PathVariable(value = "pictureGenerateRequestId") + @Schema(description = "사진생성요청id", example = "1") + Long pictureGenerateRequestId + ); + + @Operation(summary = "사진생성요청하기", description = "사진생성요청을 생성한다.") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity> createPictureGenerateRequest( + @AuthUser Long userId, + @RequestBody @Valid PGREQSaveRequestDto pgreqSaveRequestDto); + + @Deprecated + @Operation(summary = "사진생성요청 수정", description = "이전에 생성한 사진생성요청을 수정한다", deprecated = true) + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.PictureGenerateRequestNotFound), + @EnumResponse(ResponseCode.PictureGenerateRequestAlreadyInProgress) + }) + ResponseEntity> modifyPictureGenerateRequest( + @AuthUser Long userId, + @PathVariable(value = "pictureGenerateRequestId") + @Schema(description = "사진생성요청id", example = "1") + Long pictureGenerateRequestId, + @RequestBody @Valid PGREQSaveRequestDto pgreqSaveRequestDto); +} diff --git a/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/controller/AdminPGREQController.java b/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/controller/AdminPGREQController.java index 656616c3..016803c0 100644 --- a/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/controller/AdminPGREQController.java +++ b/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/controller/AdminPGREQController.java @@ -12,40 +12,31 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import com.gt.genti.error.ResponseCode; import com.gt.genti.model.LogAction; import com.gt.genti.model.LogItem; import com.gt.genti.model.LogRequester; import com.gt.genti.model.Logging; +import com.gt.genti.picturegeneraterequest.api.AdminPGREQApi; import com.gt.genti.picturegeneraterequest.dto.response.PGREQAdminMatchedDetailFindByAdminResponseDto; import com.gt.genti.picturegeneraterequest.dto.response.PGREQCreatorSubmittedDetailFindByAdminResponseDto; import com.gt.genti.picturegenerateresponse.service.mapper.PictureGenerateResponseStatusForAdmin; -import com.gt.genti.swagger.EnumResponse; -import com.gt.genti.swagger.EnumResponses; import com.gt.genti.usecase.PictureGenerateRequestUseCase; import com.gt.genti.validator.ValidEnum; -import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.ExampleObject; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; -@Tag(name = "[AdminPGREQController] 어드민 사진생성요청 컨트롤러", description = "사진생성요청을 조회") @RestController @RequestMapping("/api/v1/admin/picture-generate-requests") @RequiredArgsConstructor -public class AdminPGREQController { +public class AdminPGREQController implements AdminPGREQApi { private final PictureGenerateRequestUseCase pictureGenerateRequestUseCase; - @Operation(summary = "어드민에게 매칭된 사진생성요청 전체조회", description = "사진생성요청 전체를 매칭대상(어드민,공급자), 응답의 상태를 조건으로 조회하고 페이지네이션 조회합니다.") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @Logging(item = LogItem.PGREQ, action = LogAction.VIEW, requester = LogRequester.ADMIN) @GetMapping("/admin-matched") public ResponseEntity>> getAllAdminMatchedPGREQ( @@ -84,10 +75,6 @@ public ResponseEntity>> getAllCreatorSubmittedPGREQ( diff --git a/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/controller/CreatorPGREQController.java b/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/controller/CreatorPGREQController.java index aab9004d..b1d6118e 100644 --- a/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/controller/CreatorPGREQController.java +++ b/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/controller/CreatorPGREQController.java @@ -11,36 +11,25 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.gt.genti.error.ResponseCode; import com.gt.genti.model.LogAction; import com.gt.genti.model.LogItem; import com.gt.genti.model.LogRequester; import com.gt.genti.model.Logging; +import com.gt.genti.picturegeneraterequest.api.CreatorPGREQApi; import com.gt.genti.picturegeneraterequest.dto.response.PGREQBriefFindByCreatorResponseDto; import com.gt.genti.picturegeneraterequest.model.PictureGenerateRequestStatus; import com.gt.genti.picturegenerateresponse.service.PictureGenerateWorkService; -import com.gt.genti.swagger.EnumResponse; -import com.gt.genti.swagger.EnumResponses; import com.gt.genti.user.model.AuthUser; -import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; -@Tag(name = "[CreatorPGREQController] 공급자의 사진생성요청 컨트롤러", description = "사진생성요청 관련 작업 api") @RestController @RequestMapping("/api/v1/creators/picture-generate-requests") @RequiredArgsConstructor -public class CreatorPGREQController { +public class CreatorPGREQController implements CreatorPGREQApi { private final PictureGenerateWorkService pictureGenerateWorkService; - @Operation(summary = "내게 매칭된 요청 조회", description = "나(공급자)에게 매칭된 사진생성요청을 조회합니다. (수락할지 말지 선택하기위한 최소 정보 보여주기)") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.CreatorNotFound), - @EnumResponse(ResponseCode.PictureGenerateRequestNotFound) - }) @Logging(item = LogItem.PGREQ, action = LogAction.SEARCH, requester = LogRequester.CREATOR) @GetMapping("/assigned") public ResponseEntity> getAssignedPictureGenerateRequestBrief( @@ -50,13 +39,6 @@ public ResponseEntity> getAssigned userId, PictureGenerateRequestStatus.ASSIGNING)); } - @Operation(summary = "매칭 수락", description = "내게(공급자) 매칭된 사진생성요청을 수락합니다.") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.PictureGenerateRequestNotAssignedToCreator), - @EnumResponse(ResponseCode.CreatorNotFound), - @EnumResponse(ResponseCode.PictureGenerateRequestNotFound) - }) @Logging(item = LogItem.PGREQ_ACCEPT, action = LogAction.UPDATE, requester = LogRequester.CREATOR) @PostMapping("/{pictureGenerateRequestId}/accept") public ResponseEntity> acceptPictureGenerateRequest( @@ -67,13 +49,6 @@ public ResponseEntity> acceptPictureGenerateRequest( pictureGenerateWorkService.acceptPictureGenerateRequest(userId, pictureGenerateRequestId)); } - - @Operation(summary = "매칭 거절", description = "내게(공급자) 매칭된 사진생성요청을 거절합니다.") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.PictureGenerateRequestNotAssignedToCreator), - @EnumResponse(ResponseCode.PictureGenerateRequestNotFound) - }) @Logging(item = LogItem.PGREQ_REJECT, action = LogAction.UPDATE, requester = LogRequester.CREATOR) @PostMapping("/{pictureGenerateRequestId}/reject") public ResponseEntity> rejectPictureGenerateRequest( @@ -84,11 +59,6 @@ public ResponseEntity> rejectPictureGenerateRequest( pictureGenerateWorkService.rejectPictureGenerateRequest(userId, pictureGenerateRequestId)); } - @Operation(summary = "작업 진행중인(최대 3개) 사진생성요청 조회", description = "내가(공급자)진행중인 사진생성요청&응답을 조회합니다.") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.CreatorNotFound), - }) @Logging(item = LogItem.PGREQ_INPROGESS, action = LogAction.SEARCH, requester = LogRequester.CREATOR) @GetMapping("/in-progress") public ResponseEntity>> getInProgressPictureGenerateRequestDetail( @@ -98,10 +68,6 @@ public ResponseEntity>> getIn } @Deprecated - @Operation(summary = "작업한(하는중인) 사진생성요청&응답 전체조회", description = "내가(공급자) 작업한(하고있는) 사진생성요청&응답을 전체 조회합니다.", deprecated = true) - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @Logging(item = LogItem.PGREQ, action = LogAction.VIEW, requester = LogRequester.CREATOR) @GetMapping("/all") public ResponseEntity>> getAssignedPictureGenerateRequestsAll( @@ -111,10 +77,6 @@ public ResponseEntity>> getAs } @Deprecated - @Operation(summary = "사진생성요청&응답 1개 자세히조회", description = "내가(공급자) 작업중인 사진생성요청&응답 1개를 자세히 조회합니다.", deprecated = true) - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @Logging(item = LogItem.PGREQ, action = LogAction.SEARCH, requester = LogRequester.CREATOR) @GetMapping("/{pictureGenerateRequestId}") public ResponseEntity> getPictureGenerateRequestDetail( diff --git a/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/controller/UserPGREQController.java b/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/controller/UserPGREQController.java index 4ca32eab..ac24c1e6 100644 --- a/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/controller/UserPGREQController.java +++ b/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/controller/UserPGREQController.java @@ -11,39 +11,30 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.gt.genti.error.ResponseCode; import com.gt.genti.model.LogAction; import com.gt.genti.model.LogItem; import com.gt.genti.model.LogRequester; import com.gt.genti.model.Logging; +import com.gt.genti.picturegeneraterequest.api.UserPGREQApi; import com.gt.genti.picturegeneraterequest.dto.request.PGREQSaveRequestDto; import com.gt.genti.picturegeneraterequest.dto.response.PGREQBriefFindByUserResponseDto; import com.gt.genti.picturegeneraterequest.dto.response.PGREQStatusResponseDto; import com.gt.genti.response.GentiResponse; import com.gt.genti.response.GentiResponse.ApiResult; -import com.gt.genti.swagger.EnumResponse; -import com.gt.genti.swagger.EnumResponses; import com.gt.genti.usecase.PictureGenerateRequestUseCase; import com.gt.genti.user.model.AuthUser; -import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -@Tag(name = "[UserPGREQController] 유저의 사진 생성 요청", description = "사진 생성 요청을 생성, 조회, 수정합니다.") @RestController @RequestMapping("/api/v1/users/picture-generate-requests") @RequiredArgsConstructor -public class UserPGREQController { +public class UserPGREQController implements UserPGREQApi { private final PictureGenerateRequestUseCase pictureGenerateRequestUseCase; @Deprecated - @Operation(summary = "내 요청 전체조회", description = "내가 요청한 사진생성요청 전체 조회", deprecated = true) - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @Logging(item = LogItem.PGREQ, action = LogAction.VIEW, requester = LogRequester.USER) @GetMapping("/all") public ResponseEntity>> getAllUsersPictureGenerateRequest( @@ -53,11 +44,6 @@ public ResponseEntity>> getAllUs pictureGenerateRequestUseCase.findAllPGREQByRequester(userId)); } - @Operation(summary = "현재 진행중인 사진생성요청의 상태 조회", description = "작업이 진행중인 사진 생성요청이 있다면 해당 상태를 조회한다.") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.UserNotFound) - }) @Logging(item = LogItem.PGREQ_INPROGESS, action = LogAction.SEARCH, requester = LogRequester.USER) @GetMapping("/pending") public ResponseEntity> getPendingPGRESStatus( @@ -67,11 +53,6 @@ public ResponseEntity> getPendingPGRESStatus( pictureGenerateRequestUseCase.getPendingPGREQStatusIfExists(userId)); } - @Operation(summary = "취소된 사진생성요청 확인(폐기)처리", description = "사진생성요청이 오류로 인해 취소된 경우 실행하여 초기화한 후 새로운 요청을 할 수 있도록 변경") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.UserNotFound) - }) @Logging(item = LogItem.PGREQ_CANCEL_VERIFY, action = LogAction.UPDATE, requester = LogRequester.USER) @GetMapping("/{pictureGenerateRequestId}/confirm-cancel-status") public ResponseEntity> confirmCanceledPGREQ( @@ -84,10 +65,6 @@ public ResponseEntity> confirmCanceledPGREQ( pictureGenerateRequestUseCase.confirmCanceledPGREQ(userId, pictureGenerateRequestId)); } - @Operation(summary = "사진생성요청하기", description = "사진생성요청을 생성한다.") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @Logging(item = LogItem.PGREQ, action = LogAction.REQUEST, requester = LogRequester.USER) @PostMapping public ResponseEntity> createPictureGenerateRequest( @@ -99,12 +76,6 @@ public ResponseEntity> createPictureGenerateRequest( } @Deprecated - @Operation(summary = "사진생성요청 수정", description = "이전에 생성한 사진생성요청을 수정한다", deprecated = true) - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.PictureGenerateRequestNotFound), - @EnumResponse(ResponseCode.PictureGenerateRequestAlreadyInProgress) - }) @Logging(item = LogItem.PGREQ, action = LogAction.UPDATE, requester = LogRequester.USER) @PutMapping("/{pictureGenerateRequestId}") public ResponseEntity> modifyPictureGenerateRequest( diff --git a/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/service/PictureGenerateRequestService.java b/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/service/PictureGenerateRequestService.java index b79d8cce..6f389d83 100644 --- a/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/service/PictureGenerateRequestService.java +++ b/genti-api/src/main/java/com/gt/genti/picturegeneraterequest/service/PictureGenerateRequestService.java @@ -14,7 +14,6 @@ import com.gt.genti.error.ExpectedException; import com.gt.genti.error.ResponseCode; -import com.gt.genti.firebase.FCMService; import com.gt.genti.openai.dto.PromptAdvancementRequestCommand; import com.gt.genti.openai.service.OpenAIService; import com.gt.genti.picture.command.CreatePicturePoseCommand; @@ -59,7 +58,6 @@ public class PictureGenerateRequestService implements PictureGenerateRequestUseC private final RequestMatchService requestMatchService; private final UserRepository userRepository; private final PGRESStatusToPGRESStatusForAdminMapper pgresStatusToPGRESStatusForAdminMapper = new PGRESStatusToPGRESStatusForAdminMapper(); - private final FCMService notificationService; private final PGREQStatusToPGREQStatusForUserMapper pgreqStatusToPGREQStatusForUserMapper = new PGREQStatusToPGREQStatusForUserMapper(); diff --git a/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/api/AdminPGRESApi.java b/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/api/AdminPGRESApi.java new file mode 100644 index 00000000..caecc53a --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/api/AdminPGRESApi.java @@ -0,0 +1,57 @@ +package com.gt.genti.picturegenerateresponse.api; + +import java.util.List; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; + +import com.gt.genti.error.ResponseCode; +import com.gt.genti.picture.dto.request.CommonPictureKeyUpdateRequestDto; +import com.gt.genti.picture.dto.response.CommonPictureResponseDto; +import com.gt.genti.picturegenerateresponse.dto.request.PGRESUpdateAdminInChargeRequestDto; +import com.gt.genti.picturegenerateresponse.dto.response.PGRESSubmitByAdminResponseDto; +import com.gt.genti.picturegenerateresponse.dto.response.PGRESUpdateAdminInChargeResponseDto; +import com.gt.genti.response.GentiResponse; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.swagger.AuthorizedAdmin; +import com.gt.genti.swagger.EnumResponse; +import com.gt.genti.swagger.EnumResponses; +import com.gt.genti.swagger.RequireImageUpload; +import com.gt.genti.user.model.AuthUser; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; + +@AuthorizedAdmin +@Tag(name = "[AdminPGRESController] 어드민 사진생성응답 컨트롤러", description = "사진생성응답을 조회, 수정") +public interface AdminPGRESApi { + @Operation(summary = "사진생성응답 최종 제출", description = "사진생성응답을 최종 제출합니다.") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity> submit( + @Parameter(description = "사진생성응답 Id", example = "1", required = true) + @PathVariable(value = "pictureGenerateResponseId") Long pictureGenerateResponseId); + + @Operation(summary = "응답에 최종 사진 리스트 추가", description = "사진생성응답에 최종 사진 리스트 추가") + @RequireImageUpload + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity>> updatePictureList( + @AuthUser Long userId, + @Parameter(description = "업로드한 사진 url 리스트", required = true) + @RequestBody @Valid List<@Valid CommonPictureKeyUpdateRequestDto> reuqestDtoList, + @PathVariable(value = "pictureGenerateResponseId") Long pictureGenerateResponseId); + + @Operation(summary = "담당자(어드민) 설정", description = "사진생성응답 담당 어드민 설정") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity> updateAdminInCharge( + @PathVariable(value = "pictureGenerateResponseId") Long pictureGenerateResponseId, + @RequestBody PGRESUpdateAdminInChargeRequestDto requestDto); +} diff --git a/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/api/CreatorPGRESApi.java b/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/api/CreatorPGRESApi.java new file mode 100644 index 00000000..aab07b0e --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/api/CreatorPGRESApi.java @@ -0,0 +1,66 @@ +package com.gt.genti.picturegenerateresponse.api; + +import java.util.List; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; + +import com.gt.genti.error.ResponseCode; +import com.gt.genti.picture.dto.request.CommonPictureKeyUpdateRequestDto; +import com.gt.genti.picturegenerateresponse.dto.request.MemoUpdateRequestDto; +import com.gt.genti.picturegenerateresponse.dto.response.PGRESSubmitByCreatorResponseDto; +import com.gt.genti.response.GentiResponse; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.swagger.AuthorizedCreator; +import com.gt.genti.swagger.EnumResponse; +import com.gt.genti.swagger.EnumResponses; +import com.gt.genti.swagger.RequireImageUpload; +import com.gt.genti.user.model.AuthUser; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; + +@AuthorizedCreator +@Tag(name = "[CreatorPGRESController] 공급자의 사진생성응답 컨트롤러", description = "사진생성응답 작업 api") +public interface CreatorPGRESApi { + + @RequireImageUpload + @Operation(summary = "사진생성응답에 사진 Key 리스트 추가", description = "사진생성응답에 공급자 제출사진업로드 결과 Key 리스트를 추가합니다.") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.PictureGenerateResponseNotFound), + @EnumResponse(ResponseCode.PictureGenerateRequestNotAssignedToCreator) + }) + ResponseEntity> updatePictureUrl( + @AuthUser Long userId, + @Parameter(description = "사진 url을 업데이트할 사진생성응답 Id", example = "1") + @PathVariable(value = "pictureGenerateResponseId") Long pictureGenerateResponseId, + @RequestBody @Valid List<@Valid CommonPictureKeyUpdateRequestDto> commonPictureKeyUpdateRequestDtoList + ); + + @Operation(summary = "사진생성응답을 어드민에게 제출", description = "(사진 업로드 이후) 사진생성응답을 어드민에게 제출합니다.") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.PictureGenerateResponseNotFound), + @EnumResponse(ResponseCode.PictureGenerateRequestNotAssignedToCreator), + @EnumResponse(ResponseCode.SubmitBlockedDueToPictureGenerateResponseIsExpired), + @EnumResponse(ResponseCode.DepositNotFound) + }) + ResponseEntity> submitPictureGenerateResponse( + @AuthUser Long userId, + @Parameter(description = "제출할 사진생성응답 Id", example = "1") + @PathVariable(value = "pictureGenerateResponseId") Long pictureGenerateResponseId); + + @Deprecated + @Operation(summary = "메모 추가", description = "사진생성응답에 메모를 추가합니다.", deprecated = true) + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity> updateMemo( + @Parameter(description = "메모를 수정할 사진생성응답 Id", example = "1") + @PathVariable(value = "pictureGenerateResponseId") Long pictureGenerateResponseId, + @RequestBody @Valid MemoUpdateRequestDto memoUpdateRequestDto); +} diff --git a/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/api/UserPGRESApi.java b/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/api/UserPGRESApi.java new file mode 100644 index 00000000..7ea2ee28 --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/api/UserPGRESApi.java @@ -0,0 +1,43 @@ +package com.gt.genti.picturegenerateresponse.api; + +import org.hibernate.validator.constraints.Range; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; + +import com.gt.genti.error.ResponseCode; +import com.gt.genti.response.GentiResponse; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.swagger.AuthorizedUser; +import com.gt.genti.swagger.EnumResponse; +import com.gt.genti.swagger.EnumResponses; +import com.gt.genti.user.model.AuthUser; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.NotNull; + +@AuthorizedUser +@Tag(name = "[UserPGRESController] 유저의 요청에 의한 사진생성응답", description = "사진 생성 응답을 조회합니다.") +public interface UserPGRESApi { + @Operation(summary = "완성된 사진 확인(사진에 문제없음)처리", description = "완성된 사진을 최종 확인처리한다.") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity> verifyCompletedPGREQ( + @AuthUser Long userId, + @Parameter(example = "1", description = "사용자가 확인완료처리하고싶은 최종 사진이 포함된 '사진생성응답'의 id 값") + @PathVariable(value = "pictureGenerateResponseId") Long pictureGenerateResponseId); + + @Operation(summary = "별점 주기", description = "생성 완료된 사진에 별점 주기") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.PictureGenerateResponseNotFound) + }) + ResponseEntity> ratePicture( + @AuthUser Long userId, + @PathVariable(value = "pictureGenerateResponseId") Long pictureGenerateResponseId, + @Parameter(example = "3", description = "생성 완료된 사진에 대한 별점 (값의 범위 : 1 ~ 5)") + @RequestParam(name = "star") @NotNull @Range(min = 1, max = 5) Integer star); +} diff --git a/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/controller/AdminPGRESController.java b/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/controller/AdminPGRESController.java index 1cac6c12..af38769d 100644 --- a/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/controller/AdminPGRESController.java +++ b/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/controller/AdminPGRESController.java @@ -11,39 +11,29 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.gt.genti.error.ResponseCode; import com.gt.genti.model.LogAction; import com.gt.genti.model.LogItem; import com.gt.genti.model.LogRequester; import com.gt.genti.model.Logging; import com.gt.genti.picture.dto.request.CommonPictureKeyUpdateRequestDto; import com.gt.genti.picture.dto.response.CommonPictureResponseDto; +import com.gt.genti.picturegenerateresponse.api.AdminPGRESApi; import com.gt.genti.picturegenerateresponse.dto.request.PGRESUpdateAdminInChargeRequestDto; import com.gt.genti.picturegenerateresponse.dto.response.PGRESSubmitByAdminResponseDto; import com.gt.genti.picturegenerateresponse.dto.response.PGRESUpdateAdminInChargeResponseDto; import com.gt.genti.picturegenerateresponse.service.PictureGenerateWorkService; -import com.gt.genti.swagger.EnumResponse; -import com.gt.genti.swagger.EnumResponses; -import com.gt.genti.swagger.RequireImageUpload; import com.gt.genti.user.model.AuthUser; -import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -@Tag(name = "[AdminPGRESController] 어드민 사진생성응답 컨트롤러", description = "사진생성응답을 조회, 수정") @RestController @RequestMapping("/api/v1/admin/picture-generate-responses") @RequiredArgsConstructor -public class AdminPGRESController { +public class AdminPGRESController implements AdminPGRESApi { private final PictureGenerateWorkService pictureGenerateWorkService; - @Operation(summary = "사진생성응답 최종 제출", description = "사진생성응답을 최종 제출합니다.") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @Logging(item = LogItem.PGRES, action = LogAction.COMPLETE, requester = LogRequester.ADMIN) @PostMapping("/{pictureGenerateResponseId}/submit") public ResponseEntity> submit( @@ -52,11 +42,6 @@ public ResponseEntity> submit( return success(pictureGenerateWorkService.submitFinal(pictureGenerateResponseId)); } - @Operation(summary = "응답에 최종 사진 리스트 추가", description = "사진생성응답에 최종 사진 리스트 추가") - @RequireImageUpload - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @Logging(item = LogItem.PGRES_PICTURE_COMPLETED, action = LogAction.UPDATE, requester = LogRequester.ADMIN) @PostMapping("/{pictureGenerateResponseId}/pictures") public ResponseEntity>> updatePictureList( @@ -69,10 +54,6 @@ public ResponseEntity>> updatePictureLi pictureGenerateResponseId)); } - @Operation(summary = "담당자(어드민) 설정", description = "사진생성응답 담당 어드민 설정") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @Logging(item = LogItem.PGRES_ASSIGNEE, action = LogAction.UPDATE, requester = LogRequester.ADMIN) @PostMapping("/{pictureGenerateResponseId}/admin-in-charge") public ResponseEntity> updateAdminInCharge( diff --git a/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/controller/CreatorPGRESController.java b/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/controller/CreatorPGRESController.java index b8f01d5c..d435ca82 100644 --- a/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/controller/CreatorPGRESController.java +++ b/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/controller/CreatorPGRESController.java @@ -11,7 +11,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.gt.genti.error.ResponseCode; import com.gt.genti.model.LogAction; import com.gt.genti.model.LogItem; import com.gt.genti.model.LogRequester; @@ -20,31 +19,18 @@ import com.gt.genti.picturegenerateresponse.dto.request.MemoUpdateRequestDto; import com.gt.genti.picturegenerateresponse.dto.response.PGRESSubmitByCreatorResponseDto; import com.gt.genti.picturegenerateresponse.service.PictureGenerateWorkService; -import com.gt.genti.swagger.EnumResponse; -import com.gt.genti.swagger.EnumResponses; -import com.gt.genti.swagger.RequireImageUpload; import com.gt.genti.user.model.AuthUser; -import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -@Tag(name = "[CreatorPGRESController] 공급자의 사진생성응답 컨트롤러", description = "사진생성응답 작업 api") @RestController @RequestMapping("/api/v1/creators/picture-generate-responses") @RequiredArgsConstructor -public class CreatorPGRESController { +public class CreatorPGRESController implements com.gt.genti.picturegenerateresponse.api.CreatorPGRESApi { private final PictureGenerateWorkService pictureGenerateWorkService; - @RequireImageUpload - @Operation(summary = "사진생성응답에 사진 Key 리스트 추가", description = "사진생성응답에 공급자 제출사진업로드 결과 Key 리스트를 추가합니다.") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.PictureGenerateResponseNotFound), - @EnumResponse(ResponseCode.PictureGenerateRequestNotAssignedToCreator) - }) @Logging(item = LogItem.PGRES_PICTURE_CREATED_BY_CREATOR, action = LogAction.UPDATE, requester = LogRequester.CREATOR) @PostMapping("/{pictureGenerateResponseId}/pictures") public ResponseEntity> updatePictureUrl( @@ -58,14 +44,6 @@ public ResponseEntity> updatePictureUrl( commonPictureKeyUpdateRequestDtoList, userId)); } - @Operation(summary = "사진생성응답을 어드민에게 제출", description = "(사진 업로드 이후) 사진생성응답을 어드민에게 제출합니다.") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.PictureGenerateResponseNotFound), - @EnumResponse(ResponseCode.PictureGenerateRequestNotAssignedToCreator), - @EnumResponse(ResponseCode.SubmitBlockedDueToPictureGenerateResponseIsExpired), - @EnumResponse(ResponseCode.DepositNotFound) - }) @Logging(item = LogItem.PGRES_SUBMIT, action = LogAction.UPDATE, requester = LogRequester.CREATOR) @PostMapping("/{pictureGenerateResponseId}/submit") public ResponseEntity> submitPictureGenerateResponse( @@ -77,10 +55,6 @@ public ResponseEntity> submitPictureG } @Deprecated - @Operation(summary = "메모 추가", description = "사진생성응답에 메모를 추가합니다.", deprecated = true) - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @Logging(item = LogItem.PGRES_MEMO, action = LogAction.CREATE, requester = LogRequester.CREATOR) @PostMapping("/{pictureGenerateResponseId}/memo") public ResponseEntity> updateMemo( diff --git a/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/controller/UserPGRESController.java b/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/controller/UserPGRESController.java index 8ec22487..8fb173d4 100644 --- a/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/controller/UserPGRESController.java +++ b/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/controller/UserPGRESController.java @@ -2,40 +2,33 @@ import static com.gt.genti.response.GentiResponse.*; -import jakarta.validation.constraints.NotNull; import org.hibernate.validator.constraints.Range; import org.springframework.http.ResponseEntity; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; -import com.gt.genti.error.ResponseCode; import com.gt.genti.model.LogAction; import com.gt.genti.model.LogItem; import com.gt.genti.model.LogRequester; import com.gt.genti.model.Logging; +import com.gt.genti.picturegenerateresponse.api.UserPGRESApi; import com.gt.genti.picturegenerateresponse.service.PictureGenerateWorkService; import com.gt.genti.response.GentiResponse; -import com.gt.genti.swagger.EnumResponse; -import com.gt.genti.swagger.EnumResponses; import com.gt.genti.user.model.AuthUser; -import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; -@Tag(name = "[UserPGRESController] 유저의 요청에 의한 사진생성응답", description = "사진 생성 응답을 조회합니다.") @RestController @RequestMapping("/api/v1/users/picture-generate-responses") @RequiredArgsConstructor -@Validated -public class UserPGRESController { +public class UserPGRESController implements UserPGRESApi { private final PictureGenerateWorkService pictureGenerateWorkService; - @Operation(summary = "완성된 사진 확인(사진에 문제없음)처리", description = "완성된 사진을 최종 확인처리한다.") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @Logging(item = LogItem.PGRES_VERIFY, action = LogAction.COMPLETE, requester = LogRequester.USER) @PostMapping("/{pictureGenerateResponseId}/verify") public ResponseEntity> verifyCompletedPGREQ( @@ -46,11 +39,6 @@ public ResponseEntity> verifyCompletedPGREQ( pictureGenerateResponseId)); } - @Operation(summary = "별점 주기", description = "생성 완료된 사진에 별점 주기") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.PictureGenerateResponseNotFound) - }) @Logging(item = LogItem.PGRES_STAR, action = LogAction.CREATE, requester = LogRequester.USER) @PostMapping("/{pictureGenerateResponseId}/rate") public ResponseEntity> ratePicture( diff --git a/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/service/PGRESCompleteEventPublisher.java b/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/service/PGRESCompleteEventPublisher.java new file mode 100644 index 00000000..bd7ee267 --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/service/PGRESCompleteEventPublisher.java @@ -0,0 +1,26 @@ +package com.gt.genti.picturegenerateresponse.service; + +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import com.gt.genti.firebase.event.PictureGenerationCompletedNotificationEvent; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class PGRESCompleteEventPublisher { + + private final ApplicationEventPublisher eventPublisher; + + @Async + @Transactional(propagation = Propagation.REQUIRES_NEW) + public void publishPictureGenerateCompleteEvent(Long receiverId) { + eventPublisher.publishEvent(PictureGenerationCompletedNotificationEvent.of(receiverId)); + } + +} + diff --git a/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/service/PictureGenerateWorkService.java b/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/service/PictureGenerateWorkService.java index 561b0383..3b123beb 100644 --- a/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/service/PictureGenerateWorkService.java +++ b/genti-api/src/main/java/com/gt/genti/picturegenerateresponse/service/PictureGenerateWorkService.java @@ -55,6 +55,7 @@ public class PictureGenerateWorkService { private final DepositRepository depositRepository; private final RequestMatchService requestMatchService; private final UserRepository userRepository; + private final PGRESCompleteEventPublisher PGRESCompleteEventPublisher; public PGREQBriefFindByCreatorResponseDto getPictureGenerateRequestBrief(Long userId, PictureGenerateRequestStatus status) { @@ -190,13 +191,7 @@ public PGRESSubmitByAdminResponseDto submitFinal(Long pictureGenerateResponseId) foundPGRES.adminSubmit(); Duration elapsedDuration = foundPGRES.getAdminElapsedTime(); - //TODO 어드민은 시간 체크 할 필요 없겠지? - // edited at 2024-05-20 - // author 서병렬 - - //TODO 요청자에게 앱 푸시알림 - // edited at 2024-05-21 - // author 서병렬 + PGRESCompleteEventPublisher.publishPictureGenerateCompleteEvent(foundPGRES.getRequest().getRequester().getId()); return PGRESSubmitByAdminResponseDto.builder() .pictureGenerateResponseId(foundPGRES.getId()) diff --git a/genti-api/src/main/java/com/gt/genti/report/api/AdminReportApi.java b/genti-api/src/main/java/com/gt/genti/report/api/AdminReportApi.java new file mode 100644 index 00000000..439ea566 --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/report/api/AdminReportApi.java @@ -0,0 +1,79 @@ +package com.gt.genti.report.api; + +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import com.gt.genti.error.ResponseCode; +import com.gt.genti.report.dto.request.ReportUpdateRequestDto; +import com.gt.genti.report.dto.response.ReportFindByAdminResponseDto; +import com.gt.genti.report.model.ReportStatus; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.swagger.AuthorizedAdmin; +import com.gt.genti.swagger.EnumResponse; +import com.gt.genti.swagger.EnumResponses; +import com.gt.genti.validator.ValidEnum; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +@AuthorizedAdmin +@Tag(name = "[AdminReportController] 어드민 신고 컨트롤러", description = "유저의 신고내역을 조회,수정합니다.") +public interface AdminReportApi { + @Operation(summary = "신고내역 전체조회", description = "전체 신고내역을 페이지네이션 조회") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity>> getAllReports( + @Parameter(description = "페이지 번호 (0-based)", example = "0", required = true) + @RequestParam(name = "page", defaultValue = "0") @NotNull @Min(0) int page, + @Parameter(description = "페이지 당 요소 개수 >=1", example = "10", required = true) + @RequestParam(name = "size", defaultValue = "10") @NotNull @Min(1) int size, + @Parameter(description = "정렬 조건 - 기본값 생성일시", example = "createdAt", schema = @Schema(allowableValues = {"id", + "createdAt"})) + @RequestParam(name = "sortBy", defaultValue = "createdAt") String sortBy, + @Parameter(description = "정렬 방향 - 기본값 내림차순", example = "desc", schema = @Schema(allowableValues = {"acs", + "desc"})) + @RequestParam(name = "direction", defaultValue = "desc") String direction, + @Parameter(description = "신고의 상태 ALL : 모든 상태 조회 NOT_RESOLVED : 해결 전 RESOLVED : 해결 완료", example = "ADMIN_IN_PROGRESS", schema = @Schema( + allowableValues = {"NOT_RESOLVED", "RESOLVED", "ALL"})) + @RequestParam(name = "status", defaultValue = "ALL") @ValidEnum(value = ReportStatus.class, hasAllOption = true) String status + ); + + @Operation(summary = "특정 사용자의 신고내역 조회", description = "사용자의 email을 전달 받아 해당 사용자의 전체 신고내역을 페이지네이션 조회") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.UserNotFoundByEmail) + }) + ResponseEntity>> getReportsByUserEmail( + @Parameter(description = "페이지 번호 (0-based)", example = "0", required = true) + @RequestParam(name = "page", defaultValue = "0") @NotNull @Min(0) int page, + @Parameter(description = "페이지 당 요소 개수 >=1", example = "10", required = true) + @RequestParam(name = "size", defaultValue = "10") @NotNull @Min(1) int size, + @Parameter(description = "정렬 조건 - 기본값 생성일시", example = "createdAt", schema = @Schema(allowableValues = { + "createdAt"})) + @RequestParam(name = "sortBy", defaultValue = "createdAt") String sortBy, + @Parameter(description = "정렬 방향 - 기본값 내림차순", example = "desc", schema = @Schema(allowableValues = {"acs", + "desc"})) + @RequestParam(name = "direction", defaultValue = "desc") String direction, + @Parameter(description = "신고의 상태 ALL : 모든 상태 조회 NOT_RESOLVED : 해결 전 RESOLVED : 해결 완료", example = "ADMIN_IN_PROGRESS", schema = @Schema( + allowableValues = {"NOT_RESOLVED", "RESOLVED", "ALL"})) + @RequestParam(name = "status", defaultValue = "ALL") @ValidEnum(value = ReportStatus.class, hasAllOption = true) String status, + @Parameter(description = "사용자 이메일", example = "example@naver.com", required = true) + @PathVariable("email") @NotNull String email + ); + + @Operation(summary = "신고 상태 수정", description = "신고의 상태(어드민 처리 여부)를 변경합니다.") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity> updateReport( + @RequestBody @Valid ReportUpdateRequestDto reportUpdateRequestDto); +} diff --git a/genti-api/src/main/java/com/gt/genti/report/api/PGREQReportApi.java b/genti-api/src/main/java/com/gt/genti/report/api/PGREQReportApi.java new file mode 100644 index 00000000..9f4515ba --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/report/api/PGREQReportApi.java @@ -0,0 +1,33 @@ +package com.gt.genti.report.api; + +import static com.gt.genti.error.ResponseCode.*; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; + +import com.gt.genti.report.dto.request.ReportCreateRequestDto; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.swagger.AuthorizedUser; +import com.gt.genti.swagger.EnumResponse; +import com.gt.genti.swagger.EnumResponses; +import com.gt.genti.user.model.AuthUser; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; + +@AuthorizedUser +@Tag(name = "[PGREQReportController] 유저의 신고 요청", description = "유저가 신고를 요청한다.") +public interface PGREQReportApi { + + @Operation(summary = "신고", description = "사진생성응답을 신고합니다.") + @EnumResponses(value = { + @EnumResponse(OK), + @EnumResponse(UserNotFound), + @EnumResponse(PictureGenerateResponseNotFound)} + ) + ResponseEntity> createReport( + @AuthUser Long userId, + @RequestBody @Valid ReportCreateRequestDto reportCreateRequestDto + ); +} diff --git a/genti-api/src/main/java/com/gt/genti/report/controller/AdminReportController.java b/genti-api/src/main/java/com/gt/genti/report/controller/AdminReportController.java index 2f65baf2..5e94ec53 100644 --- a/genti-api/src/main/java/com/gt/genti/report/controller/AdminReportController.java +++ b/genti-api/src/main/java/com/gt/genti/report/controller/AdminReportController.java @@ -7,41 +7,38 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +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 com.gt.genti.error.ResponseCode; import com.gt.genti.model.LogAction; import com.gt.genti.model.LogItem; import com.gt.genti.model.LogRequester; import com.gt.genti.model.Logging; +import com.gt.genti.report.api.AdminReportApi; import com.gt.genti.report.dto.request.ReportUpdateRequestDto; import com.gt.genti.report.dto.response.ReportFindByAdminResponseDto; import com.gt.genti.report.model.ReportStatus; import com.gt.genti.report.service.ReportService; -import com.gt.genti.swagger.EnumResponse; -import com.gt.genti.swagger.EnumResponses; import com.gt.genti.validator.ValidEnum; -import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; -@Tag(name = "[AdminReportController] 어드민 신고 컨트롤러", description = "유저의 신고내역을 조회,수정합니다.") @RestController @RequestMapping("/api/v1/admin/reports") @RequiredArgsConstructor -public class AdminReportController { +public class AdminReportController implements AdminReportApi { private final ReportService reportService; - @Operation(summary = "신고내역 전체조회", description = "전체 신고내역을 페이지네이션 조회") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @Logging(item = LogItem.REPORT, action = LogAction.VIEW, requester = LogRequester.ADMIN) @GetMapping public ResponseEntity>> getAllReports( @@ -68,11 +65,6 @@ public ResponseEntity>> getAllRepor } } - @Operation(summary = "특정 사용자의 신고내역 조회", description = "사용자의 email을 전달 받아 해당 사용자의 전체 신고내역을 페이지네이션 조회") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.UserNotFoundByEmail) - }) @Logging(item = LogItem.REPORT, action = LogAction.VIEW, requester = LogRequester.ADMIN) @GetMapping("/{email}") public ResponseEntity>> getReportsByUserEmail( @@ -80,13 +72,14 @@ public ResponseEntity>> getReportsB @RequestParam(name = "page", defaultValue = "0") @NotNull @Min(0) int page, @Parameter(description = "페이지 당 요소 개수 >=1", example = "10", required = true) @RequestParam(name = "size", defaultValue = "10") @NotNull @Min(1) int size, - @Parameter(description = "정렬 조건 - 기본값 생성일시", example = "createdAt", schema = @Schema(allowableValues = {"createdAt"})) + @Parameter(description = "정렬 조건 - 기본값 생성일시", example = "createdAt", schema = @Schema(allowableValues = { + "createdAt"})) @RequestParam(name = "sortBy", defaultValue = "createdAt") String sortBy, @Parameter(description = "정렬 방향 - 기본값 내림차순", example = "desc", schema = @Schema(allowableValues = {"acs", - "desc"})) + "desc"})) @RequestParam(name = "direction", defaultValue = "desc") String direction, @Parameter(description = "신고의 상태 ALL : 모든 상태 조회 NOT_RESOLVED : 해결 전 RESOLVED : 해결 완료", example = "ADMIN_IN_PROGRESS", schema = @Schema( - allowableValues = {"NOT_RESOLVED", "RESOLVED", "ALL"})) + allowableValues = {"NOT_RESOLVED", "RESOLVED", "ALL"})) @RequestParam(name = "status", defaultValue = "ALL") @ValidEnum(value = ReportStatus.class, hasAllOption = true) String status, @Parameter(description = "사용자 이메일", example = "example@naver.com", required = true) @PathVariable("email") @NotNull String email @@ -97,10 +90,6 @@ public ResponseEntity>> getReportsB return success(reportService.getReportsByUserEmail(email, status, pageable)); } - @Operation(summary = "신고 상태 수정", description = "신고의 상태(어드민 처리 여부)를 변경합니다.") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @Logging(item = LogItem.REPORT, action = LogAction.UPDATE, requester = LogRequester.ADMIN) @PostMapping public ResponseEntity> updateReport( diff --git a/genti-api/src/main/java/com/gt/genti/report/controller/PGREQReportController.java b/genti-api/src/main/java/com/gt/genti/report/controller/PGREQReportController.java index c711dd29..09a63b6f 100644 --- a/genti-api/src/main/java/com/gt/genti/report/controller/PGREQReportController.java +++ b/genti-api/src/main/java/com/gt/genti/report/controller/PGREQReportController.java @@ -1,5 +1,8 @@ package com.gt.genti.report.controller; +import static com.gt.genti.model.LogAction.*; +import static com.gt.genti.model.LogItem.*; +import static com.gt.genti.model.LogRequester.USER; import static com.gt.genti.response.GentiResponse.*; import org.springframework.http.ResponseEntity; @@ -8,37 +11,22 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.gt.genti.error.ResponseCode; -import com.gt.genti.model.LogAction; -import com.gt.genti.model.LogItem; -import com.gt.genti.model.LogRequester; import com.gt.genti.model.Logging; +import com.gt.genti.report.api.PGREQReportApi; import com.gt.genti.report.dto.request.ReportCreateRequestDto; import com.gt.genti.report.service.ReportService; -import com.gt.genti.swagger.EnumResponse; -import com.gt.genti.swagger.EnumResponses; import com.gt.genti.user.model.AuthUser; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -@Tag(name = "[PGREQReportController] 유저의 신고 요청", description = "유저가 신고를 요청한다.") @RestController @RequestMapping("/api/v1/users/reports") @RequiredArgsConstructor -public class PGREQReportController { +public class PGREQReportController implements PGREQReportApi { private final ReportService reportService; - - @Operation(summary = "신고", description = "사진생성응답을 신고합니다.") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.UserNotFound), - @EnumResponse(ResponseCode.PictureGenerateResponseNotFound)} - ) - @Logging(item = LogItem.REPORT, action = LogAction.CREATE, requester = LogRequester.USER) + @Logging(item = REPORT, action = CREATE, requester = USER) @PostMapping public ResponseEntity> createReport( @AuthUser Long userId, diff --git a/genti-api/src/main/java/com/gt/genti/responseexample/api/AdminExampleApi.java b/genti-api/src/main/java/com/gt/genti/responseexample/api/AdminExampleApi.java new file mode 100644 index 00000000..8fef38c1 --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/responseexample/api/AdminExampleApi.java @@ -0,0 +1,58 @@ +package com.gt.genti.responseexample.api; + +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import com.gt.genti.error.ResponseCode; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.responseexample.dto.request.ExampleSaveRequestDto; +import com.gt.genti.responseexample.dto.response.ExampleWithPictureFindResponseDto; +import com.gt.genti.swagger.AuthorizedAdmin; +import com.gt.genti.swagger.EnumResponse; +import com.gt.genti.swagger.EnumResponses; +import com.gt.genti.swagger.RequireImageUpload; +import com.gt.genti.user.model.AuthUser; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +@AuthorizedAdmin +@Tag(name = "[AdminExampleController] 어드민 사진생성결과 예시 컨트롤러", description = "사진생성결과 예시를 조회, 업로드 합니다.") +public interface AdminExampleApi { + + @Operation(summary = "전체 예시 조회", description = "전체 예시 사진&프롬프트를 페이지네이션 조회합니다.") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity>> getAllResponseExamples( + @Parameter(description = "페이지 번호 (0-based)", example = "0", required = true) + @RequestParam(name = "page", defaultValue = "0") @NotNull @Min(0) int page, + @Parameter(description = "페이지 당 요소 개수 >=1", example = "10", required = true) + @RequestParam(name = "size", defaultValue = "10") @NotNull @Min(0) int size, + @Parameter(description = "정렬 조건 - 기본값 생성일시", example = "createdAt", schema = @Schema(allowableValues = {"id", + "createdAt"})) + @RequestParam(name = "sortBy", defaultValue = "createdAt") String sortBy, + @Parameter(description = "정렬 방향 - 기본값 내림차순", example = "desc", schema = @Schema(allowableValues = {"acs", + "desc"})) + @RequestParam(name = "direction", defaultValue = "desc") String direction + ); + + @Operation(summary = "예시 생성", description = "예시 사진&프롬프트 추가합니다.") + @RequireImageUpload + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity> addResponseExample( + @RequestBody @Valid List<@Valid ExampleSaveRequestDto> requestDtoList, + @AuthUser Long userId + ); +} diff --git a/genti-api/src/main/java/com/gt/genti/responseexample/api/UserResponseExampleApi.java b/genti-api/src/main/java/com/gt/genti/responseexample/api/UserResponseExampleApi.java new file mode 100644 index 00000000..535801ee --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/responseexample/api/UserResponseExampleApi.java @@ -0,0 +1,27 @@ +package com.gt.genti.responseexample.api; + +import java.util.List; + +import org.springframework.http.ResponseEntity; + +import com.gt.genti.error.ResponseCode; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.responseexample.dto.response.ExampleWithPictureFindResponseDto; +import com.gt.genti.swagger.AuthorizedUser; +import com.gt.genti.swagger.EnumResponse; +import com.gt.genti.swagger.EnumResponses; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; + +@AuthorizedUser +@Tag(name = "[UserExampleController] 유저 사진생성 예시 컨트롤러", description = "사진생성 예시를 조회합니다.") +public interface UserResponseExampleApi { + + @Operation(summary = "예시 전체조회", description = "예시 사진&프롬프트를 전체 조회합니다." + "
" + + "한번에 모두 조회해서 random하게 렌더링한다고 가정하고 pagination으로 안했습니다.") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity>> getAllResponseExamples(); +} diff --git a/genti-api/src/main/java/com/gt/genti/responseexample/controller/AdminExampleController.java b/genti-api/src/main/java/com/gt/genti/responseexample/controller/AdminExampleController.java index 35cd7956..6acfd06a 100644 --- a/genti-api/src/main/java/com/gt/genti/responseexample/controller/AdminExampleController.java +++ b/genti-api/src/main/java/com/gt/genti/responseexample/controller/AdminExampleController.java @@ -16,39 +16,30 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import com.gt.genti.error.ResponseCode; import com.gt.genti.model.LogAction; import com.gt.genti.model.LogItem; import com.gt.genti.model.LogRequester; import com.gt.genti.model.Logging; +import com.gt.genti.responseexample.api.AdminExampleApi; import com.gt.genti.responseexample.dto.request.ExampleSaveRequestDto; import com.gt.genti.responseexample.dto.response.ExampleWithPictureFindResponseDto; import com.gt.genti.responseexample.service.ResponseExampleService; -import com.gt.genti.swagger.EnumResponse; -import com.gt.genti.swagger.EnumResponses; import com.gt.genti.swagger.RequireImageUpload; import com.gt.genti.user.model.AuthUser; -import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; -@Tag(name = "[AdminExampleController] 어드민 사진생성결과 예시 컨트롤러", description = "사진생성결과 예시를 조회, 업로드 합니다.") @RestController @RequestMapping("/api/v1/admin/examples") @RequiredArgsConstructor -public class AdminExampleController { +public class AdminExampleController implements AdminExampleApi { private final ResponseExampleService responseExampleService; - @Operation(summary = "전체 예시 조회", description = "전체 예시 사진&프롬프트를 페이지네이션 조회합니다.") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @Logging(item = LogItem.RESPONSE_EXAMPLE, action = LogAction.VIEW, requester = LogRequester.ADMIN) @GetMapping("/with-picture") public ResponseEntity>> getAllResponseExamples( @@ -68,11 +59,6 @@ public ResponseEntity>> getAll return success(responseExampleService.getAllResponseExamplesPagination(pageable)); } - @Operation(summary = "예시 생성", description = "예시 사진&프롬프트 추가합니다.") - @RequireImageUpload - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @Logging(item = LogItem.RESPONSE_EXAMPLE, action = LogAction.CREATE, requester = LogRequester.ADMIN) @PostMapping("/with-picture") public ResponseEntity> addResponseExample( diff --git a/genti-api/src/main/java/com/gt/genti/responseexample/controller/UserExampleController.java b/genti-api/src/main/java/com/gt/genti/responseexample/controller/UserExampleController.java index f91f56aa..d52ef197 100644 --- a/genti-api/src/main/java/com/gt/genti/responseexample/controller/UserExampleController.java +++ b/genti-api/src/main/java/com/gt/genti/responseexample/controller/UserExampleController.java @@ -9,33 +9,23 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.gt.genti.error.ResponseCode; import com.gt.genti.model.LogAction; import com.gt.genti.model.LogItem; import com.gt.genti.model.LogRequester; import com.gt.genti.model.Logging; +import com.gt.genti.responseexample.api.UserResponseExampleApi; import com.gt.genti.responseexample.dto.response.ExampleWithPictureFindResponseDto; import com.gt.genti.responseexample.service.ResponseExampleService; -import com.gt.genti.swagger.EnumResponse; -import com.gt.genti.swagger.EnumResponses; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; -@Tag(name = "[UserExampleController] 유저 사진생성 예시 컨트롤러", description = "사진생성 예시를 조회합니다.") @RestController @RequestMapping("/api/v1/users/examples") @RequiredArgsConstructor -public class UserExampleController { +public class UserExampleController implements UserResponseExampleApi { private final ResponseExampleService responseExampleService; @Logging(item = LogItem.RESPONSE_EXAMPLE, action = LogAction.VIEW, requester = LogRequester.USER) - @Operation(summary = "예시 전체조회", description = "예시 사진&프롬프트를 전체 조회합니다." + "
" - + "한번에 모두 조회해서 random하게 렌더링한다고 가정하고 pagination으로 안했습니다.") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @GetMapping("/with-picture") public ResponseEntity>> getAllResponseExamples() { return success(responseExampleService.getAllResponseExamples()); diff --git a/genti-api/src/main/java/com/gt/genti/settlement/api/SettlementApi.java b/genti-api/src/main/java/com/gt/genti/settlement/api/SettlementApi.java new file mode 100644 index 00000000..2a83bcad --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/settlement/api/SettlementApi.java @@ -0,0 +1,44 @@ +package com.gt.genti.settlement.api; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestParam; + +import com.gt.genti.error.ResponseCode; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.settlementandwithdraw.dto.response.SettlementAndWithdrawPageResponseDto; +import com.gt.genti.swagger.AuthorizedCreator; +import com.gt.genti.swagger.EnumResponse; +import com.gt.genti.swagger.EnumResponses; +import com.gt.genti.user.model.AuthUser; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +@AuthorizedCreator +@Tag(name = "[SettlementController] 공급자 정산내역 컨트롤러", description = "공급자의 정산&출금내역을 조회합니다.") +public interface SettlementApi { + + @Operation(summary = "정산&출금내역 조회", description = "공급자의 정산&출금내역을 페이지네이션 조회합니다.") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.CreatorNotFound), + @EnumResponse(ResponseCode.DepositNotFound) + }) + ResponseEntity> getMySettlements( + @AuthUser Long userId, + @Parameter(description = "페이지 번호 (0-based)", example = "0", required = true) + @RequestParam(name = "page", defaultValue = "0") @NotNull @Min(0) int page, + @Parameter(description = "페이지 당 요소 개수 >=1", example = "10", required = true) + @RequestParam(name = "size", defaultValue = "10") @NotNull @Min(1) int size, + @Parameter(description = "정렬 조건 - 기본값 생성일시", example = "createdAt", schema = @Schema(allowableValues = { + "id", "createdAt"})) + @RequestParam(name = "sortBy", defaultValue = "createdAt") String sortBy, + @Parameter(description = "정렬 방향 - 기본값 내림차순", example = "desc", schema = @Schema(allowableValues = {"acs", + "desc"})) + @RequestParam(name = "direction", defaultValue = "desc") String direction + ); +} diff --git a/genti-api/src/main/java/com/gt/genti/settlement/controller/SettlementController.java b/genti-api/src/main/java/com/gt/genti/settlement/controller/SettlementController.java index 6bc5ccbe..31fc2c34 100644 --- a/genti-api/src/main/java/com/gt/genti/settlement/controller/SettlementController.java +++ b/genti-api/src/main/java/com/gt/genti/settlement/controller/SettlementController.java @@ -11,38 +11,27 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import com.gt.genti.error.ResponseCode; import com.gt.genti.model.LogAction; import com.gt.genti.model.LogItem; import com.gt.genti.model.LogRequester; import com.gt.genti.model.Logging; +import com.gt.genti.settlement.api.SettlementApi; import com.gt.genti.settlement.service.SettlementService; import com.gt.genti.settlementandwithdraw.dto.response.SettlementAndWithdrawPageResponseDto; -import com.gt.genti.swagger.EnumResponse; -import com.gt.genti.swagger.EnumResponses; import com.gt.genti.user.model.AuthUser; -import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; -@Tag(name = "[SettlementController] 공급자 정산내역 컨트롤러", description = "공급자의 정산&출금내역을 조회합니다.") @RestController @RequestMapping("/api/v1/creators/settlements") @RequiredArgsConstructor -public class SettlementController { +public class SettlementController implements SettlementApi { private final SettlementService settlementService; - @Operation(summary = "정산&출금내역 조회", description = "공급자의 정산&출금내역을 페이지네이션 조회합니다.") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.CreatorNotFound), - @EnumResponse(ResponseCode.DepositNotFound) - }) @Logging(item = LogItem.SETTLEMENT, action = LogAction.VIEW, requester = LogRequester.CREATOR) @GetMapping public ResponseEntity> getMySettlements( diff --git a/genti-api/src/main/java/com/gt/genti/swagger/Authorized.java b/genti-api/src/main/java/com/gt/genti/swagger/Authorized.java new file mode 100644 index 00000000..5e863809 --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/swagger/Authorized.java @@ -0,0 +1,16 @@ +package com.gt.genti.swagger; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; + +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Parameter(name = "Authorization", in = ParameterIn.HEADER, description = "Swager Authorization으로 토큰 넣으면 됩니다. 직접 작성하지마세요!! 아무 Role의 AccessToken") +public @interface Authorized { +} + diff --git a/genti-api/src/main/java/com/gt/genti/swagger/AuthorizedAdmin.java b/genti-api/src/main/java/com/gt/genti/swagger/AuthorizedAdmin.java new file mode 100644 index 00000000..6589ceed --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/swagger/AuthorizedAdmin.java @@ -0,0 +1,16 @@ +package com.gt.genti.swagger; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; + +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Parameter(name = "Authorization", in = ParameterIn.HEADER, description = "Swager Authorization으로 토큰 넣으면 됩니다. 직접 작성하지마세요!! ADMIN의 AccessToken") +public @interface AuthorizedAdmin { +} + diff --git a/genti-api/src/main/java/com/gt/genti/swagger/AuthorizedCreator.java b/genti-api/src/main/java/com/gt/genti/swagger/AuthorizedCreator.java new file mode 100644 index 00000000..9646f636 --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/swagger/AuthorizedCreator.java @@ -0,0 +1,16 @@ +package com.gt.genti.swagger; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; + +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Parameter(name = "Authorization", in = ParameterIn.HEADER, description = "Swager Authorization으로 토큰 넣으면 됩니다. 직접 작성하지마세요!! CREATOR, ADMIN의 AccessToken") +public @interface AuthorizedCreator { +} + diff --git a/genti-api/src/main/java/com/gt/genti/swagger/AuthorizedUser.java b/genti-api/src/main/java/com/gt/genti/swagger/AuthorizedUser.java new file mode 100644 index 00000000..c20886c4 --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/swagger/AuthorizedUser.java @@ -0,0 +1,16 @@ +package com.gt.genti.swagger; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; + +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Parameter(name = "Authorization", in = ParameterIn.HEADER, description = "Swager Authorization으로 토큰 넣으면 됩니다. 직접 작성하지마세요!! USER, OAUTH_FIRST_JOIN, ADMIN의 AccessToken") +public @interface AuthorizedUser { +} + diff --git a/genti-api/src/main/java/com/gt/genti/test/FcmTestController.java b/genti-api/src/main/java/com/gt/genti/test/FcmTestController.java new file mode 100644 index 00000000..89bb88d1 --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/test/FcmTestController.java @@ -0,0 +1,26 @@ +package com.gt.genti.test; + +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; + +import com.gt.genti.picturegenerateresponse.service.PGRESCompleteEventPublisher; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Controller +@RequiredArgsConstructor +@RequestMapping("/auth/v1/fcmtest") +public class FcmTestController { + private final PGRESCompleteEventPublisher PGRESCompleteEventPublisher; + + @GetMapping("/{receiverId}") + public ResponseEntity t1(@PathVariable Long receiverId) { + PGRESCompleteEventPublisher.publishPictureGenerateCompleteEvent(receiverId); + return ResponseEntity.ok().build(); + } +} diff --git a/genti-api/src/main/java/com/gt/genti/user/api/AdminUserApi.java b/genti-api/src/main/java/com/gt/genti/user/api/AdminUserApi.java new file mode 100644 index 00000000..3f8b9e06 --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/user/api/AdminUserApi.java @@ -0,0 +1,70 @@ +package com.gt.genti.user.api; + +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import com.gt.genti.error.ResponseCode; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.swagger.AuthorizedAdmin; +import com.gt.genti.swagger.EnumResponse; +import com.gt.genti.swagger.EnumResponses; +import com.gt.genti.user.dto.request.UserRoleUpdateRequestDto; +import com.gt.genti.user.dto.request.UserStatusUpdateRequestDto; +import com.gt.genti.user.dto.response.UserFindByAdminResponseDto; +import com.gt.genti.user.model.UserRole; +import com.gt.genti.validator.ValidEnum; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +@AuthorizedAdmin +@Tag(name = "[AdminUserController] 어드민 유저 컨트롤러", description = "유저의 활성상태,권한 등을 수정, 조회합니다.") +public interface AdminUserApi { + + @Operation(summary = "유저 활성화/비활성화", description = "유저의 활성상태(활성화/비활성화)를 변경") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity> changeUserStatus( + @PathVariable(value = "userId") Long userId, + @RequestBody @Valid UserStatusUpdateRequestDto userStatusUpdateRequestDto); + + @Operation(summary = "권한 수정", description = "유저의 권한을 변경") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity> changeUserRole( + @PathVariable(value = "userId") Long userId, + @RequestBody @Valid UserRoleUpdateRequestDto userRoleUpdateRequestDto); + + @Operation(summary = "유저정보 전체조회", description = "유저 전체조회 페이지네이션") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity>> getAllUserInfo( + @Parameter(description = "페이지 번호 (0-based)", example = "0", required = true) + @RequestParam(name = "page", defaultValue = "0") @NotNull @Min(0) int page, + @Parameter(description = "페이지 당 요소 개수 >=1", example = "10", required = true) + @RequestParam(name = "size", defaultValue = "10") @NotNull @Min(1) int size, + @Parameter(description = "정렬 조건 - 기본값 생성일시", example = "createdAt", schema = @Schema(allowableValues = {"id", + "createdAt"})) + @RequestParam(name = "sortBy", defaultValue = "createdAt") String sortBy, + @Parameter(description = "정렬 방향 - 기본값 내림차순", example = "desc", schema = @Schema(allowableValues = {"acs", + "desc"})) + @RequestParam(name = "direction", defaultValue = "desc") String direction, + @Parameter(description = "유저의 권한", example = "USER", schema = @Schema( + allowableValues = {"USER", "CREATOR", "ADMIN", "ALL"})) + @RequestParam(name = "role", defaultValue = "ALL") @ValidEnum(value = UserRole.class, hasAllOption = true) String role, + @Parameter(description = "유저의 email") + @RequestParam(name = "email", required = false) @Email(message = "올바른 email 형식이 아닙니다.") String email + ); +} diff --git a/genti-api/src/main/java/com/gt/genti/user/api/UserApi.java b/genti-api/src/main/java/com/gt/genti/user/api/UserApi.java new file mode 100644 index 00000000..e33e9b52 --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/user/api/UserApi.java @@ -0,0 +1,102 @@ +package com.gt.genti.user.api; + +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import com.gt.genti.auth.dto.request.SignUpRequestDTO; +import com.gt.genti.error.ResponseCode; +import com.gt.genti.picture.dto.response.CommonPictureResponseDto; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.swagger.AuthorizedUser; +import com.gt.genti.swagger.EnumResponse; +import com.gt.genti.swagger.EnumResponses; +import com.gt.genti.user.dto.request.UserInfoUpdateRequestDto; +import com.gt.genti.user.dto.response.UserFindResponseDto; +import com.gt.genti.user.model.AuthUser; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +@AuthorizedUser +@Tag(name = "[UserController] 유저 컨트롤러", description = "유저의 정보를 조회,수정합니다.") +public interface UserApi { + + @Operation(summary = "내정보보기", description = "유저의 정보를 조회합니다.") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.UserNotFound) + }) + ResponseEntity> getUserInfo( + @AuthUser Long userId); + + @Operation(summary = "내정보 수정", description = "유저의 정보를 수정합니다.") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.UserNotFound) + + }) + ResponseEntity> updateUserInfo( + @AuthUser Long userId, + @RequestBody @Valid UserInfoUpdateRequestDto userInfoUpdateRequestDto); + + @Operation(summary = "최초가입 정보등록", description = "사용자에게 생년, 성별을 받아 최종 가입을 처리") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.UserAlreadySignedUp) + }) + ResponseEntity> signUp( + @AuthUser Long userId, + @RequestBody @Valid SignUpRequestDTO signUpRequestDTO); + + @Operation(summary = "로그아웃", description = "refreshToken 삭제") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.Forbidden), + @EnumResponse(ResponseCode.REFRESH_TOKEN_NOT_EXISTS), + }) + ResponseEntity> logout(@AuthUser Long userId); + + @Operation(summary = "회원 복구", description = "회원탈퇴 취소 처리") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.UserNotFound), + @EnumResponse(ResponseCode.CannotRestoreUser) + + }) + ResponseEntity> restoreSoftDeletedUser( + @AuthUser Long userId); + + @Operation(summary = "회원 탈퇴", description = "사용자 정보 및 관련 정보를 모두 삭제(복구 불가)") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.UserNotFound) + }) + public ResponseEntity> deleteUserHard( + @AuthUser Long userId); + + @Operation(summary = "내 사진 전체조회", description = "내가 사진생성요청으로 생성된 사진 전체 조회 Pagination") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.UserNotFound) + }) + ResponseEntity>> getAllMyGeneratedPicture( + @AuthUser Long userId, + @Parameter(description = "페이지 번호 (0-based)", example = "0", required = true) + @RequestParam(name = "page", defaultValue = "0") @NotNull @Min(0) int page, + @Parameter(description = "페이지 당 요소 개수 >=1", example = "10", required = true) + @RequestParam(name = "size", defaultValue = "10") @NotNull @Min(1) int size, + @Parameter(description = "정렬 조건 - 기본값 생성일시", example = "createdAt", schema = @Schema(allowableValues = { + "id", "createdAt"})) + @RequestParam(name = "sortBy", defaultValue = "createdAt") String sortBy, + @Parameter(description = "정렬 방향 - 기본값 내림차순", example = "desc", schema = @Schema(allowableValues = {"acs", + "desc"})) + @RequestParam(name = "direction", defaultValue = "desc") String direction + ); +} diff --git a/genti-api/src/main/java/com/gt/genti/user/controller/AdminUserController.java b/genti-api/src/main/java/com/gt/genti/user/controller/AdminUserController.java index 5cbb47f0..3f9c154b 100644 --- a/genti-api/src/main/java/com/gt/genti/user/controller/AdminUserController.java +++ b/genti-api/src/main/java/com/gt/genti/user/controller/AdminUserController.java @@ -15,9 +15,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import com.gt.genti.error.ResponseCode; -import com.gt.genti.swagger.EnumResponse; -import com.gt.genti.swagger.EnumResponses; +import com.gt.genti.user.api.AdminUserApi; import com.gt.genti.user.dto.request.UserRoleUpdateRequestDto; import com.gt.genti.user.dto.request.UserStatusUpdateRequestDto; import com.gt.genti.user.dto.response.UserFindByAdminResponseDto; @@ -25,27 +23,20 @@ import com.gt.genti.user.service.UserService; import com.gt.genti.validator.ValidEnum; -import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; -@Tag(name = "[AdminUserController] 어드민 유저 컨트롤러", description = "유저의 활성상태,권한 등을 수정, 조회합니다.") @RestController @RequestMapping("/api/v1/admin") @RequiredArgsConstructor -public class AdminUserController { +public class AdminUserController implements AdminUserApi { private final UserService userService; - @Operation(summary = "유저 활성화/비활성화", description = "유저의 활성상태(활성화/비활성화)를 변경") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @PostMapping("/users/{userId}/status") public ResponseEntity> changeUserStatus( @PathVariable(value = "userId") Long userId, @@ -53,10 +44,6 @@ public ResponseEntity> changeUserStatus( return success(userService.updateUserStatus(userId, userStatusUpdateRequestDto)); } - @Operation(summary = "권한 수정", description = "유저의 권한을 변경") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @PostMapping("/users/{userId}/role") public ResponseEntity> changeUserRole( @PathVariable(value = "userId") Long userId, @@ -64,10 +51,6 @@ public ResponseEntity> changeUserRole( return success(userService.updateUserRole(userId, userRoleUpdateRequestDto)); } - @Operation(summary = "유저정보 전체조회", description = "유저 전체조회 페이지네이션") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @GetMapping("/users") public ResponseEntity>> getAllUserInfo( @Parameter(description = "페이지 번호 (0-based)", example = "0", required = true) @@ -89,7 +72,7 @@ public ResponseEntity>> getAllUserInf Sort.Direction sortDirection = Sort.Direction.fromString(direction); Pageable pageable = PageRequest.of(page, size, Sort.by(sortDirection, sortBy)); - if(email!= null){ + if (email != null) { return success(userService.getUserInfoByEmail(email)); } diff --git a/genti-api/src/main/java/com/gt/genti/user/controller/UserController.java b/genti-api/src/main/java/com/gt/genti/user/controller/UserController.java index 6f31f015..8036693a 100644 --- a/genti-api/src/main/java/com/gt/genti/user/controller/UserController.java +++ b/genti-api/src/main/java/com/gt/genti/user/controller/UserController.java @@ -17,55 +17,38 @@ import org.springframework.web.bind.annotation.RestController; import com.gt.genti.auth.dto.request.SignUpRequestDTO; -import com.gt.genti.error.ResponseCode; import com.gt.genti.model.LogAction; import com.gt.genti.model.LogItem; import com.gt.genti.model.LogRequester; import com.gt.genti.model.Logging; import com.gt.genti.picture.dto.response.CommonPictureResponseDto; import com.gt.genti.response.GentiResponse; -import com.gt.genti.response.GentiResponse.ApiResult; -import com.gt.genti.swagger.EnumResponse; -import com.gt.genti.swagger.EnumResponses; +import com.gt.genti.user.api.UserApi; import com.gt.genti.user.dto.request.UserInfoUpdateRequestDto; import com.gt.genti.user.dto.response.UserFindResponseDto; import com.gt.genti.user.model.AuthUser; import com.gt.genti.user.service.UserService; -import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; -@Tag(name = "[UserController] 유저 컨트롤러", description = "유저의 정보를 조회,수정합니다.") + @RestController @RequestMapping("/api/v1/users") @RequiredArgsConstructor -public class UserController { +public class UserController implements UserApi { private final UserService userService; - @Operation(summary = "내정보보기", description = "유저의 정보를 조회합니다.") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.UserNotFound) - - }) @GetMapping public ResponseEntity> getUserInfo( @AuthUser Long userId) { return GentiResponse.success(userService.getUserInfo(userId)); } - @Operation(summary = "내정보 수정", description = "유저의 정보를 수정합니다.") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.UserNotFound) - - }) @PutMapping public ResponseEntity> updateUserInfo( @AuthUser Long userId, @@ -73,11 +56,6 @@ public ResponseEntity> updateUserInfo( return GentiResponse.success(userService.updateUserInfo(userId, userInfoUpdateRequestDto)); } - @Operation(summary = "최초가입 정보등록", description = "사용자에게 생년, 성별을 받아 최종 가입을 처리") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.UserAlreadySignedUp) - }) @PostMapping("/signup") @Logging(item = LogItem.USER, action = LogAction.SIGNUP, requester = LogRequester.ANONYMOUS) public ResponseEntity> signUp( @@ -86,46 +64,25 @@ public ResponseEntity> signUp( return success(userService.signUp(userId, signUpRequestDTO)); } - @Operation(summary = "로그아웃", description = "refreshToken 삭제") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.Forbidden), - @EnumResponse(ResponseCode.REFRESH_TOKEN_NOT_EXISTS), - }) @PostMapping("/logout") public ResponseEntity> logout(@AuthUser Long userId) { return success(userService.logout(userId)); } - @Operation(summary = "회원 탈퇴", description = "회원 탈퇴") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.UserNotFound) - }) @DeleteMapping - public ResponseEntity> deleteUserSoft( + @Logging(item = LogItem.USER, action = LogAction.DELETE, requester = LogRequester.ANONYMOUS) + public ResponseEntity> deleteUserHard( @AuthUser Long userId) { - return GentiResponse.success(userService.deleteUserSoft(userId)); + return GentiResponse.success(userService.deleteUserHard(userId)); } - @Operation(summary = "회원 복구", description = "회원탈퇴 취소 처리") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.UserNotFound), - @EnumResponse(ResponseCode.CannotRestoreUser) - - }) + @Deprecated @PutMapping("/restore") public ResponseEntity> restoreSoftDeletedUser( @AuthUser Long userId) { return GentiResponse.success(userService.restoreSoftDeletedUser(userId)); } - @Operation(summary = "내 사진 전체조회", description = "내가 사진생성요청으로 생성된 사진 전체 조회 Pagination") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.UserNotFound) - }) @GetMapping("/pictures/my") public ResponseEntity>> getAllMyGeneratedPicture( @AuthUser Long userId, diff --git a/genti-api/src/main/java/com/gt/genti/user/service/UserService.java b/genti-api/src/main/java/com/gt/genti/user/service/UserService.java index c3bf5795..1a974b72 100644 --- a/genti-api/src/main/java/com/gt/genti/user/service/UserService.java +++ b/genti-api/src/main/java/com/gt/genti/user/service/UserService.java @@ -137,6 +137,35 @@ public Boolean restoreSoftDeletedUser(Long userId) { return true; } + public Boolean deleteUserHard(Long userId) { + User foundUser = getUserByUserId(userId); + List deleteList = new ArrayList<>(); + if (foundUser.getCreator() != null) { + Creator foundCreator = foundUser.getCreator(); + List pgresList = foundCreator.getPictureGenerateResponseList(); + foundCreator.getPictureGenerateRequestList() + .stream() + .filter(pgreq -> pgreq.getPictureGenerateRequestStatus().equals( + PictureGenerateRequestStatus.IN_PROGRESS) && (pgreq.getResponseList().isEmpty())) + .forEach(req -> pictureGenerateRequestUseCase.cancelRequest(req, SUPPLIER_EXIT)); + if (pgresList.isEmpty()) { + return true; + } + + pgresList.stream() + .filter(pgres -> CREATOR_BEFORE_WORK.equals(pgres.getStatus())) + .forEach(pgres -> { + + pictureGenerateRequestUseCase.cancelRequest(pgres.getRequest(), SUPPLIER_EXIT); + pgres.clearRelationshipsWithPGREQ(); + deleteList.add(pgres); + }); + pictureGenerateResponseRepository.deleteAll(deleteList); + } + userRepository.delete(foundUser); + return true; + } + public Boolean updateUserStatus(Long userId, UserStatusUpdateRequestDto userStatusUpdateRequestDto) { User foundUser = getUserByUserId(userId); foundUser.updateStatus(userStatusUpdateRequestDto.getUserStatus()); @@ -160,7 +189,7 @@ public Page getAllUserInfoByUserRole(UserRole userRo private User getUserByUserId(Long userId) { return userRepository.findById(userId) - .orElseThrow(() -> ExpectedException.withLogging(ResponseCode.UserNotFound, userId.toString())); + .orElseThrow(() -> ExpectedException.withLogging(ResponseCode.UserNotFound, userId)); } @NotNull diff --git a/genti-api/src/main/java/com/gt/genti/withdraw/api/AdminWithdrawRequestApi.java b/genti-api/src/main/java/com/gt/genti/withdraw/api/AdminWithdrawRequestApi.java new file mode 100644 index 00000000..efbbb29e --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/withdraw/api/AdminWithdrawRequestApi.java @@ -0,0 +1,83 @@ +package com.gt.genti.withdraw.api; + +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; + +import com.gt.genti.error.ResponseCode; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.swagger.AuthorizedAdmin; +import com.gt.genti.swagger.EnumResponse; +import com.gt.genti.swagger.EnumResponses; +import com.gt.genti.user.model.AuthUser; +import com.gt.genti.validator.ValidEnum; +import com.gt.genti.withdraw.dto.response.WithdrawCompletionResponseDto; +import com.gt.genti.withdraw.dto.response.WithdrawFindByAdminResponseDto; +import com.gt.genti.withdrawrequest.model.WithdrawRequestStatus; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +@AuthorizedAdmin +@Tag(name = "[AdminWithdrawRequestController] 어드민 출금요청 컨트롤러", description = "공급자의 출금 요청을 조회,수정합니다.") +public interface AdminWithdrawRequestApi { + @Operation(summary = "출금요청 전체조회", description = "출금요청 페이지네이션 조회") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity>> getAllWithdrawList( + @Parameter(description = "페이지 번호 (0-based)", example = "0", required = true) + @RequestParam(name = "page", defaultValue = "0") @NotNull @Min(0) int page, + @Parameter(description = "페이지 당 요소 개수 >=1", example = "10", required = true) + @RequestParam(name = "size", defaultValue = "10") @NotNull @Min(0) int size, + @Parameter(description = "정렬 조건 - 기본값 생성일시", example = "createdAt", schema = @Schema(allowableValues = {"id", + "createdAt"})) + @RequestParam(name = "sortBy", defaultValue = "createdAt") String sortBy, + @Parameter(description = "정렬 방향 - 기본값 내림차순", example = "desc", schema = @Schema(allowableValues = {"acs", + "desc"})) + @RequestParam(name = "direction", defaultValue = "desc") String direction, + @Parameter(description = "출금요청 상태, ALL : 모든 상태", example = "IN_PROGRESS", schema = @Schema( + allowableValues = {"IN_PROGRESS", "COMPLETED", "REJECTED", "ALL"})) + @RequestParam(name = "status", defaultValue = "ALL") @ValidEnum(value = WithdrawRequestStatus.class, hasAllOption = true) String status + ); + + @Operation(summary = "특정 공급자의 출금요청 조회", description = "공급자의 email을 전달 받아 해당 공급자의 전체 출금요청을 페이지네이션 조회") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.UserNotFoundByEmail) + }) + ResponseEntity>> getWithdrawListByCreatorEmail( + @Parameter(description = "페이지 번호 (0-based)", example = "0", required = true) + @RequestParam(name = "page", defaultValue = "0") @NotNull @Min(0) int page, + @Parameter(description = "페이지 당 요소 개수 >=1", example = "10", required = true) + @RequestParam(name = "size", defaultValue = "10") @NotNull @Min(0) int size, + @Parameter(description = "정렬 조건 - 기본값 생성일시", example = "createdAt", schema = @Schema(allowableValues = { + "createdAt"})) + @RequestParam(name = "sortBy", defaultValue = "createdAt") String sortBy, + @Parameter(description = "정렬 방향 - 기본값 내림차순", example = "desc", schema = @Schema(allowableValues = {"acs", + "desc"})) + @RequestParam(name = "direction", defaultValue = "desc") String direction, + @Parameter(description = "출금요청 상태, ALL : 모든 상태", example = "IN_PROGRESS", schema = @Schema( + allowableValues = {"IN_PROGRESS", "COMPLETED", "REJECTED", "ALL"})) + @RequestParam(name = "status", defaultValue = "ALL") @ValidEnum(value = WithdrawRequestStatus.class, hasAllOption = true) String status, + @Parameter(description = "사용자 이메일", example = "example@naver.com", required = true) + @PathVariable("email") @NotNull String email + ); + + @Operation(summary = "출금요청 완료처리", description = "송금 완료 후 출금요청 완료처리") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.UserNotFound), + @EnumResponse(ResponseCode.DepositNotFound) + }) + ResponseEntity> complete( + @Parameter(description = "출금요청 Id", example = "1") + @PathVariable(value = "withdrawRequestId") Long withdrawRequestId, + @AuthUser Long userId + ); +} diff --git a/genti-api/src/main/java/com/gt/genti/withdraw/api/CreatorWithdrawApi.java b/genti-api/src/main/java/com/gt/genti/withdraw/api/CreatorWithdrawApi.java new file mode 100644 index 00000000..44662535 --- /dev/null +++ b/genti-api/src/main/java/com/gt/genti/withdraw/api/CreatorWithdrawApi.java @@ -0,0 +1,53 @@ +package com.gt.genti.withdraw.api; + +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestParam; + +import com.gt.genti.error.ResponseCode; +import com.gt.genti.response.GentiResponse.ApiResult; +import com.gt.genti.swagger.AuthorizedCreator; +import com.gt.genti.swagger.EnumResponse; +import com.gt.genti.swagger.EnumResponses; +import com.gt.genti.user.model.AuthUser; +import com.gt.genti.withdraw.dto.response.WithdrawFindByCreatorResponseDto; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; + +@AuthorizedCreator +@Tag(name = "[CreatorWithdrawController] 공급자 출금요청 컨트롤러", description = "공급자가 출금요청을 수행합니다.") +public interface CreatorWithdrawApi { + + @Operation(summary = "출금요청", description = "공급자가 작업한 정산결과를 바탕으로 출금요청을 생성합니다.") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK), + @EnumResponse(ResponseCode.CreatorNotFound), + @EnumResponse(ResponseCode.CannotCreateWithdrawalDueToSettlementsNotAvailable) + }) + ResponseEntity> createWithdrawRequest( + @AuthUser Long userId + ); + + @Operation(summary = "출금요청내역조회", description = "공급자의 모든 출금요청을 페이지네이션 조회") + @EnumResponses(value = { + @EnumResponse(ResponseCode.OK) + }) + ResponseEntity>> getWithdrawRequest( + @AuthUser Long userId, + @Parameter(description = "페이지 번호 (0-based)", example = "0", required = true) + @RequestParam(name = "page", defaultValue = "0") @NotNull @Min(0) int page, + @Parameter(description = "페이지 당 요소 개수 >=1", example = "10", required = true) + @RequestParam(name = "size", defaultValue = "10") @NotNull @Min(1) int size, + @Parameter(description = "정렬 조건 - 기본값 생성일시", example = "createdAt", schema = @Schema(allowableValues = {"id", + "createdAt"})) + @RequestParam(name = "sortBy", defaultValue = "createdAt") String sortBy, + @Parameter(description = "정렬 방향 - 기본값 내림차순", example = "desc", schema = @Schema(allowableValues = {"acs", + "desc"})) + @RequestParam(name = "direction", defaultValue = "desc") String direction + ); +} diff --git a/genti-api/src/main/java/com/gt/genti/withdraw/controller/AdminWithdrawRequestController.java b/genti-api/src/main/java/com/gt/genti/withdraw/controller/AdminWithdrawRequestController.java index 1c289fcb..d653d286 100644 --- a/genti-api/src/main/java/com/gt/genti/withdraw/controller/AdminWithdrawRequestController.java +++ b/genti-api/src/main/java/com/gt/genti/withdraw/controller/AdminWithdrawRequestController.java @@ -14,13 +14,10 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import com.gt.genti.error.ResponseCode; import com.gt.genti.model.LogAction; import com.gt.genti.model.LogItem; import com.gt.genti.model.LogRequester; import com.gt.genti.model.Logging; -import com.gt.genti.swagger.EnumResponse; -import com.gt.genti.swagger.EnumResponses; import com.gt.genti.user.model.AuthUser; import com.gt.genti.validator.ValidEnum; import com.gt.genti.withdraw.dto.response.WithdrawCompletionResponseDto; @@ -28,26 +25,19 @@ import com.gt.genti.withdraw.service.WithdrawService; import com.gt.genti.withdrawrequest.model.WithdrawRequestStatus; -import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; -@Tag(name = "[AdminWithdrawRequestController] 어드민 출금요청 컨트롤러", description = "공급자의 출금 요청을 조회,수정합니다.") @RestController @RequestMapping("/api/v1/admin/withdraw-requests") @RequiredArgsConstructor -public class AdminWithdrawRequestController { +public class AdminWithdrawRequestController implements com.gt.genti.withdraw.api.AdminWithdrawRequestApi { private final WithdrawService withDrawService; @Logging(item = LogItem.CASHOUT, action = LogAction.VIEW, requester = LogRequester.ADMIN) - @Operation(summary = "출금요청 전체조회", description = "출금요청 페이지네이션 조회") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @GetMapping public ResponseEntity>> getAllWithdrawList( @Parameter(description = "페이지 번호 (0-based)", example = "0", required = true) @@ -71,27 +61,23 @@ public ResponseEntity>> getAllWit } @Logging(item = LogItem.CASHOUT, action = LogAction.VIEW, requester = LogRequester.ADMIN) - @Operation(summary = "특정 공급자의 출금요청 조회", description = "공급자의 email을 전달 받아 해당 공급자의 전체 출금요청을 페이지네이션 조회") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.UserNotFoundByEmail) - }) @GetMapping("/{email}") public ResponseEntity>> getWithdrawListByCreatorEmail( - @Parameter(description = "페이지 번호 (0-based)", example = "0", required = true) - @RequestParam(name = "page", defaultValue = "0") @NotNull @Min(0) int page, - @Parameter(description = "페이지 당 요소 개수 >=1", example = "10", required = true) - @RequestParam(name = "size", defaultValue = "10") @NotNull @Min(0) int size, - @Parameter(description = "정렬 조건 - 기본값 생성일시", example = "createdAt", schema = @Schema(allowableValues = {"createdAt"})) - @RequestParam(name = "sortBy", defaultValue = "createdAt") String sortBy, - @Parameter(description = "정렬 방향 - 기본값 내림차순", example = "desc", schema = @Schema(allowableValues = {"acs", - "desc"})) - @RequestParam(name = "direction", defaultValue = "desc") String direction, - @Parameter(description = "출금요청 상태, ALL : 모든 상태", example = "IN_PROGRESS", schema = @Schema( - allowableValues = {"IN_PROGRESS", "COMPLETED", "REJECTED", "ALL"})) - @RequestParam(name = "status", defaultValue = "ALL") @ValidEnum(value = WithdrawRequestStatus.class, hasAllOption = true) String status, - @Parameter(description = "사용자 이메일", example = "example@naver.com", required = true) - @PathVariable("email") @NotNull String email + @Parameter(description = "페이지 번호 (0-based)", example = "0", required = true) + @RequestParam(name = "page", defaultValue = "0") @NotNull @Min(0) int page, + @Parameter(description = "페이지 당 요소 개수 >=1", example = "10", required = true) + @RequestParam(name = "size", defaultValue = "10") @NotNull @Min(0) int size, + @Parameter(description = "정렬 조건 - 기본값 생성일시", example = "createdAt", schema = @Schema(allowableValues = { + "createdAt"})) + @RequestParam(name = "sortBy", defaultValue = "createdAt") String sortBy, + @Parameter(description = "정렬 방향 - 기본값 내림차순", example = "desc", schema = @Schema(allowableValues = {"acs", + "desc"})) + @RequestParam(name = "direction", defaultValue = "desc") String direction, + @Parameter(description = "출금요청 상태, ALL : 모든 상태", example = "IN_PROGRESS", schema = @Schema( + allowableValues = {"IN_PROGRESS", "COMPLETED", "REJECTED", "ALL"})) + @RequestParam(name = "status", defaultValue = "ALL") @ValidEnum(value = WithdrawRequestStatus.class, hasAllOption = true) String status, + @Parameter(description = "사용자 이메일", example = "example@naver.com", required = true) + @PathVariable("email") @NotNull String email ) { Sort.Direction sortDirection = Sort.Direction.fromString(direction); Pageable pageable = PageRequest.of(page, size, Sort.by(sortDirection, sortBy)); @@ -100,12 +86,6 @@ public ResponseEntity>> getWithdr } @Logging(item = LogItem.CASHOUT, action = LogAction.COMPLETE, requester = LogRequester.ADMIN) - @Operation(summary = "출금요청 완료처리", description = "송금 완료 후 출금요청 완료처리") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.UserNotFound), - @EnumResponse(ResponseCode.DepositNotFound) - }) @PostMapping("/{withdrawRequestId}") public ResponseEntity> complete( @Parameter(description = "출금요청 Id", example = "1") diff --git a/genti-api/src/main/java/com/gt/genti/withdraw/controller/CreatorWithdrawController.java b/genti-api/src/main/java/com/gt/genti/withdraw/controller/CreatorWithdrawController.java index 93681918..bc2e0aa0 100644 --- a/genti-api/src/main/java/com/gt/genti/withdraw/controller/CreatorWithdrawController.java +++ b/genti-api/src/main/java/com/gt/genti/withdraw/controller/CreatorWithdrawController.java @@ -13,39 +13,27 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import com.gt.genti.withdraw.dto.response.WithdrawFindByCreatorResponseDto; -import com.gt.genti.withdraw.service.WithdrawService; -import com.gt.genti.error.ResponseCode; import com.gt.genti.model.LogAction; import com.gt.genti.model.LogItem; import com.gt.genti.model.LogRequester; import com.gt.genti.model.Logging; -import com.gt.genti.swagger.EnumResponse; -import com.gt.genti.swagger.EnumResponses; import com.gt.genti.user.model.AuthUser; +import com.gt.genti.withdraw.dto.response.WithdrawFindByCreatorResponseDto; +import com.gt.genti.withdraw.service.WithdrawService; -import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; -@Tag(name = "[CreatorWithdrawController] 공급자 출금요청 컨트롤러", description = "공급자가 출금요청을 수행합니다.") @RestController @RequestMapping("/api/v1/creators/withdraw") @RequiredArgsConstructor -public class CreatorWithdrawController { +public class CreatorWithdrawController implements com.gt.genti.withdraw.api.CreatorWithdrawApi { private final WithdrawService withDrawService; @Logging(item = LogItem.CASHOUT, action = LogAction.CREATE, requester = LogRequester.CREATOR) - @Operation(summary = "출금요청", description = "공급자가 작업한 정산결과를 바탕으로 출금요청을 생성합니다.") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK), - @EnumResponse(ResponseCode.CreatorNotFound), - @EnumResponse(ResponseCode.CannotCreateWithdrawalDueToSettlementsNotAvailable) - }) @PostMapping public ResponseEntity> createWithdrawRequest( @AuthUser Long userId @@ -55,10 +43,6 @@ public ResponseEntity> createWithdra } @Logging(item = LogItem.CASHOUT, action = LogAction.VIEW, requester = LogRequester.CREATOR) - @Operation(summary = "출금요청내역조회", description = "공급자의 모든 출금요청을 페이지네이션 조회") - @EnumResponses(value = { - @EnumResponse(ResponseCode.OK) - }) @GetMapping public ResponseEntity>> getWithdrawRequest( @AuthUser Long userId, diff --git a/genti-auth/src/main/java/com/gt/genti/jwt/JwtTokenProvider.java b/genti-auth/src/main/java/com/gt/genti/jwt/JwtTokenProvider.java index 826973f0..493f91ad 100644 --- a/genti-auth/src/main/java/com/gt/genti/jwt/JwtTokenProvider.java +++ b/genti-auth/src/main/java/com/gt/genti/jwt/JwtTokenProvider.java @@ -31,7 +31,6 @@ import io.jsonwebtoken.security.Keys; import io.jsonwebtoken.security.SignatureException; import jakarta.annotation.PostConstruct; -import jakarta.validation.constraints.NotBlank; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -173,7 +172,7 @@ public Authentication getAuthentication(Long userId) { return new UserAuthentication(userDetails); } - public TokenResponse reissueIfValid(@NotBlank String accessToken, @NotBlank String refreshToken) { + public TokenResponse reissueIfValid(String accessToken, String refreshToken) { Long userId = getUserIdFromRefreshToken(refreshToken); try { validateToken(accessToken); diff --git a/genti-common/src/main/java/com/gt/genti/constants/ErrorConstants.java b/genti-common/src/main/java/com/gt/genti/constants/ErrorConstants.java index 2a7c838f..c14a33b5 100644 --- a/genti-common/src/main/java/com/gt/genti/constants/ErrorConstants.java +++ b/genti-common/src/main/java/com/gt/genti/constants/ErrorConstants.java @@ -19,6 +19,7 @@ public class ErrorConstants { private static final String AUTH = "AUTH"; private static final String OAUTH = "OAUTH"; private static final String SERVER = "SERVER"; + private static final String FCM = "FCM"; private static String CODE(String type, int seq) { return type + String.format("-%05d", seq); @@ -29,7 +30,6 @@ private static String CODE(String type, int seq) { public static final String INSUFFICIENT_PERMISSIONS = CODE(AUTH, 3); public static final String REFRESH_TOKEN_EXPIRED = CODE(AUTH, 4); public static final String REFRESH_TOKEN_INVALID = CODE(AUTH, 5); - public static final String TOKEN_REFRESH_FAILED = CODE(AUTH, 6); public static final String TOKEN_NOT_PROVIDED = CODE(AUTH, 7); public static final String REFRESH_TOKEN_NOT_EXISTS = CODE(AUTH, 8); public static final String Forbidden = CODE(AUTH, 9); @@ -48,7 +48,6 @@ private static String CODE(String type, int seq) { public static final String TimeOut = CODE(SERVER, 11); public static final String EnumMappingFailed = CODE(SERVER, 12); - public static final String ActivePictureGenerateRequestNotExists = CODE(PGREQ, 1); public static final String PictureGenerateRequestNotFound = CODE(PGREQ, 2); public static final String PictureGenerateRequestAlreadyInProgress = CODE(PGREQ, 3); @@ -69,7 +68,6 @@ private static String CODE(String type, int seq) { public static final String UserNotLoggedIn = CODE(USER, 6); public static final String UserAlreadySignedUp = CODE(USER, 7); - public static final String CreatorNotFound = CODE(CREATOR, 1); public static final String PictureGenerateResponseNotFound = CODE(PGRES, 1); @@ -109,5 +107,9 @@ private static String CODE(String type, int seq) { public static final String NoWebhookEmbeds = CODE(DISCORD, 1); public static final String DiscordIOException = CODE(DISCORD, 2); + public static final String FCM_TOKEN_NOT_FOUND = CODE(FCM, 1); + public static final String FCM_TOKEN_CONVERTING_JSON_ERROR = CODE(FCM, 2); + public static final String FCM_GOOGLE_REQUEST_TOKEN_ERROR = CODE(FCM, 3); + public static final String FCM_CONNECT_ERROR = CODE(FCM, 4); } diff --git a/genti-common/src/main/java/com/gt/genti/error/ResponseCode.java b/genti-common/src/main/java/com/gt/genti/error/ResponseCode.java index 9c09359a..f3bcac14 100644 --- a/genti-common/src/main/java/com/gt/genti/error/ResponseCode.java +++ b/genti-common/src/main/java/com/gt/genti/error/ResponseCode.java @@ -1,6 +1,7 @@ package com.gt.genti.error; import static com.gt.genti.constants.ErrorConstants.*; +import static org.springframework.http.HttpStatus.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -35,87 +36,87 @@ public enum ResponseCode { "로그인 정보가 일치하지 않습니다."), UnAuthorized(ErrorConstants.UnAuthorized, HttpStatus.UNAUTHORIZED, false, "인증되지 않은 사용자."), - EncryptAlgorithmDeprecated(ErrorConstants.EncryptAlgorithmDeprecated, HttpStatus.INTERNAL_SERVER_ERROR, false, + EncryptAlgorithmDeprecated(ErrorConstants.EncryptAlgorithmDeprecated, INTERNAL_SERVER_ERROR, false, "%s 암호화 과정 중 문제가 발생했습니다."), /** * Oauth */ - OauthProviderNotAllowed(ErrorConstants.OauthProviderNotAllowed, HttpStatus.NOT_ACCEPTABLE, false, + OauthProviderNotAllowed(ErrorConstants.OauthProviderNotAllowed, NOT_ACCEPTABLE, false, "허가되지 않은 oauth type %s"), - AppleOauthIdTokenIncorrect(ErrorConstants.AppleOauthIdTokenIncorrect, HttpStatus.BAD_REQUEST, false, + AppleOauthIdTokenIncorrect(ErrorConstants.AppleOauthIdTokenIncorrect, BAD_REQUEST, false, "Apple OAuth Identity Token 형식이 올바르지 않습니다."), - AppleOauthIdTokenExpired(ErrorConstants.AppleOauthIdTokenExpired, HttpStatus.REQUEST_TIMEOUT, false, + AppleOauthIdTokenExpired(ErrorConstants.AppleOauthIdTokenExpired, REQUEST_TIMEOUT, false, "Apple OAuth 로그인 중 Identity Token 유효기간이 만료됐습니다."), - AppleOauthIdTokenInvalid(ErrorConstants.AppleOauthIdTokenInvalid, HttpStatus.INTERNAL_SERVER_ERROR, false, + AppleOauthIdTokenInvalid(ErrorConstants.AppleOauthIdTokenInvalid, INTERNAL_SERVER_ERROR, false, "Apple OAuth Identity Token 값이 올바르지 않습니다."), - AppleOauthClaimInvalid(ErrorConstants.AppleOauthClaimInvalid, HttpStatus.INTERNAL_SERVER_ERROR, false, + AppleOauthClaimInvalid(ErrorConstants.AppleOauthClaimInvalid, INTERNAL_SERVER_ERROR, false, "Apple OAuth Claims 값이 올바르지 않습니다."), - AppleOauthPublicKeyInvalid(ErrorConstants.AppleOauthPublicKeyInvalid, HttpStatus.INTERNAL_SERVER_ERROR, false, + AppleOauthPublicKeyInvalid(ErrorConstants.AppleOauthPublicKeyInvalid, INTERNAL_SERVER_ERROR, false, "Apple OAuth 로그인 중 public key 생성에 문제가 발생했습니다."), - AppleOauthJwtValueInvalid(ErrorConstants.AppleOauthJwtValueInvalid, HttpStatus.INTERNAL_SERVER_ERROR, false, + AppleOauthJwtValueInvalid(ErrorConstants.AppleOauthJwtValueInvalid, INTERNAL_SERVER_ERROR, false, "Apple JWT 값의 alg, kid 정보가 올바르지 않습니다."), /** * 서버 오류 및 올바르지 않은 요청 */ - NotNullableEnum(ErrorConstants.NotNullableEnum, HttpStatus.BAD_REQUEST, false, " [%s] 값은 null 값을 허용하지 않습니다."), - DBToEnumFailed(ErrorConstants.DBToEnumFailed, HttpStatus.INTERNAL_SERVER_ERROR, false, + NotNullableEnum(ErrorConstants.NotNullableEnum, BAD_REQUEST, false, " [%s] 값은 null 값을 허용하지 않습니다."), + DBToEnumFailed(ErrorConstants.DBToEnumFailed, INTERNAL_SERVER_ERROR, false, "문자열 -> Enum 변환 실패 enum : %s 입력된 값 : %s"), - HandlerMethodValidation(ErrorConstants.HandlerMethodValidation, HttpStatus.BAD_REQUEST, false, "%s"), - UnrecognizedProperty(ErrorConstants.UnrecognizedProperty, HttpStatus.BAD_REQUEST, false, + HandlerMethodValidation(ErrorConstants.HandlerMethodValidation, BAD_REQUEST, false, "%s"), + UnrecognizedProperty(ErrorConstants.UnrecognizedProperty, BAD_REQUEST, false, "json property parsing 중 오류 발생, %s"), - InvalidDataAccessApiUsage(ErrorConstants.InvalidDataAccessApiUsage, HttpStatus.BAD_REQUEST, false, + InvalidDataAccessApiUsage(ErrorConstants.InvalidDataAccessApiUsage, BAD_REQUEST, false, "%s"), - HandlerNotFound(ErrorConstants.HandlerNotFound, HttpStatus.NOT_FOUND, false, + HandlerNotFound(ErrorConstants.HandlerNotFound, NOT_FOUND, false, "요청 uri에 대한 핸들러가 없습니다."), - HttpRequestMethodNotSupported(ErrorConstants.HttpRequestMethodNotSupported, HttpStatus.METHOD_NOT_ALLOWED, false, + HttpRequestMethodNotSupported(ErrorConstants.HttpRequestMethodNotSupported, METHOD_NOT_ALLOWED, false, "not allowed methods, allowed methods : %s"), - MethodArgumentTypeMismatch(ErrorConstants.MethodArgumentTypeMismatch, HttpStatus.BAD_REQUEST, false, "%s"), - MissingPathVariable(ErrorConstants.MissingPathVariableException, HttpStatus.BAD_REQUEST, false, + MethodArgumentTypeMismatch(ErrorConstants.MethodArgumentTypeMismatch, BAD_REQUEST, false, "%s"), + MissingPathVariable(ErrorConstants.MissingPathVariableException, BAD_REQUEST, false, "query param 에러 %s "), - UnHandledException(ErrorConstants.UnHandledException, HttpStatus.INTERNAL_SERVER_ERROR, false, + UnHandledException(ErrorConstants.UnHandledException, INTERNAL_SERVER_ERROR, false, "예기치 못한 문제가 발생했습니다. 오류내용 : %s"), - TimeOut(ErrorConstants.TimeOut, HttpStatus.REQUEST_TIMEOUT, false, "요청 시간이 초과되었습니다."), - EnumMappingFailed(ErrorConstants.EnumMappingFailed, HttpStatus.INTERNAL_SERVER_ERROR, false, + TimeOut(ErrorConstants.TimeOut, REQUEST_TIMEOUT, false, "요청 시간이 초과되었습니다."), + EnumMappingFailed(ErrorConstants.EnumMappingFailed, INTERNAL_SERVER_ERROR, false, "%s -> %s enum 매핑 중 예외 발생, 매핑 실패한 enum 값 : %s"), /** * Discord */ - NoWebhookEmbeds(ErrorConstants.NoWebhookEmbeds, HttpStatus.INTERNAL_SERVER_ERROR, false, + NoWebhookEmbeds(ErrorConstants.NoWebhookEmbeds, INTERNAL_SERVER_ERROR, false, "디스코드 Webhook embed 정보가 없습니다."), - DiscordIOException(ErrorConstants.DiscordIOException, HttpStatus.INTERNAL_SERVER_ERROR, false, + DiscordIOException(ErrorConstants.DiscordIOException, INTERNAL_SERVER_ERROR, false, "디스코드 IO 오류 발생 상세 : %s"), /** * PictureGenerateRequest */ - ActivePictureGenerateRequestNotExists(ErrorConstants.ActivePictureGenerateRequestNotExists, HttpStatus.NOT_FOUND, + ActivePictureGenerateRequestNotExists(ErrorConstants.ActivePictureGenerateRequestNotExists, NOT_FOUND, false, "현재 진행중인 요청이 없습니다."), - PictureGenerateRequestNotFound(ErrorConstants.PictureGenerateRequestNotFound, HttpStatus.NOT_FOUND, false, + PictureGenerateRequestNotFound(ErrorConstants.PictureGenerateRequestNotFound, NOT_FOUND, false, "사진 생성 요청을 찾지 못했습니다. 조회 조건 : %s"), - UnexpectedPictureGenerateRequestStatus(ErrorConstants.UnexpectedPictureGenerateRequestStatus, HttpStatus.CONFLICT, + UnexpectedPictureGenerateRequestStatus(ErrorConstants.UnexpectedPictureGenerateRequestStatus, CONFLICT, false, "현재 사진생성요청의 상태가 [%s]임, 요청을 수행할 수 없습니다."), - NoPictureGenerateRequest(ErrorConstants.NoPictureGenerateRequest, HttpStatus.NO_CONTENT, false, + NoPictureGenerateRequest(ErrorConstants.NoPictureGenerateRequest, NO_CONTENT, false, "사진 생성요청이 없습니다."), PictureGenerateRequestAlreadyInProgress(ErrorConstants.PictureGenerateRequestAlreadyInProgress, - HttpStatus.BAD_REQUEST, false, "이미 작업이 진행중인 요청은 수정이 불가합니다."), + BAD_REQUEST, false, "이미 작업이 진행중인 요청은 수정이 불가합니다."), PictureGenerateRequestNotAssignedToCreator(ErrorConstants.PictureGenerateRequestNotAssignedToCreator, - HttpStatus.BAD_REQUEST, + BAD_REQUEST, false, "현재 공급자에게 매칭된 요청이 아닙니다."), PictureGenerateRequestNotAcceptableDueToExpired(ErrorConstants.PictureGenerateRequestNotAcceptableDueToExpired, - HttpStatus.BAD_REQUEST, + BAD_REQUEST, false, "수락 마감 시간을 초과하였습니다."), OnlyRequesterCanViewPictureGenerateRequest(ErrorConstants.OnlyRequesterCanViewPictureGenerateRequest, - HttpStatus.FORBIDDEN, + FORBIDDEN, false, "사진생성요청을 요청한 유저만 볼 수 있습니다."), /** * User */ - AlreadyActivatedUser(ErrorConstants.AlreadyActivatedUser, HttpStatus.BAD_REQUEST, false, "비활성화 되지 않은 상태의 유저입니다."), - CannotRestoreUser(ErrorConstants.CannotRestoreUser, HttpStatus.BAD_REQUEST, false, + AlreadyActivatedUser(ErrorConstants.AlreadyActivatedUser, BAD_REQUEST, false, "비활성화 되지 않은 상태의 유저입니다."), + CannotRestoreUser(ErrorConstants.CannotRestoreUser, BAD_REQUEST, false, "탈퇴 후 한달이 지난 경우 재가입해야합니다. 찾은 userId : [%s]"), UserNotLoggedIn(ErrorConstants.UserNotLoggedIn, HttpStatus.UNAUTHORIZED, false, "로그아웃되었습니다. 다시 로그인해주세요"), WithDrawnUser(ErrorConstants.WithDrawnUser, HttpStatus.BAD_REQUEST, false, "탈퇴한 사용자입니다."), @@ -127,66 +128,79 @@ public enum ResponseCode { /** * Creator */ - CreatorNotFound(ErrorConstants.CreatorNotFound, HttpStatus.NOT_FOUND, false, "userId [%d] 에 해당하는 공급자를 찾을 수 없습니다."), + CreatorNotFound(ErrorConstants.CreatorNotFound, NOT_FOUND, false, "userId [%d] 에 해당하는 공급자를 찾을 수 없습니다."), /** * Picture */ - PictureNotFound(ErrorConstants.PictureNotFound, HttpStatus.NOT_FOUND, false, "사진을 찾을 수 없습니다."), - PictureUserFaceNotFound(ErrorConstants.PictureUserFaceNotFound, HttpStatus.NOT_FOUND, false, + PictureNotFound(ErrorConstants.PictureNotFound, NOT_FOUND, false, "사진을 찾을 수 없습니다."), + PictureUserFaceNotFound(ErrorConstants.PictureUserFaceNotFound, NOT_FOUND, false, "유저의 얼굴 사진을 찾지 못했습니다."), - PicturePoseNotFound(ErrorConstants.PicturePoseNotFound, HttpStatus.NOT_FOUND, false, "포즈 참고 사진을 찾지 못했습니다."), - PictureCompletedNotFound(ErrorConstants.PictureCompletedNotFound, HttpStatus.NOT_FOUND, false, + PicturePoseNotFound(ErrorConstants.PicturePoseNotFound, NOT_FOUND, false, "포즈 참고 사진을 찾지 못했습니다."), + PictureCompletedNotFound(ErrorConstants.PictureCompletedNotFound, NOT_FOUND, false, "최종 완성 사진을 찾지 못했습니다."), - PictureCreatedByCreatorNotFound(ErrorConstants.PictureCreatedByCreatorNotFound, HttpStatus.NOT_FOUND, false, + PictureCreatedByCreatorNotFound(ErrorConstants.PictureCreatedByCreatorNotFound, NOT_FOUND, false, "공급자가 제출한 1차 완성 사진을 찾지 못했습니다."), - PictureProfileNotFound(ErrorConstants.PictureProfileNotFound, HttpStatus.NOT_FOUND, false, + PictureProfileNotFound(ErrorConstants.PictureProfileNotFound, NOT_FOUND, false, "해당하는 유저 프로필 사진을 찾지 못했습니다."), - UploadFileTypeNotAvailable(ErrorConstants.UploadFileTypeNotAvailable, HttpStatus.NOT_ACCEPTABLE, false, + UploadFileTypeNotAvailable(ErrorConstants.UploadFileTypeNotAvailable, NOT_ACCEPTABLE, false, "업로드 가능한 파일이 아닙니다."), /** * PictureGenerateResponse */ - PictureGenerateResponseNotFound(ErrorConstants.PictureGenerateResponseNotFound, HttpStatus.NOT_FOUND, false, + PictureGenerateResponseNotFound(ErrorConstants.PictureGenerateResponseNotFound, NOT_FOUND, false, "해당하는 사진생성응답을 찾을 수 없습니다."), - FinalPictureNotUploadedYet(ErrorConstants.FinalPictureNotUploadedYet, HttpStatus.BAD_REQUEST, false, + FinalPictureNotUploadedYet(ErrorConstants.FinalPictureNotUploadedYet, BAD_REQUEST, false, "최종 작업 사진이 제출되지 않았습니다. 사진생성응답 id : [%d]"), - CreatorsPictureNotUploadedYet(ErrorConstants.CreatorsPictureNotUploadedYet, HttpStatus.BAD_REQUEST, false, + CreatorsPictureNotUploadedYet(ErrorConstants.CreatorsPictureNotUploadedYet, BAD_REQUEST, false, "공급자 작업 사진이 제출되지 않았습니다."), - RequestBlockedDueToPictureGenerateResponseStatus(PGRESStateException, HttpStatus.NOT_ACCEPTABLE, + RequestBlockedDueToPictureGenerateResponseStatus(PGRESStateException, NOT_ACCEPTABLE, false, "현재 사진생성응답 상태가 [%s] 이므로 해당 요청을 수행할 수 없습니다."), SubmitBlockedDueToPictureGenerateResponseIsExpired( - ErrorConstants.SubmitBlockedDueToPictureGenerateResponseIsExpired, HttpStatus.BAD_REQUEST, + ErrorConstants.SubmitBlockedDueToPictureGenerateResponseIsExpired, BAD_REQUEST, false, "제출 마감 시간을 초과하였습니다."), AlreadyCompletedPictureGenerateResponse(ErrorConstants.AlreadyCompletedPictureGenerateResponse, - HttpStatus.BAD_REQUEST, false, + BAD_REQUEST, false, "이미 처리 완료된 응답입니다."), /** * Report */ - ReportNotFound(ErrorConstants.ReportNotFound, HttpStatus.NOT_FOUND, false, "해당 report 건을 찾지 못했습니다."), + ReportNotFound(ErrorConstants.ReportNotFound, NOT_FOUND, false, "해당 report 건을 찾지 못했습니다."), /** * Deposit */ - DepositNotFound(ErrorConstants.DepositNotFound, HttpStatus.NOT_FOUND, false, "사용자의 포인트 정보를 불러올 수 없습니다."), - AddPointAmountCannotBeMinus(ErrorConstants.AddPointAmountCannotBeMinus, HttpStatus.BAD_REQUEST, false, + DepositNotFound(ErrorConstants.DepositNotFound, NOT_FOUND, false, "사용자의 포인트 정보를 불러올 수 없습니다."), + AddPointAmountCannotBeMinus(ErrorConstants.AddPointAmountCannotBeMinus, BAD_REQUEST, false, "적립될 포인트는 음수일 수 없습니다."), - NotEnoughBalance(ErrorConstants.NotEnoughBalance, HttpStatus.NOT_ACCEPTABLE, false, + NotEnoughBalance(ErrorConstants.NotEnoughBalance, NOT_ACCEPTABLE, false, "해당 공급자의 출금가능 잔액이 부족하여 요청을 완료할 수 없습니다."), /** * WithdrawRequest && Settlement */ CannotCreateWithdrawalDueToSettlementsNotAvailable( - ErrorConstants.CannotCreateWithdrawalDueToSettlementsNotAvailable, HttpStatus.BAD_REQUEST, false, + ErrorConstants.CannotCreateWithdrawalDueToSettlementsNotAvailable, BAD_REQUEST, false, "출금 가능한 정산 내역이 없습니다."), - WithdrawRequestNotFound(ErrorConstants.WithdrawRequestNotFound, HttpStatus.NOT_FOUND, false, + WithdrawRequestNotFound(ErrorConstants.WithdrawRequestNotFound, NOT_FOUND, false, "해당 출금 요청을 찾을 수 없습니다."), - HttpMessageNotReadable(ErrorConstants.HttpMessageNotReadable, HttpStatus.BAD_REQUEST, false, "잘못된 입력 : %s"), - FileTypeNotProvided(ErrorConstants.FileTypeNotProvided, HttpStatus.BAD_REQUEST, false, "파일 형식이 주어지지 않았습니다."); + HttpMessageNotReadable(ErrorConstants.HttpMessageNotReadable, BAD_REQUEST, false, "잘못된 입력 : %s"), + FileTypeNotProvided(ErrorConstants.FileTypeNotProvided, BAD_REQUEST, false, "파일 형식이 주어지지 않았습니다."), + + /** + * FCM + */ + + FCM_TOKEN_NOT_FOUND(ErrorConstants.FCM_TOKEN_NOT_FOUND, NOT_FOUND, false, + " id :%d 사용자의 기기를 구별할 수 있는 FCM 토큰이 존재하지 않습니다."), + FCM_TOKEN_CONVERTING_JSON_ERROR(ErrorConstants.FCM_TOKEN_CONVERTING_JSON_ERROR, INTERNAL_SERVER_ERROR, false, + "알림 메시지를 보낼 때 JSON으로 변환하는 과정에서 발생한 에러입니다."), + FCM_GOOGLE_REQUEST_TOKEN_ERROR(ErrorConstants.FCM_GOOGLE_REQUEST_TOKEN_ERROR, INTERNAL_SERVER_ERROR, false, + "파이어베이스 서버 접속 전 구글 통신 오류"), + FCM_CONNECT_ERROR(ErrorConstants.FCM_CONNECT_ERROR, INTERNAL_SERVER_ERROR, false, + "파이어베이스 서버 통신 오류 : %s"); private final String errorCode; private final HttpStatusCode httpStatusCode; diff --git a/genti-domain/src/main/java/com/gt/genti/common/baseentity/model/BaseEntity.java b/genti-domain/src/main/java/com/gt/genti/common/baseentity/model/BaseEntity.java index 107db995..552e1ffd 100644 --- a/genti-domain/src/main/java/com/gt/genti/common/baseentity/model/BaseEntity.java +++ b/genti-domain/src/main/java/com/gt/genti/common/baseentity/model/BaseEntity.java @@ -20,11 +20,11 @@ @EntityListeners(AuditingEntityListener.class) public abstract class BaseEntity extends BaseTimeEntity { - @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "created_by") User createdBy; - @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "modified_by") User modifiedBy; } diff --git a/genti-domain/src/main/java/com/gt/genti/fcm/model/FcmToken.java b/genti-domain/src/main/java/com/gt/genti/fcm/model/FcmToken.java new file mode 100644 index 00000000..5e3f2b24 --- /dev/null +++ b/genti-domain/src/main/java/com/gt/genti/fcm/model/FcmToken.java @@ -0,0 +1,33 @@ +package com.gt.genti.fcm.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class FcmToken { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(nullable = false) + private String token; + @Column(nullable = false, unique = true) + private Long userId; + + public FcmToken(final String token, final Long userId) { + this.token = token; + this.userId = userId; + } + + public void update(final String token) { + this.token = token; + } +} \ No newline at end of file diff --git a/genti-domain/src/main/java/com/gt/genti/fcm/repository/FcmTokenRepository.java b/genti-domain/src/main/java/com/gt/genti/fcm/repository/FcmTokenRepository.java new file mode 100644 index 00000000..316f0ddb --- /dev/null +++ b/genti-domain/src/main/java/com/gt/genti/fcm/repository/FcmTokenRepository.java @@ -0,0 +1,12 @@ +package com.gt.genti.fcm.repository; + +import java.util.Optional; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.gt.genti.fcm.model.FcmToken; + +public interface FcmTokenRepository extends JpaRepository { + + Optional findByUserId(final Long userId); +} \ No newline at end of file diff --git a/genti-domain/src/main/java/com/gt/genti/picturegeneraterequest/model/PictureGenerateRequest.java b/genti-domain/src/main/java/com/gt/genti/picturegeneraterequest/model/PictureGenerateRequest.java index 772448b0..3f763696 100644 --- a/genti-domain/src/main/java/com/gt/genti/picturegeneraterequest/model/PictureGenerateRequest.java +++ b/genti-domain/src/main/java/com/gt/genti/picturegeneraterequest/model/PictureGenerateRequest.java @@ -60,7 +60,7 @@ public class PictureGenerateRequest extends BaseTimeEntity { @JoinColumn(name = "creator_id") Creator creator; - @OneToMany(mappedBy = "request") + @OneToMany(mappedBy = "request", cascade = CascadeType.ALL, orphanRemoval = true) List responseList = new ArrayList<>(); @ManyToMany(cascade = CascadeType.REMOVE) diff --git a/genti-domain/src/main/java/com/gt/genti/picturegenerateresponse/model/PictureGenerateResponse.java b/genti-domain/src/main/java/com/gt/genti/picturegenerateresponse/model/PictureGenerateResponse.java index 1fd3e28c..1411e775 100644 --- a/genti-domain/src/main/java/com/gt/genti/picturegenerateresponse/model/PictureGenerateResponse.java +++ b/genti-domain/src/main/java/com/gt/genti/picturegenerateresponse/model/PictureGenerateResponse.java @@ -13,18 +13,8 @@ import com.gt.genti.picture.createdbycreator.model.PictureCreatedByCreator; import com.gt.genti.picturegeneraterequest.model.PictureGenerateRequest; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Convert; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; -import jakarta.persistence.PrePersist; -import jakarta.persistence.Table; +import com.gt.genti.report.model.Report; +import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -69,6 +59,9 @@ public class PictureGenerateResponse extends BaseTimeEntity { @Column(name = "star") Integer star; + @OneToOne(mappedBy = "pictureGenerateResponse", fetch = FetchType.LAZY, cascade = CascadeType.ALL) + Report report; + @PrePersist public void prePersist() { if (this.memo == null) { diff --git a/genti-domain/src/main/java/com/gt/genti/user/model/User.java b/genti-domain/src/main/java/com/gt/genti/user/model/User.java index 81fe102c..f584524e 100644 --- a/genti-domain/src/main/java/com/gt/genti/user/model/User.java +++ b/genti-domain/src/main/java/com/gt/genti/user/model/User.java @@ -17,8 +17,11 @@ import com.gt.genti.deposit.model.Deposit; import com.gt.genti.error.ExpectedException; import com.gt.genti.error.ResponseCode; +import com.gt.genti.picture.completed.model.PictureCompleted; +import com.gt.genti.picture.pose.model.PicturePose; import com.gt.genti.picture.profile.model.PictureProfile; import com.gt.genti.picture.userface.model.PictureUserFace; +import com.gt.genti.picturegeneraterequest.model.PictureGenerateRequest; import com.gt.genti.user.UserSerializer; import jakarta.persistence.CascadeType; @@ -123,6 +126,15 @@ public class User extends BaseTimeEntity { @Column(name = "birth_date", length = 4) String birthDate; + @OneToMany(mappedBy = "uploadedBy", cascade = CascadeType.ALL, orphanRemoval = true) + List picturePoseList; + + @OneToMany(mappedBy = "requester", cascade = CascadeType.ALL, orphanRemoval = true) + List pictureGenerateRequestList; + + @OneToMany(mappedBy = "requester", cascade = CascadeType.ALL, orphanRemoval = true) + List pictureCompletedList; + @PrePersist public void prePersist() { if (this.userStatus == null) { diff --git a/genti-external/build.gradle b/genti-external/build.gradle index 862c86b8..77138945 100644 --- a/genti-external/build.gradle +++ b/genti-external/build.gradle @@ -21,13 +21,17 @@ dependencies { implementation("org.springframework.cloud:spring-cloud-starter-openfeign:4.1.0") + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + // fcm + implementation 'com.google.firebase:firebase-admin:9.2.0' + + // test && test h2 testImplementation 'org.springframework.boot:spring-boot-starter-test' // testRuntimeOnly 'com.h2database:h2' runtimeOnly 'com.h2database:h2' implementation 'org.apache.commons:commons-text:1.10.0' - implementation project(":genti-common") - + implementation project(":genti-domain") } \ No newline at end of file diff --git a/genti-external/src/main/java/com/gt/genti/firebase/FCMService.java b/genti-external/src/main/java/com/gt/genti/firebase/FCMService.java deleted file mode 100644 index 15c82520..00000000 --- a/genti-external/src/main/java/com/gt/genti/firebase/FCMService.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.gt.genti.firebase; - -import org.springframework.stereotype.Component; - -@Component -public class FCMService { -} diff --git a/genti-external/src/main/java/com/gt/genti/firebase/client/FirebaseCloudMessageClient.java b/genti-external/src/main/java/com/gt/genti/firebase/client/FirebaseCloudMessageClient.java new file mode 100644 index 00000000..864ef52c --- /dev/null +++ b/genti-external/src/main/java/com/gt/genti/firebase/client/FirebaseCloudMessageClient.java @@ -0,0 +1,98 @@ +package com.gt.genti.firebase.client; + +import static com.gt.genti.firebase.common.NotificationType.*; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestClient; + +import com.google.auth.oauth2.GoogleCredentials; +import com.gt.genti.error.ExpectedException; +import com.gt.genti.error.ResponseCode; +import com.gt.genti.fcm.model.FcmToken; +import com.gt.genti.fcm.repository.FcmTokenRepository; +import com.gt.genti.firebase.common.Notification; +import com.gt.genti.firebase.common.NotificationType; +import com.gt.genti.firebase.event.NotificationEvent; +import com.gt.genti.firebase.generator.NotificationMessageGenerator; +import com.gt.genti.firebase.generator.PictureGenerationCompletedMessageGenerator; +import com.gt.genti.firebase.generator.PictureGenerationFailedMessageGenerator; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Service +@RequiredArgsConstructor +@Slf4j +public class FirebaseCloudMessageClient { + + private static final String PREFIX_ACCESS_TOKEN = "Bearer "; + private static final String PREFIX_FCM_REQUEST_URL = "https://fcm.googleapis.com:443/v1/projects/"; + private static final String POSTFIX_FCM_REQUEST_URL = "/messages:send"; + private static final String FIREBASE_KEY_PATH = "/firebase-genti.json"; + private static final String GOOGLE_AUTH_URL = "https://www.googleapis.com/auth/cloud-platform"; + private static final Map> GENERATOR_MAP = + Map.of( + PICTURE_GENERATION_COMPLETED, PictureGenerationCompletedMessageGenerator::new, + PICTURE_GENERATION_FAILED, PictureGenerationFailedMessageGenerator::new + ); + + private final RestClient restClient = RestClient.create(); + private final FcmTokenRepository fcmTokenRepository; + + @Value("${firebase.project_id}") + private String projectId; + + public void sendMessageTo(final NotificationEvent notificationEvent) { + sendMessageTo( + notificationEvent.getReceiverId(), + GENERATOR_MAP.get(notificationEvent.getNotificationType()).apply(notificationEvent) + ); + } + + private void sendMessageTo( + final Long receiverId, + final NotificationMessageGenerator messageGenerator + ) { + final FcmToken fcmToken = fcmTokenRepository.findByUserId(receiverId) + .orElseThrow(() -> ExpectedException.withLogging(ResponseCode.FCM_TOKEN_NOT_FOUND, receiverId)); + + final Notification testNotification = new Notification("title", "body"); + final String message = messageGenerator.makeMessage( + fcmToken.getToken(), testNotification + ); + + final String fcmRequestUrl = PREFIX_FCM_REQUEST_URL + projectId + POSTFIX_FCM_REQUEST_URL; + + restClient.post() + .uri(fcmRequestUrl) + .contentType(MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE)) + .header("Authorization", PREFIX_ACCESS_TOKEN + getAccessToken()) + .body(message) + .retrieve(); + // .onStatus(HttpStatusCode::is4xxClientError, (req, res) -> { + // throw new RuntimeException(res); + // }).onStatus() + } + + private String getAccessToken() { + try { + final GoogleCredentials googleCredentials = GoogleCredentials + .fromStream(new ClassPathResource(FIREBASE_KEY_PATH).getInputStream()) + .createScoped(List.of(GOOGLE_AUTH_URL)); + + googleCredentials.refreshIfExpired(); + + return googleCredentials.getAccessToken().getTokenValue(); + } catch (IOException e) { + throw ExpectedException.withLogging(ResponseCode.FCM_GOOGLE_REQUEST_TOKEN_ERROR); + } + } +} \ No newline at end of file diff --git a/genti-external/src/main/java/com/gt/genti/firebase/common/Notification.java b/genti-external/src/main/java/com/gt/genti/firebase/common/Notification.java new file mode 100644 index 00000000..f996d88d --- /dev/null +++ b/genti-external/src/main/java/com/gt/genti/firebase/common/Notification.java @@ -0,0 +1,13 @@ +package com.gt.genti.firebase.common; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +@AllArgsConstructor +public class Notification { + String title; + String body; +} diff --git a/genti-external/src/main/java/com/gt/genti/firebase/common/NotificationData.java b/genti-external/src/main/java/com/gt/genti/firebase/common/NotificationData.java new file mode 100644 index 00000000..6ab03dd2 --- /dev/null +++ b/genti-external/src/main/java/com/gt/genti/firebase/common/NotificationData.java @@ -0,0 +1,4 @@ +package com.gt.genti.firebase.common; + +public class NotificationData { +} diff --git a/genti-external/src/main/java/com/gt/genti/firebase/common/NotificationType.java b/genti-external/src/main/java/com/gt/genti/firebase/common/NotificationType.java new file mode 100644 index 00000000..d243780e --- /dev/null +++ b/genti-external/src/main/java/com/gt/genti/firebase/common/NotificationType.java @@ -0,0 +1,14 @@ +package com.gt.genti.firebase.common; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public enum NotificationType { + + PICTURE_GENERATION_FAILED("PICTURE_GENERATION_FAILED"), + PICTURE_GENERATION_COMPLETED("PICTURE_GENERATION_COMPLETED"); + + private final String type; +} \ No newline at end of file diff --git a/genti-external/src/main/java/com/gt/genti/firebase/controller/FcmTokenRegisterController.java b/genti-external/src/main/java/com/gt/genti/firebase/controller/FcmTokenRegisterController.java new file mode 100644 index 00000000..56492d36 --- /dev/null +++ b/genti-external/src/main/java/com/gt/genti/firebase/controller/FcmTokenRegisterController.java @@ -0,0 +1,22 @@ +package com.gt.genti.firebase.controller; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import com.gt.genti.firebase.dto.request.FcmTokenSaveOrUpdateRequestDto; +import com.gt.genti.firebase.service.FcmTokenRegisterService; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +public class FcmTokenRegisterController { + + private final FcmTokenRegisterService fcmTokenRegisterService; + + @PostMapping("/notifications/token") + public void createFcmToken(@RequestBody final FcmTokenSaveOrUpdateRequestDto fcmTokenSaveOrUpdateRequestDto) { + fcmTokenRegisterService.registerFcmToken(fcmTokenSaveOrUpdateRequestDto); + } +} \ No newline at end of file diff --git a/genti-external/src/main/java/com/gt/genti/firebase/dto/request/FcmTokenSaveOrUpdateRequestDto.java b/genti-external/src/main/java/com/gt/genti/firebase/dto/request/FcmTokenSaveOrUpdateRequestDto.java new file mode 100644 index 00000000..26579b1b --- /dev/null +++ b/genti-external/src/main/java/com/gt/genti/firebase/dto/request/FcmTokenSaveOrUpdateRequestDto.java @@ -0,0 +1,12 @@ +package com.gt.genti.firebase.dto.request; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public class FcmTokenSaveOrUpdateRequestDto { + + private final String token; + private final Long userId; +} \ No newline at end of file diff --git a/genti-external/src/main/java/com/gt/genti/firebase/event/NotificationEvent.java b/genti-external/src/main/java/com/gt/genti/firebase/event/NotificationEvent.java new file mode 100644 index 00000000..b4a4e61f --- /dev/null +++ b/genti-external/src/main/java/com/gt/genti/firebase/event/NotificationEvent.java @@ -0,0 +1,17 @@ +package com.gt.genti.firebase.event; + +import com.gt.genti.firebase.common.Notification; +import com.gt.genti.firebase.common.NotificationData; +import com.gt.genti.firebase.common.NotificationType; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public abstract class NotificationEvent { + private final Long receiverId; + private final NotificationData notificationData; + private final NotificationType notificationType; + private final Notification notification; +} \ No newline at end of file diff --git a/genti-external/src/main/java/com/gt/genti/firebase/event/NotificationEventListener.java b/genti-external/src/main/java/com/gt/genti/firebase/event/NotificationEventListener.java new file mode 100644 index 00000000..9a06a5ad --- /dev/null +++ b/genti-external/src/main/java/com/gt/genti/firebase/event/NotificationEventListener.java @@ -0,0 +1,27 @@ +package com.gt.genti.firebase.event; + +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionalEventListener; + +import com.gt.genti.firebase.client.FirebaseCloudMessageClient; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +@RequiredArgsConstructor +public class NotificationEventListener { + + private final FirebaseCloudMessageClient firebaseCloudMessageClient; + + @Transactional(propagation = Propagation.REQUIRES_NEW) + @TransactionalEventListener + public void createNotification(final NotificationEvent notificationEvent) { + firebaseCloudMessageClient.sendMessageTo( + notificationEvent + ); + } +} \ No newline at end of file diff --git a/genti-external/src/main/java/com/gt/genti/firebase/event/PictureGenerationCompletedNotificationEvent.java b/genti-external/src/main/java/com/gt/genti/firebase/event/PictureGenerationCompletedNotificationEvent.java new file mode 100644 index 00000000..744f2c0b --- /dev/null +++ b/genti-external/src/main/java/com/gt/genti/firebase/event/PictureGenerationCompletedNotificationEvent.java @@ -0,0 +1,21 @@ +package com.gt.genti.firebase.event; + +import com.gt.genti.firebase.common.Notification; +import com.gt.genti.firebase.common.NotificationType; + +import lombok.Getter; + +@Getter +public class PictureGenerationCompletedNotificationEvent extends NotificationEvent { + + private static final Notification notification = new Notification("사진 생성이 완료되었습니다.", "사진 생성이 완료되었습니다."); + + private PictureGenerationCompletedNotificationEvent( Long receiverId + ) { + super(receiverId, null, NotificationType.PICTURE_GENERATION_COMPLETED, notification); + } + + public static PictureGenerationCompletedNotificationEvent of(Long receiverId) { + return new PictureGenerationCompletedNotificationEvent(receiverId); + } +} \ No newline at end of file diff --git a/genti-external/src/main/java/com/gt/genti/firebase/event/PictureGenerationFailedNotificationEvent.java b/genti-external/src/main/java/com/gt/genti/firebase/event/PictureGenerationFailedNotificationEvent.java new file mode 100644 index 00000000..27dd3e92 --- /dev/null +++ b/genti-external/src/main/java/com/gt/genti/firebase/event/PictureGenerationFailedNotificationEvent.java @@ -0,0 +1,21 @@ +package com.gt.genti.firebase.event; + +import com.gt.genti.firebase.common.Notification; +import com.gt.genti.firebase.common.NotificationType; + +import lombok.Getter; + +@Getter +public class PictureGenerationFailedNotificationEvent extends NotificationEvent { + + private static final Notification notification = new Notification("사진 생성중 오류가 발생했습니다.", "사진 생성중 오류가 발생했습니다."); + + private PictureGenerationFailedNotificationEvent(Long receiverId) { + super(receiverId, null, NotificationType.PICTURE_GENERATION_FAILED, notification); + } + + public static PictureGenerationFailedNotificationEvent of(Long receiverId) { + return new PictureGenerationFailedNotificationEvent(receiverId); + } + +} \ No newline at end of file diff --git a/genti-external/src/main/java/com/gt/genti/firebase/generator/NotificationMessageGenerator.java b/genti-external/src/main/java/com/gt/genti/firebase/generator/NotificationMessageGenerator.java new file mode 100644 index 00000000..6291479d --- /dev/null +++ b/genti-external/src/main/java/com/gt/genti/firebase/generator/NotificationMessageGenerator.java @@ -0,0 +1,11 @@ +package com.gt.genti.firebase.generator; + +import com.gt.genti.firebase.common.Notification; + +public interface NotificationMessageGenerator { + + String makeMessage( + final String targetToken, + final Notification notification + ); +} \ No newline at end of file diff --git a/genti-external/src/main/java/com/gt/genti/firebase/generator/PictureGenerationCompletedMessageGenerator.java b/genti-external/src/main/java/com/gt/genti/firebase/generator/PictureGenerationCompletedMessageGenerator.java new file mode 100644 index 00000000..02810f15 --- /dev/null +++ b/genti-external/src/main/java/com/gt/genti/firebase/generator/PictureGenerationCompletedMessageGenerator.java @@ -0,0 +1,35 @@ +package com.gt.genti.firebase.generator; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.gt.genti.error.ExpectedException; +import com.gt.genti.error.ResponseCode; +import com.gt.genti.firebase.event.NotificationEvent; +import com.gt.genti.firebase.message.PictureGenerateCompleteMessage; +import com.gt.genti.firebase.common.Notification; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class PictureGenerationCompletedMessageGenerator implements NotificationMessageGenerator { + private final NotificationEvent event; + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public String makeMessage( + final String targetDeviceIdToken, + final Notification notification + ) { + + final PictureGenerateCompleteMessage pictureGenerateCompleteMessage = new PictureGenerateCompleteMessage( + new PictureGenerateCompleteMessage.Message(notification, targetDeviceIdToken, + PictureGenerateCompleteMessage.Data.from(event)) + ); + + try { + return objectMapper.writeValueAsString(pictureGenerateCompleteMessage); + } catch (JsonProcessingException e) { + throw ExpectedException.withLogging(ResponseCode.FCM_TOKEN_CONVERTING_JSON_ERROR); + } + } +} \ No newline at end of file diff --git a/genti-external/src/main/java/com/gt/genti/firebase/generator/PictureGenerationFailedMessageGenerator.java b/genti-external/src/main/java/com/gt/genti/firebase/generator/PictureGenerationFailedMessageGenerator.java new file mode 100644 index 00000000..9e8698c7 --- /dev/null +++ b/genti-external/src/main/java/com/gt/genti/firebase/generator/PictureGenerationFailedMessageGenerator.java @@ -0,0 +1,36 @@ +package com.gt.genti.firebase.generator; + +import static com.gt.genti.firebase.message.PictureGenerationFailedMessage.*; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.gt.genti.error.ExpectedException; +import com.gt.genti.error.ResponseCode; +import com.gt.genti.firebase.event.NotificationEvent; +import com.gt.genti.firebase.message.PictureGenerationFailedMessage; +import com.gt.genti.firebase.common.Notification; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class PictureGenerationFailedMessageGenerator implements NotificationMessageGenerator { + + private final ObjectMapper objectMapper = new ObjectMapper(); + private final NotificationEvent notificationEvent; + + @Override + public String makeMessage( + final String targetToken, + final Notification notification + ) { + try { + final PictureGenerationFailedMessage message = new PictureGenerationFailedMessage( + new Message(Data.from(notificationEvent), targetToken, notification) + ); + return objectMapper.writeValueAsString(message); + + } catch (JsonProcessingException e) { + throw ExpectedException.withLogging(ResponseCode.FCM_TOKEN_CONVERTING_JSON_ERROR); + } + } +} \ No newline at end of file diff --git a/genti-external/src/main/java/com/gt/genti/firebase/message/PictureGenerateCompleteMessage.java b/genti-external/src/main/java/com/gt/genti/firebase/message/PictureGenerateCompleteMessage.java new file mode 100644 index 00000000..5f06505d --- /dev/null +++ b/genti-external/src/main/java/com/gt/genti/firebase/message/PictureGenerateCompleteMessage.java @@ -0,0 +1,19 @@ +package com.gt.genti.firebase.message; + +import com.gt.genti.firebase.common.Notification; +import com.gt.genti.firebase.event.NotificationEvent; + +public record PictureGenerateCompleteMessage( + Message message) { + + public record Message(Notification notification, String token, Data data) { + } + + public record Data(String notificationType) { + public static Data from(final NotificationEvent notificationEvent) { + return new Data( + notificationEvent.getNotificationType().getType() + ); + } + } +} \ No newline at end of file diff --git a/genti-external/src/main/java/com/gt/genti/firebase/message/PictureGenerationFailedMessage.java b/genti-external/src/main/java/com/gt/genti/firebase/message/PictureGenerationFailedMessage.java new file mode 100644 index 00000000..5b519389 --- /dev/null +++ b/genti-external/src/main/java/com/gt/genti/firebase/message/PictureGenerationFailedMessage.java @@ -0,0 +1,20 @@ +package com.gt.genti.firebase.message; + +import com.gt.genti.firebase.common.Notification; +import com.gt.genti.firebase.event.NotificationEvent; + +public record PictureGenerationFailedMessage( + Message message) { + + public record Message(Data data, String token, Notification notification) { + } + + public record Data(String notificationType) { + + public static Data from(final NotificationEvent notificationEvent) { + return new Data( + notificationEvent.getNotificationType().getType() + ); + } + } +} \ No newline at end of file diff --git a/genti-external/src/main/java/com/gt/genti/firebase/service/FcmTokenRegisterService.java b/genti-external/src/main/java/com/gt/genti/firebase/service/FcmTokenRegisterService.java new file mode 100644 index 00000000..a06b1d07 --- /dev/null +++ b/genti-external/src/main/java/com/gt/genti/firebase/service/FcmTokenRegisterService.java @@ -0,0 +1,40 @@ +package com.gt.genti.firebase.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.gt.genti.error.ExpectedException; +import com.gt.genti.error.ResponseCode; +import com.gt.genti.fcm.model.FcmToken; +import com.gt.genti.fcm.repository.FcmTokenRepository; +import com.gt.genti.firebase.dto.request.FcmTokenSaveOrUpdateRequestDto; +import com.gt.genti.user.repository.UserRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +@Transactional +public class FcmTokenRegisterService { + + private final FcmTokenRepository fcmTokenRepository; + private final UserRepository userRepository; + + public void registerFcmToken(final FcmTokenSaveOrUpdateRequestDto fcmTokenSaveOrUpdateRequestDto) { + final Long memberId = fcmTokenSaveOrUpdateRequestDto.getUserId(); + final String token = fcmTokenSaveOrUpdateRequestDto.getToken(); + + checkUserExists(memberId); + fcmTokenRepository.findByUserId(memberId) + .ifPresentOrElse( + foundFcmToken -> foundFcmToken.update(token), + () -> fcmTokenRepository.save(new FcmToken(token, memberId)) + ); + } + + private void checkUserExists(final Long userId) { + if (!userRepository.existsById(userId)) { + throw ExpectedException.withLogging(ResponseCode.UserNotFound, userId); + } + } +} \ No newline at end of file