Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.codeit.todo.common.exception.likes;

import com.codeit.todo.common.exception.EntityNotFoundException;

public class LikesNotFoundException extends EntityNotFoundException {
private static final String ENTITY_TYPE = "likes";

/**
* @param request 엔티티를 찾기 위해 요청한 값
*/
public LikesNotFoundException(String request) {
super(request, ENTITY_TYPE);
}
}
15 changes: 15 additions & 0 deletions src/main/java/com/codeit/todo/domain/Likes.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

Expand All @@ -20,4 +21,18 @@ public class Likes {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "complete_id", nullable = false)
private Complete complete;

@Builder
public Likes(int likesId, User user, Complete complete) {
this.likesId = likesId;
this.user = user;
this.complete = complete;
}

public static Likes toEntity(User user, Complete complete) {
return Likes.builder()
.user(user)
.complete(complete)
.build();
}
}
4 changes: 4 additions & 0 deletions src/main/java/com/codeit/todo/repository/LikesRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
import com.codeit.todo.domain.Likes;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface LikesRepository extends JpaRepository<Likes, Integer> {
Boolean existsByUser_UserIdAndComplete_CompleteId(int userId, int completeId);

Optional<Likes> findByComplete_CompleteIdAndUser_UserId(int completeId, int userId);
}
10 changes: 10 additions & 0 deletions src/main/java/com/codeit/todo/service/likes/LikesService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.codeit.todo.service.likes;

import com.codeit.todo.web.dto.response.likes.CreateLikeResponse;
import com.codeit.todo.web.dto.response.likes.DeleteLikeResponse;

public interface LikesService {
CreateLikeResponse createLikes(int userId, int completeId);

DeleteLikeResponse deleteLikes(int userId, int completeId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.codeit.todo.service.likes.impl;

import com.codeit.todo.common.exception.auth.AuthorizationDeniedException;
import com.codeit.todo.common.exception.complete.CompleteNotFoundException;
import com.codeit.todo.common.exception.likes.LikesNotFoundException;
import com.codeit.todo.common.exception.user.UserNotFoundException;
import com.codeit.todo.domain.Complete;
import com.codeit.todo.domain.Likes;
import com.codeit.todo.domain.User;
import com.codeit.todo.repository.CompleteRepository;
import com.codeit.todo.repository.LikesRepository;
import com.codeit.todo.repository.UserRepository;
import com.codeit.todo.service.likes.LikesService;
import com.codeit.todo.web.dto.response.likes.CreateLikeResponse;
import com.codeit.todo.web.dto.response.likes.DeleteLikeResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional
public class LikesServiceImpl implements LikesService {

private final UserRepository userRepository;
private final CompleteRepository completeRepository;
private final LikesRepository likesRepository;

@Override
public CreateLikeResponse createLikes(int userId, int completeId) {
if (likesRepository.existsByUser_UserIdAndComplete_CompleteId(userId, completeId)) {
throw new AuthorizationDeniedException("이미 좋아요를 눌렀습니다.");
}

Complete complete = getComplete(completeId);

if (complete.getTodo().getGoal().getUser().getUserId() == userId) {
throw new AuthorizationDeniedException("자신의 게시글에 좋아요를 누를 수 없습니다.");
}

User user = getUser(userId);
Likes likes = Likes.toEntity(user, complete);
Likes createdLikes = likesRepository.save(likes);

return CreateLikeResponse.fromEntity(createdLikes);
}

@Override
public DeleteLikeResponse deleteLikes(int userId, int completeId) {
Complete complete = getComplete(completeId);
User user = getUser(userId);

Likes likes = likesRepository.findByComplete_CompleteIdAndUser_UserId(complete.getCompleteId(), user.getUserId())
.orElseThrow(() -> new LikesNotFoundException("좋아요를 누른 기록이 없습니다."));
likesRepository.delete(likes);

return DeleteLikeResponse.fromEntity(complete);
}

private Complete getComplete(int completeId) {
return completeRepository.findById(completeId)
.orElseThrow(() -> new CompleteNotFoundException(String.valueOf(completeId)));
}

private User getUser(int userId) {
return userRepository.findById(userId)
.orElseThrow(() -> new UserNotFoundException(String.valueOf(userId), "User"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.codeit.todo.web.dto.request.complete.UpdateCompleteRequest;
import com.codeit.todo.web.dto.response.Response;
import com.codeit.todo.web.dto.response.complete.ReadCompleteDetailResponse;
import com.codeit.todo.web.dto.response.complete.ReadCompleteResponse;
import com.codeit.todo.web.dto.response.complete.UpdateCompleteResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
Expand Down
36 changes: 36 additions & 0 deletions src/main/java/com/codeit/todo/web/controller/LikesController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.codeit.todo.web.controller;

import com.codeit.todo.repository.CustomUserDetails;
import com.codeit.todo.service.likes.LikesService;
import com.codeit.todo.web.dto.response.Response;
import com.codeit.todo.web.dto.response.likes.CreateLikeResponse;
import com.codeit.todo.web.dto.response.likes.DeleteLikeResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/completes/{completeId}/likes")
public class LikesController {

private final LikesService likesService;

@PostMapping
public Response<CreateLikeResponse> createLikes(
@AuthenticationPrincipal CustomUserDetails userDetails,
@PathVariable int completeId
) {
int userId = userDetails.getUserId();
return Response.ok(likesService.createLikes(userId, completeId));
}

@DeleteMapping
public Response<DeleteLikeResponse> deleteLikes(
@AuthenticationPrincipal CustomUserDetails userDetails,
@PathVariable int completeId
) {
int userId = userDetails.getUserId();
return Response.ok(likesService.deleteLikes(userId, completeId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.codeit.todo.web.dto.response.likes;

import com.codeit.todo.domain.Likes;
import lombok.Builder;

@Builder
public record CreateLikeResponse(int completeId, int likesId) {

public static CreateLikeResponse fromEntity(Likes likes) {
return CreateLikeResponse.builder()
.completeId(likes.getComplete().getCompleteId())
.likesId(likes.getLikesId())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.codeit.todo.web.dto.response.likes;

import com.codeit.todo.domain.Complete;
import lombok.Builder;

@Builder
public record DeleteLikeResponse(int completeId) {

public static DeleteLikeResponse fromEntity(Complete complete) {
return DeleteLikeResponse.builder()
.completeId(complete.getCompleteId())
.build();
}
}
Loading