瀏覽代碼

mcp更新

lamphua 1 天之前
父節點
當前提交
5068b6a9f7

+ 3
- 1
oa-back/ruoyi-agent/pom.xml 查看文件

@@ -17,7 +17,7 @@
17 17
     </description>
18 18
 
19 19
     <properties>
20
-        <solon.version>3.9.5</solon.version>
20
+        <solon.version>4.0.2</solon.version>
21 21
     </properties>
22 22
 
23 23
     <dependencies>
@@ -42,11 +42,13 @@
42 42
         <dependency>
43 43
             <groupId>org.noear</groupId>
44 44
             <artifactId>solon-ai-mcp</artifactId>
45
+            <version>4.0.2</version>
45 46
         </dependency>
46 47
 
47 48
         <dependency>
48 49
             <groupId>org.noear</groupId>
49 50
             <artifactId>solon-ai-agent</artifactId>
51
+            <version>4.0.2</version>
50 52
         </dependency>
51 53
         <dependency>
52 54
             <groupId>org.slf4j</groupId>

+ 66
- 50
oa-back/ruoyi-agent/src/main/java/com/ruoyi/agent/service/impl/McpServiceImpl.java 查看文件

@@ -23,8 +23,6 @@ import io.milvus.v2.service.vector.request.SearchReq;
23 23
 import io.milvus.v2.service.vector.request.data.BaseVector;
24 24
 import io.milvus.v2.service.vector.request.data.FloatVec;
25 25
 import io.milvus.v2.service.vector.response.SearchResp;
26
-import io.modelcontextprotocol.server.McpServer;
27
-import io.modelcontextprotocol.spec.McpServerSession;
28 26
 import org.apache.poi.hwpf.HWPFDocument;
29 27
 import org.apache.poi.hwpf.usermodel.Paragraph;
30 28
 import org.apache.poi.hwpf.usermodel.Range;
@@ -32,12 +30,13 @@ import org.apache.poi.xwpf.usermodel.*;
32 30
 import org.apache.xmlbeans.XmlCursor;
33 31
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPrGeneral;
34 32
 import org.noear.solon.Solon;
33
+import org.noear.solon.ai.agent.simple.SimpleAgent;
35 34
 import org.noear.solon.ai.annotation.ToolMapping;
36 35
 import org.noear.solon.ai.chat.ChatModel;
37
-import org.noear.solon.ai.chat.ChatResponse;
38 36
 import org.noear.solon.ai.chat.ChatSession;
39 37
 import org.noear.solon.ai.chat.message.AssistantMessage;
40 38
 import org.noear.solon.ai.chat.message.ChatMessage;
39
+import org.noear.solon.ai.chat.prompt.Prompt;
41 40
 import org.noear.solon.ai.chat.session.InMemoryChatSession;
42 41
 import org.noear.solon.ai.mcp.McpChannel;
43 42
 import org.noear.solon.ai.mcp.server.annotation.McpServerEndpoint;
@@ -52,7 +51,7 @@ import java.sql.*;
52 51
 import java.util.*;
53 52
 
54 53
 @Service
55
-@McpServerEndpoint(channel = McpChannel.STREAMABLE_STATELESS , mcpEndpoint = "/mcp/sse")
54
+@McpServerEndpoint(channel = McpChannel.STREAMABLE_STATELESS, mcpEndpoint = "/mcp/sse")
56 55
 public class McpServiceImpl implements IMcpService {
57 56
 
58 57
     private static final EmbeddingModel embeddingModel = new BgeSmallZhV15EmbeddingModel();
@@ -76,10 +75,10 @@ public class McpServiceImpl implements IMcpService {
76 75
         if (milvusServiceUrl == null || milvusServiceUrl.isEmpty()) {
77 76
             throw new IllegalStateException("Milvus service URL 未配置");
78 77
         }
79
-//        milvusClient = new MilvusClientV2(
80
-//                ConnectConfig.builder()
81
-//                        .uri(milvusServiceUrl)
82
-//                        .build());
78
+        // milvusClient = new MilvusClientV2(
79
+        // ConnectConfig.builder()
80
+        // .uri(milvusServiceUrl)
81
+        // .build());
83 82
     }
84 83
 
85 84
     @PreDestroy
@@ -96,19 +95,26 @@ public class McpServiceImpl implements IMcpService {
96 95
         }
97 96
     }
98 97
 
98
+    /**
99
+     * 获取所有表名列表
100
+     */
101
+    @ToolMapping(description = "修改文档格式")
102
+    public JSONArray ModifyDocumentFormat(@Param(description = "文档路径") String documentPath) {
103
+        return getAllTableNames();
104
+    }
105
+
99 106
     /**
100 107
      * 调用LLM+RAG(外部文件+知识库)生成回答
101 108
      */
102 109
     @ToolMapping(description = "章节撰写")
103 110
     public AssistantMessage writeParagraph(@Param(description = "知识库名称") String collectionName,
104
-                                           @Param(description = "智能体名称") String agentName,
105
-                                           @Param(description = "章节名称") String title,
106
-                                           @Param(description = "技术文件地址") String templatePath) throws IOException
107
-    {
111
+            @Param(description = "智能体名称") String agentName,
112
+            @Param(description = "章节名称") String title,
113
+            @Param(description = "技术文件地址") String templatePath) throws Throwable {
108 114
         try {
109 115
             if (templatePath.startsWith("/dev-api"))
110 116
                 templatePath = templatePath.replace("/dev-api/profile", profile);
111
-            templatePath =profile + templatePath;
117
+            templatePath = profile + templatePath;
112 118
             List<String> subTitles = extractSubTitles(templatePath, title);
113 119
             List<JSONObject> contexts = retrieveFromMilvus(collectionName, title, 10);
114 120
             return generateAnswerWithDocumentAndCollection(agentName, templatePath, subTitles, contexts);
@@ -121,9 +127,8 @@ public class McpServiceImpl implements IMcpService {
121 127
      * 查询OA数据生成回答
122 128
      */
123 129
     @ToolMapping(description = "执行SQL查询")
124
-    public JSONArray SQLQuery(@Param(description = "sql语句") String sqlString)
125
-    {
126
-        sqlString = sqlString.replace("[","").replace("]","");
130
+    public JSONArray SQLQuery(@Param(description = "sql语句") String sqlString) {
131
+        sqlString = sqlString.replace("[", "").replace("]", "");
127 132
         return executeQuery(sqlString);
128 133
     }
129 134
 
@@ -131,8 +136,7 @@ public class McpServiceImpl implements IMcpService {
131 136
      * 获取所有表名列表
132 137
      */
133 138
     @ToolMapping(description = "获取数据库中所有表名列表")
134
-    public JSONArray GetAllTableNames()
135
-    {
139
+    public JSONArray GetAllTableNames() {
136 140
         return getAllTableNames();
137 141
     }
138 142
 
@@ -140,8 +144,7 @@ public class McpServiceImpl implements IMcpService {
140 144
      * 获取指定表的结构信息
141 145
      */
142 146
     @ToolMapping(description = "获取指定表的结构信息")
143
-    public JSONObject GetTableStructure(@Param(description = "表名") String tableName)
144
-    {
147
+    public JSONObject GetTableStructure(@Param(description = "表名") String tableName) {
145 148
         return getTableStructure(tableName);
146 149
     }
147 150
 
@@ -155,8 +158,10 @@ public class McpServiceImpl implements IMcpService {
155 158
             throw new RuntimeException("MySQL 驱动未找到", e);
156 159
         }
157 160
 
158
-        try (Connection conn = DriverManager.getConnection(Solon.cfg().getProperty("cmc.mysqlService.jdbcUrl"),Solon.cfg().getProperty("cmc.mysqlService.username"), Solon.cfg().getProperty("cmc.mysqlService.password"));
159
-             PreparedStatement stmt = conn.prepareStatement(sql)) {
161
+        try (Connection conn = DriverManager.getConnection(Solon.cfg().getProperty("cmc.mysqlService.jdbcUrl"),
162
+                Solon.cfg().getProperty("cmc.mysqlService.username"),
163
+                Solon.cfg().getProperty("cmc.mysqlService.password"));
164
+                PreparedStatement stmt = conn.prepareStatement(sql)) {
160 165
 
161 166
             ResultSet rs = stmt.executeQuery();
162 167
             ResultSetMetaData metaData = rs.getMetaData();
@@ -181,6 +186,7 @@ public class McpServiceImpl implements IMcpService {
181 186
 
182 187
     /**
183 188
      * 从Milvus检索相关文档
189
+     * 
184 190
      * @return
185 191
      */
186 192
     public List<JSONObject> retrieveFromMilvus(String collectionName, String query, int topK) {
@@ -198,7 +204,8 @@ public class McpServiceImpl implements IMcpService {
198 204
     /**
199 205
      * 调用LLM生成回答
200 206
      */
201
-    public AssistantMessage generateAnswerWithDocumentAndCollection(String agentName, String templatePath, List<String> titles, List<JSONObject> contexts) throws IOException {
207
+    public AssistantMessage generateAnswerWithDocumentAndCollection(String agentName, String templatePath,
208
+            List<String> titles, List<JSONObject> contexts) throws Throwable {
202 209
         StringBuilder sb = new StringBuilder("招标文件内容:\n\n");
203 210
         String filename = templatePath.replace("_" + agentName, "");
204 211
         File profilePath = new File(filename);
@@ -235,7 +242,7 @@ public class McpServiceImpl implements IMcpService {
235 242
                     .append("段落格式").append(": ")
236 243
                     .append(context.getString("content")).append("\n\n");
237 244
         }
238
-        content.append( "招标文件分析完成,章节内容已写入【<a href='")
245
+        content.append("招标文件分析完成,章节内容已写入【<a href='")
239 246
                 .append(templatePath.replace(profile, "/dev-api/profile"))
240 247
                 .append("'> 技术文件" + "</a>】,请查阅\n\n")
241 248
                 .append("如需修改,请输入技术文件已有内容的章节标题\n\n")
@@ -245,24 +252,28 @@ public class McpServiceImpl implements IMcpService {
245 252
 
246 253
     /**
247 254
      * 调用LLM生成回答
255
+     * 
248 256
      * @return
249 257
      */
250
-    public String generateAnswer(String prompt) throws IOException {
258
+    public String generateAnswer(String prompt) throws Throwable {
251 259
         ChatModel chatModel = ChatModel.of(llmServiceUrl)
252 260
                 .model("Qwen")
253 261
                 .build();
254 262
 
255 263
         List<ChatMessage> messages = new ArrayList<>();
256 264
         messages.add(ChatMessage.ofUser(prompt));
257
-        ChatSession chatSession =  InMemoryChatSession.builder().messages(messages).build();
265
+        ChatSession chatSession = InMemoryChatSession.builder().messages(messages).build();
258 266
         chatSession.addMessage(messages);
259
-        ChatResponse response = chatModel.prompt(chatSession).call();
267
+        SimpleAgent robot = SimpleAgent.of(chatModel)
268
+                .build();
269
+        Prompt prompt1 = Prompt.of(prompt).attrPut("session", chatSession);
260 270
 
261
-        return response.lastChoice().getMessage().getContent();
271
+        return robot.prompt(prompt1).call().getContent();
262 272
     }
263 273
 
264 274
     /**
265 275
      * 写入章节内容
276
+     * 
266 277
      * @return
267 278
      */
268 279
     public void writeContent(String content, List<String> titles, String absolutePath) throws IOException {
@@ -287,8 +298,7 @@ public class McpServiceImpl implements IMcpService {
287 298
                             }
288 299
                         }
289 300
                     }
290
-                }
291
-                else
301
+                } else
292 302
                     text.append(content);
293 303
                 map.put(titles.get(i), text.toString());
294 304
             }
@@ -371,7 +381,8 @@ public class McpServiceImpl implements IMcpService {
371 381
         StringBuilder currentLevel3Content = new StringBuilder();
372 382
         String[] paragraphs = content.split("\n");
373 383
         for (String para : paragraphs) {
374
-            if (para.trim().isEmpty()) continue;
384
+            if (para.trim().isEmpty())
385
+                continue;
375 386
 
376 387
             if (isDocxLevel3Title(para.trim(), document)) {
377 388
                 if (currentLevel3Content.length() != 0) {
@@ -403,7 +414,8 @@ public class McpServiceImpl implements IMcpService {
403 414
         for (int j = 0; j < level2Range.numParagraphs(); j++) {
404 415
             Paragraph p = level2Range.getParagraph(j);
405 416
             String paraText = p.text().trim();
406
-            if (paraText.isEmpty()) continue;
417
+            if (paraText.isEmpty())
418
+                continue;
407 419
 
408 420
             int pLevel = getDocOutlineLevel(p);
409 421
             if (pLevel == 2 && paraText.equals(content.split("\n")[0].trim())) {
@@ -450,7 +462,7 @@ public class McpServiceImpl implements IMcpService {
450 462
 
451 463
         try (XWPFDocument document = new XWPFDocument(fileInputStream)) {
452 464
 
453
-            for (XWPFParagraph  paragraph : document.getParagraphs()) {
465
+            for (XWPFParagraph paragraph : document.getParagraphs()) {
454 466
                 String text = paragraph.getText();
455 467
                 int level = getDocxOutlineLevel(paragraph, document);
456 468
                 if (level > 0) {
@@ -485,7 +497,6 @@ public class McpServiceImpl implements IMcpService {
485 497
                 }
486 498
             }
487 499
 
488
-
489 500
             return subTitles;
490 501
 
491 502
         }
@@ -494,7 +505,7 @@ public class McpServiceImpl implements IMcpService {
494 505
     // 检查一个标题是否是叶子节点(没有更低级别的子标题)
495 506
     private boolean isLeafNode(XWPFDocument doc, XWPFParagraph p, int level) {
496 507
         int index = doc.getPosOfParagraph(p);
497
-        if (index == -1 || index >= doc.getParagraphs().size()-1) {
508
+        if (index == -1 || index >= doc.getParagraphs().size() - 1) {
498 509
             return true;
499 510
         }
500 511
 
@@ -529,12 +540,14 @@ public class McpServiceImpl implements IMcpService {
529 540
 
530 541
     /**
531 542
      * 检索知识库
543
+     * 
532 544
      * @return
533 545
      */
534 546
     private List<List<SearchResp.SearchResult>> retrieve(String collectionName, String query, int topK) {
535
-        List<BaseVector> queryVector = Collections.singletonList(new FloatVec(embeddingModel.embed(query).content().vector()));
547
+        List<BaseVector> queryVector = Collections
548
+                .singletonList(new FloatVec(embeddingModel.embed(query).content().vector()));
536 549
 
537
-        //  加载集合
550
+        // 加载集合
538 551
         LoadCollectionReq loadCollectionReq = LoadCollectionReq.builder()
539 552
                 .collectionName(collectionName)
540 553
                 .build();
@@ -579,7 +592,8 @@ public class McpServiceImpl implements IMcpService {
579 592
 
580 593
                 for (XWPFParagraph paragraph : xwpfDocument.getParagraphs()) {
581 594
                     String text = paragraph.getText().trim();
582
-                    if (text.isEmpty()) continue;
595
+                    if (text.isEmpty())
596
+                        continue;
583 597
 
584 598
                     int paraLevel = getDocxOutlineLevel(paragraph, xwpfDocument);
585 599
 
@@ -614,8 +628,7 @@ public class McpServiceImpl implements IMcpService {
614 628
                 }
615 629
             }
616 630
             return segments;
617
-        }
618
-        else if (filename.endsWith(".doc")) {
631
+        } else if (filename.endsWith(".doc")) {
619 632
             List<TextSegment> segments = new ArrayList<>();
620 633
             try (HWPFDocument hwpfDocument = new HWPFDocument(fileInputStream)) {
621 634
                 StringBuilder currentLevel2Content = new StringBuilder();
@@ -625,7 +638,8 @@ public class McpServiceImpl implements IMcpService {
625 638
                 for (int i = 0; i < range.numParagraphs(); i++) {
626 639
                     Paragraph paragraph = range.getParagraph(i);
627 640
                     String text = paragraph.text().trim();
628
-                    if (text.isEmpty()) continue;
641
+                    if (text.isEmpty())
642
+                        continue;
629 643
 
630 644
                     int paraLevel = getDocOutlineLevel(paragraph);
631 645
 
@@ -664,15 +678,13 @@ public class McpServiceImpl implements IMcpService {
664 678
 
665 679
         else if (filename.endsWith(".pdf")) {
666 680
             Document document = new ApachePdfBoxDocumentParser().parse(fileInputStream);
667
-            DocumentByParagraphSplitter splitter = new DocumentByParagraphSplitter(300,50);
681
+            DocumentByParagraphSplitter splitter = new DocumentByParagraphSplitter(300, 50);
668 682
             return splitter.split(document);
669
-        }
670
-        else if (filename.endsWith(".txt")) {
683
+        } else if (filename.endsWith(".txt")) {
671 684
             Document document = new TextDocumentParser().parse(fileInputStream);
672
-            DocumentByParagraphSplitter splitter = new DocumentByParagraphSplitter(300,50);
685
+            DocumentByParagraphSplitter splitter = new DocumentByParagraphSplitter(300, 50);
673 686
             return splitter.split(document);
674
-        }
675
-        else {
687
+        } else {
676 688
             throw new UnsupportedOperationException("不支持文件类型: " + filename);
677 689
         }
678 690
     }
@@ -689,7 +701,9 @@ public class McpServiceImpl implements IMcpService {
689 701
             throw new RuntimeException("MySQL 驱动未找到", e);
690 702
         }
691 703
 
692
-        try (Connection conn = DriverManager.getConnection(Solon.cfg().getProperty("cmc.mysqlService.jdbcUrl"),Solon.cfg().getProperty("cmc.mysqlService.username"), Solon.cfg().getProperty("cmc.mysqlService.password"))) {
704
+        try (Connection conn = DriverManager.getConnection(Solon.cfg().getProperty("cmc.mysqlService.jdbcUrl"),
705
+                Solon.cfg().getProperty("cmc.mysqlService.username"),
706
+                Solon.cfg().getProperty("cmc.mysqlService.password"))) {
693 707
 
694 708
             DatabaseMetaData metaData = conn.getMetaData();
695 709
 
@@ -738,10 +752,12 @@ public class McpServiceImpl implements IMcpService {
738 752
             throw new RuntimeException("MySQL 驱动未找到", e);
739 753
         }
740 754
 
741
-        try (Connection conn = DriverManager.getConnection(Solon.cfg().getProperty("cmc.mysqlService.jdbcUrl"),Solon.cfg().getProperty("cmc.mysqlService.username"), Solon.cfg().getProperty("cmc.mysqlService.password"))) {
755
+        try (Connection conn = DriverManager.getConnection(Solon.cfg().getProperty("cmc.mysqlService.jdbcUrl"),
756
+                Solon.cfg().getProperty("cmc.mysqlService.username"),
757
+                Solon.cfg().getProperty("cmc.mysqlService.password"))) {
742 758
 
743 759
             DatabaseMetaData metaData = conn.getMetaData();
744
-            ResultSet tables = metaData.getTables(null, null, null, new String[]{"TABLE"});
760
+            ResultSet tables = metaData.getTables(null, null, null, new String[] { "TABLE" });
745 761
 
746 762
             while (tables.next()) {
747 763
                 String tableName = tables.getString("TABLE_NAME");

+ 6
- 0
oa-back/ruoyi-llm/pom.xml 查看文件

@@ -32,6 +32,12 @@
32 32
             <groupId>org.springframework.boot</groupId>
33 33
             <artifactId>spring-boot-starter-web</artifactId>
34 34
         </dependency>
35
+        
36
+        <dependency>
37
+            <groupId>org.noear</groupId>
38
+            <artifactId>solon-ai-mcp</artifactId>
39
+            <version>4.0.2</version>
40
+        </dependency>
35 41
 
36 42
     </dependencies>
37 43
 

+ 38
- 51
oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/McpController.java 查看文件

@@ -12,11 +12,13 @@ import com.ruoyi.llm.domain.CmcChat;
12 12
 import com.ruoyi.llm.service.ICmcAgentService;
13 13
 import com.ruoyi.llm.service.ICmcChatService;
14 14
 import com.ruoyi.llm.service.ICmcTopicService;
15
+
16
+import org.noear.solon.ai.agent.simple.SimpleAgent;
15 17
 import org.noear.solon.ai.chat.ChatModel;
16
-import org.noear.solon.ai.chat.ChatResponse;
17 18
 import org.noear.solon.ai.chat.ChatSession;
18 19
 import org.noear.solon.ai.chat.message.AssistantMessage;
19 20
 import org.noear.solon.ai.chat.message.ChatMessage;
21
+import org.noear.solon.ai.chat.prompt.Prompt;
20 22
 import org.noear.solon.ai.chat.session.InMemoryChatSession;
21 23
 import org.noear.solon.ai.mcp.McpChannel;
22 24
 import org.noear.solon.ai.mcp.client.McpClientProvider;
@@ -44,7 +46,6 @@ import java.nio.charset.StandardCharsets;
44 46
 import java.util.ArrayList;
45 47
 import java.util.List;
46 48
 
47
-
48 49
 /**
49 50
  * mcp模型上下文协议Controller
50 51
  * 
@@ -53,12 +54,11 @@ import java.util.List;
53 54
  */
54 55
 @RestController
55 56
 @RequestMapping("/llm/mcp")
56
-public class McpController extends BaseController
57
-{
57
+public class McpController extends BaseController {
58 58
     private static final Logger log = LoggerFactory.getLogger(McpController.class);
59 59
 
60 60
     /** 仅允许上传 .docx 文件 */
61
-    private static final String[] DOCX_EXTENSION = {"docx"};
61
+    private static final String[] DOCX_EXTENSION = { "docx" };
62 62
 
63 63
     /** Python 脚本输出的文件名 */
64 64
     private static final String FORMATTED_OUTPUT_NAME = "formatted_output.docx";
@@ -89,12 +89,13 @@ public class McpController extends BaseController
89 89
 
90 90
     /**
91 91
      * 自动调用mcp工具问答
92
+     * 
92 93
      * @return
93 94
      */
94 95
     @GetMapping("/answer")
95
-    public AssistantMessage answer(String topicId, String question) throws IOException {
96
+    public AssistantMessage answer(String topicId, String question) throws Throwable {
96 97
         McpClientProvider clientProvider = McpClientProvider.builder()
97
-                .channel(McpChannel.STREAMABLE_STATELESS )
98
+                .channel(McpChannel.STREAMABLE_STATELESS)
98 99
                 .url("http://localhost:8087/mcp/sse")
99 100
                 .build();
100 101
         ChatModel chatModel = ChatModel.of(llmServiceUrl)
@@ -111,25 +112,29 @@ public class McpController extends BaseController
111 112
             messages.add(ChatMessage.ofAssistant(chat.getOutput()));
112 113
         }
113 114
         messages.add(ChatMessage.ofUser(question));
114
-        ChatSession chatSession =  InMemoryChatSession.builder().messages(messages).build();
115
-        ChatResponse response = chatModel.prompt(chatSession).call();
116
-        String resultContent = response.lastChoice().getMessage().getResultContent();
115
+        ChatSession chatSession = InMemoryChatSession.builder().messages(messages).build();
116
+        SimpleAgent robot = SimpleAgent.of(chatModel)
117
+                .build();
118
+        Prompt prompt1 = Prompt.of(question).attrPut("session", chatSession);
119
+        String resultContent = robot.prompt(prompt1).call().getMessage().getResultContent();
117 120
         AssistantMessage assistantMessage;
118 121
         if (resultContent.startsWith("<tool_call>")) {
119 122
             String content = resultContent.replace("<tool_call>\n", "").replace("\n</tool_call>", "");
120 123
             JSONObject jsonObject = JSONObject.parseObject(content);
121 124
             String name = jsonObject.getString("name");
122 125
             JSONObject arguments = jsonObject.getJSONObject("arguments");
123
-            if (arguments.getString("templatePath").contains("招标") || arguments.getString("templatePath").contains("询价")) {
126
+            if (arguments.getString("templatePath").contains("招标")
127
+                    || arguments.getString("templatePath").contains("询价")) {
124 128
                 arguments.put("collectionName", "technical");
125
-                String agentName = cmcAgentService.selectCmcAgentByAgentId(cmcTopicService.selectCmcTopicByTopicId(topicId).getAgentId()).getAgentName();
129
+                String agentName = cmcAgentService
130
+                        .selectCmcAgentByAgentId(cmcTopicService.selectCmcTopicByTopicId(topicId).getAgentId())
131
+                        .getAgentName();
126 132
                 arguments.put("agentName", agentName);
127 133
                 arguments.put("title", question);
128 134
             }
129 135
             resultContent = clientProvider.callTool(name, arguments).getContent();
130 136
             assistantMessage = ChatMessage.ofAssistant(resultContent);
131
-        }
132
-        else
137
+        } else
133 138
             throw new IOException("模型上下文工具未准备就绪,请重试");
134 139
         return assistantMessage;
135 140
     }
@@ -141,12 +146,9 @@ public class McpController extends BaseController
141 146
      * @return 格式化后的文件下载路径
142 147
      */
143 148
     @PostMapping("/formatDoc")
144
-    public AjaxResult formatDoc(MultipartFile file)
145
-    {
146
-        try
147
-        {
148
-            if (file == null || file.isEmpty())
149
-            {
149
+    public AjaxResult formatDoc(MultipartFile file) {
150
+        try {
151
+            if (file == null || file.isEmpty()) {
150 152
                 return AjaxResult.error("上传文件不能为空");
151 153
             }
152 154
 
@@ -167,8 +169,7 @@ public class McpController extends BaseController
167 169
 
168 170
             // 4. 定位格式化后的输出文件(与上传文件同目录下的 formatted_output.docx)
169 171
             File outputFile = new File(new File(uploadedAbsolutePath).getParentFile(), FORMATTED_OUTPUT_NAME);
170
-            if (!outputFile.exists())
171
-            {
172
+            if (!outputFile.exists()) {
172 173
                 return AjaxResult.error("文档格式化失败,未找到输出文件");
173 174
             }
174 175
 
@@ -182,9 +183,7 @@ public class McpController extends BaseController
182 183
             ajax.put("newFileName", FORMATTED_OUTPUT_NAME);
183 184
             ajax.put("originalFilename", file.getOriginalFilename());
184 185
             return ajax;
185
-        }
186
-        catch (Exception e)
187
-        {
186
+        } catch (Exception e) {
188 187
             log.error("Word 文档格式化失败", e);
189 188
             return AjaxResult.error("文档格式化失败: " + e.getMessage());
190 189
         }
@@ -193,16 +192,14 @@ public class McpController extends BaseController
193 192
     /**
194 193
      * 将 /profile/... 形式的资源路径转为本地绝对路径
195 194
      */
196
-    private String toAbsolutePath(String resourcePath)
197
-    {
195
+    private String toAbsolutePath(String resourcePath) {
198 196
         return RuoYiConfig.getProfile() + StringUtils.substringAfter(resourcePath, Constants.RESOURCE_PREFIX);
199 197
     }
200 198
 
201 199
     /**
202 200
      * 根据上传文件的资源路径,推导格式化输出文件的资源路径
203 201
      */
204
-    private String buildOutputResourcePath(String uploadedResourcePath)
205
-    {
202
+    private String buildOutputResourcePath(String uploadedResourcePath) {
206 203
         int lastSlash = uploadedResourcePath.lastIndexOf('/');
207 204
         return uploadedResourcePath.substring(0, lastSlash + 1) + FORMATTED_OUTPUT_NAME;
208 205
     }
@@ -210,14 +207,11 @@ public class McpController extends BaseController
210 207
     /**
211 208
      * 校验格式化服务与模版文件是否可用
212 209
      */
213
-    private void validateFormatterConfig() throws IOException
214
-    {
215
-        if (StringUtils.isEmpty(pythonServiceUrl))
216
-        {
210
+    private void validateFormatterConfig() throws IOException {
211
+        if (StringUtils.isEmpty(pythonServiceUrl)) {
217 212
             throw new IOException("Python 服务 URL 未配置: cmc.pythonService.url");
218 213
         }
219
-        if (StringUtils.isEmpty(docFormatterTemplatePath) || !new File(docFormatterTemplatePath).exists())
220
-        {
214
+        if (StringUtils.isEmpty(docFormatterTemplatePath) || !new File(docFormatterTemplatePath).exists()) {
221 215
             throw new IOException("Word 模版文件路径未配置或文件不存在: " + docFormatterTemplatePath);
222 216
         }
223 217
 
@@ -225,8 +219,7 @@ public class McpController extends BaseController
225 219
                 ? pythonServiceUrl + "health"
226 220
                 : pythonServiceUrl + "/health";
227 221
         String healthResponse = HttpUtils.sendGet(healthUrl);
228
-        if (StringUtils.isEmpty(healthResponse) || !healthResponse.contains("\"status\":\"ok\""))
229
-        {
222
+        if (StringUtils.isEmpty(healthResponse) || !healthResponse.contains("\"status\":\"ok\"")) {
230 223
             throw new IOException("Python 服务不可用,请确认已启动: " + pythonServiceUrl);
231 224
         }
232 225
     }
@@ -238,8 +231,8 @@ public class McpController extends BaseController
238 231
      * @param templateAbsolutePath 模版文件的绝对路径
239 232
      * @return 服务返回的消息内容
240 233
      */
241
-    private String callDocFormatterService(String uploadedAbsolutePath, String templateAbsolutePath) throws IOException
242
-    {
234
+    private String callDocFormatterService(String uploadedAbsolutePath, String templateAbsolutePath)
235
+            throws IOException {
243 236
         JSONObject body = new JSONObject();
244 237
         body.put("uploaded_file_path", uploadedAbsolutePath);
245 238
         body.put("template_file_path", templateAbsolutePath);
@@ -256,38 +249,32 @@ public class McpController extends BaseController
256 249
         conn.setReadTimeout(300_000);
257 250
         conn.setDoOutput(true);
258 251
 
259
-        try (OutputStream os = conn.getOutputStream())
260
-        {
252
+        try (OutputStream os = conn.getOutputStream()) {
261 253
             os.write(body.toJSONString().getBytes(StandardCharsets.UTF_8));
262 254
         }
263 255
 
264 256
         int status = conn.getResponseCode();
265 257
         InputStream responseStream = status >= 400 ? conn.getErrorStream() : conn.getInputStream();
266
-        if (responseStream == null)
267
-        {
258
+        if (responseStream == null) {
268 259
             throw new IOException("文档格式化服务无响应,HTTP 状态码: " + status);
269 260
         }
270 261
 
271 262
         StringBuilder responseBuilder = new StringBuilder();
272 263
         try (BufferedReader reader = new BufferedReader(
273
-                new InputStreamReader(responseStream, StandardCharsets.UTF_8)))
274
-        {
264
+                new InputStreamReader(responseStream, StandardCharsets.UTF_8))) {
275 265
             String line;
276
-            while ((line = reader.readLine()) != null)
277
-            {
266
+            while ((line = reader.readLine()) != null) {
278 267
                 responseBuilder.append(line);
279 268
             }
280 269
         }
281 270
 
282 271
         String responseBody = responseBuilder.toString();
283
-        if (status >= 400)
284
-        {
272
+        if (status >= 400) {
285 273
             throw new IOException("文档格式化服务请求失败,HTTP " + status + ",响应: " + responseBody);
286 274
         }
287 275
 
288 276
         JSONObject result = JSONObject.parseObject(responseBody);
289
-        if (!Boolean.TRUE.equals(result.getBoolean("success")))
290
-        {
277
+        if (!Boolean.TRUE.equals(result.getBoolean("success"))) {
291 278
             throw new IOException("文档格式化失败: " + result.getString("message"));
292 279
         }
293 280
         return result.getString("message");

Loading…
取消
儲存