diff --git a/src/main/java/com/krylov/refound/controller/PostController.java b/src/main/java/com/krylov/refound/controller/PostController.java index 02eb16b..d3413e7 100644 --- a/src/main/java/com/krylov/refound/controller/PostController.java +++ b/src/main/java/com/krylov/refound/controller/PostController.java @@ -8,6 +8,7 @@ import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; @@ -29,10 +30,7 @@ public class PostController { private final PostService service; @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public PostRequest create( - @ModelAttribute PostRequest request, - @RequestPart(value = "image", required = false) MultipartFile image - ) { + public PostRequest create(@ModelAttribute PostRequest request, @RequestPart(value = "image", required = false) MultipartFile image) { log.info(request.toString()); return service.create(request, image); } diff --git a/src/main/java/com/krylov/refound/controller/ReviewsController.java b/src/main/java/com/krylov/refound/controller/ReviewsController.java new file mode 100644 index 0000000..31a767f --- /dev/null +++ b/src/main/java/com/krylov/refound/controller/ReviewsController.java @@ -0,0 +1,39 @@ +package com.krylov.refound.controller; + +import com.krylov.refound.dto.ReviewRequestDto; +import com.krylov.refound.dto.ReviewResponseDto; +import com.krylov.refound.service.ReviewService; +import jakarta.validation.Valid; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1/reviews") +@RequiredArgsConstructor +@Slf4j +public class ReviewsController { + + private final ReviewService reviewService; + + @GetMapping + public List getAllReviews() { + List allReviews = reviewService.getAllReviews(); + log.info("Got all reviews: {}", allReviews); + return allReviews; + } + + @PostMapping + @PreAuthorize("isAuthenticated()") + public ReviewResponseDto createReview(@Valid @RequestBody ReviewRequestDto request, Authentication authentication) { + // Взять имя пользователя из authentication + return reviewService.createReview(request, authentication.getName()); + } +} diff --git a/src/main/java/com/krylov/refound/dto/ReviewRequestDto.java b/src/main/java/com/krylov/refound/dto/ReviewRequestDto.java new file mode 100644 index 0000000..bf5f563 --- /dev/null +++ b/src/main/java/com/krylov/refound/dto/ReviewRequestDto.java @@ -0,0 +1,18 @@ +package com.krylov.refound.dto; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +@Data +public class ReviewRequestDto { + @NotNull + @Min(1) + @Max(5) + private Integer rating; + + @Size(max = 500) + private String text; +} diff --git a/src/main/java/com/krylov/refound/dto/ReviewResponseDto.java b/src/main/java/com/krylov/refound/dto/ReviewResponseDto.java new file mode 100644 index 0000000..a7b3d47 --- /dev/null +++ b/src/main/java/com/krylov/refound/dto/ReviewResponseDto.java @@ -0,0 +1,11 @@ +package com.krylov.refound.dto; + +import java.time.LocalDateTime; + +public record ReviewResponseDto( + Long id, + String authorName, + Integer rating, + String text, + LocalDateTime createdAt) { +} diff --git a/src/main/java/com/krylov/refound/entity/Review.java b/src/main/java/com/krylov/refound/entity/Review.java new file mode 100644 index 0000000..cd70067 --- /dev/null +++ b/src/main/java/com/krylov/refound/entity/Review.java @@ -0,0 +1,39 @@ +package com.krylov.refound.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.PrePersist; +import jakarta.persistence.Table; +import java.time.LocalDateTime; +import lombok.Getter; +import lombok.Setter; + +@Entity +@Table(name = "reviews") +@Getter +@Setter +public class Review { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "author_name") + private String authorName; + + @Column(nullable = false) + private Integer rating; // 1-5 + + @Column(length = 500) + private String text; + + @Column(name = "created_at", nullable = false) + private LocalDateTime createdAt; + + @PrePersist + protected void onCreate() { + createdAt = LocalDateTime.now(); + } +} diff --git a/src/main/java/com/krylov/refound/repository/ReviewRepository.java b/src/main/java/com/krylov/refound/repository/ReviewRepository.java new file mode 100644 index 0000000..a36c5c8 --- /dev/null +++ b/src/main/java/com/krylov/refound/repository/ReviewRepository.java @@ -0,0 +1,9 @@ +package com.krylov.refound.repository; + +import com.krylov.refound.entity.Review; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ReviewRepository extends JpaRepository { + List findAllByOrderByCreatedAtDesc(); +} diff --git a/src/main/java/com/krylov/refound/security/SecurityConfig.java b/src/main/java/com/krylov/refound/security/SecurityConfig.java index ed65675..a2801cb 100644 --- a/src/main/java/com/krylov/refound/security/SecurityConfig.java +++ b/src/main/java/com/krylov/refound/security/SecurityConfig.java @@ -28,6 +28,7 @@ public class SecurityConfig { // Public endpoints .requestMatchers("/api/v1/auth/**").permitAll() .requestMatchers(org.springframework.http.HttpMethod.GET, "/api/v1/posts/**").permitAll() + .requestMatchers("/api/v1/reviews/**").permitAll() .requestMatchers("/files/**", "/uploads/**").permitAll() .requestMatchers("/ws/**").permitAll() // All other endpoints require authentication diff --git a/src/main/java/com/krylov/refound/service/PostService.java b/src/main/java/com/krylov/refound/service/PostService.java index 0666e1f..80b48a8 100644 --- a/src/main/java/com/krylov/refound/service/PostService.java +++ b/src/main/java/com/krylov/refound/service/PostService.java @@ -35,12 +35,14 @@ public class PostService { private final PostMapper mapper; private final ImageRepository imageRepository; private final NominatimService nominatimService; + private final UserService userService; public PostRequest create(PostRequest request, MultipartFile image) { Post post = mapper.toEntity(request); post.setCreatedAt(LocalDateTime.now()); post.setStatus(PostStatus.ACTIVE); + post.setUser(userService.getCurrentUser()); // присваиваем текущего пользователя repository.save(post); // сначала сохраняем post, чтобы получить id if (image != null && !image.isEmpty()) { diff --git a/src/main/java/com/krylov/refound/service/ReviewService.java b/src/main/java/com/krylov/refound/service/ReviewService.java new file mode 100644 index 0000000..0aea88a --- /dev/null +++ b/src/main/java/com/krylov/refound/service/ReviewService.java @@ -0,0 +1,45 @@ +package com.krylov.refound.service; + +import com.krylov.refound.dto.ReviewRequestDto; +import com.krylov.refound.dto.ReviewResponseDto; +import com.krylov.refound.entity.Review; +import com.krylov.refound.repository.ReviewRepository; +import jakarta.validation.Valid; +import java.time.LocalDateTime; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ReviewService { + + private final ReviewRepository reviewRepository; + + public List getAllReviews() { + return reviewRepository.findAllByOrderByCreatedAtDesc() + .stream() + .map(this::toResponse) + .toList(); + } + + public ReviewResponseDto createReview(ReviewRequestDto request, String username) { + Review review = new Review(); + review.setAuthorName(username); // или firstName из User + review.setRating(request.getRating()); + review.setText(request.getText()); + review.setCreatedAt(LocalDateTime.now()); + + Review saved = reviewRepository.save(review); + return toResponse(saved); + } + private ReviewResponseDto toResponse(Review r) { + return new ReviewResponseDto( + r.getId(), + r.getAuthorName(), + r.getRating(), + r.getText(), + r.getCreatedAt() + ); + } +} diff --git a/src/main/resources/db/changelog/create/008-create-reviews.yaml b/src/main/resources/db/changelog/create/008-create-reviews.yaml new file mode 100644 index 0000000..3df77ff --- /dev/null +++ b/src/main/resources/db/changelog/create/008-create-reviews.yaml @@ -0,0 +1,13 @@ +databaseChangeLog: + - changeSet: + id: 008-create-reviews + author: you + changes: + - createTable: + tableName: reviews + columns: + - column: { name: id, type: BIGSERIAL, constraints: { primaryKey: true } } + - column: { name: author_name, type: VARCHAR(255) } + - column: { name: rating, type: INTEGER, constraints: { nullable: false } } + - column: { name: text, type: VARCHAR(500) } + - column: { name: created_at, type: TIMESTAMP, constraints: { nullable: false } } diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index e70d24f..99ab00f 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -13,4 +13,5 @@ databaseChangeLog: - include: { file: db/changelog/add/012-add-email-role.yaml } - include: { file: db/changelog/add/003-add-phone-posts.yaml } - include: { file: db/changelog/add/004-add-fields-user.yaml } - - include: { file: db/changelog/add/005-add-user-avatarUrl.yaml } \ No newline at end of file + - include: { file: db/changelog/add/005-add-user-avatarUrl.yaml } + - include: { file: db/changelog/create/008-create-reviews.yaml } \ No newline at end of file