Skip to content

Commit

Permalink
Merge branch 'develop' of https://github.com/GDSC-Hongik/gdsc-server
Browse files Browse the repository at this point in the history
…into feature/787-download-study-excel
  • Loading branch information
Sangwook02 committed Oct 13, 2024
2 parents 400fa31 + 382ac9a commit ff6af2b
Show file tree
Hide file tree
Showing 32 changed files with 438 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ public void createRecruitmentRound(RecruitmentRoundCreateRequest request) {
recruitmentRoundsInThisSemester);

RecruitmentRound recruitmentRound = RecruitmentRound.create(
request.name(), request.startDate(), request.endDate(), recruitment, request.roundType());
request.name(),
Period.createPeriod(request.startDate(), request.endDate()),
recruitment,
request.roundType());
recruitmentRoundRepository.save(recruitmentRound);

log.info("[AdminRecruitmentService] 모집회차 생성: recruitmentRoundId={}", recruitmentRound.getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,7 @@ private RecruitmentRound(
this.roundType = roundType;
}

public static RecruitmentRound create(
String name, LocalDateTime startDate, LocalDateTime endDate, Recruitment recruitment, RoundType roundType) {
Period period = Period.createPeriod(startDate, endDate);
public static RecruitmentRound create(String name, Period period, Recruitment recruitment, RoundType roundType) {
return RecruitmentRound.builder()
.name(name)
.period(period)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.gdschongik.gdsc.domain.recruitment.domain;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@AllArgsConstructor
@RequiredArgsConstructor
public enum RoundType {
FIRST("1차"),
SECOND("2차");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.gdschongik.gdsc.domain.study.api;

import com.gdschongik.gdsc.domain.study.application.MentorStudyAchievementService;
import com.gdschongik.gdsc.domain.study.dto.request.OutstandingStudentRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@Tag(name = "Mentor StudyAchievement", description = "멘토 스터디 우수 스터디원 관리 API입니다.")
@RestController
@RequestMapping("/mentor/study-achievements")
@RequiredArgsConstructor
public class MentorStudyAchievementController {

private final MentorStudyAchievementService mentorStudyAchievementService;

@Operation(summary = "우수 스터디원 지정", description = "우수 스터디원으로 지정합니다.")
@PostMapping
public ResponseEntity<Void> designateOutstandingStudent(
@RequestParam(name = "studyId") Long studyId, @Valid @RequestBody OutstandingStudentRequest request) {
mentorStudyAchievementService.designateOutstandingStudent(studyId, request);
return ResponseEntity.ok().build();
}

@Operation(summary = "우수 스터디원 철회", description = "우수 스터디원 지정을 철회합니다.")
@DeleteMapping
public ResponseEntity<Void> withdrawOutstandingStudent(
@RequestParam(name = "studyId") Long studyId, @Valid @RequestBody OutstandingStudentRequest request) {
mentorStudyAchievementService.withdrawOutstandingStudent(studyId, request);
return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.gdschongik.gdsc.domain.study.application.StudentStudyHistoryService;
import com.gdschongik.gdsc.domain.study.dto.request.RepositoryUpdateRequest;
import com.gdschongik.gdsc.domain.study.dto.response.AssignmentHistoryResponse;
import com.gdschongik.gdsc.domain.study.dto.response.StudentMyCompleteStudyResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
Expand Down Expand Up @@ -42,4 +43,11 @@ public ResponseEntity<Void> submitAssignment(@RequestParam(name = "studyDetailId
studentStudyHistoryService.submitAssignment(studyDetailId);
return ResponseEntity.ok().build();
}

@Operation(summary = "내 스터디 수료 내역 조회", description = "내가 수료한 스터디를 조회합니다.")
@GetMapping("/me/complete")
public ResponseEntity<List<StudentMyCompleteStudyResponse>> getMyCompletedStudy() {
List<StudentMyCompleteStudyResponse> response = studentStudyHistoryService.getMyCompletedStudies();
return ResponseEntity.ok(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.gdschongik.gdsc.domain.study.application;

import static com.gdschongik.gdsc.global.exception.ErrorCode.*;

import com.gdschongik.gdsc.domain.member.dao.MemberRepository;
import com.gdschongik.gdsc.domain.member.domain.Member;
import com.gdschongik.gdsc.domain.study.dao.StudyAchievementRepository;
import com.gdschongik.gdsc.domain.study.dao.StudyHistoryRepository;
import com.gdschongik.gdsc.domain.study.dao.StudyRepository;
import com.gdschongik.gdsc.domain.study.domain.Study;
import com.gdschongik.gdsc.domain.study.domain.StudyAchievement;
import com.gdschongik.gdsc.domain.study.domain.StudyHistoryValidator;
import com.gdschongik.gdsc.domain.study.domain.StudyValidator;
import com.gdschongik.gdsc.domain.study.dto.request.OutstandingStudentRequest;
import com.gdschongik.gdsc.global.exception.CustomException;
import com.gdschongik.gdsc.global.util.MemberUtil;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service
@RequiredArgsConstructor
public class MentorStudyAchievementService {

private final MemberUtil memberUtil;
private final StudyValidator studyValidator;
private final StudyHistoryValidator studyHistoryValidator;
private final StudyRepository studyRepository;
private final StudyHistoryRepository studyHistoryRepository;
private final StudyAchievementRepository studyAchievementRepository;
private final MemberRepository memberRepository;

@Transactional
public void designateOutstandingStudent(Long studyId, OutstandingStudentRequest request) {
Member currentMember = memberUtil.getCurrentMember();
Study study = studyRepository.findById(studyId).orElseThrow(() -> new CustomException(STUDY_NOT_FOUND));
Long countByStudyIdAndStudentIds =
studyHistoryRepository.countByStudyIdAndStudentIds(studyId, request.studentIds());

studyValidator.validateStudyMentor(currentMember, study);
studyHistoryValidator.validateAppliedToStudy(
countByStudyIdAndStudentIds, request.studentIds().size());

List<Member> outstandingStudents = memberRepository.findAllById(request.studentIds());
List<StudyAchievement> studyAchievements = outstandingStudents.stream()
.map(member -> StudyAchievement.create(member, study, request.achievementType()))
.toList();
studyAchievementRepository.saveAll(studyAchievements);

log.info(
"[MentorStudyAchievementService] 우수 스터디원 지정: studyId={}, studentIds={}", studyId, request.studentIds());
}

@Transactional
public void withdrawOutstandingStudent(Long studyId, OutstandingStudentRequest request) {
Member currentMember = memberUtil.getCurrentMember();
Study study = studyRepository.findById(studyId).orElseThrow(() -> new CustomException(STUDY_NOT_FOUND));
long countByStudyIdAndStudentIds =
studyHistoryRepository.countByStudyIdAndStudentIds(studyId, request.studentIds());

studyValidator.validateStudyMentor(currentMember, study);
studyHistoryValidator.validateAppliedToStudy(
countByStudyIdAndStudentIds, request.studentIds().size());

studyAchievementRepository.deleteByStudyAndAchievementTypeAndMemberIds(
studyId, request.achievementType(), request.studentIds());

log.info(
"[MentorStudyAchievementService] 우수 스터디원 철회: studyId={}, studentIds={}", studyId, request.studentIds());
}
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,35 @@
package com.gdschongik.gdsc.domain.study.application;

import static com.gdschongik.gdsc.global.exception.ErrorCode.*;
import static java.util.stream.Collectors.*;

import com.gdschongik.gdsc.domain.member.domain.Member;
import com.gdschongik.gdsc.domain.study.dao.AssignmentHistoryRepository;
import com.gdschongik.gdsc.domain.study.dao.StudyAchievementRepository;
import com.gdschongik.gdsc.domain.study.dao.StudyDetailRepository;
import com.gdschongik.gdsc.domain.study.dao.StudyHistoryRepository;
import com.gdschongik.gdsc.domain.study.dao.StudyRepository;
import com.gdschongik.gdsc.domain.study.domain.AchievementType;
import com.gdschongik.gdsc.domain.study.domain.AssignmentHistory;
import com.gdschongik.gdsc.domain.study.domain.AssignmentHistoryGrader;
import com.gdschongik.gdsc.domain.study.domain.AssignmentSubmissionFetcher;
import com.gdschongik.gdsc.domain.study.domain.Study;
import com.gdschongik.gdsc.domain.study.domain.StudyAchievement;
import com.gdschongik.gdsc.domain.study.domain.StudyAssignmentHistoryValidator;
import com.gdschongik.gdsc.domain.study.domain.StudyDetail;
import com.gdschongik.gdsc.domain.study.domain.StudyHistory;
import com.gdschongik.gdsc.domain.study.domain.StudyHistoryValidator;
import com.gdschongik.gdsc.domain.study.dto.request.RepositoryUpdateRequest;
import com.gdschongik.gdsc.domain.study.dto.response.AssignmentHistoryResponse;
import com.gdschongik.gdsc.domain.study.dto.response.StudentMyCompleteStudyResponse;
import com.gdschongik.gdsc.global.exception.CustomException;
import com.gdschongik.gdsc.global.util.MemberUtil;
import com.gdschongik.gdsc.infra.github.client.GithubClient;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -44,6 +51,7 @@ public class StudentStudyHistoryService {
private final StudyAssignmentHistoryValidator studyAssignmentHistoryValidator;
private final AssignmentHistoryGrader assignmentHistoryGrader;
private final StudyRepository studyRepository;
private final StudyAchievementRepository studyAchievementRepository;

@Transactional
public void updateRepository(Long studyId, RepositoryUpdateRequest request) throws IOException {
Expand Down Expand Up @@ -107,6 +115,25 @@ public void submitAssignment(Long studyDetailId) {
assignmentHistory.getSubmissionFailureType());
}

@Transactional(readOnly = true)
public List<StudentMyCompleteStudyResponse> getMyCompletedStudies() {
Member currentMember = memberUtil.getCurrentMember();
List<StudyHistory> studyHistories = studyHistoryRepository.findAllByStudent(currentMember);
List<StudyAchievement> studyAchievements = studyAchievementRepository.findAllByStudent(currentMember);

Map<Study, List<AchievementType>> achievementsByStudy = studyAchievements.stream()
.collect(groupingBy(
StudyAchievement::getStudy, mapping(StudyAchievement::getAchievementType, toList())));

return studyHistories.stream()
.map(history -> {
List<AchievementType> achievementTypes =
achievementsByStudy.getOrDefault(history.getStudy(), new ArrayList<>());
return StudentMyCompleteStudyResponse.of(history, achievementTypes);
})
.toList();
}

private AssignmentHistory findOrCreate(Member currentMember, StudyDetail studyDetail) {
return assignmentHistoryRepository
.findByMemberAndStudyDetail(currentMember, studyDetail)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package com.gdschongik.gdsc.domain.study.dao;

import com.gdschongik.gdsc.domain.study.domain.AchievementType;
import com.gdschongik.gdsc.domain.study.domain.StudyAchievement;
import java.util.List;

public interface StudyAchievementCustomRepository {
List<StudyAchievement> findByStudyIdAndMemberIds(Long studyId, List<Long> memberIds);

void deleteByStudyAndAchievementTypeAndMemberIds(
Long studyId, AchievementType achievementType, List<Long> memberIds);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static com.gdschongik.gdsc.domain.study.domain.QStudyAchievement.*;

import com.gdschongik.gdsc.domain.study.domain.AchievementType;
import com.gdschongik.gdsc.domain.study.domain.StudyAchievement;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
Expand All @@ -21,6 +22,18 @@ public List<StudyAchievement> findByStudyIdAndMemberIds(Long studyId, List<Long>
.fetch();
}

@Override
public void deleteByStudyAndAchievementTypeAndMemberIds(
Long studyId, AchievementType achievementType, List<Long> memberIds) {
queryFactory
.delete(studyAchievement)
.where(
eqStudyId(studyId),
studyAchievement.achievementType.eq(achievementType),
studyAchievement.student.id.in(memberIds))
.execute();
}

private BooleanExpression eqStudyId(Long studyId) {
return studyId != null ? studyAchievement.study.id.eq(studyId) : null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.gdschongik.gdsc.domain.study.dao;

import com.gdschongik.gdsc.domain.member.domain.Member;
import com.gdschongik.gdsc.domain.study.domain.StudyAchievement;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;

public interface StudyAchievementRepository
extends JpaRepository<StudyAchievement, Long>, StudyAchievementCustomRepository {}
extends JpaRepository<StudyAchievement, Long>, StudyAchievementCustomRepository {

List<StudyAchievement> findAllByStudent(Member student);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.gdschongik.gdsc.domain.study.dao;

import java.util.List;

public interface StudyHistoryCustomRepository {

long countByStudyIdAndStudentIds(Long studyId, List<Long> studentIds);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.gdschongik.gdsc.domain.study.dao;

import static com.gdschongik.gdsc.domain.study.domain.QStudyHistory.*;

import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class StudyHistoryCustomRepositoryImpl implements StudyHistoryCustomRepository {

private final JPAQueryFactory queryFactory;

@Override
public long countByStudyIdAndStudentIds(Long studyId, List<Long> studentIds) {
return (long) queryFactory
.select(studyHistory.count())
.from(studyHistory)
.where(eqStudyId(studyId), studyHistory.student.id.in(studentIds))
.fetchOne();
}

private BooleanExpression eqStudyId(Long studyId) {
return studyHistory.study.id.eq(studyId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

public interface StudyHistoryRepository extends JpaRepository<StudyHistory, Long> {
public interface StudyHistoryRepository extends JpaRepository<StudyHistory, Long>, StudyHistoryCustomRepository {

List<StudyHistory> findAllByStudent(Member member);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,10 @@ public void validateUpdateRepository(
throw new CustomException(STUDY_HISTORY_REPOSITORY_NOT_UPDATABLE_OWNER_MISMATCH);
}
}

public void validateAppliedToStudy(long countStudyHistory, int studentCount) {
if (countStudyHistory != studentCount) {
throw new CustomException(STUDY_HISTORY_NOT_APPLIED_STUDENT_EXISTS);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.gdschongik.gdsc.domain.study.dto.request;

import com.gdschongik.gdsc.domain.study.domain.AchievementType;
import java.util.List;

public record OutstandingStudentRequest(List<Long> studentIds, AchievementType achievementType) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.gdschongik.gdsc.domain.study.dto.response;

import com.gdschongik.gdsc.domain.common.model.SemesterType;
import com.gdschongik.gdsc.domain.study.domain.AchievementType;
import com.gdschongik.gdsc.domain.study.domain.StudyHistory;
import com.gdschongik.gdsc.domain.study.domain.StudyHistoryStatus;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;

public record StudentMyCompleteStudyResponse(
Long studyId,
@Schema(description = "학년도") Integer academicYear,
@Schema(description = "학기") SemesterType semesterType,
@Schema(description = "이름") String title,
@Schema(description = "종류") String studyType,
@Schema(description = "상세설명 노션 링크") String notionLink,
@Schema(description = "한 줄 소개") String introduction,
@Schema(description = "멘토 이름") String mentorName,
@Schema(description = "총 주차수") Long totalWeek,
@Schema(description = "수료 여부") StudyHistoryStatus studyHistoryStatus,
@Schema(description = "우수 스터디원 선정 내역") List<AchievementType> achievements) {

public static StudentMyCompleteStudyResponse of(StudyHistory studyHistory, List<AchievementType> achievements) {
return new StudentMyCompleteStudyResponse(
studyHistory.getStudy().getId(),
studyHistory.getStudy().getAcademicYear(),
studyHistory.getStudy().getSemesterType(),
studyHistory.getStudy().getTitle(),
studyHistory.getStudy().getStudyType().getValue(),
studyHistory.getStudy().getNotionLink(),
studyHistory.getStudy().getIntroduction(),
studyHistory.getStudy().getMentor().getName(),
studyHistory.getStudy().getTotalWeek(),
studyHistory.getStudyHistoryStatus(),
achievements);
}
}
Loading

0 comments on commit ff6af2b

Please sign in to comment.