From 71df4488dce97689e7fc9909199b01a22e3a497d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=B1=84=EB=A6=B0=20=28Bryn=29?= <67696767+cofls6581@users.noreply.github.com> Date: Wed, 8 Mar 2023 23:37:37 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat=20:=20=EC=A0=95=EC=82=B0=EC=84=9C=20?= =?UTF-8?q?=EC=A0=84=EC=86=A1=20=EC=95=88=EB=82=B4=20=EC=95=8C=EB=A6=BC?= =?UTF-8?q?=ED=86=A1=20=EB=B0=B0=EC=B9=98=EC=9E=A1=20=EC=85=8D=EC=84=B1=20?= =?UTF-8?q?(#537)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/SendDoneOrderAlimTalkService.java | 2 +- .../service/SendRegisterAlimTalkService.java | 2 +- .../SendWithdrawOrderAlimTalkService.java | 2 +- .../job/EventSettlementAlimTalkToHost.java | 86 +++++++++++++++++++ .../alarm/SettlementKakaoTalkAlarm.java | 22 +++++ .../config/alilmTalk}/NcpHelper.java | 62 ++++++++++++- 6 files changed, 172 insertions(+), 4 deletions(-) create mode 100644 DuDoong-Batch/src/main/java/band/gosrock/job/EventSettlementAlimTalkToHost.java create mode 100644 DuDoong-Domain/src/main/java/band/gosrock/domain/common/alarm/SettlementKakaoTalkAlarm.java rename {DuDoong-Api/src/main/java/band/gosrock/api/alimTalk/service/helper => DuDoong-Infrastructure/src/main/java/band/gosrock/infrastructure/config/alilmTalk}/NcpHelper.java (80%) diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/alimTalk/service/SendDoneOrderAlimTalkService.java b/DuDoong-Api/src/main/java/band/gosrock/api/alimTalk/service/SendDoneOrderAlimTalkService.java index ff27343d..52726001 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/alimTalk/service/SendDoneOrderAlimTalkService.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/alimTalk/service/SendDoneOrderAlimTalkService.java @@ -2,8 +2,8 @@ import band.gosrock.api.alimTalk.dto.OrderAlimTalkDto; -import band.gosrock.api.alimTalk.service.helper.NcpHelper; import band.gosrock.domain.common.alarm.OrderKakaoTalkAlarm; +import band.gosrock.infrastructure.config.alilmTalk.NcpHelper; import band.gosrock.infrastructure.config.alilmTalk.dto.AlimTalkEventInfo; import band.gosrock.infrastructure.config.alilmTalk.dto.AlimTalkOrderInfo; import band.gosrock.infrastructure.config.alilmTalk.dto.AlimTalkUserInfo; diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/alimTalk/service/SendRegisterAlimTalkService.java b/DuDoong-Api/src/main/java/band/gosrock/api/alimTalk/service/SendRegisterAlimTalkService.java index 76d633f7..1cb4c0c1 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/alimTalk/service/SendRegisterAlimTalkService.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/alimTalk/service/SendRegisterAlimTalkService.java @@ -1,8 +1,8 @@ package band.gosrock.api.alimTalk.service; -import band.gosrock.api.alimTalk.service.helper.NcpHelper; import band.gosrock.domain.common.alarm.UserKakaoTalkAlarm; +import band.gosrock.infrastructure.config.alilmTalk.NcpHelper; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/alimTalk/service/SendWithdrawOrderAlimTalkService.java b/DuDoong-Api/src/main/java/band/gosrock/api/alimTalk/service/SendWithdrawOrderAlimTalkService.java index 4d8a2911..b8a01c48 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/alimTalk/service/SendWithdrawOrderAlimTalkService.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/alimTalk/service/SendWithdrawOrderAlimTalkService.java @@ -2,8 +2,8 @@ import band.gosrock.api.alimTalk.dto.OrderAlimTalkDto; -import band.gosrock.api.alimTalk.service.helper.NcpHelper; import band.gosrock.domain.common.alarm.OrderKakaoTalkAlarm; +import band.gosrock.infrastructure.config.alilmTalk.NcpHelper; import band.gosrock.infrastructure.config.alilmTalk.dto.AlimTalkEventInfo; import band.gosrock.infrastructure.config.alilmTalk.dto.AlimTalkOrderInfo; import band.gosrock.infrastructure.config.alilmTalk.dto.AlimTalkUserInfo; diff --git a/DuDoong-Batch/src/main/java/band/gosrock/job/EventSettlementAlimTalkToHost.java b/DuDoong-Batch/src/main/java/band/gosrock/job/EventSettlementAlimTalkToHost.java new file mode 100644 index 00000000..c7c64775 --- /dev/null +++ b/DuDoong-Batch/src/main/java/band/gosrock/job/EventSettlementAlimTalkToHost.java @@ -0,0 +1,86 @@ +package band.gosrock.job; + + +import band.gosrock.domain.common.alarm.SettlementKakaoTalkAlarm; +import band.gosrock.domain.domains.event.adaptor.EventAdaptor; +import band.gosrock.domain.domains.event.domain.Event; +import band.gosrock.domain.domains.host.adaptor.HostAdaptor; +import band.gosrock.domain.domains.host.domain.Host; +import band.gosrock.domain.domains.user.adaptor.UserAdaptor; +import band.gosrock.domain.domains.user.domain.User; +import band.gosrock.infrastructure.config.alilmTalk.NcpHelper; +import band.gosrock.parameter.EventJobParameter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; +import org.springframework.batch.core.configuration.annotation.JobScope; +import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; +import org.springframework.batch.repeat.RepeatStatus; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Slf4j +@RequiredArgsConstructor +@Configuration +public class EventSettlementAlimTalkToHost { + + private static final String JOB_NAME = "이벤트정산_알림톡발송_호스트"; + private static final String BEAN_PREFIX = JOB_NAME + "_"; + + private final JobBuilderFactory jobBuilderFactory; + private final StepBuilderFactory stepBuilderFactory; + private final EventAdaptor eventAdaptor; + private final HostAdaptor hostAdaptor; + private final UserAdaptor userAdaptor; + private final NcpHelper ncpHelper; + + @Bean(BEAN_PREFIX + "eventJobParameter") + @JobScope + public EventJobParameter eventJobParameter() { + return new EventJobParameter(eventAdaptor); + } + + @Qualifier(BEAN_PREFIX + "eventJobParameter") + private final EventJobParameter eventJobParameter; + + @Bean(JOB_NAME) + public Job slackUserStatisticJob() { + return jobBuilderFactory.get(JOB_NAME).preventRestart().start(eventSettlement()).build(); + } + + @Bean(BEAN_PREFIX + "step") + @JobScope + public Step eventSettlement() { + return stepBuilderFactory + .get(BEAN_PREFIX + "step") + .tasklet( + (contribution, chunkContext) -> { + log.info(">>>>> 정산서 전송 안내 알림톡 스탭"); + Event event = eventJobParameter.getEvent(); + Host host = hostAdaptor.findById(event.getHostId()); + User masterUser = userAdaptor.queryUser(host.getMasterUserId()); + + String to = + masterUser + .getProfile() + .getPhoneNumberVo() + .getNaverSmsToNumber(); + String content = + SettlementKakaoTalkAlarm.creationOf( + masterUser.getProfile().getName()); + + ncpHelper.sendSettlementNcpAlimTalk( + to, + SettlementKakaoTalkAlarm.creationTemplateCode(), + content, + SettlementKakaoTalkAlarm.creationHeaderOf(), + masterUser.getProfile().getEmail(), + event.getEventBasic().getName()); + return RepeatStatus.FINISHED; + }) + .build(); + } +} diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/common/alarm/SettlementKakaoTalkAlarm.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/common/alarm/SettlementKakaoTalkAlarm.java new file mode 100644 index 00000000..d9812cfc --- /dev/null +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/common/alarm/SettlementKakaoTalkAlarm.java @@ -0,0 +1,22 @@ +package band.gosrock.domain.common.alarm; + + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class SettlementKakaoTalkAlarm { + + public static String creationOf(String hostName) { + return "안녕하세요 " + hostName + "님!\n" + "이메일로 정산서 발송이 완료되어 안내드립니다."; + } + + public static String creationHeaderOf() { + return "정산서 이메일 발송 안내"; + } + + public static String creationTemplateCode() { + return "settlement"; + } +} diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/alimTalk/service/helper/NcpHelper.java b/DuDoong-Infrastructure/src/main/java/band/gosrock/infrastructure/config/alilmTalk/NcpHelper.java similarity index 80% rename from DuDoong-Api/src/main/java/band/gosrock/api/alimTalk/service/helper/NcpHelper.java rename to DuDoong-Infrastructure/src/main/java/band/gosrock/infrastructure/config/alilmTalk/NcpHelper.java index 1f7a3463..183497f6 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/alimTalk/service/helper/NcpHelper.java +++ b/DuDoong-Infrastructure/src/main/java/band/gosrock/infrastructure/config/alilmTalk/NcpHelper.java @@ -1,4 +1,4 @@ -package band.gosrock.api.alimTalk.service.helper; +package band.gosrock.infrastructure.config.alilmTalk; import band.gosrock.common.annotation.Helper; @@ -111,6 +111,31 @@ public void sendItemNcpAlimTalk( ncpClient.sendItemAlimTalk(serviceID, ncpAccessKey, timeStamp, signature, alimTalkItemBody); } + // 주문서 전송 알림톡 (아이템리스트) + public void sendSettlementNcpAlimTalk( + String to, + String templateCode, + String content, + String headerContent, + String email, + String eventName) { + // 전송 서버 검증 + if (!springEnvironmentHelper.isProdAndStagingProfile()) { + return; + } + // signature 생성 + String timeStamp = + String.valueOf(Instant.now().toEpochMilli()); // current timestamp (epoch) + String alimTalkSignatureRequestUrl = "/alimtalk/v2/services/" + serviceID + "/messages"; + String signature = + makePostSignature( + ncpAccessKey, ncpSecretKey, alimTalkSignatureRequestUrl, timeStamp); + // 바디 생성 + MessageDto.AlimTalkItemBody alimTalkItemBody = + makeSettlementItemBody(templateCode, to, content, headerContent, email, eventName); + ncpClient.sendItemAlimTalk(serviceID, ncpAccessKey, timeStamp, signature, alimTalkItemBody); + } + public MessageDto.AlimTalkItemButtonBody makeItemButtonBody( String templateCode, String to, @@ -139,6 +164,31 @@ public MessageDto.AlimTalkItemButtonBody makeItemButtonBody( .build(); } + public MessageDto.AlimTalkItemBody makeSettlementItemBody( + String templateCode, + String to, + String content, + String headerContent, + String email, + String eventName) { + MessageDto.AlimTalkItem alimTalkItem = makeSettlementItem(email, eventName); + MessageDto.AlimTalkItemMessage alimTalkMessage = + MessageDto.AlimTalkItemMessage.builder() + .to(to) + .content(content) + .headerContent(headerContent) + .item(alimTalkItem) + .build(); + List alimTalkItemMessages = new ArrayList<>(); + alimTalkItemMessages.add(alimTalkMessage); + + return MessageDto.AlimTalkItemBody.builder() + .plusFriendId(plusFriendId) + .templateCode(templateCode) + .messages(alimTalkItemMessages) + .build(); + } + public MessageDto.AlimTalkItemBody makeItemBody( String templateCode, String to, @@ -183,6 +233,16 @@ public MessageDto.AlimTalkButtonBody makeButtonBody( .build(); } + public MessageDto.AlimTalkItem makeSettlementItem(String email, String eventName) { + List list = new ArrayList<>(); + MessageDto.Item item1 = MessageDto.Item.builder().title("이메일 :").description(email).build(); + list.add(item1); + MessageDto.Item item2 = + MessageDto.Item.builder().title("이벤트 :").description(eventName).build(); + list.add(item2); + return MessageDto.AlimTalkItem.builder().list(list).build(); + } + public MessageDto.AlimTalkItem makeOrderItem(AlimTalkOrderInfo orderInfo) { List list = new ArrayList<>(); MessageDto.Item item1 = From e123c74d62e20e618b1075be4e43d0a315e63808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=85=B8=EA=B2=BD=EB=AF=BC?= Date: Wed, 8 Mar 2023 23:38:36 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix=20:=20=EC=A4=80=EB=B9=84=EC=A4=91?= =?UTF-8?q?=EC=9D=B8=20=EA=B3=B5=EC=97=B0=20=EC=9E=90=EB=8F=99=20=EB=A7=8C?= =?UTF-8?q?=EB=A3=8C=20=ED=95=B4=EC=A0=9C=20(#536)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix : 오픈 상태인 이벤트만 자동만료 * refactor : not open 검증 메소드명 수정 * fix : 이벤트 오픈 시간 검증 로직 추가 * docs : readme 블로그 글 추가 * refactor : 메소드 명 수정 * test : 로직 변경에 따른 테스트 변경 --------- Co-authored-by: 이찬진 --- .../domains/cart/domain/CartValidator.java | 2 +- .../domains/event/adaptor/EventAdaptor.java | 4 ++-- .../domain/domains/event/domain/Event.java | 9 +++++++-- .../domains/event/exception/EventErrorCode.java | 1 + .../EventOpenTimeExpiredException.java | 13 +++++++++++++ .../event/repository/EventCustomRepository.java | 2 +- .../repository/EventCustomRepositoryImpl.java | 8 ++++---- .../domains/event/service/EventService.java | 3 +-- .../order/domain/validator/OrderValidator.java | 2 +- .../domains/cart/domain/CartValidatorTest.java | 4 ++-- .../gosrock/domain/domains/event/EventTest.java | 17 +++++++++++++++++ .../domain/validator/OrderValidatorTest.java | 4 ++-- README.md | 6 ++++++ 13 files changed, 58 insertions(+), 17 deletions(-) create mode 100644 DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/EventOpenTimeExpiredException.java diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/cart/domain/CartValidator.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/cart/domain/CartValidator.java index 95f03849..0ab5dd38 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/cart/domain/CartValidator.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/cart/domain/CartValidator.java @@ -56,7 +56,7 @@ public void validItemStockEnough(Cart cart, TicketItem item) { /** 이벤트가 현재 열려있는 상태인지 확인합니다. */ public void validEventIsOpen(Event event) { - event.validateStatusOpen(); + event.validateNotOpenStatus(); } /** 카트에 담길때 아이템이 한 종류인지 확인합니다. */ diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/adaptor/EventAdaptor.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/adaptor/EventAdaptor.java index e7eaff74..07a91eeb 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/adaptor/EventAdaptor.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/adaptor/EventAdaptor.java @@ -45,8 +45,8 @@ public Slice querySliceEventsByKeyword(String keyword, Pageable pageable) return eventRepository.querySliceEventsByKeyword(keyword, pageable); } - public List queryEventsByEndAtBefore(LocalDateTime time) { - return eventRepository.queryEventsByEndAtBefore(time); + public List queryEventsByEndAtBeforeAndStatusOpen(LocalDateTime time) { + return eventRepository.queryEventsByEndAtBeforeAndStatusOpen(time); } public List findAllByIds(List ids) { 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 fdbaf2c9..fa3bc13d 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 @@ -91,12 +91,16 @@ public Event(Long hostId, String name, LocalDateTime startAt, Long runTime) { Events.raise(EventCreationEvent.of(hostId, name)); } + public void validateStartAt() { + if (this.getStartAt().isBefore(LocalDateTime.now())) + throw EventOpenTimeExpiredException.EXCEPTION; + } + public void validateOpenStatus() { if (status == OPEN) throw CannotModifyOpenEventException.EXCEPTION; - // todo : 오픈 전과 후 검증 로직 이름 변경 } - public void validateStatusOpen() { + public void validateNotOpenStatus() { if (status != OPEN) throw EventNotOpenException.EXCEPTION; } @@ -145,6 +149,7 @@ public void prepare() { } public void open() { + validateStartAt(); updateStatus(OPEN, AlreadyOpenStatusException.EXCEPTION); } 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 6b3d4a3b..8c05baaf 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 @@ -30,6 +30,7 @@ public enum EventErrorCode implements BaseErrorCode { ALREADY_DELETED_STATUS(BAD_REQUEST, "Event_400_12", "이미 삭제된 이벤트입니다."), CANNOT_DELETE_BY_ISSUED_TICKET(BAD_REQUEST, "Event_400_13", "발급 티켓이 있는 이벤트는 삭제할 수 없습니다."), CANNOT_DELETE_BY_OPEN_EVENT(BAD_REQUEST, "Event_400_14", "오픈 상태인 이벤트는 삭제할 수 없습니다."), + OPEN_TIME_EXPIRED(BAD_REQUEST, "Event_400_15", "오픈 예정 시간이 현재 시간보다 빠릅니다."), USE_OTHER_API(BAD_REQUEST, "Event_400_8", "잘못된 접근입니다."); diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/EventOpenTimeExpiredException.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/EventOpenTimeExpiredException.java new file mode 100644 index 00000000..ceb7b515 --- /dev/null +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/exception/EventOpenTimeExpiredException.java @@ -0,0 +1,13 @@ +package band.gosrock.domain.domains.event.exception; + + +import band.gosrock.common.exception.DuDoongCodeException; + +public class EventOpenTimeExpiredException extends DuDoongCodeException { + + public static final DuDoongCodeException EXCEPTION = new EventOpenTimeExpiredException(); + + private EventOpenTimeExpiredException() { + super(EventErrorCode.OPEN_TIME_EXPIRED); + } +} diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/repository/EventCustomRepository.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/repository/EventCustomRepository.java index 8bb51857..c1fad0ae 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/repository/EventCustomRepository.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/repository/EventCustomRepository.java @@ -15,5 +15,5 @@ public interface EventCustomRepository { Slice querySliceEventsByKeyword(String keyword, Pageable pageable); - List queryEventsByEndAtBefore(LocalDateTime time); + List queryEventsByEndAtBeforeAndStatusOpen(LocalDateTime time); } diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/repository/EventCustomRepositoryImpl.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/repository/EventCustomRepositoryImpl.java index 365dcd18..950307f2 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/repository/EventCustomRepositoryImpl.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/event/repository/EventCustomRepositoryImpl.java @@ -63,8 +63,8 @@ public Slice querySliceEventsByKeyword(String keyword, Pageable pageable) } @Override - public List queryEventsByEndAtBefore(LocalDateTime time) { - return queryFactory.selectFrom(event).where(endAtBefore(time), notEqClosed()).fetch(); + public List queryEventsByEndAtBeforeAndStatusOpen(LocalDateTime time) { + return queryFactory.selectFrom(event).where(endAtBefore(time), statusEq(OPEN)).fetch(); } private BooleanExpression hostIdIn(List hostId) { @@ -83,8 +83,8 @@ private BooleanExpression statusEq(EventStatus status) { return event.status.eq(status); } - private BooleanExpression notEqClosed() { - return event.status.eq(CLOSED).not(); + private BooleanExpression statusNotEq(EventStatus status) { + return event.status.eq(status).not(); } private BooleanExpression nameContains(String keyword) { 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 7f30e92a..1d572a38 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 @@ -1,6 +1,5 @@ package band.gosrock.domain.domains.event.service; -import static band.gosrock.domain.domains.event.domain.QEvent.event; import band.gosrock.common.annotation.DomainService; import band.gosrock.domain.domains.event.adaptor.EventAdaptor; @@ -71,7 +70,7 @@ public Event updateEventStatus(Event event, EventStatus status) { } public List closeExpiredEventsEndAtBefore(LocalDateTime time) { - List events = eventAdaptor.queryEventsByEndAtBefore(time); + List events = eventAdaptor.queryEventsByEndAtBeforeAndStatusOpen(time); events.forEach(event -> updateEventStatus(event, EventStatus.CLOSED)); eventRepository.saveAll(events); return events; diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/domain/validator/OrderValidator.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/domain/validator/OrderValidator.java index 6c99e3e8..3bb54f8e 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/domain/validator/OrderValidator.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/domain/validator/OrderValidator.java @@ -189,7 +189,7 @@ public void validApproveStatePurchaseLimit(Order order) { /** 이벤트가 열려있는 상태인지 */ public void validEventIsOpen(Event event) { - event.validateStatusOpen(); + event.validateNotOpenStatus(); } /** 아이템의 종류가 1종류인지. */ diff --git a/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/cart/domain/CartValidatorTest.java b/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/cart/domain/CartValidatorTest.java index 69497f9b..c9331ba0 100644 --- a/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/cart/domain/CartValidatorTest.java +++ b/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/cart/domain/CartValidatorTest.java @@ -100,7 +100,7 @@ void setUp() { @Test public void 카트_티켓팅_이벤트_상태검증_성공() { // given - willDoNothing().given(event).validateStatusOpen(); + willDoNothing().given(event).validateNotOpenStatus(); // when cartValidator.validEventIsOpen(event); // then @@ -109,7 +109,7 @@ void setUp() { @Test public void 카트_티켓팅_이벤트_상태검증_실패() { // given - willThrow(EventNotOpenException.class).given(event).validateStatusOpen(); + willThrow(EventNotOpenException.class).given(event).validateNotOpenStatus(); // when // then assertThrows(EventNotOpenException.class, () -> cartValidator.validEventIsOpen(event)); diff --git a/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/event/EventTest.java b/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/event/EventTest.java index bead58be..6d7413a7 100644 --- a/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/event/EventTest.java +++ b/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/event/EventTest.java @@ -103,6 +103,7 @@ void setup() { @Test void 이벤트_종료로_상태변경_테스트() { // given + ReflectionTestUtils.setField(event, "status", EventStatus.OPEN); final EventStatus originalStatus = event.getStatus(); final EventStatus expectedStatus = EventStatus.CLOSED; // when @@ -118,7 +119,10 @@ void setup() { // given final EventStatus originalStatus = event.getStatus(); final EventStatus expectedStatus = EventStatus.OPEN; + final LocalDateTime startAt = LocalDateTime.now().plusMinutes(1); // when + when(eventBasic.getStartAt()).thenReturn(startAt); + event.setEventBasic(eventBasic); event.open(); // then assertEquals(expectedStatus, event.getStatus()); @@ -126,6 +130,19 @@ void setup() { assertThrows(AlreadyOpenStatusException.class, () -> event.open()); } + @Test + void 오픈_시간_이전인_이벤트는_오픈할수_없음() { + // given + final EventStatus originalStatus = event.getStatus(); + final LocalDateTime startAt = LocalDateTime.now().minusMinutes(1); + // when + when(eventBasic.getStartAt()).thenReturn(startAt); + event.setEventBasic(eventBasic); + // then + assertThrows(EventOpenTimeExpiredException.class, () -> event.open()); + assertEquals(originalStatus, event.getStatus()); + } + @Test void 이벤트_준비중으로_상태변경_테스트() { // given diff --git a/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/domain/validator/OrderValidatorTest.java b/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/domain/validator/OrderValidatorTest.java index 5c6292a8..015859f5 100644 --- a/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/domain/validator/OrderValidatorTest.java +++ b/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/domain/validator/OrderValidatorTest.java @@ -392,7 +392,7 @@ void setUp() { @Test public void 주문_티켓팅_이벤트_상태검증_성공() { // given - willDoNothing().given(event).validateStatusOpen(); + willDoNothing().given(event).validateNotOpenStatus(); // when orderValidator.validEventIsOpen(event); // then @@ -401,7 +401,7 @@ void setUp() { @Test public void 주문_티켓팅_이벤트_상태검증_실패() { // given - willThrow(EventNotOpenException.class).given(event).validateStatusOpen(); + willThrow(EventNotOpenException.class).given(event).validateNotOpenStatus(); // when // then assertThrows(EventNotOpenException.class, () -> orderValidator.validEventIsOpen(event)); diff --git a/README.md b/README.md index af6e389c..212f469e 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,12 @@ - [찬진 : Spring 공통 응답 형식 만들기 ResponseBodyAdvice](https://devnm.tistory.com/28) - [찬진 : Spring swagger 같은 응답 코드 여러 에러 예시 만들기](https://devnm.tistory.com/29) - [찬진 : spring 프록시 환경에서 HttpContentCache 적용](https://devnm.tistory.com/30) +- [찬진 : spring rate limit 적용히기 bucket4j](https://devnm.tistory.com/31) +- [찬진 : spring thymeleaf to pdf 이미지,한글 적용하기](https://devnm.tistory.com/32) +- [찬진 : spring batch 도커로 세팅하기 with jenkins](https://devnm.tistory.com/33) +- [찬진 : spring feign client wiremock test](https://devnm.tistory.com/34) +- [찬진 : spring oauth Open ID Connect with kakao](https://devnm.tistory.com/35) +- [찬진 : 멀티모듈 jacoco , sonarqube (cloud) 세팅](https://devnm.tistory.com/36) - [찬진 : 도커 로그 ec2환경에서 클라우드 와치로 전송하기](https://devnm.tistory.com/8) - [경민 : Custom Enum Validator 구현하기](https://gengminy.tistory.com/47) - [경민 : Reflection 을 이용하여 Enum Validator 개선하기](https://gengminy.tistory.com/48) From ebff7085194ff761ac5907996ecd4029b40a1223 Mon Sep 17 00:00:00 2001 From: Chan Jin Date: Thu, 9 Mar 2023 00:27:52 +0900 Subject: [PATCH 3/3] =?UTF-8?q?refactor=20:=20=20rate=20limit=20whitelist?= =?UTF-8?q?=20(=20next=20js=20init=20=EB=8C=80=EC=9D=91=20)=20(#539)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor : RedissonLockAop IllegalMonitorStateException 관련 수정 * feat : acl white list 추가 --- .../config/rateLimit/ThrottlingInterceptor.java | 15 ++++++++++++++- DuDoong-Api/src/main/resources/application.yml | 2 ++ DuDoong-Batch/src/main/resources/application.yml | 1 - .../common/aop/redissonLock/RedissonLockAop.java | 3 ++- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/config/rateLimit/ThrottlingInterceptor.java b/DuDoong-Api/src/main/java/band/gosrock/api/config/rateLimit/ThrottlingInterceptor.java index 3fe79590..632e7b54 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/config/rateLimit/ThrottlingInterceptor.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/config/rateLimit/ThrottlingInterceptor.java @@ -8,10 +8,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.github.bucket4j.Bucket; import java.io.IOException; +import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; @@ -26,6 +28,9 @@ public class ThrottlingInterceptor implements HandlerInterceptor { private final IPRateLimiter ipRateLimiter; private final ObjectMapper objectMapper; + @Value("${acl.whiteList}") + private List aclWhiteList; + private final SlackThrottleErrorSender slackThrottleErrorSender; @Override @@ -33,10 +38,18 @@ public boolean preHandle( HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { Long userId = SecurityUtils.getCurrentUserId(); + String remoteAddr = request.getRemoteAddr(); + log.info("remoteAddr : " + remoteAddr); + + // next js ssr 대응 + if (aclWhiteList.contains(remoteAddr)) { + log.info("white List pass" + remoteAddr); + return true; + } + Bucket bucket; if (userId == 0L) { // 익명 유저 ip 기반처리 - String remoteAddr = request.getRemoteAddr(); bucket = ipRateLimiter.resolveBucket(remoteAddr); } else { // 비 익명 유저 유저 아이디 기반 처리 diff --git a/DuDoong-Api/src/main/resources/application.yml b/DuDoong-Api/src/main/resources/application.yml index 0c5115e2..e53da179 100644 --- a/DuDoong-Api/src/main/resources/application.yml +++ b/DuDoong-Api/src/main/resources/application.yml @@ -27,6 +27,8 @@ swagger: throttle: overdraft: ${RATE_LIMIT_OVERDRAFT:60} greedyRefill: ${RATE_LIMIT_REFILL:60} + +acl.whiteList : ${ACL_WHITELIST:127.0.0.1,127.0.0.2} --- spring: config: diff --git a/DuDoong-Batch/src/main/resources/application.yml b/DuDoong-Batch/src/main/resources/application.yml index 5d8a5c46..32a13741 100644 --- a/DuDoong-Batch/src/main/resources/application.yml +++ b/DuDoong-Batch/src/main/resources/application.yml @@ -7,7 +7,6 @@ spring: - common batch.job.names: ${job.name:NONE} - --- spring: config: diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/common/aop/redissonLock/RedissonLockAop.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/common/aop/redissonLock/RedissonLockAop.java index b6207399..714be3e6 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/common/aop/redissonLock/RedissonLockAop.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/common/aop/redissonLock/RedissonLockAop.java @@ -73,7 +73,8 @@ public Object lock(final ProceedingJoinPoint joinPoint) throws Throwable { try { rLock.unlock(); } catch (IllegalMonitorStateException e) { - rLock.forceUnlock(); + log.error(e + baseKey + dynamicKey); + throw e; } } }