Skip to content

Commit

Permalink
feat [#92] 콘서트 좋아요 삭제 API 추가 (#97)
Browse files Browse the repository at this point in the history
* feat [#92] 콘서트 좋아요 삭제 API 추가

* refactor [#92] 유저 서비스에 트랜잭션 추가 및 에러 응답 Unauthorized 설정 및 중복된 함수 제거

* feat [#90] 아티스트 좋아요 삭제 API 구현

* refactor [#90] 좋아요를 누르지 않았을 때 NOT FOUND 응답으로 변경

* feat [#91] 콘서트 좋아요 추가 API 구현 (#95)

* feat [#91] 콘서트 좋아요 추가 API 추가

* feat [#90] 아티스트 좋아요 삭제 API 구현

* refactor [#90] 좋아요를 누르지 않았을 때 NOT FOUND 응답으로 변경

* feat [#91] 콘서트 좋아요 추가 API 추가

* style [#92] 함수 위치 재배치

* feat [#92] 콘서트 좋아요 삭제 API 추가

* refactor [#92] 유저 서비스에 트랜잭션 추가 및 에러 응답 Unauthorized 설정 및 중복된 함수 제거

* feat [#92] 아티스트 접기/펼치기 기준 값 상수 추가

* refactor [#92] 아티스트 접기/펼치기 기준 값 상수 사용하도록 변경
  • Loading branch information
ch1hyun authored Jan 18, 2025
1 parent 86b463a commit 0e0150f
Show file tree
Hide file tree
Showing 10 changed files with 63 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,22 @@

import java.util.List;
import org.sopt.confeti.api.performance.facade.dto.response.ConcertDetailDTO;
import org.sopt.confeti.global.common.constant.ArtistConstant;
import org.sopt.confeti.global.util.S3FileHandler;

public record ConcertDetailResponse(
ConcertDetailInfoResponse concert,
boolean isOpen,
List<ConcertDetailArtistResponse> concertArtists
) {
private static final int OPEN_CRITERIA = 4;

public static ConcertDetailResponse of(final ConcertDetailDTO concertDetailDTO, final S3FileHandler s3FileHandler) {
List<ConcertDetailArtistResponse> concertArtists = concertDetailDTO.artists().stream()
.map(ConcertDetailArtistResponse::from)
.toList();

return new ConcertDetailResponse(
ConcertDetailInfoResponse.of(concertDetailDTO, s3FileHandler),
concertArtists.size() > OPEN_CRITERIA,
concertArtists.size() > ArtistConstant.ARTIST_BOX_OPEN_CRITERIA,
concertArtists
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

import java.util.List;
import org.sopt.confeti.api.performance.facade.dto.response.FestivalDetailDateDTO;
import org.sopt.confeti.global.common.constant.ArtistConstant;

public record FestivalDetailDateResponse(
long festivalDateId,
String festivalAt,
boolean isOpen,
List<FestivalDetailArtistResponse> artists
) {
private static final int OPEN_CRITERIA = 4;
private static final String FESTIVAL_AT_PREFIX = "Day ";

public static FestivalDetailDateResponse of(final FestivalDetailDateDTO festivalDate, final int order) {
Expand All @@ -22,7 +22,7 @@ public static FestivalDetailDateResponse of(final FestivalDetailDateDTO festival
return new FestivalDetailDateResponse(
festivalDate.festivalDateId(),
FESTIVAL_AT_PREFIX + order,
artists.size() > OPEN_CRITERIA,
artists.size() > ArtistConstant.ARTIST_BOX_OPEN_CRITERIA,
artists
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,13 @@ public ResponseEntity<BaseResponse<?>> addConcertFavorite(
userFavoriteFacade.addConcertFavorite(userId, concertId);
return ApiResponseUtil.success(SuccessMessage.SUCCESS);
}

@DeleteMapping("/concerts/{concertId}")
public ResponseEntity<BaseResponse<?>> removeConcertFavorite(
@RequestHeader("Authorization") Long userId,
@PathVariable(name = "concertId") Long concertId
) {
userFavoriteFacade.removeConcertFavorite(userId, concertId);
return ApiResponseUtil.success(SuccessMessage.SUCCESS);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package org.sopt.confeti.api.user.facade;

import jakarta.validation.constraints.Min;
import lombok.RequiredArgsConstructor;
import org.sopt.confeti.annotation.Facade;
import org.sopt.confeti.api.user.facade.dto.response.UserFavoriteArtistDTO;
import org.sopt.confeti.domain.artistfavorite.ArtistFavorite;
import org.sopt.confeti.domain.artistfavorite.application.ArtistFavoriteService;
import org.sopt.confeti.domain.concert.application.ConcertService;
import org.sopt.confeti.domain.concertfavorite.application.ConcertFavoriteService;
import org.sopt.confeti.domain.concert.Concert;
import org.sopt.confeti.domain.concert.application.ConcertService;
import org.sopt.confeti.domain.concertfavorite.application.ConcertFavoriteService;
Expand Down Expand Up @@ -86,4 +87,16 @@ public void addConcertFavorite(final long userId, final long concertId) {

concertFavoriteService.addFavorite(user, concert);
}

@Transactional
public void removeConcertFavorite(final long userId, final long concertId) {
userService.existsById(userId);
concertService.existsById(concertId);

if (!concertFavoriteService.isFavorite(userId, concertId)) {
throw new NotFoundException(ErrorMessage.NOT_FOUND);
}

concertFavoriteService.removeFavorite(userId, concertId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class UserInfoFacade {

@Transactional
public UserInfoDTO getUserInfo(Long userId) {
User user = userService.getUserInfo(userId);
User user = userService.findById(userId);
return UserInfoDTO.from(user);
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package org.sopt.confeti.domain.concert.application;

import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.sopt.confeti.domain.concert.Concert;
import org.sopt.confeti.domain.concert.infra.repository.ConcertRepository;
import org.sopt.confeti.global.exception.ConfetiException;
import org.sopt.confeti.global.exception.NotFoundException;
import org.sopt.confeti.global.message.ErrorMessage;
import org.sopt.confeti.global.util.artistsearcher.ArtistResolver;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
Expand All @@ -17,6 +16,7 @@ public class ConcertService {
private final ConcertRepository concertRepository;
private final ArtistResolver artistResolver;

@Transactional(readOnly = true)
public Concert getConcertDetailByConcertId(final long concertId) {
Concert concert = concertRepository.findById(concertId)
.orElseThrow(
Expand All @@ -28,6 +28,14 @@ public Concert getConcertDetailByConcertId(final long concertId) {
return concert;
}

@Transactional(readOnly = true)
public void existsById(long concertId) {
if (!concertRepository.existsById(concertId)) {
throw new NotFoundException(ErrorMessage.NOT_FOUND);
}
}

@Transactional(readOnly = true)
public Concert findById(final long concertId) {
return concertRepository.findById(concertId)
.orElseThrow(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,28 @@
import org.sopt.confeti.domain.concertfavorite.infra.repository.ConcertFavoriteRepository;
import org.sopt.confeti.domain.user.User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class ConcertFavoriteService {

private final ConcertFavoriteRepository concertFavoriteRepository;


@Transactional(readOnly = true)
public boolean isFavorite(final long userId, final long concertId) {
return concertFavoriteRepository.existsByUserIdAndConcertId(userId, concertId);
}

@Transactional
public void addFavorite(final User user, final Concert concert) {
concertFavoriteRepository.save(
ConcertFavorite.create(user, concert)
);
}

@Transactional
public void removeFavorite(final long userId, final long concertId) {
concertFavoriteRepository.deleteByUserIdAndConcertId(userId, concertId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@

public interface ConcertFavoriteRepository extends JpaRepository<ConcertFavorite, Long> {
boolean existsByUserIdAndConcertId(final long userId, final long concertId);
void deleteByUserIdAndConcertId(final long userId, final long concertId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,26 @@
import org.sopt.confeti.domain.user.User;
import org.sopt.confeti.domain.user.infra.repository.UserRepository;
import org.sopt.confeti.global.exception.NotFoundException;
import org.sopt.confeti.global.exception.UnauthorizedException;
import org.sopt.confeti.global.message.ErrorMessage;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;

@Transactional(readOnly = true)
public User findById(Long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND));
return user;
}

public User getUserInfo(Long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND));
.orElseThrow(
() -> new UnauthorizedException(ErrorMessage.UNAUTHORIZED)
);
return user;
}

@Transactional(readOnly = true)
public void existsById(Long userId) {
final boolean isExistUser = userRepository.existsById(userId);
if(!isExistUser) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.sopt.confeti.global.common.constant;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ArtistConstant {
public static final int ARTIST_BOX_OPEN_CRITERIA = 4;
}

0 comments on commit 0e0150f

Please sign in to comment.