Skip to content

Commit

Permalink
업적 기록 및 조회 완료 (#16)
Browse files Browse the repository at this point in the history
* refactor(BaseEntity): deleted를 BaseEntity에서 제외

* feat(Achievement): 업적 CRUD API 추가

* feat(Achievement): 유저 업적 등록, 조회 API 추가

* feat(Achievement): 도메인 이벤트 추가
  • Loading branch information
limehee authored Jul 21, 2024
1 parent 2309a17 commit 72be25b
Show file tree
Hide file tree
Showing 36 changed files with 820 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.stempo.api.domain.application.event;

import lombok.Getter;
import org.springframework.context.ApplicationEvent;

@Getter
public class AchievementDeletedEvent extends ApplicationEvent {

private final Long achievementId;

public AchievementDeletedEvent(Object source, Long achievementId) {
super(source);
this.achievementId = achievementId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.stempo.api.domain.application.event;

import lombok.RequiredArgsConstructor;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
@RequiredArgsConstructor
public class AchievementEventDispatcher {

private final List<AchievementEventProcessor> processors;

@EventListener
public void handleAchievementDeletedEvent(AchievementDeletedEvent event) {
processors.forEach(processor -> processor.processAchievementDeleted(event.getAchievementId()));
}

@EventListener
public void handleAchievementUpdatedEvent(AchievementUpdatedEvent event) {
processors.forEach(processor -> processor.processAchievementUpdated(event.getAchievementId()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.stempo.api.domain.application.event;

public interface AchievementEventProcessor {

void processAchievementDeleted(Long achievementId);

void processAchievementUpdated(Long achievementId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.stempo.api.domain.application.event;

import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Component
public class AchievementEventProcessorRegistry {

private final List<AchievementEventProcessor> processors = new ArrayList<>();

public void register(AchievementEventProcessor processor) {
processors.add(processor);
}

public List<AchievementEventProcessor> getProcessors() {
return Collections.unmodifiableList(processors);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.stempo.api.domain.application.event;

import lombok.Getter;
import org.springframework.context.ApplicationEvent;

@Getter
public class AchievementUpdatedEvent extends ApplicationEvent {

private final Long achievementId;

public AchievementUpdatedEvent(Object source, Long achievementId) {
super(source);
this.achievementId = achievementId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.stempo.api.domain.application.event;

import com.stempo.api.domain.domain.model.UserAchievement;
import com.stempo.api.domain.domain.repository.UserAchievementRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Component
@RequiredArgsConstructor
public class UserAchievementEventProcessor implements AchievementEventProcessor {

private final UserAchievementRepository userAchievementRepository;

@Override
@Transactional
public void processAchievementDeleted(Long achievementId) {
List<UserAchievement> achievements = userAchievementRepository.findByAchievementId(achievementId);
achievements.forEach(UserAchievement::delete);
userAchievementRepository.saveAll(achievements);
}

@Override
public void processAchievementUpdated(Long achievementId) {
// do nothing
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.stempo.api.domain.application.service;

import com.stempo.api.domain.presentation.dto.request.AchievementRequestDto;
import com.stempo.api.domain.presentation.dto.request.AchievementUpdateRequestDto;
import com.stempo.api.domain.presentation.dto.response.AchievementResponseDto;

import java.util.List;

public interface AchievementService {

Long registerAchievement(AchievementRequestDto requestDto);

List<AchievementResponseDto> getAchievements();

Long updateAchievement(Long achievementId, AchievementUpdateRequestDto requestDto);

Long deleteAchievement(Long achievementId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.stempo.api.domain.application.service;

import com.stempo.api.domain.application.event.AchievementDeletedEvent;
import com.stempo.api.domain.application.event.AchievementUpdatedEvent;
import com.stempo.api.domain.domain.model.Achievement;
import com.stempo.api.domain.domain.repository.AchievementRepository;
import com.stempo.api.domain.presentation.dto.request.AchievementRequestDto;
import com.stempo.api.domain.presentation.dto.request.AchievementUpdateRequestDto;
import com.stempo.api.domain.presentation.dto.response.AchievementResponseDto;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@RequiredArgsConstructor
public class AchievementServiceImpl implements AchievementService {

private final AchievementRepository repository;
private final ApplicationEventPublisher eventPublisher;

@Override
public Long registerAchievement(AchievementRequestDto requestDto) {
Achievement achievement = AchievementRequestDto.toDomain(requestDto);
return repository.save(achievement).getId();
}

@Override
public List<AchievementResponseDto> getAchievements() {
List<Achievement> achievements = repository.findAll();
return achievements.stream()
.map(AchievementResponseDto::toDto)
.toList();
}

@Override
public Long updateAchievement(Long achievementId, AchievementUpdateRequestDto requestDto) {
Achievement achievement = repository.findByIdOrThrow(achievementId);
achievement.update(requestDto);
repository.save(achievement);
eventPublisher.publishEvent(new AchievementUpdatedEvent(this, achievement.getId()));
return achievement.getId();
}

@Override
public Long deleteAchievement(Long achievementId) {
Achievement achievement = repository.findByIdOrThrow(achievementId);
achievement.delete();
repository.save(achievement);
eventPublisher.publishEvent(new AchievementDeletedEvent(this, achievement.getId()));
return achievement.getId();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.stempo.api.domain.application.service;

import com.stempo.api.domain.presentation.dto.response.UserAchievementResponseDto;

import java.util.List;

public interface UserAchievementService {

Long registerUserAchievement(Long achievementId);

List<UserAchievementResponseDto> getUserAchievements();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.stempo.api.domain.application.service;

import com.stempo.api.domain.domain.model.Achievement;
import com.stempo.api.domain.domain.model.UserAchievement;
import com.stempo.api.domain.domain.repository.AchievementRepository;
import com.stempo.api.domain.domain.repository.UserAchievementRepository;
import com.stempo.api.domain.persistence.entity.UserAchievementEntity;
import com.stempo.api.domain.presentation.dto.response.UserAchievementResponseDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class UserAchievementServiceImpl implements UserAchievementService {

private final UserService userService;
private final UserAchievementRepository userAchievementRepository;
private final AchievementRepository achievementRepository;

@Override
public Long registerUserAchievement(Long achievementId) {
String deviceTag = userService.getCurrentDeviceTag();
Optional<UserAchievementEntity> existingUserAchievement = userAchievementRepository.findByDeviceTagAndAchievementId(deviceTag, achievementId);

if (existingUserAchievement.isPresent()) {
return existingUserAchievement.get().getId();
}

UserAchievement userAchievement = UserAchievement.create(achievementId, deviceTag);
return userAchievementRepository.save(userAchievement).getId();
}

@Override
public List<UserAchievementResponseDto> getUserAchievements() {
String deviceTag = userService.getCurrentDeviceTag();
List<UserAchievementEntity> userAchievements = userAchievementRepository.findByDeviceTag(deviceTag);
return userAchievements.stream()
.map(this::getUserAchievementResponseDto)
.toList();
}

private UserAchievementResponseDto getUserAchievementResponseDto(UserAchievementEntity ua) {
Achievement achievement = achievementRepository.findByIdOrThrow(ua.getAchievementId());
return UserAchievementResponseDto.toDto(achievement);
}
}
35 changes: 35 additions & 0 deletions src/main/java/com/stempo/api/domain/domain/model/Achievement.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.stempo.api.domain.domain.model;

import com.stempo.api.domain.presentation.dto.request.AchievementUpdateRequestDto;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.Optional;

@Getter
@Setter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Achievement {

private Long id;
private String name;
private String description;
private String imageUrl;
private boolean deleted;

public void update(AchievementUpdateRequestDto requestDto) {
Optional.ofNullable(requestDto.getName()).ifPresent(this::setName);
Optional.ofNullable(requestDto.getDescription()).ifPresent(this::setDescription);
Optional.ofNullable(requestDto.getImageUrl()).ifPresent(this::setImageUrl);
}

public void delete() {
setDeleted(true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.stempo.api.domain.domain.model;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.time.LocalDateTime;

@Getter
@Setter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class UserAchievement {

private Long id;
private String deviceTag;
private Long achievementId;
private LocalDateTime createdAt;
private boolean deleted;

public static UserAchievement create(Long achievementId, String deviceTag) {
return UserAchievement.builder()
.achievementId(achievementId)
.deviceTag(deviceTag)
.build();
}

public void delete() {
setDeleted(true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.stempo.api.domain.domain.repository;

import com.stempo.api.domain.domain.model.Achievement;

import java.util.List;

public interface AchievementRepository {

Achievement findByIdOrThrow(Long achievementId);

List<Achievement> findAll();

Achievement save(Achievement achievement);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.stempo.api.domain.domain.repository;

import com.stempo.api.domain.domain.model.UserAchievement;
import com.stempo.api.domain.persistence.entity.UserAchievementEntity;

import java.util.List;
import java.util.Optional;

public interface UserAchievementRepository {

UserAchievement save(UserAchievement userAchievement);

void saveAll(List<UserAchievement> achievements);

List<UserAchievementEntity> findByDeviceTag(String deviceTag);

Optional<UserAchievementEntity> findByDeviceTagAndAchievementId(String deviceTag, Long achievementId);

List<UserAchievement> findByAchievementId(Long achievementId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,7 @@ public class AchievementEntity extends BaseEntity {

@Column(nullable = false)
private String imageUrl;

@Column(nullable = false)
private boolean deleted = false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,7 @@ public class ArticleEntity extends BaseEntity {
@Column(nullable = false)
@URL(message = "Invalid URL")
private String articleUrl;

@Column(nullable = false)
private boolean deleted = false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ public class BaseEntity {
@LastModifiedDate
private LocalDateTime updatedAt;

@Column(nullable = false)
private boolean deleted = false;

@PrePersist
protected void onCreate() {
createdAt = LocalDateTime.now();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,7 @@ public class BoardEntity extends BaseEntity {
@Column(nullable = false)
@Size(min = 1, max = 10000, message = "Content must be between 1 and 10000 characters")
private String content;

@Column(nullable = false)
private boolean deleted = false;
}
Loading

0 comments on commit 72be25b

Please sign in to comment.