집에 수정 여닫이

류한선·2024년 3월 18일

2차 프로젝트

목록 보기
21/32
<html layout:decorate="~{layout}">
<div layout:fragment="content">
    <style>
        .aside_items {
            display:none;
        }
    </style>
    <div class="pb-3 mb-3 link-body-emphasis text-decoration-none border-bottom" style="width: 100%;">
        <div class="title_book d-flex" style="padding-top:5px;">
            제목:
            <div th:text="${book.subject}"></div>
            / 출판사:
            <div th:text="${book.publisher}"></div>
        </div>
    </div>
    <div class="bt_section">
        <div class="book_top-section">
            <div class="book_left-side">
                <div style="width:400px; height:600px;">
                    <img th:src="@{|/file/${book.thumbnailImg}|}" alt="사진" style="width:
                    400px; height: 600px;">
                </div>
            </div>
            <div class="center-detail">
                <div>
                    <div class="pb-3 mb-3 link-body-emphasis text-decoration-none border-bottom"
                         style="width: 100%; padding-left:10px;">
                        <h2>
                            <div class="card-text" style="white-space: pre-line;" th:text="${book.subject}"></div>
                        </h2>
                    </div>
                    <div class="pb-3 mb-3 link-body-emphasis text-decoration-none border-bottom"
                         style="width: 100%; padding-left:10px;">
                        <h6>정가</h6>
                        <del th:text="${book.price  + ' 원'}"></del>
                        <br>
                        <h6>할인가</h6>
                        <div class="d-flex" style="color:red;">
                            <div style="margin-right:10px;" th:text="${book.discountPrice}  + ' 원'"></div>
                            <div th:text=" ' ( ' + ${book.discount} +'  % 할인' + ' ) ' "></div>
                        </div>
                    </div>
                </div>
                <div>
                    <div class="pb-3 mb-3 link-body-emphasis text-decoration-none border-bottom"
                         style="width: 100%; padding-left:10px;">
                        <h5>쿠폰 받기</h5>
                        <div class="card my-3">
                            <div class="card-body">
                                쿠폰 내용
                                <div class="card-text" style="white-space: pre-line;">
                                    홈페이지 오픈 기념 이벤트 중 입니다
                                </div>
                            </div>
                            <div>
                                <p>신간 책 이벤트 중입니다</p>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="book_right-side bg-light">
                <div class="book_right-nav">
                    <div style="margin-bottom:5px">
                        <a class="btn btn-sm btn-primary" style="width: 150px">바로구매</a>
                    </div>
                    <div style="margin-bottom:5px">
                        <a class="addCartItem btn btn-sm btn-primary" style="width: 150px"
                           th:id="'addCartItem_' + ${book.id}" onclick="addCartItem(${book.id}); return false;">장바구니
                            담기</a>
                    </div>
                    <div>
                        <a class="addWish btn btn-sm btn-primary" th:id="'addWish_' + ${book.id}"
                           onclick="addWish(${book.id}); return false;" style="width:150px;">찜하기</a>
                    </div>
                </div>
            </div>
        </div>
        <div class="book_middle-section">
            <div id="header" class="book_navbar header-scrolled text-black bg-light">
                <div class="book_nav-container">
                    <a href="#ebook_info" class="card-body btn btn-light" onclick="activateSection('ebook_info')">
                        도서정보
                    </a>
                    <a href="#review_info" class="card-body btn btn-light" onclick="activateSection('review_info')">
                        리뷰
                    </a>
                    <div class="card-body" style="font-size:20px; color: red;">
                        <div class="card-text" style="white-space: pre-line;" th:text="${book.discountPrice + ' 원'}"></div>
                    </div>
                    <div style="margin-bottom:5px" class="justify-content-end">
                        <a class="btn btn-sm btn-primary" style="width: 100px; margin-right:10px;">바로구매</a>
                        <a class="addCartItem btn btn-sm btn-primary" style="width: 120px"
                           th:id="'addCartItem_' + ${book.id}" onclick="addCartItem(${book.id}); return false;">장바구니
                            담기</a>
                    </div>
                </div>
            </div>
            <div id="ebook_info" class="book_info">
                <div class="title_book" style="padding-top:5px;">
                    <h5>판매자</h5>
                    <br>
                    <div th:text="${book.publisher}"></div>
                    <div class="pb-3 mb-3 link-body-emphasis text-decoration-none border-bottom"
                         style="width: 100%;"></div>
                    <h5>판매 소개</h5>
                    <br>
                    <div th:text="${book.content}">></div>
                    <div class="pb-3 mb-3 link-body-emphasis text-decoration-none border-bottom"
                         style="width: 100%;"></div>
                </div>
                <div class="title_book" style="padding-top:5px;">
                    <h5>책 제목</h5>
                    <br>
                    <div th:text="${book.subject}"></div>
                    <div class="pb-3 mb-3 link-body-emphasis text-decoration-none border-bottom"
                         style="width: 100%;"></div>
                    <h5>책소개</h5>
                    <br>
                    <div th:text="${book.bookIntroduce}"></div>
                    <div class="pb-3 mb-3 link-body-emphasis text-decoration-none border-bottom"
                         style="width: 100%;"></div>
                </div>
                <div class="title_book" style="padding-top:5px;">
                    <h5>출판사</h5>
                    <br>
                    <div th:text="${book.publisher}"></div>
                    <br>
                </div>
            </div>
            <div class="book_bottom-section">
                <div id="review_info" class="pb-3 mb-3 link-body-emphasis text-decoration-none border-bottom"
                     style="width: 100%;"></div>
                <h2>회원 리뷰</h2>
                <div id="review_info" class="pb-3 mb-3 link-body-emphasis text-decoration-none border-bottom"
                     style="width: 100%;"></div>
                <div class="justify-content-end" style="margin-bottom:5px">
                    <!-- 리뷰 등록 폼 -->
                    <div class="review_form mb-3">
                        <label for="content" class="form-label"></label>
                        <form th:action="@{|/review/create/${book.id}|}" method="post">
        <textarea name="content" id="content" class="form-control" rows="3"
                  placeholder="책에 대한 스포일러 및 심한 욕설은 제외됩니다"></textarea>
                            <div class="rating">
                                <input type="hidden" name="rating" id="rating-value" value="0">
                                <span class="star" data-value="1">&#9733;</span>
                                <span class="star" data-value="2">&#9733;</span>
                                <span class="star" data-value="3">&#9733;</span>
                                <span class="star" data-value="4">&#9733;</span>
                                <span class="star" data-value="5">&#9733;</span>
                            </div>
                            <button type="submit" class="btn btn-sm btn-primary" style="width: 100px; margin-top: 5px;">
                                리뷰등록
                            </button>
                        </form>
                    </div>
                    <!-- 리뷰 목록 -->
                    <div class="a_comment_top">
                        <ul th:each="review : ${reviewList}">
                            <li>
                                <div id="review_info"
                                     class="pb-3 mb-3 link-body-emphasis text-decoration-none border-bottom"
                                     style="width: 100%;">
                                    <div class="review-box mb-2">
                                        <span>평점: <span th:utext="|&#9733;| + ${review.rating}" class="star"></span></span>
                                        <div style="display:flex;" th:text="${review.author.username}"></div>
                                        <div style="display:flex; justify-content:end; padding-right:10px;"
                                             th:text="${#temporals.format(review.createDate, 'yyyy-MM-dd ')}"></div>
                                        <div style="display:flex; justify-content:end; padding-right:10px;"
                                             th:text="${#temporals.format(review.modifiedDate, 'yyyy-MM-dd ')}"></div>
                                        <div class="review-content">
                                            <span th:text="${review.content}"></span>
                                        </div>
                                        <!-- 수정 버튼 -->
                                        <button sec:authorize="isAuthenticated()" class="btn btn-sm btn-primary btn-edit" style="width: 100px; margin-right:10px;">수정</button>
                                        <!-- 삭제 버튼 -->
                                        <a th:href="@{|/review/delete/${review.id}|}" sec:authorize="isAuthenticated()" class="btn btn-sm btn-primary"
                                           style="width: 100px; margin-right:10px; margin-top:20px;">삭제</a>
                                        <!-- 리뷰 수정 폼 -->
                                        <div class="review-edit-form">
                                            <h5 class="my-3 border-bottom pb-2">리뷰수정</h5>
                                            <form th:action="@{|/review/modify/${review.id}|}" th:object="${reviewForm}" method="post">
                                                <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
                                                <div class="mb-3">
                                                    <label for="content" class="form-label">내용</label>
                                                    <textarea name="content" id="content" class="form-control" rows="10" th:text="${review.content}"></textarea>
                                                </div>
                                                <div class="mb-3">
                                                    <label for="modify-rating" class="form-label">평점</label>
                                                    <div class="rating" id="modify-rating">
                                                        <!-- 수정 시에도 별점을 선택할 수 있는 별 아이콘 -->
                                                        <input type="hidden" name="rating" id="modify-rating-value" value="0">
                                                        <span class="star" data-value="1">&#9733;</span>
                                                        <span class="star" data-value="2">&#9733;</span>
                                                        <span class="star" data-value="3">&#9733;</span>
                                                        <span class="star" data-value="4">&#9733;</span>
                                                        <span class="star" data-value="5">&#9733;</span>
                                                    </div>
                                                </div>
                                                <input type="submit" value="저장하기" class="btn btn-primary my-2">
                                            </form>
                                        </div>
                                    </div>
                                </div>
                            </li>
                        </ul>
                    </div>

                    <!-- JavaScript 코드 -->
                    <script>
                        // 페이지가 로드될 때 실행되는 함수
                        window.onload = function () {
                            // 모든 리뷰 수정 폼 숨기기
                            var reviewForms = document.querySelectorAll('.review-edit-form');
                            reviewForms.forEach(function (form) {
                                form.style.display = 'none';
                            });

                            // 수정 버튼에 클릭 이벤트 추가
                            var editButtons = document.querySelectorAll('.btn-edit');
                            editButtons.forEach(function (button) {
                                button.addEventListener('click', function () {
                                    // 클릭된 버튼의 부모 요소를 찾아 해당 리뷰 수정 폼 표시/숨김 토글
                                    var parent = button.closest('.a_comment_top');
                                    var editForm = parent.querySelector('.review-edit-form');
                                    if (editForm.style.display === 'none') {
                                        editForm.style.display = 'block';
                                    } else {
                                        editForm.style.display = 'none';
                                    }
                                });
                            });
                        };
                    </script>
                    <script>
                        document.addEventListener('DOMContentLoaded', function () {
    const reviewStars = document.querySelectorAll('.review_form .star'); // 리뷰 작성 별점
    const modifyStars = document.querySelectorAll('.a_comment_top .star'); // 리뷰 수정 별점

    reviewStars.forEach(star => {
        star.addEventListener('mouseover', () => {
            const value = parseInt(star.getAttribute('data-value'));

            // 선택된 별의 인덱스까지만 활성화
            reviewStars.forEach((s, index) => {
                if (index < value) {
                    s.classList.add('active');
                } else {
                    s.classList.remove('active');
                }
            });
        });

        star.addEventListener('click', () => {
            const value = parseInt(star.getAttribute('data-value'));

            // 선택된 별의 값을 폼에 설정
            const ratingInput = document.getElementById('rating-value');
            if (ratingInput) {
                ratingInput.value = value;
            }

            // 현재 선택된 별까지만 활성화
            reviewStars.forEach((s, index) => {
                if (index < value) {
                    s.classList.add('selected');
                } else {
                    s.classList.remove('selected');
                }
            });
        });
    });

    modifyStars.forEach(star => {
        star.addEventListener('mouseover', () => {
            const value = parseInt(star.getAttribute('data-value'));

            // 선택된 별의 인덱스까지만 활성화
            modifyStars.forEach((s, index) => {
                if (index < value) {
                    s.classList.add('active');
                } else {
                    s.classList.remove('active');
                }
            });
        });

        star.addEventListener('click', () => {
            const value = parseInt(star.getAttribute('data-value'));

            // 선택된 별의 값을 폼에 설정
            const ratingInput = document.getElementById('modify-rating-value');
            if (ratingInput) {
                ratingInput.value = value;
            }

            // 현재 선택된 별까지만 활성화
            modifyStars.forEach((s, index) => {
                if (index < value) {
                    s.classList.add('selected');
                } else {
                    s.classList.remove('selected');
                }
            });
        });
    });

    // 별점을 마우스로 선택하고 나서 마우스가 벗어났을 때 별을 초기화
    const ratingContainer = document.querySelector('.rating');
    ratingContainer.addEventListener('mouseleave', () => {
        const ratingInput = ratingContainer.querySelector('input[type="hidden"]');
        const currentValue = parseInt(ratingInput.value);

        // 현재 선택된 별까지만 활성화
        modifyStars.forEach((star, index) => {
            if (index < currentValue) {
                star.classList.add('selected');
            } else {
                star.classList.remove('selected');
            }
        });
    });
});
                    </script>
                    <!-- CSS 코드 -->
                    <style>
                        .rating {
                            display: inline-block;
                        }

                        .rating .star {
                            font-size: 20px;
                            cursor: pointer;
                            color: #aaa;
                            transition: color 0.2s;
                        }

                        .rating .star.active,
                        .rating .star.selected {
                            color: #fdd835; /* 활성화된 별 색상 */
                        }
                        /* 별 아이콘의 색상을 노란색으로 변경 */
    .star {
        color: #fdd835; /* 원하는 색상으로 변경 */
    }
                    </style>

                    <!-- 장바구니,찜 기능 -->
        <script>
            $(document).ready(function () {
            $(".addWish").click(function () {
                var bookId = this.id.split('_')[1];

                Swal.fire({
                    title: '찜 목록에',
                    text: '해당 상품을 담으시겠습니까?',
                    icon: 'warning',
                    showCancelButton: true,
                    confirmButtonColor: '#3085d6',
                    cancelButtonColor: '#d33',
                    confirmButtonText: '담기',
                    cancelButtonText: '취소',
                    preConfirm: () => {
                        return new Promise((resolve) => {
                            $.ajax({
                                url: '/wish/add/' + bookId,
                                type: 'GET',
                                success: function (data) {
                                      resolve(true);
                                },
                                error: function () {
                                    resolve(false);
                                }
                            });
                        });
                    }
                }).then((result) => {
                    if (result.isConfirmed) {
                        // SweetAlert2 확인 버튼을 누르면 실행되는 부분
                        Swal.fire(
                            '찜 목록에 상품을 담았습니다.',
                            '',
                            'success'
                        ).then(() => {
                            window.location.href = '/book/detail/' + bookId;
                        });
                    }
                });
            });
        });
        </script>
        <script>
            $(document).ready(function () {
             $(".addCartItem").click(function () {
            var bookId = this.id.split('_')[1];

            Swal.fire({
                title: '장바구니에',
                text: '해당 상품을 담으시겠습니까?',
                icon: 'warning',
                showCancelButton: true,
                confirmButtonColor: '#3085d6',
                cancelButtonColor: '#d33',
                confirmButtonText: '담기',
                cancelButtonText: '취소',
                preConfirm: () => {
                    return new Promise((resolve) => {
                        // Ajax 요청을 통한 서버에서의 삭제 작업
                        $.ajax({
                            url: '/cartitem/add/' + bookId,
                            type: 'GET',
                            success: function (data) {
                                  resolve(true);
                            },
                            error: function () {
                                resolve(false);
                            }
                        });
                    });
                }
            }).then((result) => {
                if (result.isConfirmed) {
                    // SweetAlert2 확인 버튼을 누르면 실행되는 부분
                    Swal.fire(
                        '장바구니에 상품을 담았습니다.',
                        '',
                        'success'
                    ).then(() => {
                        window.location.href = '/book/detail/' + bookId;
                    });
                }
            });
        });
    });
        </script>
    </div>
</div>
</html>
package eBook.EatBook.domain.review.controller;

import eBook.EatBook.domain.book.entity.Book;
import eBook.EatBook.domain.book.service.BookService;
import eBook.EatBook.domain.member.entity.Member;
import eBook.EatBook.domain.member.service.MemberService;
import eBook.EatBook.domain.review.DTO.ReviewForm;
import eBook.EatBook.domain.review.entity.Review;
import eBook.EatBook.domain.review.service.ReviewService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;

import org.springframework.web.bind.annotation.*;

import java.security.Principal;

import static org.springframework.data.jpa.domain.AbstractPersistable_.id;

@Controller
@RequiredArgsConstructor
@RequestMapping("/review")
public class ReviewController {

        private final ReviewService reviewService;
        private final MemberService memberService;
        private final BookService bookService;

    @PreAuthorize("isAuthenticated()")
    @PostMapping("/create/{id}")
    public String reviewCreate(@PathVariable("id") Integer id, @Valid ReviewForm reviewForm,
                               BindingResult bindingResult, Principal principal, org.springframework.ui.Model model){
        Book books = this.bookService.getBookById(id);
        if (books == null) {
            // 책이 존재하지 않는 경우에 대한 처리
            return "redirect:/books/list"; // 예시로 책 목록 페이지로 리다이렉트
        }

        float rating = reviewForm.getRating();

        if (bindingResult.hasErrors()) {
            model.addAttribute("books",books);
            return "book_detail";
        }
        Member member =  this.memberService.findByUsername(principal.getName());
        if (member == null) {
            // 작성자를 찾을 수 없는 경우에 대한 처리
            return "redirect:/book/list"; // 예시로 홈 페이지로 리다이렉트
        }
        this.reviewService.create(books,reviewForm.getContent(), member,rating);
        return String.format("redirect:/book/detail/%d", id);
    }

    @PreAuthorize("isAuthenticated()")
    @PostMapping("/modify/{id}")
    public String reviewModify(@PathVariable("id") Integer id,@Valid ReviewForm reviewForm,
                               BindingResult bindingResult, Principal principal){

        Review review = this.reviewService.getReview(id);
        if (review == null) {
            // 책이 존재하지 않는 경우에 대한 처리
            return "redirect:/book/list"; // 예시로 책 목록 페이지로 리다이렉트
        }

        float rating = reviewForm.getRating();

        if (bindingResult.hasErrors()) {

            return "book_detail";
        }
        Member author = review.getAuthor();
        // 현재 로그인한 사용자가 해당 리뷰의 작성자인지 확인
        if (!principal.getName().equals(author.getUsername())) {
            // 작성자와 다른 사용자가 수정을 시도한 경우에 대한 처리
            return "redirect:/books/list"; // 예시로 책 목록 페이지로 리다이렉트
        }
        review.setContent(reviewForm.getContent());
        this.reviewService.modify(review, reviewForm.getContent(),rating);
        return String.format("redirect:/book/detail/%s",review.getBook().getId());
    }
    @PreAuthorize("isAuthenticated()")
    @GetMapping("/delete/{id}")
    public String reviewDelete(Principal principal, @PathVariable("id") Integer id) {
        Review review = this.reviewService.getReview(id);

        Member member =  this.memberService.findByUsername(principal.getName());
        if (member == null) {
            // 작성자를 찾을 수 없는 경우에 대한 처리
            return "redirect:/book/list"; // 예시로 홈 페이지로 리다이렉트
        }

        this.reviewService.delete(review);

        return String.format("redirect:/book/detail/%s",review.getBook().getId());
    }

}
@Getter
@Setter
public class ReviewForm {

    @NotEmpty(message = "내용을 입력해주세요")
    private String content;

    private float rating;

    private Integer bookId;

    private String nickname;
}
package eBook.EatBook.domain.review.entity;

import eBook.EatBook.domain.book.entity.Book;
import eBook.EatBook.domain.member.entity.Member;
import eBook.EatBook.global.baseEntity.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import org.springframework.data.annotation.CreatedDate;

import java.time.LocalDateTime;
import java.util.Set;


@Entity
@Getter
@SuperBuilder(toBuilder = true)
@AllArgsConstructor
@NoArgsConstructor
public class Review extends BaseEntity {
    @ManyToOne
    private Book book;

    @ManyToOne
    private Member author;

    @Column
    private String content;

    @Column
    private float rating; // 별점

    @ManyToMany
    private Set<Member> voter;

    @CreatedDate
    private LocalDateTime createDate;

    public void setContent(String content) {
        this.content = content;
    }

    public void setModifiedDate(LocalDateTime now) {
    }

    public void setRating(float rating) {
        this.rating = rating;
    }
}
@Repository
public interface ReviewRepository extends JpaRepository<Review, Integer> {
    Optional<Review> findById(Integer id);
    List<Review> findAllByBook(Book book);
}
@Service
@RequiredArgsConstructor
public class ReviewService {
    private final ReviewRepository reviewRepository;
    private final BookRepository bookRepository;


    public Review getReview(Integer id) {
        Optional<Review> review = this.reviewRepository.findById(id);
        if (review.isEmpty()) {
            return null;
        }
        return review.get();
    }

    public void create(Book books, String content, Member author,float rating) {
        Review review = Review.builder()
                .book(books)
                .content(content)
                .rating(rating)
                .createDate(LocalDateTime.now())
                .modifiedDate(LocalDateTime.now())
                .author(author)
                .build();
        reviewRepository.save(review);
    }
    public void modify(Review review, String content,float rating){
        // 기존의 리뷰 객체의 내용을 업데이트
        review.setContent(content);
        review.setRating(rating);
        review.setModifiedDate(LocalDateTime.now()); // 수정일 업데이트
        reviewRepository.save(review); // 업데이트된 리뷰 저장
    }

    public void delete(Review review) {
        reviewRepository.delete(review);
    }

    public List<Review> findAllByBook(Book book) {

        return this.reviewRepository.findAllByBook(book);
    }

0개의 댓글