Skip to content

Commit

Permalink
Merge pull request #69 from Team-baebae/feature/reaction/#66
Browse files Browse the repository at this point in the history
Feat : 피드별 반응 기능 개발 (+통했당)
  • Loading branch information
tioon authored May 14, 2024
2 parents 0c5091c + 3cc41a8 commit 3b4e989
Show file tree
Hide file tree
Showing 11 changed files with 447 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.web.baebaeBE.domain.category.entity.Category;
import com.web.baebaeBE.domain.member.entity.Member;
import com.web.baebaeBE.domain.question.entity.Question;
import com.web.baebaeBE.domain.reaction.entity.ReactionValue;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.OnDelete;
Expand Down Expand Up @@ -87,6 +88,20 @@ public static Answer of(Long id, Question question, Category category, Member me
musicPicture, musicAudio, linkAttachments, heartCount,
curiousCount, sadCount, createdDate,null);
}

public void increaseReactionCount(ReactionValue reaction) {
switch (reaction) {
case HEART: // 좋아요
this.heartCount++;
break;
case CURIOUS: // 궁금해요
this.curiousCount++;
break;
case SAD: // 슬퍼요
this.sadCount++;
break;
}
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.web.baebaeBE.domain.reaction.controller;

import com.web.baebaeBE.domain.reaction.controller.api.MemberAnswerReactionApi;
import com.web.baebaeBE.domain.reaction.dto.ReactionRequest;
import com.web.baebaeBE.domain.reaction.dto.ReactionResponse;
import com.web.baebaeBE.domain.reaction.entity.MemberAnswerReaction;
import com.web.baebaeBE.domain.reaction.service.MemberAnswerReactionService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/reactions")
@RequiredArgsConstructor
public class MemberAnswerReactionController implements MemberAnswerReactionApi {
private final MemberAnswerReactionService memberAnswerReactionService;

@PostMapping("/{memberId}/{answerId}")
public ResponseEntity<ReactionResponse.ReactionInformationDto> createReaction(
@PathVariable Long memberId,
@PathVariable Long answerId,
@RequestBody ReactionRequest.create reactionDto) {

return ResponseEntity.ok(memberAnswerReactionService.createReaction(memberId, answerId, reactionDto.getReaction()));
}

// 통했당~
@PostMapping("/connection/{memberId}/{answerId}/{destinationMemberId}")
public ResponseEntity<ReactionResponse.ConnectionReactionInformationDto> createClickReaction(
@PathVariable Long memberId, // 자기자신 (통했당 하는 주체)
@PathVariable Long answerId, // 피드정보
@RequestParam(required = false) Long destinationMemberId // 피드작성자가 통했당 누군지 대상 지정
) {
// destinationMemberId 파라미터 여부로 체크
if(destinationMemberId == null)
return ResponseEntity.ok(memberAnswerReactionService.createConnectionReaction(memberId, answerId));
else
return ResponseEntity.ok(memberAnswerReactionService.connectConnectionReaction(memberId, answerId, destinationMemberId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.web.baebaeBE.domain.reaction.controller.api;

import com.web.baebaeBE.domain.reaction.dto.ReactionRequest;
import com.web.baebaeBE.domain.reaction.dto.ReactionResponse;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.RequestParam;


@Tag(name = "Reaction", description = "피드 반응 API")
@SecurityRequirement(name = "bearerAuth")
@RequestMapping("/api/reactions")
public interface MemberAnswerReactionApi {

@Operation(
summary = "피드 반응 생성",
description = "피드에 대한 지정된 멤버 ID와 답변 ID에 대한 반응을 생성합니다. " +
"(HEART, CURIOUS, SAD 만 가능합니다.)",
security = @SecurityRequirement(name = "bearerAuth")
)
@Parameter(
in = ParameterIn.HEADER,
name = "Authorization", required = true,
schema = @Schema(type = "string"),
description = "Bearer [Access 토큰]")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "생성 성공",
content = @Content(mediaType = "application/json",
schema = @Schema(
implementation = ReactionResponse.ReactionInformationDto.class
))
),
@ApiResponse(responseCode = "401", description = "토큰 인증 실패",
content = @Content(mediaType = "application/json",
examples = @ExampleObject(value = "{\n" +
" \"errorCode\": \"M-003\",\n" +
" \"message\": \"해당 토큰은 유효한 토큰이 아닙니다.\"\n" +
"}"))
),
@ApiResponse(responseCode = "404", description = "존재하지 않는 회원 또는 답변",
content = @Content(mediaType = "application/json",
examples = @ExampleObject(value = "{\n" +
" \"errorCode\": \"M-002\",\n" +
" \"message\": \"존재하지 않는 회원 또는 답변입니다.\"\n" +
"}"))
)
})
ResponseEntity<ReactionResponse.ReactionInformationDto> createReaction(@Parameter(description = "멤버의 ID", required = true) @PathVariable Long memberId,
@Parameter(description = "답변의 ID", required = true) @PathVariable Long answerId,
@RequestBody ReactionRequest.create reactionDto);

@Operation(
summary = "통했당 생성",
description = "지정된 멤버 ID, 답변 ID, 대상 멤버 ID에 대한 '통했당'을 생성합니다. " +
"다른 피드에 '통했당'을 남길경우 memberId, answerId만 필요합니다. " +
"또한 피드 주인이 통했당을 완료할 경우 destinationMemberId가 추가적으로 필요합니다." +
"(",
security = @SecurityRequirement(name = "bearerAuth")
)
@Parameter(
in = ParameterIn.HEADER,
name = "Authorization", required = true,
schema = @Schema(type = "string"),
description = "Bearer [Access 토큰]")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "생성 성공",
content = @Content(mediaType = "application/json",
schema = @Schema(
implementation = ReactionResponse.ConnectionReactionInformationDto.class
))
),
@ApiResponse(responseCode = "401", description = "토큰 인증 실패",
content = @Content(mediaType = "application/json",
examples = @ExampleObject(value = "{\n" +
" \"errorCode\": \"M-003\",\n" +
" \"message\": \"해당 토큰은 유효한 토큰이 아닙니다.\"\n" +
"}"))
),
@ApiResponse(responseCode = "404", description = "존재하지 않는 회원 또는 답변",
content = @Content(mediaType = "application/json",
examples = @ExampleObject(value = "{\n" +
" \"errorCode\": \"M-002\",\n" +
" \"message\": \"존재하지 않는 회원 또는 답변입니다.\"\n" +
"}"))
)
})
ResponseEntity<ReactionResponse.ConnectionReactionInformationDto> createClickReaction(@Parameter(description = "멤버의 ID", required = true) @PathVariable Long memberId,
@Parameter(description = "답변의 ID", required = true) @PathVariable Long answerId,
@Parameter(description = "대상 멤버의 ID (피드주인이 통했당 완료할때만 가능)", required = false) @RequestParam(required = false) Long destinationMemberId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.web.baebaeBE.domain.reaction.dto;

import com.web.baebaeBE.domain.reaction.entity.ReactionValue;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;


public class ReactionRequest {

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class create{
private ReactionValue reaction;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.web.baebaeBE.domain.reaction.dto;

import com.web.baebaeBE.domain.answer.entity.Answer;
import com.web.baebaeBE.domain.reaction.entity.MemberAnswerReaction;
import lombok.*;

public class ReactionResponse {

@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class ReactionInformationDto {
private boolean isClicked;
private int heartCount;
private int curiousCount;
private int sadCount;

public static ReactionInformationDto of(Answer answer, boolean isClicked) {
return ReactionInformationDto.builder()
.isClicked(isClicked)
.heartCount(answer.getHeartCount())
.curiousCount(answer.getCuriousCount())
.sadCount(answer.getSadCount())
.build();
}
}

@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class ConnectionReactionInformationDto {
private boolean isClicked;
private boolean isMatched;

public static ConnectionReactionInformationDto of(boolean isClicked, boolean isMatched) {
return ConnectionReactionInformationDto.builder()
.isClicked(isClicked)
.isMatched(isMatched)
.build();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.web.baebaeBE.domain.reaction.entity;


import com.web.baebaeBE.domain.answer.entity.Answer;
import com.web.baebaeBE.domain.member.entity.Member;
import jakarta.persistence.*;
import lombok.*;

@Entity
@Table(name = "member_answer_reaction")
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
public class MemberAnswerReaction {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id", nullable = false)
private Member member;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "answer_id", nullable = false)
private Answer answer;

@Enumerated(EnumType.STRING)
@Column(name = "reaction", nullable = false)
private ReactionValue reaction;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.web.baebaeBE.domain.reaction.entity;

public enum ReactionValue {
HEART, CURIOUS, SAD, CONNECTION
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.web.baebaeBE.domain.reaction.exception;

import com.web.baebaeBE.global.error.ErrorCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;

@Getter
@RequiredArgsConstructor
public enum ReactionException implements ErrorCode {

NOT_EXIST_CONNECTION_REACTION(HttpStatus.NOT_FOUND, "R-001", "상대방이 통했당을 하지 않았습니다."),
NOT_EXIST_MEMBER(HttpStatus.NOT_FOUND, "R-002", "존재하지 않는 회원입니다."),
NOT_EXIST_ANSWER(HttpStatus.NOT_FOUND, "R-003", "존재하지 않는 답변입니다.");

private final HttpStatus httpStatus;
private final String errorCode;
private final String message;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.web.baebaeBE.domain.reaction.repository;

import com.web.baebaeBE.domain.answer.entity.Answer;
import com.web.baebaeBE.domain.member.entity.Member;
import com.web.baebaeBE.domain.reaction.entity.MemberAnswerReaction;
import com.web.baebaeBE.domain.reaction.entity.ReactionValue;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface MemberAnswerReactionRepository extends JpaRepository<MemberAnswerReaction, Long> {
Optional<MemberAnswerReaction> findByMemberAndAnswerAndReaction(Member member, Answer answer, ReactionValue reaction);
}
Loading

0 comments on commit 3b4e989

Please sign in to comment.