Skip to content

Commit

Permalink
스터디 미리보기, 찜 컨트롤러, 서비스 로직 및 테스트 코드 작성 (#105)
Browse files Browse the repository at this point in the history
* feature: [#102] Study Preview API controller, service 관련 코드 작성

* feature: [#102] StudyController Study Preview response 추가

* feature: [#102] StudyPreview API Controller, Service 테스트 코드 작성

* feature: [#102] study 찜하기, 찜 취소하기 Controller, Service 로직 작성 및 테스트 코드 작성

* refactor: [#102] StudyService 타입 주석 제거
  • Loading branch information
HanKwanJin authored Feb 12, 2023
1 parent 6d85322 commit 9f9b1a8
Show file tree
Hide file tree
Showing 24 changed files with 620 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public enum ErrorCodeAndMessages implements CodeAndMessages {
SPEAKING_LOG_NOT_MATCH_MEMBER_ERROR("E-BR010", "SpeakingLog의 작성자가 해당 멤버가 아닙니다."),
FEIGN_CLIENT_WITH_WRONG_TOKEN_ERROR("E-BR011", "잘못된 형식의 토큰입니다. 토큰 정보를 다시 확인해주세요"),
STUDY_NOT_MATCH_LEADER_ERROR("E-BR012", "Study의 생성자가 해당 멤버가 아닙니다."),
STUDY_PREVIEW_TYPE_ERROR("E-BR013", "올바르지 않은 StudyPreviewType 형식입니다. MY, RANDOM 중 하나여야 합니다."),


/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.be.common.exception.study;

import com.example.be.common.exception.BaseException;
import com.example.be.common.exception.ErrorCodeAndMessages;

public class InvalidStudyPreviewTypeException extends BaseException {

public InvalidStudyPreviewTypeException() {
super(ErrorCodeAndMessages.STUDY_PREVIEW_TYPE_ERROR);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ public enum ResponseCodeAndMessages implements CodeAndMessages {
FIND_DETAIL_STUDY_SUCCESS("S-S003", "스터디 상세 조회를 성공했습니다."),
MODIFY_STUDY_SUCCESS("S-S004", "스터디 수정을 성공했습니다."),
DELETE_STUDY_SUCCESS("S-S005", "스터디 삭제를 성공했습니다."),
FIND_STUDY_PREVIEW_SUCCESS("S-S006", "스터디 미리보기 조회를 성공했습니다."),
INTEREST_STUDY_SUCCESS("S-S007", "스터디 찜하기를 성공했습니다."),
NOT_INTEREST_STUDY_SUCCESS("S-S008", "스터디 찜취소를 성공했습니다."),
FIND_RECOMMEND_STUDY_SUCCESS("S-S009", "스터디 로그 타입 RECOMMEND 조회를 성공했습니다."),
FIND_POPULAR_STUDY_SUCCESS("S-S010", "스터디 로그 타입 POPULAR 조회를 성공했습니다."),
FIND_INTEREST_STUDY_SUCCESS("S-S011", "스터디 로그 타입 INTEREST 조회를 성공했습니다."),

/**
Assignment
Expand Down
95 changes: 90 additions & 5 deletions be/src/main/java/com/example/be/core/application/StudyService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@
import com.example.be.common.exception.study.NotFoundStudyIdException;
import com.example.be.common.exception.study.NotMatchStudyAndMemberException;
import com.example.be.core.application.dto.request.StudyConditionRequest;
import com.example.be.core.application.dto.request.StudyPreviewConditionRequest;
import com.example.be.core.application.dto.request.StudyRequest;
import com.example.be.core.application.dto.response.MemberProfilesResponse;
import com.example.be.core.application.dto.response.PreviewProfilesResponse;
import com.example.be.core.application.dto.response.StudiesResponse;
import com.example.be.core.application.dto.response.StudyDetailResponse;
import com.example.be.core.application.dto.response.StudyPreviewResponse;
import com.example.be.core.application.dto.response.StudyPreviewsResponse;
import com.example.be.core.application.dto.response.StudyResponse;
import com.example.be.core.domain.member.Member;
import com.example.be.core.domain.study.StudyDayConverter;
import com.example.be.core.domain.study.Study;
import com.example.be.core.domain.study.StudyInterest;
import com.example.be.core.domain.study.StudyMember;
import com.example.be.core.domain.study.StudyPreviewType;
import com.example.be.core.repository.member.MemberRepository;
import com.example.be.core.repository.study.StudyCommentRepository;
import com.example.be.core.repository.study.StudyInterestRepository;
Expand All @@ -21,8 +27,11 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.SortedMap;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -32,13 +41,13 @@
public class StudyService {

private final StudyRepository studyRepository;

private final StudyInterestRepository studyInterestRepository;

private final StudyCommentRepository studyCommentRepository;

private final StudyMemberRepository studyMemberRepository;

private final MemberRepository memberRepository;

private static final Integer ZERO = 0;
Expand Down Expand Up @@ -153,7 +162,7 @@ public StudyDetailResponse findById(Long memberId, Long studyId) {
if (member.getLeader().equals(Boolean.TRUE)) {
leaderImg = member.getMember().getProfileImage();
isLeader = leaderCheck(memberId, isLeader, member);
}else{
} else {
otherImg.add(member.getMember().getProfileImage());
}
}
Expand Down Expand Up @@ -184,6 +193,57 @@ public StudyDetailResponse findById(Long memberId, Long studyId) {
);
}

public StudyPreviewsResponse findPreview(Long memberId, StudyPreviewConditionRequest studyPreviewConditionRequest) {
log.debug("[스터디 미리보기 조회] memberId = {}, StudyPreviewConditionRequest = {}", memberId, studyPreviewConditionRequest);

List<StudyMember> studyMembers = studyMemberRepository.findStudyMembersByMemberId(memberId);
List<String> memberProfiles = new ArrayList<>();

if (studyPreviewConditionRequest.getType().equals(StudyPreviewType.MY)) {
List<StudyPreviewResponse> myStudyPreviewResponses = studyMembers.stream().map(studyMember -> {

List<StudyMember> members = getStudyMembers(studyMember.getStudy().getId());

memberProfiles.clear();
for (StudyMember member : members) {
memberProfiles.add(member.getMember().getProfileImage());
}

return new StudyPreviewResponse(
studyMember.getStudy().getId(),
studyMember.getStudy().getTitle(),
studyMember.getStudy().getContent(),
studyMember.getStudy().getPosterImage(),
studyMember.getStudy().getCreatedAt(),
new PreviewProfilesResponse(memberProfiles)
);
}).collect(Collectors.toList());

return new StudyPreviewsResponse(myStudyPreviewResponses);
}

List<Study> studies = studyRepository.findAll(Sort.by(Direction.DESC, "createdAt"));
List<StudyPreviewResponse> randomStudyPreviewResponse = studies.stream().map(study -> {

List<StudyMember> members = studyMemberRepository.findStudyMembersByStudyId(study.getId());

memberProfiles.clear();
for (StudyMember member : members) {
memberProfiles.add(member.getMember().getProfileImage());
}

return new StudyPreviewResponse(
study.getId(),
study.getTitle(),
study.getContent(),
study.getPosterImage(),
study.getCreatedAt(),
new PreviewProfilesResponse(memberProfiles)
);
}).collect(Collectors.toList());

return new StudyPreviewsResponse(randomStudyPreviewResponse);
}
@Transactional
public StudyDetailResponse modify(Long memberId, Long studyId, StudyRequest studyRequest) {
log.debug("[스터디 수정] StudyId = {}, StudyRequest = {}", studyId, studyRequest);
Expand Down Expand Up @@ -261,6 +321,31 @@ public void delete(Long memberId, Long studyId) {
studyRepository.deleteById(studyId);
}

@Transactional
public void interest(Long memberId, Long studyId) {
log.debug("[스터디 찜하기] studyId = {}", studyId);

Member member = memberRepository.findById(memberId)
.orElseThrow(NotFoundMemberIdException::new);

Study study = studyRepository.findById(studyId)
.orElseThrow(NotFoundStudyIdException::new);

studyInterestRepository.save(
new StudyInterest(
member,
study
)
);
}

@Transactional
public void notInterest(Long memberId, Long studyId) {
log.debug("[스터디 찜 취소하기] studyId = {}", studyId);

studyInterestRepository.deleteByMemberIdAndStudyId(memberId, studyId);
}

private Integer getStudyInterestCount(Study study) {
return studyInterestRepository.countByStudyId(study.getId());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class StudyConditionRequest {

@Schema(enumAsRef = true, description = "스터디 로그 조회 타입, NULL, default = type.my")
@Schema(enumAsRef = true, description = "스터디 로그 조회 타입, NULL")
private StudyType type;

public StudyConditionRequest(String type) {
Expand All @@ -22,7 +22,7 @@ public StudyConditionRequest(String type) {
private StudyType getStudyTypeFromString(String input) {

if (input == null) {
return StudyType.MY;
throw new IllegalArgumentException("type이 정해지지 않았습니다.");
}
return StudyType.convert(input.toUpperCase());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.example.be.core.application.dto.request;

import com.example.be.core.domain.study.StudyPreviewType;
import com.example.be.core.domain.study.StudyType;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class StudyPreviewConditionRequest {

@Schema(enumAsRef = true, description = "스터디 미리보기 조회 타입, NULL")
private StudyPreviewType type;

public StudyPreviewConditionRequest(String type) {
this.type = getStudyPreviewTypeFromString(type);
}

private StudyPreviewType getStudyPreviewTypeFromString(String input) {

if (input == null) {
throw new IllegalArgumentException("type이 정해지지 않았습니다.");
}
return StudyPreviewType.convert(input.toUpperCase());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.example.be.core.application.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import lombok.Getter;

@Getter
public class PreviewProfilesResponse {

@Schema(type = "List<String>", description = "스터디에 참여중인 멤버들의 프로필 이미지, NOT NULL")
private final List<String> memberProfiles;

public PreviewProfilesResponse(List<String> memberProfiles) {
this.memberProfiles = memberProfiles;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ public class StudyPreviewResponse {
@Schema(type = "LocalDateTime", description = "생성 시간, NOT NULL")
private final LocalDateTime createdAt;

@Schema(type = "MemberProfilesResponse", description = "스터디에 참여중인 멤버들의 프로필 이미지, NOT NULL")
private final MemberProfilesResponse memberProfiles;
@Schema(type = "PreviewProfilesResponse", description = "스터디에 참여중인 멤버들의 프로필 이미지, NOT NULL")
private final PreviewProfilesResponse memberProfiles;

public StudyPreviewResponse(Long studyId, String title, String content, String posterImage,
LocalDateTime createdAt, MemberProfilesResponse memberProfiles) {
LocalDateTime createdAt, PreviewProfilesResponse memberProfiles) {
this.studyId = studyId;
this.title = title;
this.content = content;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.example.be.core.application.dto.response;

import java.util.List;
import lombok.Getter;

@Getter
public class StudyPreviewsResponse {

private final List<StudyPreviewResponse> studyPreviewResponses;

public StudyPreviewsResponse(List<StudyPreviewResponse> studyPreviewResponses) {
this.studyPreviewResponses = studyPreviewResponses;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.example.be.core.domain.study;

import com.fasterxml.jackson.annotation.JsonCreator;
import java.util.Arrays;
import lombok.Getter;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.example.be.core.domain.study;

import com.example.be.common.exception.study.InvalidStudyPreviewTypeException;
import com.example.be.common.exception.study.InvalidStudyTypeException;
import java.util.Arrays;
import lombok.Getter;

@Getter
public enum StudyPreviewType {

MY("MY"),
RANDOM("RANDOM")
;

private final String type;

StudyPreviewType(String type) {
this.type = type;
}

public static StudyPreviewType convert(String source) {

return Arrays.stream(StudyPreviewType.values())
.filter(e -> e.type.equals(source))
.findAny()
.orElseThrow(InvalidStudyPreviewTypeException::new);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
public enum StudyType {

ALL("ALL"),
MY("MY"),
MATE("MATE"),
RECOMMEND("RECOMMEND"),
POPULAR("POPULAR"),
INTEREST("INTEREST")
;

private final String type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ public interface StudyInterestRepository extends JpaRepository<StudyInterest, Lo
Integer countByStudyId(Long studyId);

Optional<StudyInterest> findByMemberIdAndStudy(Long memberId, Study study);

void deleteByMemberIdAndStudyId(Long memberId, Long studyId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
public interface StudyMemberRepository extends JpaRepository<StudyMember, Long> {
List<StudyMember> findStudyMembersByStudyId(Long studyId);

List<StudyMember> findStudyMembersByMemberId(Long memberId);

StudyMember findStudyMemberByStudyIdAndLeaderIsTrue(Long studyId);

Integer countStudyMemberByStudyId(Long studyId);
Expand Down
Loading

0 comments on commit 9f9b1a8

Please sign in to comment.