diff --git a/src/main/java/com/supervision/neo4j/service/Neo4jService.java b/src/main/java/com/supervision/neo4j/service/Neo4jService.java index 77fa0c6..b9a25ac 100644 --- a/src/main/java/com/supervision/neo4j/service/Neo4jService.java +++ b/src/main/java/com/supervision/neo4j/service/Neo4jService.java @@ -3,8 +3,10 @@ package com.supervision.neo4j.service; import com.supervision.common.domain.R; import com.supervision.neo4j.domain.CaseNode; import com.supervision.neo4j.domain.Rel; +import org.neo4j.driver.Record; import java.util.List; +import java.util.Map; /** * @author qmy @@ -42,4 +44,6 @@ public interface Neo4jService { void createAbstractGraph(String path, String sheetName); void mockTestGraph(String path, String sheetName, String recordId, String recordSplitId,String caseId); + + List executeCypher(String cypher, Map parameters); } diff --git a/src/main/java/com/supervision/neo4j/service/impl/Neo4jServiceImpl.java b/src/main/java/com/supervision/neo4j/service/impl/Neo4jServiceImpl.java index 74a649b..3219196 100644 --- a/src/main/java/com/supervision/neo4j/service/impl/Neo4jServiceImpl.java +++ b/src/main/java/com/supervision/neo4j/service/impl/Neo4jServiceImpl.java @@ -526,4 +526,9 @@ public class Neo4jServiceImpl implements Neo4jService { } } } + + @Override + public List executeCypher(String cypher,Map parameters) { + return this.driver.session().run(cypher, parameters).list(); + } } diff --git a/src/main/java/com/supervision/police/controller/ModelIndexController.java b/src/main/java/com/supervision/police/controller/ModelIndexController.java index 2106cbb..ae05036 100644 --- a/src/main/java/com/supervision/police/controller/ModelIndexController.java +++ b/src/main/java/com/supervision/police/controller/ModelIndexController.java @@ -5,8 +5,10 @@ import com.supervision.police.domain.ModelAtomicIndex; import com.supervision.police.domain.ModelIndex; import com.supervision.police.dto.CaseAtomicIndexDTO; import com.supervision.police.dto.CaseAtomicResultWrapper; +import com.supervision.police.vo.GraphDebugReqVO; import com.supervision.police.dto.ValueCalculateScopeDTO; import com.supervision.police.service.ModelIndexService; +import com.supervision.police.vo.GraphDebugResVO; import com.supervision.police.vo.ModelIndexReqVO; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -140,5 +142,13 @@ public class ModelIndexController { Map> result = modelIndexService.listAtomicIndexAttributeScope(atomicIndexIds); return R.ok(result); } + + @Operation(summary = "图谱推理调试") + @PostMapping("/atomic/debug/graph") + public R graphDebug(@RequestBody GraphDebugReqVO graphDebugDTO) { + + GraphDebugResVO result = modelIndexService.graphDebug(graphDebugDTO); + return R.ok(result); + } } diff --git a/src/main/java/com/supervision/police/dto/neo4j/NodeDTO.java b/src/main/java/com/supervision/police/dto/neo4j/NodeDTO.java new file mode 100644 index 0000000..3bc977c --- /dev/null +++ b/src/main/java/com/supervision/police/dto/neo4j/NodeDTO.java @@ -0,0 +1,30 @@ +package com.supervision.police.dto.neo4j; + +import lombok.Data; +import org.neo4j.driver.internal.InternalNode; + +import java.util.Collection; +import java.util.Map; + +@Data +public class NodeDTO { + + private long id; + + private String elementId; + + private Map properties; + + private Collection labels; + + + public NodeDTO() { + } + + public NodeDTO(InternalNode internalNode) { + this.id = internalNode.id(); + this.elementId = internalNode.elementId(); + this.properties = internalNode.asMap(); + this.labels = internalNode.labels(); + } +} diff --git a/src/main/java/com/supervision/police/dto/neo4j/PathDTO.java b/src/main/java/com/supervision/police/dto/neo4j/PathDTO.java new file mode 100644 index 0000000..09b3e6f --- /dev/null +++ b/src/main/java/com/supervision/police/dto/neo4j/PathDTO.java @@ -0,0 +1,41 @@ +package com.supervision.police.dto.neo4j; + +import lombok.Data; +import org.neo4j.driver.internal.InternalNode; +import org.neo4j.driver.internal.InternalRelationship; +import org.neo4j.driver.types.Node; +import org.neo4j.driver.types.Path; +import org.neo4j.driver.types.Relationship; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +@Data +public class PathDTO { + + private List nodes; + + private List relationships; + + public PathDTO() { + } + + public PathDTO(Path path) { + Iterator nodeIterator = path.nodes().iterator(); + List nodes = new ArrayList<>(); + while (nodeIterator.hasNext()){ + Node next = nodeIterator.next(); + nodes.add(new NodeDTO((InternalNode) next)); + } + this.nodes = nodes; + + + Iterator iterator = path.relationships().iterator(); + List relationships = new ArrayList<>(); + while (iterator.hasNext()){ + relationships.add(new RelationshipValueDTO((InternalRelationship) iterator.next())); + } + this.relationships = relationships; + } +} diff --git a/src/main/java/com/supervision/police/dto/neo4j/RelationshipValueDTO.java b/src/main/java/com/supervision/police/dto/neo4j/RelationshipValueDTO.java new file mode 100644 index 0000000..c1dbc49 --- /dev/null +++ b/src/main/java/com/supervision/police/dto/neo4j/RelationshipValueDTO.java @@ -0,0 +1,42 @@ +package com.supervision.police.dto.neo4j; + +import lombok.Data; +import org.neo4j.driver.internal.InternalRelationship; +import java.util.Map; + +@Data +public class RelationshipValueDTO { + + + private long start; + + private String startElementId; + + private long end; + + private String endElementId; + + private String type; + + private long id; + + private String elementId; + + private Map properties; + + + public RelationshipValueDTO() { + } + + public RelationshipValueDTO(InternalRelationship relationship) { + this.start = (int) relationship.startNodeId(); + this.startElementId = relationship.startNodeElementId(); + this.end = relationship.endNodeId(); + this.endElementId = relationship.endNodeElementId(); + this.type = relationship.type(); + this.id = relationship.id(); + this.elementId = relationship.elementId(); + this.properties = relationship.asMap(); + + } +} diff --git a/src/main/java/com/supervision/police/service/ModelIndexService.java b/src/main/java/com/supervision/police/service/ModelIndexService.java index ee59894..f297c00 100644 --- a/src/main/java/com/supervision/police/service/ModelIndexService.java +++ b/src/main/java/com/supervision/police/service/ModelIndexService.java @@ -7,6 +7,8 @@ import com.supervision.police.domain.ModelIndex; import com.supervision.police.dto.CaseAtomicIndexDTO; import com.supervision.police.dto.CaseAtomicResultWrapper; import com.supervision.police.dto.ValueCalculateScopeDTO; +import com.supervision.police.vo.GraphDebugReqVO; +import com.supervision.police.vo.GraphDebugResVO; import com.supervision.police.vo.ModelIndexReqVO; import java.util.List; @@ -55,5 +57,7 @@ public interface ModelIndexService extends IService { boolean checkSql(String sql); Map> listAtomicIndexAttributeScope(List atomicIndexIds); + + GraphDebugResVO graphDebug(GraphDebugReqVO graphDebugDTO); } diff --git a/src/main/java/com/supervision/police/service/impl/ModelIndexServiceImpl.java b/src/main/java/com/supervision/police/service/impl/ModelIndexServiceImpl.java index 980fc5c..513ffa4 100644 --- a/src/main/java/com/supervision/police/service/impl/ModelIndexServiceImpl.java +++ b/src/main/java/com/supervision/police/service/impl/ModelIndexServiceImpl.java @@ -7,7 +7,6 @@ import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import com.alibaba.druid.sql.ast.SQLStatement; -import com.alibaba.druid.sql.dialect.mysql.parser.MySqlSelectParser; import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; @@ -20,20 +19,32 @@ import com.supervision.common.domain.R; import com.supervision.common.utils.IPages; import com.supervision.common.utils.StringUtils; import com.supervision.constant.DataStatus; +import com.supervision.neo4j.dto.WebRelDTO; +import com.supervision.neo4j.service.Neo4jService; import com.supervision.police.domain.*; import com.supervision.police.dto.*; import com.supervision.police.dto.indexRule.IndexRule; import com.supervision.police.dto.indexRule.RuleCondition; import com.supervision.police.dto.indexRule.RuleConditionGroup; +import com.supervision.police.dto.neo4j.NodeDTO; +import com.supervision.police.dto.neo4j.PathDTO; +import com.supervision.police.dto.neo4j.RelationshipValueDTO; import com.supervision.police.mapper.CasePersonMapper; import com.supervision.police.mapper.ModelAtomicResultMapper; import com.supervision.police.mapper.ModelIndexMapper; import com.supervision.police.service.*; +import com.supervision.police.vo.GraphDebugReqVO; +import com.supervision.police.vo.GraphDebugResVO; import com.supervision.police.vo.ModelIndexReqVO; import com.supervision.utils.JudgeLogicUtil; import com.supervision.utils.SqlParserUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.neo4j.driver.Record; +import org.neo4j.driver.internal.InternalNode; +import org.neo4j.driver.internal.InternalRelationship; +import org.neo4j.driver.types.Node; +import org.neo4j.driver.types.Relationship; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -75,6 +86,8 @@ public class ModelIndexServiceImpl extends ServiceImpl allowedTables; + private final Neo4jService neo4jService; + @Override @Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = Exception.class) public R selectAll(ModelIndexReqVO modelIndex, Integer page, Integer size) { @@ -400,6 +413,161 @@ public class ModelIndexServiceImpl extends ServiceImpl(CasePerson.class).eq(CasePerson::getCaseId, graphDebugDTO.getCaseId()).eq(CasePerson::getRoleCode, "1")); + Map parameters = new HashMap<>(); + parameters.put("caseId", graphDebugDTO.getCaseId()); + if (null != casePerson){ + parameters.put("lawActor", casePerson.getId()); + } + + // 执行查询语句 + List records; + try { + records = this.neo4jService.executeCypher(graphDebugDTO.getQueryLang(), parameters); + } catch (Exception e) { + graphDebugResVO.setMessageType("1"); + graphDebugResVO.setMessage(e.getMessage()); + return graphDebugResVO; + } + + graphDebugResVO.setRecordList(mapRecords(records)); + graphDebugResVO.setGraphRelList(generateRelations(records)); + graphDebugResVO.setGraphNodeList(generateNodes(records)); + + // 尝试转换为图形数据 + return graphDebugResVO; + } + + + private List generateRelations(List records) { + + List graphRelList = records.stream().flatMap(record -> record.values().stream()) + .filter(value -> value.type().name().equals("RELATIONSHIP")) + .map(value -> { + Relationship relationship = value.asRelationship(); + return new WebRelDTO(relationship.id(), relationship.endNodeId(), relationship.type()); + }) + .collect(Collectors.toList()); + + // 拓展关系信息 + Set relationIds = new HashSet<>(); + for (Record record : records) { + // relId + org.neo4j.driver.Value relValue = record.get("relId"); + if (StrUtil.equalsAny(relValue.type().name(), "INTEGER")){ + relationIds.add(relValue.asLong()); + } + } + if (CollUtil.isNotEmpty(relationIds)){ + List extendRecord = neo4jService.executeCypher("MATCH ()-[r]-() where id(r) in $relationIds return r", Map.of("relationIds", relationIds)); + if (CollUtil.isNotEmpty(extendRecord)){ + graphRelList.addAll(extendRecord.stream().flatMap(r -> r.values().stream()).map(r -> { + Relationship relationship = r.asRelationship(); + return new WebRelDTO(relationship.startNodeId(), relationship.endNodeId(), relationship.type()); + }).toList()); + } + } + + return graphRelList; + } + + + private List> generateNodes(List records) { + + + List> graphNodeList = records.stream().flatMap(record -> record.values().stream()) + .filter(value -> value.type().name().equals("NODE")).map(value ->{ + Map map = new HashMap<>(); + Node node = value.asNode(); + map.put("id", node.id()); + map.put("name", node.get("name").asString()); + map.put("entityName", node.get("name").asString()); + return map; + } + ).collect(Collectors.toList()); + + // 拓展节点信息 + Set nodeIds = new HashSet<>(); + for (Record record : records) { + // startId ,endId + org.neo4j.driver.Value startValue = record.get("startId"); + org.neo4j.driver.Value endValue = record.get("endId"); + + if (StrUtil.equalsAny(startValue.type().name(), "INTEGER")){ + nodeIds.add(startValue.asLong()); + } + if (StrUtil.equalsAny(endValue.type().name(), "INTEGER")){ + nodeIds.add(endValue.asLong()); + } + } + + if (CollUtil.isNotEmpty(nodeIds)){ + List extendRecord = neo4jService.executeCypher("MATCH (n) where id(n) in $nodeIds return n", Map.of("nodeIds", nodeIds)); + if (CollUtil.isNotEmpty(extendRecord)){ + graphNodeList.addAll(extendRecord.stream().flatMap(r -> r.values().stream()).map(r -> { + Map map = new HashMap<>(); + map.put("id", r.asNode().id()); + map.put("name", r.get("name").asString()); + map.put("entityName", r.get("name").asString()); + return map; + }).toList()); + } + } + + return graphNodeList; + } + + private List> mapRecords(List records) { + List> recordList = new ArrayList<>(); + for (Record record : records) { + HashMap map = new HashMap<>(); + for (String key : record.keys()) { + org.neo4j.driver.Value value = record.get(key); + String typeName = value.type().name(); + if (typeName.equals("NULL")){ + map.put(key,null); + } + + if (StrUtil.equalsAny(typeName, "BOOLEAN","STRING", "NUMBER", "INTEGER", "FLOAT")){ + // MATCH (n)-[r]-() where n.caseId= '1' RETURN n.recordId limit 10 + map.put(key,value.asObject()); + } + + if (typeName.equals("PATH")){ + // MATCH p=(n)-[*2]-() where n.caseId= '1' RETURN p limit 10 + map.put(key,new PathDTO(value.asPath())); + } + + if (typeName.equals("RELATIONSHIP")){ + // MATCH (n)-[r]-() where n.caseId= '1' RETURN r limit 10 + map.put(key,new RelationshipValueDTO((InternalRelationship) value.asRelationship())); + } + if (typeName.equals("LIST OF ANY?")){ + + List list = value.asList().stream() + .map(i -> new RelationshipValueDTO((InternalRelationship) i)).toList(); + map.put(key,list); + } + if (typeName.equals("NODE")){ + // MATCH (n)-[r]-() where n.caseId= '1' RETURN r limit 10 + map.put(key,new NodeDTO((InternalNode) value.asNode())); + } + recordList.add(map); + } + } + return recordList; + } + private List makeScopes(String atomicIndexId, List atomicIndexList, List notePrompts, Map dicMap) { ModelAtomicIndex modelAtomicIndex = atomicIndexList.stream().filter(i -> StrUtil.equals(atomicIndexId, i.getId())).findFirst().orElse(null); if (null == modelAtomicIndex || StrUtil.isEmpty(modelAtomicIndex.getPromptId())) { diff --git a/src/main/java/com/supervision/police/vo/GraphDebugReqVO.java b/src/main/java/com/supervision/police/vo/GraphDebugReqVO.java new file mode 100644 index 0000000..c8316f4 --- /dev/null +++ b/src/main/java/com/supervision/police/vo/GraphDebugReqVO.java @@ -0,0 +1,34 @@ +package com.supervision.police.vo; + +import lombok.Data; + +import java.util.List; + +/** + * 图谱推理调试参数 + */ +@Data +public class GraphDebugReqVO { + + /** + * 案件id + */ + private String caseId; + + /** + * 原子指标id + */ + private String atomicIndexId; + + /** + * 查询语言 + */ + private String queryLang; + + /** + * 行为人id + */ + private List lawActorIdList; + + +} diff --git a/src/main/java/com/supervision/police/vo/GraphDebugResVO.java b/src/main/java/com/supervision/police/vo/GraphDebugResVO.java new file mode 100644 index 0000000..24052d6 --- /dev/null +++ b/src/main/java/com/supervision/police/vo/GraphDebugResVO.java @@ -0,0 +1,34 @@ +package com.supervision.police.vo; + +import com.supervision.neo4j.dto.WebRelDTO; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class GraphDebugResVO { + + /** + * 0:语句查询成功 1:执行语句出错 2:必填参数缺失 + */ + private String messageType = "0"; + + private String message; + + /** + * 查询结果列表 + */ + private List> recordList; + + /** + * 图谱关系数据 + */ + private List graphRelList; + + /** + * 图谱节点数据 name/entityName/id + */ + private List> graphNodeList; + +}