diff --git a/ruoyi-modules/management-platform/pom.xml b/ruoyi-modules/management-platform/pom.xml index cee6c681..5d8bbd33 100644 --- a/ruoyi-modules/management-platform/pom.xml +++ b/ruoyi-modules/management-platform/pom.xml @@ -248,6 +248,21 @@ hutool-all 5.8.5 + + + org.springframework.boot + spring-boot-starter-data-neo4j + + + com.alibaba + easyexcel + 4.0.3 + + + org.springframework.boot + spring-boot-starter-test + test + diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/config/Neo4jConfig.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/config/Neo4jConfig.java new file mode 100644 index 00000000..5f90cbca --- /dev/null +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/config/Neo4jConfig.java @@ -0,0 +1,15 @@ +package com.ruoyi.platform.config; + +import org.neo4j.cypherdsl.core.renderer.Dialect; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +@Configuration +public class Neo4jConfig { + @Bean + org.neo4j.cypherdsl.core.renderer.Configuration cypherDslConfiguration() { + return org.neo4j.cypherdsl.core.renderer.Configuration.newConfig() + .withDialect(Dialect.NEO4J_5).build(); + } +} \ No newline at end of file diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/kg/MatKGController.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/kg/MatKGController.java new file mode 100644 index 00000000..5a381afd --- /dev/null +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/kg/MatKGController.java @@ -0,0 +1,124 @@ +package com.ruoyi.platform.controller.kg; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.platform.utils.ExcelReaderInputStream; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +@RestController +@RequestMapping("/kg") +public class MatKGController { + + @PostMapping("/uploadOntology") + public R uploadOntology(@RequestParam("file") MultipartFile file, + @RequestParam("param") String param) { + if (file.isEmpty()) { + return R.fail("文件为空"); + } + + try { + // 直接读取文件内容为字符串 + String jsonContent = new String(file.getBytes(), StandardCharsets.UTF_8); + + return R.ok(); + } catch (Exception e) { + return R.fail("文件处理失败: " + e.getMessage()); + } + } + + @PostMapping("/uploadKGData") + public R uploadKGData(@RequestParam("file") MultipartFile file, + @RequestParam("id") String id) { + if (file.isEmpty()) { + return R.fail("文件为空"); + } + // 检查文件类型 + String contentType = file.getContentType(); + String fileName = file.getOriginalFilename(); + if (!isValidFileType(contentType, fileName)) { + return R.fail("不支持的文件格式。请上传CSV、XLSX或JSON文件。"); + } + + try { + // 创建目标文件 + InputStream inputStream = file.getInputStream(); + + // 处理文件和参数 + processJsonFile(inputStream, id); + + return R.ok(); + } catch (Exception e) { + return R.fail("文件处理失败: " + e.getMessage()); + } + } + + private void processJsonFile(InputStream inputStream, String id) { + ExcelReaderInputStream reader = new ExcelReaderInputStream<>(inputStream,Map.class,data->{ + //todo some logic + }); + + // 读取Excel文件 + reader.read(); + } + + @PostMapping("/uploadKGData2") + public R uploadKGData2(@RequestParam("file") MultipartFile file, + @RequestParam("param") String param) { + if (file.isEmpty()) { + return R.fail("文件为空"); + } + // 检查文件类型 + String contentType = file.getContentType(); + String fileName = file.getOriginalFilename(); + if (!isValidFileType(contentType, fileName)) { + return R.fail("不支持的文件格式。请上传CSV、XLSX或JSON文件。"); + } + + try { + // 创建目标文件 + File targetFile = new File("uploadDir", file.getOriginalFilename()); + // 确保目录存在 + if (!targetFile.getParentFile().exists()) { + targetFile.getParentFile().mkdirs(); + } + // 将文件写入目标路径 + file.transferTo(targetFile); + + // 处理文件和参数 +// processJsonFile(targetFile, param); + + return R.ok("文件上传并处理成功,参数: " + param); + } catch (Exception e) { + return R.fail("文件处理失败: " + e.getMessage()); + } + } + + private boolean isValidFileType(String contentType, String fileName) { + // 检查文件扩展名 + String extension = getFileExtension(fileName); + + // 检查MIME类型 + if ("application/json".equals(contentType) || "text/csv".equals(contentType) || "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet".equals(contentType)) { + return true; + } + + // 检查文件扩展名 + return "csv".equalsIgnoreCase(extension) || "xlsx".equalsIgnoreCase(extension) || "json".equalsIgnoreCase(extension); + } + + private String getFileExtension(String fileName) { + if (fileName != null && fileName.contains(".")) { + return fileName.substring(fileName.lastIndexOf('.') + 1); + } + return ""; + } + +} diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/material/DOI.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/material/DOI.java new file mode 100644 index 00000000..cf87d5f8 --- /dev/null +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/material/DOI.java @@ -0,0 +1,15 @@ +package com.ruoyi.platform.domain.material; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.neo4j.core.schema.Node; + +@Node +@Data +@AllArgsConstructor +public class DOI { + @Id + private String doi; // DOI + private Integer year;// YEAR +} diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/material/MaterialEntity.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/material/MaterialEntity.java new file mode 100644 index 00000000..bf8fcac3 --- /dev/null +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/material/MaterialEntity.java @@ -0,0 +1,29 @@ +package com.ruoyi.platform.domain.material; + +import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Data +@Node +public class MaterialEntity { + @Id + private String id; + + private String entity; + + @Relationship(type = "HAS_ENTITY") + private PartOfText partOfText; + + @Relationship(type = "HAS_NER_TAG") + + private NERTag nerTag; + + @Relationship(type = "HAS_DOI") + private DOI doi; + + @Relationship(type = "HAS_PREFERRED_ENTITY") + private PreferredEntity preferredEntity; +} + diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/material/NERTag.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/material/NERTag.java new file mode 100644 index 00000000..933bc5d5 --- /dev/null +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/material/NERTag.java @@ -0,0 +1,15 @@ +package com.ruoyi.platform.domain.material; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.neo4j.core.schema.Node; + +@Node +@Data +@AllArgsConstructor +public class NERTag { + @Id + private String tag; // NER_Tag + +} diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/material/PartOfText.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/material/PartOfText.java new file mode 100644 index 00000000..4bee3986 --- /dev/null +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/material/PartOfText.java @@ -0,0 +1,16 @@ +package com.ruoyi.platform.domain.material; + +import cn.hutool.core.util.IdUtil; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.neo4j.core.schema.Node; + +@Node +@Data +@AllArgsConstructor + public class PartOfText { + @Id + private String text; // Part_of_text + +} diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/material/PreferredEntity.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/material/PreferredEntity.java new file mode 100644 index 00000000..dc73f8e3 --- /dev/null +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/material/PreferredEntity.java @@ -0,0 +1,14 @@ +package com.ruoyi.platform.domain.material; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.data.annotation.Id; +import org.springframework.data.neo4j.core.schema.Node; + +@Node +@Data +@AllArgsConstructor +public class PreferredEntity { + @Id + private String preferredEntity; +} diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/repository/MatKGRepository.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/repository/MatKGRepository.java new file mode 100644 index 00000000..d8074e97 --- /dev/null +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/repository/MatKGRepository.java @@ -0,0 +1,8 @@ +package com.ruoyi.platform.repository; + +import com.ruoyi.platform.domain.material.MaterialEntity; +import org.springframework.data.neo4j.repository.Neo4jRepository; + +public interface MatKGRepository + extends Neo4jRepository { +} diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/MatKGServiceImpl.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/MatKGServiceImpl.java new file mode 100644 index 00000000..93931eb0 --- /dev/null +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/MatKGServiceImpl.java @@ -0,0 +1,32 @@ +package com.ruoyi.platform.service.impl; + +import com.ruoyi.platform.domain.material.MaterialEntity; +import com.ruoyi.platform.repository.MatKGRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +@Service +public class MatKGServiceImpl { + + @Autowired + private MatKGRepository repository; + + public MaterialEntity save(MaterialEntity entity) { + return repository.save(entity); + } + + public List saveAll(List entity) { + return repository.saveAll(entity); + } + + public Optional findById(String id) { + return repository.findById(id); + } + + public void deleteById(String id) { + repository.deleteById(id); + } +} diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/Neo4JDBService.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/Neo4JDBService.java new file mode 100644 index 00000000..57a89567 --- /dev/null +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/Neo4JDBService.java @@ -0,0 +1,154 @@ +package com.ruoyi.platform.service.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.stereotype.Service; + +import java.util.Map; +import java.util.Random; + +@Service +public class Neo4JDBService { + + @Autowired + private Neo4jClient neo4jClient; + + private static final String CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; + private static final Random RANDOM = new Random(); + private static final int NAME_LENGTH = 10; // 数据库名称的长度 + + public void createDatabase(String dbName) { + neo4jClient.query("CREATE DATABASE $dbName") + .bind(dbName).to("dbName") + .run(); + } + + public void dropDatabase(String dbName) { + neo4jClient.query("DROP DATABASE $dbName") + .bind(dbName).to("dbName") + .run(); + } + + public void createNode(String nodeType, Map properties) { + StringBuilder query = new StringBuilder("CREATE (n:" + nodeType + " {"); + properties.forEach((key, value) -> query.append(key).append(": $").append(key).append(", ")); + query.setLength(query.length() - 2); // 移除最后的逗号和空格 + query.append("})"); + + Neo4jClient.UnboundRunnableSpec neo4jQuery = neo4jClient.query(query.toString()); + properties.forEach((key, value) -> neo4jQuery.bind(value).to(key)); + neo4jQuery.run(); + } + + public void updateNode(String nodeType, String identifierKey, Object identifierValue, Map properties) { + StringBuilder query = new StringBuilder("MATCH (n:" + nodeType + " {" + identifierKey + ": $" + identifierKey + "}) SET "); + properties.forEach((key, value) -> query.append("n.").append(key).append(" = $").append(key).append(", ")); + query.setLength(query.length() - 2); // 移除最后的逗号和空格 + + Neo4jClient.UnboundRunnableSpec neo4jQuery = neo4jClient.query(query.toString()); + neo4jQuery.bind(identifierValue).to(identifierKey); + properties.forEach((key, value) -> neo4jQuery.bind(value).to(key)); + neo4jQuery.run(); + } + + public void deleteNode(String nodeType, String identifierKey, Object identifierValue) { + String query = "MATCH (n:" + nodeType + " {" + identifierKey + ": $" + identifierKey + "}) DETACH DELETE n"; + neo4jClient.query(query) + .bind(identifierValue).to(identifierKey) + .run(); + } + + public void createRelationship(String startNodeType, String startIdentifierKey, Object startIdentifierValue, + String endNodeType, String endIdentifierKey, Object endIdentifierValue, + String relationshipType) { + String query = "MATCH (a:" + startNodeType + " {" + startIdentifierKey + ": $" + startIdentifierKey + "}), " + + "(b:" + endNodeType + " {" + endIdentifierKey + ": $" + endIdentifierKey + "}) " + + "CREATE (a)-[r:" + relationshipType + "]->(b)"; + neo4jClient.query(query) + .bind(startIdentifierValue).to(startIdentifierKey) + .bind(endIdentifierValue).to(endIdentifierKey) + .run(); + } + + + public void updateRelationship(String startNodeType, String startIdentifierKey, Object startIdentifierValue, + String endNodeType, String endIdentifierKey, Object endIdentifierValue, + String relationshipType, Map properties) { + StringBuilder query = new StringBuilder("MATCH (a:" + startNodeType + " {" + startIdentifierKey + ": $" + startIdentifierKey + "}) " + + "-[r:" + relationshipType + "]->(b:" + endNodeType + " {" + endIdentifierKey + ": $" + endIdentifierKey + "}) SET "); + properties.forEach((key, value) -> query.append("r.").append(key).append(" = $").append(key).append(", ")); + query.setLength(query.length() - 2); // 移除最后的逗号和空格 + + Neo4jClient.UnboundRunnableSpec neo4jQuery = neo4jClient.query(query.toString()); + neo4jQuery.bind(startIdentifierValue).to(startIdentifierKey); + neo4jQuery.bind(endIdentifierValue).to(endIdentifierKey); + properties.forEach((key, value) -> neo4jQuery.bind(value).to(key)); + neo4jQuery.run(); + } + + + public void deleteRelationship(String startNodeType, String startIdentifierKey, Object startIdentifierValue, + String endNodeType, String endIdentifierKey, Object endIdentifierValue, + String relationshipType) { + String query = "MATCH (a:" + startNodeType + " {" + startIdentifierKey + ": $" + startIdentifierKey + "}) " + + "-[r:" + relationshipType + "]->(b:" + endNodeType + " {" + endIdentifierKey + ": $" + endIdentifierKey + "}) " + + "DELETE r"; + neo4jClient.query(query) + .bind(startIdentifierValue).to(startIdentifierKey) + .bind(endIdentifierValue).to(endIdentifierKey) + .run(); + } + + + public void clearDatabase(String dbName) { + neo4jClient.query("MATCH (n) DETACH DELETE n") + .bind(dbName).to("dbName") // 指定数据库 + .run(); + } + + public boolean doesDatabaseExist(String dbName) { + String query = "CALL dbs() YIELD name WHERE name = $dbName RETURN name"; + return neo4jClient.query(query) + .bind(dbName).to("dbName") + .fetchAs(String.class) + .mappedBy((type, record) -> record.get("name").asString()) + .one() + .isPresent(); + } + + public boolean isValidAndExists(String dbName) { + return !doesDatabaseExist(dbName) && isValidDatabaseName(dbName); + } + + public static String generateRandomDatabaseName() { + StringBuilder name = new StringBuilder(NAME_LENGTH); + // 首先添加一个字母 + name.append(CHARACTERS.charAt(RANDOM.nextInt(52))); + + // 随后添加随机字符 + for (int i = 1; i < NAME_LENGTH; i++) { + name.append(CHARACTERS.charAt(RANDOM.nextInt(CHARACTERS.length()))); + } + + return name.toString(); + } + + public static boolean isValidDatabaseName(String name) { + // 检查长度 + if (name == null || name.isEmpty() || name.length() > 255) { + return false; + } + // 检查首字符是否为字母 + if (!Character.isLetter(name.charAt(0))) { + return false; + } + // 检查字符是否合法 + for (char c : name.toCharArray()) { + if (!Character.isLetterOrDigit(c) && c != '_') { + return false; + } + } + return true; + } + +} diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/ExcelReader.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/ExcelReader.java new file mode 100644 index 00000000..91b85ebb --- /dev/null +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/ExcelReader.java @@ -0,0 +1,34 @@ +package com.ruoyi.platform.utils; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.listener.ReadListener; + +import java.util.function.Consumer; + +public class ExcelReader { + + private final String filePath; + private final Class dataClass; + private final Consumer processor; + + public ExcelReader(String filePath, Class dataClass, Consumer processor) { + this.filePath = filePath; + this.dataClass = dataClass; + this.processor = processor; + } + + public void read() { + EasyExcel.read(filePath, dataClass, new ReadListener() { + @Override + public void invoke(T data, AnalysisContext context) { + processor.accept(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + // 所有数据处理完毕后的操作 + } + }).sheet().doRead(); + } +} diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/ExcelReaderInputStream.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/ExcelReaderInputStream.java new file mode 100644 index 00000000..f4807eb0 --- /dev/null +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/ExcelReaderInputStream.java @@ -0,0 +1,35 @@ +package com.ruoyi.platform.utils; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.listener.ReadListener; + +import java.io.InputStream; +import java.util.function.Consumer; + +public class ExcelReaderInputStream { + + private final InputStream inputStream; + private final Class dataClass; + private final Consumer processor; + + public ExcelReaderInputStream(InputStream inputStream, Class dataClass, Consumer processor) { + this.inputStream = inputStream; + this.dataClass = dataClass; + this.processor = processor; + } + + public void read() { + EasyExcel.read(inputStream, dataClass, new ReadListener() { + @Override + public void invoke(T data, AnalysisContext context) { + processor.accept(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + // 所有数据处理完毕后的操作 + } + }).sheet().doRead(); + } +} diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/MatKGVo.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/MatKGVo.java new file mode 100644 index 00000000..bac5f2bd --- /dev/null +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/MatKGVo.java @@ -0,0 +1,14 @@ +package com.ruoyi.platform.vo; + +import lombok.Data; + +@Data +public class MatKGVo { + private String Unnamed; + private String Entity; + private String Part_of_text; + private String NER_Tag; + private String DOI; + private String Preferred_Entity; + private Integer Year; +}