diff --git a/go/internal/api/server.go b/go/internal/api/server.go index fda375f..af2a78e 100644 --- a/go/internal/api/server.go +++ b/go/internal/api/server.go @@ -31,9 +31,10 @@ func normalizeEntity(entity string) string { if strings.HasPrefix(entity, "n_") { entity = entity[2:] } - // 过滤特殊字符 + // 保留中文、字母、数字、下划线、连字符,过滤其他特殊字符 clean := strings.Map(func(r rune) rune { - if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' || r == '-' { + if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' || r == '-' || + (r >= 0x4e00 && r <= 0x9fa5) { // 中文 Unicode 范围 return r } return '_' @@ -278,6 +279,7 @@ func NewServer() http.Handler { // 后续在 catch-all 中处理(见文件末尾) mux.HandleFunc("/health", routes.HandleHealth) + mux.HandleFunc("/api/v1/health", routes.HandleHealth) // ─── Prometheus /metrics ───────────────────── mux.Handle("/metrics", metrics.Handler()) @@ -333,6 +335,38 @@ func NewServer() http.Handler { mux.HandleFunc("/api/v1/graph/stats", graphAPI.Stats) mux.HandleFunc("/api/v1/graph/query", graphAPI.Query) mux.HandleFunc("/api/v1/graph/navigate", graphAPI.Navigate) + // 新增:添加关系边(POST JSON body: {"from":"实体A","to":"实体B","relation":"关系类型","namespace":""}) + mux.HandleFunc("/api/v1/graph/edge", func(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + http.Error(w, "POST only", 405) + return + } + var req struct { + From string `json:"from"` + To string `json:"to"` + Relation string `json:"relation"` + Namespace string `json:"namespace"` + } + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, err.Error(), 400) + return + } + if req.From == "" || req.To == "" || req.Relation == "" { + http.Error(w, "from, to, relation required", 400) + return + } + ns := req.Namespace + if ns == "" { + ns = "default" + } + edgeID := fmt.Sprintf("e_%s_%s_%d", req.From, req.Relation, time.Now().UnixNano()) + err := graphStore.AddEdge(edgeID, req.From, req.To, req.Relation, ns, 1.0) + if err != nil { + http.Error(w, err.Error(), 500) + return + } + json.NewEncoder(w).Encode(map[string]interface{}{"status": "ok", "edge_id": edgeID}) + }) // 新增:pagerank + evidence_count mux.HandleFunc("/api/v1/graph/pagerank", func(w http.ResponseWriter, r *http.Request) { rank := graphStore.PageRank(0.85, 20) @@ -478,7 +512,9 @@ func NewServer() http.Handler { // 找 A → B 的直接边 var directPath map[string]interface{} for _, p := range paths { - if strings.TrimPrefix(p["to"].(string), "n_") == normalizeEntity(entityB) { + toNorm := normalizeEntity(p["to"].(string)) + targetNorm := normalizeEntity(entityB) + if toNorm == targetNorm { directPath = p break }