optional = n.getAnnotationByName("RestController");
+ if (optional.isPresent()) {
+ type = "controller";
+ }
+
+ optional = n.getAnnotationByName("Table");
+ if (optional.isPresent()) {
+ type = "entity";
+ }
+
+ return type;
+ }
+
+ //读取resource下某个文件的文本内容(class)
+ public String readResourceFileContent(String fileName) {
+ try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(fileName);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
+ return reader.lines().collect(Collectors.joining(System.lineSeparator()));
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+
+}
diff --git a/jcommon/ai/neo4j/src/main/java/run/mone/neo4j/MoneMethodCodeParser.java b/jcommon/ai/neo4j/src/main/java/run/mone/neo4j/MoneMethodCodeParser.java
new file mode 100644
index 000000000..05833ffde
--- /dev/null
+++ b/jcommon/ai/neo4j/src/main/java/run/mone/neo4j/MoneMethodCodeParser.java
@@ -0,0 +1,299 @@
+package run.mone.neo4j;
+
+import com.github.javaparser.JavaParser;
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.ImportDeclaration;
+import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
+import com.github.javaparser.ast.body.FieldDeclaration;
+import com.github.javaparser.ast.body.MethodDeclaration;
+import com.github.javaparser.ast.comments.Comment;
+import com.github.javaparser.ast.comments.JavadocComment;
+import com.github.javaparser.ast.expr.FieldAccessExpr;
+import com.github.javaparser.ast.expr.MethodCallExpr;
+import com.github.javaparser.ast.type.ClassOrInterfaceType;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.neo4j.driver.*;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * @author goodjava@qq.com
+ *
+ * 建立方法调用之间的关系(call),且只建立业务调用的关系
+ */
+@Slf4j
+public class MoneMethodCodeParser {
+
+ private String NEO4J_URI = "bolt://localhost:7687";
+
+ private String NEO4J_USER = "neo4j";
+
+ private String password = "";
+
+ public MoneMethodCodeParser setPassword(String password) {
+ this.password = password;
+ return this;
+ }
+
+
+ public void writeJavaFilesToNeo4j(String directoryPath) {
+ if (new File(directoryPath).isFile()) {
+ writeToNeo4j(directoryPath);
+ return;
+ }
+
+ getJavaFilesInDirectory(directoryPath).forEach(it -> {
+ log.info("parse it:{}", it);
+ writeToNeo4j(it);
+ });
+ }
+
+ //给一个文件夹,获取里边是.java文件的列表,注意你需要递归获取(class)
+ public static List getJavaFilesInDirectory(String directoryPath) {
+ List javaFiles = new ArrayList<>();
+ File directory = new File(directoryPath);
+
+ if (directory.exists() && directory.isDirectory()) {
+ File[] files = directory.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ if (file.isDirectory()) {
+ javaFiles.addAll(getJavaFilesInDirectory(file.getAbsolutePath()));
+ } else if (file.getName().endsWith(".java")) {
+ javaFiles.add(file.getAbsolutePath());
+ }
+ }
+ }
+ }
+ return javaFiles;
+ }
+
+
+ @SneakyThrows
+ private void writeToNeo4j(String filePath) {
+ //写入到neo4j中
+ // 替换成你的 Java 文件路径
+ String projectName = "MyProject";
+
+ try (Driver driver = GraphDatabase.driver(NEO4J_URI, AuthTokens.basic(NEO4J_USER, password));
+ Session session = driver.session()) {
+ // 解析 Java 文件
+ CompilationUnit cu = new JavaParser().parse(new File(filePath)).getResult().get();
+
+ // 遍历类、接口、方法等
+ cu.accept(new MoneMethodCodeParser.Visitor(session, projectName, filePath), null);
+ }
+ }
+
+
+ private static class Visitor extends com.github.javaparser.ast.visitor.VoidVisitorAdapter {
+
+ private final Session session;
+
+ public Visitor(Session session, String projectName, String filePath) {
+ this.session = session;
+ }
+
+
+ @Override
+ public void visit(ClassOrInterfaceDeclaration n, Void arg) {
+ super.visit(n, arg);
+ }
+
+
+ private String getFullMethodName(MethodDeclaration method) {
+ String packageName = method.findCompilationUnit()
+ .flatMap(cu -> cu.getPackageDeclaration())
+ .map(pd -> pd.getNameAsString())
+ .orElse("");
+ String className = method.findAncestor(com.github.javaparser.ast.body.ClassOrInterfaceDeclaration.class)
+ .map(c -> c.getNameAsString())
+ .orElse("");
+ String methodName = method.getNameAsString();
+ return packageName + "." + className + "." + methodName;
+ }
+
+ /**
+ * 获取方法调用的完整路径,包括包名、类名和方法名
+ *
+ * @param methodCall 方法调用表达式
+ * @return 方法调用的完整路径
+ */
+ public String getFullMethodPath(MethodCallExpr methodCall) {
+ StringBuilder fullPath = new StringBuilder();
+
+ // 获取包名
+ Optional cu = methodCall.findCompilationUnit();
+ if (cu.isPresent()) {
+ cu.get().getPackageDeclaration().ifPresent(pkg ->
+ fullPath.append(pkg.getNameAsString()).append(".")
+ );
+ }
+
+ // 获取类名
+ String className = methodCall.findAncestor(ClassOrInterfaceDeclaration.class)
+ .map(ClassOrInterfaceDeclaration::getNameAsString)
+ .orElse("");
+
+ // 获取方法调用的对象
+ String objectName = methodCall.getScope()
+ .map(scope -> scope.toString())
+ .orElse("");
+
+
+ //静态调用
+ if (methodCall.getScope().isPresent() && methodCall.getScope().get() instanceof FieldAccessExpr) {
+ return objectName + "." + methodCall.getNameAsString();
+ }
+
+ //lombok 的log
+ if (isLogCall(methodCall)) {
+ return objectName + "." + methodCall.getNameAsString();
+ }
+
+ // 如果对象名不为空,尝试找到它的类型
+ if (!objectName.isEmpty()) {
+ Optional field = methodCall.findAncestor(ClassOrInterfaceDeclaration.class)
+ .flatMap(classDecl -> classDecl.getFieldByName(objectName));
+
+ if (field.isPresent()) {
+ ClassOrInterfaceType type = field.get().getVariable(0).getType().asClassOrInterfaceType();
+ String v = resolveTypePath(type);
+ return v + "." + methodCall.getNameAsString();
+ }
+ }
+
+
+ // 构建完整路径
+ fullPath.append(className).append(".");
+ fullPath.append(methodCall.getNameAsString());
+
+ return fullPath.toString();
+ }
+
+ public static String resolveTypePath(ClassOrInterfaceType type) {
+ String typeName = type.getNameAsString();
+
+ Optional cu = type.findAncestor(CompilationUnit.class);
+ if (cu.isPresent()) {
+ // 尝试从导入声明中查找匹配
+ Optional importedPath = findMatchingImport(cu.get(), typeName);
+ if (importedPath.isPresent()) {
+ return importedPath.get();
+ }
+
+ // 如果没有找到匹配的导入,检查是否在同一包中
+ Optional currentPackage = getCurrentPackage(cu.get());
+ if (currentPackage.isPresent()) {
+ return currentPackage.get() + "." + typeName;
+ }
+ }
+
+ // 如果无法解析,返回原始类型名称
+ return typeName;
+ }
+
+ private static Optional findMatchingImport(CompilationUnit cu, String typeName) {
+ return cu.getImports().stream()
+ .filter(importDecl -> !importDecl.isAsterisk() && importDecl.getNameAsString().endsWith("." + typeName))
+ .map(ImportDeclaration::getNameAsString)
+ .findFirst();
+ }
+
+ private static Optional getCurrentPackage(CompilationUnit cu) {
+ return cu.getPackageDeclaration().map(pd -> pd.getNameAsString());
+ }
+
+ /**
+ * 判断方法调用是否为日志调用
+ *
+ * @param n 方法调用表达式
+ * @return 如果方法调用是日志调用则返回true,否则返回false
+ */
+ private boolean isLogCall(MethodCallExpr n) {
+ if (!n.getScope().isPresent()) {
+ return false;
+ }
+ String scope = n.getScope().get().toString();
+ String method = n.getNameAsString();
+ return scope.equals("log") &&
+ (method.equals("trace") || method.equals("debug") || method.equals("info") ||
+ method.equals("warn") || method.equals("error"));
+ }
+
+
+ @Override
+ public void visit(MethodDeclaration n, Void arg) {
+ super.visit(n, arg);
+ if (n.findAncestor(ClassOrInterfaceDeclaration.class).isEmpty()) {
+ return;
+ }
+ // 处理方法调用
+ processMethodCalls(n);
+
+ }
+
+
+ // 处理方法调用
+ private void processMethodCalls(MethodDeclaration n) {
+ n.findAll(MethodCallExpr.class).forEach(methodCall -> {
+ Map callParams = new HashMap<>();
+ callParams.put("callerName", getFullMethodName(n));
+ callParams.put("calleeName", getFullMethodPath(methodCall));
+
+ // 检查 callerName 和 calleeName 是否都存在
+ Result result = session.run("MATCH (caller:Method {name: $callerName}), (callee:Method {name: $calleeName}) " +
+ "RETURN caller, callee", callParams);
+
+ //只有两个业务method,才有必要创建这个边
+ if (result.hasNext()) {
+ // 创建 CALLS 关系
+ session.run("MATCH (caller:Method {name: $callerName}) " +
+ "MATCH (callee:Method {name: $calleeName}) " +
+ "MERGE (caller)-[:CALLS]->(callee)",
+ callParams);
+ }
+ });
+ }
+
+
+ // 处理注释
+ private void processComments(MethodDeclaration n) {
+ for (Comment comment : n.getAllContainedComments()) {
+ createCommentNode(comment, n);
+ }
+
+ Optional optional = n.getJavadocComment();
+ if (optional.isPresent()) {
+ createCommentNode(optional.get(), n);
+ }
+
+ Optional commentOptional = n.getComment();
+ if (commentOptional.isPresent()) {
+ createCommentNode(commentOptional.get(), n);
+ }
+ }
+
+ private void createCommentNode(Comment comment, MethodDeclaration n) {
+ Map commentParams = new HashMap<>();
+ commentParams.put("text", comment.getContent());
+ commentParams.put("text_vector", new float[]{}); // 替换为实际的文本向量
+
+ session.run("MERGE (comment:Comment {text: $text, text_vector: $text_vector})", commentParams);
+
+ // 创建 DOCUMENTS 关系 (Comment -[:DOCUMENTS]-> Method)
+ Map documentsParams = new HashMap<>();
+ documentsParams.put("commentText", comment.getContent());
+ documentsParams.put("methodName", getFullMethodName(n));
+ documentsParams.put("methodSignature", n.getSignature().asString());
+ session.run("MATCH (comment:Comment {text: $commentText}) " +
+ "MATCH (m:Method {name: $methodName, signature: $methodSignature}) " +
+ "MERGE (comment)-[:DOCUMENTS]->(m)",
+ documentsParams);
+ }
+
+ }
+
+}
diff --git a/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/MoneCodeParserTest.java b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/MoneCodeParserTest.java
new file mode 100644
index 000000000..d2b6f63c2
--- /dev/null
+++ b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/MoneCodeParserTest.java
@@ -0,0 +1,96 @@
+package run.mone.neo4j.test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.Gson;
+import org.junit.Test;
+import run.mone.neo4j.BotCall;
+import run.mone.neo4j.MoneCodeParser;
+import run.mone.neo4j.MoneMethodCodeParser;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+
+/**
+ * @author goodjava@qq.com
+ * @date 2024/8/16 09:42
+ */
+public class MoneCodeParserTest {
+
+
+
+ @Test
+ public void testWriteCatServiceToNeo4j() {
+// new MoneCodeParser().writeJavaFilesToNeo4j("/Users/zhangzhiyong/IdeaProjects/ai/m78/m78-service/src/main/java/run/mone/m78/service");
+// MoneCodeParser.writeJavaFilesToNeo4j("/Users/zhangzhiyong/IdeaProjects/ai/m78/m78-service/src/main/java/run/mone/m78/service/database");
+// MoneCodeParser.writeJavaFilesToNeo4j("/Users/zhangzhiyong/IdeaProjects/ai/m78/m78-service/src/main/java/run/mone/m78/service/database/SqlParseUtil.java");
+// new MoneCodeParser().writeJavaFilesToNeo4j("/Users/zhangzhiyong/IdeaProjects/goodjava/mone/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/A.java");
+ new MoneCodeParser().setPassword(System.getenv("password")).writeJavaFilesToNeo4j("/Users/zhangzhiyong/IdeaProjects/goodjava/mone/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m");
+// new MoneCodeParser().setPassword(System.getenv("password")).writeJavaFilesToNeo4j("/Users/zhangzhiyong/IdeaProjects/goodjava/mone/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/CatService.java");
+// new MoneCodeParser().setPassword(System.getenv("password")).writeJavaFilesToNeo4j("/Users/zhangzhiyong/IdeaProjects/goodjava/mone/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/PersonService.java");
+ }
+
+
+ @Test
+ public void testParserMethod() {
+ new MoneMethodCodeParser().setPassword(System.getenv("password")).writeJavaFilesToNeo4j("/Users/zhangzhiyong/IdeaProjects/goodjava/mone/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m");
+ }
+
+ @Test
+ public void test1() {
+ new MoneCodeParser().queryEntityClasses();
+ }
+
+
+
+
+ @Test
+ public void testFindClassesWithAnnotation() {
+ MoneCodeParser moneCodeParser = new MoneCodeParser().setPassword(System.getenv("password"));
+ List