feat: AI 추천에 중성화 조건 필터 (neutered)
Deploy (dev) / deploy (push) Failing after 13m42s
CI / build (push) Failing after 16m0s

GET /api/spots/{id}/ai-mates?dogId=&neutered=true|false — 해당 중성화 여부
강아지 중에서만 추천. 미지정 시 전체.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
sb
2026-07-11 22:14:41 +09:00
parent 3c79d1fffc
commit 20bfdd7576
2 changed files with 12 additions and 5 deletions
@@ -57,10 +57,13 @@ public class SpotController {
return spotService.activeMates(id, userId).stream().map(MateResponse::from).toList(); return spotService.activeMates(id, userId).stream().map(MateResponse::from).toList();
} }
/** AI 궁합 메이트 추천 — 내 강아지(dogId)와 사이좋게 지낼 만한 스팟 내 강아지 목록 */ /** AI 궁합 메이트 추천 — 내 강아지(dogId)와 사이좋게 지낼 만한 스팟 내 강아지 목록.
* neutered: 중성화 조건(true/false). 미지정 시 전체. */
@GetMapping("/{id}/ai-mates") @GetMapping("/{id}/ai-mates")
public List<AiMateResponse> aiMates(@PathVariable Long id, @RequestParam Long dogId) { public List<AiMateResponse> aiMates(@PathVariable Long id,
return mateAiService.compatibleMates(id, dogId); @RequestParam Long dogId,
@RequestParam(required = false) Boolean neutered) {
return mateAiService.compatibleMates(id, dogId, neutered);
} }
/** 현재 요일·시간대에 이 스팟에 자주 오는 견종(통계) */ /** 현재 요일·시간대에 이 스팟에 자주 오는 견종(통계) */
@@ -56,9 +56,12 @@ public class MateAiService {
this.gemini = gemini; this.gemini = gemini;
} }
/** 스팟 활성 메이트 중 내 강아지와 궁합 좋은 강아지 목록(궁합점수 내림차순). */ /**
* 스팟 활성 메이트 중 내 강아지와 궁합 좋은 강아지 목록(궁합점수 내림차순).
* neutered 가 주어지면 해당 중성화 여부에 맞는 강아지 중에서만 추천한다.
*/
@Transactional(readOnly = true) @Transactional(readOnly = true)
public List<AiMateResponse> compatibleMates(Long spotId, Long myDogId) { public List<AiMateResponse> compatibleMates(Long spotId, Long myDogId, Boolean neutered) {
if (!gemini.isConfigured()) { if (!gemini.isConfigured()) {
throw new ApiException(HttpStatus.SERVICE_UNAVAILABLE, "AI 추천이 설정되지 않았습니다."); throw new ApiException(HttpStatus.SERVICE_UNAVAILABLE, "AI 추천이 설정되지 않았습니다.");
} }
@@ -71,6 +74,7 @@ public class MateAiService {
return List.of(); return List.of();
} }
Map<Long, Dog> candidates = dogRepository.findWithTraitsByIdIn(activeIds).stream() Map<Long, Dog> candidates = dogRepository.findWithTraitsByIdIn(activeIds).stream()
.filter(d -> neutered == null || d.isNeutered() == neutered) // 중성화 조건 필터
.collect(Collectors.toMap(Dog::getId, Function.identity())); .collect(Collectors.toMap(Dog::getId, Function.identity()));
if (candidates.isEmpty()) { if (candidates.isEmpty()) {
return List.of(); return List.of();