feat: 添加Neo4j图数据库支持及前端代码重构

- 新增 Neo4j 图数据库 handler、service、model
- 后端添加 SaveGraph API 接口
- 前端 Database.vue 重构,拆分为独立组件
- 新增 web/src/views/database/ 组件目录
- 删除临时文件 (temp_*.go)
- 添加 Neo4j 相关 API 需求文档

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 09:11:08 +08:00
parent 20015dbd2a
commit c917d6b04c
41 changed files with 4453 additions and 1021 deletions

View File

@@ -259,6 +259,27 @@ func (s *DatabaseService) Check(req model.CheckRequest) (*model.CheckResponse, e
var db *sql.DB
var err error
// Neo4j 处理
if dbType == "neo4j" {
log.Printf("[Check] 检测到 Neo4j 类型,使用图数据库连接...")
neo4jService := NewNeo4jService(s.repo)
graph, err := neo4jService.GetGraphOverview(req)
if err != nil {
log.Printf("[Check] Neo4j 连接失败: %v", err)
return &model.CheckResponse{
Success: false,
Message: fmt.Sprintf("neo4j connection failed: %v", err),
}, nil
}
log.Printf("[Check] Neo4j 连接成功,获取到 %d 个标签", len(graph.Labels))
return &model.CheckResponse{
Success: true,
Message: "connection successful",
Graphs: graph,
Database: req.Database,
}, nil
}
switch dbType {
case "mysql":
db, err = sql.Open("mysql", dsn)
@@ -684,6 +705,92 @@ func (s *DatabaseService) Update(id string, req model.UpdateDatabaseRequest) (*m
return s.repo.FindByID(id)
}
// SaveGraph 保存图谱信息
func (s *DatabaseService) SaveGraph(req model.SaveGraphRequest) (*model.SaveGraphResponse, error) {
log.Printf("[SaveGraph] 保存图谱信息, databaseId=%s, databaseName=%s", req.DatabaseID, req.DatabaseName)
// 检查数据库是否存在
_, err := s.repo.FindByID(req.DatabaseID)
if err != nil {
// 如果不存在,创建一个新的
if err == ErrDatabaseNotFound {
// 创建新的数据库记录
dbType := "neo4j"
// 从 URI 解析 host 和 port
host := "localhost"
port := 7687
if req.URI != "" {
uri := strings.TrimPrefix(req.URI, "bolt://")
uri = strings.TrimPrefix(uri, "neo4j://")
if idx := strings.Index(uri, ":"); idx > 0 {
host = uri[:idx]
fmt.Sscanf(uri[idx+1:], "%d", &port)
}
}
// 将 labels 和 relationshipTypes 转为 JSON 字符串
labelsJSON, _ := json.Marshal(req.Labels)
relJSON, _ := json.Marshal(req.RelationshipTypes)
info := &model.DatabaseInfo{
ID: req.DatabaseID,
Name: req.DatabaseName,
DBType: dbType,
Host: host,
Port: port,
Username: req.Username,
URI: req.URI,
GraphLabels: string(labelsJSON),
GraphRelationship: string(relJSON),
SelectedLabel: req.SelectedLabel,
}
if err := s.repo.Create(info); err != nil {
log.Printf("[SaveGraph] 创建失败: %v", err)
return &model.SaveGraphResponse{
Success: false,
Message: fmt.Sprintf("创建失败: %v", err),
}, err
}
return &model.SaveGraphResponse{
Success: true,
Message: "保存成功",
}, nil
}
log.Printf("[SaveGraph] 查询失败: %v", err)
return &model.SaveGraphResponse{
Success: false,
Message: fmt.Sprintf("查询失败: %v", err),
}, err
}
// 更新现有记录
labelsJSON, _ := json.Marshal(req.Labels)
relJSON, _ := json.Marshal(req.RelationshipTypes)
updates := map[string]interface{}{
"uri": req.URI,
"username": req.Username,
"graph_labels": string(labelsJSON),
"graph_relationship": string(relJSON),
"selected_label": req.SelectedLabel,
}
if err := s.repo.UpdateFields(req.DatabaseID, updates); err != nil {
log.Printf("[SaveGraph] 更新失败: %v", err)
return &model.SaveGraphResponse{
Success: false,
Message: fmt.Sprintf("更新失败: %v", err),
}, err
}
return &model.SaveGraphResponse{
Success: true,
Message: "保存成功",
}, nil
}
// fillFieldMappings 填充字段映射到表结构中
func (s *DatabaseService) fillFieldMappings(databaseID string, tables []model.TableDDLInfo) {
// 从数据库中获取该数据库下所有子表的字段映射