Henry 1 سال پیش
والد
کامیت
bb97a9cf28
35فایلهای تغییر یافته به همراه1776 افزوده شده و 0 حذف شده
  1. 22 0
      web/pom.xml
  2. 30 0
      web/src/main/java/cn/seecoder/web/controller/gradlellm/ChatController.java
  3. 91 0
      web/src/main/java/cn/seecoder/web/controller/gradlellm/ConfigController.java
  4. 70 0
      web/src/main/java/cn/seecoder/web/controller/gradlellm/ProjectAnalysisController.java
  5. 21 0
      web/src/main/java/cn/seecoder/web/core/gradlellm/CodeFileService.java
  6. 17 0
      web/src/main/java/cn/seecoder/web/core/gradlellm/ConfigService.java
  7. 24 0
      web/src/main/java/cn/seecoder/web/core/gradlellm/ConversationService.java
  8. 24 0
      web/src/main/java/cn/seecoder/web/core/gradlellm/DifyMessageService.java
  9. 65 0
      web/src/main/java/cn/seecoder/web/core/gradlellm/DifyService.java
  10. 25 0
      web/src/main/java/cn/seecoder/web/core/gradlellm/FileChangeService.java
  11. 21 0
      web/src/main/java/cn/seecoder/web/core/gradlellm/ProjectAnalysisService.java
  12. 60 0
      web/src/main/java/cn/seecoder/web/core/gradlellm/impl/CodeFileServiceImpl.java
  13. 220 0
      web/src/main/java/cn/seecoder/web/core/gradlellm/impl/ConfigServiceImpl.java
  14. 66 0
      web/src/main/java/cn/seecoder/web/core/gradlellm/impl/ConversationServiceImpl.java
  15. 56 0
      web/src/main/java/cn/seecoder/web/core/gradlellm/impl/DifyMessageServiceImpl.java
  16. 61 0
      web/src/main/java/cn/seecoder/web/core/gradlellm/impl/FileChangeServiceImpl.java
  17. 198 0
      web/src/main/java/cn/seecoder/web/core/gradlellm/impl/ProjectAnalysisServiceImpl.java
  18. 14 0
      web/src/main/java/cn/seecoder/web/core/gradlellm/utils/Config.java
  19. 29 0
      web/src/main/java/cn/seecoder/web/core/gradlellm/utils/FIleUtil.java
  20. 172 0
      web/src/main/java/cn/seecoder/web/core/gradlellm/utils/ModuleGradleVisitor.java
  21. 51 0
      web/src/main/java/cn/seecoder/web/core/gradlellm/utils/PrintGradleVisitor.java
  22. 124 0
      web/src/main/java/cn/seecoder/web/core/gradlellm/utils/RootGradleVisitor.java
  23. 33 0
      web/src/main/java/cn/seecoder/web/dao/gradlellm/CodeFileMapper.java
  24. 34 0
      web/src/main/java/cn/seecoder/web/dao/gradlellm/ConversationMapper.java
  25. 38 0
      web/src/main/java/cn/seecoder/web/dao/gradlellm/DifyMessageMapper.java
  26. 39 0
      web/src/main/java/cn/seecoder/web/dao/gradlellm/FileChangeMapper.java
  27. 30 0
      web/src/main/java/cn/seecoder/web/model/enums/DifyApiKeyEnum.java
  28. 20 0
      web/src/main/java/cn/seecoder/web/model/po/gradlellm/CodeFilePO.java
  29. 21 0
      web/src/main/java/cn/seecoder/web/model/po/gradlellm/ConversationPO.java
  30. 22 0
      web/src/main/java/cn/seecoder/web/model/po/gradlellm/DifyMessagePO.java
  31. 22 0
      web/src/main/java/cn/seecoder/web/model/po/gradlellm/FileChangePO.java
  32. 18 0
      web/src/main/java/cn/seecoder/web/model/vo/gradlellm/ChatMessageRequest.java
  33. 19 0
      web/src/main/java/cn/seecoder/web/model/vo/gradlellm/ChatMessageResponse.java
  34. 9 0
      web/src/main/java/cn/seecoder/web/model/vo/gradlellm/CodeQuestion.java
  35. 10 0
      web/src/main/java/cn/seecoder/web/model/vo/gradlellm/CodeQuestionRequest.java

+ 22 - 0
web/pom.xml

@@ -132,6 +132,28 @@
             <artifactId>seecoder-gitlab-client</artifactId>
             <artifactId>seecoder-gitlab-client</artifactId>
             <version>0.1.2</version>
             <version>0.1.2</version>
         </dependency>
         </dependency>
+
+        <dependency>
+            <groupId>org.apache.groovy</groupId>
+            <artifactId>groovy</artifactId>
+            <version>4.0.24</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.groovy</groupId>
+            <artifactId>groovy-console</artifactId>
+            <version>4.0.24</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jetbrains.kotlin</groupId>
+            <artifactId>kotlin-compiler-embeddable</artifactId>
+            <version>1.9.20</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jetbrains.kotlin</groupId>
+            <artifactId>kotlin-stdlib</artifactId>
+            <version>1.9.20</version>
+        </dependency>
     </dependencies>
     </dependencies>
 
 
     <dependencyManagement>
     <dependencyManagement>

+ 30 - 0
web/src/main/java/cn/seecoder/web/controller/gradlellm/ChatController.java

@@ -0,0 +1,30 @@
+package cn.seecoder.web.controller.gradlellm;
+
+
+import cn.seecoder.web.model.enums.DifyApiKeyEnum;
+import cn.seecoder.web.model.vo.gradlellm.ChatMessageRequest;
+import cn.seecoder.web.model.vo.gradlellm.ChatMessageResponse;
+import cn.seecoder.web.core.gradlellm.DifyService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/chat")
+public class ChatController {
+
+    @Autowired
+    private DifyService difyService;
+
+    @PostMapping("/sendMessage")
+    public ResponseEntity<ChatMessageResponse> sendMessage(@RequestBody ChatMessageRequest request) {
+        difyService.setApiKey(DifyApiKeyEnum.PROJECT.getApiKey());
+        try {
+            ChatMessageResponse response = difyService.sendMessage(request);
+            return new ResponseEntity<>(response, HttpStatus.OK);
+        } catch (Exception e) {
+            return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+    }
+}

+ 91 - 0
web/src/main/java/cn/seecoder/web/controller/gradlellm/ConfigController.java

@@ -0,0 +1,91 @@
+package cn.seecoder.web.controller.gradlellm;
+
+import cn.seecoder.web.core.gradlellm.ConfigService;
+import cn.seecoder.web.model.po.gradlellm.FileChangePO;
+import cn.seecoder.web.model.po.gradlellm.CodeFilePO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/config")
+public class ConfigController {
+
+    @Autowired
+    private ConfigService configService;
+
+    @PostMapping("/modify-by-ast")
+    public ResponseEntity<String> modifyConfigByAST(@RequestBody String filePath) {
+        try {
+            boolean result = configService.modifyConfigByAST(filePath, "content");
+            if (result) {
+                return ResponseEntity.ok("Gradle配置文件已通过AST方式修改成功");
+            } else {
+                return ResponseEntity.badRequest().body("Gradle配置文件修改失败");
+            }
+        } catch (Exception e) {
+            return ResponseEntity.status(500).body("修改Gradle配置文件时发生错误: " + e.getMessage());
+        }
+    }
+
+
+    @PostMapping("/modify-by-llm")
+    public ResponseEntity<String> modifyConfigByLLM(@RequestBody String filePath) {
+        try {
+            boolean result = configService.modifyConfigByLLM(filePath,"content");
+            if (result) {
+                return ResponseEntity.ok("Gradle配置文件已通过LLM方式修改成功");
+            } else {
+                return ResponseEntity.badRequest().body("Gradle配置文件修改失败");
+            }
+        } catch (Exception e) {
+            return ResponseEntity.status(500).body("修改Gradle配置文件时发生错误: " + e.getMessage());
+        }
+    }
+
+
+    @GetMapping("/changes")
+    public ResponseEntity<List<FileChangePO>> listConfigChanges() {
+        try {
+            List<FileChangePO> changes = configService.listConfigChanges();
+            if (changes != null) {
+                return ResponseEntity.ok(changes);
+            } else {
+                return ResponseEntity.notFound().build();
+            }
+        } catch (Exception e) {
+            return ResponseEntity.status(500).body(null);
+        }
+    }
+
+
+    @GetMapping("/changes/{fileChangeId}")
+    public ResponseEntity<List<CodeFilePO>> getChangeDetail(@PathVariable Long fileChangeId) {
+        try {
+            List<CodeFilePO> details = configService.getChangeDetail(fileChangeId);
+            if (details != null) {
+                return ResponseEntity.ok(details);
+            } else {
+                return ResponseEntity.notFound().build();
+            }
+        } catch (Exception e) {
+            return ResponseEntity.status(500).body(null);
+        }
+    }
+
+    @PostMapping("/rollback/{fileChangeId}")
+    public ResponseEntity<String> rollbackChange(@PathVariable Long fileChangeId) {
+        try {
+            boolean result = configService.rollbackChange(fileChangeId);
+            if (result) {
+                return ResponseEntity.ok("配置变更已成功回滚");
+            } else {
+                return ResponseEntity.badRequest().body("回滚配置变更失败");
+            }
+        } catch (Exception e) {
+            return ResponseEntity.status(500).body("回滚配置变更时发生错误: " + e.getMessage());
+        }
+    }
+}

+ 70 - 0
web/src/main/java/cn/seecoder/web/controller/gradlellm/ProjectAnalysisController.java

@@ -0,0 +1,70 @@
+package cn.seecoder.web.controller.gradlellm;
+
+import cn.seecoder.web.core.gradlellm.ProjectAnalysisService;
+import cn.seecoder.web.model.vo.gradlellm.ChatMessageResponse;
+import cn.seecoder.web.model.po.gradlellm.ConversationPO;
+import cn.seecoder.web.model.po.gradlellm.DifyMessagePO;
+import cn.seecoder.web.model.vo.gradlellm.CodeQuestion;
+import cn.seecoder.web.model.vo.gradlellm.CodeQuestionRequest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/project")
+public class ProjectAnalysisController {
+
+    @Autowired
+    private ProjectAnalysisService projectAnalysisService;
+
+
+    @PostMapping("/analyze")
+    public String analyzeProjectStructure(@RequestBody String filePath) throws IOException {
+        List<File> files = new ArrayList<>();
+        files.add(new File("E:/2024大四/毕设/grit/app/build.gradle.kts"));
+        files.add(new File("E:/2024大四/毕设/grit/app/src/main/AndroidManifest.xml"));
+        return projectAnalysisService.analyzeProjectStructure(filePath, files);
+    }
+
+
+    @PostMapping("/answer-code-question")
+    public ChatMessageResponse answerCodeQuestion(@RequestBody CodeQuestionRequest codeQuestionRequest) {
+        try {
+            String filePath = codeQuestionRequest.getFilePath();
+            String question = codeQuestionRequest.getQuestion();
+            return projectAnalysisService.answerCodeQuestion(filePath, question);
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+
+    @GetMapping("/qa-records")
+    public List<ConversationPO> listQARecords() {
+        return projectAnalysisService.listQARecords();
+    }
+
+
+    @GetMapping("/conversation-history")
+    public List<DifyMessagePO> getConversationHistory(@RequestParam String conversationId) {
+        return projectAnalysisService.getConversationHistory(conversationId);
+    }
+
+    @PostMapping("/answer-code")
+    public ChatMessageResponse answerCodeQuestionByLLM(@RequestBody CodeQuestion codeQuestion) {
+        String conversationId = codeQuestion.getConversationId();
+        String question = codeQuestion.getQuestion();
+        try {
+            return projectAnalysisService.answerCodeQuestionByLLM(conversationId, question);
+        } catch (IOException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+}

+ 21 - 0
web/src/main/java/cn/seecoder/web/core/gradlellm/CodeFileService.java

@@ -0,0 +1,21 @@
+package cn.seecoder.web.core.gradlellm;
+
+import cn.seecoder.web.model.po.gradlellm.CodeFilePO;
+import java.util.List;
+
+public interface CodeFileService {
+
+    CodeFilePO createCodeFile(CodeFilePO codeFilePO, String... ignoredCols);
+
+    int updateCodeFile(CodeFilePO codeFilePO);
+
+    int deleteCodeFile(Long id);
+
+    CodeFilePO getCodeFileById(Long id);
+
+    List<CodeFilePO> getAllCodeFiles();
+
+    List<CodeFilePO> getCodeFilesByFilePath(String filePath);
+
+    boolean exists(Long id);
+}

+ 17 - 0
web/src/main/java/cn/seecoder/web/core/gradlellm/ConfigService.java

@@ -0,0 +1,17 @@
+package cn.seecoder.web.core.gradlellm;
+
+import cn.seecoder.web.model.po.gradlellm.CodeFilePO;
+import cn.seecoder.web.model.po.gradlellm.FileChangePO;
+import java.util.List;
+
+public interface ConfigService {
+    boolean modifyConfigByLLM(String FilePath, String content);
+
+    boolean modifyConfigByAST(String FilePath, String content);
+
+    List<FileChangePO> listConfigChanges();
+
+    List<CodeFilePO> getChangeDetail(Long fileChangeId);
+
+    boolean rollbackChange(Long fileChangeId);
+}

+ 24 - 0
web/src/main/java/cn/seecoder/web/core/gradlellm/ConversationService.java

@@ -0,0 +1,24 @@
+package cn.seecoder.web.core.gradlellm;
+
+import cn.seecoder.web.model.po.gradlellm.ConversationPO;
+import java.util.Date;
+import java.util.List;
+
+public interface ConversationService {
+
+    ConversationPO createConversation(ConversationPO conversationPO);
+
+    int updateConversation(ConversationPO conversationPO);
+
+    int deleteConversation(Long id);
+
+    ConversationPO getConversationById(Long id);
+
+    List<ConversationPO> getAllConversations();
+
+    List<ConversationPO> getConversationsByUserId(Long userId);
+
+    ConversationPO getConversationByConversationId(String conversationId);
+
+    boolean exists(Long id);
+}

+ 24 - 0
web/src/main/java/cn/seecoder/web/core/gradlellm/DifyMessageService.java

@@ -0,0 +1,24 @@
+package cn.seecoder.web.core.gradlellm;
+
+import cn.seecoder.web.model.po.gradlellm.DifyMessagePO;
+import java.util.List;
+
+public interface DifyMessageService {
+
+
+    int insert(DifyMessagePO difyMessagePO, String... ignoredCols);
+
+    int update(DifyMessagePO difyMessagePO);
+
+    int delete(Long id);
+
+    DifyMessagePO selectById(Long id);
+
+    List<DifyMessagePO> selectByUserId(Long userId);
+
+    List<DifyMessagePO> selectByConversationId(String conversationId);
+
+    List<DifyMessagePO> selectByRole(String role);
+
+    List<DifyMessagePO> selectByConversationIdAndRole(String conversationId, String role);
+}

+ 65 - 0
web/src/main/java/cn/seecoder/web/core/gradlellm/DifyService.java

@@ -0,0 +1,65 @@
+package cn.seecoder.web.core.gradlellm;
+
+import cn.seecoder.web.model.vo.gradlellm.ChatMessageRequest;
+import cn.seecoder.web.model.vo.gradlellm.ChatMessageResponse;
+import lombok.Setter;
+import org.springframework.stereotype.Service;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.*;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+
+import org.springframework.web.client.RestTemplate;
+
+@Service
+public class DifyService {
+    private String difyApiUrl = "https://dify.seec.seecoder.cn/v1/chat-messages";
+
+    @Setter
+    private String apiKey;
+
+//    private ConversationService conversationService;
+//
+//    @Autowired
+//    public DifyService(ConversationService conversationService) {
+//        this.conversationService = conversationService;
+//    }
+
+    private final RestTemplate restTemplate = myRestTemplate();
+
+    public RestTemplate myRestTemplate() {
+        RestTemplate restTemplate = new RestTemplate();
+
+        // 使用 SimpleClientHttpRequestFactory 并设置超时
+        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
+        factory.setConnectTimeout(120 * 1000);  // 连接超时 2 分钟
+        factory.setReadTimeout(120 * 1000);     // 读取超时 2 分钟
+        restTemplate.setRequestFactory(factory);
+
+        return restTemplate;
+    }
+
+    public ChatMessageResponse sendMessage(ChatMessageRequest request) {
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        headers.set("Authorization", "Bearer " + apiKey);
+        HttpEntity<ChatMessageRequest> requestEntity = new HttpEntity<>(request, headers);
+
+        try {
+            // 使用配置了超时的 restTemplate 发送请求
+            ResponseEntity<ChatMessageResponse> response = restTemplate.postForEntity(difyApiUrl, requestEntity, ChatMessageResponse.class);
+
+            if (response.getStatusCode() == HttpStatus.OK) {
+//                if (response.getBody().getConversation_id() != null) {
+//                    conversationService.createConversation(2999L, response.getBody().getConversation_id());
+//                }
+                return response.getBody();
+            } else {
+                throw new RuntimeException("Failed to send message. Status code: " + response.getStatusCode());
+            }
+        } catch (Exception e) {
+            System.err.println("Error occurred while sending message: " + e.getMessage());
+            throw new RuntimeException("Failed to send message", e);
+        }
+    }
+
+}

+ 25 - 0
web/src/main/java/cn/seecoder/web/core/gradlellm/FileChangeService.java

@@ -0,0 +1,25 @@
+package cn.seecoder.web.core.gradlellm;
+
+import cn.seecoder.web.model.po.gradlellm.FileChangePO;
+import java.util.List;
+
+public interface FileChangeService {
+
+    int insert(FileChangePO fileChangePO, String... ignoredCols);
+
+    int update(FileChangePO fileChangePO);
+
+    int delete(Long id);
+
+    FileChangePO selectById(Long id);
+
+    List<FileChangePO> selectByUserId(Long userId);
+
+    List<FileChangePO> selectByOldFileId(Long oldFileId);
+
+    List<FileChangePO> selectByNewFileId(Long newFileId);
+
+    List<FileChangePO> selectByFileId(Long fileId);
+
+    List<FileChangePO> selectAll();
+}

+ 21 - 0
web/src/main/java/cn/seecoder/web/core/gradlellm/ProjectAnalysisService.java

@@ -0,0 +1,21 @@
+package cn.seecoder.web.core.gradlellm;
+
+import cn.seecoder.web.model.po.gradlellm.ConversationPO;
+import cn.seecoder.web.model.po.gradlellm.DifyMessagePO;
+import cn.seecoder.web.model.vo.gradlellm.ChatMessageResponse;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+public interface ProjectAnalysisService {
+    String analyzeProjectStructure(String FilePath, List<File> inputs) throws IOException;
+
+    ChatMessageResponse answerCodeQuestion(String FilePath, String question) throws IOException;
+
+    List<ConversationPO> listQARecords();
+
+    List<DifyMessagePO> getConversationHistory(String conversationId);
+
+    ChatMessageResponse answerCodeQuestionByLLM(String conversationId, String question) throws IOException;
+}

+ 60 - 0
web/src/main/java/cn/seecoder/web/core/gradlellm/impl/CodeFileServiceImpl.java

@@ -0,0 +1,60 @@
+package cn.seecoder.web.core.gradlellm.impl;
+
+import cn.seecoder.web.core.gradlellm.CodeFileService;
+import cn.seecoder.web.dao.gradlellm.CodeFileMapper;
+import cn.seecoder.web.model.po.gradlellm.CodeFilePO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+@Service
+public class CodeFileServiceImpl implements CodeFileService {
+
+    private final CodeFileMapper codeFileMapper;
+
+    @Autowired
+    public CodeFileServiceImpl(CodeFileMapper codeFileMapper) {
+        this.codeFileMapper = codeFileMapper;
+    }
+
+    @Override
+    @Transactional
+    public CodeFilePO createCodeFile(CodeFilePO codeFilePO, String... ignoredCols) {
+        codeFileMapper.insert(codeFilePO, ignoredCols);
+        return codeFilePO;
+    }
+
+    @Override
+    @Transactional
+    public int updateCodeFile(CodeFilePO codeFilePO) {
+        return codeFileMapper.update(codeFilePO);
+    }
+
+    @Override
+    @Transactional
+    public int deleteCodeFile(Long id) {
+        return codeFileMapper.delete(id);
+    }
+
+    @Override
+    public CodeFilePO getCodeFileById(Long id) {
+        return codeFileMapper.selectById(id);
+    }
+
+    @Override
+    public List<CodeFilePO> getAllCodeFiles() {
+        return codeFileMapper.selectAll();
+    }
+
+    @Override
+    public List<CodeFilePO> getCodeFilesByFilePath(String filePath) {
+        return codeFileMapper.selectByFilePath(filePath);
+    }
+
+    @Override
+    public boolean exists(Long id) {
+        return codeFileMapper.selectById(id) != null;
+    }
+}

+ 220 - 0
web/src/main/java/cn/seecoder/web/core/gradlellm/impl/ConfigServiceImpl.java

@@ -0,0 +1,220 @@
+package cn.seecoder.web.core.gradlellm.impl;
+
+import cn.seecoder.web.core.gradlellm.CodeFileService;
+import cn.seecoder.web.core.gradlellm.ConfigService;
+import cn.seecoder.web.core.gradlellm.DifyService;
+import cn.seecoder.web.core.gradlellm.FileChangeService;
+import cn.seecoder.web.core.gradlellm.utils.FIleUtil;
+import cn.seecoder.web.core.gradlellm.utils.ModuleGradleVisitor;
+import cn.seecoder.web.core.gradlellm.utils.PrintGradleVisitor;
+import cn.seecoder.web.core.gradlellm.utils.RootGradleVisitor;
+import cn.seecoder.web.dao.gradlellm.FileChangeMapper;
+import cn.seecoder.web.model.enums.DifyApiKeyEnum;
+import cn.seecoder.web.model.po.gradlellm.CodeFilePO;
+import cn.seecoder.web.model.po.gradlellm.FileChangePO;
+import cn.seecoder.web.model.vo.gradlellm.ChatMessageRequest;
+import cn.seecoder.web.model.vo.gradlellm.ChatMessageResponse;
+import cn.seecoder.web.service.user.UserService;
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.builder.AstBuilder;
+import org.springframework.stereotype.Service;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+@Service
+public class ConfigServiceImpl implements ConfigService {
+
+    @Autowired
+    private FileChangeService fileChangeService;
+
+    @Autowired
+    private CodeFileService codeFileService;
+
+    @Autowired
+    private DifyService difyService;
+
+    @Autowired
+    private FileChangeMapper fileChangeMapper;
+
+    @Override
+    public boolean modifyConfigByAST(String FilePath, String content) {
+        if (FilePath == null || FilePath.isEmpty()){
+            throw new IllegalArgumentException("Project directory path cannot be null or empty");
+        }
+
+        File projectDir = new File(FilePath);
+        if (!projectDir.exists()) {
+            throw new IllegalArgumentException("Project directory does not exist: " + FilePath);
+        }
+
+        try {
+            Files.walk(projectDir.toPath())
+                    .filter(path -> path.getFileName().toString().trim().equals("build.gradle"))
+                    .forEach(path -> {
+                        try {
+                            patchGradleFile(path.toFile(), path.getParent().equals(projectDir.toPath()));
+                        } catch (IOException e) {
+                            throw new RuntimeException(e);
+                        }
+                    });
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to patch gradle files", e);
+        }
+        return true;
+    }
+
+    private void patchGradleFile(File file, boolean isRoot) throws IOException {
+        String modifyString;
+        String codeString;
+        try {
+            AstBuilder astBuilder = new AstBuilder();
+            codeString = FIleUtil.readFileAsString(file.toPath());
+            java.util.List<?> ast = astBuilder.buildFromString(codeString);
+            ClassCodeVisitorSupport visitor = isRoot ?
+                    new RootGradleVisitor() :
+                    new ModuleGradleVisitor();
+
+            for (Object node : ast) {
+                ((org.codehaus.groovy.ast.ASTNode) node).visit(visitor);
+            }
+
+            StringWriter writer = new StringWriter();
+            PrintGradleVisitor visitorOutput = new PrintGradleVisitor(writer);
+
+            for (Object node : ast) {
+                ((org.codehaus.groovy.ast.ASTNode) node).visit(visitorOutput);
+            }
+
+            modifyString = writer.toString();
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to patch gradle file: " + file.getPath(), e);
+        }
+        System.out.println(modifyString);
+        CodeFilePO oldFile = new CodeFilePO();
+        oldFile.setFilePath(file.getAbsolutePath());
+        oldFile.setContent(codeString);
+        oldFile.setCreatedTime(new Date());
+        codeFileService.createCodeFile(oldFile);
+        FileChangePO fileChange = new FileChangePO();
+        fileChange.setOldFileId(oldFile.getId());
+        fileChange.setUserId(2999L);
+        CodeFilePO newFile = new CodeFilePO();
+        newFile.setFilePath(file.getAbsolutePath());
+        newFile.setContent(modifyString);
+        newFile.setCreatedTime(new Date());
+        codeFileService.createCodeFile(newFile);
+        fileChange.setNewFileId(newFile.getId());
+        fileChange.setCreatedTime(new Date());
+        fileChangeService.insert(fileChange);
+        FIleUtil.writeString(file.toPath(), modifyString);
+    }
+
+    @Override
+    public boolean modifyConfigByLLM(String FilePath, String content) {
+        if (FilePath == null || FilePath.isEmpty()) {
+            throw new IllegalArgumentException("Project directory path cannot be null or empty");
+        }
+
+        File projectDir = new File(FilePath);
+        if (!projectDir.exists()) {
+            throw new IllegalArgumentException("Project directory does not exist: " + FilePath);
+        }
+
+        try{
+            Files.walk(projectDir.toPath())
+                    .filter(path -> path.getFileName().toString().trim().equals("build.gradle"))
+                    .forEach(path -> {
+                        System.out.println(path.toAbsolutePath());
+                        modifyWithDify(path.toFile(), path.getParent().equals(projectDir.toPath()));
+                    });
+        }catch (Exception e) {
+            throw new RuntimeException("Failed to patch gradle files", e);
+        }
+        return true;
+    }
+
+    private void modifyWithDify(File file, boolean isRoot) {
+        try {
+            String codeString = FIleUtil.readFileAsString(file.toPath());
+            ChatMessageRequest request = new ChatMessageRequest();
+            CodeFilePO oldFile = new CodeFilePO();
+            oldFile.setFilePath(file.getAbsolutePath());
+            oldFile.setContent(codeString);
+            oldFile.setCreatedTime(new Date());
+            codeFileService.createCodeFile(oldFile);
+            request.setQuery( isRoot ? "\"file_type\":\"project\" \n " + codeString : "\"file_type\":\"module\" \n " + codeString);
+            request.setUser("abc-123");
+            request.setResponse_mode("blocking");
+            difyService.setApiKey(DifyApiKeyEnum.GRADLE.getApiKey());
+            ChatMessageResponse modifiedCode = difyService.sendMessage(request);
+            String modifiedCodeString = modifiedCode.getAnswer();
+            if (modifiedCodeString == null || modifiedCodeString.isEmpty()) {
+                throw new RuntimeException("Received empty response from Dify");
+            }
+            modifiedCodeString = modifiedCodeString.replace("gradle\n", "");
+            modifiedCodeString = modifiedCodeString.replace("```", "");
+            FileChangePO fileChange = new FileChangePO();
+            CodeFilePO newFile = new CodeFilePO();
+            newFile.setFilePath(file.getAbsolutePath());
+            newFile.setContent(modifiedCodeString);
+            newFile.setCreatedTime(new Date());
+            codeFileService.createCodeFile(newFile);
+            fileChange.setOldFileId(oldFile.getId());
+            fileChange.setUserId(2999L);
+            fileChange.setNewFileId(newFile.getId());
+            fileChange.setCreatedTime(new Date());
+            fileChangeService.insert(fileChange);
+            FIleUtil.writeString(file.toPath(), modifiedCodeString);
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to modify gradle file", e);
+        }
+    }
+
+
+    @Override
+    public List<FileChangePO> listConfigChanges() {
+        int userId = UserService.loginUser().getId();
+        return fileChangeMapper.selectByUserId((long) userId);
+    }
+
+    @Override
+    public List<CodeFilePO> getChangeDetail(Long fileChangeId) {
+        FileChangePO fileChange = fileChangeMapper.selectById(fileChangeId);
+        long oldFileId = fileChange.getOldFileId();
+        long newFileId = fileChange.getNewFileId();
+        CodeFilePO oldFile = codeFileService.getCodeFileById(oldFileId);
+        CodeFilePO newFile = codeFileService.getCodeFileById(newFileId);
+        List<CodeFilePO> res = new ArrayList<>();
+        res.add(oldFile);
+        res.add(newFile);
+        return res;
+    }
+
+    @Override
+    public boolean rollbackChange(Long fileChangeId) {
+        FileChangePO fileChange = fileChangeMapper.selectById(fileChangeId);
+        long oldFileId = fileChange.getOldFileId();
+        CodeFilePO oldFile = codeFileService.getCodeFileById(oldFileId);
+        String filePath = oldFile.getFilePath();
+        String content = oldFile.getContent();
+        File file = new File(filePath);
+        if (!file.exists()) {
+            throw new IllegalArgumentException("File does not exist: " + filePath);
+        }
+        try {
+            FIleUtil.writeString(file.toPath(), content);
+            fileChangeMapper.delete(fileChangeId);
+            codeFileService.deleteCodeFile(oldFileId);
+            return true;
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to rollback change", e);
+        }
+    }
+}

+ 66 - 0
web/src/main/java/cn/seecoder/web/core/gradlellm/impl/ConversationServiceImpl.java

@@ -0,0 +1,66 @@
+package cn.seecoder.web.core.gradlellm.impl;
+
+import cn.seecoder.web.core.gradlellm.ConversationService;
+import cn.seecoder.web.dao.gradlellm.ConversationMapper;
+import cn.seecoder.web.model.po.gradlellm.ConversationPO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Date;
+import java.util.List;
+
+@Service
+public class ConversationServiceImpl implements ConversationService {
+
+    private final ConversationMapper conversationMapper;
+
+    @Autowired
+    public ConversationServiceImpl(ConversationMapper conversationMapper) {
+        this.conversationMapper = conversationMapper;
+    }
+
+    @Override
+    @Transactional
+    public ConversationPO createConversation(ConversationPO conversationPO) {
+        conversationMapper.insert(conversationPO);
+        return conversationPO;
+    }
+
+    @Override
+    @Transactional
+    public int updateConversation(ConversationPO conversationPO) {
+        return conversationMapper.update(conversationPO);
+    }
+
+    @Override
+    @Transactional
+    public int deleteConversation(Long id) {
+        return conversationMapper.delete(id);
+    }
+
+    @Override
+    public ConversationPO getConversationById(Long id) {
+        return conversationMapper.selectById(id);
+    }
+
+    @Override
+    public List<ConversationPO> getAllConversations() {
+        return conversationMapper.selectAll();
+    }
+
+    @Override
+    public List<ConversationPO> getConversationsByUserId(Long userId) {
+        return conversationMapper.selectByUserId(userId);
+    }
+
+    @Override
+    public ConversationPO getConversationByConversationId(String conversationId) {
+        return conversationMapper.selectByConversationId(conversationId);
+    }
+
+    @Override
+    public boolean exists(Long id) {
+        return conversationMapper.selectById(id) != null;
+    }
+}

+ 56 - 0
web/src/main/java/cn/seecoder/web/core/gradlellm/impl/DifyMessageServiceImpl.java

@@ -0,0 +1,56 @@
+package cn.seecoder.web.core.gradlellm.impl;
+
+import cn.seecoder.web.core.gradlellm.DifyMessageService;
+import cn.seecoder.web.dao.gradlellm.DifyMessageMapper;
+import cn.seecoder.web.model.po.gradlellm.DifyMessagePO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class DifyMessageServiceImpl implements DifyMessageService {
+
+    @Autowired
+    private DifyMessageMapper difyMessageMapper;
+
+    @Override
+    public int insert(DifyMessagePO difyMessagePO, String... ignoredCols) {
+        return difyMessageMapper.insert(difyMessagePO, ignoredCols);
+    }
+
+    @Override
+    public int update(DifyMessagePO difyMessagePO) {
+        return difyMessageMapper.update(difyMessagePO);
+    }
+
+    @Override
+    public int delete(Long id) {
+        return difyMessageMapper.delete(id);
+    }
+
+    @Override
+    public DifyMessagePO selectById(Long id) {
+        return difyMessageMapper.selectById(id);
+    }
+
+    @Override
+    public List<DifyMessagePO> selectByUserId(Long userId) {
+        return difyMessageMapper.selectByUserId(userId);
+    }
+
+    @Override
+    public List<DifyMessagePO> selectByConversationId(String conversationId) {
+        return difyMessageMapper.selectByConversationId(conversationId);
+    }
+
+    @Override
+    public List<DifyMessagePO> selectByRole(String role) {
+        return difyMessageMapper.selectByRole(role);
+    }
+
+    @Override
+    public List<DifyMessagePO> selectByConversationIdAndRole(String conversationId, String role) {
+        return difyMessageMapper.selectByConversationIdAndRole(conversationId, role);
+    }
+}

+ 61 - 0
web/src/main/java/cn/seecoder/web/core/gradlellm/impl/FileChangeServiceImpl.java

@@ -0,0 +1,61 @@
+package cn.seecoder.web.core.gradlellm.impl;
+
+import cn.seecoder.web.core.gradlellm.FileChangeService;
+import cn.seecoder.web.dao.gradlellm.FileChangeMapper;
+import cn.seecoder.web.model.po.gradlellm.FileChangePO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class FileChangeServiceImpl implements FileChangeService {
+
+    @Autowired
+    private FileChangeMapper fileChangeMapper;
+
+    @Override
+    public int insert(FileChangePO fileChangePO, String... ignoredCols) {
+        return fileChangeMapper.insert(fileChangePO, ignoredCols);
+    }
+
+    @Override
+    public int update(FileChangePO fileChangePO) {
+        return fileChangeMapper.update(fileChangePO);
+    }
+
+    @Override
+    public int delete(Long id) {
+        return fileChangeMapper.delete(id);
+    }
+
+    @Override
+    public FileChangePO selectById(Long id) {
+        return fileChangeMapper.selectById(id);
+    }
+
+    @Override
+    public List<FileChangePO> selectByUserId(Long userId) {
+        return fileChangeMapper.selectByUserId(userId);
+    }
+
+    @Override
+    public List<FileChangePO> selectByOldFileId(Long oldFileId) {
+        return fileChangeMapper.selectByOldFileId(oldFileId);
+    }
+
+    @Override
+    public List<FileChangePO> selectByNewFileId(Long newFileId) {
+        return fileChangeMapper.selectByNewFileId(newFileId);
+    }
+
+    @Override
+    public List<FileChangePO> selectByFileId(Long fileId) {
+        return fileChangeMapper.selectByFileId(fileId);
+    }
+
+    @Override
+    public List<FileChangePO> selectAll() {
+        return fileChangeMapper.selectAll();
+    }
+}

+ 198 - 0
web/src/main/java/cn/seecoder/web/core/gradlellm/impl/ProjectAnalysisServiceImpl.java

@@ -0,0 +1,198 @@
+package cn.seecoder.web.core.gradlellm.impl;
+
+import cn.seecoder.web.core.gradlellm.ConversationService;
+import cn.seecoder.web.core.gradlellm.DifyMessageService;
+import cn.seecoder.web.core.gradlellm.DifyService;
+import cn.seecoder.web.core.gradlellm.ProjectAnalysisService;
+import cn.seecoder.web.core.gradlellm.utils.FIleUtil;
+import cn.seecoder.web.model.enums.DifyApiKeyEnum;
+import cn.seecoder.web.model.po.gradlellm.ConversationPO;
+import cn.seecoder.web.model.po.gradlellm.DifyMessagePO;
+import cn.seecoder.web.model.vo.gradlellm.ChatMessageRequest;
+import cn.seecoder.web.model.vo.gradlellm.ChatMessageResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.List;
+
+@Service
+public class ProjectAnalysisServiceImpl implements ProjectAnalysisService {
+
+    @Autowired
+    private DifyService difyService;
+
+
+    @Autowired
+    private ConversationService conversationService;
+
+    @Autowired
+    DifyMessageService difyMessageService;
+
+    @Override
+    public String analyzeProjectStructure(String FilePath, List<File> inputs) throws IOException {
+        File rootDirectory = new File(FilePath);
+
+        // 检查路径是否存在且是一个目录
+        if (!rootDirectory.exists() || !rootDirectory.isDirectory()) {
+            System.out.println("The specified path does not exist or is not a directory: " + FilePath);
+        }
+
+        StringBuilder output = new StringBuilder();
+
+        // 调用 printTree 方法,将结果追加到 StringBuilder 中
+        printTree(new File(FilePath), "", output);
+
+        ChatMessageRequest request = new ChatMessageRequest();
+        request.setQuery("这是build.gradle.kts文件:\n" + FIleUtil.readFileAsString(inputs.get(0).toPath()) +
+                "\n这是AndroidManifest.xml内容:\n" + FIleUtil.readFileAsString(inputs.get(1).toPath()) + "这是项目的目录结构" + output);
+        request.setUser("abc-123");
+        request.setResponse_mode("blocking");
+
+        difyService.setApiKey(DifyApiKeyEnum.PROJECT.getApiKey());
+        ChatMessageResponse response = difyService.sendMessage(request);
+        if (response == null) {
+            System.out.println("Response is null");
+            return null;
+        }
+        ConversationPO conversationPO = new ConversationPO();
+        conversationPO.setUserId(2999L);
+        conversationPO.setCreatedTime(new Date());
+        conversationPO.setConversationId(response.getConversation_id());
+        conversationPO.setCategory("Project");
+        conversationService.createConversation(conversationPO);
+        DifyMessagePO botMessagePO = new DifyMessagePO();
+        botMessagePO.setConversationId(response.getConversation_id());
+        botMessagePO.setUserId(2999L);
+        botMessagePO.setCreatedTime(new Date());
+        botMessagePO.setRole("assistant");
+        botMessagePO.setContent(response.getAnswer());
+        difyMessageService.insert(botMessagePO);
+        return response.getAnswer();
+    }
+
+    @Override
+    public ChatMessageResponse answerCodeQuestion(String FilePath, String question) throws IOException {
+        File codeFile = new File(FilePath);
+        if (!codeFile.exists() || !codeFile.isFile()) {
+            System.out.println("The specified path does not exist or is not a file: " + FilePath);
+            return null;
+        }
+        String codeContent = FIleUtil.readFileAsString(codeFile.toPath());
+
+        ConversationPO conversationPO = new ConversationPO();
+        conversationPO.setUserId(2999L);
+        conversationPO.setCreatedTime(new Date());
+
+
+        ChatMessageRequest request = new ChatMessageRequest();
+        request.setQuery("这是代码文件内容:\n" + codeContent + "\n这是我的问题:"+ question);
+        request.setUser("abc-123");
+        request.setResponse_mode("blocking");
+
+        difyService.setApiKey(DifyApiKeyEnum.CODE.getApiKey());
+        ChatMessageResponse response = difyService.sendMessage(request);
+
+        conversationPO.setConversationId(response.getConversation_id());
+        conversationPO.setCategory("Code");
+        conversationService.createConversation(conversationPO);
+
+        DifyMessagePO userMessagePO = new DifyMessagePO();
+        userMessagePO.setConversationId(response.getConversation_id());
+        userMessagePO.setUserId(2999L);
+        userMessagePO.setCreatedTime(new Date());
+        userMessagePO.setRole("user");
+        userMessagePO.setContent(question);
+        difyMessageService.insert(userMessagePO);
+
+        DifyMessagePO botMessagePO = new DifyMessagePO();
+        botMessagePO.setConversationId(response.getConversation_id());
+        botMessagePO.setUserId(2999L);
+        botMessagePO.setCreatedTime(new Date());
+        botMessagePO.setRole("assistant");
+        botMessagePO.setContent(response.getAnswer());
+        difyMessageService.insert(botMessagePO);
+
+        if (response == null) {
+            System.out.println("Response is null");
+            return null;
+        }
+        return response;
+    }
+
+    @Override
+    public List<ConversationPO> listQARecords(){
+        long userId = 2999L;
+        List<ConversationPO> conversations = conversationService.getConversationsByUserId(userId);
+        if (conversations == null || conversations.isEmpty()) {
+            System.out.println("No conversations found for user ID: " + userId);
+            return null;
+        }
+        return conversations;
+    }
+
+    @Override
+    public List<DifyMessagePO> getConversationHistory(String conversationId){
+        List<DifyMessagePO> messages = difyMessageService.selectByConversationId(conversationId);
+        if (messages == null || messages.isEmpty()) {
+            System.out.println("No messages found for conversation ID: " + conversationId);
+            return null;
+        }
+        return messages;
+    }
+
+    private static void printTree(File dir, String indent, StringBuilder output) {
+        File[] files = dir.listFiles();
+        if (files != null) {
+            for (File file : files) {
+                // 只处理目录,忽略文件
+                if (file.isDirectory()) {
+                    // 忽略 .git 和 .github 目录
+                    if (file.getName().equals(".git") || file.getName().equals(".github")) {
+                        continue;
+                    }
+
+                    // 将目录名称追加到 StringBuilder 中
+                    output.append(indent).append("|__ ").append(file.getName()).append("\n");
+
+                    // 递归调用,增加缩进
+                    printTree(file, indent + "    ", output);
+                }
+            }
+        }
+    }
+
+    @Override
+    public ChatMessageResponse answerCodeQuestionByLLM(String conversationId, String question) throws IOException{
+        ConversationPO conversationPO = conversationService.getConversationByConversationId(conversationId);
+        ChatMessageRequest request = new ChatMessageRequest();
+        request.setQuery(question);
+        request.setUser("abc-123");
+        request.setResponse_mode("blocking");
+        request.setConversation_id(conversationId);
+        difyService.setApiKey(DifyApiKeyEnum.CODE.getApiKey());
+        ChatMessageResponse response = difyService.sendMessage(request);
+        if (response == null) {
+            System.out.println("Response is null");
+            return null;
+        }
+        DifyMessagePO userMessagePO = new DifyMessagePO();
+        userMessagePO.setConversationId(conversationId);
+        userMessagePO.setUserId(2999L);
+        userMessagePO.setCreatedTime(new Date());
+        userMessagePO.setRole("user");
+        userMessagePO.setContent(question);
+        difyMessageService.insert(userMessagePO);
+        DifyMessagePO botMessagePO = new DifyMessagePO();
+        botMessagePO.setConversationId(conversationId);
+        botMessagePO.setUserId(2999L);
+        botMessagePO.setCreatedTime(new Date());
+        botMessagePO.setRole("assistant");
+        botMessagePO.setContent(response.getAnswer());
+        difyMessageService.insert(botMessagePO);
+        return response;
+    }
+
+}

+ 14 - 0
web/src/main/java/cn/seecoder/web/core/gradlellm/utils/Config.java

@@ -0,0 +1,14 @@
+package cn.seecoder.web.core.gradlellm.utils;
+
+import lombok.Data;
+
+
+@Data
+public class Config {
+    private String minSdkVersion = "29";
+    private String ndkVersion = "23.2.0";
+    private String ndkPath = "/opt/android-ndk-r23c";
+    private String mavenRepoPath = "C:/Users/zh907/AppData/Local/Android/plugin/repo";
+    private String classPath = "om.android.tools.build:gradle:7.4.0-dev";
+}
+

+ 29 - 0
web/src/main/java/cn/seecoder/web/core/gradlellm/utils/FIleUtil.java

@@ -0,0 +1,29 @@
+package cn.seecoder.web.core.gradlellm.utils;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileReader;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public class FIleUtil {
+    public static String readFileAsString(Path filePath) throws IOException {
+        StringBuilder contentBuilder = new StringBuilder();
+        try (BufferedReader br = Files.newBufferedReader(filePath)) {
+            String currentLine;
+            while ((currentLine = br.readLine()) != null) {
+                contentBuilder.append(currentLine).append("\n");
+            }
+        }
+        return contentBuilder.toString();
+    }
+
+    public static void writeString(Path filePath, String content) throws IOException {
+        // 使用 BufferedWriter 来写入文件
+        try (BufferedWriter bw = Files.newBufferedWriter(filePath, StandardCharsets.UTF_8)) {
+            bw.write(content); // 写入字符串内容
+        }
+    }
+}

+ 172 - 0
web/src/main/java/cn/seecoder/web/core/gradlellm/utils/ModuleGradleVisitor.java

@@ -0,0 +1,172 @@
+package cn.seecoder.web.core.gradlellm.utils;
+
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.*;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.ast.builder.AstBuilder;
+
+
+public class ModuleGradleVisitor extends ClassCodeVisitorSupport {
+
+    Config config = new Config();
+
+    @Override
+    public SourceUnit getSourceUnit() {
+        return null;
+    }
+
+    @Override
+    public void visitMethodCallExpression(MethodCallExpression call) {
+        if (call.getMethodAsString().equals("android")) {
+            ArgumentListExpression androidBlock = (ArgumentListExpression) call.getArguments();
+            ClosureExpression closure = (ClosureExpression) androidBlock.getExpressions().get(0);
+
+            modifyNdkPath(closure);
+            modifyNdkVersion(closure);
+            modifyDefaultConfig(closure);
+        }
+    }
+
+    private Expression getExpressionFromStatement(Statement statement) {
+        if (statement instanceof ExpressionStatement) {
+            return ((ExpressionStatement) statement).getExpression();
+        } else if (statement instanceof ReturnStatement) {
+            return ((ReturnStatement) statement).getExpression();
+        }
+        return null;
+    }
+
+    private void modifyNdkVersion(ClosureExpression closure) {
+        BlockStatement block = (BlockStatement) closure.getCode();
+        java.util.List<Statement> statements = block.getStatements();
+
+        for (Statement statement : statements) {
+            Expression expr = getExpressionFromStatement(statement);
+            if (expr instanceof MethodCallExpression) {
+                MethodCallExpression expression = (MethodCallExpression) expr;
+                if (expression.getMethodAsString().equals("ndkVersion")) {
+                    statements.remove(statement);
+                    break;
+                }
+            }
+        }
+
+        statements.add(0, createNdkVersionExpression());
+    }
+
+    private void modifyNdkPath(ClosureExpression closure) {
+        BlockStatement block = (BlockStatement) closure.getCode();
+        java.util.List<Statement> statements = block.getStatements();
+
+        for (Statement statement : statements) {
+            Expression expr = getExpressionFromStatement(statement);
+            if (expr instanceof MethodCallExpression) {
+                MethodCallExpression expression = (MethodCallExpression) expr;
+                if (expression.getMethodAsString().equals("ndkPath")) {
+                    statements.remove(statement);
+                    break;
+                }
+            }
+        }
+
+        statements.add(0, createNdkPathExpression());
+    }
+
+    private void modifyDefaultConfig(ClosureExpression closure) {
+        BlockStatement block = (BlockStatement) closure.getCode();
+        java.util.List<Statement> statements = block.getStatements();
+
+        for (Statement statement : statements) {
+            Expression expr = getExpressionFromStatement(statement);
+            if (expr instanceof MethodCallExpression) {
+                MethodCallExpression expression = (MethodCallExpression) expr;
+                if (expression.getMethodAsString().equals("defaultConfig")) {
+                    ArgumentListExpression defaultConfigBlock = (ArgumentListExpression) expression.getArguments();
+                    ClosureExpression defaultConfigClosure = (ClosureExpression) defaultConfigBlock.getExpressions().get(0);
+                    modifyMinSdkVersion(defaultConfigClosure);
+
+                    ArgumentListExpression externalNativeBuild = findExternalNativeBuild(defaultConfigClosure);
+                    if (externalNativeBuild == null) {
+                        ((BlockStatement) defaultConfigClosure.getCode()).getStatements().add(
+                                0,
+                                createExternalNativeBuildExpression()
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    private void modifyMinSdkVersion(ClosureExpression closure) {
+        BlockStatement block = (BlockStatement) closure.getCode();
+        java.util.List<Statement> statements = block.getStatements();
+
+        boolean exist = false;
+
+        for (Statement statement : statements) {
+            Expression expr = getExpressionFromStatement(statement);
+            if (expr instanceof MethodCallExpression) {
+                MethodCallExpression expression = (MethodCallExpression) expr;
+                if (expression.getMethodAsString().equals("minSdkVersion")) {
+                    ArgumentListExpression minSdkVersionBlock = (ArgumentListExpression) expression.getArguments();
+                    ConstantExpression minSdkVersion = (ConstantExpression) minSdkVersionBlock.getExpressions().get(0);
+                    int cfgMinSdkVersion = config.getMinSdkVersion() != null ? Integer.parseInt(config.getMinSdkVersion()) : (int) minSdkVersion.getValue();
+                    minSdkVersionBlock.getExpressions().set(0, new ConstantExpression(cfgMinSdkVersion));
+                    exist = true;
+                    break;
+                }
+            }
+        }
+
+        if (!exist) {
+            statements.add(0, createMinSdkVersionExpression());
+        }
+    }
+
+    private ArgumentListExpression findExternalNativeBuild(ClosureExpression closure) {
+        BlockStatement block = (BlockStatement) closure.getCode();
+        java.util.List<Statement> statements = block.getStatements();
+
+        for (Statement statement : statements) {
+            Expression expr = getExpressionFromStatement(statement);
+            if (expr instanceof MethodCallExpression) {
+                MethodCallExpression expression = (MethodCallExpression) expr;
+                if (expression.getMethodAsString().equals("externalNativeBuild")) {
+                    return (ArgumentListExpression) expression.getArguments();
+                }
+            }
+        }
+
+        return null;
+    }
+
+    private ExpressionStatement createNdkPathExpression() {
+        String mavenNdkPathCode = "ndkPath '" + config.getNdkPath() + "'";
+        AstBuilder builder = new AstBuilder();
+        BlockStatement mavenNdkPathAst = (BlockStatement) builder.buildFromString(mavenNdkPathCode).get(0);
+        return new ExpressionStatement((MethodCallExpression) ((ReturnStatement) mavenNdkPathAst.getStatements().get(0)).getExpression());
+    }
+
+    private ExpressionStatement createMinSdkVersionExpression() {
+        String mavenMinSdkVersionCode = "minSdkVersion " + config.getMinSdkVersion();
+        AstBuilder builder = new AstBuilder();
+        BlockStatement mavenMinSdkVersionAst = (BlockStatement) builder.buildFromString(mavenMinSdkVersionCode).get(0);
+        return new ExpressionStatement((MethodCallExpression) ((ReturnStatement) mavenMinSdkVersionAst.getStatements().get(0)).getExpression());
+    }
+
+    private ExpressionStatement createNdkVersionExpression() {
+        String mavenNdkVersionCode = "ndkVersion '" + config.getNdkVersion() + "'";
+        AstBuilder builder = new AstBuilder();
+        BlockStatement mavenNdkVersionAst = (BlockStatement) builder.buildFromString(mavenNdkVersionCode).get(0);
+        return new ExpressionStatement((MethodCallExpression) ((ReturnStatement) mavenNdkVersionAst.getStatements().get(0)).getExpression());
+    }
+
+    private ExpressionStatement createExternalNativeBuildExpression() {
+        String mavenExternalNativeBuildCode = "externalNativeBuild { ndk { abiFilters 'riscv64' } }";
+        AstBuilder builder = new AstBuilder();
+        BlockStatement mavenExternalNativeBuildAst = (BlockStatement) builder.buildFromString(mavenExternalNativeBuildCode).get(0);
+        return new ExpressionStatement((MethodCallExpression) ((ReturnStatement) mavenExternalNativeBuildAst.getStatements().get(0)).getExpression());
+    }
+}
+

+ 51 - 0
web/src/main/java/cn/seecoder/web/core/gradlellm/utils/PrintGradleVisitor.java

@@ -0,0 +1,51 @@
+package cn.seecoder.web.core.gradlellm.utils;
+
+import groovy.console.ui.AstNodeToScriptVisitor;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import java.io.Writer;
+
+public class PrintGradleVisitor extends AstNodeToScriptVisitor {
+    public PrintGradleVisitor(Writer writer) {
+        super(writer);
+    }
+
+    private void printExpression(Expression expression) {
+        if (expression instanceof VariableExpression) {
+            super.visitVariableExpression((VariableExpression) expression, false);
+        } else {
+            if (expression != null) {
+                expression.visit(this);
+            }
+        }
+    }
+
+    @Override
+    public void visitMethodCallExpression(MethodCallExpression call) {
+        Expression objectExpr = call.getObjectExpression();
+        if (!(objectExpr instanceof VariableExpression) || !((VariableExpression) objectExpr).getName().equals("this")) {
+            printExpression(objectExpr);
+            if (call.isSpreadSafe()) {
+                print("*");
+            }
+            if (call.isSafe()) {
+                print("?");
+            }
+            print(".");
+        }
+        Expression method = call.getMethod();
+        if (method instanceof ConstantExpression) {
+            visitConstantExpression((ConstantExpression) method, true);
+        } else {
+            method.visit(this);
+        }
+        call.getArguments().visit(this);
+    }
+
+    @Override
+    public void visitReturnStatement(ReturnStatement statement) {
+        printLineBreak();
+        statement.getExpression().visit(this);
+        printLineBreak();
+    }
+}

+ 124 - 0
web/src/main/java/cn/seecoder/web/core/gradlellm/utils/RootGradleVisitor.java

@@ -0,0 +1,124 @@
+package cn.seecoder.web.core.gradlellm.utils;
+
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.*;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.ast.builder.AstBuilder;
+
+
+
+public class RootGradleVisitor extends ClassCodeVisitorSupport {
+    Config config = new Config();
+
+    @Override
+    public SourceUnit getSourceUnit() {
+        return null;
+    }
+
+    @Override
+    public void visitMethodCallExpression(MethodCallExpression call) {
+        if (call.getMethodAsString().equals("buildscript")) {
+            ArgumentListExpression buildscriptBlock = (ArgumentListExpression) call.getArguments();
+            ClosureExpression closure = (ClosureExpression) buildscriptBlock.getExpressions().get(0);
+            ArgumentListExpression repositoriesBlock = findRepositoriesBlock(closure);
+            BlockStatement repositories = repositoriesBlock != null ?
+                    (BlockStatement) ((ClosureExpression) repositoriesBlock.getExpressions().get(0)).getCode() : null;
+            if (repositories != null) {
+                ExpressionStatement mavenRepo = createMavenRepoExpression();
+                repositories.getStatements().add(0, mavenRepo);
+            }
+
+            ArgumentListExpression dependenciesBlock = findDependenciesBlock(closure);
+            BlockStatement dependencies = dependenciesBlock != null ?
+                    (BlockStatement) ((ClosureExpression) dependenciesBlock.getExpressions().get(0)).getCode() : null;
+
+            if (dependencies != null) {
+                removeRepositoriesIfExist(dependencies.getStatements());
+                ExpressionStatement mavenRepo = createClassPathExpression();
+                dependencies.getStatements().add(0, mavenRepo);
+            }
+        } else if (call.getMethodAsString().equals("allprojects")) {
+            ArgumentListExpression buildscriptBlock = (ArgumentListExpression) call.getArguments();
+            ClosureExpression closure = (ClosureExpression) buildscriptBlock.getExpressions().get(0);
+            ArgumentListExpression repositoriesBlock = findRepositoriesBlock(closure);
+            BlockStatement repositories = repositoriesBlock != null ?
+                    (BlockStatement) ((ClosureExpression) repositoriesBlock.getExpressions().get(0)).getCode() : null;
+            if (repositories != null) {
+                ExpressionStatement mavenRepo = createMavenRepoExpression();
+                repositories.getStatements().add(0, mavenRepo);
+            }
+        }
+        super.visitMethodCallExpression(call);
+    }
+
+    private Expression getExpressionFromStatement(Statement statement) {
+        if (statement instanceof ExpressionStatement) {
+            return ((ExpressionStatement) statement).getExpression();
+        } else if (statement instanceof ReturnStatement) {
+            return ((ReturnStatement) statement).getExpression();
+        }
+        return null;
+    }
+
+    private ArgumentListExpression findRepositoriesBlock(ClosureExpression closure) {
+        BlockStatement block = (BlockStatement) closure.getCode();
+        java.util.List<Statement> statements = block.getStatements();
+        for (Statement statement : statements) {
+            Expression expr = getExpressionFromStatement(statement);
+            if (expr instanceof MethodCallExpression) {
+                MethodCallExpression expression = (MethodCallExpression) expr;
+                if (expression.getMethodAsString().equals("repositories")) {
+                    return (ArgumentListExpression) expression.getArguments();
+                }
+            }
+        }
+        return null;
+    }
+
+    private ArgumentListExpression findDependenciesBlock(ClosureExpression closure) {
+        BlockStatement block = (BlockStatement) closure.getCode();
+        java.util.List<Statement> statements = block.getStatements();
+        for (Statement statement : statements) {
+            Expression expr = getExpressionFromStatement(statement);
+            if (expr instanceof MethodCallExpression) {
+                MethodCallExpression expression = (MethodCallExpression) expr;
+                if (expression.getMethodAsString().equals("dependencies")) {
+                    return (ArgumentListExpression) expression.getArguments();
+                }
+            }
+        }
+        return null;
+    }
+
+    private void removeRepositoriesIfExist(java.util.List<Statement> statements) {
+        for (Statement statement : statements) {
+            Expression expr = getExpressionFromStatement(statement);
+            if (expr instanceof MethodCallExpression &&
+                    ((MethodCallExpression) expr).getMethodAsString().equals("classpath") &&
+                    ((MethodCallExpression) expr).getArguments() instanceof ArgumentListExpression) {
+                ArgumentListExpression arguments = (ArgumentListExpression) ((MethodCallExpression) expr).getArguments();
+                ConstantExpression className = (ConstantExpression) arguments.getExpressions().get(0);
+                if (className.getValue().toString().startsWith("com.android.tools.build:gradle")) {
+                    statements.remove(statement);
+                    return;
+                }
+            }
+        }
+    }
+
+    private ExpressionStatement createMavenRepoExpression() {
+        String mavenRepoCode = "maven { url \"" + config.getMavenRepoPath() + "\" }";
+        AstBuilder builder = new AstBuilder();
+        BlockStatement mavenRepoAst = (BlockStatement) builder.buildFromString(mavenRepoCode).get(0);
+        return new ExpressionStatement((MethodCallExpression) ((ReturnStatement) mavenRepoAst.getStatements().get(0)).getExpression());
+    }
+
+    private ExpressionStatement createClassPathExpression() {
+        String classPathCode = "classpath '" + config.getClassPath() + "'";
+        AstBuilder builder = new AstBuilder();
+        BlockStatement classPathAst = (BlockStatement) builder.buildFromString(classPathCode).get(0);
+        return new ExpressionStatement((MethodCallExpression) ((ReturnStatement) classPathAst.getStatements().get(0)).getExpression());
+    }
+
+}

+ 33 - 0
web/src/main/java/cn/seecoder/web/dao/gradlellm/CodeFileMapper.java

@@ -0,0 +1,33 @@
+package cn.seecoder.web.dao.gradlellm;
+
+import cn.seecoder.web.dao.provider.GeneralInsertUpdateSqlProvider;
+import cn.seecoder.web.model.po.gradlellm.CodeFilePO;
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface CodeFileMapper {
+
+    @InsertProvider(type = GeneralInsertUpdateSqlProvider.class, method = "insert")
+    @Options(useGeneratedKeys = true, keyProperty = "param1.id", keyColumn = "id")
+    int insert(CodeFilePO codeFilePO, String... ignoredCols);
+
+    @UpdateProvider(type = GeneralInsertUpdateSqlProvider.class, method = "updateById")
+    int update(CodeFilePO codeFilePO);
+
+    @Delete("delete from code_file where id = #{id}")
+    int delete(Long id);
+
+    @Select("select * from code_file where id = #{id}")
+    CodeFilePO selectById(Long id);
+
+    @Select("select * from code_file")
+    List<CodeFilePO> selectAll();
+
+    // Additional methods if needed
+    // For example, if you need to query by filePath:
+    @Select("select * from code_file where file_path = #{filePath}")
+    List<CodeFilePO> selectByFilePath(String filePath);
+}

+ 34 - 0
web/src/main/java/cn/seecoder/web/dao/gradlellm/ConversationMapper.java

@@ -0,0 +1,34 @@
+package cn.seecoder.web.dao.gradlellm;
+
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Repository;
+import cn.seecoder.web.dao.provider.GeneralInsertUpdateSqlProvider;
+import cn.seecoder.web.model.po.gradlellm.ConversationPO;
+
+import java.util.List;
+
+@Repository
+public interface ConversationMapper {
+
+    @InsertProvider(type = GeneralInsertUpdateSqlProvider.class, method = "insert")
+    @Options(useGeneratedKeys = true, keyProperty = "param1.id", keyColumn = "id")
+    int insert(ConversationPO conversationPO, String... ignoredCols);
+
+    @UpdateProvider(type = GeneralInsertUpdateSqlProvider.class, method = "updateById")
+    int update(ConversationPO conversationPO);
+
+    @Delete("delete from conversation where id = #{id}")
+    int delete(Long id);
+
+    @Select("select * from conversation where id = #{id}")
+    ConversationPO selectById(Long id);
+
+    @Select("select * from conversation where user_id = #{userId}")
+    List<ConversationPO> selectByUserId(Long userId);
+
+    @Select("select * from conversation where conversation_id = #{conversationId}")
+    ConversationPO selectByConversationId(String conversationId);
+
+    @Select("select * from conversation")
+    List<ConversationPO> selectAll();
+}

+ 38 - 0
web/src/main/java/cn/seecoder/web/dao/gradlellm/DifyMessageMapper.java

@@ -0,0 +1,38 @@
+package cn.seecoder.web.dao.gradlellm;
+
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Repository;
+import cn.seecoder.web.dao.provider.GeneralInsertUpdateSqlProvider;
+import cn.seecoder.web.model.po.gradlellm.DifyMessagePO;
+import java.util.List;
+
+@Repository
+public interface DifyMessageMapper {
+
+    @InsertProvider(type = GeneralInsertUpdateSqlProvider.class, method = "insert")
+    @Options(useGeneratedKeys = true, keyProperty = "param1.id", keyColumn = "id")
+    int insert(DifyMessagePO difyMessagePO, String... ignoredCols);
+
+    @UpdateProvider(type = GeneralInsertUpdateSqlProvider.class, method = "updateById")
+    int update(DifyMessagePO difyMessagePO);
+
+    @Delete("delete from dify_message where id = #{id}")
+    int delete(Long id);
+
+    @Select("select * from dify_message where id = #{id}")
+    DifyMessagePO selectById(Long id);
+
+    @Select("select * from dify_message where user_id = #{userId}")
+    List<DifyMessagePO> selectByUserId(Long userId);
+
+    @Select("select * from dify_message where conversation_id = #{conversationId}")
+    List<DifyMessagePO> selectByConversationId(String conversationId);
+
+    @Select("select * from dify_message where role = #{role}")
+    List<DifyMessagePO> selectByRole(String role);
+
+    @Select("select * from dify_message where conversation_id = #{conversationId} and role = #{role}")
+    List<DifyMessagePO> selectByConversationIdAndRole(
+            @Param("conversationId") String conversationId,
+            @Param("role") String role);
+}

+ 39 - 0
web/src/main/java/cn/seecoder/web/dao/gradlellm/FileChangeMapper.java

@@ -0,0 +1,39 @@
+package cn.seecoder.web.dao.gradlellm;
+
+import org.apache.ibatis.annotations.*;
+import org.springframework.stereotype.Repository;
+import cn.seecoder.web.dao.provider.GeneralInsertUpdateSqlProvider;
+import cn.seecoder.web.model.po.gradlellm.FileChangePO;
+import java.util.List;
+
+@Repository
+public interface FileChangeMapper {
+
+    @InsertProvider(type = GeneralInsertUpdateSqlProvider.class, method = "insert")
+    @Options(useGeneratedKeys = true, keyProperty = "param1.id", keyColumn = "id")
+    int insert(FileChangePO fileChangePO, String... ignoredCols);
+
+    @UpdateProvider(type = GeneralInsertUpdateSqlProvider.class, method = "updateById")
+    int update(FileChangePO fileChangePO);
+
+    @Delete("delete from file_change where id = #{id}")
+    int delete(Long id);
+
+    @Select("select * from file_change where id = #{id}")
+    FileChangePO selectById(Long id);
+
+    @Select("select * from file_change where user_id = #{userId}")
+    List<FileChangePO> selectByUserId(Long userId);
+
+    @Select("select * from file_change where old_file_id = #{oldFileId}")
+    List<FileChangePO> selectByOldFileId(Long oldFileId);
+
+    @Select("select * from file_change where new_file_id = #{newFileId}")
+    List<FileChangePO> selectByNewFileId(Long newFileId);
+
+    @Select("select * from file_change where old_file_id = #{oldFileId} or new_file_id = #{newFileId}")
+    List<FileChangePO> selectByFileId(Long fileId);
+
+    @Select("select * from file_change order by created_time desc")
+    List<FileChangePO> selectAll();
+}

+ 30 - 0
web/src/main/java/cn/seecoder/web/model/enums/DifyApiKeyEnum.java

@@ -0,0 +1,30 @@
+package cn.seecoder.web.model.enums;
+
+public enum DifyApiKeyEnum {
+
+    GRADLE("app-B2gVcRY5JtvFyNGTiFcLBHhk"),
+
+
+    PROJECT("app-XNNO7d9o6h3Z5U4jLaaG3RN5"),
+
+    CODE("app-E1PIITBBtvKoZ9fLav2pIVhS");
+
+    private final String apiKey;
+
+    DifyApiKeyEnum(String apiKey) {
+        this.apiKey = apiKey;
+    }
+
+    public String getApiKey() {
+        return apiKey;
+    }
+
+    public static DifyApiKeyEnum fromName(String name) {
+        for (DifyApiKeyEnum apiKeyEnum : DifyApiKeyEnum.values()) {
+            if (apiKeyEnum.name().equalsIgnoreCase(name)) {
+                return apiKeyEnum;
+            }
+        }
+        throw new IllegalArgumentException("Invalid DifyApiKeyEnum name: " + name);
+    }
+}

+ 20 - 0
web/src/main/java/cn/seecoder/web/model/po/gradlellm/CodeFilePO.java

@@ -0,0 +1,20 @@
+package cn.seecoder.web.model.po.gradlellm;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class CodeFilePO {
+    private Long id;
+    private String filePath;
+    private Date createdTime;
+    private String content;
+}

+ 21 - 0
web/src/main/java/cn/seecoder/web/model/po/gradlellm/ConversationPO.java

@@ -0,0 +1,21 @@
+package cn.seecoder.web.model.po.gradlellm;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ConversationPO {
+    private Long id;
+    private Long userId;
+    private String conversationId;
+    private Date createdTime;
+    private String category;
+}

+ 22 - 0
web/src/main/java/cn/seecoder/web/model/po/gradlellm/DifyMessagePO.java

@@ -0,0 +1,22 @@
+package cn.seecoder.web.model.po.gradlellm;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class DifyMessagePO {
+    private Long id;
+    private Long userId;
+    private String conversationId;
+    private Date createdTime;
+    private String role;
+    private String content;
+}

+ 22 - 0
web/src/main/java/cn/seecoder/web/model/po/gradlellm/FileChangePO.java

@@ -0,0 +1,22 @@
+package cn.seecoder.web.model.po.gradlellm;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+
+public class FileChangePO {
+    private Long id;
+    private Long userId;
+    private Long oldFileId;
+    private Date createdTime;
+    private Long newFileId;
+}

+ 18 - 0
web/src/main/java/cn/seecoder/web/model/vo/gradlellm/ChatMessageRequest.java

@@ -0,0 +1,18 @@
+package cn.seecoder.web.model.vo.gradlellm;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.util.List;
+import java.util.Map;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ChatMessageRequest {
+    private Map<String, Object> inputs;
+    private String query;
+    private String response_mode;
+    private String user;
+    private String conversation_id;
+    private List<Object> files;
+}

+ 19 - 0
web/src/main/java/cn/seecoder/web/model/vo/gradlellm/ChatMessageResponse.java

@@ -0,0 +1,19 @@
+package cn.seecoder.web.model.vo.gradlellm;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+import java.util.Map;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ChatMessageResponse {
+    private String event;
+    private String task_id;
+    private String id;
+    private String message_id;
+    private String conversation_id;
+    private String mode;
+    private String answer;
+    private Map<String, Object> metadata;
+    private int created_at;
+}

+ 9 - 0
web/src/main/java/cn/seecoder/web/model/vo/gradlellm/CodeQuestion.java

@@ -0,0 +1,9 @@
+package cn.seecoder.web.model.vo.gradlellm;
+
+import lombok.Data;
+
+@Data
+public class CodeQuestion {
+    String conversationId;
+    String question;
+}

+ 10 - 0
web/src/main/java/cn/seecoder/web/model/vo/gradlellm/CodeQuestionRequest.java

@@ -0,0 +1,10 @@
+package cn.seecoder.web.model.vo.gradlellm;
+
+import lombok.Data;
+
+@Data
+public class CodeQuestionRequest {
+    private String filePath;
+    private String question;
+
+}