Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
char-yb committed Oct 31, 2023
2 parents 7ba99cb + f821df5 commit b25e754
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 57 deletions.
7 changes: 5 additions & 2 deletions src/main/java/com/seniors/config/S3Uploader.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

@Slf4j
@RequiredArgsConstructor // final 멤버변수가 있으면 생성자 항목에 포함시킴
Expand All @@ -37,7 +38,8 @@ public String upload(MultipartFile multipartFile, String dirName) throws IOExcep
}

private String upload(File uploadFile, String dirName) {
String fileName = dirName + "/" + uploadFile.getName();
// 파일 이름 중복과 한글 문자열 인코딩 이슈로 uuid 방식을 사용
String fileName = dirName + "/" + UUID.randomUUID();
String uploadImageUrl = putS3(uploadFile, fileName);

removeNewFile(uploadFile); // 로컬에 생성된 File 삭제 (MultipartFile -> File 전환 하며 로컬에 파일 생성됨)
Expand Down Expand Up @@ -72,7 +74,8 @@ private void removeNewFile(File targetFile) {
}

private Optional<File> convert(MultipartFile file) throws IOException {
File convertFile = new File(file.getOriginalFilename());
// 파일 이름 중복과 한글 문자열 인코딩 이슈로 uuid 방식을 사용
File convertFile = new File(System.getProperty("user.dir") + "/" + UUID.randomUUID());
if(convertFile.createNewFile()) {
try (FileOutputStream fos = new FileOutputStream(convertFile)) {
fos.write(file.getBytes());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import com.seniors.common.exception.type.NotFoundException;
import com.seniors.config.security.CustomUserDetails;
import com.seniors.domain.post.dto.PostDto.GetPostRes;
import com.seniors.domain.post.dto.PostDto.PostCreateDto;
import com.seniors.domain.post.dto.PostDto.SetPostDto;
import com.seniors.domain.post.dto.PostLikeDto.SetLikeDto;
import com.seniors.domain.post.service.PostService;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -21,10 +21,12 @@
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;

@Tag(name = "게시글", description = "게시글 API 명세서")
@Slf4j
Expand All @@ -37,7 +39,7 @@ public class PostController {

@Operation(summary = "게시글 생성")
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "생성 요청 body",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = PostCreateDto.class)))
content = @Content(mediaType = "application/json", schema = @Schema(implementation = SetPostDto.class)))
@ApiResponse(responseCode = "200", description = "생성 성공",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = DataResponseDto.class)))
@ApiResponse(responseCode = "400", description = "유효성 검증 실패",
Expand All @@ -48,11 +50,12 @@ public class PostController {
content = @Content(mediaType = "application/json", schema = @Schema(implementation = NotFoundException.class)))
@ApiResponse(responseCode = "500", description = "서버 에러.",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)))
@PostMapping("")
@PostMapping(value = "", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_VALUE})
public DataResponseDto<String> postAdd(
@ModelAttribute @Valid PostCreateDto postCreateDto, BindingResult bindingResult,
@RequestPart(value = "data") @Valid SetPostDto setPostDto,
@RequestPart(value = "files", required = false) List<MultipartFile> files,
@LoginUsers CustomUserDetails userDetails) throws IOException {
postService.addPost(postCreateDto, bindingResult, userDetails.getUserId());
postService.addPost(setPostDto, files, userDetails.getUserId());
return DataResponseDto.of("SUCCESS");
}

Expand Down Expand Up @@ -95,7 +98,7 @@ public DataResponseDto<CustomPage<GetPostRes>> postList(

@Operation(summary = "게시글 수정")
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "수정 요청 body",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = PostCreateDto.class)))
content = @Content(mediaType = "application/json", schema = @Schema(implementation = SetPostDto.class)))
@ApiResponse(responseCode = "200", description = "단건 수정 성공",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = DataResponseDto.class)))
@ApiResponse(responseCode = "400", description = "유효성 검증 실패",
Expand All @@ -106,12 +109,13 @@ public DataResponseDto<CustomPage<GetPostRes>> postList(
content = @Content(mediaType = "application/json", schema = @Schema(implementation = NotFoundException.class)))
@ApiResponse(responseCode = "500", description = "서버 에러.",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)))
@PatchMapping("/{postId}")
@PatchMapping(value = "/{postId}", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_VALUE})
public DataResponseDto<String> postModify(
@Parameter(description = "게시글 ID") @PathVariable(value = "postId") Long postId,
@ModelAttribute @Valid PostCreateDto postCreateDto, BindingResult bindingResult,
@RequestPart(value = "data") @Valid SetPostDto setPostDto,
@RequestPart(value = "files", required = false) List<MultipartFile> files,
@LoginUsers CustomUserDetails userDetails) throws IOException {
postService.modifyPost(postCreateDto, bindingResult, postId, userDetails.getUserId());
postService.modifyPost(setPostDto, files, postId, userDetails.getUserId());
return DataResponseDto.of("SUCCESS");
}

Expand Down
20 changes: 13 additions & 7 deletions src/main/java/com/seniors/domain/post/dto/PostDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import lombok.*;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.util.List;
Expand All @@ -24,24 +23,31 @@
public class PostDto {

@Data
@Builder
public static class PostCreateDto {
public static class SetPostDto {
@NotEmpty(message = "게시글 제목은 비워둘 수 없습니다.")
@Schema(description = "게시글 제목", defaultValue = "제목 1", example = "제목 1이요")
private String title;

@NotEmpty(message = "게시글 내용은 비워둘 수 없습니다.")
@Schema(description = "게시글 내용", defaultValue = "내용 1", example = "내용 1이요")
private String content;
}

@Schema(description = "사진 및 동영상")
private List<MultipartFile> files;
@Data
@Builder
public static class PostCreateDto extends SetPostDto {
@NotEmpty(message = "게시글 제목은 비워둘 수 없습니다.")
@Schema(description = "게시글 제목", defaultValue = "제목 1", example = "제목 1이요")
private String title;

@NotEmpty(message = "게시글 내용은 비워둘 수 없습니다.")
@Schema(description = "게시글 내용", defaultValue = "내용 1", example = "내용 1이요")
private String content;

public static PostCreateDto of(String title, String content, List<MultipartFile> files) {
public static SetPostDto of(String title, String content) {
return PostCreateDto.builder()
.title(title)
.content(content)
.files(files)
.build();
}
}
Expand Down
21 changes: 11 additions & 10 deletions src/main/java/com/seniors/domain/post/service/PostService.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import com.seniors.config.S3Uploader;
import com.seniors.domain.notification.service.NotificationService;
import com.seniors.domain.post.dto.PostDto.GetPostRes;
import com.seniors.domain.post.dto.PostDto.PostCreateDto;
import com.seniors.domain.post.dto.PostDto.SetPostDto;
import com.seniors.domain.post.entity.Post;
import com.seniors.domain.post.entity.PostMedia;
import com.seniors.domain.post.repository.post.PostRepository;
Expand All @@ -24,10 +24,10 @@
import org.springframework.data.domain.Sort.Direction;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;

@Slf4j
@Service
Expand All @@ -42,13 +42,14 @@ public class PostService {
private final NotificationService notificationService;

@Transactional
public Long addPost(PostCreateDto postCreateDto, BindingResult bindingResult, Long userId) throws IOException {
public Long addPost(SetPostDto setPostDto, List<MultipartFile> files, Long userId) throws IOException {
// post에 user 객체를 넣어주기 위해 조회
Users users = usersRepository.findById(userId).orElseThrow(
() -> new NotAuthorizedException("유효하지 않은 회원입니다.")
);
Post post = postRepository.save(Post.of(postCreateDto.getTitle(), postCreateDto.getContent(), users));
if (postCreateDto.getFiles() != null && !postCreateDto.getFiles().isEmpty()) {
for (MultipartFile file : postCreateDto.getFiles()) {
Post post = postRepository.save(Post.of(setPostDto.getTitle(), setPostDto.getContent(), users));
if (files != null && !files.isEmpty()) {
for (MultipartFile file :files) {
String uploadImagePath = s3Uploader.upload(file, "posts/media/" + post.getId().toString());
postMediaRepository.save(PostMedia.of(uploadImagePath, post));
}
Expand All @@ -68,18 +69,18 @@ public CustomPage<GetPostRes> findPost(int page, int size, Long userId) {
}

@Transactional
public Long modifyPost(PostCreateDto postCreateDto, BindingResult bindingResult, Long postId, Long userId) throws IOException {
public Long modifyPost(SetPostDto setPostDto, List<MultipartFile> files, Long postId, Long userId) throws IOException {

Post post = postRepository.findById(postId).orElseThrow(() -> new NotFoundException("유효하지 않은 게시글입니다."));
postRepository.modifyPost(postCreateDto.getTitle(), postCreateDto.getContent(), postId, userId);
postRepository.modifyPost(setPostDto.getTitle(), setPostDto.getContent(), postId, userId);

// 기존 미디어 파일 삭제
s3Uploader.deleteS3Object("posts/media/" + post.getId().toString());

postMediaRepository.deleteByPostId(postId);

if (postCreateDto.getFiles() != null && !postCreateDto.getFiles().isEmpty()) {
for (MultipartFile file : postCreateDto.getFiles()) {
if (files != null && !files.isEmpty()) {
for (MultipartFile file : files) {
String uploadImagePath = s3Uploader.upload(file, "posts/media/" + post.getId().toString());
postMediaRepository.save(PostMedia.of(uploadImagePath, post));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public Long addResume(SaveResumeReq resumeReq, MultipartFile image, Long userId)

for(CareerDto.saveCareerReq saveCareerReq : resumeReq.getCareerList()){
if(saveCareerReq.getEndedAt()!= null && saveCareerReq.getIsAttendanced()==true){
throw new BadRequestException("퇴사연도를 입력하심면 재직중 여부를 체크하실 수 없습니다.");
throw new BadRequestException("퇴사연도를 입력하시면 재직중 여부를 체크하실 수 없습니다.");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
import com.seniors.common.constant.OAuthProvider;
import com.seniors.common.constant.ResultCode;
import com.seniors.config.security.CustomUserDetails;
import com.seniors.config.security.JwtUtil;
import com.seniors.config.security.TokenService;
import com.seniors.domain.post.dto.PostDto.PostCreateDto;
import com.seniors.domain.post.dto.PostDto.SetPostDto;
import com.seniors.domain.post.entity.Post;
import com.seniors.domain.post.entity.PostLike;
import com.seniors.domain.post.repository.post.PostRepository;
Expand All @@ -17,20 +20,24 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpMethod;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static org.hamcrest.Matchers.is;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
Expand All @@ -44,6 +51,7 @@
@Transactional
class PostControllerTest {


@Autowired
private ObjectMapper objectMapper;
@Autowired
Expand All @@ -54,6 +62,11 @@ class PostControllerTest {
@Autowired
private UsersRepository usersRepository;
private Authentication authentication;
private String accessToken;

@Autowired
private TokenService tokenService;

private Users users;

@BeforeEach
Expand All @@ -71,6 +84,7 @@ void setUp() {

users = usersRepository.save(Users.builder()
.snsId(String.valueOf(randomNumber))
.nickname("tester1")
.email("[email protected]")
.gender("male")
.ageRange("20~29")
Expand All @@ -86,7 +100,10 @@ void setUp() {
users.getProfileImageUrl());
userDetails.setUserId(users.getId());

accessToken = tokenService.generateToken(users, Long.valueOf(1000 * 60 * 60 * 24));

authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());

SecurityContextHolder.getContext().setAuthentication(authentication);
}

Expand All @@ -96,13 +113,20 @@ void postAdd() throws Exception {
// given
String title = "글 제목입니다.";
String content = "글 내용입니다.";
PostCreateDto createDto = PostCreateDto.builder().title(title).content(content).build();
ObjectMapper objectMapper = new ObjectMapper();
String createDtoJson = objectMapper.writeValueAsString(createDto);

// MockMultipartFile file = new MockMultipartFile("files", "tooth.png", "multipart/form-data", "uploadFile".getBytes(StandardCharsets.UTF_8));
MockMultipartFile request = new MockMultipartFile("data", null, "application/json", createDtoJson.getBytes(StandardCharsets.UTF_8));

// expected
mockMvc.perform(post("/api/posts")
.contentType(APPLICATION_JSON)
.param("title", title)
.param("content", content)
.principal(authentication)
mockMvc.perform(multipart(HttpMethod.POST, "/api/posts")
// .file(file)
.file(request)
.accept(APPLICATION_JSON)
.contentType(MULTIPART_FORM_DATA)
.header("Authorization", accessToken)
)
.andExpect(status().isOk())
.andDo(print());
Expand All @@ -112,16 +136,15 @@ void postAdd() throws Exception {
@DisplayName("생성 요청 시 title 값은 필수")
void postAddNotExistTitle() throws Exception {
// given
PostCreateDto request = PostCreateDto.builder()
.content("글 내용입니다.")
.build();
String json = objectMapper.writeValueAsString(request);

SetPostDto setPostDto = PostCreateDto.of("글 내용입니다.", "");
String json = objectMapper.writeValueAsString(setPostDto);
MockMultipartFile request = new MockMultipartFile("data", null, "application/json", json.getBytes(StandardCharsets.UTF_8));
// expected
mockMvc.perform(post("/api/posts")
.contentType(APPLICATION_JSON)
.content(json)
.principal(authentication)
mockMvc.perform(multipart(HttpMethod.POST, "/api/posts")
.file(request)
.accept(APPLICATION_JSON)
.contentType(MULTIPART_FORM_DATA)
.header("Authorization", accessToken)
)
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.success").value(false))
Expand Down Expand Up @@ -179,15 +202,18 @@ void modifyPost() throws Exception {
// given
Post post = postRepository.save(Post.of("글 제목1", "글 내용1", users));

// given
String title = "글 수정 제목1입니다.";
String content = "글 수정 내용1입니다.";
SetPostDto setPostDto = PostCreateDto.of(title, content);
String json = objectMapper.writeValueAsString(setPostDto);
MockMultipartFile request = new MockMultipartFile("data", null, "application/json", json.getBytes(StandardCharsets.UTF_8));

// expected
mockMvc.perform(patch("/api/posts/{postId}", post.getId())
.contentType(APPLICATION_JSON)
.param("title", title)
.param("content", content)
.principal(authentication)
mockMvc.perform(multipart(HttpMethod.PATCH, "/api/posts/{postId}", post.getId())
.file(request)
.accept(APPLICATION_JSON)
.contentType(MULTIPART_FORM_DATA)
.header("Authorization", accessToken)
)
.andExpect(status().isOk())
.andDo(print());
Expand Down
Loading

0 comments on commit b25e754

Please sign in to comment.