mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-04 23:37:32 +00:00
feat: 全局格式化代码
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import java.util.Map;
|
||||
|
||||
/**
|
||||
* 图关系实体
|
||||
*
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-09-30
|
||||
*/
|
||||
|
||||
@@ -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)
|
||||
*/
|
||||
|
||||
@@ -14,7 +14,7 @@ import java.util.Map;
|
||||
|
||||
/**
|
||||
* 图节点实体
|
||||
*
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2025-09-30
|
||||
*/
|
||||
|
||||
@@ -7,7 +7,6 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.core.domain.BaseEntity;
|
||||
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 是否成功
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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();
|
||||
|
||||
@@ -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())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -16,7 +16,7 @@ import java.util.List;
|
||||
/**
|
||||
* 智谱AI(Zhipu)图谱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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user