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 online.wesal.wesal.dto.ApiResponse;
|
||||
import online.wesal.wesal.dto.PostCreateRequestDTO;
|
||||
import online.wesal.wesal.dto.PostLikeRequestDTO;
|
||||
import online.wesal.wesal.dto.PostResponseDTO;
|
||||
import online.wesal.wesal.entity.Post;
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
@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.entity.Post;
|
||||
import online.wesal.wesal.entity.PostLike;
|
||||
import online.wesal.wesal.entity.User;
|
||||
import online.wesal.wesal.repository.PostLikeRepository;
|
||||
import online.wesal.wesal.repository.PostRepository;
|
||||
import online.wesal.wesal.repository.UserRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
@ -19,6 +22,9 @@ public class PostService {
|
||||
@Autowired
|
||||
private PostRepository postRepository;
|
||||
|
||||
@Autowired
|
||||
private PostLikeRepository postLikeRepository;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@ -64,4 +70,57 @@ public class PostService {
|
||||
post = postRepository.save(post);
|
||||
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