Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#46] 내 문의 조회 기능 구현 #209

Merged
merged 7 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from 6 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
@@ -1,5 +1,6 @@
package org.example.backend.domain.board.repository;

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

import org.example.backend.domain.board.model.entity.ProductBoard;
Expand All @@ -10,4 +11,6 @@
public interface ProductBoardRepository extends JpaRepository<ProductBoard, Long>, ProductBoardRepositoryCustom {
@Query("SELECT pb FROM ProductBoard pb JOIN fetch pb.category WHERE pb.idx = :idx")
Optional<ProductBoard> findByIdx(Long idx);

List<ProductBoard> findByCompanyEmail(String companyEmail);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,50 @@
package org.example.backend.domain.qna.controller;

import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.example.backend.domain.qna.model.dto.AnswerDto;
import org.example.backend.domain.qna.service.AnswerService;
import org.example.backend.global.common.constants.BaseResponse;
import org.example.backend.global.common.constants.BaseResponseStatus;
import org.example.backend.global.exception.InvalidCustomException;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/qna/answer")
@RequiredArgsConstructor
public class AnswerController {

private final AnswerService answerService;

@Operation(summary = "답변 등록 API", description = "기업회원이 문의에 대한 답변을 등록합니다.")
@PostMapping("/create")
public BaseResponse createAnswer(@RequestBody AnswerDto.AnswerCreateRequest request, @AuthenticationPrincipal UserDetails userDetails) {
try {
String email = userDetails.getUsername(); // 인증된 기업회원 이메일

// 답변 등록 서비스 호출
answerService.createAnswer(request, email);
return new BaseResponse<>(BaseResponseStatus.SUCCESS);
} catch (InvalidCustomException e) {
return new BaseResponse<>(e.getStatus());
} catch (Exception e) {
return new BaseResponse<>(BaseResponseStatus.FAIL);
}
}

@Operation(summary = "답변 삭제 API", description = "답변을 삭제합니다.")
@DeleteMapping("/delete/{id}")
public BaseResponse deleteAnswer(@PathVariable Long id, @AuthenticationPrincipal UserDetails userDetails) {
try {
String email = userDetails.getUsername(); // 인증된 기업회원 이메일
answerService.deleteAnswer(id, email);
return new BaseResponse<>(BaseResponseStatus.SUCCESS);
} catch (InvalidCustomException e) {
return new BaseResponse<>(e.getStatus());
} catch (Exception e) {
return new BaseResponse<>(BaseResponseStatus.FAIL);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,28 +36,12 @@ public class QuestionController {
))

@PostMapping("/create")
public BaseResponse create(@RequestBody QuestionDto.QuestionCreateRequest request, @AuthenticationPrincipal UserDetails userDetails){
try{
String email = userDetails.getUsername(); // 인증된 사용자의 이메일 가져오기
public BaseResponse create(@RequestBody QuestionDto.QuestionCreateRequest request, @AuthenticationPrincipal UserDetails userDetails) {
String email = userDetails.getUsername(); // 인증된 사용자의 이메일 가져오기
Long productBoardIdx = request.getProductBoardIdx(); // Request Body로 전달받은 productBoardIdx 사용

if (request.getTitle() == null || request.getTitle().trim().isEmpty()) {
throw new InvalidCustomException(BaseResponseStatus.QNA_QUESTION_FAIL_EMPTY_TITLE);
}
if (request.getContent() == null || request.getContent().trim().isEmpty()) {
throw new InvalidCustomException(BaseResponseStatus.QNA_QUESTION_FAIL_EMPTY_CONTENT);
}

Long productBoardIdx = request.getProductBoardIdx(); // Request Body로 전달받은 productBoardIdx 사용

// 문의 등록 후 사용자 이름, 날짜, 답변 상태를 함께 응답
QuestionDto.QuestionCreateResponse response = questionService.createQuestion(request, email, productBoardIdx);
return new BaseResponse<>(response);

} catch (InvalidCustomException e){
return new BaseResponse<>(e.getStatus());
} catch (Exception e){
return new BaseResponse<>(BaseResponseStatus.FAIL);
}
QuestionDto.QuestionCreateResponse response = questionService.createQuestion(request, email, productBoardIdx);
return new BaseResponse<>(response);
}

@Operation(summary = "문의 목록 조회 API", description = "DB에 저장된 문의 목록을 반환합니다.")
Expand All @@ -66,4 +50,27 @@ public BaseResponse<List<QuestionDto.QuestionListResponse>> getQuestions() {
List<QuestionDto.QuestionListResponse> questionList = questionService.getQuestions();
return new BaseResponse<>(questionList);
}

@DeleteMapping("/delete/{id}")
public BaseResponse deleteQuestion(@PathVariable Long id, @AuthenticationPrincipal UserDetails userDetails) {
String email = userDetails.getUsername();
questionService.deleteQuestion(id, email);
return new BaseResponse<>(BaseResponseStatus.SUCCESS);
}

@Operation(summary = "문의 목록 조회 API", description = "로그인된 기업 회원이 자신의 게시글에 달린 문의만 조회합니다.")
@GetMapping("/list/company")
public BaseResponse<List<QuestionDto.QuestionListResponse>> getCompanyQuestions(@AuthenticationPrincipal UserDetails userDetails) {
String companyEmail = userDetails.getUsername(); // 인증된 기업 회원의 이메일 가져오기
List<QuestionDto.QuestionListResponse> questionList = questionService.getQuestionsByCompanyEmail(companyEmail);
return new BaseResponse<>(questionList);
}

@Operation(summary = "로그인된 사용자의 문의 목록 조회 API", description = "로그인된 사용자가 작성한 문의 목록만 조회합니다.")
@GetMapping("/list/my")
public BaseResponse<List<QuestionDto.QuestionListResponse>> getMyQuestions(@AuthenticationPrincipal UserDetails userDetails) {
String userEmail = userDetails.getUsername(); // 인증된 사용자의 이메일 가져오기
List<QuestionDto.QuestionListResponse> questionList = questionService.getQuestionsByUserEmail(userEmail);
return new BaseResponse<>(questionList);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,42 @@
package org.example.backend.domain.qna.model.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.example.backend.domain.company.model.entity.Company;
import org.example.backend.domain.qna.model.entity.Answer;
import org.example.backend.domain.qna.model.entity.Question;

import java.time.LocalDateTime;

public class AnswerDto {
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class AnswerCreateRequest {
private Long questionIdx;
private String content;

public Answer toEntity(Company company, Question question) {
return Answer.builder()
.content(this.content)
.company(company)
.question(question)
.createdAt(LocalDateTime.now())
.build();
}
}

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class AnswerResponse {
private Long idx;
private String content;
private String companyName;
private LocalDateTime createdAt;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.example.backend.global.common.constants.AnswerStatus;

import java.time.LocalDateTime;
import java.util.List;

public class QuestionDto {

Expand Down Expand Up @@ -44,22 +45,32 @@ public Question toEntity(User user, ProductBoard productBoard) {
@AllArgsConstructor
@NoArgsConstructor
public static class QuestionCreateResponse{
private Long idx;
private String title;
private String content;
private String userName;
private String answerStatus;
private LocalDateTime createdAt;

private String answerContent;
private LocalDateTime answerCreatedAt;
}

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class QuestionListResponse {
private Long idx;
private String title;
private String content;
private String userName;
private String answerStatus;
private LocalDateTime createdAt;
private String email;
private Long productBoardIdx;
private String productTitle;

private List<AnswerDto.AnswerResponse> answers;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,45 @@
package org.example.backend.domain.qna.model.entity;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.example.backend.domain.company.model.entity.Company;
import org.example.backend.domain.qna.model.dto.AnswerDto;

import java.time.LocalDateTime;

@Entity
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Answer {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idx;

private String content;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "company_idx")
private Company company;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "question_idx")
private Question question;

private LocalDateTime createdAt;

// DTO 변환 메서드: 답변 조회 응답
public AnswerDto.AnswerResponse toResponse() {
return AnswerDto.AnswerResponse.builder()
.idx(this.idx)
.content(this.content)
.companyName(this.company.getName())
.createdAt(this.createdAt)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
package org.example.backend.domain.qna.model.entity;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.*;
import org.example.backend.domain.board.model.entity.ProductBoard;
import org.example.backend.domain.qna.model.dto.AnswerDto;
import org.example.backend.domain.qna.model.dto.QuestionDto;
import org.example.backend.domain.user.model.entity.User;
import org.example.backend.global.common.constants.AnswerStatus;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Entity
@Getter
Expand All @@ -37,19 +38,64 @@ public class Question {
@JoinColumn(name = "product_board_idx")
private ProductBoard productBoard;

@OneToMany(mappedBy = "question", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Answer> answers = new ArrayList<>();

@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;

@LastModifiedDate
@Column(insertable = false)
private LocalDateTime modifiedAt;

@Column(name = "answer_status")
private String answerStatus = AnswerStatus.ANSWER_WAITING.getStatus();

// DTO 변환 메서드: 문의 생성 후 응답
public QuestionDto.QuestionCreateResponse toCreateResponse() {
return QuestionDto.QuestionCreateResponse.builder()
.idx(this.idx)
.title(this.title)
.content(this.content)
.userName(this.user.getName())
.answerStatus(this.answerStatus)
.createdAt(this.createdAt)
.build();
}

// DTO 변환 메서드: 문의 목록 조회
public QuestionDto.QuestionListResponse toListResponse() {
List<AnswerDto.AnswerResponse> answerResponses = this.answers.stream()
.map(answer -> answer.toResponse())
.collect(Collectors.toList());

return QuestionDto.QuestionListResponse.builder()
.idx(this.idx)
.title(this.title)
.content(this.content)
.userName(this.user.getName())
.answerStatus(this.answerStatus)
.createdAt(this.createdAt)
.email(this.user.getEmail())
.answers(answerResponses) // 답변 리스트 포함
.productBoardIdx(getProductBoard().getIdx())
.productTitle(getProductBoard().getTitle())
.build();
}

// 답변이 등록 되면 문의 상태를 "답변완료"로 변경하는 메서드
// public void markAsAnswered(){
// this.answerStatus = AnswerStatus.ANSWER_COMPLETED.getStatus();
// }
public void markAsAnswered(){
this.answerStatus = AnswerStatus.ANSWER_COMPLETED.getStatus();
}

// 답변 상태를 "답변 대기"로 변경하는 메서드
public void markAsWaiting() {
this.answerStatus = AnswerStatus.ANSWER_WAITING.getStatus();
}

// 문의 수정 시에만 수정 시간(modifiedAt) 갱신
public void updateContent(String newContent) {
this.content = newContent;
this.modifiedAt = LocalDateTime.now(); // 문의가 수정될 때만 modifiedAt 갱신
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
package org.example.backend.domain.qna.repository;

public interface AnswerRepository {
import org.example.backend.domain.qna.model.entity.Answer;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface AnswerRepository extends JpaRepository<Answer, Long> {
// 특정 문의에 달린 모든 답변을 리스트로 반환
List<Answer> findAllByQuestionIdx(Long questionIdx);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package org.example.backend.domain.qna.repository;

import org.example.backend.domain.board.model.entity.ProductBoard;
import org.example.backend.domain.qna.model.entity.Question;
import org.example.backend.domain.user.model.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface QuestionRepository extends JpaRepository<Question, Long> {
List<Question> findByProductBoardIn(List<ProductBoard> productBoards);
List<Question> findByUser(User user);
}
Loading