Skip to content

Commit e89c227

Browse files
authored
feat(be): 커밋메시지에 적어놨습니다. (#18) (#31)
* 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 추가 * 글로벌 익셉션 설정에따라 êpr리뷰 ë설정 변경 + 컨트롤러 응답 공통 응답으로 변경
1 parent 5eb9ba7 commit e89c227

16 files changed

+240
-221
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: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package com.back.koreaTravelGuide.domain.ai.aiChat.controller
2+
3+
import com.back.koreaTravelGuide.common.ApiResponse
4+
import com.back.koreaTravelGuide.domain.ai.aiChat.dto.AiChatRequest
5+
import com.back.koreaTravelGuide.domain.ai.aiChat.dto.AiChatResponse
6+
import com.back.koreaTravelGuide.domain.ai.aiChat.dto.SessionMessagesResponse
7+
import com.back.koreaTravelGuide.domain.ai.aiChat.dto.SessionsResponse
8+
import com.back.koreaTravelGuide.domain.ai.aiChat.dto.UpdateSessionTitleRequest
9+
import com.back.koreaTravelGuide.domain.ai.aiChat.dto.UpdateSessionTitleResponse
10+
import com.back.koreaTravelGuide.domain.ai.aiChat.service.AiChatService
11+
import org.springframework.http.ResponseEntity
12+
import org.springframework.web.bind.annotation.DeleteMapping
13+
import org.springframework.web.bind.annotation.GetMapping
14+
import org.springframework.web.bind.annotation.PatchMapping
15+
import org.springframework.web.bind.annotation.PathVariable
16+
import org.springframework.web.bind.annotation.PostMapping
17+
import org.springframework.web.bind.annotation.RequestBody
18+
import org.springframework.web.bind.annotation.RequestMapping
19+
import org.springframework.web.bind.annotation.RequestParam
20+
import org.springframework.web.bind.annotation.RestController
21+
22+
@RestController
23+
@RequestMapping("/api/aichat")
24+
class AiChatController(
25+
private val aiChatService: AiChatService,
26+
) {
27+
@GetMapping("/sessions")
28+
fun getSessions(
29+
@RequestParam userId: Long,
30+
): ResponseEntity<ApiResponse<List<SessionsResponse>>> {
31+
val sessions =
32+
aiChatService.getSessions(userId).map {
33+
SessionsResponse(it.id!!, it.sessionTitle)
34+
}
35+
return ResponseEntity.ok(ApiResponse("채팅방 목록을 성공적으로 조회했습니다.", sessions))
36+
}
37+
38+
@PostMapping("/sessions")
39+
fun createSession(
40+
@RequestParam userId: Long,
41+
): ResponseEntity<ApiResponse<SessionsResponse>> {
42+
val session = aiChatService.createSession(userId)
43+
val response = SessionsResponse(session.id!!, session.sessionTitle)
44+
return ResponseEntity.ok(ApiResponse("채팅방이 성공적으로 생성되었습니다.", response))
45+
}
46+
47+
@DeleteMapping("/sessions/{sessionId}")
48+
fun deleteSession(
49+
@PathVariable sessionId: Long,
50+
@RequestParam userId: Long,
51+
): ResponseEntity<ApiResponse<Unit>> {
52+
aiChatService.deleteSession(sessionId, userId)
53+
return ResponseEntity.ok(ApiResponse("채팅방이 성공적으로 삭제되었습니다."))
54+
}
55+
56+
@GetMapping("/sessions/{sessionId}/messages")
57+
fun getSessionMessages(
58+
@PathVariable sessionId: Long,
59+
@RequestParam userId: Long,
60+
): ResponseEntity<ApiResponse<List<SessionMessagesResponse>>> {
61+
val messages = aiChatService.getSessionMessages(sessionId, userId)
62+
val response =
63+
messages.map {
64+
SessionMessagesResponse(it.content, it.senderType)
65+
}
66+
return ResponseEntity.ok(ApiResponse("채팅 메시지를 성공적으로 조회했습니다.", response))
67+
}
68+
69+
@PostMapping("/sessions/{sessionId}/messages")
70+
fun sendMessage(
71+
@PathVariable sessionId: Long,
72+
@RequestParam userId: Long,
73+
@RequestBody request: AiChatRequest,
74+
): ResponseEntity<ApiResponse<AiChatResponse>> {
75+
val (userMessage, aiMessage) = aiChatService.sendMessage(sessionId, userId, request.message)
76+
val response =
77+
AiChatResponse(
78+
userMessage = userMessage.content,
79+
aiMessage = aiMessage.content,
80+
)
81+
return ResponseEntity.ok(ApiResponse("메시지가 성공적으로 전송되었습니다.", response))
82+
}
83+
84+
@PatchMapping("/sessions/{sessionId}/title")
85+
fun updateSessionTitle(
86+
@PathVariable sessionId: Long,
87+
@RequestParam userId: Long,
88+
@RequestBody request: UpdateSessionTitleRequest,
89+
): ResponseEntity<ApiResponse<UpdateSessionTitleResponse>> {
90+
val updatedSession = aiChatService.updateSessionTitle(sessionId, userId, request.newTitle)
91+
val response =
92+
UpdateSessionTitleResponse(
93+
newTitle = updatedSession.sessionTitle,
94+
)
95+
return ResponseEntity.ok(ApiResponse("채팅방 제목이 성공적으로 수정되었습니다.", response))
96+
}
97+
}

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

Lines changed: 0 additions & 196 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)