Создала методы добавления и удаления отзыва.

На странице заведения добавила формы для добавления и удаления отзыва.
Сделала проверку на повторный отзыв.
Сделала ограничение по ролям.
Изменила пароль в миграции для админа, т.к. тот не работал
parent 37de2cc9
package com.example.final_exam_l.controller;
import com.example.final_exam_l.dto.add.PlaceAddDTO;
import com.example.final_exam_l.dto.add.ReviewAddDTO;
import com.example.final_exam_l.entity.Place;
import com.example.final_exam_l.entity.PlacePhoto;
import com.example.final_exam_l.repository.PlaceRepository;
......@@ -34,7 +35,7 @@ public class PlaceController {
private final PlacePhotoService placePhotoService;
private final PropertiesService propertiesService;
private final StorageService storageService;
private final PlaceRepository repository;
private final ReviewService reviewService;
@GetMapping("/{id}")
public String one(@PathVariable Integer id, Model model){
......@@ -92,6 +93,32 @@ public class PlaceController {
return "redirect:/";
}
@PreAuthorize("hasAuthority('USER') || hasAuthority('ADMIN')")
@PostMapping("/{id}/review")
public String addReview(@PathVariable Integer id,
@Valid ReviewAddDTO dto,
BindingResult validationResult,
RedirectAttributes attributes,
Principal principal){
attributes.addFlashAttribute("dto", dto);
if (validationResult.hasFieldErrors()) {
attributes.addFlashAttribute("errors", validationResult.getFieldErrors());
return "redirect:/" + id;
}
else if (reviewService.isUnique(id, userService.findByEmail(principal.getName()).getId())) {
attributes.addFlashAttribute("nameError", "Вы уже оставляли отзыв к данному заведению.");
return "redirect:/" + id;
}
reviewService.add(dto, principal.getName(), service.findOne(id));
return "redirect:/" + id;
}
@PostMapping("/{id}/review/{reviewId}")
public String deleteReview(@PathVariable Integer id, @PathVariable Integer reviewId){
reviewService.delete(reviewId, id);
return "redirect:/" + id;
}
@GetMapping("/file/{filename:.+}")
public ResponseEntity<Resource> getFilePic(@PathVariable String filename) {
{
......
......@@ -28,7 +28,6 @@ public class ReviewDTO {
.id(review.getId())
.description(review.getDescription())
.grade(review.getGrade())
.isDel(review.isDel())
.place(PlaceDTO.from(review.getPlace()))
.user(UserDTO.from(review.getUser()))
.build();
......
package com.example.final_exam_l.dto.add;
import lombok.AllArgsConstructor;
import lombok.Getter;
import javax.persistence.Column;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Getter
@AllArgsConstructor
public class ReviewAddDTO {
@NotNull
@Size(min = 2, message = "Минимальная длина комментария - два символа")
private String description;
@NotNull
@Min(value = 1, message = "Минимальная оценка 1")
@Max(value = 5, message = "Максимальная оценка 5")
private Integer grade;
}
......@@ -33,11 +33,6 @@ public class Review {
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate date = LocalDate.now();
@Column
@NotNull
@Builder.Default
private boolean isDel = false;
@NotNull
@ManyToOne
private Place place;
......
......@@ -6,4 +6,5 @@ import org.springframework.stereotype.Repository;
@Repository
public interface ReviewRepository extends JpaRepository<Review, Integer> {
boolean existsByPlaceIdAndUserId(int placeId, int userId);
}
......@@ -11,8 +11,6 @@ import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@Service
@RequiredArgsConstructor
......@@ -60,9 +58,11 @@ public class PlaceService {
return repository.save(place);
}
public double rating(int id){
public void rating(int id){
int commonGrade = findOne(id).getReviews().stream().mapToInt(Review::getGrade).sum();
int amount = findOne(id).getReviews().size();
return commonGrade / amount;
Place place = findOne(id);
place.setRating(commonGrade / amount);
repository.save(place);
}
}
package com.example.final_exam_l.service;
import com.example.final_exam_l.dto.add.ReviewAddDTO;
import com.example.final_exam_l.entity.Place;
import com.example.final_exam_l.entity.Review;
import com.example.final_exam_l.exception.ResourceNotFoundException;
import com.example.final_exam_l.repository.ReviewRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
......@@ -8,4 +12,33 @@ import org.springframework.stereotype.Service;
@RequiredArgsConstructor
public class ReviewService {
private final ReviewRepository repository;
private final UserService userService;
private final PlaceService placeService;
public Review findOne(int id){
return repository.findById(id).orElseThrow(() -> {
return new ResourceNotFoundException("Отзыв", id);
});
}
public boolean isUnique(int placeId, int userId){
return repository.existsByPlaceIdAndUserId(placeId, userId);
}
public Review add(ReviewAddDTO dto, String email, Place place){
Review review = repository.save(Review.builder()
.description(dto.getDescription())
.grade(dto.getGrade())
.user(userService.findByEmail(email))
.place(place)
.build());
placeService.rating(place.getId());
return review;
}
public void delete(int id, int placeId){
Review review = findOne(id);
repository.delete(review);
placeService.rating(placeId);
}
}
......@@ -4,7 +4,6 @@ CREATE Table `reviews` (
`id` int auto_increment not null,
`description` varchar(128) not null,
`grade` int not null,
`is_del` boolean not null,
`date` date not null,
`user_id` int not null references `users`(`id`),
`place_id` int not null references `places`(`id`),
......
......@@ -7,9 +7,11 @@
<div class="nav-item">
<a href="/">Заведения</a>
</div>
<div class="nav-item">
<a href="/add">Добавить завeдение</a>
</div>
<#if user??>
<div class="nav-item">
<a href="/add">Добавить завeдение</a>
</div>
</#if>
</div>
<div class="flex">
<#if !(user??)>
......
......@@ -2,7 +2,7 @@
<@main.renderWith title="Новое заведение">
<div class="container">
<#if user?? && (user.role == "ADMIN" || user.role == "USER")>
<div class="card">
<h1>Добавить новое заведение</h1>
<div class="form">
......@@ -22,7 +22,7 @@
</div>
<button type="submit" class="btn btn-primary mt-1" id="btn">Регистрация</button>
<button type="submit" class="btn btn-primary mt-1" id="btn">Добавить</button>
</form>
</div>
</div>
......@@ -37,5 +37,8 @@
<h2>${fileError}</h2>
</#if>
</div>
<#else>
<h1>У Вас нет доступа к данному действию</h1>
</#if>
</div>
</@main.renderWith>
\ No newline at end of file
......@@ -6,16 +6,60 @@
<div class="item">
<a href="/${place.id}">
<img src="/file/${place.placePhoto.filePath}" alt="Изображение">
<p class="info">${place.name}</p>
<p class="info">${place.user.login}</p>
<p class="info">${place.rating}</p>
<p class="info">${place.reviews?size}</p>
<p class="info">Наименование ${place.name}</p>
<p class="info">Создал ${place.user.login}</p>
<p class="info">Рейтинг ${place.rating}</p>
<p class="info">Отзывов ${place.reviews?size}</p>
<p class="info">Фото ${place.photos?size}</p>
<h4>Отзывы</h4>
<#list place.reviews as r>
<p>${r.description}</p>
<p>${r.user.login}</p>
<p>${r.grade}</p>
<p>${r.date}</p>
<#if user?? && user.role == "ADMIN">
<form action="/${place.id}/review/${r.id}">
<input type='hidden' value='${_csrf.token}' name='${_csrf.parameterName}'/>
<button type="submit" class="btn btn-danger">X</button>
</form>
</#if>
</#list>
</a>
</div>
<#if user?? && user.role == "ADMIN">
<form action="/${place.id}/delete" method="post">
<input type='hidden' value='${_csrf.token}' name='${_csrf.parameterName}'/>
<button type="submit" class="btn btn-danger">Удалить заведение</button>
</form>
</#if>
<#if user?? && (user.role == "ADMIN" || user.role == "USER")>
<h3>Добавить отзыв</h3>
<div class="form">
<form action="/${place.id}/review" method="post">
<input type='hidden' value='${_csrf.token}' name='${_csrf.parameterName}'/>
<div class="col">
<label for="description" class="form-label fs-4">Комментарий</label>
<input type="text" name="description" class="form-control" id="description">
</div>
<div class="col">
<label for="grade" class="form-label fs-4">Оценка</label>
<select name="grade" id="grade">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</div>
<button type="submit" class="btn btn-primary mt-1" id="btn">Добавить отзыв</button>
</form>
<#if errors??>
<#list errors as e>
<h2>${e.defaultMessage!'__no message__'}</h2>
</#list>
<#elseif nameError??>
<h2>${nameError}</h2>
</#if>
</div>
</#if>
</@main.renderWith>
\ No newline at end of file
......@@ -5,16 +5,18 @@
<div class="items">
<#if places?? && places?size != 0>
<#list places as c>
<div class="item">
<a href="/${c.id}">
<img src="/file/${c.placePhoto.filePath}" alt="Изображение">
<p class="info">Наименование ${c.name}</p>
<p class="info">Создал ${c.user.login}</p>
<p class="info">Рейтинг ${c.rating}</p>
<p class="info">Отзывов ${c.reviews?size}</p>
<p class="info">Фото ${c.photos?size}</p>
</a>
</div>
<#if !c.isDel()>
<div class="item">
<a href="/${c.id}">
<img src="/file/${c.placePhoto.filePath}" alt="Изображение">
<p class="info">Наименование ${c.name}</p>
<p class="info">Создал ${c.user.login}</p>
<p class="info">Рейтинг ${c.rating}</p>
<p class="info">Отзывов ${c.reviews?size}</p>
<p class="info">Фото ${c.photos?size}</p>
</a>
</div>
</#if>
</#list>
<#include "../pagination.ftlh">
<#else>
......
<#import "main.ftlh" as main/>
<@main.renderWith title="Страница не найдена">
<div class="container">
<#if resource??>
<h1 class="exception">${resource} с id ${id} не найден(-а, -о)</h1>
<#elseif forbidden??>
<h1 class="exception">${forbidden}</h1>
<#elseif result??>
<h1 class="exception">${result}</h1>
</#if>
</div>
</@main.renderWith>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment