Skip to content
15 changes: 13 additions & 2 deletions src/main/java/com/example/spot/domain/mapping/MemberStudy.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
import com.example.spot.domain.Member;
import com.example.spot.domain.common.BaseEntity;
import com.example.spot.domain.enums.ApplicationStatus;
import com.example.spot.domain.enums.Status;
import com.example.spot.domain.study.Study;
import jakarta.persistence.*;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;

@Getter
@Entity
Expand Down Expand Up @@ -38,6 +41,12 @@ public class MemberStudy extends BaseEntity {
@Setter
private String reason;

@Column(nullable = false)
@Enumerated(EnumType.STRING)
private Status activeStatus;

private LocalDateTime finishedAt;

//== 회원 ==//
@Setter
@ManyToOne(fetch = FetchType.LAZY)
Expand All @@ -63,6 +72,8 @@ public MemberStudy(Boolean isOwned, String introduction, Member member, Study st
this.study = study;
this.status = status;
}


public void disable() {
this.activeStatus = Status.OFF;
this.finishedAt = LocalDateTime.now();
}
}
8 changes: 4 additions & 4 deletions src/main/java/com/example/spot/domain/study/Study.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package com.example.spot.domain.study;

import com.example.spot.domain.Notification;
import com.example.spot.domain.Quiz;
import com.example.spot.domain.common.BaseEntity;
import com.example.spot.domain.enums.Gender;
import com.example.spot.domain.enums.Status;
import com.example.spot.domain.enums.StudyState;
import com.example.spot.domain.enums.ThemeType;
import com.example.spot.domain.mapping.MemberStudy;
import com.example.spot.domain.mapping.PreferredStudy;
import com.example.spot.domain.mapping.RegionStudy;
Expand All @@ -20,12 +18,11 @@
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import lombok.*;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;

@Entity
@Getter
Expand Down Expand Up @@ -88,6 +85,8 @@ public class Study extends BaseEntity {
@Column(nullable = false)
private Long maxPeople;

private LocalDateTime finishedAt;

@Builder.Default
@OneToMany(mappedBy = "study", cascade = CascadeType.ALL)
private List<Schedule> schedules = new ArrayList<>();
Expand Down Expand Up @@ -207,6 +206,7 @@ public void terminateStudy(String performance) {
this.studyState = StudyState.COMPLETED;
this.status = Status.OFF;
this.performance = performance;
this.finishedAt = LocalDateTime.now();
}

public void updateStudyInfo(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
import com.example.spot.domain.enums.ApplicationStatus;
import com.example.spot.domain.enums.Status;
import com.example.spot.domain.mapping.MemberStudy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.List;
Expand All @@ -28,6 +31,35 @@ public interface MemberStudyRepository extends JpaRepository<MemberStudy, Long>

Optional<MemberStudy> findByMemberIdAndStudyId(Long memberId, Long studyId);

@Query(
value = """
SELECT ms.*
FROM member_study ms
JOIN study s ON ms.study_id = s.id
WHERE ms.member_id = :memberId
AND ms.status = 'APPROVED'
AND (
s.study_state = 'COMPLETED'
OR ms.active_status = 'OFF'
)
ORDER BY ms.created_at DESC
""",
countQuery = """
SELECT COUNT(*)
FROM member_study ms
JOIN study s ON ms.study_id = s.id
WHERE ms.member_id = :memberId
AND ms.status = 'APPROVED'
AND (
s.study_state = 'COMPLETED'
OR ms.active_status = 'OFF'
)
""",
nativeQuery = true
)
Page<MemberStudy> findAllByMemberIdAndConditions(@Param("memberId") Long memberId, Pageable pageable);


long countByStatusAndStudyId(ApplicationStatus status, Long studyId);
long countByMemberIdAndStatusAndStudy_Status(Long memberId, ApplicationStatus applicationStatus, Status status);
long countByMemberIdAndIsOwnedAndStudy_Status(Long memberId, Boolean isOwned, Status status);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package com.example.spot.repository.querydsl;

import com.example.spot.domain.Member;
import com.example.spot.domain.enums.Status;
import com.example.spot.domain.enums.StudySortBy;
import com.example.spot.domain.enums.StudyState;
import com.example.spot.domain.mapping.MemberStudy;
import com.example.spot.domain.mapping.RegionStudy;
import com.example.spot.domain.mapping.StudyTheme;
import com.example.spot.domain.study.Study;
import java.util.List;
import java.util.Map;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

public interface StudyRepositoryCustom {
Expand Down Expand Up @@ -37,6 +40,7 @@ List<Study> findStudyByConditionsAndRegionStudiesAndNotInIds(Map<String, Object>
List<Study> findByMemberStudiesAndStatus(List<MemberStudy> memberStudy, Pageable pageable, Status status);
List<Study> findRecruitingStudiesByMemberStudy(List<MemberStudy> memberStudy, Pageable pageable);


long countStudyByConditionsAndThemeTypesAndNotInIds(
Map<String, Object> search, List<StudyTheme> themeTypes, StudySortBy sortBy, List<Long> studyIds);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.example.spot.repository.querydsl.impl;

import com.example.spot.domain.Member;
import com.example.spot.domain.Region;
import com.example.spot.domain.enums.ApplicationStatus;
import com.example.spot.domain.enums.Gender;
import com.example.spot.domain.enums.Status;
import com.example.spot.domain.enums.StudySortBy;
Expand All @@ -21,8 +23,11 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;

import static com.example.spot.domain.mapping.QMemberStudy.memberStudy;
import static com.example.spot.domain.study.QStudy.study;
@RequiredArgsConstructor
@Slf4j
Expand Down Expand Up @@ -278,8 +283,6 @@ public List<Study> findRecruitingStudiesByMemberStudy(List<MemberStudy> memberSt
.fetch();
}



@Override
public long countStudyByConditionsAndThemeTypesAndNotInIds(Map<String, Object> search,
List<StudyTheme> themeTypes, StudySortBy sortBy, List<Long> studyIds) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ public StudyWithdrawalResponseDTO.WithdrawalDTO withdrawFromStudy(Long studyId)
throw new StudyHandler(ErrorStatus._STUDY_OWNER_CANNOT_WITHDRAW);
}

memberStudyRepository.delete(memberStudy);
memberStudy.disable();
memberStudyRepository.save(memberStudy);

return StudyWithdrawalResponseDTO.WithdrawalDTO.toDTO(member, study);
}
Expand All @@ -117,7 +118,11 @@ public WithdrawalDTO withdrawHostFromStudy(Long studyId, StudyHostWithdrawReques
MemberStudy newHostStudy = memberStudyRepository.findByMemberIdAndStudyIdAndStatus(requestDTO.getNewHostId(), studyId, ApplicationStatus.APPROVED)
.orElseThrow(() -> new StudyHandler(ErrorStatus._STUDY_MEMBER_NOT_EXIST));

memberStudyRepository.delete(memberStudy);

// memberStudyRepository.delete(memberStudy);

memberStudy.disable();
memberStudy.setIsOwned(false);

newHostStudy.setIsOwned(true);
newHostStudy.setReason(requestDTO.getReason());
Expand Down Expand Up @@ -154,6 +159,7 @@ public StudyTerminationResponseDTO.TerminationDTO terminateStudy(Long studyId, S
throw new StudyHandler(ErrorStatus._STUDY_ALREADY_TERMINATED);
}

memberStudy.disable();
study.terminateStudy(performance);
studyRepository.save(study);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public StudyJoinResponseDTO.JoinDTO applyToStudy(Long studyId, StudyJoinRequestD
.member(member)
.study(study)
.status(ApplicationStatus.APPLIED)
.activeStatus(Status.ON)
.build();

member.addMemberStudy(memberStudy);
Expand Down Expand Up @@ -239,6 +240,7 @@ private void createMemberStudy(Member member, Study study) {
.member(member)
.study(study)
.status(ApplicationStatus.APPROVED)
.activeStatus(Status.ON)
.build();

member.addMemberStudy(memberStudy);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@
import com.example.spot.web.dto.search.SearchResponseDTO.HotKeywordDTO;
import com.example.spot.web.dto.search.SearchResponseDTO.MyPageDTO;
import com.example.spot.web.dto.search.SearchResponseDTO.StudyPreviewDTO;
import com.example.spot.web.dto.search.StudyHistoryResponseDTO;
import com.example.spot.web.dto.study.response.StudyInfoResponseDTO;
import com.example.spot.web.dto.study.response.StudyMemberResponseDTO;
import com.example.spot.web.dto.study.response.StudyPostResponseDTO;
import com.example.spot.web.dto.study.response.StudyScheduleResponseDTO;
import org.springframework.data.domain.Pageable;

public interface StudyQueryService {
Expand Down Expand Up @@ -71,4 +69,5 @@ StudyPreviewDTO findRecruitingStudiesByConditions(
// 내가 모집중인 스터디 조회
StudyPreviewDTO findMyRecruitingStudies(Pageable pageable, Long memberId);

StudyHistoryResponseDTO getFinishedStudies(Pageable pageable, Long currentUserId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.example.spot.domain.enums.Status;
import com.example.spot.domain.enums.StudyLikeStatus;
import com.example.spot.domain.enums.StudySortBy;
import com.example.spot.domain.enums.StudyState;
import com.example.spot.domain.enums.ThemeType;
import com.example.spot.domain.mapping.MemberStudy;
import com.example.spot.domain.mapping.MemberTheme;
Expand All @@ -38,6 +39,7 @@
import com.example.spot.web.dto.search.SearchResponseDTO.MyPageDTO;
import com.example.spot.web.dto.search.SearchResponseDTO.SearchStudyDTO;
import com.example.spot.web.dto.search.SearchResponseDTO.StudyPreviewDTO;
import com.example.spot.web.dto.search.StudyHistoryResponseDTO;
import com.example.spot.web.dto.study.response.StudyInfoResponseDTO;
import java.util.ArrayList;
import java.util.Collections;
Expand Down Expand Up @@ -833,6 +835,14 @@ public StudyPreviewDTO findMyRecruitingStudies(Pageable pageable, Long memberId)
return getDTOs(studies, pageable, totalElements, memberId);
}

@Override
public StudyHistoryResponseDTO getFinishedStudies(Pageable pageable, Long currentUserId) {
Member member = memberRepository.findById(currentUserId)
.orElseThrow(() -> new MemberHandler(ErrorStatus._MEMBER_NOT_FOUND));
Page<MemberStudy> finishedStudies = memberStudyRepository.findAllByMemberIdAndConditions(currentUserId, pageable);
return StudyHistoryResponseDTO.of(finishedStudies);
}

/**
* 특정 회원이 참가하고 있는 스터디를 조회합니다.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.example.spot.web.dto.search.SearchResponseDTO.HotKeywordDTO;
import com.example.spot.web.dto.search.SearchResponseDTO.MyPageDTO;
import com.example.spot.web.dto.search.SearchResponseDTO.StudyPreviewDTO;
import com.example.spot.web.dto.search.StudyHistoryResponseDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
Expand Down Expand Up @@ -425,6 +426,24 @@ public ApiResponse<StudyPreviewDTO> getAppliedStudies(
return ApiResponse.onSuccess(SuccessStatus._STUDY_FOUND, studies);
}

/* ----------------------------- 신청한 스터디 목록 조회 ------------------------------------- */
@Tag(name = "마이 페이지")
@Operation(summary = "[마이 페이지] 사용자님의 노력들 조회", description = """

## [마이페이지]
호스트가 종료한 스터디, 그리고 스터디원(호스트포함) 본인이 탈퇴한 스터디의 정보와 스터디 한줄 평가를 조회합니다.
""")
@GetMapping("/search/studies/finished-studies")
public ApiResponse<?> getFinishedStudies(
@RequestParam @Min(0) Integer page,
@RequestParam @Min(1) Integer size
) {
StudyHistoryResponseDTO responseDTO = studyQueryService.getFinishedStudies(PageRequest.of(page, size),
SecurityUtils.getCurrentUserId());
return ApiResponse.onSuccess(SuccessStatus._STUDY_FOUND, responseDTO);
}




}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.example.spot.web.dto.search;

import com.example.spot.domain.mapping.MemberStudy;
import com.example.spot.domain.study.Study;
import java.time.LocalDateTime;
import org.springframework.data.domain.Page;

public record StudyHistoryResponseDTO (
Page<StudyHistoryDTO> studyHistories
) {
public static StudyHistoryResponseDTO of(Page<MemberStudy> studies) {
return new StudyHistoryResponseDTO(studies.map(memberStudy -> new StudyHistoryDTO(
memberStudy.getStudy().getId(),
memberStudy.getStudy().getTitle(),
memberStudy.getStudy().getPerformance(),
memberStudy.getCreatedAt(),
memberStudy.getFinishedAt()
)));
}

private record StudyHistoryDTO (
Long studyId,
String title,
String performance,
LocalDateTime createdAt,
LocalDateTime finishedAt
) {
}
}