Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a4bb038
refactor(NotificationSubscriber): 사용하지 않는 클래스 삭제
m-a-king Dec 9, 2024
2469f63
feat(ServerEventType): 도메인 이벤트 타입 DOMAIN 추가
m-a-king Dec 9, 2024
566958a
feat(ServerEventType): 서버 이벤트 타입 DOMAIN_EVENT 추가
m-a-king Dec 9, 2024
3451d4f
feat(NotificationMessageConverter): rename MessageConverter
m-a-king Dec 9, 2024
822381f
feat(DomainEventSubType): 도메인 이벤트 서브타입 추가
m-a-king Dec 9, 2024
3f0788a
feat(CreateRecruitBoardService): publishCreateRecruitBoardEvent 추가
m-a-king Dec 9, 2024
83b23f0
feat(CreateRecruitBoard): CreateRecruitBoardEvent subscriber, convert…
m-a-king Dec 9, 2024
e14f0c1
feat(CreateRecruitBoardHandler): CreateRecruitBoardEvent -> InterestC…
m-a-king Dec 9, 2024
9f6447f
refactor: 패키지 명 수정
m-a-king Dec 9, 2024
ee2e234
feat(event): 역직렬화 지원
m-a-king Dec 9, 2024
9c56b00
test(RedisCreateRecruitBoardSubscriber): 메시지 컨버팅, 핸들링 모킹 테스트 추가
m-a-king Dec 9, 2024
9615172
test(CreateRecruitBoardMessageConverterTest): 메시지 컨버팅 테스트 추가
m-a-king Dec 9, 2024
c014a8c
test(CreateRecruitBoardHandlerImpl): 이벤트 핸들링 모킹 테스트 추가
m-a-king Dec 9, 2024
f448688
style(개행): 마지막 줄 개행
m-a-king Dec 9, 2024
8cbaa54
test(CreateRecruitBoardMessageConverter): 예외 테스트 케이스 추가
m-a-king Dec 9, 2024
0a2b427
test(InterestCenterQueryService): 센터 아이디로 봉사자 아이디 목록 조회 테스트 추가
m-a-king Dec 9, 2024
f739c4a
test(NotificationMessageConverter): INTEREST_CENTER_CREATE_RECRUIT_BO…
m-a-king Dec 9, 2024
98598fa
fix(RedisListenerRegistrar): 올바른 리스너 등록
m-a-king Dec 9, 2024
0fb521a
refactor(NotificationMessageConverter): 상수 처리, 노트 알림 명명 수정
m-a-king Dec 10, 2024
e1a837b
refactor(RedisListenerRegistrar): 로그 수정
m-a-king Dec 10, 2024
d2d2fcb
refactor(VolunteerApplyStatusChangeService): 더티 체킹 활용
m-a-king Dec 10, 2024
81616ee
style: 불필요한 라인 삭제, 개행
m-a-king Dec 10, 2024
dcfe12e
refactor: 패키지 정리
m-a-king Dec 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.somemore.global.common.event;

import lombok.RequiredArgsConstructor;

import java.util.Arrays;

@RequiredArgsConstructor
public enum DomainEventSubType {
CREATE_RECRUIT_BOARD("모집 글 등록"),
;

private final String description;

public static DomainEventSubType from(String value) {
return Arrays.stream(DomainEventSubType.values())
.filter(type -> type.name().equalsIgnoreCase(value))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("잘못된 도메인 이벤트 타입입니다: " + value));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,8 @@
@SuperBuilder
public abstract class ServerEvent<T extends Enum<T>> {

@JsonProperty("type")
private final ServerEventType type;

@JsonProperty("subType")
private final T subType;

@JsonProperty("createdAt")
private final LocalDateTime createdAt;

protected ServerEvent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
@RequiredArgsConstructor
@Getter
public enum ServerEventType {
NOTIFICATION(NotificationSubType.class);
NOTIFICATION(NotificationSubType.class),
DOMAIN_EVENT(DomainEventSubType.class),
;

private final Class<? extends Enum<?>> subtype;

Expand Down
17 changes: 13 additions & 4 deletions src/main/java/com/somemore/global/redis/config/RedisConfig.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.somemore.global.redis.config;

import com.somemore.global.common.event.ServerEventType;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
Expand All @@ -19,6 +18,8 @@
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.util.Map;

@Configuration
@EnableRedisRepositories
@RequiredArgsConstructor
Expand Down Expand Up @@ -55,13 +56,21 @@ public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connec

@Bean
public ChannelTopic notificationTopic() {
return new ChannelTopic("notifications");
return new ChannelTopic("notification");
}

@Bean
public Map<ServerEventType, ChannelTopic> eventTopicMap(ChannelTopic notificationTopic) {
public ChannelTopic domainEventTopic() {
return new ChannelTopic("domainEvent");
}


@Bean
public Map<ServerEventType, ChannelTopic> eventTopicMap(ChannelTopic notificationTopic,
ChannelTopic domainEventTopic) {
return Map.of(
ServerEventType.NOTIFICATION, notificationTopic
ServerEventType.NOTIFICATION, notificationTopic,
ServerEventType.DOMAIN_EVENT, domainEventTopic
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.somemore.global.redis.registrar;

import com.somemore.notification.subscriber.RedisNotificationSubscriber;
import com.somemore.interestcenter.event.subscriber.RedisCreateRecruitBoardSubscriber;
import com.somemore.notification.event.subscriber.RedisNotificationSubscriber;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -15,7 +16,9 @@ public class RedisListenerRegistrar {

private final RedisMessageListenerContainer container;
private final RedisNotificationSubscriber redisNotificationSubscriber;
private final RedisCreateRecruitBoardSubscriber redisCreateRecruitBoardSubscriber;
private final ChannelTopic notificationTopic;
private final ChannelTopic domainEventTopic;

@PostConstruct
public void registerListeners() {
Expand All @@ -24,6 +27,7 @@ public void registerListeners() {

private void registerNotificationListener() {
container.addMessageListener(redisNotificationSubscriber, notificationTopic);
log.info("Redis 알림 리스너가 '{}' 토픽에 성공적으로 등록되었습니다.", notificationTopic.getTopic());
container.addMessageListener(redisCreateRecruitBoardSubscriber, domainEventTopic);
log.info("리스너가 토픽에 성공적으로 등록되었습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.somemore.interestcenter.event.converter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.somemore.global.common.event.DomainEventSubType;
import com.somemore.recruitboard.event.CreateRecruitBoardEvent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@RequiredArgsConstructor
@Component
public class CreateRecruitBoardMessageConverter {

private final ObjectMapper objectMapper;

public CreateRecruitBoardEvent from(String message) {
try {
JsonNode rootNode = objectMapper.readTree(message);
String eventType = rootNode.get("subType").asText();

return switch (DomainEventSubType.from(eventType)) {
case CREATE_RECRUIT_BOARD -> parseCreateRecruitBoardEvent(message);
};
} catch (Exception e) {
log.error(e.getMessage());
throw new IllegalStateException();
}
}

private CreateRecruitBoardEvent parseCreateRecruitBoardEvent(String message) throws JsonProcessingException {

return objectMapper.readValue(message, CreateRecruitBoardEvent.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.somemore.interestcenter.event.domain;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.somemore.global.common.event.ServerEvent;
import com.somemore.global.common.event.ServerEventType;
import com.somemore.notification.domain.NotificationSubType;
import lombok.Getter;
import lombok.experimental.SuperBuilder;

import java.time.LocalDateTime;
import java.util.UUID;

@Getter
@SuperBuilder
public class InterestCenterCreateRecruitBoardEvent extends ServerEvent<NotificationSubType> {
private final UUID volunteerId;
private final UUID centerId;
private final Long recruitBoardId;

@JsonCreator
public InterestCenterCreateRecruitBoardEvent(
@JsonProperty(value = "volunteerId", required = true) UUID volunteerId,
@JsonProperty(value = "centerId", required = true) UUID centerId,
@JsonProperty(value = "recruitBoardId", required = true) Long recruitBoardId
) {
super(ServerEventType.NOTIFICATION, NotificationSubType.INTEREST_CENTER_CREATE_RECRUIT_BOARD, LocalDateTime.now());
this.volunteerId = volunteerId;
this.centerId = centerId;
this.recruitBoardId = recruitBoardId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.somemore.interestcenter.event.handler;

import com.somemore.recruitboard.event.CreateRecruitBoardEvent;

public interface CreateRecruitBoardHandler {

void handle(CreateRecruitBoardEvent event);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.somemore.interestcenter.event.handler;

import com.somemore.global.common.event.ServerEventPublisher;
import com.somemore.global.common.event.ServerEventType;
import com.somemore.interestcenter.event.domain.InterestCenterCreateRecruitBoardEvent;
import com.somemore.interestcenter.usecase.InterestCenterQueryUseCase;
import com.somemore.notification.domain.NotificationSubType;
import com.somemore.recruitboard.event.CreateRecruitBoardEvent;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.UUID;

@Component
@RequiredArgsConstructor
@Transactional
public class CreateRecruitBoardHandlerImpl implements CreateRecruitBoardHandler {

private final InterestCenterQueryUseCase interestCenterQueryUseCase;
private final ServerEventPublisher serverEventPublisher;

@Override
public void handle(CreateRecruitBoardEvent createRecruitBoardEvent) {
UUID centerId = createRecruitBoardEvent.getCenterId();
List<UUID> volunteerIdsByCenterId = interestCenterQueryUseCase.getVolunteerIdsByCenterId(centerId);

volunteerIdsByCenterId.forEach(volunteerId ->
publishInterestCenterCreateRecruitBoardEvent(createRecruitBoardEvent, volunteerId, centerId)
);
}

private void publishInterestCenterCreateRecruitBoardEvent(CreateRecruitBoardEvent createRecruitBoardEvent, UUID volunteerId, UUID centerId) {
InterestCenterCreateRecruitBoardEvent event = InterestCenterCreateRecruitBoardEvent.builder()
.type(ServerEventType.NOTIFICATION)
.subType(NotificationSubType.INTEREST_CENTER_CREATE_RECRUIT_BOARD)
.volunteerId(volunteerId)
.centerId(centerId)
.recruitBoardId(createRecruitBoardEvent.getRecruitBoardId())
.build();

serverEventPublisher.publish(event);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.somemore.interestcenter.event.subscriber;

import com.somemore.interestcenter.event.converter.CreateRecruitBoardMessageConverter;
import com.somemore.interestcenter.event.handler.CreateRecruitBoardHandler;
import com.somemore.recruitboard.event.CreateRecruitBoardEvent;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class RedisCreateRecruitBoardSubscriber implements MessageListener {

private final CreateRecruitBoardHandler createRecruitBoardHandler;
private final CreateRecruitBoardMessageConverter messageConverter;

@Override
public void onMessage(Message message, byte[] pattern) {
CreateRecruitBoardEvent event = messageConverter.from(
new String(message.getBody())
);

createRecruitBoardHandler.handle(event);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public interface InterestCenterRepository {
Optional<InterestCenter> findById(Long id);
Optional<RegisterInterestCenterResponseDto> findInterestCenterResponseById(Long id);
List<UUID> findInterestCenterIdsByVolunteerId(UUID volunteerId);
List<UUID> findVolunteerIdsByCenterId(UUID centerId);
boolean existsByVolunteerIdAndCenterId(UUID volunteerId, UUID centerId);
Optional<InterestCenter> findByVolunteerIdAndCenterId(UUID volunteerId, UUID centerId);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.somemore.interestcenter.repository;

import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.somemore.interestcenter.domain.InterestCenter;
import com.somemore.interestcenter.domain.QInterestCenter;
Expand All @@ -18,20 +19,21 @@ public class InterestCenterRepositoryImpl implements InterestCenterRepository {
private final JPAQueryFactory queryFactory;
private final InterestCenterJpaRepository interestCenterJpaRepository;

private final static QInterestCenter interestCenter = QInterestCenter.interestCenter;

@Override
public InterestCenter save(InterestCenter interestCenter) {
return interestCenterJpaRepository.save(interestCenter);
}

@Override
public Optional<InterestCenter> findById(Long id) {
QInterestCenter interestCenter = QInterestCenter.interestCenter;

InterestCenter result = queryFactory
.selectFrom(interestCenter)
.where(
interestCenter.id.eq(id)
.and(interestCenter.deleted.eq(false))
.and(isNotDeleted())
)
.fetchOne();

Expand All @@ -40,7 +42,6 @@ public Optional<InterestCenter> findById(Long id) {

@Override
public Optional<RegisterInterestCenterResponseDto> findInterestCenterResponseById(Long id) {
QInterestCenter interestCenter = QInterestCenter.interestCenter;

RegisterInterestCenterResponseDto result = queryFactory
.select(
Expand All @@ -54,7 +55,7 @@ public Optional<RegisterInterestCenterResponseDto> findInterestCenterResponseByI
.from(interestCenter)
.where(
interestCenter.id.eq(id)
.and(interestCenter.deleted.eq(false))
.and(isNotDeleted())
)
.fetchOne();

Expand All @@ -63,38 +64,49 @@ public Optional<RegisterInterestCenterResponseDto> findInterestCenterResponseByI

@Override
public List<UUID> findInterestCenterIdsByVolunteerId(UUID volunteerId) {
QInterestCenter interestCenter = QInterestCenter.interestCenter;

return queryFactory
.select(interestCenter.centerId)
.from(interestCenter)
.where(
interestCenter.volunteerId.eq(volunteerId)
.and(interestCenter.deleted.eq(false))
.and(isNotDeleted())
)
.fetch();
}

@Override
public List<UUID> findVolunteerIdsByCenterId(UUID centerId) {

return queryFactory
.select(interestCenter.volunteerId)
.from(interestCenter)
.where(
interestCenter.centerId.eq(centerId),
isNotDeleted()
)
.fetch();
}

@Override
public boolean existsByVolunteerIdAndCenterId(UUID volunteerId, UUID centerId) {
QInterestCenter interestCenter = QInterestCenter.interestCenter;

Integer result = queryFactory
.selectOne()
.from(interestCenter)
.where(
interestCenter.volunteerId.eq(volunteerId)
.and(interestCenter.centerId.eq(centerId))
.and(interestCenter.deleted.eq(false))
.and(isNotDeleted())
)
.fetchFirst();

return result != null;
}


@Override
public Optional<InterestCenter> findByVolunteerIdAndCenterId(UUID volunteerId, UUID centerId) {
QInterestCenter interestCenter = QInterestCenter.interestCenter;

InterestCenter result = queryFactory.selectFrom(interestCenter)
.where(
Expand All @@ -106,4 +118,7 @@ public Optional<InterestCenter> findByVolunteerIdAndCenterId(UUID volunteerId, U
return Optional.ofNullable(result);
}

private static BooleanExpression isNotDeleted() {
return interestCenter.deleted.eq(false);
}
}
Loading
Loading