Skip to content

Commit 82a3bd7

Browse files
committed
feat(be): AI 챗 컨트롤러 및 REST API 엔드포인트 구현
- AiChatController 6개 엔드포인트 완성 * GET /sessions (세션 목록) * POST /sessions (세션 생성) * DELETE /sessions/{id} (세션 삭제) * GET /sessions/{id}/messages (메시지 조회) * POST /sessions/{id}/messages (메시지 전송) * PATCH /sessions/{id}/title (제목 수정) - Service Layer Entity 반환으로 리팩토링 - Controller Layer DTO 변환 책임 분리 - getSessionWithOwnershipCheck 헬퍼 메서드 적용 - ChatController → AiChatController 리네임 - DTO 구조 정리 및 신규 DTO 추가
1 parent 9b4d06c commit 82a3bd7

16 files changed

+228
-225
lines changed

docs/api-specification.yaml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,50 @@ paths:
521521
aiMessage:
522522
$ref: '#/components/schemas/AiChatMessage'
523523

524+
/api/aichat/sessions/{sessionId}/title:
525+
patch:
526+
tags:
527+
- aichat
528+
summary: AI 채팅 세션 제목 수정
529+
description: AI 채팅 세션의 제목을 사용자가 직접 수정합니다
530+
parameters:
531+
- name: sessionId
532+
in: path
533+
required: true
534+
schema:
535+
type: integer
536+
format: int64
537+
description: AI 채팅 세션 ID
538+
requestBody:
539+
required: true
540+
content:
541+
application/json:
542+
schema:
543+
type: object
544+
required:
545+
- title
546+
properties:
547+
title:
548+
type: string
549+
maxLength: 100
550+
description: 새로운 세션 제목
551+
responses:
552+
'200':
553+
description: 제목 수정 성공
554+
content:
555+
application/json:
556+
schema:
557+
allOf:
558+
- $ref: '#/components/schemas/ApiResponse'
559+
- type: object
560+
properties:
561+
data:
562+
$ref: '#/components/schemas/AiChatSession'
563+
'404':
564+
description: 세션을 찾을 수 없음
565+
'403':
566+
description: 권한 없음
567+
524568
# ===================
525569
# User Chat Domain APIs
526570
# ===================
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.back.koreaTravelGuide.domain.ai.aiChat.controller
2+
3+
import com.back.koreaTravelGuide.domain.ai.aiChat.dto.AiChatRequest
4+
import com.back.koreaTravelGuide.domain.ai.aiChat.dto.AiChatResponse
5+
import com.back.koreaTravelGuide.domain.ai.aiChat.dto.SessionMessagesResponse
6+
import com.back.koreaTravelGuide.domain.ai.aiChat.dto.SessionsResponse
7+
import com.back.koreaTravelGuide.domain.ai.aiChat.dto.UpdateSessionTitleRequest
8+
import com.back.koreaTravelGuide.domain.ai.aiChat.dto.UpdateSessionTitleResponse
9+
import com.back.koreaTravelGuide.domain.ai.aiChat.service.AiChatService
10+
import org.springframework.web.bind.annotation.DeleteMapping
11+
import org.springframework.web.bind.annotation.GetMapping
12+
import org.springframework.web.bind.annotation.PatchMapping
13+
import org.springframework.web.bind.annotation.PathVariable
14+
import org.springframework.web.bind.annotation.PostMapping
15+
import org.springframework.web.bind.annotation.RequestBody
16+
import org.springframework.web.bind.annotation.RequestMapping
17+
import org.springframework.web.bind.annotation.RequestParam
18+
import org.springframework.web.bind.annotation.RestController
19+
20+
@RestController
21+
@RequestMapping("/api/aichat")
22+
class AiChatController(
23+
private val aiChatService: AiChatService,
24+
) {
25+
@GetMapping("/sessions")
26+
fun getSessions(
27+
@RequestParam userId: Long,
28+
): List<SessionsResponse> {
29+
return aiChatService.getSessions(userId).map {
30+
SessionsResponse(it.id!!, it.sessionTitle)
31+
}
32+
}
33+
34+
@PostMapping("/sessions")
35+
fun createSession(
36+
@RequestParam userId: Long,
37+
): SessionsResponse {
38+
val session = aiChatService.createSession(userId)
39+
return SessionsResponse(session.id!!, session.sessionTitle)
40+
}
41+
42+
@DeleteMapping("/sessions/{sessionId}")
43+
fun deleteSession(
44+
@PathVariable sessionId: Long,
45+
@RequestParam userId: Long,
46+
) {
47+
aiChatService.deleteSession(sessionId, userId)
48+
}
49+
50+
@GetMapping("/sessions/{sessionId}/messages")
51+
fun getSessionMessages(
52+
@PathVariable sessionId: Long,
53+
@RequestParam userId: Long,
54+
): List<SessionMessagesResponse> {
55+
val messages = aiChatService.getSessionMessages(sessionId, userId)
56+
return messages.map {
57+
SessionMessagesResponse(it.content, it.senderType)
58+
}
59+
}
60+
61+
@PostMapping("/sessions/{sessionId}/messages")
62+
fun sendMessage(
63+
@PathVariable sessionId: Long,
64+
@RequestParam userId: Long,
65+
@RequestBody request: AiChatRequest,
66+
): AiChatResponse {
67+
val (userMessage, aiMessage) = aiChatService.sendMessage(sessionId, userId, request.message)
68+
return AiChatResponse(
69+
userMessage = userMessage.content,
70+
aiMessage = aiMessage.content,
71+
)
72+
}
73+
74+
@PatchMapping("/sessions/{sessionId}/title")
75+
fun updateSessionTitle(
76+
@PathVariable sessionId: Long,
77+
@RequestParam userId: Long,
78+
@RequestBody request: UpdateSessionTitleRequest,
79+
): UpdateSessionTitleResponse {
80+
val updatedSession = aiChatService.updateSessionTitle(sessionId, userId, request.newTitle)
81+
return UpdateSessionTitleResponse(
82+
newTitle = updatedSession.sessionTitle,
83+
)
84+
}
85+
}

src/main/kotlin/com/back/koreaTravelGuide/domain/ai/aiChat/controller/ChatController.kt

Lines changed: 0 additions & 200 deletions
This file was deleted.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.back.koreaTravelGuide.domain.ai.aiChat.dto
2+
3+
data class AiChatRequest(
4+
val message: String,
5+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.back.koreaTravelGuide.domain.ai.aiChat.dto
2+
3+
data class AiChatResponse(
4+
val userMessage: String,
5+
val aiMessage: String,
6+
)

src/main/kotlin/com/back/koreaTravelGuide/domain/ai/aiChat/dto/ChatRequest.kt

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/main/kotlin/com/back/koreaTravelGuide/domain/ai/aiChat/dto/ChatResponse.kt

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.back.koreaTravelGuide.domain.ai.aiChat.dto
2+
3+
data class DeleteAiChatRequest(
4+
val sessionId: Long,
5+
)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.back.koreaTravelGuide.domain.ai.aiChat.dto
2+
3+
import com.back.koreaTravelGuide.domain.ai.aiChat.entity.SenderType
4+
5+
data class SessionMessagesResponse(
6+
val content: String,
7+
val senderType: SenderType,
8+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.back.koreaTravelGuide.domain.ai.aiChat.dto
2+
3+
data class SessionsResponse(
4+
val sessionId: Long,
5+
val sessionTitle: String,
6+
)

0 commit comments

Comments
 (0)