|
| 1 | +package store.lastdance.controller.analysis; |
| 2 | + |
| 3 | +import io.swagger.v3.oas.annotations.Operation; |
| 4 | +import io.swagger.v3.oas.annotations.Parameter; |
| 5 | +import io.swagger.v3.oas.annotations.tags.Tag; |
| 6 | +import jakarta.validation.Valid; |
| 7 | +import jakarta.validation.constraints.Min; |
| 8 | +import lombok.RequiredArgsConstructor; |
| 9 | +import org.springframework.data.domain.Pageable; |
| 10 | +import org.springframework.data.domain.Sort; |
| 11 | +import org.springframework.data.web.PageableDefault; |
| 12 | +import org.springframework.http.ResponseEntity; |
| 13 | +import org.springframework.security.core.annotation.AuthenticationPrincipal; |
| 14 | +import org.springframework.validation.annotation.Validated; |
| 15 | +import org.springframework.web.bind.annotation.*; |
| 16 | +import store.lastdance.aspect.RateLimit; |
| 17 | +import store.lastdance.domain.analysis.FeedbackType; |
| 18 | +import store.lastdance.dto.analysis.AnalyzeExpenseRequestDTO; |
| 19 | +import store.lastdance.dto.analysis.AnalyzeExpenseResponseDTO; |
| 20 | +import store.lastdance.dto.analysis.ExpenseAnalysisHistoryDTO; |
| 21 | +import store.lastdance.dto.response.ApiResponse; |
| 22 | +import store.lastdance.dto.response.PageWithSummaryResponse; |
| 23 | +import store.lastdance.security.oauth.CustomOAuth2User; |
| 24 | +import store.lastdance.service.analysis.AnalysisV2CommandService; |
| 25 | +import store.lastdance.service.analysis.AnalysisV2QueryService; |
| 26 | + |
| 27 | +import java.util.UUID; |
| 28 | + |
| 29 | +@RestController |
| 30 | +@RequestMapping("/api/v2/analysis") |
| 31 | +@RequiredArgsConstructor |
| 32 | +@Tag(name = "Analysis V2", description = "AI 지출 분석 V2 API") |
| 33 | +@Validated |
| 34 | +public class AnalysisV2Controller { |
| 35 | + |
| 36 | + private final AnalysisV2CommandService analysisV2CommandService; |
| 37 | + private final AnalysisV2QueryService analysisV2QueryService; |
| 38 | + |
| 39 | + @PostMapping("/expenses") |
| 40 | + @RateLimit // 30초에 1번만 요청 가능 |
| 41 | + @Operation(summary = "LLM 지출 분석 요청", description = "지정된 기간의 지출 내역을 LLM을 통해 분석") |
| 42 | + public ResponseEntity<ApiResponse<AnalyzeExpenseResponseDTO>> analyzeExpenses( |
| 43 | + @Parameter(hidden = true) @AuthenticationPrincipal CustomOAuth2User oAuth2User, |
| 44 | + @Valid @RequestBody AnalyzeExpenseRequestDTO requestDTO |
| 45 | + ) { |
| 46 | + UUID userId = oAuth2User.getUserId(); |
| 47 | + AnalyzeExpenseResponseDTO response = analysisV2QueryService.analyzeExpenses(userId, requestDTO); |
| 48 | + return ResponseEntity.ok(ApiResponse.success(response)); |
| 49 | + } |
| 50 | + |
| 51 | + @PostMapping("/expenses/{historyId}/feedback") |
| 52 | + @RateLimit // 30초에 1번만 요청 가능 |
| 53 | + @Operation(summary = "LLM 지출 분석 피드백 토글", description = "LLM 지출분석 결과에 대해 피드백(좋아요/싫어요)을 남기거나 취소합니다.") |
| 54 | + public ResponseEntity<?> feedbackAnalyzeExpense( |
| 55 | + @Parameter(hidden = true) @AuthenticationPrincipal CustomOAuth2User oAuth2User, |
| 56 | + @PathVariable @Min(1) Long historyId, |
| 57 | + @RequestParam FeedbackType type |
| 58 | + ) { |
| 59 | + UUID userId = oAuth2User.getUserId(); |
| 60 | + FeedbackType result = analysisV2CommandService.toggleFeedback(historyId, userId, type); |
| 61 | + |
| 62 | + if (result == null) { |
| 63 | + return ResponseEntity.noContent().build(); |
| 64 | + } |
| 65 | + return ResponseEntity.ok(ApiResponse.success(result)); |
| 66 | + } |
| 67 | + |
| 68 | + |
| 69 | + @GetMapping("/expenses") |
| 70 | + @Operation(summary = "LLM 지출 분석 내역 조회", description = "사용자의 전체 지출 분석 내역을 최신순으로 조회 (페이징 포함)") |
| 71 | + public ResponseEntity<ApiResponse<PageWithSummaryResponse<ExpenseAnalysisHistoryDTO>>> getAnalysisHistoryList( |
| 72 | + @Parameter(hidden = true) @AuthenticationPrincipal CustomOAuth2User oAuth2User, |
| 73 | + @PageableDefault( |
| 74 | + sort = "createdAt", |
| 75 | + direction = Sort.Direction.DESC |
| 76 | + ) Pageable pageable |
| 77 | + ) { |
| 78 | + UUID userId = oAuth2User.getUserId(); |
| 79 | + PageWithSummaryResponse<ExpenseAnalysisHistoryDTO> response = analysisV2QueryService.getExpenseAnalysisHistory(userId, pageable); |
| 80 | + |
| 81 | + return ResponseEntity.ok(ApiResponse.success(response)); |
| 82 | + } |
| 83 | +} |
0 commit comments