Skip to content

Commit

Permalink
feat(vote-paper) register
Browse files Browse the repository at this point in the history
refactor: VoteRegisterEvent Produce, Consume 로직 동작되도록 수정
refactor: Spring AWS SQS Listening 이슈로 인해 Spring Devtools 비활성화 처리

chore: RegisterVotePaper FCM 전송 로직 추가
  • Loading branch information
habinkim committed Apr 29, 2024
1 parent 78cdd75 commit d2953af
Show file tree
Hide file tree
Showing 21 changed files with 143 additions and 52 deletions.
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ dependencies {

// development
implementation 'org.springframework.boot:spring-boot-starter-actuator'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
// @see https://github.com/awspring/spring-cloud-aws/issues/657
// developmentOnly 'org.springframework.boot:spring-boot-devtools'
developmentOnly 'org.springframework.boot:spring-boot-docker-compose'

// External
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public List<NotificationApprovedDevice> fetchNotificationApprovedDevice(
)
)
.from(DEVICE)
.join(DEVICE.account, ACCOUNT).fetchJoin()
.join(DEVICE.account, ACCOUNT)
.where(predicate)
.fetch();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
package com.tune_fun.v1.account.domain.value;

public record NotificationApprovedDevice(
String id,
String username,
String nickname,
String fcmToken,
String deviceToken
) {
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public final class NotificationApprovedDevice {
private Long id;
private String username;
private String nickname;
private String fcmToken;
private String deviceToken;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.messaging.MessageHeaders.CONTENT_TYPE;

@Component
@RequiredArgsConstructor
public class SqsProvider {
Expand All @@ -23,7 +20,6 @@ public SendResult<?> sendMessageRangedQueue(@NotBlank final String queueName, @N

return sqsTemplate.send(to -> to
.queue(sqsProducer.queueName())
.header(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.payload(message)
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.tune_fun.v1.vote.adapter.input.message;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.firebase.messaging.FirebaseMessagingException;
import com.tune_fun.v1.vote.application.port.input.usecase.SendVotePaperRegisterFcmUseCase;
import com.tune_fun.v1.vote.domain.behavior.ProduceVotePaperUploadEvent;
import com.tune_fun.v1.vote.domain.behavior.ProduceVotePaperRegisterEvent;
import io.awspring.cloud.sqs.annotation.SqsListener;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -14,10 +16,10 @@ public class VoteMessageConsumer {

private final SendVotePaperRegisterFcmUseCase sendVotePaperRegisterFcmUseCase;

@SqsListener("send-vote-paper-upload-notification-dev")
public void consumeVotePaperUploadEvent(final ProduceVotePaperUploadEvent produceVotePaperUploadEvent) {
log.info("send-vote-paper-upload-notification-dev: {}", produceVotePaperUploadEvent.id());
sendVotePaperRegisterFcmUseCase.send(produceVotePaperUploadEvent);
@SqsListener(value = "send-vote-paper-upload-notification-dev")
public void consumeVotePaperUploadEvent(final ProduceVotePaperRegisterEvent produceVotePaperRegisterEvent) throws JsonProcessingException, FirebaseMessagingException {
log.info("send-vote-paper-upload-notification-dev: {}", produceVotePaperRegisterEvent.id());
sendVotePaperRegisterFcmUseCase.send(produceVotePaperRegisterEvent);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.tune_fun.v1.vote.adapter.input.rest;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.tune_fun.v1.account.domain.value.CurrentUser;
import com.tune_fun.v1.common.config.Uris;
import com.tune_fun.v1.common.hexagon.WebAdapter;
Expand Down Expand Up @@ -29,7 +30,7 @@ public class VotePaperController {
@PreAuthorize("hasRole('ARTIST')")
@PostMapping(value = Uris.REGISTER_VOTE_PAPER)
public ResponseEntity<Response<BasePayload>> registerVotePaper(@Valid @RequestBody final VotePaperCommands.Register command,
@CurrentUser final User user) {
@CurrentUser final User user) throws JsonProcessingException {
registerVotePaperUseCase.register(command, user);
return responseMapper.ok(MessageCode.CREATED);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.tune_fun.v1.vote.adapter.output.firebase;

import com.google.firebase.messaging.FirebaseMessagingException;
import com.tune_fun.v1.external.firebase.FirebaseMessagingMediator;
import com.tune_fun.v1.external.firebase.FirebaseMto;
import com.tune_fun.v1.vote.application.port.output.SendVoteFcmPort;
import com.tune_fun.v1.vote.domain.behavior.SendVotePaperRegisterFcm;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

Expand All @@ -12,4 +15,11 @@ public class VoteFcmAdapter implements SendVoteFcmPort {

private final FirebaseMessagingMediator firebaseMessagingMediator;

private final VoteFirebaseMessagingMapper voteFirebaseMessagingMapper;

@Override
public void notification(final SendVotePaperRegisterFcm behavior) throws FirebaseMessagingException {
FirebaseMto.ByTokens mto = voteFirebaseMessagingMapper.fromSendVotePaperRegisterFcmBehavior(behavior);
firebaseMessagingMediator.sendMulticastMessageByTokens(mto);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.tune_fun.v1.vote.adapter.output.firebase;

import com.tune_fun.v1.common.config.BaseMapperConfig;
import com.tune_fun.v1.external.firebase.FirebaseMto;
import com.tune_fun.v1.vote.domain.behavior.SendVotePaperRegisterFcm;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper(config = BaseMapperConfig.class)
public abstract class VoteFirebaseMessagingMapper {

@Mapping(target = "tokens", source = "fcmTokens")
public abstract FirebaseMto.ByTokens fromSendVotePaperRegisterFcmBehavior(final SendVotePaperRegisterFcm behavior);

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import com.tune_fun.v1.external.aws.sqs.SqsProvider;
import com.tune_fun.v1.vote.application.port.output.ProduceVotePaperUploadEventPort;
import com.tune_fun.v1.vote.domain.behavior.ProduceVotePaperUploadEvent;
import com.tune_fun.v1.vote.domain.behavior.ProduceVotePaperRegisterEvent;
import io.awspring.cloud.sqs.operations.SendResult;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

Expand All @@ -15,7 +16,7 @@ public class VoteMessageBrokerAdapter implements ProduceVotePaperUploadEventPort


@Override
public void produceVotePaperUploadEvent(final ProduceVotePaperUploadEvent produceVotePaperUploadEvent) {
sqsProvider.sendMessageRangedQueue(VOTE_PAPER_UPLOAD_QUEUE, produceVotePaperUploadEvent);
public SendResult<?> produceVotePaperUploadEvent(final ProduceVotePaperRegisterEvent produceVotePaperRegisterEvent) {
return sqsProvider.sendMessageRangedQueue(VOTE_PAPER_UPLOAD_QUEUE, produceVotePaperRegisterEvent);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
public abstract class VotePaperMapper {

@Mapping(target = "option", source = "option", qualifiedByName = "toValue")
@Mapping(target = "author", source = "author.nickname")
public abstract RegisteredVotePaper registeredVotePaper(final VotePaperJpaEntity votePaperJpaEntity);

@Named("toValue")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.tune_fun.v1.vote.application.port.input.usecase;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.tune_fun.v1.vote.application.port.input.command.VotePaperCommands;
import org.springframework.security.core.userdetails.User;

@FunctionalInterface
public interface RegisterVotePaperUseCase {

void register(final VotePaperCommands.Register command, User user);
void register(final VotePaperCommands.Register command, User user) throws JsonProcessingException;

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.tune_fun.v1.vote.application.port.input.usecase;

import com.tune_fun.v1.vote.domain.behavior.ProduceVotePaperUploadEvent;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.firebase.messaging.FirebaseMessagingException;
import com.tune_fun.v1.vote.domain.behavior.ProduceVotePaperRegisterEvent;

public interface SendVotePaperRegisterFcmUseCase {
void send(final ProduceVotePaperUploadEvent produceVotePaperUploadEvent);
void send(final ProduceVotePaperRegisterEvent produceVotePaperRegisterEvent) throws JsonProcessingException, FirebaseMessagingException;
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package com.tune_fun.v1.vote.application.port.output;

import com.tune_fun.v1.vote.domain.behavior.ProduceVotePaperUploadEvent;
import com.tune_fun.v1.vote.domain.behavior.ProduceVotePaperRegisterEvent;
import io.awspring.cloud.sqs.operations.SendResult;

public interface ProduceVotePaperUploadEventPort {

void produceVotePaperUploadEvent(final ProduceVotePaperUploadEvent produceVotePaperUploadEvent);
SendResult<?> produceVotePaperUploadEvent(final ProduceVotePaperRegisterEvent produceVotePaperRegisterEvent);

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
package com.tune_fun.v1.vote.application.port.output;

import com.google.firebase.messaging.FirebaseMessagingException;
import com.tune_fun.v1.vote.domain.behavior.SendVotePaperRegisterFcm;

public interface SendVoteFcmPort {
void notification(final SendVotePaperRegisterFcm sendVotePaperRegisterFcmBehavior) throws FirebaseMessagingException;
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
package com.tune_fun.v1.vote.application.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.tune_fun.v1.common.exception.CommonApplicationException;
import com.tune_fun.v1.common.hexagon.UseCase;
import com.tune_fun.v1.common.util.ObjectUtil;
import com.tune_fun.v1.vote.application.port.input.command.VotePaperCommands;
import com.tune_fun.v1.vote.application.port.input.usecase.RegisterVotePaperUseCase;
import com.tune_fun.v1.vote.application.port.output.LoadVotePaperPort;
import com.tune_fun.v1.vote.application.port.output.ProduceVotePaperUploadEventPort;
import com.tune_fun.v1.vote.application.port.output.SaveVoteChoicePort;
import com.tune_fun.v1.vote.application.port.output.SaveVotePaperPort;
import com.tune_fun.v1.vote.domain.behavior.ProduceVotePaperUploadEvent;
import com.tune_fun.v1.vote.domain.behavior.ProduceVotePaperRegisterEvent;
import com.tune_fun.v1.vote.domain.behavior.SaveVoteChoice;
import com.tune_fun.v1.vote.domain.behavior.SaveVotePaper;
import com.tune_fun.v1.vote.domain.value.RegisteredVotePaper;
import io.awspring.cloud.sqs.operations.SendResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Service;
Expand All @@ -22,6 +26,7 @@

import static com.tune_fun.v1.common.response.MessageCode.VOTE_POLICY_ONE_VOTE_PAPER_PER_USER;

@Slf4j
@Service
@UseCase
@RequiredArgsConstructor
Expand All @@ -36,17 +41,21 @@ public class RegisterVotePaperService implements RegisterVotePaperUseCase {

private final VoteBehaviorMapper voteBehaviorMapper;

private final ObjectUtil objectUtil;


@Transactional
@Override
public void register(final VotePaperCommands.Register command, final User user) {
public void register(final VotePaperCommands.Register command, final User user) throws JsonProcessingException {
validateRegistrableVotePaperCount(user);
RegisteredVotePaper registeredVotePaper = saveVotePaper(command);
saveVoteChoiceByRegisteredVotePaper(command, registeredVotePaper);

// TODO : Scheduling 정책 수립
// ProduceVotePaperUploadEvent produceVotePaperUploadEventBehavior = getProduceVotePaperUploadEventBehavior(registeredVotePaper);
// produceVotePaperUploadEventPort.produceVotePaperUploadEvent(produceVotePaperUploadEventBehavior);
ProduceVotePaperRegisterEvent produceVotePaperRegisterEventBehavior = getProduceVotePaperUploadEventBehavior(registeredVotePaper);
SendResult<?> sendResult = produceVotePaperUploadEventPort.produceVotePaperUploadEvent(produceVotePaperRegisterEventBehavior);

log.info("sendResult: {}", objectUtil.objectToPrettyJson(sendResult.message().getPayload()));
}

public void validateRegistrableVotePaperCount(final User user) {
Expand All @@ -66,8 +75,7 @@ public void saveVoteChoiceByRegisteredVotePaper(VotePaperCommands.Register comma
saveVoteChoicePort.saveVoteChoice(registeredVotePaper.id(), saveVoteChoicesBehavior);
}

private static @NotNull ProduceVotePaperUploadEvent getProduceVotePaperUploadEventBehavior(RegisteredVotePaper registeredVotePaper) {
return new ProduceVotePaperUploadEvent(registeredVotePaper.uuid(), registeredVotePaper.title(), registeredVotePaper.content(),
registeredVotePaper.voteStartAt(), registeredVotePaper.voteEndAt());
private static @NotNull ProduceVotePaperRegisterEvent getProduceVotePaperUploadEventBehavior(RegisteredVotePaper registeredVotePaper) {
return new ProduceVotePaperRegisterEvent(registeredVotePaper.uuid(), registeredVotePaper.author(), registeredVotePaper.title(), registeredVotePaper.content());
}
}
Original file line number Diff line number Diff line change
@@ -1,33 +1,47 @@
package com.tune_fun.v1.vote.application.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.firebase.messaging.FirebaseMessagingException;
import com.tune_fun.v1.account.application.port.output.device.LoadDevicePort;
import com.tune_fun.v1.account.domain.value.NotificationApprovedDevice;
import com.tune_fun.v1.common.hexagon.UseCase;
import com.tune_fun.v1.common.util.ObjectUtil;
import com.tune_fun.v1.vote.application.port.input.usecase.SendVotePaperRegisterFcmUseCase;
import com.tune_fun.v1.vote.application.port.output.SendVoteFcmPort;
import com.tune_fun.v1.vote.domain.behavior.ProduceVotePaperUploadEvent;
import com.tune_fun.v1.vote.domain.behavior.ProduceVotePaperRegisterEvent;
import com.tune_fun.v1.vote.domain.behavior.SendVotePaperRegisterFcm;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

import static org.springframework.transaction.annotation.Propagation.REQUIRES_NEW;

@Slf4j
@Service
@UseCase
@RequiredArgsConstructor
public class SendVotePaperRegisterFcmService implements SendVotePaperRegisterFcmUseCase {

private final LoadDevicePort loadDevicePort;

private final SendVoteFcmPort sendVoteFcmPort;

@Transactional(propagation = Propagation.REQUIRES_NEW)
private final VoteBehaviorMapper voteBehaviorMapper;

private final ObjectUtil objectUtil;

@Transactional(propagation = REQUIRES_NEW)
@Override
public void send(ProduceVotePaperUploadEvent produceVotePaperUploadEvent) {
public void send(ProduceVotePaperRegisterEvent produceVotePaperRegisterEvent) throws JsonProcessingException, FirebaseMessagingException {
List<NotificationApprovedDevice> notificationApprovedDevices = loadDevicePort.
loadNotificationApprovedDevice(true, null, null);

// TODO : FCM 발송 로직 구현
log.info("notificationApprovedDevices: {}", objectUtil.objectToPrettyJson(notificationApprovedDevices));

SendVotePaperRegisterFcm sendVotePaperRegisterFcmBehavior = voteBehaviorMapper
.sendVotePaperRegisterFcm(produceVotePaperRegisterEvent, notificationApprovedDevices);
sendVoteFcmPort.notification(sendVotePaperRegisterFcmBehavior);
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
package com.tune_fun.v1.vote.application.service;

import com.tune_fun.v1.account.domain.value.NotificationApprovedDevice;
import com.tune_fun.v1.common.config.BaseMapperConfig;
import com.tune_fun.v1.vote.application.port.input.command.VotePaperCommands;
import com.tune_fun.v1.vote.domain.behavior.ProduceVotePaperRegisterEvent;
import com.tune_fun.v1.vote.domain.behavior.SaveVoteChoice;
import com.tune_fun.v1.vote.domain.behavior.SaveVotePaper;
import com.tune_fun.v1.vote.domain.behavior.SendVotePaperRegisterFcm;
import org.mapstruct.IterableMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;

import java.util.List;
import java.util.Set;

import static java.util.stream.Collectors.toSet;

@Mapper(config = BaseMapperConfig.class)
public abstract class VoteBehaviorMapper {

Expand All @@ -25,4 +31,19 @@ public abstract class VoteBehaviorMapper {
@Mapping(target = "offerDurationMs", source = "durationMs")
@Mapping(target = "offerReleaseDate", source = "releaseDate")
public abstract SaveVoteChoice saveVoteChoice(final VotePaperCommands.Offer offer);

@Mapping(target = "title", source = "event", qualifiedByName = "title")
@Mapping(target = "body", source = "event.title")
@Mapping(target = "fcmTokens", source = "devices", qualifiedByName = "fcmTokens")
public abstract SendVotePaperRegisterFcm sendVotePaperRegisterFcm(final ProduceVotePaperRegisterEvent event, final List<NotificationApprovedDevice> devices);

@Named("title")
public String title(final ProduceVotePaperRegisterEvent event) {
return String.format("[%s]님이 투표를 게재하였습니다.", event.author());
}

@Named("fcmTokens")
public Set<String> fcmTokens(final List<NotificationApprovedDevice> devices) {
return devices.stream().map(NotificationApprovedDevice::getFcmToken).collect(toSet());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.tune_fun.v1.vote.domain.behavior;

public record ProduceVotePaperRegisterEvent(String id, String author, String title, String content) {
}
Loading

0 comments on commit d2953af

Please sign in to comment.