Skip to content

Commit

Permalink
Merge branch 'main' into dev/refactor/jobpost/#64
Browse files Browse the repository at this point in the history
  • Loading branch information
YooJHyun authored Oct 4, 2024
2 parents f4432f9 + 5d13aa8 commit d08bb92
Show file tree
Hide file tree
Showing 32 changed files with 696 additions and 246 deletions.
Binary file added .DS_Store
Binary file not shown.
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ repositories {
}

dependencies {

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
Expand Down
Binary file added src/.DS_Store
Binary file not shown.
Binary file added src/main/.DS_Store
Binary file not shown.
5 changes: 1 addition & 4 deletions src/main/java/com/prgrms2/java/bitta/BittaApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class
BittaApplication {

public class BittaApplication {
public static void main(String[] args) {
SpringApplication.run(BittaApplication.class, args);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import java.util.Map;

Expand All @@ -18,16 +19,4 @@ public ResponseEntity<?> handleArgsException(FeedTaskException e) {
return ResponseEntity.status(e.getCode())
.body(Map.of("error", e.getMessage()));
}

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?> handleArgsException(MethodArgumentNotValidException e) {
return ResponseEntity.status(e.getStatusCode())
.body(Map.of("error", "잘못된 요청입니다.", "reason", e.getMessage()));
}

@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<?> handleArgsException(ConstraintViolationException e) {
return ResponseEntity.badRequest()
.body(Map.of("error", "잘못된 요청입니다.", "reason", "ID는 음수가 될 수 없습니다."));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ public void update(FeedDTO feedDTO, List<MultipartFile> filesToUpload, List<Stri
@Override
@Transactional
public void delete(Long id) {
photoService.delete(id);
videoService.delete(id);

if (feedRepository.deleteByIdAndReturnCount(id) == 0) {
throw FeedException.CANNOT_DELETE.get();
}
Expand Down Expand Up @@ -148,7 +151,7 @@ private FeedDTO entityToDto(Feed feed) {
.title(feed.getTitle())
.content(feed.getContent())
.createdAt(feed.getCreatedAt())
.id(feed.getMember().getId())
.memberId(feed.getMember().getId())
.photoUrls(feed.getPhotos().stream()
.map(Photo::getPhotoUrl).toList())
.videoUrls(feed.getVideos().stream()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.prgrms2.java.bitta.global.advice;

import jakarta.validation.ConstraintViolationException;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import java.util.List;
import java.util.Map;

@RestControllerAdvice
public class GlobalControllerAdvice {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?> handleArgsException(MethodArgumentNotValidException e) {
List<String> reasons = e.getBindingResult()
.getFieldErrors()
.stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.toList();

return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(Map.of("error", "잘못된 요청입니다.", "reason", reasons));
}

@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<?> handleArgsException(ConstraintViolationException e) {
return ResponseEntity.badRequest()
.body(Map.of("error", "잘못된 요청입니다.", "reason", "ID는 음수가 될 수 없습니다."));
}

@ExceptionHandler(MaxUploadSizeExceededException.class)
public ResponseEntity<?> handleFileSizeLimitExceeded(MaxUploadSizeExceededException e) {
return ResponseEntity.status(HttpStatus.PAYLOAD_TOO_LARGE) // Status 413
.body(Map.of("error", "Payload Too Large", "reason", "파일의 크기가 허용된 한도를 초과했습니다."));
}

}
16 changes: 16 additions & 0 deletions src/main/java/com/prgrms2/java/bitta/global/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.prgrms2.java.bitta.global.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;

@Configuration
public class WebConfig {

@Bean
public MultipartResolver multipartResolver() {
StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
return multipartResolver;
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
package com.prgrms2.java.bitta.member.controller;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.prgrms2.java.bitta.member.dto.MemberDTO;
import com.prgrms2.java.bitta.member.dto.SignInDTO;
import com.prgrms2.java.bitta.member.dto.SignUpDTO;
import com.prgrms2.java.bitta.member.service.MemberService;
import com.prgrms2.java.bitta.security.JwtToken;
import com.prgrms2.java.bitta.security.JwtTokenProvider;
import com.prgrms2.java.bitta.security.SecurityUtil;
import com.prgrms2.java.bitta.security.dto.RefreshTokenRequestDTO;
import com.prgrms2.java.bitta.security.exception.InvalidTokenException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.Map;
import java.io.IOException;

import static com.prgrms2.java.bitta.global.constants.ApiResponses.*;

Expand All @@ -31,10 +34,31 @@
public class MemberController {

private final MemberService memberService;
private final ObjectMapper objectMapper;
private final JwtTokenProvider jwtTokenProvider;

@Operation(
summary = "테스트",
description = "SecurityUtil 로부터 회원아이디를 얻는 테스트용 API입니다."
)
@PostMapping("/test")
public String test() {
return SecurityUtil.getCurrentUsername();
}

@Operation(
summary = "로그인",
description = "아이디와 비밀번호를 검증하고, 토큰을 반환합니다."
description = "아이디와 비밀번호를 검증하고, 토큰을 반환합니다.",
responses = {
@ApiResponse(
responseCode = "200",
description = "로그인이 성공적으로 완료되었습니다.",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = JwtToken.class)
)
)
}
)
@PostMapping("/sign-in")
public JwtToken signIn(@RequestBody SignInDTO signInDTO) {
Expand All @@ -46,15 +70,6 @@ public JwtToken signIn(@RequestBody SignInDTO signInDTO) {
return jwtToken;
}

@Operation(
summary = "테스트",
description = "SecurityUtil 로부터 회원아이디를 얻는 테스트용 API입니다."
)
@PostMapping("/test")
public String test() {
return SecurityUtil.getCurrentUsername();
}

@Operation(
summary = "회원가입",
description = "회원을 등록합니다.",
Expand All @@ -64,7 +79,7 @@ public String test() {
description = "회원을 성공적으로 등록했습니다.",
content = @Content(
mediaType = "application/json",
schema = @Schema(example = MEMBER_SUCCESS_SIGN_UP)
schema = @Schema(implementation = MemberDTO.class, example = MEMBER_SUCCESS_SIGN_UP)
)
)
}
Expand All @@ -76,57 +91,83 @@ public ResponseEntity<MemberDTO> signUp(@RequestBody SignUpDTO signUpDTO) {
}

@Operation(
summary = "프로필 이미지 수정",
description = "회원의 프로필 이미지를 수정합니다.",
summary = "회원 조회",
description = "회원 ID로 회원 정보를 조회합니다.",
responses = {
@ApiResponse(
responseCode = "200",
description = "프로필 이미지를 성공적으로 수정했습니다.",
description = "회원 정보를 성공적으로 조회했습니다.",
content = @Content(
mediaType = "application/json",
schema = @Schema(example = MEMBER_SUCCESS_UPDATE_PROFILE_IMAGE)
schema = @Schema(implementation = MemberDTO.class)
)
),
)
}
)
@Parameter(
name = "id",
description = "프로필 이미지를 수정할 회원의 ID",
required = true,
example = "1",
schema = @Schema(type = "integer")
)
@PostMapping("/{id}/profile-image")
public ResponseEntity<?> updateProfileImage(@PathVariable Long id,
@RequestParam("file") MultipartFile file) {
memberService.updateProfileImage(id, file);
return ResponseEntity.ok(Map.of("message", "프로필이미지 수정이 완료되었습니다."));
@GetMapping("/{id}")
public ResponseEntity<MemberDTO> getMemberById(@PathVariable Long id) {
return ResponseEntity.ok(memberService.getMemberById(id));
}

@Operation(
summary = "프로필 이미지 삭제",
description = "회원의 프로필 이미지를 삭제합니다.",
summary = "회원 수정",
description = "회원 ID로 회원 정보를 수정합니다. 프로필 이미지를 업데이트할 수 있습니다.",
responses = {
@ApiResponse(
responseCode = "200",
description = "프로필 이미지를 성공적으로 수정했습니다.",
description = "회원 정보를 성공적으로 수정했습니다.",
content = @Content(
mediaType = "application/json",
schema = @Schema(example = MEMBER_SUCCESS_DELETE_PROFILE_IMAGE)
schema = @Schema(implementation = MemberDTO.class)
)
),
)
}
)
@Parameter(
name = "id",
description = "프로필 이미지를 삭제할 회원의 ID",
required = true,
example = "1",
schema = @Schema(type = "integer")
@PutMapping(value = "/{id}", consumes = "multipart/form-data")
public ResponseEntity<MemberDTO> updateMemberById(@PathVariable Long id,
@RequestParam("dto") String dtoJson,
@RequestParam(value = "profileImage", required = false) MultipartFile profileImage,
@RequestParam(value = "removeProfileImage", required = false, defaultValue = "false") Boolean removeProfileImage) {
try {
MemberDTO memberDTO = objectMapper.readValue(dtoJson, MemberDTO.class);
MemberDTO updatedMember = memberService.updateMember(id, memberDTO, profileImage, removeProfileImage);
return ResponseEntity.ok(updatedMember);
} catch (IOException e) {
log.error("Failed to update member profile", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
}

@Operation(
summary = "회원 삭제",
description = "회원 ID로 회원 정보를 삭제합니다.",
responses = {
@ApiResponse(
responseCode = "200",
description = "회원 정보를 성공적으로 삭제했습니다.",
content = @Content(
mediaType = "text/plain",
schema = @Schema(example = "회원 삭제가 완료되었습니다.")
)
)
}
)
@DeleteMapping("/{id}/profile-image")
public ResponseEntity<?> deleteProfileImage(@PathVariable Long id) {
memberService.resetProfileImageToDefault(id);
return ResponseEntity.ok(Map.of("message", "프로필이미지가 삭제되었습니다."));

@DeleteMapping("/{id}")
public ResponseEntity<String> deleteMemberById(@PathVariable Long id) {
memberService.deleteMember(id);
return ResponseEntity.ok("회원 삭제가 완료되었습니다.");
}


@PostMapping("/refresh")
public JwtToken refreshToken(@RequestBody RefreshTokenRequestDTO refreshTokenRequestDTO) {
// 리프레시 토큰이 유효한지 검사
String refreshToken = refreshTokenRequestDTO.getRefreshToken();
if (refreshToken == null || !jwtTokenProvider.validateToken(refreshToken)) {
throw new InvalidTokenException("Invalid or expired refresh token");
}
// 유효한 리프레시 토큰이면 새 액세스 토큰 발급
return memberService.refreshToken(refreshToken);
}
}
23 changes: 11 additions & 12 deletions src/main/java/com/prgrms2/java/bitta/member/dto/MemberDTO.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
package com.prgrms2.java.bitta.member.dto;

import com.prgrms2.java.bitta.apply.dto.ApplyDTO;
import com.prgrms2.java.bitta.feed.dto.FeedDTO;
import com.prgrms2.java.bitta.member.entity.Member;
import com.prgrms2.java.bitta.member.entity.Role;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Getter
@ToString
@AllArgsConstructor
Expand All @@ -33,15 +24,23 @@ public class MemberDTO {
private String address;

@Schema(title = "프로필 이미지 URL", description = "프로필 이미지의 URL 입니다.", example = "IMAGE_URL")
private String profileImg;
private String profile;

public MemberDTO(Member member) {
this.id = member.getId();
this.username = member.getUsername();
this.nickname = member.getNickname();
this.address = member.getAddress();
this.profile = member.getProfile();
}

static public MemberDTO toDTO(Member member) {
return MemberDTO.builder()
.id(member.getId())
.username(member.getUsername())
.nickname(member.getNickname())
.address(member.getAddress())
.profileImg(member.getProfileImg()).build();
.profile(member.getProfile()).build();
}

public Member toEntity() {
Expand All @@ -50,6 +49,6 @@ public Member toEntity() {
.username(username)
.nickname(nickname)
.address(address)
.profileImg(profileImg).build();
.profile(profile).build();
}
}
Loading

0 comments on commit d08bb92

Please sign in to comment.