Skip to content

Commit

Permalink
Merge pull request #15 from DDD-Community/develop
Browse files Browse the repository at this point in the history
2차 배포
  • Loading branch information
kikingki authored Aug 22, 2024
2 parents faee47f + 38c1998 commit 60fc3cc
Show file tree
Hide file tree
Showing 36 changed files with 1,158 additions and 109 deletions.
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ dependencies {
annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
}

def QDomains = []
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.dissonance.itit.common.converter;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.stereotype.Component;

import java.lang.reflect.Type;

@Component
public class MultipartJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {
/**
* Converter for support http request with header Content-Type: multipart/form-data
*/
public MultipartJackson2HttpMessageConverter(ObjectMapper objectMapper) {
super(objectMapper, MediaType.APPLICATION_OCTET_STREAM);
}

@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return false;
}

@Override
public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
return false;
}

@Override
protected boolean canWrite(MediaType mediaType) {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.dissonance.itit.common.exception;

import lombok.Getter;

@Getter
public class CustomException extends RuntimeException {
private final ErrorCode errorCode;

public CustomException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}
}
30 changes: 30 additions & 0 deletions src/main/java/com/dissonance/itit/common/exception/ErrorCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.dissonance.itit.common.exception;

import org.springframework.http.HttpStatus;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum ErrorCode {
// 400
INVALID_PROVIDER(HttpStatus.BAD_REQUEST, "존재하지 않는 Provider입니다."),
INVALID_FILE_TYPE(HttpStatus.BAD_REQUEST, "파일 형식은 이미지만 가능합니다."),
INVALID_FILE_SIZE(HttpStatus.BAD_REQUEST, "파일 용량은 10MB를 넘을 수 없습니다."),
INVALID_DATE_FORMAT(HttpStatus.BAD_REQUEST, "날짜 변환에 실패했습니다."),

// 404
NON_EXISTENT_USER_ID(HttpStatus.NOT_FOUND, "해당 id의 사용자가 존재하지 않습니다."),
NON_EXISTENT_EMAIL(HttpStatus.NOT_FOUND, "해당 email의 사용자가 존재하지 않습니다."),
IMAGE_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 id의 이미지가 존재하지 않습니다."),
NON_EXISTENT_CATEGORY_ID(HttpStatus.NOT_FOUND, "해당 id의 카테고리가 존재하지 않습니다."),
NON_EXISTENT_INFO_POST_ID(HttpStatus.NOT_FOUND, "해당 id의 공고 게시글이 존재하지 않습니다."),
REPORTED_INFO_POST_ID(HttpStatus.NOT_FOUND, "해당 id의 게시글은 신고 처리되었습니다."),

// 500
IO_EXCEPTION(HttpStatus.INTERNAL_SERVER_ERROR, "파일 입출력 에러");

private final HttpStatus httpStatus;
private final String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.dissonance.itit.common.exception;

public record ErrorResponse(
String errorCode,
String message
) {
public ErrorResponse(String errorCode, String message) {
this.errorCode = errorCode;
this.message = message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.dissonance.itit.common.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(CustomException.class)
protected ResponseEntity<ErrorResponse> handleCustomException(CustomException e) {
ErrorResponse response = new ErrorResponse(e.getErrorCode().name(), e.getMessage());
log.error("CustomException : {}", e.getMessage());
return new ResponseEntity<>(response, e.getErrorCode().getHttpStatus());
}

@ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleAllException(final Exception e) {
ErrorResponse response = new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.name(), e.getMessage());
log.error("handleAllException {}", e.getMessage());
return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.dissonance.itit.common.jwt.filter;

import com.dissonance.itit.common.exception.ErrorCode;
import com.dissonance.itit.common.exception.CustomException;
import com.dissonance.itit.common.jwt.util.JwtUtil;
import com.dissonance.itit.domain.entity.User;
import com.dissonance.itit.repository.UserRepository;
Expand Down Expand Up @@ -44,7 +46,7 @@ protected void doFilterInternal(HttpServletRequest request,
if (jwtUtil.verifyToken(accessToken)) {
// AccessToken의 payload에 있는 email로 user를 조회한다.
User findUser = userRepository.findByEmail(jwtUtil.getUid(accessToken))
.orElseThrow(() -> new IllegalArgumentException("해당 email의 사용자가 존재하지 않습니다."));
.orElseThrow(() -> new CustomException(ErrorCode.NON_EXISTENT_EMAIL));

// SecurityContext에 인증 객체를 등록한다.
Authentication auth = getAuthentication(findUser);
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/com/dissonance/itit/common/util/DateUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.dissonance.itit.common.util;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;

import com.dissonance.itit.common.exception.CustomException;
import com.dissonance.itit.common.exception.ErrorCode;

public class DateUtil {
public static LocalDate stringToDate(String dateString) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy년 M월 d일");
try {
return LocalDate.parse(dateString, formatter);
} catch (DateTimeParseException e) {
throw new CustomException(ErrorCode.INVALID_DATE_FORMAT);
}
}

public static String formatPeriod(LocalDate startDate, LocalDate endDate) {
return formatDate(startDate) + " ~ " + formatDate(endDate);
}

public static String formatDate(LocalDate date) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일");
return date.format(formatter);
}
}
31 changes: 31 additions & 0 deletions src/main/java/com/dissonance/itit/config/S3Config.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.dissonance.itit.config;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class S3Config {
@Value("${cloud.aws.credentials.access-key}")
private String accessKey;

@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;

@Value("${cloud.aws.region.static}")
private String region;

@Bean
public AmazonS3Client amazonS3Client() {
BasicAWSCredentials awsCreds = new BasicAWSCredentials(accessKey, secretKey);

return (AmazonS3Client) AmazonS3ClientBuilder.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(awsCreds))
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.dissonance.itit.dto.response.FeaturedPostRes;
import com.dissonance.itit.service.FeaturedPostService;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -17,6 +18,7 @@ public class FeaturedPostContorller {
private final FeaturedPostService featuredPostService;

@GetMapping
@Operation(summary = "추천 게시글 조회", description = "운영진 추천 게시글 5개를 조회합니다.")
public ResponseEntity<List<FeaturedPostRes>> getFeaturedPosts() {
List<FeaturedPostRes> featuredPostRes = featuredPostService.getFeaturedPost();
return ResponseEntity.ok(featuredPostRes);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.dissonance.itit.controller;

import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.dissonance.itit.domain.entity.User;
import com.dissonance.itit.dto.request.InfoPostReq;
import com.dissonance.itit.dto.response.InfoPostCreateRes;
import com.dissonance.itit.dto.response.InfoPostDetailRes;
import com.dissonance.itit.service.InfoPostService;
import com.dissonance.itit.service.UserService;

import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
@RequestMapping("/info-posts")
public class InfoPostController {
private final InfoPostService infoPostService;
private final UserService userService;

@PostMapping(consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_VALUE})
@Operation(summary = "공고 게시글 등록", description = "공고 게시글을 등록합니다.")
public ResponseEntity<InfoPostCreateRes> createInfoPost(@RequestPart MultipartFile imgFile,
@Valid @RequestPart InfoPostReq infoPostReq) {
User loginUser = userService.findById(1L); // TODO: 로그인 유저 정보 적용 예정
InfoPostCreateRes infoPostCreateRes = infoPostService.createInfoPost(imgFile, infoPostReq, loginUser);
return ResponseEntity.ok(infoPostCreateRes);
}

@GetMapping("/{infoPostId}")
@Operation(summary = "공고 게시글 조회", description = "공고 게시글을 상세 조회합니다.")
public ResponseEntity<InfoPostDetailRes> getInfoPostDetail(@PathVariable Long infoPostId) {
InfoPostDetailRes infoPostDetailRes = infoPostService.getInfoPostDetailById(infoPostId);
return ResponseEntity.ok(infoPostDetailRes);
}

@PatchMapping("/{infoPostId}/reports")
@Operation(summary = "공고 게시글 신고", description = "공고 게시글을 신고 처리합니다. (즉시 반영)")
public ResponseEntity<String> reportedInfoPost(@PathVariable Long infoPostId) {
Long resultId = infoPostService.reportedInfoPost(infoPostId);
return ResponseEntity.ok(resultId + "번 게시글의 신고가 성공적으로 접수되었습니다.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.dissonance.itit.dto.request.OauthTokenReq;
import com.dissonance.itit.dto.response.GeneratedToken;
import com.dissonance.itit.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
Expand All @@ -16,6 +17,7 @@ public class OauthController {

// TODO: 커스텀 어노테이션으로 로그인 유저 정보 추출
@PostMapping("/{provider}")
@Operation(summary = "소셜 로그인", description = "소셜 로그인 (provider - kakao, apple)")
public ResponseEntity<GeneratedToken> getUserInfos(@PathVariable String provider,
@Valid @RequestBody OauthTokenReq oauthTokenReq) {
GeneratedToken token = userService.login(provider, oauthTokenReq.accessToken());
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/com/dissonance/itit/domain/entity/Image.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.dissonance.itit.domain.entity;

import com.dissonance.itit.domain.enums.Directory;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
Expand Down Expand Up @@ -27,8 +28,8 @@ public class Image {
@Column(name = "convert_image_name")
private String convertImageName;

@Size(max = 20)
@Enumerated(EnumType.STRING)
@NotNull
@Column(name = "directory")
private String directory;
private Directory directory;
}
Loading

0 comments on commit 60fc3cc

Please sign in to comment.