본문 바로가기
web/Bakery Shop Project

쇼핑몰 구현 12 - 리뷰 , Q&A 기능

by su0a 2024. 2. 23.

Review

 

1. ReviewRepository

@Repository
public interface ReviewRepository extends JpaRepository<Review,Long> {
    
    //해당 상품의 모든 리뷰 조회
    Page<Review> findAllByItemId(Long itemId, PageRequest pageRequest);

}

 

2. ReviewService

@Service
@RequiredArgsConstructor
public class ReviewService {
    private final ReviewRepository reviewRepository;
    private  final MemberService memberService;
    private final ItemService itemService;
    private final UploadImageService uploadImageService;
    private final MemberRepository memberRepository;

    public Page<Review> findReviewByItemId(Long itemId, PageRequest pageRequest){
        Page<Review> reviews = reviewRepository.findAllByItemId(itemId,pageRequest);
        return reviews;
    }

    //리뷰 저장
    @Transactional
    public Review saveReview(Long itemId, ReviewJoinRequest reviewJoinRequest, Authentication auth) throws IOException {
        Review saveReview = reviewRepository.save(ReviewJoinRequest.toEntity(reviewJoinRequest));
        Member loginMember = memberService.myInfo(auth.getName());
        Item item = itemService.findItem(itemId);
        saveReview.saveMemberItem(loginMember,item);
        UploadImage uploadImage = uploadImageService.saveReviewImage(reviewJoinRequest.getUploadImage(), saveReview);
        if(uploadImage!=null){
            saveReview.setUploadImage(uploadImage);
        }
        return saveReview;
    }

    //리뷰 삭제
    @Transactional
    public boolean deleteReview(Long reviewId){
        Review review = reviewRepository.findById(reviewId).get();
        Member member = review.getMember();
        if (member != null) {
            member.getReviews().remove(review);
            memberRepository.save(member); // 변경사항 저장
        }
        reviewRepository.delete(review);
        return true;
    }
}

 

3. ReviewController

@Controller
@RequiredArgsConstructor
@RequestMapping("/reviews")
public class ReviewController {
    private final ReviewService reviewService;
    private final ReviewRepository reviewRepository;
    private final MemberService memberService;
    private final ItemService itemService;
    @GetMapping("/{itemId}")
    public String ReviewListPage(@PathVariable Long itemId, Model model,
                                 @RequestParam(required = false,defaultValue = "1")int page,Authentication auth){
        if(auth!=null){
            model.addAttribute("loginUserLoginId",auth.getName());
        }
        PageRequest pageRequest = PageRequest.of(page - 1, 10, Sort.by("id").descending());
        Page<Review> reviews = reviewService.findReviewByItemId(itemId,pageRequest);
        ItemDto itemDto = itemService.findById(itemId);
        model.addAttribute("reviews",reviews);
        model.addAttribute("itemDto",itemDto);
        return "reviews/list";
    }

    @PostMapping("/{itemId}/{reviewId}/delete")
    public String ReviewDelete(@PathVariable Long itemId,@PathVariable Long reviewId, Model model){
        reviewService.deleteReview(reviewId);
        model.addAttribute("nextUrl","/reviews/"+itemId);
        model.addAttribute("message","리뷰가 삭제되었습니다!");
        return "printMessage";
    }
    @GetMapping("/{itemId}/write")
    public String ReviewWrite(@PathVariable Long itemId, Model model){
        ItemDto itemDto = itemService.findById(itemId);
        model.addAttribute("itemDto",itemDto);
        model.addAttribute("reviewJoinRequest",new ReviewJoinRequest());
        return "reviews/write";
    }
    @PostMapping("/{itemId}/write")
    public String  CreateReview(@PathVariable Long itemId, @ModelAttribute ReviewJoinRequest reviewJoinRequest,
                                Authentication auth, Model model)throws IOException {
        Review review = reviewService.saveReview(itemId, reviewJoinRequest, auth);
        model.addAttribute("nextUrl","/reviews/"+itemId);
        model.addAttribute("message","리뷰가 등록되었습니다!");
        return "printMessage";
    }
}

 

4. ReviewJoinRequest

@Data
public class ReviewJoinRequest {
    private Integer rating;

    private String title;
    private String content;

    private MultipartFile uploadImage;

    public static Review toEntity(ReviewJoinRequest reviewJoinRequest){
        return Review.builder()
                .rating(reviewJoinRequest.getRating())
                .title(reviewJoinRequest.getTitle())
                .content(reviewJoinRequest.getContent())
                .build();
    }
}

 

5. ReviewDto

@Data
@Builder
public class ReviewDto {
    private Long id;

    private Member member;

    private Integer rating;

    private String title;
    private String content;

    private UploadImage uploadImage;

    public static ReviewDto of(Review review){
        return ReviewDto.builder()
                .id(review.getId())
                .member(review.getMember())
                .title(review.getTitle())
                .content(review.getContent())
                .uploadImage(review.getUploadImage())
                .build();
    }
}

 

Q&A

 

1. QnARepository

@Repository
public interface QnARepository extends JpaRepository<QnA,Long> {
    
    //해당 상품의 모든 질문 조회
    Page<QnA> findAllByItemId(Long itemId, PageRequest pageRequest);
}

 

2. QnAService

@Service
@RequiredArgsConstructor
public class QnAService {
    private final QnARepository qnaRepository;
    private final MemberService memberService;
    private final MemberRepository memberRepository;
    private final ItemService itemService;
    private final UploadImageService uploadImageService;
    
    //Q&A 저장
    @Transactional
    public QnA saveQnA(Long itemId, QnAJoinRequest qnaJoinRequest, Authentication auth) throws IOException {
        QnA saveQnA = qnaRepository.save(QnAJoinRequest.toEntity(qnaJoinRequest));
        Member loginMember = memberService.myInfo(auth.getName());
        Item item = itemService.findItem(itemId);
        saveQnA.saveMemberItem(loginMember,item);
        UploadImage uploadImage = uploadImageService.saveQnAImage(qnaJoinRequest.getUploadImage(), saveQnA);
        if(uploadImage!=null){
            saveQnA.setUploadImage(uploadImage);
        }
        return saveQnA;
    }

    //상품 id로 Q&A 조회
    public Page<QnA> findQnAByItemId(Long itemId, PageRequest pageRequest) {
        Page<QnA> qnas = qnaRepository.findAllByItemId(itemId,pageRequest);
        return qnas;
    }

    //관리자가 모든 Q&A 보기 위해 사용
    public Page<QnAJoinRequest.QnADto> findAllQnA(PageRequest pageRequest){
        Page<QnA> allQnAs = qnaRepository.findAll(pageRequest);
        long count = qnaRepository.count();
        List<QnAJoinRequest.QnADto> qnaDtoList=new ArrayList<>();
        for(QnA qna:allQnAs){
            QnAJoinRequest.QnADto qnaDto = QnAJoinRequest.QnADto.of(qna);
            qnaDtoList.add(qnaDto);
        }
        return new PageImpl<>(qnaDtoList,pageRequest,count);
    }
    
    //Q&A 삭제
    @Transactional
    public void deleteQnA(Long qnaId) {
        QnA qna = qnaRepository.findById(qnaId).orElse(null);
        Member member = qna.getMember();
        if (member != null) {
            member.getQnAs().remove(qna);
            memberRepository.save(member); // 변경사항 저장
        }
        qnaRepository.deleteById(qnaId);
    }

    //Q&A 답변 저장
    @Transactional
    public boolean saveQnAAnswer(Long qnaId, QnAAnswerRequest answerRequest) {
        QnA qna = qnaRepository.findById(qnaId).get();
        qna.createAnswer(answerRequest.getAnswer());
        return true;
    }
}

 

3. QnAController

@Controller
@AllArgsConstructor
@RequestMapping("/qna")
public class QnAController {
    private final QnAService qnaService;
    private final QnARepository qnaRepository;
    private final MemberService memberService;
    private final ItemService itemService;
    @GetMapping("/{itemId}")
    public String QnAListPage(@PathVariable Long itemId, Model model,
                                 @RequestParam(required = false,defaultValue = "1")int page, Authentication auth){
        if(auth!=null){
            model.addAttribute("loginUserLoginId",auth.getName());
        }
        PageRequest pageRequest = PageRequest.of(page - 1, 10, Sort.by("id").descending());
        Page<QnA> qnas = qnaService.findQnAByItemId(itemId,pageRequest);
        ItemDto itemDto = itemService.findById(itemId);
        model.addAttribute("qnas",qnas);
        model.addAttribute("qnaAnswer",new QnAAnswerRequest());
        model.addAttribute("itemDto",itemDto);
        return "qna/list";
    }
    @GetMapping("/all")
    public String allQnAListPage(@PathVariable Long itemId, Model model,
                              @RequestParam(required = false,defaultValue = "1")int page, Authentication auth){
        if(auth!=null){
            model.addAttribute("loginUserLoginId",auth.getName());
        }
        PageRequest pageRequest = PageRequest.of(page - 1, 10, Sort.by("id").descending());
        Page<QnAJoinRequest.QnADto> qnas = qnaService.findAllQnA(pageRequest);
        model.addAttribute("qnaDtoList",qnas);
        model.addAttribute("qnaAnswer",new QnAAnswerRequest());
        return "qna/list";
    }
    @PostMapping("/{itemId}/{qnaId}/answer")
    public String AnswerJoin(@PathVariable Long itemId, @PathVariable Long qnaId, Model model, @ModelAttribute QnAAnswerRequest answerRequest){
        qnaService.saveQnAAnswer(qnaId,answerRequest);
        model.addAttribute("nextUrl","/qna/"+itemId);
        model.addAttribute("message","답변이 등록되었습니다!");
        return "printMessage";
    }

    @PostMapping("/{itemId}/{qnaId}/delete")
    public String QnADelete(@PathVariable Long itemId,@PathVariable Long qnaId, Model model){
        qnaService.deleteQnA(qnaId);
        model.addAttribute("nextUrl","/qna/"+itemId);
        model.addAttribute("message","QnA가 삭제되었습니다!");
        return "printMessage";
    }
    @GetMapping("/{itemId}/write")
    public String QnAWrite(@PathVariable Long itemId, Model model){
        ItemDto itemDto = itemService.findById(itemId);
        model.addAttribute("itemDto",itemDto);
        model.addAttribute("qnaJoinRequest",new QnAJoinRequest());
        return "qna/write";
    }
    @PostMapping("/{itemId}/write")
    public String QnACreate(@PathVariable Long itemId, @ModelAttribute QnAJoinRequest qnaJoinRequest,
                                Authentication auth, Model model) throws IOException {
        QnA qna = qnaService.saveQnA(itemId, qnaJoinRequest, auth);
        model.addAttribute("nextUrl","/qna/"+itemId);
        model.addAttribute("message","Q&A가 등록되었습니다!");
        return "printMessage";
    }
}

 

4. QnAJoinRequest

@Data
public class QnAJoinRequest {
    private String title;
    private String content;

    private MultipartFile uploadImage;

    public static QnA toEntity(QnAJoinRequest qnaJoinRequest){
        return QnA.builder()
                .title(qnaJoinRequest.getTitle())
                .content(qnaJoinRequest.getContent())
                .answerStatus(AnswerStatus.READY)
                .build();
    }

    @Data
    @Builder
    public static class QnADto {
        private Long id;

        private Long memberId; //글을 작성한 회원

        private Long itemId; //어떤 상품에 대한 글인지 확인하기 위해

        private String title; //제목
        private String content; //내용

        private String answer;

        private AnswerStatus answerStatus; //답변 상태

        private UploadImage uploadImage;

        public static QnADto of(QnA qna){
            return QnADto.builder()
                    .id(qna.getId())
                    .memberId(qna.getMember().getId())
                    .itemId(qna.getItem().getId())
                    .title(qna.getTitle())
                    .content(qna.getContent())
                    .answer(qna.getAnswer())
                    .answerStatus(qna.getAnswerStatus())
                    .uploadImage(qna.getUploadImage())
                    .build();
        }

    }
}

 

5. QnAAnswerRequest

  • QnA 답변 생성에 사용되는 DTO
@Data
public class QnAAnswerRequest {
    private String answer;
}