diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/auth/service/RefreshUseCase.java b/DuDoong-Api/src/main/java/band/gosrock/api/auth/service/RefreshUseCase.java index 40e21e21..693dac41 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/auth/service/RefreshUseCase.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/auth/service/RefreshUseCase.java @@ -9,6 +9,7 @@ import band.gosrock.domain.domains.user.adaptor.UserAdaptor; import band.gosrock.domain.domains.user.domain.RefreshTokenEntity; import band.gosrock.domain.domains.user.domain.User; +import band.gosrock.domain.domains.user.service.UserDomainService; import lombok.RequiredArgsConstructor; @UseCase @@ -17,6 +18,7 @@ public class RefreshUseCase { private final UserAdaptor userAdaptor; private final JwtTokenProvider jwtTokenProvider; + private final UserDomainService userDomainService; private final RefreshTokenAdaptor refreshTokenAdaptor; private final TokenGenerateHelper tokenGenerateHelper; @@ -29,7 +31,8 @@ public TokenAndUserResponse execute(String refreshToken) { jwtTokenProvider.parseRefreshToken(savedRefreshTokenEntity.getRefreshToken()); User user = userAdaptor.queryUser(refreshUserId); - + // 리프레쉬 시에도 last로그인 정보 업데이트 + userDomainService.loginUser(user.getOauthInfo()); return tokenGenerateHelper.execute(user); } } diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/auth/service/helper/TokenGenerateHelper.java b/DuDoong-Api/src/main/java/band/gosrock/api/auth/service/helper/TokenGenerateHelper.java index 239d6331..35a1627b 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/auth/service/helper/TokenGenerateHelper.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/auth/service/helper/TokenGenerateHelper.java @@ -9,6 +9,7 @@ import band.gosrock.domain.domains.user.domain.RefreshTokenEntity; import band.gosrock.domain.domains.user.domain.User; import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; @Helper @RequiredArgsConstructor @@ -18,6 +19,7 @@ public class TokenGenerateHelper { private final RefreshTokenAdaptor refreshTokenAdaptor; + @Transactional public TokenAndUserResponse execute(User user) { String newAccessToken = jwtTokenProvider.generateAccessToken( diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/comment/controller/CommentController.java b/DuDoong-Api/src/main/java/band/gosrock/api/comment/controller/CommentController.java index 49be246e..18ad93b6 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/comment/controller/CommentController.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/comment/controller/CommentController.java @@ -12,26 +12,31 @@ import band.gosrock.api.comment.service.RetrieveCommentUseCase; import band.gosrock.api.comment.service.RetrieveRandomCommentUseCase; import band.gosrock.api.common.slice.SliceResponse; +import band.gosrock.common.annotation.DisableSwaggerSecurity; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; import javax.validation.Valid; +import javax.validation.constraints.Min; import lombok.RequiredArgsConstructor; import org.springdoc.api.annotations.ParameterObject; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; 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; @SecurityRequirement(name = "access-token") @Tag(name = "응원톡 컨트롤러") @RestController @RequestMapping("/v1/events/{eventId}/comments") +@Validated @RequiredArgsConstructor public class CommentController { @@ -53,6 +58,7 @@ public CreateCommentResponse postComment( return createCommentUseCase.execute(eventId, createCommentRequest); } + @DisableSwaggerSecurity @Operation(summary = "응원글을 조회합니다.") @GetMapping public SliceResponse getComments( @@ -67,15 +73,19 @@ public void deleteComment(@PathVariable Long eventId, @PathVariable Long comment deleteCommentUseCase.execute(eventId, commentId); } + @DisableSwaggerSecurity @Operation(summary = "응원글 개수를 카운팅합니다.") @GetMapping(value = "/counts") public RetrieveCommentCountResponse getCommentCounts(@PathVariable Long eventId) { return retrieveCommentCountUseCase.execute(eventId); } + @DisableSwaggerSecurity @Operation(summary = "응원글을 랜덤으로 뽑아옵니다.") @GetMapping(value = "/random") - public RetrieveRandomCommentResponse getRandomComment(@PathVariable Long eventId) { - return retrieveRandomCommentUseCase.execute(eventId); + public RetrieveRandomCommentResponse getRandomComment( + @PathVariable Long eventId, + @RequestParam @Min(value = 1L, message = "limit 값은 0보다 커야 합니다.") Long limit) { + return retrieveRandomCommentUseCase.execute(eventId, limit); } } diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/comment/mapper/CommentMapper.java b/DuDoong-Api/src/main/java/band/gosrock/api/comment/mapper/CommentMapper.java index 1f683319..49bb79b5 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/comment/mapper/CommentMapper.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/comment/mapper/CommentMapper.java @@ -13,6 +13,7 @@ import band.gosrock.domain.domains.comment.dto.condition.CommentCondition; import band.gosrock.domain.domains.event.domain.Event; import band.gosrock.domain.domains.user.domain.User; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Slice; import org.springframework.transaction.annotation.Transactional; @@ -49,8 +50,8 @@ public RetrieveCommentCountResponse toRetrieveCommentCountResponse(Long commentC return RetrieveCommentCountResponse.of(commentCount); } - public RetrieveRandomCommentResponse toRetrieveRandomCommentResponse(Comment comment) { - return RetrieveRandomCommentResponse.of(comment); + public RetrieveRandomCommentResponse toRetrieveRandomCommentResponse(List comments) { + return RetrieveRandomCommentResponse.of(comments); } private RetrieveCommentDTO toRetrieveCommentDTO(Comment comment, Long currentUserId) { diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/comment/model/response/RetrieveRandomCommentResponse.java b/DuDoong-Api/src/main/java/band/gosrock/api/comment/model/response/RetrieveRandomCommentResponse.java index f2cd2b19..934321e0 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/comment/model/response/RetrieveRandomCommentResponse.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/comment/model/response/RetrieveRandomCommentResponse.java @@ -3,6 +3,7 @@ import band.gosrock.domain.common.vo.CommentInfoVo; import band.gosrock.domain.domains.comment.domain.Comment; +import java.util.List; import lombok.Builder; import lombok.Getter; @@ -10,11 +11,11 @@ @Builder public class RetrieveRandomCommentResponse { - private final CommentInfoVo commentInfo; + private final List commentInfos; - public static RetrieveRandomCommentResponse of(Comment comment) { + public static RetrieveRandomCommentResponse of(List comments) { return RetrieveRandomCommentResponse.builder() - .commentInfo(comment.toCommentInfoVo()) + .commentInfos(comments.stream().map(Comment::toCommentInfoVo).toList()) .build(); } } diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/comment/service/RetrieveRandomCommentUseCase.java b/DuDoong-Api/src/main/java/band/gosrock/api/comment/service/RetrieveRandomCommentUseCase.java index 511c2399..ae35fe59 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/comment/service/RetrieveRandomCommentUseCase.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/comment/service/RetrieveRandomCommentUseCase.java @@ -8,6 +8,7 @@ import band.gosrock.domain.domains.comment.domain.Comment; import band.gosrock.domain.domains.event.adaptor.EventAdaptor; import band.gosrock.domain.domains.event.domain.Event; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; @@ -22,9 +23,9 @@ public class RetrieveRandomCommentUseCase { private final EventAdaptor eventAdaptor; @Transactional(readOnly = true) - public RetrieveRandomCommentResponse execute(Long eventId) { + public RetrieveRandomCommentResponse execute(Long eventId, Long limit) { Event event = eventAdaptor.findById(eventId); - Comment comment = commentAdaptor.queryRandomComment(event.getId()); - return commentMapper.toRetrieveRandomCommentResponse(comment); + List comments = commentAdaptor.queryRandomComment(event.getId(), limit); + return commentMapper.toRetrieveRandomCommentResponse(comments); } } diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/config/response/GlobalExceptionHandler.java b/DuDoong-Api/src/main/java/band/gosrock/api/config/response/GlobalExceptionHandler.java index 214faf42..baf23174 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/config/response/GlobalExceptionHandler.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/config/response/GlobalExceptionHandler.java @@ -2,7 +2,7 @@ import band.gosrock.api.config.security.SecurityUtils; -import band.gosrock.api.config.slack.SlackApiProvider; +import band.gosrock.api.slack.sender.SlackInternalErrorSender; import band.gosrock.common.dto.ErrorReason; import band.gosrock.common.dto.ErrorResponse; import band.gosrock.common.exception.BaseErrorCode; @@ -39,7 +39,7 @@ @RequiredArgsConstructor public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { - private final SlackApiProvider slackApiProvider; + private final SlackInternalErrorSender slackInternalErrorSender; // /** Json 날짜 형식 파싱에 대한 에러 핸들러 */ // @ExceptionHandler({ InvalidFormatException.class, DateTimeParseException.class}) // public ResponseEntity JsonParseExceptionHandler( @@ -181,7 +181,7 @@ protected ResponseEntity handleException(Exception e, HttpServlet internalServerError.getReason(), url); - slackApiProvider.sendError(cachingRequest, e, userId); + slackInternalErrorSender.execute(cachingRequest, e, userId); return ResponseEntity.status(HttpStatus.valueOf(internalServerError.getStatus())) .body(errorResponse); } diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/config/security/SecurityConfig.java b/DuDoong-Api/src/main/java/band/gosrock/api/config/security/SecurityConfig.java index 7e8d6082..425869b0 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/config/security/SecurityConfig.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/config/security/SecurityConfig.java @@ -37,6 +37,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .permitAll() .mvcMatchers(HttpMethod.GET, "/v1/events/{eventId:[\\d+]}/ticketItems") .permitAll() + .mvcMatchers(HttpMethod.GET, "/v1/events/{eventId:[\\d+]}/comments/**") + .permitAll() .mvcMatchers(HttpMethod.POST, "/v1/coupons/campaigns") .hasRole("SUPER_ADMIN") .anyRequest() diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/event/controller/EventController.java b/DuDoong-Api/src/main/java/band/gosrock/api/event/controller/EventController.java index c06cb73d..679f7f18 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/event/controller/EventController.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/event/controller/EventController.java @@ -36,6 +36,7 @@ public class EventController { private final UpdateEventBasicUseCase updateEventBasicUseCase; private final UpdateEventDetailUseCase updateEventDetailUseCase; private final UpdateEventStatusUseCase updateEventStatusUseCase; + private final OpenEventUseCase openEventStatusUseCase; @Operation(summary = "자신이 관리 중인 이벤트 리스트를 가져옵니다.") @GetMapping @@ -79,7 +80,13 @@ public EventResponse updateEventDetail( return updateEventDetailUseCase.execute(eventId, updateEventDetailRequest); } - @Operation(summary = "공연 상태를 변경합니다.") + @Operation(summary = "공연을 오픈 상태로 변경합니다. 모든 체크리스트를 달성해야 합니다.") + @PatchMapping("/{eventId}/open") + public EventResponse updateEventStatus(@PathVariable Long eventId) { + return openEventStatusUseCase.execute(eventId); + } + + @Operation(summary = "공연 상태를 변경합니다. (OPEN 제외)") @PatchMapping("/{eventId}/status") public EventResponse updateEventStatus( @PathVariable Long eventId, diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/event/model/dto/request/CreateEventRequest.java b/DuDoong-Api/src/main/java/band/gosrock/api/event/model/dto/request/CreateEventRequest.java index 340ad527..e680dcbb 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/event/model/dto/request/CreateEventRequest.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/event/model/dto/request/CreateEventRequest.java @@ -9,6 +9,7 @@ import javax.validation.constraints.Positive; import lombok.Getter; import lombok.RequiredArgsConstructor; +import org.hibernate.validator.constraints.Length; @Getter @RequiredArgsConstructor @@ -19,6 +20,7 @@ public class CreateEventRequest { @Schema(defaultValue = "고스락 제 22회 정기공연", description = "공연 이름") @NotBlank(message = "공연 이름을 입력하세요") + @Length(max = 25) private String name; @Schema( diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/event/model/dto/request/UpdateEventBasicRequest.java b/DuDoong-Api/src/main/java/band/gosrock/api/event/model/dto/request/UpdateEventBasicRequest.java index fee560f0..9005ba6a 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/event/model/dto/request/UpdateEventBasicRequest.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/event/model/dto/request/UpdateEventBasicRequest.java @@ -9,12 +9,14 @@ import javax.validation.constraints.Positive; import lombok.Getter; import lombok.RequiredArgsConstructor; +import org.hibernate.validator.constraints.Length; @Getter @RequiredArgsConstructor public class UpdateEventBasicRequest { @Schema(defaultValue = "고스락 제 22회 정기공연", description = "공연 이름") @NotBlank(message = "공연 이름을 입력하세요") + @Length(max = 25) private String name; @Schema( diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/event/service/OpenEventUseCase.java b/DuDoong-Api/src/main/java/band/gosrock/api/event/service/OpenEventUseCase.java new file mode 100644 index 00000000..fbcc331b --- /dev/null +++ b/DuDoong-Api/src/main/java/band/gosrock/api/event/service/OpenEventUseCase.java @@ -0,0 +1,27 @@ +package band.gosrock.api.event.service; + +import static band.gosrock.api.common.aop.hostRole.FindHostFrom.EVENT_ID; +import static band.gosrock.api.common.aop.hostRole.HostQualification.MANAGER; + +import band.gosrock.api.common.aop.hostRole.HostRolesAllowed; +import band.gosrock.api.event.model.dto.response.EventResponse; +import band.gosrock.common.annotation.UseCase; +import band.gosrock.domain.domains.event.adaptor.EventAdaptor; +import band.gosrock.domain.domains.event.domain.Event; +import band.gosrock.domain.domains.event.service.EventService; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@UseCase +@RequiredArgsConstructor +public class OpenEventUseCase { + private final EventService eventService; + private final EventAdaptor eventAdaptor; + + @Transactional + @HostRolesAllowed(role = MANAGER, findHostFrom = EVENT_ID) + public EventResponse execute(Long eventId) { + final Event event = eventAdaptor.findById(eventId); + return EventResponse.of(eventService.openEvent(event)); + } +} diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/host/model/dto/request/CreateHostRequest.java b/DuDoong-Api/src/main/java/band/gosrock/api/host/model/dto/request/CreateHostRequest.java index 5ee70e20..2369b893 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/host/model/dto/request/CreateHostRequest.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/host/model/dto/request/CreateHostRequest.java @@ -4,16 +4,18 @@ import band.gosrock.common.annotation.Phone; import io.swagger.v3.oas.annotations.media.Schema; import javax.validation.constraints.Email; -import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotBlank; import lombok.Getter; import lombok.RequiredArgsConstructor; +import org.hibernate.validator.constraints.Length; /** 호스트 간편 생성 요청 DTO */ @Getter @RequiredArgsConstructor public class CreateHostRequest { @Schema(defaultValue = "고스락", description = "호스트 이름") - @NotEmpty(message = "호스트 이름을 입력해주세요") + @NotBlank(message = "호스트 이름을 입력해주세요") + @Length(max = 15) private String name; @Schema(defaultValue = "gosrock@gsrk.com", description = "마스터 이메일") diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/config/slack/SlackApiProvider.java b/DuDoong-Api/src/main/java/band/gosrock/api/slack/sender/SlackInternalErrorSender.java similarity index 59% rename from DuDoong-Api/src/main/java/band/gosrock/api/config/slack/SlackApiProvider.java rename to DuDoong-Api/src/main/java/band/gosrock/api/slack/sender/SlackInternalErrorSender.java index a4732f93..0d7fd57f 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/config/slack/SlackApiProvider.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/slack/sender/SlackInternalErrorSender.java @@ -1,68 +1,40 @@ -package band.gosrock.api.config.slack; +package band.gosrock.api.slack.sender; import static com.slack.api.model.block.Blocks.divider; import static com.slack.api.model.block.Blocks.section; import static com.slack.api.model.block.composition.BlockCompositions.plainText; +import band.gosrock.infrastructure.config.slack.SlackProvider; import com.fasterxml.jackson.databind.ObjectMapper; -import com.slack.api.methods.MethodsClient; -import com.slack.api.methods.SlackApiException; -import com.slack.api.methods.request.chat.ChatPostMessageRequest; import com.slack.api.model.block.Blocks; import com.slack.api.model.block.LayoutBlock; import com.slack.api.model.block.composition.MarkdownTextObject; import com.slack.api.model.block.composition.TextObject; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.env.Environment; -import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; import org.springframework.web.util.ContentCachingRequestWrapper; @Component @RequiredArgsConstructor @Slf4j -public class SlackApiProvider { - private final MethodsClient methodsClient; +public class SlackInternalErrorSender { private final ObjectMapper objectMapper; - private final Environment env; + private final SlackProvider slackProvider; - private final List sendAlarmProfiles = List.of("staging", "prod"); - - @Value("${slack.webhook.id}") - private String CHANNEL_ID; - - private final int MAX_LEN = 500; - - @Async - public void sendError(ContentCachingRequestWrapper cachingRequest, Exception e, Long userId) - throws IOException { - String[] activeProfiles = env.getActiveProfiles(); - List currentProfile = Arrays.stream(activeProfiles).toList(); - if (CollectionUtils.containsAny(sendAlarmProfiles, currentProfile)) { - executeSendError(cachingRequest, e, userId); - } - } - - private void executeSendError( - ContentCachingRequestWrapper cachingRequest, Exception e, Long userId) + public void execute(ContentCachingRequestWrapper cachingRequest, Exception e, Long userId) throws IOException { final String url = cachingRequest.getRequestURL().toString(); final String method = cachingRequest.getMethod(); final String body = objectMapper.readTree(cachingRequest.getContentAsByteArray()).toString(); - final String exceptionAsString = Arrays.toString(e.getStackTrace()); - final int cutLength = Math.min(exceptionAsString.length(), MAX_LEN); final String errorMessage = e.getMessage(); - final String errorStack = exceptionAsString.substring(0, cutLength); + String errorStack = slackProvider.getErrorStack(e); final String errorUserIP = cachingRequest.getRemoteAddr(); List layoutBlocks = new ArrayList<>(); @@ -99,16 +71,6 @@ private void executeSendError( layoutBlocks.add( section(section -> section.fields(List.of(errorNameMarkdown, errorStackMarkdown)))); - ChatPostMessageRequest chatPostMessageRequest = - ChatPostMessageRequest.builder() - .channel(CHANNEL_ID) - .text("") - .blocks(layoutBlocks) - .build(); - try { - methodsClient.chatPostMessage(chatPostMessageRequest); - } catch (SlackApiException slackApiException) { - log.error(slackApiException.toString()); - } + slackProvider.sendNotification(layoutBlocks); } } diff --git a/DuDoong-Api/src/main/resources/application.yml b/DuDoong-Api/src/main/resources/application.yml index c26a9c7c..8f9b0e45 100644 --- a/DuDoong-Api/src/main/resources/application.yml +++ b/DuDoong-Api/src/main/resources/application.yml @@ -18,10 +18,6 @@ server: context-path: /api forward-headers-strategy: framework - -slack: - webhook: - id: ${SLACK_WEBHOOK_ID:} --- spring: config: diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/order/CreateOrderEvent.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/order/CreateOrderEvent.java index bb384316..e14ec5a1 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/order/CreateOrderEvent.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/order/CreateOrderEvent.java @@ -6,10 +6,12 @@ import band.gosrock.domain.domains.order.domain.OrderMethod; import lombok.Builder; import lombok.Getter; +import lombok.ToString; import org.springframework.lang.Nullable; @Getter @Builder +@ToString public class CreateOrderEvent extends DomainEvent { private final String orderUuid; diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/order/DoneOrderEvent.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/order/DoneOrderEvent.java index 2492d071..628ee226 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/order/DoneOrderEvent.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/order/DoneOrderEvent.java @@ -6,10 +6,12 @@ import band.gosrock.domain.domains.order.domain.OrderMethod; import lombok.Builder; import lombok.Getter; +import lombok.ToString; import org.springframework.lang.Nullable; @Getter @Builder +@ToString public class DoneOrderEvent extends DomainEvent { private final String orderUuid; diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/order/WithDrawOrderEvent.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/order/WithDrawOrderEvent.java index 9d6bebbc..996389ef 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/order/WithDrawOrderEvent.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/order/WithDrawOrderEvent.java @@ -7,10 +7,12 @@ import band.gosrock.domain.domains.order.domain.OrderStatus; import lombok.Builder; import lombok.Getter; +import lombok.ToString; import org.springframework.lang.Nullable; @Getter @Builder +@ToString public class WithDrawOrderEvent extends DomainEvent { private final String orderUuid; diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/user/UserRegisterEvent.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/user/UserRegisterEvent.java index 82ed6c4c..98735a46 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/user/UserRegisterEvent.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/user/UserRegisterEvent.java @@ -4,8 +4,10 @@ import band.gosrock.domain.common.aop.domainEvent.DomainEvent; import lombok.Builder; import lombok.Getter; +import lombok.ToString; @Getter +@ToString public class UserRegisterEvent extends DomainEvent { private final Long userId; diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/config/CustomAsyncExceptionHandler.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/config/CustomAsyncExceptionHandler.java new file mode 100644 index 00000000..c8f72b58 --- /dev/null +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/config/CustomAsyncExceptionHandler.java @@ -0,0 +1,27 @@ +package band.gosrock.domain.config; + + +import band.gosrock.infrastructure.config.slack.SlackAsyncErrorSender; +import java.lang.reflect.Method; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler { + + private final SlackAsyncErrorSender slackAsyncErrorSender; + + @Override + public void handleUncaughtException(Throwable throwable, Method method, Object... params) { + log.error("Exception message - " + throwable); + log.error("Method name - " + method.getName()); + for (Object param : params) { + log.error("Parameter value - " + param); + } + slackAsyncErrorSender.execute(method.getName(), throwable, params); + } +} diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/config/EnableAsyncConfig.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/config/EnableAsyncConfig.java index 44263c97..d0f98be4 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/config/EnableAsyncConfig.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/config/EnableAsyncConfig.java @@ -1,9 +1,21 @@ package band.gosrock.domain.config; +import lombok.RequiredArgsConstructor; +import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; @EnableAsync @Configuration -public class EnableAsyncConfig {} +@RequiredArgsConstructor +public class EnableAsyncConfig implements AsyncConfigurer { + + private final CustomAsyncExceptionHandler customAsyncExceptionHandler; + + @Override + public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { + return customAsyncExceptionHandler; + } +} diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/comment/adaptor/CommentAdaptor.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/comment/adaptor/CommentAdaptor.java index f0e0ab09..ebdfd791 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/comment/adaptor/CommentAdaptor.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/comment/adaptor/CommentAdaptor.java @@ -6,6 +6,7 @@ import band.gosrock.domain.domains.comment.dto.condition.CommentCondition; import band.gosrock.domain.domains.comment.exception.CommentNotFoundException; import band.gosrock.domain.domains.comment.repository.CommentRepository; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Slice; @@ -33,8 +34,9 @@ public Long queryCommentCount(Long eventId) { return commentRepository.countComment(eventId); } - public Comment queryRandomComment(Long eventId) { + public List queryRandomComment(Long eventId, Long limit) { Long countComment = queryCommentCount(eventId); - return commentRepository.queryRandomComment(eventId, countComment); + // return commentRepository.queryRandomComment(eventId, countComment, offset); + return commentRepository.findAllRandom(eventId, limit); } } diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/comment/repository/CommentCustomRepository.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/comment/repository/CommentCustomRepository.java index 0549217c..8b1f2846 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/comment/repository/CommentCustomRepository.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/comment/repository/CommentCustomRepository.java @@ -10,6 +10,4 @@ public interface CommentCustomRepository { Slice searchToPage(CommentCondition commentCondition); Long countComment(Long eventId); - - Comment queryRandomComment(Long eventId, Long count); } diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/comment/repository/CommentCustomRepositoryImpl.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/comment/repository/CommentCustomRepositoryImpl.java index 14959669..822d9503 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/comment/repository/CommentCustomRepositoryImpl.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/comment/repository/CommentCustomRepositoryImpl.java @@ -7,17 +7,11 @@ import band.gosrock.domain.domains.comment.domain.Comment; import band.gosrock.domain.domains.comment.domain.CommentStatus; import band.gosrock.domain.domains.comment.dto.condition.CommentCondition; -import band.gosrock.domain.domains.comment.exception.RetrieveRandomCommentNotFoundException; import com.querydsl.core.types.dsl.BooleanExpression; -import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; -import java.security.SecureRandom; import java.util.List; import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; -import org.springframework.data.support.PageableExecutionUtils; @RequiredArgsConstructor public class CommentCustomRepositoryImpl implements CommentCustomRepository { @@ -70,33 +64,4 @@ public Long countComment(Long eventId) { .where(eventIdEq(eventId), comment.commentStatus.eq(CommentStatus.ACTIVE)) .fetchOne(); } - - @Override - public Comment queryRandomComment(Long eventId, Long countComment) { - SecureRandom secureRandom = new SecureRandom(); - int idx = (int) (secureRandom.nextFloat(1) * countComment); - - PageRequest pageRequest = PageRequest.of(idx, 1); - List comments = - queryFactory - .selectFrom(comment) - .where(eventIdEq(eventId), comment.commentStatus.eq(CommentStatus.ACTIVE)) - .offset(pageRequest.getOffset()) - .limit(1) - .fetch(); - - JPAQuery countQuery = - queryFactory - .select(comment.count()) - .from(comment) - .where(eventIdEq(eventId), comment.commentStatus.eq(CommentStatus.ACTIVE)); - - Page commentOfPage = - PageableExecutionUtils.getPage(comments, pageRequest, countQuery::fetchOne); - - if (commentOfPage.hasContent()) { - return commentOfPage.getContent().get(0); - } - throw RetrieveRandomCommentNotFoundException.EXCEPTION; - } } diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/comment/repository/CommentRepository.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/comment/repository/CommentRepository.java index 91d9aa64..52dc1e35 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/comment/repository/CommentRepository.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/comment/repository/CommentRepository.java @@ -2,6 +2,20 @@ import band.gosrock.domain.domains.comment.domain.Comment; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; -public interface CommentRepository extends JpaRepository, CommentCustomRepository {} +public interface CommentRepository extends JpaRepository, CommentCustomRepository { + + @Query( + nativeQuery = true, + value = + "SELECT * " + + "FROM tbl_comment as c " + + "WHERE c.event_id = :eventId " + + "ORDER BY RAND() DESC " + + "LIMIT :offset") + List findAllRandom(@Param("eventId") Long eventId, @Param("offset") Long limit); +} diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/domain/Event.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/domain/Event.java index 3d4620d1..2c41ec66 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/domain/Event.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/domain/Event.java @@ -3,10 +3,7 @@ import band.gosrock.domain.common.model.BaseTimeEntity; import band.gosrock.domain.common.vo.*; -import band.gosrock.domain.domains.event.exception.CannotModifyEventBasicException; -import band.gosrock.domain.domains.event.exception.EventCannotEndBeforeStartException; -import band.gosrock.domain.domains.event.exception.EventNotOpenException; -import band.gosrock.domain.domains.event.exception.EventTicketingTimeIsPassedException; +import band.gosrock.domain.domains.event.exception.*; import java.time.LocalDateTime; import javax.persistence.*; import lombok.AccessLevel; @@ -156,22 +153,23 @@ public EventPlaceVo toEventPlaceVo() { } public void prepare() { - // TODO : 오픈할수 있는 상태인지 검증필요함. + if (this.status == EventStatus.PREPARING) throw AlreadyPreparingStatusException.EXCEPTION; this.status = EventStatus.PREPARING; } public void open() { - // TODO : 오픈할수 있는 상태인지 검증필요함. + if (this.status == EventStatus.OPEN) throw AlreadyOpenStatusException.EXCEPTION; this.status = EventStatus.OPEN; } public void calculate() { - // TODO : 오픈할수 있는 상태인지 검증필요함. + if (this.status == EventStatus.CALCULATING) + throw AlreadyCalculatingStatusException.EXCEPTION; this.status = EventStatus.CALCULATING; } public void close() { - // TODO : 오픈할수 있는 상태인지 검증필요함. - this.status = EventStatus.OPEN; + if (this.status == EventStatus.CLOSED) throw AlreadyCloseStatusException.EXCEPTION; + this.status = EventStatus.CLOSED; } } diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/domain/EventBasic.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/domain/EventBasic.java index a217ef97..55af7abe 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/domain/EventBasic.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/domain/EventBasic.java @@ -2,6 +2,7 @@ import java.time.LocalDateTime; +import javax.persistence.Column; import javax.persistence.Embeddable; import lombok.AccessLevel; import lombok.Builder; @@ -12,7 +13,9 @@ @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class EventBasic { + @Column(length = 25) private String name; + private LocalDateTime startAt; private Long runTime; diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/AlreadyCalculatingStatusException.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/AlreadyCalculatingStatusException.java new file mode 100644 index 00000000..efcfd5cd --- /dev/null +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/AlreadyCalculatingStatusException.java @@ -0,0 +1,13 @@ +package band.gosrock.domain.domains.event.exception; + + +import band.gosrock.common.exception.DuDoongCodeException; + +public class AlreadyCalculatingStatusException extends DuDoongCodeException { + + public static final DuDoongCodeException EXCEPTION = new AlreadyCalculatingStatusException(); + + private AlreadyCalculatingStatusException() { + super(EventErrorCode.ALREADY_CALCULATING_STATUS); + } +} diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/AlreadyCloseStatusException.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/AlreadyCloseStatusException.java new file mode 100644 index 00000000..36259e53 --- /dev/null +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/AlreadyCloseStatusException.java @@ -0,0 +1,13 @@ +package band.gosrock.domain.domains.event.exception; + + +import band.gosrock.common.exception.DuDoongCodeException; + +public class AlreadyCloseStatusException extends DuDoongCodeException { + + public static final DuDoongCodeException EXCEPTION = new AlreadyCloseStatusException(); + + private AlreadyCloseStatusException() { + super(EventErrorCode.ALREADY_CLOSE_STATUS); + } +} diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/AlreadyOpenStatusException.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/AlreadyOpenStatusException.java new file mode 100644 index 00000000..5615b97f --- /dev/null +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/AlreadyOpenStatusException.java @@ -0,0 +1,13 @@ +package band.gosrock.domain.domains.event.exception; + + +import band.gosrock.common.exception.DuDoongCodeException; + +public class AlreadyOpenStatusException extends DuDoongCodeException { + + public static final DuDoongCodeException EXCEPTION = new AlreadyOpenStatusException(); + + private AlreadyOpenStatusException() { + super(EventErrorCode.ALREADY_OPEN_STATUS); + } +} diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/AlreadyPreparingStatusException.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/AlreadyPreparingStatusException.java new file mode 100644 index 00000000..3008da3d --- /dev/null +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/AlreadyPreparingStatusException.java @@ -0,0 +1,13 @@ +package band.gosrock.domain.domains.event.exception; + + +import band.gosrock.common.exception.DuDoongCodeException; + +public class AlreadyPreparingStatusException extends DuDoongCodeException { + + public static final DuDoongCodeException EXCEPTION = new AlreadyPreparingStatusException(); + + private AlreadyPreparingStatusException() { + super(EventErrorCode.ALREADY_PREPARING_STATUS); + } +} diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/CannotOpenEventException.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/CannotOpenEventException.java new file mode 100644 index 00000000..2cc613f1 --- /dev/null +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/CannotOpenEventException.java @@ -0,0 +1,13 @@ +package band.gosrock.domain.domains.event.exception; + + +import band.gosrock.common.exception.DuDoongCodeException; + +public class CannotOpenEventException extends DuDoongCodeException { + + public static final DuDoongCodeException EXCEPTION = new CannotOpenEventException(); + + private CannotOpenEventException() { + super(EventErrorCode.CANNOT_OPEN_EVENT); + } +} diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/EventErrorCode.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/EventErrorCode.java index 90b9f8f9..803bea2e 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/EventErrorCode.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/EventErrorCode.java @@ -15,14 +15,21 @@ @AllArgsConstructor public enum EventErrorCode implements BaseErrorCode { EVENT_NOT_FOUND(NOT_FOUND, "Event_404_1", "이벤트를 찾을 수 없습니다."), - HOST_NOT_AUTH_EVENT(BAD_REQUEST, "Event_400_1", "Host Not Auth Event."), + HOST_NOT_AUTH_EVENT(BAD_REQUEST, "Event_400_1", "Host Not Auth Event."), EVENT_CANNOT_END_BEFORE_START(BAD_REQUEST, "Event_400_2", "시작 시각은 종료 시각보다 빨라야 합니다."), - EVENT_URL_NAME_ALREADY_EXIST(BAD_REQUEST, "Event_400_3", "중복된 URL 표시 이름입니다."), CANNOT_MODIFY_EVENT_BASIC(BAD_REQUEST, "Event_400_4", "이벤트 기본 정보는 수정할 수 없습니다."), EVENT_NOT_OPEN(BAD_REQUEST, "Event_400_5", "아직 오픈되지 않은 이벤트에는 접근할 수 없습니다."), - EVENT_TICKETING_TIME_IS_PASSED(BAD_REQUEST, "Event_400_6", "이벤트 시작시간이 지나 티켓팅을 할 수 없습니다."); + EVENT_TICKETING_TIME_IS_PASSED(BAD_REQUEST, "Event_400_6", "이벤트 시작시간이 지나 티켓팅을 할 수 없습니다."), + CANNOT_OPEN_EVENT(BAD_REQUEST, "Event_400_7", "이벤트 오픈 조건을 충족하지 않았습니다."), + ALREADY_OPEN_STATUS(BAD_REQUEST, "Event_400_8", "이미 오픈 중인 이벤트입니다."), + ALREADY_CALCULATING_STATUS(BAD_REQUEST, "Event_400_9", "이미 정산중인 이벤트입니다."), + ALREADY_CLOSE_STATUS(BAD_REQUEST, "Event_400_10", "이미 닫은 이벤트입니다."), + ALREADY_PREPARING_STATUS(BAD_REQUEST, "Event_400_11", "이미 준비중인 이벤트입니다."), + + USE_OTHER_API(BAD_REQUEST, "Event_400_8", "잘못된 접근입니다."); + private Integer status; private String code; private String reason; diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/UseOtherApiException.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/UseOtherApiException.java new file mode 100644 index 00000000..ec296a3b --- /dev/null +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/UseOtherApiException.java @@ -0,0 +1,13 @@ +package band.gosrock.domain.domains.event.exception; + + +import band.gosrock.common.exception.DuDoongCodeException; + +public class UseOtherApiException extends DuDoongCodeException { + + public static final DuDoongCodeException EXCEPTION = new UseOtherApiException(); + + private UseOtherApiException() { + super(EventErrorCode.USE_OTHER_API); + } +} diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/service/EventService.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/service/EventService.java index e6998607..0a4c4f33 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/service/EventService.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/service/EventService.java @@ -4,8 +4,10 @@ import band.gosrock.common.annotation.DomainService; import band.gosrock.domain.domains.event.adaptor.EventAdaptor; import band.gosrock.domain.domains.event.domain.*; -import band.gosrock.domain.domains.event.exception.HostNotAuthEventException; +import band.gosrock.domain.domains.event.exception.CannotOpenEventException; +import band.gosrock.domain.domains.event.exception.UseOtherApiException; import band.gosrock.domain.domains.event.repository.EventRepository; +import band.gosrock.domain.domains.ticket_item.service.TicketItemService; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; @@ -15,6 +17,7 @@ public class EventService { private final EventRepository eventRepository; private final EventAdaptor eventAdaptor; + private final TicketItemService ticketItemService; public Event createEvent(Event event) { return eventRepository.save(event); @@ -36,19 +39,32 @@ public Event updateEventPlace(Event event, EventPlace eventPlace) { return eventRepository.save(event); } + public void validateEventBasicExistence(Event event) { + if (!event.hasEventBasic() || !event.hasEventPlace()) { + throw CannotOpenEventException.EXCEPTION; + } + } + + public void validateEventDetailExistence(Event event) { + if (!event.hasEventDetail()) { + throw CannotOpenEventException.EXCEPTION; + } + } + + public Event openEvent(Event event) { + this.validateEventBasicExistence(event); + this.validateEventDetailExistence(event); + ticketItemService.validateExistenceByEventId(event.getId()); + event.open(); + return eventRepository.save(event); + } + public Event updateEventStatus(Event event, EventStatus status) { - // todo :: 이벤트 상태 변경시 검증 필요 - if (status == EventStatus.OPEN) event.open(); - else if (status == EventStatus.CLOSED) event.close(); + if (status == EventStatus.OPEN) { + throw UseOtherApiException.EXCEPTION; // open 은 다른 API 강제 + } else if (status == EventStatus.CLOSED) event.close(); else if (status == EventStatus.CALCULATING) event.calculate(); else if (status == EventStatus.PREPARING) event.prepare(); return eventRepository.save(event); } - - public void checkEventHost(Long hostId, Long eventId) { - Event event = eventAdaptor.findById(eventId); - if (!event.getHostId().equals(hostId)) { - throw HostNotAuthEventException.EXCEPTION; - } - } } diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/host/domain/HostProfile.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/host/domain/HostProfile.java index 1152c338..0053186b 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/host/domain/HostProfile.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/host/domain/HostProfile.java @@ -15,6 +15,7 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) public class HostProfile { // 호스트 이름 + @Column(length = 15) private String name; // 간단 소개 diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/service/TicketItemService.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/service/TicketItemService.java index fdabd704..b08c2d01 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/service/TicketItemService.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/service/TicketItemService.java @@ -5,6 +5,7 @@ import band.gosrock.domain.common.aop.redissonLock.RedissonLock; import band.gosrock.domain.domains.ticket_item.adaptor.TicketItemAdaptor; import band.gosrock.domain.domains.ticket_item.domain.TicketItem; +import band.gosrock.domain.domains.ticket_item.exception.InvalidTicketItemException; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; @@ -23,6 +24,12 @@ public TicketItem createTicketItem(TicketItem ticketItem, Boolean isPartner) { return ticketItemAdaptor.save(ticketItem); } + public void validateExistenceByEventId(Long eventId) { + if (!ticketItemAdaptor.existsByEventId(eventId)) { + throw InvalidTicketItemException.EXCEPTION; + } + } + @RedissonLock(LockName = "티켓관리", identifier = "ticketItemId") public void softDeleteTicketItem(Long eventId, Long ticketItemId) { diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/user/domain/User.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/user/domain/User.java index 11b90742..d554fb71 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/user/domain/User.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/user/domain/User.java @@ -9,6 +9,7 @@ import band.gosrock.domain.domains.user.exception.AlreadyDeletedUserException; import band.gosrock.domain.domains.user.exception.ForbiddenUserException; import band.gosrock.infrastructure.config.mail.dto.EmailUserInfo; +import java.time.LocalDateTime; import javax.persistence.Column; import javax.persistence.Embedded; import javax.persistence.Entity; @@ -46,6 +47,8 @@ public class User extends BaseTimeEntity { @Enumerated(EnumType.STRING) private AccountRole accountRole = AccountRole.USER; + private LocalDateTime lastLoginAt = LocalDateTime.now(); + @Builder public User(Profile profile, OauthInfo oauthInfo) { this.profile = profile; @@ -74,6 +77,7 @@ public void login() { if (!AccountState.NORMAL.equals(this.accountState)) { throw ForbiddenUserException.EXCEPTION; } + lastLoginAt = LocalDateTime.now(); } public UserInfoVo toUserInfoVo() { diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/user/service/UserDomainService.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/user/service/UserDomainService.java index ea45ebee..4bda32b3 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/user/service/UserDomainService.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/user/service/UserDomainService.java @@ -50,6 +50,7 @@ public void validUserCanRegister(OauthInfo oauthInfo) { if (!checkUserCanRegister(oauthInfo)) throw AlreadySignUpUserException.EXCEPTION; } + @Transactional public User loginUser(OauthInfo oauthInfo) { User user = userAdaptor.queryUserByOauthInfo(oauthInfo); user.login(); @@ -62,12 +63,4 @@ public void withDrawUser(Long userId) { User user = userAdaptor.queryUser(userId); user.withDrawUser(); } - - /* - 유저 id로 유저 가져오기 작성 - 민준 - */ - @Transactional(readOnly = true) - public User retrieveUser(Long userId) { - return userAdaptor.queryUser(userId); - } } diff --git a/DuDoong-Infrastructure/src/main/java/band/gosrock/infrastructure/config/slack/SlackAsyncErrorSender.java b/DuDoong-Infrastructure/src/main/java/band/gosrock/infrastructure/config/slack/SlackAsyncErrorSender.java new file mode 100644 index 00000000..22082192 --- /dev/null +++ b/DuDoong-Infrastructure/src/main/java/band/gosrock/infrastructure/config/slack/SlackAsyncErrorSender.java @@ -0,0 +1,59 @@ +package band.gosrock.infrastructure.config.slack; + +import static com.slack.api.model.block.Blocks.divider; +import static com.slack.api.model.block.Blocks.section; +import static com.slack.api.model.block.composition.BlockCompositions.plainText; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.LayoutBlock; +import com.slack.api.model.block.composition.MarkdownTextObject; +import java.util.ArrayList; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@RequiredArgsConstructor +@Component +@Slf4j +public class SlackAsyncErrorSender { + private final SlackProvider slackProvider; + + public void execute(String name, Throwable throwable, Object[] params) { + List layoutBlocks = new ArrayList<>(); + layoutBlocks.add( + Blocks.header( + headerBlockBuilder -> headerBlockBuilder.text(plainText("비동기 에러 알림")))); + layoutBlocks.add(divider()); + + MarkdownTextObject errorUserIdMarkdown = + MarkdownTextObject.builder().text("* 메소드 이름 :*\n" + name).build(); + MarkdownTextObject errorUserIpMarkdown = + MarkdownTextObject.builder() + .text("* 요청 파라미터 :*\n" + getParamsToString(params)) + .build(); + layoutBlocks.add( + section( + section -> + section.fields(List.of(errorUserIdMarkdown, errorUserIpMarkdown)))); + + layoutBlocks.add(divider()); + String errorStack = slackProvider.getErrorStack(throwable); + String message = throwable.toString(); + MarkdownTextObject errorNameMarkdown = + MarkdownTextObject.builder().text("* Message :*\n" + message).build(); + MarkdownTextObject errorStackMarkdown = + MarkdownTextObject.builder().text("* Stack Trace :*\n" + errorStack).build(); + layoutBlocks.add( + section(section -> section.fields(List.of(errorNameMarkdown, errorStackMarkdown)))); + slackProvider.sendNotification(layoutBlocks); + } + + private String getParamsToString(Object[] params) { + StringBuilder paramToString = new StringBuilder(); + for (Object param : params) { + paramToString.append(param.toString()); + } + return paramToString.toString(); + } +} diff --git a/DuDoong-Infrastructure/src/main/java/band/gosrock/infrastructure/config/slack/SlackProvider.java b/DuDoong-Infrastructure/src/main/java/band/gosrock/infrastructure/config/slack/SlackProvider.java new file mode 100644 index 00000000..ae6daa75 --- /dev/null +++ b/DuDoong-Infrastructure/src/main/java/band/gosrock/infrastructure/config/slack/SlackProvider.java @@ -0,0 +1,63 @@ +package band.gosrock.infrastructure.config.slack; + + +import com.slack.api.methods.MethodsClient; +import com.slack.api.methods.SlackApiException; +import com.slack.api.methods.request.chat.ChatPostMessageRequest; +import com.slack.api.model.block.LayoutBlock; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.env.Environment; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +@Component +@RequiredArgsConstructor +@Slf4j +public class SlackProvider { + + private final Environment env; + + private final List sendAlarmProfiles = List.of("staging", "prod"); + private final int MAX_LEN = 500; + + @Value("${slack.webhook.id}") + private String CHANNEL_ID; + + private final MethodsClient methodsClient; + + private Boolean isNeedToNotificationProfile() { + String[] activeProfiles = env.getActiveProfiles(); + List currentProfile = Arrays.stream(activeProfiles).toList(); + return CollectionUtils.containsAny(sendAlarmProfiles, currentProfile); + } + + public String getErrorStack(Throwable throwable) { + String exceptionAsString = Arrays.toString(throwable.getStackTrace()); + int cutLength = Math.min(exceptionAsString.length(), MAX_LEN); + return exceptionAsString.substring(0, cutLength); + } + + @Async + public void sendNotification(List layoutBlocks) { + if (!isNeedToNotificationProfile()) { + return; + } + ChatPostMessageRequest chatPostMessageRequest = + ChatPostMessageRequest.builder() + .channel(CHANNEL_ID) + .text("") + .blocks(layoutBlocks) + .build(); + try { + methodsClient.chatPostMessage(chatPostMessageRequest); + } catch (SlackApiException | IOException slackApiException) { + log.error(slackApiException.toString()); + } + } +} diff --git a/DuDoong-Infrastructure/src/main/java/band/gosrock/infrastructure/outer/api/oauth/dto/KakaoInformationResponse.java b/DuDoong-Infrastructure/src/main/java/band/gosrock/infrastructure/outer/api/oauth/dto/KakaoInformationResponse.java index d0674869..23da1960 100644 --- a/DuDoong-Infrastructure/src/main/java/band/gosrock/infrastructure/outer/api/oauth/dto/KakaoInformationResponse.java +++ b/DuDoong-Infrastructure/src/main/java/band/gosrock/infrastructure/outer/api/oauth/dto/KakaoInformationResponse.java @@ -58,7 +58,7 @@ public String getPhoneNumber() { } public String getName() { - return kakaoAccount.getName(); + return kakaoAccount.getName() != null ? kakaoAccount.getName() : properties.getNickname(); } public String getProfileUrl() { diff --git a/DuDoong-Infrastructure/src/main/resources/application-infrastructure.yml b/DuDoong-Infrastructure/src/main/resources/application-infrastructure.yml index f776b5e4..ab643117 100644 --- a/DuDoong-Infrastructure/src/main/resources/application-infrastructure.yml +++ b/DuDoong-Infrastructure/src/main/resources/application-infrastructure.yml @@ -15,9 +15,7 @@ spring: slack: webhook: token: ${SLACK_WEBHOOK_TOKEN:} - - - + id: ${SLACK_WEBHOOK_ID:} ---