Skip to content

Commit

Permalink
fix: 미션 삭제 푸시 알림 (#114)
Browse files Browse the repository at this point in the history
* fix: 미션 삭제 시 호스트에게 알림이 가지 않도록 푸시 알림을 보내기 전에 구독 취소

* comment: 디버깅용 로그 추가
  • Loading branch information
kimyu0218 authored Dec 22, 2024
1 parent 80d137c commit 5484604
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public void subscribeToMission(final Long memberId, final Mission mission) {
* <b>미션 취소/종료 시 미션 구독 취소</b><br>
* 해당 미션을 구독한 디바이스를 대상으로 구독 취소
*
* @param missionId 취소/종료된 미션
* @param missionId 취소/종료된 미션 아이디
*/
@Transactional
public void unsubscribeFromMission(final Long missionId) {
Expand All @@ -79,6 +79,27 @@ public void unsubscribeFromMission(final Long missionId) {
topicSubscriber.unsubscribeFromTopic(deviceTokens, TopicGenerator.getTopic(missionId));
}

/**
* <b>미션 삭제 시 호스트의 미션 구독 취소</b><br>
* 삭제 푸시 알림을 보내기 전, 호스트는 구독을 취소하여 푸시 알림이 가지 않도록 처리
*
* @param memberId 호스트 멤버 아이디
* @param missionId 호스트가 삭제한 미션 아이디
*/
@Transactional
public void unsubscribeFromDeletedMissionForHost(final Long memberId, final Long missionId) {
String topic = TopicGenerator.getTopic(missionId);
Devices devices = new Devices(
deviceRepository.findAllByMemberId(memberId)
);

deviceSubscriptionRepository.findAllWithDeviceByMissionIdAndDeviceIds(missionId, devices.getActivatedDeviceIds())
.forEach(it -> {
topicSubscriber.unsubscribeFromTopic(List.of(it.getDevice().getDeviceToken()), topic);
deviceSubscriptionRepository.deleteById(it.getId());
});
}

private List<String> findTopicSubscribers(final Long missionId) {
List<DeviceSubscription> subscriptions = deviceSubscriptionRepository.findAllWithDeviceAndMissionByMissionId(missionId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.nexters.goalpanzi.exception.NotFoundException;
import com.nexters.goalpanzi.infrastructure.firebase.PushMessageSender;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -32,6 +33,7 @@
import static com.nexters.goalpanzi.domain.firebase.PushMessage.MISSION_CANCELLATION_WARNING;
import static com.nexters.goalpanzi.domain.firebase.PushMessage.MISSION_READY;

@Slf4j
@Transactional(readOnly = true)
@RequiredArgsConstructor
@Service
Expand Down Expand Up @@ -82,7 +84,12 @@ private void validateAlreadyJoin(final Member member, final Mission mission) {
});
}

// FIXME: 호스트에게 알림 가는 데 확인 필요
private void sendJoinPushMessage(final Member member, final Mission mission) {
// TODO: 오류 확인 후 삭제
log.info("Host member: " + mission.getHostMemberId());
log.info("Join member: " + member.getId());

if (mission.isHostMember(member.getId())) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ public void deleteMission(final Long memberId, final Long missionId) {
Mission mission = missionRepository.getMission(missionId);
validateAuthority(memberId, mission);
mission.delete();
eventPublisher.publishEvent(new DeleteMissionEvent(mission.getId()));
eventPublisher.publishEvent(
new DeleteMissionEvent(memberId, mission.getId())
);
}

private void validateAuthority(final Long memberId, final Mission mission) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.nexters.goalpanzi.application.mission.event;

public record DeleteMissionEvent(
Long memberId,
Long missionId
) {
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.nexters.goalpanzi.application.mission.event.handler;

import com.nexters.goalpanzi.application.device.DeviceSubscriptionService;
import com.nexters.goalpanzi.application.firebase.TopicGenerator;
import com.nexters.goalpanzi.application.member.event.DeleteMemberEvent;
import com.nexters.goalpanzi.application.mission.MissionMemberService;
Expand Down Expand Up @@ -30,6 +31,7 @@ public class MissionMemberEventHandler {
private final MissionMemberService missionMemberService;
private final MissionVerificationService missionVerificationService;
private final MissionRetryPushMessageService missionRetryPushMessageService;
private final DeviceSubscriptionService deviceSubscriptionService;

private final PushMessageSender pushMessageSender;

Expand Down Expand Up @@ -59,6 +61,7 @@ void handleDeleteMissionEvent(final DeleteMissionEvent event) {
Map<String, String> data = new HashMap<>();
data.put("missionId", event.missionId().toString());

deviceSubscriptionService.unsubscribeFromDeletedMissionForHost(event.memberId(), event.missionId());
pushMessageSender.sendGroupNotificationWithData(
MISSION_DELETED.getTitle(),
MISSION_DELETED.getBody(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ public List<Device> getActivatedDevices() {
.toList();
}

public List<Long> getActivatedDeviceIds() {
return getActivatedDevices().stream()
.map(Device::getId)
.toList();
}

public List<String> getActivatedDeviceTokens() {
return getActivatedDevices().stream()
.map(Device::getDeviceToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,21 @@
public interface DeviceSubscriptionRepository extends JpaRepository<DeviceSubscription, Long> {

@Query("SELECT ds FROM DeviceSubscription ds"
+ " JOIN FETCH ds.mission JOIN FETCH ds.device"
+ " WHERE ds.device.id = :deviceId"
+ " JOIN FETCH ds.mission JOIN FETCH ds.device dd"
+ " WHERE dd.id = :deviceId"
)
List<DeviceSubscription> findAllWithMissionAndDeviceByDeviceId(final Long deviceId);

@Query("SELECT ds FROM DeviceSubscription ds"
+ " JOIN FETCH ds.device JOIN FETCH ds.mission"
+ " WHERE ds.mission.id = :missionId"
+ " JOIN FETCH ds.device JOIN FETCH ds.mission dm"
+ " WHERE dm.id = :missionId"
)
List<DeviceSubscription> findAllWithDeviceAndMissionByMissionId(final Long missionId);

@Query("SELECT ds FROM DeviceSubscription ds"
+ " JOIN FETCH ds.device dd"
+ " WHERE ds.mission.id = :missionId AND dd.id IN :deviceIds")
List<DeviceSubscription> findAllWithDeviceByMissionIdAndDeviceIds(final Long missionId, final List<Long> deviceIds);

void deleteAllByMissionId(final Long missionId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,26 @@ void setUp() {
.unsubscribeFromTopic(List.of(DEVICE_TOKEN), TopicGenerator.getTopic(MISSION_ID));
}

@Test
void 미션_호스트는_삭제한_미션에_대해_구독을_해지한다() {
Device mockDevice = mock(Device.class);
when(mockDevice.getId()).thenReturn(DEVICE_ID);
when(mockDevice.getDeviceToken()).thenReturn(DEVICE_TOKEN);
when(mockDevice.getPushActivationStatus()).thenReturn(true);

DeviceSubscription mockDeviceSubscription = mock(DeviceSubscription.class);
when(mockDeviceSubscription.getDevice()).thenReturn(mockDevice);

when(deviceRepository.findAllByMemberId(MEMBER_ID)).thenReturn(List.of(mockDevice));
when(deviceSubscriptionRepository.findAllWithDeviceByMissionIdAndDeviceIds(MISSION_ID, List.of(mockDevice.getId())))
.thenReturn(List.of(mockDeviceSubscription));

deviceSubscriptionService.unsubscribeFromDeletedMissionForHost(MEMBER_ID, MISSION_ID);

verify(topicSubscriber)
.unsubscribeFromTopic(List.of(DEVICE_TOKEN), TopicGenerator.getTopic(MISSION_ID));
}

@Test
void 구독했거나_구독_가능한_미션을_찾아_구독을_시작한다() {
Long SUBSCRIBED_MISSION_ID = 1L;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,23 @@ void setUp() {
List<DeviceSubscription> subscriptions = deviceSubscriptionRepository.findAllWithDeviceAndMissionByMissionId(mission.getId());
assertThat(subscriptions.size()).isEqualTo(0);
}

@Test
void 특정_디바이스들이_구독한_특정_미션_구독_현황을_디바이스와_함께_조회한다() {
Device device1 = deviceRepository.save(new Device(member, "deviceIdentifier1", "deviceToken2", OsType.AOS));
Device device2 = deviceRepository.save(new Device(member, "deviceIdentifier2", "deviceToken2", OsType.AOS));

deviceSubscriptionRepository.save(new DeviceSubscription(device1, mission));
deviceSubscriptionRepository.save(new DeviceSubscription(device2, mission));

List<DeviceSubscription> subscriptions = deviceSubscriptionRepository.findAllWithDeviceByMissionIdAndDeviceIds(
mission.getId(),
List.of(device1.getId(), device2.getId())
);
assertAll(
() -> assertThat(subscriptions.size()).isEqualTo(2),
() -> assertThat(subscriptions.stream().map(DeviceSubscription::getDevice))
.containsExactlyInAnyOrder(device1, device2)
);
}
}

0 comments on commit 5484604

Please sign in to comment.