diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 878b4b6..c727a65 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -5,7 +5,17 @@ "Bash(curl -X PUT \"http://localhost:3001/api/nodes/test_node_1\" -H \"Content-Type: application/json\" -d \"{\\\\\"label\\\\\":\\\\\"更新后的测试节点\\\\\",\\\\\"x\\\\\":150}\" -s)", "Bash(curl:*)", "Bash(~/go/bin/swag init:*)", - "Bash(go run:*)" + "Bash(go run:*)", + "Bash(go list:*)", + "Bash(cat \"D:\\\\code\\\\knowledge-graph-agent\\\\backend\\\\handlers\"/*.go)", + "Bash(cat \"D:\\\\code\\\\knowledge-graph-agent\\\\backend\\\\neo4j\"/*.go)", + "Bash(mkdir:*)", + "Bash(go build:*)", + "Bash(sed -i 's/models\\\\./model\\\\./g' internal/service/neo4j_service.go)", + "Bash(sed -i 's/models\\\\./model\\\\./g' internal/handler/*.go)", + "Bash(sed -i 's/models\\\\./model\\\\./g' cmd/server/main.go)", + "Bash(go test:*)", + "Bash(swag init:*)" ] } } diff --git a/backend/main.go b/backend/cmd/server/main.go similarity index 84% rename from backend/main.go rename to backend/cmd/server/main.go index d6ebf7d..1624f87 100644 --- a/backend/main.go +++ b/backend/cmd/server/main.go @@ -10,11 +10,11 @@ import ( swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" - "knowledge-graph-backend/config" "knowledge-graph-backend/docs" - "knowledge-graph-backend/handlers" - neo4jClient "knowledge-graph-backend/neo4j" - "knowledge-graph-backend/services" + "knowledge-graph-backend/internal/config" + "knowledge-graph-backend/internal/handler" + neo4jClient "knowledge-graph-backend/internal/repository/neo4j" + "knowledge-graph-backend/internal/service" ) // @title 知识图谱 API @@ -31,15 +31,15 @@ func main() { driver := neo4jClient.NewDriver(cfg.Neo4j) defer driver.Close(context.Background()) - graphService := services.NewNeo4jService(driver) + graphService := service.NewNeo4jService(driver) - graphHandler := handlers.NewGraphHandler(graphService) - searchHandler := handlers.NewSearchHandler(graphService) - nodeHandler := handlers.NewNodeHandler(graphService) + graphHandler := handler.NewGraphHandler(graphService) + searchHandler := handler.NewSearchHandler(graphService) + nodeHandler := handler.NewNodeHandler(graphService) // 增删改处理器 - nodeCRUDHandler := handlers.NewNodeCRUDHandler(graphService) - edgeCRUDHandler := handlers.NewEdgeCRUDHandler(graphService) + nodeCRUDHandler := handler.NewNodeCRUDHandler(graphService) + edgeCRUDHandler := handler.NewEdgeCRUDHandler(graphService) router := gin.Default() diff --git a/backend/docs/docs.go b/backend/docs/docs.go index 216b307..04e0d76 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -32,7 +32,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/models.CreateEdgeRequest" + "$ref": "#/definitions/model.CreateEdgeRequest" } } ], @@ -40,31 +40,31 @@ const docTemplate = `{ "200": { "description": "创建成功", "schema": { - "$ref": "#/definitions/models.EdgeResponse" + "$ref": "#/definitions/model.EdgeResponse" } }, "400": { "description": "请求错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "404": { "description": "源节点或目标节点未找到", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "409": { "description": "边已存在", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -90,19 +90,19 @@ const docTemplate = `{ "200": { "description": "删除成功", "schema": { - "$ref": "#/definitions/models.DeleteResponse" + "$ref": "#/definitions/model.DeleteResponse" } }, "404": { "description": "边未找到", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -119,13 +119,13 @@ const docTemplate = `{ "200": { "description": "成功", "schema": { - "$ref": "#/definitions/models.GraphData" + "$ref": "#/definitions/model.GraphData" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -142,13 +142,13 @@ const docTemplate = `{ "200": { "description": "成功", "schema": { - "$ref": "#/definitions/models.SimpleGraphData" + "$ref": "#/definitions/model.SimpleGraphData" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -172,7 +172,7 @@ const docTemplate = `{ "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -195,7 +195,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/models.CreateNodeRequest" + "$ref": "#/definitions/model.CreateNodeRequest" } } ], @@ -203,25 +203,25 @@ const docTemplate = `{ "200": { "description": "创建成功", "schema": { - "$ref": "#/definitions/models.Node" + "$ref": "#/definitions/model.Node" } }, "400": { "description": "请求错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "409": { "description": "节点已存在", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -247,25 +247,25 @@ const docTemplate = `{ "200": { "description": "成功", "schema": { - "$ref": "#/definitions/models.NodeResponse" + "$ref": "#/definitions/model.NodeResponse" } }, "400": { "description": "请求错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "404": { "description": "节点未找到", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -293,7 +293,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/models.UpdateNodeRequest" + "$ref": "#/definitions/model.UpdateNodeRequest" } } ], @@ -301,25 +301,25 @@ const docTemplate = `{ "200": { "description": "更新成功", "schema": { - "$ref": "#/definitions/models.Node" + "$ref": "#/definitions/model.Node" } }, "400": { "description": "请求错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "404": { "description": "节点未找到", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -343,19 +343,19 @@ const docTemplate = `{ "200": { "description": "删除成功", "schema": { - "$ref": "#/definitions/models.DeleteResponse" + "$ref": "#/definitions/model.DeleteResponse" } }, "404": { "description": "节点未找到", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -381,25 +381,25 @@ const docTemplate = `{ "200": { "description": "成功", "schema": { - "$ref": "#/definitions/models.NeighborResponse" + "$ref": "#/definitions/model.NeighborResponse" } }, "400": { "description": "请求错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "404": { "description": "节点未找到", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -425,19 +425,19 @@ const docTemplate = `{ "200": { "description": "成功", "schema": { - "$ref": "#/definitions/models.SearchResponse" + "$ref": "#/definitions/model.SearchResponse" } }, "400": { "description": "请求错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -445,7 +445,7 @@ const docTemplate = `{ } }, "definitions": { - "models.CreateEdgeRequest": { + "model.CreateEdgeRequest": { "type": "object", "required": [ "id", @@ -455,38 +455,32 @@ const docTemplate = `{ ], "properties": { "id": { - "description": "边的唯一标识", "type": "string", "example": "edge_123" }, "label": { - "description": "边的显示标签", "type": "string", "example": "包含" }, "properties": { - "description": "自定义属性", "type": "object", "additionalProperties": true }, "source": { - "description": "源节点ID", "type": "string", "example": "node_1" }, "target": { - "description": "目标节点ID", "type": "string", "example": "node_2" }, "type": { - "description": "边的类型(可选,默认为关系类型)", "type": "string", "example": "CONTAINS" } } }, - "models.CreateNodeRequest": { + "model.CreateNodeRequest": { "type": "object", "required": [ "id", @@ -495,58 +489,49 @@ const docTemplate = `{ ], "properties": { "id": { - "description": "节点唯一标识", "type": "string", "example": "node_123" }, "label": { - "description": "节点显示标签", "type": "string", "example": "人工智能" }, "properties": { - "description": "自定义属性", "type": "object", "additionalProperties": true }, "type": { - "description": "节点类型", "type": "string", "example": "概念" }, "x": { - "description": "X坐标(可选)", "type": "number", "example": 100 }, "y": { - "description": "Y坐标(可选)", "type": "number", "example": 200 } } }, - "models.DeleteResponse": { + "model.DeleteResponse": { "type": "object", "properties": { "id": { - "description": "被删除的实体ID(可选)", "type": "string", "example": "node_123" }, "message": { - "description": "消息", "type": "string", "example": "节点已成功删除" }, "success": { - "description": "是否成功", "type": "boolean", "example": true } } }, - "models.Edge": { + "model.Edge": { "type": "object", "properties": { "id": { @@ -574,42 +559,36 @@ const docTemplate = `{ } } }, - "models.EdgeResponse": { + "model.EdgeResponse": { "type": "object", "properties": { "id": { - "description": "边的ID", "type": "string", "example": "edge_123" }, "label": { - "description": "边的显示标签", "type": "string", "example": "包含" }, "properties": { - "description": "自定义属性", "type": "object", "additionalProperties": true }, "source": { - "description": "源节点ID", "type": "string", "example": "node_1" }, "target": { - "description": "目标节点ID", "type": "string", "example": "node_2" }, "type": { - "description": "边的类型", "type": "string", "example": "CONTAINS" } } }, - "models.ErrorResponse": { + "model.ErrorResponse": { "type": "object", "properties": { "error": { @@ -620,41 +599,41 @@ const docTemplate = `{ } } }, - "models.GraphData": { + "model.GraphData": { "type": "object", "properties": { "edges": { "type": "array", "items": { - "$ref": "#/definitions/models.Edge" + "$ref": "#/definitions/model.Edge" } }, "nodes": { "type": "array", "items": { - "$ref": "#/definitions/models.Node" + "$ref": "#/definitions/model.Node" } } } }, - "models.NeighborResponse": { + "model.NeighborResponse": { "type": "object", "properties": { "edges": { "type": "array", "items": { - "$ref": "#/definitions/models.Edge" + "$ref": "#/definitions/model.Edge" } }, "nodes": { "type": "array", "items": { - "$ref": "#/definitions/models.Node" + "$ref": "#/definitions/model.Node" } } } }, - "models.Node": { + "model.Node": { "type": "object", "properties": { "id": { @@ -682,42 +661,58 @@ const docTemplate = `{ } } }, - "models.NodeResponse": { + "model.NodeResponse": { "type": "object", "properties": { "id": { - "type": "string", - "example": "1" + "type": "string" }, "label": { - "type": "string", - "example": "人工智能" + "type": "string" + }, + "neighbors": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Node" + } }, "properties": { "type": "object", "additionalProperties": true }, + "style": { + "type": "object", + "additionalProperties": true + }, "type": { - "type": "string", - "example": "概念" + "type": "string" + }, + "x": { + "type": "number" + }, + "y": { + "type": "number" } } }, - "models.SearchResponse": { + "model.SearchResponse": { "type": "object", "properties": { "count": { "type": "integer" }, - "results": { + "nodes": { "type": "array", "items": { - "$ref": "#/definitions/models.Node" + "$ref": "#/definitions/model.Node" } + }, + "query": { + "type": "string" } } }, - "models.SimpleEdge": { + "model.SimpleEdge": { "type": "object", "properties": { "label": { @@ -738,24 +733,24 @@ const docTemplate = `{ } } }, - "models.SimpleGraphData": { + "model.SimpleGraphData": { "type": "object", "properties": { "edges": { "type": "array", "items": { - "$ref": "#/definitions/models.SimpleEdge" + "$ref": "#/definitions/model.SimpleEdge" } }, "nodes": { "type": "array", "items": { - "$ref": "#/definitions/models.SimpleNode" + "$ref": "#/definitions/model.SimpleNode" } } } }, - "models.SimpleNode": { + "model.SimpleNode": { "type": "object", "properties": { "id": { @@ -772,31 +767,26 @@ const docTemplate = `{ } } }, - "models.UpdateNodeRequest": { + "model.UpdateNodeRequest": { "type": "object", "properties": { "label": { - "description": "节点显示标签", "type": "string", "example": "机器学习" }, "properties": { - "description": "自定义属性(更新)", "type": "object", "additionalProperties": true }, "type": { - "description": "节点类型", "type": "string", "example": "工具" }, "x": { - "description": "X坐标", "type": "number", "example": 150 }, "y": { - "description": "Y坐标", "type": "number", "example": 250 } diff --git a/backend/docs/swagger.json b/backend/docs/swagger.json index 4170c45..f9b4682 100644 --- a/backend/docs/swagger.json +++ b/backend/docs/swagger.json @@ -26,7 +26,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/models.CreateEdgeRequest" + "$ref": "#/definitions/model.CreateEdgeRequest" } } ], @@ -34,31 +34,31 @@ "200": { "description": "创建成功", "schema": { - "$ref": "#/definitions/models.EdgeResponse" + "$ref": "#/definitions/model.EdgeResponse" } }, "400": { "description": "请求错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "404": { "description": "源节点或目标节点未找到", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "409": { "description": "边已存在", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -84,19 +84,19 @@ "200": { "description": "删除成功", "schema": { - "$ref": "#/definitions/models.DeleteResponse" + "$ref": "#/definitions/model.DeleteResponse" } }, "404": { "description": "边未找到", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -113,13 +113,13 @@ "200": { "description": "成功", "schema": { - "$ref": "#/definitions/models.GraphData" + "$ref": "#/definitions/model.GraphData" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -136,13 +136,13 @@ "200": { "description": "成功", "schema": { - "$ref": "#/definitions/models.SimpleGraphData" + "$ref": "#/definitions/model.SimpleGraphData" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -166,7 +166,7 @@ "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -189,7 +189,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/models.CreateNodeRequest" + "$ref": "#/definitions/model.CreateNodeRequest" } } ], @@ -197,25 +197,25 @@ "200": { "description": "创建成功", "schema": { - "$ref": "#/definitions/models.Node" + "$ref": "#/definitions/model.Node" } }, "400": { "description": "请求错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "409": { "description": "节点已存在", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -241,25 +241,25 @@ "200": { "description": "成功", "schema": { - "$ref": "#/definitions/models.NodeResponse" + "$ref": "#/definitions/model.NodeResponse" } }, "400": { "description": "请求错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "404": { "description": "节点未找到", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -287,7 +287,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/models.UpdateNodeRequest" + "$ref": "#/definitions/model.UpdateNodeRequest" } } ], @@ -295,25 +295,25 @@ "200": { "description": "更新成功", "schema": { - "$ref": "#/definitions/models.Node" + "$ref": "#/definitions/model.Node" } }, "400": { "description": "请求错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "404": { "description": "节点未找到", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -337,19 +337,19 @@ "200": { "description": "删除成功", "schema": { - "$ref": "#/definitions/models.DeleteResponse" + "$ref": "#/definitions/model.DeleteResponse" } }, "404": { "description": "节点未找到", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -375,25 +375,25 @@ "200": { "description": "成功", "schema": { - "$ref": "#/definitions/models.NeighborResponse" + "$ref": "#/definitions/model.NeighborResponse" } }, "400": { "description": "请求错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "404": { "description": "节点未找到", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -419,19 +419,19 @@ "200": { "description": "成功", "schema": { - "$ref": "#/definitions/models.SearchResponse" + "$ref": "#/definitions/model.SearchResponse" } }, "400": { "description": "请求错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } }, "500": { "description": "内部错误", "schema": { - "$ref": "#/definitions/models.ErrorResponse" + "$ref": "#/definitions/model.ErrorResponse" } } } @@ -439,7 +439,7 @@ } }, "definitions": { - "models.CreateEdgeRequest": { + "model.CreateEdgeRequest": { "type": "object", "required": [ "id", @@ -449,38 +449,32 @@ ], "properties": { "id": { - "description": "边的唯一标识", "type": "string", "example": "edge_123" }, "label": { - "description": "边的显示标签", "type": "string", "example": "包含" }, "properties": { - "description": "自定义属性", "type": "object", "additionalProperties": true }, "source": { - "description": "源节点ID", "type": "string", "example": "node_1" }, "target": { - "description": "目标节点ID", "type": "string", "example": "node_2" }, "type": { - "description": "边的类型(可选,默认为关系类型)", "type": "string", "example": "CONTAINS" } } }, - "models.CreateNodeRequest": { + "model.CreateNodeRequest": { "type": "object", "required": [ "id", @@ -489,58 +483,49 @@ ], "properties": { "id": { - "description": "节点唯一标识", "type": "string", "example": "node_123" }, "label": { - "description": "节点显示标签", "type": "string", "example": "人工智能" }, "properties": { - "description": "自定义属性", "type": "object", "additionalProperties": true }, "type": { - "description": "节点类型", "type": "string", "example": "概念" }, "x": { - "description": "X坐标(可选)", "type": "number", "example": 100 }, "y": { - "description": "Y坐标(可选)", "type": "number", "example": 200 } } }, - "models.DeleteResponse": { + "model.DeleteResponse": { "type": "object", "properties": { "id": { - "description": "被删除的实体ID(可选)", "type": "string", "example": "node_123" }, "message": { - "description": "消息", "type": "string", "example": "节点已成功删除" }, "success": { - "description": "是否成功", "type": "boolean", "example": true } } }, - "models.Edge": { + "model.Edge": { "type": "object", "properties": { "id": { @@ -568,42 +553,36 @@ } } }, - "models.EdgeResponse": { + "model.EdgeResponse": { "type": "object", "properties": { "id": { - "description": "边的ID", "type": "string", "example": "edge_123" }, "label": { - "description": "边的显示标签", "type": "string", "example": "包含" }, "properties": { - "description": "自定义属性", "type": "object", "additionalProperties": true }, "source": { - "description": "源节点ID", "type": "string", "example": "node_1" }, "target": { - "description": "目标节点ID", "type": "string", "example": "node_2" }, "type": { - "description": "边的类型", "type": "string", "example": "CONTAINS" } } }, - "models.ErrorResponse": { + "model.ErrorResponse": { "type": "object", "properties": { "error": { @@ -614,41 +593,41 @@ } } }, - "models.GraphData": { + "model.GraphData": { "type": "object", "properties": { "edges": { "type": "array", "items": { - "$ref": "#/definitions/models.Edge" + "$ref": "#/definitions/model.Edge" } }, "nodes": { "type": "array", "items": { - "$ref": "#/definitions/models.Node" + "$ref": "#/definitions/model.Node" } } } }, - "models.NeighborResponse": { + "model.NeighborResponse": { "type": "object", "properties": { "edges": { "type": "array", "items": { - "$ref": "#/definitions/models.Edge" + "$ref": "#/definitions/model.Edge" } }, "nodes": { "type": "array", "items": { - "$ref": "#/definitions/models.Node" + "$ref": "#/definitions/model.Node" } } } }, - "models.Node": { + "model.Node": { "type": "object", "properties": { "id": { @@ -676,42 +655,58 @@ } } }, - "models.NodeResponse": { + "model.NodeResponse": { "type": "object", "properties": { "id": { - "type": "string", - "example": "1" + "type": "string" }, "label": { - "type": "string", - "example": "人工智能" + "type": "string" + }, + "neighbors": { + "type": "array", + "items": { + "$ref": "#/definitions/model.Node" + } }, "properties": { "type": "object", "additionalProperties": true }, + "style": { + "type": "object", + "additionalProperties": true + }, "type": { - "type": "string", - "example": "概念" + "type": "string" + }, + "x": { + "type": "number" + }, + "y": { + "type": "number" } } }, - "models.SearchResponse": { + "model.SearchResponse": { "type": "object", "properties": { "count": { "type": "integer" }, - "results": { + "nodes": { "type": "array", "items": { - "$ref": "#/definitions/models.Node" + "$ref": "#/definitions/model.Node" } + }, + "query": { + "type": "string" } } }, - "models.SimpleEdge": { + "model.SimpleEdge": { "type": "object", "properties": { "label": { @@ -732,24 +727,24 @@ } } }, - "models.SimpleGraphData": { + "model.SimpleGraphData": { "type": "object", "properties": { "edges": { "type": "array", "items": { - "$ref": "#/definitions/models.SimpleEdge" + "$ref": "#/definitions/model.SimpleEdge" } }, "nodes": { "type": "array", "items": { - "$ref": "#/definitions/models.SimpleNode" + "$ref": "#/definitions/model.SimpleNode" } } } }, - "models.SimpleNode": { + "model.SimpleNode": { "type": "object", "properties": { "id": { @@ -766,31 +761,26 @@ } } }, - "models.UpdateNodeRequest": { + "model.UpdateNodeRequest": { "type": "object", "properties": { "label": { - "description": "节点显示标签", "type": "string", "example": "机器学习" }, "properties": { - "description": "自定义属性(更新)", "type": "object", "additionalProperties": true }, "type": { - "description": "节点类型", "type": "string", "example": "工具" }, "x": { - "description": "X坐标", "type": "number", "example": 150 }, "y": { - "description": "Y坐标", "type": "number", "example": 250 } diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml index 3a2fd90..c26f216 100644 --- a/backend/docs/swagger.yaml +++ b/backend/docs/swagger.yaml @@ -1,29 +1,23 @@ basePath: / definitions: - models.CreateEdgeRequest: + model.CreateEdgeRequest: properties: id: - description: 边的唯一标识 example: edge_123 type: string label: - description: 边的显示标签 example: 包含 type: string properties: additionalProperties: true - description: 自定义属性 type: object source: - description: 源节点ID example: node_1 type: string target: - description: 目标节点ID example: node_2 type: string type: - description: 边的类型(可选,默认为关系类型) example: CONTAINS type: string required: @@ -32,30 +26,24 @@ definitions: - source - target type: object - models.CreateNodeRequest: + model.CreateNodeRequest: properties: id: - description: 节点唯一标识 example: node_123 type: string label: - description: 节点显示标签 example: 人工智能 type: string properties: additionalProperties: true - description: 自定义属性 type: object type: - description: 节点类型 example: 概念 type: string x: - description: X坐标(可选) example: 100 type: number "y": - description: Y坐标(可选) example: 200 type: number required: @@ -63,22 +51,19 @@ definitions: - label - type type: object - models.DeleteResponse: + model.DeleteResponse: properties: id: - description: 被删除的实体ID(可选) example: node_123 type: string message: - description: 消息 example: 节点已成功删除 type: string success: - description: 是否成功 example: true type: boolean type: object - models.Edge: + model.Edge: properties: id: type: string @@ -97,63 +82,57 @@ definitions: type: type: string type: object - models.EdgeResponse: + model.EdgeResponse: properties: id: - description: 边的ID example: edge_123 type: string label: - description: 边的显示标签 example: 包含 type: string properties: additionalProperties: true - description: 自定义属性 type: object source: - description: 源节点ID example: node_1 type: string target: - description: 目标节点ID example: node_2 type: string type: - description: 边的类型 example: CONTAINS type: string type: object - models.ErrorResponse: + model.ErrorResponse: properties: error: type: string message: type: string type: object - models.GraphData: + model.GraphData: properties: edges: items: - $ref: '#/definitions/models.Edge' + $ref: '#/definitions/model.Edge' type: array nodes: items: - $ref: '#/definitions/models.Node' + $ref: '#/definitions/model.Node' type: array type: object - models.NeighborResponse: + model.NeighborResponse: properties: edges: items: - $ref: '#/definitions/models.Edge' + $ref: '#/definitions/model.Edge' type: array nodes: items: - $ref: '#/definitions/models.Node' + $ref: '#/definitions/model.Node' type: array type: object - models.Node: + model.Node: properties: id: type: string @@ -172,31 +151,41 @@ definitions: "y": type: number type: object - models.NodeResponse: + model.NodeResponse: properties: id: - example: "1" type: string label: - example: 人工智能 type: string + neighbors: + items: + $ref: '#/definitions/model.Node' + type: array properties: additionalProperties: true type: object + style: + additionalProperties: true + type: object type: - example: 概念 type: string + x: + type: number + "y": + type: number type: object - models.SearchResponse: + model.SearchResponse: properties: count: type: integer - results: + nodes: items: - $ref: '#/definitions/models.Node' + $ref: '#/definitions/model.Node' type: array + query: + type: string type: object - models.SimpleEdge: + model.SimpleEdge: properties: label: example: 包含 @@ -211,18 +200,18 @@ definitions: example: CONTAINS type: string type: object - models.SimpleGraphData: + model.SimpleGraphData: properties: edges: items: - $ref: '#/definitions/models.SimpleEdge' + $ref: '#/definitions/model.SimpleEdge' type: array nodes: items: - $ref: '#/definitions/models.SimpleNode' + $ref: '#/definitions/model.SimpleNode' type: array type: object - models.SimpleNode: + model.SimpleNode: properties: id: example: "1" @@ -234,26 +223,21 @@ definitions: example: 概念 type: string type: object - models.UpdateNodeRequest: + model.UpdateNodeRequest: properties: label: - description: 节点显示标签 example: 机器学习 type: string properties: additionalProperties: true - description: 自定义属性(更新) type: object type: - description: 节点类型 example: 工具 type: string x: - description: X坐标 example: 150 type: number "y": - description: Y坐标 example: 250 type: number type: object @@ -275,30 +259,30 @@ paths: name: request required: true schema: - $ref: '#/definitions/models.CreateEdgeRequest' + $ref: '#/definitions/model.CreateEdgeRequest' produces: - application/json responses: "200": description: 创建成功 schema: - $ref: '#/definitions/models.EdgeResponse' + $ref: '#/definitions/model.EdgeResponse' "400": description: 请求错误 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' "404": description: 源节点或目标节点未找到 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' "409": description: 边已存在 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' "500": description: 内部错误 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' summary: 创建边 /api/edges/{id}: delete: @@ -315,15 +299,15 @@ paths: "200": description: 删除成功 schema: - $ref: '#/definitions/models.DeleteResponse' + $ref: '#/definitions/model.DeleteResponse' "404": description: 边未找到 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' "500": description: 内部错误 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' summary: 删除边 /api/graph: get: @@ -334,11 +318,11 @@ paths: "200": description: 成功 schema: - $ref: '#/definitions/models.GraphData' + $ref: '#/definitions/model.GraphData' "500": description: 内部错误 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' summary: 获取图数据 /api/graph/simpleJson: get: @@ -349,11 +333,11 @@ paths: "200": description: 成功 schema: - $ref: '#/definitions/models.SimpleGraphData' + $ref: '#/definitions/model.SimpleGraphData' "500": description: 内部错误 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' summary: 获取简化图数据 /api/graph/stats: get: @@ -369,7 +353,7 @@ paths: "500": description: 内部错误 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' summary: 获取图统计信息 /api/nodes: post: @@ -382,26 +366,26 @@ paths: name: request required: true schema: - $ref: '#/definitions/models.CreateNodeRequest' + $ref: '#/definitions/model.CreateNodeRequest' produces: - application/json responses: "200": description: 创建成功 schema: - $ref: '#/definitions/models.Node' + $ref: '#/definitions/model.Node' "400": description: 请求错误 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' "409": description: 节点已存在 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' "500": description: 内部错误 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' summary: 创建节点 /api/nodes/{id}: delete: @@ -418,15 +402,15 @@ paths: "200": description: 删除成功 schema: - $ref: '#/definitions/models.DeleteResponse' + $ref: '#/definitions/model.DeleteResponse' "404": description: 节点未找到 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' "500": description: 内部错误 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' summary: 删除节点 get: description: 根据节点 ID 获取知识图谱中的节点详细信息 @@ -442,19 +426,19 @@ paths: "200": description: 成功 schema: - $ref: '#/definitions/models.NodeResponse' + $ref: '#/definitions/model.NodeResponse' "400": description: 请求错误 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' "404": description: 节点未找到 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' "500": description: 内部错误 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' summary: 获取节点详情 put: consumes: @@ -471,26 +455,26 @@ paths: name: request required: true schema: - $ref: '#/definitions/models.UpdateNodeRequest' + $ref: '#/definitions/model.UpdateNodeRequest' produces: - application/json responses: "200": description: 更新成功 schema: - $ref: '#/definitions/models.Node' + $ref: '#/definitions/model.Node' "400": description: 请求错误 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' "404": description: 节点未找到 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' "500": description: 内部错误 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' summary: 更新节点 /api/nodes/{id}/neighbors: get: @@ -507,19 +491,19 @@ paths: "200": description: 成功 schema: - $ref: '#/definitions/models.NeighborResponse' + $ref: '#/definitions/model.NeighborResponse' "400": description: 请求错误 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' "404": description: 节点未找到 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' "500": description: 内部错误 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' summary: 获取节点邻居 /api/search: get: @@ -536,14 +520,14 @@ paths: "200": description: 成功 schema: - $ref: '#/definitions/models.SearchResponse' + $ref: '#/definitions/model.SearchResponse' "400": description: 请求错误 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' "500": description: 内部错误 schema: - $ref: '#/definitions/models.ErrorResponse' + $ref: '#/definitions/model.ErrorResponse' summary: 搜索节点 swagger: "2.0" diff --git a/backend/config/config.go b/backend/internal/config/config.go similarity index 95% rename from backend/config/config.go rename to backend/internal/config/config.go index 5c07570..febe05a 100644 --- a/backend/config/config.go +++ b/backend/internal/config/config.go @@ -41,8 +41,8 @@ type Config struct { func Load() (*Config, error) { var err error - err = godotenv.Load("../config/.env") - err = godotenv.Load("./config/.env") + err = godotenv.Load("../configs/.env") + err = godotenv.Load("./configs/.env") if err != nil { return nil, fmt.Errorf("error loading .env file: %w", err) } diff --git a/backend/handlers/crud_handler.go b/backend/internal/handler/crud_handler.go similarity index 56% rename from backend/handlers/crud_handler.go rename to backend/internal/handler/crud_handler.go index 54fa0bc..740c189 100644 --- a/backend/handlers/crud_handler.go +++ b/backend/internal/handler/crud_handler.go @@ -1,34 +1,34 @@ -package handlers +package handler import ( "net/http" "github.com/gin-gonic/gin" - "knowledge-graph-backend/models" - "knowledge-graph-backend/services" + "knowledge-graph-backend/internal/model" + "knowledge-graph-backend/internal/service" ) // NodeCRUDHandler 节点的增删改处理器 type NodeCRUDHandler struct { - service services.GraphService + service service.GraphService } // NewNodeCRUDHandler 创建节点CRUD处理器 -func NewNodeCRUDHandler(service services.GraphService) *NodeCRUDHandler { +func NewNodeCRUDHandler(svc service.GraphService) *NodeCRUDHandler { return &NodeCRUDHandler{ - service: service, + service: svc, } } // EdgeCRUDHandler 边的增删改处理器 type EdgeCRUDHandler struct { - service services.GraphService + service service.GraphService } // NewEdgeCRUDHandler 创建边CRUD处理器 -func NewEdgeCRUDHandler(service services.GraphService) *EdgeCRUDHandler { +func NewEdgeCRUDHandler(svc service.GraphService) *EdgeCRUDHandler { return &EdgeCRUDHandler{ - service: service, + service: svc, } } @@ -37,16 +37,16 @@ func NewEdgeCRUDHandler(service services.GraphService) *EdgeCRUDHandler { // @Description 在知识图谱中创建一个新节点 // @Accept json // @Produce json -// @Param request body models.CreateNodeRequest true "节点创建请求" -// @Success 200 {object} models.Node "创建成功" -// @Failure 400 {object} models.ErrorResponse "请求错误" -// @Failure 409 {object} models.ErrorResponse "节点已存在" -// @Failure 500 {object} models.ErrorResponse "内部错误" +// @Param request body model.CreateNodeRequest true "节点创建请求" +// @Success 200 {object} model.Node "创建成功" +// @Failure 400 {object} model.ErrorResponse "请求错误" +// @Failure 409 {object} model.ErrorResponse "节点已存在" +// @Failure 500 {object} model.ErrorResponse "内部错误" // @Router /api/nodes [post] func (h *NodeCRUDHandler) CreateNode(c *gin.Context) { - var req models.CreateNodeRequest + var req model.CreateNodeRequest if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, models.ErrorResponse{ + c.JSON(http.StatusBadRequest, model.ErrorResponse{ Error: "invalid_request", Message: err.Error(), }) @@ -55,7 +55,7 @@ func (h *NodeCRUDHandler) CreateNode(c *gin.Context) { node, err := h.service.CreateNode(req) if err != nil { - c.JSON(http.StatusConflict, models.ErrorResponse{ + c.JSON(http.StatusConflict, model.ErrorResponse{ Error: "create_failed", Message: err.Error(), }) @@ -71,18 +71,18 @@ func (h *NodeCRUDHandler) CreateNode(c *gin.Context) { // @Accept json // @Produce json // @Param id path string true "节点ID" -// @Param request body models.UpdateNodeRequest true "节点更新请求" -// @Success 200 {object} models.Node "更新成功" -// @Failure 400 {object} models.ErrorResponse "请求错误" -// @Failure 404 {object} models.ErrorResponse "节点未找到" -// @Failure 500 {object} models.ErrorResponse "内部错误" +// @Param request body model.UpdateNodeRequest true "节点更新请求" +// @Success 200 {object} model.Node "更新成功" +// @Failure 400 {object} model.ErrorResponse "请求错误" +// @Failure 404 {object} model.ErrorResponse "节点未找到" +// @Failure 500 {object} model.ErrorResponse "内部错误" // @Router /api/nodes/{id} [put] func (h *NodeCRUDHandler) UpdateNode(c *gin.Context) { id := c.Param("id") - var req models.UpdateNodeRequest + var req model.UpdateNodeRequest if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, models.ErrorResponse{ + c.JSON(http.StatusBadRequest, model.ErrorResponse{ Error: "invalid_request", Message: err.Error(), }) @@ -91,7 +91,7 @@ func (h *NodeCRUDHandler) UpdateNode(c *gin.Context) { node, err := h.service.UpdateNode(id, req) if err != nil { - c.JSON(http.StatusNotFound, models.ErrorResponse{ + c.JSON(http.StatusNotFound, model.ErrorResponse{ Error: "update_failed", Message: err.Error(), }) @@ -106,23 +106,23 @@ func (h *NodeCRUDHandler) UpdateNode(c *gin.Context) { // @Description 从知识图谱中删除指定节点及其相关的所有边 // @Produce json // @Param id path string true "节点ID" -// @Success 200 {object} models.DeleteResponse "删除成功" -// @Failure 404 {object} models.ErrorResponse "节点未找到" -// @Failure 500 {object} models.ErrorResponse "内部错误" +// @Success 200 {object} model.DeleteResponse "删除成功" +// @Failure 404 {object} model.ErrorResponse "节点未找到" +// @Failure 500 {object} model.ErrorResponse "内部错误" // @Router /api/nodes/{id} [delete] func (h *NodeCRUDHandler) DeleteNode(c *gin.Context) { id := c.Param("id") err := h.service.DeleteNode(id) if err != nil { - c.JSON(http.StatusNotFound, models.ErrorResponse{ + c.JSON(http.StatusNotFound, model.ErrorResponse{ Error: "delete_failed", Message: err.Error(), }) return } - c.JSON(http.StatusOK, models.DeleteResponse{ + c.JSON(http.StatusOK, model.DeleteResponse{ Success: true, Message: "节点已成功删除", ID: id, @@ -134,17 +134,17 @@ func (h *NodeCRUDHandler) DeleteNode(c *gin.Context) { // @Description 在知识图谱中创建一个新边(关系) // @Accept json // @Produce json -// @Param request body models.CreateEdgeRequest true "边创建请求" -// @Success 200 {object} models.EdgeResponse "创建成功" -// @Failure 400 {object} models.ErrorResponse "请求错误" -// @Failure 404 {object} models.ErrorResponse "源节点或目标节点未找到" -// @Failure 409 {object} models.ErrorResponse "边已存在" -// @Failure 500 {object} models.ErrorResponse "内部错误" +// @Param request body model.CreateEdgeRequest true "边创建请求" +// @Success 200 {object} model.EdgeResponse "创建成功" +// @Failure 400 {object} model.ErrorResponse "请求错误" +// @Failure 404 {object} model.ErrorResponse "源节点或目标节点未找到" +// @Failure 409 {object} model.ErrorResponse "边已存在" +// @Failure 500 {object} model.ErrorResponse "内部错误" // @Router /api/edges [post] func (h *EdgeCRUDHandler) CreateEdge(c *gin.Context) { - var req models.CreateEdgeRequest + var req model.CreateEdgeRequest if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, models.ErrorResponse{ + c.JSON(http.StatusBadRequest, model.ErrorResponse{ Error: "invalid_request", Message: err.Error(), }) @@ -153,7 +153,7 @@ func (h *EdgeCRUDHandler) CreateEdge(c *gin.Context) { edge, err := h.service.CreateEdge(req) if err != nil { - c.JSON(http.StatusConflict, models.ErrorResponse{ + c.JSON(http.StatusConflict, model.ErrorResponse{ Error: "create_failed", Message: err.Error(), }) @@ -161,7 +161,7 @@ func (h *EdgeCRUDHandler) CreateEdge(c *gin.Context) { } // 转换为响应格式 - response := models.EdgeResponse{ + response := model.EdgeResponse{ ID: edge.ID, Source: edge.Source, Target: edge.Target, @@ -178,23 +178,23 @@ func (h *EdgeCRUDHandler) CreateEdge(c *gin.Context) { // @Description 从知识图谱中删除指定的边 // @Produce json // @Param id path string true "边ID" -// @Success 200 {object} models.DeleteResponse "删除成功" -// @Failure 404 {object} models.ErrorResponse "边未找到" -// @Failure 500 {object} models.ErrorResponse "内部错误" +// @Success 200 {object} model.DeleteResponse "删除成功" +// @Failure 404 {object} model.ErrorResponse "边未找到" +// @Failure 500 {object} model.ErrorResponse "内部错误" // @Router /api/edges/{id} [delete] func (h *EdgeCRUDHandler) DeleteEdge(c *gin.Context) { edgeID := c.Param("id") err := h.service.DeleteEdge(edgeID) if err != nil { - c.JSON(http.StatusNotFound, models.ErrorResponse{ + c.JSON(http.StatusNotFound, model.ErrorResponse{ Error: "delete_failed", Message: err.Error(), }) return } - c.JSON(http.StatusOK, models.DeleteResponse{ + c.JSON(http.StatusOK, model.DeleteResponse{ Success: true, Message: "边已成功删除", ID: edgeID, diff --git a/backend/handlers/graph_handler.go b/backend/internal/handler/graph_handler.go similarity index 68% rename from backend/handlers/graph_handler.go rename to backend/internal/handler/graph_handler.go index cb77e2f..e459e3f 100644 --- a/backend/handlers/graph_handler.go +++ b/backend/internal/handler/graph_handler.go @@ -1,27 +1,27 @@ -package handlers +package handler import ( "net/http" "github.com/gin-gonic/gin" - "knowledge-graph-backend/services" + "knowledge-graph-backend/internal/service" ) type GraphHandler struct { - service services.GraphService + service service.GraphService } -func NewGraphHandler(service services.GraphService) *GraphHandler { +func NewGraphHandler(svc service.GraphService) *GraphHandler { return &GraphHandler{ - service: service, + service: svc, } } // @Summary 获取图数据 // @Description 获取知识图谱的全部节点和边数据 // @Produce json -// @Success 200 {object} models.GraphData "成功" -// @Failure 500 {object} models.ErrorResponse "内部错误" +// @Success 200 {object} model.GraphData "成功" +// @Failure 500 {object} model.ErrorResponse "内部错误" // @Router /api/graph [get] func (h *GraphHandler) GetGraphData(c *gin.Context) { data := h.service.GetGraphData() @@ -32,7 +32,7 @@ func (h *GraphHandler) GetGraphData(c *gin.Context) { // @Description 获取知识图谱的节点数、边数等统计信息 // @Produce json // @Success 200 {object} map[string]interface{} "成功" -// @Failure 500 {object} models.ErrorResponse "内部错误" +// @Failure 500 {object} model.ErrorResponse "内部错误" // @Router /api/graph/stats [get] func (h *GraphHandler) GetStats(c *gin.Context) { stats := h.service.GetStats() @@ -42,8 +42,8 @@ func (h *GraphHandler) GetStats(c *gin.Context) { // @Summary 获取简化图数据 // @Description 获取知识图谱的全部节点和边数据(仅保留核心信息,适合LLM处理) // @Produce json -// @Success 200 {object} models.SimpleGraphData "成功" -// @Failure 500 {object} models.ErrorResponse "内部错误" +// @Success 200 {object} model.SimpleGraphData "成功" +// @Failure 500 {object} model.ErrorResponse "内部错误" // @Router /api/graph/simpleJson [get] func (h *GraphHandler) GetSimpleGraphData(c *gin.Context) { data := h.service.GetSimpleGraphData() diff --git a/backend/handlers/node_handler.go b/backend/internal/handler/node_handler.go similarity index 70% rename from backend/handlers/node_handler.go rename to backend/internal/handler/node_handler.go index ff6dd89..bb3ad25 100644 --- a/backend/handlers/node_handler.go +++ b/backend/internal/handler/node_handler.go @@ -1,19 +1,19 @@ -package handlers +package handler import ( "net/http" "github.com/gin-gonic/gin" - "knowledge-graph-backend/services" + "knowledge-graph-backend/internal/service" ) type NodeHandler struct { - service services.GraphService + service service.GraphService } -func NewNodeHandler(service services.GraphService) *NodeHandler { +func NewNodeHandler(svc service.GraphService) *NodeHandler { return &NodeHandler{ - service: service, + service: svc, } } @@ -21,10 +21,10 @@ func NewNodeHandler(service services.GraphService) *NodeHandler { // @Description 根据节点 ID 获取知识图谱中的节点详细信息 // @Produce json // @Param id path string true "节点 ID" -// @Success 200 {object} models.NodeResponse "成功" -// @Failure 400 {object} models.ErrorResponse "请求错误" -// @Failure 404 {object} models.ErrorResponse "节点未找到" -// @Failure 500 {object} models.ErrorResponse "内部错误" +// @Success 200 {object} model.NodeResponse "成功" +// @Failure 400 {object} model.ErrorResponse "请求错误" +// @Failure 404 {object} model.ErrorResponse "节点未找到" +// @Failure 500 {object} model.ErrorResponse "内部错误" // @Router /api/nodes/{id} [get] func (h *NodeHandler) GetNodeByID(c *gin.Context) { nodeID := c.Param("id") @@ -52,10 +52,10 @@ func (h *NodeHandler) GetNodeByID(c *gin.Context) { // @Description 根据节点 ID 获取其所有邻居节点及关联边 // @Produce json // @Param id path string true "节点 ID" -// @Success 200 {object} models.NeighborResponse "成功" -// @Failure 400 {object} models.ErrorResponse "请求错误" -// @Failure 404 {object} models.ErrorResponse "节点未找到" -// @Failure 500 {object} models.ErrorResponse "内部错误" +// @Success 200 {object} model.NeighborResponse "成功" +// @Failure 400 {object} model.ErrorResponse "请求错误" +// @Failure 404 {object} model.ErrorResponse "节点未找到" +// @Failure 500 {object} model.ErrorResponse "内部错误" // @Router /api/nodes/{id}/neighbors [get] func (h *NodeHandler) GetNeighbors(c *gin.Context) { nodeID := c.Param("id") diff --git a/backend/handlers/search_handler.go b/backend/internal/handler/search_handler.go similarity index 63% rename from backend/handlers/search_handler.go rename to backend/internal/handler/search_handler.go index 00a919f..32cef9c 100644 --- a/backend/handlers/search_handler.go +++ b/backend/internal/handler/search_handler.go @@ -1,19 +1,19 @@ -package handlers +package handler import ( "net/http" "github.com/gin-gonic/gin" - "knowledge-graph-backend/services" + "knowledge-graph-backend/internal/service" ) type SearchHandler struct { - service services.GraphService + service service.GraphService } -func NewSearchHandler(service services.GraphService) *SearchHandler { +func NewSearchHandler(svc service.GraphService) *SearchHandler { return &SearchHandler{ - service: service, + service: svc, } } @@ -21,9 +21,9 @@ func NewSearchHandler(service services.GraphService) *SearchHandler { // @Description 根据关键词搜索知识图谱中的节点 // @Produce json // @Param q query string true "搜索关键词" -// @Success 200 {object} models.SearchResponse "成功" -// @Failure 400 {object} models.ErrorResponse "请求错误" -// @Failure 500 {object} models.ErrorResponse "内部错误" +// @Success 200 {object} model.SearchResponse "成功" +// @Failure 400 {object} model.ErrorResponse "请求错误" +// @Failure 500 {object} model.ErrorResponse "内部错误" // @Router /api/search [get] func (h *SearchHandler) SearchNodes(c *gin.Context) { query := c.Query("q") diff --git a/backend/models/graph.go b/backend/internal/model/graph.go similarity index 52% rename from backend/models/graph.go rename to backend/internal/model/graph.go index 1823116..bc2b7ed 100644 --- a/backend/models/graph.go +++ b/backend/internal/model/graph.go @@ -1,25 +1,28 @@ -package models +package model +// Node represents a graph node type Node struct { ID string `json:"id"` Label string `json:"label"` Type string `json:"type,omitempty"` - Properties map[string]interface{} `json:"properties,omitempty"` X float64 `json:"x,omitempty"` Y float64 `json:"y,omitempty"` Style map[string]interface{} `json:"style,omitempty"` + Properties map[string]interface{} `json:"properties,omitempty"` } +// Edge represents a graph edge type Edge struct { ID string `json:"id"` Source string `json:"source"` Target string `json:"target"` Label string `json:"label,omitempty"` Type string `json:"type,omitempty"` - Properties map[string]interface{} `json:"properties,omitempty"` Style map[string]interface{} `json:"style,omitempty"` + Properties map[string]interface{} `json:"properties,omitempty"` } +// GraphData represents the complete graph structure type GraphData struct { Nodes []Node `json:"nodes"` Edges []Edge `json:"edges"` @@ -45,3 +48,43 @@ type SimpleGraphData struct { Nodes []SimpleNode `json:"nodes"` Edges []SimpleEdge `json:"edges"` } + +// StatsResponse represents graph statistics +type StatsResponse struct { + TotalNodes int `json:"totalNodes"` + TotalEdges int `json:"totalEdges"` + ConceptNodes int `json:"conceptNodes,omitempty"` + ToolNodes int `json:"toolNodes,omitempty"` + ApplicationNodes int `json:"applicationNodes,omitempty"` +} + +// NeighborResponse represents a node's neighbors and related edges +type NeighborResponse struct { + Nodes []Node `json:"nodes"` + Edges []Edge `json:"edges"` +} + +// NodeResponse represents a node detail response +type NodeResponse struct { + ID string `json:"id"` + Label string `json:"label"` + Type string `json:"type,omitempty"` + X float64 `json:"x,omitempty"` + Y float64 `json:"y,omitempty"` + Style map[string]interface{} `json:"style,omitempty"` + Properties map[string]interface{} `json:"properties,omitempty"` + Neighbors []Node `json:"neighbors,omitempty"` +} + +// SearchResponse represents search results +type SearchResponse struct { + Count int `json:"count"` + Query string `json:"query"` + Nodes []Node `json:"nodes"` +} + +// ErrorResponse represents an error response +type ErrorResponse struct { + Error string `json:"error"` + Message string `json:"message"` +} diff --git a/backend/internal/model/node.go b/backend/internal/model/node.go new file mode 100644 index 0000000..4004c86 --- /dev/null +++ b/backend/internal/model/node.go @@ -0,0 +1,12 @@ +package model + +// NodeInfo represents detailed node information +type NodeInfo struct { + ID string `json:"id"` + Label string `json:"label"` + Type string `json:"type"` + X float64 `json:"x,omitempty"` + Y float64 `json:"y,omitempty"` + Properties map[string]interface{} `json:"properties,omitempty"` + Neighbors []Node `json:"neighbors,omitempty"` +} diff --git a/backend/internal/model/response.go b/backend/internal/model/response.go new file mode 100644 index 0000000..2aeb15b --- /dev/null +++ b/backend/internal/model/response.go @@ -0,0 +1,47 @@ +package model + +// CreateNodeRequest creates a new node +type CreateNodeRequest struct { + ID string `json:"id" binding:"required" example:"node_123"` + Label string `json:"label" binding:"required" example:"人工智能"` + Type string `json:"type" binding:"required" example:"概念"` + X float64 `json:"x" example:"100"` + Y float64 `json:"y" example:"200"` + Properties map[string]interface{} `json:"properties,omitempty"` +} + +// UpdateNodeRequest updates an existing node +type UpdateNodeRequest struct { + Label string `json:"label,omitempty" example:"机器学习"` + Type string `json:"type,omitempty" example:"工具"` + X *float64 `json:"x,omitempty" example:"150"` + Y *float64 `json:"y,omitempty" example:"250"` + Properties map[string]interface{} `json:"properties,omitempty"` +} + +// DeleteResponse represents a delete operation response +type DeleteResponse struct { + Success bool `json:"success" example:"true"` + Message string `json:"message" example:"节点已成功删除"` + ID string `json:"id,omitempty" example:"node_123"` +} + +// CreateEdgeRequest creates a new edge +type CreateEdgeRequest struct { + ID string `json:"id" binding:"required" example:"edge_123"` + Source string `json:"source" binding:"required" example:"node_1"` + Target string `json:"target" binding:"required" example:"node_2"` + Label string `json:"label" binding:"required" example:"包含"` + Type string `json:"type" example:"CONTAINS"` + Properties map[string]interface{} `json:"properties,omitempty"` +} + +// EdgeResponse represents an edge response +type EdgeResponse struct { + ID string `json:"id" example:"edge_123"` + Source string `json:"source" example:"node_1"` + Target string `json:"target" example:"node_2"` + Label string `json:"label" example:"包含"` + Type string `json:"type" example:"CONTAINS"` + Properties map[string]interface{} `json:"properties,omitempty"` +} diff --git a/backend/neo4j/client.go b/backend/internal/repository/neo4j/client.go similarity index 90% rename from backend/neo4j/client.go rename to backend/internal/repository/neo4j/client.go index 159be3f..05f4de8 100644 --- a/backend/neo4j/client.go +++ b/backend/internal/repository/neo4j/client.go @@ -5,7 +5,7 @@ import ( "github.com/neo4j/neo4j-go-driver/v5/neo4j" - "knowledge-graph-backend/config" + "knowledge-graph-backend/internal/config" ) func NewDriver(cfg config.Neo4jConfig) neo4j.DriverWithContext { diff --git a/backend/neo4j/client_test.go b/backend/internal/repository/neo4j/client_test.go similarity index 89% rename from backend/neo4j/client_test.go rename to backend/internal/repository/neo4j/client_test.go index f81d2f0..7742431 100644 --- a/backend/neo4j/client_test.go +++ b/backend/internal/repository/neo4j/client_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "knowledge-graph-backend/config" + "knowledge-graph-backend/internal/config" ) func TestNewDriver(t *testing.T) { diff --git a/backend/internal/service/interface.go b/backend/internal/service/interface.go new file mode 100644 index 0000000..062eb75 --- /dev/null +++ b/backend/internal/service/interface.go @@ -0,0 +1,20 @@ +package service + +import "knowledge-graph-backend/internal/model" + +type GraphService interface { + GetGraphData() model.GraphData + GetSimpleGraphData() model.SimpleGraphData + GetNodeByID(id string) (model.Node, bool) + SearchNodes(query string) []model.Node + GetNeighbors(nodeID string) (model.NeighborResponse, bool) + GetStats() map[string]int + + // 新增增删改接口 + CreateNode(req model.CreateNodeRequest) (model.Node, error) + UpdateNode(id string, req model.UpdateNodeRequest) (model.Node, error) + DeleteNode(id string) error + + CreateEdge(req model.CreateEdgeRequest) (model.Edge, error) + DeleteEdge(id string) error +} diff --git a/backend/services/neo4j_service.go b/backend/internal/service/neo4j_service.go similarity index 82% rename from backend/services/neo4j_service.go rename to backend/internal/service/neo4j_service.go index a88cc1d..6590f9a 100644 --- a/backend/services/neo4j_service.go +++ b/backend/internal/service/neo4j_service.go @@ -1,4 +1,4 @@ -package services +package service import ( "context" @@ -7,7 +7,7 @@ import ( "github.com/neo4j/neo4j-go-driver/v5/neo4j" - "knowledge-graph-backend/models" + "knowledge-graph-backend/internal/model" ) type Neo4jService struct { @@ -18,10 +18,10 @@ func NewNeo4jService(driver neo4j.DriverWithContext) *Neo4jService { return &Neo4jService{driver: driver} } -func (s *Neo4jService) GetGraphData() models.GraphData { +func (s *Neo4jService) GetGraphData() model.GraphData { ctx := context.Background() - var nodes []models.Node - var edges []models.Edge + var nodes []model.Node + var edges []model.Edge nodeResult, err := neo4j.ExecuteQuery(ctx, s.driver, "MATCH (n) RETURN n", @@ -30,7 +30,7 @@ func (s *Neo4jService) GetGraphData() models.GraphData { ) if err != nil { fmt.Printf("Error querying nodes: %v\n", err) - return models.GraphData{} + return model.GraphData{} } for _, record := range nodeResult.Records { if v, ok := record.Get("n"); ok { @@ -47,7 +47,7 @@ func (s *Neo4jService) GetGraphData() models.GraphData { ) if err != nil { fmt.Printf("Error querying edges: %v\n", err) - return models.GraphData{Nodes: nodes} + return model.GraphData{Nodes: nodes} } for _, record := range edgeResult.Records { source, _ := record.Get("source") @@ -65,10 +65,10 @@ func (s *Neo4jService) GetGraphData() models.GraphData { } } - return models.GraphData{Nodes: nodes, Edges: edges} + return model.GraphData{Nodes: nodes, Edges: edges} } -func (s *Neo4jService) GetNodeByID(id string) (models.Node, bool) { +func (s *Neo4jService) GetNodeByID(id string) (model.Node, bool) { ctx := context.Background() result, err := neo4j.ExecuteQuery(ctx, s.driver, "MATCH (n {id: $id}) RETURN n", @@ -76,18 +76,18 @@ func (s *Neo4jService) GetNodeByID(id string) (models.Node, bool) { neo4j.EagerResultTransformer, ) if err != nil || len(result.Records) == 0 { - return models.Node{}, false + return model.Node{}, false } v, _ := result.Records[0].Get("n") if n, ok := v.(neo4j.Node); ok { return neo4jNodeToModel(n), true } - return models.Node{}, false + return model.Node{}, false } -func (s *Neo4jService) SearchNodes(query string) []models.Node { +func (s *Neo4jService) SearchNodes(query string) []model.Node { if query == "" { - return []models.Node{} + return []model.Node{} } ctx := context.Background() result, err := neo4j.ExecuteQuery(ctx, s.driver, @@ -100,9 +100,9 @@ func (s *Neo4jService) SearchNodes(query string) []models.Node { neo4j.EagerResultTransformer, ) if err != nil { - return []models.Node{} + return []model.Node{} } - var nodes []models.Node + var nodes []model.Node for _, record := range result.Records { v, _ := record.Get("n") if n, ok := v.(neo4j.Node); ok { @@ -112,12 +112,12 @@ func (s *Neo4jService) SearchNodes(query string) []models.Node { return nodes } -func (s *Neo4jService) GetNeighbors(nodeID string) (models.NeighborResponse, bool) { +func (s *Neo4jService) GetNeighbors(nodeID string) (model.NeighborResponse, bool) { ctx := context.Background() _, exists := s.GetNodeByID(nodeID) if !exists { - return models.NeighborResponse{}, false + return model.NeighborResponse{}, false } result, err := neo4j.ExecuteQuery(ctx, s.driver, @@ -127,11 +127,11 @@ func (s *Neo4jService) GetNeighbors(nodeID string) (models.NeighborResponse, boo neo4j.EagerResultTransformer, ) if err != nil { - return models.NeighborResponse{Nodes: []models.Node{}, Edges: []models.Edge{}}, true + return model.NeighborResponse{Nodes: []model.Node{}, Edges: []model.Edge{}}, true } - neighborMap := make(map[string]models.Node) - edgeMap := make(map[string]models.Edge) + neighborMap := make(map[string]model.Node) + edgeMap := make(map[string]model.Edge) for _, record := range result.Records { if v, ok := record.Get("m"); ok { @@ -159,17 +159,17 @@ func (s *Neo4jService) GetNeighbors(nodeID string) (models.NeighborResponse, boo } } - var neighborNodes []models.Node + var neighborNodes []model.Node for _, n := range neighborMap { neighborNodes = append(neighborNodes, n) } - var relatedEdges []models.Edge + var relatedEdges []model.Edge for _, e := range edgeMap { relatedEdges = append(relatedEdges, e) } - return models.NeighborResponse{ + return model.NeighborResponse{ Nodes: neighborNodes, Edges: relatedEdges, }, true @@ -216,10 +216,10 @@ func (s *Neo4jService) GetStats() map[string]int { } // GetSimpleGraphData 获取简化的图数据,仅保留核心信息,适合 LLM 处理 -func (s *Neo4jService) GetSimpleGraphData() models.SimpleGraphData { +func (s *Neo4jService) GetSimpleGraphData() model.SimpleGraphData { ctx := context.Background() - var nodes []models.SimpleNode - var edges []models.SimpleEdge + var nodes []model.SimpleNode + var edges []model.SimpleEdge nodeResult, err := neo4j.ExecuteQuery(ctx, s.driver, "MATCH (n) RETURN n", @@ -228,13 +228,13 @@ func (s *Neo4jService) GetSimpleGraphData() models.SimpleGraphData { ) if err != nil { fmt.Printf("Error querying nodes: %v\n", err) - return models.SimpleGraphData{} + return model.SimpleGraphData{} } for _, record := range nodeResult.Records { if v, ok := record.Get("n"); ok { if n, ok := v.(neo4j.Node); ok { props := n.Props - node := models.SimpleNode{ + node := model.SimpleNode{ ID: getStr(props, "id"), Label: getStr(props, "label"), Type: getStr(props, "type"), @@ -251,7 +251,7 @@ func (s *Neo4jService) GetSimpleGraphData() models.SimpleGraphData { ) if err != nil { fmt.Printf("Error querying edges: %v\n", err) - return models.SimpleGraphData{Nodes: nodes} + return model.SimpleGraphData{Nodes: nodes} } for _, record := range edgeResult.Records { source, _ := record.Get("source") @@ -259,7 +259,7 @@ func (s *Neo4jService) GetSimpleGraphData() models.SimpleGraphData { v, _ := record.Get("r") if rel, ok := v.(neo4j.Relationship); ok { props := rel.Props - edge := models.SimpleEdge{ + edge := model.SimpleEdge{ Source: getStr(props, "source"), Target: getStr(props, "target"), Type: rel.Type, @@ -275,12 +275,12 @@ func (s *Neo4jService) GetSimpleGraphData() models.SimpleGraphData { } } - return models.SimpleGraphData{Nodes: nodes, Edges: edges} + return model.SimpleGraphData{Nodes: nodes, Edges: edges} } -func neo4jNodeToModel(n neo4j.Node) models.Node { +func neo4jNodeToModel(n neo4j.Node) model.Node { props := n.Props - node := models.Node{ + node := model.Node{ ID: getStr(props, "id"), Label: getStr(props, "label"), Type: getStr(props, "type"), @@ -307,9 +307,9 @@ func neo4jNodeToModel(n neo4j.Node) models.Node { return node } -func neo4jRelToModel(r neo4j.Relationship) models.Edge { +func neo4jRelToModel(r neo4j.Relationship) model.Edge { props := r.Props - edge := models.Edge{ + edge := model.Edge{ ID: getStr(props, "id"), Label: getStr(props, "label"), Type: r.Type, @@ -365,7 +365,7 @@ func getInt(record *neo4j.Record, key string) int { } // CreateNode 创建新节点 -func (s *Neo4jService) CreateNode(req models.CreateNodeRequest) (models.Node, error) { +func (s *Neo4jService) CreateNode(req model.CreateNodeRequest) (model.Node, error) { ctx := context.Background() // 检查节点是否已存在 @@ -373,10 +373,10 @@ func (s *Neo4jService) CreateNode(req models.CreateNodeRequest) (models.Node, er checkResult, err := neo4j.ExecuteQuery(ctx, s.driver, checkQuery, map[string]any{"id": req.ID}, neo4j.EagerResultTransformer) if err != nil { - return models.Node{}, fmt.Errorf("error checking node existence: %w", err) + return model.Node{}, fmt.Errorf("error checking node existence: %w", err) } if len(checkResult.Records) > 0 { - return models.Node{}, fmt.Errorf("node with id %s already exists", req.ID) + return model.Node{}, fmt.Errorf("node with id %s already exists", req.ID) } // 构建创建属性 @@ -419,11 +419,11 @@ func (s *Neo4jService) CreateNode(req models.CreateNodeRequest) (models.Node, er map[string]any{"props": props}, neo4j.EagerResultTransformer) if err != nil { fmt.Printf("Error creating node: %v\n", err) - return models.Node{}, fmt.Errorf("failed to create node: %w", err) + return model.Node{}, fmt.Errorf("failed to create node: %w", err) } if len(result.Records) == 0 { - return models.Node{}, fmt.Errorf("failed to create node: no result returned") + return model.Node{}, fmt.Errorf("failed to create node: no result returned") } v, _ := result.Records[0].Get("n") @@ -431,11 +431,11 @@ func (s *Neo4jService) CreateNode(req models.CreateNodeRequest) (models.Node, er return neo4jNodeToModel(n), nil } - return models.Node{}, fmt.Errorf("unexpected result type while creating node") + return model.Node{}, fmt.Errorf("unexpected result type while creating node") } // CreateNodeV2 创建新节点(改进版) -func (s *Neo4jService) CreateNodeV2(req models.CreateNodeRequest) (models.Node, error) { +func (s *Neo4jService) CreateNodeV2(req model.CreateNodeRequest) (model.Node, error) { ctx := context.Background() // 先检查节点是否已存在 @@ -443,10 +443,10 @@ func (s *Neo4jService) CreateNodeV2(req models.CreateNodeRequest) (models.Node, checkResult, err := neo4j.ExecuteQuery(ctx, s.driver, checkQuery, map[string]any{"id": req.ID}, neo4j.EagerResultTransformer) if err != nil { - return models.Node{}, err + return model.Node{}, err } if len(checkResult.Records) > 0 { - return models.Node{}, fmt.Errorf("node with id %s already exists", req.ID) + return model.Node{}, fmt.Errorf("node with id %s already exists", req.ID) } // 构建创建属性 @@ -489,11 +489,11 @@ func (s *Neo4jService) CreateNodeV2(req models.CreateNodeRequest) (models.Node, map[string]any{"props": props}, neo4j.EagerResultTransformer) if err != nil { fmt.Printf("Error creating node: %v\n", err) - return models.Node{}, err + return model.Node{}, err } if len(result.Records) == 0 { - return models.Node{}, fmt.Errorf("failed to create node") + return model.Node{}, fmt.Errorf("failed to create node") } v, _ := result.Records[0].Get("n") @@ -501,17 +501,17 @@ func (s *Neo4jService) CreateNodeV2(req models.CreateNodeRequest) (models.Node, return neo4jNodeToModel(n), nil } - return models.Node{}, fmt.Errorf("unexpected result type while creating node") + return model.Node{}, fmt.Errorf("unexpected result type while creating node") } // UpdateNode 更新节点 -func (s *Neo4jService) UpdateNode(id string, req models.UpdateNodeRequest) (models.Node, error) { +func (s *Neo4jService) UpdateNode(id string, req model.UpdateNodeRequest) (model.Node, error) { ctx := context.Background() // 检查节点是否存在 _, exists := s.GetNodeByID(id) if !exists { - return models.Node{}, fmt.Errorf("node with id %s not found", id) + return model.Node{}, fmt.Errorf("node with id %s not found", id) } // 构建更新语句 @@ -558,11 +558,11 @@ func (s *Neo4jService) UpdateNode(id string, req models.UpdateNodeRequest) (mode result, err := neo4j.ExecuteQuery(ctx, s.driver, query, params, neo4j.EagerResultTransformer) if err != nil { fmt.Printf("Error updating node: %v\n", err) - return models.Node{}, err + return model.Node{}, err } if len(result.Records) == 0 { - return models.Node{}, fmt.Errorf("failed to update node") + return model.Node{}, fmt.Errorf("failed to update node") } v, _ := result.Records[0].Get("n") @@ -570,7 +570,7 @@ func (s *Neo4jService) UpdateNode(id string, req models.UpdateNodeRequest) (mode return neo4jNodeToModel(n), nil } - return models.Node{}, fmt.Errorf("unexpected result type while updating node") + return model.Node{}, fmt.Errorf("unexpected result type while updating node") } // DeleteNode 删除节点 @@ -611,17 +611,17 @@ func (s *Neo4jService) DeleteNode(id string) error { } // CreateEdge 创建新边 -func (s *Neo4jService) CreateEdge(req models.CreateEdgeRequest) (models.Edge, error) { +func (s *Neo4jService) CreateEdge(req model.CreateEdgeRequest) (model.Edge, error) { ctx := context.Background() // 检查源节点和目标节点是否存在 _, sourceExists := s.GetNodeByID(req.Source) if !sourceExists { - return models.Edge{}, fmt.Errorf("source node with id %s not found", req.Source) + return model.Edge{}, fmt.Errorf("source node with id %s not found", req.Source) } _, targetExists := s.GetNodeByID(req.Target) if !targetExists { - return models.Edge{}, fmt.Errorf("target node with id %s not found", req.Target) + return model.Edge{}, fmt.Errorf("target node with id %s not found", req.Target) } // 构建创建属性 @@ -663,11 +663,11 @@ func (s *Neo4jService) CreateEdge(req models.CreateEdgeRequest) (models.Edge, er result, err := neo4j.ExecuteQuery(ctx, s.driver, query, params, neo4j.EagerResultTransformer) if err != nil { fmt.Printf("Error creating edge: %v\n", err) - return models.Edge{}, err + return model.Edge{}, err } if len(result.Records) == 0 { - return models.Edge{}, fmt.Errorf("failed to create edge") + return model.Edge{}, fmt.Errorf("failed to create edge") } v, _ := result.Records[0].Get("r") @@ -678,7 +678,7 @@ func (s *Neo4jService) CreateEdge(req models.CreateEdgeRequest) (models.Edge, er return edge, nil } - return models.Edge{}, fmt.Errorf("unexpected result type while creating edge") + return model.Edge{}, fmt.Errorf("unexpected result type while creating edge") } // DeleteEdge 删除边 diff --git a/backend/models/nodes.go b/backend/models/nodes.go deleted file mode 100644 index 0e55cf0..0000000 --- a/backend/models/nodes.go +++ /dev/null @@ -1,23 +0,0 @@ -package models - -type NodeResponse struct { - ID string `json:"id" example:"1"` - Label string `json:"label" example:"人工智能"` - Type string `json:"type,omitempty" example:"概念"` - Properties map[string]interface{} `json:"properties,omitempty"` -} - -type NeighborResponse struct { - Nodes []Node `json:"nodes"` - Edges []Edge `json:"edges"` -} - -type SearchResponse struct { - Results []Node `json:"results"` - Count int `json:"count"` -} - -type ErrorResponse struct { - Error string `json:"error"` - Message string `json:"message"` -} diff --git a/backend/models/requests.go b/backend/models/requests.go deleted file mode 100644 index 0ae7443..0000000 --- a/backend/models/requests.go +++ /dev/null @@ -1,47 +0,0 @@ -package models - -// CreateNodeRequest 创建节点的请求结构 -type CreateNodeRequest struct { - ID string `json:"id" binding:"required" example:"node_123"` // 节点唯一标识 - Label string `json:"label" binding:"required" example:"人工智能"` // 节点显示标签 - Type string `json:"type" binding:"required" example:"概念"` // 节点类型 - X float64 `json:"x" example:"100"` // X坐标(可选) - Y float64 `json:"y" example:"200"` // Y坐标(可选) - Properties map[string]interface{} `json:"properties,omitempty"` // 自定义属性 -} - -// UpdateNodeRequest 更新节点的请求结构 -type UpdateNodeRequest struct { - Label string `json:"label,omitempty" example:"机器学习"` // 节点显示标签 - Type string `json:"type,omitempty" example:"工具"` // 节点类型 - X *float64 `json:"x,omitempty" example:"150"` // X坐标 - Y *float64 `json:"y,omitempty" example:"250"` // Y坐标 - Properties map[string]interface{} `json:"properties,omitempty"` // 自定义属性(更新) -} - -// DeleteResponse 删除操作的响应结构 -type DeleteResponse struct { - Success bool `json:"success" example:"true"` // 是否成功 - Message string `json:"message" example:"节点已成功删除"` // 消息 - ID string `json:"id,omitempty" example:"node_123"` // 被删除的实体ID(可选) -} - -// CreateEdgeRequest 创建边的请求结构 -type CreateEdgeRequest struct { - ID string `json:"id" binding:"required" example:"edge_123"` // 边的唯一标识 - Source string `json:"source" binding:"required" example:"node_1"` // 源节点ID - Target string `json:"target" binding:"required" example:"node_2"` // 目标节点ID - Label string `json:"label" binding:"required" example:"包含"` // 边的显示标签 - Type string `json:"type" example:"CONTAINS"` // 边的类型(可选,默认为关系类型) - Properties map[string]interface{} `json:"properties,omitempty"` // 自定义属性 -} - -// EdgeResponse 边的响应结构 -type EdgeResponse struct { - ID string `json:"id" example:"edge_123"` // 边的ID - Source string `json:"source" example:"node_1"` // 源节点ID - Target string `json:"target" example:"node_2"` // 目标节点ID - Label string `json:"label" example:"包含"` // 边的显示标签 - Type string `json:"type" example:"CONTAINS"` // 边的类型 - Properties map[string]interface{} `json:"properties,omitempty"` // 自定义属性 -} diff --git a/backend/services/data_service.go b/backend/services/data_service.go deleted file mode 100644 index d877858..0000000 --- a/backend/services/data_service.go +++ /dev/null @@ -1,161 +0,0 @@ -package services - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "os" - "strings" - "sync" - - "knowledge-graph-backend/models" -) - -type DataService struct { - data models.GraphData - mutex sync.RWMutex - filePath string -} - -func NewDataService(filePath string) (*DataService, error) { - ds := &DataService{ - filePath: filePath, - mutex: sync.RWMutex{}, - } - - if err := ds.loadData(); err != nil { - return nil, fmt.Errorf("failed to load data: %w", err) - } - - return ds, nil -} - -func (ds *DataService) loadData() error { - ds.mutex.Lock() - defer ds.mutex.Unlock() - - file, err := os.Open(ds.filePath) - if err != nil { - return fmt.Errorf("failed to open data file: %w", err) - } - defer file.Close() - - byteValue, err := ioutil.ReadAll(file) - if err != nil { - return fmt.Errorf("failed to read data file: %w", err) - } - - if err := json.Unmarshal(byteValue, &ds.data); err != nil { - return fmt.Errorf("failed to parse data file: %w", err) - } - - log.Printf("Loaded %d nodes and %d edges from %s", - len(ds.data.Nodes), len(ds.data.Edges), ds.filePath) - - return nil -} - -func (ds *DataService) GetGraphData() models.GraphData { - ds.mutex.RLock() - defer ds.mutex.RUnlock() - return ds.data -} - -func (ds *DataService) GetNodeByID(id string) (models.Node, bool) { - ds.mutex.RLock() - defer ds.mutex.RUnlock() - - for _, node := range ds.data.Nodes { - if node.ID == id { - return node, true - } - } - return models.Node{}, false -} - -func (ds *DataService) SearchNodes(query string) []models.Node { - ds.mutex.RLock() - defer ds.mutex.RUnlock() - - if query == "" { - return []models.Node{} - } - - searchLower := strings.ToLower(query) - var results []models.Node - - for _, node := range ds.data.Nodes { - labelMatch := strings.Contains(strings.ToLower(node.Label), searchLower) - idMatch := strings.Contains(strings.ToLower(node.ID), searchLower) - typeMatch := node.Type != "" && strings.Contains(strings.ToLower(node.Type), searchLower) - - if labelMatch || idMatch || typeMatch { - results = append(results, node) - } - } - - return results -} - -func (ds *DataService) GetNeighbors(nodeID string) (models.NeighborResponse, bool) { - ds.mutex.RLock() - defer ds.mutex.RUnlock() - - _, exists := ds.GetNodeByID(nodeID) - if !exists { - return models.NeighborResponse{}, false - } - - var neighborIDs []string - var relatedEdges []models.Edge - - for _, edge := range ds.data.Edges { - if edge.Source == nodeID { - neighborIDs = append(neighborIDs, edge.Target) - relatedEdges = append(relatedEdges, edge) - } else if edge.Target == nodeID { - neighborIDs = append(neighborIDs, edge.Source) - relatedEdges = append(relatedEdges, edge) - } - } - - var neighborNodes []models.Node - for _, node := range ds.data.Nodes { - for _, neighborID := range neighborIDs { - if node.ID == neighborID { - neighborNodes = append(neighborNodes, node) - break - } - } - } - - return models.NeighborResponse{ - Nodes: neighborNodes, - Edges: relatedEdges, - }, true -} - -func (ds *DataService) ReloadData() error { - return ds.loadData() -} - -func (ds *DataService) GetStats() map[string]int { - ds.mutex.RLock() - defer ds.mutex.RUnlock() - - typeCounts := make(map[string]int) - for _, node := range ds.data.Nodes { - if node.Type != "" { - typeCounts[node.Type]++ - } - } - - return map[string]int{ - "totalNodes": len(ds.data.Nodes), - "totalEdges": len(ds.data.Edges), - "conceptNodes": typeCounts["概念"], - "toolNodes": typeCounts["工具"], - "applicationNodes": typeCounts["应用"], - } -} diff --git a/backend/services/service.go b/backend/services/service.go deleted file mode 100644 index 91f2986..0000000 --- a/backend/services/service.go +++ /dev/null @@ -1,20 +0,0 @@ -package services - -import "knowledge-graph-backend/models" - -type GraphService interface { - GetGraphData() models.GraphData - GetSimpleGraphData() models.SimpleGraphData - GetNodeByID(id string) (models.Node, bool) - SearchNodes(query string) []models.Node - GetNeighbors(nodeID string) (models.NeighborResponse, bool) - GetStats() map[string]int - - // 新增增删改接口 - CreateNode(req models.CreateNodeRequest) (models.Node, error) - UpdateNode(id string, req models.UpdateNodeRequest) (models.Node, error) - DeleteNode(id string) error - - CreateEdge(req models.CreateEdgeRequest) (models.Edge, error) - DeleteEdge(id string) error -}