Skip to content

Commit

Permalink
Merge pull request #56 from Travel-in-nanaland/feat/#55-response-with…
Browse files Browse the repository at this point in the history
…-like-status

[#55] Feat : 검색 결과에 좋아요 여부 반환
  • Loading branch information
jyajoo authored Apr 20, 2024
2 parents ef4f453 + 0b15f87 commit caef5b6
Show file tree
Hide file tree
Showing 28 changed files with 1,337 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.jeju.nanaland.domain.common.data;

public enum CategoryContent {
NANA,
EXPERIENCE,
FESTIVAL,
NATURE,
MARKET
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.jeju.nanaland.domain.common.entity;

import com.jeju.nanaland.domain.common.data.CategoryContent;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.validation.constraints.NotBlank;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.validation.constraints.NotNull;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
Expand All @@ -16,7 +19,8 @@
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Category extends BaseEntity {

@NotBlank
@NotNull
@Column(nullable = false, unique = true)
private String content;
@Enumerated(EnumType.STRING)
private CategoryContent content;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.jeju.nanaland.domain.common.repository;

import com.jeju.nanaland.domain.common.data.CategoryContent;
import com.jeju.nanaland.domain.common.entity.Category;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface CategoryRepository extends JpaRepository<Category, Long> {

public Optional<Category> findByContent(CategoryContent content);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.jeju.nanaland.domain.experience.controller;

import static com.jeju.nanaland.global.exception.SuccessCode.POST_LIKE_TOGGLE_SUCCESS;

import com.jeju.nanaland.domain.experience.service.ExperienceService;
import com.jeju.nanaland.domain.member.entity.Member;
import com.jeju.nanaland.global.BaseResponse;
import com.jeju.nanaland.global.jwt.AuthMember;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/experience")
@RequiredArgsConstructor
@Slf4j
@Tag(name = "이색체험(Experience)", description = "이색체험(Experience) API입니다.")
public class ExperienceController {

private final ExperienceService experienceService;

@Operation(summary = "좋아요 토글", description = "좋아요 토글 기능 (좋아요 상태 -> 좋아요 취소 상태, 좋아요 취소 상태 -> 좋아요 상태)")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "400", description = "필요한 입력이 없는 경우 또는 해당 id의 게시물이 없는 경우", content = @Content),
@ApiResponse(responseCode = "500", description = "서버측 에러", content = @Content)
})
@PostMapping("/like/{id}")
public BaseResponse<String> toggleLikeStatus(@AuthMember Member member, @PathVariable Long id) {
String result = experienceService.toggleLikeStatus(member, id);
return BaseResponse.success(POST_LIKE_TOGGLE_SUCCESS, result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.jeju.nanaland.domain.experience.dto;

import lombok.Data;

public class ExperienceRequest {

@Data
public static class LikeDto {

private Long postId;

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.jeju.nanaland.domain.experience.dto;

public class ExperienceResponse {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.jeju.nanaland.domain.experience.service;

import com.jeju.nanaland.domain.common.data.CategoryContent;
import com.jeju.nanaland.domain.experience.repository.ExperienceRepository;
import com.jeju.nanaland.domain.favorite.service.FavoriteService;
import com.jeju.nanaland.domain.member.entity.Member;
import com.jeju.nanaland.global.exception.BadRequestException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Slf4j
public class ExperienceService {

private final ExperienceRepository experienceRepository;
private final FavoriteService favoriteService;

@Transactional
public String toggleLikeStatus(Member member, Long postId) {
experienceRepository.findById(postId)
.orElseThrow(() -> new BadRequestException("해당 id의 이색체험 게시물이 존재하지 않습니다."));

return favoriteService.toggleLikeStatus(member, CategoryContent.EXPERIENCE, postId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.jeju.nanaland.domain.favorite.repository;

import com.jeju.nanaland.domain.common.entity.Category;
import com.jeju.nanaland.domain.favorite.entity.Favorite;
import com.jeju.nanaland.domain.member.entity.Member;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface FavoriteRepository extends JpaRepository<Favorite, Long> {

Optional<Favorite> findByMemberAndCategoryAndPostId(Member member, Category category,
Long postId);

List<Favorite> findAllByMemberAndCategory(Member member, Category category);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.jeju.nanaland.domain.favorite.service;

import static com.jeju.nanaland.domain.common.data.CategoryContent.EXPERIENCE;
import static com.jeju.nanaland.domain.common.data.CategoryContent.FESTIVAL;
import static com.jeju.nanaland.domain.common.data.CategoryContent.MARKET;
import static com.jeju.nanaland.domain.common.data.CategoryContent.NANA;
import static com.jeju.nanaland.domain.common.data.CategoryContent.NATURE;

import com.jeju.nanaland.domain.common.data.CategoryContent;
import com.jeju.nanaland.domain.common.entity.Category;
import com.jeju.nanaland.domain.common.repository.CategoryRepository;
import com.jeju.nanaland.domain.favorite.entity.Favorite;
import com.jeju.nanaland.domain.favorite.repository.FavoriteRepository;
import com.jeju.nanaland.domain.member.entity.Member;
import com.jeju.nanaland.global.exception.ServerErrorException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Slf4j
public class FavoriteService {

private final CategoryRepository categoryRepository;
private final FavoriteRepository favoriteRepository;

public List<Long> getMemberFavoritePostIds(Member member, CategoryContent categoryContent) {

Category category = getCategoryFromCategoryContent(categoryContent);
List<Favorite> favorites = favoriteRepository.findAllByMemberAndCategory(member, category);

List<Long> postIds = new ArrayList<>();
for (Favorite favorite : favorites) {
postIds.add(favorite.getPostId());
}
return postIds;
}

@Transactional
public String toggleLikeStatus(Member member, CategoryContent categoryContent, Long postId) {

Category category = getCategoryFromCategoryContent(categoryContent);

Optional<Favorite> favoriteOptional = favoriteRepository.findByMemberAndCategoryAndPostId(
member, category, postId);

// 좋아요 상태일 때
if (favoriteOptional.isPresent()) {
Favorite favorite = favoriteOptional.get();

// 좋아요 삭제
favoriteRepository.delete(favorite);
return "좋아요 삭제";
}
// 좋아요 상태가 아닐 때
else {
Favorite favorite = Favorite.builder()
.member(member)
.category(category)
.postId(postId)
.build();

// 좋아요 추가
favoriteRepository.save(favorite);
return "좋아요 추가";
}
}

private Category getCategoryFromCategoryContent(CategoryContent categoryContent) {
return switch (categoryContent) {
case NANA -> categoryRepository.findByContent(NANA)
.orElseThrow(() -> new ServerErrorException("NANA에 해당하는 카테고리가 없습니다."));

case EXPERIENCE -> categoryRepository.findByContent(EXPERIENCE)
.orElseThrow(() -> new ServerErrorException("EXPERIENCE에 해당하는 카테고리가 없습니다."));

case NATURE -> categoryRepository.findByContent(NATURE)
.orElseThrow(() -> new ServerErrorException("NATURE에 해당하는 카테고리가 없습니다."));

case MARKET -> categoryRepository.findByContent(MARKET)
.orElseThrow(() -> new ServerErrorException("MARKET에 해당하는 카테고리가 없습니다."));

case FESTIVAL -> categoryRepository.findByContent(FESTIVAL)
.orElseThrow(() -> new ServerErrorException("FESTIVAL에 해당하는 카테고리가 없습니다."));
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.jeju.nanaland.domain.festival.controller;

import static com.jeju.nanaland.global.exception.SuccessCode.POST_LIKE_TOGGLE_SUCCESS;

import com.jeju.nanaland.domain.festival.service.FestivalService;
import com.jeju.nanaland.domain.member.entity.Member;
import com.jeju.nanaland.global.BaseResponse;
import com.jeju.nanaland.global.jwt.AuthMember;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/festival")
@Slf4j
@Tag(name = "축제(Festival)", description = "축제(Festival) API입니다.")
public class FestivalController {

private final FestivalService festivalService;

@Operation(summary = "좋아요 토글", description = "좋아요 토글 기능 (좋아요 상태 -> 좋아요 취소 상태, 좋아요 취소 상태 -> 좋아요 상태)")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "400", description = "필요한 입력이 없는 경우 또는 해당 id의 게시물이 없는 경우", content = @Content),
@ApiResponse(responseCode = "500", description = "서버측 에러", content = @Content)
})
@PostMapping("/like/{id}")
public BaseResponse<String> toggleLikeStatus(@AuthMember Member member, @PathVariable Long id) {
String result = festivalService.toggleLikeStatus(member, id);
return BaseResponse.success(POST_LIKE_TOGGLE_SUCCESS, result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.jeju.nanaland.domain.festival.service;

import com.jeju.nanaland.domain.common.data.CategoryContent;
import com.jeju.nanaland.domain.favorite.service.FavoriteService;
import com.jeju.nanaland.domain.festival.repository.FestivalRepository;
import com.jeju.nanaland.domain.member.entity.Member;
import com.jeju.nanaland.global.exception.BadRequestException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Slf4j
public class FestivalService {

private final FestivalRepository festivalRepository;
private final FavoriteService favoriteService;

@Transactional
public String toggleLikeStatus(Member member, Long postId) {
festivalRepository.findById(postId)
.orElseThrow(() -> new BadRequestException("해당 id의 축제 게시물이 존재하지 않습니다."));

return favoriteService.toggleLikeStatus(member, CategoryContent.FESTIVAL, postId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.jeju.nanaland.domain.market.controller;

import static com.jeju.nanaland.global.exception.SuccessCode.POST_LIKE_TOGGLE_SUCCESS;

import com.jeju.nanaland.domain.market.service.MarketService;
import com.jeju.nanaland.domain.member.entity.Member;
import com.jeju.nanaland.global.BaseResponse;
import com.jeju.nanaland.global.jwt.AuthMember;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/market")
@Slf4j
@Tag(name = "전통시장(Market)", description = "전통시장(Market) API입니다.")
public class MarketController {

private final MarketService marketService;

@Operation(summary = "좋아요 토글", description = "좋아요 토글 기능 (좋아요 상태 -> 좋아요 취소 상태, 좋아요 취소 상태 -> 좋아요 상태)")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "400", description = "필요한 입력이 없는 경우 또는 해당 id의 게시물이 없는 경우", content = @Content),
@ApiResponse(responseCode = "500", description = "서버측 에러", content = @Content)
})
@PostMapping("/like/{id}")
public BaseResponse<String> toggleLikeStatus(@AuthMember Member member, @PathVariable Long id) {
String result = marketService.toggleLikeStatus(member, id);
return BaseResponse.success(POST_LIKE_TOGGLE_SUCCESS, result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.jeju.nanaland.domain.market.service;

import com.jeju.nanaland.domain.common.data.CategoryContent;
import com.jeju.nanaland.domain.favorite.service.FavoriteService;
import com.jeju.nanaland.domain.market.repository.MarketRepository;
import com.jeju.nanaland.domain.member.entity.Member;
import com.jeju.nanaland.global.exception.BadRequestException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Slf4j
public class MarketService {

private final MarketRepository marketRepository;
private final FavoriteService favoriteService;

@Transactional
public String toggleLikeStatus(Member member, Long postId) {
marketRepository.findById(postId)
.orElseThrow(() -> new BadRequestException("해당 id의 전통시장 게시물이 존재하지 않습니다."));

return favoriteService.toggleLikeStatus(member, CategoryContent.MARKET, postId);
}
}
Loading

0 comments on commit caef5b6

Please sign in to comment.