Skip to content

Commit

Permalink
Merge pull request #146 from sharemindteam/feature/139-post-get
Browse files Browse the repository at this point in the history
feat: 일대다 상담 질문 조회 구현
  • Loading branch information
letskuku authored Mar 18, 2024
2 parents 27c2188 + 1e245a8 commit c58b118
Show file tree
Hide file tree
Showing 13 changed files with 305 additions and 5 deletions.
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ dependencies {
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'

implementation "com.querydsl:querydsl-jpa:5.0.0:jakarta"
annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.example.sharemind.global.config;

import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@RequiredArgsConstructor
public class QueryDslConfig {

private final EntityManager entityManager;

@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.example.sharemind.post.domain.Post;
import com.example.sharemind.post.dto.request.PostCreateRequest;
import com.example.sharemind.post.dto.request.PostUpdateRequest;
import com.example.sharemind.post.dto.response.PostGetIsSavedResponse;
import com.example.sharemind.post.dto.response.PostGetResponse;
import java.util.List;

public interface PostService {
Expand All @@ -14,4 +16,10 @@ public interface PostService {
Post getPostByPostId(Long postId);

void updatePost(PostUpdateRequest postUpdateRequest, Long customerId);

PostGetIsSavedResponse getIsSaved(Long postId);

PostGetResponse getPost(Long postId);

List<PostGetResponse> getPostsByCustomer(Boolean filter, Long postId, Long customerId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import com.example.sharemind.post.domain.Post;
import com.example.sharemind.post.dto.request.PostCreateRequest;
import com.example.sharemind.post.dto.request.PostUpdateRequest;
import com.example.sharemind.post.dto.response.PostGetIsSavedResponse;
import com.example.sharemind.post.dto.response.PostGetResponse;
import com.example.sharemind.post.exception.PostErrorCode;
import com.example.sharemind.post.exception.PostException;
import com.example.sharemind.post.repository.PostRepository;
Expand All @@ -19,6 +21,8 @@
@Transactional(readOnly = true)
public class PostServiceImpl implements PostService {

private static final int POST_CUSTOMER_PAGE_SIZE = 4;

private final PostRepository postRepository;
private final CustomerService customerService;

Expand Down Expand Up @@ -52,4 +56,31 @@ public void updatePost(PostUpdateRequest postUpdateRequest, Long customerId) {
post.updatePost(consultCategory, postUpdateRequest.getTitle(),
postUpdateRequest.getContent(), postUpdateRequest.getIsCompleted(), customer);
}

@Override
public PostGetIsSavedResponse getIsSaved(Long postId) {
Post post = getPostByPostId(postId);

if ((post.getIsCompleted() != null) && !post.getIsCompleted()) {
return PostGetIsSavedResponse.of(post);
} else {
return PostGetIsSavedResponse.of();
}
}

@Override
public PostGetResponse getPost(Long postId) {
return PostGetResponse.of(getPostByPostId(postId));
}

@Override
public List<PostGetResponse> getPostsByCustomer(Boolean filter, Long postId, Long customerId) {
Customer customer = customerService.getCustomerByCustomerId(customerId);

return postRepository.findAllByCustomerAndIsActivatedIsTrue(customer, filter, postId,
POST_CUSTOMER_PAGE_SIZE).stream()
.map(post -> (post.getIsCompleted() != null && !post.getIsCompleted())
? PostGetResponse.ofIsNotCompleted(post) : PostGetResponse.of(post))
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
@RequiredArgsConstructor
public enum PostStatus {

WAITING("상담 대기"),
PROCEEDING("상담 진행 중"),
COMPLETED("상담 마감"),
WAITING("답변 대기"),
PROCEEDING("답변 진행 중"),
COMPLETED("답변 완료"),
CANCELLED("상담 취소"),
REPORTED("신고로 인한 게시 중단");

private final String displayName;
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/com/example/sharemind/post/domain/Post.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class Post extends BaseEntity {
@Enumerated(EnumType.STRING)
private ConsultCategory consultCategory;

@Size(max = 50, message = "제목은 최대 50자입니다.")
private String title;

@Size(max = 1000, message = "상담 내용은 최대 1000자입니다.")
Expand All @@ -61,6 +62,9 @@ public class Post extends BaseEntity {
@Column(name = "total_comment", nullable = false)
private Long totalComment;

@Column(name = "total_scrap", nullable = false)
private Long totalScrap;

@Column(name = "is_paid", nullable = false)
private Boolean isPaid;

Expand All @@ -75,6 +79,7 @@ public Post(Customer customer, Long cost, Boolean isPublic) {
this.postStatus = PostStatus.WAITING;
this.totalLike = 0L;
this.totalComment = 0L;
this.totalScrap = 0L;
setIsPaid(isPublic);
}

Expand Down Expand Up @@ -112,7 +117,7 @@ private void checkWriteAuthority(Customer customer) {
}

private void checkUpdatability() {
if (this.isCompleted.equals(true)) {
if (this.isCompleted != null && this.isCompleted.equals(true)) {
throw new PostException(PostErrorCode.POST_ALREADY_COMPLETED);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class PostUpdateRequest {

@Schema(description = "상담 제목", example = "남자친구의 심리가 궁금해요")
@NotBlank(message = "상담 제목은 공백일 수 없습니다.")
@Size(max = 50, message = "제목은 최대 50자입니다.")
private String title;

@Schema(description = "상담 내용", example = "안녕하세요 어쩌구저쩌구~")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.example.sharemind.post.dto.response;

import com.example.sharemind.post.domain.Post;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDateTime;
import lombok.Builder;
import lombok.Getter;

@Getter
public class PostGetIsSavedResponse {

@Schema(description = "임시저장 메시지 존재하면 true, 아니면 false", example = "true")
private final Boolean isSaved;

@Schema(description = "마지막 수정일시, isSaved false면 null", example = "2023년 12월 25일 오후 12시 34분", type = "string")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy년 MM월 dd일 a HH시 mm분")
private final LocalDateTime updatedAt;

@Builder
public PostGetIsSavedResponse(Boolean isSaved, LocalDateTime updatedAt) {
this.isSaved = isSaved;
this.updatedAt = updatedAt;
}

public static PostGetIsSavedResponse of(Post post) {
return PostGetIsSavedResponse.builder()
.isSaved(true)
.updatedAt(post.getUpdatedAt())
.build();
}

public static PostGetIsSavedResponse of() {
return PostGetIsSavedResponse.builder().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.example.sharemind.post.dto.response;

import com.example.sharemind.global.utils.TimeUtil;
import com.example.sharemind.post.domain.Post;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Getter;

@Getter
public class PostGetResponse {

@Schema(description = "일대다 질문 아이디")
private final Long postId;

@Schema(description = "상담 카테고리", example = "연애갈등")
private final String consultCategory;

@Schema(description = "제목")
private final String title;

@Schema(description = "상담 내용")
private final String content;

@Schema(description = "공개/비공개 여부")
private final Boolean isPublic;

@Schema(description = "좋아요 수")
private final Long totalLike;

@Schema(description = "스크랩 수")
private final Long totalScrap;

@Schema(description = "마지막 업데이트 일시", example = "8분 전")
private final String updatedAt;

@Builder
public PostGetResponse(Long postId, String consultCategory, String title, String content,
Boolean isPublic, Long totalLike, Long totalScrap, String updatedAt) {
this.postId = postId;
this.consultCategory = consultCategory;
this.title = title;
this.content = content;
this.isPublic = isPublic;
this.totalLike = totalLike;
this.totalScrap = totalScrap;
this.updatedAt = updatedAt;
}

public static PostGetResponse of(Post post) {
String consultCategory = post.getConsultCategory() == null ? null
: post.getConsultCategory().getDisplayName();

return PostGetResponse.builder()
.postId(post.getPostId())
.consultCategory(consultCategory)
.title(post.getTitle())
.content(post.getContent())
.isPublic(post.getIsPublic())
.totalLike(post.getTotalLike())
.totalScrap(post.getTotalScrap())
.updatedAt(TimeUtil.getUpdatedAt(post.getUpdatedAt()))
.build();
}

public static PostGetResponse ofIsNotCompleted(Post post) {
return PostGetResponse.builder()
.postId(post.getPostId())
.consultCategory(null)
.title(null)
.content(null)
.isPublic(post.getIsPublic())
.totalLike(post.getTotalLike())
.totalScrap(post.getTotalScrap())
.updatedAt(TimeUtil.getUpdatedAt(post.getUpdatedAt()))
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,29 @@
import com.example.sharemind.post.application.PostService;
import com.example.sharemind.post.dto.request.PostCreateRequest;
import com.example.sharemind.post.dto.request.PostUpdateRequest;
import com.example.sharemind.post.dto.response.PostGetIsSavedResponse;
import com.example.sharemind.post.dto.response.PostGetResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
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.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
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.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@Tag(name = "Post Controller", description = "일대다 상담 질문 컨트롤러")
Expand Down Expand Up @@ -73,4 +81,62 @@ public ResponseEntity<Void> updatePost(@Valid @RequestBody PostUpdateRequest pos
postService.updatePost(postUpdateRequest, customUserDetails.getCustomer().getCustomerId());
return ResponseEntity.ok().build();
}

@Operation(summary = "일대다 상담 질문 임시저장 내용 존재 여부 조회",
description = "임시저장 내용 존재 여부 조회")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "조회 성공(임시저장 내용 존재하지 않으면 수정일시는 null로 반환됨)"),
@ApiResponse(responseCode = "404", description = "존재하지 않는 일대다 질문 아이디로 요청됨",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = CustomExceptionResponse.class))
)
})
@Parameters({
@Parameter(name = "postId", description = "일대다 질문 아이디")
})
@GetMapping("/drafts/{postId}")
public ResponseEntity<PostGetIsSavedResponse> getIsSaved(@PathVariable Long postId) {
return ResponseEntity.ok(postService.getIsSaved(postId));
}

@Operation(summary = "일대다 상담 질문 단건 조회", description = "일대다 상담 질문 단건 조회")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "404", description = "존재하지 않는 일대다 질문 아이디로 요청됨",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = CustomExceptionResponse.class))
)
})
@Parameters({
@Parameter(name = "postId", description = "일대다 질문 아이디")
})
@GetMapping("/{postId}")
public ResponseEntity<PostGetResponse> getPost(@PathVariable Long postId) {
return ResponseEntity.ok(postService.getPost(postId));
}

@Operation(summary = "구매자 본인 일대다 상담 리스트 조회", description = """
- 구매자 상담 탭에서 본인이 작성한 일대다 상담 질문 리스트 조회
- 주소 형식: /api/v1/posts/customers?filter=true&postId=0""")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "404", description = "존재하지 않는 회원",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = CustomExceptionResponse.class))
)
})
@Parameters({
@Parameter(name = "filter", description = "완료/취소된 상담 제외: true, 포함: false"),
@Parameter(name = "postId", description = """
- 조회 결과는 4개씩 반환하며, postId로 구분
1. 최초 조회 요청이면 postId는 0
2. 2번째 요청부터 postId는 직전 요청의 조회 결과 4개 중 마지막 postId""")
})
@GetMapping("/customers")
public ResponseEntity<List<PostGetResponse>> getPostsByCustomer(@RequestParam Boolean filter,
@RequestParam Long postId,
@AuthenticationPrincipal CustomUserDetails customUserDetails) {
return ResponseEntity.ok(postService.getPostsByCustomer(filter, postId,
customUserDetails.getCustomer().getCustomerId()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.sharemind.post.repository;

import com.example.sharemind.customer.domain.Customer;
import com.example.sharemind.post.domain.Post;
import java.util.List;

public interface PostCustomRepository {

List<Post> findAllByCustomerAndIsActivatedIsTrue(Customer customer, Boolean filter,
Long postId, int size);
}
Loading

0 comments on commit c58b118

Please sign in to comment.