Skip to content

Commit

Permalink
feat: 스터디 공지 생성,수정,삭제 API 추가 (#641)
Browse files Browse the repository at this point in the history
* feat: 스터디 공지 생성,수정,삭제 API 추가

* refactor: spotless적용

* style: 스터디 명단조회 API와 스터디 공지 생성 API 코드 순서 변경

* refactor: 스터디 공지 테이블명 StudyNotification에서 StudyAnnouncement로 수정

* refactor: 스터디 공지 수정 PUT메소드로 변경

* refactor: 스터디 공지 생성 수정 리퀘스트명 변경
  • Loading branch information
seulgi99 authored Aug 18, 2024
1 parent e48c72d commit e242f91
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 7 deletions.
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

0 comments on commit e242f91

Please sign in to comment.