Skip to content

Commit

Permalink
Merge branch 'develop' into feat/#126/apnsSetting
Browse files Browse the repository at this point in the history
  • Loading branch information
jaewonLeeKOR committed Sep 12, 2024
2 parents 47fa03c + 5ca7e39 commit d87a6c4
Show file tree
Hide file tree
Showing 48 changed files with 740 additions and 142 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ dependencies {
implementation 'com.google.code.findbugs:jsr305:3.0.2'
// webflux
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'io.netty:netty-resolver-dns-native-macos:4.1.68.Final:osx-aarch_64'
}

dependencyManagement {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.requestMatchers("/reports", "/reports/**").authenticated()
.requestMatchers("/boards", "/boards/**").authenticated()
.requestMatchers("/boardComments", "/boardComments/**").authenticated()
.requestMatchers("/dummy","/dummy/**").hasRole(Role.ROLE_ADMIN.getName())
.anyRequest().denyAll());
http
.addFilterBefore(new JwtFilter(jwtService), UsernamePasswordAuthenticationFilter.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ public static class AnswerId {
@Builder
public static class AnswerInfo {
private Long answerId;
private Long writerId;
private String profileImage;
private String nickname;
private String content;
private Boolean isMyAnswer;
private Boolean isMine;
private Boolean isReported;
private Boolean isLiked;
private String writeAt;
}

@Getter
Expand All @@ -44,11 +47,13 @@ public static class AnswerLike {
public static class MemberAnswerInfo {
private Long questionId;
private Long answerId;
private Long writerId;
private String nickname;
private String profileImage;
private String content;
private int heartCount;
private String writeAt;
private Boolean isLiked;
}

@Getter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import com.server.capple.domain.question.entity.Question;
import org.springframework.stereotype.Component;

import java.time.format.DateTimeFormatter;
import java.util.List;

@Component
Expand All @@ -29,26 +28,31 @@ public AnswerList toAnswerList(List<AnswerInfo> answerInfoList) {
.build();
}

public AnswerInfo toAnswerInfo(Answer answer, Long memberId, Boolean isReported) {
public AnswerInfo toAnswerInfo(Answer answer, Long memberId, Boolean isReported, Boolean isLiked, Boolean isMine) {
return AnswerInfo.builder()
.answerId(answer.getId())
.writerId(answer.getMember().getId())
.profileImage(answer.getMember().getProfileImage())
.nickname(answer.getMember().getNickname())
.content(answer.getContent())
.isMyAnswer(answer.getMember().getId() == memberId)
.isMine(isMine)
.isReported(isReported)
.isLiked(isLiked)
.writeAt(answer.getCreatedAt().toString())
.build();
}

public MemberAnswerInfo toMemberAnswerInfo(Answer answer, int heartCount) {
public MemberAnswerInfo toMemberAnswerInfo(Answer answer, int heartCount, Boolean isLiked) {
return MemberAnswerInfo.builder()
.questionId(answer.getQuestion().getId())
.answerId(answer.getId())
.writerId(answer.getMember().getId())
.nickname(answer.getMember().getNickname())
.profileImage(answer.getMember().getProfileImage())
.content(answer.getContent())
.heartCount(heartCount)
.writeAt(answer.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy.MM.dd")))
.writeAt(answer.getCreatedAt().toString())
.isLiked(isLiked)
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,10 @@ public Set<Long> getMemberHeartsAnswer(Long memberId) {
}
return answerIds;
}

public boolean isMemberLikedAnswer(Long memberId, Long answerId) {
String key = ANSWER_HEART_KEY_PREFIX + answerId;
String memberKey = MEMBER_KEY_PREFIX + memberId;
return redisTemplate.opsForSet().isMember(key, memberKey);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.server.capple.domain.answer.dto.AnswerRequest;
import com.server.capple.domain.answer.dto.AnswerResponse;
import com.server.capple.domain.answer.dto.AnswerResponse.AnswerLike;
import com.server.capple.domain.answer.dto.AnswerResponse.AnswerList;
import com.server.capple.domain.answer.dto.AnswerResponse.MemberAnswerList;
import com.server.capple.domain.answer.entity.Answer;
Expand Down Expand Up @@ -40,8 +41,13 @@ public AnswerResponse.AnswerId createAnswer(Member loginMember, Long questionId,
Member member = memberService.findMember(loginMember.getId());
Question question = questionService.findQuestion(questionId);

if (answerRepository.existsByQuestionAndMember(question, loginMember)) {
throw new RestApiException(AnswerErrorCode.ANSWER_ALREADY_EXIST);
}

//답변 저장
Answer answer = answerRepository.save(answerMapper.toAnswerEntity(request, member, question));
// answer.getQuestion().increaseCommentCount();

return new AnswerResponse.AnswerId(answer.getId());
}
Expand All @@ -66,6 +72,8 @@ public AnswerResponse.AnswerId deleteAnswer(Member loginMember, Long answerId) {
Answer answer = findAnswer(answerId);

checkPermission(loginMember, answer);
// answer.getQuestion().decreaseCommentCount();


answer.delete();

Expand All @@ -75,11 +83,11 @@ public AnswerResponse.AnswerId deleteAnswer(Member loginMember, Long answerId) {

//답변 좋아요 / 취소
@Override
public AnswerResponse.AnswerLike toggleAnswerHeart(Member loginMember, Long answerId) {
public AnswerLike toggleAnswerHeart(Member loginMember, Long answerId) {
Member member = memberService.findMember(loginMember.getId());

Boolean isLiked = answerHeartRedisRepository.toggleAnswerHeart(member.getId(), answerId);
return new AnswerResponse.AnswerLike(answerId, isLiked);
return new AnswerLike(answerId, isLiked);
}

@Override
Expand All @@ -90,14 +98,14 @@ public AnswerList getAnswerList(Long memberId, Long questionId, String keyword,
answerRepository.findByQuestion(questionId, pageable).orElseThrow(()
-> new RestApiException(AnswerErrorCode.ANSWER_NOT_FOUND))
.stream()
.map(answer -> answerMapper.toAnswerInfo(answer, memberId, reportRepository.existsReportByAnswer(answer)))
.map(answer -> answerMapper.toAnswerInfo(answer, memberId, reportRepository.existsReportByAnswer(answer), answerHeartRedisRepository.isMemberLikedAnswer(memberId, answer.getId()), answer.getMember().getId().equals(memberId)))
.toList());
} else {
return answerMapper.toAnswerList(
answerRepository.findByQuestionAndKeyword(questionId, keyword, pageable).orElseThrow(()
-> new RestApiException(AnswerErrorCode.ANSWER_NOT_FOUND))
.stream()
.map(answer -> answerMapper.toAnswerInfo(answer, memberId, reportRepository.existsReportByAnswer(answer)))
.map(answer -> answerMapper.toAnswerInfo(answer, memberId, reportRepository.existsReportByAnswer(answer), answerHeartRedisRepository.isMemberLikedAnswer(memberId, answer.getId()), answer.getMember().getId().equals(memberId)))
.toList());
}

Expand All @@ -109,7 +117,7 @@ public MemberAnswerList getMemberAnswer(Member member) {
List<Answer> answers = answerRepository.findByMember(member).orElse(null);
return answerMapper.toMemberAnswerList(
answers.stream()
.map(answer -> answerMapper.toMemberAnswerInfo(answer, answerHeartRedisRepository.getAnswerHeartsCount(answer.getId())))
.map(answer -> answerMapper.toMemberAnswerInfo(answer, answerHeartRedisRepository.getAnswerHeartsCount(answer.getId()), answerHeartRedisRepository.isMemberLikedAnswer(member.getId(), answer.getId())))
.toList()
);
}
Expand All @@ -120,12 +128,11 @@ public MemberAnswerList getMemberHeartAnswer(Member member) {
return answerMapper.toMemberAnswerList(
answerHeartRedisRepository.getMemberHeartsAnswer(member.getId())
.stream()
.map(answerId -> answerMapper.toMemberAnswerInfo(findAnswer((answerId)), answerHeartRedisRepository.getAnswerHeartsCount(answerId)))
.map(answerId -> answerMapper.toMemberAnswerInfo(findAnswer((answerId)), answerHeartRedisRepository.getAnswerHeartsCount(answerId), answerHeartRedisRepository.isMemberLikedAnswer(member.getId(), answerId)))
.toList()
);
}


//로그인된 유저와 작성자가 같은지 체크
private void checkPermission(Member loginMember, Answer answer) {
Member member = memberService.findMember(loginMember.getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public static class AnswerCommentHeart {
@Builder
public static class AnswerCommentInfo {
private Long answerCommentId;
private String writer;
private Long writerId;
private String content;
private Long heartCount;
private LocalDateTime createdAt;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public AnswerComment toAnswerCommentEntity(Member member, Answer answer, String
public AnswerCommentInfo toAnswerCommentInfo(AnswerComment comment, Long heartCount) {
return AnswerCommentInfo.builder()
.answerCommentId(comment.getId())
.writer(comment.getMember().getNickname())
.writerId(comment.getMember().getId())
.content(comment.getContent())
.heartCount(heartCount)
.createdAt(comment.getCreatedAt())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
package com.server.capple.domain.board.controller;

import com.server.capple.config.security.AuthMember;
import com.server.capple.domain.answer.dto.AnswerResponse;
import com.server.capple.domain.board.dto.BoardRequest;
import com.server.capple.domain.board.dto.BoardResponse;
import com.server.capple.domain.board.entity.BoardType;
import com.server.capple.domain.board.service.BoardService;
import com.server.capple.domain.member.entity.Member;
import com.server.capple.domain.question.dto.response.QuestionResponse;
import com.server.capple.global.common.BaseResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.annotation.*;

@Tag(name = "게시판 API", description = "게시판 관련 API")
Expand All @@ -40,17 +34,32 @@ private BaseResponse<BoardResponse.BoardCreate> createBoard(
return BaseResponse.onSuccess(boardService.createBoard(member, request.getBoardType(), request.getContent()));
}

@Operation(summary = "카테고리별 게시글 조회 API", description = "카테고리별 게시글을 조회합니다.")
@Operation(summary = "카테고리별 게시글 조회 with REDIS API(프론트 사용 X, 성능 테스트 용)", description = "카테고리별 게시글을 조회합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "COMMON200", description = "성공"),
})
@GetMapping("/redis")
private BaseResponse<BoardResponse.BoardsGetByBoardType> getBoardsByBoardTypeWithRedis(
@AuthMember Member member,
@RequestParam(name = "boardType", required = false) BoardType boardType
// TODO: 페이징 프론트 이슈로 추후 구현
// @PageableDefault(sort = "created_at", direction = Sort.Direction.DESC) @Parameter(hidden = true) Pageable pageable
) {
return BaseResponse.onSuccess(boardService.getBoardsByBoardTypeWithRedis(member, boardType));
}

@Operation(summary = "카테고리별 게시글 조회", description = "카테고리별 게시글을 조회합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "COMMON200", description = "성공"),
})
@GetMapping()
private BaseResponse<BoardResponse.BoardsGetByBoardType> getBoardsByBoardType(
@AuthMember Member member,
@RequestParam(name = "boardType", required = false) BoardType boardType
// TODO: 페이징 프론트 이슈로 추후 구현
// @PageableDefault(sort = "created_at", direction = Sort.Direction.DESC) @Parameter(hidden = true) Pageable pageable
) {
return BaseResponse.onSuccess(boardService.getBoardsByBoardType(boardType));
) {
return BaseResponse.onSuccess(boardService.getBoardsByBoardType(member, boardType));
}

@Operation(summary = "게시글 삭제 API", description = "게시글을 삭제합니다.")
Expand Down Expand Up @@ -79,8 +88,7 @@ private BaseResponse<BoardResponse.BoardsSearchByKeyword> searchBoardsByKeyword(
@Operation(summary = "게시글 좋아요/취소 API", description = " 게시글 좋아요/취소 API 입니다." +
"pathvariable 으로 boardId를 주세요.")
@PostMapping("/{boardId}/heart")
public BaseResponse<BoardResponse.BoardToggleHeart> toggleBoardHeart(@AuthMember Member member, @PathVariable(value = "boardId") Long boardId) {
public BaseResponse<BoardResponse.ToggleBoardHeart> toggleBoardHeart(@AuthMember Member member, @PathVariable(value = "boardId") Long boardId) {
return BaseResponse.onSuccess(boardService.toggleBoardHeart(member, boardId));
}

}
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package com.server.capple.domain.board.dto;

import com.server.capple.domain.member.entity.Member;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

public class BoardResponse {
Expand All @@ -25,7 +23,7 @@ public static class BoardCreate {
@AllArgsConstructor
@NoArgsConstructor
public static class BoardsGetByBoardType {
private List<BoardsGetByBoardTypeBoardInfo> boards = new ArrayList<>();
private List<BoardsGetByBoardTypeBoardInfo> boards;
}

@Getter
Expand All @@ -39,6 +37,9 @@ public static class BoardsGetByBoardTypeBoardInfo {
private Integer heartCount;
private Integer commentCount;
private LocalDateTime createAt;
private Boolean isMine;
private Boolean isReported;
private Boolean isLiked;
}

@Getter
Expand All @@ -54,7 +55,7 @@ public static class BoardDelete {
@AllArgsConstructor
@NoArgsConstructor
public static class BoardsSearchByKeyword {
private List<BoardsSearchByKeywordBoardInfo> boards = new ArrayList<>();
private List<BoardsSearchByKeywordBoardInfo> boards;
}

@Getter
Expand All @@ -74,7 +75,7 @@ public static class BoardsSearchByKeywordBoardInfo {
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class BoardToggleHeart {
public static class ToggleBoardHeart {
private Long boardId;
private Boolean isLiked;
}
Expand Down
22 changes: 21 additions & 1 deletion src/main/java/com/server/capple/domain/board/entity/Board.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.server.capple.global.common.BaseEntity;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.SQLRestriction;

Expand All @@ -29,6 +30,25 @@ public class Board extends BaseEntity {
@Column(nullable = false)
private String content;

@Column(nullable = false)
@ColumnDefault("0")
private Integer commentCount;

@ColumnDefault("0")
private Integer heartCount;

public void setHeartCount(boolean isLiked) {
if (isLiked) {
this.heartCount++;
} else {
this.heartCount--;
}
}

public void increaseCommentCount() {
this.commentCount++;
}

public void decreaseCommentCount() {
this.commentCount--;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.server.capple.domain.board.entity;

import com.server.capple.domain.member.entity.Member;
import com.server.capple.global.common.BaseEntity;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.SQLRestriction;

@Getter
@Builder
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@SQLRestriction("deleted_at is null")
public class BoardHeart extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "board_id", nullable = false)
private Board board;

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

private boolean isLiked;

public boolean toggleHeart() {
this.isLiked = !this.isLiked;
return isLiked;
}
}
Loading

0 comments on commit d87a6c4

Please sign in to comment.