diff --git a/jcommon/ai/google/src/main/java/run/mone/ai/google/bo/RequestPayload.java b/jcommon/ai/google/src/main/java/run/mone/ai/google/bo/RequestPayload.java index b649a1331..ac55968a9 100644 --- a/jcommon/ai/google/src/main/java/run/mone/ai/google/bo/RequestPayload.java +++ b/jcommon/ai/google/src/main/java/run/mone/ai/google/bo/RequestPayload.java @@ -14,6 +14,8 @@ @Builder public class RequestPayload { + @SerializedName("system") + private String system; @SerializedName("anthropic_version") private String anthropicVersion; diff --git a/jcommon/ai/gpt/pom.xml b/jcommon/ai/gpt/pom.xml new file mode 100644 index 000000000..1e751705d --- /dev/null +++ b/jcommon/ai/gpt/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + run.mone + ai + 1.4-jdk20-SNAPSHOT + + + gpt + 1.5-jdk8-SNAPSHOT + + + 8 + 8 + UTF-8 + + + + + + com.squareup.okhttp3 + okhttp + 4.10.0 + + + + + + + + + + maven-compiler-plugin + 3.11.0 + + 8 + 8 + true + UTF-8 + + ${project.basedir}/src/main/java + + + + + + + + + + \ No newline at end of file diff --git a/jcommon/ai/gpt/src/main/java/run/mone/ai/gpt/GptClient.java b/jcommon/ai/gpt/src/main/java/run/mone/ai/gpt/GptClient.java new file mode 100644 index 000000000..49eb76c8a --- /dev/null +++ b/jcommon/ai/gpt/src/main/java/run/mone/ai/gpt/GptClient.java @@ -0,0 +1,54 @@ +package run.mone.ai.gpt; + +import com.google.gson.Gson; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import run.mone.ai.gpt.bo.ResponsePayload; +import run.mone.ai.gpt.bo.multiModal.GptVisionRequest; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +@Data +@Slf4j +public class GptClient { + + private static Gson gson = new Gson(); + + public ResponsePayload visionCall(String url, String token, GptVisionRequest gptVisionRequest) { + return baseCall(url, token, gson.toJson(gptVisionRequest)); + } + + private ResponsePayload baseCall(String url, String token, String bodyStr) { + OkHttpClient client = new OkHttpClient.Builder().readTimeout(5, TimeUnit.MINUTES).build(); + MediaType mediaType = MediaType.parse("application/json; charset=utf-8"); + RequestBody body = RequestBody.create(mediaType, bodyStr); + Request request = new Request.Builder() + .url(url) + .post(body) + .addHeader("api-key", token) + .addHeader("Content-Type", "application/json; charset=utf-8") + .build(); + + try (Response response = client.newCall(request).execute()) { + if (response.code() == 429) { + ResponsePayload res = new ResponsePayload(); + ResponsePayload.Error error = new ResponsePayload.Error(); + error.setCode("429"); + error.setMessage("被gpt限流了"); + res.setError(error); + return res; + } + if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); + // Handle the response + String res = response.body().string(); + log.info("claude3 res:{}", res); + return new Gson().fromJson(res, ResponsePayload.class); + } catch (Throwable e) { + log.error(e.getMessage(), e); + } + return null; + } + +} diff --git a/jcommon/ai/gpt/src/main/java/run/mone/ai/gpt/bo/ResponsePayload.java b/jcommon/ai/gpt/src/main/java/run/mone/ai/gpt/bo/ResponsePayload.java new file mode 100644 index 000000000..1aabe2c5d --- /dev/null +++ b/jcommon/ai/gpt/src/main/java/run/mone/ai/gpt/bo/ResponsePayload.java @@ -0,0 +1,87 @@ +package run.mone.ai.gpt.bo; + +import lombok.Data; + +import java.util.List; + +@Data +public class ResponsePayload { + private List choices; + private long created; + private String id; + private String model; + private String object; + private List prompt_filter_results; + private String system_fingerprint; + private Usage usage; + private Error error; + + @Data + public static class Error { + private String code; + private String message; + } + + @Data + public static class Choice { + private ContentFilterResults content_filter_results; + private String finish_reason; + private int index; + private Message message; + + + @Data + public static class ContentFilterResults { + private FilterResult hate; + private FilterResult self_harm; + private FilterResult sexual; + private FilterResult violence; + + } + + @Data + public static class FilterResult { + private boolean filtered; + private String severity; + + } + + @Data + public static class Message { + private String content; + private String role; + + } + } + + @Data + public static class PromptFilterResult { + private int prompt_index; + private ContentFilterResult content_filter_result; + + + @Data + public static class ContentFilterResult { + private JailbreakResult jailbreak; + private Choice.FilterResult sexual; + private Choice.FilterResult violence; + private Choice.FilterResult hate; + private Choice.FilterResult self_harm; + + + @Data + public static class JailbreakResult { + private boolean filtered; + private boolean detected; + + } + } + } + + @Data + public static class Usage { + private int completion_tokens; + private int prompt_tokens; + private int total_tokens; + } +} diff --git a/jcommon/ai/gpt/src/main/java/run/mone/ai/gpt/bo/multiModal/GptVisionContent.java b/jcommon/ai/gpt/src/main/java/run/mone/ai/gpt/bo/multiModal/GptVisionContent.java new file mode 100644 index 000000000..36343a787 --- /dev/null +++ b/jcommon/ai/gpt/src/main/java/run/mone/ai/gpt/bo/multiModal/GptVisionContent.java @@ -0,0 +1,22 @@ +package run.mone.ai.gpt.bo.multiModal; + +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Data; + +import java.util.Map; + +@Data +@Builder +public class GptVisionContent { + + @SerializedName("type") + private String type; + + @SerializedName("text") + private String text; + + @SerializedName("image_url") + private Map imageUrl; + +} diff --git a/jcommon/ai/gpt/src/main/java/run/mone/ai/gpt/bo/multiModal/GptVisionMsg.java b/jcommon/ai/gpt/src/main/java/run/mone/ai/gpt/bo/multiModal/GptVisionMsg.java new file mode 100644 index 000000000..6787b3146 --- /dev/null +++ b/jcommon/ai/gpt/src/main/java/run/mone/ai/gpt/bo/multiModal/GptVisionMsg.java @@ -0,0 +1,23 @@ +package run.mone.ai.gpt.bo.multiModal; + +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * @author goodjava@qq.com + * @date 2023/5/25 14:16 + */ +@Data +@Builder +public class GptVisionMsg implements Serializable { + + @SerializedName("role") + private String role; + + @SerializedName("content") + private List content; +} diff --git a/jcommon/ai/gpt/src/main/java/run/mone/ai/gpt/bo/multiModal/GptVisionRequest.java b/jcommon/ai/gpt/src/main/java/run/mone/ai/gpt/bo/multiModal/GptVisionRequest.java new file mode 100644 index 000000000..fab363962 --- /dev/null +++ b/jcommon/ai/gpt/src/main/java/run/mone/ai/gpt/bo/multiModal/GptVisionRequest.java @@ -0,0 +1,46 @@ +package run.mone.ai.gpt.bo.multiModal; + +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +/** + * @author goodjava@qq.com + * @date 2024/4/9 16:36 + */ +@Data +@Builder +public class GptVisionRequest { + + @SerializedName("model") + private String model; + + @SerializedName("temperature") + private double temperature; + + @SerializedName("n") + @Builder.Default + private int n = 1; + + @SerializedName("stream") + private boolean stream; + + @SerializedName("top_p") + private double topP; + + @SerializedName("max_tokens") + private int maxTokens; + + @SerializedName("presence_penalty") + private double presencePenalty; + + @SerializedName("frequency_penalty") + private double frequencyPenalty; + + @SerializedName("messages") + private List messages; + + +} diff --git a/jcommon/ai/neo4j/src/main/java/run/mone/neo4j/BotCall.java b/jcommon/ai/neo4j/src/main/java/run/mone/neo4j/BotCall.java new file mode 100644 index 000000000..0930633ac --- /dev/null +++ b/jcommon/ai/neo4j/src/main/java/run/mone/neo4j/BotCall.java @@ -0,0 +1,64 @@ +package run.mone.neo4j; + +import com.google.gson.JsonObject; +import lombok.Setter; +import okhttp3.*; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +/** + * @author goodjava@qq.com + * @date 2024/8/21 13:50 + */ +public class BotCall { + + //调用bot的地址 + @Setter + private static String url = ""; + + + public static String call(String desc, String input) { + OkHttpClient client = new OkHttpClient.Builder() + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .writeTimeout(30, TimeUnit.SECONDS) + .build(); + + // 使用 Gson 的 JsonObject 构建请求体 + JsonObject mainObject = new JsonObject(); + mainObject.addProperty("userName", ""); + mainObject.addProperty("botId", ""); + mainObject.addProperty("input", ""); + + JsonObject paramsObject = new JsonObject(); + paramsObject.addProperty("desc", desc); + paramsObject.addProperty("input", input); + mainObject.add("params", paramsObject); + + // 将 JsonObject 转换为字符串 + String jsonBody = mainObject.toString(); + + RequestBody body = RequestBody.create(MediaType.parse("application/json"), jsonBody); + + Request request = new Request.Builder() + .url(url) + .post(body) + .addHeader("Accept", "application/json, text/plain, */*") + .addHeader("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8") + .addHeader("Cache-Control", "no-cache") + .addHeader("Connection", "keep-alive") + .addHeader("Content-Type", "application/json") + .addHeader("Authorization", "") + .build(); + + try { + Response response = client.newCall(request).execute(); + return response.body().string(); + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } + +} diff --git a/jcommon/ai/neo4j/src/main/java/run/mone/neo4j/MoneCodeParser.java b/jcommon/ai/neo4j/src/main/java/run/mone/neo4j/MoneCodeParser.java index 328dc825b..b9521ab5e 100644 --- a/jcommon/ai/neo4j/src/main/java/run/mone/neo4j/MoneCodeParser.java +++ b/jcommon/ai/neo4j/src/main/java/run/mone/neo4j/MoneCodeParser.java @@ -2,10 +2,18 @@ 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.AnnotationExpr; +import com.github.javaparser.ast.expr.FieldAccessExpr; +import com.github.javaparser.ast.expr.MethodCallExpr; +import com.github.javaparser.ast.nodeTypes.NodeWithName; +import com.github.javaparser.ast.type.ClassOrInterfaceType; +import com.google.common.collect.ImmutableMap; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonObject; @@ -16,10 +24,12 @@ import okhttp3.*; import org.neo4j.driver.Record; import org.neo4j.driver.*; +import org.neo4j.driver.types.Node; -import java.io.File; -import java.io.IOException; +import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.stream.Collectors; @Slf4j @Data @@ -30,14 +40,47 @@ public class MoneCodeParser { private String NEO4J_USER = "neo4j"; - private String NEO4J_PASSWORD = ""; + private String password = ""; private String embeddingUrl = ""; + public MoneCodeParser setPassword(String password) { + this.password = password; + return this; + } + + + public void queryEntityClasses() { + try (Driver driver = GraphDatabase.driver(NEO4J_URI, AuthTokens.basic(NEO4J_USER, password)); + Session session = driver.session()) { + // 查询 type 为 'entity' 的所有 Class 节点 + String query = "MATCH (c:Class {type: 'entity'}) RETURN c"; + Result result = session.run(query); + while (result.hasNext()) { + Record record = result.next(); + Node classNode = record.get("c").asNode(); + String name = classNode.get("name").asString(); + String fullName = classNode.get("full_name").asString(); + String type = classNode.get("type").asString(); + + // 输出或处理查询结果 + System.out.println("Class Name: " + name); + System.out.println("Full Name: " + fullName); + System.out.println("Type: " + type); + System.out.println(classNode.get("code").asString()); + } + } + } + + //获取session(class) + public Session getSession() { + return GraphDatabase.driver(NEO4J_URI, AuthTokens.basic(NEO4J_USER, password)).session(); + } + //查询所有Comment的信息(使用neo4j),返回是个List(class) public List> getAllComments() { - try (Driver driver = GraphDatabase.driver(NEO4J_URI, AuthTokens.basic(NEO4J_USER, NEO4J_PASSWORD)); + try (Driver driver = GraphDatabase.driver(NEO4J_URI, AuthTokens.basic(NEO4J_USER, password)); Session session = driver.session()) { List> comments = new ArrayList<>(); Result result = session.run("MATCH (comment:Comment) RETURN comment, id(comment) as commentId"); @@ -52,18 +95,30 @@ public List> getAllComments() { } + //给你ClassOrInterfaceDeclaration,帮我过滤掉所有method中的body,返回这个class的String内容(class) + public static String filterMethodBodies(ClassOrInterfaceDeclaration classOrInterfaceDeclaration) { + classOrInterfaceDeclaration.getMethods().forEach(method -> method.setBody(null)); + return classOrInterfaceDeclaration.toString(); + } + + //给你ClassOrInterfaceDeclaration,帮我删除掉所有method,返回这个class的String内容(class) + public static String removeAllMethods(ClassOrInterfaceDeclaration classOrInterfaceDeclaration) { + classOrInterfaceDeclaration.getMethods().forEach(MethodDeclaration::remove); + return classOrInterfaceDeclaration.toString(); + } + /** * 根据文本向量查询评论 * * @param text 输入的文本,用于生成查询向量 * @return 查询结果的列表,包含评论节点和相似度分数 */ - @SneakyThrows + @SneakyThrows public List> queryCommentsByTextVector(String text) { // 替换为你的查询向量 double[] queryVector = getTextVectorFromHttp(text); - try (Driver driver = GraphDatabase.driver(NEO4J_URI, AuthTokens.basic(NEO4J_USER, NEO4J_PASSWORD)); + try (Driver driver = GraphDatabase.driver(NEO4J_URI, AuthTokens.basic(NEO4J_USER, password)); Session session = driver.session()) { // 执行查询 @@ -118,7 +173,7 @@ public double[] convertListToFloatArray(List floatList) { * @param vectorB 第二个向量 * @return 两个向量的余弦相似度 */ - private double calculateCosineSimilarity(double[] vectorA, double[] vectorB) { + private double calculateCosineSimilarity(double[] vectorA, double[] vectorB) { double dotProduct = 0.0; double normA = 0.0; double normB = 0.0; @@ -147,7 +202,7 @@ public void fillCommentTextVectors() { //把Comment的修改,刷新回neo4j(class) public void updateCommentsInNeo4j(List> comments) { - try (Driver driver = GraphDatabase.driver(NEO4J_URI, AuthTokens.basic(NEO4J_USER, NEO4J_PASSWORD)); + try (Driver driver = GraphDatabase.driver(NEO4J_URI, AuthTokens.basic(NEO4J_USER, password)); Session session = driver.session()) { for (Map comment : comments) { Long commentId = (Long) comment.get("commentId"); @@ -166,7 +221,7 @@ public void updateCommentsInNeo4j(List> comments) { private static Gson gson = new Gson(); - private double[] getTextVectorFromHttp(String text) throws IOException { + private double[] getTextVectorFromHttp(String text) throws IOException { JsonObject jsonRequest = new JsonObject(); jsonRequest.addProperty("text", text); @@ -232,7 +287,7 @@ public static List getJavaFilesInDirectory(String directoryPath) { //删除所有节点(class) public void deleteAllNodes() { - try (Driver driver = GraphDatabase.driver(NEO4J_URI, AuthTokens.basic(NEO4J_USER, NEO4J_PASSWORD)); + try (Driver driver = GraphDatabase.driver(NEO4J_URI, AuthTokens.basic(NEO4J_USER, password)); Session session = driver.session()) { session.run("MATCH (n) DETACH DELETE n"); } @@ -243,13 +298,13 @@ public void deleteAllNodes() { * * @param filePath Java文件的路径 */ - @SneakyThrows + @SneakyThrows private void writeToNeo4j(String filePath) { //写入到neo4j中 // 替换成你的 Java 文件路径 String projectName = "MyProject"; - try (Driver driver = GraphDatabase.driver(NEO4J_URI, AuthTokens.basic(NEO4J_USER, NEO4J_PASSWORD)); + try (Driver driver = GraphDatabase.driver(NEO4J_URI, AuthTokens.basic(NEO4J_USER, password)); Session session = driver.session()) { // 解析 Java 文件 @@ -272,6 +327,32 @@ private static void createProjectNode(Session session, String projectName) { session.run("MERGE (p:Project {name: $name})", projectParams); } + /** + * 查找具有指定注解的类 + * + * @param session 数据库会话 + * @param annotationToFind 要查找的注解 + * @return 具有指定注解的类的列表,每个类以Map形式表示 + */ + public List> findClassesWithAnnotation(Session session, String annotationToFind) { + Map params = new HashMap<>(); + params.put("annotation", annotationToFind); + Result result = session.run( + "MATCH (c:Class) " + + "WHERE ANY(anno IN c.anno WHERE anno = $annotation) " + + "RETURN c", + params + ); + List> list = new ArrayList<>(); + while (result.hasNext()) { + Record record = result.next(); + System.out.println(record.get("c").asMap()); + list.add(record.get("c").asMap()); + } + return list; + } + + private static void createFileNode(Session session, String projectName, String filePath) { Map fileParams = new HashMap<>(); fileParams.put("name", filePath); @@ -301,6 +382,7 @@ public Visitor(Session session, String projectName, String filePath) { this.filePath = filePath; } + @Override public void visit(ClassOrInterfaceDeclaration n, Void arg) { // 创建 Class/Interface 节点 @@ -308,7 +390,32 @@ public void visit(ClassOrInterfaceDeclaration n, Void arg) { classParams.put("name", n.getNameAsString()); classParams.put("fullName", n.getFullyQualifiedName().orElse("")); - session.run("MERGE (c:Class {name: $name, full_name: $fullName})", classParams); + //class 的类型 + String type = getControllerType(n); + classParams.put("type", type); + + String code = ""; + + if (type.equals("entity")) { + code = removeAllMethods(n); + } + + classParams.put("code", code); + + //获取ClassOrInterfaceDeclaration中的注解 + List annoList = n.getAnnotations().stream().peek(annotation -> { + System.out.println("Annotation: " + annotation.getNameAsString()); + }).map(NodeWithName::getNameAsString).toList(); + + //注解 + classParams.put("annotations", annoList); + + session.run( + "MERGE (c:Class {name: $name}) " + + "ON CREATE SET c.full_name = $fullName, c.type = $type, c.code = $code, c.anno = $annotations " + + "ON MATCH SET c.full_name = $fullName, c.type = $type, c.code = $code, c.anno = $annotations", + classParams + ); // 创建 CONTAINS 关系 (File -[:CONTAINS]-> Class) Map containsParams = new HashMap<>(); @@ -319,17 +426,165 @@ public void visit(ClassOrInterfaceDeclaration n, Void arg) { "MERGE (f)-[:CONTAINS]->(c)", containsParams); + + // 处理字段声明,查找 @Resource 注解 + n.findAll(FieldDeclaration.class).forEach(field -> { + field.getAnnotations().forEach(annotation -> { + if (annotation.getNameAsString().equals("Resource")) { + String fieldName = field.getVariables().get(0).getNameAsString(); + String fieldType = field.getElementType().asString(); + + // 创建 DEPENDS_ON 关系 (Class -[:DEPENDS_ON]-> Service) + Map dependsOnParams = new HashMap<>(); + dependsOnParams.put("className", n.getNameAsString()); + dependsOnParams.put("serviceName", fieldType); + dependsOnParams.put("fieldName", fieldName); + + + session.run("MERGE (c:Class {name: $name})", ImmutableMap.of("name", fieldType)); + + session.run("MATCH (c:Class {name: $className}) " + + "MATCH (s:Class {name: $serviceName}) " + + "MERGE (c)-[:DEPENDS_ON {field: $fieldName}]->(s)", + dependsOnParams); + } + }); + }); + + 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); // 创建 Method 节点 Map methodParams = new HashMap<>(); - methodParams.put("name", n.getNameAsString()); + methodParams.put("name", getFullMethodName(n)); methodParams.put("signature", n.getSignature().asString()); methodParams.put("code_vector", new float[]{}); // 替换为实际的代码向量 @@ -343,7 +598,7 @@ public void visit(MethodDeclaration n, Void arg) { } declaresParams.put("className", n.findAncestor(ClassOrInterfaceDeclaration.class).get().getNameAsString()); - declaresParams.put("methodName", n.getNameAsString()); + declaresParams.put("methodName", getFullMethodName(n)); session.run("MATCH (c:Class {name: $className}) " + "MATCH (m:Method {name: $methodName}) " + @@ -351,6 +606,12 @@ public void visit(MethodDeclaration n, Void arg) { declaresParams); // 处理注释 + processComments(n); + + } + + // 处理注释 + private void processComments(MethodDeclaration n) { for (Comment comment : n.getAllContainedComments()) { createCommentNode(comment, n); } @@ -364,7 +625,6 @@ public void visit(MethodDeclaration n, Void arg) { if (commentOptional.isPresent()) { createCommentNode(commentOptional.get(), n); } - } private void createCommentNode(Comment comment, MethodDeclaration n) { @@ -377,7 +637,7 @@ private void createCommentNode(Comment comment, MethodDeclaration n) { // 创建 DOCUMENTS 关系 (Comment -[:DOCUMENTS]-> Method) Map documentsParams = new HashMap<>(); documentsParams.put("commentText", comment.getContent()); - documentsParams.put("methodName", n.getNameAsString()); + 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}) " + @@ -387,5 +647,31 @@ private void createCommentNode(Comment comment, MethodDeclaration n) { } + private static String getControllerType(ClassOrInterfaceDeclaration n) { + String type = ""; + Optional 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 index 0c83cb788..d2b6f63c2 100644 --- 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 @@ -1,7 +1,18 @@ 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 @@ -10,11 +21,76 @@ public class MoneCodeParserTest { + @Test public void testWriteCatServiceToNeo4j() { - new MoneCodeParser().writeJavaFilesToNeo4j("/Users/zhangzhiyong/IdeaProjects/ai/m78/m78-service/src/main/java/run/mone/m78/service"); +// 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> actual = moneCodeParser.findClassesWithAnnotation(moneCodeParser.getSession(), "Table"); + System.out.println(actual); + +// String xuqiu = "获取狗的主人"; + String xuqiu = "管理宠物鸟"; + String res = BotCall.call(moneCodeParser.readResourceFileContent("entity.md") + xuqiu, new Gson().toJson(actual)); + System.out.println(res); + } + + + + @Test + public void testFindClassesWithAnnotation2() { + MoneCodeParser moneCodeParser = new MoneCodeParser().setPassword(System.getenv("password")); + List> actual = moneCodeParser.findClassesWithAnnotation(moneCodeParser.getSession(), "RestController"); + System.out.println(actual); + + String xuqiu = "获取狗的主人"; +// String xuqiu = "管理宠物鸟"; + String res = BotCall.call(moneCodeParser.readResourceFileContent("service.md") + xuqiu, new Gson().toJson(actual)); + System.out.println(res); + } + + + @Test + public void testJson() { + Map m = ImmutableMap.of("input", "a+b=?"); + System.out.println(new Gson().toJson(m)); + } + + + @Test + public void testReadResourceFileContent() { + String fileName = "entity.md"; + // Assuming the test resource file is already placed in the resources directory + String actualContent = new MoneCodeParser().readResourceFileContent(fileName); + + assertNotNull(actualContent); + } + + + + } diff --git a/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/anno/Resource.java b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/anno/Resource.java new file mode 100644 index 000000000..2366ed8d8 --- /dev/null +++ b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/anno/Resource.java @@ -0,0 +1,8 @@ +package run.mone.neo4j.test.anno; + +/** + * @author goodjava@qq.com + * @date 2024/8/19 18:20 + */ +public @interface Resource { +} diff --git a/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/anno/RestController.java b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/anno/RestController.java new file mode 100644 index 000000000..770f21261 --- /dev/null +++ b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/anno/RestController.java @@ -0,0 +1,8 @@ +package run.mone.neo4j.test.anno; + +/** + * @author goodjava@qq.com + * @date 2024/8/19 16:51 + */ +public @interface RestController { +} diff --git a/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/anno/Service.java b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/anno/Service.java new file mode 100644 index 000000000..b76563edf --- /dev/null +++ b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/anno/Service.java @@ -0,0 +1,8 @@ +package run.mone.neo4j.test.anno; + +/** + * @author goodjava@qq.com + * @date 2024/8/21 16:06 + */ +public @interface Service { +} diff --git a/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/anno/Table.java b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/anno/Table.java new file mode 100644 index 000000000..e009de88d --- /dev/null +++ b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/anno/Table.java @@ -0,0 +1,8 @@ +package run.mone.neo4j.test.anno; + +/** + * @author goodjava@qq.com + * @date 2024/8/19 16:50 + */ +public @interface Table { +} diff --git a/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/anno/Test.java b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/anno/Test.java new file mode 100644 index 000000000..a28757ccb --- /dev/null +++ b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/anno/Test.java @@ -0,0 +1,8 @@ +package run.mone.neo4j.test.anno; + +/** + * @author goodjava@qq.com + * @date 2024/8/21 16:06 + */ +public @interface Test { +} diff --git a/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/Cat.java b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/Cat.java new file mode 100644 index 000000000..5e3c87714 --- /dev/null +++ b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/Cat.java @@ -0,0 +1,21 @@ +package run.mone.neo4j.test.m; + +import run.mone.neo4j.test.anno.Table; + +/** + * @author goodjava@qq.com + * @date 2024/8/19 17:03 + */ +@Table +public class Cat { + + private int id; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } +} diff --git a/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/Dog.java b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/Dog.java new file mode 100644 index 000000000..4b5dc12c3 --- /dev/null +++ b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/Dog.java @@ -0,0 +1,14 @@ +package run.mone.neo4j.test.m; + +import run.mone.neo4j.test.anno.Table; + +/** + * @author goodjava@qq.com + * @date 2024/8/19 17:03 + */ +@Table +public class Dog { + + private int id; + +} diff --git a/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/Person.java b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/Person.java new file mode 100644 index 000000000..1c67046be --- /dev/null +++ b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/Person.java @@ -0,0 +1,11 @@ +package run.mone.neo4j.test.m; + +import run.mone.neo4j.test.anno.Table; + +/** + * @author goodjava@qq.com + * @date 2024/8/19 16:51 + */ +@Table +public class Person { +} diff --git a/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/A.java b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/PersonService.java similarity index 55% rename from jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/A.java rename to jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/PersonService.java index 8281e28ce..3201894ad 100644 --- a/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/A.java +++ b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/PersonService.java @@ -1,11 +1,29 @@ -package run.mone.neo4j.test; +package run.mone.neo4j.test.m; + +import lombok.extern.slf4j.Slf4j; +import run.mone.neo4j.test.anno.Resource; +import run.mone.neo4j.test.anno.Service; +import run.mone.neo4j.test.m.cat.CatService; /** * @author goodjava@qq.com * @date 2024/8/16 10:16 - * AAA */ -public class A { +@Service +@Slf4j +public class PersonService { + + + @Resource + private CatService catService; + + + //获取猫的数量 + public int getCatCount() { + log.info("info"); + System.out.println("abc"); + return catService.getCatCount(); + } /** diff --git a/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/PersonServiceTest.java b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/PersonServiceTest.java new file mode 100644 index 000000000..a1ebd24a3 --- /dev/null +++ b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/PersonServiceTest.java @@ -0,0 +1,22 @@ +package run.mone.neo4j.test.m; + +import run.mone.neo4j.test.anno.Resource; +import run.mone.neo4j.test.anno.Service; +import run.mone.neo4j.test.anno.Test; + +/** + * @author goodjava@qq.com + * @date 2024/8/26 18:23 + */ +@Service +public class PersonServiceTest { + + @Resource + private PersonService personService; + + @Test + public void test1() { + int res = personService.getCatCount(); + System.out.println(res); + } +} diff --git a/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/cat/CatService.java b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/cat/CatService.java new file mode 100644 index 000000000..492be9300 --- /dev/null +++ b/jcommon/ai/neo4j/src/test/java/run/mone/neo4j/test/m/cat/CatService.java @@ -0,0 +1,27 @@ +package run.mone.neo4j.test.m.cat; + +import lombok.extern.slf4j.Slf4j; +import run.mone.neo4j.test.anno.Service; + +import java.util.HashMap; + +/** + * @author goodjava@qq.com + * @date 2024/8/19 18:21 + */ +@Service +@Slf4j +public class CatService { + + private HashMap data = new HashMap<>(); + + + //获取小猫的数量 + public int getCatCount() { + log.info("abc"); + System.out.println("123"); + return data.size(); + } + + +} diff --git a/jcommon/ai/neo4j/src/test/resources/entity.md b/jcommon/ai/neo4j/src/test/resources/entity.md new file mode 100644 index 000000000..0b8529b60 --- /dev/null +++ b/jcommon/ai/neo4j/src/test/resources/entity.md @@ -0,0 +1,15 @@ +我有这些表映射类,你帮我分析.我给你一个新功能,你看看能不能满足. +不满足的原因: +1.缺少相应的字段 +2.就没有这个类 + +如果不满足,你需要按如下规定返回: +1.缺少字段 satisfies:false reason:missing_fields code:返回缺少字段的代码 (example: private String name;) +2.缺少某个类 satisfies:false reason:missing_class code:返回缺少的类的代码 + +一些规则: +1.缺少字段的时候,尽量不要添加类字段,尽量引用那个类的id即可. +2.如果缺少类,你要帮我尽量补全这个类中缺少的字段. +3.生成的字段或者类尽量添加合适的中文注释 + +需求: \ No newline at end of file diff --git a/jcommon/ai/neo4j/src/test/resources/service.md b/jcommon/ai/neo4j/src/test/resources/service.md new file mode 100644 index 000000000..0670fbd6e --- /dev/null +++ b/jcommon/ai/neo4j/src/test/resources/service.md @@ -0,0 +1,16 @@ +我有这些表映射类和service类的信息,然后我给你一个新功能,你分析下看看能不能满足. + +不满足的原因: +1.缺少相应的方法 +2.缺少某个service类 + +如果不满足,你需要按如下规定返回: +1.缺少字段 satisfies:false reason:missing_methods code:返回缺少方法的代码 (example: private String name;) +2.缺少某个service类 satisfies:false reason:missing_class code:返回缺少的类的代码 + +一些规则: +1.缺少method的时候,生成的方法只需要给方法定义即可. +2.如果缺少类,你要帮我生成这个类,类中的方法也只需要生成接口定义. +3.生成的方法或者类尽量添加合适的中文注释 + +需求: \ No newline at end of file diff --git a/jcommon/ai/pom.xml b/jcommon/ai/pom.xml index 41a73b29f..748017515 100644 --- a/jcommon/ai/pom.xml +++ b/jcommon/ai/pom.xml @@ -18,6 +18,7 @@ aws minimax bytedance + gpt diff --git a/jcommon/codegen/src/main/java/run/mone/ai/codegen/FeatureGenerator.java b/jcommon/codegen/src/main/java/run/mone/ai/codegen/FeatureGenerator.java index 5ddc8b3df..b5f946410 100644 --- a/jcommon/codegen/src/main/java/run/mone/ai/codegen/FeatureGenerator.java +++ b/jcommon/codegen/src/main/java/run/mone/ai/codegen/FeatureGenerator.java @@ -8,15 +8,11 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.jdbc.core.JdbcTemplate; -import run.mone.ai.codegen.bo.FeatureGenerateType; import run.mone.ai.codegen.bo.FeatureGenerateBo; +import run.mone.ai.codegen.bo.FeatureGenerateType; import run.mone.ai.codegen.util.TemplateUtils; import java.lang.reflect.Type; -import run.mone.ai.codegen.bo.FeatureGeneratType; -import run.mone.ai.codegen.bo.FeatureGenerateBo; -import run.mone.ai.codegen.util.TemplateUtils; - import java.util.Base64; import java.util.HashMap; import java.util.Map; @@ -141,26 +137,29 @@ public static void generateWithGenerator(FeatureGenerateBo featureGenerateBo) { } //配置数据源 - HikariDataSource dataSource = new HikariDataSource(); - dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); - dataSource.setJdbcUrl(featureGenerateBo.getJdbcUrl()); - dataSource.setUsername(featureGenerateBo.getUserName()); - dataSource.setPassword(featureGenerateBo.getPassword()); - - //创建mapper相关代码 - - if (FeatureGenerateType.CODE_WITH_TEMPLATE == featureGenType) { - GlobalConfig globalConfig = createGlobalConfigUseStyle(featureGenerateBo); - Generator generator = new Generator(dataSource, globalConfig); - generator.generate(); - return; - } + try (HikariDataSource dataSource = new HikariDataSource()) { + dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); + dataSource.setJdbcUrl(featureGenerateBo.getJdbcUrl()); + dataSource.setUsername(featureGenerateBo.getUserName()); + dataSource.setPassword(featureGenerateBo.getPassword()); + + //创建mapper相关代码 + + if (FeatureGenerateType.CODE_WITH_TEMPLATE == featureGenType) { + GlobalConfig globalConfig = createGlobalConfigUseStyle(featureGenerateBo); + Generator generator = new Generator(dataSource, globalConfig); + generator.generate(); + return; + } - //创建table + //创建table - if (FeatureGenerateType.TABLE == featureGenType) { - JdbcTemplate jt = new JdbcTemplate(dataSource); - jt.update(featureGenerateBo.getSql()); + if (FeatureGenerateType.TABLE == featureGenType) { + JdbcTemplate jt = new JdbcTemplate(dataSource); + jt.update(featureGenerateBo.getSql()); + } + } catch (Exception e) { + log.error("generate with template error", e); } } diff --git a/jcommon/docean-plugin/docean-plugin-configuration/src/main/java/com/xiaomi/youpin/docean/plugin/configuration/ConfigurationPlugin.java b/jcommon/docean-plugin/docean-plugin-configuration/src/main/java/com/xiaomi/youpin/docean/plugin/configuration/ConfigurationPlugin.java index e78431c34..37d10ecad 100644 --- a/jcommon/docean-plugin/docean-plugin-configuration/src/main/java/com/xiaomi/youpin/docean/plugin/configuration/ConfigurationPlugin.java +++ b/jcommon/docean-plugin/docean-plugin-configuration/src/main/java/com/xiaomi/youpin/docean/plugin/configuration/ConfigurationPlugin.java @@ -38,7 +38,7 @@ * @author shanwb * @date 2022-09-13 */ -@DOceanPlugin(order = Integer.MAX_VALUE) +@DOceanPlugin(order = Integer.MAX_VALUE - 2) @Slf4j public class ConfigurationPlugin implements IPlugin { diff --git a/jcommon/docean-plugin/docean-plugin-mybatis-plus/src/main/java/com/xiaomi/youpin/docean/plugin/mybatisplus/MybatisPlugin.java b/jcommon/docean-plugin/docean-plugin-mybatis-plus/src/main/java/com/xiaomi/youpin/docean/plugin/mybatisplus/MybatisPlugin.java index 76035713b..37278b1d2 100644 --- a/jcommon/docean-plugin/docean-plugin-mybatis-plus/src/main/java/com/xiaomi/youpin/docean/plugin/mybatisplus/MybatisPlugin.java +++ b/jcommon/docean-plugin/docean-plugin-mybatis-plus/src/main/java/com/xiaomi/youpin/docean/plugin/mybatisplus/MybatisPlugin.java @@ -16,6 +16,7 @@ package com.xiaomi.youpin.docean.plugin.mybatisplus; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.xiaomi.youpin.docean.Aop; import com.xiaomi.youpin.docean.Ioc; import com.xiaomi.youpin.docean.anno.DOceanPlugin; @@ -33,6 +34,7 @@ import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; +import org.springframework.util.CollectionUtils; import javax.sql.DataSource; import java.lang.reflect.InvocationHandler; @@ -47,7 +49,7 @@ /** * @author goodjava@qq.com */ -@DOceanPlugin +@DOceanPlugin(order = Integer.MAX_VALUE - 1) @Slf4j public class MybatisPlugin implements IPlugin { @@ -93,6 +95,11 @@ private void addDAO(Ioc ioc, String beanName, String mapperLocation, boolean one bean.setPlugins(new Interceptor[]{interceptor}); } + Set plusInterceptors = ioc.getBeans(MybatisPlusInterceptor.class); + if (!CollectionUtils.isEmpty(plusInterceptors)) { + bean.setPlugins(plusInterceptors.toArray(new Interceptor[]{})); + } + SqlSessionFactory factory = bean.buildSqlSessionFactory(); ioc.putBean("mybatis_" + beanName + config.getName(), factory); diff --git a/jcommon/file/src/main/java/com/xiaomi/mone/file/LogFile2.java b/jcommon/file/src/main/java/com/xiaomi/mone/file/LogFile2.java index fbbd399c4..0381a698a 100644 --- a/jcommon/file/src/main/java/com/xiaomi/mone/file/LogFile2.java +++ b/jcommon/file/src/main/java/com/xiaomi/mone/file/LogFile2.java @@ -190,6 +190,7 @@ public void readLine() throws IOException { readResult.setFilePathName(file); readResult.setLineNumber(++lineNumber); ReadEvent event = new ReadEvent(readResult); + listener.setReadTime(); listener.onEvent(event); } diff --git a/jcommon/file/src/main/java/com/xiaomi/mone/file/ReadListener.java b/jcommon/file/src/main/java/com/xiaomi/mone/file/ReadListener.java index 0329b66bf..75e92b157 100644 --- a/jcommon/file/src/main/java/com/xiaomi/mone/file/ReadListener.java +++ b/jcommon/file/src/main/java/com/xiaomi/mone/file/ReadListener.java @@ -37,4 +37,7 @@ default void setPointer(Object obj) { default void saveProgress() { } + default void setReadTime() { + } + } diff --git a/jcommon/file/src/main/java/com/xiaomi/mone/file/listener/OzHeraReadListener.java b/jcommon/file/src/main/java/com/xiaomi/mone/file/listener/OzHeraReadListener.java index e2b3c2d42..43fa64876 100644 --- a/jcommon/file/src/main/java/com/xiaomi/mone/file/listener/OzHeraReadListener.java +++ b/jcommon/file/src/main/java/com/xiaomi/mone/file/listener/OzHeraReadListener.java @@ -72,4 +72,12 @@ public void setPointer(Object obj) { } } } + + @Override + public void setReadTime() { + HeraFile f = monitor.getFileMap().get(logFile.getFileKey()); + if (null != f) { + f.getReadTime().set(System.currentTimeMillis()); + } + } } diff --git a/jcommon/file/src/main/java/com/xiaomi/mone/file/ozhera/HeraFile.java b/jcommon/file/src/main/java/com/xiaomi/mone/file/ozhera/HeraFile.java index 8acb1a989..fbbde66fe 100644 --- a/jcommon/file/src/main/java/com/xiaomi/mone/file/ozhera/HeraFile.java +++ b/jcommon/file/src/main/java/com/xiaomi/mone/file/ozhera/HeraFile.java @@ -30,4 +30,7 @@ public class HeraFile { @Builder.Default private AtomicLong utime = new AtomicLong(System.currentTimeMillis()); + + @Builder.Default + private AtomicLong readTime = new AtomicLong(System.currentTimeMillis()); } diff --git a/jcommon/file/src/main/java/com/xiaomi/mone/file/ozhera/HeraFileMonitor.java b/jcommon/file/src/main/java/com/xiaomi/mone/file/ozhera/HeraFileMonitor.java index c8a2d1e84..0f7c7d385 100644 --- a/jcommon/file/src/main/java/com/xiaomi/mone/file/ozhera/HeraFileMonitor.java +++ b/jcommon/file/src/main/java/com/xiaomi/mone/file/ozhera/HeraFileMonitor.java @@ -34,6 +34,7 @@ public class HeraFileMonitor { @Getter private ConcurrentHashMap map = new ConcurrentHashMap<>(); + @Getter private ConcurrentHashMap fileMap = new ConcurrentHashMap<>(); @Setter @@ -52,7 +53,7 @@ public HeraFileMonitor(long removeTime) { List> remList = Lists.newArrayList(); long now = System.currentTimeMillis(); fileMap.values().forEach(it -> { - if (now - it.getUtime().get() >= removeTime) { + if (now - it.getUtime().get() >= removeTime && now - it.getReadTime().get() >= removeTime) { remList.add(Pair.of(it.getFileName(), it.getFileKey())); } }); @@ -129,7 +130,7 @@ public void reg(String path, Predicate predicate) throws IOException, In map.putIfAbsent(k, hf); fileMap.put(filePath, hf); - listener.onEvent(FileEvent.builder().type(EventType.create).fileName(file.getPath()).build()); + listener.onEvent(FileEvent.builder().type(EventType.create).fileKey(k).fileName(file.getPath()).build()); } } } @@ -167,7 +168,12 @@ private HeraFile initFile(File it) { log.info("initFile fileName:{},fileKey:{}", name, fileKey); map.put(hf.getFileKey(), hf); fileMap.put(hf.getFileName(), hf); - this.listener.onEvent(FileEvent.builder().pointer(pointer).type(EventType.init).fileName(hf.getFileName()).build()); + this.listener.onEvent(FileEvent.builder() + .pointer(pointer) + .type(EventType.init) + .fileName(hf.getFileName()) + .fileKey(hf.getFileKey()) + .build()); return hf; } catch (Exception e) { log.error("init file error,fileName:{}", name, e); diff --git a/jcommon/file/src/test/java/com/xiaomi/mone/file/LogFileTest.java b/jcommon/file/src/test/java/com/xiaomi/mone/file/LogFileTest.java index d7b987194..e57859f58 100644 --- a/jcommon/file/src/test/java/com/xiaomi/mone/file/LogFileTest.java +++ b/jcommon/file/src/test/java/com/xiaomi/mone/file/LogFileTest.java @@ -24,6 +24,8 @@ import org.junit.Test; import java.io.IOException; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; @@ -74,9 +76,14 @@ public void testLogFileMonitor() { monitor.setListener(new DefaultMonitorListener(monitor, readEvent -> { System.out.println(readEvent.getReadResult().getLines()); })); - String fileName = "/home/work/log/file.log.*"; + String fileName = "/home/work/log/test/file*.txt"; Pattern pattern = Pattern.compile(fileName); - monitor.reg("/home/work/log", it -> { + + ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); + + scheduler.scheduleAtFixedRate(this::test, 1, 2, TimeUnit.SECONDS); + + monitor.reg("/home/work/log/test", it -> { boolean matches = pattern.matcher(it).matches(); log.info("file:{},matches:{}", it, true); return true; @@ -85,6 +92,11 @@ public void testLogFileMonitor() { System.in.read(); } + private void test() { + log.info("test save progress"); + FileInfoCache.ins().shutdown(); + } + @Test public void testLogWS() throws IOException { LogFile log = new LogFile("D:\\test.log", new ReadListener() { diff --git a/jcommon/gitlab/src/main/java/com/xiaomi/youpin/gitlab/Gitlab.java b/jcommon/gitlab/src/main/java/com/xiaomi/youpin/gitlab/Gitlab.java index 718043bc2..418d060bd 100644 --- a/jcommon/gitlab/src/main/java/com/xiaomi/youpin/gitlab/Gitlab.java +++ b/jcommon/gitlab/src/main/java/com/xiaomi/youpin/gitlab/Gitlab.java @@ -516,7 +516,7 @@ private static boolean modifyFile(String changedBody, String filePathToReplace, return true; } - private static boolean clone(String gitUrl, String branch, String username, String token, String gitPath) { + public static boolean clone(String gitUrl, String branch, String username, String token, String gitPath) { clearIfPresent(gitPath); CloneCommand cloneCommand = Git.cloneRepository().setURI(gitUrl) .setCredentialsProvider(new UsernamePasswordCredentialsProvider(username, token))