feat: like and unlike backend endpoints
This commit is contained in:
parent
86a4535f9a
commit
066e032120
@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import online.wesal.wesal.dto.ApiResponse;
|
import online.wesal.wesal.dto.ApiResponse;
|
||||||
import online.wesal.wesal.dto.PostCreateRequestDTO;
|
import online.wesal.wesal.dto.PostCreateRequestDTO;
|
||||||
|
import online.wesal.wesal.dto.PostLikeRequestDTO;
|
||||||
import online.wesal.wesal.dto.PostResponseDTO;
|
import online.wesal.wesal.dto.PostResponseDTO;
|
||||||
import online.wesal.wesal.entity.Post;
|
import online.wesal.wesal.entity.Post;
|
||||||
import online.wesal.wesal.service.PostService;
|
import online.wesal.wesal.service.PostService;
|
||||||
@ -109,4 +110,78 @@ public class PostController {
|
|||||||
return ResponseEntity.status(500).body(ApiResponse.error("Something went wrong.. We're sorry but try again later"));
|
return ResponseEntity.status(500).body(ApiResponse.error("Something went wrong.. We're sorry but try again later"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/like", consumes = "application/json", produces = "application/json")
|
||||||
|
@Operation(summary = "Like post", description = "Like a post. Returns the updated post with new like count.")
|
||||||
|
public ResponseEntity<ApiResponse<PostResponseDTO>> likePost(
|
||||||
|
@Valid @RequestBody PostLikeRequestDTO request,
|
||||||
|
BindingResult bindingResult,
|
||||||
|
Authentication authentication) {
|
||||||
|
|
||||||
|
if (bindingResult.hasErrors()) {
|
||||||
|
String errorMessage = bindingResult.getFieldErrors().stream()
|
||||||
|
.map(error -> {
|
||||||
|
String field = error.getField();
|
||||||
|
if ("postId".equals(field)) return "Valid post ID is required";
|
||||||
|
return error.getDefaultMessage();
|
||||||
|
})
|
||||||
|
.findFirst()
|
||||||
|
.orElse("Invalid input");
|
||||||
|
return ResponseEntity.badRequest().body(ApiResponse.error(errorMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
PostResponseDTO response = postService.likePost(request.getPostId());
|
||||||
|
return ResponseEntity.ok(ApiResponse.success(response));
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
String message;
|
||||||
|
if (e.getMessage().contains("Post not found")) {
|
||||||
|
message = "Post not found";
|
||||||
|
} else if (e.getMessage().contains("User not found")) {
|
||||||
|
message = "Authentication error. Please log in again.";
|
||||||
|
} else {
|
||||||
|
message = "Something went wrong.. We're sorry but try again later";
|
||||||
|
}
|
||||||
|
return ResponseEntity.badRequest().body(ApiResponse.error(message));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return ResponseEntity.status(500).body(ApiResponse.error("Something went wrong.. We're sorry but try again later"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/unlike", consumes = "application/json", produces = "application/json")
|
||||||
|
@Operation(summary = "Unlike post", description = "Unlike a post. Returns the updated post with new like count.")
|
||||||
|
public ResponseEntity<ApiResponse<PostResponseDTO>> unlikePost(
|
||||||
|
@Valid @RequestBody PostLikeRequestDTO request,
|
||||||
|
BindingResult bindingResult,
|
||||||
|
Authentication authentication) {
|
||||||
|
|
||||||
|
if (bindingResult.hasErrors()) {
|
||||||
|
String errorMessage = bindingResult.getFieldErrors().stream()
|
||||||
|
.map(error -> {
|
||||||
|
String field = error.getField();
|
||||||
|
if ("postId".equals(field)) return "Valid post ID is required";
|
||||||
|
return error.getDefaultMessage();
|
||||||
|
})
|
||||||
|
.findFirst()
|
||||||
|
.orElse("Invalid input");
|
||||||
|
return ResponseEntity.badRequest().body(ApiResponse.error(errorMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
PostResponseDTO response = postService.unlikePost(request.getPostId());
|
||||||
|
return ResponseEntity.ok(ApiResponse.success(response));
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
String message;
|
||||||
|
if (e.getMessage().contains("Post not found")) {
|
||||||
|
message = "Post not found";
|
||||||
|
} else if (e.getMessage().contains("User not found")) {
|
||||||
|
message = "Authentication error. Please log in again.";
|
||||||
|
} else {
|
||||||
|
message = "Something went wrong.. We're sorry but try again later";
|
||||||
|
}
|
||||||
|
return ResponseEntity.badRequest().body(ApiResponse.error(message));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return ResponseEntity.status(500).body(ApiResponse.error("Something went wrong.. We're sorry but try again later"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package online.wesal.wesal.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Positive;
|
||||||
|
|
||||||
|
public class PostLikeRequestDTO {
|
||||||
|
|
||||||
|
@NotNull(message = "Post ID is required")
|
||||||
|
@Positive(message = "Post ID must be a positive number")
|
||||||
|
private Long postId;
|
||||||
|
|
||||||
|
public PostLikeRequestDTO() {}
|
||||||
|
|
||||||
|
public PostLikeRequestDTO(Long postId) {
|
||||||
|
this.postId = postId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getPostId() {
|
||||||
|
return postId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPostId(Long postId) {
|
||||||
|
this.postId = postId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
package online.wesal.wesal.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "post_likes", uniqueConstraints = {
|
||||||
|
@UniqueConstraint(columnNames = {"post_id", "user_id"})
|
||||||
|
})
|
||||||
|
public class PostLike {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(nullable = false, name = "post_id")
|
||||||
|
private Long postId;
|
||||||
|
|
||||||
|
@Column(nullable = false, name = "user_id")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private LocalDateTime createdAt = LocalDateTime.now();
|
||||||
|
|
||||||
|
public PostLike() {}
|
||||||
|
|
||||||
|
public PostLike(Long postId, Long userId) {
|
||||||
|
this.postId = postId;
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getPostId() {
|
||||||
|
return postId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPostId(Long postId) {
|
||||||
|
this.postId = postId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserId(Long userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getCreatedAt() {
|
||||||
|
return createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreatedAt(LocalDateTime createdAt) {
|
||||||
|
this.createdAt = createdAt;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package online.wesal.wesal.repository;
|
||||||
|
|
||||||
|
import online.wesal.wesal.entity.PostLike;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface PostLikeRepository extends JpaRepository<PostLike, Long> {
|
||||||
|
Optional<PostLike> findByPostIdAndUserId(Long postId, Long userId);
|
||||||
|
boolean existsByPostIdAndUserId(Long postId, Long userId);
|
||||||
|
void deleteByPostIdAndUserId(Long postId, Long userId);
|
||||||
|
long countByPostId(Long postId);
|
||||||
|
}
|
||||||
@ -2,11 +2,14 @@ package online.wesal.wesal.service;
|
|||||||
|
|
||||||
import online.wesal.wesal.dto.PostResponseDTO;
|
import online.wesal.wesal.dto.PostResponseDTO;
|
||||||
import online.wesal.wesal.entity.Post;
|
import online.wesal.wesal.entity.Post;
|
||||||
|
import online.wesal.wesal.entity.PostLike;
|
||||||
import online.wesal.wesal.entity.User;
|
import online.wesal.wesal.entity.User;
|
||||||
|
import online.wesal.wesal.repository.PostLikeRepository;
|
||||||
import online.wesal.wesal.repository.PostRepository;
|
import online.wesal.wesal.repository.PostRepository;
|
||||||
import online.wesal.wesal.repository.UserRepository;
|
import online.wesal.wesal.repository.UserRepository;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -19,6 +22,9 @@ public class PostService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private PostRepository postRepository;
|
private PostRepository postRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PostLikeRepository postLikeRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserRepository userRepository;
|
private UserRepository userRepository;
|
||||||
|
|
||||||
@ -64,4 +70,57 @@ public class PostService {
|
|||||||
post = postRepository.save(post);
|
post = postRepository.save(post);
|
||||||
return new PostResponseDTO(post, currentUser);
|
return new PostResponseDTO(post, currentUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public PostResponseDTO likePost(Long postId) {
|
||||||
|
User currentUser = userService.getCurrentUser();
|
||||||
|
Long userId = currentUser.getId();
|
||||||
|
|
||||||
|
// Check if post exists
|
||||||
|
Post post = postRepository.findById(postId)
|
||||||
|
.orElseThrow(() -> new RuntimeException("Post not found"));
|
||||||
|
|
||||||
|
// Check if user already liked this post
|
||||||
|
if (!postLikeRepository.existsByPostIdAndUserId(postId, userId)) {
|
||||||
|
// Add like record
|
||||||
|
PostLike postLike = new PostLike(postId, userId);
|
||||||
|
postLikeRepository.save(postLike);
|
||||||
|
|
||||||
|
// Update post likes count
|
||||||
|
post.setLikes(post.getLikes() + 1);
|
||||||
|
post = postRepository.save(post);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get creator for response
|
||||||
|
User creator = userRepository.findById(post.getCreatorId())
|
||||||
|
.orElseThrow(() -> new RuntimeException("Creator not found"));
|
||||||
|
|
||||||
|
return new PostResponseDTO(post, creator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public PostResponseDTO unlikePost(Long postId) {
|
||||||
|
User currentUser = userService.getCurrentUser();
|
||||||
|
Long userId = currentUser.getId();
|
||||||
|
|
||||||
|
// Check if post exists
|
||||||
|
Post post = postRepository.findById(postId)
|
||||||
|
.orElseThrow(() -> new RuntimeException("Post not found"));
|
||||||
|
|
||||||
|
// Check if user has liked this post
|
||||||
|
if (postLikeRepository.existsByPostIdAndUserId(postId, userId)) {
|
||||||
|
// Remove like record
|
||||||
|
postLikeRepository.deleteByPostIdAndUserId(postId, userId);
|
||||||
|
|
||||||
|
// Update post likes count
|
||||||
|
post.setLikes(Math.max(0, post.getLikes() - 1));
|
||||||
|
post = postRepository.save(post);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get creator for response
|
||||||
|
User creator = userRepository.findById(post.getCreatorId())
|
||||||
|
.orElseThrow(() -> new RuntimeException("Creator not found"));
|
||||||
|
|
||||||
|
return new PostResponseDTO(post, creator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user