Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 14 additions & 11 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ plugins {
id 'java'
}

javadoc.options.encoding = 'UTF-8'

tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
group = 'com.springboot'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
compileOnly {
Expand All @@ -28,12 +26,17 @@ dependencies {
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.mapstruct:mapstruct:1.5.1.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.1.Final'
implementation 'org.mapstruct:mapstruct:1.4.2.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'com.google.code.gson:gson'
// (2) JWT 기능을 위한 jjwt 라이브러리
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
}

tasks.named('javadoc') {
source = sourceSets.main.allJava
options.memberLevel = JavadocMemberLevel.PRIVATE
destinationDir = file("build/docs/javadoc")
tasks.named('test') {
useJUnitPlatform()
}
4 changes: 4 additions & 0 deletions src/main/java/com/springboot/SpringStartApplication.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package com.springboot;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@EnableJpaAuditing
@SpringBootApplication
public class SpringStartApplication {
public static void main(String[] args) {
SpringApplication.run(SpringStartApplication.class, args);
}

}
89 changes: 89 additions & 0 deletions src/main/java/com/springboot/advice/GlobalExceptionAdvice.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.springboot.advice;

import com.springboot.exception.BusinessLogicException;
import com.springboot.response.ErrorResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolationException;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionAdvice {
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleMethodArgumentNotValidException(
MethodArgumentNotValidException e){
final ErrorResponse response = ErrorResponse.of(e.getBindingResult());

return response;
}
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleConstraintViolationException(
ConstraintViolationException e) {
final ErrorResponse response = ErrorResponse.of(e.getConstraintViolations());

return response;
}

@ExceptionHandler
public ResponseEntity handleBusinessLogicException(BusinessLogicException e) {
final ErrorResponse response = ErrorResponse.of(e.getExceptionCode());

return new ResponseEntity<>(response, HttpStatus.valueOf(e.getExceptionCode()
.getStatus()));
}

@ExceptionHandler
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
public ErrorResponse handleHttpRequestMethodNotSupportedException(
HttpRequestMethodNotSupportedException e) {

final ErrorResponse response = ErrorResponse.of(HttpStatus.METHOD_NOT_ALLOWED);

return response;
}

@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleHttpMessageNotReadableException(
HttpMessageNotReadableException e) {

final ErrorResponse response = ErrorResponse.of(HttpStatus.BAD_REQUEST,
"Required request body is missing");

return response;
}

@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleMissingServletRequestParameterException(
MissingServletRequestParameterException e) {

final ErrorResponse response = ErrorResponse.of(HttpStatus.BAD_REQUEST,
e.getMessage());

return response;
}

@ExceptionHandler
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse handleException(Exception e) {
log.error("# handle Exception", e);
// TODO 애플리케이션의 에러는 에러 로그를 로그에 기록하고, 관리자에게 이메일이나 카카오 톡,
// 슬랙 등으로 알려주는 로직이 있는게 좋습니다.

final ErrorResponse response = ErrorResponse.of(HttpStatus.INTERNAL_SERVER_ERROR);

return response;
}
}
100 changes: 100 additions & 0 deletions src/main/java/com/springboot/answer/controller/AnswerController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package com.springboot.answer.controller;


import com.springboot.answer.dto.AnswerPatchDto;
import com.springboot.answer.dto.AnswerPostDto;
import com.springboot.answer.entity.Answer;
import com.springboot.answer.mapper.AnswerMapper;
import com.springboot.answer.service.AnswerService;
import com.springboot.dto.MultiResponseDto;
import com.springboot.dto.SingleResponseDto;
import com.springboot.member.entity.Member;
import com.springboot.utils.UriCreator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.Positive;
import java.net.URI;
import java.util.Collection;
import java.util.List;

@RestController
@RequestMapping("/v11/answers")
@Validated
public class AnswerController {
private final static String ANSWER_DEFAULT_URL = "/v11/answers";
private final AnswerService answerService;
private final AnswerMapper mapper;


public AnswerController(AnswerService answerService, AnswerMapper mapper) {
this.answerService = answerService;
this.mapper = mapper;
}

@PostMapping
public ResponseEntity postAnswer(@Valid @RequestBody AnswerPostDto requestBody,
Authentication authentication){
String email = (String) authentication.getPrincipal();


Answer answer = mapper.answerPostDtoToAnswer(requestBody);
Answer createdAnswer = answerService.createAnswer(answer,email);
URI location = UriCreator.createUri(ANSWER_DEFAULT_URL, createdAnswer.getAnswerId());
return ResponseEntity.created(location).build();
}

@PatchMapping("/{answer-id}")
public ResponseEntity patchAnswer(@PathVariable("answer-id") @Positive long answerId,
@Valid @RequestBody AnswerPatchDto requestBody,
Authentication authentication){
requestBody.setAnswerId(answerId);
String email = (String) authentication.getPrincipal();

Answer answer = answerService.updateAnswer(mapper.answerPatchDtoToAnswer(requestBody),email);

return new ResponseEntity(
new SingleResponseDto<>(mapper.answerToAnswerResponseDto(answer)),HttpStatus.OK
);
}

@GetMapping("/{answer-id}")
public ResponseEntity getAnswer(@PathVariable("answer-id") @Positive long answerId){
Answer answer = answerService.findAnswer(answerId);

return new ResponseEntity(
new SingleResponseDto<>(mapper.answerToAnswerResponseDto(answer)),HttpStatus.OK
);
}

@GetMapping
public ResponseEntity getAnswers(@Positive @RequestParam int page,
@Positive @RequestParam int size) {
Page<Answer> pageAnswers = answerService.findAnswers(page - 1, size);
List<Answer> answers = pageAnswers.getContent();
return new ResponseEntity<>(
new MultiResponseDto<>(mapper.answersToAnswerResponseDtos(answers),
pageAnswers),
HttpStatus.OK);
}

@DeleteMapping("/{answer-id}")
public ResponseEntity deleteAnswer(@PathVariable("answer-id") @Positive long answerId,
Authentication authentication){

String email = (String) authentication.getPrincipal();
answerService.deleteAnswer(answerId, email);

return new ResponseEntity(HttpStatus.NO_CONTENT);
}



}
17 changes: 17 additions & 0 deletions src/main/java/com/springboot/answer/dto/AnswerPatchDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.springboot.answer.dto;

import lombok.Getter;

import javax.validation.constraints.Positive;

@Getter
public class AnswerPatchDto {
private long answerId;

private String content;

public void setAnswerId(long answerId){
this.answerId = answerId;
}

}
16 changes: 16 additions & 0 deletions src/main/java/com/springboot/answer/dto/AnswerPostDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.springboot.answer.dto;


import lombok.Getter;

import javax.validation.constraints.Positive;

@Getter
public class AnswerPostDto {

@Positive
private long boardId;

private String content;

}
30 changes: 30 additions & 0 deletions src/main/java/com/springboot/answer/dto/AnswerResponseDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.springboot.answer.dto;


import com.springboot.board.entity.Board;
import com.springboot.member.entity.Member;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDateTime;

@Getter
@Setter
public class AnswerResponseDto {
private long answerId;

@Setter(AccessLevel.NONE)
private long memberId;
private long boardId;
private String content;
private LocalDateTime createdAt;

public void setMember(Member member){
this.memberId = member.getMemberId();
}
public void setBoard(Board board){
this.boardId = board.getBoardId();
}

}
46 changes: 46 additions & 0 deletions src/main/java/com/springboot/answer/entity/Answer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.springboot.answer.entity;


import com.springboot.board.entity.Board;
import com.springboot.member.entity.Member;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.persistence.*;
import java.time.LocalDateTime;

@Getter
@Setter
@NoArgsConstructor
@Entity
public class Answer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long answerId;

@Column(nullable = false)
private String content = "";

@Column(nullable = false)
private LocalDateTime createdAt = LocalDateTime.now();

@Column(nullable = false, name = "LAST_MODIFIED_AT")
private LocalDateTime modifiedAt = LocalDateTime.now();

@OneToOne
@JoinColumn(name = "BOARD_ID")
private Board board;

@ManyToOne
@JoinColumn(name = "MEMBER_ID")
private Member member;

public void setBoard(Board board){
this.board = board;
if(board.getAnswer() != this){
board.setAnswer(this);
}
}

}
27 changes: 27 additions & 0 deletions src/main/java/com/springboot/answer/mapper/AnswerMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.springboot.answer.mapper;


import com.springboot.answer.dto.AnswerPatchDto;
import com.springboot.answer.dto.AnswerPostDto;
import com.springboot.answer.dto.AnswerResponseDto;
import com.springboot.answer.entity.Answer;
import com.springboot.board.entity.Board;
import com.springboot.member.entity.Member;
import org.mapstruct.Mapper;

import java.util.List;

@Mapper(componentModel = "spring")
public interface AnswerMapper {
default Answer answerPostDtoToAnswer(AnswerPostDto answerPostDto){
Answer answer = new Answer();
Board board = new Board();
board.setBoardId(answerPostDto.getBoardId());
answer.setBoard(board);
answer.setContent(answerPostDto.getContent());
return answer;
}
Answer answerPatchDtoToAnswer(AnswerPatchDto answerPatchDto);
AnswerResponseDto answerToAnswerResponseDto(Answer answer);
List<AnswerResponseDto> answersToAnswerResponseDtos(List<Answer> answers);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.springboot.answer.repository;

import com.springboot.answer.entity.Answer;
import org.springframework.data.jpa.repository.JpaRepository;

public interface AnswerRepository extends JpaRepository<Answer, Long> {
}
Loading