From 8f415d3da34f1bcab6b855513200a100a51ba08a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=92=E1=85=A2=E1=84=83?= =?UTF-8?q?=E1=85=A9=E1=86=BC?= Date: Wed, 18 Oct 2023 09:32:10 +0900 Subject: [PATCH] [feature/liked] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 프로필 조회/수정 --- .../dto/UserProfileUpdateRequest.java | 42 +++++++++++ .../com/koliving/api/my/ui/MyController.java | 73 +++++++++++++++++++ .../room/application/dto/WriterResponse.java | 8 +- src/main/java/com/koliving/api/user/User.java | 34 +++++++-- .../api/user/application/UserService.java | 34 ++++++++- .../user/application/dto/UserResponse.java | 22 +++++- .../koliving/api/user/UserServiceTest.java | 2 +- 7 files changed, 202 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/koliving/api/my/application/dto/UserProfileUpdateRequest.java create mode 100644 src/main/java/com/koliving/api/my/ui/MyController.java diff --git a/src/main/java/com/koliving/api/my/application/dto/UserProfileUpdateRequest.java b/src/main/java/com/koliving/api/my/application/dto/UserProfileUpdateRequest.java new file mode 100644 index 00000000..af7de61c --- /dev/null +++ b/src/main/java/com/koliving/api/my/application/dto/UserProfileUpdateRequest.java @@ -0,0 +1,42 @@ +package com.koliving.api.my.application.dto; + +import com.koliving.api.file.domain.ImageFile; +import com.koliving.api.user.Gender; +import com.koliving.api.user.User; +import io.swagger.v3.oas.annotations.media.Schema; +import org.springframework.lang.Nullable; + +import javax.validation.constraints.NotNull; +import java.time.LocalDate; + +@Schema(description = "작성자 정보") +public record UserProfileUpdateRequest( + + @NotNull + @Schema(description = "이미지 URL 고유 Key") + Long profileId, + + @NotNull + @Schema(description = "성별") + Gender gender, + + @NotNull + @Schema(description = "이름") + String firstName, + + @NotNull + @Schema(description = "성") + String lastName, + + @NotNull + @Schema(description = "생년월일") + LocalDate birthDate, + + @Nullable + @Schema(description = "설명") + String description +) { + public User toUser(ImageFile imageFile) { + return User.of(imageFile, gender, firstName, lastName, birthDate, description); + } +} diff --git a/src/main/java/com/koliving/api/my/ui/MyController.java b/src/main/java/com/koliving/api/my/ui/MyController.java new file mode 100644 index 00000000..1d1ce149 --- /dev/null +++ b/src/main/java/com/koliving/api/my/ui/MyController.java @@ -0,0 +1,73 @@ +package com.koliving.api.my.ui; + +import com.koliving.api.base.ErrorResponse; +import com.koliving.api.my.application.dto.UserProfileUpdateRequest; +import com.koliving.api.user.User; +import com.koliving.api.user.application.UserService; +import com.koliving.api.user.application.dto.UserResponse; +import io.swagger.v3.oas.annotations.Operation; +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 org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +@Tag(name = "MY API", description = "MY API") +@RestController +@RequestMapping("api/v1/my") +@RequiredArgsConstructor +public class MyController { + private final UserService userService; + + @Operation( + summary = "프로필 수정", + description = "프로필을 수정합니다.", + responses = { + @ApiResponse( + responseCode = "204", + description = "프로필 수정 성공" + ), + @ApiResponse( + responseCode = "400", + description = "프로필 수정 실패", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) + ), + }) + @PutMapping("/profile") + public ResponseEntity updateProfile(@RequestBody UserProfileUpdateRequest request, @AuthenticationPrincipal User user) { + userService.updateProfile(request, user.getId()); + return ResponseEntity.noContent().build(); + } + + + @Operation( + summary = "프로필 조회", + description = "프로필 정보를 조회합니다", + responses = { + @ApiResponse( + responseCode = "200", + description = "프로필 조회 성공", + content = @Content(schema = @Schema(implementation = UserResponse.class)) + ), + @ApiResponse( + responseCode = "400", + description = "프로필 조회 실패", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) + ), + }) + @GetMapping + public ResponseEntity myProfile(@AuthenticationPrincipal User user) { + UserResponse response = userService.findById(user.getId()); + + return ResponseEntity.ok() + .body(response); + } +} diff --git a/src/main/java/com/koliving/api/room/application/dto/WriterResponse.java b/src/main/java/com/koliving/api/room/application/dto/WriterResponse.java index f037b765..f6ff57e8 100644 --- a/src/main/java/com/koliving/api/room/application/dto/WriterResponse.java +++ b/src/main/java/com/koliving/api/room/application/dto/WriterResponse.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import java.time.LocalDate; +import java.util.Objects; @Schema(description = "작성자 정보") public record WriterResponse( @@ -17,11 +18,16 @@ public record WriterResponse( Gender gender, @Schema(description = "작성자 생년월일") LocalDate birthDate, + + @Schema(description = "작성자 소개") + String description, + @Schema(description = "작성자 프로필 URL") String imageUrl ) { public static WriterResponse of(User entity) { - return new WriterResponse(entity.getFirstName(), entity.getLastName(), entity.getGender(), entity.getBirthDate(), entity.getImageUrl()); + String imageUrl = Objects.isNull(entity.getImageFile()) ? null : entity.getImageFile().getPath(); + return new WriterResponse(entity.getFirstName(), entity.getLastName(), entity.getGender(), entity.getBirthDate(), entity.getDescription(), imageUrl); } } diff --git a/src/main/java/com/koliving/api/user/User.java b/src/main/java/com/koliving/api/user/User.java index b7700666..f1d83820 100644 --- a/src/main/java/com/koliving/api/user/User.java +++ b/src/main/java/com/koliving/api/user/User.java @@ -1,6 +1,7 @@ package com.koliving.api.user; import com.koliving.api.base.exception.KolivingServiceException; +import com.koliving.api.file.domain.ImageFile; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; @@ -8,6 +9,9 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Lob; +import jakarta.persistence.OneToOne; import jakarta.persistence.Temporal; import jakarta.persistence.TemporalType; import lombok.AccessLevel; @@ -25,7 +29,6 @@ import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.crypto.password.PasswordEncoder; -import javax.swing.*; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Collection; @@ -61,10 +64,12 @@ public class User implements UserDetails { @Column(name = "BIRTH_DATE") private LocalDate birthDate; + @Lob private String description; - @Column - private String imageUrl; + @OneToOne + @JoinColumn(name = "IMAGE_FILE_ID") + private ImageFile imageFile; @Enumerated(EnumType.STRING) @Column(name = "USER_ROLE") @@ -95,16 +100,26 @@ public User(String email) { this.userRole = UserRole.USER; } - private User(String email, String password, UserRole userRole) { + private User(String email, String password, String firstName, String lastName, Gender gender, LocalDate birthDate, String description, ImageFile imageFile, UserRole userRole) { this.email = email; this.password = password; + this.firstName = firstName; + this.lastName = lastName; + this.gender = gender; + this.birthDate = birthDate; + this.description = description; + this.imageFile = imageFile; this.userRole = userRole; } + @Deprecated public static User valueOf(String email, String encodedPassword, UserRole role) { - return new User(email, encodedPassword, role); + return new User(email, encodedPassword, null, null, null, null, null,null, role); } + public static User of(ImageFile imageFile, Gender gender, String firstName, String lastName, LocalDate birthDate, String description) { + return new User(null, null, firstName, lastName, gender, birthDate,description, imageFile, null); + } public void setPassword(String password) { this.password = password; this.signUpStatus = SignUpStatus.PROFILE_INFORMATION_PENDING; @@ -159,4 +174,13 @@ public void checkPassword(PasswordEncoder passwordEncoder, String rawPassword) { throw new KolivingServiceException(UNAUTHORIZED); } } + + public void update(User updatable) { + this.imageFile = updatable.imageFile; + this.firstName = updatable.firstName; + this.lastName = updatable.lastName; + this.gender = updatable.gender; + this.birthDate = updatable.birthDate; + this.description = updatable.description; + } } diff --git a/src/main/java/com/koliving/api/user/application/UserService.java b/src/main/java/com/koliving/api/user/application/UserService.java index 3739c296..94e4bb53 100644 --- a/src/main/java/com/koliving/api/user/application/UserService.java +++ b/src/main/java/com/koliving/api/user/application/UserService.java @@ -1,10 +1,13 @@ package com.koliving.api.user.application; +import com.koliving.api.base.ServiceError; +import com.koliving.api.base.exception.KolivingServiceException; +import com.koliving.api.file.domain.ImageFile; +import com.koliving.api.file.infra.ImageFileRepository; +import com.koliving.api.my.application.dto.UserProfileUpdateRequest; import com.koliving.api.user.User; import com.koliving.api.user.UserRepository; import com.koliving.api.user.application.dto.UserResponse; -import java.util.List; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; @@ -13,22 +16,26 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; +import java.util.stream.Collectors; + @Service -@Transactional +@Transactional(readOnly = true) @RequiredArgsConstructor public class UserService implements IUserService, UserDetailsService { private final UserRepository userRepository; + private final ImageFileRepository imageFileRepository; private final PasswordEncoder passwordEncoder; @Override - @Transactional(readOnly = true) public UserDetails loadUserByUsername(String email) { return userRepository.findByEmail(email).orElseThrow(() -> new UsernameNotFoundException(String.format("User with email %s not found", email))); } @Override + @Transactional public User save(User user) { return userRepository.save(user); } @@ -51,4 +58,23 @@ public List list() { .map(UserResponse::valueOf) .collect(Collectors.toList()); } + + @Transactional + public void updateProfile(UserProfileUpdateRequest request, Long userId) { + ImageFile imageFile = getImageFile(request); + User updatable = request.toUser(imageFile); + + User user = userRepository.findById(userId).orElseThrow(() -> new KolivingServiceException(ServiceError.RECORD_NOT_EXIST)); + user.update(updatable); + } + + private ImageFile getImageFile(UserProfileUpdateRequest request) { + return imageFileRepository.findById(request.profileId()) + .orElseThrow(() -> new KolivingServiceException(ServiceError.RECORD_NOT_EXIST)); + } + + public UserResponse findById(Long id) { + User user = userRepository.findById(id).orElseThrow(() -> new KolivingServiceException(ServiceError.RECORD_NOT_EXIST)); + return UserResponse.valueOf(user); + } } diff --git a/src/main/java/com/koliving/api/user/application/dto/UserResponse.java b/src/main/java/com/koliving/api/user/application/dto/UserResponse.java index 65c923aa..36c62fba 100644 --- a/src/main/java/com/koliving/api/user/application/dto/UserResponse.java +++ b/src/main/java/com/koliving/api/user/application/dto/UserResponse.java @@ -1,11 +1,29 @@ package com.koliving.api.user.application.dto; +import com.koliving.api.file.domain.ImageFile; +import com.koliving.api.user.Gender; +import com.koliving.api.user.SignUpStatus; import com.koliving.api.user.User; +import com.koliving.api.user.UserRole; -public record UserResponse(Long id, String email, String imageUrl) { +import java.time.LocalDate; + +public record UserResponse(Long id, String email, String firstName, String lastName, Gender gender, LocalDate birthDate, + String description, ImageFile imageFile, UserRole userRole, SignUpStatus signUpStatus) { public static UserResponse valueOf(User entity) { - return new UserResponse(entity.getId(), entity.getEmail(), entity.getImageUrl()); + return new UserResponse( + entity.getId(), + entity.getEmail(), + entity.getFirstName(), + entity.getLastName(), + entity.getGender(), + entity.getBirthDate(), + entity.getDescription(), + entity.getImageFile(), + entity.getUserRole(), + entity.getSignUpStatus() + ); } } diff --git a/src/test/java/com/koliving/api/user/UserServiceTest.java b/src/test/java/com/koliving/api/user/UserServiceTest.java index 6451fa1d..11a355bd 100644 --- a/src/test/java/com/koliving/api/user/UserServiceTest.java +++ b/src/test/java/com/koliving/api/user/UserServiceTest.java @@ -146,7 +146,7 @@ void list_success() { List expected = userList.stream() .map(UserResponse::valueOf) - .collect(Collectors.toList()); + .toList(); assertTrue(actual.equals(expected)); assertTrue(actual.containsAll(expected));