Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 13 additions & 0 deletions backend/sql/drop.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
drop table ho;
drop table hang;
drop table jo;
drop table jang;
drop table law;
drop table precedent;

ALTER TABLE ho AUTO_INCREMENT = 1;
ALTER TABLE hang AUTO_INCREMENT = 1;
ALTER TABLE jo AUTO_INCREMENT = 1;
ALTER TABLE jang AUTO_INCREMENT = 1;
ALTER TABLE law AUTO_INCREMENT = 1;
ALTER TABLE precedent AUTO_INCREMENT = 1;
150,586 changes: 150,586 additions & 0 deletions backend/sql/lawData.sql

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,22 @@
import com.ai.lawyer.domain.law.entity.Law;
import com.ai.lawyer.domain.law.service.LawService;
import com.ai.lawyer.global.dto.PageResponseDto;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@RequestMapping("/law")
@RequestMapping("/api/law")
public class LawController {

private final LawService lawService;

// 법령 리스트 출력
@GetMapping(value = "/list")
public ResponseEntity<?> list(
@RequestParam String query,
@RequestParam int page
) throws Exception {
String lawList = lawService.getLawList(query, page);
return ResponseEntity.ok().body(lawList);
}


@GetMapping(value = "/list/save")
@Operation(summary = "키워드 관련 법령 데이터 저장(벡엔드 전용 API)", description = "벡엔드 데이터 저장용 API입니다")
public ResponseEntity<?> getStatisticsCard(
@RequestParam String query,
@RequestParam int page
Expand All @@ -44,15 +36,8 @@ public ResponseEntity<?> getStatisticsCard(
return ResponseEntity.ok().body("Success");
}

@GetMapping("/{id}")
public ResponseEntity<Law> getFullLaw(@PathVariable Long id) {
Law law = lawService.getLawWithAllChildren(id);

return ResponseEntity.ok(law);
}


@PostMapping("/search")
@Operation(summary = "볍령 목록 검색 기능", description = "조건에 맞는 법령 목록을 가져옵니다")
Copy link

Copilot AI Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are typos in the Korean text. '볍령' should be '법령' in both operation summaries.

Copilot uses AI. Check for mistakes.
public ResponseEntity<PageResponseDto> searchLaws(@RequestBody LawSearchRequestDto searchRequest) {
Page<LawsDto> laws = lawService.searchLaws(searchRequest);
PageResponseDto response = PageResponseDto.builder()
Expand All @@ -64,4 +49,13 @@ public ResponseEntity<PageResponseDto> searchLaws(@RequestBody LawSearchRequestD
.build();
return ResponseEntity.ok(response);
}

@GetMapping("/{id}")
@Operation(summary = "볍령 상세 조회 기능", description = "법령 상세 데이터를 조회합니다 \n" +
Copy link

Copilot AI Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are typos in the Korean text. '볍령' should be '법령' in both operation summaries.

Copilot uses AI. Check for mistakes.
"예시: /api/law/1")
public ResponseEntity<Law> getFullLaw(@PathVariable Long id) {
Law law = lawService.getLawWithAllChildren(id);

return ResponseEntity.ok(law);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.ai.lawyer.domain.law.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Data;

Expand All @@ -8,13 +9,31 @@
@Data
@Builder
public class LawSearchRequestDto {

@Schema(description = "법령명", example = "노동")
private String lawName; // 법령명

@Schema(description = "법령분야", example = "법률")
private String lawField; // 법령분야

@Schema(description = "소관부처", example = "고용노동부")
private String ministry; // 소관부처

@Schema(description = "공포일자 시작", example = "2000-03-25")
private LocalDate promulgationDateStart; // 공포일자 시작

@Schema(description = "공포일자 종료", example = "2025-03-25")
private LocalDate promulgationDateEnd; // 공포일자 종료

@Schema(description = "시행일자 시작", example = "2000-03-25")
private LocalDate enforcementDateStart; // 시행일자 시작

@Schema(description = "시행일자 종료", example = "2025-03-25")
private LocalDate enforcementDateEnd; // 시행일자 종료

@Schema(description = "페이지 번호 (0부터 시작)", example = "0")
private int pageNumber; // 페이지 번호

@Schema(description = "페이지 크기", example = "10")
private int pageSize; // 페이지 크기
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.ai.lawyer.domain.lawWord.controller;

import com.ai.lawyer.domain.lawWord.service.LawWordService;
import com.ai.lawyer.domain.precedent.entity.Precedent;
Copy link

Copilot AI Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This import is unused in the controller. Remove the unused import statement.

Suggested change
import com.ai.lawyer.domain.precedent.entity.Precedent;

Copilot uses AI. Check for mistakes.
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/law-word")
public class LawWordController {

private final LawWordService lawWordService;

@GetMapping("/{word}")
@Operation(summary = "법령 용어 검색", description = "법령 용어에 대한 정의를 반환합니다. \n" +
"예시: /api/law-word/선박")
public ResponseEntity<?> getPrecedent(@PathVariable String word) {
return ResponseEntity.ok(lawWordService.findDefinition(word));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.ai.lawyer.domain.lawWord.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class LawWord {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@JsonIgnore
private Long id;

private String word; // 법률 용어

@Lob
@Column(columnDefinition = "LONGTEXT")
private String definition; // 정의

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.ai.lawyer.domain.lawWord.repository;

import com.ai.lawyer.domain.lawWord.entity.LawWord;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface LawWordRepository extends JpaRepository<LawWord, Long> {
Optional<LawWord> findByWord(String word);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.ai.lawyer.domain.lawWord.service;

import com.ai.lawyer.domain.lawWord.entity.LawWord;
import com.ai.lawyer.domain.lawWord.repository.LawWordRepository;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
@AllArgsConstructor
public class LawWordService {

private final LawWordRepository lawWordRepository;

private final RestTemplate restTemplate = new RestTemplate();
private final ObjectMapper objectMapper = new ObjectMapper();

public String findDefinition(String word) {
// 1) DB 확인
return lawWordRepository.findByWord(word)
.map(LawWord::getDefinition)
.orElseGet(() -> {
try {
String url = "https://www.law.go.kr/DRF/lawService.do?OC=noheechul"
+ "&target=lstrm&type=JSON&query=" + word;
String json = restTemplate.getForObject(url, String.class);
JsonNode rootNode = objectMapper.readTree(json);
// 오류 응답 처리
if (rootNode.has("Law")) {
return rootNode.get("Law").asText();
}
JsonNode serviceNode = rootNode.path("LsTrmService");
JsonNode defNode = serviceNode.path("법령용어정의");
String definition;
if (defNode.isArray() && defNode.size() > 0) {
// definition = defNode.get(0).asText().split("\\.",2)[0].trim();
definition = defNode.get(0).asText().trim();
} else {
// definition = defNode.asText().split("\\.",2)[0].trim();
definition = defNode.asText().trim();
}
LawWord entity = LawWord.builder()
.word(word)
.definition(definition)
.build();
lawWordRepository.save(entity);
return definition;
} catch (Exception e) {
throw new RuntimeException("Failed to fetch definition", e);
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,36 @@
import com.ai.lawyer.domain.precedent.entity.Precedent;
import com.ai.lawyer.domain.precedent.service.PrecedentService;
import com.ai.lawyer.global.dto.PageResponseDto;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@RequestMapping("/precedent")
@RequestMapping("/api/precedent")
public class PrecedentController {

private final PrecedentService precedentService;

@GetMapping(value = "/list/save")
@Operation(summary = "키워드 관련 판례 데이터 저장(벡엔드 전용 API)", description = "벡엔드 데이터 저장용 API입니다")
public ResponseEntity<?> list(
@RequestParam String query
) throws Exception {
return ResponseEntity.ok().body(precedentService.searchAndSaveAll(query));
}

/**
* POST /api/precedent/search
* 키워드로 판례 검색 (판시사항, 판결요지, 판례내용, 사건명에서 검색)
* @param requestDto
* @return id, 사건명, 사건번호, 선고일자 리스트
*/

@PostMapping("/search")
@Operation(summary = "판례 목록 검색 기능", description = "조건에 맞는 판례 목록을 가져옵니다")
public ResponseEntity<PageResponseDto> searchPrecedents(
@RequestBody PrecedentSearchRequestDto requestDto) {

Expand All @@ -44,8 +54,11 @@ public ResponseEntity<PageResponseDto> searchPrecedents(
* 주어진 id로 Precedent 조회
*
* @param id Precedent PK
* @return Precedent 엔티티
*/
@GetMapping("/{id}")
@Operation(summary = "판례 상세 조회 기능", description = "판례 상세 데이터를 조회합니다 \n" +
"예시: /api/precedent/1")
public ResponseEntity<Precedent> getPrecedent(@PathVariable Long id) {
Precedent precedent = precedentService.getPrecedentById(id);
return ResponseEntity.ok(precedent);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package com.ai.lawyer.domain.precedent.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

@Data
public class PrecedentSearchRequestDto {

@Schema(description = "검색 키워드", example = "노동")
private String keyword; // 검색 키워드

@Schema(description = "페이지 번호 (0부터 시작)", example = "0")
private int pageNumber; // 페이지 번호

@Schema(description = "페이지 크기", example = "10")
private int pageSize; // 페이지 크기
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public class Precedent {
@Column(columnDefinition = "TEXT")
private String caseName; // 사건명

@Lob
@Column(columnDefinition = "TEXT")
private String caseNumber; // 사건번호

private LocalDate sentencingDate; // 선고일자
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import com.ai.lawyer.domain.precedent.repository.PrecedentRepository;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.persistence.EntityManager;
import lombok.AllArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
Expand All @@ -29,6 +31,8 @@ public class PrecedentService {
private final String BASE_URL = "http://www.law.go.kr/DRF";
private final String OC = "noheechul"; // 실제 OC로 변경 필요
private final ObjectMapper objectMapper = new ObjectMapper();
private final EntityManager entityManager;



/**
Expand Down Expand Up @@ -159,25 +163,30 @@ public List<Precedent> getPrecedentDetails(List<String> precedentIds) throws Exc
* @param precedents 저장할 Precedent 객체 리스트
* @return 저장된 Precedent 객체 리스트
*/
@Transactional
public List<Precedent> savePrecedents(List<Precedent> precedents) {
List<Precedent> savedPrecedents = new ArrayList<>();

for (Precedent precedent : precedents) {
try {
// 중복 확인 (판례일련번호 기준)
if (!precedentRepository.existsByPrecedentNumber(precedent.getPrecedentNumber())) {
Precedent saved = precedentRepository.save(precedent);
savedPrecedents.add(saved);
System.out.println("Saved precedent: " + precedent.getPrecedentNumber());
} else {
System.out.println("Already exists: " + precedent.getPrecedentNumber());
}
} catch (Exception e) {
System.err.println("Error saving precedent " + precedent.getPrecedentNumber() + ": " + e.getMessage());
}
}
// List<Precedent> savedPrecedents = new ArrayList<>();
//
// for (Precedent precedent : precedents) {
// try {
// // 중복 확인 (판례일련번호 기준)
// if (!precedentRepository.existsByPrecedentNumber(precedent.getPrecedentNumber())) {
// Precedent saved = precedentRepository.save(precedent);
// savedPrecedents.add(saved);
// System.out.println("Saved precedent: " + precedent.getPrecedentNumber());
// } else {
// System.out.println("Already exists: " + precedent.getPrecedentNumber());
// }
// } catch (Exception e) {
// System.err.println("Error saving precedent " + precedent.getPrecedentNumber() + ": " + e.getMessage());
// }
// }

precedentRepository.saveAll(precedents);
precedentRepository.flush(); // 즉시 DB 반영
entityManager.clear(); // 영속성 컨텍스트 정리

return savedPrecedents;
return precedents;
Comment on lines +185 to +189
Copy link

Copilot AI Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new implementation removes duplicate checking that was present in the commented code. This could lead to constraint violations if duplicate precedents are saved. Consider adding duplicate checking logic or ensuring uniqueness at the database level.

Copilot uses AI. Check for mistakes.
}

/**
Expand Down
Loading