Skip to content

Commit

Permalink
Fix: 나의 뱃지 목록 조회 응답 변경 사항에 맞게 수정 (#292)
Browse files Browse the repository at this point in the history
* Refactor: AchievedBadgesResponse의 AchievedBadge를 공통으로 사용하기 위해 분리

* Feat: 사용자가 획득한 뱃지를 뱃지 타입순, 최신 획득 순으로 가져오는 repository 추가

* Fix: 뱃지 목록 조회의 새로운 응답 형식 변경에 따른 수정

- AllBadgesListResponse변경
- 기존의 뱃지 목록을 조회하는 repository 삭제
- 필요 없어진 BadgeWithAchieveStatusAndAchievedAt DTO 삭제
- 기존 테스트 코드 삭제
- BagdeType에 타입한글 이름 추가

* Fix: 최신 획득 뱃지는 badgeList에 추가하지 않고 따로 리턴하게 수정

* Test: findByMemberIdOrderByBadgeTypeAndAchievedAt 리파지토리 테스트

* Test: 나의 벳지 목록 조회 서비스 테스트 코드 추가

* Fix: 보여주는 우선순위 값 추가

* Fix: 벳지의 타입 별 보여주는 우선 순위로 배지 리스트 반환하게 수정

* Test: findBadgesList의 잘못된 테스트 코드 수정

* Docs: 나의 뱃지 목록 조회 API 문서 수정

* Fix: Collection 자료형 타입을 interface 타입으로 사용

* Fix: responseBadges 리스트에 forEach로 추가하는 대신, 스트림 map을 이용해 리스트로 반환
  • Loading branch information
hee9841 authored Oct 31, 2024
1 parent 57144c6 commit 5626eb0
Show file tree
Hide file tree
Showing 16 changed files with 142 additions and 291 deletions.
57 changes: 37 additions & 20 deletions src/main/java/com/dnd/runus/application/badge/BadgeService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,24 @@
import com.dnd.runus.domain.badge.*;
import com.dnd.runus.domain.member.Member;
import com.dnd.runus.global.constant.BadgeType;
import com.dnd.runus.presentation.v1.badge.dto.response.AchievedBadge;
import com.dnd.runus.presentation.v1.badge.dto.response.AchievedBadgesResponse;
import com.dnd.runus.presentation.v1.badge.dto.response.AllBadgesListResponse;
import com.dnd.runus.presentation.v1.badge.dto.response.AllBadgesListResponse.BadgeWithAchievedStatus;
import com.dnd.runus.presentation.v1.badge.dto.response.AllBadgesListResponse.BadgesWithType;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

import static com.dnd.runus.global.constant.TimeConstant.SERVER_TIMEZONE_ID;
Expand All @@ -41,7 +47,7 @@ public void achieveBadge(Member member, BadgeType badgeType, int value) {
public AchievedBadgesResponse getAchievedBadges(long memberId) {
return new AchievedBadgesResponse(
badgeAchievementRepository.findByMemberIdWithBadgeOrderByAchievedAtLimit(memberId, 3).stream()
.map(badgeAchievement -> new AchievedBadgesResponse.AchievedBadge(
.map(badgeAchievement -> new AchievedBadge(
badgeAchievement.badge().badgeId(),
badgeAchievement.badge().name(),
badgeAchievement.badge().imageUrl(),
Expand All @@ -52,36 +58,47 @@ public AchievedBadgesResponse getAchievedBadges(long memberId) {
@Transactional(readOnly = true)
public AllBadgesListResponse getListOfAllBadges(long memberId) {

List<BadgeWithAchieveStatusAndAchievedAt> allBadges =
badgeRepository.findAllBadgesWithAchieveStatusByMemberId(memberId);
List<BadgeAchievement.OnlyBadge> allBadges =
badgeAchievementRepository.findByMemberIdOrderByBadgeTypeAndAchievedAt(memberId);

// 최신 획득한 뱃지(오늘 기준으로 일주일)
LocalDateTime oneWeekAgo = LocalDate.now(SERVER_TIMEZONE_ID)
.atStartOfDay(SERVER_TIMEZONE_ID)
.toOffsetDateTime()
.minusDays(7)
.toLocalDateTime();

EnumMap<BadgeType, List<BadgeWithAchievedStatus>> badgeMap = allBadges.stream()
List<AchievedBadge> recencyBadges = allBadges.stream()
.filter(v -> oneWeekAgo.isBefore(v.createdAt().toLocalDateTime()))
.map(v -> new AchievedBadge(
v.badge().badgeId(),
v.badge().name(),
v.badge().imageUrl(),
v.createdAt().toLocalDateTime()))
.toList();

// badgeType별 획득한 배지
Map<BadgeType, List<AchievedBadge>> badgesWithType = allBadges.stream()
.collect(Collectors.groupingBy(
v -> v.badge().type(),
() -> new EnumMap<>(BadgeType.class),
Collectors.mapping(BadgeWithAchievedStatus::from, Collectors.toList())));
Collectors.mapping(
v -> new AchievedBadge(
v.badge().badgeId(),
v.badge().name(),
v.badge().imageUrl(),
v.createdAt().toLocalDateTime()),
Collectors.toList())));

List<BadgeWithAchievedStatus> recencyBadges = allBadges.stream()
.filter(badge -> isRecent(badge, oneWeekAgo))
.map(BadgeWithAchievedStatus::from)
.toList();
// 타입 별, showPriority순서로 리스트에 추가
Set<BadgeType> badgeTypesSet = new TreeSet<>(Comparator.comparingInt(BadgeType::getShowPriority));
badgeTypesSet.addAll(EnumSet.allOf(BadgeType.class));

return new AllBadgesListResponse(
recencyBadges,
badgeMap.getOrDefault(BadgeType.PERSONAL_RECORD, Collections.emptyList()),
badgeMap.getOrDefault(BadgeType.DISTANCE_METER, Collections.emptyList()),
badgeMap.getOrDefault(BadgeType.STREAK, Collections.emptyList()),
badgeMap.getOrDefault(BadgeType.DURATION_SECONDS, Collections.emptyList()),
badgeMap.getOrDefault(BadgeType.LEVEL, Collections.emptyList()));
}
List<BadgesWithType> responseBadges = badgeTypesSet.stream()
.map(badgeType -> new BadgesWithType(
badgeType.getName(), badgesWithType.getOrDefault(badgeType, Collections.emptyList())))
.toList();

private boolean isRecent(BadgeWithAchieveStatusAndAchievedAt badge, LocalDateTime criterionDate) {
return badge.isAchieved() && criterionDate.isBefore(badge.achievedAt());
return new AllBadgesListResponse(recencyBadges, responseBadges);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public interface BadgeAchievementRepository {

List<BadgeAchievement.OnlyBadge> findByMemberIdWithBadgeOrderByAchievedAtLimit(long memberId, int limit);

List<BadgeAchievement.OnlyBadge> findByMemberIdOrderByBadgeTypeAndAchievedAt(long memberId);

BadgeAchievement save(BadgeAchievement badgeAchievement);

void saveAllIgnoreDuplicated(List<BadgeAchievement> badgeAchievements);
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/com/dnd/runus/domain/badge/BadgeRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,4 @@

public interface BadgeRepository {
List<Badge> findByTypeAndRequiredValueLessThanEqual(BadgeType badgeType, int requiredValue);

List<BadgeWithAchieveStatusAndAchievedAt> findAllBadgesWithAchieveStatusByMemberId(long memberId);
}

This file was deleted.

20 changes: 15 additions & 5 deletions src/main/java/com/dnd/runus/global/constant/BadgeType.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
package com.dnd.runus.global.constant;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

import static lombok.AccessLevel.PRIVATE;

@Getter
@RequiredArgsConstructor(access = PRIVATE)
public enum BadgeType {
STREAK,
DISTANCE_METER,
PERSONAL_RECORD,
DURATION_SECONDS,
LEVEL,
PERSONAL_RECORD("개인기록", 1),
DISTANCE_METER("러닝거리", 2),
STREAK("연속", 3),
DURATION_SECONDS("시간", 4),
LEVEL("레벨", 5),
;
private final String name;
private final int showPriority;
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ public List<BadgeAchievement.OnlyBadge> findByMemberIdWithBadgeOrderByAchievedAt
return jooqBadgeAchievementRepository.findByMemberIdWithBadgeOrderByAchievedAtLimit(memberId, limit);
}

@Override
public List<BadgeAchievement.OnlyBadge> findByMemberIdOrderByBadgeTypeAndAchievedAt(long memberId) {
return jooqBadgeAchievementRepository.findByMemberIdOrderByBadgeTypeAndAchievedAt(memberId);
}

@Override
public BadgeAchievement save(BadgeAchievement badgeAchievement) {
return jpaBadgeAchievementRepository
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

import com.dnd.runus.domain.badge.Badge;
import com.dnd.runus.domain.badge.BadgeRepository;
import com.dnd.runus.domain.badge.BadgeWithAchieveStatusAndAchievedAt;
import com.dnd.runus.global.constant.BadgeType;
import com.dnd.runus.infrastructure.persistence.jooq.badge.JooqBadgeRepository;
import com.dnd.runus.infrastructure.persistence.jpa.badge.JpaBadgeRepository;
import com.dnd.runus.infrastructure.persistence.jpa.badge.entity.BadgeEntity;
import lombok.RequiredArgsConstructor;
Expand All @@ -16,17 +14,11 @@
@RequiredArgsConstructor
public class BadgeRepositoryImpl implements BadgeRepository {
private final JpaBadgeRepository jpaBadgeRepository;
private final JooqBadgeRepository jooqBadgeRepository;

@Override
public List<Badge> findByTypeAndRequiredValueLessThanEqual(BadgeType badgeType, int requiredValue) {
return jpaBadgeRepository.findByTypeAndRequiredValueLessThanEqual(badgeType, requiredValue).stream()
.map(BadgeEntity::toDomain)
.toList();
}

@Override
public List<BadgeWithAchieveStatusAndAchievedAt> findAllBadgesWithAchieveStatusByMemberId(long memberId) {
return jooqBadgeRepository.findAllBadgesWithAchieveStatusByMemberId(memberId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ public List<BadgeAchievement.OnlyBadge> findByMemberIdWithBadgeOrderByAchievedAt
badge.get(BADGE_ACHIEVEMENT.UPDATED_AT, OffsetDateTime.class)));
}

public List<BadgeAchievement.OnlyBadge> findByMemberIdOrderByBadgeTypeAndAchievedAt(long memberId) {
return dsl.select()
.from(BADGE_ACHIEVEMENT)
.join(BADGE)
.on(BADGE_ACHIEVEMENT.BADGE_ID.eq(BADGE.ID))
.where(BADGE_ACHIEVEMENT.MEMBER_ID.eq(memberId))
.orderBy(BADGE.TYPE, BADGE_ACHIEVEMENT.CREATED_AT.desc())
.fetch(badge -> new BadgeAchievement.OnlyBadge(
badge.get(BADGE_ACHIEVEMENT.ID),
new JooqBadgeMapper().map(badge),
badge.get(BADGE_ACHIEVEMENT.CREATED_AT, OffsetDateTime.class),
badge.get(BADGE_ACHIEVEMENT.UPDATED_AT, OffsetDateTime.class)));
}

public void saveAllIgnoreDuplicated(List<BadgeAchievement> badgeAchievements) {
OffsetDateTime now = OffsetDateTime.now();
dsl.batch(badgeAchievements.stream()
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ public AchievedBadgesResponse getMyBadges(@MemberId long memberId) {
description =
"""
서비스의 전체 뱃지를 조회 합니다.<br>
자신이 획득한 뱃지는 isAchieved가 true로 표시됩니다.<br>
뱃지의 카테고리별 리스트로 뱃지가 리턴됩니다.<br>
최근 일주일에 사용자가 획득한 뱃지는 recencyBadges 리스트에 리턴됩니다.<br>
뱃지의 카테고리 별 리스트로 뱃지가 리턴됩니다.<br>
자세한 응답 구조는 AllBadgesListResponse의 Schema을 확인해주세요.<br>
""")
public AllBadgesListResponse getAllBadgesList(@MemberId long memberId) {
return badgeService.getListOfAllBadges(memberId);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.dnd.runus.presentation.v1.badge.dto.response;

import java.time.LocalDateTime;

public record AchievedBadge(long badgeId, String name, String imageUrl, LocalDateTime achievedAt) {}
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
package com.dnd.runus.presentation.v1.badge.dto.response;

import java.time.LocalDateTime;
import java.util.List;

public record AchievedBadgesResponse(
List<AchievedBadge> badges
) {
public record AchievedBadge(
long badgeId,
String name,
String imageUrl,
LocalDateTime achievedAt
) {
}
}
Original file line number Diff line number Diff line change
@@ -1,41 +1,18 @@
package com.dnd.runus.presentation.v1.badge.dto.response;

import com.dnd.runus.domain.badge.Badge;
import com.dnd.runus.domain.badge.BadgeWithAchieveStatusAndAchievedAt;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDateTime;
import java.util.List;

public record AllBadgesListResponse(
@Schema(description = "신규 뱃지 목록")
List<BadgeWithAchievedStatus> recencyBadges,
@Schema(description = "개인기록 뱃지 목록")
List<BadgeWithAchievedStatus> personalBadges,
@Schema(description = "러닝 거리 뱃지 목록")
List<BadgeWithAchievedStatus> distanceBadges,
@Schema(description = "연속 뱃지 목록")
List<BadgeWithAchievedStatus> streakBadges,
@Schema(description = "사간 뱃지 목록")
List<BadgeWithAchievedStatus> durationBadges,
@Schema(description = "레벨 뱃지 목록")
List<BadgeWithAchievedStatus> levelBadges
@Schema(description = "최신 뱃지 목록(일주일 기준), 없으면 빈리스트 리턴")
List<AchievedBadge> recencyBadges,
List<BadgesWithType> badgesList
) {
public record BadgeWithAchievedStatus(
@Schema(description = "뱃지 id")
long badgeId,
@Schema(description = "뱃지 이름")
String name,
@Schema(description = "뱃지 이미지 url")
String imageUrl,
@Schema(description = "뱃지 달성 여부")
boolean isAchieved,
@Schema(description = "배지 달성 날짜")
LocalDateTime achievedAt
public record BadgesWithType(
@Schema(description = "뱃지 카테고리 이름")
String category,
@Schema(description = "뱃지 리스트")
List<AchievedBadge> badges
) {
public static BadgeWithAchievedStatus from(
BadgeWithAchieveStatusAndAchievedAt badgeWithAchievedStatus) {
Badge badge = badgeWithAchievedStatus.badge();
return new BadgeWithAchievedStatus(badge.badgeId(), badge.name(), badge.imageUrl(), badgeWithAchievedStatus.isAchieved(), badgeWithAchievedStatus.achievedAt());
}
}
}
Loading

0 comments on commit 5626eb0

Please sign in to comment.