Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT] 회원 도전 루틴 조회 #299

Merged
merged 7 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.soptie.server.memberroutine.controller.v2;

import static com.soptie.server.memberroutine.message.MemberRoutineSuccessMassage.*;

import java.security.Principal;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.soptie.server.common.dto.SuccessResponse;
import com.soptie.server.memberroutine.controller.v2.docs.MemberChallengeRoutineControllerV2Docs;
import com.soptie.server.memberroutine.controller.v2.dto.response.MemberChallengeRoutineAcquireResponseV2;
import com.soptie.server.memberroutine.service.MemberRoutineReadService;
import com.soptie.server.memberroutine.service.dto.request.MemberChallengeRoutineAcquireServiceRequest;

import lombok.RequiredArgsConstructor;
import lombok.val;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v2/routines/challenge/member")
public class MemberChallengeRoutineControllerV2 implements MemberChallengeRoutineControllerV2Docs {

private final MemberRoutineReadService memberRoutineReadService;

@GetMapping
public ResponseEntity<?> acquire(Principal principal) {
val memberId = Long.parseLong(principal.getName());
return memberRoutineReadService.acquire(MemberChallengeRoutineAcquireServiceRequest.of(memberId))
.map(response -> ResponseEntity.ok(SuccessResponse.success(
SUCCESS_GET_ROUTINE.getMessage(),
MemberChallengeRoutineAcquireResponseV2.of(response))))
.orElseGet(() -> ResponseEntity.noContent().build());
Chan531 marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.soptie.server.memberroutine.controller.v2.docs;

import java.security.Principal;

import org.springframework.http.ResponseEntity;

import com.soptie.server.common.dto.ErrorResponse;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;

@Tag(name = "member challenge routines V2", description = "회원의 도전 루틴 API Version2")
public interface MemberChallengeRoutineControllerV2Docs {

@Operation(
summary = "회원의 도전 루틴 목록 조회",
description = "회원의 도전 루틴을 테마와 함께 조회한다.",
responses = {
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(
responseCode = "401",
description = "유효하지 않은 토큰",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(
responseCode = "4xx",
description = "클라이언트(요청) 오류",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(
responseCode = "500",
description = "서버 내부 오류",
content = @Content(schema = @Schema(implementation = ErrorResponse.class)))}
)
ResponseEntity<?> acquire(
@Parameter(hidden = true) Principal principal
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.soptie.server.memberroutine.controller.v2.dto.response;

import static lombok.AccessLevel.*;

import com.soptie.server.memberroutine.service.dto.response.MemberChallengeRoutineAcquireServiceResponse;

import lombok.Builder;
import lombok.NonNull;

@Builder(access = PRIVATE)
public record MemberChallengeRoutineAcquireResponseV2(
long routineId,
long themeId,
@NonNull String themeName,
@NonNull String title,
@NonNull String content,
@NonNull String detailContent,
@NonNull String place,
@NonNull String timeTaken
) {

public static MemberChallengeRoutineAcquireResponseV2 of(MemberChallengeRoutineAcquireServiceResponse response) {
return MemberChallengeRoutineAcquireResponseV2.builder()
.routineId(response.routineId())
.themeId(response.theme().themeId())
.themeName(response.theme().name())
.title(response.routineContent())
.content(response.content())
.detailContent(response.description())
.place(response.place())
.timeTaken(response.requiredTime())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
import com.soptie.server.member.adapter.MemberFinder;
import com.soptie.server.memberroutine.adapter.MemberRoutineFinder;
import com.soptie.server.memberroutine.repository.dto.MemberRoutineResponse;
import com.soptie.server.memberroutine.service.dto.request.MemberChallengeRoutineAcquireServiceRequest;
import com.soptie.server.memberroutine.service.dto.request.MemberDailyRoutineListAcquireServiceRequest;
import com.soptie.server.memberroutine.service.dto.request.MemberHappinessRoutineGetServiceRequest;
import com.soptie.server.memberroutine.service.dto.response.MemberChallengeRoutineAcquireServiceResponse;
import com.soptie.server.memberroutine.service.dto.response.MemberDailyRoutineListAcquireServiceResponse;
import com.soptie.server.memberroutine.service.dto.response.MemberDailyRoutinesAcquireServiceResponse;
import com.soptie.server.memberroutine.service.dto.response.MemberHappinessRoutineGetServiceResponse;
Expand Down Expand Up @@ -50,6 +52,14 @@ public MemberDailyRoutineListAcquireServiceResponse acquireAll(
return MemberDailyRoutineListAcquireServiceResponse.of(routinesWithTheme);
}

public Optional<MemberChallengeRoutineAcquireServiceResponse> acquire(
MemberChallengeRoutineAcquireServiceRequest request
) {
val member = memberFinder.findById(request.memberId());
val memberRoutine = memberRoutineFinder.findChallengeByMember(member);
return memberRoutine.map(MemberChallengeRoutineAcquireServiceResponse::of);
}

private List<MemberDailyRoutinesAcquireServiceResponse> collectByTheme(List<MemberRoutineResponse> routines) {
val routinesByTheme = routines.stream().collect(Collectors.groupingBy(MemberRoutineResponse::themeId));
return routinesByTheme.values().stream()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.soptie.server.memberroutine.service.dto.request;

import static lombok.AccessLevel.*;

import lombok.Builder;

@Builder(access = PRIVATE)
public record MemberChallengeRoutineAcquireServiceRequest(
long memberId
) {
thguss marked this conversation as resolved.
Show resolved Hide resolved

public static MemberChallengeRoutineAcquireServiceRequest of(long memberId) {
return MemberChallengeRoutineAcquireServiceRequest.builder()
.memberId(memberId)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.soptie.server.memberroutine.service.dto.response;

import static lombok.AccessLevel.*;

import com.soptie.server.memberroutine.repository.dto.MemberChallengeResponse;
import com.soptie.server.theme.entity.Theme;

import lombok.Builder;

@Builder(access = PRIVATE)
public record MemberChallengeRoutineAcquireServiceResponse(
long routineId,
ThemeServiceResponse theme,
String routineContent,
String content,
String description,
String place,
String requiredTime
) {
thguss marked this conversation as resolved.
Show resolved Hide resolved

public static MemberChallengeRoutineAcquireServiceResponse of(MemberChallengeResponse routine) {
return MemberChallengeRoutineAcquireServiceResponse.builder()
.routineId(routine.id())
.theme(ThemeServiceResponse.of(routine.theme()))
.routineContent(routine.routineContent())
.content(routine.content())
.description(routine.description())
.place(routine.place())
.requiredTime(routine.requiredTime())
.build();
}

@Builder(access = PRIVATE)
public record ThemeServiceResponse(
long themeId,
String name
) {

private static ThemeServiceResponse of(Theme theme) {
return ThemeServiceResponse.builder()
.themeId(theme.getId())
.name(theme.getName())
.build();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.soptie.server.membeerroutine.service;
package com.soptie.server.memberroutine.service;

import static com.soptie.server.routine.entity.RoutineType.*;
import static org.assertj.core.api.Assertions.*;
Expand All @@ -20,7 +20,6 @@
import com.soptie.server.memberroutine.adapter.MemberRoutineDeleter;
import com.soptie.server.memberroutine.adapter.MemberRoutineFinder;
import com.soptie.server.memberroutine.entity.MemberRoutine;
import com.soptie.server.memberroutine.service.MemberRoutineUpdateService;
import com.soptie.server.memberroutine.service.dto.request.MemberRoutineAchieveServiceRequest;
import com.soptie.server.support.fixture.MemberFixture;
import com.soptie.server.support.fixture.MemberRoutineFixture;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.soptie.server.membeerroutine.service.integration;
package com.soptie.server.memberroutine.service.integration;

import static com.soptie.server.routine.entity.RoutineType.*;
import static com.soptie.server.routine.message.RoutineErrorCode.*;
Expand All @@ -25,11 +25,14 @@
import com.soptie.server.memberroutine.service.MemberRoutineCreateService;
import com.soptie.server.memberroutine.service.MemberRoutineDeleteService;
import com.soptie.server.memberroutine.service.MemberRoutineReadService;
import com.soptie.server.memberroutine.service.dto.request.MemberChallengeRoutineAcquireServiceRequest;
import com.soptie.server.memberroutine.service.dto.request.MemberDailyRoutineCreateServiceRequest;
import com.soptie.server.memberroutine.service.dto.request.MemberDailyRoutineListAcquireServiceRequest;
import com.soptie.server.memberroutine.service.dto.request.MemberHappinessRoutineCreateServiceRequest;
import com.soptie.server.memberroutine.service.dto.request.MemberHappinessRoutineGetServiceRequest;
import com.soptie.server.memberroutine.service.dto.request.MemberRoutinesDeleteServiceRequest;
import com.soptie.server.memberroutine.service.dto.response.MemberChallengeRoutineAcquireServiceResponse;
import com.soptie.server.memberroutine.service.dto.response.MemberDailyRoutineListAcquireServiceResponse;
import com.soptie.server.memberroutine.service.dto.response.MemberDailyRoutinesAcquireServiceResponse;
import com.soptie.server.memberroutine.service.dto.response.MemberDailyRoutinesAcquireServiceResponse.MemberDailyRoutineServiceResponse;
import com.soptie.server.memberroutine.service.dto.response.MemberHappinessRoutineGetServiceResponse;
Expand Down Expand Up @@ -264,7 +267,8 @@ class Acquire {

Member member1;
Member member2;
Theme theme;
Theme theme1;
Theme theme2;
Routine routine1;
Routine routine2;
Routine routine3;
Expand All @@ -275,16 +279,17 @@ void setUp() {
member1 = memberRepository.save(MemberFixture.member().build());
member2 = memberRepository.save(MemberFixture.member().build());

theme = themeRepository.save(ThemeFixture.theme().build());
theme1 = themeRepository.save(ThemeFixture.theme().name("테마 1").build());
theme2 = themeRepository.save(ThemeFixture.theme().name("테마 2").build());

routine1 = routineRepository.save(
RoutineFixture.routine().theme(theme).type(DAILY).content("새로운 나").build());
RoutineFixture.routine().theme(theme1).type(DAILY).content("새로운 나").build());
routine2 = routineRepository.save(
RoutineFixture.routine().theme(theme).type(DAILY).content("깨끗한 나").build());
RoutineFixture.routine().theme(theme1).type(DAILY).content("깨끗한 나").build());
routine3 = routineRepository.save(
RoutineFixture.routine().theme(theme).type(DAILY).content("똑똑한 나").build());
RoutineFixture.routine().theme(theme2).type(DAILY).content("똑똑한 나").build());
challengeRoutine = routineRepository.save(
RoutineFixture.routine().theme(theme).type(CHALLENGE).content("도전 루틴").build());
RoutineFixture.routine().theme(theme1).type(CHALLENGE).content("도전 루틴").build());
}

@Test
Expand Down Expand Up @@ -376,6 +381,75 @@ void getEmptyWhenMemberHasNotHappinessRoutine() {
// then
assertThat(actual).isEmpty();
}
}

@Test
@DisplayName("[성공] 회원이 가진 모든 데일리 루틴을 테마별로 조회한다. 이 때, 루틴은 가나다순으로 정렬된다.")
void acquireAllByMember() {
// given
memberRoutineRepository.save(MemberRoutineFixture.memberRoutine()
.member(member1).routineId(routine1.getId()).type(routine1.getType()).build());
memberRoutineRepository.save(MemberRoutineFixture.memberRoutine()
.member(member1).routineId(routine2.getId()).type(routine2.getType()).build());
memberRoutineRepository.save(MemberRoutineFixture.memberRoutine()
.member(member1).routineId(routine3.getId()).type(routine3.getType()).build());

MemberDailyRoutineListAcquireServiceRequest request = MemberDailyRoutineListAcquireServiceRequest.of(
member1.getId());

// when
final MemberDailyRoutineListAcquireServiceResponse actual = memberRoutineReadService.acquireAll(request);

// then
int themeCount = actual.routines().size();
assertThat(themeCount).isEqualTo(2);
List<String> contents = actual.routines().get(0).routines().stream().map(
MemberDailyRoutinesAcquireServiceResponse.MemberDailyRoutineServiceResponse::content).toList();
assertThat(contents).hasSize(2);
assertThat(contents).containsExactly(routine2.getContent(), routine1.getContent());
}

@Test
@DisplayName("[성공] 회원의 도전 루틴이 존재한다면 해당 도전 루틴을 테마와 함께 조회한다.")
void acquireByMember() {
// given
Challenge challenge = challengeRepository.save(
ChallengeFixture.challenge()
.content("무한~ 도전~")
.description("무한으로 즐겨요")
.routine(challengeRoutine)
.build());
memberRoutineRepository.save(MemberRoutineFixture.memberRoutine()
.member(member1).routineId(challenge.getId()).type(challengeRoutine.getType()).build());

MemberChallengeRoutineAcquireServiceRequest request =
MemberChallengeRoutineAcquireServiceRequest.of(member1.getId());

// when
final Optional<MemberChallengeRoutineAcquireServiceResponse> actual =
memberRoutineReadService.acquire(request);

// then
assertThat(actual).isPresent();

final MemberChallengeRoutineAcquireServiceResponse response = actual.get();
assertThat(response.theme().themeId()).isEqualTo(challenge.getRoutine().getTheme().getId());
assertThat(response.theme().name()).isEqualTo(challenge.getRoutine().getTheme().getName());
assertThat(response.content()).isEqualTo(challenge.getContent());
}

@Test
@DisplayName("[성공] 회원이 가진 도전 루틴이 없으면 빈 값으로 조회된다.")
void acquireEmptyWhenMemberHasNotChallengeRoutine() {
// given
MemberChallengeRoutineAcquireServiceRequest request =
MemberChallengeRoutineAcquireServiceRequest.of(member1.getId());

// when
final Optional<MemberChallengeRoutineAcquireServiceResponse> actual =
memberRoutineReadService.acquire(request);

// then
assertThat(actual).isEmpty();
}
}
}
Loading