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

HF-87: Event Service, Controller 구현 및 테스트 코드 작성 + 글로벌 예외 처리 추가 #13

Merged
merged 8 commits into from
Jul 17, 2024
65 changes: 65 additions & 0 deletions src/main/java/gible/domain/event/controller/EventController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package gible.domain.event.controller;

import gible.domain.event.dto.EventDetailRes;
import gible.domain.event.dto.EventReq;
import gible.domain.event.dto.EventSummaryRes;
import gible.domain.event.service.EventService;
import gible.global.util.api.ApiUtil;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.UUID;

@RequiredArgsConstructor
@RequestMapping("/event")
@RestController
public class EventController {

private final EventService eventService;

/* 이벤트 등록 */
@PostMapping("/upload")
public ResponseEntity<?> saveEvent(@Valid @RequestBody EventReq eventReq) {

eventService.saveEvent(eventReq);
return ResponseEntity.created(null).body(ApiUtil.from("이벤트 작성 성공."));
}

/* 이벤트 목록 조회 */
@GetMapping
public Page<EventSummaryRes> getAllEvents(
@PageableDefault(sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable) {

return eventService.getAllEvents(pageable);
}

/* 특정 이벤트 조회 */
@GetMapping("/{eventId}")
public EventDetailRes getEvent(@PathVariable UUID eventId) {

return eventService.getEvent(eventId);
}

/* 이벤트 수정 */
@PutMapping("/upload/{eventId}")
public ResponseEntity<?> updateEvent(@Valid @RequestBody EventReq updateEventReq,
@PathVariable UUID eventId) {

eventService.updateEvent(updateEventReq, eventId);
return ResponseEntity.ok(ApiUtil.from("이벤트 수정 성공."));
}

/* 이벤트 삭제 */
@DeleteMapping("/{eventId}")
public ResponseEntity<?> deleteEvent(@PathVariable UUID eventId) {

eventService.deleteEvent(eventId);
return ResponseEntity.ok(ApiUtil.from("이벤트 삭제 성공."));
}
}
Empty file.
3 changes: 3 additions & 0 deletions src/main/java/gible/domain/event/dto/EventReq.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package gible.domain.event.dto;

import gible.domain.event.entity.Event;
import jakarta.validation.constraints.NotBlank;

public record EventReq(
@NotBlank(message = "제목은 필수 작성 항목입니다.")
String title,
@NotBlank(message = "내용은 필수 작성 항목입니다.")
String content,
String imageUrl
) {
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/gible/domain/event/entity/Event.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
package gible.domain.event.entity;

import gible.domain.event.dto.EventReq;
import gible.domain.participate.entity.Participate;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EntityListeners(AuditingEntityListener.class)
@Entity
public class Event {

Expand All @@ -30,6 +35,10 @@ public class Event {
@Column(name = "image_url")
private String imageUrl;

@Column(name = "created_at")
@CreatedDate
private LocalDateTime createdAt;

@OneToMany(mappedBy = "event", cascade = CascadeType.REMOVE)
private List<Participate> participates = new ArrayList<>();

Expand All @@ -39,4 +48,11 @@ public Event(String title, String content, String imageUrl) {
this.content = content;
this.imageUrl = imageUrl;
}

/* 이벤트 업데이트 */
public void updateEvent(EventReq eventReq) {
this.title = eventReq.title();
this.content = eventReq.content();
this.imageUrl = eventReq.imageUrl();
}
}
64 changes: 64 additions & 0 deletions src/main/java/gible/domain/event/service/EventService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package gible.domain.event.service;

import gible.domain.event.dto.EventDetailRes;
import gible.domain.event.dto.EventReq;
import gible.domain.event.dto.EventSummaryRes;
import gible.domain.event.entity.Event;
import gible.domain.event.repository.EventRepository;
import gible.exception.CustomException;
import gible.exception.error.ErrorType;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.UUID;

@RequiredArgsConstructor
@Service
public class EventService {

private final EventRepository eventRepository;

/* 이벤트 생성 */
@Transactional
public void saveEvent(EventReq eventReq) {

eventRepository.save(EventReq.toEntity(eventReq));
}

/* 이벤트 목록 조회 */
@Transactional(readOnly = true)
public Page<EventSummaryRes> getAllEvents(Pageable pageable) {

Page<Event> events = eventRepository.findAll(pageable);
return events.map(EventSummaryRes::fromEntity);
}

/* 특정 이벤트 조회 */
@Transactional(readOnly = true)
public EventDetailRes getEvent(UUID eventId) {

Event event = eventRepository.findById(eventId).orElseThrow(() ->
new CustomException(ErrorType.EVENT_NOT_FOUND));
return EventDetailRes.fromEntity(event);
}

/* 이벤트 수정 */
@Transactional
public void updateEvent(EventReq updateEventReq, UUID eventId) {

Event event = eventRepository.findById(eventId).orElseThrow(() ->
new CustomException(ErrorType.EVENT_NOT_FOUND));

event.updateEvent(updateEventReq);
}

/* 이벤트 삭제 */
@Transactional
public void deleteEvent(UUID eventId) {

eventRepository.deleteById(eventId);
}
}
Empty file.
26 changes: 26 additions & 0 deletions src/main/java/gible/exception/GlobalExceptionHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,18 @@
import gible.exception.error.ErrorType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

import static gible.exception.error.ErrorType.INTERNAL_SERVER_ERROR;

@RestControllerAdvice
public class GlobalExceptionHandler {
private final Logger log = LoggerFactory.getLogger(getClass());
Expand All @@ -19,4 +27,22 @@ public ResponseEntity<?> handle(CustomException ex){
log.warn("Error occurred : [errorCode={}, message={}]", errortype.getStatus(), errortype.getMessage());
return ResponseEntity.status(errortype.getStatus()).body(ErrorDto.of(errortype.getStatus(), errortype.getMessage()));
}

/* 일반 예외 처리 */
@ExceptionHandler
protected ResponseEntity customServerException(Exception ex) {
ErrorDto error = new ErrorDto(INTERNAL_SERVER_ERROR.getStatus(), INTERNAL_SERVER_ERROR.getMessage());
log.warn("Error occurred : [errorCode={}, message={}]", error.status(), error.message());
return ResponseEntity.status(error.status()).body(error);
}

@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<Map<String, String>> handleValidationException(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
for (FieldError error : ex.getBindingResult().getFieldErrors()) {
errors.put(error.getField(), error.getDefaultMessage());
}
log.error("Error occurred : [message={}]", errors);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errors);
}
}
Empty file.
Loading