Skip to content

Commit

Permalink
[REFACT] 유저의 이력 처리, 업적의 달성 체크, 업적 달성 알림에 대한 이벤트 구독 처리 핸들러 리팩토링
Browse files Browse the repository at this point in the history
  • Loading branch information
JIN-076 committed Mar 23, 2024
1 parent b197849 commit 40cfdfe
Show file tree
Hide file tree
Showing 3 changed files with 319 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package io.oeid.mogakgo.common.event.handler;

import static io.oeid.mogakgo.exception.code.ErrorCode400.NON_ACHIEVED_USER_ACHIEVEMENT;

import io.oeid.mogakgo.common.event.AchievementCompletionEvent;
import io.oeid.mogakgo.domain.achievement.application.AchievementFacadeService;
import io.oeid.mogakgo.domain.achievement.application.AchievementProgressService;
import io.oeid.mogakgo.domain.achievement.domain.entity.Achievement;
import io.oeid.mogakgo.domain.achievement.domain.entity.AchievementMessage;
import io.oeid.mogakgo.domain.achievement.domain.entity.UserAchievement;
import io.oeid.mogakgo.domain.achievement.domain.entity.UserActivity;
import io.oeid.mogakgo.domain.achievement.domain.entity.enums.RequirementType;
import io.oeid.mogakgo.domain.achievement.exception.UserAchievementException;
import io.oeid.mogakgo.domain.achievement.infrastructure.UserAchievementJpaRepository;
import io.oeid.mogakgo.domain.achievement.infrastructure.UserActivityJpaRepository;
import io.oeid.mogakgo.domain.user.application.UserCommonService;
import io.oeid.mogakgo.domain.user.domain.User;
import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionalEventListener;

@Slf4j
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class AchievementCheckListener {

private static final String SUBSCRIBE_DESTINATIONN = "/topic/achievement/";

private final UserCommonService userCommonService;
private final AchievementFacadeService achievementFacadeService;
private final UserAchievementJpaRepository userAchievementRepository;
private final UserActivityJpaRepository userActivityRepository;
private final AchievementProgressService achievementProgressService;
private final SimpMessagingTemplate messagingTemplate;

@TransactionalEventListener
public void executeEvent(final AchievementCompletionEvent event) {

Long achievementId = achievementFacadeService
.getAvailableAchievementId(event.getUserId(), event.getActivityType());

if (achievementId != null) {

User user = userCommonService.getUserById(event.getUserId());
UserAchievement userAchievement = getByUserAndAchievement(event.getUserId(), achievementId);

Boolean isExist = achievementFacadeService
.validateAchivementAlreadyInProgress(event.getUserId(), achievementId);

// -- 업적에 대한 진행도가 존재하지 않을 경우
if (isExist.equals(Boolean.FALSE)) {
userAchievementRepository.save(
UserAchievement.builder()
.user(user)
.achievement(userAchievement.getAchievement())
.completed(Boolean.FALSE)
.build()
);
}

Object progressCount = event.getTarget() == null
? getProgressCountForAchievement(event.getUserId(), userAchievement.getAchievement()) + 1
: event.getTarget();

// -- 업적이 달성 가능한 조건을 만족했을 경우
if (validateAvailabilityToAchieve(progressCount, userAchievement.getAchievement())) {
userAchievement.updateCompleted();

// -- 해당 업적이 연속적으로 달성 가능한 업적인 경우
if (userAchievement.getAchievement().getRequirementType()
.equals(RequirementType.SEQUENCE)) {
List<UserActivity> history = userActivityRepository.getActivityHistoryByActivityType(
event.getUserId(), userAchievement.getAchievement().getActivityType(),
userAchievement.getAchievement().getRequirementValue());
history.forEach(UserActivity::delete);
}

// -- 업적 달성에 대한 STOMP 통신
messagingTemplate.convertAndSend(SUBSCRIBE_DESTINATIONN + event.getUserId(),
AchievementMessage.builder()
.userId(event.getUserId())
.achievementId(achievementId)
.progressCount(userAchievement.getAchievement().getRequirementValue())
.requirementValue(userAchievement.getAchievement().getRequirementValue())
.completed(Boolean.TRUE)
.build()
);
}
}
}

private UserAchievement getByUserAndAchievement(Long userId, Long achievementId) {
return userAchievementRepository.findByUserAndAchievementId(userId, achievementId)
.orElseThrow(() -> new UserAchievementException(NON_ACHIEVED_USER_ACHIEVEMENT));
}

private Integer getProgressCountForAchievement(Long userId, Achievement achievement) {
if (achievement.getRequirementType().equals(RequirementType.ACCUMULATE)) {
return achievementProgressService.getAccumulatedProgressCount(userId, achievement.getActivityType());
}
return achievementProgressService.getProgressCountMap(userId,
List.of(achievement.getActivityType())).get(achievement.getActivityType());
}

private boolean validateAvailabilityToAchieve(Object target, Achievement achievement) {
if (target instanceof Integer) {
return Objects.equals(achievement.getRequirementValue(), target);
} else if (target instanceof Double) {
return achievement.getRequirementValue() <= (Double) target;
} else {
throw new IllegalArgumentException("Unsupported target type");
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package io.oeid.mogakgo.common.event.handler;

import static io.oeid.mogakgo.exception.code.ErrorCode400.NON_ACHIEVED_USER_ACHIEVEMENT;

import io.oeid.mogakgo.common.event.AchievementNotificationEvent;
import io.oeid.mogakgo.domain.achievement.application.AchievementFacadeService;
import io.oeid.mogakgo.domain.achievement.application.AchievementProgressService;
import io.oeid.mogakgo.domain.achievement.domain.entity.Achievement;
import io.oeid.mogakgo.domain.achievement.domain.entity.AchievementMessage;
import io.oeid.mogakgo.domain.achievement.domain.entity.UserAchievement;
import io.oeid.mogakgo.domain.achievement.domain.entity.enums.RequirementType;
import io.oeid.mogakgo.domain.achievement.exception.UserAchievementException;
import io.oeid.mogakgo.domain.achievement.infrastructure.AchievementJpaRepository;
import io.oeid.mogakgo.domain.achievement.infrastructure.UserAchievementJpaRepository;
import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionalEventListener;

@Slf4j
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class NotificationListener {

private static final String SUBSCRIBE_DESTINATIONN = "/topic/achievement/";

private final SimpMessagingTemplate messagingTemplate;
private final AchievementJpaRepository achievementRepository;
private final UserAchievementJpaRepository userAchievementRepository;
private final AchievementProgressService achievementProgressService;
private final AchievementFacadeService achievementFacadeService;

@TransactionalEventListener
public void executeEvent(final AchievementNotificationEvent event) {

// 사용자가 현재 달성할 수 있는 업적 ID
Long achievementId = achievementFacadeService
.getAvailableAchievementId(event.getUserId(), event.getActivityType());

if (achievementId != null) {

UserAchievement userAchievement = getByUserAndAchievement(event.getUserId(), achievementId);
Object progressCount = event.getTarget() == null
? getProgressCountForAchievement(event.getUserId(), userAchievement.getAchievement()) + 1
: event.getTarget();

// -- 업적이 달성 가능한 조건을 만족했을 경우
if (validateAvailabilityToAchieve(progressCount, userAchievement.getAchievement())) {
userAchievement.updateCompleted();

messagingTemplate.convertAndSend(SUBSCRIBE_DESTINATIONN + event.getUserId(),
AchievementMessage.builder()
.userId(event.getUserId())
.achievementId(achievementId)
.progressCount((Integer) progressCount)
.requirementValue(userAchievement.getAchievement().getRequirementValue())
.build()
);
}
}
}

private UserAchievement getByUserAndAchievement(Long userId, Long achievementId) {
return userAchievementRepository.findByUserAndAchievementId(userId, achievementId)
.orElseThrow(() -> new UserAchievementException(NON_ACHIEVED_USER_ACHIEVEMENT));
}

private Integer getProgressCountForAchievement(Long userId, Achievement achievement) {
if (achievement.getRequirementType().equals(RequirementType.ACCUMULATE)) {
return achievementProgressService.getAccumulatedProgressCount(userId, achievement.getActivityType());
}
return achievementProgressService.getProgressCountMap(userId,
List.of(achievement.getActivityType())).get(achievement.getActivityType());
}

private boolean validateAvailabilityToAchieve(Object target, Achievement achievement) {
if (target instanceof Integer) {
return Objects.equals(achievement.getRequirementValue(), (Integer) target + 1);
} else if (target instanceof Double) {
return achievement.getRequirementValue() <= (Double) target;
} else {
throw new IllegalArgumentException("Unsupported target type");
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package io.oeid.mogakgo.common.event.handler;

import io.oeid.mogakgo.common.event.UserActivityEvents;
import io.oeid.mogakgo.domain.achievement.application.AchievementFacadeService;
import io.oeid.mogakgo.domain.achievement.application.AchievementProgressService;
import io.oeid.mogakgo.domain.achievement.domain.entity.Achievement;
import io.oeid.mogakgo.domain.achievement.domain.entity.AchievementMessage;
import io.oeid.mogakgo.domain.achievement.domain.entity.UserActivity;
import io.oeid.mogakgo.domain.achievement.domain.entity.enums.ActivityType;
import io.oeid.mogakgo.domain.achievement.domain.entity.enums.RequirementType;
import io.oeid.mogakgo.domain.achievement.infrastructure.AchievementJpaRepository;
import io.oeid.mogakgo.domain.achievement.infrastructure.UserActivityJpaRepository;
import io.oeid.mogakgo.domain.user.application.UserCommonService;
import io.oeid.mogakgo.domain.user.domain.User;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionalEventListener;

@Slf4j
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class UserActivityListener {

private static final Integer MIN_PROGRESS_SIZE = 1;
private static final String SUBSCRIBE_DESTINATIONN = "/topic/achievement/";

private final UserCommonService userCommonService;
private final AchievementJpaRepository achievementRepository;
private final UserActivityJpaRepository userActivityRepository;
private final AchievementFacadeService achievementFacadeService;
private final AchievementProgressService achievementProgressService;
private final SimpMessagingTemplate messagingTemplate;

@TransactionalEventListener
public void executeEvent(final UserActivityEvents event) {

// 사용자가 현재 달성할 수 있는 업적 ID
Long achievementId = achievementFacadeService
.getAvailableAchievementId(event.getUserId(), event.getActivityType());

if (achievementId != null) {

// -- 업적이 연속적으로 달성 가능한 업적인 경우
Achievement achievement = achievementFacadeService.getById(achievementId);
if (achievement.getRequirementType().equals(RequirementType.SEQUENCE)) {

// -- 당일에 연속적으로 달성 가능한 업적에 대한 이벤트를 발행한 적이 없는 경우
if (achievementFacadeService.validateActivityDuplicate(event.getUserId(), event.getActivityType())) {
saveActivity(event);
}
} else {
saveActivity(event);
}

// 업적이 진행중이거나 한 번에 달성 가능한 업적이 아닌 경우
Integer progressCount = getProgressCountForAchievement(event.getUserId(), achievement);
if (!isAvailableToAchieve(progressCount, achievement) || !isAvailableToAchieveOnce(event.getActivityType())) {

// 업적 진행에 대한 STOMP 통신
messagingTemplate.convertAndSend(SUBSCRIBE_DESTINATIONN + event.getUserId(),
AchievementMessage.builder()
.userId(event.getUserId())
.achievementId(achievementId)
.progressCount(progressCount)
.requirementValue(achievement.getRequirementValue())
.build()
);
}
}
}

private void saveActivity(final UserActivityEvents event) {
User user = userCommonService.getUserById(event.getUserId());
userActivityRepository.save(
UserActivity.builder()
.user(user)
.activityType(event.getActivityType())
.build()
);
}

private Integer getProgressCountForAchievement(Long userId, Achievement achievement) {
if (achievement.getRequirementType().equals(RequirementType.ACCUMULATE)) {
return achievementProgressService.getAccumulatedProgressCount(userId, achievement.getActivityType());
}
return achievementProgressService.getProgressCountMap(userId,
List.of(achievement.getActivityType())).get(achievement.getActivityType());
}

private boolean isAvailableToAchieve(Integer progressCount, Achievement achievement) {
return achievement.getRequirementValue().equals(progressCount);
}

private boolean isAvailableToAchieveOnce(ActivityType activityType) {
return getProgressLevelSize(activityType).equals(MIN_PROGRESS_SIZE);
}

private Integer getProgressLevelSize(ActivityType activityType) {
return achievementRepository.findByActivityType(activityType).size();
}

}

0 comments on commit 40cfdfe

Please sign in to comment.