99 lines
4.3 KiB
Java
99 lines
4.3 KiB
Java
package com.zioinfo.esn.rag;
|
|
|
|
import com.zioinfo.esn.common.ApiResponse;
|
|
import lombok.RequiredArgsConstructor;
|
|
import org.springframework.security.core.Authentication;
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* ESN 최신 AI 기법(중앙 guardia-rag) 배선 API — 별개 레이어, 테넌트 격리.
|
|
*
|
|
* <p>기존 {@code /api/ai/*}(AiController) 는 <b>불변</b>. 본 컨트롤러는 중앙 경유 신규 경로만 추가한다.
|
|
*
|
|
* <ul>
|
|
* <li>POST /api/ai/rag/analyze-alarm — ESL/HCore 알람 원인분석·우선순위(/agent+graph+structured)</li>
|
|
* <li>POST /api/ai/rag/classify-pos — POS 데이터 자동 분류(/structured, 임계 미만 needsReview)</li>
|
|
* <li>POST /api/ai/rag/feedback — 👍/👎 피드백(/rag/feedback, solution=esn 격리)</li>
|
|
* <li>GET /api/ai/rag/toggles — 현재 테넌트 기법 토글 스냅샷</li>
|
|
* <li>PUT /api/ai/rag/toggles/{key} — 테넌트 토글 변경(MANAGER+, esn_rag_setting 격리)</li>
|
|
* </ul>
|
|
*
|
|
* <p>테넌트는 {@code X-Tenant-Code} 헤더 또는 {@code tenantCode} 바디 필드로 식별(없으면 기본).
|
|
*/
|
|
@RestController
|
|
@RequestMapping("/api/ai/rag")
|
|
@RequiredArgsConstructor
|
|
public class RagController {
|
|
|
|
private final RagAiService service;
|
|
|
|
@PostMapping("/analyze-alarm")
|
|
public ApiResponse<Map<String, Object>> analyzeAlarm(
|
|
@RequestBody Map<String, Object> req,
|
|
@RequestHeader(value = "X-Tenant-Code", required = false) String tenantHeader,
|
|
Authentication auth) {
|
|
return ApiResponse.ok(service.analyzeAlarm(tenant(tenantHeader, req), req, name(auth)));
|
|
}
|
|
|
|
@PostMapping("/classify-pos")
|
|
public ApiResponse<Map<String, Object>> classifyPos(
|
|
@RequestBody Map<String, Object> req,
|
|
@RequestHeader(value = "X-Tenant-Code", required = false) String tenantHeader,
|
|
Authentication auth) {
|
|
return ApiResponse.ok(service.classifyPos(tenant(tenantHeader, req), req, name(auth)));
|
|
}
|
|
|
|
@PostMapping("/feedback")
|
|
public ApiResponse<Map<String, Object>> feedback(
|
|
@RequestBody Map<String, Object> req,
|
|
@RequestHeader(value = "X-Tenant-Code", required = false) String tenantHeader,
|
|
Authentication auth) {
|
|
return ApiResponse.ok(service.feedback(tenant(tenantHeader, req), req, name(auth)));
|
|
}
|
|
|
|
@GetMapping("/toggles")
|
|
public ApiResponse<Map<String, Object>> toggles(
|
|
@RequestHeader(value = "X-Tenant-Code", required = false) String tenantHeader,
|
|
@RequestParam(value = "tenantCode", required = false) String tenantParam) {
|
|
String tc = tenantHeader != null ? tenantHeader : tenantParam;
|
|
return ApiResponse.ok(service.currentToggles(tc));
|
|
}
|
|
|
|
/**
|
|
* 토글 변경 — rag.* 화이트리스트 키만, 테넌트 격리. RBAC(MANAGER+)은 SecurityConfig 통제.
|
|
*/
|
|
@PutMapping("/toggles/{key}")
|
|
public ApiResponse<Map<String, Object>> updateToggle(
|
|
@PathVariable String key,
|
|
@RequestBody Map<String, Object> req,
|
|
@RequestHeader(value = "X-Tenant-Code", required = false) String tenantHeader) {
|
|
if (key == null || !key.startsWith("rag.")) {
|
|
return ApiResponse.fail("ERR-RAG-400: rag.* 키만 변경 가능");
|
|
}
|
|
String tc = tenantHeader != null ? tenantHeader : str(req.get("tenantCode"));
|
|
Object v = req.get("value");
|
|
try {
|
|
return ApiResponse.ok(service.updateToggle(tc, key, v == null ? "" : String.valueOf(v)));
|
|
} catch (IllegalArgumentException e) {
|
|
return ApiResponse.fail(e.getMessage());
|
|
}
|
|
}
|
|
|
|
// ── helpers ──────────────────────────────────────────────────────────────
|
|
private String tenant(String header, Map<String, Object> req) {
|
|
if (header != null && !header.isBlank()) return header;
|
|
Object t = req == null ? null : req.get("tenantCode");
|
|
return t == null ? null : String.valueOf(t);
|
|
}
|
|
|
|
private String name(Authentication auth) {
|
|
return auth == null ? "anon" : auth.getName();
|
|
}
|
|
|
|
private String str(Object o) {
|
|
return o == null ? null : String.valueOf(o);
|
|
}
|
|
}
|