Skip to content

Commit b2b9aff

Browse files
authored
[feat] 나만의 bar(킵) 추가/재추가 기능 구현 #30 (#33)
* feat: 마이바(MyBar) 엔티티에 keptAt 필드 추가 - `keptAt`은 칵테일이 마지막으로 킵된 시점을 저장하며, 목록 정렬 기준으로 활용될 예정 기존의 `createdAt` 필드는 엔티티가 생성된 시점만을 나타내므로, '킵' 해제 후 다시 '킵'하는 경우 목록의 최상단에 재배치되게 하려면 최신 '킵' 시간을 추적할 수 있는 별도의 필드가 필요했습니다. 이를 위해 `keptAt` 필드를 추가하여 최신 킵 시간을 관리하고, 이를 목록 정렬의 기준으로 사용하도록 개선했습니다. * feat: MyBarItemResponseDto에 keptAt 필드 추가 * refactor: MyBarRepository에 킵 관련 쿼리 메소드 추가 및 정렬 기준 변경 - `findByUserIdAndStatusOrderByIdDesc` 쿼리 메소드의 정렬 기준을 `KeptAt`으로 변경 - 최근에 킵한 칵테일이 목록 상단에 오도록 `keptAt`을 기준으로 내림차순 정렬하고, 동일한 시간일 경우 `id`로 보조 정렬 - `countByUserIdAndStatus` 쿼리 메소드의 매개변수명을 `user_Id`로 통일 - 칵테일 킵 상태 확인(`existsByUser_IdAndCocktail_IdAndStatus`)을 위한 쿼리 메소드 추가 - 복원/재킵(`findByUser_IdAndCocktail_Id`)을 위한 쿼리 메소드 추가 * feat: 마이바(MyBar) '킵' 기능 추가 및 목록 조회 로직 개선 - `keep` 메서드를 추가하여 칵테일을 킵하거나 재킵하는 로직 구현 - 기존 목록 조회 메서드(`getMyBar`)의 쿼리 정렬 기준을 `KeptAt`으로 변경 - `UserRepository`와 `CocktailRepository`를 추가하여 엔티티 참조에 활용 * feat: 마이바(MyBar) 칵테일 킵(Keep) API 추가 - `POST /me/bar/{cocktailId}/keep` 엔드포인트 추가 - URL `@PathVariable`로 칵테일 ID를 받고, `@AuthenticationPrincipal`로 사용자 ID를 획득 - `MyBarService`의 `keep` 메서드를 호출하여 킵 기능 처리 - 성공 시 HTTP 201 상태 코드와 "kept" 메시지 반환 * refactor: MyBarRepository 쿼리 메소드의 필드명 수정 -`@ManyToOne` 관계에서 연관 엔티티의 ID 필드를 직접 참조할 때의 **JPA 규칙**을 따르기 위함입니다. 이전 코드에서는 `Cocktail` 엔티티의 PK 필드명(`cocktailId`)을 생략했으나, 명확한 필드 경로(`Cocktail_CocktailId`)를 명시하여 코드의 가독성과 일관성을 높였습니다.
1 parent db43ac5 commit b2b9aff

File tree

5 files changed

+63
-7
lines changed

5 files changed

+63
-7
lines changed

src/main/java/com/back/domain/mybar/controller/MyBarController.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@
88
import lombok.RequiredArgsConstructor;
99
import org.springframework.security.core.annotation.AuthenticationPrincipal;
1010
import org.springframework.validation.annotation.Validated;
11-
import org.springframework.web.bind.annotation.GetMapping;
12-
import org.springframework.web.bind.annotation.RequestMapping;
13-
import org.springframework.web.bind.annotation.RequestParam;
14-
import org.springframework.web.bind.annotation.RestController;
11+
import org.springframework.web.bind.annotation.*;
1512

1613
@RestController
1714
@RequestMapping("/me/bar")
@@ -30,4 +27,14 @@ public RsData<MyBarListResponseDto> getMyBarList(
3027
MyBarListResponseDto body = myBarService.getMyBar(userId, page, pageSize);
3128
return RsData.successOf(body); // code=200, message="success"
3229
}
30+
31+
/** 킵 추가(생성/복원/재킵) */
32+
@PostMapping("/{cocktailId}/keep")
33+
public RsData<Void> keep(
34+
@AuthenticationPrincipal(expression = "id") Long userId,
35+
@PathVariable Long cocktailId
36+
) {
37+
myBarService.keep(userId, cocktailId);
38+
return RsData.of(201, "kept"); // Aspect가 HTTP 201로 설정
39+
}
3340
}

src/main/java/com/back/domain/mybar/dto/MyBarItemResponseDto.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public class MyBarItemResponseDto {
1414
private String cocktailName;
1515
private String imageUrl;
1616
private LocalDateTime createdAt;
17+
private LocalDateTime keptAt;
1718

1819
public static MyBarItemResponseDto from(MyBar m) {
1920
return MyBarItemResponseDto.builder()
@@ -22,6 +23,7 @@ public static MyBarItemResponseDto from(MyBar m) {
2223
.cocktailName(m.getCocktail().getCocktailName())
2324
.imageUrl(m.getCocktail().getCocktailImgUrl())
2425
.createdAt(m.getCreatedAt())
26+
.keptAt(m.getKeptAt())
2527
.build();
2628
}
2729
}

src/main/java/com/back/domain/mybar/entity/MyBar.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ public class MyBar {
2727
@CreatedDate
2828
private LocalDateTime createdAt;
2929

30+
/** 최근에 '킵'된 시각(생성/복원/재킵 시 갱신) — 목록 정렬에 사용 */
31+
@Column(name = "kept_at", nullable = false)
32+
private LocalDateTime keptAt;
33+
3034
/** 킵 해제 시각 (ACTIVE일 때는 null) */
3135
private LocalDateTime deletedAt;
3236

src/main/java/com/back/domain/mybar/repository/MyBarRepository.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,19 @@
77
import org.springframework.data.jpa.repository.JpaRepository;
88
import org.springframework.stereotype.Repository;
99

10+
import java.util.Optional;
11+
1012
@Repository
1113
public interface MyBarRepository extends JpaRepository<MyBar, Long> {
1214
/** 나만의 bar(킵) 목록: ACTIVE만, id desc */
13-
Page<MyBar> findByUserIdAndStatusOrderByIdDesc(Long userId, KeepStatus status, Pageable pageable);
15+
Page<MyBar> findByUser_IdAndStatusOrderByKeptAtDescIdDesc(Long userId, KeepStatus status, Pageable pageable);
1416

1517
/** 프로필/요약용: ACTIVE 개수 */
16-
long countByUserIdAndStatus(Long userId, KeepStatus status);
18+
long countByUser_IdAndStatus(Long userId, KeepStatus status);
19+
20+
/** 현재 킵 상태 확인(아이콘 등): ACTIVE 존재 여부 */
21+
boolean existsByUser_IdAndCocktail_CocktailIdAndStatus(Long userId, Long cocktailId, KeepStatus status);
22+
23+
/** 복원/재킵을 위해 status 무시하고 한 건 찾기 (없으면 Optional.empty) */
24+
Optional<MyBar> findByUser_IdAndCocktail_CocktailId(Long userId, Long cocktailId);
1725
}

src/main/java/com/back/domain/mybar/service/MyBarService.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,34 @@
11
package com.back.domain.mybar.service;
22

3+
import com.back.domain.cocktail.repository.CocktailRepository;
34
import com.back.domain.mybar.dto.MyBarItemResponseDto;
45
import com.back.domain.mybar.dto.MyBarListResponseDto;
56
import com.back.domain.mybar.entity.MyBar;
67
import com.back.domain.mybar.enums.KeepStatus;
78
import com.back.domain.mybar.repository.MyBarRepository;
9+
import com.back.domain.user.repository.UserRepository;
810
import lombok.RequiredArgsConstructor;
911
import org.springframework.data.domain.Page;
1012
import org.springframework.data.domain.PageRequest;
1113
import org.springframework.stereotype.Service;
1214
import org.springframework.transaction.annotation.Transactional;
1315

16+
import java.time.LocalDateTime;
1417
import java.util.ArrayList;
1518
import java.util.List;
19+
import java.util.Optional;
1620

1721
@Service
1822
@RequiredArgsConstructor
1923
public class MyBarService {
2024

2125
private final MyBarRepository myBarRepository;
26+
private final UserRepository userRepository;
27+
private final CocktailRepository cocktailRepository;
2228

2329
@Transactional(readOnly = true)
2430
public MyBarListResponseDto getMyBar(Long userId, int page, int pageSize) {
25-
Page<MyBar> myBarPage = myBarRepository.findByUserIdAndStatusOrderByIdDesc(userId, KeepStatus.ACTIVE, PageRequest.of(page, pageSize));
31+
Page<MyBar> myBarPage = myBarRepository.findByUser_IdAndStatusOrderByKeptAtDescIdDesc(userId, KeepStatus.ACTIVE, PageRequest.of(page, pageSize));
2632

2733
List<MyBar> myBars = myBarPage.getContent();
2834
List<MyBarItemResponseDto> items = new ArrayList<>();
@@ -33,4 +39,33 @@ public MyBarListResponseDto getMyBar(Long userId, int page, int pageSize) {
3339

3440
return new MyBarListResponseDto(items, hasNext, nextPage);
3541
}
42+
43+
@Transactional
44+
public void keep(Long userId, Long cocktailId) {
45+
Optional<MyBar> existingMyBar =
46+
myBarRepository.findByUser_IdAndCocktail_CocktailId(userId, cocktailId);
47+
48+
LocalDateTime now = LocalDateTime.now();
49+
50+
if (existingMyBar.isPresent()) {
51+
// 이미 행이 있으면: 최근에 다시 킵했다고 보고 keptAt만 갱신
52+
MyBar myBar = existingMyBar.get();
53+
myBar.setKeptAt(now);
54+
if (myBar.getStatus() == KeepStatus.DELETED) {
55+
// 해제돼 있던 건 복원
56+
myBar.setStatus(KeepStatus.ACTIVE);
57+
myBar.setDeletedAt(null);
58+
}
59+
return; // 이미 ACTIVE여도 keptAt 갱신으로 충분
60+
}
61+
62+
// 없으면 새로 생성
63+
MyBar myBar = new MyBar();
64+
myBar.setUser(userRepository.getReferenceById(userId));
65+
myBar.setCocktail(cocktailRepository.getReferenceById(cocktailId));
66+
myBar.setStatus(KeepStatus.ACTIVE);
67+
myBar.setKeptAt(now);
68+
69+
myBarRepository.save(myBar);
70+
}
3671
}

0 commit comments

Comments
 (0)