feat: 全局格式化代码

This commit is contained in:
evo
2025-12-06 14:38:41 +08:00
parent 96c53390aa
commit 7c7d5838cd
538 changed files with 21132 additions and 14160 deletions

View File

@@ -29,40 +29,40 @@ public class GraphAsyncConfig {
@Bean("graphBuildExecutor")
public Executor graphBuildExecutor() {
log.info("初始化图谱构建线程池...");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数CPU核心数
int processors = Runtime.getRuntime().availableProcessors();
executor.setCorePoolSize(processors);
// 最大线程数CPU核心数 * 2
executor.setMaxPoolSize(processors * 2);
// 队列容量100个任务
executor.setQueueCapacity(100);
// 线程空闲时间60秒
executor.setKeepAliveSeconds(60);
// 线程名称前缀
executor.setThreadNamePrefix("graph-build-");
// 拒绝策略:由调用线程处理
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务完成后关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
// 等待时间60秒
executor.setAwaitTerminationSeconds(60);
// 初始化
executor.initialize();
log.info("图谱构建线程池初始化完成: corePoolSize={}, maxPoolSize={}, queueCapacity={}",
processors, processors * 2, 100);
processors, processors * 2, 100);
return executor;
}
}

View File

@@ -61,12 +61,12 @@ public class Neo4jConfig {
@Bean
public Driver neo4jDriver() {
return GraphDatabase.driver(
uri,
AuthTokens.basic(username, password),
org.neo4j.driver.Config.builder()
.withMaxConnectionPoolSize(maxConnectionPoolSize)
.withConnectionTimeout(connectionTimeoutSeconds, java.util.concurrent.TimeUnit.SECONDS)
.build()
uri,
AuthTokens.basic(username, password),
org.neo4j.driver.Config.builder()
.withMaxConnectionPoolSize(maxConnectionPoolSize)
.withConnectionTimeout(connectionTimeoutSeconds, java.util.concurrent.TimeUnit.SECONDS)
.build()
);
}
}

View File

@@ -62,13 +62,13 @@ public class GraphConstants {
* 默认实体抽取类型列表
*/
public static final String[] DEFAULT_ENTITY_TYPES = {
ENTITY_TYPE_PERSON,
ENTITY_TYPE_ORGANIZATION,
ENTITY_TYPE_LOCATION,
ENTITY_TYPE_CONCEPT,
ENTITY_TYPE_EVENT,
ENTITY_TYPE_PRODUCT,
ENTITY_TYPE_TECHNOLOGY
ENTITY_TYPE_PERSON,
ENTITY_TYPE_ORGANIZATION,
ENTITY_TYPE_LOCATION,
ENTITY_TYPE_CONCEPT,
ENTITY_TYPE_EVENT,
ENTITY_TYPE_PRODUCT,
ENTITY_TYPE_TECHNOLOGY
};
/**

View File

@@ -41,7 +41,7 @@ public class GraphInstanceController extends BaseController {
*/
private GraphInstance getInstanceByIdOrUuid(String id) {
GraphInstance instance = null;
// 尝试作为数字ID查询
try {
Long numericId = Long.parseLong(id);
@@ -50,7 +50,7 @@ public class GraphInstanceController extends BaseController {
// 不是数字尝试作为UUID查询
instance = graphInstanceService.getByUuid(id);
}
return instance;
}
@@ -70,11 +70,11 @@ public class GraphInstanceController extends BaseController {
// 创建基础实例
GraphInstance instance = graphInstanceService.createInstance(
graphInstance.getKnowledgeId(),
graphInstance.getInstanceName(),
graphInstance.getConfig()
graphInstance.getKnowledgeId(),
graphInstance.getInstanceName(),
graphInstance.getConfig()
);
// 设置扩展属性
boolean needUpdate = false;
if (graphInstance.getModelName() != null) {
@@ -93,12 +93,12 @@ public class GraphInstanceController extends BaseController {
instance.setRemark(graphInstance.getRemark());
needUpdate = true;
}
// 如果有扩展属性,更新到数据库
if (needUpdate) {
graphInstanceService.updateInstance(instance);
}
return R.ok(instance);
} catch (Exception e) {
return R.fail("创建图谱实例失败: " + e.getMessage());
@@ -115,20 +115,20 @@ public class GraphInstanceController extends BaseController {
if (graphInstance.getId() == null && (graphInstance.getGraphUuid() == null || graphInstance.getGraphUuid().trim().isEmpty())) {
return R.fail("图谱ID不能为空");
}
// 如果有 instanceName更新基本信息
if (graphInstance.getInstanceName() != null) {
// 这里可以添加更新实例名称的逻辑
}
// 更新配置
if (graphInstance.getConfig() != null) {
graphInstanceService.updateConfig(graphInstance.getGraphUuid(), graphInstance.getConfig());
}
// 更新模型名称、实体类型、关系类型等
// 注意:这里需要在 Service 层实现完整的更新逻辑
GraphInstance instance = graphInstanceService.getByUuid(graphInstance.getGraphUuid());
return R.ok(instance);
} catch (Exception e) {
@@ -144,7 +144,7 @@ public class GraphInstanceController extends BaseController {
public R<GraphInstance> getByUuid(@PathVariable String id) {
try {
GraphInstance instance = getInstanceByIdOrUuid(id);
if (instance == null) {
return R.fail("图谱实例不存在");
}
@@ -168,18 +168,18 @@ public class GraphInstanceController extends BaseController {
try {
// 使用枚举转换前端状态字符串为数字状态码
Integer graphStatus = GraphStatusEnum.getCodeByStatusKey(status);
// 创建分页对象
Page<GraphInstance> page = new Page<>(pageNum, pageSize);
// 调用 Service 层分页查询
Page<GraphInstance> result = graphInstanceService.queryPage(page, instanceName, knowledgeId, graphStatus);
// 构造返回结果
Map<String, Object> data = new HashMap<>();
data.put("rows", result.getRecords());
data.put("total", result.getTotal());
return R.ok(data);
} catch (Exception e) {
return R.fail("获取图谱列表失败: " + e.getMessage());
@@ -262,7 +262,7 @@ public class GraphInstanceController extends BaseController {
if (instance == null) {
return R.fail("图谱实例不存在");
}
boolean success = graphInstanceService.deleteInstance(instance.getGraphUuid());
return success ? R.ok() : R.fail("删除失败");
} catch (Exception e) {
@@ -307,25 +307,25 @@ public class GraphInstanceController extends BaseController {
try {
// 获取图谱实例
GraphInstance instance = getInstanceByIdOrUuid(id);
if (instance == null) {
return R.fail("图谱实例不存在");
}
// 更新状态为构建中
graphInstanceService.updateStatus(instance.getGraphUuid(), 10); // 10=构建中
// 创建构建任务(全量构建)
GraphBuildTask task = buildTaskService.createTask(
instance.getGraphUuid(),
instance.getKnowledgeId(),
null, // docId=null 表示全量构建
1 // taskType=1 全量构建
instance.getGraphUuid(),
instance.getKnowledgeId(),
null, // docId=null 表示全量构建
1 // taskType=1 全量构建
);
// 异步启动任务
buildTaskService.startTask(task.getTaskUuid());
return R.ok(task);
} catch (Exception e) {
return R.fail("启动构建任务失败: " + e.getMessage());
@@ -341,25 +341,25 @@ public class GraphInstanceController extends BaseController {
try {
// 获取图谱实例
GraphInstance instance = getInstanceByIdOrUuid(id);
if (instance == null) {
return R.fail("图谱实例不存在");
}
// 更新状态为构建中
graphInstanceService.updateStatus(instance.getGraphUuid(), 10); // 10=构建中
// 创建重建任务
GraphBuildTask task = buildTaskService.createTask(
instance.getGraphUuid(),
instance.getKnowledgeId(),
null, // docId=null 表示全量
2 // taskType=2 重建
instance.getGraphUuid(),
instance.getKnowledgeId(),
null, // docId=null 表示全量
2 // taskType=2 重建
);
// 异步启动任务
buildTaskService.startTask(task.getTaskUuid());
return R.ok(task);
} catch (Exception e) {
return R.fail("启动重建任务失败: " + e.getMessage());
@@ -375,32 +375,32 @@ public class GraphInstanceController extends BaseController {
try {
// 获取图谱实例
GraphInstance instance = getInstanceByIdOrUuid(id);
if (instance == null) {
return R.fail("图谱实例不存在");
}
// 获取最新的构建任务
GraphBuildTask latestTask = buildTaskService.getLatestTask(instance.getGraphUuid());
Map<String, Object> result = new HashMap<>();
result.put("graphStatus", instance.getGraphStatus());
result.put("nodeCount", instance.getNodeCount());
result.put("relationshipCount", instance.getRelationshipCount());
if (latestTask != null) {
result.put("taskStatus", latestTask.getTaskStatus());
// ⭐ 确保 progress 不为 null前端期望是 number 类型
Integer progress = latestTask.getProgress();
result.put("progress", progress != null ? progress : 0);
result.put("errorMessage", latestTask.getErrorMessage());
// 转换状态字符串(兼容前端)
String status = "NOT_BUILT";
if (instance.getGraphStatus() == 10) status = "BUILDING";
else if (instance.getGraphStatus() == 20) status = "COMPLETED";
else if (instance.getGraphStatus() == 30) status = "FAILED";
result.put("status", status);
} else {
// ⭐ 如果没有任务,也返回默认值
@@ -409,7 +409,7 @@ public class GraphInstanceController extends BaseController {
result.put("errorMessage", null);
result.put("status", "NOT_BUILT");
}
return R.ok(result);
} catch (Exception e) {
return R.fail("获取构建状态失败: " + e.getMessage());

View File

@@ -133,13 +133,13 @@ public class GraphQueryController extends BaseController {
String modelName = request.get("modelName");
GraphExtractionResult result;
if (modelName != null && !modelName.trim().isEmpty()) {
result = graphExtractionService.extractFromTextWithModel(text, modelName);
} else {
result = graphExtractionService.extractFromText(text);
}
return R.ok(result);
} catch (Exception e) {
return R.fail("实体抽取失败: " + e.getMessage());

View File

@@ -45,17 +45,17 @@ public class Neo4jTestController {
public R<Map<String, Object>> getConfig() {
Map<String, Object> config = new HashMap<>();
config.put("neo4j", Map.of(
"uri", neo4jConfig.getUri(),
"username", neo4jConfig.getUsername(),
"database", neo4jConfig.getDatabase(),
"maxConnectionPoolSize", neo4jConfig.getMaxConnectionPoolSize()
"uri", neo4jConfig.getUri(),
"username", neo4jConfig.getUsername(),
"database", neo4jConfig.getDatabase(),
"maxConnectionPoolSize", neo4jConfig.getMaxConnectionPoolSize()
));
config.put("graph", Map.of(
"enabled", graphProperties.getEnabled(),
"databaseType", graphProperties.getDatabaseType(),
"batchSize", graphProperties.getBatchSize(),
"extraction", graphProperties.getExtraction(),
"query", graphProperties.getQuery()
"enabled", graphProperties.getEnabled(),
"databaseType", graphProperties.getDatabaseType(),
"batchSize", graphProperties.getBatchSize(),
"extraction", graphProperties.getExtraction(),
"query", graphProperties.getQuery()
));
return R.ok(config);
}
@@ -87,8 +87,8 @@ public class Neo4jTestController {
@PostMapping("/relationship")
public R<Map<String, Object>> createTestRelationship(
@RequestParam String source,
@RequestParam String target) {
@RequestParam String source,
@RequestParam String target) {
Map<String, Object> result = neo4jTestUtil.createTestRelationship(source, target);

View File

@@ -14,7 +14,7 @@ import java.util.Map;
/**
* 图关系实体
*
*
* @author ruoyi
* @date 2025-09-30
*/

View File

@@ -43,7 +43,7 @@ public class GraphInstance extends BaseEntity {
* 图谱名称
*/
private String graphName;
/**
* 图谱实例名称(前端使用,不映射到数据库)
*/
@@ -69,17 +69,17 @@ public class GraphInstance extends BaseEntity {
* 图谱配置(JSON格式)
*/
private String config;
/**
* LLM模型名称
*/
private String modelName;
/**
* 实体类型(逗号分隔)
*/
private String entityTypes;
/**
* 关系类型(逗号分隔)
*/
@@ -94,19 +94,19 @@ public class GraphInstance extends BaseEntity {
* 删除标志0代表存在 1代表删除
*/
private String delFlag;
/**
* 备注
*/
private String remark;
/**
* 获取实例名称(兼容前端)
*/
public String getInstanceName() {
return instanceName != null ? instanceName : graphName;
}
/**
* 设置实例名称同步到graphName
*/

View File

@@ -14,7 +14,7 @@ import java.util.Map;
/**
* 图节点实体
*
*
* @author ruoyi
* @date 2025-09-30
*/

View File

@@ -7,7 +7,6 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import org.ruoyi.core.domain.BaseEntity;
import java.io.Serial;
/**

View File

@@ -21,23 +21,23 @@ import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
public class GraphLLMServiceFactory implements ApplicationContextAware {
private final Map<String, IGraphLLMService> llmServiceMap = new ConcurrentHashMap<>();
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// 初始化时收集所有 IGraphLLMService 的实现
Map<String, IGraphLLMService> serviceMap = applicationContext.getBeansOfType(IGraphLLMService.class);
for (IGraphLLMService service : serviceMap.values()) {
if (service != null) {
String category = service.getCategory();
llmServiceMap.put(category, service);
log.info("注册图谱LLM服务: category={}, service={}",
category, service.getClass().getSimpleName());
log.info("注册图谱LLM服务: category={}, service={}",
category, service.getClass().getSimpleName());
}
}
log.info("图谱LLM服务工厂初始化完成共注册 {} 个服务", llmServiceMap.size());
}
@@ -50,13 +50,13 @@ public class GraphLLMServiceFactory implements ApplicationContextAware {
*/
public IGraphLLMService getLLMService(String category) {
IGraphLLMService service = llmServiceMap.get(category);
if (service == null) {
log.error("不支持的模型类别: {}, 可用类别: {}", category, llmServiceMap.keySet());
throw new IllegalArgumentException("不支持的模型类别: " + category +
", 可用类别: " + llmServiceMap.keySet());
throw new IllegalArgumentException("不支持的模型类别: " + category +
", 可用类别: " + llmServiceMap.keySet());
}
return service;
}

View File

@@ -43,10 +43,10 @@ public class GraphExtractionListener extends EventSourceListener {
// 解析响应
ChatCompletionResponse completionResponse = objectMapper.readValue(data, ChatCompletionResponse.class);
if (completionResponse != null &&
completionResponse.getChoices() != null &&
!completionResponse.getChoices().isEmpty()) {
if (completionResponse != null &&
completionResponse.getChoices() != null &&
!completionResponse.getChoices().isEmpty()) {
Object content = completionResponse.getChoices().get(0).getDelta().getContent();
if (content != null) {
responseBuilder.append(content);
@@ -79,7 +79,7 @@ public class GraphExtractionListener extends EventSourceListener {
}
log.error("LLM调用失败: {}", errorMsg, t);
responseFuture.completeExceptionally(
new RuntimeException(errorMsg, t)
new RuntimeException(errorMsg, t)
);
}
}

View File

@@ -47,8 +47,8 @@ public interface GraphBuildTaskMapper extends BaseMapper<GraphBuildTask> {
/**
* 更新任务进度
*
* @param taskUuid 任务UUID
* @param progress 进度
* @param taskUuid 任务UUID
* @param progress 进度
* @param processedDocs 已处理文档数
* @return 影响行数
*/
@@ -58,7 +58,7 @@ public interface GraphBuildTaskMapper extends BaseMapper<GraphBuildTask> {
* 更新任务状态
*
* @param taskUuid 任务UUID
* @param status 状态
* @param status 状态
* @return 影响行数
*/
int updateStatus(String taskUuid, Integer status);

View File

@@ -30,8 +30,8 @@ public interface GraphInstanceMapper extends BaseMapper<GraphInstance> {
/**
* 更新节点和关系数量
*
* @param graphUuid 图谱UUID
* @param nodeCount 节点数量
* @param graphUuid 图谱UUID
* @param nodeCount 节点数量
* @param relationshipCount 关系数量
* @return 影响行数
*/
@@ -41,7 +41,7 @@ public interface GraphInstanceMapper extends BaseMapper<GraphInstance> {
* 更新图谱状态
*
* @param graphUuid 图谱UUID
* @param status 状态
* @param status 状态
* @return 影响行数
*/
int updateStatus(String graphUuid, Integer status);

View File

@@ -3,7 +3,6 @@ package org.ruoyi.graph.service;
import org.ruoyi.graph.domain.GraphBuildTask;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* 图谱构建任务服务接口
@@ -16,10 +15,10 @@ public interface IGraphBuildTaskService {
/**
* 创建构建任务
*
* @param graphUuid 图谱UUID
* @param graphUuid 图谱UUID
* @param knowledgeId 知识库ID
* @param docId 文档ID可选
* @param taskType 任务类型
* @param docId 文档ID可选
* @param taskType 任务类型
* @return 任务信息
*/
GraphBuildTask createTask(String graphUuid, String knowledgeId, String docId, Integer taskType);
@@ -74,8 +73,8 @@ public interface IGraphBuildTaskService {
/**
* 更新任务进度
*
* @param taskUuid 任务UUID
* @param progress 进度百分比
* @param taskUuid 任务UUID
* @param progress 进度百分比
* @param processedDocs 已处理文档数
* @return 是否成功
*/
@@ -85,7 +84,7 @@ public interface IGraphBuildTaskService {
* 更新任务状态
*
* @param taskUuid 任务UUID
* @param status 状态
* @param status 状态
* @return 是否成功
*/
boolean updateStatus(String taskUuid, Integer status);
@@ -93,8 +92,8 @@ public interface IGraphBuildTaskService {
/**
* 更新提取统计信息
*
* @param taskUuid 任务UUID
* @param extractedEntities 提取的实体数
* @param taskUuid 任务UUID
* @param extractedEntities 提取的实体数
* @param extractedRelations 提取的关系数
* @return 是否成功
*/
@@ -103,7 +102,7 @@ public interface IGraphBuildTaskService {
/**
* 标记任务为成功
*
* @param taskUuid 任务UUID
* @param taskUuid 任务UUID
* @param resultSummary 结果摘要
* @return 是否成功
*/
@@ -112,7 +111,7 @@ public interface IGraphBuildTaskService {
/**
* 标记任务为失败
*
* @param taskUuid 任务UUID
* @param taskUuid 任务UUID
* @param errorMessage 错误信息
* @return 是否成功
*/

View File

@@ -17,8 +17,8 @@ public interface IGraphInstanceService {
* 创建图谱实例
*
* @param knowledgeId 知识库ID
* @param graphName 图谱名称
* @param config 配置信息
* @param graphName 图谱名称
* @param config 配置信息
* @return 图谱实例
*/
GraphInstance createInstance(String knowledgeId, String graphName, String config);
@@ -58,10 +58,10 @@ public interface IGraphInstanceService {
/**
* 条件查询图谱实例列表(分页)
*
* @param page 分页对象
* @param page 分页对象
* @param instanceName 图谱名称(模糊查询)
* @param knowledgeId 知识库ID
* @param graphStatus 图谱状态码
* @param knowledgeId 知识库ID
* @param graphStatus 图谱状态码
* @return 分页结果
*/
Page<GraphInstance> queryPage(Page<GraphInstance> page, String instanceName, String knowledgeId, Integer graphStatus);
@@ -70,7 +70,7 @@ public interface IGraphInstanceService {
* 更新图谱状态
*
* @param graphUuid 图谱UUID
* @param status 状态
* @param status 状态
* @return 是否成功
*/
boolean updateStatus(String graphUuid, Integer status);
@@ -78,8 +78,8 @@ public interface IGraphInstanceService {
/**
* 更新图谱统计信息
*
* @param graphUuid 图谱UUID
* @param nodeCount 节点数量
* @param graphUuid 图谱UUID
* @param nodeCount 节点数量
* @param relationshipCount 关系数量
* @return 是否成功
*/
@@ -89,7 +89,7 @@ public interface IGraphInstanceService {
* 更新图谱配置
*
* @param graphUuid 图谱UUID
* @param config 配置信息
* @param config 配置信息
* @return 是否成功
*/
boolean updateConfig(String graphUuid, String config);

View File

@@ -16,9 +16,9 @@ public interface IGraphRAGService {
/**
* 将文本入库到图谱
*
* @param text 文本内容
* @param knowledgeId 知识库ID
* @param metadata 元数据
* @param text 文本内容
* @param knowledgeId 知识库ID
* @param metadata 元数据
* @return 抽取结果
*/
GraphExtractionResult ingestText(String text, String knowledgeId, Map<String, Object> metadata);
@@ -26,10 +26,10 @@ public interface IGraphRAGService {
/**
* 将文本入库到图谱(指定模型)
*
* @param text 文本内容
* @param knowledgeId 知识库ID
* @param metadata 元数据
* @param modelName LLM模型名称
* @param text 文本内容
* @param knowledgeId 知识库ID
* @param metadata 元数据
* @param modelName LLM模型名称
* @return 抽取结果
*/
GraphExtractionResult ingestTextWithModel(String text, String knowledgeId, Map<String, Object> metadata, String modelName);

View File

@@ -35,7 +35,7 @@ public interface IGraphStoreService {
/**
* 获取节点信息
*
* @param nodeId 节点ID
* @param nodeId 节点ID
* @param graphUuid 图谱UUID
* @return 节点信息
*/
@@ -45,8 +45,8 @@ public interface IGraphStoreService {
* 根据条件搜索节点
*
* @param graphUuid 图谱UUID
* @param label 节点标签(可选)
* @param limit 返回数量限制
* @param label 节点标签(可选)
* @param limit 返回数量限制
* @return 节点列表
*/
List<GraphVertex> searchVertices(String graphUuid, String label, Integer limit);
@@ -55,7 +55,7 @@ public interface IGraphStoreService {
* 根据名称搜索节点
*
* @param graphUuid 图谱UUID
* @param name 节点名称
* @param name 节点名称
* @return 节点列表
*/
List<GraphVertex> searchVerticesByName(String graphUuid, String name);
@@ -90,8 +90,8 @@ public interface IGraphStoreService {
/**
* 删除节点
*
* @param nodeId 节点ID
* @param graphUuid 图谱UUID
* @param nodeId 节点ID
* @param graphUuid 图谱UUID
* @param includeEdges 是否同时删除相关关系
* @return 是否成功
*/
@@ -118,7 +118,7 @@ public interface IGraphStoreService {
/**
* 获取关系信息
*
* @param edgeId 关系ID
* @param edgeId 关系ID
* @param graphUuid 图谱UUID
* @return 关系信息
*/
@@ -127,10 +127,10 @@ public interface IGraphStoreService {
/**
* 搜索关系
*
* @param graphUuid 图谱UUID
* @param graphUuid 图谱UUID
* @param sourceNodeId 源节点ID可选
* @param targetNodeId 目标节点ID可选
* @param limit 返回数量限制
* @param limit 返回数量限制
* @return 关系列表
*/
List<GraphEdge> searchEdges(String graphUuid, String sourceNodeId, String targetNodeId, Integer limit);
@@ -147,7 +147,7 @@ public interface IGraphStoreService {
/**
* 获取节点的所有关系
*
* @param nodeId 节点ID
* @param nodeId 节点ID
* @param graphUuid 图谱UUID
* @param direction 方向: IN(入边), OUT(出边), BOTH(双向)
* @return 关系列表
@@ -165,7 +165,7 @@ public interface IGraphStoreService {
/**
* 删除关系
*
* @param edgeId 关系ID
* @param edgeId 关系ID
* @param graphUuid 图谱UUID
* @return 是否成功
*/
@@ -220,8 +220,8 @@ public interface IGraphStoreService {
*
* @param sourceNodeId 源节点ID
* @param targetNodeId 目标节点ID
* @param graphUuid 图谱UUID
* @param maxDepth 最大深度
* @param graphUuid 图谱UUID
* @param maxDepth 最大深度
* @return 路径列表
*/
List<List<GraphVertex>> findPaths(String sourceNodeId, String targetNodeId, String graphUuid, Integer maxDepth);
@@ -239,9 +239,9 @@ public interface IGraphStoreService {
/**
* 查找节点的邻居节点
*
* @param nodeId 节点ID
* @param nodeId 节点ID
* @param graphUuid 图谱UUID
* @param depth 深度(几度关系)
* @param depth 深度(几度关系)
* @return 邻居节点列表
*/
List<GraphVertex> findNeighbors(String nodeId, String graphUuid, Integer depth);

View File

@@ -26,7 +26,6 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
/**
* 图谱构建任务服务实现
@@ -62,7 +61,7 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
taskMapper.insert(task);
log.info("创建图谱构建任务: taskId={}, taskUuid={}, graphUuid={}, knowledgeId={}, type={}",
task.getId(), task.getTaskUuid(), graphUuid, knowledgeId, task.getTaskType());
task.getId(), task.getTaskUuid(), graphUuid, knowledgeId, task.getTaskType());
return task;
}
@@ -73,9 +72,9 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
// 记录线程信息
String threadName = Thread.currentThread().getName();
log.info("🚀 图谱构建任务启动 - taskUuid: {}, 线程: {}", taskUuid, threadName);
long startTime = System.currentTimeMillis();
try {
// 1. 验证任务存在性
GraphBuildTask task = getByUuid(taskUuid);
@@ -83,11 +82,11 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
log.error("❌ 任务不存在: taskUuid={}", taskUuid);
return;
}
// 2. 检查任务状态(防止重复执行)
if (task.getTaskStatus() != 1) { // 1-待处理
log.warn("⚠️ 任务状态不允许执行: taskUuid={}, currentStatus={}",
taskUuid, task.getTaskStatus());
log.warn("⚠️ 任务状态不允许执行: taskUuid={}, currentStatus={}",
taskUuid, task.getTaskStatus());
return;
}
@@ -97,35 +96,35 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
log.error("❌ 更新任务状态失败: taskUuid={}", taskUuid);
return;
}
log.info("✅ 任务状态已更新为运行中: taskUuid={}", taskUuid);
// 4. 执行图谱构建逻辑
try {
executeTaskLogic(task);
long duration = (System.currentTimeMillis() - startTime) / 1000;
log.info("🎉 图谱构建任务完成: taskUuid={}, 耗时: {}秒, 线程: {}",
taskUuid, duration, threadName);
log.info("🎉 图谱构建任务完成: taskUuid={}, 耗时: {}秒, 线程: {}",
taskUuid, duration, threadName);
} catch (OutOfMemoryError oom) {
// 特殊处理OOM错误
log.error("💥 图谱构建任务内存溢出: taskUuid={}, 线程: {}", taskUuid, threadName, oom);
markFailed(taskUuid, "内存溢出请减少批处理文档数量或增加JVM内存");
// 建议垃圾回收
System.gc();
} catch (InterruptedException ie) {
// 特殊处理中断异常
Thread.currentThread().interrupt();
log.error("⚠️ 图谱构建任务被中断: taskUuid={}, 线程: {}", taskUuid, threadName, ie);
markFailed(taskUuid, "任务被中断: " + ie.getMessage());
} catch (Exception e) {
// 处理其他业务异常
log.error("❌ 图谱构建任务执行失败: taskUuid={}, 线程: {}", taskUuid, threadName, e);
// 提取简洁的错误信息
String errorMsg = extractErrorMessage(e);
markFailed(taskUuid, errorMsg);
@@ -134,7 +133,7 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
} catch (Exception e) {
// 处理外层异常(如数据库访问异常)
log.error("❌ 图谱构建任务启动失败: taskUuid={}, 线程: {}", taskUuid, threadName, e);
try {
String errorMsg = extractErrorMessage(e);
markFailed(taskUuid, errorMsg);
@@ -143,10 +142,10 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
}
}
}
/**
* 提取简洁的错误信息(用于前端显示)
*
*
* @param e 异常对象
* @return 简洁的错误信息
*/
@@ -156,7 +155,7 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
if (StrUtil.isNotBlank(message) && message.length() < 200) {
return message;
}
// 2. 检查原因链
Throwable cause = e.getCause();
if (cause != null && StrUtil.isNotBlank(cause.getMessage())) {
@@ -165,10 +164,10 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
return causeMsg;
}
}
// 3. 使用异常类名
return e.getClass().getSimpleName() + ": " +
(message != null ? message.substring(0, Math.min(150, message.length())) : "未知错误");
return e.getClass().getSimpleName() + ": " +
(message != null ? message.substring(0, Math.min(150, message.length())) : "未知错误");
}
@Override
@@ -223,8 +222,8 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
}
int rows = taskMapper.update(null, wrapper);
log.info("📊 更新任务进度: taskUuid={}, progress={}%, processedDocs={}, rows={}",
taskUuid, progress, processedDocs, rows);
log.info("📊 更新任务进度: taskUuid={}, progress={}%, processedDocs={}, rows={}",
taskUuid, progress, processedDocs, rows);
return rows > 0;
} catch (Exception e) {
log.error("更新任务进度失败: taskUuid={}, progress={}", taskUuid, progress, e);
@@ -381,10 +380,10 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
// 创建新任务
GraphBuildTask newTask = createTask(
oldTask.getGraphUuid(),
oldTask.getKnowledgeId(),
oldTask.getDocId(),
oldTask.getTaskType()
oldTask.getGraphUuid(),
oldTask.getKnowledgeId(),
oldTask.getDocId(),
oldTask.getTaskType()
);
log.info("重试任务: oldTaskUuid={}, newTaskUuid={}", taskUuid, newTask.getTaskUuid());
@@ -419,9 +418,9 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
// ⭐ 记录初始内存状态
Runtime runtime = Runtime.getRuntime();
long initialMemory = runtime.totalMemory() - runtime.freeMemory();
log.info("📊 初始内存使用: {} MB / {} MB",
initialMemory / 1024 / 1024,
runtime.maxMemory() / 1024 / 1024);
log.info("📊 初始内存使用: {} MB / {} MB",
initialMemory / 1024 / 1024,
runtime.maxMemory() / 1024 / 1024);
try {
// 0. 获取图谱实例配置包括LLM模型
@@ -433,7 +432,7 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
log.info("使用图谱实例配置的模型: {}", modelName);
}
}
// 1. 获取需要处理的文档列表
List<KnowledgeAttachVo> documents;
@@ -491,19 +490,19 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
if (documents == null || documents.isEmpty()) {
String errorMsg = String.format(
"❌ 没有找到需要处理的文档!\n" +
" taskUuid: %s\n" +
" knowledgeId: %s\n" +
" docId: %s\n" +
" taskType: %d\n" +
" documents: %s\n" +
"请检查:\n" +
" 1. knowledge_attach 表中是否有 kid='%s' 的记录\n" +
" 2. knowledgeId 是否正确传递\n" +
" 3. KnowledgeAttachService.queryList() 是否正确执行",
taskUuid, knowledgeId, docId, taskType,
documents == null ? "null" : "empty list",
knowledgeId
"❌ 没有找到需要处理的文档!\n" +
" taskUuid: %s\n" +
" knowledgeId: %s\n" +
" docId: %s\n" +
" taskType: %d\n" +
" documents: %s\n" +
"请检查:\n" +
" 1. knowledge_attach 表中是否有 kid='%s' 的记录\n" +
" 2. knowledgeId 是否正确传递\n" +
" 3. KnowledgeAttachService.queryList() 是否正确执行",
taskUuid, knowledgeId, docId, taskType,
documents == null ? "null" : "empty list",
knowledgeId
);
log.warn(errorMsg);
@@ -532,7 +531,7 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
log.warn("文档数量较多({}个),建议分批处理,当前批次限制为{}个", totalDocs, maxDocsPerBatch);
documents = documents.subList(0, Math.min(maxDocsPerBatch, totalDocs));
totalDocs = documents.size();
// ⭐ 重新更新 total_docs因为被限制了
LambdaUpdateWrapper<GraphBuildTask> updateWrapper2 = new LambdaUpdateWrapper<>();
updateWrapper2.eq(GraphBuildTask::getTaskUuid, taskUuid);
@@ -545,18 +544,18 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
for (int i = 0; i < documents.size(); i++) {
KnowledgeAttachVo doc = documents.get(i);
long docStartTime = System.currentTimeMillis();
try {
// ⭐ 检查内存状态
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
long maxMemory = runtime.maxMemory();
double memoryUsage = (double) usedMemory / maxMemory * 100;
if (memoryUsage > 80) {
log.warn("⚠️ 内存使用率过高: {}/{}MB ({}%), 建议垃圾回收",
usedMemory / 1024 / 1024,
maxMemory / 1024 / 1024,
String.format("%.2f", memoryUsage));
log.warn("⚠️ 内存使用率过高: {}/{}MB ({}%), 建议垃圾回收",
usedMemory / 1024 / 1024,
maxMemory / 1024 / 1024,
String.format("%.2f", memoryUsage));
System.gc();
try {
Thread.sleep(1000); // 等待GC完成
@@ -565,9 +564,9 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
log.warn("⚠️ 等待GC时被中断");
}
}
log.info("📄 处理文档 [{}/{}]: docId={}, docName={}",
i + 1, totalDocs, doc.getDocId(), doc.getDocName());
log.info("📄 处理文档 [{}/{}]: docId={}, docName={}",
i + 1, totalDocs, doc.getDocId(), doc.getDocName());
// 2.1 获取文档内容
String content = doc.getContent();
@@ -580,8 +579,8 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
// 限制单个文档内容大小,避免内存溢出
if (content.length() > 50000) {
log.warn("⚠️ 文档内容过大({} 字符),截断处理: docId={}",
content.length(), doc.getDocId());
log.warn("⚠️ 文档内容过大({} 字符),截断处理: docId={}",
content.length(), doc.getDocId());
content = content.substring(0, 50000);
}
@@ -598,11 +597,11 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
if (content.length() > 2000) {
// 长文档,使用分片处理
result = graphRAGService.ingestDocumentWithModel(
content, knowledgeId, metadata, modelName);
content, knowledgeId, metadata, modelName);
} else {
// 短文档,直接处理
result = graphRAGService.ingestTextWithModel(
content, knowledgeId, metadata, modelName);
content, knowledgeId, metadata, modelName);
}
} catch (OutOfMemoryError oom) {
// OOM单独处理强制GC后继续
@@ -620,8 +619,8 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
failedDocs++;
continue;
} catch (Exception e) {
log.error("❌ LLM调用失败跳过文档: docId={}, error={}",
doc.getDocId(), e.getMessage());
log.error("❌ LLM调用失败跳过文档: docId={}, error={}",
doc.getDocId(), e.getMessage());
processedDocs++;
failedDocs++;
continue;
@@ -634,14 +633,14 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
totalEntities += entities;
totalRelations += relations;
successDocs++;
long docDuration = System.currentTimeMillis() - docStartTime;
log.info("✅ 文档处理成功: docId={}, 实体数={}, 关系数={}, 耗时={}ms",
doc.getDocId(), entities, relations, docDuration);
doc.getDocId(), entities, relations, docDuration);
} else {
failedDocs++;
log.warn("⚠️ 文档处理失败: docId={}, error={}",
doc.getDocId(), result != null ? result.getErrorMessage() : "unknown");
doc.getDocId(), result != null ? result.getErrorMessage() : "unknown");
}
// 2.5 更新进度
@@ -656,8 +655,8 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
// 2.6 定期进行垃圾回收和内存检查
if ((i + 1) % 10 == 0) {
long currentMemory = runtime.totalMemory() - runtime.freeMemory();
log.info("📊 已处理{}/{}个文档, 内存使用: {} MB",
i + 1, totalDocs, currentMemory / 1024 / 1024);
log.info("📊 已处理{}/{}个文档, 内存使用: {} MB",
i + 1, totalDocs, currentMemory / 1024 / 1024);
System.gc();
try {
Thread.sleep(500); // 短暂等待GC
@@ -673,8 +672,8 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
log.error("⚠️ 任务被中断,停止处理文档: docId={}", doc.getDocId());
throw ie;
} catch (Exception e) {
log.error("❌ 处理文档时发生异常: docId={}, error={}",
doc.getDocId(), e.getMessage(), e);
log.error("❌ 处理文档时发生异常: docId={}, error={}",
doc.getDocId(), e.getMessage(), e);
processedDocs++;
failedDocs++;
// 继续处理下一个文档(不中断整个任务)
@@ -687,7 +686,7 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
// 3. 构建完成,生成详细摘要
long duration = (System.currentTimeMillis() - startTime) / 1000;
long finalMemory = runtime.totalMemory() - runtime.freeMemory();
Map<String, Object> summary = new HashMap<>();
summary.put("totalDocs", totalDocs);
summary.put("processedDocs", processedDocs);
@@ -703,7 +702,7 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
// 更新统计信息到任务
updateExtractionStats(taskUuid, totalEntities, totalRelations);
markSuccess(taskUuid, JSON.toJSONString(summary));
log.info("🎉 图谱构建任务完成汇总:");
@@ -728,9 +727,9 @@ public class GraphBuildTaskServiceImpl implements IGraphBuildTaskService {
} finally {
// 清理资源帮助GC
System.gc();
log.info("📊 最终内存状态: {} MB / {} MB",
(runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024,
runtime.maxMemory() / 1024 / 1024);
log.info("📊 最终内存状态: {} MB / {} MB",
(runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024,
runtime.maxMemory() / 1024 / 1024);
}
}
}

View File

@@ -35,38 +35,36 @@ import java.util.regex.Pattern;
@ConditionalOnProperty(prefix = "knowledge.graph", name = "enabled", havingValue = "true")
public class GraphExtractionServiceImpl implements IGraphExtractionService {
private final IChatModelService chatModelService;
private final GraphLLMServiceFactory llmServiceFactory;
/**
* 实体匹配正则表达式
* 格式: ("entity"<|>ENTITY_NAME<|>ENTITY_TYPE<|>ENTITY_DESCRIPTION)
*/
private static final Pattern ENTITY_PATTERN = Pattern.compile(
"\\(\"entity\"" +
Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) +
"([^" + Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) + "]+)" +
Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) +
"([^" + Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) + "]+)" +
Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) +
"([^)]+)\\)"
"\\(\"entity\"" +
Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) +
"([^" + Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) + "]+)" +
Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) +
"([^" + Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) + "]+)" +
Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) +
"([^)]+)\\)"
);
/**
* 关系匹配正则表达式
* 格式: ("relationship"<|>SOURCE<|>TARGET<|>DESCRIPTION<|>STRENGTH)
*/
private static final Pattern RELATION_PATTERN = Pattern.compile(
"\\(\"relationship\"" +
Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) +
"([^" + Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) + "]+)" +
Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) +
"([^" + Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) + "]+)" +
Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) +
"([^" + Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) + "]+)" +
Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) +
"([^)]+)\\)"
"\\(\"relationship\"" +
Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) +
"([^" + Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) + "]+)" +
Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) +
"([^" + Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) + "]+)" +
Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) +
"([^" + Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) + "]+)" +
Pattern.quote(GraphConstants.GRAPH_TUPLE_DELIMITER) +
"([^)]+)\\)"
);
private final IChatModelService chatModelService;
private final GraphLLMServiceFactory llmServiceFactory;
@Override
public GraphExtractionResult extractFromText(String text) {
@@ -90,18 +88,18 @@ public class GraphExtractionServiceImpl implements IGraphExtractionService {
result.setSuccess(true);
log.info("抽取完成,实体数: {}, 关系数: {}",
result.getEntities().size(), result.getRelations().size());
result.getEntities().size(), result.getRelations().size());
return result;
} catch (Exception e) {
log.error("实体关系抽取失败", e);
return GraphExtractionResult.builder()
.entities(new ArrayList<>())
.relations(new ArrayList<>())
.success(false)
.errorMessage(e.getMessage())
.build();
.entities(new ArrayList<>())
.relations(new ArrayList<>())
.success(false)
.errorMessage(e.getMessage())
.build();
}
}
@@ -129,13 +127,13 @@ public class GraphExtractionServiceImpl implements IGraphExtractionService {
result.setSuccess(true);
log.info("抽取完成,实体数: {}, 关系数: {}, 使用模型: {}",
result.getEntities().size(), result.getRelations().size(), modelName);
result.getEntities().size(), result.getRelations().size(), modelName);
// ⭐ 调试:如果没有关系,记录原始响应(便于诊断)
if (result.getRelations().isEmpty() && !result.getEntities().isEmpty()) {
log.warn("⚠️ LLM 提取到 {} 个实体,但没有提取到任何关系!", result.getEntities().size());
log.warn("LLM 原始响应预览前500字符: {}",
llmResponse.length() > 500 ? llmResponse.substring(0, 500) + "..." : llmResponse);
log.warn("LLM 原始响应预览前500字符: {}",
llmResponse.length() > 500 ? llmResponse.substring(0, 500) + "..." : llmResponse);
}
return result;
@@ -143,11 +141,11 @@ public class GraphExtractionServiceImpl implements IGraphExtractionService {
} catch (Exception e) {
log.error("实体关系抽取失败,模型: {}", modelName, e);
return GraphExtractionResult.builder()
.entities(new ArrayList<>())
.relations(new ArrayList<>())
.success(false)
.errorMessage(e.getMessage())
.build();
.entities(new ArrayList<>())
.relations(new ArrayList<>())
.success(false)
.errorMessage(e.getMessage())
.build();
}
}
@@ -161,11 +159,11 @@ public class GraphExtractionServiceImpl implements IGraphExtractionService {
if (StrUtil.isBlank(response)) {
log.warn("响应为空,无法解析");
return GraphExtractionResult.builder()
.entities(entities)
.relations(relations)
.success(false)
.errorMessage("LLM响应为空")
.build();
.entities(entities)
.relations(relations)
.success(false)
.errorMessage("LLM响应为空")
.build();
}
try {
@@ -183,10 +181,10 @@ public class GraphExtractionServiceImpl implements IGraphExtractionService {
}
ExtractedEntity entity = ExtractedEntity.builder()
.name(name)
.type(type)
.description(description)
.build();
.name(name)
.type(type)
.description(description)
.build();
entities.add(entity);
log.debug("解析到实体: name={}, type={}", name, type);
@@ -204,34 +202,34 @@ public class GraphExtractionServiceImpl implements IGraphExtractionService {
Double confidence = calculateConfidence(strength);
ExtractedRelation relation = ExtractedRelation.builder()
.sourceEntity(sourceEntity)
.targetEntity(targetEntity)
.description(description)
.strength(strength)
.confidence(confidence)
.build();
.sourceEntity(sourceEntity)
.targetEntity(targetEntity)
.description(description)
.strength(strength)
.confidence(confidence)
.build();
relations.add(relation);
log.debug("解析到关系: sourceEntity={}, targetEntity={}, strength={}",
sourceEntity, targetEntity, strength);
sourceEntity, targetEntity, strength);
}
log.info("解析完成,实体数: {}, 关系数: {}", entities.size(), relations.size());
return GraphExtractionResult.builder()
.entities(entities)
.relations(relations)
.success(true)
.build();
.entities(entities)
.relations(relations)
.success(true)
.build();
} catch (Exception e) {
log.error("解析图谱响应失败", e);
return GraphExtractionResult.builder()
.entities(entities)
.relations(relations)
.success(false)
.errorMessage("解析失败: " + e.getMessage())
.build();
.entities(entities)
.relations(relations)
.success(false)
.errorMessage("解析失败: " + e.getMessage())
.build();
}
}
@@ -251,12 +249,12 @@ public class GraphExtractionServiceImpl implements IGraphExtractionService {
defaultModel = models.get(0);
}
}
if (defaultModel == null) {
log.error("未找到可用的LLM模型");
throw new RuntimeException("未找到可用的LLM模型请先配置聊天模型");
}
log.info("使用默认模型: {}", defaultModel.getModelName());
return callLLMWithModel(prompt, defaultModel);
}
@@ -270,24 +268,24 @@ public class GraphExtractionServiceImpl implements IGraphExtractionService {
*/
private String callLLMWithModel(String prompt, ChatModelVo chatModel) {
log.info("调用LLM模型: model={}, category={}, 提示词长度={}",
chatModel.getModelName(), chatModel.getCategory(), prompt.length());
chatModel.getModelName(), chatModel.getCategory(), prompt.length());
try {
// 根据模型类别获取对应的LLM服务实现
IGraphLLMService llmService = llmServiceFactory.getLLMService(chatModel.getCategory());
// 调用LLM进行图谱抽取
String responseText = llmService.extractGraph(prompt, chatModel);
log.info("LLM调用成功: model={}, category={}, 响应长度={}",
chatModel.getModelName(), chatModel.getCategory(), responseText.length());
chatModel.getModelName(), chatModel.getCategory(), responseText.length());
return responseText;
} catch (IllegalArgumentException e) {
// 不支持的模型类别,降级到默认实现
log.warn("不支持的模型类别: {}, 尝试使用OpenAI兼容模式", chatModel.getCategory());
try {
IGraphLLMService openAiService = llmServiceFactory.getLLMService("openai");
return openAiService.extractGraph(prompt, chatModel);
@@ -295,7 +293,7 @@ public class GraphExtractionServiceImpl implements IGraphExtractionService {
log.error("降级调用也失败: {}", fallbackEx.getMessage(), fallbackEx);
throw new RuntimeException("LLM调用失败: " + fallbackEx.getMessage(), fallbackEx);
}
} catch (Exception e) {
log.error("LLM调用失败: {}", e.getMessage(), e);
throw new RuntimeException("LLM调用失败: " + e.getMessage(), e);
@@ -333,26 +331,26 @@ public class GraphExtractionServiceImpl implements IGraphExtractionService {
if ("N/A".equalsIgnoreCase(name) || "N/A".equalsIgnoreCase(type)) {
return true;
}
// 2. 检查是否为空或纯空格
if (StrUtil.isBlank(name) || StrUtil.isBlank(type)) {
return true;
}
// 3. 检查类型是否包含 Neo4j Label 不支持的字符
// Neo4j Label 规则:不能包含 / : & | 等特殊字符
if (type.matches(".*[/:&|\\\\].*")) {
log.warn("⚠️ 实体类型包含非法字符,将被过滤: type={}", type);
return true;
}
// 4. 检查名称是否过长Neo4j 建议 < 256
if (name.length() > 255 || type.length() > 64) {
log.warn("⚠️ 实体名称或类型过长,将被过滤: name.length={}, type.length={}",
name.length(), type.length());
log.warn("⚠️ 实体名称或类型过长,将被过滤: name.length={}, type.length={}",
name.length(), type.length());
return true;
}
return false;
}

View File

@@ -7,8 +7,6 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.ruoyi.common.core.utils.StringUtils;
import org.ruoyi.graph.domain.GraphInstance;
import org.ruoyi.graph.mapper.GraphInstanceMapper;
@@ -108,28 +106,28 @@ public class GraphInstanceServiceImpl implements IGraphInstanceService {
@Override
public Page<GraphInstance> queryPage(Page<GraphInstance> page, String instanceName, String knowledgeId, Integer graphStatus) {
LambdaQueryWrapper<GraphInstance> wrapper = new LambdaQueryWrapper<>();
// 图谱名称模糊查询
if (StringUtils.isNotBlank(instanceName)) {
wrapper.like(GraphInstance::getGraphName, instanceName.trim());
}
// 知识库ID精确查询
if (StringUtils.isNotBlank(knowledgeId)) {
wrapper.eq(GraphInstance::getKnowledgeId, knowledgeId.trim());
}
// 状态精确查询
if (graphStatus != null) {
wrapper.eq(GraphInstance::getGraphStatus, graphStatus);
}
// 只查询未删除的记录
wrapper.eq(GraphInstance::getDelFlag, "0");
// 按创建时间倒序
wrapper.orderByDesc(GraphInstance::getCreateTime);
return graphInstanceMapper.selectPage(page, wrapper);
}
@@ -166,7 +164,7 @@ public class GraphInstanceServiceImpl implements IGraphInstanceService {
int rows = graphInstanceMapper.update(null, wrapper);
log.info("更新图谱统计: graphUuid={}, nodeCount={}, relationshipCount={}, rows={}",
graphUuid, nodeCount, relationshipCount, rows);
graphUuid, nodeCount, relationshipCount, rows);
return rows > 0;
} catch (Exception e) {
log.error("更新图谱统计失败: graphUuid={}", graphUuid, e);
@@ -196,16 +194,16 @@ public class GraphInstanceServiceImpl implements IGraphInstanceService {
public boolean deleteInstance(String graphUuid) {
try {
log.info("🗑️ 开始删除图谱实例及数据graphUuid: {}", graphUuid);
// ⭐ 1. 先获取实例信息获取knowledgeId
GraphInstance instance = getByUuid(graphUuid);
if (instance == null) {
log.warn("⚠️ 图谱实例不存在: graphUuid={}", graphUuid);
return false;
}
String knowledgeId = instance.getKnowledgeId();
// ⭐ 2. 删除Neo4j中的图数据通过knowledgeId
if (StrUtil.isNotBlank(knowledgeId)) {
log.info("删除Neo4j图数据knowledgeId: {}", knowledgeId);
@@ -218,14 +216,14 @@ public class GraphInstanceServiceImpl implements IGraphInstanceService {
} else {
log.warn("⚠️ 实例没有关联知识库ID跳过Neo4j数据删除");
}
// 3. 删除MySQL中的实例记录
LambdaQueryWrapper<GraphInstance> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(GraphInstance::getGraphUuid, graphUuid);
int rows = graphInstanceMapper.delete(wrapper);
log.info("✅ 删除图谱实例成功: graphUuid={}, knowledgeId={}, rows={}",
graphUuid, knowledgeId, rows);
log.info("✅ 删除图谱实例成功: graphUuid={}, knowledgeId={}, rows={}",
graphUuid, knowledgeId, rows);
return rows > 0;
} catch (Exception e) {
log.error("❌ 删除图谱实例失败: graphUuid={}", graphUuid, e);
@@ -244,7 +242,7 @@ public class GraphInstanceServiceImpl implements IGraphInstanceService {
boolean instanceDeleted = deleteInstance(graphUuid);
log.info("删除图谱实例及数据: graphUuid={}, graphDeleted={}, instanceDeleted={}",
graphUuid, graphDeleted, instanceDeleted);
graphUuid, graphDeleted, instanceDeleted);
return graphDeleted && instanceDeleted;
} catch (Exception e) {
@@ -262,9 +260,9 @@ public class GraphInstanceServiceImpl implements IGraphInstanceService {
// 更新到 MySQL异步
if (stats.containsKey("nodeCount") && stats.containsKey("relationshipCount")) {
updateCounts(
graphUuid,
(Integer) stats.get("nodeCount"),
(Integer) stats.get("relationshipCount")
graphUuid,
(Integer) stats.get("nodeCount"),
(Integer) stats.get("relationshipCount")
);
}

View File

@@ -41,7 +41,7 @@ public class GraphRAGServiceImpl implements IGraphRAGService {
@Override
public GraphExtractionResult ingestTextWithModel(String text, String knowledgeId, Map<String, Object> metadata, String modelName) {
log.info("开始将文本入库到图谱知识库ID: {}, 模型: {}, 文本长度: {}",
knowledgeId, modelName != null ? modelName : "默认", text.length());
knowledgeId, modelName != null ? modelName : "默认", text.length());
try {
// 1. 从文本中抽取实体和关系
@@ -59,9 +59,9 @@ public class GraphRAGServiceImpl implements IGraphRAGService {
// 2. 将抽取的实体转换为图节点
List<GraphVertex> vertices = convertEntitiesToVertices(
extractionResult.getEntities(),
knowledgeId,
metadata
extractionResult.getEntities(),
knowledgeId,
metadata
);
// 3. 批量添加节点到Neo4j并建立实体名称→nodeId的映射
@@ -69,7 +69,7 @@ public class GraphRAGServiceImpl implements IGraphRAGService {
if (!vertices.isEmpty()) {
int addedCount = graphStoreService.addVertices(vertices);
log.info("成功添加 {} 个节点到图谱", addedCount);
// ⭐ 建立映射:实体名称 → nodeId
for (GraphVertex vertex : vertices) {
entityNameToNodeIdMap.put(vertex.getName(), vertex.getNodeId());
@@ -79,10 +79,10 @@ public class GraphRAGServiceImpl implements IGraphRAGService {
// 4. 将抽取的关系转换为图边使用映射填充nodeId
List<GraphEdge> edges = convertRelationsToEdges(
extractionResult.getRelations(),
knowledgeId,
metadata,
entityNameToNodeIdMap // ⭐ 传入映射
extractionResult.getRelations(),
knowledgeId,
metadata,
entityNameToNodeIdMap // ⭐ 传入映射
);
// 5. 批量添加关系到Neo4j
@@ -96,11 +96,11 @@ public class GraphRAGServiceImpl implements IGraphRAGService {
} catch (Exception e) {
log.error("文本入库失败", e);
return GraphExtractionResult.builder()
.entities(new ArrayList<>())
.relations(new ArrayList<>())
.success(false)
.errorMessage(e.getMessage())
.build();
.entities(new ArrayList<>())
.relations(new ArrayList<>())
.success(false)
.errorMessage(e.getMessage())
.build();
}
}
@@ -112,7 +112,7 @@ public class GraphRAGServiceImpl implements IGraphRAGService {
@Override
public GraphExtractionResult ingestDocumentWithModel(String documentText, String knowledgeId, Map<String, Object> metadata, String modelName) {
log.info("开始将文档入库到图谱知识库ID: {}, 模型: {}, 文档长度: {}",
knowledgeId, modelName != null ? modelName : "默认", documentText.length());
knowledgeId, modelName != null ? modelName : "默认", documentText.length());
// 如果文档较短,直接处理
if (documentText.length() < GraphConstants.RAG_MAX_SEGMENT_SIZE_IN_TOKENS * 4) {
@@ -153,11 +153,11 @@ public class GraphRAGServiceImpl implements IGraphRAGService {
log.info("去重后实体数: {} -> {}", allEntities.size(), uniqueEntities.size());
return GraphExtractionResult.builder()
.entities(uniqueEntities)
.relations(allRelations)
.tokenUsed(totalTokenUsed)
.success(true)
.build();
.entities(uniqueEntities)
.relations(allRelations)
.tokenUsed(totalTokenUsed)
.success(true)
.build();
}
@Override
@@ -177,7 +177,7 @@ public class GraphRAGServiceImpl implements IGraphRAGService {
List<GraphVertex> matchedNodes = new ArrayList<>();
for (String keyword : keywords) {
List<GraphVertex> nodes = graphStoreService.searchVerticesByName(
keyword, knowledgeId, Math.min(5, maxResults)
keyword, knowledgeId, Math.min(5, maxResults)
);
matchedNodes.addAll(nodes);
}
@@ -216,15 +216,15 @@ public class GraphRAGServiceImpl implements IGraphRAGService {
// 获取邻居节点1跳
List<GraphVertex> neighbors = graphStoreService.getNeighbors(
node.getNodeId(), knowledgeId, 5
node.getNodeId(), knowledgeId, 5
);
if (!neighbors.isEmpty()) {
result.append(" 关联实体: ");
List<String> neighborNames = neighbors.stream()
.map(GraphVertex::getName)
.limit(5)
.collect(java.util.stream.Collectors.toList());
.map(GraphVertex::getName)
.limit(5)
.collect(java.util.stream.Collectors.toList());
result.append(String.join(", ", neighborNames));
result.append("\n");
}
@@ -262,8 +262,8 @@ public class GraphRAGServiceImpl implements IGraphRAGService {
// 3. 过滤停用词和短词
Set<String> stopWords = new HashSet<>(java.util.Arrays.asList(
"", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", ""
"", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", ""
));
for (String word : words) {
@@ -285,9 +285,9 @@ public class GraphRAGServiceImpl implements IGraphRAGService {
// 去重并限制数量
return keywords.stream()
.distinct()
.limit(5)
.collect(java.util.stream.Collectors.toList());
.distinct()
.limit(5)
.collect(java.util.stream.Collectors.toList());
}
@Override
@@ -335,10 +335,10 @@ public class GraphRAGServiceImpl implements IGraphRAGService {
/**
* 将抽取的关系转换为图边
*
* @param relations 抽取的关系列表
* @param knowledgeId 知识库ID
* @param metadata 元数据
*
* @param relations 抽取的关系列表
* @param knowledgeId 知识库ID
* @param metadata 元数据
* @param entityNameToNodeIdMap 实体名称到节点ID的映射
* @return 图边列表
*/
@@ -358,8 +358,8 @@ public class GraphRAGServiceImpl implements IGraphRAGService {
// 如果找不到对应的节点ID跳过这个关系
if (sourceNodeId == null || targetNodeId == null) {
log.warn("⚠️ 跳过关系(节点未找到): {} -> {}",
relation.getSourceEntity(), relation.getTargetEntity());
log.warn("⚠️ 跳过关系(节点未找到): {} -> {}",
relation.getSourceEntity(), relation.getTargetEntity());
skippedCount++;
continue;
}
@@ -415,13 +415,13 @@ public class GraphRAGServiceImpl implements IGraphRAGService {
}
chunks.add(text.substring(start, end));
// ⭐ 修复死循环:确保 start 一定会增加
// 如果已经到达文本末尾,直接退出
if (end >= text.length()) {
break;
}
// 计算下一个起始位置确保至少前进1个字符
int nextStart = end - overlap;
if (nextStart <= start) {

View File

@@ -15,7 +15,7 @@ import java.util.concurrent.TimeUnit;
/**
* DeepSeek 图谱LLM服务实现
* 支持 DeepSeek 系列模型
*
* <p>
* 注意:使用 langchain4j 的 OpenAiStreamingChatModel通过 CompletableFuture 转换为同步调用
* 参考 DeepSeekChatImpl 的实现,但改为同步模式
*
@@ -29,18 +29,18 @@ public class DeepSeekGraphLLMServiceImpl implements IGraphLLMService {
@Override
public String extractGraph(String prompt, ChatModelVo chatModel) {
log.info("DeepSeek模型调用: model={}, apiHost={}, 提示词长度={}",
chatModel.getModelName(), chatModel.getApiHost(), prompt.length());
chatModel.getModelName(), chatModel.getApiHost(), prompt.length());
try {
// 使用 langchain4j 的 OpenAiStreamingChatModel参考 DeepSeekChatImpl
StreamingChatModel streamingModel = OpenAiStreamingChatModel.builder()
.baseUrl(chatModel.getApiHost())
.apiKey(chatModel.getApiKey())
.modelName(chatModel.getModelName())
.temperature(0.8)
.logRequests(false)
.logResponses(false)
.build();
.baseUrl(chatModel.getApiHost())
.apiKey(chatModel.getApiKey())
.modelName(chatModel.getModelName())
.temperature(0.8)
.logRequests(false)
.logResponses(false)
.build();
// 用于收集完整响应
StringBuilder fullResponse = new StringBuilder();

View File

@@ -20,7 +20,7 @@ import java.util.concurrent.TimeUnit;
/**
* Dify 图谱LLM服务实现
* 支持 Dify 平台的对话模型
*
* <p>
* 注意Dify 使用流式调用,通过 CompletableFuture 实现同步等待
*
* @author ruoyi
@@ -33,26 +33,26 @@ public class DifyGraphLLMServiceImpl implements IGraphLLMService {
@Override
public String extractGraph(String prompt, ChatModelVo chatModel) {
log.info("Dify模型调用: model={}, apiHost={}, 提示词长度={}",
chatModel.getModelName(), chatModel.getApiHost(), prompt.length());
chatModel.getModelName(), chatModel.getApiHost(), prompt.length());
try {
// 创建 Dify 客户端配置
DifyConfig config = DifyConfig.builder()
.baseUrl(chatModel.getApiHost())
.apiKey(chatModel.getApiKey())
.connectTimeout(5000)
.readTimeout(120000) // 2分钟超时
.writeTimeout(30000)
.build();
.baseUrl(chatModel.getApiHost())
.apiKey(chatModel.getApiKey())
.connectTimeout(5000)
.readTimeout(120000) // 2分钟超时
.writeTimeout(30000)
.build();
DifyClient chatClient = DifyClientFactory.createClient(config);
// 创建聊天消息(使用流式模式)
ChatMessage message = ChatMessage.builder()
.query(prompt)
.user("graph-system") // 图谱系统用户
.responseMode(ResponseMode.STREAMING) // 流式模式
.build();
.query(prompt)
.user("graph-system") // 图谱系统用户
.responseMode(ResponseMode.STREAMING) // 流式模式
.build();
// 用于收集完整响应
StringBuilder fullResponse = new StringBuilder();
@@ -70,8 +70,8 @@ public class DifyGraphLLMServiceImpl implements IGraphLLMService {
public void onMessageEnd(MessageEndEvent event) {
long duration = System.currentTimeMillis() - startTime;
String responseText = fullResponse.toString();
log.info("Dify模型响应成功: 耗时={}ms, 响应长度={}, messageId={}",
duration, responseText.length(), event.getMessageId());
log.info("Dify模型响应成功: 耗时={}ms, 响应长度={}, messageId={}",
duration, responseText.length(), event.getMessageId());
responseFuture.complete(responseText);
}
@@ -79,7 +79,7 @@ public class DifyGraphLLMServiceImpl implements IGraphLLMService {
public void onError(ErrorEvent event) {
log.error("Dify模型调用错误: {}", event.getMessage());
responseFuture.completeExceptionally(
new RuntimeException("Dify调用错误: " + event.getMessage())
new RuntimeException("Dify调用错误: " + event.getMessage())
);
}

View File

@@ -27,29 +27,29 @@ public class OpenAIGraphLLMServiceImpl implements IGraphLLMService {
@Override
public String extractGraph(String prompt, ChatModelVo chatModel) {
log.info("OpenAI模型调用: model={}, apiHost={}, 提示词长度={}",
chatModel.getModelName(), chatModel.getApiHost(), prompt.length());
chatModel.getModelName(), chatModel.getApiHost(), prompt.length());
try {
// 创建 OpenAiStreamClient
OpenAiStreamClient client = ChatConfig.createOpenAiStreamClient(
chatModel.getApiHost(),
chatModel.getApiKey()
chatModel.getApiHost(),
chatModel.getApiKey()
);
// 构建消息
List<Message> messages = Collections.singletonList(
Message.builder()
.role(Message.Role.USER)
.content(prompt)
.build()
Message.builder()
.role(Message.Role.USER)
.content(prompt)
.build()
);
// 构建请求(非流式,同步调用)
ChatCompletion completion = ChatCompletion.builder()
.messages(messages)
.model(chatModel.getModelName())
.stream(false) // 同步调用
.build();
.messages(messages)
.model(chatModel.getModelName())
.stream(false) // 同步调用
.build();
// 同步调用 LLM
long startTime = System.currentTimeMillis();
@@ -59,7 +59,7 @@ public class OpenAIGraphLLMServiceImpl implements IGraphLLMService {
// 提取响应文本
Object content = response.getChoices().get(0).getMessage().getContent();
String responseText = content != null ? content.toString() : "";
log.info("OpenAI模型响应成功: 耗时={}ms, 响应长度={}", duration, responseText.length());
return responseText;

View File

@@ -16,7 +16,7 @@ import java.util.List;
/**
* 通义千问Qwen图谱LLM服务实现
* 支持阿里云通义千问系列模型
*
* <p>
* 注意通义千问的API与OpenAI兼容因此可以复用 OpenAiStreamClient
*
* @author ruoyi
@@ -29,29 +29,29 @@ public class QwenGraphLLMServiceImpl implements IGraphLLMService {
@Override
public String extractGraph(String prompt, ChatModelVo chatModel) {
log.info("Qwen模型调用: model={}, apiHost={}, 提示词长度={}",
chatModel.getModelName(), chatModel.getApiHost(), prompt.length());
chatModel.getModelName(), chatModel.getApiHost(), prompt.length());
try {
// 通义千问API与OpenAI兼容可以直接使用 OpenAiStreamClient
OpenAiStreamClient client = ChatConfig.createOpenAiStreamClient(
chatModel.getApiHost(),
chatModel.getApiKey()
chatModel.getApiHost(),
chatModel.getApiKey()
);
// 构建消息
List<Message> messages = Collections.singletonList(
Message.builder()
.role(Message.Role.USER)
.content(prompt)
.build()
Message.builder()
.role(Message.Role.USER)
.content(prompt)
.build()
);
// 构建请求(非流式,同步调用)
ChatCompletion completion = ChatCompletion.builder()
.messages(messages)
.model(chatModel.getModelName())
.stream(false) // 同步调用
.build();
.messages(messages)
.model(chatModel.getModelName())
.stream(false) // 同步调用
.build();
// 同步调用 LLM
long startTime = System.currentTimeMillis();
@@ -61,7 +61,7 @@ public class QwenGraphLLMServiceImpl implements IGraphLLMService {
// 提取响应文本
Object content = response.getChoices().get(0).getMessage().getContent();
String responseText = content != null ? content.toString() : "";
log.info("Qwen模型响应成功: 耗时={}ms, 响应长度={}", duration, responseText.length());
return responseText;

View File

@@ -16,7 +16,7 @@ import java.util.List;
/**
* 智谱AIZhipu图谱LLM服务实现
* 支持智谱AI系列模型GLM-4等
*
* <p>
* 注意智谱AI的API与OpenAI兼容因此可以复用 OpenAiStreamClient
*
* @author ruoyi
@@ -29,29 +29,29 @@ public class ZhipuGraphLLMServiceImpl implements IGraphLLMService {
@Override
public String extractGraph(String prompt, ChatModelVo chatModel) {
log.info("Zhipu模型调用: model={}, apiHost={}, 提示词长度={}",
chatModel.getModelName(), chatModel.getApiHost(), prompt.length());
chatModel.getModelName(), chatModel.getApiHost(), prompt.length());
try {
// 智谱AI API与OpenAI兼容可以直接使用 OpenAiStreamClient
OpenAiStreamClient client = ChatConfig.createOpenAiStreamClient(
chatModel.getApiHost(),
chatModel.getApiKey()
chatModel.getApiHost(),
chatModel.getApiKey()
);
// 构建消息
List<Message> messages = Collections.singletonList(
Message.builder()
.role(Message.Role.USER)
.content(prompt)
.build()
Message.builder()
.role(Message.Role.USER)
.content(prompt)
.build()
);
// 构建请求(非流式,同步调用)
ChatCompletion completion = ChatCompletion.builder()
.messages(messages)
.model(chatModel.getModelName())
.stream(false) // 同步调用
.build();
.messages(messages)
.model(chatModel.getModelName())
.stream(false) // 同步调用
.build();
// 同步调用 LLM
long startTime = System.currentTimeMillis();
@@ -61,7 +61,7 @@ public class ZhipuGraphLLMServiceImpl implements IGraphLLMService {
// 提取响应文本
Object content = response.getChoices().get(0).getMessage().getContent();
String responseText = content != null ? content.toString() : "";
log.info("Zhipu模型响应成功: 耗时={}ms, 响应长度={}", duration, responseText.length());
return responseText;

View File

@@ -38,7 +38,7 @@ public class Neo4jTestUtil {
*/
public Map<String, Object> testConnection() {
Map<String, Object> result = new HashMap<>();
try (Session session = driver.session()) {
// 1. 测试基本连接
Result pingResult = session.run("RETURN 1 as num");
@@ -47,7 +47,7 @@ public class Neo4jTestUtil {
result.put("connection", "SUCCESS");
result.put("pingResult", record.get("num").asInt());
}
// 2. 获取数据库信息
Result dbInfoResult = session.run("CALL dbms.components() YIELD name, versions, edition");
if (dbInfoResult.hasNext()) {
@@ -55,16 +55,16 @@ public class Neo4jTestUtil {
result.put("neo4jVersion", dbInfo.get("versions").asList().get(0));
result.put("neo4jEdition", dbInfo.get("edition").asString());
}
// 3. 测试节点数量
Result countResult = session.run("MATCH (n) RETURN count(n) as count");
if (countResult.hasNext()) {
result.put("totalNodes", countResult.single().get("count").asInt());
}
log.info("Neo4j连接测试成功: {}", result);
return result;
} catch (Exception e) {
log.error("Neo4j连接测试失败", e);
result.put("connection", "FAILED");
@@ -81,27 +81,27 @@ public class Neo4jTestUtil {
*/
public Map<String, Object> createTestNode(String name) {
Map<String, Object> result = new HashMap<>();
try (Session session = driver.session()) {
Result queryResult = session.run(
"CREATE (p:TestNode {name: $name, createTime: datetime()}) RETURN p",
parameters("name", name)
"CREATE (p:TestNode {name: $name, createTime: datetime()}) RETURN p",
parameters("name", name)
);
if (queryResult.hasNext()) {
Record record = queryResult.single();
Node node = record.get("p").asNode();
result.put("success", true);
result.put("nodeId", node.elementId());
result.put("labels", node.labels());
result.put("properties", node.asMap());
log.info("测试节点创建成功: {}", result);
}
return result;
} catch (Exception e) {
log.error("创建测试节点失败", e);
result.put("success", false);
@@ -118,26 +118,26 @@ public class Neo4jTestUtil {
*/
public Map<String, Object> queryTestNode(String name) {
Map<String, Object> result = new HashMap<>();
try (Session session = driver.session()) {
Result queryResult = session.run(
"MATCH (p:TestNode {name: $name}) RETURN p",
parameters("name", name)
"MATCH (p:TestNode {name: $name}) RETURN p",
parameters("name", name)
);
if (queryResult.hasNext()) {
Record record = queryResult.single();
Node node = record.get("p").asNode();
result.put("found", true);
result.put("nodeId", node.elementId());
result.put("properties", node.asMap());
} else {
result.put("found", false);
}
return result;
} catch (Exception e) {
log.error("查询测试节点失败", e);
result.put("error", e.getMessage());
@@ -152,21 +152,21 @@ public class Neo4jTestUtil {
*/
public Map<String, Object> deleteAllTestNodes() {
Map<String, Object> result = new HashMap<>();
try (Session session = driver.session()) {
Result queryResult = session.run(
"MATCH (p:TestNode) DELETE p RETURN count(p) as deleted"
"MATCH (p:TestNode) DELETE p RETURN count(p) as deleted"
);
if (queryResult.hasNext()) {
int deleted = queryResult.single().get("deleted").asInt();
result.put("success", true);
result.put("deletedCount", deleted);
log.info("删除测试节点数量: {}", deleted);
}
return result;
} catch (Exception e) {
log.error("删除测试节点失败", e);
result.put("success", false);
@@ -184,33 +184,33 @@ public class Neo4jTestUtil {
*/
public Map<String, Object> createTestRelationship(String sourceName, String targetName) {
Map<String, Object> result = new HashMap<>();
try (Session session = driver.session()) {
// 先创建两个节点
session.run(
"MERGE (s:TestNode {name: $sourceName}) " +
"MERGE (t:TestNode {name: $targetName})",
parameters("sourceName", sourceName, "targetName", targetName)
"MERGE (s:TestNode {name: $sourceName}) " +
"MERGE (t:TestNode {name: $targetName})",
parameters("sourceName", sourceName, "targetName", targetName)
);
// 创建关系
Result queryResult = session.run(
"MATCH (s:TestNode {name: $sourceName}) " +
"MATCH (t:TestNode {name: $targetName}) " +
"CREATE (s)-[r:TEST_RELATION {createTime: datetime()}]->(t) " +
"RETURN r",
parameters("sourceName", sourceName, "targetName", targetName)
"MATCH (s:TestNode {name: $sourceName}) " +
"MATCH (t:TestNode {name: $targetName}) " +
"CREATE (s)-[r:TEST_RELATION {createTime: datetime()}]->(t) " +
"RETURN r",
parameters("sourceName", sourceName, "targetName", targetName)
);
if (queryResult.hasNext()) {
result.put("success", true);
result.put("source", sourceName);
result.put("target", targetName);
log.info("测试关系创建成功: {} -> {}", sourceName, targetName);
}
return result;
} catch (Exception e) {
log.error("创建测试关系失败", e);
result.put("success", false);
@@ -226,19 +226,19 @@ public class Neo4jTestUtil {
*/
public Map<String, Object> getStatistics() {
Map<String, Object> result = new HashMap<>();
try (Session session = driver.session()) {
// 节点总数
Result nodeCountResult = session.run("MATCH (n) RETURN count(n) as count");
result.put("totalNodes", nodeCountResult.single().get("count").asInt());
// 关系总数
Result relCountResult = session.run("MATCH ()-[r]->() RETURN count(r) as count");
result.put("totalRelationships", relCountResult.single().get("count").asInt());
// 节点标签分布
Result labelResult = session.run(
"MATCH (n) RETURN labels(n) as label, count(*) as count ORDER BY count DESC LIMIT 10"
"MATCH (n) RETURN labels(n) as label, count(*) as count ORDER BY count DESC LIMIT 10"
);
Map<String, Integer> labelDistribution = new HashMap<>();
labelResult.stream().forEach(record -> {
@@ -247,10 +247,10 @@ public class Neo4jTestUtil {
labelDistribution.put(label, count);
});
result.put("labelDistribution", labelDistribution);
log.info("Neo4j统计信息: {}", result);
return result;
} catch (Exception e) {
log.error("获取统计信息失败", e);
result.put("error", e.getMessage());
@@ -266,53 +266,53 @@ public class Neo4jTestUtil {
*/
public Map<String, Object> debugRelationships(String knowledgeId) {
Map<String, Object> result = new HashMap<>();
try (Session session = driver.session()) {
log.info("🔍 开始调试关系查询 - knowledgeId: {}", knowledgeId);
// 1. 检查该知识库的节点数量
Result nodeCountResult = session.run(
"MATCH (n {knowledgeId: $knowledgeId}) RETURN count(n) as count",
parameters("knowledgeId", knowledgeId)
"MATCH (n {knowledgeId: $knowledgeId}) RETURN count(n) as count",
parameters("knowledgeId", knowledgeId)
);
int nodeCount = nodeCountResult.single().get("count").asInt();
result.put("nodeCount", nodeCount);
log.info("✅ 该知识库节点数量: {}", nodeCount);
// 2. 检查总关系数量(不限制知识库)
Result totalRelResult = session.run("MATCH ()-[r]->() RETURN count(r) as count");
int totalRelCount = totalRelResult.single().get("count").asInt();
result.put("totalRelationships", totalRelCount);
log.info("✅ 数据库总关系数量: {}", totalRelCount);
// 3. 检查带 knowledgeId 的关系数量(旧查询方式)
Result relWithKnowledgeIdResult = session.run(
"MATCH ()-[r {knowledgeId: $knowledgeId}]->() RETURN count(r) as count",
parameters("knowledgeId", knowledgeId)
"MATCH ()-[r {knowledgeId: $knowledgeId}]->() RETURN count(r) as count",
parameters("knowledgeId", knowledgeId)
);
int relWithKnowledgeId = relWithKnowledgeIdResult.single().get("count").asInt();
result.put("relationshipsWithKnowledgeId", relWithKnowledgeId);
log.info("✅ 带 knowledgeId 的关系数量: {}", relWithKnowledgeId);
// 4. 通过节点过滤关系数量(新查询方式)
Result relByNodesResult = session.run(
"MATCH (s {knowledgeId: $knowledgeId})-[r]->(t {knowledgeId: $knowledgeId}) RETURN count(r) as count",
parameters("knowledgeId", knowledgeId)
"MATCH (s {knowledgeId: $knowledgeId})-[r]->(t {knowledgeId: $knowledgeId}) RETURN count(r) as count",
parameters("knowledgeId", knowledgeId)
);
int relByNodes = relByNodesResult.single().get("count").asInt();
result.put("relationshipsByNodes", relByNodes);
log.info("✅ 通过节点过滤的关系数量: {}", relByNodes);
// 5. 采样前5个关系详情如果有
if (relByNodes > 0) {
Result sampleResult = session.run(
"MATCH (s {knowledgeId: $knowledgeId})-[r]->(t {knowledgeId: $knowledgeId}) " +
"RETURN s.name as sourceName, s.id as sourceId, type(r) as relType, " +
"t.name as targetName, t.id as targetId, r.knowledgeId as relKnowledgeId " +
"LIMIT 5",
parameters("knowledgeId", knowledgeId)
"MATCH (s {knowledgeId: $knowledgeId})-[r]->(t {knowledgeId: $knowledgeId}) " +
"RETURN s.name as sourceName, s.id as sourceId, type(r) as relType, " +
"t.name as targetName, t.id as targetId, r.knowledgeId as relKnowledgeId " +
"LIMIT 5",
parameters("knowledgeId", knowledgeId)
);
java.util.List<Map<String, Object>> samples = new java.util.ArrayList<>();
sampleResult.stream().forEach(record -> {
Map<String, Object> sample = new HashMap<>();
@@ -321,25 +321,25 @@ public class Neo4jTestUtil {
sample.put("relationType", record.get("relType").asString());
sample.put("targetName", record.get("targetName").asString());
sample.put("targetId", record.get("targetId").asString());
sample.put("relationshipKnowledgeId",
record.get("relKnowledgeId").isNull() ? null : record.get("relKnowledgeId").asString());
sample.put("relationshipKnowledgeId",
record.get("relKnowledgeId").isNull() ? null : record.get("relKnowledgeId").asString());
samples.add(sample);
});
result.put("sampleRelationships", samples);
log.info("✅ 采样到 {} 个关系", samples.size());
}
// 6. 检查是否有孤立节点(没有任何关系的节点)
Result isolatedNodesResult = session.run(
"MATCH (n {knowledgeId: $knowledgeId}) " +
"WHERE NOT (n)-[]-() " +
"RETURN count(n) as count",
parameters("knowledgeId", knowledgeId)
"MATCH (n {knowledgeId: $knowledgeId}) " +
"WHERE NOT (n)-[]-() " +
"RETURN count(n) as count",
parameters("knowledgeId", knowledgeId)
);
int isolatedNodes = isolatedNodesResult.single().get("count").asInt();
result.put("isolatedNodes", isolatedNodes);
log.info("✅ 孤立节点数量: {}", isolatedNodes);
// 7. 诊断结论
Map<String, String> diagnosis = new HashMap<>();
if (nodeCount == 0) {
@@ -356,10 +356,10 @@ public class Neo4jTestUtil {
diagnosis.put("message", "关系数据正常,可以正常查询");
}
result.put("diagnosis", diagnosis);
log.info("🎯 调试完成: {}", diagnosis);
return result;
} catch (Exception e) {
log.error("❌ 调试关系查询失败", e);
result.put("error", e.getMessage());