diff --git a/dds-backoffice/src/main/java/com/cloudhandson/ddsbackoffice/web/DdsVectorKnowledgeController.java b/dds-backoffice/src/main/java/com/cloudhandson/ddsbackoffice/web/DdsVectorKnowledgeController.java index 22dcb3b..0ef1176 100644 --- a/dds-backoffice/src/main/java/com/cloudhandson/ddsbackoffice/web/DdsVectorKnowledgeController.java +++ b/dds-backoffice/src/main/java/com/cloudhandson/ddsbackoffice/web/DdsVectorKnowledgeController.java @@ -12,6 +12,8 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.mvc.support.RedirectAttributes; +import java.util.UUID; + @Controller public class DdsVectorKnowledgeController { @@ -34,28 +36,38 @@ public class DdsVectorKnowledgeController { @PostMapping("/vector-knowledge/ingest") public String ingest( - @RequestParam String documentId, + @RequestParam(required = false, defaultValue = "") String documentId, @RequestParam String title, @RequestParam(required = false, defaultValue = "") String sourceUri, @RequestParam String content, @RequestParam String techTags, @RequestParam(defaultValue = "600") int chunkSize, @RequestParam(defaultValue = "DEMO") String embeddingMode, + @RequestParam(defaultValue = "false") boolean replaceExisting, RedirectAttributes redirectAttributes ) { try { var result = service.ingest(new VectorIngestCommand( - documentId, title, sourceUri, content, techTags, chunkSize, embeddingMode)); + resolveDocumentId(documentId, replaceExisting), title, sourceUri, content, techTags, chunkSize, embeddingMode)); redirectAttributes.addFlashAttribute("ingestResult", result); redirectAttributes.addFlashAttribute("message", - result.chunkCount() + "개 청크를 저장했습니다. DDS 권한 평가용 태그 " - + result.tagCount() + "개가 연결되었습니다."); + "지식자료를 등록하고 검색 준비를 완료했습니다."); } catch (Exception exception) { redirectAttributes.addFlashAttribute("errorMessage", exception.getMessage()); } return "redirect:/vector-knowledge"; } + private String resolveDocumentId(String documentId, boolean replaceExisting) { + if (documentId == null || documentId.isBlank()) { + return "knowledge-" + UUID.randomUUID(); + } + if (!replaceExisting) { + throw new AppException("기존 자료를 교체하려면 교체 확인을 선택하세요."); + } + return documentId.trim(); + } + @PostMapping("/vector-knowledge/search") public String search( @RequestParam String userKey, diff --git a/dds-backoffice/src/main/resources/static/css/dds.css b/dds-backoffice/src/main/resources/static/css/dds.css index a101783..d430e2a 100644 --- a/dds-backoffice/src/main/resources/static/css/dds.css +++ b/dds-backoffice/src/main/resources/static/css/dds.css @@ -75,6 +75,23 @@ body { margin: 0; background: var(--dds-bg); color: var(--dds-ink); font-family: .page-title .context-summary { font-size: .95rem; max-width: 700px; } .explanation-details > summary { color: #43516a; font-size: .86rem; } .explanation-details > summary::marker { color: var(--dds-accent); } +.registration-advanced-options { background: #f8faff; border: 1px solid var(--dds-line); border-radius: 8px; padding: .85rem 1rem; } +.registration-advanced-options > summary { color: #43516a; cursor: pointer; font-size: .875rem; font-weight: 700; } +.registration-advanced-options > p { color: var(--dds-muted); font-size: .82rem; margin: .7rem 0; } +.registration-advanced-grid { align-items: end; } +.registration-checkbox { align-items: center; color: var(--dds-ink) !important; display: flex; gap: .5rem; } +.registration-checkbox input { height: 1rem; width: 1rem; } +.registration-actions { border-top: 1px solid var(--dds-line); padding-top: 1rem; } +.registration-actions .btn { min-width: 7rem; } +.registration-result { align-items: flex-start; background: #f3fbf7; border: 1px solid #bfe0cf; border-radius: 9px; display: flex; gap: 1.5rem; justify-content: space-between; padding: 1rem; } +.registration-result strong, .registration-result span { display: block; } +.registration-result span { color: var(--dds-muted); font-size: .86rem; margin-top: .25rem; } +.registration-result dl { display: flex; flex-wrap: wrap; gap: 1.2rem; margin: 0; } +.registration-result dl div { min-width: 7rem; } +.registration-result dt { color: var(--dds-muted); font-size: .72rem; font-weight: 700; } +.registration-result dd { font-size: .9rem; font-weight: 700; margin: .2rem 0 0; } +.advanced-workflow > details > summary { color: var(--dds-ink); cursor: pointer; font-size: 1rem; font-weight: 700; } +.advanced-workflow .section-subtitle { margin: .4rem 0 0; } .protection-summary { border-top: 3px solid #3157d5; } .protection-status-card { background: #fbfcff; border: 1px solid #dbe3f4; border-radius: 14px; margin-top: 1rem; padding: 1.25rem; } .protection-status-card.status-danger { background: #fffafa; border-color: #f2c8c8; } @@ -112,5 +129,6 @@ body { margin: 0; background: var(--dds-bg); color: var(--dds-ink); font-family: .panel { padding: 19px; } .hero { padding-top: 30px; } .protection-status-topline, .protection-status-footer { align-items: flex-start; flex-direction: column; } + .registration-result { flex-direction: column; } .dds-product-nav { border-radius: 8px; } } diff --git a/dds-backoffice/src/main/resources/templates/fragments/layout.html b/dds-backoffice/src/main/resources/templates/fragments/layout.html index cc6ac4c..d2341ab 100644 --- a/dds-backoffice/src/main/resources/templates/fragments/layout.html +++ b/dds-backoffice/src/main/resources/templates/fragments/layout.html @@ -45,7 +45,7 @@