💡 게시물 기능
요구사항
PostMan API Docs 작성
https://documenter.getpostman.com/view/23682055/2s8ZDU6jkR
Open-Session-in-view 옵션: false (트랜잭션 내부의 프록시객체 초기화 필요)
연관관계 : Member 1 < - > Post N
@ManyToOne 단방향 매핑으로 진행
FetchType : Lazy
1. 게시물 생성
2. 게시물 수정
3. 게시물 단건조회
4. 게시물 전체조회(Pagenation)
5. 게시물 삭제
6. 조회수 카운트(Update Query)
게시물 생성
- 게시물 생성 시, HttpServletRequest를 파라미터로 받아
유저의 Role (Admin, User, Counselor)을 검증 후 게시글에 유저 or 상담사 할당 - 그럼 Post 엔티티 내부에 존재하는 Member, Counselor 중 하나는 Null이 들어가므로
@ManyToOne의 optional 옵션을, 기존의 false에서 기본값인 true로 변경해주었음
게시물 수정
- 쿼리파라미터로 Post의 식별자를 입력받아 Entity에서 생성한 update 메서드를 이용해 필드값 수정
- Member 프록시 객체 초기화 로직 필요
게시물 단건조회
- 쿼리파라미터로 Post의 식별자를 입력받아 조회
- Member 프록시 객체 초기화 로직 필요
- @Transactional(readOnly = true)
게시물 전체 조회
- Pagenation을 이용하여 쿼리파라미터로 page, size를 입력받아 페이지 출력
- Member 프록시 객체 초기화 로직 필요
- @Transactional(readOnly = true)
게시물 삭제
- 쿼리파라미터로 Post의 식별자를 입력받아 삭제
조회수 증가 로직
- Repository 인터페이스에 JQPL을 이용한 Update Query 작성
💡 소스코드
Entity
@Getter @Setter
@Entity
@NoArgsConstructor
@AllArgsConstructor
public class Post extends BaseEntity {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long postId;
@Column(nullable = false, length = 50)
private String title;
@Column(nullable = false)
private String content;
@Column(columnDefinition = "integer default 0" ,nullable = false)
private int views;
@Enumerated(value = EnumType.STRING)
@Column(nullable = false)
private Kind kinds;
// ------------------ 연관관계 매핑 ------------------
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "MEMBER_ID")
@JsonIgnore
private Member member;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "COUNSELOR_ID")
@JsonIgnore
private Counselor counselor;
public void setMember(Member member) {
this.member = member;
}
public void setCounselor(Counselor counselor) {
this.counselor = counselor;
}
public void update(String title, String content, Kind kinds) {
this.title = title;
this.content = content;
this.kinds = kinds;
}
// ------------------ Test 를 위한 팩토리 메서드 ------------------
private Post(String title, String content, Kind kinds, Member member) {
this.title = title;
this.content = content;
this.kinds = kinds;
this.member = member;
}
public static Post of(String title, String content, Kind kinds, Member member) {
return new Post(title,content,kinds,member);
}
// ------------------ Enum ------------------
public enum Kind {
GENERAL("일반"),
REVIEW("후기");
@Getter
private String status;
Kind(String status) {
this.status = status;
}
}
}
DTO
public class PostDto {
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class Post {
private String title;
private String content;
private Role role;
private com.server.seb41_main_11.domain.post.entity.Post.Kind kinds;
}
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class Patch {
private long postId;
private String title;
private String content;
private com.server.seb41_main_11.domain.post.entity.Post.Kind kinds;
public void setPostId(long postId) {
this.postId = postId;
}
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class SingleResponse {
private long postId;
private String title;
private String content;
private com.server.seb41_main_11.domain.post.entity.Post.Kind kinds;
private int views;
private String writer;
private LocalDateTime createdTime;
public static SingleResponse ofMember(com.server.seb41_main_11.domain.post.entity.Post post) {
return SingleResponse.builder()
.postId(post.getPostId())
.title(post.getTitle())
.content(post.getContent())
.kinds(post.getKinds())
.views(post.getViews())
.writer(post.getMember().getMemberName())
.createdTime(post.getCreateTime())
.build();
}
public static SingleResponse ofCounselor(com.server.seb41_main_11.domain.post.entity.Post post) {
return SingleResponse.builder()
.postId(post.getPostId())
.title(post.getTitle())
.content(post.getContent())
.kinds(post.getKinds())
.views(post.getViews())
.writer(post.getCounselor().getCounselorName())
.createdTime(post.getCreateTime())
.build();
}
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class MultiResponse {
private long postId;
private String title;
private com.server.seb41_main_11.domain.post.entity.Post.Kind kinds;
private int views;
private String writer;
private LocalDateTime createdTime;
public static MultiResponse of(com.server.seb41_main_11.domain.post.entity.Post post) {
return MultiResponse.builder()
.postId(post.getPostId())
.title(post.getTitle())
.kinds(post.getKinds())
.views(post.getViews())
.writer(post.getMember().getMemberName())
.createdTime(post.getCreateTime())
.build();
}
}
}
Mapper
@Mapper(componentModel = "spring")
public interface PostMapper {
Post postToEntity(PostDto.Post post);
Post patchToEntity(PostDto.Patch patch);
// 생성, 단건조회
default PostDto.SingleResponse entityToMemberSingleResponse(Post post) {
return PostDto.SingleResponse.ofMember(post);
}
default PostDto.SingleResponse entityToCounselorSingleResponse(Post post) {
return PostDto.SingleResponse.ofMember(post);
}
// 전체 조회
default List<PostDto.MultiResponse> entityToMultiResponse(List<Post> posts) {
List<PostDto.MultiResponse> list = new ArrayList<PostDto.MultiResponse>(posts.size());
for (Post a : posts) {
list.add(PostDto.MultiResponse.of(a));
}
return list;
}
}
Repository
public interface PostRepository extends JpaRepository<Post, Long> {
// @Modifying
// @Query("update Post p set p.views = p.views + 1 where p.postId = :postId")
// void updateViews(@Param("postId") Long postId);
@Query(value = "select p FROM Post p WHERE p.postId = :postId")
Optional<Post> findById(@Param("postId") Long postId);
}
Service
@Service
@Transactional
@RequiredArgsConstructor
public class PostService {
// ----------------- DI ---------------------
private final PostRepository postRepository;
private final MemberService memberService;
private final CounselorService counselorService;
// ----------------- DI ---------------------
// 멤버 글 등록
public Post createByMember(Post post, Member member) {
post.setMember(member);
return postRepository.save(post);
}
// 상담사 글 등록
public Post createByCounselor(Post post, Counselor counselor) {
post.setCounselor(counselor);
return postRepository.save(post);
}
// 멤버 글 수정
public Post updateByMember(Post post, HttpServletRequest httpServletRequest) {
Post findPost = findVerifiedPost(post.getPostId());
if(findPost.getMember().getMemberId() != memberService.getLoginMember(httpServletRequest).getMemberId()){
throw new BusinessException(ErrorCode.NO_RIGHT_EDIT);
}
findPost.update(post.getTitle(),post.getContent(),post.getKinds());
return postRepository.save(findPost);
}
// 상담사 글 수정
public Post updateByCounselor(Post post, HttpServletRequest httpServletRequest) {
Post findPost = findVerifiedPost(post.getPostId());
if (findPost.getCounselor().getCounselorId() != counselorService.getLoginCounselor(httpServletRequest).getCounselorId()) {
throw new BusinessException(ErrorCode.NO_RIGHT_EDIT);
}
findPost.update(post.getTitle(),post.getContent(),post.getKinds());
return postRepository.save(findPost);
}
//관리자 글 수정
public Post updateByAdmin(Post post) {
Post findPost = findVerifiedPost(post.getPostId());
findPost.update(post.getTitle(),post.getContent(),post.getKinds());
return postRepository.save(findPost);
}
// 글 1건 조회
// @Transactional(readOnly = true) (조회수 로직에서 막힘 set)
public Post find(long postId) {
Post post = findVerifiedPost(postId);
String memberName = post.getMember().getMemberName();
updateViews(postId);
return post;
}
// 글 전체 조회
@Transactional(readOnly = true)
public Page<Post> findAll(int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by("postId").descending());
Page<Post> pagePost = postRepository.findAll(pageable);
List<Post> listPost = pagePost.getContent();
for (Post a : listPost) {
if (Objects.equals(a.getMember().getStatus(), DELETE)) {
// a.setTitle("탈퇴한 회원의 글입니다." + a.getTitle());
a.getMember().setMemberName("탈퇴한 회원");
}
}
return pagePost;
}
//회원 글 삭제
public void deleteByMember(long postId, HttpServletRequest httpServletRequest) {
Post findPost = findVerifiedPost(postId);
Member member = memberService.getLoginMember(httpServletRequest);
if(findPost.getMember().getMemberId() != member.getMemberId()){
throw new BusinessException(ErrorCode.NO_RIGHT_DELETE);
}
postRepository.deleteById(postId);
}
//상담사 글 삭제
public void deleteByCounselor(long postId, HttpServletRequest httpServletRequest) {
Post findPost = findVerifiedPost(postId);
Counselor counselor = counselorService.getLoginCounselor(httpServletRequest);
if(findPost.getCounselor().getCounselorId() != counselor.getCounselorId()){
throw new BusinessException(ErrorCode.NO_RIGHT_DELETE);
}
postRepository.deleteById(postId);
}
//관리자 글 삭제
public void deleteByAdmin(long postId) {
postRepository.deleteById(postId);
}
// 조회수 증가 로직
public void updateViews(Long id) {
Post post = findVerifiedPost(id);
int findViews = post.getViews() + 1;
post.setViews(findViews);
postRepository.save(post);
}
// 글 검증
private Post findVerifiedPost(long postId) {
Optional<Post> optPost = Optional.of(postRepository.getReferenceById(postId));
Post findPost = optPost.orElseThrow(() -> new EntityNotFoundException("글이 없습니다"));
return findPost;
}
}
Controller
@RestController
@RequestMapping("/api/posts")
@Validated
@RequiredArgsConstructor
public class PostController {
private final PostService postService;
private final MemberService memberService;
private final CounselorService counselorService;
private final PostMapper mapper;
// 일반 유저가 글 등록
@PostMapping("/post")
public ResponseEntity createPost(@Valid @RequestBody PostDto.Post post,
HttpServletRequest request) {
Role role = memberService.getLoginRole(request);
if (role.equals(Role.USER)) {
Post findPostByMember = postService.createByMember(mapper.postToEntity(post), memberService.getLoginMember(request));
return new ResponseEntity<>(new SingleResponseDto<>(mapper.entityToMemberSingleResponse(findPostByMember)), HttpStatus.CREATED);
} else if (role.equals(Role.COUNSELOR)) {
Post findPostByCounselor = postService.createByCounselor(mapper.postToEntity(post), counselorService.getLoginCounselor(request));
return new ResponseEntity<>(new SingleResponseDto<>(mapper.entityToCounselorSingleResponse(findPostByCounselor)), HttpStatus.CREATED);
} else {
Post findPostByMember = postService.createByMember(mapper.postToEntity(post), memberService.getLoginMember(request));
return new ResponseEntity<>(new SingleResponseDto<>(mapper.entityToMemberSingleResponse(findPostByMember)), HttpStatus.CREATED);
}
}
@PatchMapping("/patch/{post-id}")
public ResponseEntity updatePost(@PathVariable("post-id") @Positive long postId,
@Valid @RequestBody PostDto.Patch patch,
HttpServletRequest httpServletRequest) {
patch.setPostId(postId);
Role role = memberService.getLoginRole(httpServletRequest);
if(role.equals(Role.USER)) {
Post update = postService.updateByMember(mapper.patchToEntity(patch), httpServletRequest);
return new ResponseEntity<>(new SingleResponseDto<>(mapper.entityToMemberSingleResponse(update)), HttpStatus.OK);
}else if(role.equals(Role.COUNSELOR)){
Post update = postService.updateByCounselor(mapper.patchToEntity(patch), httpServletRequest);
return new ResponseEntity<>(new SingleResponseDto<>(mapper.entityToMemberSingleResponse(update)), HttpStatus.OK);
}else{
Post update = postService.updateByAdmin(mapper.patchToEntity(patch));
return new ResponseEntity<>(new SingleResponseDto<>(mapper.entityToMemberSingleResponse(update)), HttpStatus.OK);
}
}
@GetMapping("/lookup/{post-id}")
public ResponseEntity findPost(@PathVariable("post-id") @Positive long postId) {
Post findPost = postService.find(postId);
return new ResponseEntity(new SingleResponseDto<>(mapper.entityToMemberSingleResponse(findPost)), HttpStatus.OK);
}
@GetMapping("/lookup/list")
public ResponseEntity findAllByPostId(@Positive @RequestParam(defaultValue = "1") int page,
@Positive @RequestParam(defaultValue = "10") int size) {
Page<Post> pagePost = postService.findAll(page-1, size);
List<Post> posts = pagePost.getContent();
return new ResponseEntity<>(new MultiResponseDto<>(mapper.entityToMultiResponse(posts), pagePost), HttpStatus.OK);
}
@DeleteMapping("/delete/{post-id}")
public ResponseEntity deletePost(@PathVariable("post-id") @Positive long postId,
HttpServletRequest httpServletRequest) {
Role role = memberService.getLoginRole(httpServletRequest);
if(role.equals(Role.USER)) {
postService.deleteByMember(postId, httpServletRequest);
}else if(role.equals(Role.COUNSELOR)){
postService.deleteByCounselor(postId, httpServletRequest);
}else{
postService.deleteByAdmin(postId);
}
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
'Project > Main Project' 카테고리의 다른 글
💻 기능 개발 - 상담사 (0) | 2023.01.27 |
---|---|
💻 기능 개발 - 공지사항 (0) | 2023.01.26 |
💻 기능 개발 - 회원 (0) | 2023.01.26 |
📄 API 명세서 - 상담프로그램 & 결제 & 예약 (0) | 2023.01.26 |
📄 API 명세서 - 상담사 & 게시물 & 공지사항 (0) | 2023.01.26 |