From 051157ebc9e8184714493b62539f944a1a4b74c7 Mon Sep 17 00:00:00 2001 From: liu Date: Fri, 19 Jul 2024 16:11:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E6=9E=84=E5=BB=BA=E6=8A=BD?= =?UTF-8?q?=E8=B1=A1=E5=9B=BE=E8=B0=B1=E7=9A=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../neo4j/controller/Neo4jController.java | 31 +++- .../supervision/neo4j/domain/CaseNode.java | 6 + .../com/supervision/neo4j/domain/Rel.java | 9 ++ .../neo4j/service/Neo4jService.java | 10 +- .../neo4j/service/impl/Neo4jServiceImpl.java | 132 ++++++++++++++++-- .../supervision/neo4j/utils/Neo4jUtils.java | 2 +- .../police/controller/RecordController.java | 5 + .../service/ModelRecordTypeService.java | 2 + .../impl/ModelRecordTypeServiceImpl.java | 52 ++++++- src/main/resources/application-dev.yml | 2 + 10 files changed, 228 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/supervision/neo4j/controller/Neo4jController.java b/src/main/java/com/supervision/neo4j/controller/Neo4jController.java index f592460..634d1ac 100644 --- a/src/main/java/com/supervision/neo4j/controller/Neo4jController.java +++ b/src/main/java/com/supervision/neo4j/controller/Neo4jController.java @@ -6,6 +6,7 @@ import com.supervision.neo4j.domain.Rel; import com.supervision.neo4j.service.Neo4jService; //import io.swagger.annotations.Api; //import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -23,13 +24,25 @@ public class Neo4jController { private Neo4jService neo4jService; @PostMapping("/save") - public R save(@RequestBody CaseNode caseNode) { - return neo4jService.save(caseNode); + public R save(@RequestBody CaseNode caseNode) { + try { + CaseNode save = neo4jService.save(caseNode); + return R.ok(save); + } catch (RuntimeException e) { + return R.fail(e.getMessage()); + } } @PostMapping("/delNode") public R delNode(Long id) { - return neo4jService.delNode(id); + neo4jService.delNode(id); + return R.ok(); + } + + @PostMapping("/deleteRel") + public R deleteRel(Long relId) { + neo4jService.deleteRel(relId); + return R.ok(); } @PostMapping("/findById") @@ -81,4 +94,16 @@ public class Neo4jController { return neo4jService.test(); } + @ApiOperation("构建抽象图谱") + @GetMapping("createAbstractGraph") + public void createAbstractGraph(String path, String sheetName) { + neo4jService.createAbstractGraph(path, sheetName); + } + + @ApiOperation("清除抽象图谱") + @GetMapping("deleteAbstractGraph") + public void deleteAbstractGraph() { + neo4jService.deleteAbstractGraph(); + } + } diff --git a/src/main/java/com/supervision/neo4j/domain/CaseNode.java b/src/main/java/com/supervision/neo4j/domain/CaseNode.java index 3dc1bd3..0e0e6ce 100644 --- a/src/main/java/com/supervision/neo4j/domain/CaseNode.java +++ b/src/main/java/com/supervision/neo4j/domain/CaseNode.java @@ -55,4 +55,10 @@ public class CaseNode { this.picType = picType; } + public CaseNode( String name, String nodeType, String picType){ + this.name = name; + this.nodeType = nodeType; + this.picType = picType; + } + } diff --git a/src/main/java/com/supervision/neo4j/domain/Rel.java b/src/main/java/com/supervision/neo4j/domain/Rel.java index 391fd37..6c503bd 100644 --- a/src/main/java/com/supervision/neo4j/domain/Rel.java +++ b/src/main/java/com/supervision/neo4j/domain/Rel.java @@ -14,6 +14,8 @@ public class Rel { private String target; private Long targetId; + // 是否是抽象,0抽象 1真实 + private String picType = "1"; public Rel() { } @@ -24,6 +26,13 @@ public class Rel { this.targetId = targetId; } + public Rel(Long sourceId, String name, Long targetId,String picType) { + this.sourceId = sourceId; + this.name = name; + this.targetId = targetId; + this.picType = picType; + } + public Rel(Long id, String source, Long sourceId, String name, String target, Long targetId) { this.id = id; this.source = source; diff --git a/src/main/java/com/supervision/neo4j/service/Neo4jService.java b/src/main/java/com/supervision/neo4j/service/Neo4jService.java index 9a5f6f9..46ea25f 100644 --- a/src/main/java/com/supervision/neo4j/service/Neo4jService.java +++ b/src/main/java/com/supervision/neo4j/service/Neo4jService.java @@ -12,9 +12,11 @@ import java.util.List; */ public interface Neo4jService { - R save(CaseNode caseNode); + CaseNode save(CaseNode caseNode); - R delNode(Long id); + void delNode(Long id); + + void deleteRel(Long relId); CaseNode findById(Long id); @@ -28,4 +30,8 @@ public interface Neo4jService { R getNode(String picType, String caseId); R test(); + + void deleteAbstractGraph(); + + void createAbstractGraph(String path,String sheetName); } 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 0961562..73981ab 100644 --- a/src/main/java/com/supervision/neo4j/service/impl/Neo4jServiceImpl.java +++ b/src/main/java/com/supervision/neo4j/service/impl/Neo4jServiceImpl.java @@ -1,42 +1,47 @@ package com.supervision.neo4j.service.impl; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.poi.excel.ExcelReader; +import cn.hutool.poi.excel.ExcelUtil; import com.supervision.common.domain.R; import com.supervision.common.utils.StringUtils; import com.supervision.neo4j.domain.CaseNode; import com.supervision.neo4j.domain.Rel; import com.supervision.neo4j.service.Neo4jService; import com.supervision.neo4j.utils.Neo4jUtils; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.neo4j.cypherdsl.core.Case; import org.neo4j.driver.*; import org.neo4j.driver.Record; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * @author qmy * @since 2023-10-26 */ +@Slf4j @Service public class Neo4jServiceImpl implements Neo4jService { private final Driver driver; + @Autowired private Neo4jServiceImpl(Driver driver) { this.driver = driver; } @Override - public R save(CaseNode caseNode) { + public CaseNode save(CaseNode caseNode) { if (StringUtils.isEmpty(caseNode.getName()) || StringUtils.isEmpty(caseNode.getNodeType())) { - return R.fail("未传节点名称或节点类型或图谱类型!"); + throw new RuntimeException("未传节点名称或节点类型或图谱类型!"); } List byName = findByName(caseNode.getCaseId(), caseNode.getRecordsId(), caseNode.getNodeType(), caseNode.getName(), caseNode.getPicType()); if (byName != null && !byName.isEmpty()) { - return R.fail("名称已存在"); + throw new RuntimeException("名称已存在!"); } CaseNode res = null; try { @@ -67,20 +72,50 @@ public class Neo4jServiceImpl implements Neo4jService { } catch (Exception e) { e.printStackTrace(); } - return R.ok(res); + return res; } + /** + * 删除节点,注意,删除节点的时候,要先把边删除掉,不然会报错 + * + * @param id 点的ID + */ @Override - public R delNode(Long id) { + public void delNode(Long id) { try { Session session = driver.session(); StringBuffer cql = new StringBuffer(); cql.append("MATCH (n) where id(n) = ").append(id).append(" DELETE n"); + log.info(cql.toString()); Result run = session.run(cql.toString()); + while (run.hasNext()) { + Record next = run.next(); + // log.info(next.toString()); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + + /** + * 删除边 + * + * @param relId 边ID + */ + public void deleteRel(Long relId) { + try { + Session session = driver.session(); + StringBuilder cql = new StringBuilder(); + cql.append("MATCH ()-[r]->() WHERE id(r) = ").append(relId).append(" DELETE r"); + log.info(cql.toString()); + Result run = session.run(cql.toString()); + while (run.hasNext()) { + Record next = run.next(); + // log.info(next.toString()); + } } catch (Exception e) { e.printStackTrace(); } - return R.ok(); } @Override @@ -267,13 +302,84 @@ public class Neo4jServiceImpl implements Neo4jService { Result run = session.run("MATCH (m:LawActor), (n:FictionalOrgan) where m.name=$lawActor OPTIONAL MATCH (m)-[r:`冒充`]->(n) RETURN id(m) as startId, id(n) as endId, id(r) as relId, m.recordId as recordId, m.recordsId as recordsId", params); while (run.hasNext()) { Record record = run.next(); - String id = record.get("startId").asLong() + ""; - String endId = record.get("endId").asLong() + ""; - String relId = record.get("relId").asLong() + ""; + + String id = Neo4jUtils.valueTransportString(record.get("startId")); + String endId = Neo4jUtils.valueTransportString(record.get("endId")); + String relId = Neo4jUtils.valueTransportString(record.get("relId")); System.out.println("************" + id); System.out.println("************" + endId); System.out.println("************" + relId); } return R.ok("222"); } + + @Override + public void createAbstractGraph(String path,String sheetName) { + // 首先从数据库中读到数据 + ExcelReader reader = ExcelUtil.getReader(path, sheetName); + List abstractGraphExcelHeaders = reader.readAll(AbstractGraphExcelHeader.class); + Map nodeMap = new HashMap<>(); + Map relMap = new HashMap<>(); + for (AbstractGraphExcelHeader abstractGraphExcelHeader : abstractGraphExcelHeaders) { + // from + if (!nodeMap.containsKey(abstractGraphExcelHeader.getFrom())) { + CaseNode caseNode = new CaseNode(abstractGraphExcelHeader.getFrom(), abstractGraphExcelHeader.getFrom(), "0"); + log.info("点:{}插入成功", abstractGraphExcelHeader.getFrom()); + CaseNode save = save(caseNode); + nodeMap.put(abstractGraphExcelHeader.getFrom(), save); + } + // to + if (!nodeMap.containsKey(abstractGraphExcelHeader.getTo())) { + CaseNode caseNode = new CaseNode(abstractGraphExcelHeader.getTo(), abstractGraphExcelHeader.getTo(), "0"); + CaseNode save = save(caseNode); + log.info("点:{}插入成功", abstractGraphExcelHeader.getTo()); + nodeMap.put(abstractGraphExcelHeader.getTo(), save); + } + // relation + if (!relMap.containsKey(abstractGraphExcelHeader.getFrom() + "->" + abstractGraphExcelHeader.getRelation() + "->" + abstractGraphExcelHeader.getTo())) { + Rel rel = new Rel(nodeMap.get(abstractGraphExcelHeader.getFrom()).getId(), abstractGraphExcelHeader.getRelation(), nodeMap.get(abstractGraphExcelHeader.getTo()).getId(), "0"); + saveRelation(rel); + log.info("关系:{}插入成功", (abstractGraphExcelHeader.getFrom() + "->" + abstractGraphExcelHeader.getRelation() + "->" + abstractGraphExcelHeader.getTo())); + relMap.put(abstractGraphExcelHeader.getFrom() + "->" + abstractGraphExcelHeader.getRelation() + "->" + abstractGraphExcelHeader.getTo(), rel); + } + } + + + } + + public void deleteAbstractGraph() { + Session session = driver.session(); + // 首先查出来所有的抽象节点 + Result run = session.run("MATCH (n) WHERE n.picType = '0' OPTIONAL MATCH (n)-[r]-() RETURN id(n) as nodeId, id(r) as relId"); + Set nodeIdSet = new HashSet<>(); + HashSet relIdSet = new HashSet<>(); + while (run.hasNext()) { + Record record = run.next(); + String nodeId = Neo4jUtils.valueTransportString(record.get("nodeId")); + nodeIdSet.add(nodeId); + String relId = Neo4jUtils.valueTransportString(record.get("relId")); + relIdSet.add(relId); + } + // 删除边 + for (String s : relIdSet) { + long relId = Long.parseLong(s); + deleteRel(relId); + log.info("删除边:{} 成功", relId); + } + // 删除节点 + for (String s : nodeIdSet) { + long nodeId = Long.parseLong(s); + delNode(nodeId); + log.info("删除节点:{} 成功", nodeId); + } + + + } + + @Data + private static class AbstractGraphExcelHeader { + private String from; + private String relation; + private String to; + } } diff --git a/src/main/java/com/supervision/neo4j/utils/Neo4jUtils.java b/src/main/java/com/supervision/neo4j/utils/Neo4jUtils.java index e4593d0..233cd7b 100644 --- a/src/main/java/com/supervision/neo4j/utils/Neo4jUtils.java +++ b/src/main/java/com/supervision/neo4j/utils/Neo4jUtils.java @@ -103,7 +103,7 @@ public class Neo4jUtils { /** * neo4j类型转换 */ - private static String valueTransportString(Value value) { + public static String valueTransportString(Value value) { if (value instanceof NullValue) { return null; } else if (value instanceof IntegerValue) { diff --git a/src/main/java/com/supervision/police/controller/RecordController.java b/src/main/java/com/supervision/police/controller/RecordController.java index 3868319..368cbfd 100644 --- a/src/main/java/com/supervision/police/controller/RecordController.java +++ b/src/main/java/com/supervision/police/controller/RecordController.java @@ -87,6 +87,11 @@ public class RecordController { return R.ok(modelRecordTypeService.getThreeInfo(caseId, name, recordId)); } + @GetMapping("testExtractThreeInfo") + public void testExtractThreeInfo(){ + modelRecordTypeService.testExtractThreeInfo(); + } + @ApiOperation("将三元组信息保存到知识图谱") @PostMapping("/addNeo4j") public R addNeo4j(@RequestBody ListDTO list) { diff --git a/src/main/java/com/supervision/police/service/ModelRecordTypeService.java b/src/main/java/com/supervision/police/service/ModelRecordTypeService.java index 51a799d..8dc52fe 100644 --- a/src/main/java/com/supervision/police/service/ModelRecordTypeService.java +++ b/src/main/java/com/supervision/police/service/ModelRecordTypeService.java @@ -22,5 +22,7 @@ public interface ModelRecordTypeService extends IService { List getThreeInfo(String caseId, String name, String recordId); + void testExtractThreeInfo(); + String addNeo4j(List ids); } diff --git a/src/main/java/com/supervision/police/service/impl/ModelRecordTypeServiceImpl.java b/src/main/java/com/supervision/police/service/impl/ModelRecordTypeServiceImpl.java index 1a4ca83..bcf45af 100644 --- a/src/main/java/com/supervision/police/service/impl/ModelRecordTypeServiceImpl.java +++ b/src/main/java/com/supervision/police/service/impl/ModelRecordTypeServiceImpl.java @@ -119,16 +119,60 @@ public class ModelRecordTypeServiceImpl extends ServiceImpl getThreeInfo(String caseId, String name, String recordId) { // TODO 这里应该改成异步的形式,通过异步的形式来进行提取三元组信息,不能每次点击就跑一遍 return extractTripleInfo(caseId, name, recordId); } + @Override + public void testExtractThreeInfo() { + String prompt = """ + 请从 + --- + 办案警官问:你是如何想到这个方法骗钱的? 裴金禄回答:因为我是在中卫市沙坡头区中兰铁路上做监理人员,我这样说的话别人会更容易相信。 + --- + 这段话中按(主体类型:行为人姓名;关系:伪造;客体类型:合同名称)提取特定的三元组信息。 + 如果未提取到或关系不匹配或客体类型不匹配,则返回空的json即可。 + 如果可以提取到三元组关系,则返回格式为{"主体":"主体名称","关系":"关系名称","客体":"客体名称"}。 + 例如‘办案警官问:描述一下事情的经过。行为人小明答:我做了一份假的租车合同,然后骗小刚签了这个合同’,该示例存在给定的三元组关系.则结果为:{"主体":"小明","关系":"伪造","客体":"租车合同"}。 + 例如‘办案警官问:办案警官问:讲一下那个软件的名称?小明回答:现在叫畅聊。‘该示例不存在给定的三元组关系,则结果为:{} + 请保证提取准确且完整,主体和客体要具体明确,不能是宽泛的概念。提取的客体要必须属于客体类型! + + 以只返回JSON数据 + """; + // 抽取的客体名称一定属于给定的客体类型分类,且抽取的关系完全等于给定的关系。 + Prompt ask = new Prompt(new UserMessage(prompt)); + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + log.info("开始分析:"); + ChatResponse call = chatClient.call(ask); + stopWatch.stop(); + log.info("耗时:{}", stopWatch.getTotalTimeSeconds()); + String content = call.getResult().getOutput().getContent(); + log.info("分析的结果是:{}", content); + + String oldPrompt = """ + 请分析以下内容中所有规定的三元组信息并补充完整, + 要求:1.返回式: + {result: + [ + {startNodeType:'LawActor',entity:'',endNodeType:'FictionalOrgan',property:'',value:'冒充' }, + {startNodeType:'LawActor',entity:'',endNodeType:'Seal',property:'',value:'伪造'}, + {startNodeType:'LawActor',entity:'',endNodeType:'BusinessLicense',property:'',value:'伪造'} + ] + }。 + 2.必须考虑上下文语境分析。 + 3.对话中不包含此类型三元组则返回{ "result": [] }。 + 例子1:办案警官问:你为了骗取更多的钱都做了哪些准备?裴金禄回答:我刚开始我就是自己想了一些关于骗钱的点子,后面为了更不容易让别人识破我为了更佳逼真,我就从网上随便搜了一家租赁公司,我就搜到了兰州胜利机械租赁有限公司,被讯问人:裴金禄我又想到了我管理的中铁北京局和中铁电气化局施工公司。我先是通过百度搜索了“办证”之后就在网页上面弹出了一个页面上面有一个QQ号,我就加上了。加上之后我就将我的要求给他说了,要求他给我刻两个假的公章,一个是兰州胜利机械租赁有限公司合同专用章,另一个是中铁北京局集团有限公司合同专用章。我还要求他给我伪造了一张兰州胜利机械租赁有限公司的营业执照。 + 回答:{"result":[{"startNodeType":"LawActor","entity":"裴金禄","endNodeType":"FictionalOrgan","property":"兰州胜利机械租赁有限公司","value":"冒充"},{"startNodeType":"LawActor","entity":"裴金禄","endNodeType":"Seal","property":"兰州胜利机械租赁有限公司合同专用章","value":"伪造"},{"startNodeType":"LawActor","entity":"裴金禄","endNodeType":"Seal","property":"中铁北京局集团有限公司合同专用章","value":"伪造"},{"startNodeType":"LawActor","entity":"裴金禄","endNodeType":"BusinessLicense","property":"兰州胜利机械租赁有限公司营业执照","value":"伪造"}]}。 + 例子2:办案警官问:你为什么要在中卫市沙坡头区签订这些合同?裴金禄回答:因为我在这边上班,我在中卫市沙坡头区签订合同更能有说服力,这样才能更好地骗到钱。回答:{ "result": [] }。 + """; + } + private List extractTripleInfo(String caseId, String name, String recordId) { // 首先获取所有切分后的笔录 - List recordSplitList= noteRecordSplitMapper.selectRecord(caseId, name, recordId); + List recordSplitList = noteRecordSplitMapper.selectRecord(caseId, name, recordId); List tripleInfos = new ArrayList<>(); // 对切分后的笔录进行遍历 for (NoteRecordSplit record : recordSplitList) { @@ -189,7 +233,7 @@ public class ModelRecordTypeServiceImpl extends ServiceImpl