diff --git a/README.md b/README.md index c7584e0..4368e4a 100644 --- a/README.md +++ b/README.md @@ -1 +1,4 @@ # UMC Spring A팀 레포지토리입니다. +# erd +![스크린샷 2024-07-02 175331](https://github.com/UMC-CAU-6th/Spring-A/assets/65022037/c632642c-ba27-464d-862a-5d9f26be0bd2) + diff --git a/umc/build.gradle b/umc/build.gradle index 73dda60..fe79c72 100644 --- a/umc/build.gradle +++ b/umc/build.gradle @@ -22,25 +22,34 @@ repositories { } dependencies { - + //implementation group: 'com.mysql', name: 'mysql-connector-j', version: '8.0.33' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-validation' + testImplementation 'junit:junit:4.13.1' + compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' - implementation 'org.springframework.boot:spring-boot-starter-validation' + + runtimeOnly 'com.mysql:mysql-connector-j' - implementation group: 'org.hibernate', name: 'hibernate-spatial', version: '5.6.15.Final' + implementation 'mysql:mysql-connector-java:8.0.33' // jwt implementation 'io.jsonwebtoken:jjwt-api:0.11.5' implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' + implementation 'org.springdoc:springdoc-openapi-ui:1.6.15' - implementation 'io.springfox:springfox-swagger2:2.9.2' - implementation 'io.springfox:springfox-swagger-ui:2.9.2' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' + + //implementation 'io.springfox:springfox-swagger2:3.0.0' + implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap' + //implementation 'io.springfox:springfox-swagger-ui:2.9.2' } diff --git a/umc/src/main/java/com/umc/common/config/SwaggerConfig.java b/umc/src/main/java/com/umc/common/config/SwaggerConfig.java index c2a158a..7720770 100644 --- a/umc/src/main/java/com/umc/common/config/SwaggerConfig.java +++ b/umc/src/main/java/com/umc/common/config/SwaggerConfig.java @@ -15,8 +15,8 @@ public class SwaggerConfig { @Bean public OpenAPI SchrodingerApi() { Info info = new Info() - .title("BuddyU API") - .description("BuddyU API 명세서") + .title("제목") + .description("명세서 설명") .version("1.0.0"); String jwtSchemeName = "JWT TOKEN"; diff --git a/umc/src/main/java/com/umc/common/entity/BaseTimeEntity.java b/umc/src/main/java/com/umc/common/entity/BaseTimeEntity.java index 15bb6ab..f0c808e 100644 --- a/umc/src/main/java/com/umc/common/entity/BaseTimeEntity.java +++ b/umc/src/main/java/com/umc/common/entity/BaseTimeEntity.java @@ -2,15 +2,17 @@ import java.time.LocalDateTime; -import javax.persistence.Column; -import javax.persistence.EntityListeners; -import javax.persistence.MappedSuperclass; +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; import lombok.Getter; +import lombok.Setter; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; @Getter +@Setter @MappedSuperclass @EntityListeners(AuditingEntityListener.class) // Auditing 기능 포함 public abstract class BaseTimeEntity { diff --git a/umc/src/main/java/com/umc/domain/board/controller/BoardController.java b/umc/src/main/java/com/umc/domain/board/controller/BoardController.java new file mode 100644 index 0000000..49e48d4 --- /dev/null +++ b/umc/src/main/java/com/umc/domain/board/controller/BoardController.java @@ -0,0 +1,31 @@ +package com.umc.domain.board.controller; + +import com.umc.common.response.ApiResponse; +import com.umc.domain.board.dto.BoardResponseDTO; +import com.umc.domain.board.service.BoardService; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@Slf4j +@RequiredArgsConstructor +@RequestMapping("/api/board") +@RestController +public class BoardController { + + private final BoardService boardService; + + @CrossOrigin + @Operation(summary = "모든 게시판 조회 API") + @GetMapping("/") + public ApiResponse> getALLBoards() { + List boards = boardService.getAllBoards(); + return ApiResponse.onSuccess(boards); + } +} diff --git a/umc/src/main/java/com/umc/domain/board/dto/BoardRequestDTO.java b/umc/src/main/java/com/umc/domain/board/dto/BoardRequestDTO.java new file mode 100644 index 0000000..f524df9 --- /dev/null +++ b/umc/src/main/java/com/umc/domain/board/dto/BoardRequestDTO.java @@ -0,0 +1,9 @@ +package com.umc.domain.board.dto; + +import lombok.Data; + +@Data +public class BoardRequestDTO { + private Long board_id; + private String name; +} diff --git a/umc/src/main/java/com/umc/domain/board/dto/BoardResponseDTO.java b/umc/src/main/java/com/umc/domain/board/dto/BoardResponseDTO.java new file mode 100644 index 0000000..7fd799a --- /dev/null +++ b/umc/src/main/java/com/umc/domain/board/dto/BoardResponseDTO.java @@ -0,0 +1,9 @@ +package com.umc.domain.board.dto; + +import lombok.Data; + +@Data +public class BoardResponseDTO { + private Long board_id; + private String name; +} diff --git a/umc/src/main/java/com/umc/domain/board/entity/Board.java b/umc/src/main/java/com/umc/domain/board/entity/Board.java new file mode 100644 index 0000000..644aab7 --- /dev/null +++ b/umc/src/main/java/com/umc/domain/board/entity/Board.java @@ -0,0 +1,30 @@ +package com.umc.domain.board.entity; + +import com.umc.common.entity.BaseTimeEntity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "member") +public class Board extends BaseTimeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "board_id") + private Long id; + + @Column + private String name; + + + +} diff --git a/umc/src/main/java/com/umc/domain/board/repository/BoardRepository.java b/umc/src/main/java/com/umc/domain/board/repository/BoardRepository.java new file mode 100644 index 0000000..d00337f --- /dev/null +++ b/umc/src/main/java/com/umc/domain/board/repository/BoardRepository.java @@ -0,0 +1,11 @@ +package com.umc.domain.board.repository; + + +import com.umc.domain.board.entity.Board; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface BoardRepository extends JpaRepository { +} + diff --git a/umc/src/main/java/com/umc/domain/board/service/BoardService.java b/umc/src/main/java/com/umc/domain/board/service/BoardService.java new file mode 100644 index 0000000..0f24271 --- /dev/null +++ b/umc/src/main/java/com/umc/domain/board/service/BoardService.java @@ -0,0 +1,31 @@ +package com.umc.domain.board.service; + +import com.umc.domain.board.dto.BoardResponseDTO; +import com.umc.domain.board.entity.Board; +import com.umc.domain.board.repository.BoardRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class BoardService { + + private final BoardRepository boardRepository; + + public List getAllBoards() { + List boards = boardRepository.findAll(); + return boards.stream() + .map(this::convertToResponseDto) + .collect(Collectors.toList()); + } + + private BoardResponseDTO convertToResponseDto(Board board) { + BoardResponseDTO boardResponseDto = new BoardResponseDTO(); + boardResponseDto.setBoard_id(board.getId()); + boardResponseDto.setName(board.getName()); + return boardResponseDto; + } +} diff --git a/umc/src/main/java/com/umc/domain/comment/controller/CommentController.java b/umc/src/main/java/com/umc/domain/comment/controller/CommentController.java new file mode 100644 index 0000000..cc70648 --- /dev/null +++ b/umc/src/main/java/com/umc/domain/comment/controller/CommentController.java @@ -0,0 +1,50 @@ +package com.umc.domain.comment.controller; + +import com.umc.common.response.ApiResponse; +import com.umc.domain.comment.dto.CommentRequestDTO; +import com.umc.domain.comment.dto.CommentResponseDTO; +import com.umc.domain.comment.service.CommentService; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Slf4j +@RequiredArgsConstructor +@RequestMapping("/api/comment") +@RestController +public class CommentController { + + private final CommentService commentService; + + @CrossOrigin + @Operation(summary = "댓글 작성 API") + @PostMapping("/") + public ApiResponse addComment( + @RequestBody CommentRequestDTO commentRequestDTO, + @RequestParam Long postId, + @RequestParam Long memberId) { + CommentResponseDTO createdComment = commentService.addComment(commentRequestDTO, postId, memberId); + return ApiResponse.onSuccess(createdComment); + } + + @CrossOrigin + @Operation(summary = "댓글 수정 API") + @PostMapping("/{comment_id}") + public ApiResponse modifyComment( + @PathVariable("comment_id") Long commentId, + @RequestBody CommentRequestDTO commentRequestDTO) { + CommentResponseDTO updatedComment = commentService.modifyComment(commentId, commentRequestDTO); + return ApiResponse.onSuccess(updatedComment); + } + + @CrossOrigin + @Operation(summary = "특정 게시물 댓글 읽기 API") + @GetMapping("/{post_id}") + public ApiResponse> getComments(@PathVariable("post_id") Long postId) { + List comments = commentService.getCommentsByPostId(postId); + return ApiResponse.onSuccess(comments); + } +} diff --git a/umc/src/main/java/com/umc/domain/comment/dto/CommentRequestDTO.java b/umc/src/main/java/com/umc/domain/comment/dto/CommentRequestDTO.java new file mode 100644 index 0000000..cc4eb69 --- /dev/null +++ b/umc/src/main/java/com/umc/domain/comment/dto/CommentRequestDTO.java @@ -0,0 +1,15 @@ +package com.umc.domain.comment.dto; + +import jakarta.validation.constraints.Pattern; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +@Builder +@Data +public class CommentRequestDTO { + + @Pattern(regexp = "^[a-zA-z0-9]{1,50}$", message = "1 ~ 50자로 작성해 주십시오.") + private String body; +} diff --git a/umc/src/main/java/com/umc/domain/comment/dto/CommentResponseDTO.java b/umc/src/main/java/com/umc/domain/comment/dto/CommentResponseDTO.java new file mode 100644 index 0000000..f885375 --- /dev/null +++ b/umc/src/main/java/com/umc/domain/comment/dto/CommentResponseDTO.java @@ -0,0 +1,20 @@ +package com.umc.domain.comment.dto; + +import lombok.Builder; +import lombok.Data; +import lombok.Getter; + +import java.time.LocalDateTime; + +@Builder +@Getter +@Data +public class CommentResponseDTO { + private Long comment_id; + private Long post_id; + private Long member_id; + private String nickname; + private String body; + private LocalDateTime createAt; + private LocalDateTime updateAt; +} diff --git a/umc/src/main/java/com/umc/domain/comment/entity/Comment.java b/umc/src/main/java/com/umc/domain/comment/entity/Comment.java new file mode 100644 index 0000000..e2f856c --- /dev/null +++ b/umc/src/main/java/com/umc/domain/comment/entity/Comment.java @@ -0,0 +1,33 @@ +package com.umc.domain.comment.entity; + +import com.umc.common.entity.BaseTimeEntity; +import com.umc.domain.post.entity.Post; +import com.umc.domain.user.entity.Member; +import jakarta.persistence.*; +import lombok.*; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "comment") +public class Comment extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "comment_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "post_id", nullable = false) + private Post post; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + private Member member; + + @Column(name = "body", length = 50, nullable = false) + private String body; + +} diff --git a/umc/src/main/java/com/umc/domain/comment/repository/CommentRepository.java b/umc/src/main/java/com/umc/domain/comment/repository/CommentRepository.java new file mode 100644 index 0000000..84e93b1 --- /dev/null +++ b/umc/src/main/java/com/umc/domain/comment/repository/CommentRepository.java @@ -0,0 +1,11 @@ +package com.umc.domain.comment.repository; + +import com.umc.domain.comment.entity.Comment; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +@Repository +public interface CommentRepository extends JpaRepository { + List findByPostId(Long postId); +} diff --git a/umc/src/main/java/com/umc/domain/comment/service/CommentService.java b/umc/src/main/java/com/umc/domain/comment/service/CommentService.java new file mode 100644 index 0000000..6dae08c --- /dev/null +++ b/umc/src/main/java/com/umc/domain/comment/service/CommentService.java @@ -0,0 +1,72 @@ +package com.umc.domain.comment.service; + +import com.umc.domain.comment.dto.CommentRequestDTO; +import com.umc.domain.comment.dto.CommentResponseDTO; +import com.umc.domain.comment.entity.Comment; +import com.umc.domain.comment.repository.CommentRepository; +import com.umc.domain.post.entity.Post; +import com.umc.domain.user.entity.Member; +import com.umc.domain.post.repository.PostsRepository; +import com.umc.domain.user.repository.MemberRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class CommentService { + + private final CommentRepository commentRepository; + private final PostsRepository postRepository; + private final MemberRepository memberRepository; + + public CommentResponseDTO addComment(CommentRequestDTO commentRequestDTO, Long postId, Long memberId) { + Post post = postRepository.findById(postId) + .orElseThrow(() -> new RuntimeException("Post not found with id: " + postId)); + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new RuntimeException("Member not found with id: " + memberId)); + + Comment comment = Comment.builder() + .post(post) + .member(member) + .body(commentRequestDTO.getBody()) + .build(); + + Comment savedComment = commentRepository.save(comment); + return convertToResponseDto(savedComment); + } + + public CommentResponseDTO modifyComment(Long commentId, CommentRequestDTO commentRequestDTO) { + Optional existingComment = commentRepository.findById(commentId); + if (existingComment.isPresent()) { + Comment updatedComment = existingComment.get(); + updatedComment.setBody(commentRequestDTO.getBody()); + Comment savedComment = commentRepository.save(updatedComment); + return convertToResponseDto(savedComment); + } else { + throw new RuntimeException("Comment not found with id: " + commentId); + } + } + + public List getCommentsByPostId(Long postId) { + List comments = commentRepository.findByPostId(postId); + return comments.stream() + .map(this::convertToResponseDto) + .collect(Collectors.toList()); + } + + private CommentResponseDTO convertToResponseDto(Comment comment) { + return CommentResponseDTO.builder() + .comment_id(comment.getId()) + .post_id(comment.getPost().getId()) + .member_id(comment.getMember().getId()) + .nickname(comment.getMember().getNickname()) + .body(comment.getBody()) + .createAt(comment.getCreatedAt()) + .updateAt(comment.getModifiedAt()) + .build(); + } +} diff --git a/umc/src/main/java/com/umc/domain/like/controller/LikeController.java b/umc/src/main/java/com/umc/domain/like/controller/LikeController.java new file mode 100644 index 0000000..9bf712e --- /dev/null +++ b/umc/src/main/java/com/umc/domain/like/controller/LikeController.java @@ -0,0 +1,40 @@ +package com.umc.domain.like.controller; + +import com.umc.common.response.ApiResponse; +import com.umc.domain.like.dto.LikeRequestDTO; +import com.umc.domain.like.service.LikeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/likes") +public class LikeController { + + + @Autowired + private LikeService likeService; + + + @GetMapping("/count/{postId}") + public ApiResponse countLikesByPostId(@PathVariable Long postId) { + long likeCount = likeService.countLikesByPostId(postId); + return ApiResponse.onSuccess(likeCount); + } + + + @GetMapping("/isLiked") + public ApiResponse isPostLikedByUser(@RequestParam Long memberId, @RequestParam Long postId) { + boolean isLiked = likeService.isPostLikedByUser(memberId, postId); + return ApiResponse.onSuccess(isLiked); + } + + + @PostMapping("/toggle") + public ApiResponse toggleLike(@RequestBody LikeRequestDTO likeDTO) { + boolean isLiked = likeService.toggleLike(likeDTO.getMember_id(), likeDTO.getPost_id()); + String message = isLiked ? "Post liked successfully" : "Post unliked successfully"; + return ApiResponse.onSuccess(isLiked); + } +} + diff --git a/umc/src/main/java/com/umc/domain/like/dto/LikeRequestDTO.java b/umc/src/main/java/com/umc/domain/like/dto/LikeRequestDTO.java new file mode 100644 index 0000000..a88bf1c --- /dev/null +++ b/umc/src/main/java/com/umc/domain/like/dto/LikeRequestDTO.java @@ -0,0 +1,14 @@ +package com.umc.domain.like.dto; + +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +@Data +@Getter +@Setter +public class LikeRequestDTO { + private Long member_id; + private Long post_id; + +} diff --git a/umc/src/main/java/com/umc/domain/like/dto/LikeResponseDTO.java b/umc/src/main/java/com/umc/domain/like/dto/LikeResponseDTO.java new file mode 100644 index 0000000..6bdb370 --- /dev/null +++ b/umc/src/main/java/com/umc/domain/like/dto/LikeResponseDTO.java @@ -0,0 +1,10 @@ +package com.umc.domain.like.dto; + +import lombok.Data; + +@Data +public class LikeResponseDTO { + private Long member_id; + private Long post_id; + private boolean liked; +} diff --git a/umc/src/main/java/com/umc/domain/like/entity/Like.java b/umc/src/main/java/com/umc/domain/like/entity/Like.java new file mode 100644 index 0000000..7899b80 --- /dev/null +++ b/umc/src/main/java/com/umc/domain/like/entity/Like.java @@ -0,0 +1,32 @@ +package com.umc.domain.like.entity; + +import com.umc.common.entity.BaseTimeEntity; +import com.umc.domain.post.entity.Post; +import com.umc.domain.user.entity.Member; +import jakarta.persistence.*; +import lombok.*; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "`like`") +public class Like extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "comment_id") + private Long id; + + @Column + private boolean liked; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "post_id", nullable = false) + private Post post; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + private Member member; +} diff --git a/umc/src/main/java/com/umc/domain/like/repository/LikeRepository.java b/umc/src/main/java/com/umc/domain/like/repository/LikeRepository.java new file mode 100644 index 0000000..5ce7891 --- /dev/null +++ b/umc/src/main/java/com/umc/domain/like/repository/LikeRepository.java @@ -0,0 +1,12 @@ +package com.umc.domain.like.repository; + +import com.umc.domain.like.entity.Like; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +public interface LikeRepository extends JpaRepository { + long countByPostId(Long postId); + boolean existsByMemberIdAndPostId(Long memberId, Long postId); + Like findByMemberIdAndPostId(Long memberId, Long postId); + void deleteByMemberIdAndPostId(Long memberId, Long postId); +} diff --git a/umc/src/main/java/com/umc/domain/like/service/LikeService.java b/umc/src/main/java/com/umc/domain/like/service/LikeService.java new file mode 100644 index 0000000..ccf4948 --- /dev/null +++ b/umc/src/main/java/com/umc/domain/like/service/LikeService.java @@ -0,0 +1,57 @@ +package com.umc.domain.like.service; + +import com.umc.domain.like.entity.Like; +import com.umc.domain.like.repository.LikeRepository; +import com.umc.domain.post.entity.Post; +import com.umc.domain.post.repository.PostsRepository; +import com.umc.domain.user.entity.Member; +import com.umc.domain.user.repository.MemberRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class LikeService { + + @Autowired + private LikeRepository likeRepository; + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private PostsRepository postsRepository; + + public long countLikesByPostId(Long postId) { + return likeRepository.countByPostId(postId); + } + + public boolean isPostLikedByUser(Long memberId, Long postId) {//레코드생성 + return likeRepository.existsByMemberIdAndPostId(memberId, postId); + } + + + public boolean toggleLike(Long memberId, Long postId) {//레코드수정 + Like like = likeRepository.findByMemberIdAndPostId(memberId, postId); + if (like == null) { + Member member = memberRepository.findById(memberId).orElseThrow(() -> new IllegalArgumentException("Invalid member ID")); + Post post = postsRepository.findById(postId).orElseThrow(() -> new IllegalArgumentException("Invalid post ID")); + like = Like.builder() + .member(member) + .post(post) + .liked(true) + .build(); + likeRepository.save(like); + return true; //좋아요 눌림 + } else { + if (like.isLiked()) { + likeRepository.deleteByMemberIdAndPostId(memberId, postId); // 좋아요 취소: 레코드 삭제 + return false; + } else { + like.setLiked(true); //좋아요 눌림: 플래그 업데이트 + likeRepository.save(like); + return true; + } + } + } + +} \ No newline at end of file diff --git a/umc/src/main/java/com/umc/domain/post/controller/PostsController.java b/umc/src/main/java/com/umc/domain/post/controller/PostsController.java new file mode 100644 index 0000000..06f7a0c --- /dev/null +++ b/umc/src/main/java/com/umc/domain/post/controller/PostsController.java @@ -0,0 +1,88 @@ +package com.umc.domain.post.controller; + +import com.umc.common.response.ApiResponse; +import com.umc.domain.post.dto.PostRequestDTO; +import com.umc.domain.post.dto.PostResponseDTO; +import com.umc.domain.post.dto.PostResponseListDTO; +import com.umc.domain.post.entity.Post; +import com.umc.domain.post.service.PostsService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.web.bind.annotation.*; +import org.springframework.data.domain.Pageable; +import java.util.List; +import java.util.Optional; + +@Slf4j +@RequiredArgsConstructor +@RequestMapping("/api/posts") +@RestController +public class PostsController { + + private final PostsService postsService; + + @CrossOrigin + @Operation(summary = "게시글 작성 API") + @PostMapping("/") + public ApiResponse addPosts(@RequestBody PostRequestDTO postRequestDTO) { + PostResponseDTO createdPost = postsService.addPost(postRequestDTO); + return ApiResponse.onSuccess(createdPost); + } + + @CrossOrigin + @Operation(summary = "게시글 수정 API") + @PostMapping("/{post_id}") + public ApiResponse modifyPosts(@PathVariable("post_id") Long postId, @RequestBody PostRequestDTO postRequestDTO) { + PostResponseDTO updatedPost = postsService.modifyPost(postId, postRequestDTO); + return ApiResponse.onSuccess(updatedPost); + } + + @CrossOrigin + @Operation(summary = "게시글 목록 읽기 API") + @GetMapping("/") + public ApiResponse> getAllPosts() { + List posts = postsService.getAllPosts(); + return ApiResponse.onSuccess(posts); + } + + @CrossOrigin + @Operation(summary = "게시글 세부내용 API") + @GetMapping("/{post_id}") + public ApiResponse getPosts(@PathVariable("post_id") Long postId) { + PostResponseDTO post = postsService.getPost(postId); + return ApiResponse.onSuccess(post); + } + + //paging + @CrossOrigin + @Operation(summary = "특정 게시판의 게시물 목록 조회 API", description = "특정 게시판의 게시물 목록 조회 API이며, 페이징을 포함합니다. query String 으로 page 번호를 주세요") + @GetMapping("/{board_id}/posts") + @Parameter(name = "board_id", description = "게시판 id, path variable 입니다") + public ApiResponse getBoardPosts(@PathVariable(name = "board_id") Long boardId, + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "10") int size) { + Pageable pageable = PageRequest.of(page, size); + Optional> postPageOptional = postsService.getPostsByBoardId(boardId, pageable); + + + if (postPageOptional.isPresent()) { + Page postPage = postPageOptional.get(); + PostResponseListDTO responseListDTO = new PostResponseListDTO(postPage.getContent(), postPage.getNumber(), postPage.getTotalPages(), postPage.getTotalElements(), postPage.isFirst(), postPage.isLast()); + return ApiResponse.onSuccess(responseListDTO); + } else { + return ApiResponse.onFailure("NOT_FOUND", "Board not found", null); + } + } + + @CrossOrigin + @Operation(summary = "게시글 삭제 API") + @DeleteMapping("/{post_id}") + public ApiResponse deletePosts(@PathVariable("post_id") Long postId) { + postsService.deletePost(postId); + return ApiResponse.onSuccess(null); + } +} diff --git a/umc/src/main/java/com/umc/domain/post/dto/PostRequestDTO.java b/umc/src/main/java/com/umc/domain/post/dto/PostRequestDTO.java new file mode 100644 index 0000000..1048c0d --- /dev/null +++ b/umc/src/main/java/com/umc/domain/post/dto/PostRequestDTO.java @@ -0,0 +1,13 @@ +package com.umc.domain.post.dto; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +@Data +public class PostRequestDTO { + @NotBlank(message = "제목은 필수 입력 값입니다.") + private String title; + + @NotBlank(message = "내용은 필수 입력 값입니다.") + private String body; +} diff --git a/umc/src/main/java/com/umc/domain/post/dto/PostResponseDTO.java b/umc/src/main/java/com/umc/domain/post/dto/PostResponseDTO.java new file mode 100644 index 0000000..1403a17 --- /dev/null +++ b/umc/src/main/java/com/umc/domain/post/dto/PostResponseDTO.java @@ -0,0 +1,38 @@ +package com.umc.domain.post.dto; + +import com.umc.domain.comment.dto.CommentResponseDTO; +import com.umc.domain.post.entity.Post; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + + +import java.time.LocalDateTime; +import java.util.List; +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class PostResponseDTO { + private Long post_id; + private Long board_id; + private Long member_id; + private String title; + private String body; + private LocalDateTime createdAt; + private LocalDateTime modifiedAt; + private List comments; + + public PostResponseDTO(Post post) { + this.post_id = post.getId(); + this.board_id = post.getBoard().getId(); + this.member_id = post.getMember().getId(); + this.title = post.getTitle(); + this.body = post.getBody(); + this.createdAt = post.getCreatedAt(); + this.modifiedAt = post.getModifiedAt(); + } + +} + diff --git a/umc/src/main/java/com/umc/domain/post/dto/PostResponseListDTO.java b/umc/src/main/java/com/umc/domain/post/dto/PostResponseListDTO.java new file mode 100644 index 0000000..1717343 --- /dev/null +++ b/umc/src/main/java/com/umc/domain/post/dto/PostResponseListDTO.java @@ -0,0 +1,32 @@ +package com.umc.domain.post.dto; + +import com.umc.domain.post.entity.Post; +import lombok.*; + +import java.util.List; +import java.util.stream.Collectors; + +@Builder +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class PostResponseListDTO { + List postList; + Integer listSize; + Integer totalPage; + Long totalElements; + Boolean isFirst; + Boolean isLast; + + public PostResponseListDTO(List content, int number, int totalPages, long totalElements, boolean isFirst, boolean isLast) { + this.postList = content.stream() + .map(PostResponseDTO::new) + .collect(Collectors.toList()); + this.listSize = content.size(); + this.totalPage = totalPages; + this.totalElements = totalElements; + this.isFirst = isFirst; + this.isLast = isLast; + } + +} diff --git a/umc/src/main/java/com/umc/domain/post/entity/Post.java b/umc/src/main/java/com/umc/domain/post/entity/Post.java new file mode 100644 index 0000000..54125af --- /dev/null +++ b/umc/src/main/java/com/umc/domain/post/entity/Post.java @@ -0,0 +1,42 @@ +package com.umc.domain.post.entity; + +import com.umc.common.entity.BaseTimeEntity; +import com.umc.domain.board.entity.Board; +import com.umc.domain.comment.entity.Comment; +import com.umc.domain.user.entity.Member; +import jakarta.persistence.*; +import lombok.*; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "post") +public class Post extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "post_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "board_id", nullable = false) + private Board board; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + private Member member; + + @Column(name = "title", length = 20, nullable = false) + private String title; + + @Column(name = "body", length = 50, nullable = false) + private String body; + + @OneToMany(mappedBy ="post", cascade = CascadeType.ALL) + private List comments = new ArrayList<>(); +} diff --git a/umc/src/main/java/com/umc/domain/post/repository/PostsRepository.java b/umc/src/main/java/com/umc/domain/post/repository/PostsRepository.java new file mode 100644 index 0000000..a3243c3 --- /dev/null +++ b/umc/src/main/java/com/umc/domain/post/repository/PostsRepository.java @@ -0,0 +1,13 @@ +package com.umc.domain.post.repository; + +import com.umc.domain.post.entity.Post; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; +@Repository +public interface PostsRepository extends JpaRepository { + Page findByBoard_Id(Long boardId, Pageable pageable); +} diff --git a/umc/src/main/java/com/umc/domain/post/service/PostsService.java b/umc/src/main/java/com/umc/domain/post/service/PostsService.java new file mode 100644 index 0000000..abd5469 --- /dev/null +++ b/umc/src/main/java/com/umc/domain/post/service/PostsService.java @@ -0,0 +1,80 @@ +package com.umc.domain.post.service; + +import com.umc.domain.board.entity.Board; +import com.umc.domain.board.repository.BoardRepository; +import com.umc.domain.post.dto.PostRequestDTO; +import com.umc.domain.post.dto.PostResponseDTO; +import com.umc.domain.post.dto.PostResponseListDTO; +import com.umc.domain.post.entity.Post; +import com.umc.domain.post.repository.PostsRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class PostsService { + + private final PostsRepository postsRepository; + private final BoardRepository boardRepository; + + public PostResponseDTO addPost(PostRequestDTO postRequestDTO) { + Post post = new Post(); + post.setTitle(postRequestDTO.getTitle()); + post.setBody(postRequestDTO.getBody()); + post.setCreatedAt(LocalDateTime.now()); + Post savedPost = postsRepository.save(post); + return convertToResponseDto(savedPost); + } + + public PostResponseDTO modifyPost(Long postId, PostRequestDTO postRequestDTO) { + Optional existingPost = postsRepository.findById(postId); + if (existingPost.isPresent()) { + Post updatedPost = existingPost.get(); + updatedPost.setTitle(postRequestDTO.getTitle()); + updatedPost.setBody(postRequestDTO.getBody()); + updatedPost.setModifiedAt(LocalDateTime.now()); + Post savedPost = postsRepository.save(updatedPost); + return convertToResponseDto(savedPost); + } else { + throw new RuntimeException("Post not found with id: " + postId); + } + } + + public List getAllPosts() { + List posts = postsRepository.findAll(); + return posts.stream() + .map(this::convertToResponseDto) + .collect(Collectors.toList()); + } + + public Optional> getPostsByBoardId(Long boardId, Pageable pageable){ + Optional board = boardRepository.findById(boardId); + return Optional.of(postsRepository.findByBoard_Id(boardId, pageable)); + } + + public PostResponseDTO getPost(Long postId) { + Post post = postsRepository.findById(postId) + .orElseThrow(() -> new RuntimeException("Post not found with id: " + postId)); + return convertToResponseDto(post); + } + + public void deletePost(Long postId) { + postsRepository.deleteById(postId); + } + + private PostResponseDTO convertToResponseDto(Post post) { + PostResponseDTO postResponseDto = new PostResponseDTO(); + postResponseDto.setPost_id(post.getId()); + postResponseDto.setTitle(post.getTitle()); + postResponseDto.setCreatedAt(post.getCreatedAt()); + postResponseDto.setModifiedAt(post.getModifiedAt()); + return postResponseDto; + } +} diff --git a/umc/src/main/java/com/umc/domain/user/dto/MemberLoginRequestDTO.java b/umc/src/main/java/com/umc/domain/user/dto/MemberLoginRequestDTO.java index af097dc..6401d8b 100644 --- a/umc/src/main/java/com/umc/domain/user/dto/MemberLoginRequestDTO.java +++ b/umc/src/main/java/com/umc/domain/user/dto/MemberLoginRequestDTO.java @@ -1,7 +1,7 @@ package com.umc.domain.user.dto; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.Pattern; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Pattern; import lombok.Builder; import lombok.Getter; diff --git a/umc/src/main/java/com/umc/domain/user/entity/Member.java b/umc/src/main/java/com/umc/domain/user/entity/Member.java index 271c7c1..9587743 100644 --- a/umc/src/main/java/com/umc/domain/user/entity/Member.java +++ b/umc/src/main/java/com/umc/domain/user/entity/Member.java @@ -5,7 +5,7 @@ import java.util.Collection; import java.util.List; import java.util.stream.Collectors; -import javax.persistence.*; +import jakarta.persistence.*; import com.umc.common.entity.BaseTimeEntity; import lombok.AllArgsConstructor;