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

Study Controller, Service 추가 #11

Merged
merged 22 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
22dd9b1
feat: 스터디 생성 기능 추가
ybkang1108 Jan 16, 2025
90ad73f
feat: StudyCreateResponse DTO 추가
ybkang1108 Jan 16, 2025
2ea0826
feat: 스터디 전체 목록 조회 기능 추가
ybkang1108 Jan 16, 2025
3310c20
feat: 스터디 정보 조회 기능 추가
ybkang1108 Jan 16, 2025
b700946
feat: 스터디 삭제 기능 추가
ybkang1108 Jan 16, 2025
e99cab5
refactor: delete, detail response dto수정 및 cascade 설정
ybkang1108 Jan 17, 2025
52858dc
feat: 스터디 수정 기능 추가
ybkang1108 Jan 17, 2025
33b6e77
refactor: response에 메시지 제거
ybkang1108 Jan 17, 2025
baf1a6e
refactor: dto를 record로 변경
ybkang1108 Jan 21, 2025
e45b8ad
feat: exception 추가
ybkang1108 Jan 21, 2025
a412bbf
refactor: curriculum 수정
ybkang1108 Jan 22, 2025
2a4f89f
feat: day에 create 추가
ybkang1108 Jan 22, 2025
d864b11
refactor: record로 변경
ybkang1108 Jan 22, 2025
eb57a8f
feat: custom exception 추가
ybkang1108 Jan 22, 2025
6633caf
refactor: record로 변경
ybkang1108 Jan 22, 2025
047112b
feat: study 도메인에 create, update 추가
ybkang1108 Jan 22, 2025
2ce3d78
refactor: study service 수정
ybkang1108 Jan 22, 2025
889051a
refactor: study controller 수정
ybkang1108 Jan 22, 2025
ee5864b
refactor: springdoc 버전 수정
ybkang1108 Jan 23, 2025
ff5eb99
refactor: 불필요한 생성자 제거
ybkang1108 Jan 24, 2025
31ad47d
refactor: create, update 메소드 도메인으로 이동
ybkang1108 Jan 24, 2025
872757d
fix: 잘못된 로직 수정
ybkang1108 Jan 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0'
implementation 'org.postgresql:postgresql'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import lombok.Getter;
import lombok.Setter;
import lombok.*;

@Entity
@Getter
@Setter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Curriculum {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand All @@ -24,4 +25,8 @@ public class Curriculum {

private Integer week;
private String subject; // 해당 회차의 주제

public static Curriculum create(Study study, Integer week, String subject) {
ybkang1108 marked this conversation as resolved.
Show resolved Hide resolved
return Curriculum.builder().study(study).week(week).subject(subject).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.gdgoc.study_group.curriculum.dto;

import com.gdgoc.study_group.curriculum.domain.Curriculum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;

public record CurriculumDTO(
@NotBlank(message = "스터디 주차를 입력해 주세요.") @Schema(description = "스터디 주차") Integer week,
@NotBlank(message = "주제를 입력해 주세요.") @Schema(description = "해당 주차의 주제") String subject) {

public static CurriculumDTO from(Curriculum curriculum) {
return new CurriculumDTO(curriculum.getWeek(), curriculum.getSubject());
}
}
14 changes: 11 additions & 3 deletions src/main/java/com/gdgoc/study_group/day/domain/Day.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.gdgoc.study_group.day.domain;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.gdgoc.study_group.study.domain.Study;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
Expand All @@ -8,12 +9,13 @@
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import java.time.LocalTime;
import lombok.Getter;
import lombok.Setter;
import lombok.*;

@Entity
@Getter
@Setter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Day {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand All @@ -24,5 +26,11 @@ public class Day {
private Study study;

private String day;

@JsonFormat(pattern = "HH:mm")
private LocalTime startTime;

public static Day create(Study study, String day, LocalTime startTime) {
return Day.builder().study(study).day(day).startTime(startTime).build();
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/gdgoc/study_group/day/dto/DayDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.gdgoc.study_group.day.dto;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.gdgoc.study_group.day.domain.Day;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import java.time.LocalTime;

public record DayDTO(
@NotBlank(message = "스터디 요일을 입력해 주세요.") @Schema(description = "스터디 요일") String day,
@JsonFormat(pattern = "HH:mm") // "startTime": "14:00" 형식으로 입력
@NotBlank(message = "스터디 시간을 입력해 주세요")
@Schema(description = "스터디 시간")
LocalTime startTime) {

public static DayDTO from(Day day) {
return new DayDTO(day.getDay(), day.getStartTime());
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/gdgoc/study_group/exception/CustomException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.gdgoc.study_group.exception;

import lombok.Getter;

@Getter
public class CustomException extends RuntimeException {

private final ErrorCode errorCode;

public CustomException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}

public CustomException(ErrorCode errorCode, String errorMessage) {
super(errorMessage);
this.errorCode = errorCode;
}
}
18 changes: 18 additions & 0 deletions src/main/java/com/gdgoc/study_group/exception/ErrorCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.gdgoc.study_group.exception;

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum ErrorCode {
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "내부 서버 에러가 발생했습니다."),

// study
STUDY_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 스터디입니다."),
;

private final HttpStatus status;
private final String message;
}
16 changes: 16 additions & 0 deletions src/main/java/com/gdgoc/study_group/exception/ErrorResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.gdgoc.study_group.exception;

public record ErrorResponse(String errorCodeName, String errorMessage) {

public static ErrorResponse of(ErrorCode errorCode) {
return new ErrorResponse(errorCode.name(), errorCode.getMessage());
}

public static ErrorResponse of(ErrorCode errorCode, String errorMessage) {
return new ErrorResponse(errorCode.name(), errorMessage);
}

public static ErrorResponse of(String errorCodeName, String errorMessage) {
return new ErrorResponse(errorCodeName, errorMessage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.gdgoc.study_group.exception;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

@ExceptionHandler(CustomException.class)
public ResponseEntity<ErrorResponse> handleCustomException(CustomException e) {
log.info("CustomException : {}", e.getMessage());
return ResponseEntity.status(e.getErrorCode().getStatus())
.body(ErrorResponse.of(e.getErrorCode()));
}

@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
log.error("INTERNAL_SERVER_ERROR : {}", e.getMessage());
return ResponseEntity.status(ErrorCode.INTERNAL_SERVER_ERROR.getStatus())
.body(ErrorResponse.of(ErrorCode.INTERNAL_SERVER_ERROR));
}
}
65 changes: 65 additions & 0 deletions src/main/java/com/gdgoc/study_group/study/api/StudyController.java
ybkang1108 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.gdgoc.study_group.study.api;

import com.gdgoc.study_group.study.application.LeaderStudyService;
import com.gdgoc.study_group.study.application.StudentStudyService;
import com.gdgoc.study_group.study.dto.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/studies")
@Tag(name = "Study", description = "스터디 API")
@RequiredArgsConstructor
public class StudyController {

public final StudentStudyService studentStudyService;
public final LeaderStudyService leaderStudyService;

@Operation(summary = "스터디 생성", description = "자율스터디를 생성합니다.")
@PostMapping()
public ResponseEntity<Long> createStudy(@RequestBody StudyCreateUpdateRequest request) {
Long studyId = studentStudyService.createStudy(request);

return ResponseEntity.ok(studyId);
}

@Operation(summary = "전체 스터디 조회", description = "모든 스터디를 조회합니다.")
@GetMapping()
public ResponseEntity<List<StudyResponse>> getStudyList() {
List<StudyResponse> studyList = studentStudyService.getAllStudies();

return ResponseEntity.status(HttpStatus.OK).body(studyList);
}

@Operation(summary = "개별 스터디 조회", description = "스터디 하나의 정보를 조회합니다.")
@GetMapping("/{studyId}")
public ResponseEntity<?> getStudyDetail(@PathVariable("studyId") Long studyId) {
ybkang1108 marked this conversation as resolved.
Show resolved Hide resolved
ybkang1108 marked this conversation as resolved.
Show resolved Hide resolved
StudyResponse studyDetail = studentStudyService.getStudyDetail(studyId);

return ResponseEntity.status(HttpStatus.OK).body(studyDetail);
}

@Operation(summary = "스터디 수정", description = "스터디 정보를 수정합니다. 스터디장만 수정할 수 있습니다.")
@PatchMapping("/{studyId}")
public ResponseEntity<StudyResponse> updateStudy(
@PathVariable("studyId") Long studyId, @RequestBody StudyCreateUpdateRequest updateRequest) {

leaderStudyService.updateStudy(studyId, updateRequest);

return ResponseEntity.ok().build();
}

@Operation(summary = "스터디 삭제", description = "스터디를 삭제합니다. 스터디장만 삭제할 수 있습니다.")
@DeleteMapping("/{studyId}")
public ResponseEntity<String> deleteStudy(@PathVariable("studyId") Long studyId) {

leaderStudyService.deleteStudy(studyId);

return ResponseEntity.status(HttpStatus.RESET_CONTENT).body("스터디가 삭제되었습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.gdgoc.study_group.study.application;

import static com.gdgoc.study_group.exception.ErrorCode.STUDY_NOT_FOUND;

import com.gdgoc.study_group.exception.CustomException;
import com.gdgoc.study_group.study.dao.StudyRepository;
import com.gdgoc.study_group.study.domain.Study;
import com.gdgoc.study_group.study.dto.StudyCreateUpdateRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class LeaderStudyService {

// TODO: 스터디장 권한 확인 필요

public final StudyRepository studyRepository;

/**
* 스터디 정보를 수정합니다.
*
* @param studyId 수정할 스터디 ID
* @param request 수정할 스터디의 정보
*/
@Transactional(readOnly = false)
public void updateStudy(Long studyId, StudyCreateUpdateRequest request) {
Study study =
studyRepository.findById(studyId).orElseThrow(() -> new CustomException(STUDY_NOT_FOUND));

study.update(
request.name(),
request.description(),
request.requirement(),
request.question(),
request.maxParticipants(),
request.studyStatus(),
request.curriculums(),
request.days());

studyRepository.save(study);
}

/**
* 스터디장 권한 확인 필요 스터디를 삭제합니다
*
* @param studyId 삭제할 스터디의 아이디
* @return 해당하는 스터디의 존재 여부
*/
@Transactional(readOnly = false)
public void deleteStudy(Long studyId) {
Study study =
studyRepository.findById(studyId).orElseThrow(() -> new CustomException(STUDY_NOT_FOUND));
studyRepository.delete(study);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.gdgoc.study_group.study.application;

import static com.gdgoc.study_group.exception.ErrorCode.STUDY_NOT_FOUND;

import com.gdgoc.study_group.exception.CustomException;
import com.gdgoc.study_group.study.dao.StudyRepository;
import com.gdgoc.study_group.study.domain.Study;
import com.gdgoc.study_group.study.dto.*;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class StudentStudyService {

public final StudyRepository studyRepository;

/**
* 스터디를 생성합니다.
*
* @param request 스터디 생성 DTO
* @return ResponseDTO 반환
*/
@Transactional(readOnly = false)
public Long createStudy(StudyCreateUpdateRequest request) {

Study study =
Study.create(
request.name(),
request.description(),
request.requirement(),
request.question(),
request.maxParticipants(),
request.studyStatus());

// TODO: 스터디를 생성한 유저를 스터디장으로 설정한 뒤 studyMembers에 추가

study.addInfo(request.curriculums(), request.days());
studyRepository.save(study);

return study.getId();
}

/**
* 스터디 전체 목록을 조회합니다.
*
* @return 스터디 전체 목록 리스트
*/
public List<StudyResponse> getAllStudies() {
return studyRepository.findAll().stream().map(StudyResponse::from).toList();
}

/**
* 스터디 상세 정보를 조회합니다.
*
* @param studyId 조회할 스터디 아이디
* @return 스터디 정보 반환
*/
public StudyResponse getStudyDetail(Long studyId) {
Study study =
studyRepository.findById(studyId).orElseThrow(() -> new CustomException(STUDY_NOT_FOUND));
return StudyResponse.from(study);
}
}
7 changes: 0 additions & 7 deletions src/main/java/com/gdgoc/study_group/study/domain/Status.java

This file was deleted.

Loading
Loading