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: 스터디 공지 생성,수정,삭제 API 추가 #641

Merged
merged 7 commits into from
Aug 18, 2024
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
package com.gdschongik.gdsc.domain.study.api;

import com.gdschongik.gdsc.domain.study.application.MentorStudyService;
import com.gdschongik.gdsc.domain.study.dto.request.StudyAnnouncementCreateUpdateRequest;
import com.gdschongik.gdsc.domain.study.dto.response.MentorStudyResponse;
import com.gdschongik.gdsc.domain.study.dto.response.StudyStudentResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -34,4 +40,27 @@ public ResponseEntity<List<StudyStudentResponse>> getStudyStudents(@PathVariable
List<StudyStudentResponse> response = mentorStudyService.getStudyStudents(studyId);
return ResponseEntity.ok(response);
}

@Operation(summary = "스터디 공지 생성", description = "스터디의 공지사항을 생성합니다.")
@PostMapping("/{studyId}/announcements")
public ResponseEntity<Void> createStudyAnnouncement(
@PathVariable Long studyId, @Valid @RequestBody StudyAnnouncementCreateUpdateRequest request) {
mentorStudyService.createStudyAnnouncement(studyId, request);
return ResponseEntity.ok().build();
}

@Operation(summary = "스터디 공지 수정", description = "스터디의 공지사항을 수정합니다.")
@PutMapping("/announcements/{studyAnnouncementId}")
public ResponseEntity<Void> updateStudyAnnouncement(
@PathVariable Long studyAnnouncementId, @Valid @RequestBody StudyAnnouncementCreateUpdateRequest request) {
mentorStudyService.updateStudyAnnouncement(studyAnnouncementId, request);
return ResponseEntity.ok().build();
}

@Operation(summary = "스터디 공지 삭제", description = "스터디의 공지사항을 삭제합니다.")
@DeleteMapping("/announcements/{studyAnnouncementId}")
public ResponseEntity<Void> deleteStudyAnnouncement(@PathVariable Long studyAnnouncementId) {
mentorStudyService.deleteStudyAnnouncement(studyAnnouncementId);
return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.gdschongik.gdsc.domain.study.api;

import com.gdschongik.gdsc.domain.study.application.StudentStudyService;
import com.gdschongik.gdsc.domain.study.dto.request.StudyAttendRequest;
import com.gdschongik.gdsc.domain.study.dto.request.StudyAttendCreateRequest;
import com.gdschongik.gdsc.domain.study.dto.response.StudyResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand Down Expand Up @@ -49,7 +49,7 @@ public ResponseEntity<Void> cancelStudyApply(@PathVariable Long studyId) {
@Operation(summary = "스터디 출석체크", description = "스터디에 출석체크합니다. 현재 진행중인 스터디 회차에 출석체크해야 하며, 중복출석체크할 수 없습니다.")
@PostMapping("/study-details/{studyDetailId}/attend")
public ResponseEntity<Void> attend(
@PathVariable Long studyDetailId, @Valid @RequestBody StudyAttendRequest request) {
@PathVariable Long studyDetailId, @Valid @RequestBody StudyAttendCreateRequest request) {
studentStudyService.attend(studyDetailId, request);
return ResponseEntity.ok().build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
package com.gdschongik.gdsc.domain.study.application;

import com.gdschongik.gdsc.domain.member.domain.Member;
import com.gdschongik.gdsc.domain.study.dao.StudyAnnouncementRepository;
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.StudyAnnouncement;
import com.gdschongik.gdsc.domain.study.domain.StudyHistory;
import com.gdschongik.gdsc.domain.study.domain.StudyValidator;
import com.gdschongik.gdsc.domain.study.dto.request.StudyAnnouncementCreateUpdateRequest;
import com.gdschongik.gdsc.domain.study.dto.response.MentorStudyResponse;
import com.gdschongik.gdsc.domain.study.dto.response.StudyStudentResponse;
import com.gdschongik.gdsc.global.exception.CustomException;
import com.gdschongik.gdsc.global.exception.ErrorCode;
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 MentorStudyService {

private final MemberUtil memberUtil;
private final StudyRepository studyRepository;
private final StudyAnnouncementRepository studyAnnouncementRepository;
private final StudyHistoryRepository studyHistoryRepository;
private final StudyValidator studyValidator;

Expand All @@ -43,4 +49,45 @@ public List<StudyStudentResponse> getStudyStudents(Long studyId) {

return studyHistories.stream().map(StudyStudentResponse::from).toList();
}

@Transactional
public void createStudyAnnouncement(Long studyId, StudyAnnouncementCreateUpdateRequest request) {
Member currentMember = memberUtil.getCurrentMember();
final Study study = studyRepository.getById(studyId);

studyValidator.validateStudyMentor(currentMember, study);

StudyAnnouncement studyAnnouncement =
StudyAnnouncement.createStudyAnnouncement(study, request.title(), request.link());
studyAnnouncementRepository.save(studyAnnouncement);

log.info("[MentorStudyService] 스터디 공지 생성: studyAnnouncementId={}", studyAnnouncement.getId());
}

@Transactional
public void updateStudyAnnouncement(Long studyAnnouncementId, StudyAnnouncementCreateUpdateRequest request) {
Member currentMember = memberUtil.getCurrentMember();
final StudyAnnouncement studyAnnouncement = studyAnnouncementRepository.getById(studyAnnouncementId);
Study study = studyAnnouncement.getStudy();

studyValidator.validateStudyMentor(currentMember, study);

studyAnnouncement.update(request.title(), request.link());
studyAnnouncementRepository.save(studyAnnouncement);

log.info("[MentorStudyService] 스터디 공지 수정 완료: studyAnnouncementId={}", studyAnnouncement.getId());
}

@Transactional
public void deleteStudyAnnouncement(Long studyAnnouncementId) {
Member currentMember = memberUtil.getCurrentMember();
final StudyAnnouncement studyAnnouncement = studyAnnouncementRepository.getById(studyAnnouncementId);
Study study = studyAnnouncement.getStudy();

studyValidator.validateStudyMentor(currentMember, study);

studyAnnouncementRepository.delete(studyAnnouncement);

log.info("[MentorStudyService] 스터디 공지 삭제 완료: studyAnnouncementId={}", studyAnnouncement.getId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import com.gdschongik.gdsc.domain.study.domain.*;
import com.gdschongik.gdsc.domain.study.domain.Attendance;
import com.gdschongik.gdsc.domain.study.domain.AttendanceValidator;
import com.gdschongik.gdsc.domain.study.dto.request.StudyAttendRequest;
import com.gdschongik.gdsc.domain.study.dto.request.StudyAttendCreateRequest;
import com.gdschongik.gdsc.domain.study.dto.response.StudyResponse;
import com.gdschongik.gdsc.global.exception.CustomException;
import com.gdschongik.gdsc.global.util.MemberUtil;
Expand Down Expand Up @@ -73,7 +73,7 @@ public void cancelStudyApply(Long studyId) {
}

@Transactional
public void attend(Long studyDetailId, StudyAttendRequest request) {
public void attend(Long studyDetailId, StudyAttendCreateRequest request) {
final StudyDetail studyDetail = studyDetailRepository
.findById(studyDetailId)
.orElseThrow(() -> new CustomException(STUDY_DETAIL_NOT_FOUND));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.gdschongik.gdsc.domain.study.dao;

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

import com.gdschongik.gdsc.domain.study.domain.StudyAnnouncement;
import com.gdschongik.gdsc.global.exception.CustomException;
import org.springframework.data.jpa.repository.JpaRepository;

public interface StudyAnnouncementRepository extends JpaRepository<StudyAnnouncement, Long> {

default StudyAnnouncement getById(Long id) {
return findById(id).orElseThrow(() -> new CustomException(STUDY_ANNOUNCEMENT_NOT_FOUND));
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package com.gdschongik.gdsc.domain.study.dao;

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

import com.gdschongik.gdsc.domain.member.domain.Member;
import com.gdschongik.gdsc.domain.study.domain.Study;
import com.gdschongik.gdsc.global.exception.CustomException;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;

public interface StudyRepository extends JpaRepository<Study, Long> {

default Study getById(Long id) {
return findById(id).orElseThrow(() -> new CustomException(STUDY_NOT_FOUND));
}

List<Study> findAllByMentor(Member mentor);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class StudyNotification extends BaseEntity {
public class StudyAnnouncement extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "study_notification_id")
@Column(name = "study_announcement_id")
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
Expand All @@ -31,4 +32,20 @@ public class StudyNotification extends BaseEntity {

@Column(columnDefinition = "TEXT")
private String link;

@Builder(access = AccessLevel.PRIVATE)
public StudyAnnouncement(Study study, String title, String link) {
this.study = study;
this.title = title;
this.link = link;
}

public static StudyAnnouncement createStudyAnnouncement(Study study, String title, String link) {
return StudyAnnouncement.builder().study(study).title(title).link(link).build();
}

public void update(String title, String link) {
this.title = title;
this.link = link;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.gdschongik.gdsc.domain.study.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;

public record StudyAnnouncementCreateUpdateRequest(
@NotBlank(message = "공지제목이 비었습니다.") @Schema(description = "공지제목") String title,
@Schema(description = "공지링크") String link) {}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;

public record StudyAttendRequest(
public record StudyAttendCreateRequest(
@NotBlank
@Pattern(regexp = ATTENDANCE_NUMBER, message = "출석번호는 " + ATTENDANCE_NUMBER + " 형식이어야 합니다.")
@Schema(description = "출석번호")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ public enum ErrorCode {
HttpStatus.CONFLICT, "이미 제출한 과제가 있으므로 레포지토리를 수정할 수 없습니다."),
STUDY_HISTORY_REPOSITORY_NOT_UPDATABLE_OWNER_MISMATCH(HttpStatus.CONFLICT, "레포지토리 소유자가 현재 멤버와 다릅니다."),

// StudyAnnouncement
STUDY_ANNOUNCEMENT_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 스터디 공지입니다."),

// Attendance
ATTENDANCE_DATE_INVALID(HttpStatus.CONFLICT, "강의일이 아니면 출석체크할 수 없습니다."),
ATTENDANCE_NUMBER_MISMATCH(HttpStatus.CONFLICT, "출석번호가 일치하지 않습니다."),
Expand Down