-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* refactor: mutation 시 isPending으로 중복 제출 방지 * feat: reaction entity 구현 * feat: like service 구현 * refactor: 좋아요하지 않은 상태에서 좋아요 시 예외 처리 변경 * refactor: Like, LikeTarget dto 생성 * feat: exma like, unlike api 구현 * test: exam like document test 작성 * feat: reaction ddl
- Loading branch information
Showing
20 changed files
with
343 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
== Exam Like API | ||
|
||
=== 시험 좋아요 등록 | ||
|
||
operation::exam-like-document-test/like[] | ||
|
||
=== 시험 좋아요 취소 | ||
|
||
operation::exam-like-document-test/unlike[] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
server/src/main/java/com/fluffy/exam/api/ExamLikeController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package com.fluffy.exam.api; | ||
|
||
import com.fluffy.exam.application.ExamLikeService; | ||
import com.fluffy.global.web.Accessor; | ||
import com.fluffy.global.web.Auth; | ||
import lombok.RequiredArgsConstructor; | ||
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; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
public class ExamLikeController { | ||
|
||
private final ExamLikeService examLikeService; | ||
|
||
@PostMapping("/api/v1/exams/{examId}/like") | ||
public ResponseEntity<Void> like( | ||
@PathVariable Long examId, | ||
@Auth Accessor accessor | ||
) { | ||
examLikeService.like(examId, accessor); | ||
|
||
return ResponseEntity.ok().build(); | ||
} | ||
|
||
@DeleteMapping("/api/v1/exams/{examId}/like") | ||
public ResponseEntity<Void> unlike( | ||
@PathVariable Long examId, | ||
@Auth Accessor accessor | ||
) { | ||
examLikeService.unlike(examId, accessor); | ||
|
||
return ResponseEntity.ok().build(); | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
server/src/main/java/com/fluffy/exam/application/ExamLikeService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package com.fluffy.exam.application; | ||
|
||
import com.fluffy.global.web.Accessor; | ||
import com.fluffy.reaction.application.Like; | ||
import com.fluffy.reaction.application.LikeService; | ||
import com.fluffy.reaction.application.LikeTarget; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class ExamLikeService { | ||
|
||
private final LikeService likeService; | ||
|
||
@Transactional | ||
public Long like(Long examId, Accessor accessor) { | ||
return likeService.like(new Like(LikeTarget.EXAM, examId), accessor.id()); | ||
} | ||
|
||
@Transactional | ||
public Long unlike(Long examId, Accessor accessor) { | ||
return likeService.removeLike(new Like(LikeTarget.EXAM, examId), accessor.id()); | ||
} | ||
} |
4 changes: 4 additions & 0 deletions
4
server/src/main/java/com/fluffy/reaction/application/Like.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package com.fluffy.reaction.application; | ||
|
||
public record Like(LikeTarget target, Long targetId) { | ||
} |
42 changes: 42 additions & 0 deletions
42
server/src/main/java/com/fluffy/reaction/application/LikeService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package com.fluffy.reaction.application; | ||
|
||
import com.fluffy.global.exception.BadRequestException; | ||
import com.fluffy.reaction.domain.Reaction; | ||
import com.fluffy.reaction.domain.ReactionRepository; | ||
import com.fluffy.reaction.domain.ReactionType; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class LikeService { | ||
|
||
private final ReactionRepository reactionRepository; | ||
|
||
@Transactional | ||
public Long like(Like like, Long memberId) { | ||
String targetType = like.target().name(); | ||
Long targetId = like.targetId(); | ||
|
||
Reaction reaction = reactionRepository.findByTargetTypeAndTargetIdAndMemberId(targetType, targetId, memberId) | ||
.orElse(reactionRepository.save(new Reaction(targetType, targetId, memberId, ReactionType.LIKE))); | ||
|
||
reaction.active(); | ||
|
||
return reaction.getId(); | ||
} | ||
|
||
@Transactional | ||
public Long removeLike(Like like, Long memberId) { | ||
String targetType = like.target().name(); | ||
Long targetId = like.targetId(); | ||
|
||
Reaction reaction = reactionRepository.findByTargetTypeAndTargetIdAndMemberId(targetType, targetId, memberId) | ||
.orElseThrow(() -> new BadRequestException("좋아요를 한 상태가 아닙니다.")); | ||
|
||
reaction.delete(); | ||
|
||
return reaction.getId(); | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
server/src/main/java/com/fluffy/reaction/application/LikeTarget.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.fluffy.reaction.application; | ||
|
||
public enum LikeTarget { | ||
|
||
EXAM | ||
} |
72 changes: 72 additions & 0 deletions
72
server/src/main/java/com/fluffy/reaction/domain/Reaction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package com.fluffy.reaction.domain; | ||
|
||
import com.fluffy.global.persistence.AuditableEntity; | ||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.EnumType; | ||
import jakarta.persistence.Enumerated; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.GenerationType; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.Table; | ||
import lombok.AccessLevel; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Entity | ||
@Table(uniqueConstraints = { | ||
@jakarta.persistence.UniqueConstraint(columnNames = {"targetType", "targetId", "memberId", "type"}) | ||
}) | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
@Getter | ||
public class Reaction extends AuditableEntity { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
@Column(nullable = false) | ||
private String targetType; | ||
|
||
@Column(nullable = false) | ||
private Long targetId; | ||
|
||
@Column(nullable = false) | ||
private Long memberId; | ||
|
||
@Enumerated(EnumType.STRING) | ||
@Column(nullable = false) | ||
private ReactionType type; | ||
|
||
@Enumerated(EnumType.STRING) | ||
@Column(nullable = false) | ||
private ReactionStatus status; | ||
|
||
public Reaction(String targetType, Long targetId, Long memberId, ReactionType type) { | ||
this(null, targetType, targetId, memberId, type, ReactionStatus.ACTIVE); | ||
} | ||
|
||
public Reaction( | ||
Long id, | ||
String targetType, | ||
Long targetId, | ||
Long memberId, | ||
ReactionType type, | ||
ReactionStatus status | ||
) { | ||
this.id = id; | ||
this.targetType = targetType; | ||
this.targetId = targetId; | ||
this.memberId = memberId; | ||
this.type = type; | ||
this.status = status; | ||
} | ||
|
||
public void active() { | ||
this.status = ReactionStatus.ACTIVE; | ||
} | ||
|
||
public void delete() { | ||
this.status = ReactionStatus.DELETED; | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
server/src/main/java/com/fluffy/reaction/domain/ReactionRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.fluffy.reaction.domain; | ||
|
||
import java.util.Optional; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface ReactionRepository extends JpaRepository<Reaction, Long> { | ||
|
||
Optional<Reaction> findByTargetTypeAndTargetIdAndMemberId(String targetType, Long targetId, Long memberId); | ||
} |
6 changes: 6 additions & 0 deletions
6
server/src/main/java/com/fluffy/reaction/domain/ReactionStatus.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.fluffy.reaction.domain; | ||
|
||
public enum ReactionStatus { | ||
|
||
ACTIVE, DELETED | ||
} |
6 changes: 6 additions & 0 deletions
6
server/src/main/java/com/fluffy/reaction/domain/ReactionType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.fluffy.reaction.domain; | ||
|
||
public enum ReactionType { | ||
|
||
LIKE | ||
} |
18 changes: 18 additions & 0 deletions
18
server/src/main/resources/db/migration/V5__add_reaction_table.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
CREATE TABLE reaction | ||
( | ||
id BIGINT GENERATED BY DEFAULT AS IDENTITY, | ||
targetType VARCHAR(20) NOT NULL, | ||
targetId BIGINT NOT NULL, | ||
memberId BIGINT NOT NULL, | ||
type VARCHAR(20) NOT NULL, | ||
status VARCHAR(20) NOT NULL, | ||
created_at TIMESTAMP(6) NOT NULL, | ||
updated_at TIMESTAMP(6) NOT NULL, | ||
PRIMARY KEY (id) | ||
); | ||
|
||
ALTER TABLE reaction | ||
ADD CONSTRAINT fk_member FOREIGN KEY (memberId) REFERENCES member (id); | ||
|
||
ALTER TABLE reaction | ||
ADD CONSTRAINT unique_reaction UNIQUE (targetType, targetId, memberId, type); |
42 changes: 42 additions & 0 deletions
42
server/src/test/java/com/fluffy/exam/api/ExamLikeDocumentTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package com.fluffy.exam.api; | ||
|
||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.Mockito.doNothing; | ||
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; | ||
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; | ||
import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; | ||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
|
||
import com.fluffy.support.AbstractDocumentTest; | ||
import jakarta.servlet.http.Cookie; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
|
||
class ExamLikeDocumentTest extends AbstractDocumentTest { | ||
|
||
@Test | ||
@DisplayName("시험에 좋아요를 할 수 있다.") | ||
void like() throws Exception { | ||
mockMvc.perform(post("/api/v1/exams/{examId}/like", 1) | ||
.cookie(new Cookie("accessToken", "{ACCESS_TOKEN}"))) | ||
.andExpect(status().isOk()) | ||
.andDo(restDocs.document( | ||
pathParameters( | ||
parameterWithName("examId").description("시험 ID") | ||
) | ||
)); | ||
} | ||
|
||
@Test | ||
@DisplayName("시험에 좋아요를 취소할 수 있다.") | ||
void unlike() throws Exception { | ||
mockMvc.perform(post("/api/v1/exams/{examId}/like", 1) | ||
.cookie(new Cookie("accessToken", "{ACCESS_TOKEN}"))) | ||
.andExpect(status().isOk()) | ||
.andDo(restDocs.document( | ||
pathParameters( | ||
parameterWithName("examId").description("시험 ID") | ||
) | ||
)); | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
server/src/test/java/com/fluffy/reaction/domain/ReactionTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package com.fluffy.reaction.domain; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatCode; | ||
|
||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
|
||
class ReactionTest { | ||
|
||
@Test | ||
@DisplayName("반응을 정상적으로 생성할 수 있다.") | ||
void create() { | ||
// when & then | ||
assertThatCode(() -> new Reaction("EXAM", 1L, 1L, ReactionType.LIKE)) | ||
.doesNotThrowAnyException(); | ||
} | ||
|
||
@Test | ||
@DisplayName("반응을 삭제 상태로 변경할 수 있다.") | ||
void delete() { | ||
// given | ||
Reaction reaction = new Reaction("EXAM", 1L, 1L, ReactionType.LIKE); | ||
|
||
// when | ||
reaction.delete(); | ||
|
||
// then | ||
assertThat(reaction.getStatus()).isEqualTo(ReactionStatus.DELETED); | ||
} | ||
|
||
@Test | ||
@DisplayName("삭제 상태의 반응을 활성 상태로 변경할 수 있다.") | ||
void active() { | ||
// given | ||
Reaction reaction = new Reaction("EXAM", 1L, 1L, ReactionType.LIKE); | ||
reaction.delete(); | ||
|
||
// when | ||
reaction.active(); | ||
|
||
// then | ||
assertThat(reaction.getStatus()).isEqualTo(ReactionStatus.ACTIVE); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.