Skip to content

Commit

Permalink
Feature/#222 스터디 랭킹 반환시 페이지네이션으로 반환한다 (#224)
Browse files Browse the repository at this point in the history
* feat: 스터디 랭킹 목록 반환시 페이지네이션을 반환한다.

#222

* refactor: 책임 분리

#222

* fix: 생성자 주입을 추가한다.

* refactor: MemberTeam의 빌더는 항상 isDelete=fasle로 한다.

* test: 수정된 코드에 맞춰 테스트 수정

#222
  • Loading branch information
lcqff authored Aug 31, 2024
1 parent 9abf6ec commit 153c751
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 84 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package doore.member.application.convenience;

import static doore.member.domain.StudyRoleType.*;

import doore.member.domain.StudyRole;
import doore.member.domain.repository.StudyRoleRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
@RequiredArgsConstructor
public class StudyRoleConvenience {
private final StudyRoleRepository studyRoleRepository;

public void assignStudyLeaderRole(final Long studyId, final Long memberId) {
studyRoleRepository.save(StudyRole.builder()
.studyRoleType(ROLE_스터디장)
.studyId(studyId)
.memberId(memberId)
.build());
}

public Long findStudyLeaderId(final Long studyId) {
return studyRoleRepository.findLeaderIdByStudyId(studyId);
}
}
2 changes: 1 addition & 1 deletion src/main/java/doore/member/domain/MemberTeam.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ private MemberTeam(final Long id, final Long teamId, final Member member, final
this.id = id;
this.teamId = teamId;
this.member = member;
this.isDeleted = isDeleted;
this.isDeleted = false;
}
}
5 changes: 3 additions & 2 deletions src/main/java/doore/study/api/StudyController.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import jakarta.validation.constraints.PositiveOrZero;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand Down Expand Up @@ -81,11 +82,11 @@ public ResponseEntity<List<StudyReferenceResponse>> getMyStudies(@PathVariable f
}

@GetMapping("/teams/{teamId}/studies") // 비회원
public ResponseEntity<List<StudyRankResponse>> getTeamStudies(
public ResponseEntity<Page<StudyRankResponse>> getTeamStudies(
@PathVariable final Long teamId,
@RequestParam(defaultValue = "0") @PositiveOrZero final int page,
@RequestParam(defaultValue = "4") @PositiveOrZero final int size) {
final List<StudyRankResponse> studyReferenceResponses =
final Page<StudyRankResponse> studyReferenceResponses =
studyQueryService.getTeamStudies(teamId, PageRequest.of(page, size));
return ResponseEntity.status(HttpStatus.OK).body(studyReferenceResponses);
}
Expand Down
49 changes: 12 additions & 37 deletions src/main/java/doore/study/application/StudyCommandService.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
package doore.study.application;

import static doore.member.domain.StudyRoleType.ROLE_스터디장;
import static doore.member.exception.MemberExceptionType.NOT_FOUND_MEMBER;
import static doore.study.exception.StudyExceptionType.INVALID_ENDDATE;
import static doore.study.exception.StudyExceptionType.NOT_FOUND_STATUS;
import static doore.study.exception.StudyExceptionType.NOT_FOUND_STUDY;
import static doore.team.exception.TeamExceptionType.NOT_FOUND_TEAM;

import doore.member.application.convenience.StudyRoleConvenience;
import doore.member.application.convenience.StudyRoleValidateAccessPermission;
import doore.member.application.convenience.TeamRoleValidateAccessPermission;
import doore.member.domain.Participant;
import doore.member.domain.StudyRole;
import doore.member.domain.repository.MemberRepository;
import doore.member.domain.repository.ParticipantRepository;
import doore.member.domain.repository.StudyRoleRepository;
import doore.member.exception.MemberException;
import doore.study.application.convenience.StudyAuthorization;
import doore.study.application.dto.request.StudyCreateRequest;
import doore.study.application.dto.request.StudyUpdateRequest;
import doore.study.domain.CurriculumItem;
Expand All @@ -25,8 +19,7 @@
import doore.study.domain.repository.ParticipantCurriculumItemRepository;
import doore.study.domain.repository.StudyRepository;
import doore.study.exception.StudyException;
import doore.team.domain.TeamRepository;
import doore.team.exception.TeamException;
import doore.team.application.convenience.TeamAuthorization;
import java.time.LocalDate;
import java.util.List;
import lombok.RequiredArgsConstructor;
Expand All @@ -38,29 +31,23 @@
@RequiredArgsConstructor
public class StudyCommandService {
private final StudyRepository studyRepository;
private final TeamRepository teamRepository;
private final MemberRepository memberRepository;
private final StudyRoleRepository studyRoleRepository;
private final CurriculumItemRepository curriculumItemRepository;
private final ParticipantCurriculumItemRepository participantCurriculumItemRepository;
private final ParticipantRepository participantRepository;
private final ParticipantCommandService participantCommandService;
private final StudyRoleConvenience studyRoleConvenience;
private final StudyAuthorization studyAuthorization;
private final TeamAuthorization teamAuthorization;

private final TeamRoleValidateAccessPermission teamRoleValidateAccessPermission;
private final StudyRoleValidateAccessPermission studyRoleValidateAccessPermission;

public void createStudy(final StudyCreateRequest request, final Long teamId, final Long memberId) {
validateExistMember(memberId);
validateExistTeam(teamId);
teamRoleValidateAccessPermission.validateExistMemberTeam(teamId, memberId);

teamAuthorization.validateExistTeam(teamId);
checkEndDateValid(request.startDate(), request.endDate());
final Study study = studyRepository.save(request.toStudy(teamId));
studyRoleRepository.save(StudyRole.builder()
.studyRoleType(ROLE_스터디장)
.studyId(study.getId())
.memberId(memberId)
.build());
studyRoleConvenience.assignStudyLeaderRole(study.getId(), memberId);
saveParticipant(study.getId(), memberId, memberId);
}

Expand All @@ -76,7 +63,7 @@ private void checkEndDateValid(final LocalDate startDate, final LocalDate endDat

public void deleteStudy(final Long studyId, final Long memberId) {
studyRoleValidateAccessPermission.validateExistStudyLeader(studyId, memberId);
validateExistStudy(studyId);
studyAuthorization.validateExistStudy(studyId);

deleteCurriculumItemAndParticipantCurriculumItem(studyId);
deleteParticipant(studyId);
Expand All @@ -85,19 +72,19 @@ public void deleteStudy(final Long studyId, final Long memberId) {

public void updateStudy(final StudyUpdateRequest request, final Long studyId, final Long memberId) {
studyRoleValidateAccessPermission.validateExistStudyLeader(studyId, memberId);
final Study study = validateExistStudy(studyId);
final Study study = studyAuthorization.getStudyOrThrow(studyId);
study.update(request.name(), request.description(), request.startDate(), request.endDate(), request.status());
}

public void terminateStudy(final Long studyId, final Long memberId) {
studyRoleValidateAccessPermission.validateExistStudyLeader(studyId, memberId);
final Study study = validateExistStudy(studyId);
final Study study = studyAuthorization.getStudyOrThrow(studyId);
study.terminate();
}

public void changeStudyStatus(final String status, final Long studyId, final Long memberId) {
studyRoleValidateAccessPermission.validateExistStudyLeader(studyId, memberId);
final Study study = validateExistStudy(studyId);
final Study study = studyAuthorization.getStudyOrThrow(studyId);
try {
final StudyStatus changedStatus = StudyStatus.valueOf(status);
study.changeStatus(changedStatus);
Expand All @@ -106,18 +93,6 @@ public void changeStudyStatus(final String status, final Long studyId, final Lon
}
}

private void validateExistTeam(final Long teamId) {
teamRepository.findById(teamId).orElseThrow(() -> new TeamException(NOT_FOUND_TEAM));
}

private Study validateExistStudy(final Long studyId) {
return studyRepository.findById(studyId).orElseThrow(() -> new StudyException(NOT_FOUND_STUDY));
}

private void validateExistMember(final Long memberId) {
memberRepository.findById(memberId).orElseThrow(() -> new MemberException(NOT_FOUND_MEMBER));
}

private void deleteCurriculumItemAndParticipantCurriculumItem(final Long studyId) {
final List<CurriculumItem> curriculumItems = curriculumItemRepository.findAllByStudyId(studyId);
final List<Long> curriculumItemIds = curriculumItems.stream()
Expand Down
39 changes: 12 additions & 27 deletions src/main/java/doore/study/application/StudyQueryService.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
package doore.study.application;

import static doore.member.domain.StudyRoleType.ROLE_스터디원;
import static doore.member.domain.StudyRoleType.ROLE_스터디장;
import static doore.member.exception.MemberExceptionType.NOT_FOUND_MEMBER;
import static doore.member.exception.MemberExceptionType.NOT_FOUND_MEMBER_ROLE_IN_STUDY;
import static doore.member.exception.MemberExceptionType.UNAUTHORIZED;
import static doore.study.exception.StudyExceptionType.NOT_FOUND_STUDY;
import static doore.team.exception.TeamExceptionType.NOT_FOUND_TEAM;

import doore.member.application.convenience.StudyRoleConvenience;
import doore.member.domain.Participant;
import doore.member.domain.StudyRole;
import doore.member.domain.repository.MemberRepository;
import doore.member.domain.repository.ParticipantRepository;
import doore.member.domain.repository.StudyRoleRepository;
import doore.member.exception.MemberException;
import doore.study.application.convenience.StudyAuthorization;
import doore.study.application.dto.response.StudyRankResponse;
import doore.study.application.dto.response.StudyReferenceResponse;
import doore.study.application.dto.response.StudyResponse;
Expand All @@ -22,11 +19,13 @@
import doore.study.domain.repository.ParticipantCurriculumItemRepository;
import doore.study.domain.repository.StudyRepository;
import doore.study.exception.StudyException;
import doore.team.application.convenience.TeamAuthorization;
import doore.team.domain.Team;
import doore.team.domain.TeamRepository;
import doore.team.exception.TeamException;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -36,19 +35,17 @@
@RequiredArgsConstructor
public class StudyQueryService {
private final StudyRepository studyRepository;
private final StudyRoleRepository studyRoleRepository;
private final ParticipantRepository participantRepository;
private final ParticipantCurriculumItemRepository participantCurriculumItemRepository;
private final CurriculumItemRepository curriculumItemRepository;
private final TeamRepository teamRepository;
private final MemberRepository memberRepository;
private final StudyAuthorization studyAuthorization;
private final StudyRoleConvenience studyRoleConvenience;
private final TeamAuthorization teamAuthorization;

public StudyResponse findStudyById(final Long studyId) {
final Study study = studyRepository.findById(studyId).orElseThrow(() -> new StudyException(NOT_FOUND_STUDY));
final Long studyLeaderId = studyRoleRepository.findLeaderIdByStudyId(study.getId());

final Team team = teamRepository.findById(study.getTeamId())
.orElseThrow(() -> new TeamException(NOT_FOUND_TEAM));
final Study study = studyAuthorization.getStudyOrThrow(studyId);
final Long studyLeaderId = studyRoleConvenience.findStudyLeaderId(study.getId());
final Team team = teamAuthorization.getTeamOrThrow(study.getTeamId());
final long studyProgressRatio = checkStudyProgressRatio(studyId);

return StudyResponse.of(study, team, studyProgressRatio, studyLeaderId);
Expand All @@ -74,18 +71,6 @@ private void checkSameMemberIdAndTokenMemberId(final Long memberId, final Long t
}
}

private void validateExistStudyLeaderAndParticipant(final Long memberId) {
final StudyRole studyRole = studyRoleRepository.findById(memberId)
.orElseThrow(() -> new MemberException(NOT_FOUND_MEMBER_ROLE_IN_STUDY));
if (!(studyRole.getStudyRoleType().equals(ROLE_스터디장) || studyRole.getStudyRoleType().equals(ROLE_스터디원))) {
throw new MemberException(UNAUTHORIZED);
}
}

private void validateExistMember(final Long memberId) {
memberRepository.findById(memberId).orElseThrow(() -> new MemberException(NOT_FOUND_MEMBER));
}

private long checkStudyProgressRatio(final Long studyId) {
final List<Long> curriculumItemIds = curriculumItemRepository.findIdsByStudyId(studyId);
final long totalCurriculumItems = participantCurriculumItemRepository.countByCurriculumItemIdIn(
Expand All @@ -95,9 +80,9 @@ private long checkStudyProgressRatio(final Long studyId) {
return totalCurriculumItems > 0 ? (checkedTrueCurriculumItems * 100) / totalCurriculumItems : 0;
}

public List<StudyRankResponse> getTeamStudies(final Long teamId, final Pageable pageable) {
public Page<StudyRankResponse> getTeamStudies(final Long teamId, final Pageable pageable) {
return studyRepository.findAllByTeamId(teamId, pageable)
.map(this::convertStudyToStudyRankResponse).getContent();
.map(this::convertStudyToStudyRankResponse);
//todo: (24.07.09) point 기반 정렬 로직 추가;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static doore.team.exception.TeamExceptionType.NOT_FOUND_TEAM;

import doore.team.domain.Team;
import doore.team.domain.TeamRepository;
import doore.team.exception.TeamException;
import lombok.RequiredArgsConstructor;
Expand All @@ -14,7 +15,11 @@
public class TeamAuthorization {
private final TeamRepository teamRepository;

public void validateExistTeam(Long groupId) {
teamRepository.findById(groupId).orElseThrow(() -> new TeamException(NOT_FOUND_TEAM));
public void validateExistTeam(final Long teamId) {
teamRepository.findById(teamId).orElseThrow(() -> new TeamException(NOT_FOUND_TEAM));
}

public Team getTeamOrThrow(final Long teamId) {
return teamRepository.findById(teamId).orElseThrow(() -> new TeamException(NOT_FOUND_TEAM));
}
}
9 changes: 7 additions & 2 deletions src/test/java/doore/restdocs/docs/StudyApiDocsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;

import doore.document.application.dto.response.DocumentResponse;
import doore.restdocs.RestDocsTest;
import doore.study.application.dto.request.StudyCreateRequest;
import doore.study.application.dto.request.StudyUpdateRequest;
Expand All @@ -24,6 +25,9 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders;
Expand Down Expand Up @@ -225,9 +229,10 @@ private StudyReferenceResponse getStudyReferenceResponse() {
new StudyReferenceResponse(2L, "study2", "this is study 2", LocalDate.of(2024, 7, 6),
LocalDate.of(2024, 8, 7), StudyStatus.IN_PROGRESS, 2L, 50L);
final StudyRankResponse otherStudyRankResponse = new StudyRankResponse(20, otherStudyReferenceResponse);
final List<StudyRankResponse> studies = List.of(studyRankResponse, otherStudyRankResponse);
final Page<StudyRankResponse> studyRankResponsePage = new PageImpl<>(studies, PageRequest.of(0,4),studies.size());

when(studyQueryService.getTeamStudies(any(), any())).thenReturn(
List.of(studyRankResponse, otherStudyRankResponse));
when(studyQueryService.getTeamStudies(any(), any())).thenReturn(studyRankResponsePage);

final MultiValueMap<String, String> params = new LinkedMultiValueMap<>() {{
add("page", "0");
Expand Down
19 changes: 14 additions & 5 deletions src/test/java/doore/study/api/StudyControllerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@

import doore.helper.IntegrationTest;
import doore.member.domain.Member;
import doore.member.domain.MemberTeam;
import doore.member.domain.StudyRole;
import doore.member.domain.TeamRole;
import doore.member.domain.TeamRoleType;
import doore.member.domain.repository.MemberTeamRepository;
import doore.member.domain.repository.StudyRoleRepository;
import doore.member.domain.repository.TeamRoleRepository;
import doore.study.application.dto.request.StudyCreateRequest;
Expand All @@ -30,6 +33,8 @@ public class StudyControllerTest extends IntegrationTest {
private StudyRoleRepository studyRoleRepository;
@Autowired
private TeamRoleRepository teamRoleRepository;
@Autowired
private MemberTeamRepository memberTeamRepository;

private Member member;
private Study study;
Expand All @@ -43,16 +48,20 @@ void setUp() {
member = createMember();
study = createStudy();
team = createTeam();
teamRole = teamRoleRepository.save(TeamRole.builder()
.teamId(team.getId())
.teamRoleType(ROLE_팀원)
.memberId(member.getId())
.build());
studyRole = studyRoleRepository.save(StudyRole.builder()
.studyId(study.getId())
.studyRoleType(ROLE_스터디장)
.memberId(member.getId())
.build());
memberTeamRepository.save(MemberTeam.builder()
.teamId(team.getId())
.member(member)
.build());
teamRoleRepository.save(TeamRole.builder()
.teamRoleType(TeamRoleType.ROLE_팀원)
.memberId(member.getId())
.teamId(team.getId())
.build());
token = jwtTokenGenerator.generateToken(String.valueOf(member.getId()));
}

Expand Down
Loading

0 comments on commit 153c751

Please sign in to comment.