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

На странице заведения добавила формы для добавления и удаления отзыва.
Сделала проверку на повторный отзыв.
Сделала ограничение по ролям.
Изменила пароль в миграции для админа, т.к. тот не работал
parent 37de2cc9
package com.example.final_exam_l.controller; package com.example.final_exam_l.controller;
import com.example.final_exam_l.dto.add.PlaceAddDTO; 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.Place;
import com.example.final_exam_l.entity.PlacePhoto; import com.example.final_exam_l.entity.PlacePhoto;
import com.example.final_exam_l.repository.PlaceRepository; import com.example.final_exam_l.repository.PlaceRepository;
...@@ -34,7 +35,7 @@ public class PlaceController { ...@@ -34,7 +35,7 @@ public class PlaceController {
private final PlacePhotoService placePhotoService; private final PlacePhotoService placePhotoService;
private final PropertiesService propertiesService; private final PropertiesService propertiesService;
private final StorageService storageService; private final StorageService storageService;
private final PlaceRepository repository; private final ReviewService reviewService;
@GetMapping("/{id}") @GetMapping("/{id}")
public String one(@PathVariable Integer id, Model model){ public String one(@PathVariable Integer id, Model model){
...@@ -92,6 +93,32 @@ public class PlaceController { ...@@ -92,6 +93,32 @@ public class PlaceController {
return "redirect:/"; 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:.+}") @GetMapping("/file/{filename:.+}")
public ResponseEntity<Resource> getFilePic(@PathVariable String filename) { public ResponseEntity<Resource> getFilePic(@PathVariable String filename) {
{ {
......
...@@ -28,7 +28,6 @@ public class ReviewDTO { ...@@ -28,7 +28,6 @@ public class ReviewDTO {
.id(review.getId()) .id(review.getId())
.description(review.getDescription()) .description(review.getDescription())
.grade(review.getGrade()) .grade(review.getGrade())
.isDel(review.isDel())
.place(PlaceDTO.from(review.getPlace())) .place(PlaceDTO.from(review.getPlace()))
.user(UserDTO.from(review.getUser())) .user(UserDTO.from(review.getUser()))
.build(); .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 { ...@@ -33,11 +33,6 @@ public class Review {
@DateTimeFormat(pattern = "yyyy-MM-dd") @DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate date = LocalDate.now(); private LocalDate date = LocalDate.now();
@Column
@NotNull
@Builder.Default
private boolean isDel = false;
@NotNull @NotNull
@ManyToOne @ManyToOne
private Place place; private Place place;
......
...@@ -6,4 +6,5 @@ import org.springframework.stereotype.Repository; ...@@ -6,4 +6,5 @@ import org.springframework.stereotype.Repository;
@Repository @Repository
public interface ReviewRepository extends JpaRepository<Review, Integer> { public interface ReviewRepository extends JpaRepository<Review, Integer> {
boolean existsByPlaceIdAndUserId(int placeId, int userId);
} }
...@@ -11,8 +11,6 @@ import org.springframework.data.domain.Pageable; ...@@ -11,8 +11,6 @@ import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
...@@ -60,9 +58,11 @@ public class PlaceService { ...@@ -60,9 +58,11 @@ public class PlaceService {
return repository.save(place); 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 commonGrade = findOne(id).getReviews().stream().mapToInt(Review::getGrade).sum();
int amount = findOne(id).getReviews().size(); 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; 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 com.example.final_exam_l.repository.ReviewRepository;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
...@@ -8,4 +12,33 @@ import org.springframework.stereotype.Service; ...@@ -8,4 +12,33 @@ import org.springframework.stereotype.Service;
@RequiredArgsConstructor @RequiredArgsConstructor
public class ReviewService { public class ReviewService {
private final ReviewRepository repository; 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` ( ...@@ -4,7 +4,6 @@ CREATE Table `reviews` (
`id` int auto_increment not null, `id` int auto_increment not null,
`description` varchar(128) not null, `description` varchar(128) not null,
`grade` int not null, `grade` int not null,
`is_del` boolean not null,
`date` date not null, `date` date not null,
`user_id` int not null references `users`(`id`), `user_id` int not null references `users`(`id`),
`place_id` int not null references `places`(`id`), `place_id` int not null references `places`(`id`),
......
...@@ -7,9 +7,11 @@ ...@@ -7,9 +7,11 @@
<div class="nav-item"> <div class="nav-item">
<a href="/">Заведения</a> <a href="/">Заведения</a>
</div> </div>
<div class="nav-item"> <#if user??>
<a href="/add">Добавить завeдение</a> <div class="nav-item">
</div> <a href="/add">Добавить завeдение</a>
</div>
</#if>
</div> </div>
<div class="flex"> <div class="flex">
<#if !(user??)> <#if !(user??)>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<@main.renderWith title="Новое заведение"> <@main.renderWith title="Новое заведение">
<div class="container"> <div class="container">
<#if user?? && (user.role == "ADMIN" || user.role == "USER")>
<div class="card"> <div class="card">
<h1>Добавить новое заведение</h1> <h1>Добавить новое заведение</h1>
<div class="form"> <div class="form">
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
</div> </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> </form>
</div> </div>
</div> </div>
...@@ -37,5 +37,8 @@ ...@@ -37,5 +37,8 @@
<h2>${fileError}</h2> <h2>${fileError}</h2>
</#if> </#if>
</div> </div>
<#else>
<h1>У Вас нет доступа к данному действию</h1>
</#if>
</div> </div>
</@main.renderWith> </@main.renderWith>
\ No newline at end of file
...@@ -6,16 +6,60 @@ ...@@ -6,16 +6,60 @@
<div class="item"> <div class="item">
<a href="/${place.id}"> <a href="/${place.id}">
<img src="/file/${place.placePhoto.filePath}" alt="Изображение"> <img src="/file/${place.placePhoto.filePath}" alt="Изображение">
<p class="info">${place.name}</p> <p class="info">Наименование ${place.name}</p>
<p class="info">${place.user.login}</p> <p class="info">Создал ${place.user.login}</p>
<p class="info">${place.rating}</p> <p class="info">Рейтинг ${place.rating}</p>
<p class="info">${place.reviews?size}</p> <p class="info">Отзывов ${place.reviews?size}</p>
<p class="info">Фото ${place.photos?size}</p>
<h4>Отзывы</h4>
<#list place.reviews as r> <#list place.reviews as r>
<p>${r.description}</p> <p>${r.description}</p>
<p>${r.user.login}</p> <p>${r.user.login}</p>
<p>${r.grade}</p> <p>${r.grade}</p>
<p>${r.date}</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> </#list>
</a> </a>
</div> </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> </@main.renderWith>
\ No newline at end of file
...@@ -5,16 +5,18 @@ ...@@ -5,16 +5,18 @@
<div class="items"> <div class="items">
<#if places?? && places?size != 0> <#if places?? && places?size != 0>
<#list places as c> <#list places as c>
<div class="item"> <#if !c.isDel()>
<a href="/${c.id}"> <div class="item">
<img src="/file/${c.placePhoto.filePath}" alt="Изображение"> <a href="/${c.id}">
<p class="info">Наименование ${c.name}</p> <img src="/file/${c.placePhoto.filePath}" alt="Изображение">
<p class="info">Создал ${c.user.login}</p> <p class="info">Наименование ${c.name}</p>
<p class="info">Рейтинг ${c.rating}</p> <p class="info">Создал ${c.user.login}</p>
<p class="info">Отзывов ${c.reviews?size}</p> <p class="info">Рейтинг ${c.rating}</p>
<p class="info">Фото ${c.photos?size}</p> <p class="info">Отзывов ${c.reviews?size}</p>
</a> <p class="info">Фото ${c.photos?size}</p>
</div> </a>
</div>
</#if>
</#list> </#list>
<#include "../pagination.ftlh"> <#include "../pagination.ftlh">
<#else> <#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