Skip to content

Commit 48943b5

Browse files
authored
Merge pull request #88 from TU-NEBULA/feature/SCRUM-198
SCRUM-198: 챗봇 API 구현
2 parents 7aca707 + d7559e6 commit 48943b5

File tree

16 files changed

+615
-164
lines changed

16 files changed

+615
-164
lines changed

build.gradle

Lines changed: 98 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,130 +1,133 @@
11
plugins {
2-
id 'java'
3-
id 'jacoco'
4-
id 'org.springframework.boot' version '3.3.7'
5-
id 'io.spring.dependency-management' version '1.1.7'
6-
id 'com.google.cloud.tools.jib' version '3.4.4'
2+
id 'java'
3+
id 'jacoco'
4+
id 'org.springframework.boot' version '3.3.7'
5+
id 'io.spring.dependency-management' version '1.1.7'
6+
id 'com.google.cloud.tools.jib' version '3.4.4'
77
}
88

99
group = 'com.team-nebula'
1010
version = '0.0.1-SNAPSHOT'
1111

1212
java {
13-
toolchain {
14-
languageVersion = JavaLanguageVersion.of(17)
15-
}
13+
toolchain {
14+
languageVersion = JavaLanguageVersion.of(17)
15+
}
1616
}
1717

1818
configurations {
19-
compileOnly {
20-
extendsFrom annotationProcessor
21-
}
19+
compileOnly {
20+
extendsFrom annotationProcessor
21+
}
2222
}
2323

2424
repositories {
25-
mavenCentral()
25+
mavenCentral()
2626
}
2727

2828
dependencies {
29-
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
30-
implementation 'org.springframework.boot:spring-boot-starter-web'
31-
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.4' // Swagger
32-
implementation 'org.springframework.boot:spring-boot-starter-data-neo4j' // Neo4j
33-
implementation 'org.springframework.boot:spring-boot-starter-security' // Spring Security
34-
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' // OAuth2
35-
36-
// JWT
37-
implementation 'io.jsonwebtoken:jjwt-api:0.12.3'
38-
implementation 'io.jsonwebtoken:jjwt-impl:0.12.3'
39-
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3'
40-
41-
// AWS S3
42-
implementation 'com.amazonaws:aws-java-sdk-s3:1.12.300'
43-
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
44-
45-
// Logback
46-
implementation 'org.springframework.boot:spring-boot-starter-logging'
47-
48-
// Apache Commons IO
49-
implementation 'commons-io:commons-io:2.18.0'
50-
51-
compileOnly 'org.projectlombok:lombok'
52-
developmentOnly 'org.springframework.boot:spring-boot-devtools'
53-
runtimeOnly 'com.mysql:mysql-connector-j'
54-
runtimeOnly 'com.h2database:h2'
55-
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
56-
annotationProcessor 'org.projectlombok:lombok'
57-
testImplementation 'org.springframework.boot:spring-boot-starter-test'
58-
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
59-
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.0'
60-
implementation 'org.springframework.boot:spring-boot-starter-webflux'
61-
implementation 'org.springframework.boot:spring-boot-starter-amqp'
29+
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
30+
implementation 'org.springframework.boot:spring-boot-starter-web'
31+
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.4' // Swagger
32+
implementation 'org.springframework.boot:spring-boot-starter-data-neo4j' // Neo4j
33+
implementation 'org.springframework.boot:spring-boot-starter-security' // Spring Security
34+
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' // OAuth2
35+
36+
// JWT
37+
implementation 'io.jsonwebtoken:jjwt-api:0.12.3'
38+
implementation 'io.jsonwebtoken:jjwt-impl:0.12.3'
39+
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3'
40+
41+
// AWS S3
42+
implementation 'com.amazonaws:aws-java-sdk-s3:1.12.300'
43+
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
44+
45+
// Logback
46+
implementation 'org.springframework.boot:spring-boot-starter-logging'
47+
48+
// Apache Commons IO
49+
implementation 'commons-io:commons-io:2.18.0'
50+
51+
// WebFlux
52+
implementation 'org.springframework.boot:spring-boot-starter-webflux'
53+
54+
compileOnly 'org.projectlombok:lombok'
55+
developmentOnly 'org.springframework.boot:spring-boot-devtools'
56+
runtimeOnly 'com.mysql:mysql-connector-j'
57+
runtimeOnly 'com.h2database:h2'
58+
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
59+
annotationProcessor 'org.projectlombok:lombok'
60+
testImplementation 'org.springframework.boot:spring-boot-starter-test'
61+
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
62+
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.0'
63+
implementation 'org.springframework.boot:spring-boot-starter-webflux'
64+
implementation 'org.springframework.boot:spring-boot-starter-amqp'
6265
}
6366

6467
tasks.named('test') {
65-
useJUnitPlatform()
66-
finalizedBy 'jacocoTestReport'
68+
useJUnitPlatform()
69+
finalizedBy 'jacocoTestReport'
6770
}
6871

6972
tasks.named('check') {
70-
dependsOn 'jacocoTestCoverageVerification'
73+
dependsOn 'jacocoTestCoverageVerification'
7174
}
7275

7376
jacoco {
74-
toolVersion = "0.8.11"
77+
toolVersion = "0.8.11"
7578
}
7679

7780
jacocoTestReport {
78-
dependsOn test
79-
reports {
80-
xml.required = true
81-
html.required = true
82-
}
81+
dependsOn test
82+
reports {
83+
xml.required = true
84+
html.required = true
85+
}
8386
}
8487

8588
jacocoTestCoverageVerification {
86-
afterEvaluate {
87-
classDirectories.setFrom(files(classDirectories.files.collect {
88-
fileTree(dir: it, exclude: [
89-
'com/team_nebula/nebula/NebulaApplication.class'
90-
])
91-
}))
92-
}
93-
violationRules {
94-
rule {
95-
enabled = true
96-
element = 'CLASS'
97-
98-
limit {
99-
counter = 'LINE'
100-
value = 'COVEREDRATIO'
101-
minimum = 0.80
102-
}
103-
limit {
104-
counter = 'BRANCH'
105-
value = 'COVEREDRATIO'
106-
minimum = 0.70
107-
}
108-
limit {
109-
counter = 'LINE'
110-
value = 'TOTALCOUNT'
111-
maximum = 200
112-
}
113-
}
114-
}
89+
afterEvaluate {
90+
classDirectories.setFrom(files(classDirectories.files.collect {
91+
fileTree(dir: it, exclude: [
92+
'com/team_nebula/nebula/NebulaApplication.class'
93+
])
94+
}))
95+
}
96+
violationRules {
97+
rule {
98+
enabled = true
99+
element = 'CLASS'
100+
101+
limit {
102+
counter = 'LINE'
103+
value = 'COVEREDRATIO'
104+
minimum = 0.80
105+
}
106+
limit {
107+
counter = 'BRANCH'
108+
value = 'COVEREDRATIO'
109+
minimum = 0.70
110+
}
111+
limit {
112+
counter = 'LINE'
113+
value = 'TOTALCOUNT'
114+
maximum = 200
115+
}
116+
}
117+
}
115118
}
116119

117120
jib {
118-
from {
119-
image = 'openjdk:17-alpine'
120-
platforms {
121-
platform {
122-
architecture = 'amd64'
123-
os = 'linux'
124-
}
125-
}
126-
}
127-
to {
128-
image = 'zmflspa123/nebula:latest'
129-
}
121+
from {
122+
image = 'openjdk:17-alpine'
123+
platforms {
124+
platform {
125+
architecture = 'amd64'
126+
os = 'linux'
127+
}
128+
}
129+
}
130+
to {
131+
image = 'zmflspa123/nebula:latest'
132+
}
130133
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.team_nebula.nebula.domain.chatbot.api;
2+
3+
import java.util.List;
4+
5+
import org.springframework.web.bind.annotation.GetMapping;
6+
import org.springframework.web.bind.annotation.PathVariable;
7+
import org.springframework.web.bind.annotation.PostMapping;
8+
import org.springframework.web.bind.annotation.RequestBody;
9+
import org.springframework.web.bind.annotation.RequestMapping;
10+
import org.springframework.web.bind.annotation.RequestParam;
11+
import org.springframework.web.bind.annotation.RestController;
12+
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
13+
14+
import com.team_nebula.nebula.domain.chatbot.dto.request.ChatRequestDTO;
15+
import com.team_nebula.nebula.domain.chatbot.dto.request.SessionRequestDTO;
16+
import com.team_nebula.nebula.domain.chatbot.dto.response.CharResponseDTO;
17+
import com.team_nebula.nebula.domain.chatbot.dto.response.SessionListResponseDTO;
18+
import com.team_nebula.nebula.domain.chatbot.dto.response.SessionResponseDTO;
19+
import com.team_nebula.nebula.domain.chatbot.service.ChatbotService;
20+
import com.team_nebula.nebula.global.annotation.AuthUser;
21+
import com.team_nebula.nebula.global.apipayload.ApiResponse;
22+
23+
import io.swagger.v3.oas.annotations.Operation;
24+
import io.swagger.v3.oas.annotations.tags.Tag;
25+
import lombok.RequiredArgsConstructor;
26+
27+
@RestController
28+
@RequestMapping("/api/v1/chat")
29+
@RequiredArgsConstructor
30+
@Tag(name = "[챗봇]")
31+
public class ChatbotController {
32+
33+
private final ChatbotService chatbotService;
34+
35+
@Operation(summary = "새 채팅 세션 생성", description = "새로운 채팅 세션을 생성하는 API")
36+
@PostMapping("/sessions")
37+
public ApiResponse<SessionResponseDTO> createSession(
38+
@AuthUser Long userId,
39+
@RequestBody SessionRequestDTO request) {
40+
return ApiResponse.onSuccess(chatbotService.createSession(userId, request));
41+
}
42+
43+
@Operation(summary = "사용자 채팅 세션 목록 조회", description = "사용자 채팅 세션 목록을 조회하는 API")
44+
@GetMapping("/sessions")
45+
public ApiResponse<List<SessionListResponseDTO>> getSessions(
46+
@AuthUser Long userId,
47+
@RequestParam(name = "limit", defaultValue = "20") int limit,
48+
@RequestParam(name = "offset", defaultValue = "0") int offset) {
49+
return ApiResponse.onSuccess(chatbotService.getSessions(userId, limit, offset));
50+
}
51+
52+
@Operation(summary = "채팅 스트림", description = "채팅 세션에서 메시지를 스트리밍하는 API")
53+
@PostMapping("/stream")
54+
public SseEmitter chatStream(
55+
@RequestParam Long userId,
56+
@RequestBody ChatRequestDTO request) {
57+
return chatbotService.chatStream(userId, request);
58+
}
59+
60+
@Operation(summary = "채팅 세션 메시지 조회", description = "채팅 세션의 메시지를 조회하는 API")
61+
@GetMapping("/sessions/{sessionId}/messages")
62+
public ApiResponse<List<CharResponseDTO>> getSessionMessages(
63+
@AuthUser Long userId,
64+
@PathVariable String sessionId) {
65+
return ApiResponse.onSuccess(chatbotService.getSessionMessages(userId, sessionId));
66+
}
67+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.team_nebula.nebula.domain.chatbot.dto.request;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
8+
@Getter
9+
@Builder
10+
@NoArgsConstructor
11+
@AllArgsConstructor
12+
public class ChatRequestDTO {
13+
private String message;
14+
private String sessionId;
15+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.team_nebula.nebula.domain.chatbot.dto.request;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
8+
@Getter
9+
@Builder
10+
@NoArgsConstructor
11+
@AllArgsConstructor
12+
public class SessionRequestDTO {
13+
private String title;
14+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.team_nebula.nebula.domain.chatbot.dto.response;
2+
3+
import java.util.List;
4+
5+
import lombok.AllArgsConstructor;
6+
import lombok.Builder;
7+
import lombok.Getter;
8+
import lombok.NoArgsConstructor;
9+
10+
@Getter
11+
@Builder
12+
@NoArgsConstructor
13+
@AllArgsConstructor
14+
public class CharResponseDTO {
15+
private String sessionId;
16+
private List<MessageResponseDTO> messages;
17+
18+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.team_nebula.nebula.domain.chatbot.dto.response;
2+
3+
import java.util.Map;
4+
5+
import lombok.AllArgsConstructor;
6+
import lombok.Builder;
7+
import lombok.Getter;
8+
import lombok.NoArgsConstructor;
9+
10+
@Getter
11+
@Builder
12+
@NoArgsConstructor
13+
@AllArgsConstructor
14+
public class MessageResponseDTO {
15+
private String id;
16+
private String content;
17+
private String role;
18+
private String createdAt;
19+
private Map<String, String> metadata;
20+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.team_nebula.nebula.domain.chatbot.dto.response;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
8+
@Getter
9+
@Builder
10+
@NoArgsConstructor
11+
@AllArgsConstructor
12+
public class SessionListResponseDTO {
13+
private String sessionId;
14+
private String title;
15+
private String sessionType;
16+
private String createdAt;
17+
private String updatedAt;
18+
private boolean isActive;
19+
}

0 commit comments

Comments
 (0)