Repository: CorporationX/post_service
Pull request #1826: BJS2-77458  Система лайков
Author: login: danbg12
Created: 2025-05-23T13:06:08Z | Merged: 2025-05-30T07:12:46Z

Add Comment, Like, and Post functionalities
This commit introduces the initial implementation for managing comments, likes, and posts. Includes:
- Data models (DTOs) for Comment, Like, and Post.
- Interfaces and implementations for services (CommentService, LikeService, PostService).
- Mappers for converting between DTOs and entities.
- Controllers (LikeController) for exposing functionalities via API.
- Initial unit tests for services and controllers.


Diff:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index fee41c8cf0..11f4ea1f1b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -3,7 +3,7 @@ name: Post Service CI Pipeline
 on:
   pull_request:
     branches:
-      - hippogriff-master-stream8
+      - hydra-master-stream10
 
 jobs:
   build:
@@ -11,10 +11,10 @@ jobs:
 
     steps:
       - name: Checkout code
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - name: Set up JDK 17
-        uses: actions/setup-java@v3
+        uses: actions/setup-java@v4
         with:
           java-version: '17'
           distribution: 'temurin'
@@ -63,7 +63,7 @@ jobs:
         run: ./gradlew clean build test jacocoTestReport jacocoTestCoverageVerification
 
       - name: Upload JaCoCo Report
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: jacoco-report
           path: build/reports/jacoco/test
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index be03a7a865..1f92db69a8 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -5,6 +5,49 @@ plugins {
     id("jacoco")
 }
 
+jacoco {
+    toolVersion = "0.8.11"
+}
+
+tasks.test {
+    finalizedBy(tasks.jacocoTestReport)
+}
+
+tasks.jacocoTestReport {
+    reports {
+        xml.required.set(true)
+        html.required.set(true)
+        csv.required.set(false)
+    }
+
+    classDirectories.setFrom(
+        fileTree(buildDir) {
+            include ("**/faang/school/postservice/**")
+            exclude ("**/faang/school/postservice/dto/**" )
+            exclude ("**/faang/school/postservice/mapper/**")
+            exclude ("**/faang/school/postservice/config/**")
+            exclude ("**/faang/school/postservice/exception/**")
+            exclude ("**/faang/school/postservice/model/**")
+            exclude ("**/faang/school/postservice/repository/**")
+            exclude ("**/faang/school/postservice/client/**")
+        }
+    )
+}
+
+tasks.jacocoTestCoverageVerification {
+    violationRules {
+        rule {
+            limit {
+                minimum = BigDecimal("0.70")
+            }
+        }
+    }
+}
+
+tasks.check {
+    dependsOn(tasks.jacocoTestCoverageVerification)
+}
+
 group = "faang.school"
 version = "1.0"
 java.sourceCompatibility = JavaVersion.VERSION_17
@@ -72,3 +115,5 @@ val test by tasks.getting(Test::class) { testLogging.showStandardStreams = true
 tasks.bootJar {
     archiveFileName.set("service.jar")
 }
+
+
diff --git a/src/main/java/faang/school/postservice/controller/LikeController.java b/src/main/java/faang/school/postservice/controller/LikeController.java
new file mode 100644
index 0000000000..bf8886cc2d
--- /dev/null
+++ b/src/main/java/faang/school/postservice/controller/LikeController.java
@@ -0,0 +1,74 @@
+package faang.school.postservice.controller;
+
+import faang.school.postservice.dto.like.LikeDto;
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.service.LikeService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Objects;
+
+@Slf4j
+@RestController
+@RequestMapping("/api/v1/likes/")
+@RequiredArgsConstructor
+public class LikeController {
+    private final LikeService likeService;
+
+    @PostMapping("post/{postId}/user/{userId}")
+    public LikeDto addLikeToPost(@PathVariable Long postId, @PathVariable Long userId) {
+        log.info("Start method addLikeToPost with postId: {} and userId: {}",
+                postId,
+                userId);
+        validateId(userId);
+        validateId(postId);
+
+        return likeService.addLikeToPost(userId, postId);
+    }
+
+    @DeleteMapping("post/{postId}/user/{userId}")
+    public ResponseEntity<Void> removeLikeFromPost(@PathVariable Long postId, @PathVariable Long userId) {
+        log.info("Start method removeLikeFromPost with postId: {}", postId);
+        validateId(postId);
+        validateId(userId);
+
+        return likeService.removeLikeFromPost(postId, userId) ?
+                new ResponseEntity<>(HttpStatus.NO_CONTENT) :
+                new ResponseEntity<>(HttpStatus.NOT_FOUND);
+    }
+
+    @PostMapping("comment/{commentId}/user/{userId}")
+    public LikeDto addLikeToComment(@PathVariable Long commentId, @PathVariable Long userId) {
+        log.info("Start method addLikeToComment with commentId: {} and userId: {}", commentId, userId);
+        validateId(userId);
+        validateId(commentId);
+
+        return likeService.addLikeToComment(userId, commentId);
+    }
+
+    @DeleteMapping("comment/{commentId}/user/{userId}")
+    public ResponseEntity<Void> removeLikeFromComment(@PathVariable Long commentId, @PathVariable Long userId) {
+        log.info("Start method removeLikeFromComment with commentId: {}", commentId);
+        validateId(commentId);
+        validateId(userId);
+
+        return likeService.removeLikeFromComment(commentId, userId) ?
+                new ResponseEntity<>(HttpStatus.NO_CONTENT) :
+                new ResponseEntity<>(HttpStatus.NOT_FOUND);
+    }
+
+    private void validateId(Long id) {
+        if (Objects.isNull(id) || id <= 0) {
+            log.error("Used id is null or negative: {}", id);
+            throw new DataValidationException("Negative or null id! Use valid Id!");
+        }
+        log.info("Used id {} was successfully validated!", id);
+    }
+}
diff --git a/src/main/java/faang/school/postservice/dto/comment/CommentDto.java b/src/main/java/faang/school/postservice/dto/comment/CommentDto.java
new file mode 100644
index 0000000000..083a862a43
--- /dev/null
+++ b/src/main/java/faang/school/postservice/dto/comment/CommentDto.java
@@ -0,0 +1,19 @@
+package faang.school.postservice.dto.comment;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class CommentDto {
+    private Long id;
+    private String content;
+    private Long authorId;
+    private List<Long> likesIds;
+    private Long postId;
+    private Integer likeCount;
+}
diff --git a/src/main/java/faang/school/postservice/dto/like/LikeDto.java b/src/main/java/faang/school/postservice/dto/like/LikeDto.java
new file mode 100644
index 0000000000..9a5c19ac8c
--- /dev/null
+++ b/src/main/java/faang/school/postservice/dto/like/LikeDto.java
@@ -0,0 +1,15 @@
+package faang.school.postservice.dto.like;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class LikeDto {
+    private Long id;
+    private Long userId;
+    private Long commentId;
+    private Long postId;
+}
diff --git a/src/main/java/faang/school/postservice/dto/post/PostDto.java b/src/main/java/faang/school/postservice/dto/post/PostDto.java
new file mode 100644
index 0000000000..d550667225
--- /dev/null
+++ b/src/main/java/faang/school/postservice/dto/post/PostDto.java
@@ -0,0 +1,19 @@
+package faang.school.postservice.dto.post;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class PostDto {
+    private Long id;
+    private String content;
+    private Long authorId;
+    private Long projectId;
+    private List<Long> likesIds;
+    private Integer likeCount;
+}
diff --git a/src/main/java/faang/school/postservice/exception/DataValidationException.java b/src/main/java/faang/school/postservice/exception/DataValidationException.java
new file mode 100644
index 0000000000..31987bc750
--- /dev/null
+++ b/src/main/java/faang/school/postservice/exception/DataValidationException.java
@@ -0,0 +1,7 @@
+package faang.school.postservice.exception;
+
+public class DataValidationException extends RuntimeException {
+    public DataValidationException(String message) {
+        super(message);
+    }
+}
diff --git a/src/main/java/faang/school/postservice/mapper/CommentMapper.java b/src/main/java/faang/school/postservice/mapper/CommentMapper.java
new file mode 100644
index 0000000000..ee954e5259
--- /dev/null
+++ b/src/main/java/faang/school/postservice/mapper/CommentMapper.java
@@ -0,0 +1,15 @@
+package faang.school.postservice.mapper;
+
+import faang.school.postservice.dto.comment.CommentDto;
+import faang.school.postservice.model.Comment;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+
+@Mapper(componentModel = "spring")
+public interface CommentMapper {
+    Comment toEntity(CommentDto commentDto);
+
+    @Mapping(target = "likeCount",
+            expression = "java(comment.getLikes() != null ? comment.getLikes().size() : 0)")
+    CommentDto toDto(Comment comment);
+}
diff --git a/src/main/java/faang/school/postservice/mapper/LikeMapper.java b/src/main/java/faang/school/postservice/mapper/LikeMapper.java
new file mode 100644
index 0000000000..aec5667dbf
--- /dev/null
+++ b/src/main/java/faang/school/postservice/mapper/LikeMapper.java
@@ -0,0 +1,12 @@
+package faang.school.postservice.mapper;
+
+import faang.school.postservice.dto.like.LikeDto;
+import faang.school.postservice.model.Like;
+import org.mapstruct.Mapper;
+
+@Mapper(componentModel = "spring")
+public interface LikeMapper {
+    Like toEntity(LikeDto likeDto);
+
+    LikeDto toDto(Like like);
+}
diff --git a/src/main/java/faang/school/postservice/mapper/PostMapper.java b/src/main/java/faang/school/postservice/mapper/PostMapper.java
new file mode 100644
index 0000000000..9686de3025
--- /dev/null
+++ b/src/main/java/faang/school/postservice/mapper/PostMapper.java
@@ -0,0 +1,15 @@
+package faang.school.postservice.mapper;
+
+import faang.school.postservice.dto.post.PostDto;
+import faang.school.postservice.model.Post;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+
+@Mapper(componentModel = "spring")
+public interface PostMapper {
+    Post toEntity(PostDto postDto);
+
+    @Mapping(target = "likeCount",
+            expression = "java(post.getLikes() != null ? post.getLikes().size() : 0)")
+    PostDto toDto(Post post);
+}
diff --git a/src/main/java/faang/school/postservice/service/CommentService.java b/src/main/java/faang/school/postservice/service/CommentService.java
new file mode 100644
index 0000000000..9bc2f0b61d
--- /dev/null
+++ b/src/main/java/faang/school/postservice/service/CommentService.java
@@ -0,0 +1,23 @@
+package faang.school.postservice.service;
+
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.model.Comment;
+import faang.school.postservice.repository.CommentRepository;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class CommentService {
+    private final CommentRepository commentRepository;
+
+    @Transactional
+    public Comment getComment(Long commentId) {
+        log.info("Start method getComment with commentId: {}", commentId);
+        return commentRepository.findById(commentId)
+                .orElseThrow(() -> new DataValidationException("Comment not found!"));
+    }
+}
diff --git a/src/main/java/faang/school/postservice/service/LikeService.java b/src/main/java/faang/school/postservice/service/LikeService.java
new file mode 100644
index 0000000000..5aec757878
--- /dev/null
+++ b/src/main/java/faang/school/postservice/service/LikeService.java
@@ -0,0 +1,97 @@
+package faang.school.postservice.service;
+
+import faang.school.postservice.client.UserServiceClient;
+import faang.school.postservice.dto.like.LikeDto;
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.mapper.LikeMapper;
+import faang.school.postservice.model.Like;
+import faang.school.postservice.repository.LikeRepository;
+import feign.FeignException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class LikeService {
+    private final LikeRepository likeRepository;
+    private final LikeMapper likeMapper;
+    private final PostService postService;
+    private final CommentService commentService;
+    private final UserServiceClient userServiceClient;
+
+    @Transactional
+    public LikeDto addLikeToPost(Long userId, Long postId) {
+        checkIfLikeAuthorExists(userId);
+        checkIfPostIsAlreadyLiked(userId, postId);
+
+        Like newLikeToPost = Like.builder()
+                .userId(userId)
+                .post(postService.getPost(postId))
+                .build();
+        return likeMapper.toDto(likeRepository.save(newLikeToPost));
+    }
+
+    @Transactional
+    public boolean removeLikeFromPost(Long postId, Long userId) {
+        if (likeRepository.findByPostIdAndUserId(postId, userId).isPresent()) {
+            likeRepository.deleteByPostIdAndUserId(postId, userId);
+            log.info("Successfully removed like from postId: {} and userId: {}", postId, userId);
+            return true;
+        }
+        return false;
+    }
+
+    @Transactional
+    public LikeDto addLikeToComment(Long userId, Long commentId) {
+        checkIfLikeAuthorExists(userId);
+        checkIfCommentIsAlreadyLiked(userId, commentId);
+
+        Like newLikeToComment = Like.builder()
+                .userId(userId)
+                .comment(commentService.getComment(commentId))
+                .createdAt(LocalDateTime.now())
+                .build();
+        return likeMapper.toDto(likeRepository.save(newLikeToComment));
+    }
+
+    @Transactional
+    public boolean removeLikeFromComment(Long commentId, Long userId) {
+        if (likeRepository.findByCommentIdAndUserId(commentId, userId).isPresent()) {
+            likeRepository.deleteByCommentIdAndUserId(commentId, userId);
+            log.info("Successfully removed like from commentId: {} and userId: {}", commentId, userId);
+            return true;
+        }
+        return false;
+    }
+
+    private void checkIfLikeAuthorExists(Long authorId) {
+        try {
+            userServiceClient.getUser(authorId);
+        } catch (FeignException e) {
+            log.error("Like authorId {} not found", authorId);
+            throw new DataValidationException("Like author not found");
+        }
+        log.info("Successfully validated like authorId: {}", authorId);
+    }
+
+    private void checkIfPostIsAlreadyLiked(Long postId, Long userId) {
+        if (likeRepository.findByPostIdAndUserId(postId, userId).isPresent()) {
+            log.error("This post {} is already liked by this user {}", postId, userId);
+            throw new DataValidationException("Post is already liked by this user");
+        }
+        log.info("Post {} is not liked by this user {} and is possible to do it!", postId, userId);
+    }
+
+    private void checkIfCommentIsAlreadyLiked(Long commentId, Long userId) {
+        if (likeRepository.findByCommentIdAndUserId(commentId, userId).isPresent()) {
+            log.error("This comment {} is already liked by this user {}", commentId, userId);
+            throw new DataValidationException("Comment is already liked by this user");
+        }
+        log.info("Comment {} is not liked by this user {} and is possible to do it!", commentId, userId);
+    }
+}
diff --git a/src/main/java/faang/school/postservice/service/PostService.java b/src/main/java/faang/school/postservice/service/PostService.java
new file mode 100644
index 0000000000..10fe3e8322
--- /dev/null
+++ b/src/main/java/faang/school/postservice/service/PostService.java
@@ -0,0 +1,23 @@
+package faang.school.postservice.service;
+
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.model.Post;
+import faang.school.postservice.repository.PostRepository;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class PostService {
+    private final PostRepository postRepository;
+
+    @Transactional
+    public Post getPost(Long postId) {
+        log.info("Start method getPost with postId: {}", postId);
+        return postRepository.findById(postId)
+                .orElseThrow(() -> new DataValidationException("Post not found!"));
+    }
+}
diff --git a/src/test/java/faang/school/postservice/controller/LikeControllerTest.java b/src/test/java/faang/school/postservice/controller/LikeControllerTest.java
new file mode 100644
index 0000000000..7b67c4b9dd
--- /dev/null
+++ b/src/test/java/faang/school/postservice/controller/LikeControllerTest.java
@@ -0,0 +1,99 @@
+package faang.school.postservice.controller;
+
+import faang.school.postservice.dto.like.LikeDto;
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.service.LikeService;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.http.HttpStatus;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class LikeControllerTest {
+    @Mock
+    private LikeService likeService;
+    @InjectMocks
+    private LikeController likeController;
+
+    @Test
+    void testAddLikeToPost() {
+        LikeDto likeDto = createLikeDto(4444L);
+        when(likeService.addLikeToPost(anyLong(), anyLong())).thenReturn(likeDto);
+
+        assertEquals(likeDto, likeController.addLikeToPost(1L, 1L));
+        verify(likeService).addLikeToPost(any(), anyLong());
+    }
+
+    @Test
+    void testAddNullLikeToPost() {
+        DataValidationException dataValidationException = assertThrows(DataValidationException.class, () ->
+                likeController.addLikeToPost(null, null));
+        assertEquals("Negative or null id! Use valid Id!", dataValidationException.getMessage());
+    }
+
+    @Test
+    void testRemoveLikeFromPost() {
+        when(likeService.removeLikeFromPost(anyLong(), anyLong())).thenReturn(true);
+
+        assertEquals(HttpStatus.NO_CONTENT, likeController.removeLikeFromPost(1L, 1L).getStatusCode());
+        verify(likeService).removeLikeFromPost(anyLong(), anyLong());
+    }
+
+    @Test
+    void testFailedRemoveFromPost() {
+        when(likeService.removeLikeFromPost(anyLong(), anyLong())).thenReturn(false);
+
+        assertEquals(HttpStatus.NOT_FOUND, likeController.removeLikeFromPost(1L, 1L).getStatusCode());
+        verify(likeService).removeLikeFromPost(anyLong(), anyLong());
+    }
+
+    @Test
+    void testAddLikeToComment() {
+        LikeDto likeDto = createLikeDto(5555L);
+        when(likeService.addLikeToComment(any(), anyLong())).thenReturn(likeDto);
+
+        assertEquals(likeDto, likeService.addLikeToComment(1L, 1l));
+        verify(likeService).addLikeToComment(anyLong(), anyLong());
+    }
+
+    @Test
+    void testAddNullLikeToComment() {
+        DataValidationException dataValidationException = assertThrows(DataValidationException.class, () ->
+                likeController.addLikeToComment(null, null));
+        assertEquals("Negative or null id! Use valid Id!", dataValidationException.getMessage());
+    }
+
+    @Test
+    void testRemoveLikeFromComment() {
+        when(likeService.removeLikeFromComment(anyLong(), anyLong())).thenReturn(true);
+
+        assertEquals(HttpStatus.NO_CONTENT, likeController.removeLikeFromComment(1L, 1L).getStatusCode());
+        verify(likeService).removeLikeFromComment(anyLong(), anyLong());
+    }
+
+    @Test
+    void testFailedRemoveLikeFromComment() {
+        when(likeService.removeLikeFromComment(anyLong(), anyLong())).thenReturn(false);
+
+        assertEquals(HttpStatus.NOT_FOUND, likeController.removeLikeFromComment(1L, 1L).getStatusCode());
+        verify(likeService).removeLikeFromComment(anyLong(), anyLong());
+    }
+
+    private LikeDto createLikeDto(Long userId) {
+        LikeDto likeDto = new LikeDto();
+        likeDto.setId(231423L);
+        likeDto.setCommentId(2433L);
+        likeDto.setUserId(userId);
+        likeDto.setPostId(34356L);
+        return likeDto;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/faang/school/postservice/service/CommentServiceTest.java b/src/test/java/faang/school/postservice/service/CommentServiceTest.java
new file mode 100644
index 0000000000..9073f1c790
--- /dev/null
+++ b/src/test/java/faang/school/postservice/service/CommentServiceTest.java
@@ -0,0 +1,39 @@
+package faang.school.postservice.service;
+
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.model.Comment;
+import faang.school.postservice.repository.CommentRepository;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class CommentServiceTest {
+    @Mock
+    private CommentRepository commentRepository;
+    @InjectMocks
+    private CommentService commentService;
+
+    @Test
+    void testGetComment() {
+        Comment comment = new Comment();
+        when(commentRepository.findById(anyLong())).thenReturn(Optional.of(comment));
+        assertEquals(comment, commentService.getComment(1L));
+    }
+
+    @Test
+    void testCommentIsNotFound() {
+        when(commentRepository.findById(anyLong())).thenReturn(Optional.empty());
+        DataValidationException dataValidationException = assertThrows(DataValidationException.class,
+                () -> commentService.getComment(1L));
+        assertEquals("Comment not found!", dataValidationException.getMessage());
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/faang/school/postservice/service/LikeServiceTest.java b/src/test/java/faang/school/postservice/service/LikeServiceTest.java
new file mode 100644
index 0000000000..b38fb7c2ab
--- /dev/null
+++ b/src/test/java/faang/school/postservice/service/LikeServiceTest.java
@@ -0,0 +1,148 @@
+package faang.school.postservice.service;
+
+import faang.school.postservice.client.UserServiceClient;
+import faang.school.postservice.dto.user.UserDto;
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.mapper.LikeMapper;
+import faang.school.postservice.model.Comment;
+import faang.school.postservice.model.Like;
+import faang.school.postservice.model.Post;
+import faang.school.postservice.repository.LikeRepository;
+import feign.FeignException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.time.LocalDateTime;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class LikeServiceTest {
+    @Mock
+    private LikeRepository likeRepository;
+    @Mock
+    private PostService postService;
+    @Mock
+    private CommentService commentService;
+    @Mock
+    private UserServiceClient userServiceClient;
+    @Spy
+    private LikeMapper likeMapper;
+    @InjectMocks
+    private LikeService likeService;
+
+    private Like likeEntity;
+
+    @BeforeEach
+    void setUp() {
+        likeEntity = Like.builder()
+                .userId(9999L)
+                .createdAt(LocalDateTime.now())
+                .build();
+    }
+
+    @Test
+    void testAddLikeToPostAuthorNotFound() {
+        when(userServiceClient.getUser(anyLong())).thenThrow(FeignException.class);
+        DataValidationException dataValidationException = assertThrows(DataValidationException.class,
+                () -> likeService.addLikeToPost(1L, 1L));
+        assertEquals("Like author not found", dataValidationException.getMessage());
+    }
+
+    @Test
+    void testAddLikeToPostAuthorAlreadyLiked() {
+        when(likeRepository.findByPostIdAndUserId(anyLong(), anyLong()))
+                .thenReturn(Optional.of(Mockito.mock(Like.class)));
+        DataValidationException dataValidationException = assertThrows(DataValidationException.class,
+                () -> likeService.addLikeToPost(1L, 1L));
+        assertEquals("Post is already liked by this user", dataValidationException.getMessage());
+    }
+
+    @Test
+    void testAddLikeToPostReturnedValue() {
+        UserDto userDto = new UserDto(2343L, "test", "test");
+        when(userServiceClient.getUser(anyLong())).thenReturn(userDto);
+        when(likeRepository.findByPostIdAndUserId(anyLong(), anyLong()))
+                .thenReturn(Optional.empty());
+        when(postService.getPost(anyLong())).thenReturn(Mockito.mock(Post.class));
+        when(likeRepository.save(any())).thenReturn(likeEntity);
+
+        assertNotNull(likeRepository.save(any()));
+        assertEquals(likeMapper.toDto(likeEntity), likeService.addLikeToPost(1L, 1L));
+    }
+
+    @Test
+    void testRemoveInexistentAuthorsLikeFromPost() {
+        when(likeRepository.findByPostIdAndUserId(anyLong(), anyLong()))
+                .thenReturn(Optional.empty());
+        assertFalse(likeService.removeLikeFromPost(1L, 1L));
+    }
+
+    @Test
+    void testRemoveLikeFromPostSuccesfull() {
+        when(likeRepository.findByPostIdAndUserId(anyLong(), anyLong()))
+                .thenReturn(Optional.of(new Like()));
+        assertTrue(likeService.removeLikeFromPost(1L, 1L));
+        verify(likeRepository).deleteByPostIdAndUserId(anyLong(), anyLong());
+    }
+
+    @Test
+    void testAddLikeToCommentAuthorNotFound() {
+        when(userServiceClient.getUser(anyLong())).thenThrow(FeignException.class);
+        DataValidationException dataValidationException = assertThrows(DataValidationException.class,
+                () -> likeService.addLikeToComment(1L, 1L));
+        assertEquals("Like author not found", dataValidationException.getMessage());
+    }
+
+    @Test
+    void testAddLikeToCommentAuthorAlreadyLiked() {
+        when(likeRepository.findByCommentIdAndUserId(anyLong(), anyLong()))
+                .thenReturn(Optional.of(Mockito.mock(Like.class)));
+        DataValidationException dataValidationException = assertThrows(DataValidationException.class,
+                () -> likeService.addLikeToComment(1L, 1L));
+        assertEquals("Comment is already liked by this user", dataValidationException.getMessage());
+    }
+
+    @Test
+    void testAddLikeToCommentReturnedValue() {
+        UserDto userDto = new UserDto(8364L, "test2", "test2");
+        when(userServiceClient.getUser(anyLong())).thenReturn(userDto);
+        when(likeRepository.findByCommentIdAndUserId(anyLong(), anyLong()))
+                .thenReturn(Optional.empty());
+        when(commentService.getComment(anyLong())).thenReturn(Mockito.mock(Comment.class));
+        when(likeRepository.save(any())).thenReturn(likeEntity);
+
+        assertNotNull(likeRepository.save(any()));
+        assertEquals(likeMapper.toDto(likeEntity), likeService.addLikeToComment(1L, 1L));
+    }
+
+    @Test
+    void testRemoveInexistentAuthorsLikeFromComment() {
+        when(likeRepository.findByCommentIdAndUserId(anyLong(), anyLong()))
+                .thenReturn(Optional.empty());
+        assertFalse(likeService.removeLikeFromComment(1L, 1L));
+    }
+
+    @Test
+    void testRemoveLikeFromCommentSuccesfull() {
+        when(likeRepository.findByCommentIdAndUserId(anyLong(), anyLong()))
+                .thenReturn(Optional.of(new Like()));
+        assertTrue(likeService.removeLikeFromComment(1L, 1L));
+        verify(likeRepository).deleteByCommentIdAndUserId(anyLong(), anyLong());
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/faang/school/postservice/service/PostServiceTest.java b/src/test/java/faang/school/postservice/service/PostServiceTest.java
new file mode 100644
index 0000000000..b1787181e1
--- /dev/null
+++ b/src/test/java/faang/school/postservice/service/PostServiceTest.java
@@ -0,0 +1,40 @@
+package faang.school.postservice.service;
+
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.model.Post;
+import faang.school.postservice.repository.PostRepository;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class PostServiceTest {
+    @Mock
+    private PostRepository postRepository;
+    @InjectMocks
+    private PostService postService;
+
+    @Test
+    void testGetPost() {
+        Post post = new Post();
+        when(postRepository.findById(anyLong())).thenReturn(Optional.of(post));
+        assertEquals(post, postService.getPost(1L));
+    }
+
+    @Test
+    void testPostIsNotFound() {
+        when(postRepository.findById(anyLong())).thenReturn(Optional.empty());
+        DataValidationException dataValidationException = assertThrows(DataValidationException.class,
+                () -> postService.getPost(1L));
+        assertEquals("Post not found!", dataValidationException.getMessage());
+    }
+}
\ No newline at end of file


Reviews:
user: login: Raidanmark
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-23T15:00:29Z
user: login: danbg12
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-23T15:11:34Z
user: login: danbg12
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-23T15:13:40Z
user: login: danbg12
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-23T15:14:44Z
user: login: danbg12
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-23T17:37:32Z
user: login: AlexeyAntipin
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-26T04:50:07Z
user: login: danbg12
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-26T18:41:12Z
user: login: AlexeyAntipin
state: APPROVED
author_association: NONE
submitted_at: 2025-05-30T07:12:40Z

Review comments:
diff_hunk: @@ -0,0 +1,68 @@
+package faang.school.postservice.controller;
+
+import faang.school.postservice.dto.like.LikeDto;
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.service.LikeService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Objects;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class LikeController {
+    private final LikeService likeService;
+
+    @PostMapping("/post/{postId}/like")
path: src/main/java/faang/school/postservice/controller/LikeController.java
user: login: Raidanmark
body: Структура для коллекции поста должна быть во множественном числе /posts/{postId}/like
Но вообще я бы задумался о том что тебе стоит использовать не коллекцию posts а все таки likes. Уже как бы лайки относятся не только к постам но и к комментами этих постов. А если завтра можно будет лайкать что то еще то прийдется снова переделывать - теряется модульность.
Ну и втаком случае легче указать если не ошибаюсь @RequestMapping("likes")
created_at: 2025-05-23T14:34:35Z
author_association: NONE
diff_hunk: @@ -0,0 +1,68 @@
+package faang.school.postservice.controller;
+
+import faang.school.postservice.dto.like.LikeDto;
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.service.LikeService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Objects;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class LikeController {
+    private final LikeService likeService;
+
+    @PostMapping("/post/{postId}/like")
+    public LikeDto addLikeToPost(@PathVariable Long userId, @PathVariable Long postId) {
+        log.info("Start method addLikeToPost with postId: {} and userId: {}", postId, userId);
+        validateId(userId);
+        validateId(postId);
+
+        return likeService.addLikeToPost(userId, postId);
+    }
+
+    @DeleteMapping("/post/{postId}/like")
+    public ResponseEntity<Void> removeLikeFromPost(@PathVariable Long postId, @PathVariable Long userId) {
+        log.info("Start method removeLikeFromPost with postId: {}", postId);
+        validateId(postId);
+
+        return likeService.removeLikeFromPost(postId, userId) ?
+                new ResponseEntity<>(HttpStatus.NO_CONTENT) :
+                new ResponseEntity<>(HttpStatus.NOT_FOUND);
+    }
+
+    @PostMapping("comment/{commentId}/like")
+    public LikeDto addLikeToComment(@PathVariable Long userId, @PathVariable Long commentId) {
+        log.info("Start method addLikeToComment with commentId: {} and userId: {}", commentId, userId);
+        validateId(userId);
+        validateId(commentId);
+
+        return likeService.addLikeToComment(userId, commentId);
+    }
+
+    @DeleteMapping("comment/{commentId}/like")
+    public ResponseEntity<Void> removeLikeFromComment(@PathVariable Long commentId, @PathVariable Long userId) {
+        log.info("Start method removeLikeFromComment with commentId: {}", commentId);
+        validateId(commentId);
+
+        return likeService.removeLikeFromComment(commentId, userId) ?
+                new ResponseEntity<>(HttpStatus.NO_CONTENT) :
+                new ResponseEntity<>(HttpStatus.NOT_FOUND);
+    }
+
+    private void validateId(Long id) {
+        if (Objects.isNull(id) || id <= 0) {
+            log.error("Used id is null or negative: {}", id);
+            throw new DataValidationException("Negative or null id! Use valid Id!");
+        }
+        log.info("Used id {} was successfully validated!", id);
path: src/main/java/faang/school/postservice/controller/LikeController.java
user: login: Raidanmark
body: Навреное если ты решил заморочиться с валидацией и логирыванием айдищников, было бы круто в логах показать какой айдишник не прошел валидацию пользователя, лайка, поста или коммента.

created_at: 2025-05-23T14:40:44Z
author_association: NONE
diff_hunk: @@ -0,0 +1,68 @@
+package faang.school.postservice.controller;
+
+import faang.school.postservice.dto.like.LikeDto;
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.service.LikeService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Objects;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class LikeController {
+    private final LikeService likeService;
+
+    @PostMapping("/post/{postId}/like")
+    public LikeDto addLikeToPost(@PathVariable Long userId, @PathVariable Long postId) {
+        log.info("Start method addLikeToPost with postId: {} and userId: {}", postId, userId);
+        validateId(userId);
+        validateId(postId);
+
+        return likeService.addLikeToPost(userId, postId);
+    }
+
+    @DeleteMapping("/post/{postId}/like")
+    public ResponseEntity<Void> removeLikeFromPost(@PathVariable Long postId, @PathVariable Long userId) {
+        log.info("Start method removeLikeFromPost with postId: {}", postId);
+        validateId(postId);
+
+        return likeService.removeLikeFromPost(postId, userId) ?
path: src/main/java/faang/school/postservice/controller/LikeController.java
user: login: Raidanmark
body: UserId не валидируется
created_at: 2025-05-23T14:41:56Z
author_association: NONE
path: src/main/java/faang/school/postservice/dto/like/PostDto.java
user: login: Raidanmark
body: У тебя пост дто в фолдере лайка
created_at: 2025-05-23T14:49:44Z
author_association: NONE
path: src/main/java/faang/school/postservice/dto/like/CommentDto.java
user: login: Raidanmark
body: У тебя коммент дто в фолдере лайка
created_at: 2025-05-23T14:50:06Z
author_association: NONE
diff_hunk: @@ -0,0 +1,23 @@
+package faang.school.postservice.service;
+
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.model.Comment;
+import faang.school.postservice.repository.CommentRepository;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class CommentService {
+    private final CommentRepository commentRepository;
+
+    @Transactional(rollbackFor = Exception.class)
+    public Comment getComment(Long commentId) {
path: src/main/java/faang/school/postservice/service/CommentService.java
user: login: Raidanmark
body: Паблик метод в сервисе возвращает сущность - это не очень хорошо
created_at: 2025-05-23T14:52:23Z
author_association: NONE
diff_hunk: @@ -0,0 +1,23 @@
+package faang.school.postservice.service;
+
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.model.Post;
+import faang.school.postservice.repository.PostRepository;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class PostService {
+    private final PostRepository postRepository;
+
+    @Transactional(rollbackFor = Exception.class)
+    public Post getPost(Long postId) {
path: src/main/java/faang/school/postservice/service/PostService.java
user: login: Raidanmark
body: Метод сервиса отдает сущность это не хорошо
created_at: 2025-05-23T14:58:13Z
author_association: NONE
diff_hunk: @@ -0,0 +1,68 @@
+package faang.school.postservice.controller;
+
+import faang.school.postservice.dto.like.LikeDto;
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.service.LikeService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Objects;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class LikeController {
+    private final LikeService likeService;
+
+    @PostMapping("/post/{postId}/like")
+    public LikeDto addLikeToPost(@PathVariable Long userId, @PathVariable Long postId) {
+        log.info("Start method addLikeToPost with postId: {} and userId: {}", postId, userId);
+        validateId(userId);
+        validateId(postId);
+
+        return likeService.addLikeToPost(userId, postId);
+    }
+
+    @DeleteMapping("/post/{postId}/like")
+    public ResponseEntity<Void> removeLikeFromPost(@PathVariable Long postId, @PathVariable Long userId) {
+        log.info("Start method removeLikeFromPost with postId: {}", postId);
+        validateId(postId);
+
+        return likeService.removeLikeFromPost(postId, userId) ?
+                new ResponseEntity<>(HttpStatus.NO_CONTENT) :
+                new ResponseEntity<>(HttpStatus.NOT_FOUND);
+    }
+
+    @PostMapping("comment/{commentId}/like")
+    public LikeDto addLikeToComment(@PathVariable Long userId, @PathVariable Long commentId) {
+        log.info("Start method addLikeToComment with commentId: {} and userId: {}", commentId, userId);
+        validateId(userId);
+        validateId(commentId);
+
+        return likeService.addLikeToComment(userId, commentId);
+    }
+
+    @DeleteMapping("comment/{commentId}/like")
+    public ResponseEntity<Void> removeLikeFromComment(@PathVariable Long commentId, @PathVariable Long userId) {
+        log.info("Start method removeLikeFromComment with commentId: {}", commentId);
+        validateId(commentId);
+
+        return likeService.removeLikeFromComment(commentId, userId) ?
+                new ResponseEntity<>(HttpStatus.NO_CONTENT) :
+                new ResponseEntity<>(HttpStatus.NOT_FOUND);
+    }
+
+    private void validateId(Long id) {
+        if (Objects.isNull(id) || id <= 0) {
+            log.error("Used id is null or negative: {}", id);
+            throw new DataValidationException("Negative or null id! Use valid Id!");
+        }
+        log.info("Used id {} was successfully validated!", id);
path: src/main/java/faang/school/postservice/controller/LikeController.java
user: login: danbg12
body: в log.error указанно какои id не прошел 
created_at: 2025-05-23T15:11:33Z
author_association: NONE
diff_hunk: @@ -0,0 +1,23 @@
+package faang.school.postservice.service;
+
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.model.Comment;
+import faang.school.postservice.repository.CommentRepository;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class CommentService {
+    private final CommentRepository commentRepository;
+
+    @Transactional(rollbackFor = Exception.class)
+    public Comment getComment(Long commentId) {
path: src/main/java/faang/school/postservice/service/CommentService.java
user: login: danbg12
body: его вызывает другои сервис так что нормально 
created_at: 2025-05-23T15:13:39Z
author_association: NONE
path: src/main/java/faang/school/postservice/dto/like/PostDto.java
user: login: danbg12
body: прозевал :D
created_at: 2025-05-23T15:14:43Z
author_association: NONE
diff_hunk: @@ -0,0 +1,68 @@
+package faang.school.postservice.controller;
+
+import faang.school.postservice.dto.like.LikeDto;
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.service.LikeService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Objects;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class LikeController {
+    private final LikeService likeService;
+
+    @PostMapping("/post/{postId}/like")
path: src/main/java/faang/school/postservice/controller/LikeController.java
user: login: danbg12
body: Лаик один , Юзер один , пост или коммент тоже один , тут нет никаких колекции . Один юзер по условию всего один раз ставит лаик одному посту и все. Затем если у поста есть лаик от того юзера то он может его от туда убрать. С комментами все так же 
created_at: 2025-05-23T17:37:32Z
author_association: NONE
diff_hunk: @@ -0,0 +1,70 @@
+package faang.school.postservice.controller;
+
+import faang.school.postservice.dto.like.LikeDto;
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.service.LikeService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Objects;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+public class LikeController {
+    private final LikeService likeService;
+
+    @PostMapping("like/post/{postId}/user/{userId}")
path: src/main/java/faang/school/postservice/controller/LikeController.java
user: login: AlexeyAntipin
body: Вынеси /like на уровень контроллера. И лучше назови не like, а likes, так более restful
created_at: 2025-05-26T04:47:49Z
author_association: NONE
diff_hunk: @@ -0,0 +1,98 @@
+package faang.school.postservice.service;
+
+import faang.school.postservice.client.UserServiceClient;
+import faang.school.postservice.dto.like.LikeDto;
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.mapper.LikeMapper;
+import faang.school.postservice.model.Like;
+import faang.school.postservice.repository.LikeRepository;
+import feign.FeignException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class LikeService {
+    private final LikeRepository likeRepository;
+    private final LikeMapper likeMapper;
+    private final PostService postService;
+    private final CommentService commentService;
+    private final UserServiceClient userServiceClient;
+
+    @Transactional(rollbackFor = Exception.class)
+    public LikeDto addLikeToPost(Long userId, Long postId) {
+        checkIfLikeAuthorExists(userId);
+        checkIfPostIsAlreadyLiked(userId, postId);
+
+        Like newLikeToPost = Like.builder()
+                .userId(userId)
+                .post(postService.getPost(postId))
+                .createdAt(LocalDateTime.now())
path: src/main/java/faang/school/postservice/service/LikeService.java
user: login: AlexeyAntipin
body: Можно внутри сущности like на поле createdAt поставить аннотацию @CreationTimestamp, тогда нужная дата будет автоматически ставиться
created_at: 2025-05-26T04:49:19Z
author_association: NONE
diff_hunk: @@ -0,0 +1,98 @@
+package faang.school.postservice.service;
+
+import faang.school.postservice.client.UserServiceClient;
+import faang.school.postservice.dto.like.LikeDto;
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.mapper.LikeMapper;
+import faang.school.postservice.model.Like;
+import faang.school.postservice.repository.LikeRepository;
+import feign.FeignException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class LikeService {
+    private final LikeRepository likeRepository;
+    private final LikeMapper likeMapper;
+    private final PostService postService;
+    private final CommentService commentService;
+    private final UserServiceClient userServiceClient;
+
+    @Transactional(rollbackFor = Exception.class)
path: src/main/java/faang/school/postservice/service/LikeService.java
user: login: AlexeyAntipin
body: А зачем rollbackFor = Exception.class? Он и так Exceptionы ролбэчит
created_at: 2025-05-26T04:49:58Z
author_association: NONE
diff_hunk: @@ -0,0 +1,98 @@
+package faang.school.postservice.service;
+
+import faang.school.postservice.client.UserServiceClient;
+import faang.school.postservice.dto.like.LikeDto;
+import faang.school.postservice.exception.DataValidationException;
+import faang.school.postservice.mapper.LikeMapper;
+import faang.school.postservice.model.Like;
+import faang.school.postservice.repository.LikeRepository;
+import feign.FeignException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class LikeService {
+    private final LikeRepository likeRepository;
+    private final LikeMapper likeMapper;
+    private final PostService postService;
+    private final CommentService commentService;
+    private final UserServiceClient userServiceClient;
+
+    @Transactional(rollbackFor = Exception.class)
+    public LikeDto addLikeToPost(Long userId, Long postId) {
+        checkIfLikeAuthorExists(userId);
+        checkIfPostIsAlreadyLiked(userId, postId);
+
+        Like newLikeToPost = Like.builder()
+                .userId(userId)
+                .post(postService.getPost(postId))
+                .createdAt(LocalDateTime.now())
path: src/main/java/faang/school/postservice/service/LikeService.java
user: login: danbg12
body: Даже не заметил что оня там уже есть )))
created_at: 2025-05-26T18:41:12Z
author_association: NONE

PR comments:
user: login: Raidanmark
created_at: 2025-05-23T15:05:48Z
body: Я так понимаю там блокнутый один метод, сязанный с моей задачи с постами
author_association: NONE
user: login: AlexeyAntipin
created_at: 2025-05-27T05:01:31Z
body: Вмерзли фикс, сделай рибейз и закинь новую версию
author_association: NONE