diff --git a/src/main/java/HookKiller/server/admin/service/AdminService.java b/src/main/java/HookKiller/server/admin/service/AdminService.java index ab67a63..a8732ed 100644 --- a/src/main/java/HookKiller/server/admin/service/AdminService.java +++ b/src/main/java/HookKiller/server/admin/service/AdminService.java @@ -37,7 +37,6 @@ public class AdminService { private final UserUtils userUtils; private final UserRepository userRepository; - private final PasswordEncoder passwordEncoder; private final ReplyRepository replyRepository; private final ArticleRepository articleRepository; private final ReplyContentRepository replyContentRepository; @@ -59,7 +58,7 @@ public void regAdmin(SingUpRequest registerRequest) { User.builder() .email(registerRequest.getEmail()) .nickName(registerRequest.getNickName()) - .password(passwordEncoder.encode(registerRequest.getPassword())) + .password(registerRequest.getPassword()) .role(ADMIN) .build() ); diff --git a/src/main/java/HookKiller/server/auth/service/AuthService.java b/src/main/java/HookKiller/server/auth/service/AuthService.java index 3458a9b..e0905e6 100644 --- a/src/main/java/HookKiller/server/auth/service/AuthService.java +++ b/src/main/java/HookKiller/server/auth/service/AuthService.java @@ -12,6 +12,7 @@ import HookKiller.server.auth.helper.OIDCHelper; import HookKiller.server.auth.helper.TokenGenerateHelper; import HookKiller.server.common.dto.AccessTokenDetail; +import HookKiller.server.common.util.SecurityUtils; import HookKiller.server.jwt.JwtTokenProvider; import HookKiller.server.outer.api.oauth.client.KakaoOauthClient; import HookKiller.server.properties.KakaoOauthProperties; @@ -25,6 +26,8 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; +import static HookKiller.server.common.util.SecurityUtils.passwordEncoder; + @Component @RequiredArgsConstructor @Slf4j @@ -37,14 +40,11 @@ public class AuthService { private final OIDCHelper oidcHelper; private final KakaoOauthHelper kakaoOauthHelper; private final TokenGenerateHelper tokenGenerateHelper; - private final PasswordEncoder passwordEncoder; private static final String KAKAO_OAUTH_QUERY_STRING = "/oauth/authorize?client_id=%s&redirect_uri=%s&response_type=code"; public ResponseEntity loginExecute(AuthRequest request) { User user = userRepository.findByEmail(request.getEmail()).orElseThrow(()-> UserNotFoundException.EXCEPTION ); - String requestEncodePassword = passwordEncoder.encode(request.getPassword()); - if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) { throw PasswordIncorrectException.EXCEPTION; } diff --git a/src/main/java/HookKiller/server/board/repository/ArticleContentRepository.java b/src/main/java/HookKiller/server/board/repository/ArticleContentRepository.java index a869cea..61de02a 100644 --- a/src/main/java/HookKiller/server/board/repository/ArticleContentRepository.java +++ b/src/main/java/HookKiller/server/board/repository/ArticleContentRepository.java @@ -14,4 +14,5 @@ public interface ArticleContentRepository extends JpaRepository findByArticleAndLanguage(Article article, LanguageType language); List findAllByArticle(Article article); + } diff --git a/src/main/java/HookKiller/server/board/repository/ArticleLikeRepository.java b/src/main/java/HookKiller/server/board/repository/ArticleLikeRepository.java index 3c84f3b..72d9c87 100644 --- a/src/main/java/HookKiller/server/board/repository/ArticleLikeRepository.java +++ b/src/main/java/HookKiller/server/board/repository/ArticleLikeRepository.java @@ -3,6 +3,8 @@ import HookKiller.server.board.entity.Article; import HookKiller.server.board.entity.ArticleLike; import HookKiller.server.user.entity.User; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; @@ -12,4 +14,6 @@ public interface ArticleLikeRepository extends JpaRepository Optional findByArticleAndUser(Article article, User user); Optional deleteByArticleAndUser(Article article, User user); + + Page findAllByUserOrderByCreateAtDesc(User user, Pageable pageable); } diff --git a/src/main/java/HookKiller/server/common/exception/BadTypeRequestException.java b/src/main/java/HookKiller/server/common/exception/BadTypeRequestException.java new file mode 100644 index 0000000..3810af9 --- /dev/null +++ b/src/main/java/HookKiller/server/common/exception/BadTypeRequestException.java @@ -0,0 +1,12 @@ +package HookKiller.server.common.exception; + +import static HookKiller.server.common.exception.GlobalException.TYPE_BAD_REQUEST_ERROR; + +public class BadTypeRequestException extends BaseException { + public static final BaseException EXCEPTION = new BadTypeRequestException(); + + private BadTypeRequestException() { + super(TYPE_BAD_REQUEST_ERROR); + } +} + diff --git a/src/main/java/HookKiller/server/common/exception/GlobalException.java b/src/main/java/HookKiller/server/common/exception/GlobalException.java index bbca367..2b91bb8 100644 --- a/src/main/java/HookKiller/server/common/exception/GlobalException.java +++ b/src/main/java/HookKiller/server/common/exception/GlobalException.java @@ -30,7 +30,8 @@ public enum GlobalException implements BaseErrorCode { FILE_IO_ERROR(INTERNAL_SERVER_ERROR.value(), "500-12", "파일 변환 중 오류가 발생하였습니다"), MAIL_SEND_ERROR(INTERNAL_SERVER_ERROR.value(), "500-20", "메일 발송중 오류가 발생하였습니다"), NAVER_ERROR(INTERNAL_SERVER_ERROR.value(), "500-30", "네이버 번역 도중 오류가 발생하였습니다"), - DELETE_FAIL_ERROR(INTERNAL_SERVER_ERROR.value(), "500-02", "삭제처리 도중 오류가 발생하였습니다"); + DELETE_FAIL_ERROR(INTERNAL_SERVER_ERROR.value(), "500-02", "삭제처리 도중 오류가 발생하였습니다"), + TYPE_BAD_REQUEST_ERROR(BAD_REQUEST.value(), "400-1", "잘못된 타입을 요청하였습니다."); private final Integer statusCode; private final String errorCode; private final String reason; diff --git a/src/main/java/HookKiller/server/common/util/SecurityUtils.java b/src/main/java/HookKiller/server/common/util/SecurityUtils.java index 4827c79..ef01031 100644 --- a/src/main/java/HookKiller/server/common/util/SecurityUtils.java +++ b/src/main/java/HookKiller/server/common/util/SecurityUtils.java @@ -1,7 +1,6 @@ package HookKiller.server.common.util; import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Bean; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; @@ -9,9 +8,6 @@ @Component @RequiredArgsConstructor public class SecurityUtils { - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } + public static PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + } diff --git a/src/main/java/HookKiller/server/user/controller/MypageController.java b/src/main/java/HookKiller/server/user/controller/MypageController.java new file mode 100644 index 0000000..2d12540 --- /dev/null +++ b/src/main/java/HookKiller/server/user/controller/MypageController.java @@ -0,0 +1,81 @@ +package HookKiller.server.user.controller; + +import HookKiller.server.common.dto.CommonBooleanResultResponse; +import HookKiller.server.common.type.LanguageType; +import HookKiller.server.user.dto.MyPageUserResponse; +import HookKiller.server.user.dto.MyPageUserUpdateRequest; +import HookKiller.server.user.service.MyPageService; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Slf4j +@RestController +@RequestMapping("/mypage") +@RequiredArgsConstructor +public class MypageController { + + private final MyPageService myPageService; + + /** + * 마이페이지 조회 + * 애초애 Security에서 사용자 정보없으면 Exception발생하기 떄문에 Paramete가 필요가 없다. + * @return + */ + @GetMapping + public ResponseEntity getMyPage() { + return ResponseEntity.ok(myPageService.getMyPage()); + + } + + /** + * 정보 수정, Null로 준 경우 해당 파라미터는 변경하지 않는다. + * @param requestDto + */ + @PutMapping + public ResponseEntity updateUserInfo(@RequestBody MyPageUserUpdateRequest requestDto) { + myPageService.updateUserInfo(requestDto); + return ResponseEntity.ok( + CommonBooleanResultResponse.builder().result(true).message("수정이 완료되었습니다.").build() + ); + } + + /** + * Thumnail Path변경하고자 하는 경우 + * @return + */ + @PutMapping("/thumnail") + public ResponseEntity updateUserThumnailPath(@RequestBody MyPageUserUpdateRequest request) { + myPageService.updateUserThumbnailPath(request); + return ResponseEntity.ok( + CommonBooleanResultResponse.builder().result(true).message("수정이 완료되었습니다.").build() + ); + } + + /** + * 마이페이지 내가쓴 댓글, 게시물, 좋아요한 게시물의 리스트를 획득 할 수 있는 API + * @param searchType 대소문자 구분없이 "reply", "article", "like" + * @param page + * @param limit + * @param request + * @return + */ + @GetMapping("/mylist/{searchType}") + public ResponseEntity getList( + @PathVariable String searchType, + @RequestParam(defaultValue = "0", required = false) int page, + @RequestParam(defaultValue = "10", required = false) int limit, + HttpServletRequest request + ) { + return ResponseEntity.ok(myPageService.getMyCreatedList(page, limit, searchType, LanguageType.findTypeByRequest(request))); + } + +} diff --git a/src/main/java/HookKiller/server/user/dto/MyPageUserResponse.java b/src/main/java/HookKiller/server/user/dto/MyPageUserResponse.java new file mode 100644 index 0000000..45e87e0 --- /dev/null +++ b/src/main/java/HookKiller/server/user/dto/MyPageUserResponse.java @@ -0,0 +1,33 @@ +package HookKiller.server.user.dto; + +import HookKiller.server.user.entity.User; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class MyPageUserResponse { + private final Long userId; + private final String email; + private final String thumbnail; + private final String nickName; + + + @Builder + public MyPageUserResponse(Long userId, String email, String thumbnail, String nickName) { + this.userId = userId; + this.email = email; + this.thumbnail = thumbnail; + this.nickName = nickName; + } + + + + public static MyPageUserResponse from(User user) { + return MyPageUserResponse.builder() + .userId(user.getId()) + .email(user.getEmail()) + .thumbnail(user.getThumbnail()) + .nickName(user.getNickName()) + .build(); + } +} diff --git a/src/main/java/HookKiller/server/user/dto/MyPageUserUpdateRequest.java b/src/main/java/HookKiller/server/user/dto/MyPageUserUpdateRequest.java new file mode 100644 index 0000000..199e45b --- /dev/null +++ b/src/main/java/HookKiller/server/user/dto/MyPageUserUpdateRequest.java @@ -0,0 +1,17 @@ +package HookKiller.server.user.dto; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder +public class MyPageUserUpdateRequest { + + private Long userId; + private String email; + private String password; + private String thumbnail; + private String nickName; +} diff --git a/src/main/java/HookKiller/server/user/entity/User.java b/src/main/java/HookKiller/server/user/entity/User.java index 8881be0..7ae39ab 100644 --- a/src/main/java/HookKiller/server/user/entity/User.java +++ b/src/main/java/HookKiller/server/user/entity/User.java @@ -1,6 +1,7 @@ package HookKiller.server.user.entity; import HookKiller.server.common.AbstractTimeStamp; +import HookKiller.server.common.util.SecurityUtils; import HookKiller.server.user.type.LoginType; import HookKiller.server.user.type.Status; import HookKiller.server.user.type.UserRole; @@ -13,20 +14,19 @@ import jakarta.persistence.Id; import jakarta.persistence.Table; import lombok.AccessLevel; -import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; import lombok.ToString; @Entity @Getter +@Setter @ToString -@Builder -@AllArgsConstructor -@NoArgsConstructor(access = AccessLevel.PROTECTED) @Table(name = "tbl_user") +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class User extends AbstractTimeStamp { @Id @@ -80,20 +80,39 @@ public class User extends AbstractTimeStamp { // private String expoToken; + @Builder + public User( + String email, + String password, + String nickName, + String thumbnail, + UserRole role, + OauthInfo oauthInfo, + LoginType loginType, + Status status + ) { + this.email = email; + this.password = SecurityUtils.passwordEncoder.encode(password); + this.nickName = nickName; + this.thumbnail = thumbnail; + this.role = role; + this.status = status; + this.oauthInfo = oauthInfo; + this.loginType = loginType; + } @Enumerated(EnumType.STRING) - @Builder.Default private Status status = Status.ACTIVE; + @Enumerated(EnumType.STRING) + private LoginType loginType; + public void updateUserStatus(Status userStatus) { this.status = userStatus; } - @Enumerated(EnumType.STRING) - private LoginType loginType; -// -// @Column -// @ColumnDefault(value = "false") -// private Boolean isDeleted; + public void setPassword(String password) { + this.password = SecurityUtils.passwordEncoder.encode(password); + } } diff --git a/src/main/java/HookKiller/server/user/exception/UserException.java b/src/main/java/HookKiller/server/user/exception/UserException.java index 267d1cb..9f5a2ef 100644 --- a/src/main/java/HookKiller/server/user/exception/UserException.java +++ b/src/main/java/HookKiller/server/user/exception/UserException.java @@ -16,6 +16,7 @@ public enum UserException implements BaseErrorCode { USER_NOT_FOUND_ERROR(NOT_FOUND.value(), "User_404_1", "유저를 찾을 수 없습니다."), ALREADY_REGISTER_USER_ID_ERROR(BAD_REQUEST.value(), "User_400_2", "이미 등록된 유저 아이디입니다."), USER_ACCOUNT_NOT_ACTIVE(UNAUTHORIZED.value(), "User_401_1", "유저의 계정이 활성 상태가 아닙니다."), + USER_UPDATE_UNAUTHORIZED(UNAUTHORIZED.value(), "User_401_2", "권한이 존재하지 않습니다."), ; private final Integer statusCode; diff --git a/src/main/java/HookKiller/server/user/exception/UserUpdateUnAuthorizedException.java b/src/main/java/HookKiller/server/user/exception/UserUpdateUnAuthorizedException.java new file mode 100644 index 0000000..9915bde --- /dev/null +++ b/src/main/java/HookKiller/server/user/exception/UserUpdateUnAuthorizedException.java @@ -0,0 +1,15 @@ +package HookKiller.server.user.exception; + +import HookKiller.server.common.exception.BaseException; + +import static HookKiller.server.user.exception.UserException.USER_UPDATE_UNAUTHORIZED; + +public class UserUpdateUnAuthorizedException extends BaseException { + + public static final BaseException EXCEPTION = new UserUpdateUnAuthorizedException(); + + private UserUpdateUnAuthorizedException() { + super(USER_UPDATE_UNAUTHORIZED); + } +} + diff --git a/src/main/java/HookKiller/server/user/service/MyPageService.java b/src/main/java/HookKiller/server/user/service/MyPageService.java new file mode 100644 index 0000000..b722fa0 --- /dev/null +++ b/src/main/java/HookKiller/server/user/service/MyPageService.java @@ -0,0 +1,111 @@ +package HookKiller.server.user.service; + +import HookKiller.server.board.dto.ArticleRequestDto; +import HookKiller.server.board.dto.ReplyResponseDto; +import HookKiller.server.board.exception.ArticleContentNotFoundException; +import HookKiller.server.board.exception.ReplyContentNotFoundException; +import HookKiller.server.board.repository.ArticleContentRepository; +import HookKiller.server.board.repository.ArticleLikeRepository; +import HookKiller.server.board.repository.ArticleRepository; +import HookKiller.server.board.repository.ReplyContentRepository; +import HookKiller.server.board.repository.ReplyRepository; +import HookKiller.server.common.exception.BadTypeRequestException; +import HookKiller.server.common.type.LanguageType; +import HookKiller.server.common.util.UserUtils; +import HookKiller.server.user.dto.MyPageUserUpdateRequest; +import HookKiller.server.user.dto.MyPageUserResponse; +import HookKiller.server.user.entity.User; +import HookKiller.server.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +import static HookKiller.server.common.util.SecurityUtils.passwordEncoder; + +@Slf4j +@Service +@RequiredArgsConstructor +public class MyPageService { + + private final UserUtils userUtils; + private final ReplyRepository replyRepository; + private final ArticleRepository articleRepository; + private final ArticleLikeRepository articleLikeRepository; + private final ReplyContentRepository replyContentRepository; + private final ArticleContentRepository articleContentRepository; + + @Transactional(readOnly = true) + public MyPageUserResponse getMyPage() { + return MyPageUserResponse.from(userUtils.getUser()); + } + + @Transactional(propagation = Propagation.NESTED) + public void updateUserInfo(MyPageUserUpdateRequest request) { + User user = userUtils.getUser(); + + // 변경이 존재하는 경우에만 변경한다. + if(request.getEmail() != null && !user.getEmail().equalsIgnoreCase(request.getEmail())) + user.setEmail(request.getEmail()); + if (request.getPassword() != null && !passwordEncoder.matches(request.getPassword(), user.getPassword())) + user.setPassword(request.getPassword()); + if (request.getThumbnail() != null && !user.getThumbnail().equalsIgnoreCase(request.getThumbnail())) + user.setThumbnail(request.getThumbnail()); + if(request.getNickName() != null && !user.getNickName().equals(request.getNickName())) + user.setNickName(request.getNickName()); + } + + @Transactional(propagation = Propagation.NESTED) + public void updateUserThumbnailPath(MyPageUserUpdateRequest request) { + User user = userUtils.getUser(); + if (request.getThumbnail() != null && !user.getThumbnail().equalsIgnoreCase(request.getThumbnail())) + user.setThumbnail(request.getThumbnail()); + } + + + + @Transactional(readOnly = true) + public Object getMyCreatedList(int page, int limit, String searchType, LanguageType language) { + User user = userUtils.getUser(); + Pageable pageable = PageRequest.of(page, limit); + if(searchType.equalsIgnoreCase("ARTICLE")) { + return articleRepository.findAllByCreatedUserOrderByCreateAtDesc(user, pageable) + .stream().map( + article -> ArticleRequestDto.of(article, articleContentRepository + .findByArticleAndLanguage(article, language) + .orElseThrow(() -> ArticleContentNotFoundException.EXCEPTION)) + ).toList(); + } + + if(searchType.equalsIgnoreCase("REPLY")){ + return replyRepository.findAllByCreatedUserOrderByCreateAtDesc(user, pageable) + .stream() + .map(reply -> + ReplyResponseDto.of( + reply, + replyContentRepository.findByReplyAndLanguage(reply, language) + .orElseThrow(() -> ReplyContentNotFoundException.EXCEPTION) + ) + ).toList(); + } + + if(searchType.equalsIgnoreCase("LIKE")) { + return articleLikeRepository + .findAllByUserOrderByCreateAtDesc(user, pageable) + .map(articleLike -> ArticleRequestDto.of(articleLike.getArticle(), articleContentRepository + .findByArticleAndLanguage(articleLike.getArticle(), language) + .orElseThrow(() -> ArticleContentNotFoundException.EXCEPTION)) + ).toList(); + } + + throw BadTypeRequestException.EXCEPTION; + } + + + +} diff --git a/src/main/java/HookKiller/server/user/service/UserService.java b/src/main/java/HookKiller/server/user/service/UserService.java index 57fe1b5..7ed52f2 100644 --- a/src/main/java/HookKiller/server/user/service/UserService.java +++ b/src/main/java/HookKiller/server/user/service/UserService.java @@ -29,7 +29,6 @@ public class UserService { private final JwtTokenProvider jwtTokenProvider; private final UserRepository userRepository; - private final PasswordEncoder passwordEncoder; private final KakaoOauthHelper kakaoOauthHelper; private final OIDCHelper oidcHelper; private final KakaoOauthProperties kakaoOauthProperties; @@ -45,7 +44,7 @@ public ResponseEntity registerUser(@RequestBody SingUpRequest request) { User user = userRepository.save(User.builder() .email(request.getEmail()) - .password(passwordEncoder.encode(request.getPassword())) + .password(request.getPassword()) .nickName(request.getNickName()) .role(UserRole.valueOf(request.getRole())) .loginType(LoginType.DEFAULT)