Skip to content

Commit 52c39ed

Browse files
authored
Merge pull request #178 from Central-MakeUs/release
Release
2 parents 563f2b6 + 5cc39ae commit 52c39ed

File tree

10 files changed

+128
-88
lines changed

10 files changed

+128
-88
lines changed

src/main/java/com/example/ForDay/domain/activity/controller/ActivityController.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
@RestController
1414
@RequiredArgsConstructor
1515
@RequestMapping("/activities")
16-
public class ActivityController implements ActivityControllerDocs{
16+
public class ActivityController implements ActivityControllerDocs {
1717
private final ActivityService activityService;
1818

1919
@Override
@@ -31,8 +31,10 @@ public MessageResDto deleteActivity(@PathVariable(name = "activityId") Long acti
3131
return activityService.deleteActivity(activityId, user);
3232
}
3333

34+
@Override
3435
@GetMapping("/ai-recommend/items")
35-
public GetAiRecommendItemsResDto getAiRecommendItems(@AuthenticationPrincipal CustomUserDetails user) {
36-
return activityService.getAiRecommendItems(user);
36+
public GetAiRecommendItemsResDto getAiRecommendItems(@RequestParam(name = "hobbyId") Long hobbyId,
37+
@AuthenticationPrincipal CustomUserDetails user) {
38+
return activityService.getAiRecommendItems(hobbyId, user);
3739
}
3840
}

src/main/java/com/example/ForDay/domain/activity/controller/ActivityControllerDocs.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.example.ForDay.domain.activity.controller;
22

33
import com.example.ForDay.domain.activity.dto.request.UpdateActivityReqDto;
4+
import com.example.ForDay.domain.activity.dto.response.GetAiRecommendItemsResDto;
45
import com.example.ForDay.domain.hobby.dto.request.*;
56
import com.example.ForDay.domain.hobby.dto.response.*;
67
import com.example.ForDay.domain.hobby.type.HobbyStatus;
@@ -105,7 +106,6 @@ public interface ActivityControllerDocs {
105106
)
106107
)
107108
})
108-
@PatchMapping("/{activityId}")
109109
MessageResDto updateActivity(
110110
@PathVariable(name = "activityId")
111111
@Parameter(description = "수정할 활동 ID", example = "1")
@@ -219,13 +219,36 @@ MessageResDto updateActivity(
219219
)
220220
)
221221
})
222-
@DeleteMapping("/{activityId}")
223-
public MessageResDto deleteActivity(
222+
MessageResDto deleteActivity(
224223
@PathVariable
225224
@Parameter(description = "삭제할 활동 ID", example = "1")
226225
Long activityId,
227226
@AuthenticationPrincipal CustomUserDetails user
228227
);
229228

229+
@Operation(
230+
summary = "AI 추천 활동 아이템 조회",
231+
description = "특정 취미(hobbyId)에 대해 이전에 추천받았던 활동 아이템 리스트를 조회합니다."
232+
)
233+
@ApiResponses({
234+
@ApiResponse(
235+
responseCode = "200",
236+
description = "조회 성공",
237+
content = @Content(
238+
mediaType = "application/json",
239+
schema = @Schema(implementation = GetAiRecommendItemsResDto.class)
240+
)
241+
)
242+
})
243+
GetAiRecommendItemsResDto getAiRecommendItems(
244+
@Parameter(
245+
description = "조회하고자 하는 활동 추천 리스트의 취미 ID",
246+
required = true,
247+
example = "6356892"
248+
)
249+
@RequestParam(name = "hobbyId") Long hobbyId,
250+
@AuthenticationPrincipal CustomUserDetails user
251+
);
252+
230253

231254
}

src/main/java/com/example/ForDay/domain/activity/dto/response/GetAiRecommendItemsResDto.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,17 @@
1010
@AllArgsConstructor
1111
@NoArgsConstructor
1212
public class GetAiRecommendItemsResDto {
13-
private MessageDto message;
13+
private String message;
14+
private Long hobbyId;
15+
private String hobbyName;
1416
private List<ItemDto> activityItems;
1517

16-
@Data
17-
@AllArgsConstructor
18-
@NoArgsConstructor
19-
public static class MessageDto {
20-
private String message;
21-
}
2218

2319
@Data
2420
@AllArgsConstructor
2521
@NoArgsConstructor
2622
public static class ItemDto {
2723
private Long itemId;
28-
private Long hobbyId;
29-
private String hobbyName;
3024
private String content;
3125
private String description;
3226
}
Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.example.ForDay.domain.activity.repository;
22

33
import com.example.ForDay.domain.activity.entity.ActivityRecommendItem;
4-
import com.example.ForDay.domain.hobby.entity.Hobby;
54
import org.springframework.data.jpa.repository.JpaRepository;
65
import org.springframework.data.jpa.repository.Modifying;
76
import org.springframework.data.jpa.repository.Query;
@@ -11,17 +10,17 @@
1110
import java.util.List;
1211

1312
public interface ActivityRecommendItemRepository extends JpaRepository<ActivityRecommendItem, Long> {
13+
@Modifying
14+
@Query("DELETE FROM ActivityRecommendItem a WHERE a.createdAt < :targetDate")
15+
void deleteOldItems(@Param("targetDate") LocalDateTime targetDate);
16+
1417
@Query("SELECT a FROM ActivityRecommendItem a " +
15-
"JOIN FETCH a.hobby " +
16-
"WHERE a.hobby IN :hobbies " +
17-
"AND a.createdAt >= :start AND a.createdAt <= :end")
18-
List<ActivityRecommendItem> findAllByHobbiesAndDate(
19-
@Param("hobbies") List<Hobby> hobbies,
18+
"JOIN FETCH a.hobby h " +
19+
"WHERE h.id = :hobbyId " +
20+
"AND a.createdAt BETWEEN :start AND :end")
21+
List<ActivityRecommendItem> findAllByHobbyIdAndDate(
22+
@Param("hobbyId") Long hobbyId,
2023
@Param("start") LocalDateTime start,
2124
@Param("end") LocalDateTime end
2225
);
23-
24-
@Modifying
25-
@Query("DELETE FROM ActivityRecommendItem a WHERE a.createdAt < :targetDate")
26-
void deleteOldItems(@Param("targetDate") LocalDateTime targetDate);
2726
}

src/main/java/com/example/ForDay/domain/activity/repository/ActivityRepositoryImpl.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public GetHobbyActivitiesResDto getHobbyActivities(Long hobbyId, Integer size) {
3636
.from(activity)
3737
.where(activity.hobby.id.eq(hobbyId))
3838
.orderBy(
39+
activity.createdAt.desc(),
3940
activity.lastRecordedAt.desc().nullsLast(),
4041
activity.collectedStickerNum.desc(),
4142
activity.content.asc()
@@ -69,6 +70,7 @@ public GetActivityListResDto getActivityList(Long hobbyId, String userId) {
6970
activity.user.id.eq(userId)
7071
)
7172
.orderBy(
73+
activity.createdAt.desc(),
7274
activity.lastRecordedAt.desc().nullsLast(),
7375
activity.collectedStickerNum.desc(),
7476
activity.content.asc()

src/main/java/com/example/ForDay/domain/activity/service/ActivityService.java

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@
1010
import com.example.ForDay.domain.activity.repository.ActivityRecommendItemRepository;
1111
import com.example.ForDay.domain.friend.repository.FriendRelationRepository;
1212
import com.example.ForDay.domain.friend.type.FriendRelationStatus;
13-
import com.example.ForDay.domain.hobby.dto.request.FastAPIRecommendReqDto;
1413
import com.example.ForDay.domain.hobby.dto.response.CollectActivityResDto;
15-
import com.example.ForDay.domain.hobby.dto.response.FastAPIRecommendResDto;
1614
import com.example.ForDay.domain.hobby.entity.HobbyCard;
1715
import com.example.ForDay.domain.hobby.repository.HobbyCardRepository;
1816
import com.example.ForDay.domain.hobby.repository.HobbyRepository;
17+
import com.example.ForDay.domain.hobby.service.UserSummaryAIService;
1918
import com.example.ForDay.domain.record.entity.ActivityRecord;
2019
import com.example.ForDay.domain.record.repository.ActivityRecordRepository;
2120
import com.example.ForDay.domain.activity.repository.ActivityRepository;
@@ -33,7 +32,6 @@
3332
import lombok.RequiredArgsConstructor;
3433
import lombok.extern.slf4j.Slf4j;
3534
import org.springframework.beans.factory.annotation.Value;
36-
import org.springframework.security.core.parameters.P;
3735
import org.springframework.stereotype.Service;
3836
import org.springframework.transaction.annotation.Transactional;
3937
import org.springframework.util.StringUtils;
@@ -61,6 +59,7 @@ public class ActivityService {
6159
private final FriendRelationRepository friendRelationRepository;
6260
private final RestTemplate restTemplate;
6361
private final ActivityRecommendItemRepository recommendItemRepository;
62+
private final UserSummaryAIService userSummaryAIService;
6463

6564
@Value("${fastapi.url}")
6665
private String fastApiBaseUrl;
@@ -363,43 +362,53 @@ public CollectActivityResDto collectActivity(Long hobbyId, Long activityId, Cust
363362
}
364363

365364
@Transactional(readOnly = true)
366-
public GetAiRecommendItemsResDto getAiRecommendItems(CustomUserDetails user) {
365+
public GetAiRecommendItemsResDto getAiRecommendItems(Long hobbyId, CustomUserDetails user) {
367366
User currentUser = userUtil.getCurrentUser(user);
368367
String currentUserId = currentUser.getId();
368+
String socialId = currentUser.getSocialId();
369369

370-
// 메세지 조회 -> 논의 한 후 수정 예정
371-
372-
// 1. 현재 유저의 현재 진행 중인 취미 조회
373-
List<Hobby> progressHobbies = hobbyRepository.findAllByUserIdAndStatusOrderByIdDesc(
374-
currentUserId,
375-
HobbyStatus.IN_PROGRESS
376-
);
377-
378-
if (progressHobbies.isEmpty()) {
379-
return new GetAiRecommendItemsResDto(new GetAiRecommendItemsResDto.MessageDto(), Collections.emptyList());
380-
}
370+
// 1. 취미 조회
371+
Hobby hobby = hobbyRepository.findByIdAndUserId(hobbyId, currentUserId).orElseThrow(() -> new CustomException(ErrorCode.HOBBY_NOT_FOUND));
381372

382373
// 2. 오늘 날짜 범위 설정
383374
LocalDateTime startOfToday = LocalDate.now().atStartOfDay();
384375
LocalDateTime endOfToday = LocalDate.now().atTime(LocalTime.MAX);
385376

386377
// 3. 오늘 생성된 추천 아이템 조회
387-
List<ActivityRecommendItem> items = recommendItemRepository.findAllByHobbiesAndDate(
388-
progressHobbies, startOfToday, endOfToday
378+
List<ActivityRecommendItem> items = recommendItemRepository.findAllByHobbyIdAndDate(
379+
hobby.getId(), startOfToday, endOfToday
389380
);
390381

382+
if(items.isEmpty()) {
383+
return new GetAiRecommendItemsResDto();
384+
}
385+
391386
// 4. DTO 변환
392387
List<GetAiRecommendItemsResDto.ItemDto> itemDtos = items.stream()
393388
.map(item -> new GetAiRecommendItemsResDto.ItemDto(
394389
item.getId(),
395-
item.getHobby().getId(),
396-
item.getHobby().getHobbyName(),
397390
item.getContent(),
398391
item.getDescription()
399392
))
400393
.toList();
401394

402-
return new GetAiRecommendItemsResDto(new GetAiRecommendItemsResDto.MessageDto(), itemDtos);
395+
// 메세지 조회
396+
String userSummaryText = "";
397+
long recordCount = activityRecordRepository.countByUserIdAndHobbyId(currentUser.getId(), hobbyId);
398+
399+
if(recordCount >=5) {
400+
// 기존에 사용자 요약 문구가 존재하는지 redis에 조회
401+
if(userSummaryAIService.hasSummary(socialId, hobbyId)) {
402+
userSummaryText = userSummaryAIService.getSummary(socialId, hobby.getId());
403+
} else {
404+
// fast api에 요청
405+
userSummaryText = userSummaryAIService.fetchAndSaveUserSummary(currentUserId, socialId, hobbyId, hobby.getHobbyName());
406+
}
407+
408+
}
409+
userSummaryText += " 이전에 추천 받은 활동들이에요.";
410+
411+
return new GetAiRecommendItemsResDto(userSummaryText, hobby.getId(), hobby.getHobbyName(), itemDtos);
403412
}
404413

405414
// 유틸 클래스

src/main/java/com/example/ForDay/domain/hobby/dto/response/GetHomeHobbyInfoResDto.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ public class GetHomeHobbyInfoResDto {
2020
private String userSummaryText; // AI가 분석한 요약 문구 (기록 5개 이상 시)
2121
private String recommendMessage; // "포데이 AI가 알맞은 취미활동을 추천해드려요"
2222
private boolean aiCallRemaining; // 오늘 AI 호출 가능 여부
23+
private Integer aiCallRemainingCount; // 현재까지 호출한 횟수
24+
private String nickname;
2325

2426
@Data
2527
@NoArgsConstructor

src/main/java/com/example/ForDay/domain/hobby/service/HobbyService.java

Lines changed: 6 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ public ActivityAIRecommendResDto activityAiRecommend(Long hobbyId, CustomUserDet
171171
userSummaryText = userSummaryAIService.getSummary(socialId, hobby.getId());
172172
} else {
173173
// fast api에 요청
174-
userSummaryText = fetchAndSaveUserSummary(userId, socialId, hobbyId, hobby.getHobbyName());
174+
userSummaryText = userSummaryAIService.fetchAndSaveUserSummary(userId, socialId, hobbyId, hobby.getHobbyName());
175175
}
176176

177177
}
@@ -314,14 +314,14 @@ public GetHomeHobbyInfoResDto getHomeHobbyInfo(Long hobbyId, CustomUserDetails u
314314

315315
if(targetHobby == null) {
316316
log.info("[GetHomeHobbyInfo] 진행 중인 취미 없음 - 기본 대시보드 반환. UserId: {}", currentUser.getId());
317-
return new GetHomeHobbyInfoResDto(List.of(), null, "반가워요, " + currentUser.getNickname() + "님! 👋", "", "포데이 AI가 알맞은 취미활동을 추천해드려요", false);
317+
return new GetHomeHobbyInfoResDto(List.of(), null, "반가워요, " + currentUser.getNickname() + "님! 👋", "", "포데이 AI가 알맞은 취미활동을 추천해드려요", false, 0, null);
318318
}
319319

320320
GetHomeHobbyInfoResDto response = hobbyRepository.getHomeHobbyInfo(targetHobby.getId(), currentUser);
321321

322322
if (response == null) {
323323
log.warn("[GetHomeHobbyInfo] 취미 정보 조회 실패(DB 데이터 불일치 가능성) - HobbyId: {}", targetHobby.getId());
324-
return new GetHomeHobbyInfoResDto(List.of(), null, "반가워요, " + currentUser.getNickname() + "님! 👋", "", "포데이 AI가 알맞은 취미활동을 추천해드려요", false);
324+
return new GetHomeHobbyInfoResDto(List.of(), null, "반가워요, " + currentUser.getNickname() + "님! 👋", "", "포데이 AI가 알맞은 취미활동을 추천해드려요", false, 0, null);
325325
}
326326
// AI 관련 로직 처리
327327
String socialId = currentUser.getSocialId();
@@ -352,7 +352,7 @@ public GetHomeHobbyInfoResDto getHomeHobbyInfo(Long hobbyId, CustomUserDetails u
352352
} else {
353353
log.info("[GetHomeHobbyInfo] 새로운 AI 요약 생성 요청 - User: {}, Hobby: {}", currentUser.getId(), targetHobby.getHobbyName());
354354
try {
355-
userSummaryText = fetchAndSaveUserSummary(currentUser.getId(), socialId, targetHobby.getId(), targetHobby.getHobbyName());
355+
userSummaryText = userSummaryAIService.fetchAndSaveUserSummary(currentUser.getId(), socialId, targetHobby.getId(), targetHobby.getHobbyName());
356356
} catch (Exception e) {
357357
log.error("AI 요약 생성 중 오류 발생: {}", e.getMessage());
358358
userSummaryText = "";
@@ -370,6 +370,8 @@ public GetHomeHobbyInfoResDto getHomeHobbyInfo(Long hobbyId, CustomUserDetails u
370370
.userSummaryText(userSummaryText)
371371
.recommendMessage("포데이 AI가 알맞은 취미활동을 추천해드려요")
372372
.aiCallRemaining(isAiCallRemaining)
373+
.aiCallRemainingCount(maxCallLimit - aiCallCountService.getCurrentCount(socialId, targetHobby.getId()))
374+
.nickname(currentUser.getNickname())
373375
.build();
374376
}
375377

@@ -677,44 +679,6 @@ private Hobby getLatestInProgressHobby(User user) {
677679
.orElse(null);
678680
}
679681

680-
/**
681-
* FastAPI에 요약을 요청하고 Redis에 저장하는 전용 메서드
682-
*/
683-
private String fetchAndSaveUserSummary(String userId, String socialId, Long hobbyId, String hobbyName) {
684-
try {
685-
// 1. 요청 DTO 구성
686-
ActivitySummaryRequest requestDto = ActivitySummaryRequest.builder()
687-
.userId(userId)
688-
.userHobbyId(hobbyId)
689-
.hobbyName(hobbyName)
690-
.build();
691-
692-
String fastapiUrl = fastApiBaseUrl + "/ai/summary";
693-
694-
// 2. FastAPI 호출 및 DTO 응답 받기
695-
ActivitySummaryResponse response = restTemplate.postForObject(
696-
fastapiUrl,
697-
requestDto,
698-
ActivitySummaryResponse.class
699-
);
700-
701-
// 3. 결과 처리
702-
if (response != null && response.getSummary() != null) {
703-
String summary = response.getSummary();
704-
705-
// Redis에 7일간 저장
706-
userSummaryAIService.saveSummary(socialId, hobbyId, summary);
707-
return summary;
708-
}
709-
} catch (Exception e) {
710-
log.error("FastAPI 요약 요청 실패 | socialId: {}, hobbyId: {}, error: {}",
711-
socialId, hobbyId, e.getMessage());
712-
}
713-
714-
// 예외 발생 시 기본 가이드 문구 반환
715-
return "";
716-
}
717-
718682
@Transactional
719683
public SetHobbyCoverImageResDto setHobbyCoverImage(@Valid SetHobbyCoverImageReqDto reqDto, CustomUserDetails user) throws Exception {
720684
User currentUser = userUtil.getCurrentUser(user);

0 commit comments

Comments
 (0)