Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
cb34db7
refactor[keyword]: 키워드 리팩토링 및 프롬프트 수정
yongho9064 Oct 3, 2025
3230d9c
feat[OAuth]: 소셜로그인 콜백 로직 수정
asowjdan Oct 7, 2025
bb648f3
test[OAuth]: 소셜로그인 콜백 로직 수정으로 인한 테스트 코드 수정
asowjdan Oct 7, 2025
e045223
chore[infra]: 테스트.env 파일 변경
DooHyoJeong Oct 8, 2025
85a6424
chore[infra]: 이메일 변수 수정
DooHyoJeong Oct 8, 2025
2b87b55
Merge pull request #227 from prgrms-web-devcourse-final-project/ref/k…
DooHyoJeong Oct 8, 2025
c74dc3b
Merge branch 'develop' into feat/oauth
asowjdan Oct 8, 2025
82f0aaf
Merge pull request #230 from prgrms-web-devcourse-final-project/chore…
DooHyoJeong Oct 8, 2025
97d318c
Merge branch 'develop' into feat/oauth
asowjdan Oct 8, 2025
a0d4ca9
chore[infra]: database url 수정
DooHyoJeong Oct 8, 2025
f62cfa0
chore[infra]: dialect 추가
DooHyoJeong Oct 8, 2025
95d9714
Merge pull request #231 from prgrms-web-devcourse-final-project/chore…
DooHyoJeong Oct 8, 2025
6bb4cb7
chore[infra]: active 변경
DooHyoJeong Oct 8, 2025
8bf1efe
Merge pull request #232 from prgrms-web-devcourse-final-project/chore…
DooHyoJeong Oct 8, 2025
18756d5
chore[infra]: test수정
DooHyoJeong Oct 8, 2025
e2b232e
Merge pull request #233 from prgrms-web-devcourse-final-project/chore…
DooHyoJeong Oct 8, 2025
0d3db46
chore[infra]: test수정
DooHyoJeong Oct 8, 2025
c8a0dd2
Merge pull request #234 from prgrms-web-devcourse-final-project/chore…
DooHyoJeong Oct 8, 2025
347cc37
chore[infra]: test수정
DooHyoJeong Oct 8, 2025
e881969
Merge pull request #235 from prgrms-web-devcourse-final-project/chore…
DooHyoJeong Oct 8, 2025
b86d4d5
chore[infra]: test수정
DooHyoJeong Oct 8, 2025
3c6185f
Merge pull request #236 from prgrms-web-devcourse-final-project/chore…
DooHyoJeong Oct 8, 2025
192fcb4
feat[post]:페이징추가
GarakChoi Oct 8, 2025
bb72226
feat[post]:페이징추가
GarakChoi Oct 8, 2025
b9987b3
feat[post]:페이징추가
GarakChoi Oct 8, 2025
e82aa00
feat[post]:페ìautoípost…Œ스트 수정
GarakChoi Oct 8, 2025
2c98095
fix[ã…post]:포스트테스트수정
GarakChoi Oct 8, 2025
e685d0a
fix[post]:api name
GarakChoi Oct 8, 2025
c70179f
fix[post]:api name
GarakChoi Oct 8, 2025
ad30807
Merge pull request #237 from prgrms-web-devcourse-final-project/feat/…
DooHyoJeong Oct 8, 2025
8b1229c
Merge branch 'refs/heads/develop' into feat/oauth
asowjdan Oct 8, 2025
86297b8
feat[OAuth]: 백엔드 소셜 로그인 테스트를 위한 코드 작성 및 리팩토링
asowjdan Oct 8, 2025
c670b5b
test[OAuth]: 백엔드 소셜 로그인 및 jwt 토큰 생성 로직 수정으로 인한 테스트 코드 수정
asowjdan Oct 8, 2025
c777318
Merge pull request #229 from asowjdan/feat/oauth
DooHyoJeong Oct 9, 2025
b66ac35
feat[law]: 리펙토링
Nohheechul Oct 9, 2025
1c4f211
feat[law]: 법령 용어 검색 기능 Version 2 구현
Nohheechul Oct 9, 2025
5f77afb
Merge pull request #240 from prgrms-web-devcourse-final-project/feat/…
DooHyoJeong Oct 9, 2025
63842fd
refactor[chat]: 채팅 응답 형식 변경
yongho9064 Oct 9, 2025
6b01ce9
Merge pull request #241 from prgrms-web-devcourse-final-project/ref/chat
yongho9064 Oct 9, 2025
9647cce
chore[infra]: main, relese, develop 브랜치 별 분기
DooHyoJeong Oct 9, 2025
60e36a1
Merge pull request #242 from prgrms-web-devcourse-final-project/chore…
DooHyoJeong Oct 9, 2025
5078e19
chore[infra]: action 권한 추가
DooHyoJeong Oct 9, 2025
0cc98d4
Merge pull request #243 from prgrms-web-devcourse-final-project/chore…
DooHyoJeong Oct 9, 2025
b9aa6c0
Merge pull request #244 from prgrms-web-devcourse-final-project/develop
DooHyoJeong Oct 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
384 changes: 312 additions & 72 deletions .github/workflows/CI-CD_Pipeline.yml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ dependencies {
// Testing (테스트)
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'org.mockito:mockito-inline:5.2.0'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation("it.ozimov:embedded-redis:0.7.3") {
exclude group: "org.slf4j", module: "slf4j-simple"
Expand Down

This file was deleted.

44 changes: 28 additions & 16 deletions backend/src/main/java/com/ai/lawyer/domain/chatbot/dto/ChatDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
import org.springframework.ai.document.Document;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Schema(description = "채팅 관련 DTO")
public class ChatDto {
Expand Down Expand Up @@ -42,11 +40,10 @@ public static class ChatResponse {
@Schema(description = "AI 챗봇의 응답 메시지", example = "네, 관련 법령과 판례를 바탕으로 답변해 드리겠습니다.")
private String message;

@Schema(description = "응답 생성에 참고한 유사 판례 정보 목록")
private List<Document> similarCases;
private ChatPrecedentDto precedent;

private ChatLawDto law;

@Schema(description = "응답 생성에 참고한 유사 법령 정보 목록")
private List<Document> similarLaws;
}

@Getter
Expand All @@ -73,6 +70,14 @@ public static ChatPrecedentDto from(ChatPrecedent cp) {
.caseName(cp.getCaseName())
.build();
}

public static ChatPrecedentDto from(Document doc) {
return ChatPrecedentDto.builder()
.precedentContent(doc.getText())
.caseNumber(doc.getMetadata().get("caseNumber").toString())
.caseName(doc.getMetadata().get("caseName").toString())
.build();
}
}

@Data
Expand All @@ -93,6 +98,13 @@ public static ChatLawDto from(ChatLaw cl) {
.lawName(cl.getLawName())
.build();
}

public static ChatLawDto from(Document doc) {
return ChatLawDto.builder()
.content(doc.getText())
.lawName(doc.getMetadata().get("lawName").toString())
.build();
}
}

@Getter
Expand All @@ -109,31 +121,31 @@ public static class ChatHistoryDto {
@Schema(description = "메시지 내용", example = "안녕하세요~~")
private String message;

private List<ChatPrecedentDto> precedents;
private ChatPrecedentDto precedent;

private List<ChatLawDto> laws;
private ChatLawDto law;

@Schema(description = "생성 시간")
private LocalDateTime createdAt;

public static ChatHistoryDto from(Chat chat) {

List<ChatPrecedentDto> precedentDtos = new ArrayList<>();
for (ChatPrecedent cp : chat.getChatPrecedents()) {
precedentDtos.add(ChatPrecedentDto.from(cp));
ChatPrecedentDto precedentDto = null;
if (chat.getChatPrecedents() != null && !chat.getChatPrecedents().isEmpty()) {
precedentDto = ChatPrecedentDto.from(chat.getChatPrecedents().get(0));
}

List<ChatLawDto> lawDtos = new ArrayList<>();
for (ChatLaw cl : chat.getChatLaws()) {
lawDtos.add(ChatLawDto.from(cl));
ChatLawDto lawDto = null;
if (chat.getChatLaws() != null && !chat.getChatLaws().isEmpty()) {
lawDto = ChatLawDto.from(chat.getChatLaws().get(0));
}

return ChatHistoryDto.builder()
.type(chat.getType().toString())
.message(chat.getMessage())
.createdAt(chat.getCreatedAt())
.precedents(precedentDtos)
.laws(lawDtos)
.precedent(precedentDto)
.law(lawDto)
.build();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.ai.lawyer.domain.chatbot.service;

import com.ai.lawyer.domain.chatbot.dto.ChatDto.ChatLawDto;
import com.ai.lawyer.domain.chatbot.dto.ChatDto.ChatPrecedentDto;
import com.ai.lawyer.domain.chatbot.dto.ChatDto.ChatRequest;
import com.ai.lawyer.domain.chatbot.dto.ChatDto.ChatResponse;
import com.ai.lawyer.domain.chatbot.dto.ExtractionDto.KeywordExtractionDto;
Expand Down Expand Up @@ -37,6 +39,7 @@ public class ChatBotService {

private final QdrantService qdrantService;
private final HistoryService historyService;
private final KeywordService keywordService;

private final ChatRepository chatRepository;
private final HistoryRepository historyRepository;
Expand Down Expand Up @@ -91,21 +94,26 @@ public Flux<ChatResponse> sendMessage(Long memberId, ChatRequest chatChatRequest
.onErrorResume(throwable -> Flux.just(handleError(history))); // 에러 발생 시 에러 핸들링 -> 재전송 유도
}

// 키워드 추출 메서드
public <T> T keywordExtract(String content, String promptTemplate, Class<T> classType) {
String prompt = promptTemplate + content;
return chatClient.prompt(new Prompt(new UserMessage(prompt)))
.call()
.entity(classType);
}

private ChatResponse ChatResponse(History history, String fullResponse, List<Document> cases, List<Document> laws) {

ChatPrecedentDto precedentDto = null;
if (cases != null && !cases.isEmpty()) {
Document firstCase = cases.get(0);
precedentDto = ChatPrecedentDto.from(firstCase);
}

ChatLawDto lawDto = null;
if (laws != null && !laws.isEmpty()) {
Document firstLaw = laws.get(0);
lawDto = ChatLawDto.from(firstLaw);
}

return ChatResponse.builder()
.roomId(history.getHistoryId())
.title(history.getTitle())
.message(fullResponse)
.similarCases(cases)
.similarLaws(laws)
.precedent(precedentDto)
.law(lawDto)
.build();
}

Expand Down Expand Up @@ -161,7 +169,7 @@ private void handlerTasks(ChatRequest chatDto, History history, String fullRespo
}

private void extractAndUpdateKeywordRanks(String message) {
KeywordExtractionDto keywordResponse = keywordExtract(message, keywordExtraction, KeywordExtractionDto.class);
KeywordExtractionDto keywordResponse = keywordService.keywordExtract(message, keywordExtraction, KeywordExtractionDto.class);

KeywordRank keywordRank = keywordRankRepository.findByKeyword(keywordResponse.getKeyword());

Expand All @@ -180,7 +188,7 @@ private void extractAndUpdateKeywordRanks(String message) {

private void setHistoryTitle(ChatRequest chatDto, History history, String fullResponse) {
String targetText = fullResponse.contains("해당 질문은 법률") ? chatDto.getMessage() : fullResponse;
TitleExtractionDto titleDto = keywordExtract(targetText, titleExtraction, TitleExtractionDto.class);
TitleExtractionDto titleDto = keywordService.keywordExtract(targetText, titleExtraction, TitleExtractionDto.class);
history.setTitle(titleDto.getTitle());
historyRepository.save(history);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import com.ai.lawyer.domain.chatbot.entity.KeywordRank;
import com.ai.lawyer.domain.chatbot.repository.KeywordRankRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.stereotype.Service;

import java.util.List;
Expand All @@ -11,10 +14,20 @@
@RequiredArgsConstructor
public class KeywordService {

private final ChatClient chatClient;

private final KeywordRankRepository keywordRepository;

public List<KeywordRank> getTop5KeywordRanks() {
return keywordRepository.findTop5ByOrderByScoreDesc();
}

// 키워드 추출 메서드
public <T> T keywordExtract(String content, String promptTemplate, Class<T> classType) {
String prompt = promptTemplate + content;
return chatClient.prompt(new Prompt(new UserMessage(prompt)))
.call()
.entity(classType);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.IOException;
Expand All @@ -36,8 +37,8 @@ public class LawService {
private final JoRepository joRepository;
private final HangRepository hangRepository;
private final HoRepository hoRepository;
private final RestTemplate restTemplate = new RestTemplate();
private final ObjectMapper objectMapper = new ObjectMapper();
private final WebClient webClient = WebClient.builder().build();

// 상수 정의
private static final String BASE_URL = "http://www.law.go.kr/DRF";
Expand Down Expand Up @@ -149,7 +150,11 @@ private String getLawSearchResponse(String query) {
.build()
.toUriString();

return restTemplate.getForObject(url, String.class);
return webClient.get()
.uri(url)
.retrieve()
.bodyToMono(String.class)
.block();
}

/**
Expand All @@ -167,7 +172,11 @@ private String getLawDetailResponse(String lawId) {
.build()
.toUriString();

return restTemplate.getForObject(url, String.class);
return webClient.get()
.uri(url)
.retrieve()
.bodyToMono(String.class)
.block();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,25 @@ public class LawWordController {

private final LawWordService lawWordService;

@GetMapping("/{word}")
@Operation(summary = "법령 용어 검색", description = "법령 용어에 대한 정의를 반환합니다. \n" +
"예시: /api/law-word/선박")
public ResponseEntity<?> getPrecedent(@PathVariable String word) {
@GetMapping("/v1/{word}")
@Operation(summary = "법령 용어 검색 version 1", description = "법령 용어에 대한 정의를 반환합니다. \n" +
"예시: /api/law-word/승소")
public ResponseEntity<?> getPrecedentV1(@PathVariable String word) {
try {
return ResponseEntity.ok(lawWordService.findDefinition(word));
}catch (Exception e){
return ResponseEntity.badRequest().body(e.getMessage());
}
}

@GetMapping("/v2/{word}")
@Operation(summary = "법령 용어 검색 version 2", description = "법령 용어에 대한 정의를 반환합니다. \n" +
"예시: /api/law-word/승소")
public ResponseEntity<?> getPrecedentV2(@PathVariable String word) {
try {
return ResponseEntity.ok(lawWordService.findDefinitionV2(word));
}catch (Exception e){
return ResponseEntity.badRequest().body(e.getMessage());
}
}
}
Loading
Loading