Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.moongeul.backend.api.category.controller;

import com.moongeul.backend.api.category.dto.CategoryCreateRequestDTO;
import com.moongeul.backend.api.category.dto.CategoryListResponseDTO;
import com.moongeul.backend.api.category.dto.CategoryResponseDTO;
import com.moongeul.backend.api.category.service.CategoryService;
import com.moongeul.backend.common.response.ApiResponse;
import com.moongeul.backend.common.response.SuccessStatus;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;

@Tag(name = "Category", description = "Category(카테고리-기록) 관련 API 입니다.")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v2/category")
public class CategoryController {

private final CategoryService categoryService;

@Operation(
summary = "내 카테고리 전체 조회 API",
description = "사용자의 카테고리를 가져오는 API 입니다."
)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "카테고리 전체 조회 성공"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "카테고리명은 필수입니다. (category)"),
})
@GetMapping
public ResponseEntity<ApiResponse<CategoryListResponseDTO>> getCategory(@AuthenticationPrincipal UserDetails userDetails) {

CategoryListResponseDTO response = categoryService.getCategory(userDetails.getUsername());
return ApiResponse.success(SuccessStatus.GET_CATEGORY_SUCCESS, response);
}

@Operation(
summary = "카테고리 생성 API",
description = "기록에 대한 카테고리를 생성하는 API 입니다."
)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "카테고리 생성 성공"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "카테고리명은 필수입니다. (category)"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "해당 카테고리를 찾을 수 없습니다."),
})
@PostMapping("/create")
public ResponseEntity<ApiResponse<CategoryResponseDTO>> createCategory(@AuthenticationPrincipal UserDetails userDetails,
@Valid @RequestBody CategoryCreateRequestDTO categoryCreateRequestDTO) {

CategoryResponseDTO response = categoryService.createCategory(categoryCreateRequestDTO, userDetails.getUsername());
return ApiResponse.success(SuccessStatus.CREATE_CATEGORY_SUCCESS, response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.moongeul.backend.api.category.dto;

import com.moongeul.backend.api.member.entity.Member;
import com.moongeul.backend.api.category.entity.Category;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CategoryCreateRequestDTO {

@NotBlank(message = "카테고리명은 필수입니다")
private String category; // 카테고리명

public Category toEntity(Member member){

return Category.builder()
.title(this.category)
.member(member)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.moongeul.backend.api.category.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CategoryListResponseDTO {

private List<CategoryResponseDTO> categoryList; // 카테고리 리스트
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.moongeul.backend.api.category.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CategoryResponseDTO {

private Long categoryId; // 카테고리 번호
private String title; // 카테고리 제목
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.moongeul.backend.api.post.entity;
package com.moongeul.backend.api.category.entity;

import com.moongeul.backend.api.member.entity.Member;
import jakarta.persistence.*;
Expand All @@ -19,7 +19,7 @@ public class Category {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; // 카테고리 id

private String title; // 카테고리 제목
private String title; // 카테고리명

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id", nullable = false)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.moongeul.backend.api.category.repository;


import com.moongeul.backend.api.member.entity.Member;
import com.moongeul.backend.api.category.entity.Category;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;
import java.util.Optional;

public interface CategoryRepository extends JpaRepository<Category, Long> {

Optional<List<Category>> findByMember(Member member);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.moongeul.backend.api.category.service;

import com.moongeul.backend.api.category.dto.CategoryCreateRequestDTO;
import com.moongeul.backend.api.category.dto.CategoryListResponseDTO;
import com.moongeul.backend.api.category.dto.CategoryResponseDTO;
import com.moongeul.backend.api.member.entity.Member;
import com.moongeul.backend.api.member.repository.MemberRepository;
import com.moongeul.backend.api.category.entity.Category;
import com.moongeul.backend.api.category.repository.CategoryRepository;
import com.moongeul.backend.common.exception.NotFoundException;
import com.moongeul.backend.common.response.ErrorStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class CategoryService {

private final MemberRepository memberRepository;
private final CategoryRepository categoryRepository;

/* 카테고리 생성 */
@Transactional
public CategoryResponseDTO createCategory(CategoryCreateRequestDTO categoryCreateRequestDTO, String email){

Member member = getMemberByEmail(email);

Category newCategory = categoryCreateRequestDTO.toEntity(member);
Category savedCategory = categoryRepository.save(newCategory);

return CategoryResponseDTO.builder()
.categoryId(savedCategory.getId())
.title(savedCategory.getTitle())
.build();
}

/* 내 카테고리 전체 조회 */
@Transactional
public CategoryListResponseDTO getCategory(String email){

Member member = getMemberByEmail(email);

List<Category> categoryList = categoryRepository.findByMember(member)
.orElseThrow(() -> new NotFoundException(ErrorStatus.CATEGORY_NOTFOUND_EXCEPTION.getMessage()));

List<CategoryResponseDTO> categoryResponseDTOList = categoryList.stream()
.map(category -> CategoryResponseDTO.builder()
.categoryId(category.getId())
.title(category.getTitle())
.build())
.toList();

return CategoryListResponseDTO.builder()
.categoryList(categoryResponseDTOList)
.build();
}

// 사용자 정보 가져오기 메서드
private Member getMemberByEmail(String email) {
return memberRepository.findByEmail(email)
.orElseThrow(() -> new NotFoundException(ErrorStatus.USER_NOTFOUND_EXCEPTION.getMessage()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class MemberController {

@Operation(
summary = "구글 로그인 API",
description = "구글 인가코드을 통해 사용자의 정보를 등록 및 토큰 + 역할을 발급합니다. (ROLE -> 처음사용자 : ROLE_GUEST, 일반사용자 : ROLE_USER, 관리자 : ROLE_ADMIN)"
description = "구글 인가코드을 통해 사용자의 정보를 등록 및 토큰 + 역할을 발급합니다. (ROLE -> 처음사용자 : GUEST, 일반사용자 : USER, 관리자 : ADMIN)"
)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "로그인 성공"),
Expand All @@ -44,7 +44,7 @@ public ResponseEntity<ApiResponse<LoginResponseDTO>> loginWithGoogle(@Valid @Req

@Operation(
summary = "카카오 로그인 API",
description = "카카오 인가코드을 통해 사용자의 정보를 등록 및 토큰 + 역할을 발급합니다. (ROLE -> 처음사용자 : ROLE_GUEST, 일반사용자 : ROLE_USER, 관리자 : ROLE_ADMIN)"
description = "카카오 인가코드을 통해 사용자의 정보를 등록 및 토큰 + 역할을 발급합니다. (ROLE -> 처음사용자 : GUEST, 일반사용자 : USER, 관리자 : ADMIN)"
)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "로그인 성공"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
// Request Header에서 토큰 정보 추출
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
log.info("Authorization Header Value: [{}]", bearerToken); // 💡 값 전체 출력 (로그 레벨 info 이상)

if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer")) {
return bearerToken.substring(7).trim();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

@Tag(name = "Post", description = "Post(게시글) 관련 API 입니다.")
@RestController
Expand All @@ -30,7 +27,8 @@ public class PostController {
summary = "글쓰기 API",
description = "기록(게시글)을 작성하는 글쓰기 API 입니다." +
"<br>필수: isbn, readDate / 선택: rating(default = 5.0), page(default = 300), content, quotes" +
"<br>선택 요소의 경우 입력되지 않았을 때 'null'로 전달 바랍니다."
"<br>선택 요소의 경우 입력되지 않았을 때 'null'로 전달 바랍니다." +
"<br><br>[enum] postVisibility -> 전체 공개 : PUBLIC, 팔로워 공개 : FOLLOWERS, 나만보기 : PRIVATE"
)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "글쓰기 성공"),
Expand All @@ -39,7 +37,7 @@ public class PostController {
})
@PostMapping("/create")
public ResponseEntity<ApiResponse<PostCreateResponseDTO>> createPost(@AuthenticationPrincipal UserDetails userDetails,
@Valid @RequestBody PostCreateRequestDTO postCreateRequestDTO) {
@Valid @RequestBody PostCreateRequestDTO postCreateRequestDTO) {

PostCreateResponseDTO response = postService.createPost(postCreateRequestDTO, userDetails.getUsername());
return ApiResponse.success(SuccessStatus.CREATE_POST_SUCCESS, response);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,32 @@

import com.moongeul.backend.api.book.entity.Book;
import com.moongeul.backend.api.member.entity.Member;
import com.moongeul.backend.api.category.entity.Category;
import com.moongeul.backend.api.post.entity.Post;
import com.moongeul.backend.api.post.entity.PostVisibility;
import com.moongeul.backend.api.post.entity.Quote;
import jakarta.validation.constraints.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PostCreateRequestDTO {

/* 드롭다운 - 필수 입력*/
@NotNull(message = "공개여부는 필수입니다")
private PostVisibility postVisibility; // 공개 여부

@NotNull(message = "카테고리는 필수입니다.")
private Long categoryId; // 카테고리 번호

/* 필수 입력 */
@NotBlank(message = "ISBN은 필수입니다")
Expand Down Expand Up @@ -45,13 +56,15 @@ public static class QuoteRequestDTO {
private Integer pageNumber; // 페이지 번호
}

public Post toEntity(Member member, Book book) {
public Post toEntity(Category category, Member member, Book book) {

// rating과 page가 null로 들어오면 기본값으로 대체
Double finalRating = (this.rating != null) ? this.rating : 5.0;
Integer finalPage = (this.page != null) ? this.page : 300;

Post post = Post.builder()
.postVisibility(this.postVisibility)
.category(category)
.readDate(this.readDate)
.rating(finalRating)
.page(finalPage)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package com.moongeul.backend.api.post.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PostCreateResponseDTO {

private Long postId; // 생성된 Post id
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.moongeul.backend.api.post.dto;

import com.moongeul.backend.api.book.dto.BookDTO;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PostResponseDTO {

// 필수
Expand Down
Loading